2016-06-14 12 views
5

HintergrundUWP - Streaming von WebCam über Socket zu MediaElement - Broken Picture?

Der Code, den ich Aufzeichnungen Videoclips von einer Webcam geschrieben habe, um sie zu einem Speicher-Stream schreibt, überträgt dann die Daten über eine Socketverbindung, wo es in Video wieder zusammengesetzt und spielte wieder auf ein Medienelement

Das ultimative Ziel ist es, ein Baby-Monitor-System zu erstellen, mit dem Server/Kamera auf einem Windows IOT Raspberry Pi und eine UWP-App, die meine Freundin und ich auf unseren Mobiltelefonen oder dem Laptop sehen können. Ich kann nicht nur die Kamera von einem anderen Teil des Hauses aus sehen, sondern auch, wenn einer von uns nicht zu Hause ist und rechtzeitig einen PIR-Bewegungsmelder und ein Warnsystem anschließen, aber die ersten Dinge zuerst.

Das System als Ganzes funktioniert ziemlich gut, es gibt eine Verzögerung von 5 Sekunden im Video, die für mich akzeptabel ist (vorerst), und mit einer MediaPlaybackList wird das Video mit einer ziemlich konstanten Rate mit nahtlosen (so nahtlos wie Dies kann für jetzt) ​​Übergang zwischen Videos. Die MediaPlaybackList entfernt Elemente so, wie sie abgespielt wurden, wodurch der Speicherbedarf relativ konstant gehalten wird.

Die Ausgabe

Wenn das Video auf dem Client-Ende spielt zurück, wird es häufig, aber zufällige Abschnitte gebrochen Bild. Es gibt kein Muster dafür, das ich sowieso nicht finden kann, und die einzige Art, wie ich es beschreiben kann, ist, dass ein Teil des Bildes horizontal halbiert wird und die beiden Hälften vertauscht werden, wobei die rechte Seite des Bildes angezeigt wird die linke und umgekehrt. Es ist wie ein Flackern, denn das gebrochene Bit wird nur für einen Bruchteil einer Sekunde angezeigt, weil ein anderes etwa eine Sekunde später irgendwo anders auf dem Bild erscheint.

Hier ist ein Beispiel:

Here you can see part of the frame is in the wrong position Jetzt ist hier ein paar interessanten Punkte ..

1) Bevor ich mit einem MediaPlaybackList gestartet, ein Verfahren zur Warteschlange von Datenströmen, ich wurde mit der Extraktion jedes Video aus dem eingehenden Socket-Stream, speichert es als StorageFile auf dem lokalen Datenträger, stellt diese StorageFiles in eine Warteschlange, spielt sie in der richtigen Reihenfolge ab und löscht sie anschließend (ich habe immer noch eine Version dieses Codes in der Quellcodeverwaltung, die ich ausgraben kann) aber ich mag die Idee nicht, StorageFiles zu erstellen und zu zerstören, da es horrend ineffizient erscheint). Allerdings führte die Verwendung dieser Methode nicht zu den zerbrochenen Bildern, die ich jetzt sehe ... das lässt mich glauben, dass das Video selbst in Ordnung ist und dass es vielleicht ein Problem mit der Art und Weise ist, wie es wieder zusammengesetzt und gestreamt wird das Medienelement?

2) Die Katze meiner Freundin klopfte die Webcam (eine Microsoft Lifecam HD-3000) auf die Seite, ohne dass ich es bemerkte, und ich bemerkte nicht, bis ich den Server lief und bemerkte, dass das Bild in einem 90-Grad-Winkel war. Das Interessante (und verwirrende) daran war, dass das Bild, das dem Kunden geliefert wurde, nicht zerbrach, wie ich oben beschrieben habe. Der einzige Unterschied, den ich sehen kann, ist, dass das Bild dann als 480 x 640 (von der auf der Seite sitzenden Kamera) durchgesickert ist, anstatt den Standard 640 x 480. Was das bedeutet, bin ich mir nicht sicher ...

Gedanken über das Problem

  • Etwas mit Sizing/Abmessungen des Videos zu tun (es auf der Seite fein gespielt, so ist es etwas damit zu tun)?
  • Etwas mit Bitrate zu tun?
  • Etwas mit der Art und Weise zu tun, wie die Bytes auf dem Client neu zusammengesetzt werden?
  • Etwas mit der Codierung des Streams zu tun?

Quelle

Hier ein paar Code-Schnipsel, die ich denke, wahrscheinlich relevant sind, kann die volle Lösungsquelle auf GitHub, hier: Video Socket Server .

Server

while (true) 
{ 
    try 
    { 
     //record a 5 second video to stream 
     Debug.WriteLine($"Recording started"); 
     var memoryStream = new InMemoryRandomAccessStream(); 
     await _mediaCap.StartRecordToStreamAsync(MediaEncodingProfile.CreateMp4(VideoEncodingQuality.Vga), memoryStream); 
     await Task.Delay(TimeSpan.FromSeconds(5)); 
     await _mediaCap.StopRecordAsync(); 
     Debug.WriteLine($"Recording finished, {memoryStream.Size} bytes"); 

     //create a CurrentVideo object to hold stream data and give it a unique id 
     //which the client app can use to ensure they only request each video once 
     memoryStream.Seek(0); 
     CurrentVideo.Id = Guid.NewGuid(); 
     CurrentVideo.Data = new byte[memoryStream.Size]; 

     //read the stream data into the CurrentVideo 
     await memoryStream.ReadAsync(CurrentVideo.Data.AsBuffer(), (uint)memoryStream.Size, InputStreamOptions.None); 
     Debug.WriteLine($"Bytes written to stream"); 

     //signal to waiting connections that there's a new video 
     _signal.Set(); 
     _signal.Reset(); 
    } 
    catch (Exception ex) 
    { 
     Debug.WriteLine($"StartRecording -> {ex.Message}"); 
     break; 
    } 
} 

Anschluss

//use the guid to either get the current video, or wait for the 
//next new one that's added by the server 
Guid guid = Guid.Empty; 
Guid.TryParse(command, out guid); 
byte[] data = _server.GetCurrentVideoDataAsync(guid); 
if (data != null) 
    await _socket.OutputStream.WriteAsync(data.AsBuffer()); 

Client-App

byte[] inbuffer = new byte[10000000]; 

//block on the input stream until we've received the full packet, 
//but use the Partial option so that we don't have to fill the entire buffer before we continue. 
//this is important, because the idea is to set the buffer big enough to handle any packet we'll receive, 
//meaning we'll never fill the entire buffer... and we don't want to block here indefinitely 
result = await socket.InputStream.ReadAsync(inbuffer.AsBuffer(), inbuffer.AsBuffer().Capacity, InputStreamOptions.Partial); 

//strip off the Guid, leaving just the video data 
byte[] guid = result.ToArray().Take(16).ToArray(); 
byte[] data = result.ToArray().Skip(16).ToArray(); 
_guid = new Guid(guid); 

//wrap the data in a stream, create a MediaSource from it, 
//then use that to create a MediaPlackbackItem which gets added 
//to the back of the playlist... 
var stream = new MemoryStream(data); 
var source = MediaSource.CreateFromStream(stream.AsRandomAccessStream(), "video/mp4"); 
var item = new MediaPlaybackItem(source); 
_playlist.Items.Add(item); 
+0

Ich habe Ihren Code ohne Video Störungen laufen, indem Sie diese Server-Seite auf dem rPI3 mit einer Logitech-Cam hinzufügen. Ich denke, dass die Streaming-Implementierung etwas mehr Gedanken braucht, aber für mich war das Problem auf dem Server. 'erwarten _mediaCap.VideoDeviceController.SetMediaStreamPropertiesAsync (MediaStreamType.VideoRecord, New VideoEncodingProperties() { Framerate = {Denominator = 1, Numerator = 15}, Height = 480 Width = 640, Subtyp = "YUY2" }) ; ' –

Antwort

2

Ich suche etwas ähnliches (Stream-Video/Audio von einem UWP-App auf einem Raspberry Pi) zu tun, aber ich habe die einfach-Kommunikations Probe aus den Windows 10 SDK, die nach einem wenig zwicken Ich benutze waren in der Lage, zuverlässig zu arbeiten (es gibt Thread-Synchronisierungsprobleme mit dem Beispielcode). Allerdings verwendet das SDK-Beispiel ein proprietäres Protokoll, das Medienerweiterungen verwendet, und es ist nicht einfach, den Stream über das Internet umzuleiten, was mein Anwendungsfall ist, also habe ich mir Ihren Code angeschaut und ihn zum Laufen gebracht (mit denselben Fehlern). Simple Real Time communication

Ein paar Kommentare zu Ihrem Ansatz:

1) RPI kann nicht sehr gut Video auf Win10 verarbeiten, da es nicht den Hardware-Video-Encoder verwenden kann, so tut alles in Software. Dies führt zu Störimpulsen und ich sehe, dass die CPU-Leistung mit über 50% Auslastung signifikant ansteigt, was bedeutet, dass mindestens einer der CPU-Kerne nahe bei max arbeitet, möglicherweise derjenige, der die Videokompression zu MP4 verarbeitet. Allerdings habe ich das SDK-Beispiel hochgelaufen und habe glitch free viewing und etwa 70% CPU, also ist dein Problem wahrscheinlich woanders.

2) 5 Sekunden Latenzverzögerung ist signifikant. Ich erhalte weniger als 100mSec Latenz mit dem Echtzeit-Sample, aber als ich den Streaming-Timer auf 1 Sekunde heruntergeregelt habe, war die Trennung signifikant und nicht praktikabel. Haben Sie darüber nachgedacht, das Design so zu ändern, dass es während der Aufnahme streamt, aber ich bin mir nicht sicher, ob Sie das mit InMemoryRandomAccessStream tun können. Eine andere Alternative besteht darin, den Vorschau-Stream zu erfassen und eine benutzerdefinierte Mediensenke in den Puffer zu schreiben (schwieriger zu handhaben als nicht gemanagter Code und wahrscheinlich nicht so einfach komprimierbar), wie dies bei dem Simple Communication-Beispiel der Fall ist.

3) MP4 ist ein Container, kein Komprimierungsformat, und ist nicht für das Streaming geeignet, da die gesamte Datei vor dem Start heruntergeladen werden muss, es sei denn, der MOOV-Metadatensatz wird am Anfang der Datei platziert. Nicht sicher, wie UWP dies handhabt, ist wahrscheinlich Ihr Ansatz, den Stream vor dem Senden zu schließen, erforderlich, um sicherzustellen, dass das andere Ende es richtig wiedergeben kann.

Also keine vollständige Antwort, aber hoffentlich hilft das oben.

+0

Ich versuche, etwas ähnliches zu tun, und obwohl ich klonen kann und die einfache Echtzeitkommunikation in ihrer Probe funktioniert, kann ich nicht für das Leben von mir bekommen, dass es funktioniert, indem ich den exakt gleichen Code in meiner eigenen App benutze ... – ChadT

+0

Hallo! Haben Sie zufällig eine funktionierende Lösung zwischen dem ursprünglichen Beitrag oder etwas, das auf dem Simple Communication Sample basiert, gefunden? Ich würde gerne etwas Hilfe einbringen und wenn du seit diesen Posts irgendwelche Fortschritte gemacht hättest, würde ich gerne hören. –

+0

Nein, ich habe mir das in letzter Zeit nicht angeschaut, aber als ich das letzte Mal getan habe, habe ich es über die neuen ORTC-APIs für die Browser-Wiedergabe implementiert. – deandob