Echtzeit-video-streaming in C#
Ich entwickle eine Anwendung für Echtzeit-streaming. Zwei Teile sind für das streaming.
Ich benutze eine capture-Karte zu erfassen einige der live-Quelle und müssen stream in Echtzeit.
und auch brauchen, um Strom eine lokale video-Datei.
Streamen Sie lokale video-Datei in Echtzeit nutze ich emgu cv zu erfassen den video-frame als Bitmap.
Um dies zu erreichen habe ich das erstellen der bitmap-Liste und ich speichern Sie die aufgezeichneten bitmap zu dieser Liste mit einem thread.
und auch ich, zeigen die Bilder in ein Bild-Feld. Bitmap-Liste speichern können, 1 Sekunde video. wenn die frame-rate ist
30 speichern 30 video-frames. Nach ausfüllen dieser Liste starte ich einen weiteren thread zu codieren, dass 1 Sekunde chunk
video.
Zur Kodierung Zweck benutze ich ffmpeg wrapper genannt nreco. Ich Schreibe, dass die video-frames, um ffmpeg
und starten Sie den ffmpeg zu codieren. Nach Beendigung dieser Aufgabe, die ich bekommen kann-codierte Daten als byte-array.
Dann bin ich mit dem senden von Daten über UDP-Protokoll, über LAN.
Diese funktioniert einwandfrei. Aber ich nicht erreichen kann, die smooth streaming. Ich erhielt stream über VLC-player gibt es einige Millisekunden Verzögerung zwischen Paketen und auch ich habe bemerkt, dass ein frame verloren.
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, um live-streaming. Aber der stream ist nicht glatt. Ich habe versucht, mit einer Warteschlange statt
der Liste zu reduzieren, die die Wartezeit zum füllen der Liste. Weil ich dachte, dass die Wartezeit passiert encoding-thread-Encodierung
und send 2-Sekunden-video sehr schnell. Aber wenn es fertig ist diese Codierung Prozess der bitmap-Liste nicht
komplett voll. Also encoding-thread zu stoppen, bis die nächsten 2-Sekunden-video ist fertig.
Wenn jemand mir helfen kann, dies herauszufinden, ist es sehr dankbar. Wenn der Weg, ich mache dies falsch ist, bitte korrigieren Sie mich.
Danke!!!
- Ich bin mir nicht sicher, dass dies Ihr problem lösen, aber ich glaube nicht, dass es eine gute Idee, um eine neue zu erstellen
Thread
jedes mal in den code erfassen. Dies ist ein nicht benötigter Aufwand für die Erfassung routine. Es ist besser, erstellen Sie eine thread-sichere queue zwei-Sekunden-frames, Listen und verarbeiten Sie in einem separaten Arbeits-thread. Der Arbeits-thread ausgelöst werden kann, um die Bearbeitung zu starten, beispielsweise unter Verwendung vonManualResetEvent
. Nach anfänglichen Frage ein Arbeits-thread genug sein. Ich würde auch erwägen, TPL, aber das ist nicht Gegenstand der Eingangsfrage.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Es ist schwer zu sagen, etwas über den code, da Ihre Fragmente nicht bieten details für den gesamten Prozess.
Zuerst kann beseitigen frames Puffer (Liste von bitmaps) überhaupt.
Erstellen Sie einfach 1 live-stream encoding-Prozesses (Entwicklung neuer Verfahren für die alle 2 Sekunden chunk ist sehr schlechte Idee) und schieben bitmaps zu VideoConverter mit Write-Methode, wie Sie kommen. Da sind Sie erste Bilder vom Aufnahme-Gerät in Echtzeit sind Sie auch nicht brauchen, um jede manuelle Verzögerungen ( Thread.Sleep((int)(1000.5 /FrameRate)) ). Als Ergebnis sollten Sie erhalten glatte video mit VLC-Seite (einige Wartezeit - in der Regel etwa 200-500ms - ist unvermeidlich, denn die Kodierung und übertragung über das Netzwerk).
Wenn Sie frames aus der Aufnahme Gerät von Passungen und beginnt, können Sie versuchen, mit "-re" FFMpeg-option.
Änderte ich meinen code nun. Wenn der frame-Puffer gefüllt war, dann fing ich an, den thread, der verwendet wird zum Kodieren des video-Rahmens. In diesem thread hab ich codieren von video-frames, und ich rettete die kodierten Daten in thread-sichere queue. Nach dem Befüllen, die Warteschlange zu einem gewissen Grad I starten Sie den timer. Timer löst jede 200 Millisekunden-Intervall und senden kodierte Daten.
Diese funktioniert sehr gut und ich bekam den reibungslosen Datenstrom auf der Empfängerseite. Getestet habe ich diese mit 720 video. Aber wenn ich versuche zu streamen, 1080p-video-Streams sehr gut, zumindest am Anfang. Aber nach einiger Zeit stream anzeigt, Teil für Teil. Ich bemerkte dies geschieht, wenn mein streaming-Anwendung ist nicht das senden von Daten sehr schnell. So Spieler Puffer leer bekommen für kleine Millisekunde Zeit. Ich denke, das passiert, weil emgu cv nicht erfassen frames in Echtzeit. Es erfasst Rahmen sehr schnell für niedrige Auflösung-video. Wenn ich das capture-1080p-HD-video erfassen wird langsam. Beides hat die gleiche frame rate. Jedes mal, es wird das aufnehmen des Rahmens, wenn “ - Anwendung.Leerlauf += ProcessFrame;" - Ereignis auslösen.
Habe ich eine capture-Karte verwenden können, um Videos in Echtzeit und verwenden können, wenn ich die HDMI-Linie. Also ich weiß nicht, wie man video-capture-Datei mit capture-Karte. Das ist, warum ich verwendet, open cv. Und auch ich entfernte alle der thread, wie du gesagt hast.