当前位置:   article > 正文

基于FFmpeg的编解码器设计实现方案_ffmpeg; mjpeg; 解码

ffmpeg; mjpeg; 解码

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接口

  1. static {
  2. System.loadLibrary("keffmpeg");
  3. }
  4. /**
  5. * @return 版本号
  6. */
  7. public static native String getVersion();
  8. /**
  9. * 获取媒体文件(输入/输出)信息,封装信息+视频流信息+音频流信息
  10. *
  11. * @param filePath 文件路径
  12. * @return
  13. */
  14. public static native String getFileInfo(String filePath);
  15. /**
  16. * 通过命令行调用FFmpeg中的main方法
  17. *
  18. * @param argc 参数个数
  19. * @param argv FFmpeg命令行参数
  20. */
  21. public static native void exec(int argc, String[] argv);
  22. /**
  23. * 终止执行
  24. */
  25. public static native void exit();
  26. /**
  27. * FFmpeg执行结束后的结果回调,由C代码中调用
  28. */
  29. public static void onResult(int ret) {
  30. LogUtil.d(TAG, "onResult ret:" + ret);
  31. CmdExecutor.getInstance().getExecListener().onResult(ret);
  32. }
  33. /**
  34. * FFmpeg执行进度回调,由C代码调用
  35. */
  36. public static void onProgress(float progress) {
  37. LogUtil.d(TAG, "onProcess:" + progress);
  38. CmdExecutor.getInstance().getExecListener().onProgress(progress);
  39. }

2.2、回调接口

FFmpeg的执行进度、执行结果需要告知调用方,通过Java中的反射机制获取jclass中的回调方法,JNIEnv调用Java中的回调方法并将C程序的执行结果传递给Java层。

  1. /**
  2. * 回调执行Java方法
  3. * 参看 Jni反射+Java反射
  4. */
  5. void callJavaMethod(JNIEnv *env, jclass clazz, int ret) {
  6. if (clazz == NULL) {
  7. LOGE("---------------clazz isNULL---------------");
  8. return;
  9. }
  10. jmethodID methodID = (*env)->GetStaticMethodID(env, clazz, "onResult", "(I)V");
  11. if (methodID == NULL) {
  12. LOGE("---------------methodID isNULL---------------");
  13. return;
  14. }
  15. //调用该java方法
  16. (*env)->CallStaticVoidMethod(env, clazz, methodID, ret);
  17. }
  18. void callJavaMethodProgress(JNIEnv *env, jclass clazz, float ret) {
  19. if (clazz == NULL) {
  20. LOGE("---------------clazz isNULL---------------");
  21. return;
  22. }
  23. jmethodID methodID = (*env)->GetStaticMethodID(env, clazz, "onProgress", "(F)V");
  24. if (methodID == NULL) {
  25. LOGE("---------------methodID isNULL---------------");
  26. return;
  27. }
  28. //调用该java方法
  29. (*env)->CallStaticVoidMethod(env, clazz, methodID, ret);
  30. }
  31. /**
  32. * 执行进度回调
  33. *
  34. * @param progress 执行进度,单位:秒
  35. */
  36. void onProgress(float progress);
  37. /**
  38. * 命令执行结束回调
  39. *
  40. * @param code 执行结果,code==0执行完成,!=0命令执行过程中出错
  41. */
  42. void onResult(int code);

2.3、调用接口

注册监听器、反注册监听器、执行命令、终止执行

  1. public void registerListener(OnCmdExecListener onCmdExecListener) {
  2. if (onCmdExecListener == null) return;
  3. if (!mOnCmdExecListenerList.contains(onCmdExecListener)) {
  4. mOnCmdExecListenerList.add(onCmdExecListener);
  5. }
  6. }
  7. public void unRegisterListener(OnCmdExecListener onCmdExecListener) {
  8. if (onCmdExecListener == null) return;
  9. if (mOnCmdExecListenerList.contains(onCmdExecListener)) {
  10. mOnCmdExecListenerList.remove(onCmdExecListener);
  11. }
  12. }
  13. public void exec(String[] cmdArray) {
  14. if (cmdArray == null || cmdArray.length == 0) return;
  15. CodecNative.exec(cmdArray.length, cmdArray);
  16. }
  17. public void exit() {
  18. CodecNative.exit();
  19. }
  20. public OnCmdExecListener getExecListener() {
  21. return mOnCmdExecListener;
  22. }

3、编译FFmpeg

1、FFmpeg官网下载源码

Download FFmpeg ,本方案下载的5.0.1 "Lorentz"版本,包含如下库:

  1. libavutil 57. 17.100
  2. libavcodec 59. 18.100
  3. libavformat 59. 16.100
  4. libavdevice 59. 4.100
  5. libavfilter 8. 24.100
  6. libswscale 6. 4.100
  7. libswresample 4. 3.100
  8. libpostproc 56. 3.100

2、配置ndk环境变量

ndk下载地址:NDK 下载 | Android NDK | Android Developers ,本方案使用android-ndk-r17c。

vim编辑bash_profile配置ndk环境变量,vim ~/.bash_profile,如下:

  1. export NDKROOT=/Users/xiaobeizhang/Desktop/ke/ndk/android-ndk-r17c
  2. export PATH=${PATH}:${NDKROOT}

source ~/.bash_profile保存

将ndk目录下的所有文件赋予最高权限,chmod -R 777 ./

3、编辑FFmpeg中的configure文件

由于默认configure脚本编译出来的动态库版本号在文件名后缀.so之后,如:libavcodec.so.7所以需要对configure进行修改:

  1. #SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)'
  2. #LIB_INSTALL_EXTRA_CMD='$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"'
  3. #SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'
  4. #SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR) $(SLIBNAME)'
  5. SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)$(LIBMAJOR)$(SLIBSUF)'
  6. LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'
  7. SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'
  8. SLIB_INSTALL_LINKS='$(SLIBNAME)'

将默认编译器由clang改为gcc

  1. set_default target_os
  2. if test "$target_os" = android; then
  3. cc_default="clang"
  4. fi

4、编辑编译脚本

ffmpeg-5.0.1目录下创建编译动态库的脚本,touch build_android.sh

  1. # 清空上次的编译
  2. make clean
  3. ADDI_CFLAGS="-marm"
  4. PLATFORM=arm-linux-androideabi
  5. # NDK路径
  6. NDK=/Users/xiaobeizhang/Desktop/ke/ndk/android-ndk-r17c
  7. # 设置android平台编译器的版本 这里采用Android 4.4
  8. SYSROOT=$NDK/platforms/android-19/arch-arm/
  9. ISYSROOT=$NDK/sysroot
  10. ASM=$ISYSROOT/usr/include/$PLATFORM
  11. #编译使用的工具链
  12. TOOLCHAIN=$NDK/toolchains/$PLATFORM-4.9/prebuilt/darwin-x86_64
  13. # 编译产物输出路径
  14. OUTPUT=/Users/xiaobeizhang/Desktop/ke/ffmpeg/ffmpeg-5.0.1/android_out
  15. function build
  16. {
  17. ./configure \
  18. --prefix=$OUTPUT \
  19. --enable-shared \
  20. --disable-static \
  21. --disable-doc \
  22. --disable-ffmpeg \
  23. --disable-ffplay \
  24. --disable-ffprobe \
  25. --disable-avdevice \
  26. --disable-doc \
  27. --disable-symver \
  28. --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
  29. --target-os=android \
  30. --arch=armeabi-v7a \
  31. --enable-cross-compile \
  32. --sysroot=$SYSROOT \
  33. --extra-cflags="-I$ASM -isysroot $ISYSROOT -Os -fpic -marm" \
  34. --extra-ldflags="-marm" \
  35. $ADDITIONAL_CONFIGURE_FLAG
  36. make clean
  37. #启动4个线程同时编译
  38. make -j4
  39. #生成的二进制文件安装到configure设置的prefix路径
  40. make install
  41. }
  42. build

添加可执行权限,chmod a+x build_android.sh。

上述方式各个库会打包成6个动态库,不方便调用,可采用如下脚本打包成一个动态库:

  1. # 清空上次的编译
  2. make clean
  3. ADDI_CFLAGS="-marm"
  4. PLATFORM=arm-linux-androideabi
  5. # NDK路径
  6. NDK=/Users/xiaobeizhang/Desktop/ke/ndk/android-ndk-r17c
  7. # 设置android平台编译器的版本 这里采用Android 4.4
  8. SYSROOT=$NDK/platforms/android-17/arch-arm/
  9. ISYSROOT=$NDK/sysroot
  10. ASM=$ISYSROOT/usr/include/$PLATFORM
  11. #编译使用的工具链
  12. TOOLCHAIN=$NDK/toolchains/$PLATFORM-4.9/prebuilt/darwin-x86_64
  13. # 编译产物输出路径
  14. OUTPUT=/Users/xiaobeizhang/Desktop/ke/ffmpeg/ffmpeg-5.0.1/android_out_single
  15. function build
  16. {
  17. ./configure \
  18. --target-os=android \
  19. --prefix=$OUTPUT \
  20. --enable-cross-compile \
  21. --enable-static \
  22. --disable-shared \
  23. --disable-doc \
  24. --disable-ffmpeg \
  25. --disable-ffplay \
  26. --disable-ffprobe \
  27. --disable-avdevice \
  28. --disable-doc \
  29. --disable-symver \
  30. --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
  31. --arch=arm \
  32. --sysroot=$SYSROOT \
  33. --extra-cflags="-I$ASM -isysroot $ISYSROOT -D__ANDROID_API__=17 -U_FILE_OFFSET_BITS -Os -fPIC -DANDROID -Wno-deprecated -mfloat-abi=softfp -marm" \
  34. --extra-ldflags="$ADDI_LDFLAGS" \
  35. $ADDITIONAL_CONFIGURE_FLAG
  36. make clean
  37. #启动4个线程同时编译
  38. make -j4
  39. #生成的二进制文件安装到configure设置的prefix路径
  40. make install
  41. # 打包
  42. $TOOLCHAIN/bin/arm-linux-androideabi-ld \
  43. -rpath-link=$SYSROOT/usr/lib \
  44. -L$SYSROOT/usr/lib \
  45. -L$PREFIX/lib \
  46. -soname libffmpeg.so -shared -nostdlib -Bsymbolic --whole-archive --no-undefined -o \
  47. $OUTPUT/libffmpeg.so \
  48. libavcodec/libavcodec.a \
  49. libavfilter/libavfilter.a \
  50. libavformat/libavformat.a \
  51. libavutil/libavutil.a \
  52. libswresample/libswresample.a \
  53. libswscale/libswscale.a \
  54. -lc -lm -lz -ldl -llog --dynamic-linker=/system/bin/linker \
  55. $TOOLCHAIN/lib/gcc/arm-linux-androideabi/4.9.x/libgcc.a
  56. # strip 精简文件
  57. $TOOLCHAIN/bin/arm-linux-androideabi-strip $OUTPUT/libffmpeg.so
  58. }
  59. 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。

  1. cmake_minimum_required(VERSION 3.4.1)
  2. add_library(
  3. # Sets the name of the library.
  4. keffmpeg
  5. # Sets the library as a shared library.
  6. SHARED
  7. # Provides a relative path to your source file(s).
  8. src/main/cpp/ffmpeg/cmdutils.c
  9. src/main/cpp/ffmpeg/ffmpeg.c
  10. src/main/cpp/ffmpeg/ffmpeg_filter.c
  11. src/main/cpp/ffmpeg/ffmpeg_opt.c
  12. src/main/cpp/ffmpeg_cmd.c
  13. src/main/cpp/ffmpeg_thread.c
  14. src/main/cpp/ffmpeg_base.cpp)
  15. # 设置ffmpeg库所在路径的目录
  16. set(distribution_DIR ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI})
  17. # 添加libffmpeg.so库
  18. add_library(ffmpeg
  19. SHARED
  20. IMPORTED )
  21. # 指定libffmpeg.so库的位置
  22. set_target_properties(ffmpeg
  23. PROPERTIES IMPORTED_LOCATION
  24. ${distribution_DIR}/libffmpeg.so )
  25. # 添加头文件路径
  26. include_directories(src/main/cpp)
  27. include_directories(src/main/cpp/ffmpeg)
  28. include_directories(src/main/cpp/include)
  29. # 查找日志库
  30. find_library(log-lib log)
  31. find_library(android-lib android)
  32. # 将其他库链接到目标库keffmpeg
  33. target_link_libraries(keffmpeg
  34. ffmpeg
  35. ${log-lib} ${android-lib})

原文链接:基于FFmpeg的编解码器设计实现方案_libffmpeg c gnu_喜六六的博客-CSDN博客

★文末名片可以免费领取音视频开发学习资料,内容包括(FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)以及音视频学习路线图等等。

见下方!↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/446364
推荐阅读
相关标签
  

闽ICP备14008679号