標籤:style blog http color io 使用 ar 檔案 2014
其實最初不是為瞭解決這個問題而來的,是Peter兄給我的提示解決另一個問題卻讓我誤打誤撞解決了另外一個問題之後也把這個隱藏了很久的bug找到(之前總是有一些特別短的視頻產生不知所措還以為是視頻素材本身有問題呢),今天真是收穫頗豐啊,對libav的理解更深。
一直以來我的程式架構是讓讀一幀av_read_frame,然後去嘗試根據packet的type類型分別去decode video或者audio,然而這樣總是在視頻的結尾會有許多幀丟失的問題,我找過avplay代碼中似乎沒找到我想象中的那種讀packet完畢後特殊處理的代碼。
一下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。
為了這些bug我破例無恥的使用了goto語句:
首先定義了類成員變數,初始化都為false:
bool _no_packet; //代表是否還有packetbool _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為我所用。希望能協助到與我遇到了相同問題的人。
再次感謝Peter~
關於ffmpeg(libav)解碼視頻最後丟幀的問題