原来FFmpeg这么有意思 (二)

作者: cuieney | 来源:发表于2019-03-17 15:51 被阅读2次

    前提

    文章耽搁了两星期了,可能不少老铁已经忘了,上一篇文章的内容了,不妨回顾一下,之前的文章里面就简单的提及了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 官方

    x264 官方

    github下载地址

    预热

    我会把编译好的FFmpeg静态库传到github,大家可以直接拿来用。
    github下载地址

    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下载地址
    扫二维码也是可以下载的

    downloadapl.png

    接下来就是正题了。编译这个东西。

    编译Android平台FFmpeg

    关于编译Android 平台的库可能网上有一大堆,反正一搜索,肯定有你需要的,这边我主要教大家使用静态库而非动态库,这样你会省了很多很多的麻烦,各种so库的来回粘贴复制,还要写cmakelist文集,配置gradle,对于没怎么玩过FFmpeg的人来说可能需要搞很长时间,这里我将带给大家另一种玩法

    动态库

    动态库其实就是编译出来的so库,link到我们的项目中然后load library 然后通过jni的方式进行操作c上面的东西,这边就是简单概括一下,那我知道了需要哪些东西了,那我们接下来就是,编译这个so库,大家可以在网上看到FFmpeg编译出来的有很多so 你要一个一个的把他们放进我们的项目中。然后cmakelist里面添加东西。这里我教大家把这几个库编译到一个so里面,可以省了你很多麻烦

    • 以上准备都ok的话,这边就可以执行脚本命令了打开我们下载的FFmpeg压缩包,可以看到这些目录结构,我们编译主要用到的就是configure这个binary。可以在下图中看到
    image.png

    脚本我这边就把一部分代码贴上去,全部的我放在了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已经编译出来了

    image.png

    静态库已经出来了

    把编译出来的库合成一个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库

    image.png

    可以看到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已经编译出来了

    image.png

    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,添加背景音乐,视频画面剪切...有想要了解的也可以在留言中提及。我会做相关方面的调研,

    相关文章

      网友评论

        本文标题:原来FFmpeg这么有意思 (二)

        本文链接:https://www.haomeiwen.com/subject/lqaymqtx.html