QT 6.6.0 + FFmpeg + SDL2实现音频视频播放(音视频播放同步改进)

B站视频讲解

GitHub源码下载地址

目录

前置知识点(官方 + 网上查询)

第一点:AVCodecContext的time_base变量含义

第二点:AVFrame 结构体的 repeat_pict 变量含义

第三点:AVFrame 结构体的 best_effort_timestamp 变量含义

最后一点:每秒钟总的字节数

特别的“加权平均”

video_refresh_timer函数实现大致含义

get_audio_clock(VideoState * videoState)函数实现含义 


初学案例教程
QT6.6.0实现打开电脑摄像头或其他的外接摄像头(实现拍照功能)
QT6.6.0实现简单的录音器
QT6.6.0实现一个简单的视频和音频播放器
QT6.6.0实现简单的视频录制(包含语音录制)器
QT6.6.0关于QMediaMetaData功能以及列子说明使用方法
QT6.6.0实现QtCamera功能(拍照,录像以及图像和视频的相关配置)
T6.6.0实现更好的视频播放器(前期改进)
QT6.6.0实现图表如饼图,柱状图,散点图以及盒图等的基本使用
QT6.6.0实现客户端Socket编程以及socket测试软件通信
QT6.6.0 实现服务端Server和多个客户端Client通信以及socket测试软件通信
QT6.6.0 实现QNetworkAccessManager的基本应用以及其拓展使用
QT6.6.0 实现服务端Server和多个客户端Client互相发送文件信息
QT6.6.0和QT6.0以下的版本实现FTP文件上传和下载(过程详解)
QT6.6.0实现QNetworkInterface网络接口,QHostAddress网络地址IP以及域名解析等相关信息查询。
QT6.6.0基于QTcpSocket和QTcpServer实现多个客户端群聊
QT6.6.0实现基于UDP协议的简单通信(过程详解)
QT 6.6.0基于UDP协议实现广播和多播机制(过程详解)
QT 6.6.0基于UDP协议实现群聊功能(过程详解)
QT 6.6.0中OpenCV两种环境的配置方法以及基本使用例子
QT 6.6.0 中基于OpenCV的图像变换以及基于鼠标点击移动事件的图像绘制
QT 6.6.0 基于OpenCV对图像进行旋转,缩放和裁剪等操作
QT 6.6.0 基于OpenCV实现图像风格的改变(add和subtract)
PyTorch 训练之后的网络模型.pth转.onnx文件并对图像进行预测
QT 6.6.0 基于OpenCV加载.pth模型文件转换之后的ONNX模型文件,并且实现图像分类
QT 6.6.0 基于OpenCV中的cv::dnn::ClassificationModel实现图像分类
yolov5目标检测和QT 6.6.0 基于OpenCV加载yolov5.onnx模型文件实现目标检测
FCN图像分割和QT 6.6.0 加载分割FCN_Resnet50.ONNX模型文件进行图像分割(过程详解)
QT 6.6.0 中基于SDL2库实现WAV音频格式的播放(过程详解)
QT 6.6.0 中SDL2库的基本使用以及利用SDL2库创建的界面实现音频播放(过程详解)
QT 6.6.0 中基于SDL2,SDL2_ttf,SDL2_image,SDL2_mixer实现音频播放器(改进版)
QT 6.6.0 + SDL2 + SDL2_mixer实现音频播放器(改进)
QT 6.6.0 中基于FFmpeg + SDL2 + SDL2_mixer实现音频录音(过程详解)
QT 6.6.0 + FFmpeg + SDL2实现MP4视频播放(过程详解)
QT 6.6.0 + FFmpeg + SDL2实现视频播放器(视频和音频同时播放-改进)
QT 6.6.0 + FFmpeg + SDL2实现音频视频播放(改进)
QT 6.6.0 + FFmpeg + SDL2实现音频视频播放(视频帧播放多线程改进)

        其实在前面的视频播放器实现的过程中我们就已经视频和音频的运行比较流畅(链接),但是其实很多视频并不是这样的,因此,我们需要将视频和音频进行同步,就像参考1 所说的,可以将音频同步到视频,或者将视频同步到音频,再或者两者同步到外部时钟。虽然我们前面都采用了多线程,互斥量和条件变量操作,但是还是需要进行改进,这一系列的改进不仅仅是因为实现视频和音频的同步播放,而且方便功能的拓展。参考2

       从前面的改进到现在,我们一直都在说这样的改进,比如使用链队列来存储视频帧和音频数据,使用多线程,互斥量和条件变量(其实我们在实施改进之前就已经使用了这些技术)等操作将视频帧或者音频播放部分进行进一步的拆解,那么这些操作真的是在为后面的功能拓展做铺垫吗?看完这篇文章之后,小伙伴就应该有感受了。视频帧有解码时间戳(DTS)(视频帧或音频解码)和显示时间戳(PTS)(视频帧显示或音频播放),那么这些是不是影响着视频或者音频的播放速度和播放时间以及视频帧是否会丢失的问题,也就是我们的视频帧的解码和播放之间可能并不同步,本篇文章主要基于上面给出的参考链接1和参考链接2来视频帧播放的同步,正是利用实现同步为后期实现快进和快退等功能打下基础。

        视频帧播放和音频播放(buffer size)时间轴参考:也就是视频帧和音频在播放的过程可能不同步。建议看这个时间轴之前先去看视频和代码实现,然后来看时间轴会更好理解一点。

注:图中涉及的变量名称和代码中是提到的名称是对应的。 

注:这里之所以没有给出一个流程图,是因为整个改进的过程虽然代码不多,但是逻辑上不好去绘制流程图给大家看,大家可以看一下上面给出的视频大致讲解。


前置知识点(官方 + 网上查询)

第一点:AVCodecContexttime_base变量含义

  • 时间基准time_base 是一个 AVRational 结构体,表示时间戳的分数形式。它的含义是“每一秒钟有多少个时间单位”,即:

                                  time_base = numerator / denominator(可以理解为播放一帧所需要的间)

  • 时间戳计算:在解码和播放视频时,帧的时间戳(pts-显示时间戳 和 dts-解码时间戳)通常是以 time_base 为单位的。通过将时间戳与 time_base 相乘,可以将其转换为秒:

                                   seconds = pts×(time_base.numerator / time_base.denominator)

第二点:AVFrame 结构体的 repeat_pict 变量含义

repeat_pict 是一个整数值,表示当前帧的重复次数。它指示当前帧在播放时需要重复的次数。该变量的值通常与视频的时间基准(time_base)和帧率(frame rate)结合使用,以确定帧的实际播放时间。

  • repeat_pict 值的含义

    • 0:表示当前帧不需要重复。
    • 1:表示当前帧需要重复一次(即播放两次)。
    • 2:表示当前帧需要重复两次(即播放三次)。
    • 以此类推。

第三点:AVFrame 结构体的 best_effort_timestamp 变量含义

best_effort_timestamp 是一个 int64_t 类型的变量,表示当前帧的最佳努力 PTS(显示时间戳,Presentation Time Stamp

与 PTS 和 DTS 的关系

  • PTS(Presentation Time Stamp):表示帧应该在播放时显示的时间。它是帧的实际显示时间。
  • DTS(Decoding Time Stamp):表示帧应该在解码时被处理的时间。它通常用于确定解码顺序。
  • best_effort_timestamp:在无法获取精确 PTS 的情况下,使用 best_effort_timestamp 作为最佳努力的时间戳,以尽量保持播放的顺序和同步。

注:如果不理解“最佳努力 PTS”就认为是“最佳时刻视频帧的显示时间戳”,让视频帧在解码时间戳和显示时间戳之间是保持同步的。

最后一点:每秒钟总的字节数

  • 采样率(sample_ratio):44100 Hz (samples/second)
  • 位深度(音频格式):16 位(即每个样本 2 字节)(bytes/sample)
  • 声道数(channels):2(立体声,1-单声道)(channels)

那么每秒钟的字节数计算如下:

每秒字节数=44100 * 2  * 2  = 176400 bytes / second


特别的“加权平均”

   注:我这里所说的“特别”是表达和我们之前所想的的求解加权平均的方式不同,没有其他的意思。

avgx = \frac{\sum {​{w_{i}{x_{i}}}}}{\sum w_{i}}  标准的加权平均求解方式  -求解x的平均值                                     (1)

根据教程的作者所说的,采用几何级数作为权重实现加权平均。

\lim_{n\rightarrow\ \propto}\sum (C^{0} + C^{1} + C^{2} + ... + C^{n}) = \frac{1}{1 - C} (|C| < 1)                              (2)

作者的描述是当n趋近于无穷大的时候,级数的结果为1 / (1 - C),其实关于上面公式2是怎么推来的,大家直接看高数或者网上搜索推理过程即可知道(提醒:从等比数列推来)。

\lim_{n\rightarrow\ \propto}\sum (C^{0}+ C^{1} + C^{2} + ... + C^{n}) = \frac{1}{1 - C} (|C| < 1) \\\\ avgX = C^{0} * x_{0} + C^{1} * x_{1} + ... + C^{n} * x_{n} = \frac{\sum C^{i} * x_{i}}{\sum C^{i}} = \frac{total}{1 / (1 - C)} = total * (1 - C)

注:这也就是代码中所对应的大致公式。

video_refresh_timer函数实现大致含义

get_audio_clock(VideoState * videoState)函数实现含义 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值