FFmpeg中的多线程解码

1、共享变量的互斥


互斥锁(mutex-lock)是一种信号量,用来防止两个线程在同一时刻访问相同的共享资源,它有锁定状态和非锁定状态。

在任意时刻,一个线程要想存取共享数据,线程必须首先获得mutex-lock,当此线程释放此共享数据的时候必须对mutex-lock解锁,在一个任意的时间内,只有一个线程能锁定互斥锁,通过函数pthread_mutex_lock上锁,通过函数pthread_mutex_unlock解锁。

2、同步条件变量


条件变量用来提供另一种线程同步的方法,其基于实际的变量值来实现线程的同步操作,设置了条件变量的情况下,线程就不需要通过不停的轮询来查询条件是否满足,也不需要不停的忙等,从而能够节省很多系统资源。

一个条件变量总是和一个mutex-lock对应,系统通过pthread_cond_await函数来阻塞调用的线程,一直到条件变量得到满足。

当这个线程阻塞的时候对应的mutex-lock会自动解锁,但当该线程运行的时候,其对应的mutex-lock会被加锁。

使用函数pthread_cond_signal来唤醒等待在条件变量的另一个线程,当用来唤醒多个处于阻塞状态线程时通过pthread_cond_broadcast函数来完成。

3、ffmpeg实现多线程方案:


Thread List,线程列表,线程列表中的每一项都映射一个解码线程。

主线程会从线程列表中按照序号由小到大(循环)提取解码线程,并把解码任务提交到该解码线程。

同时主线程在提交完解码任务后也会从线程列表中按照序号由小到大(循环)提取解码线程,并尝试从该解码线程获取解码完成的帧。

M,主线程,主要目的有两个:

  • 向解码线程提交解码任务。FFmpeg中是以packet为单位进行解码任务的提交的,按照前一小节的描述,FFmpeg就是以frame为单位进行解码任务的提交的。

  • 从解码线程获取解码所得的帧并进行返回。不过在第一轮进行任务提交的时候是不会去获取帧,在第一轮任务提交完成后,此时所有解码线程都已经开始进行了解码作业,那么主线程就可以开始等待第一个线程解码完成,然后尝试去获得解码完成的帧。这里的“尝试”,是因为就像单线程解码时那样,并不一定是每次调用解码API都会返回一帧的。由于h264编码的视频中常常包含B帧,这会使得码流的解码顺序并非帧的播放顺序,但是解码API必须按照帧的播放顺序进行返回,因此在进行帧的返回时会进行相应的调整。接下来每次向一个线程提交一个解码任务后,都需要等待下一个线程空闲并尝试返回帧。

  • T,解码线程,接收解码任务并进行解码。解码线程是以frame为单位进行处理的。解码线程解码主线程所提交的packet,执行与单线程时一样的解码作业。

ffmpeg中的多线程解码主要分为片 Slice级别的多线程解码 和 帧Frame级别的多线程解码:

Slice级的解码效率比Frame级的解码效率要低,故,本文先考虑Frame级的多线程解码。

4、Frame级的多线程解码


通过查看ffmpeg源码,了解到Frame级的多线程解码流程大致如下:

其中右侧的frame_worker_thread为解码线程,在open解码器时就已经创建,随后阻塞在pthread_cond_wait(&p->input_cond, &p->mutex)函数。等待被主线程唤醒。

当主线程运行到ff_thread_decode_frame函数时,会调用submit_packet函数,这个函数的目的就是将packet包交给解码线程。

submit_packet函数会调用pthread_cond_signal(&p->input_cond)函数,这个函数就是为唤醒刚才阻塞的解码线程。

当主线程唤醒解码线程后,其pthread_cond_wait(&p->output_cond, &p->progress_mutex)函数会进入阻塞状态,等待解码线程唤醒。

  1. 如果Codec未实现update_thread_context()和线程安全的get_buffer(),则必须在解码完成后才能将状态转换为STATUS_SETUP_FINISHED,意味着下一个线程只能在当前线程解码完成后才能开始解码。当解码线程解码完成后,会用pthread_cond_signal(&p->output_cond)将主线程唤醒,其中会通过回调函数将解码线程解码出来的frame获取,从而输出。

\2. 如果Codec实现update_thread_context()和线程安全的get_buffer(),线程状态可以在解码开始之前转换为STATUS_SETUP_FINISHED,这样,主线程就能够被唤醒,就能够去读包进行下一个包的解码,因此下一个线程就可能与当前线程并行。

5、Slice级的多线程解码:


ffmpeg的slice级并行只能在帧内并行。

因此,如果在某个视频在编码时,一帧图像分为多个slice进行编码的话,那么在使用ffmpeg解码时调用slice级并行解码就会得到不错的效果。

而在实际应用中,大多数h264编码的视频都是一帧只有一个slice,对于这种视频,就算采用了slice级并行,也只有一个线程在进行解码作业。

如果一帧,即一个packet分为几个slice时,会先把这一帧前面的slice加入队列,到最后一个slice时统一对这一帧的所有slice进行并行解码。其中涉及到的关键要素如下:

Slice Context List,slice的上下文是slice context(FFmpeg中的变量为slice_ctx),如果一帧中有多个slice,那么会把slice上下文组成一个列表。前面所说的入队列操作会对该列表进行填充以供后续解码使用。

M,主线程,如单线程一样的流程,从用户调用解码API一直执行到我们前面所说的入队列,到最后一个slice时会调用一个入口函数启动多线程解码操作。在调用入口函数后,主线程参与的多线程解码过程一共包含三个步骤:

  1. 通过发送启动消息激活其它正在等待的解码线程。

  1. 在启动多线程解码后,主线程也会一同作为其中一个线程进行slice的解码。

  1. 最后等待所有线程完成任务后返回。

T,解码线程,接收到主线程所发起的启动消息后,解码线程会到Slice Context List去提取其中一个slice context(原子操作),然后进行slice解码。

原文 https://zhuanlan.zhihu.com/p/479949826

★文末名片可以免费领取音视频开发学习资料,内容包括(FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)以及音视频学习路线图等等。

见下方!↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
敬告:该系列的课程在抓紧录制更新,敬请大家关注。敬告:本课程项目仅供学习参考,请不要直接商用,概不负责任何法律责任。 该系列的课程涉及:FFmpeg,WebRTC,SRS,Nginx,Darwin,Live555,等。包括:音视频、流媒体、直播、Android、视频监控28181、等。 我将带领大家一起来学习使用FFmpeg开发视频监控项目,并动手操练。具体内容包括: 一、视频监控的架构和流程二、FFmpeg4.3+SDL2+Qt5开发环境的搭建三、FFmpeg的SDK编程回顾总结并操练四、SDL2.0的编程回顾总结并操练五、颜色空间转换RGB和YUV的原理与实战六、Qt5+FFmpeg本地摄像头采集预览实战七、代码封装:摄像头h264/5编码并存储八、Qt5+FFmpeg单路网络摄像头采集预览九、Qt5+FFmpeg单路网络摄像头采集预览录制会看十、onvif与GB/T-28181的简介  音视频与流媒体是一门很复杂的技术,涉及的概念、原理、理论非常多,很多初学者不学 基础理论,而是直接做项目,往往会看到c/c++的代码时一头雾水,不知道代码到底是什么意思,这是为什么呢?   因为没有学习音视频和流媒体的基础理论,就比如学习英语,不学习基本单词,而是天天听英语新闻,总也听不懂。 所以呢,一定要认真学习基础理论,然后再学习播放器、转码器、非编、流媒体直播、视频监控、等等。   梅老师从事音视频与流媒体行业18年;曾在永新视博、科大洋、百度、美国Harris广播事业部等公司就职,经验丰富;曾亲手主导广电直播全套项目,精通h.264/h.265/aac,曾亲自参与百度app上的网页播放器等实战产品。  目前全身心自主创业,主要聚焦音视频+流媒体行业,精通音视频加密、流媒体在线转码快编等热门产品。  

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值