赞
踩
1、提供的能力
codec组件基于FFmpeg做的封装,通过命令行+jni调用FFmpeg库源码的方式提供编解码、封装/解封装、滤镜、重采样等能力,具体包括:
1、编码
原始音频(pcm)、视频(yuv)数据编码(aac、ac3、mp3、h264、h265、mpeg4、mjpeg)
2、解码
编码的音频、视频数据解码成原始数据(pcm、yuv)
3、转码
基于1-2实现转码,如RV40转换为MPEG4格式。有的封装格式标准规定,只支持几种固定的音频、视频编码格式,比如FLV视频支持H263、H264、Screen Video、On2 VP6,音频支持MP3、pcm、AAC、speex,在封装FLV时音频、视频不符合标准的话,会封装失败,比如想把AC3封装进FLV,此时需要转码。
4、封装
音频流、视频流封装成媒体文件
5、解封装
音视频数据(mp4、avi、flv)解封装音频流、视频流
6、转封装
基于3、4、5实现媒体文件格式转换,如mp4 ↔flv。可选定编码格式,也可以直接copy只转换媒体文件格式。
7、滤镜
视频裁剪、编辑、合成、水印等滤镜功能。
2、接口设计
接口分为jni接口、结果回调接口、调用接口。jni接口调用FFmpeg源码实现音视频处理,jni接口分为两部分,一是命令行接口,也就是通过FFmpeg命令行调用FFmpeg动态库,并返回命令执行进度以及执行结果;二是普通的jni接口,有些功能没有必要通过命令行实现,通过调用FFmpeg中源码更容易实现,比如获取FFmpeg版本、媒体文件信息。
2.1、jni接口
- static {
- System.loadLibrary("keffmpeg");
- }
-
- /**
- * @return 版本号
- */
- public static native String getVersion();
-
- /**
- * 获取媒体文件(输入/输出)信息,封装信息+视频流信息+音频流信息
- *
- * @param filePath 文件路径
- * @return
- */
- public static native String getFileInfo(String filePath);
-
- /**
- * 通过命令行调用FFmpeg中的main方法
- *
- * @param argc 参数个数
- * @param argv FFmpeg命令行参数
- */
- public static native void exec(int argc, String[] argv);
-
- /**
- * 终止执行
- */
- public static native void exit();
-
-
- /**
- * FFmpeg执行结束后的结果回调,由C代码中调用
- */
- public static void onResult(int ret) {
- LogUtil.d(TAG, "onResult ret:" + ret);
- CmdExecutor.getInstance().getExecListener().onResult(ret);
- }
-
- /**
- * FFmpeg执行进度回调,由C代码调用
- */
- public static void onProgress(float progress) {
- LogUtil.d(TAG, "onProcess:" + progress);
- CmdExecutor.getInstance().getExecListener().onProgress(progress);
- }
FFmpeg的执行进度、执行结果需要告知调用方,通过Java中的反射机制获取jclass中的回调方法,JNIEnv调用Java中的回调方法并将C程序的执行结果传递给Java层。
- /**
- * 回调执行Java方法
- * 参看 Jni反射+Java反射
- */
- void callJavaMethod(JNIEnv *env, jclass clazz, int ret) {
- if (clazz == NULL) {
- LOGE("---------------clazz isNULL---------------");
- return;
- }
- jmethodID methodID = (*env)->GetStaticMethodID(env, clazz, "onResult", "(I)V");
- if (methodID == NULL) {
- LOGE("---------------methodID isNULL---------------");
- return;
- }
- //调用该java方法
- (*env)->CallStaticVoidMethod(env, clazz, methodID, ret);
- }
-
- void callJavaMethodProgress(JNIEnv *env, jclass clazz, float ret) {
- if (clazz == NULL) {
- LOGE("---------------clazz isNULL---------------");
- return;
- }
- jmethodID methodID = (*env)->GetStaticMethodID(env, clazz, "onProgress", "(F)V");
- if (methodID == NULL) {
- LOGE("---------------methodID isNULL---------------");
- return;
- }
- //调用该java方法
- (*env)->CallStaticVoidMethod(env, clazz, methodID, ret);
- }
-
-
-
- /**
- * 执行进度回调
- *
- * @param progress 执行进度,单位:秒
- */
- void onProgress(float progress);
-
- /**
- * 命令执行结束回调
- *
- * @param code 执行结果,code==0执行完成,!=0命令执行过程中出错
- */
- void onResult(int code);
注册监听器、反注册监听器、执行命令、终止执行
- public void registerListener(OnCmdExecListener onCmdExecListener) {
- if (onCmdExecListener == null) return;
- if (!mOnCmdExecListenerList.contains(onCmdExecListener)) {
- mOnCmdExecListenerList.add(onCmdExecListener);
- }
-
-
- }
-
- public void unRegisterListener(OnCmdExecListener onCmdExecListener) {
- if (onCmdExecListener == null) return;
- if (mOnCmdExecListenerList.contains(onCmdExecListener)) {
- mOnCmdExecListenerList.remove(onCmdExecListener);
- }
-
- }
-
- public void exec(String[] cmdArray) {
- if (cmdArray == null || cmdArray.length == 0) return;
- CodecNative.exec(cmdArray.length, cmdArray);
- }
-
- public void exit() {
- CodecNative.exit();
- }
-
- public OnCmdExecListener getExecListener() {
- return mOnCmdExecListener;
- }
1、FFmpeg官网下载源码
Download FFmpeg ,本方案下载的5.0.1 "Lorentz"版本,包含如下库:
- libavutil 57. 17.100
- libavcodec 59. 18.100
- libavformat 59. 16.100
- libavdevice 59. 4.100
- libavfilter 8. 24.100
- libswscale 6. 4.100
- libswresample 4. 3.100
- libpostproc 56. 3.100
2、配置ndk环境变量
ndk下载地址:NDK 下载 | Android NDK | Android Developers ,本方案使用android-ndk-r17c。
vim编辑bash_profile配置ndk环境变量,vim ~/.bash_profile,如下:
- export NDKROOT=/Users/xiaobeizhang/Desktop/ke/ndk/android-ndk-r17c
- export PATH=${PATH}:${NDKROOT}
source ~/.bash_profile保存
将ndk目录下的所有文件赋予最高权限,chmod -R 777 ./
3、编辑FFmpeg中的configure文件
由于默认configure脚本编译出来的动态库版本号在文件名后缀.so之后,如:libavcodec.so.7所以需要对configure进行修改:
- #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)'
将默认编译器由clang改为gcc
- set_default target_os
- if test "$target_os" = android; then
- cc_default="clang"
- fi
4、编辑编译脚本
ffmpeg-5.0.1目录下创建编译动态库的脚本,touch build_android.sh
- # 清空上次的编译
- make clean
- ADDI_CFLAGS="-marm"
- PLATFORM=arm-linux-androideabi
- # NDK路径
- NDK=/Users/xiaobeizhang/Desktop/ke/ndk/android-ndk-r17c
- # 设置android平台编译器的版本 这里采用Android 4.4
- SYSROOT=$NDK/platforms/android-19/arch-arm/
- ISYSROOT=$NDK/sysroot
- ASM=$ISYSROOT/usr/include/$PLATFORM
- #编译使用的工具链
- TOOLCHAIN=$NDK/toolchains/$PLATFORM-4.9/prebuilt/darwin-x86_64
- # 编译产物输出路径
- OUTPUT=/Users/xiaobeizhang/Desktop/ke/ffmpeg/ffmpeg-5.0.1/android_out
- function build
- {
- ./configure \
- --prefix=$OUTPUT \
- --enable-shared \
- --disable-static \
- --disable-doc \
- --disable-ffmpeg \
- --disable-ffplay \
- --disable-ffprobe \
- --disable-avdevice \
- --disable-doc \
- --disable-symver \
- --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
- --target-os=android \
- --arch=armeabi-v7a \
- --enable-cross-compile \
- --sysroot=$SYSROOT \
- --extra-cflags="-I$ASM -isysroot $ISYSROOT -Os -fpic -marm" \
- --extra-ldflags="-marm" \
- $ADDITIONAL_CONFIGURE_FLAG
- make clean
- #启动4个线程同时编译
- make -j4
- #生成的二进制文件安装到configure设置的prefix路径
- make install
- }
- build
添加可执行权限,chmod a+x build_android.sh。
上述方式各个库会打包成6个动态库,不方便调用,可采用如下脚本打包成一个动态库:
- # 清空上次的编译
- make clean
- ADDI_CFLAGS="-marm"
- PLATFORM=arm-linux-androideabi
- # NDK路径
- NDK=/Users/xiaobeizhang/Desktop/ke/ndk/android-ndk-r17c
- # 设置android平台编译器的版本 这里采用Android 4.4
- SYSROOT=$NDK/platforms/android-17/arch-arm/
- ISYSROOT=$NDK/sysroot
- ASM=$ISYSROOT/usr/include/$PLATFORM
- #编译使用的工具链
- TOOLCHAIN=$NDK/toolchains/$PLATFORM-4.9/prebuilt/darwin-x86_64
- # 编译产物输出路径
- OUTPUT=/Users/xiaobeizhang/Desktop/ke/ffmpeg/ffmpeg-5.0.1/android_out_single
- function build
- {
- ./configure \
- --target-os=android \
- --prefix=$OUTPUT \
- --enable-cross-compile \
- --enable-static \
- --disable-shared \
- --disable-doc \
- --disable-ffmpeg \
- --disable-ffplay \
- --disable-ffprobe \
- --disable-avdevice \
- --disable-doc \
- --disable-symver \
- --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
- --arch=arm \
- --sysroot=$SYSROOT \
- --extra-cflags="-I$ASM -isysroot $ISYSROOT -D__ANDROID_API__=17 -U_FILE_OFFSET_BITS -Os -fPIC -DANDROID -Wno-deprecated -mfloat-abi=softfp -marm" \
- --extra-ldflags="$ADDI_LDFLAGS" \
- $ADDITIONAL_CONFIGURE_FLAG
- make clean
- #启动4个线程同时编译
- make -j4
- #生成的二进制文件安装到configure设置的prefix路径
- make install
- # 打包
- $TOOLCHAIN/bin/arm-linux-androideabi-ld \
- -rpath-link=$SYSROOT/usr/lib \
- -L$SYSROOT/usr/lib \
- -L$PREFIX/lib \
- -soname libffmpeg.so -shared -nostdlib -Bsymbolic --whole-archive --no-undefined -o \
- $OUTPUT/libffmpeg.so \
- libavcodec/libavcodec.a \
- libavfilter/libavfilter.a \
- libavformat/libavformat.a \
- libavutil/libavutil.a \
- libswresample/libswresample.a \
- libswscale/libswscale.a \
- -lc -lm -lz -ldl -llog --dynamic-linker=/system/bin/linker \
- $TOOLCHAIN/lib/gcc/arm-linux-androideabi/4.9.x/libgcc.a
-
- # strip 精简文件
- $TOOLCHAIN/bin/arm-linux-androideabi-strip $OUTPUT/libffmpeg.so
- }
- build
上述方式各个库先打包成静态库,最后链接为动态库,最后通过strip去符号。
5、生成产物
ffmpeg-5.0.1目录新建android_out目录用于存放产物。运行脚本,生成lib和include,lib中为动态库、include中为头文件。
4、集成FFmpeg
4.1、动态库
动态库采用armeabi-v7a架构,32位/64位的CPU都可以使用,并减少包提交。
4.2、FFmpeg源文件
命令行方式需要将FFmpeg中的部分源码(ffmpeg.c、cmdutils.c)做修改,命令行参数需要传入ffmpeg.c中的main方法,需要在ffmpeg.c的print_report中计算处理时长。
4.3、jni以及native
调用FFmpeg头文件以及源码文件
4.4、cmakeList
cmake版本注意版本不要太高,3.10以上的版本在链接目标库时经常失败,我采用的3.6。
- cmake_minimum_required(VERSION 3.4.1)
- add_library(
- # Sets the name of the library.
- keffmpeg
- # Sets the library as a shared library.
- SHARED
- # Provides a relative path to your source file(s).
- src/main/cpp/ffmpeg/cmdutils.c
- src/main/cpp/ffmpeg/ffmpeg.c
- src/main/cpp/ffmpeg/ffmpeg_filter.c
- src/main/cpp/ffmpeg/ffmpeg_opt.c
- src/main/cpp/ffmpeg_cmd.c
- src/main/cpp/ffmpeg_thread.c
- src/main/cpp/ffmpeg_base.cpp)
- # 设置ffmpeg库所在路径的目录
- set(distribution_DIR ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI})
- # 添加libffmpeg.so库
- add_library(ffmpeg
- SHARED
- IMPORTED )
- # 指定libffmpeg.so库的位置
- set_target_properties(ffmpeg
- PROPERTIES IMPORTED_LOCATION
- ${distribution_DIR}/libffmpeg.so )
-
- # 添加头文件路径
- include_directories(src/main/cpp)
- include_directories(src/main/cpp/ffmpeg)
- include_directories(src/main/cpp/include)
-
- # 查找日志库
- find_library(log-lib log)
- find_library(android-lib android)
-
- # 将其他库链接到目标库keffmpeg
- target_link_libraries(keffmpeg
- ffmpeg
- ${log-lib} ${android-lib})
原文链接:基于FFmpeg的编解码器设计实现方案_libffmpeg c gnu_喜六六的博客-CSDN博客
★文末名片可以免费领取音视频开发学习资料,内容包括(FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)以及音视频学习路线图等等。
见下方!↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。