用Cmake编译程序时,链接到FFmpeg库

用Cmake编译程序时,链接到FFmpeg库

一、前言

可喜可贺,折腾了一晚上终于把这个勾八链接成功了,已经要吐了。看到下面控制台的输出,吾心甚慰呀😭

[100%] Linking CXX executable rknn_yolov5_demo
[100%] Built target rknn_yolov5_demo
[100%] Built target rknn_yolov5_demo
Install the project...

下面总结一下,我之前链接失败的几点原因:

  1. 忽略了系统架构之间的差别

    Cmake工具运行的环境是Ubuntu18.04操作系统,系统架构为X86_64;而我的目标环境是嵌入式操作系统,该系统安装在RKNN 1808(瑞芯微)开发板上,系统架构为aarch(ARM 64);

    这导致链接时出现了千奇百怪的错误,几度差点心态崩溃
    在这里插入图片描述

  2. 盲目拷贝文件,没考虑各种依赖问题

    上面第一点屡试不爽后,我转移了目标,让甲方开发人员在RKNN开发板上先安装了FFmpeg工具,然后我直接将板子上的共享库so文件和头文件拷贝了过来。

    继续尝试链接,依然失败,而且此次的问题比上次更多了,疯狂查阅资料发现是我只拷贝了文件却忽略了这些文件的依赖,因此惨败。

    直到第3次,我尝试自己对FFmpeg进行交叉编译【见另外一篇文章:ubuntu下交叉编译ffmpeg到目标架构为aarch架构的系统-CSDN博客】,最终才得以链接成功,不过嘛我好像跑题了嘿嘿,我是要介绍怎么链接FFmpeg而不是怎么正确地搞到FFmpeg相关东西。

二、包含头文件和链接共享库

  1. 找到共享库的路径和头文件所在路径(小声说:其实如果你是自己编译的ffmpeg,你应该知道在哪)

     find / -name "libav*
    

    运行命令后,操作系统会在整个文件系统中查找这个东西,然后返回位置,比如我的返回:

    /home/fy/LIBS/ffmpeg5.0.1_linux_arm64/lib/libavfilter.so
    /home/fy/LIBS/ffmpeg5.0.1_linux_arm64/lib/libavcodec.so
    /home/fy/LIBS/ffmpeg5.0.1_linux_arm64/lib/libavdevice.so
    /home/fy/LIBS/ffmpeg5.0.1_linux_arm64/lib/libavutil.so
    /home/fy/LIBS/ffmpeg5.0.1_linux_arm64/lib/libavformat.so
    

    注意,返回的可能有多个不同路径的结果,此时你就需要甄别,哪些是当前系统的,哪些是用在目标系统的

    然后,就可以打开【/home/fy/LIBS/ffmpeg5.0.1_linux_arm64】这个文件夹,头文件和共享库都在这个文件夹的子文件夹下,瞅瞅我的:

    (base) root@110kmg49ac7fk-0:/home/fy/LIBS/ffmpeg5.0.1_linux_arm64# ls
    bin  include  lib  share
    

    上面列出了4个文件夹,其中【include】是头文件的目录,【lib】是共享库文件的目录

  2. 包含头文件

    打开你用于编译C++程序的CMakeLists文件,在任何位置,当然,一般写在中下部,写下面的语句,设置头文件的目录:

    set(FFMPEG_INCLUDE_DIRS /home/fy/LIBS/ffmpeg5.0.1_linux_arm64/include
    )
    include_directories(${FFMPEG_INCLUDE_DIRS})
    
    • 【set】该关键字在Cmake工具里用于设置一个变量
    • 【FFMPEG_INCLUDE_DIRS】该字符串为变量的名字
    • 【/home/fy/LIBS/ffmpeg5.0.1_linux_arm64/include】该串为头文件的目录,现在这个是我的,你应该修改为你的
    • 【include_directories】该关键字用于包含一个目录
    • 【${FFMPEG_INCLUDE_DIRS}】这个表示我们的头文件目录了,里面的变量介绍过了,【${}】用来引用一个变量

    因为【include_directories】的参数就是一个路径,而我们的变量【FFMPEG_INCLUDE_DIRS】就是给我们的路径起了个别名而已,因此上面的语句还可以简化为

    include_directories(/home/fy/LIBS/ffmpeg5.0.1_linux_arm64/include)
    
  3. 链接共享库

    在上面包含头文件的语句下面可以接着写下面的设置变量语句

    set(FFMPEG_LIBRARIES /home/fy/LIBS/ffmpeg5.0.1_linux_arm64/lib/libavcodec.so/home/fy/LIBS/ffmpeg5.0.1_linux_arm64/lib/libavformat.so/home/fy/LIBS/ffmpeg5.0.1_linux_arm64/lib/libavutil.so/home/fy/LIBS/ffmpeg5.0.1_linux_arm64/lib/libswscale.so
    )
    

    该语句使得【FFMPEG_LIBRARIES】能代表参数中的四个共享库路径

    然后,最终写一条链接语句

    target_link_libraries(rknn_yolov5_demo stdc++fs ${FFMPEG_LIBRARIES} ${RKNN_API_LIB} dl)
    
    • 【target_link_libraries】关键字,顾名思义,就是设置目标链接库

    • 【rknn_yolov5_demo】这是链接的目标,表示这些库给谁用,在这里它是一个一组文件的别名,下面是我的定义,由于设置了链接目标,那么下面3个文件编译后,均可以使用链接到的库。

      add_executable(rknn_yolov5_demosrc/rga_func.csrc/postprocess.ccsrc/main.cc)
      
    • 【stdc++fs】表示C++标准文件系统库,这是我自己项目用的,所以读者不必了解,介绍它仅为了讲解的完备性

    后面是其他的要链接的库了,由于都是变量,所以用了【${}】 ,至于后面还有个【dl】,则表示链接到动态链接库,也就是共享库咯

三、实例

下面是我写的一个完整的编译C++程序并链接到了FFmpeg库的CMakeLists代码,我写了详细的注释,如果还是有疑惑可以在评论区讨论。

这只是一个测试程序实际情况可恨复杂得多

# 设置项目名称为 FFmpegTest
project(FFmpegTest)	# 设置编译器选项,这里设置了C和C++的,具体参数自行百度,为了美观此处不赘述
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -g -s -O3")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -Wall -g -s -O3")# 指定 FFmpeg 的头文件目录
include_directories(/home/fy/LIBS/ffmpeg5.0.1_linux_arm64/include)# 指定 FFmpeg 的动态链接库路径变量 FFMPEG_LIBRARIES,有2个是so.60是因为我的目标系统需要
set(FFMPEG_LIBRARIES /home/fy/LIBS/ffmpeg5.0.1_linux_arm64/lib/libavcodec.so.60/home/fy/LIBS/ffmpeg5.0.1_linux_arm64/lib/libavformat.so.60/home/fy/LIBS/ffmpeg5.0.1_linux_arm64/lib/libavutil.so/home/fy/LIBS/ffmpeg5.0.1_linux_arm64/lib/libswscale.so
)# 添加源文件
add_executable(SourceFiletest.cpp)# 链接FFmpeg库,链接到的库给SourceFile这个可执行文件(由test.cpp编译得到)用
target_link_libraries(SourceFile ${FFMPEG_LIBRARIES} dl)
  • 创建一个目录用来存放构建的东西,并进入目录

    (base) root@110kmg49ac7fk-0:/17106/Pengcaiping/FFmpeg# mkdir build
    (base) root@110kmg49ac7fk-0:/17106/Pengcaiping/FFmpeg# cd build/
    
  • 开始生成后面两个参数是指定交叉编译器的路径;第二段代码是控制台的返回信息

    (base) root@110kmg49ac7fk-0:/17106/Pengcaiping/FFmpeg/build# 
    cmake ..     -DCMAKE_BUILD_TYPE=Debug     -DCMAKE_C_COMPILER=/17106/Pengcaiping/gcc-linaro-6.4.1-2017.08-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc     -DCMAKE_CXX_COMPILER=/17106/Pengcaiping/gcc-linaro-6.4.1-2017.08-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-g++
    
    # ······此处省略约2000字母的输出,下面的才是核心
    -- Configuring done
    -- Generating done
    -- Build files have been written to: /17106/Pengcaiping/FFmpeg/build
    

    上面的信息表示生成这一步成功了

  • 开始编译,出现后面的输出表示编译成功

    (base) root@110kmg49ac7fk-0:/17106/Pengcaiping/FFmpeg/build# make
    
    Scanning dependencies of target SourceFile
    [ 50%] Building CXX object CMakeFiles/SourceFile.dir/test.o
    [100%] Linking CXX executable SourceFile
    [100%] Built target SourceFile
    
  • 拷贝build目录下的SourceFile可执行文件到目标系统(RKNN1808下的aarch64架构的Linux系统)

    [root@rk1808:/home/user]$ ls
    1472340076-1-192.mp4  SourceFile  ffmpeg  rknn  testlib
    

    从以上 输出证明我拷贝过去了

  • 拷贝库文件过去,放到testlib目录下

    [root@rk1808:/home/user/testlib]$ ls
    libavcodec.so.60  libavformat.so.60  libavutil.so.58  libswscale.so.7
    

    证明我拷过去了

  • 指定链接库的目录路径,此时程序运行时如果需要链接库就会来这个目录下找

    [root@rk1808:/home/user]$ export LD_LIBRARY_PATH=testlib:$LD_LIBRARY_PATH
    
  • 运行程序

    [root@rk1808:/home/user]$ ./SourceFile
    Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '1472340076-1-192.mp4':Metadata:major_brand     : isomminor_version   : 512compatible_brands: isomiso2avc1mp41encoder         : Lavf58.29.100description     : Packed by Bilibili XCoder v2.0.2Duration: 00:05:23.14, start: 0.000000, bitrate: 1231 kb/sStream #0:0[0x1](und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(progressive), 1280x720, 1153 kb/s, 30 fps, 30 tbr, 16k tbn (default)Metadata:handler_name    : VideoHandlervendor_id       : [0][0][0][0]Stream #0:1[0x2](und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 68 kb/s (default)Metadata:handler_name    : SoundHandlervendor_id       : [0][0][0][0]
    FFmpeg test successful!
    

    程序正确运行,读取了视频信息,并且输出了,这说明我们前面的工作都是有效的。

再看看我的cpp源码吧

#include <iostream>  	// 包含输入输出流库
#include <string>  		// 包含字符串库extern "C" {
#include <libavformat/avformat.h>  // 包含 FFmpeg 格式处理库
#include <libavutil/imgutils.h>    // 包含 FFmpeg 图像工具库
#include <libavutil/pixdesc.h>     // 包含 FFmpeg 像素格式库
}int main() {avformat_network_init();  		// 初始化 FFmpeg 网络模块const char* filename = "1472340076-1-192.mp4";  // 设置视频文件名AVFormatContext *format_ctx = nullptr;  		// 声明格式上下文指针,初始化为空指针int ret = avformat_open_input(&format_ctx, filename, nullptr, nullptr);  // 打开输入文件并将格式上下文赋给 format_ctxif (ret != 0) {  	// 检查是否成功打开文件std::cerr << "Error opening input file" << std::endl;  // 输出错误信息到标准错误流return 1;  		// 返回错误码}ret = avformat_find_stream_info(format_ctx, nullptr);  		// 获取流信息if (ret < 0) {  // 检查是否成功获取流信息std::cerr << "Error finding stream information" << std::endl;  // 输出错误信息到标准错误流avformat_close_input(&format_ctx);  // 关闭输入文件并释放资源return 1;  	// 返回错误码}av_dump_format(format_ctx, 0, filename, 0);  			// 打印格式信息到标准输出流avformat_close_input(&format_ctx);  					// 关闭输入文件并释放资源std::cout << "FFmpeg test successful!" << std::endl;  	// 输出成功信息到标准输出流return 0;  // 返回成功码
}

四、心灵的救赎

现在是北京时间2024年4月16日2时30分,现在这种时间睡觉实际已经成为了我的日常。

幸运的是,这样的日常倒不是为谁所迫,自己也还愿意这样,享受每天吸收知识感觉。

感觉组里没有搞科研的环境,看着师兄们的时间都被杂事占据了,被横向占据了,这样的生活与我当初想象中的科研生活还是相去甚远。

就到这吧,下面抄点句子,补一补心灵,然后就可以进入梦乡了,如果你也凌晨在看我的文章,且看到了这,那么:陌生人,祝你好梦。

  • 生命是华丽错觉,时间是贼,偷走一切。 ——阿信《如烟》
  • 世界有缺陷,可能性才大 。 ——朱光潜
  • 人生的意义,就在于一直在找寻的路上,寻寻觅觅,我们也变得越来越丰富。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/826630.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot 操作 Redis

导入对应版本的依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency>修改配置文件中的信息 spring:redis:host: 127.0.0.1port: 8888注意: 我这里 xsh…

【python】深度解剖!一文搞懂字符串常用功能

【python】深度解剖!一文搞懂字符串常用功能 【先赞后看养成习惯】求点赞+关注+收藏😀 目录 【python】深度解剖!一文搞懂字符串常用功能字符串的创建:字符串的格式化——占位符.format方法字符串的分割字符串的连接字符串的重复字符串的大小写转换字符串的判断字符串的…

sherpa + ncnn 离线语音识别

目录结构 前言音视频格式转为wavsherpa-ncnn编译LinuxWindowswindows编译中遇到的问题问题“nmake -? failed with: no such file or directory”编译失败原因 成功编译截图 可执行程序说明模型下载语言识别测试LinuxWindows 参考文献 前言 小编需要实现离线音视频语言部分识…

C++ 程序的内存分配

C 程序的内存分配 C 程序的内存分配栈堆数据区程序代码区参考 C 程序的内存分配 一个 C 编译的程序占用内存分为以下几个部分&#xff08;从高地址到低地址&#xff09;&#xff1a; 内核空间&#xff1a;由操作系统创建并控制&#xff0c;用户代码不能读写。栈&#xff1a;由…

mysql 日环比 统计

接到一个任务&#xff0c;要计算日环比的情况。 16、查询销售额日环比情况 日环比&#xff1a; &#xff08;今日-昨日&#xff09;/ 昨日 的一个比率情况。 1&#xff0c;建表 DROP TABLE IF EXISTS sale; create table sale(id int not null AUTO_INCREMENT,record_date da…

IDEA插件:CodeGeex

前言 CodeGeeX是由清华大学和智谱AI联合开发的多语言代码生成模型。CodeGeeX是一款AI编程助手&#xff0c;其功能类似于Github Copilot、Codeium、CodeWhisperer、Bito等智能编程助手。CodeGeeX支持Python、C、Java、JavaScript、Go等10多种主流编程语言。它可以帮助程…

windows驱动开发-WDM框架(一)

在前面的文章中解释过&#xff0c;NT5.0之后windows确定了新的架构Windows Driver Model (WDM)&#xff0c;在Vista之后又推出了Windows Driver Framework(WDF)&#xff0c;这两个都属于驱动程序框架&#xff0c;那么它们的之间的关系是怎样的&#xff1f; WDF是对WDM进行的封…

利用大语言模型,矢量数据库实现数据库的智能搜索

目的 数据库使用SQL 语言查询数据&#xff0c;数据库的记录中要有一个关键字段&#xff08;通常称为主键字段&#xff0c;它的值在数据库列表中是唯一的&#xff09;,数据记录是结构化的. 如果你需要根据数据记录的内容来查询数据记录&#xff0c;就需要通过Select 语句在数据库…

OpenCV杂记(1):绘制OSD(cv::getTextSize, cv::putText)

1. 简述 我们使用OpenCV时&#xff0c;有时会在图像的某个位置绘制OSD信息&#xff0c;如绘制一些字符串作为指示信息。 本文将简要介绍在图像&#xff08;cv::Mat&#xff09;上绘制固定的字符串信息。 2. 使用的API &#xff08;1&#xff09;cv::getTextSize() CV_EXPORT…

vue3 删除对象中的属性,可以使用js里的delete,但需注意ts定义对象类型!

如上如&#xff0c;当使用delete 删除stateData中的属性时&#xff0c; 报错&#xff0c;意思为 TypeScript 错误“‘delete’ 运算符的操作数必须是可选的 什么原因呢&#xff1f;是因为我偷懒 缺少了ts定义类型 方法一&#xff1a; &#xff08;不推荐&#xff09; delete …

MultiHeadAttention在Tensorflow中的实现原理

前言 通过这篇文章&#xff0c;你可以学习到Tensorflow实现MultiHeadAttention的底层原理。 一、MultiHeadAttention的本质内涵 1.Self_Atention机制 MultiHeadAttention是Self_Atention的多头堆嵌&#xff0c;有必要对Self_Atention机制进行一次深入浅出的理解&#xff0c;这…

Linux Makefile用法

1、什么是makefile&#xff1f; Makefile&#xff1a;将不同模块放在不同的目录中&#xff0c;定义一系列的规则进行 “自动化编译”2、Makefile写法 vim makefile 填写样例&#xff1a; app:sub.c add.c mult.c div.c main.cgcc sub.c add.c mult.c div.c main.c -o app3、工作…

刷代码随想录有感(39):每层最大值

题干&#xff1a; 代码&#xff1a; /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), …

OpenCV基本图像处理操作(十一)——图像特征Sift算法

图像尺度空间 在一定的范围内&#xff0c;无论物体是大还是小&#xff0c;人眼都可以分辨出来&#xff0c;然而计算机要有相同的能力却很难&#xff0c;所以要让机器能够对物体在不同尺度下有一个统一的认知&#xff0c;就需要考虑图像在不同的尺度下都存在的特点。 尺度空间的…

《6G数据面架构研究》

目录 一、数据服务的定义二、6G数据服务驱动力及面临的挑战6G数据服务的业务驱动6G数据服务的技术驱动6G数据服务的网络内在驱动6G数据面面临的挑战 三、6G数据服务典型场景自动化网络运维用户体验提升通信感知数据服务 四、6G数据面架构研究数据面架构视图功能定义说明&#x…

kafka部分partition的leader=-1修复方案整理

kafka部分partition的leader-1修复方案整理 1. 背景说明2. 修复测试2.1 创建正常的topic并验证生产和消费2.2 停止kafka模拟leader-12.3 修复parition2.4 修复完成验证生产消费是否恢复 3. 疑问和思考3.1 kafka在进行数据消费时&#xff0c;如果有partition的leader-1&#xff…

从迷宫问题理解dfs

文章目录 迷宫问题打印路径1思路定义一个结构体要保存所走的路径&#xff0c;就需要使用到栈遍历所有的可能性核心代码 部分函数递归图源代码 迷宫问题返回最短路径这里的思想同上面类似。源代码 迷宫问题打印路径1 定义一个二维数组 N*M &#xff0c;如 5 5 数组下所示&…

十一、Yocto集成tcpdump等网络工具

文章目录 Yocto集成tcpdump等网络工具networking layer集成 Yocto集成tcpdump等网络工具 本篇文章为基于raspberrypi 4B单板的yocto实战系列的第十一篇文章&#xff1a; 一、yocto 编译raspberrypi 4B并启动 二、yocto 集成ros2(基于raspberrypi 4B) 三、Yocto创建自定义的lay…

ctf.show_web14

在switch中&#xff0c;case 里如果没有 break&#xff0c;则会继续向下执行 case。 过滤了information_schema.tables、information_schema.column、空格 information_schema.tables 或 .columns 用反引号 information_schema.tables 同时查3个字段 ?query-1/**/union/**/…

ssh免秘钥登录与时钟同步

ssh免秘钥登录及数据拷贝 ssh免秘钥登录及数据拷贝环境生成秘钥拷贝公钥到到远程服务器通过ssh-copy-id命令拷贝公钥到远程服务器通过手动拷贝公钥到远程服务器 非root用户远程拷贝公钥 设置编码方式临时设置编码永久设置方法一永久设置方法二 设置时钟同步使用 ntpdate 命令使…