初识 ijkplayer 之 从编译到运行
-
环境
macOS(Linux 下基本相同)
-
目录
- 一、搭建环境
- 二、下载 ijkplayer
- 三、开始编译
- 四、优化 SO
- 五、优化 Demo
- 六、运行 Demo
- 附
一、搭建环境
-
安装 brew
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
-
安装 git
brew install git
-
安装 yasm
brew install yasm
-
配置 NDK
https://developer.android.google.cn/ndk/downloads/older_releases.html
据说 r10e 到 r15c 都可以,没测试,我用的 r10e 版本。
下载后解压,并设置相应的环境变量,如果已有高版本 NDK,比如 SDK 自带的r20,可以新建一个文件,将老版本 NDK 环境变量单独配置:
vim env.android
加入如下文本:
export ANDROID_NDK=~/workspace/android/ndk-r10e
export PATH=$PATH:$ANDROID_NDK
保存退出,注意:变量名必须使用 ANDROID_NDK,不然 ijkplayer 不认.
你去改 ijkplayer 源码的话当我没说。
需要时在终端使用 source 单独加载即可,如下:
source env.android
二、下载 ijkplayer
-
官方地址
-
下载代码
git clone https://github.com/bilibili/ijkplayer.git
三、开始编译
1. 加载 NDK 环境
source env.android
2. 初始化依赖项目
cd ijkplayer
// 查看分支
git branch -a
// 选择最新的稳定分支,我这边是r0.7.5
* master
remotes/origin/HEAD -> origin/master
......
remotes/origin/r0.7.5-subtitile
// 创建基于远程分支 r0.7.5-subtitile 的本地分支 main
git checkout -b main remotes/origin/r0.7.5-subtitile
git branch -a
master
* main
remotes/origin/HEAD -> origin/master
......
remotes/origin/r0.7.5-subtitile
// 这步会下载 FFmpeg 源码,并自动执行 init-config.sh 和 ./init-android-libyuv.sh
./init-android.sh
// 如果需要启用 ssl、https 支持,执行这步
./init-android-openssl.sh
// 其他文件有待探索
// ./init-android-j4a.sh
// ./init-android-prof.sh
// ./init-android-libsoxr.sh
// ./init-android-exo.sh
3. 编译依赖库
cd android/contrib/
./compile-openssl.sh clean
./compile-openssl.sh all
./compile-ffmpeg.sh clean
./compile-ffmpeg.sh all
4. 编译 ijkplayer
// 返回 android 目录
cd ../
./compile-ijk.sh all
5. 输出路径
编译成功后,so 文件会存放在 ijkplayer/android/ijkplayer/ 下的对应的库中。
例如 arm64 的 so 文件路径为:ijkplayer/android/ijkplayer/ijkplayer-arm64/src/main/libs/arm64-v8a/
- libijkffmpeg.so
- libijkplayer.so
- libijksdl.so
路径中粗体部分根据编译目标平台不同而不同,这一点 ijkplayer 分的特别详细,很用心。
四、优化 SO
进入 ijkplayer 根目录下的 config 目录,编辑 module-lite.sh 文件:
// 添加格式、rtsp(tcp)支持
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=mpeg4"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=mp2*"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=ac3"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=mjpeg"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=rtsp"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=mjpeg"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=rtp"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=tcp"
并找到下面内容,注释掉
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=rtp"
改为
#export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=rtp"
添加完成后,重新编译 FFmpeg,然后重编 ijk 即可。
同时,可以在此处对 FFmpeg 进行裁剪。
五、优化 Demo
- 修改 Setting.java 增加自己的配置:
public void setFast(boolean fast) {
String key = mAppContext.getString(R.string.pref_key_model_fast);
mSharedPreferences.edit().putBoolean(key, fast).apply();
}
public boolean isFast() {
String key = mAppContext.getString(R.string.pref_key_model_fast);
return mSharedPreferences.getBoolean(key, false);
}
- 修改 IjkVideoView.java 的 createPlayer() 方法,根据自己的配置设置不同参数:
if (mSettings.isFast()) {
// 直播通用配置
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzemaxduration", 100L);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 10240L);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "flush_packets", 1L);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0L);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 1L);
// 最大缓冲
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-buffer-size", 0);
// CPU处理不过来时跳帧处理
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 5);
// seekTo优化
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "enable-accurate-seek", 1);
// m3u8拖动loading加速
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fflags", "fastseek");
// 软解码
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 0);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 0);
// OPENSSL
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 0);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", IjkMediaPlayer.SDL_FCC_RV32);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "http-detect-range-support", 0);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "reconnect", 1);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-hevc", 0);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_transport", "tcp");
}
六、运行 Demo
Demo 默认打开的根目录,应用是没有权限搜索根目录下文件的,一般视频文件放在相册,所以修改默认目录为相册路径就可以播放视频了。
- FileExplorerActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (mSettings == null) {
mSettings = new Settings(this);
}
String path = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/";
String lastDirectory = mSettings.getLastDirectory();
if (!TextUtils.isEmpty(lastDirectory) && new File(lastDirectory).isDirectory())
doOpenDirectory(lastDirectory, false);
else
doOpenDirectory(path, false);
}
使用 Android Studio 打开 ijkplayer/android/ijkplayer/ ,直接执行即可。
重编 so 后会放入该项目下的各个 module,所以也可以直接执行。
附
我将配置好的代码上传到 github.com,有需要的可以从这儿下载:
- https://github.com/ren-gh/ijkplayer
- master:官方0.8.8
- rengh:基于官方r0.7.5做的一些简单配置,并作为后续研发主线
- v0.1.0:基于官方r0.7.5做的一些简单配置
仅修改配置,编译时各个脚本还是需要执行的。
例如:./init-android.sh 和 ./init-android-openssl.sh
这两个脚本,一个下载 FFmpeg,一个下载 OpenSSL,并且是从 github 上下载,速度可能会很慢,自行翻墙吧。