天天看點

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

用FFMPEG SDK進行視訊轉碼壓縮的時候,轉碼成功後去看視訊的内容,發現音視訊是不同步的。這個的确是一個惱火的事情。我在用FFMPEG SDK做h264格式的FLV檔案編碼Filter的時候就碰到了這個問題。

        經過研究發現,FFMPEG SDK寫入視訊的時候有兩個地方用來控制寫入的時間戳,一個是AvPacket, 一個是AvFrame。 在調用avcodec_encode_video的時候需要傳入AvFrame的對象指針,也就是傳入一幀未壓縮的視訊進行壓縮處理,AvFrame包含一個pts的參數,這個參數就是目前幀将來在還原播放的時候的時間戳。而AvPacket裡面也有pts,還有dts。說起這個就必須要說明一下I,P,B三種視訊壓縮幀。I幀就是關鍵幀,不依賴于其他視訊幀,P幀是向前預測的幀,隻依賴于前面的視訊幀,而B幀是雙向預測視訊幀,依賴于前後視訊幀。由于B幀的存在,因為它是雙向的,必須知道前面的視訊幀和後面的視訊幀的詳細内容後,才能知道本B幀最終該呈現什麼圖像。而pts和dts兩個參數就是用來控制視訊幀的顯示和解碼的順序。

      pts就是幀顯示的順序。

      dts就是幀被讀取進行解碼的順序。

     如果沒有B幀存在,dts和pts是相同的。反之,則是不相同的。關于這個的詳細介紹可以參考一下mpeg的原理。

再說說AvPacket中包含的pts和dts兩個到底該設定什麼值?

pts和dts需要設定的就是視訊幀解碼和顯示的順序。每增加一幀就加一,并不是播放視訊的時間戳。

但是實踐證明經過rmvb解碼的視訊有時候并不是固定幀率的,而是變幀率的,這樣,如果每壓縮一幀,pts和dts加一的方案為導緻音視訊不同步。

那怎麼來解決音視訊同步的問題呢?

請看如下代碼段。

lTimeStamp 是通過directshow 擷取的目前的視訊幀的時間戳。

m_llframe_index為目前已經經過壓縮處理的幀的數量。

首先av_rescale計算得到目前壓縮處理已經需要處理什麼時間戳的視訊幀,如果該時間戳尚未到達directshow目前提供的視訊幀的時間戳,則将該幀丢棄掉。

否則進行壓縮操作。并設定AVPacket的pts和dts。這裡假設B幀不存在。

因為在将來播放的時候視訊以我們設定的固定播放幀率進行播放,是以需要根據設定的播放幀率計算得到的視訊幀時間戳和directshow提供的目前視訊幀的時間戳進行比較,設定是否需要進行實施延緩播放的政策。如果需要延緩播放,則将pts增加步長2,否則以普通速度播放,則設定為1.dts與之相同。

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

__int64 x = av_rescale(m_llframe_index,AV_TIME_BASE*(int64_t)c->time_base.num,c->time_base.den);

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法
用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

if( x > lTimeStamp )

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

{

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

return TRUE;

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

}

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

m_pVideoFrame2->pts = lTimeStamp;

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

m_pVideoFrame2->pict_type = 0;

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法
用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

int out_size = avcodec_encode_video( c, m_pvideo_outbuf, video_outbuf_size, m_pVideoFrame2 );

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法
用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

if (out_size > 0)

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法
用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

AVPacket pkt;

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

av_init_packet(&pkt);

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法
用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法
用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法
用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

   pkt.pts = pkt.dts = m_llframe_index;

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

   pkt.duration = 0;

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法
用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

else

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法
用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

   pkt.duration = (lTimeStamp - x)*c->time_base.den/1000000 + 1;

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

   pkt.pts = m_llframe_index;

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

   pkt.dts = pkt.pts;

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

   m_llframe_index += pkt.duration;

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法
用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法
用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

//pkt.pts = lTimeStamp * (__int64)frame_rate.den / 1000;

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

if( c->coded_frame && c->coded_frame->key_frame )

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法
用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

    pkt.flags |= PKT_FLAG_KEY;

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法
用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法
用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

pkt.stream_index= m_pVideoStream->index;

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

pkt.data= m_pvideo_outbuf;

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

pkt.size= out_size;

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法
用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法
用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

ret = av_interleaved_write_frame( m_pAvFormatContext, &pkt );

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法
用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法
用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法
用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法

ret = 0;

用FFMPEG SDK進行視訊轉碼壓縮時解決音視訊不同步問題的方法