关于FFMPEG调用avcodec库解码H264数据失败的问题探究及解决方案

2 篇文章 0 订阅
2 篇文章 0 订阅

如题,最近公司遇到了一个关于流媒体解码的问题:

平板电脑(安卓5.0以上)将本身的屏幕数据(H264)发送至PC端,PC端将接收到的数据转发给一个班级(60人)的所有平板电脑上,让电脑与所有的平板电脑都同步显示播放发送端平板的桌面,简称“屏幕广播”。

遇到的问题是,在特定的PC机上,会出现PC播放数据黑屏,但是所有的平板(60个)都正常播放屏幕数据。

先粗略分析一下,PC使用的数据与平板使用的数据是完全一样的,因为平板的数据是PC发送出去的,因此先判断数据层面上没有问题,那就只能是PC端的代码有问题。

然后我开始查PC端的代码,PC的播放客户端是用c#开发的,核心是用C++调用了FFMPEG中的avcodec工具类,然后封装成了DLL给c#使用,工具类中主要使用其中的av_parser_parse2函数来获取流媒体数据中的图片,然后传递给c#写的UI层用于显示。

代码逻辑很简单,那就着手找是哪里出的问题吧:

首先先看一下c#写的UI层有没有问题,打了断点,跟踪了数据,发现问题出现在c#在调用中间层的工具类时,一直都没有接收到图片数据,所以就一直处于黑屏状态,那也就是说,问题精确到了工具类里面。

继续查找问题,既然没有返回图片数据,那就看看解码器在解码数据的时候是不是出现了什么问题,我先跟踪到解码出图片的代码:

ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, &packet);

在出现黑屏的现象时,len的值一直都是小于0的,也就是说,是解码器解码失败,没有解码出来图片导致的问题。

avcodec是FFMPEG中的一个工具类,那就下载FFMPEG源码,看看为啥出问题。下载下来之后,先看代码里面最前面的注释信息,看看作者有没有需要我们特别注意的地方,详细的我另写了篇博客翻译了下,大家可以参考下FFMPEG中关于avcodec库中相关注释的简单翻译

从注释中看,作者用avcodec_send_packet与avcodec_receive_frame替代了公司使用的av_parser_parse2接口,使用新的解码接口需要简单修改下解码的逻辑,我第一个想法就是,会不会是使用的旧接口才出现的问题?所以我将旧的av_parser_parse2接口更换成了新的接口,并简单修改了下解码的逻辑,让其符合新的解码接口调用顺序,然后赶紧测试了一下:失败

问题依旧没有解决,调用新的接口同样面临着解码器无法解码出图像数据,这下就很尴尬了,继续找问题原因呗。

再思考一下,既然平板使用同样的数据是可以解码出图像的,那么流媒体数据是没问题的,问题是解码过程,没招了,看源码吧。期间蛋疼不多说,直接说结果吧,内部函数在解码的过程中,是有一个小问题的,就是关于H264中I帧、P帧、B帧解码的问题,如果不知道这三个帧,只要记住I帧是关键就可以。

这次出问题的地方也就是这三个帧导致的,原因就是:PC端接收到平板端发送的流媒体数据出现了没有接收到I帧便开始解B帧与P帧的情况,导致收到的B帧与P帧没有参考的I帧,因此解码器的解码接口无法正确解码出数据,一直处于问题状态。

既然问题找到了,那就改呗,如何修改很简单,就是在解码器首次开始解码前,只有等到I帧出现,才开始正式解码,I帧出现前,就不进行解码,所以必须要保证发送端必须要多次发送I帧:

if (first){
    switch (pCodecParserCtx->pict_type) {
    case AV_PICTURE_TYPE_I: first = false; break;
    case AV_PICTURE_TYPE_P: printf("Type:P\t"); continue;
    case AV_PICTURE_TYPE_B: printf("Type:B\t"); continue;
    default: printf("Type:Other\t"); continue;
    }
}

上面这段代码就是写下解码循环里面,直到收到I帧才开始使用解码器进行数据的解码。修改完之后进行测试,问题解决(话说安卓系统内部的解码器还多带了一个解码纠错的功能,汗……)。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值