本文目录
1.2.1 配置ffmpeg模块路径FFMPEG_MODULE_PATH
1.2.6 在 FFmpeg 模块 jni 目录中添加指向源代码的链接。
1.3.1 问题1 make: command not found
3.2 创建FfmpegRenderersFactory.java文件
Android项目中需要使用Exoplayer接收RTSP流进行解码显示。
在解码过程中出现无法解码AAC音频的问题( aacDecoder_DecodeFrame decoderErr = 0x0005),因此决定在Exoplayer中引入FFMpeg扩展,进行音频解码。
google的官方指导:https://github.com/google/ExoPlayer/tree/release-v2/extensions/ffmpeg
1 Ubuntu环境下编译ffmpeg
1.1 环境准备
1.1.1 Ubuntu系统
系统:ubuntu-20.04.6-desktop-amd64.iso
1.1.2 Exoplayer源码
Exoplayer源码:ExoPlayer-r2.16.0
1.1.3 JAVA
JAVA版本:JAVA11
因为在windows里面使用的是java1.8,所以一开始linux里面也使用同样的1.8版本。在exoplayer交叉编译的时候,提示需要使用JAVA11。
1.1.4 NDK
NDK:r21e
一开始是用windows的android studio的ndk文件夹,后来发现需要下载Linux版本的。( android-ndk-r21e-linux-x86_64.zip)
下载路径:下载路径
1.2 编译ffmpeg源码
1.2.1 配置ffmpeg模块路径FFMPEG_MODULE_PATH
cd "Exoplayer工程目录"
FFMPEG_MODULE_PATH="$(pwd)/extensions/ffmpeg/src/main"
1.2.2 配置NDK环境变量NDK_PATH
NDK_PATH="NDK的安装目录"
1.2.3 设置主机平台变量
Linux 使用linux-x86_64,Mac OS X 使用 darwin-x86_64。
HOST_PLATFORM="linux-x86_64"
1.2.4 设置ffmpeg路径FFMPEG_PATH
cd "Ffmpeg工程目录"
FFMPEG_PATH="$(pwd)"
1.2.5 设置解码器类型
FFMPEG支持的解码器类型可以查阅:Supported formats - ExoPlayer
ENABLED_DECODERS=(vorbis opus flac aac ac3 mp3)
1.2.6 在 FFmpeg 模块 jni 目录中添加指向源代码的链接。
cd "${FFMPEG_MODULE_PATH}/jni" && \
ln -s "$FFMPEG_PATH" ffmpeg
1.2.7 编译
执行build_ffmpeg.sh,为 armeabi-v7a、arm64-v8a、x86 和 x86_64 构建 FFmpeg。
如果需要针对不同的体系结构进行构建,可以编辑该脚本。
我图省事,直接全部编译了。
cd "${FFMPEG_MODULE_PATH}/jni" && \
./build_ffmpeg.sh \
"${FFMPEG_MODULE_PATH}" "${NDK_PATH}" "${HOST_PLATFORM}" "${ENABLED_DECODERS[@]}"
1.3 编译时出现的问题
1.3.1 问题1 make: command not found
出现错误:-bash: make: command not found
原因分析:因为安装系统的时候使用的是最小化mini安装,系统没有安装make、vim等常用命令,直接sudo apt-get install安装下即可。
解决方案:
sudo apt-get update
sudo apt-get install gcc automake autoconf libtool make
1.3.2 问题2
出现错误:编译中出现的错误,都可以通过查阅“ffbuild/config.log”文件查阅。
If you think configure made a mistake, make sure you are using the latest
version from Git. If the latest version fails, report the problem to the
ffmpeg-user@ffmpeg.org mailing list or IRC #ffmpeg on irc.libera.chat.
Include the log file "ffbuild/config.log" produced by configure as this will help
solve the problem.
1.4 编译结果
执行完成之后,ffmpeg项目下会多出一个android-libs,里面是各个平台对应的文件。
2 交叉编译AAR文件
2.1 编译
在完成上述步骤以后,此时的文件并不能直接在Android系统上使用,需要进行交叉编译。
在ExoPlayer项目根目录,通过执行gradlew生成AAR文件。
./gradlew extension-ffmpeg:assembleRelease
2.2 编译中出现的问题
2.2.1 SSLException
编译中出现如下的SSLException异常,尝试了几种办法都没有解决,后来灵机一动把将VPN断掉,结果就好了。
Exception in thread "main" javax.net.ssl.SSLException: Connection has been shutdown: javax.net.ssl.SSLException: java.net.SocketException: Connection reset
at sun.security.ssl.SSLSocketImpl.checkEOF(SSLSocketImpl.java:1533)
at sun.security.ssl.AppInputStream.available(AppInputStream.java:60)
at java.io.BufferedInputStream.available(BufferedInputStream.java:410)
at sun.net.www.MeteredStream.available(MeteredStream.java:170)
at sun.net.www.http.KeepAliveStream.close(KeepAliveStream.java:85)
at java.io.FilterInputStream.close(FilterInputStream.java:181)
at sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.close(HttpURLConnection.java:3517)
at org.gradle.wrapper.Download.downloadInternal(Download.java:71)
at org.gradle.wrapper.Download.download(Download.java:42)
at org.gradle.wrapper.Install$1.call(Install.java:57)
at org.gradle.wrapper.Install$1.call(Install.java:44)
at org.gradle.wrapper.ExclusiveFileAccessManager.access(ExclusiveFileAccessManager.java:65)
at org.gradle.wrapper.Install.createDist(Install.java:44)
at org.gradle.wrapper.WrapperExecutor.execute(WrapperExecutor.java:126)
at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:56)
2.2.2 Java版本异常
Android Gradle plugin requires Java 11 to run. You are currently using Java 1.8.
如前所述,交叉编译时需要java11。
sudo apt-get -y install openjdk-11-jdk
2.3 编译结果
在/ExoPlayer/extensions/ffmpeg/buildout/output/aar下生成需要的aar文件。
3 在Android工程中引入ffmpeg
3.1 在android studio里面引入aar
将编译好的aar文件放在如下路径中。
在build.gradle文件中添加“implementation(fileTree('libs'))”
dependencies {
implementation 'androidx.appcompat:appcompat:1.0.0'
//...
implementation(fileTree('libs'))
}
3.2 创建FfmpegRenderersFactory.java文件
import android.content.Context;
import android.os.Handler;
import com.google.android.exoplayer2.DefaultRenderersFactory;
import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
import com.google.android.exoplayer2.audio.AudioSink;
import com.google.android.exoplayer2.ext.ffmpeg.FfmpegAudioRenderer;
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
import java.util.ArrayList;
public class FfmpegRenderersFactory extends DefaultRenderersFactory {
public FfmpegRenderersFactory(Context context) {
super(context);
setExtensionRendererMode(EXTENSION_RENDERER_MODE_PREFER);
}
@Override
protected void buildAudioRenderers(Context context, int extensionRendererMode, MediaCodecSelector mediaCodecSelector, boolean enableDecoderFallback, AudioSink audioSink, Handler eventHandler, AudioRendererEventListener eventListener, ArrayList<Renderer> out) {
out.add(new FfmpegAudioRenderer());
super.buildAudioRenderers(context, extensionRendererMode, mediaCodecSelector, enableDecoderFallback, audioSink, eventHandler, eventListener, out);
}
}
3.3 创建播放器实例
ExoPlayer exoPlayer =new ExoPlayer.Builder(getApplicationContext(),new FfmpegRenderersFactory(this)).build();
3.4 播放rtsp代码
java文件
String rtspUri = "rtsp://192.168.1.10:554/live/1_0";
ExoPlayer exoPlayer = null;
PlayerView playerView = null;
playerView = findViewById(R.id.styled_player_view);
exoPlayer =new ExoPlayer.Builder(getApplicationContext(),new FfmpegRenderersFactory(this)).build();
playerView.setPlayer(exoPlayer );
playerView.clearFocus();
MediaSource mediaSource = new RtspMediaSource.Factory().createMediaSource(MediaItem.fromUri(rtspUri));
exoPlayer.setMediaSource(mediaSource);
exoPlayer.prepare();
exoPlayer.setPlayWhenReady(true);
xml布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/styled_player_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>