UWP - Streaming von WebCam über Socket zu MediaElement - Defektes Bild?

Hintergrun

Der Code, den ich geschrieben habe, zeichnet Videoclips von einer Webcam auf, schreibt sie in einen Speicherstrom und überträgt die Daten dann über eine Socket-Verbindung, wo sie zu Video zusammengesetzt und auf einem Media Element wiedergegeben werden.

Das ultimative Ziel besteht darin, ein Babyphone-System zu erstellen, bei dem der Server / die Kamera auf einem Windows IOT Raspberry Pi ausgeführt wird und eine UWP-App, die meine Freundin und ich auf unseren Mobiltelefonen oder auf dem Laptop anzeigen können. Wir können nicht nur die Kamera von einem anderen Teil des Hauses aus betrachten, sondern uns auch einloggen, wenn einer von uns nicht zu Hause ist. Mit der Zeit werde ich auch einen PIR-Bewegungsmelder und ein Warnsystem anschließen, aber als Erstes zuerst

Das System als Ganzes funktioniert ziemlich gut, es gibt eine Verzögerung von 5 Sekunden im Video, die für mich (vorerst) akzeptabel ist, und bei Verwendung einer MediaPlaybackList wird das Video mit einer ziemlich konstanten Rate mit Seamless gestreamt (so nahtlos wie möglich) vorerst) übergang zwischen videos. Die MediaPlaybackList entfernt Elemente, während sie abgespielt wurden, wobei der Speicherbedarf relativ konstant bleibt.

Die Angelegenhei

Wenn das Video auf der Client-Seite abgespielt wird, werden häufig zufällige Bildausschnitte angezeigt. Es gibt kein Muster, das ich sowieso nicht finden kann, und ich kann es nur so beschreiben, dass ein Teil des Bildes horizontal in zwei Hälften geteilt und die beiden Hälften vertauscht werden, wobei die rechte Seite des Bildes angezeigt wird die linke und umgekehrt. Es ist wie ein Flackern, da das abgebrochene Bit nur für den Bruchteil einer Sekunde angezeigt wird, weil ein anderes eine Sekunde oder so später irgendwo anders auf dem Bild erscheint.

Hier ist ein Beispiel:

Nun, hier sind ein paar interessante Punkte ..

1) Bevor ich mit einer MediaPlaybackList begonnen habe, Datenströme in die Warteschlange zu stellen, habe ich jedes Video aus dem eingehenden Socket-Stream extrahiert, auf der lokalen Festplatte als StorageFile gespeichert und diese StorageFiles dann in die Warteschlange gestellt und abgespielt ordne und lösche sie danach (Ich habe noch eine Version dieses Codes in der Quellcodeverwaltung, die ich ausgraben kann, aber ich mag die Idee, StorageFiles zu erstellen und zu zerstören, nicht, da sie fürchterlich ineffizient erscheint). Die Verwendung dieser Methode hat jedoch nicht zu den fehlerhaften Bildern geführt, die ich jetzt sehe. Dies lässt mich glauben, dass das Video selbst in Ordnung ist und dass es möglicherweise ein Problem mit der Art und Weise darstellt, wie es wieder zusammengesetzt und übertragen wird das Medienelement?

2) Die Katze meiner Freundin hat die Webcam (eine Microsoft Lifecam HD-3000) auf die Seite geschleudert, ohne dass ich es merkte, und ich habe es nicht bemerkt, bis ich den Server gestartet habe und festgestellt habe, dass das Bild in einem 90-Grad-Winkel war. Und das Rätselhafte daran war, dass das Bild, das dem Kunden geliefert wurde, nicht in die oben beschriebenen Teile zerfiel. Der einzige Unterschied, den ich hier sehen kann, ist, dass das Bild dann als 480 x 640 (von der Kamera auf der Seite) statt als Standard 640 x 480 durchgekommen ist. Was das bedeutet, ich bin nicht sicher ...

Gedanken zum Problem

Etwas mit der Größe / Dimension des Videos zu tun (es hat sich gut abgespielt, also hat es etwas damit zu tun)?Something mit Bitrate zu tun?Hat das etwas damit zu tun, wie die Bytes auf dem Client neu zusammengesetzt werden?Hat das etwas mit der Codierung des Streams zu tun?

Quell

Hier sind ein paar Codeausschnitte, die meiner Meinung nach relevant sind. Die vollständige Lösungsquelle finden Sie auf GitHub, hier:Video Socket Server .

Serve

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;
    }
}

Verbindun

//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);

Antworten auf die Frage(2)

Ihre Antwort auf die Frage