SfondoUWP - Streaming WebCam over Socket a MediaElement - Immagine interrotta?
Il codice che ho scritto clip registrazioni video da una webcam, li scrive in un flusso di memoria, quindi trasmette i dati attraverso una connessione socket dove è ri-assemblati in video e riprodotti su un elemento multimediale.
L'obiettivo finale è quello di creare un sistema di baby monitor, con il server/fotocamera in esecuzione su Windows IOT Raspberry Pi e un'app UWP che io e la mia ragazza possiamo visualizzare sui nostri telefoni cellulari o sul laptop. Oltre a guardare la telecamera da un'altra parte della casa, potremo anche accedere quando uno di noi è fuori casa e in tempo collegherò anche un sensore di movimento PIR e un sistema di allarme, ma prima di tutto primo.
Il sistema nel suo complesso funziona abbastanza bene, c'è un ritardo di 5 secondi nel video che è accettabile per me (per ora), e utilizzando un MediaPlaybackList il video viene riprodotto a una velocità abbastanza costante con seamless (senza interruzioni come questo può ottenere per ora) transizione tra video. MediaPlaybackList rimuove gli oggetti così come sono stati riprodotti, mantenendo l'impronta della memoria su una costante relativa.
La questione
Quando il video viene riprodotto sul lato client, ottiene frequenti, ma casuali sezioni di immagine rotta. Non c'è un modello, non lo trovo comunque, e l'unico modo per descriverlo è che parte dell'immagine viene divisa a metà orizzontalmente e le due metà vengono scambiate, il lato destro dell'immagine viene visualizzato su la sinistra e viceversa. È come uno sfarfallio, poiché nel bit spezzato viene visualizzato solo per una frazione di secondo, perché un altro appare dopo circa un secondo da un'altra parte dell'immagine.
Ecco un esempio:
Ora, ecco un paio di punti interessanti ..
1) Prima di iniziare a utilizzare un MediaPlaybackList fare la coda flussi di dati, stavo usando un metodo di estrazione ogni video dal flusso di socket in entrata, salvandolo sul disco locale come StorageFile, quindi accodando questi StorageFiles, riproducendoli in ordine e cancellandoli successivamente (ho ancora una versione di questo codice nel controllo del codice sorgente che posso scavare, ma non mi piace l'idea di creare e distruggere StorageFiles perché sembra orrendo inefficiente). Tuttavia, l'utilizzo di questo metodo non ha comportato le immagini spezzate che ora sto vedendo ... questo mi porta a credere che il video stesso sia soddisfacente, e che forse è un problema con il modo in cui viene rimesso insieme e trasmesso in streaming a l'elemento multimediale?
2) Il gatto della mia ragazza ha bussato alla sua webcam (una Microsoft Lifecam HD-3000) senza che me ne rendessi conto, e non mi sono reso conto finché non ho eseguito il server e ho notato che l'immagine era a un angolo di 90 gradi. la cosa interessante (e sconcertante) su questo era che l'immagine consegnata al cliente non si rompeva come ho descritto sopra. L'unica differenza qui che posso vedere è che l'immagine è passata come 480 x 640 (dalla fotocamera seduta su un lato), piuttosto che lo standard 640 x 480. Ciò significa che non sono sicuro ...
Riflessioni sul problema
- Qualcosa a che fare con dimensionamento/dimensioni del video (che ha giocato bene sul suo lato, quindi è qualcosa a che fare con quello)?
- Qualcosa che ha a che fare con il bitrate?
- Qualcosa che riguarda il modo in cui i byte vengono riassemblati sul client?
- Qualcosa che ha a che fare con la codifica del flusso?
Fonte
Ecco alcuni frammenti di codice che penso siano probabilmente rilevanti, la fonte soluzione completa può essere trovata su GitHub, qui: 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;
}
}
Connection
//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);
Ho ottenuto il codice in esecuzione senza problemi video aggiungendo questo lato server sull'RPI3 con una cam logitech. Penso che l'implementazione dello streaming abbia bisogno di un po 'di riflessione, ma per me il problema era sul server. 'attendono _mediaCap.VideoDeviceController.SetMediaStreamPropertiesAsync (MediaStreamType.VideoRecord, nuovi VideoEncodingProperties() { FrameRate = {Denominatore = 1, numeratore = 15}, Altezza = 480, Width = 640, sottotipo = "YUY2" }) ; ' –