前面我们介绍了ffplay的调试环境集成、ffplay总体架构、ffplay的读取线程等相关内容,今天介绍下ffplay解码线程工作流程。
因为视频解码和音频解码的过程大概一致,因此本文主要介绍视频的解码线程内容,字幕的解码忽略...
我们还是从这张图开始:
ffplay总体架构
图导出的可能有点模糊,再加上上传图床后不知道有没有更加模糊了,想要高清大图的可以后台留言,加v信索取。
从图中可以看出,解码线程的主要工作内容是将资源包从待解码队列中取出,然后送进解码器,最后将解码出的数据帧放入帧队列中,等待SDL获取播放。
解码过程
解码线程是在打开流的时候创建的,也就是在函数创建的,视频解码线程的工作函数是。
以下是函数的内容,可以看到去掉filter的相关处理后,这个函数是非常精简的,就是在for循环中通过函数获取到一帧图像数据后,调整数据帧的pts,然后将数据帧放入帧队列中:
我们来看看函数做了什么:
这个函数内部又调用了函数获取解码帧,然后根据同步时钟判断是否需要进行丢帧处理。那么我们再看看函数
原来函数才是真正的解码核心,可以看到视频、音频和字幕都是通过调用这个函数进行解码的。这个函数的主要工作内容就是在for循环中不断取出数据包然后调用FFmpeg的API进行解码。
1、首先判断待解码队列的播放序列和解码器的播放序列是否一致:
如果一致则通过FFmpeg函数获取数据帧,然后更新数据帧pts。
在这里有个疑问,我们之前不是说解码过程是先然后是n个获取数据帧吗?这里怎么直接就先调用获取数据帧了呢?
这是因为函数是一个不断被调用的for循环,优先调用可以防止解码器内部缓冲过多数据而导致解码包无法送入解码器的现象出现。
2、待解码队列中是否有数据,如果没有则唤醒读取线程
3、判断是否有送入解码器失败的包,如果有则优先处理
这包是在调用返回了时所缓存下来的。
4、如果没有缓存的包则从队列中获取packet_queue_get
5、将包送进解码器,如果返回的值是,则将包缓存起来,下次再优先送入解码器处理
同时这里也可以解析了在第一步中为什么优先调用的原因,就是为了数据可以顺利地送进解码器。
领取专属 10元无门槛券
私享最新 技术干货