Ich entwickle eine Anwendung für Echtzeit-Streaming. Zwei Teile enthalten für das Streaming. Ich benutze eine Capture-Karte, um einige Live-Quellen zu erfassen und in Echtzeit streamen zu müssen. und müssen auch eine lokale Videodatei streamen.Echtzeit-Video-Streaming in C#
Um lokale Videodateien in Echtzeit zu streamen benutze ich emgu cv, um den Videorahmen als Bitmaps zu erfassen. Um dies zu erreichen, erstelle ich die Bitmap-Liste und speichere gefangene Bitmap mit einem Thread in diese Liste. und auch ich diese Bilder in einer Bildbox anzeigen. Bitmap-Liste kann 1 Sekunde Video speichern. Wenn die Bildrate ist, werden 30 Videoframes gespeichert. Nach dem Füllen dieser Liste beginne ich einen anderen Thread, um das 1 Sekunde Chunk Video zu kodieren.
Zum Zweck der Codierung verwende ich ffmpeg Wrapper namens Nreco. Ich schreibe den Videoframe in ffmpeg und starte den ffmpeg zum Enkodieren. Nach dem Beenden dieser Aufgabe kann ich codierte Daten als Byte-Array erhalten.
Dann sende ich diese Daten mit UDP-Protokoll über LAN.
Das funktioniert gut. Aber ich kann das flüssige Streaming nicht erreichen. Wenn ich Stream über VLC-Player empfangen habe, gibt es einige Millisekunden Verzögerung zwischen den Paketen und auch dort habe ich festgestellt, dass ein Frame verloren gegangen ist.
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);
}
}
Dies ist der Prozess, den ich verwendet habe, um das Live-Streaming zu erreichen. Aber der Strom ist nicht glatt. Ich habe versucht, eine Warteschlange statt der Liste zu verwenden, um die Latenz zu reduzieren, um die Liste zu füllen. Weil ich dachte, dass Latenz passiert Kodierung Thread encodieren und senden Sie 2 Sekunden Video sehr schnell. Aber wenn dieser Codiervorgang der Bitmap-Liste beendet ist, ist nicht vollständig voll. Der Encoding-Thread stoppt also, bis das nächste 2-Sekunden-Video fertig ist.
Wenn mir jemand helfen kann, das herauszufinden, ist es sehr dankbar. Wenn die Art, wie ich das tue, falsch ist, korrigiere mich bitte. Danke!
Ich bin mir nicht sicher, dass dies Ihr Problem lösen wird, aber ich nicht Ich denke, es ist eine gute Idee, jedes Mal im Capturing-Code ein neues 'Thread' zu erstellen. Dies ist ein unnötiger Overhead für die Erfassung von Routine. Es ist besser, eine thread-sichere Warteschlange von Zwei-Sekunden-Rahmenlisten zu erstellen und sie in einer separaten Arbeit zu verarbeiten thread.Working thread kann ausgelöst werden, um die Verarbeitung zu starten, zum Beispiel mit 'ManualResetEvent'. Nach der ersten Frage wird ein arbeitender Thread sein genug. Ich würde auch in Betracht ziehen, TPL zu verwenden, aber das ist außerhalb des Bereichs der ersten Frage. –