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 - 简书
网友评论