2016-11-08 10 views
-1

私のアプリケーションでは、RTSPビデオをレンダリングするためにffmpegライブラリ(Autogenラッパーの場合はv3.1.4)を使用する以下のコードがあります。コードは非常によく機能します。しかしながら、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時間を食べていると推測している。この問題を回避する最も良い方法は、デコードを別々のスレッドに分割することです.1つはオーディオとビデオ用、もう1つはサブタイトル用です。これは少なくともCPU時間を解放します。パケットは各スレッドに送信できます。

  3. ビデオの最終的な表示方法が表示されていません。 OpenGLのようなものを使用すると、デコードされたフレーム(YUV420)をYUVからRGBへのシェーダを使って直接レンダリングすることができ、フレームを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を使用して、私が1つ以上のフレームであることを知ることができると思いますか?私はそれに精通していない。 – Brannon

+0

私は実際にそれを実装しています。なぜなら、50iフレームもインターレース解除する必要があるからです。私はシステムクロックに同期していますので、パケットリーダーが2/50秒のパケットに達したときに、それらをデコーダに追加しません。ここまでは順調ですね。そう、そうすることができます。私はそれを行う必要がありますので、プレーヤーは古いARMハードウェアで作業します。 av_q2d()を使用して、PTS値をdoubleに変換します。 – WLGfx

+0

私は 'av_read_frame'呼び出しのために別のスレッドを使う必要がありました。なぜなら、データが利用できないときはブロック呼び出しであるからです。その場所で、新しいキーフレームを取得するたびにパケットキューをクリアします。それは私のために今かなり良く働いているようです。 – Brannon

関連する問題