Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/336.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如果落后,则跳过帧渲染_C#_Ffmpeg_Rtsp - Fatal编程技术网

C# 如果落后,则跳过帧渲染

C# 如果落后,则跳过帧渲染,c#,ffmpeg,rtsp,C#,Ffmpeg,Rtsp,下面的代码使用ffmpeg库(带有Autogen包装的v3.1.4)在我的应用程序中呈现RTSP视频。该代码通常运行良好。但是,receptical.Write方法的性能并不特别好。在速度较慢的机器上,我的视频渲染开始落后。最终我的缓冲区填满了,我开始看到视频损坏。当代码开始落后时,如何更改下面的代码以跳过帧?如果有多个帧准备好了,我真的只想显示最近可用的帧——毕竟这是实时视频。我相信avcodec\u发送\u数据包和avcodec\u接收\u帧方法大约是1:1 while (!token.Is

下面的代码使用ffmpeg库(带有Autogen包装的v3.1.4)在我的应用程序中呈现RTSP视频。该代码通常运行良好。但是,
receptical.Write
方法的性能并不特别好。在速度较慢的机器上,我的视频渲染开始落后。最终我的缓冲区填满了,我开始看到视频损坏。当代码开始落后时,如何更改下面的代码以跳过帧?如果有多个帧准备好了,我真的只想显示最近可用的帧——毕竟这是实时视频。我相信
avcodec\u发送\u数据包
avcodec\u接收\u帧
方法大约是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);
}
while(!token.iscancellationrequest)
{
if(ffmpeg.av_read_frame(pFormatContext,pPacket)!=0)
{
//水到渠成
ffmpeg.av_包_unref(pPacket);
ffmpeg.av_frame_unref(pDecodedFrame);
打破
}
如果(pPacket->stream_index!=pStream->index | | |(pPacket->flags&ffmpeg.AV_PKT_FLAG_CORRUPT)>0)
{
//这永远不会发生;我们只订阅一个流
//我相信腐败的数据包会被自动丢弃
ffmpeg.av_包_unref(pPacket);
ffmpeg.av_frame_unref(pDecodedFrame);
继续;
}
var sendResult=ffmpeg.avcodec\u send\u数据包(pCodecContext,pPacket);
如果(发送结果<0)
{
//可能的结果之一是“缓冲区已满”,但我认为只要我们调用1对1接收帧,就不应该发生这种情况
ffmpeg.av_包_unref(pPacket);
ffmpeg.av_frame_unref(pDecodedFrame);
_logger.Warn(“FFmpeg avcodec_发送_数据包失败:“+sendResult”);
打破
}
while(ffmpeg.avcodec_接收_帧(pCodecContext,pDecodedFrame)==0)
{
var src=&pDecodedFrame->data0;
var dst=&pConvertedFrame->data0;
var srcStride=pDecodedFrame->linesize;
var dststrade=pConvertedFrame->linesize;
sws_标度(pConvertContext,src,srcStride,0,height,dst,dstStride);
sbyte*convertedFrameAddress=pConvertedFrame->data0;
int linesize=dstStride[0];
如果(接收==null)
{
receptical=writableBitampCreationCallback.Invoke(新检测到的图像尺寸{Width=Width,Height=Height,Format=DetectedPixelFormat.Bgr24,Linesize=Linesize});
}
var imageBufferPtr=新的IntPtr(转换的帧地址);
接受写入(宽度、高度、imageBufferPtr、线宽);
ffmpeg.av_frame_unref(pDecodedFrame);
}
ffmpeg.av_包_unref(pPacket);
}
  • 取决于您同步播放的方式,无论是与系统时钟同步还是其他同步。您可以检查传入数据包的PTS,如果它们开始落后于播放,则转储它们。实时解码是cpu密集型的

  • 解码和使用sws_规模将消耗您的cpu。我不知道什么是“writableBitampCreationCallback”,但我也猜这也在消耗cpu时间。最好的解决方法是将解码分为不同的线程,音频和视频各一个线程,可能还有字幕。这至少可以节省cpu时间。数据包可以发送到每个线程

  • 您没有显示视频最终是如何渲染的。使用类似openGL的工具,您可以使用YUV到RGB着色器直接渲染解码帧(YUV420),这样就不需要将帧转换为RGB。这节省了大量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); \
    }
    
    下面是一个片段着色器的示例,它从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); \
    }
    

    你认为我可以利用PTS来知道我落后了不止一帧吗?我对它不熟悉。实际上我现在正在实现它,因为我也必须去交错50i帧。我正在与系统时钟同步,所以当数据包读取器到达超出2/50秒的数据包时,我不会将它们添加到解码器中。到现在为止,一直都还不错。所以是的,这是可以做到的。我只需要这样做,玩家就可以在旧的ARM硬件上工作。使用av_q2d()将PTS值转换为double。我最后不得不为
    av_read_帧
    调用使用单独的线程(因为在没有可用数据时它是阻塞调用)。有了它,每当我得到一个新的关键帧时,我就会清除我的数据包队列。这对我来说似乎很有效。