文章目录
一、Qt 项目搭建二、Qt 可执行程序运行环境分析1、Qt 可执行程序运行环境2、C 语言代码中解析输入参数3、获取命令行输入参数4、代码示例及执行结果 三、FFmpeg 函数错误码处理1、FFmpeg 函数执行返回值 - 错误码引入2、获取错误码对应的描述 - av_strerror 函数3、常见错误码列举4、错误处理代码示例 四、获取视频文件的参数信息1、打开视频文件并获取详细信息2、打印视频文件的格式信息 - av_dump_format 函数3、代码示例 五、获取 AVFormatContext 参数信息 - 文件地址 / 码流个数 / 播放时长1、获取视频文件地址2、获取视频文件码流个数3、获取视频文件播放时长4、完整代码示例
FFmpeg 4.0 版本源码地址 :
GitHub : https://github.com/FFmpeg/FFmpeg/tree/release/4.0GitCode : https://gitcode.com/gh_mirrors/ff/FFmpeg/tree/release/4.0FFmpeg/libavcodec/avpacket.c 源码 : https://gitcode.com/gh_mirrors/ff/FFmpeg/blob/release/4.0/libavcodec/avpacket.c一、Qt 项目搭建
参考 【FFmpeg】FFmpeg 内存结构 ⑥ ( 搭建开发环境 | AVPacket 创建与释放代码分析 | AVPacket 内存使用注意事项 ) 博客的 第一章节 , 搭建开发环境 ;
拷贝头文件 : 将 ffmpeg-4.2.1-win32-dev 开发库 目录 拷贝到 Qt 中 C 语言 项目的根目录 , 这是 FFmpeg 的头文件 ;
配置头文件和依赖库 : 在 .pro 配置文件中进行如下配置 :
# 仅在 Windows 32 位 平台上生效win32 {# 指定项目的头文件搜索路径INCLUDEPATH += $$PWD/ffmpeg-4.2.1-win32-dev/include# 指定链接的静态库或动态库路径LIBS += $$PWD/ffmpeg-4.2.1-win32-dev/lib/avformat.lib \$$PWD/ffmpeg-4.2.1-win32-dev/lib/avcodec.lib \$$PWD/ffmpeg-4.2.1-win32-dev/lib/avdevice.lib \$$PWD/ffmpeg-4.2.1-win32-dev/lib/avfilter.lib \$$PWD/ffmpeg-4.2.1-win32-dev/lib/avutil.lib \$$PWD/ffmpeg-4.2.1-win32-dev/lib/postproc.lib \$$PWD/ffmpeg-4.2.1-win32-dev/lib/swresample.lib \$$PWD/ffmpeg-4.2.1-win32-dev/lib/swscale.lib}
拷贝动态库 : 将 .dll 动态库 拷贝到 C:\Windows\SysWOW64 目录 中 , 这是 Windows 32 位动态库 的位置 ; 也可以将动态库拷贝到 编译输出目录 的根目录 ;导入头文件 : 在 main.c 中导入 FFmpeg 头文件 ;
#include <stdio.h>#include <libavutil/avutil.h>#include <libavformat/avformat.h>#include <libavcodec/avcodec.h>int main(int argc, char **argv){ return 0;}
本篇博客会 在该项目环境基础上 , 进行后续开发 ;
二、Qt 可执行程序运行环境分析
1、Qt 可执行程序运行环境
在 命令行终端 中 , 添加参数 , 直接在命令后添加空格 , 空格后面添加参数 ;
命令 参数
在 Qt 项目中 , 如果要设置 运行参数 , 可以在 左侧 " 项目 " 面板 中 , " Build & Run " 选项下的 Run 面板设置 ;
Qt 项目 编译后 生成的目录是 " D:\002_Project\008_Qt\build-FFmpegC-Desktop_Qt_5_14_2_MSVC2015_32bit-Debug " , 这也是 终端命令行 的操作目录 ;
在上述目录中的 " D:\002_Project\008_Qt\build-FFmpegC-Desktop_Qt_5_14_2_MSVC2015_32bit-Debug\debug\FFmpegC.exe " 文件 是 C/C++ 代码 编译 生成的 可执行程序 ;
在上述目录中拷贝了
test.flvtest.mp4test.ts三个视频文件 , 用于作为 解封装 测试文件 ;
同时拷贝了
avcodec-58.dllavdevice-58.dllavfilter-7.dllavformat-58.dllavutil-56.dllpostproc-55.dllswresample-3.dllswscale-5.dll8 个动态库文件 , 编译后的 FFmpegC.exe 可执行程序 , 需要依赖这些动态库 ;
Qt 中 按 Ctrl + R 快捷键 或 按钮 运行 编译后的 C/C++ 可执行程序 , 相当于在 终端命令行 中 " D:\002_Project\008_Qt\build-FFmpegC-Desktop_Qt_5_14_2_MSVC2015_32bit-Debug " 目录中 , 执行可执行程序 " D:\002_Project\008_Qt\build-FFmpegC-Desktop_Qt_5_14_2_MSVC2015_32bit-Debug\debug\FFmpegC.exe " ;
执行结果如下 :
D:\002_Project\008_Qt\build-FFmpegC-Desktop_Qt_5_14_2_MSVC2015_32bit-Debug>D:\002_Project\008_Qt\build-FFmpegC-Desktop_Qt_5_14_2_MSVC2015_32bit-Debug\debug\FFmpegC.exeFFmpeg version is 4.2.1FFmpeg Success
2、C 语言代码中解析输入参数
C 语言的 main 函数声明 为
int main(int argc, char **argv)
, 该函数 用于从 命令行 获取输入参数并执行程序逻辑 ;
int argc 参数 : 表示 命令行参数的个数 , 包括 可执行程序名称 本身 ; 如果运行程序的命令是 ./program , 那么 argc 值为 1 ;如果运行命令是 ./program arg1 , 那么 argc 值为 2 ;如果运行命令是 ./program arg1 arg2 , 那么 argc 值为 3 ; char **argv 参数 : 指向字符串数组的指针 , 其中每个字符串表示一个命令行参数 ; argv[0] 是 可执行程序 名称 ;argv[1] 是 第一个 命令行参数 ;argv[2] 是 第二个 命令行参数 ;3、获取命令行输入参数
在 main 函数中 , 通过 判断 argc >= 2 就可以确定是否有命令行参数输入 , argv[1] 就是输入的参数内容 ;
// 要解封装的 视频文件 的 相对路径 char *file_name = "test.ts"; // 打印参数基本信息 printf("argc = %d , argv[0] = %s\n", argc, argv[0]); // 检测 参数中 是否传入了 参数 // 命令行中 可执行程序 是第 0 个参数 , 之后的附加参数是第 1 个参数 if(argc >= 2 && argv[1] != NULL) { file_name = argv[1]; // 打印传入的参数 printf("argv[1] = %s\n", argv[1]); }
4、代码示例及执行结果
完整代码示例 :
#include <stdio.h>#include <libavutil/avutil.h>#include <libavformat/avformat.h>#include <libavcodec/avcodec.h>int main(int argc, char **argv){ // 打印 FFmpeg 版本号 printf("FFmpeg version is %s\n", av_version_info()); // 初始化网络环境 // 如果只打开本地文件 , 则不需要调用该函数 //avformat_network_init(); // 1. 获取命令行参数 // 要解封装的 视频文件 的 相对路径 char *file_name = "test.ts"; // 打印参数基本信息 printf("argc = %d , argv[0] = %s\n", argc, argv[0]); // 检测 参数中 是否传入了 参数 // 命令行中 可执行程序 是第 0 个参数 , 之后的附加参数是第 1 个参数 if(argc >= 2 && argv[1] != NULL) { file_name = argv[1]; // 打印传入的参数 printf("argv[1] = %s\n", argv[1]); } printf("FFmpeg End\n"); return 0;}
在 Qt 项目的 运行设置 中 , 设置 test.mp4 参数 ;
使用 Ctrl + R 快捷键 , 运行编译后的程序 , 得到如下 结果 :
FFmpeg version is 4.2.1argc = 2 , argv[0] = D:\002_Project\008_Qt\build-FFmpegC-Desktop_Qt_5_14_2_MSVC2015_32bit-Debug\debug\FFmpegC.exeargv[1] = test.mp4FFmpeg End
上述 Qt 中 执行程序 的 操作 , 相当于在 " D:\002_Project\008_Qt\build-FFmpegC-Desktop_Qt_5_14_2_MSVC2015_32bit-Debug " 目录中执行 如下命令 :
D:\002_Project\008_Qt\build-FFmpegC-Desktop_Qt_5_14_2_MSVC2015_32bit-Debug\debug\FFmpegC.exe test.mp4
其执行结果 , 与 Qt 中的运行结果相同 ;
三、FFmpeg 函数错误码处理
1、FFmpeg 函数执行返回值 - 错误码引入
在使用 FFmpeg 函数时 ,
函数执行成功 , 一般都返回 大于等于 0 的数值 ;函数执行失败 , 一般都会返 小于 0 的数值 , 该数值又称为 错误码 ;错误码 通常是一个负数 , 用于表示该函数操作失败 , 处理 负数错误码 并进行调试是 FFmpeg 音视频 开发中非常重要的一部分 ;
2、获取错误码对应的描述 - av_strerror 函数
av_strerror 函数原型 : 该函数可以 根据 错误码 获取对应的 字符串描述信息 ;
int av_strerror(int errnum, char *errbuf, size_t errbuf_size);
参数解析 : int errnum 参数 : 输入的错误码 , 其它 FFmpeg 函数返回的 负数错误码 ;char *errbuf 参数 : 用于 存储 错误描述信息 的 字符串缓冲区 ;size_t errbuf_size 参数 : errbuf 错误信息字符串缓冲区的字节大小 ; 返回值 : 函数执行成功 : 返回 0 , 将 错误信息 写入到 errbuf 指针 指向的 内存空间中 ;函数执行失败 : 返回 负数 , 最大的可能是 errbuf 分配的内存空间太小导致无法写入错误信息 ; 3、常见错误码列举
常见错误码列举 :
AVERROR(EINVAL)
无效参数 ;AVERROR(ENOMEM)
内存不足 ;AVERROR(EIO)
IO 错误 ;AVERROR(ENOENT)
文件不存在 ;AVERROR(EBUSY)
资源繁忙 ;AVERROR(EPERM)
权限错误 ;AVERROR_UNKNOWN
未知错误 ; 错误码 AVERROR() 是一个宏定义 , 该 宏定义定义在 error.h 头文件中 ,
#define AVERROR(e) (-(e)) ///< 将一个POSIX错误代码转换为一个负数的错误代码,以供库函数返回。
上述宏定义中的 参数 e 的取值定义在 errno.h 头文件中 ;
// 错误代码(Error codes)#define EPERM 1 // 操作不允许(Operation not permitted)#define ENOENT 2 // 文件或目录不存在(No such file or directory)#define ESRCH 3 // 无法找到进程(No such process)#define EINTR 4 // 系统调用被中断(Interrupted system call)#define EIO 5 // 输入/输出错误(I/O error)#define ENXIO 6 // 设备不存在或不可用(No such device or address)#define E2BIG 7 // 参数列表太长(Argument list too long)#define ENOEXEC 8 // 可执行文件格式错误(Exec format error)#define EBADF 9 // 文件描述符无效(Bad file descriptor)#define ECHILD 10 // 无子进程(No child processes)#define EAGAIN 11 // 资源暂时不可用(Resource temporarily unavailable)#define ENOMEM 12 // 内存不足(Out of memory)#define EACCES 13 // 权限被拒绝(Permission denied)#define EFAULT 14 // 指针地址无效(Bad address)#define EBUSY 16 // 设备或资源正忙(Device or resource busy)#define EEXIST 17 // 文件已存在(File exists)#define EXDEV 18 // 无法跨设备链接(Cross-device link)#define ENODEV 19 // 设备不存在(No such device)#define ENOTDIR 20 // 非目录(Not a directory)#define EISDIR 21 // 是一个目录(Is a directory)#define ENFILE 23 // 系统文件描述符表已满(File table overflow)#define EMFILE 24 // 打开文件过多(Too many open files)#define ENOTTY 25 // 不支持的设备请求(Inappropriate ioctl for device)#define EFBIG 27 // 文件过大(File too large)#define ENOSPC 28 // 设备空间不足(No space left on device)#define ESPIPE 29 // 非法的文件指针移动操作(Illegal seek)#define EROFS 30 // 文件系统只读(Read-only file system)#define EMLINK 31 // 链接数太多(Too many links)#define EPIPE 32 // 管道错误(Broken pipe)#define EDOM 33 // 数学函数域错误(Math argument out of domain)#define EDEADLK 36 // 资源死锁(Resource deadlock avoided)#define ENAMETOOLONG 38 // 文件名太长(File name too long)#define ENOLCK 39 // 无法再创建锁(No locks available)#define ENOSYS 40 // 函数未实现(Function not implemented)#define ENOTEMPTY 41 // 目录非空(Directory not empty)// 安全 CRT(C 运行时库)函数使用的错误代码#ifndef RC_INVOKED #define _SECURECRT_ERRCODE_VALUES_DEFINED #define EINVAL 22 // 无效参数(Invalid argument) #define ERANGE 34 // 结果超出范围(Result too large) #define EILSEQ 42 // 非法字节序列(Illegal byte sequence) #define STRUNCATE 80 // 字符串被截断(String was truncated)#endif
4、错误处理代码示例
下面的代码中 , 调用了 avformat_open_input 函数 打开视频文件 ,
并将 探测到的 视频文件 参数信息 填充到 AVFormatContext 结构体中 ;
如果 avformat_open_input 函数 返回负数 返回值 , 说明 打开视频文件 失败 , 需要进行错误处理 , 调用 av_strerror 函数 获取 负数错误码 对应的 错误描述信息 ;
错误处理代码示例 :
// 2. 打开视频文件 // 描述 媒体文件 或 媒体流 的构成和基本信息 AVFormatContext *fmt_ctx = NULL; // 打开文件 , 探测协议类型 , 将获取的参数信息 填充到 AVFormatContext 结构体中 int ret = avformat_open_input(&fmt_ctx, file_name, NULL, NULL); // avformat_open_input 函数 : 执行成功返回 0 , 执行失败返回负数 ( 错误码 ) if (ret < 0) { // 用于接收错误信息的字符串 char buf[1024] = { 0 }; // 获取 负数错误码 对应的 错误日志信息 av_strerror(ret, buf, sizeof(buf) - 1); // 打印错误信息 printf("open %s failed : %s\n", file_name, buf); goto failed; }
四、获取视频文件的参数信息
1、打开视频文件并获取详细信息
在之前的博客 【FFmpeg】解封装 ① ( 封装与解封装流程 | 解封装函数简介 | 查找码流标号和码流参数信息 | 使用 MediaInfo 分析视频文件 ) 中介绍了 使用 FFmpeg 对 视频文件 进行 解封装 的 流程 ,
首先 , 调用 avformat_open_input 函数 , 打开 视频文件 ;int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);
然后 , 调用 avformat_find_stream_info 函数 , 获取 视频文件 码流信息 , 该函数仅在 头文件信息不足 的情况下使用 ; int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
绝大多数情况下 , 执行完这两个函数之后 , 就可以获取完整的 视频文件 码流格式信息 , 这些信息保存在 AVFormatContext 结构体中 ;
2、打印视频文件的格式信息 - av_dump_format 函数
av_dump_format 函数 用于打印 视频文件 或 媒体流 的 格式信息 , 包括
文件格式码率格式特征信息音视频流格式参数等 , 该函数原型定义在 libavformat/avformat.h 头文件中 , 使用前先导入该头文件 ;
av_dump_format 函数原型 : 该函数 用于 打印 视频文件 或 媒体流 的 格式信息 ;
void av_dump_format(AVFormatContext *ic, int index, const char *url, int is_output);
AVFormatContext *ic 参数 : 指向 AVFormatContext 的指针 , 表示输入或输出的多媒体文件或流的上下文 ;int index 参数 : 码流 索引编号 , 从 0 开始计数 ;const char *url 参数 : 视频文件路径 或 网络地址 ; 这个路径既可以是 相对路径 , 又可以是绝对路径 ;int is_output 参数 : 是否是输出文件 ; 一般情况下 输入文件 设置 0 , 输出文件设置 1 ; 3、代码示例
核心代码示例 :
// 4. 打印 视频文件 的 参数信息 ☆☆☆ printf_s("\n==== av_dump_format start ===\n\n"); av_dump_format(fmt_ctx, 0, file_name, 0); printf_s("\n==== av_dump_format end ===\n");
完整代码示例 :
#include <stdio.h>#include <libavutil/avutil.h>#include <libavformat/avformat.h>#include <libavcodec/avcodec.h>int main(int argc, char **argv){ // 打印 FFmpeg 版本号 printf("FFmpeg version is %s\n", av_version_info()); // 初始化网络环境 // 如果只打开本地文件 , 则不需要调用该函数 //avformat_network_init(); // 1. 获取命令行参数 // 要解封装的 视频文件 的 相对路径 char *file_name = "test.ts"; // 打印参数基本信息 printf("argc = %d , argv[0] = %s\n", argc, argv[0]); // 检测 参数中 是否传入了 参数 // 命令行中 可执行程序 是第 0 个参数 , 之后的附加参数是第 1 个参数 if(argc >= 2 && argv[1] != NULL) { file_name = argv[1]; // 打印传入的参数 printf("argv[1] = %s\n", argv[1]); } // 2. 打开视频文件 // 描述 媒体文件 或 媒体流 的构成和基本信息 AVFormatContext *fmt_ctx = NULL; // 打开文件 , 探测协议类型 , 将获取的参数信息 填充到 AVFormatContext 结构体中 int ret = avformat_open_input(&fmt_ctx, file_name, NULL, NULL); // avformat_open_input 函数 : 执行成功返回 0 , 执行失败返回负数 ( 错误码 ) if (ret < 0) { // 用于接收错误信息的字符串 char buf[1024] = { 0 }; // 获取 负数错误码 对应的 错误日志信息 av_strerror(ret, buf, sizeof(buf) - 1); // 打印错误信息 printf("avformat_open_input failed : %s\n", file_name, buf); goto failed; } // 3. 获取 媒体流 详细信息 ret = avformat_find_stream_info(fmt_ctx, NULL); // 获取 码流 信息失败 , 返回 负数错误码 if (ret < 0) { // 用于接收错误信息的字符串 char buf[1024] = { 0 }; // 获取 负数错误码 对应的 错误日志信息 av_strerror(ret, buf, sizeof(buf) - 1); // 打印错误信息 printf("avformat_find_stream_info failed:%s\n", file_name, buf); goto failed; } // 4. 打印 视频文件 的 参数信息 ☆☆☆ printf_s("\n==== av_dump_format start ===\n\n"); av_dump_format(fmt_ctx, 0, file_name, 0); printf_s("\n==== av_dump_format end ===\n");failed: // FFmpeg 出错 , 函数执行失败后 释放内存 if(fmt_ctx){ // 与 avformat_open_input 配对使用 , 防止内存泄漏 avformat_close_input(&fmt_ctx); } // 执行结束 printf("\nFFmpeg End\n"); // 防止 命令行终端 打印完信息 后 马上退出 getchar(); return 0;}
执行结果 :
==== av_dump_format start ===Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'test.mp4': Metadata: major_brand : isom minor_version : 512 compatible_brands: isomiso2avc1mp41 encoder : Lavf56.38.102 comment : www.ieway.cn Duration: 00:03:42.53, start: 0.000000, bitrate: 281 kb/s Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p, 1920x1080, 150 kb/s, 14.46 fps, 15 tbr, 15360 tbn, 30 tbc (default) Metadata: handler_name : VideoHandler Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 128 kb/s (default) Metadata: handler_name : SoundHandler==== av_dump_format end ===
五、获取 AVFormatContext 参数信息 - 文件地址 / 码流个数 / 播放时长
1、获取视频文件地址
AVFormatContext 结构体 的 url 字段 , 就是 读取到的 音视频文件的本地路径 或 网络地址 ;
AVFormatContext *ifmt_ctx = NULL; avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL);printf("url : %s\n", ifmt_ctx->url);
在 AVFormatContext 结构体 中 , 还有一个 废弃的字段 filename , 表示文件名称 , 这里不推荐使用 ;
char filename[1024];
2、获取视频文件码流个数
AVFormatContext 结构体 的 nb_streams 字段 , 就是 读取到的 视频文件的 码流数量 ;
AVFormatContext *ifmt_ctx = NULL; avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL);printf("stream number : %s\n", ifmt_ctx->nb_streams);
3、获取视频文件播放时长
AVFormatContext 结构体 的 duration 字段 , 就是 读取到的 视频文件的 播放时长 , 单位是 微秒 , 1000 微秒 = 1 毫秒 , 1000 毫秒 = 1秒 ;
int total_seconds, hour, minute, second; // duration: 播放时长 , 单位 微妙 (us) , 1000us = 1ms , 1000ms = 1秒 // 计算总的秒数 total_seconds = (ifmt_ctx->duration) / AV_TIME_BASE; // 计算小时数 hour = total_seconds / 3600; // 计算分钟数 minute = (total_seconds % 3600) / 60; // 计算秒数 second = (total_seconds % 60); printf("duration: %02d:%02d:%02d\n", hour, minute, second);
4、完整代码示例
完整代码示例 :
#include <stdio.h>#include <libavutil/avutil.h>#include <libavformat/avformat.h>#include <libavcodec/avcodec.h>int main(int argc, char **argv){ // 打印 FFmpeg 版本号 printf("FFmpeg version is %s\n", av_version_info()); // 初始化网络环境 // 如果只打开本地文件 , 则不需要调用该函数 //avformat_network_init(); // 1. 获取命令行参数 // 要解封装的 视频文件 的 相对路径 char *file_name = "test.ts"; // 打印参数基本信息 printf("argc = %d , argv[0] = %s\n", argc, argv[0]); // 检测 参数中 是否传入了 参数 // 命令行中 可执行程序 是第 0 个参数 , 之后的附加参数是第 1 个参数 if(argc >= 2 && argv[1] != NULL) { file_name = argv[1]; // 打印传入的参数 printf("argv[1] = %s\n", argv[1]); } // 2. 打开视频文件 // 描述 媒体文件 或 媒体流 的构成和基本信息 AVFormatContext *fmt_ctx = NULL; // 打开文件 , 探测协议类型 , 将获取的参数信息 填充到 AVFormatContext 结构体中 int ret = avformat_open_input(&fmt_ctx, file_name, NULL, NULL); // avformat_open_input 函数 : 执行成功返回 0 , 执行失败返回负数 ( 错误码 ) if (ret < 0) { // 用于接收错误信息的字符串 char buf[1024] = { 0 }; // 获取 负数错误码 对应的 错误日志信息 av_strerror(ret, buf, sizeof(buf) - 1); // 打印错误信息 printf("avformat_open_input failed : %s\n", file_name, buf); goto failed; } // 3. 获取 媒体流 详细信息 ret = avformat_find_stream_info(fmt_ctx, NULL); // 获取 码流 信息失败 , 返回 负数错误码 if (ret < 0) { // 用于接收错误信息的字符串 char buf[1024] = { 0 }; // 获取 负数错误码 对应的 错误日志信息 av_strerror(ret, buf, sizeof(buf) - 1); // 打印错误信息 printf("avformat_find_stream_info failed:%s\n", file_name, buf); goto failed; } // 4. 打印 视频文件 的 参数信息 ☆☆☆ printf_s("\n==== av_dump_format start ===\n\n"); av_dump_format(fmt_ctx, 0, file_name, 0); printf_s("\n==== av_dump_format end ===\n"); // 5. 获取文件路径或地址 printf("url : %s\n", fmt_ctx->url); // 6. 获取码流个数 printf("stream number : %d\n", fmt_ctx->nb_streams); // 7. 获取播放时长 int total_seconds, hour, minute, second; // duration: 播放时长 , 单位 微妙 (us) , 1000us = 1ms , 1000ms = 1秒 // 计算总的秒数 total_seconds = (fmt_ctx->duration) / AV_TIME_BASE; // 计算小时数 hour = total_seconds / 3600; // 计算分钟数 minute = (total_seconds % 3600) / 60; // 计算秒数 second = (total_seconds % 60); printf("duration: %02d:%02d:%02d\n", hour, minute, second);failed: // FFmpeg 出错 , 函数执行失败后 释放内存 if(fmt_ctx){ // 与 avformat_open_input 配对使用 , 防止内存泄漏 avformat_close_input(&fmt_ctx); } // 执行结束 printf("\nFFmpeg End\n"); // 防止 命令行终端 打印完信息 后 马上退出 getchar(); return 0;}
执行结果 :
部分结果 : 获取的 视频地址 , 码流数量 , 播放时长 信息 :url : test.mp4stream number : 2duration: 00:01:01
完整结果 : FFmpeg version is 4.2.1argc = 2 , argv[0] = Y:\002_WorkSpace\006_Qt\build-FFmpegC-Desktop_Qt_5_14_2_MSVC2015_32bit-Debug\debug\FFmpegC.exeargv[1] = test.mp4==== av_dump_format start ===Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'test.mp4': Metadata: major_brand : isom minor_version : 512 compatible_brands: mp41 creation_time : 2024-02-09T06:29:29.000000Z encoder : Bandicam 4.5.0.1587 / GDI / Nvidia NVENC encoder-eng : Bandicam 4.5.0.1587 / GDI / Nvidia NVENC Duration: 00:01:01.78, start: 0.000000, bitrate: 1783 kb/s Stream #0:0(eng): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 752x420 [SAR 1:1 DAR 188:105], 1580 kb/s, 28.61 fps, 30 tbr, 30k tbn, 60 tbc (default) Metadata: creation_time : 2024-02-09T06:29:29.000000Z handler_name : VideoHandler Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 195 kb/s (default) Metadata: creation_time : 2024-02-09T06:29:29.000000Z handler_name : SoundHandler==== av_dump_format end ===url : test.mp4stream number : 2duration: 00:01:01FFmpeg End