前言
最近在学习如何使用ffmpeg解析视频,在网上查找的办法大同小异,直接说解码函数avcodec_send_packet和avcodec_receive_frame,这两个是一起的,必须同时出现,我是通过子线程读取视频文件,不停地将读取出来的包放入videoQueue或audioQueue中,再利用SDL创建videoThread来不断从video队列里拿出packet送入解码器中,即利用avcodec_send_packet将包送入解码器中,利用avcodec_receive_frame将ffmpeg解析出来的Frame取出来,但通过进度条或直接打印解码出来的Frame->pts来看当前帧的时间,总会在最后少那么几帧,很郁闷
一、帧丢失原因
解码器内部有缓存队列,开始时会先存几帧数据,再开始输出有效Frame,所以到最后,解码器里还有几帧数据,根据视频大小不同,解码器里还剩余的packet数量也不太一样,所以到最后还有几帧没有输出,出现帧丢失现象。
二、解决方法
第一想法就是来网上搜如何解决帧丢失,但很少有,有也是没源代码,特别是ffmpeg的avcodec_send_packet和avcodec_receive_frame的帧丢失解决办法更少,于是就转向官方文档,终于找到一点头绪,如下图是在avcode.h中找到的,也是这两函数声明的头文件
上面说明出于需要或者提升性能,在最后编解码器还可能缓存了几帧数据,需要刷新编解码器,还提供了思路
1.送入空包进avcodec_send_packet
如上图官方文档提供的思路,需要送入空包进解码器,代码如下
packet->data = nullptr;
packet->size = 0;
avcodec_send_packet(pVideoCodecCtx, packet);
2.调用avcodec_receive_frame
送入空包后,再调用avcodec_receive_frame将解码出来的frame取出来,直到avcodec_receive_frame返回AVERROR_EOF才结束,代码如下
/**队列里取数据**/
if (packet_queue_get(&pVideosState->videoQueue, packet, 0) <= 0)
{
if (pVideosState->readFinished)
{
/**队列里面没有数据了且读取完毕了**/
while(1)
{
packet->data = nullptr;
packet->size = 0;
avcodec_send_packet(pVideoCodecCtx, packet);
ret = avcodec_receive_frame(pVideoCodecCtx, pFrame);
if(ret == AVERROR_EOF)
break;
//进行你所需的一系列操作
}
//这个循环跳出后就全部取出解码器缓存的数据了
}