我需要从 IP 摄像机播放 RTSP 视频。 如果我使用 ffplay 或 VLC,一切都很好。 但如果我使用 FFmpeg API(C#、FFmpeg.AutoGen),则帧上会出现伪像。使用 FFmpeg API 可能会出现什么问题?
更新
根据 Christoph 的建议,我尝试将 ffplay 的 UDP 替换为 TCP。
TCP 和 UDP 都给出了 3-4 秒的延迟(我的应用程序给出了不到一秒的延迟)。
TCP 和 UDP 都可以正常工作。
使用UDP时,开头有错误信息:
max delay reached. need to consume packet
RTP: missed 436 packets
Invalid level prefix
error while decoding MB 17 28
这些错误才刚刚开始。那么就没有错误了。 当使用TCP时,根本不会报告任何错误。
我的应用程序中的
discardcorrupt
标志无法修复工件。
如果我在应用程序中使用 TCP(
rtsp_transport=tcp
标志),工件就会消失。这很好。但我不想因为 TCP 而在网络上产生额外的负载。另外,可能会有视频延迟。
ffplay 和 VLC 如何在 UDP 上运行而不会出现伪影?为什么 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);
}