2015-12-22 15 views
15

Ultimamente ho lavorato su un semplice programma di condivisione dello schermo.C# Programma di streaming dello schermo

In realtà il programma funziona su un TCP protocol e utilizza il Desktop duplicazione API - un servizio cool che supporta la cattura schermo molto veloce e anche fornire informazioni su MovedRegions (aree che hanno cambiato solo la loro posizione sullo schermo, ma esistono ancora) e UpdatedRegions (aree modificate).

La duplicazione desktop ha 2 Properties- 2 matrici di byte improtant una matrice per la previouspixels e una matrice NewPixels. Ogni 4 byte rappresentano un pixel nel RGBA forma così per esempio se lo schermo è 1920 x 1080 la dimensione del buffer è di 1920 x 1080 * 4.

riportano i principali dati importanti della mia strategia

  1. Nello stato iniziale (la prima volta) invio l'intero buffer di pixel (nel mio caso è 1920 x 1080 * 3) - il componente alpha è sempre 255 sugli schermi :)
  2. Da ora in poi, iterato su AggiornatoRegioni (è una matrice di rettangoli) e invio i limiti delle regioni e Xo'r i pixel al loro interno qualcosa del genere:

    writer.Position = 0; 
        var n = frame._newPixels; 
        var w = 1920 * 4; //frame boundaries. 
        var p = frame._previousPixels; 
        foreach (var region in frame.UpdatedRegions) 
         { 
          writer.WriteInt(region.Top); 
          writer.WriteInt(region.Height); 
          writer.WriteInt(region.Left); 
          writer.WriteInt(region.Width); 
          for (int y = region.Top, yOffset = y * w; y < region.Bottom; y++, yOffset += w) 
          { 
           for (int x = region.Left, xOffset = x * 4, i = yOffset + xOffset; x < region.Right; x++, i += 4) 
           { 
            writer.WriteByte(n[i]^p[i]); //'n' is the newpixels buffer and 'p' is the previous.xoring for differences. 
            writer.WriteByte(n[i+1]^p[i+1]); 
            writer.WriteByte(n[i + 2]^p[i + 2]); 
    
           } 
          } 
         } 
    
  3. I Comprimere il buffer utilizzando il wrapper lz4 scritto in C# (fare riferimento a [email protected]). Quindi, scrivo i dati su un NetworkStream.
  4. ho fondere le aree del lato ricevitore per ottenere l'immagine aggiornata - questo non è il nostro problema oggi :)

'scrittore' è un'istanza della classe 'QuickBinaryWriter' ho scritto (semplicemente quello di riutilizzare la stessa buffer di nuovo).

public class QuickBinaryWriter 
{ 
    private readonly byte[] _buffer; 
    private int _position; 

    public QuickBinaryWriter(byte[] buffer) 
    { 
     _buffer = buffer; 
    } 

    public int Position 
    { 
     get { return _position; } 
     set { _position = value; } 
    } 

    public void WriteByte(byte value) 
    { 
     _buffer[_position++] = value; 
    } 


    public void WriteInt(int value) 
    { 

     byte[] arr = BitConverter.GetBytes(value); 
     for (int i = 0; i < arr.Length; i++) 
      WriteByte(arr[i]); 
    } 

} 

Da molte misure, ho visto che i dati inviati è davvero enorme, e, a volte per un singolo aggiornamento fotogramma i dati potrebbero arrivare fino a 200KB (dopo la compressione!). Siamo sinceri: 200kb non è proprio nulla, ma se voglio scorrere lo schermo in modo fluido e poter guardare in alta frequenza Fps dovrei lavorare un po 'su questo - a minimizzare il traffico di rete e l'utilizzo della larghezza di banda .

Sto cercando suggerimenti e idee creative per migliorare l'efficienza del programma, principalmente i dati inviati sulla parte di rete (imballandola in altri modi o qualsiasi altra idea), apprezzerò qualsiasi aiuto e idee. Grazie.

+0

tuo la domanda è un po 'vaga. È necessario specificare quale parte si desidera ottimizzare. In questo momento la domanda ha troppe risposte potenziali, che possono portare a un calo dei voti e essere messe in attesa per essere troppo ampie. Darò un esempio di quanto ampio. Vuoi ottimizzare il codice, come invia i dati, la compressione o come aggiorna lo schermo? – dakre18

+0

@ dakre18 grazie per l'attenzione, pricipalmente cerco per la compressione dei dati - ho bisogno di concentrarsi sulla minimizzazione del networktrafic - magari imballando i dati grafici in altro modo ...non so che è quello che ho scritto la mia domanda :) – Slashy

+0

Hai già fatto questa domanda prima. – harold

risposta

16

Per il vostro schermo di 1920 x 1080, con 4 colori di byte, si sta cercando di circa 8 MB per fotogramma. Con 20 FPS, hai 160 MB/s. Quindi ottenere da 8 MB a 200 KB (4 MB/s 20 FPS) è un grande miglioramento.

Vorrei attirare la vostra attenzione su alcuni aspetti su cui non sono sicuro che vi stiate concentrando, e speriamo che ciò sia d'aiuto.

  1. Quanto più si comprimere l'immagine dello schermo, più il trattamento potrebbe essere necessario
  2. In realtà è necessario concentrarsi su meccanismi di compressione progettato per la serie di immagini in continuo cambiamento, simile a codec video (sans audio però). Ad esempio: H.264
  3. Ricordare che è necessario utilizzare un tipo di protocollo in tempo reale per il trasferimento dei dati. L'idea alla base di questo è che, se uno dei tuoi fotogrammi raggiunge la macchina di destinazione con un ritardo, potresti anche rilasciare i prossimi fotogrammi per recuperare il ritardo. Altrimenti sarete in una situazione perennemente in ritardo, che dubito che gli utenti possano apprezzare.
  4. È sempre possibile sacrificare la qualità per le prestazioni. Il meccanismo più semplice che si vede in tecnologie simili (come MS Remote Desktop, VNC, ecc.) È di inviare un colore a 8 bit (ARGB ciascuno di 2 bit) invece del colore a 3 byte che si sta utilizzando.
  5. Un altro modo per migliorare la situazione sarebbe concentrarsi su uno specifico rettangolo sullo schermo che si desidera trasmettere, invece di eseguire lo streaming dell'intero desktop. Questo ridurrà le dimensioni del telaio stesso.
  6. Un altro modo sarebbe quello di ridimensionare l'immagine dello schermo su un'immagine più piccola prima di trasmetterla e ridimensionarla alla normalità prima di visualizzarla.
  7. Dopo aver inviato la schermata iniziale, è sempre possibile inviare il diff tra newpixels e previouspixels. Inutile dire che lo schermo originale e lo schermo diff saranno tutti compressi/decompressi in LZ4. Ogni tanto dovresti inviare l'array completo al posto del diff, se usi un algoritmo lossy per comprimere il diff.
  8. Does UpdatedRegions, hanno aree sovrapposte? Può essere ottimizzato per non inviare informazioni duplicate sui pixel?

Le idee sopra possono essere applicate l'una sull'altra per ottenere un'esperienza utente migliore. In definitiva, dipende dalle specifiche della tua applicazione e dagli utenti finali.

EDIT:

+0

grazie per l'aiuto. Pochi punti come commento. 1.Io per implementare una semplice compressione rle per le diverse regioni. (Dopo aver sdrammato i buffer), pensi che possa ottenere buoni risultati? 2.per la parte di qualità - intendevi veramente 2 bit per canale? ? Significa un massimo di 2^2 opzioni. . I pixel saranno così corrotti dalla loro fonte. – Slashy

+0

3. Sto puntando allo streaming desktop completo in questo momento. ;) 4.scalare l'immagine può anche distruggere la qualità in maniera massiccia - magari trasferire la regione come Jpeg sarebbe una buona idea.8. Non ci sono aree sovrapposte;) l'API desktop funziona piuttosto bene - questa è l'unica cosa che ho soddisfatto . – Slashy

+1

Dovrai vedere se rle funziona meglio di lz4 nel tuo caso, ma ne dubito. Se si usano 2 bit per colore, non avrà un valore di 8 bit per colore, ma i requisiti di larghezza di banda saranno ridotti di 4. È possibile utilizzare 4 bit per colore e verificare se è accettabile. Dovrai fare quella chiamata.Come ho accennato nel post, per ridurre la larghezza di banda, dovresti considerare RTP con opzioni di codifica video con perdite – Vikhram

1

slashy,

Dal momento che si sta utilizzando un res alti frame e volete un buon frame rate è molto probabile che andando a essere guardando la codifica H.264. Ho fatto un po 'di lavoro con i video broadcast HD/SDI che dipendono totalmente da H.264, e un po' spostandomi su H.265. La maggior parte delle librerie usate in broadcast sono scritte in C++ per la velocità.

mi piacerebbe suggerire a guardare qualcosa di simile https://msdn.microsoft.com/en-us/library/windows/desktop/dd797816(v=vs.85).aspx

+0

ho sentito parlare di quel codificatore molto tempo fa, ma in che modo posso implementarlo nel mio progetto C#? grazie per l'attenzione :) – Slashy

+1

Il problema che ho incontrato con lo streaming video HiDef in C# è che il framework è solo lento quando si parla di enormi quantità di dati in tempo reale trasmessi su rame. Recentemente ho testato alcuni frame rate per l'output di dati HD/SDI su una scheda encoder HD/SDI molto costosa utilizzando una libreria C# fornita. Il mio framerate massimo era di circa 30 fps senza alcuna grafica o sovrapposizione generata nel mio codice. La maggior parte dei produttori di hardware non fornisce librerie C# per questo motivo, il che significa che sei bloccato a scrivere un wrapper CLI per una libreria C++. O separare il processo di codifica in un'applicazione C++ –

+0

@ AaronThomas lo capisco .. ma cosa intendi con "separare il processo di codifica"? Non penso che ci sia un vero problema di prestazioni qui perché la parte di elaborazione è fatta abbastanza veloce ... – Slashy