多个FFmpeg静态库合并成一个动态库的3个方法

需求场景:

  • 移动端上,一个动态库比多个动态库好管理
  • FFmpeg编译输出多个静态库或多个动态库
  • FFmpeg自身是LGPL协议,应用静态链接FFmpeg有copyright/copyleft问题,应用代码需要开源,动态链接只需要开源FFmpeg动态库

问题归结为,怎么把FFmpeg输出的多个静态库合并成一个动态库。

方法1:修改FFmpeg编译构建,让FFmpeg直接编译出一个动态库

存在问题:自己维护个修改的FFmpeg版本。

除非能让社区接受patch(99.999%不接受),支持这个功能,不推荐这个方法。

能在FFmpeg之外简单地解决的问题不建议hack FFmpeg。

方法2:whole-archieve方式链接

关于whole-archieve的说明,见gcc ld手册(虽然Android toolchain现在用lld做linker,与gcc ld兼容)

  • --whole-archiveFor each archive mentioned on the command line after the --whole-archive option, include every object file in the archive in the link, rather than searching the archive for the required object files. This is normally used to turn an archive file into a shared library, forcing every object to be included in the resulting shared library. This option may be used more than once.

这里说明一下:

  • 当我们实现一个动态库或一个可执行程序,链接一个静态库时,默认只会把引用到的符号从静态库链接进来,没引用到的符号不链接进来
  • 所以,虽然静态库可能非常大,链接生成的动态库可能很小
    • 一方面是只链接了需要的符号
    • 一方面是可以裁剪掉静态库里包含的调试信息

whole-archieve与之相反,不管需不需要,全部链接进来。

我们可以通过调用toolchain,加上whole-archieve配置,手动合并FFmpeg多个静态库,输出一个动态库。也可以通过一个简单的CMake工程来实现,例如:


cmake_minimum_required(VERSION 3.16)
project(ffmpeg LANGUAGES C CXX)

add_library(ffmpeg SHARED
  dummy.cpp)

target_link_libraries(ffmpeg
  -Wl,--whole-archive
  ${CMAKE_CURRENT_SOURCE_DIR}/libavfilter/libavfilter.a
  ${CMAKE_CURRENT_SOURCE_DIR}/libswresample/libswresample.a
  ${CMAKE_CURRENT_SOURCE_DIR}/libavdevice/libavdevice.a
  ${CMAKE_CURRENT_SOURCE_DIR}/libavcodec/libavcodec.a
  ${CMAKE_CURRENT_SOURCE_DIR}/libpostproc/libpostproc.a
  ${CMAKE_CURRENT_SOURCE_DIR}/libavformat/libavformat.a
  ${CMAKE_CURRENT_SOURCE_DIR}/libswscale/libswscale.a
  ${CMAKE_CURRENT_SOURCE_DIR}/libavutil/libavutil.a
  -Wl,--no-whole-archive
  m
  atomic
  android
  z)

执行编译链接:

cmake -B build -DANDROID_ABI=armeabi-v7a  --toolchain ~/Library/Android/sdk/ndk/r25/build/cmake/android.toolchain.cmake
cmake --build build/

方法3:精细调校的方法

方法2的缺点是:

  1. 不管用不用的API,一股脑都打包进去,包体比较大
  2. 少数情况会出现编译错误,两个静态库里有重复的object,加上whole-archieve报符号重复定义

如果想达到用哪个API才链接哪个API的效果,可以用方法3:

1. 收集所用到的FFmpeg API

  1. 可以批量处理自己的源码,字符串匹配过滤出FFmpeg API(FFmpeg API有固定的几个前缀)
  2. 也可以从自己的项目编译输出的动态库里,查看函数符号过滤出FFmpeg API

2. 把所用到的API加到一个dummy.c里,举个例子


#include "libavformat/avformat.h"
#include "libswscale/swscale.h"

const void *ffmpeg_symbol_list[] = {
    avformat_free_context,
    avformat_close_input,
    avformat_find_stream_info,
    avformat_open_input,

    sws_alloc_context,
};

3. 生成动态库


cmake_minimum_required(VERSION 3.16)
project(ffmpeg LANGUAGES C CXX)

add_library(ffmpeg SHARED
  dummy.c)

target_include_directories(ffmpeg PRIVATE
  ${CMAKE_CURRENT_SOURCE_DIR}
  src)

target_link_libraries(ffmpeg
  ${CMAKE_CURRENT_SOURCE_DIR}/libavfilter/libavfilter.a
  ${CMAKE_CURRENT_SOURCE_DIR}/libswresample/libswresample.a
  ${CMAKE_CURRENT_SOURCE_DIR}/libavdevice/libavdevice.a
  ${CMAKE_CURRENT_SOURCE_DIR}/libavcodec/libavcodec.a
  ${CMAKE_CURRENT_SOURCE_DIR}/libpostproc/libpostproc.a
  ${CMAKE_CURRENT_SOURCE_DIR}/libavformat/libavformat.a
  ${CMAKE_CURRENT_SOURCE_DIR}/libswscale/libswscale.a
  ${CMAKE_CURRENT_SOURCE_DIR}/libavutil/libavutil.a
  m
  atomic
  android
  z)

这样生成的动态库是ffmpeg_symbol_list中所列出的符号加上它们引用到的符号,达到了精细调校的目标

但是,你大概率会发现动态库节省的比较有限。因为只要引入一个avformat_open_input(),会引入几百个封装格式、传输协议、编解码器……怎么做功能裁剪,且待下回分解。

作者:quink
来源:Fun With FFmpeg
原文:https://mp.weixin.qq.com/s/Bz_yEiBN7IxDYlZwDKxW2Q

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。

(0)

相关推荐

发表回复

登录后才能评论