我有下面的代码,它使用ffmpeg库(带有Autogen包装器的v3.1.4)在我的应用程序中呈现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 :(得分:0)
取决于您同步播放的方式,是否与系统时钟或其他方式相反。您可以检查传入数据包的PTS,如果它们开始落后于播放,则转储它们。动态实时解码是cpu密集型。
解码并使用sws_scale会占用你的cpu。我不知道什么&#39; writableBitampCreationCallback&#39;但我也猜测这也是在耗费CPU时间。解决此问题的最佳方法是将解码分成单独的线程,每个线程用于音频和视频以及字幕。这至少可以释放cpu时间。数据包可以发送到每个线程。
您没有显示最终呈现视频的方式。使用类似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); \
}