JNI和【NDK】

初识 ijkplayer 之 从编译到运行

2019-08-15  本文已影响0人  挽风阁2019

macOS(Linux 下基本相同)

  • 一、搭建环境
  • 二、下载 ijkplayer
  • 三、开始编译
  • 四、优化 SO
  • 五、优化 Demo
  • 六、运行 Demo

一、搭建环境

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
brew install git
brew install yasm

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

https://github.com/bilibili/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

    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);
    }
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 默认打开的根目录,应用是没有权限搜索根目录下文件的,一般视频文件放在相册,所以修改默认目录为相册路径就可以播放视频了。

@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 上下载,速度可能会很慢,自行翻墙吧。
上一篇 下一篇

猜你喜欢

热点阅读