前提
文章耽搁了两星期了, 可能不少老铁已经忘了, 上一篇文章的内容了, 不妨回顾一下, 之前的文章里面就简单的提及了 FFMPEG 的一些简单命令的用法, 官方下载不同平台的静态库, 可以直接执行 binary 文件来编辑一些音视频文件. 我上次只是说了视频画面合成的用法, 通过 vstack 和 hstack, 来进行合成. 这次呢 我将教大家, 如何在自己的 Android 手机上进行视频画面拼接的方法 以及如何通过官方库编译出 Android 平台的 so 库及静态库. 这次内容可能会很多, 也涉及到了很多 shell 脚本语言的的东西. 希望老铁们耐心看看, 绝对会有帮助.
这个是之前的系列
原来 FFMPEG 这么有意思 (一)
这里教大家一些骚操作,
根本不需要通过 JNI 的方式来执行 FFMPEG 的方法, 直接 java 语言就可以来玩 FFMPEG, 但是有些功能是有局限性的.(静态库)
把编译出的所有 so 库打包成一个 so 来玩, 省事.(动态库)
准备
这边主要是为了大家下载版本号相同, 根据脚本可以编译成功, 不然每个版本里面可能要修改一些参数 我这里就按照我的环境和大家说一下把
Ubuntu ( Ubuntu 18.10 )
FFMPEG 官方库(4.0.2) x264 官方库(最新就行)
这边已经上传到了 GitHub(编译脚本及编译库, 脚本通用, 但是不同平台库可能不同)
FFMPEG 官方 http://ffmpeg.org/download.html#build-linux
x264 官方 https://www.videolan.org/developers/x264.html
GitHub 下载地址 https://github.com/Cuieney/Super-Stitch
预热
我会把编译好的 FFMPEG 静态库传到 GitHub, 大家可以直接拿来用. GitHub 下载地址 https://github.com/Cuieney/Super-Stitch
App 执行静态库脚本
在我们的 App 中如果说想执行二进制文件, 必须放在我们的私有目录下, sdcard 只是 Android 文件系统 linker 出来的一个文件夹, 是没有权限执行二进制文件的, 而我们 App 的私有目录是可以的. 下面我会给出一下代码 仅供大家参考.
二进制的 FFMPEG 已经上传到了 GitHub , 如果有兴趣的同学可以下载下来, 自己的 App 中跑起来, 我们可以把这个文件放在 assets 文件夹下, 然后 App 运行的时候把这个文件 copy 到 App 的私有目录下
- boolean isFileCopied = FileUtils.copyBinaryFromAssetsToData(App.getInstance(),
- cpuArchNameFromAssets + File.separator + FileUtils.ffmpegFileName,
- FileUtils.ffmpegFileName);
- // make file executable
- if (isFileCopied) {
- if (!ffmpegFile.canExecute()) {
- Log.d(BuildInfo.TAG, "FFmpeg is not executable, trying to make it executable ...");
- if (ffmpegFile.setExecutable(true)) {
- Log.d(BuildInfo.TAG, "FFmpeg is executable");
- }
- } else {
- Log.d(BuildInfo.TAG, "FFmpeg is executable");
- }
- }
上面的代码自己可以编写. 我这就不全部贴了. App 运行起来后, 把这个文件 copy 到本地, 然后调用
ffmpegFile.setExecutable(true)
这样就可以执行 FFMPEG 了. Android 中也提供了执行 commend 的方法
- public static Process run(String[] commandString) {
- Process process = null;
- try {
- process = Runtime.getRuntime().exec(commandString);
- String output = Util.convertInputStreamToString(process.getErrorStream());
- Log.i("cuieney",output);
- } catch (IOException e) {
- Log.e(BuildInfo.TAG,"Exception while trying to run:" + commandString+e.toString());
- }
- return process;
- }
就是通过调用 runtime.exec 就可以了 把命令写进去就好.
run({"ffmpeg",""})
这样就 ok 了. 成功的话可以在 logcat 中看到这些, log 太多了 我就没复制
FFMPEG version 4.0.2 Copyright (c) 2000-2018 the FFMPEG developersbuilt with gcc 4.9.x (GCC) 20150123 (prerelease)........
我这里根据编译出来的库, 完成了一些功能
apk 下载地址 扫二维码也是可以下载的
接下来就是正题了. 编译这个东西.
编译 Android 平台 FFMPEG
关于编译 Android 平台的库可能网上有一大堆, 反正一搜索, 肯定有你需要的, 这边我主要教大家使用静态库而非动态库, 这样你会省了很多很多的麻烦, 各种 so 库的来回粘贴复制, 还要写 cmakelist 文集, 配置 gradle, 对于没怎么玩过 FFMPEG 的人来说可能需要搞很长时间, 这里我将带给大家另一种玩法
动态库
动态库其实就是编译出来的 so 库, link 到我们的项目中然后 load library 然后通过 jni 的方式进行操作 c 上面的东西, 这边就是简单概括一下, 那我知道了需要哪些东西了, 那我们接下来就是, 编译这个 so 库, 大家可以在网上看到 FFMPEG 编译出来的有很多 so 你要一个一个的把他们放进我们的项目中. 然后 cmakelist 里面添加东西. 这里我教大家把这几个库编译到一个 so 里面, 可以省了你很多麻烦
以上准备都 ok 的话, 这边就可以执行脚本命令了打开我们下载的 FFMPEG 压缩包, 可以看到这些目录结构, 我们编译主要用到的就是 configure 这个 binary. 可以在下图中看到
脚本我这边就把一部分代码贴上去, 全部的我放在了 GitHub 上了(下面这个脚本名称叫做 build_ffmpeg_android.sh, 可以在我上面的写的地址里找到), 我会写一些注释在上面
可以看到下面的 代码中有一个 MODULE(主要做一些里面库的 enabel 和 disable, 把需要的库我们编译进去 不需要的当然是不用了) GENERAL(主要作用是一些参数的设置和额外的库添加)和 LIB_TYPE(这个就是设置编译 shared 还是 static 的了)静态库或者动态库
- function build
- {
- pushd FFMPEG
- ./configure \
- --logfile=config.log \
- --target-os=Android \
- --prefix=$PREFIX \
- ${MODULE} \
- ${GENERAL} \
- ${LIB_TYPE} \
- --sysroot=$PLATFORM \
- --extra-cflags="-fPIE -fPIC -std=c11" \
- --extra-ldflags="$flags $shared_flag" \
- # essencial for dynamic library
- sed -i -E "s/^(#define HAVE_SYSCTL).*/\1 0/" config.h
- make clean
- make -j$NUM_OF_CORE
- make install
- popd
- }
- build
这个就是 GENERAL 的参数
- GENERAL=" \
- --extra-libs="-lgcc" \
- --arch=${ARCH} \
- --cc=$PREBUILT/bin/${BIN_PREFIX}-gcc \
- --cross-prefix=$PREBUILT/bin/${BIN_PREFIX}- \
- --nm=$PREBUILT/bin/${BIN_PREFIX}-nm \
- --extra-cflags="-I$X264_INCLUDE" \
- --extra-ldflags="-L$X264_LIB" \
- "
这个是 module 的参数 只放了一部分, 用到的可以 enable 用不到的 disable 不然编译出来的库很大. 那我们的 apk 也会相应的很大, 可以到 GitHub 下载原始文件
- MODULE=" \
- --enable-jni \
- --enable-pic \
- --enable-gpl \
- --enable-zlib \
- --enable-yasm \
- --enable-small \
- --enable-pthreads \
- --enable-mediacodec \
- --enable-libx264 \
- --enable-cross-compile \
- --disable-doc \
- --disable-ffplay \
- --disable-ffprobe \
- --disable-network \
- --enable-neon
- --disable-Linux-perf \
- --enable-encoder=libx264 \
- --enable-encoder=aac \
- --enable-encoder=mpeg4 \
- --enable-encoder=mjpeg \
- --enable-encoder=PNG \
- --disable-muxers \
静态库
这边脚本里写了一些判断, 我们可以执行脚本的时候 再加个字段就可以编译出我们需要的静态库, 我这边的脚本名字叫做 build_ffmpeg_android.sh, 所以只要按照下面的命令执行即可
./build_ffmpeg_andori.sh static
如果你想编译动态库 只要把 static 改成 shared 即可.
现在的电脑应该编译的很快, 执行成功应该可以看到下面的目录, so 已经编译出来了
静态库已经出来了
把编译出来的库合成一个 so 库
只要把以下的代码添加到编译脚本里面即可.
- $TOOLCHAIN/bin/${BIN_PREFIX}-ld \
- -rpath-link=$PLATFORM/usr/lib \
- -L$PLATFORM/usr/lib \
- -L$PREFIX/lib \
- -L$X264_LIB \
- -soname libffmpeg.so -shared -nostdlib -Bsymbolic --whole-archive --no-undefined -o \
- $PREFIX/libffmpeg.so \
- $PREFIX/lib/libavcodec.a \
- $PREFIX/lib/libavfilter.a \
- $PREFIX/lib/libavformat.a \
- $PREFIX/lib/libavutil.a \
- $PREFIX/lib/libswresample.a \
- $PREFIX/lib/libswscale.a \
- $X264_ALIB/libx264.a \
- -lc -lm -lz -ldl -llog --dynamic-linker=/system/bin/linker \
- $TOOLCHAIN/lib/gcc/${BIN_PREFIX}/4.9.x/libgcc.a
从上面脚本可以看到 相当于把这些库 linker 到我们上面的 libffmpeg.so 里面.
成功的话可以看到 FFMPEG 目录下的 Android 中看到这个 so 库
可以看到 libffmpeg.so 已经出来了
编译 Lib264 库
作用
为什么用这个库呢, 如果说你已经以上步骤都成功了, 而且已经运行到 Android 机上面了, 你会发现编码出来的视频文件明显质量很差, 不应该说很差, 反正肯定是自己不满意的结果. 说了这么多, 大家应该知道这个库的作用了, 提高编码质量, 为什么我在官网下载的 pc 库会质量很好呢, 那是因为他们已经把这个库编进去了而且已经 enable. 那么我们这里要做的就是去下载 Lib264 官方源码, 编译出 Android 平台的 然后把这个库给打进 FFMPEG 里面.
编译 Lib264
这个库编译就比较简单了. 参数和代码没有那么多, GitHub 上面放的脚本名字叫做 (build_x264_andorid.sh) 大家下载下来就可以用的
如果想编译不同的版本同样可以通过 后缀 shared 或者 static 就可以了
- LIB_TYPE=${1-static}
- echo '@@@#####'${LIB_TYPE}
- function build
- {
- pushd x264
- # remove suffix of libx264.so
- sed -in 's/so\.\$API/so/g' configure
- ./configure \
- --prefix=./Android/$ABI \
- --enable-$LIB_TYPE \
- --enable-pic \
- --host=$BIN_PREFIX \
- --cross-prefix=${PREBUILT}/bin/${BIN_PREFIX}- \
- --extra-cflags="-fPIC -fPIE -std=c11" \
- --sysroot=$PLATFORM
- sed -i 's/-f -s/-f/g' ./Makefile
- make clean
- make -j$NUM_OF_CORE
- make install
- tree Android
- popd
- }
- build
- printf "$success" "x264"
./build_x264_android.sh shared
执行成功应该可以看到下面的目录在 x264/Android / 目录下, so 已经编译出来了
FFMPEG Lib264 合成
上面已经把每个平台的库都编译好了, 那我们怎么把这两个库合成在一起呢, 细心的同学已经看到了, 我上面贴脚本的时候已经把代码贴进去了, 就是在我们编译脚本 build_ffmpeg_android.sh 的时候已经带进去了就是那个 GENERAL 字段
看看下面的字段 cflags 和 ldflags 已经把我们之前编译的 x264 编译进去了.
- X264_INCLUDE=../x264/Android/$ABI/include
- X264_LIB=../x264/Android/$ABI/lib
- X264_ALIB=../x264
- GENERAL=" \
- --extra-libs="-lgcc" \
- --arch=${ARCH} \
- --cc=$PREBUILT/bin/${BIN_PREFIX}-gcc \
- --cross-prefix=$PREBUILT/bin/${BIN_PREFIX}- \
- --nm=$PREBUILT/bin/${BIN_PREFIX}-nm \
- --extra-cflags="-I$X264_INCLUDE" \
- --extra-ldflags="-L$X264_LIB" \
- "
编译不同 ARCH 库(armeabi-v7a arm64-v8a...)
这个就比较简单了, 既然一个平台已经成功, 那么其他的改一下 编译平台不就行了. 可以在我们的脚本中修改一些参数即可,
我这边做了一些判断可以在编译脚本前, 在我们的 common.sh 目录下修改以下 ARCH 既可以, 然后在执行 build_ffmpeg_android.sh 即可.
- #ARCH=ARM
- ARCH=aarch64
- [[ $ARCH = "arm" ]] && \
- { BIN_PREFIX=ARM-Linux-androideabi; ABI=armeabi-v7a; } || { BIN_PREFIX=aarch64-Linux-Android; ABI=arm64-v8a; }
- NDK=~/Downloads/Android-ndk-r14b
- PLATFORM=$NDK/platforms/Android-21/arch-arm64
- PREBUILT=$NDK/toolchains/${BIN_PREFIX}-4.9/prebuilt/Linux-x86_64
- TOOLCHAIN=$NDK/toolchains/${BIN_PREFIX}-4.9/prebuilt/Linux-x86_64
- NUM_OF_CORE=$(nproc)
- success="${BLACKB}${YELLOWF}build %s success${RESET}\n"
以上就是 common.sh 脚本. 只要修改上面的 arch 参数就行 , 如果要变异 ARM 的话记得把 PLATFORM 这个参数后面的 64 去了.
收尾
可能上面有说的不清楚的. 大家可以在留言中或者私信中 提问. 如果上面有说错的地方, 大家可以积极的和我说. 我会在文章中纠正.
To be continue..... 下回我会带给大家 关于 App 的一些内容. 比如画面拼接, 添加 logo, 添加背景音乐, 视频画面剪切... 有想要了解的也可以在留言中提及. 我会做相关方面的调研,
来源: https://juejin.im/post/5c8dfd4f51882545f075d4d8