美文网首页
FFmpeg NDK 编译

FFmpeg NDK 编译

作者: Nothing_655f | 来源:发表于2022-04-18 15:49 被阅读0次

    FFmpeg NDK 编译

    编译环境

    4.4.0-31-generic #50~14.04.1-Ubuntu SMP x86_64 x86_64 x86_64 GNU/Linux

    配置ndk

    我用的是ndk r15c,下载ndk r15c

    将ndk下载到指定目录下,下载完成后执行unzip android-ndk-r15c-linux-x86_64.zip解压

    再把NDK 目录指定下,这样子方便修改编译脚本

    vi ~/.bashrc  
    export NDK=/home/workspace5/xxx/tools/android-ndk-r15c
    

    yasm 安装

    yasm下载:http://yasm.tortall.net/Download.html

    apt-get install yasm
    

    或者

    tar -xvzf yasm-1.3.0.tar.gz   
    pwd  
    cd yasm-1.3.0/  
    ./configure --prefix=/home/xxx/xxx/tools  
    make   
    make install
    

    x264编译

    x264下载:http://download.videolan.org/x264/snapshots/

    tar -xvzf x264-stable.tar.gz  
    cd x264-stable/
    

    进入x264源码目录后

    touch build_for_an.sh  
    chmod -R 777 build_for_an.sh  
    vi build_for_an.sh
    

    添加如下内容

    #!/bin/bash  
    
    configure()  
    {  
     CPU=$1  
     PREFIX=$(pwd)/android/$CPU  
     HOST=""  
     CROSS_PREFIX=""  
     SYSROOT=""  
     if [ "$CPU" == "armv7-a" ]  
     then  
     HOST=arm-linux  
     SYSROOT=$NDK/platforms/android-21/arch-arm/  
     CROSS_PREFIX=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-  
     else  
     HOST=aarch64-linux  
     SYSROOT=$NDK/platforms/android-21/arch-arm64/  
     CROSS_PREFIX=$NDK/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-  
     fi  
     ./configure \  
     --prefix=$PREFIX \  
     --host=$HOST \  
     --enable-pic \  
     --enable-static \  
     --enable-neon \  
     --extra-cflags="-fPIE -pie" \  
     --extra-ldflags="-fPIE -pie" \  
     --cross-prefix=$CROSS_PREFIX \  
     --sysroot=$SYSROOT  
    }  
    
    build()  
    {  
     make clean  
     cpu=$1  
     echo "build $cpu"  
    
     configure $cpu  
     #-j<CPU核心数>  
     make -j4  
     make install  
    }  
    build arm64  
    build armv7-a
    

    编译成功后输出文件在这个目录

    x264-stable$ ls android/  
    arm64 armv7-a
    

    android/armv7-a/lib和android/arm64/lib目录下找到编译好的静态库libx264.a

    ffmpeg 编译

    ffmpeg下载:http://www.ffmpeg.org/download.html

    我这边使用的3.4 版本

    FFmpeg$ git log --d
    commit 8f5e16b5f129f6d62b5135bd095c41e64998d2ed (HEAD -> 2021-08-31-3.4, origin/release/3.4)

    看支持ffmpeg的支持功能可以用help 命令

    FFmpeg$ ./configure --help
    Usage: configure [options]
    Options: [defaults in brackets after descriptions]
    
    Help options:
      --help                   print this message
      --quiet                  Suppress showing informative output
      --list-decoders          show all available decoders
      --list-encoders          show all available encoders
      --list-hwaccels          show all available hardware accelerators
      --list-demuxers          show all available demuxers
      --list-muxers            show all available muxers
      --list-parsers           show all available parsers
      --list-protocols         show all available protocols
      --list-bsfs              show all available bitstream filters
      --list-indevs            show all available input devices
      --list-outdevs           show all available output devices
      --list-filters           show all available filters
    

    3.4 版本可以支持mediacodec

    FFmpeg$ ./configure --list-hwaccels  
    h263_vaapi               hevc_cuvid               mpeg2_cuvid               mpeg4_mmal               vp8_mediacodec  
    h263_videotoolbox         hevc_d3d11va             mpeg2_d3d11va             mpeg4_vaapi               vp8_qsv  
    h264_cuvid               hevc_d3d11va2             mpeg2_d3d11va2           mpeg4_vdpau               vp9_cuvid  
    h264_d3d11va             hevc_dxva2               mpeg2_dxva2               mpeg4_videotoolbox       vp9_d3d11va  
    h264_d3d11va2             hevc_mediacodec           mpeg2_mediacodec         vc1_cuvid                 vp9_d3d11va2  
    h264_dxva2               hevc_qsv                 mpeg2_mmal               vc1_d3d11va               vp9_dxva2  
    h264_mediacodec           hevc_vaapi               mpeg2_qsv                 vc1_d3d11va2             vp9_mediacodec  
    h264_mmal                 hevc_vdpau               mpeg2_vaapi               vc1_dxva2                 vp9_vaapi  
    h264_qsv                 hevc_videotoolbox         mpeg2_vdpau               vc1_mmal                 wmv3_d3d11va  
    h264_vaapi               mjpeg_cuvid               mpeg2_videotoolbox       vc1_qsv                   wmv3_d3d11va2  
    h264_vda                 mpeg1_cuvid               mpeg2_xvmc               vc1_vaapi                 wmv3_dxva2  
    h264_vda_old             mpeg1_vdpau               mpeg4_cuvid               vc1_vdpau                 wmv3_vaapi  
    h264_vdpau               mpeg1_videotoolbox       mpeg4_mediacodec         vp8_cuvid                 wmv3_vdpau  
    h264_videotoolbox         mpeg1_xvmc
    

    可以看到FFmpeg只支持mediacodec解码,并不支持mediacodec编码,如果要使用FFmpeg进行编码的话需要引入x264,需要编码hevc的话还要引入x265,如上已经编译了x264,接下来编译ffmpeg并导入x264

    修改ffmpeg configure

    diff --git a/configure b/configure  
    index 8de1146..a6d67b1 100755  
    --- a/configure  
    +++ b/configure  
    @@ -3413,10 +3413,10 @@ SLIBPREF="lib"  
     SLIBSUF=".so"  
     SLIBNAME='$(SLIBPREF)$(FULLNAME)$(SLIBSUF)'  
     SLIBNAME_WITH_VERSION='$(SLIBNAME).$(LIBVERSION)'  
    -SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)'  
    -LIB_INSTALL_EXTRA_CMD='$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"'  
    -SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'  
    -SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR) $(SLIBNAME)'  
    +SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'  
    +LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'  
    +SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'  
    +SLIB_INSTALL_LINKS='$(SLIBNAME)'
    

    同样新建编译脚本

    FFmpeg$ vi build_for_android.sh  
    FFmpeg$ chmod -R 777 build_for_android.sh 
    

    注意 libx264 目录要自己指定下,这个是将前面编译的x264拷贝到ffmpeg x264的目录下

    #!/bin/bash
    # NDK=/home/ndk/android-ndk-r15c
    
    ADDI_CFLAGS="-fPIE -pie"
    ADDI_LDFLAGS="-fPIE -pie"
    
    configure()
    {
       CPU=$1
       PREFIX=$(pwd)/android/$CPU
       x264=$(pwd)/x264/android/$CPU  ## 配置X264路径
       HOST=""
       CROSS_PREFIX=""
       SYSROOT=""
       ARCH=""
       if [ "$CPU" == "armv7-a" ]
       then
           ARCH="arm"
           HOST=arm-linux
           SYSROOT=$NDK/platforms/android-21/arch-arm/
           CROSS_PREFIX=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-
       else
           ARCH="aarch64"
           HOST=aarch64-linux
           SYSROOT=$NDK/platforms/android-21/arch-arm64/
           CROSS_PREFIX=$NDK/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-
       fi
       ./configure \
       --cc=gcc \
       --prefix=$PREFIX \
       --disable-encoders \
       --disable-decoders \
       --disable-avdevice \
       --disable-static \
       --disable-doc \
       --disable-ffplay \
       --disable-network \
       --disable-doc \
       --disable-symver \
       --enable-neon \
       --enable-shared \
       --enable-libx264 \
       --enable-gpl \
       --enable-pic \
       --enable-jni \
       --enable-pthreads \
       --enable-mediacodec \
       --enable-encoder=aac \
    --enable-decoder=aac_latm \
    --enable-decoder=aac_at \
    --enable-decoder=aac_fixed \
    --enable-decoder=eac3 \
    --enable-decoder=eac3_at \
    --enable-decoder=atrac3 \
    --enable-decoder=atrac3al \
    --enable-decoder=atrac3p \
    --enable-decoder=atrac3pal \
    --enable-decoder=ac3 \
    --enable-decoder=ac3_at \
    --enable-decoder=ac3_fixed \
       --enable-encoder=gif \
       --enable-encoder=libopenjpeg \
       --enable-encoder=libmp3lame \
       --enable-encoder=libwavpack \
       --enable-encoder=libx264 \
       --enable-encoder=mpeg4 \
       --enable-encoder=pcm_s16le \
       --enable-encoder=png \
       --enable-encoder=srt \
       --enable-encoder=subrip \
       --enable-encoder=yuv4 \
       --enable-encoder=text \
       --enable-decoder=aac \
       --enable-decoder=aac_latm \
       --enable-decoder=libopenjpeg \
       --enable-decoder=mp3 \
       --enable-decoder=mpeg4_mediacodec \
       --enable-decoder=pcm_s16le \
       --enable-decoder=flac \
       --enable-decoder=flv \
       --enable-decoder=gif \
       --enable-decoder=png \
       --enable-decoder=srt \
       --enable-decoder=xsub \
       --enable-decoder=yuv4 \
       --enable-decoder=vp8_mediacodec \
       --enable-decoder=h264_mediacodec \
       --enable-decoder=hevc_mediacodec \
       --enable-hwaccel=h264_mediacodec \
       --enable-hwaccel=mpeg4_mediacodec \
       --enable-ffmpeg \
       --enable-bsf=aac_adtstoasc \
       --enable-bsf=h264_mp4toannexb \
       --enable-bsf=hevc_mp4toannexb \
       --enable-bsf=mpeg4_unpack_bframes \
       --enable-cross-compile \
       --cross-prefix=$CROSS_PREFIX \
       --target-os=android \
       --arch=$ARCH \
       --sysroot=$SYSROOT \
       --extra-cflags="-I$x264/include $ADDI_CFLAGS" \
       --extra-ldflags="-L$x264/lib"
    }
    
    build()
    {
       make clean
       cpu=$1
       echo "build $cpu"
    
       configure $cpu
       make -j4
       make install
    }
    
    build arm64
    build armv7-a
    

    编译输出目录在

    PREFIX=$(pwd)/android/$CPU
    

    导入AS APK

    在Android Studio中新建工程,在第一步中选择Native C++

    在工程的\app\src\main\cpp\目录下新建ffmpeg目录,将上面ffmpeg 编译输出的目录拷贝如下,将lib下的so 分别拷贝出来放在 arm64-v8a,armeabi-v7a 目录下

    D:\AS_WS\FFDEMO\APP\SRC\MAIN\CPP\FFMPEG
    ├─arm64-v8a
    │ ├─bin
    │ ├─include
    │ │ ├─libavcodec
    │ │ ├─libavfilter
    │ │ ├─libavformat
    │ │ ├─libavutil
    │ │ ├─libpostproc
    │ │ ├─libswresample
    │ │ └─libswscale
    │ ├─lib
    │ │ └─pkgconfig
    │ └─share
    │ └─ffmpeg
    │ └─examples
    └─armeabi-v7a
    ├─bin
    ├─include
    │ ├─libavcodec
    │ ├─libavfilter
    │ ├─libavformat
    │ ├─libavutil
    │ ├─libpostproc
    │ ├─libpostproc
    │ ├─libswresample
    │ └─libswscale
    ├─lib
    │ └─pkgconfig
    └─share
    └─ffmpeg
    └─examples

    配置CMakeLists.txt文件

    #导入头文件(可以让项目找到头文件,这样才能调用函数)  
    include_directories("${CMAKE_SOURCE_DIR}/ffmpeg/${ANDROID_ABI}/include")  
    #配置动态链接库所在的目录  
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/ffmpeg/${ANDROID_ABI}")  
    
    # ....
    
    target_link_libraries( # Specifies the target library.  
     native-lib  
     #下面这些是链接的库名称(其实就是so文件去掉前面的lib和后面的.so)  
     avcodec  
     avfilter  
     avformat  
     avutil  
     postproc  
     swresample  
     swscale  
    
    # Links the target library to the log library
    
    # included in the NDK.
    
     ${log-lib} )
    

    配置app的build.gradle文件

    ndk {  
     abiFilters 'armeabi-v7a', 'arm64-v8a'  
    }  
    sourceSets {  
     main {  
     jniLibs.srcDirs = ['src/main/cpp/ffmpeg']  
     }  
    }
    

    这里配置了才会将 ffmpeg的so打包到apk中,注意这里的so目录对应为

    src/main/cpp/ffmpeg/armeabi-v7a/*.so

    src/main/cpp/ffmpeg/arm64-v8a/*.so

    调用ffmpeg 接口

    #include <jni.h>  
    #include <string>  
    extern "C" {  
    #include "libavcodec/avcodec.h"  
    }  
    extern "C" JNIEXPORT jstring JNICALL  
    Java_com_horace_ffdemo_MainActivity_stringFromJNI(  
     JNIEnv* env,  
     jobject /* this */) {  
     std::string hello = "Hello from C++";  
     return env->NewStringUTF(av_version_info());  
    }
    

    可以看到Demo 默认的 Hello 打印变成了 ffmpeg的 av_version_info 版本号

    编译错误记录

    1\. arm-linux-androideabi-clang is unable to create an executable file.C compiler test failed.

    # ffmpeg 4.2版本中configure默认的target-os是clang,需要修改为gcc
    4358 set_default target_os
    4359 if test "$target_os" = android; then
    4360     cc_default="clang"
    4361 fi
    

    2\. error: undefined reference to 'av_version_info()'

    出错原因: ffmpeg是纯C的库,头文件没有做好C++调用的准备 用extern “C”{}套住ffmpeg头文件,用C语言的编译规则来编译ffmpeg代码,就可以了
    extern "C"{
        #include <libavutil/avutil.h>
    }
    

    3\. libavutil/log.c:186: error: undefined reference to 'stderr'

    出错原因:
    
    代码中使用了大量的标准IO设备:stderr 等,这些在NDK15以后,这些都不被支持了,代码本身没问题,只是编译器链接时找不到对应的静态库定义了;
    
    解决方案:
    在编译选项中添加语句-D**ANDROID_API**=[你的android API版本号]即可; 比如我的测试手机为android 5.1.1 对应 API = 22,编译选项中应该添加:-D**ANDROID_API**=22
     adb shell 获取 android 系统版本: adb shell getprop ro.build.version.release` adb shell 获取 android 系统 API 版本: `adb shell getprop ro.build.version.sdk`
    

    4 . libavformat/utils.c:513: error: undefined reference to 'av_parser_close'

    出错原因: 链接静态库先后顺序不正确,引起的符号定义找不到。
    
    解决方案:
    
    1\. 修改静态库的链接顺序。
       target_link_libraries(
               native-lib
               avfilter avformat avcodec avutil swresample swscale
               log)
    
    1\. 忽略静态库的链接顺序。
       target_link_libraries(
               native-lib
               -Wl,--start-group
               avcodec avfilter avformat avutil swresample swscale
              -Wl,--end-group
    
     log)
    

    5\. libavformat/http.c:1649: error: undefined reference to 'inflateEnd

    出错原因: 找不到的z库中的函数的实现。因为 ffmpeg 依赖了z库。编译ffmpeg的时候如果仔细看编译时输出的日志,就可以看到 `External libraries: zlib`
    解决方案:添加z库的依赖。
    
    target_link_libraries(
            native-lib
            -Wl,--start-group
            avcodec avfilter avformat avutil swresample swscale
            -Wl,--end-group
            log
            z
    )
    

    6\. libavformat/hls.c:845: error: undefined reference to 'atof'

    出错原因:
    Google have moved some of the C standard library functions like atof() from being inline functions in header files to normal functions. The latest NDKs will default to building a .so that is only compatible with the latest Android devices that have the atof() function in the device's standard C library (libc.so). This means if you run a library on an older device that has an older version of the C library, you will get an error loading the dll as the expected atof() function will not exist.
    解决方案: 修改ffmpeg编译脚本,指定Android API版本为17,重新编译。
    这里又有一个问题:
    libavcodec/v4l2_buffers.c:434:44: error: call to 'mmap' declared with attribute error: mmap is not available > with _FILE_OFFSET_BITS=64 when using GCC until android-21\. Either raise your minSdkVersion, disable > _FILE_OFFSET_BITS=64, or switch to Clang.
    所以21版本以下,需要取消 _FILE_OFFSET_BITS宏定义。添加编译参数: `-U_FILE_OFFSET_BITS`
    

    参考链接

    Android集成FFmpeg - 简书
    Android集成FFmpeg并实现视频转码_bobcat_kay的博客-CSDN博客_android ffmpeg
    CMake使用简介及CMakeList.txt编写_bobcat_kay的博客-CSDN博客_makelist
    ffmpeg - 简书

    相关文章

      网友评论

          本文标题:FFmpeg NDK 编译

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