You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用FFmpeg API(C#)播放RTSP出现帧失真的原因排查

RTSP视频流播放失真问题排查与疑问

我需要播放IP摄像头的RTSP视频流:使用ffplay或VLC播放时一切正常,但通过C#的FFmpeg.AutoGen调用FFmpeg API播放时,画面帧出现失真。以下是排查过程及疑问:

更新内容

根据Christoph的建议,我在ffplay中分别测试UDP与TCP传输:

  • TCP与UDP均存在3-4秒延迟(我的应用延迟小于1秒);
  • 两种传输方式下播放均无失真;
  • 使用UDP时,初期会出现如下错误信息,之后无报错:
max delay reached. need to consume packet
RTP: missed 436 packets
Invalid level prefix
error while decoding MB 17 28

使用TCP时则全程无错误;

  • 在我的应用中设置discardcorrupt标记无法解决失真问题;
  • 若在应用中启用rtsp_transport=tcp标记切换为TCP传输,失真消失,但会增加网络负载且可能带来延迟。

疑问

为何ffplay与VLC使用UDP播放无失真,而FFmpeg API却无法做到?

问题截图及代码实现

画面失真截图(显示画面存在块状、色彩错乱的失真现象)

AVFormatContext* pFormatContext = ffmpeg.avformat_alloc_context();

int error;

error = ffmpeg.avformat_open_input(&pFormatContext, rtspUrl, null, null);

if (error != 0)
    throw new ApplicationException(Helpers.GetErrorMessage(error));

error = ffmpeg.avformat_find_stream_info(pFormatContext, null);

if (error != 0)
    throw new ApplicationException(Helpers.GetErrorMessage(error));

AVStream* pStream = null;

for (var i = 0; i < pFormatContext->nb_streams; i++)
{
    if (pFormatContext->streams[i]->codec->codec_type == AVMediaType.AVMEDIA_TYPE_VIDEO)
    {
        pStream = pFormatContext->streams[i];
    }
}

if (pStream == null)
    throw new ApplicationException(@"Could not found video stream.");

AVCodecContext codecContext = *pStream->codec;

int width = codecContext.width;
int height = codecContext.height;
AVPixelFormat sourcePixFmt = codecContext.pix_fmt;
AVCodecID codecId = codecContext.codec_id;

AVPixelFormat destinationPixFmt = AVPixelFormat.AV_PIX_FMT_BGR24;

if (sourcePixFmt == AVPixelFormat.AV_PIX_FMT_NONE && codecId == AVCodecID.AV_CODEC_ID_H264)
{
    sourcePixFmt = AVPixelFormat.AV_PIX_FMT_YUV420P;
}

SwsContext* pConvertContext = ffmpeg.sws_getContext(width, height, sourcePixFmt, width, height, destinationPixFmt, ffmpeg.SWS_FAST_BILINEAR, null, null, null);

if (pConvertContext == null)
    throw new ApplicationException(@"Could not initialize the conversion context.");

AVFrame* pConvertedFrame = ffmpeg.av_frame_alloc();

int convertedFrameBufferSize = ffmpeg.av_image_get_buffer_size(destinationPixFmt, width, height, 1);

IntPtr convertedFrameBufferPtr = Marshal.AllocHGlobal(convertedFrameBufferSize);

byte_ptrArray4 dstData = new byte_ptrArray4();
int_array4 dstLinesize = new int_array4();

ffmpeg.av_image_fill_arrays(ref dstData, ref dstLinesize, (byte*)convertedFrameBufferPtr, destinationPixFmt, width, height, 1);



WorkEnabled = true;

AVCodec* pCodec = ffmpeg.avcodec_find_decoder(codecId);

if (pCodec == null)
    throw new ApplicationException(@"Unsupported codec.");

AVCodecContext* pCodecContext = &codecContext;

if ((pCodec->capabilities & ffmpeg.AV_CODEC_CAP_TRUNCATED) == ffmpeg.AV_CODEC_CAP_TRUNCATED)
{
    pCodecContext->flags |= ffmpeg.AV_CODEC_FLAG_TRUNCATED;
}

error = ffmpeg.avcodec_open2(pCodecContext, pCodec, null);

if (error < 0)
    throw new ApplicationException(Helpers.GetErrorMessage(error));

AVFrame* pDecodedFrame = ffmpeg.av_frame_alloc();

AVPacket packet = new AVPacket();
AVPacket* pPacket = &packet;
ffmpeg.av_init_packet(pPacket);


Bitmap bitmap = null;
Bitmap previousbBitmap = null;

while (WorkEnabled)
{
    try
    {
        do
        {
            error = ffmpeg.av_read_frame(pFormatContext, pPacket);

            if (error == ffmpeg.AVERROR_EOF)
            {
                Thread.Sleep(1000);
                break;
            }
            if (error < 0)
                throw new ApplicationException(Helpers.GetErrorMessage(error));

            if (pPacket->stream_index != pStream->index) continue;

            error = ffmpeg.avcodec_send_packet(pCodecContext, pPacket);

            if (error < 0)
                throw new ApplicationException(Helpers.GetErrorMessage(error));

            error = ffmpeg.avcodec_receive_frame(pCodecContext, pDecodedFrame);


        } while (error == ffmpeg.AVERROR(ffmpeg.EAGAIN) && WorkEnabled);

        if (error == ffmpeg.AVERROR_EOF)
        {
            Thread.Sleep(1000);
            continue;
        }
        if (error < 0)
            throw new ApplicationException(Helpers.GetErrorMessage(error));

        if (pPacket->stream_index != pStream->index) continue;

        // YUV->RGB
        ffmpeg.sws_scale(pConvertContext, pDecodedFrame->data, pDecodedFrame->linesize, 0, height, dstData, dstLinesize);
    }
    catch (Exception)
    {}
    finally
    {
        ffmpeg.av_packet_unref(pPacket);
        ffmpeg.av_frame_unref(pDecodedFrame);
    }

    bitmap = new Bitmap(width, height, dstLinesize[0], PixelFormat.Format24bppRgb, convertedFrameBufferPtr);

    previousbBitmap = bitmap;

    System.Windows.Application.Current.Dispatcher.Invoke(() => _pictureBox.Image = bitmap);

}

内容的提问来源于stack exchange,提问作者ffvideoner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 00:14:55