FFmpeg NDK 编译

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  
 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   
#include   
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 
}

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 编译)