2016-06-14 13 views
5

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:

Here you can see part of the frame is in the wrong position 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); 
+0

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

risposta

2

Sto cercando di fare qualcosa di simile (streaming video/audio da un'app UWP su un Raspberry Pi), ma ho utilizzato il campione di comunicazione semplice dall'SDK di Windows 10 che dopo un po 'di tweaking I sono stati in grado di funzionare in modo affidabile (ci sono problemi di sincronizzazione dei thread con il codice di esempio). Tuttavia, l'esempio SDK utilizza un protocollo proprietario che utilizza estensioni multimediali e non è facile reindirizzare il flusso su Internet, che è il mio caso d'uso, quindi ho dato un'occhiata al codice e l'ho fatto funzionare (con gli stessi bug). Simple Real Time communication

Un paio di commenti sul tuo approccio:

1) L'RPI non può elaborare video su Win10 molto bene in quanto non è possibile utilizzare il video encoder hardware così fa tutto in un software. Ciò causerà problemi e vedo che le prestazioni della CPU aumentano in modo significativo con oltre il 50% di utilizzo, il che significa che almeno uno dei core della CPU sta lavorando vicino al massimo, forse quello che gestisce la compressione video in MP4. Tuttavia ho eseguito l'analisi del campione SDK e ho ottenuto una visualizzazione senza errori e circa il 70% della CPU, quindi il problema è probabilmente altrove.

2) 5 secondi di ritardo di latenza sono significativi. Ottengo meno di 100mSec di latenza con il campione in tempo reale, tuttavia, quando ho regolato il timer di streaming a 1 secondo, la rottura è stata significativa e non lavorabile. Hai pensato di cambiare il design in modo che storni durante l'acquisizione, ma non sono sicuro che InMemoryRandomAccessStream ti consenta di farlo. Un'altra alternativa è quella di acquisire il flusso di anteprima e scrivere un buffer multimediale personalizzato sul buffer (più difficile da fare come codice non gestito e probabilmente non in grado di comprimere altrettanto facilmente) come fa l'esempio di Simple Communication.

3) MP4 è un contenitore non un formato di compressione e non è progettato per lo streaming poiché l'intero file deve essere scaricato prima dell'avvio a meno che il record dei metadati moov sia posizionato all'inizio del file. Non sei sicuro di come sia gestito da UWP, è probabile che il tuo approccio di chiusura dello stream prima di inviarlo sia necessario per garantire che l'altro capo possa riprodurlo correttamente.

Quindi non una risposta completa ma si spera che quanto sopra aiuti.

+0

Sto provando a fare qualcosa di simile, e anche se posso clonare e ottenere la comunicazione Simple Real Time che funziona nel loro esempio, non riesco a far funzionare la mia vita usando lo stesso identico codice nella mia app ... – ChadT

+0

Ciao! Ti è capitato di trovare una soluzione funzionante tra il post originale o qualcosa basato sul Simple Communication Sample? Mi piacerebbe fare un po 'di aiuto e se hai fatto passi da gigante dopo questi post mi piacerebbe sentire. –

+0

No, non ho guardato a questo ultimamente, ma quando l'ho fatto l'ultima volta ho intenzione di implementarlo tramite le nuove API ORTC per la riproduzione del browser. – deandob

Problemi correlati