2016-11-08 54 views
-1

我有下面的代碼使用ffmpeg庫(v3.1.4與Autogen包裝)在我的應用程序中呈現RTSP視頻。代碼一般工作得很好。但是,receptical.Write方法不是特別有效。在慢速機器上,我的視頻渲染開始落後。最後我的緩衝區滿了,我開始看到視頻損壞。如何更改下面的代碼以在開始落後時跳過幀?如果有多個幀準備好了,我真的只關心顯示最近可用的幀 - 畢竟這是實況視頻。我相信avcodec_send_packetavcodec_receive_frame方法大約是1比1。跳過幀渲染如果落後

while (!token.IsCancellationRequested) 
{ 
    if (ffmpeg.av_read_frame(pFormatContext, pPacket) != 0) 
    { 
     // end of the stream 
     ffmpeg.av_packet_unref(pPacket); 
     ffmpeg.av_frame_unref(pDecodedFrame); 
     break; 
    } 

    if (pPacket->stream_index != pStream->index || (pPacket->flags & ffmpeg.AV_PKT_FLAG_CORRUPT) > 0) 
    { 
     // this should never happen; we only subscribe to one stream 
     // and I believe corrupt packets are automatically discarded 
     ffmpeg.av_packet_unref(pPacket); 
     ffmpeg.av_frame_unref(pDecodedFrame); 
     continue; 
    } 

    var sendResult = ffmpeg.avcodec_send_packet(pCodecContext, pPacket); 
    if (sendResult < 0) 
    { 
     // one of the possible results is a "buffer full", but I don't think that should happen as long as we call 1-to-1 receive_frame 
     ffmpeg.av_packet_unref(pPacket); 
     ffmpeg.av_frame_unref(pDecodedFrame); 
     _logger.Warn("Failure in FFmpeg avcodec_send_packet: " + sendResult); 
     break; 
    } 

    while (ffmpeg.avcodec_receive_frame(pCodecContext, pDecodedFrame) == 0) 
    { 
     var src = &pDecodedFrame->data0; 
     var dst = &pConvertedFrame->data0; 
     var srcStride = pDecodedFrame->linesize; 
     var dstStride = pConvertedFrame->linesize; 
     ffmpeg.sws_scale(pConvertContext, src, srcStride, 0, height, dst, dstStride); 

     sbyte* convertedFrameAddress = pConvertedFrame->data0; 

     int linesize = dstStride[0]; 

     if (receptical == null) 
     { 
      receptical = writableBitampCreationCallback.Invoke(new DetectedImageDimensions {Width = width, Height = height, Format = DetectedPixelFormat.Bgr24, Linesize = linesize}); 
     } 

     var imageBufferPtr = new IntPtr(convertedFrameAddress); 
     receptical.Write(width, height, imageBufferPtr, linesize); 

     ffmpeg.av_frame_unref(pDecodedFrame); 
    } 
    ffmpeg.av_packet_unref(pPacket); 
} 

回答

0
  1. 取決於你如何同步您的播放,無論是對系統時鐘或以其他方式。您可以檢查傳入數據包的PTS,以及它們是否開始滯後播放,然後轉儲它們。動態實時解碼是cpu密集型的。

  2. 解碼和使用sws_scale會吃掉你的CPU。我不知道'writableBitampCreationCallback'是什麼,但我也猜測這也是cpu時間的消耗。解決這個問題的最好方法是將解碼分成單獨的線程,每個線程用於音頻和視頻以及字幕。這至少會釋放CPU時間。數據包可以發送到每個線程。

  3. 您未展示視頻如何最終呈現。使用類似openGL的東西,您可以使用YUV到RGB着色器直接渲染解碼幀(YUV420),從而無需將幀轉換爲RGB。這節省了很多CPU時間。

下面是一個片段着色器的例子,它從YUV數據中獲取3個紋理。希望這可以幫助你減少很多CPU時間。

precision mediump float; 
uniform sampler2D qt_TextureY; 
uniform sampler2D qt_TextureU; 
uniform sampler2D qt_TextureV; 
varying vec2 qt_TexCoord0; 
void main(void) 
{ 
    float y = texture2D(qt_TextureY, qt_TexCoord0).r; 
    float u = texture2D(qt_TextureU, qt_TexCoord0).r - 0.5; 
    float v = texture2D(qt_TextureV, qt_TexCoord0).r - 0.5; 
    gl_FragColor = vec4(y + 1.403 * v, 
         y - 0.344 * u - 0.714 * v, 
         y + 1.770 * u, 1.0); \ 
} 
+0

你覺得我可以用PTS知道我背後不止一個框架?我不熟悉它。 – Brannon

+0

我實際上只是現在就實施它,因爲我不得不對50i幀進行去隔行掃描。我正在同步到系統時鐘,所以當數據包讀取器達到超出2/50秒的數據包時,我不會將它們添加到解碼器。到現在爲止還挺好。所以是的,它可以做到。我只需要這樣做,這樣玩家就可以使用舊的ARM硬件。使用av_q2d()將PTS值轉換爲double。 – WLGfx

+0

我最終不得不爲「av_read_frame」調用使用單獨的線程(因爲在沒有數據可用時它是阻塞調用)。有了這個功能,每當我得到一個新的關鍵幀時,我都會清除我的數據包隊列。現在對我來說這似乎工作得很好。 – Brannon