C#中的实时视频流

Nuw*_*wan 8 c# video ffmpeg

我正在开发一个实时流媒体应用程序.两个部分包括流媒体.我使用捕获卡捕获一些实时源并需要实时流式传输.并且还需要流式传输本地视频文件.

为了实时流式传输本地视频文件,我使用emgu cv将视频帧捕获为位图.为此,我创建了位图列表,并使用一个线程将捕获的位图保存到此列表中.我还在图片框中显示这些帧.位图列表可以存储1秒的视频.如果帧速率为30,则将存储30个视频帧.填写此列表后,我启动另一个线程来编码1秒块视频.

出于编码目的,我使用名为nreco的ffmpeg包装器.我将视频帧写入ffmpeg并启动ffmpeg进行编码.停止该任务后,我可以将编码数据作为字节数组.

然后我通过LAN使用UDP协议发送数据.

这很好用.但我无法实现流畅的流媒体.当我通过VLC播放器接收到流时,数据包之间有一些毫秒的延迟,我也注意到帧丢失了.

private Capture _capture = null;
Image<Bgr, Byte> frame;

// Here I capture the frames and store them in a list
private void ProcessFrame(object sender, EventArgs arg)
{
     frame = _capture.QueryFrame();
     frameBmp = new Bitmap((int)frameWidth, (int)frameHeight, PixelFormat.Format24bppRgb);
     frameBmp = frame.ToBitmap(); 


 twoSecondVideoBitmapFramesForEncode.Add(frameBmp);
                        ////}
     if (twoSecondVideoBitmapFramesForEncode.Count == (int)FrameRate)
     {
         isInitiate = false;
         thread = new Thread(new ThreadStart(encodeTwoSecondVideo));
         thread.IsBackground = true;
         thread.Start();
     }  
 }

public void encodeTwoSecondVideo()
{
    List<Bitmap> copyOfTwoSecondVideo = new List<Bitmap>();
    copyOfTwoSecondVideo = twoSecondVideoBitmapFramesForEncode.ToList();
    twoSecondVideoBitmapFramesForEncode.Clear();

    int g = (int)FrameRate * 2;

    // create the ffmpeg task. these are the parameters i use for h264 encoding

        string outPutFrameSize = frameWidth.ToString() + "x" + frameHeight.ToString();
        //frame.ToBitmap().Save(msBit, frame.ToBitmap().RawFormat);
        ms = new MemoryStream();
        //Create video encoding task and set main parameters for the video encode

        ffMpegTask = ffmpegConverter.ConvertLiveMedia(
            Format.raw_video,
            ms,
            Format.h264,
            new ConvertSettings()
            {

                CustomInputArgs = " -pix_fmt bgr24 -video_size " + frameWidth + "x" + frameHeight + " -framerate " + FrameRate + " ", // windows bitmap pixel format
                CustomOutputArgs = " -threads 7 -preset ultrafast -profile:v baseline -level 3.0 -tune zerolatency -qp 0 -pix_fmt yuv420p -g " + g + " -keyint_min " + g + " -flags -global_header -sc_threshold 40 -qscale:v 1 -crf 25 -b:v 10000k -bufsize 20000k -s " + outPutFrameSize + " -r " + FrameRate + " -pass 1 -coder 1 -movflags frag_keyframe -movflags +faststart -c:a libfdk_aac -b:a 128k "
                //VideoFrameSize = FrameSize.hd1080,
                //VideoFrameRate = 30

            });

        ////////ffMpegTask.Start();
        ffMpegTask.Start();


      // I get the 2 second chunk video bitmap from the list and write to the ffmpeg 
  foreach (var item in copyOfTwoSecondVideo)
        {
            id++;
            byte[] buf = null;
            BitmapData bd = null;
            Bitmap frameBmp = null;

            Thread.Sleep((int)(1000.5 / FrameRate));

            bd = item.LockBits(new Rectangle(0, 0, item.Width, item.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            buf = new byte[bd.Stride * item.Height];
            Marshal.Copy(bd.Scan0, buf, 0, buf.Length);
            ffMpegTask.Write(buf, 0, buf.Length);
            item.UnlockBits(bd);
        }
   }
Run Code Online (Sandbox Code Playgroud)

这是我用来实现直播的过程.但是流不顺畅.我尝试使用队列而不是列表来减少填充列表的延迟.因为我认为延迟发生在编码线程编码并且非常快速地发送2秒视频.但是当它完成这个位图列表的编码过程并不完全充满时.因此编码线程将停止,直到下一个2秒的视频准备就绪.

如果有人能帮我解决这个问题,那就非常感激.如果我这样做的方式有误,请纠正我.谢谢!

Vit*_*nko 1

很难对代码说些什么,因为您的片段没有提供整个过程的详细信息。

首先,您可以完全消除帧缓冲区(位图列表)。只需创建 1 个实时流编码进程(每 2 秒块创建一个新进程是非常糟糕的主意),然后使用 Write 方法将位图推送到 VideoConverter。由于您是实时从捕获设备获取帧,因此您也不需要进行任何手动延迟( Thread.Sleep((int)(1000.5 / FrameRate)) )。因此,您应该在 VLC 端获得流畅的视频(由于编码和网络传输,一些延迟 - 通常约为 200-500 毫秒 - 是不可避免的)。

如果您时断时续地从捕获设备获取帧,您可以尝试“-re”FFMpeg 选项。