[SimplePlayer] 7. 多线程处理
在前面的文章中,我们分别实现了视频图像解码、播放,音频解码、播放,现在则需要把这些功能组合起来。总体上来说,整个程序的功能可以分为两条线路:视频以及音频,两条线之间除了后续的同步操作之外基本没有任何关联。而在线路当中,各个模块之间并没有太紧密的耦合,只要上游模块提供了原料,下游模块就可以执行处理。因此,我们可以为各个模块建立独立的线程,这会使得程序结构更加清晰,并且在多核的平台下能更好地发挥平台性能。
所需的各线程及其功能:
- 主线程,除了进行各个模块的初始化之外,所要承担的任务是整个程序的事件处理,如关闭程序。
- 视频线程,进行视频图像解码,把video packet解码成frame。
- 音频线程,进行音频解码,把audio packet解码成frame。
- 读取线程,读取视频文件,demux后分别向视频线程与音频线程提供packet。
- 视频显示线程,进行视频图像显示,这部分并非繁重的任务,因此可以被合并到主线程当中。
- 音频播放线程,进行音频播放,通过SDL的callback实现(SDL会自动为音频输出创建一个线程)。
上述各个线程的处理效率各不相同。例如,读取线程仅需要从磁盘读取视频文件,然后进行复杂度较简单的demux,也就是说很短时间只能就能输出一帧的packet;而视频解码线程则由于其中流程繁杂,需要大量运算,因此通常需要相对较长的时间才能解码出一帧图像。对于这种上下游模块数据处理的效率差异,如果不采取一些应对措施,则会导致线程的频繁切换(每demux、decode、play一帧都需要进行一次线程切换,而线程的上下文切换也会消耗cpu资源),从而降低程序的处理效率。
在上下游线程之间添加一个缓冲就可以很好地改善这一问题。为上下游线程之间添加缓冲后,只要缓冲区还有空间,那么上游的线程就可以继续执行下一帧的处理,并把处理结果输出到该缓冲区内。
本文所用到的缓冲区如下:
- video packet list,存储read thread所输出的video packet。
- audio packet list,存储read thread所输出的audio packet。
- frame queue,存储video thread解码后所输出的视频帧。
- audio ring buffer,存储audio thread解码后所输出的音频数据。

Packet List
Packet list作为demuxer与decoder之间的缓冲区,目的是实现一个packet队列,该队列中的packet先进先出。FFmpeg提供了一个AVPacketList结构体,我们可以用这个结构体来进行队列的构建。
typedef struct AVPacketList {
AVPacket pkt;
struct AVPacketList *next;
} AVPacketList;
AVPacketList当作链表的节点,其中pkt用于维护packet,next用于连接相邻的节点。由于是用链表来实现队列,因此需要一个指向链表头的指针first_pkt以及一个指向链表尾的指针last_pkt。当需要把packet入队列时,把packet加入到链表末尾,而当要取出packet时,则从链表头部取出。

Frame Queue
Video Thread解码出来的视频帧会被缓存在Frame Queue中,显示模块在需要进行图像显示的时候从Frame Queue中取出图像进行显示。由于通常frame所占用的空间都比较大,因此缓存的frame的数量会有所限制,那么我们就可以用一个指向frame的指针数组来进行队列的维护。

为了实现队列的效果,需要分别有两个数字指示队列的头与尾,其中read_index标记的是队列头部,write_index指示的是队列尾部。当要从队列中取出frame时,去获取read_index的数组元素所指示的frame,然后read_index++;当要把frame加入到队列中时,令write_index的数组元素指向需要加入队列的frame,然后write_index++。
Ring Buffer
在前面的章节中我们手动把fltp格式的音频转换为s16的音频,s16的音频格式是把左右声道的音频样本交叉排列的串行数据,ring buffer是一种比较时候用于存储串行数据的数据结构。
Ring buffer,环形缓冲区,原型为一块连续的缓冲区,通过运用指向数据头部(rIndex)以及数据尾部(wIndex)的指针来维护数据的存取,当数据尾部的指针到达缓冲区末尾,就会把尾部指针指向缓冲区开头,同理数据头部的指针也会进行循环移动,如此实现环形缓冲区。

当需要存储数据时,首先需要保证有足够的空间来进行存储,然后从wIndex处开始写入数据,并根据写入数据的长度更新wIndex;当要读取数据时,从rIndex处读取数据,并根据读取的数据长度更新rIndex。
线程安全
对于上面描述的3种队列,为了线程安全(使得对队列的操作能在多线程上安全使用),我们需要保证出/入队列的操作为原子操作。实现则可以采用SDL提供的mutex。
中途退出
视频播放可以进行中途退出的操作,那么我们也有必要提供能在中途终止队列的功能。我们这里所说的终止队列,就是使得再次调用出/入队列的函数时,会返回-1,以表示队列已被终止。我们可以通过设置一个变量abort_request来进行判断,当abort_request为1时队列终止,为0则队列正常运行。
队列的abort函数需要实现:
- 把abort_request设置为1。
- 由于出/入队列函数可能此时会处于等待状态(如:此时已经满队列,入队列函数在等待队列腾出空间),因此abort函数还需要解除出/入队列函数的等待状态。
那么在实现出/入队列的函数时
- 在函数的开头加入对变量abort_request的判断来决定是否返回-1。
- 由于出/入队列函数可能此时会处于等待状态(如:此时已经满队列,入队列函数在等待队列腾出空间),那么在abort函数解除当前函数的等待状态后,应该再次进行abort_request变量的判断,如果abort_request为1则应该直接返回-1,而不应该继续执行后续操作。
[SimplePlayer] 7. 多线程处理的更多相关文章
- 《C#本质论》读书笔记(18)多线程处理
.NET Framework 4.0 看(本质论第3版) .NET Framework 4.5 看(本质论第4版) .NET 4.0为多线程引入了两组新API:TPL(Task Parallel Li ...
- QT实现HTTP JSON高效多线程处理服务器
QT实现HTTP JSON高效多线程处理服务器 Legahero QQ:1395449850 现在一个平台级的系统光靠web打天下是不太现实的了,至少包含APP和web两部分,在早期APP直接访问we ...
- 由一篇文章引发的思考——多线程处理大数组
今天领导给我们发了一篇文章文章,让我们学习一下. 文章链接:TAM - Threaded Array Manipulator 这是codeproject上的一篇文章,花了一番时间阅读了一下.文章主要是 ...
- 多线程处理sql server2008出现Transaction (Process ID) was deadlocked on lock resources with another process and has been chose问题
多线程处理sql server2008某个表中的数据时,在Update记录的时候出现了[Transaction (Process ID 146) was deadlocked on lock reso ...
- WPF 多线程处理(1)
WPF 多线程处理(1) WPF 多线程处理(2) WPF 多线程处理(3) WPF 多线程处理(4) WPF 多线程处理(5) WPF 多线程处理(6) 废话不多说,先上图: 多线程处理数据后在th ...
- WPF 多线程处理(5)
WPF 多线程处理(1) WPF 多线程处理(2) WPF 多线程处理(3) WPF 多线程处理(4) WPF 多线程处理(5) WPF 多线程处理(6) 项目的目录: 以下是FileStroage的 ...
- WPF 多线程处理(4)
WPF 多线程处理(1) WPF 多线程处理(2) WPF 多线程处理(3) WPF 多线程处理(4) WPF 多线程处理(5) WPF 多线程处理(6) 开始一个线程处理读取的文件并且更新到list ...
- WPF 多线程处理(6)
WPF 多线程处理(1) WPF 多线程处理(2) WPF 多线程处理(3) WPF 多线程处理(4) WPF 多线程处理(5) WPF 多线程处理(6) 以下是子窗体的UI: <Window ...
- WPF 多线程处理(3)
WPF 多线程处理(1) WPF 多线程处理(2) WPF 多线程处理(3) WPF 多线程处理(4) WPF 多线程处理(5) WPF 多线程处理(6) 首先我们需要几个属性来保存取得的数据,因为在 ...
随机推荐
- JavaScript的自定义属性(事件内获得事件外的变量值)
写轮播图点击下方圆点banBtnLi[i],切换到第i个图片banBtnLi是按钮集合,假设banBtnLi.length是4banImhLi是装图片的li,自然banImgLi.length也是4点 ...
- vue的项目结构记录
vue的项目结构 不知道大家有没这样的情况,面对刚配置好的脚手架,创建的文件不知道该放哪个文件下,导致后面开发一些文件不好找,不利于维护. 接下来我说说我项目中的一些文件: 首先是components ...
- Dynamics 365中的非交互式账号(Non-interactive User)介绍
摘要: 本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复272或者20180616可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyon ...
- 企业信息化-Excel快速生成系统
企业信息化,主要是指对企业生产运营过程所形成的信息数字化,最终形成了数字资产.大型企业为了节约成本,提高协同工作效率,都会定制ERP.办公OA.流程审批等系统做信息化支撑.但是中小企业精力投入到生成中 ...
- 案例解析|政府信息化的BI建设应用 .
一.行业背景 某建设厅综合监管信息化平台,是政企业务协同的平台之一,同时兼具协作.门户.办公应用集成.用户权限管理等多项功能.在此要求基础上,选择中间件基础技术平台,可以在最大程度满足平台功能需求的前 ...
- UDK命令
UDK命令行参数与控制台命令都是大小写不敏感的 命令行 udn中文 udn英文 全词大小写匹配,正则表达式,在c++代码中搜索减号开头的命令行参数(如:-BENCHMARK.-onethread等 ...
- Ubuntu下面MySQL的参数文件my.cnf浅析
前几天刚接手一个MySQL数据,操作系统为Ubuntu 16.04.5 LTS, 数据库版本为5.7.23-0ubuntu0.16.04.1(APT方式安装的MySQL).这个操作系统下的MySQL ...
- 关于tomcat 配置时一闪而过的问题
TOMCAT JAVA_HOME or JRE_HOME environment variable is not defined correctly 按照教程已经安装了JDK并设置好了JAVA_HOM ...
- phpstorm ftp主动模式能连接上,但获取不到目录;
前面一直都在使用ST做开发,但是也想试试传说中的phpstorm神器.一切都弄好了,想使用它的远程开发功能,省去我本地开发然后再ftp上传做法. 但是却遇到了这个问题,困扰了我三四天!!!我各种百度都 ...
- eclipse去除对js文件的检测