目录
第二点:AVCodecContext 结构体包含的sample_aspect_ratio
第五点:SDL2中的SDL_PollEvent和SDL_WaitEvent之间的区别
前一篇博文我们已经讲了关于视频播放的进一步改进,并且也使用到了多线程等操作,之所以会这样做的原因还是因为后期在拓展功能的时候更加的方便,比如快进,快退等功能。这篇博文主要是对视频帧读取,解码以及显示部分进行了拆解,将视频帧读取和解码部分分别放到了不同的线程,并且其中还使用了定时器,互斥量和条件变量,细节请看代码,同时也会视频讲解。其中参考了参考1 和 参考2
实现的整体流程:
整体代码实现流程
视频帧播放流程:
注:可以看到这个视频帧播放流程其实看了代码之后并没有像上面画的流程图那么简单,其中很多细节之处都省略了,视频代码讲解不封将细讲。
音频数据播放流程:
注:关于音频数据播放部分的流程实现之前的视频已经讲解,请看视频讲解 。
视频帧数据和音频数据保存到链队列中的结构如下:
实现思路
- 创建窗口和渲染器,用于视频文件的选择,播放以及程序关闭
- 选择视频MP4文件
- 视频数据读取线程
- 根据打开的视频文件,得到视频流和音频流其对应的流索引号,用于后面视频帧和音频数据读取时所用
- 找到对应的视频流和音频流索引号的同时,分别找到对应的视频和音频解码信息
- 音频相关参数配置以及用于缩放的视频帧格式定义
- 读取视频帧数据和音频数据,分别存储到视频和音频链队列中
- 定义从视频帧解码线程
- 从视频链队列中取出视频帧数据(同时要判断队列中是否有数据可取,如果没有数据可取就跳出循环并加锁和等待入队列函数发出条件信号)
- 对视频帧数据解码和缩放操作(这个过程中也涉及加锁和解锁等操作)
- 显示视频帧(显示视频帧的同时也会涉及到高宽比的缩放操作)
-
如果为音频数据播放
-
读取音频数据(根据音频流索引号判断当前是否为音频数据)
-
向链队列中加入音频数据
-
加锁Mutex
-
将音频数据加入链队列中
-
发送信号通知
-
解锁Mutex
-
-
从链队列中取出音频数据
-
加锁Mutex
-
判断链队列中是否有数据可取
-
如果有数据集可取,即可取出并进行音频数据解码如果没有数据可取,则等待信号通知
-
解锁Mutex
-
-
- 视频播放完成之后,关闭播放窗口,可以继续选择播放或者选择其他视频文件播放
- 关闭主线程窗口,完全结束视频播放。
注:这个文字的解释部分并没有十分清楚的从细节上讲解,不想看这个文字部分的小伙伴就直接看视频讲解即可。
附加知识点
第一点:
AVRational
有理数结构体
typedef struct AVRational {
int num; ///< Numerator
int den; ///< Denominator
} AVRational;
AVRational 是 FFmpeg 中用于表示有理数(即分数)的结构体。它通常用于处理与时间、
帧率、比特率等相关的数值,尤其是在音视频处理领域。
-
num:分子(Numerator)
- 这是有理数的上部分,表示分数的分子。
-
den:分母(Denominator)
- 这是有理数的下部分,表示分数的分母。
AVRational
结构体的主要用途包括:
-
时间表示:在处理时间戳时,
AVRational
可以用来表示时间的比率,例如帧率(frames per second, fps)或采样率(samples per second, sps)。 -
比特率:在音视频编码中,
AVRational
可以用来表示比特率,例如每秒多少比特。 -
转换和计算:由于有理数可以精确表示分数,使用
AVRational
可以避免浮点数运算带来的精度问题。在进行时间、帧率等计算时,可以使用AVRational
进行加法、减法、乘法和除法等操作。
第二点
:AVCodecContext
结构体包含的sample_aspect_ratio
样本宽高比(Sample Aspect Ratio):
sample_aspect_ratio(四舍五入的整数)
是一个AVRational
类型的字段,表示视频帧的宽高比。它通常用于描述视频图像的像素宽度与高度的比例。
第三点
:av_q2d
函数
将 AVRational
类型的有理数转换为双精度浮点数(double
)。这个函数的主要作用是将表示为分数的值(如帧率、比特率等)转换为更易于处理的浮点数格式。
第四点:SDL中自定义事件
-
定义事件类型:使用
SDL_USEREVENT
及其后续的事件类型(SDL_USEREVENT + n
)来定义您的自定义事件。 -
创建事件:使用
SDL_Event
结构体来创建和初始化事件。 -
推送事件:使用
SDL_PushEvent
将事件推送到事件队列。 -
处理事件:在事件循环中检查并处理自定义事件。
#define FF_REFRESH_EVENT (SDL_USEREVENT) //定义定时刷新界面的事件
#define FF_QUIT_EVENT (SDL_USEREVENT + 1) //定义退出程序的事件
第五点:SDL2中的SDL_PollEvent和SDL_WaitEvent之间的区别
SDL_PollEvent
- 功能:
SDL_PollEvent
用于检查事件队列中是否有待处理的事件。 - 阻塞行为:该函数是非阻塞的。如果事件队列中没有事件,
SDL_PollEvent
会立即返回0
,并不会等待事件的到来。 - 使用场景:适用于需要在主循环中频繁检查事件的场景,例如游戏循环中,在每一帧中检查事件并更新游戏状态。
SDL_WaitEvent
- 功能:
SDL_WaitEvent
用于等待事件的到来,并在事件到达时处理它。 - 阻塞行为:该函数是阻塞的。如果事件队列为空,
SDL_WaitEvent
会阻塞程序,直到有事件到达。 - 使用场景:适用于需要在没有事件时不消耗 CPU 资源的场景,例如在 GUI 应用程序中,在没有事件时让程序处于等待状态。
QT 6.6.0 + FFmpeg+SDL2实现音频视频播放