Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Screen streaming program

Lately I have been working on a simple screen sharing program.

Actually the program works on a TCP protocol and uses the Desktop duplication API- a cool service that support very fast screen capturing and also provide information about MovedRegions(areas that only changed their position on the screen but still exist) and UpdatedRegions(changed areas).

The Desktop duplication has 2 improtant properties-2 byte arrays an array for the previouspixels and a NewPixels array. Every 4 bytes represent a pixel in the RGBA form so for example if my screen is 1920 x 1080 the buffer size is 1920 x 1080 * 4.

Below are the important highlights of my strategy

  1. In the initial state (the first time) I send the entire pixel buffer (in my case it's 1920 x 1080 * 3) - the alpha component is always 255 on screens :)
  2. From now on, I iterate over the UpdatedRegions (it's a rectangles array) and I send the regions bounds and Xo'r the pixels in it something like this:

        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 Compress the buffer using lz4 wrapper written in c# (refer to lz4.NET@github). Then, I write the data on a NetworkStream.
  4. I merge the areas in the receiver side to get the updated image - this is not our problem today :)

'writer' is a instance of 'QuickBinaryWriter' class i wrote (simply to reuse the same buffer again).

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

}

From many measures, I've seen that the data sent is really huge, and sometimes for a single frame update the data could get up to 200kb (after compression!). Lets be honest-200kb is really nothing,but if i want to smoothly stream the screen and be able to watch in high Fps rate i would have to work on this a little bit - to minimize the network traffic and the bandwidth usage.

I'm looking for suggestion and creative ideas to improve the efficiency of the program- mainly the data sent on the network part (by packing it in other ways or any other idea)i'll appreciate any help and ideas.Thanks.

like image 868
Slashy Avatar asked Dec 22 '15 17:12

Slashy


People also ask

What C is used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

What is C in C language?

What is C? C is a general-purpose programming language created by Dennis Ritchie at the Bell Laboratories in 1972. It is a very popular language, despite being old. C is strongly associated with UNIX, as it was developed to write the UNIX operating system.

Is C language easy?

Compared to other languages—like Java, PHP, or C#—C is a relatively simple language to learn for anyone just starting to learn computer programming because of its limited number of keywords.

What is C full form?

Originally Answered: What is the full form of C ? C - Compiler . C is a general-purpose, high-level language that was originally developed by Dennis M. Ritchie to develop the UNIX operating system at Bell Labs. C was originally first implemented on the DEC PDP-11 computer in 1972.


2 Answers

For your screen of 1920 x 1080, with 4 byte color, you are looking at approximately 8 MB per frame. With 20 FPS, you have 160 MB/s. So getting from 8 MB to 200 KB (4 MB/s @ 20 FPS) is a great improvement.

I would like to get your attention to certain aspects that I am not sure you are focusing on, and hopefully it helps.

  1. The more you compress your screen image, the more processing it might need
  2. You actually need to focus on compression mechanisms designed for series of continuously changing images, similar to video codecs (sans audio though). For example: H.264
  3. Remember, you need to use some kind of real-time protocol for transferring your data. The idea behind that is, if one of your frame makes it to the destination machine with a lag, you might as well drop the next few frames to play catch-up. Else you will be in a perennially lagging situation, which I doubt the users are going to enjoy.
  4. You can always sacrifice quality for performance. The simplest such mechanism that you see in similar technologies (like MS remote desktop, VNC, etc) is to send a 8 bit color (ARGB each of 2 bits) instead of 3 byte color that you are using.
  5. Another way to improve your situation would be to focus on a specific rectangle on the screen that you want to stream, instead of streaming the whole desktop. This will reduce the size of the frame itself.
  6. Another way would be to scale your screen image to a smaller image before transmitting and then scale it back to normal before displaying.
  7. After sending the initial screen, you can always send the diff between newpixels and previouspixels. Needless to say the the original screen and the diff screen will all be LZ4 compressed/decompressed. Every so often you should send the full array instead of the diff, if you use some lossy algorithm to compress the diff.
  8. Does UpdatedRegions, have overlapping areas? Can that be optimized to not send duplicate pixel information?

The ideas above can be applied one on top of the other to get a better user experience. Ultimately, it depends on the specifics of your application and end-users.

EDIT:

  • Color Quantization can be used to reduce the number of bits used for a color. Below are some links to concrete implementations of Color Quantization

    • Optimizing Color Quantization for Images
    • nQuant library
  • Usually the quantized colors are stored in a Color Palette and only the index into this palette is given to the decoding logic

like image 80
Vikhram Avatar answered Oct 25 '22 09:10

Vikhram


Slashy,

Since you are using a high res frames and you want a good frame rate you're likely going to be looking at H.264 encoding. I've done some work in HD/SDI broadcast video which is totaly dependent on H.264, and a little now moving to H.265. Most of the libraries used in broadcast are written in C++ for speed.

I'd suggest looking at something like this https://msdn.microsoft.com/en-us/library/windows/desktop/dd797816(v=vs.85).aspx

like image 38
Aaron Thomas Avatar answered Oct 25 '22 10:10

Aaron Thomas