У меня есть код ниже, который использует библиотеки 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);
}
Как вы думаете, я могу использовать PTS, чтобы знать, что у меня больше одного кадра? Я не знаком с этим. – Brannon
Я на самом деле просто реализую это сейчас, потому что мне нужно деинтерлейсировать 50i кадров. Я синхронизируюсь с системными часами, поэтому, когда считыватель пакетов достигает пакетов, которые выходят на 2/50-е секунды, я не буду добавлять их в декодер. Все идет нормально. Так что это можно сделать. Мне нужно только сделать это, чтобы игрок работал на старом оборудовании ARM. Используйте av_q2d() для преобразования значения PTS в double. – WLGfx
Мне пришлось использовать отдельный поток для вызовов 'av_read_frame' (поскольку это блокирующий вызов, когда нет данных). При этом я очищаю свою очередь пакетов, когда я получаю новый ключевой фрейм. Кажется, сейчас это работает для меня хорошо. – Brannon