当前位置:首页 » 《休闲阅读》 » 正文

【FFmpeg】解封装 ② ( 解封装代码实战 | Qt 项目搭建 | Qt 可执行程序运行环境分析 | FFmpeg 函数错误码处理 | 获取视频文件的参数信息 | 获取文 码流个数/播放时长 )

15 人参与  2024年12月30日 18:01  分类 : 《休闲阅读》  评论

点击全文阅读


文章目录

一、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.dll

8 个动态库文件 , 编译后的 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

在这里插入图片描述


点击全文阅读


本文链接:http://m.zhangshiyu.com/post/209411.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1