学习ffmpeg时候遇到的一个死锁问题,六个队列互相等待造成的悲剧!

学习ffmpeg时候遇到的一个死锁问题,六个队列互相等待造成的悲剧!

前情提要:

​ 需求是实现一个修改流媒体文件封装格式的程序,然后中间还要穿插过滤对原视频进行一系列修改,旋转,裁剪啥的,最后重新将音频流和视频流封装成指定格式的文件。计划是,创建六个线程,每个线程都各司其职,然后中间的编解码数据使用队列进行传递。像这个样子。。。。

首先俺来解释一下,这六个线程和这六个队列的作用,队列名字起的太长了,这里就用首字母来代替啦。

  • Demuxer: 解复用线程,用于接收流媒体文件如mp4, flv…, 将流媒体文件的音频流和视频流分别解码出Packet,然后将Packet送入DVP(DemuxerVideoPacketQueue)队列, 便于后面的模块可以从这个队列中获取Packet数据。
  • VideoDecoder: 视频解码线程,这个线程只管两件事,从预先设置好的Packet队列(这里就是指的DVP)中读取Packet数据,然后将其解码成Frame,再送到预先设定好的Frame队列中(这里指的是VF)。
  • VideoEncoder: 视频编码线程,这个线程也只管两件事,和上面说的VideoDecoder功能正好相反,从预先设定好的Frame队列(VF)中获取Frame,再将Frame按照指定编码格式编码成Packet,再传给预先设定好的Packet队列(MVP)。
  • AudioDecoder: 音频解码线程,和上面的VideoDecoder线程一样,一个是解码视频一个是解码音频。
  • AudioEncoder: 音频编码线程。
  • Muxer: 复用线程,作用是将得到的音频流和视频流的Packet按照一定的顺序,封装进预先设定的流媒体文件中。

然后说一下这个队列的特性, 这里所有的队列都是阻塞队列,获取的时候如果队列中没东西可以拿了,那就会阻塞,同样新增的时候,如果达到了实现设定的最大长度的情况下,也会阻塞。

问题分析:

然后Demuxer中,由于解复用的速度较快,不需要等待任何人,因此在这里对队列设定了一个输出Packet队列长度不能超过设定长度的机制,每次都会先检查,当前队列长度是多少,如果>=设定的最大长度,那就会睡眠一段时间(问题就出现在这里,由于Demuxer管理两个队列,因此任何一个队列长度大于等于最大长度都会睡眠)。

然后Muxer中,也是一样的,Muxer会从实现设定好的音频队列和视频队列中取Packet,如果音频或视频的任意一个Packet队列没有数据的话也会阻塞。

然后问题来了,如下图。

从Demuxer逆时针方向看哇。。。。

从上面可以看到,六个队列互相等待,谁都动不了,分析了半天,总结出原因就是因为,Demuxer和Muxer设计的不合理,例如Muxer模块,为什么视频数据获取到了不能直接消费还需要等待音频数据呢,Demuxer也一样,视频队列是满了,但是音频队列没有满啊,可以先执行插入音频数据的逻辑,再回来处理视频的。

Demuxer和Muxer都分别等待了他们本不该等待的逻辑,导致了最后的死锁。

解决方案:

抓耳挠腮了两个多小时,从打印日志分析,到得出结论之后,下面就是解决了。既然原因是Demuxer和Muxer都等待了他们原本不该等待的逻辑,那就修改这部分代码就好了。

Demuxer将原先逻辑修改为,只有当两个队列同时满了,才睡眠一段时间, 如下:

Muxer将原本的peek阻塞等待修改为非阻塞等待,如下:

peekWithoutBlock方法不同于peek方法如果没有数据可以取,那就返回null值,后续的逻辑会根据null值进行特殊处理。

最后阻塞问题解决,舒服啦~

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值