ffmpeg C语音 读取视频帧源码

文章目录


前言

FFmpeg 是一个非常强大的音视频处理库,广泛应用于多媒体流的处理与转换。在本篇文章中,我们将通过一个 C++/Qt 示例,展示如何使用 FFmpeg 打开视频文件,读取视频帧并提取时间戳。文章会重点介绍涉及到的 FFmpeg 函数和结构体,帮助你了解 FFmpeg 的基本用法。

主体

1. AVPacket

作用:

AVPacket 结构体用于存储一个编码后音视频数据包。每个音视频流的帧数据都会以包的形式存在,AVPacket 结构体就存储了这些信息,包括了数据包的时间戳、数据内容等。

相关字段:
  • pts:该字段存储解码时间戳(以流的时间基准单位表示)。它用于标识数据包的时间点。
  • dts:解码时间戳,通常与 PTS 一致,但对于 B 帧可能不同。
  • data:存储实际的音视频数据(例如,视频帧或音频帧)。
  • sizedata 中的数据大小。
作用场景:

在视频解码和播放时,每一帧的音视频数据会被封装成一个 AVPacket 对象。我们可以通过读取这些数据包来获取视频的每一帧,并通过时间戳等信息进行同步。

2. av_read_frame

函数原型:
c 复制代码
int av_read_frame(AVFormatContext *s, AVPacket *pkt);
作用:

av_read_frame 函数从媒体文件中读取一帧数据,并将数据封装在 AVPacket 中。此函数对于读取视频或音频的流数据是至关重要的。它会从输入流中读取音视频数据包,直到所有数据都被处理完。

参数:
  • s:指向 AVFormatContext 的指针,表示已打开的媒体文件。
  • pkt:指向 AVPacket 的指针,用于存储读取到的数据包。
返回值:

成功时返回 0,失败时返回负值。

相关结构体:
  • AVFormatContext:表示已打开的媒体文件的上下文。
  • AVPacket:表示存储在数据包中的音视频帧数据。

3. av_packet_unref

函数原型:
c 复制代码
void av_packet_unref(AVPacket *pkt);
作用:

av_packet_unref 用于释放 AVPacket 中的内存,清除数据包中的内容。每当我们使用完 AVPacket 后,都应该调用该函数进行内存清理,避免内存泄漏。

参数:
  • pkt:指向 AVPacket 结构体的指针,表示需要清理的 AVPacket
返回值:

4. AVRational

作用:

AVRational 是 FFmpeg 中用于表示有理数的结构体。在视频处理中,AVRational 结构体经常用来表示时间戳与流时间基之间的关系。

相关字段:
  • num:分子。
  • den:分母。
作用场景:

在 FFmpeg 中,许多与时间相关的计算(如帧率、时间基准等)都使用 AVRational 结构体来进行表示和计算。例如,视频的时间基(time_base)就是一个 AVRational,表示每个时间单位的长度。

5. r2d(AVRational r)

作用:

r2d 是一个辅助函数,将 AVRational 转换为 double 类型。它通过将 AVRational 的分子除以分母,返回一个浮动数值。

代码实现:
cpp 复制代码
static double r2d(AVRational r)
{
    return r.num == 0 || r.den == 0 ? 0. : (double)r.num / (double)r.den;
}
参数:
  • rAVRational 结构体,表示一个有理数。
返回值:

返回 AVRational 的值,转换为 double 类型。

代码分析

打开视频文件

cpp 复制代码
av_register_all();
char *path = "video.mp4";
AVFormatContext *ic = NULL;
int re = avformat_open_input(&ic, path, 0, 0);
if (re != 0)
{
    char buf[1024] = { 0 };
    av_strerror(re, buf, sizeof(buf));
    printf("open %s failed: %s\n", path, buf);
    getchar();
    return -1;
}

首先,我们调用 av_register_all() 来初始化 FFmpeg 库。接着,通过 avformat_open_input 打开视频文件。如果打开失败,程序会输出错误信息并退出。

读取视频帧

cpp 复制代码
int totalSec = ic->duration / AV_TIME_BASE;
printf("file totalSec is %d-%d\n", totalSec / 60, totalSec % 60);
for (;;)
{
    AVPacket pkt;
    re = av_read_frame(ic, &pkt);
    if (re != 0) break;
    int pts = pkt.pts * r2d(ic->streams[pkt.stream_index]->time_base) * 1000;
    printf("pts = %d\n", pts);
    av_packet_unref(&pkt);
}

接下来,我们计算视频文件的总时长。通过 av_read_frame 函数,我们逐帧读取视频文件的数据包(AVPacket)。每读取一帧,我们会计算并打印该帧的时间戳(PTS)。使用 r2d 函数将 AVRational 转换为 double 类型后,再乘以 1000 转换为毫秒。最后,调用 av_packet_unref 清除数据包。

关闭视频文件

cpp 复制代码
avformat_close_input(&ic);

读取完所有帧后,我们调用 avformat_close_input 来关闭文件并释放相关资源。

总结

在本篇文章中,我们介绍了如何通过 FFmpeg 打开视频文件,读取视频数据包,并提取时间戳。关键的 FFmpeg 函数如 av_read_frameav_packet_unref 和结构体如 AVPacketAVFormatContext 在此过程中扮演了重要角色。通过这个示例,我们展示了如何结合 FFmpeg 和 Qt 进行音视频处理与播放,并且理解了 FFmpeg 内部的处理流程。这为进一步深入了解 FFmpeg 提供了良好的基础。

相关推荐
亦是远方11 分钟前
C++编译过程
java·开发语言·c++
萝诗粉30 分钟前
c语言在程序开发过程的各个阶段遇到的问题和知识总结
c语言·开发语言
刘好念32 分钟前
[OpenGL] Transform feedback 介绍以及使用示例
c++·计算机图形学·opengl
快乐飒男39 分钟前
C语言基础13(内存管理)
c语言·开发语言·学习
小憩-1 小时前
【重生之我要苦学C语言】文件操作
c语言·开发语言
程序leo源1 小时前
深入理解指针
android·c语言·开发语言·汇编·c++·青少年编程·c#
因特麦克斯1 小时前
C++ 中面向对象编程如何实现动态绑定?C++如何管理内存?
开发语言·c++
S hh1 小时前
【C++】模板机制
开发语言·c++·学习·算法
万事大吉CC1 小时前
680: Jack Straws
数据结构·c++·算法