關於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

相關文章
相關標籤/搜索