创建静态包含 ffmpeg 的共享库

Creating a shared library that statically includes ffmpeg

我很难尝试创建一个将 ffmpeg 库 "baked in" 作为静态库的共享库。

考虑以下目录模式:

include/
  my own .h files
  ext/
    ffmpeg .h files
lib/
  libav*.a archive files (softlinks to the actual .a files)
  libValkka.so (my shared library)
test/
  mytest.cpp
bin/
  (binaries appear here)

我已经走了很长一段路(参见 )并且库编译正常:(为简洁起见省略了 [STUFF])

/usr/bin/c++ -fPIC -std=c++14 -pthread -Iinclude/ext -I/usr/include/libdrm -g -shared -Wl,-soname,libValkka.so -o lib/libValkka.so CMakeFiles/Valkka.dir/src/avthread.cpp.o CMakeFiles/Valkka.dir/src/opengl.cpp.o CMakeFiles/Valkka.dir/src/openglthread.cpp.o [STUFF] CMakeFiles/Valkka.dir/src/filters.cpp.o -lX11 -lGLEW -lGLU -lGL -Wl,--allow-multiple-definition -Wl,-Bsymbolic -Wl,--whole-archive -Wreorder lib/libavdevice.a lib/libavfilter.a lib/libavformat.a lib/libavcodec.a lib/libavutil.a lib/libswscale.a lib/libswresample.a -Wl,--no-whole-archive

但是,在创建可执行文件时 - 他们的源代码不使用任何 ffmpeg api(只是我自己的 api) - 使用:

c++ -std=c++14 -pthread -Iinclude -Iinclude/ext -Llib test/mytest.cpp -lValkka -g -o bin/mytest

我收到一堆关于缺少 ffmpeg 依赖项的错误。不是所有东西都不见了,只是一些奇怪的东西:

lib/libValkka.so: undefined reference to `pa_stream_get_index'
lib/libValkka.so: undefined reference to `deflateInit_'
lib/libValkka.so: undefined reference to `pa_stream_get_state'
lib/libValkka.so: undefined reference to `lzma_stream_decoder'
lib/libValkka.so: undefined reference to `BZ2_bzDecompress'
lib/libValkka.so: undefined reference to `vaInitialize'
lib/libValkka.so: undefined reference to `pa_stream_unref'
lib/libValkka.so: undefined reference to `deflateInit2_'
lib/libValkka.so: undefined reference to `snd_pcm_close'
...
lib/libValkka.so: undefined reference to `vaGetDisplayDRM'
lib/libValkka.so: undefined reference to `vaMaxNumEntrypoints'
lib/libValkka.so: undefined reference to `uncompress'
lib/libValkka.so: undefined reference to `pa_stream_drop'
lib/libValkka.so: undefined reference to `pa_context_connect'
lib/libValkka.so: undefined reference to `FT_Get_Kerning'
lib/libValkka.so: undefined reference to `ass_free_track'
lib/libValkka.so: undefined reference to `pa_operation_unref'
lib/libValkka.so: undefined reference to `FT_Stroker_Done'
lib/libValkka.so: undefined reference to `vaTerminate'
lib/libValkka.so: undefined reference to `ass_new_track'
lib/libValkka.so: undefined reference to `jack_client_close'
...
lib/libValkka.so: undefined reference to `xcb_xfixes_query_version'
lib/libValkka.so: undefined reference to `xcb_shape_rectangles'
lib/libValkka.so: undefined reference to `pa_mainloop_free'
lib/libValkka.so: undefined reference to `snd_device_name_hint'
lib/libValkka.so: undefined reference to `vaCreateImage'
lib/libValkka.so: undefined reference to `vaBeginPicture'
lib/libValkka.so: undefined reference to `DtsSetColorSpace'
lib/libValkka.so: undefined reference to `vaDestroyConfig'
lib/libValkka.so: undefined reference to `pa_stream_writable_size'
lib/libValkka.so: undefined reference to `snd_pcm_hw_params_get_buffer_size_max'
lib/libValkka.so: undefined reference to `ass_read_file'

这真令人沮丧,尤其是当我看到共享库中包含这些名称时...!

nm lib/libValkka.so | grep "vaBeginPicture"

给予

U vaBeginPicture

等我认为这可能是关于存档 .a 文件的依赖顺序的问题,并且还尝试了:

..... -Wl,--allow-multiple-definition -Wl,-Bsymbolic -Wl,--start-group -Wl,--whole-archive -Wreorder lib/libavdevice.a lib/libavfilter.a lib/libavformat.a lib/libavcodec.a lib/libavutil.a lib/libswscale.a lib/libswresample.a -Wl,--no-whole-archive -Wl,--end-group

但问题依旧。

我已经成功创建了一个共享库,它 "bake in"那些.a档案,即动态依赖于ffmpeg库,没有这样的问题.

我很困惑..我是不是误解了一些基本的东西,忘记了一些烦人的链接选项,或者两者都有?感谢帮助!

告诉链接器在创建可执行文件时忽略未解析的符号就可以了:

c++ -std=c++14 -pthread -Iinclude -Iinclude/ext -Llib test/mytest.cpp -lValkka -g -o bin/mytest -Wl,--unresolved-symbols=ignore-all

生成的可执行文件也运行正常。

但是..使用这样的链接器标志让我感到不快。也许有更好的选择?为什么一开始就找不到这些符号?

编辑

根据 Andrey 的建议,我借助 ffmpeg 的配置脚本从 ffmpeg 中删除了所有外部库。这是一个有点尴尬的过程,所以我创建了一个很好的 python 脚本来自动完成它。这可能有点矫枉过正,但就是这样:

#!/usr/bin/python3
"""
* Creates script "run_configure.bash" that launches ffmpeg's "configure" script with correct parameters (enabling/disabling stuff)
* Run in the same directory where you have ffmpeg's configure script
"""
import subprocess
import os
import re

def features(switch, adstring="", remove=[]):
  p=subprocess.Popen(["./configure",switch],stdout=subprocess.PIPE)
  st=p.stdout.read()
  fst=""
  for s in st.split():
    ss=s.decode("utf-8")
    ok=True
    for rem in remove:
      if (ss.find(rem)!=-1):
        ok=False
    if ok: fst+=adstring+ss+" "
  return fst


def disable_external():  
  p=subprocess.Popen(["./configure","-h"],stdout=subprocess.PIPE)
  st=p.stdout.read().decode("utf-8")

  # find some text tags from the configure output:
  # i1=st.find("External library support:")
  i1=st.find("themselves, not all their features will necessarily be usable by FFmpeg.")
  i2=st.find("Toolchain options:")
  st=st[i1:i2]
  """ # debugging ..
  print(st)
  stop
  """
  p=re.compile('--(enable|disable)-(\S*)')
  switches=[]
  for sw in p.findall(st):
    if (sw[1] not in switches):
      # print(sw[1]) # debugging
      switches.append(sw[1])
  fst=""
  for sw in switches:
    fst+="--disable-"+sw+" "
  return fst

st ="./configure "
st+="--disable-everything --disable-doc --disable-gpl --disable-pthreads --enable-static --enable-shared "
st+= disable_external()
st+= features("--list-decoders",adstring="--enable-decoder=", remove=["vdpau","crystalhd","zlib"])
st+= features("--list-muxers",  adstring="--enable-muxer=")
st+= features("--list-demuxers",adstring="--enable-demuxer=")
st+= features("--list-parsers", adstring="--enable-parser=")

f=open("run_configure.bash","w")
f.write("#!/bin/bash\n")
f.write(st+"\n")
f.close()
os.system("chmod a+x run_configure.bash")
print("\nNext run ./run_configure.bash\n")

"""
For cleaning up .a and .so files, use
find -name *.a -exec ls {} \;
find -name *.so* -exec ls {} \;
"""

希望有人觉得它有用。 运行 与 python3.

您需要 link 您的共享库与 ffmpeg 所需的第三个 party/system 库:libbz2、libva、libxcb、libass、freetype2 等。实际列表应该在 ffmpeg 中的某处 distribution/build 工件(automake 的 .pc 文件)

忽略全部不是一个好主意;您的应用程序可能 运行 OK,但这些未解决的项目仍然存在;一旦碰到其中任何一个,它就会崩溃。我的猜测是它们中的大多数永远不会被击中,因为它们是用于您可能甚至不会使用的 libavdevice 的,但这仍然是一个坏主意。另外,检查你是否真的需要那个 libavdevice 库——如果你去掉那个库,你可能 trim 需要的库列表很多。