为什么要学 FFmpeg 开发
FFmpeg 是一款知名的开源音视频处理软件,它提供了丰富而友好的接口支持开发者进行二次开发。
FFmpeg官网
FFmpeg 有六个常用的功能模块:
- libavformat:多媒体文件或协议的封装和解封装库,RTMP、RTSP 等网络协议封装格式;
- libavcodec:音视频编解码库;
- libavfilter:音视频、字幕滤镜库;
- libswscale:图像格式转换库;
- libswresample:音频重采样库;
- libavutil:工具库;
编译环境:
- MacOS 10.15.7
- android-ndk-21.3.6528147
- ffmpeg-4.3.2
交叉编译工具链
第一步,编译ffmpeg
首先下载并解压
wget https://ffmpeg.org/releases/ffmpeg-4.3.2.tar.bz2
tar xvf ffmpeg-4.3.2.tar.bz2
然后编写编译脚本
build_ffmpeg_clang_all.sh
#!/usr/bin/env bash
NDK_PATH=/Volumes/CodeApp/Applications/AndroidSDK/ndk/21.3.6528147
HOST_PLATFORM_WIN=darwin-x86_64
HOST_PLATFORM=$HOST_PLATFORM_WIN
API=21
TOOLCHAINS="$NDK_PATH/toolchains/llvm/prebuilt/$HOST_PLATFORM"
SYSROOT="$NDK_PATH/toolchains/llvm/prebuilt/$HOST_PLATFORM/sysroot"
CFLAG="-D__ANDROID_API__=$API -Os -fPIC -DANDROID "
LDFLAG="-lc -lm -ldl -llog "
PREFIX=/Volumes/CodeApp/C_Build_Work/libs/ffmpeg4-3-2
CONFIG_LOG_PATH=${PREFIX}/log
COMMON_OPTIONS=
CONFIGURATION=
build() {
APP_ABI=$1
echo "======== > Start build $APP_ABI"
case ${APP_ABI} in
armeabi-v7a)
ARCH="arm"
CPU="armv7-a"
MARCH="armv7-a"
TARGET=armv7a-linux-androideabi
CC="$TOOLCHAINS/bin/$TARGET$API-clang"
CXX="$TOOLCHAINS/bin/$TARGET$API-clang++"
LD="$TOOLCHAINS/bin/$TARGET$API-clang"
CROSS_PREFIX="$TOOLCHAINS/bin/arm-linux-androideabi-"
EXTRA_CFLAGS="$CFLAG -mfloat-abi=softfp -mfpu=vfp -marm -march=$MARCH "
EXTRA_LDFLAGS="$LDFLAG"
EXTRA_OPTIONS="--enable-neon --cpu=$CPU "
;;
arm64-v8a)
ARCH="aarch64"
TARGET=$ARCH-linux-android
CC="$TOOLCHAINS/bin/$TARGET$API-clang"
CXX="$TOOLCHAINS/bin/$TARGET$API-clang++"
LD="$TOOLCHAINS/bin/$TARGET$API-clang"
CROSS_PREFIX="$TOOLCHAINS/bin/$TARGET-"
EXTRA_CFLAGS="$CFLAG"
EXTRA_LDFLAGS="$LDFLAG"
EXTRA_OPTIONS=""
;;
x86)
ARCH="x86"
CPU="i686"
MARCH="i686"
TARGET=i686-linux-android
CC="$TOOLCHAINS/bin/$TARGET$API-clang"
CXX="$TOOLCHAINS/bin/$TARGET$API-clang++"
LD="$TOOLCHAINS/bin/$TARGET$API-clang"
CROSS_PREFIX="$TOOLCHAINS/bin/$TARGET-"
EXTRA_CFLAGS="$CFLAG -march=$MARCH -mtune=intel -mssse3 -mfpmath=sse -m32"
EXTRA_LDFLAGS="$LDFLAG"
EXTRA_OPTIONS="--cpu=$CPU "
;;
x86_64)
ARCH="x86_64"
CPU="x86-64"
MARCH="x86_64"
TARGET=$ARCH-linux-android
CC="$TOOLCHAINS/bin/$TARGET$API-clang"
CXX="$TOOLCHAINS/bin/$TARGET$API-clang++"
LD="$TOOLCHAINS/bin/$TARGET$API-clang"
CROSS_PREFIX="$TOOLCHAINS/bin/$TARGET-"
EXTRA_CFLAGS="$CFLAG -march=$CPU -mtune=intel -msse4.2 -mpopcnt -m64"
EXTRA_LDFLAGS="$LDFLAG"
EXTRA_OPTIONS="--cpu=$CPU "
;;
esac
echo "-------- > Start clean workspace"
make clean
echo "-------- > Start build configuration"
CONFIGURATION="$COMMON_OPTIONS"
CONFIGURATION="$CONFIGURATION --logfile=$CONFIG_LOG_PATH/config_$APP_ABI.log"
CONFIGURATION="$CONFIGURATION --prefix=$PREFIX"
CONFIGURATION="$CONFIGURATION --libdir=$PREFIX/libs/$APP_ABI"
CONFIGURATION="$CONFIGURATION --incdir=$PREFIX/includes/$APP_ABI"
CONFIGURATION="$CONFIGURATION --pkgconfigdir=$PREFIX/pkgconfig/$APP_ABI"
CONFIGURATION="$CONFIGURATION --cross-prefix=$CROSS_PREFIX"
CONFIGURATION="$CONFIGURATION --arch=$ARCH"
CONFIGURATION="$CONFIGURATION --sysroot=$SYSROOT"
CONFIGURATION="$CONFIGURATION --cc=$CC"
CONFIGURATION="$CONFIGURATION --cxx=$CXX"
CONFIGURATION="$CONFIGURATION --ld=$LD"
CONFIGURATION="$CONFIGURATION $EXTRA_OPTIONS"
echo "-------- > Start config makefile with $CONFIGURATION --extra-cflags=${EXTRA_CFLAGS} --extra-ldflags=${EXTRA_LDFLAGS}"
./configure ${CONFIGURATION} \
--extra-cflags="$EXTRA_CFLAGS" \
--extra-ldflags="$EXTRA_LDFLAGS"
echo "-------- > Start make $APP_ABI with -j8"
make -j8
echo "-------- > Start install $APP_ABI"
make install
echo "++++++++ > make and install $APP_ABI complete."
}
build_all() {
COMMON_OPTIONS="$COMMON_OPTIONS --target-os=android"
COMMON_OPTIONS="$COMMON_OPTIONS --disable-static"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-shared"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-protocols"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-cross-compile"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-optimizations"
COMMON_OPTIONS="$COMMON_OPTIONS --disable-debug"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-small"
COMMON_OPTIONS="$COMMON_OPTIONS --disable-doc"
COMMON_OPTIONS="$COMMON_OPTIONS --disable-programs"
COMMON_OPTIONS="$COMMON_OPTIONS --disable-ffmpeg"
COMMON_OPTIONS="$COMMON_OPTIONS --disable-ffplay"
COMMON_OPTIONS="$COMMON_OPTIONS --disable-ffprobe"
COMMON_OPTIONS="$COMMON_OPTIONS --disable-symver"
COMMON_OPTIONS="$COMMON_OPTIONS --disable-network"
COMMON_OPTIONS="$COMMON_OPTIONS --disable-x86asm"
COMMON_OPTIONS="$COMMON_OPTIONS --disable-asm"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-pthreads"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-mediacodec"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-jni"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-zlib"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-pic"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-avresample"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=h264"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=mpeg4"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=mjpeg"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=png"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=vorbis"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=opus"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=flac"
echo "COMMON_OPTIONS=$COMMON_OPTIONS"
echo "PREFIX=$PREFIX"
echo "CONFIG_LOG_PATH=$CONFIG_LOG_PATH"
mkdir -p ${CONFIG_LOG_PATH}
build "armeabi-v7a"
# build "arm64-v8a"
# build "x86"
# build "x86_64"
}
echo "-------- Start --------"
build_all
echo "-------- End --------"
cd ffmpeg-4.3.2
./configure --help
交叉工具编译链
,通过该交叉工具编译链,开发者就能在 PC
上编译出可以运行在ARM
平台下的程序了。
无论是自行安装PC上的编译器,还是下载其他平台(Android或者 iOS)的交叉工具编译链,它们都会提供以下几个工具:CC
、AS
、 AR
、LD
、NM
、GDB
。那么,这几个工具到底是做什么用的呢?
-
CC
编译器,对C源文件进行编译处理,生成汇编文件。 -
AS
将汇编文件生成目标文件(汇编文件使用的是指令助记符,AS将它翻译成机器码)。 -
AR
打包器,用于库操作,可以通过该工具从一个库中删除或者增加目标代码模块。 -
LD
链接器,为前面生成的目标代码分配地址空间,将多个目标文件链接成一个库或者是可执行文件。 -
GDB
调试工具,可以对运行过程中的程序进行代码调试工作。 -
STRIP
以最终生成的可执行文件或者库文件作为输入,然后消除掉其中的源码。 -
NM
查看静态库文件中的符号表。 -
Objdump
查看静态库或者动态库的方法签名。
在NDK目录中,交叉编译的工具有
aarch64-linux-android-
对应armv8 arm-linux-androideabi-
对应armv7a
# armeabi-v7a
CROSS_PREFIX="$TOOLCHAINS/bin/arm-linux-androideabi-"
# arm64-v8a
CROSS_PREFIX=aarch64-linux-android-
--sysroot
查找库,有点类似-L 使用
--cc=CC use C compiler CC [gcc]
--cxx=CXX use C compiler CXX [g++]
--ld=LD use linker LD []
上面对交叉编译参数进行了部分的解释,其实就是配置到对应的本台的交叉编译工具链上使用,替换原来系统的gcc g++等工具,链接了NDK的lib库进行编译。对 FFmpeg 项目的编译配置细节就不过多阐述。
此脚本实现了 armeabi-v7a
,arm64-v8a
,x86
,x86_64
4个平台的编译。
- 需要设置环境变量
$NDK_PATH
PREFIX
路径
执行编译
chmod +x build_ffmpeg_clang_all.sh
./build_ffmpeg_clang_all.sh
FFmpeg 集成
基于上节编译好的 FFmpeg 动态库,我们在 Android Studio 上进行简单的集成测试
把编译好目录的libs 和Include分别导入工程
这里因为只导入armeabi-v7a
平台的库,所以先配置Android abi
externalNativeBuild {
cmake {
abiFilters 'armeabi-v7a'
cppFlags ""
}
}
ndk{
abiFilters 'armeabi-v7a'
}
修改cmakeList.txt
导入头文件
include_directories(include)
配置动态库
#添加系统环境变量
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}")
加入链接
target_link_libraries( # Specifies the target library.
native-lib
avcodec
avdevice
avfilter
avformat
avutil
swresample
swscale
# Links the target library to the log library
# included in the NDK.
android
${log-lib} )
修改C++代码
#include
#include
#include
#define LOGD(...) __android_log_print(ANDROID_LOG_INFO,"test",__VA_ARGS__)
//由于 FFmpeg 库是 C 语言实现的,告诉编译器按照 C 的规则进行编译
extern "C" {
#include
#include
#include
#include
#include
#include
#include
};
extern "C" JNIEXPORT jstring JNICALL
Java_com_lecture_av_helloffmpeg_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
char strBuffer[1024 * 4] = {0};
strcat(strBuffer, "libavcodec : ");
strcat(strBuffer, AV_STRINGIFY(LIBAVCODEC_VERSION));
strcat(strBuffer, "\nlibavformat : ");
strcat(strBuffer, AV_STRINGIFY(LIBAVFORMAT_VERSION));
strcat(strBuffer, "\nlibavutil : ");
strcat(strBuffer, AV_STRINGIFY(LIBAVUTIL_VERSION));
strcat(strBuffer, "\nlibavfilter : ");
strcat(strBuffer, AV_STRINGIFY(LIBAVFILTER_VERSION));
strcat(strBuffer, "\nlibswresample : ");
strcat(strBuffer, AV_STRINGIFY(LIBSWRESAMPLE_VERSION));
strcat(strBuffer, "\nlibswscale : ");
strcat(strBuffer, AV_STRINGIFY(LIBSWSCALE_VERSION));
strcat(strBuffer, "\navcodec_configure : \n");
strcat(strBuffer, avcodec_configuration());
strcat(strBuffer, "\navcodec_license : ");
strcat(strBuffer, avcodec_license());
LOGD("GetFFmpegVersion\n%s", strBuffer);
return env->NewStringUTF(hello.c_str());
}
run->
2021-04-11 22:55:29.337 2388-2388/com.lecture.av.helloffmpeg I/test: GetFFmpegVersion libavcodec : 58.135.100 libavformat : 58.77.100 libavutil : 56.71.100 libavfilter : 7.111.100 libswresample : 3.10.100 libswscale : 5.10.100 avcodec_configure : --target-os=android --disable-static --enable-shared --enable-protocols --enable-cross-compile --enable-optimizations --disable-debug --enable-small --disable-doc --disable-programs --disable-ffmpeg --disable-ffplay --disable-ffprobe --disable-symver --disable-network --disable-x86asm --disable-asm --enable-pthreads --enable-mediacodec --enable-jni --enable-zlib --enable-pic --enable-avresample --enable-decoder=h264 --enable-decoder=mpeg4 --enable-decoder=mjpeg --enable-decoder=png --enable-decoder=vorbis --enable-decoder=opus --enable-decoder=flac --logfile=/Volumes/CodeApp/C_Build_Work/libs/ffmpeg4-3-2/log/config_armeabi-v7a.log --prefix=/Volumes/CodeApp/C_Build_Work/libs/ffmpeg4-3-2 --libdir=/Volumes/CodeApp/C_Build_Work/libs/ffmpeg4-3-2/libs/armeabi-v7a --incdir=/Volumes/CodeApp/C_Build_Work/libs/ffmpeg4-3-2/includes/armeabi-v7a
FFmpeg集成完成!
Android 工程代码