2014-07-19 11 views
8

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!

+1

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. –

Antwort

1

Es ist schwer, etwas über den Code zu sagen, da Ihre Fragmente keine Details für den gesamten Prozess bereitstellen.

Zunächst einmal können Sie Frames-Puffer (Liste der Bitmaps) überhaupt eliminieren. Erstellen Sie einfach 1 Live-Stream-Encoding-Prozess (Erstellen neuer Prozess für alle 2 Sekunden Chunk ist sehr schlechte Idee) und schieben Sie Bitmaps zu VideoConverter mit Write-Methode, wie sie kommen. Da Sie Frames vom Gerät in Echtzeit erhalten, müssen Sie auch keine manuellen Verzögerungen machen (Thread.Sleep ((int) (1000.5/FrameRate))). Als Ergebnis sollten Sie ein flüssiges Video auf der VLC-Seite bekommen (eine gewisse Latenz - normalerweise etwa 200 - 500 ms - ist aufgrund der Kodierung und Netzwerkübertragung unvermeidlich).

Wenn Sie Bilder von der Aufnahme von Gerät durch Anschnallen erhalten, können Sie versuchen, "-re" FFMpeg Option.

+0

Wie Sie sagten, habe ich einen Prozess von ffmpeg Konverter erstellt und Bitmaps zu VideoConverter mit Write-Methode geschoben, wie sie kommen und ich habe codierte Daten als Ausgabestrom. Dann habe ich diese Daten über UDP gesendet. Es scheint gut zu funktionieren, aber Stream ist überhaupt nicht gut. Kann nicht identifizieren, was im Player angezeigt wird. – Nuwan

+0

Ich versuche, direkt in den Konverter-Task zu schreiben, aber es gibt mir einen Fehler: Ausgabedatei # 0 enthält keinen Stream. Kannst du mir helfen? –

0

Ich habe meinen Code jetzt geändert. Als der Bildspeicher voll war, habe ich den Thread gestartet, der den Videoframe codiert hat. Innerhalb dieses Threads kodiere ich Videoframes und ich speichere diese kodierten Daten in einer threadsicheren Warteschlange. Nachdem ich diese Warteschlange einigermaßen gefüllt habe, starte ich den Timer. Der Timer löst jedes 200-Millisekunden-Intervall aus und sendet codierte Daten.

Das funktioniert sehr gut und ich habe den reibungslosen Stream auf der Empfängerseite. Ich habe das mit 720 Video getestet. Aber wenn ich versuche, 1080p Video zu streamen, stream es am Anfang sehr gut. Aber nach einiger Zeit wird der Stream Teil für Teil angezeigt. Ich habe festgestellt, dass dies passiert, wenn meine Streaming-Anwendung nicht sehr schnell Daten sendet.So wird der Player-Puffer für kleine Millisekunden leer. Ich denke, das passiert, weil emgu cv keine Frames in Echtzeit aufgenommen hat. Es wurde sehr schnell für Videos mit niedriger Auflösung aufgenommen. Wenn ich 1080p HD-Video aufnehme, wird die Aufnahme langsamer. Sogar beide haben die gleiche Bildrate. Jedes Mal wird es den Rahmen erfassen, wenn "Application.Idle + = ProcessFrame;" Ereignis-Trigger.

Ich habe eine Capture-Karte, die Video in Echtzeit zu erfassen und verwenden kann, wenn ich HDMI-Leitung habe. Also ich don Ich weiß nicht, wie man Videodatei mit Capture-Karte zu erfassen. Deshalb habe ich open cv. Und auch ich entfernte den ganzen Thread wie Sie sagten.

+0

Können Sie Ihre Änderungen bitte posten? –

+0

Wir möchten Ihre Änderung sehen. –