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:
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);
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" }) ; ' –