背景

业务需求中有一个需要通过 ffconcat 下载视频文件,完成后进行合并。由于视频文件PTS 都是从0开始的,不能直接下载后直接合并,使用如下命令

  1. ffmpeg -protocol_whitelist "file,http,https,rtp,udp,tcp,tls" -f concat -safe 0 -i 1.ffconcat -vcodec copy -acodec copy tmp.ts

如果是这样能将 ffmpeg 集成到 IJKPlayer 中就方便多了,以后其他的格式也可以使用命令进行转换,非常方便

首先我们要研究一下 IJKPlayer 的编译的配置。

IJKPlayer 编译方式

依赖结构

IJKPlayer 的编译配置参考了Github 上的一个编译脚本,并对其进行了改造。先编译 ffmpeg 的主要代码库 libavcodec ``libavfilter libavformat libavutil libswscale libswresample ,这些代码库的包括了协议支持,封装格式支持,解码器和编码器,支持配置都由主目录下 config 下的 module.sh决定。 IJKPlayer 通过修改 ffplay 的代码和调用 ffmpeg 的库函数(应该还有 SDL 的相关代码,先挖坑)完成播放器的主要功能。所以 ffmpeg 这一层是可以自行编译更换的。
下载后看一下目录:

  1. .
  2. ...
  3. ├── android
  4. ├── compile-android-j4a.sh
  5. ├── config
  6. ├── doc
  7. ├── extra
  8. ├── ijkmedia
  9. ├── ijkprof
  10. ├── init-android-exo.sh
  11. ├── init-android-j4a.sh
  12. ├── init-android-libsoxr.sh
  13. ├── init-android-libyuv.sh
  14. ├── init-android-openssl.sh
  15. ├── init-android-prof.sh
  16. ├── init-android-soundtouch.sh
  17. ├── init-android.sh
  18. ├── init-config.sh
  19. ├── init-ios-openssl.sh
  20. ├── init-ios.sh
  21. ├── ios
  22. ├── tools
  23. └── version.sh

关键的为 init-ios.sh 和 iOS 目录。
编译和配置的大致流程

  • 执行 ./init-ios.sh获取代码
  • 修改 config 下的module.sh配置,支持或者关闭一些解码器,编码器,库等
  • cd 到 ios目录下执行 ./compile-ffmpeg.sh all 进行代码编译
  • 完成后到 ios/build目录下可以看到编译后的结果。./compile-ffmpeg.sh实际是调用的 ios/tool/do-compile-ffmpeg.sh脚本,该脚本最后根据编译参数调用make

例如
如果想打开硬解码编译,除了配置

  1. export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-audiotoolbox"
  2. export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-videotoolbox"

还需要改变一下支持系统版本,这个修改在ios/tool/do-compile-ffmpeg.sh 中,搜索一下 FF_XCRUN_OSVERSION将系统版本改为8.0。
每次编译后 ios/tool/do-compile-ffmpeg.sh 判断对应编译目录下(如 ios/ffmpeg-arm64)下有没有 config.h文件,如果有会直接用之前编译后的文件,所以如果修改了 config/module.sh需要使用 ./compile-ffmpeg.sh clean 后重新编译。

编译增加库

除此之外还有其他库如果需要参与编译,也可以在 module.sh文件下找到如libavdevice默认不参与编译是由编译选择决定的

  1. export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-avdevice"

如果需要参与编译设置为 --enable-avdevice 配合脚本 complie-ffmpeg.shFF_LIBS 来决定编译。 修改 iOS\compile-ffmpeg.hFF_LIBS 变量增加 libavdevice 在编译后的产物里就可以找到libavdevice.a文件。

配置 ffmpeg

请勿误解这里的 ffmpeg 不是指的 ffmpeg 的库,而是 ffmpeg 的执行文件。我们经常用 ffmpeg -i xxx做一些事情。新建一个空工程,将 ios/build/的头文件和静态编译库加入工程中,将 Build SettingHeader Search Paths 添加搜索目录 $(PROJECT_DIR)/iOSffmpeg/ffmpeg/include, 这里根据你的情况具体添加。

编译 ffmpeg

在 ios/ffmpeg-arm64 目录下将 fftools添加到项目中,删除 ffprobe.cffplay.c 文件,其他多余的文件也可以删除。将 ffmpeg.c 中的 main函数修改为ffmain, 并暴露到头文件。
image.png
尝试进行编译,当缺少头文件后到原来 ios/ffmpeg-arm64对应的目录下进行查找放入相应的库中。一部分代码可以注释掉,由于不引进 libpostproc库(应该是视频后期处理的一个库,挖坑)可以先删除。注释cmdutils.c目录下 **staticvoid**print_all_libs_info(**int** flags, **int** level) 的最后一句话

  1. static void print_all_libs_info(int flags, int level)
  2. {
  3. PRINT_LIB_INFO(avutil, AVUTIL, flags, level);
  4. PRINT_LIB_INFO(avcodec, AVCODEC, flags, level);
  5. PRINT_LIB_INFO(avformat, AVFORMAT, flags, level);
  6. PRINT_LIB_INFO(avdevice, AVDEVICE, flags, level);
  7. PRINT_LIB_INFO(avfilter, AVFILTER, flags, level);
  8. PRINT_LIB_INFO(avresample, AVRESAMPLE, flags, level);
  9. PRINT_LIB_INFO(swscale, SWSCALE, flags, level);
  10. PRINT_LIB_INFO(swresample, SWRESAMPLE, flags, level);
  11. //We not need this lib
  12. // PRINT_LIB_INFO(postproc, POSTPROC, flags, level);
  13. }

注释掉引入的额外文件

  1. //#include "compat/va_copy.h"
  2. //#include "libpostproc/postprocess.h"

添加系统库 AudioToolBox.framwork VideoToolbox.framwork AVFoundation.framwork CoreMedia.framwork libbz2 libz libiconv
尝试编译应该就能通过了。

修改退出代码

ffmpeg 由于大多数是进程模式执行,所以在 main 方法下有很多退出的代码,我们需要进行修改后才能在 iOS 下运行。修改的基本思路是保留出错的日志,将退出代码改为 return + 相应的错误即可。具体的内容可以查看 demo

总结

  • 使用./init-ios.sh获取代码,修改支持的编解码库 config/module.sh
  • 修改 complie-ffmpeg.sh 库中FF_LIBS 支持增加的库
  • 如果遇到 iOS SDK 版本过低的问题修改 ios/tool/do-compile-ffmpeg.shFF_XCRUN_OSVERSION 字段
  • 增加 Xcode 的 Header Search Paths保证可以编译到代码
  • fftools 下的代码移动到库中,根据编译情况修改部分代码

最后就可以得到支持当前 IJKPlayer 的ffmpeg库。
文章参考