关于ffmpeg(libav)解码视频最后丢帧的问题

    其实最初不是为了解决这个问题而来的,是Peter兄给个人提示解决另外一个问题却让我误打误撞解决了另一个问题以后也把这个隐藏了好久的bug找到(以前老是有一些特别短的视频产生不知所措还觉得是视频素材自己有问题呢),今天真是收获颇丰啊,对libav的理解更深。架构

    一直以来个人程序架构是让读一帧av_read_frame,而后去尝试根据packet的type类型分别去decode video或者audio,然而这样老是在视频的结尾会有许多帧丢失的问题,我找过avplay代码中彷佛没找到我想象中的那种读packet完毕后特殊处理的代码。ide

一下log是我设置将其avcodec_decode_video2函数调用先后输出的pts、dts值(注:Frame:为decode以后,测试视频为纯视频无音频的文件),从图中能够看出来,开头为了顺序正确,libav暂存了9个packet,读packet完毕后,我固然不能就直接无论了,仍是要将libav暂存的那些帧取出来才行。函数


    综上几点我认为是在调用avcodec_decode_video2函数的时候,函数内会去发现这个packet尚不足以解码下一帧的时候就会暂存packet在内部队列中(我本身给他们取的名字,望能理解),这样问题就来了,等到整个视频文件都读取完了,剩下libav内部存的packet还有一大堆没处理,个人架构又是要求函数直接向视频索取一帧,所以就须要在读新packet完毕后还 单独调用decode video函数或者decode audio函数来取出剩余的AVFrame。oop

为了这些bug我破例无耻的使用了goto语句:测试

首先定义了类成员变量,初始化都为false:spa

bool            _no_packet;   //表明是否还有packet
bool            _end_video_frame;   //表明是否video中的队列取出完毕
bool            _end_audio_frame;   //表明是否audio中的队列取出完毕
AVPacket packet = {0};
while(true)
{
        auto auto_releaser = std::shared_ptr<AVPacket>(&packet, [](AVPacket* p) { av_free_packet(p); });
        int ret = av_read_frame(_context.get(), &packet);
        if ( ret != 0)
        {
            _no_packet = true;
            if(!_end_video_frame)
                goto loop_end_video;
            if(!_end_audio_frame)
                goto loop_end_audio;
            if(_end_video_frame && _end_audio_frame)
                return false;
        }
        if(packet.stream_index == vstream_index)
        {
loop_end_video:
            int frame_finished = true;
            if(avcodec_decode_video2(_vcodec, _decoded_frame, &frame_finished, &packet) < 0)
             {
// 。。。。do something。。。
             }
             if(frame_finished)
             {
//。。。。do something。。。
             }
             else
             {
                if(_no_packet)
                    _end_video_frame = true;
            }
       }
       else if (packet.stream_index == astream_index && (type & MEDIA_AUDIO) != 0)
        {
            loop_end_audio:
            int frame_finished;
            if (avcodec_decode_audio4(_acodec, _decoded_frame, &frame_finished, &packet) < 0)
             {
// 。。。。do something。。。
             }
             if(frame_finished)
             {
//。。。。do something。。。
             }
             else
             {
                if(_no_packet)
                    _end_audio_frame = true;
            }
       }
}

有了这些跳转,就能完美的在read_frame完以后还继续decode为我所用。但愿能帮助到与我遇到了相同问题的人。.net

再次感谢Peter~code

相关文章
相关标签/搜索