2016-11-08 2 views
-1

У меня есть код ниже, который использует библиотеки ffmpeg (v3.1.4 с оберткой Autogen) для рендеринга RTSP-видео в моем приложении. Код работает очень хорошо. Однако метод receptical.Write не особенно эффективен. На медленных машинах мой рендеринг видео начинает отставать. В конце концов мои буферы заполняются, и я начинаю видеть повреждение видео. Как изменить код ниже, чтобы пропустить кадры, когда он начинает отставать? Если готово несколько кадров, я действительно хочу показать только что доступный кадр - это, в конце концов, видео в реальном времени. Я считаю, что методы avcodec_send_packet и avcodec_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 входящих пакетов и если они начинают отставать от воспроизведения, а затем сбрасывать их. В реальном времени декодирование «на лету» интенсивно.

  2. Декодирование и использование sws_scale будут использовать ваш процессор. Я не знаю, что такое «writableBitampCreationCallback», но я также предполагаю, что тоже ел процессор. Лучший способ - разделить ваше декодирование на отдельные потоки, по одному для аудио и видео и, возможно, субтитров. Это, по крайней мере, освободит время процессора. Пакеты могут быть отправлены в каждый поток.

  3. Вы не показываете, как видео окончательно отображается. Используя что-то вроде openGL, вы можете визуализировать декодированный кадр (YUV420) напрямую, используя шейдер YUV для RGB, который устраняет необходимость преобразования кадра в RGB. Это экономит много времени процессора.

Ниже приведен пример фрагментарного шейдера, который принимает 3 текстуры из данных YUV. Надеюсь, это поможет вам сэкономить много времени на процессоре.

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