Programme de streaming d’écran C #

Dernièrement, j’ai travaillé sur un programme de partage d’écran simple.

En réalité, le programme fonctionne sur un TCP protocol et utilise l’ API de duplication de bureau , un service génial qui prend en charge la capture d’écran très rapide et fournit également des informations sur MovedRegions (les zones qui ont seulement changé de position à l’écran mais qui existent toujours) et UpdatedRegions (zones modifiées). .

La duplication du bureau a 2 propriétés improtantes: des tableaux de 2 octets, un tableau pour les NewPixels previouspixels et un tableau NewPixels . Tous les 4 octets représentent un pixel sous la forme RGBA . Par exemple, si mon écran est de 1920 x 1080, la taille de la mémoire tampon est de 1920 x 1080 * 4.

Vous trouverez ci-dessous les points saillants de ma stratégie.

  1. Dans l’état initial (la première fois), j’envoie l’ensemble du tampon de pixels (dans mon cas, il s’agit de 1920 x 1080 * 3) – la composante alpha est toujours de 255 sur les écrans 🙂
  2. À partir de maintenant, j’itère sur les UpdatedRegions (c’est un tableau de rectangles) et j’envoie les limites des régions et Xo’r les pixels qu’il contient, comme ceci:

      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 Compressez le tampon en utilisant le wrapper lz4 écrit en c # (reportez-vous à lz4.NET@github ). Ensuite, j’écris les données sur un NetworkStream.
  4. Je fusionne les zones du côté du récepteur pour obtenir l’image mise à jour – ce n’est pas notre problème aujourd’hui 🙂

‘writer’ est une instance de la classe ‘QuickBinaryWriter’ que j’ai écrite (simplement pour réutiliser le même tampon).

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

De nombreuses mesures ont montré que les données envoyées sont vraiment énormes, et parfois, pour une mise à jour unique, les données pouvaient atteindre 200 Ko (après compression!). Soyons honnêtes – 200 Ko n’est vraiment rien, mais si je veux diffuser l’écran en douceur et pouvoir regarder en FPS, je dois travailler un peu dessus pour minimiser le trafic réseau et l’utilisation de la bande passante .

Je recherche des suggestions et des idées créatives pour améliorer l’efficacité du programme – principalement les données envoyées sur la partie réseau (en les emballant d’une autre manière ou de toute autre idée). J’apprécierai votre aide et vos idées. Merci.

Pour votre écran de 1920 x 1080, avec une couleur de 4 octets, vous recherchez environ 8 Mo par image. Avec 20 images par seconde, vous avez 160 Mo / s. Passer de 8 Mo à 200 Ko (4 Mo / s à 20 IPS) représente donc une amélioration considérable.

J’aimerais attirer votre attention sur certains aspects sur lesquels je ne suis pas sûr que vous vous concensortingez, et j’espère que cela aidera.

  1. Plus vous compressez votre image à l’écran, plus le traitement requirejs sera important.
  2. Vous devez vous concentrer sur les mécanismes de compression conçus pour une série d’images en constante évolution, semblables aux codecs vidéo (sans audio toutefois). Par exemple: H.264
  3. N’oubliez pas que vous devez utiliser un protocole en temps réel pour transférer vos données. L’idée sous-jacente est que, si l’un de vos frameworks parvient à la machine de destination avec un décalage, vous pouvez tout aussi bien laisser tomber les prochains frameworks pour jouer le rattrapage. Sinon, vous serez dans une situation de retard persistant, ce que je doute que les utilisateurs vont apprécier.
  4. Vous pouvez toujours sacrifier la qualité pour la performance. Le mécanisme le plus simple que vous voyez dans des technologies similaires (telles que MS Remote Desktop, VNC, etc.) consiste à envoyer une couleur de 8 bits (ARVB de 2 bits chacun) au lieu de la couleur de 3 octets que vous utilisez.
  5. Une autre façon d’améliorer votre situation serait de vous concentrer sur un rectangle spécifique sur l’écran que vous souhaitez diffuser, au lieu de diffuser l’ensemble du bureau. Cela réduira la taille du cadre lui-même.
  6. Une autre solution consisterait à redimensionner l’image de votre écran en une image plus petite avant de la transmettre, puis à la redimensionner à la normale avant de l’afficher.
  7. Après l’envoi de l’écran initial, vous pouvez toujours envoyer le diff entre newpixels et previouspixels . Inutile de dire que l’écran original et l’écran diff seront tous compressés / décompressés avec LZ4. De temps en temps, vous devriez envoyer le tableau complet au lieu du diff, si vous utilisez un algorithme avec perte pour compresser le diff.
  8. Est-ce que UpdatedRegions a des zones qui se chevauchent? Cela peut-il être optimisé pour ne pas envoyer d’informations de pixel en double?

Les idées ci-dessus peuvent être appliquées les unes sur les autres pour une meilleure expérience utilisateur. En fin de compte, cela dépend des spécificités de votre application et des utilisateurs finaux.

MODIFIER:

  • La quantification des couleurs peut être utilisée pour réduire le nombre de bits utilisés pour une couleur. Vous trouverez ci-dessous des liens vers des implémentations concrètes de Color Quantization

    • Optimisation de la quantification des couleurs pour les images
    • Bibliothèque nQuant
  • Habituellement, les couleurs quantifiées sont stockées dans une palette de couleurs et seul l’index de cette palette est atsortingbué à la logique de décodage.

Slashy,

Étant donné que vous utilisez des images haute résolution et que vous souhaitez obtenir une bonne cadence, vous envisagez probablement l’encodage H.264. J’ai travaillé sur des vidéos de diffusion HD / SDI qui dépendent totalement de H.264 et qui évoluent un peu en H.265. La plupart des bibliothèques utilisées en broadcast sont écrites en C ++ pour plus de rapidité.

Je suggérerais de regarder quelque chose comme ceci: https://msdn.microsoft.com/en-us/library/windows/desktop/dd797816(v=vs.85).aspx