frankfan的胡思乱想

学海无涯,回头是岸

Linux系统使用Qt Creator集成FFMpeg

Linux系统使用Qt Creator集成FFMpeg

FFMpeg的源码编译

这类知名且功能强大的项目通常在编译时就提供功能丰富的「配置」选项,可以通过这些配置从而编译出功能各异的库,因为这类项目通常复杂且庞大,提供的功能丰富多样,很多时候我们可能只需要其提供的一部分功能即可,这样我们就可以通过配置编译选项来达成目的。

FFmpeg采用make工具进行编译,先配置好makefile文件,再使用make工具即可,但复杂项目手写makefile通常是很痛苦的,好在这类知名开源项目都提供一个「配置」工具,通过这个配置脚本生成makefile文件,我们只需要指定配置工具提供的预备选项,就可以运行配置脚本生成makefile文件了(配置脚本名字通常叫configconfigure

接下来开始我们的操作。

  • ./configure --enable-shared --prefix=/usr/local/ffmpeg

    指定生成动态库和指定编译产物的输出位置

    其余配置选择默认

    期间不出意外,则会生成新的makefile文件,若出意外则更大的可能是这个错误:

    yasm/nasm not found or too old. Use --disable-yasm for a crippled build.

    这说明本地没有yasm这个工具或者工具太老了,这个工具是用来解析汇编指令的(FFmpeg编译中采用了汇编指令),我们只需要安装这个工具即可

    sudo apt-get install yasm

  • make

    开始进行编译,长时间等待编译结果即可

  • make install

    将编译产物拷贝到我们之前指定的位置上,方便使用

到此,从FFmpeg源码到动态库的过程完成。

FFmpeg动态库集成进我们的项目中(项目环境:linux/Qt creator)

  • 导入项目头文件

    extern "C"{
        #include <libavcodec/avcodec.h>
    }
    
    

    Qt项目是C++语言,而FFmpegC语言开发,C++中存在函数重载,为了避免命令倾轧而导致的链接时C++二进制产物与C二进制产物的ABI不兼容,因此在C++源码中需要通过exten "C"的形式指明该处C++源码使用C风格的编译,不要进行命名倾轧。

  • qMake编译配置文件中指明ffmpeg的头文件所在,这样进行编译时让编译器能找到头文件的正确位置进行引用

    INCLUDEPATH += /usr/local/ffmpeg/include
    

    通过追加(+=)的方式指明ffmpeg的头文件所在位置

  • qMake编译配置文件中指明ffmpeg相关需要链接的动态库,这样才能在编译期将动态库的相关符号写入可执行产物的动态符号表中

    LIBS += -L /usr/local/ffmpeg/lib -lavcodec -lavdevice -lavfilter -lavformat -lavutil -lswresample -lswscale
    

    这里我们指明了需要进行链接的动态库「名称」,这里,我们需要说明下linux系统下关于静态库与动态库的命名问题,这个比较重要。

    linux系统中,静态库与动态库的命名有一套规范,只有生成方与使用方都遵循这套规范,才能使编译链接工作正确执行。

    我们有一个项目, 叫supertool,准备将其分别编译为静态库和动态库,那么最终产物的名字为:

    • 静态库:libsupertool.a

      不管我们项目的真正名字叫什么(X),最终生成的静态库名字规范为libX.a

    • 动态库:libsupertool.so

      不管我们项目的真正名字叫什么(X),最终生成的动态库名字规范为libX.so

      而动态库在linux中使用非常广泛,更新迭代不断,修复bug或者新增功能都需要版本迭代,因此动态库的名字中通常包含其版本号,libsupertool.so.1.2.3,如果动态库名字中要带有版本号,那么这就是「带有版本号的命名规范」,但是,实际中总有些幺蛾子并不遵循这个规范,而是变成了libsupertool.1.2.3.so ,情况似乎混乱起来,如果这样的话那么动态库的使用方就非常头痛了,因为当动态库作者方更新了新版本的动态库后使用方必须重新编译,将新动态库的信息写入可执行文件中,这对于bug修复频繁的动态库来说无疑是噩梦。不过好在,我们上文所说的命名规范依旧是有效的,使用方只承认libX.alibX.so这两种命名,那怎么解决带版本号的动态库名称问题呢?答案是:「符号链接

      就像windows系统中常见的「快捷方式」,桌面上的文件并不一定要有真实内容,它可能只是一个指向别处的快捷文件而已,别处文件的名字与快捷方式无关,两者是独立的,这就意味着,快捷方式的名字可以不变,而别处文件的名字可以随意变动,只要保持连接关系即可。

      linux中我们使用ln命令创建符号链接

      ln -s /usr/local/tool/libsupertool.so /usr/lib/libsupertool.so.2.3.1

      这样,我们就在 /usr/local/tool/目录下创建了一个符号链接文件libsupertool.so,这个链接指向了真实的文件/usr/lib/libsupertool.so.2.3.1,这样我们就可以无视真实的动态库名字是什么,我们只管创建一个符合命名规范的文件即可。

      至此,动态库作者方的命名规范已经讲解完毕,那么,使用方又改如何做呢?

      这取决使用方使用什么编译系统,常见的编译工具有

      • make
      • cmake
      • qmake

      等等,make工具使用的配置文件为makefile,cmake工具使用的配置文件为CMakeLists.txt,qmake使用的配置文件为theProjectName.pro

      makefile写法为:

      LIBVAR   = -lsupertool #编译时需要链接动态库libsupertool.so           
      LIBPATH  = -L ./dynamiclib/libs #指明动态库位置
      

      CMakeLists.txt写法为:

      #指明动态库libsupertool.so的头文件位置
      include_directories(/usr/local/supertool/include)
      #指明动态库libsupertool.so的库文件位置
      link_directories(/usr/local/supertool/lib)
      
      #生成theProjectOut可执行文件
      add_executable(theProjectOut xx.c xxx.c)
      
      #指明需要与编译产物链接到一起的动态库名字
      target_link_libraries(theProjectOut supertool)
      

      theProjectName.pro的写法为:

      #指明动态库libsupertool.so的头文件位置
      INCLUDEPATH += /usr/local/supertool/include
      #指明动态库的文件路径和需要链接的文件名
      LIBS += -L /usr/local/supertool/lib -lsupertool
      

      对比以上较为流行的编译工具,其中CMakeLists.txt是直接指定动态库的本名,而其他两种都是使用lX的命名方式(其中l表示link的意思)

到目前为止,我们的项目应该可以正常的编译链接成功了,但是,却不一定能够运行成功!

动态库有2种使用方式:

1、编译时链接动态库信息到可执行文件

2、运行时使用dlopen函数动态加载

对于方式1,在程序启动时,加载器会去「默认」路径下寻找相关动态库文件,通常这个默认路径在环境变量中已经给出,而很有可能运行时加载器无法在默认路径下找到正确的库路径,这时候需要明确告诉加载器(ld)动态库文件的所处路径。

linux中,ld动态库加载器会在这个目录下/etc/ld.so.conf.d/遍历文件,尝试从这些文件内容中加载动态库(/etc/ld.so.conf.d/目录下的文件中记录的是文件路径),因此,我们可以在这个目录下创建一个文件(文件名随意),文件内容为supertool.so动态库文件的所在路径

cd /etc/ld.so.conf.d/
touch spt.conf
echo /usr/local/supertool/supertool.so >> spt.conf
sudo ldconfig

经过这样4步后,程序运行时就能够在正确的位置找到动态库supertool.so

posted on 2021-12-27 23:35  shadow_fan  阅读(881)  评论(0)    收藏  举报

导航