我开发了一个实时流媒体应用程序。两个部分包括流媒体。 我使用捕获卡捕获一些实时源并需要实时流式传输。 并且还需要流式传输本地视频文件。
要实时流式传输本地视频文件,我使用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);
}
}
这是我用来实现直播的过程。但是流不顺畅。我尝试使用队列代替 列表,以减少填充列表的延迟。因为我认为延迟发生在编码线程编码 并非常快速地发送2秒视频。但是当它完成位图列表的编码过程时 全满。所以编码线程将停止,直到下一个2秒的视频准备就绪。
如果有人能帮我解决这个问题,那就非常感激。如果我这样做的方式有误,请纠正我。 谢谢!
答案 0 :(得分:1)
由于您的片段不提供整个过程的详细信息,因此很难对代码说些什么。
首先,你可以完全消除帧缓冲区(位图列表)。 只需创建1个实时流编码过程(每2秒创建一个新的进程块是非常糟糕的主意)并使用Write方法将位图推送到VideoConverter。由于您实时从捕获设备获取帧,因此您也不需要进行任何手动延迟(Thread.Sleep((int)(1000.5 / FrameRate)))。因此,您应该在VLC端获得流畅的视频(由于编码和网络传输,一些延迟 - 通常大约200-500ms - 是不可避免的。)
如果您通过适合和开始捕获设备获取帧,您可以尝试“-re”FFMpeg选项。
答案 1 :(得分:0)
我现在改变了我的代码。填充帧缓冲区后,我启动了用于编码视频帧的线程。在这个线程中我编码视频帧,我将这些编码数据保存在线程安全队列中。在某种程度上填充该队列后,我启动计时器。定时器将触发每200毫秒的间隔并发送编码数据。
这很好用,我在接收端得到了流畅的流。我用720视频测试了这个。但是,当我尝试流式传输1080p视频时,它在开始时非常流畅。但经过一段时间后,流逐个显示。我注意到当我的流应用程序没有快速发送数据时会发生这种情况。因此播放器缓冲区在很短的时间内变空。我认为这是因为emgu cv没有实时捕获帧。它可以非常快速地捕获帧以获得低分辨率视频当我捕获1080p高清视频时,捕获速度会变慢。即使两者都具有相同的帧速率。每次它将捕获帧时“Application.Idle + = ProcessFrame;&#34;事件触发器。
我有一张可以用来实时捕捉视频的采集卡,如果我有HDMI线路可以使用。所以我不知道如何使用捕获卡捕获视频文件。这就是我使用open cv的原因。我也按照你的说法删除了所有的帖子。