I'm trying to optimize my screen sharing app. I've already used a few ways to make it faster and stable, such as only sending the deltas between two frames, and using Gzip to compress the data.
This is my client code:
private void Form1_Load(object sender, EventArgs e)
{
Thread th = new Thread(startSend);
th.Start();
}
private void startSend()
{
Bitmap curr;
Bitmap diff;
encoderParams.Param[0] = qualityParam;
Bitmap pre = screenshot();
bmpBytes = imageToByteArray(pre);
SendVarData(handler, bmpBytes);
while (true)
{
curr= screenshot();
diff= Difference(pre, curr);
bmpBytes = imageToByteArray(diff);
SendVarData(handler, bmpBytes);
pre = curr;
}
}
Screenshot:
public Bitmap screenshot()
{
Bitmap screenshot = new Bitmap(SystemInformation.VirtualScreen.Width,
SystemInformation.VirtualScreen.Height,
PixelFormat.Format24bppRgb);
Graphics screenGraph = Graphics.FromImage(screenshot);
screenGraph.CopyFromScreen(0,
0,
0,
0,
SystemInformation.VirtualScreen.Size,
CopyPixelOperation.SourceCopy);
return screenshot;
}
The Difference
method:
public Bitmap Difference(Bitmap bmp0, Bitmap bmp1)
{
Bitmap bmp2;
int Bpp = 3;
bmp2 = new Bitmap(bmp0.Width, bmp0.Height, bmp0.PixelFormat);
var bmpData0 = bmp0.LockBits(
new Rectangle(0, 0, bmp0.Width, bmp0.Height),
ImageLockMode.ReadOnly, bmp0.PixelFormat);
var bmpData1 = bmp1.LockBits(
new Rectangle(0, 0, bmp1.Width, bmp1.Height),
ImageLockMode.ReadOnly, bmp1.PixelFormat);
var bmpData2 = bmp2.LockBits(
new Rectangle(0, 0, bmp2.Width, bmp2.Height),
ImageLockMode.ReadWrite, bmp2.PixelFormat);
bmp0.UnlockBits(bmpData0);
bmp1.UnlockBits(bmpData1);
bmp2.UnlockBits(bmpData2);
int len = bmpData0.Height * bmpData0.Stride;
// MessageBox.Show(bmpData0.Stride.ToString());
bool changed=false;
byte[] data0 = new byte[len];
byte[] data1 = new byte[len];
byte[] data2 = new byte[len];
Marshal.Copy(bmpData0.Scan0, data0, 0, len);
Marshal.Copy(bmpData1.Scan0, data1, 0, len);
Marshal.Copy(bmpData2.Scan0, data2, 0, len);
for (int i = 0; i < len; i += Bpp)
{
changed = ((data0[i] != data1[i])
|| (data0[i + 1] != data1[i + 1])
|| (data0[i + 2] != data1[i + 2]));
// this.Invoke(new Action(() => this.Text = changed.ToString()));
data2[i] = changed ? data1[i] : (byte)2; // special markers
data2[i + 1] = changed ? data1[i + 1] : (byte)3; // special markers
data2[i + 2] = changed ? data1[i + 2] : (byte)7; // special markers
if (Bpp == 4) data2[i + 3] =
changed ? (byte)255 : (byte)42; // special markers
}
// this.Invoke(new Action(() => this.Text = changed.ToString()));
Marshal.Copy(data2, 0, bmpData2.Scan0, len);
return bmp2;
}
and the SendVarData
function:
int total = 0;
byte[] datasize;
private int SendVarData(Socket s, byte[] data)
{
total = 0;
int size = data.Length;
int dataleft = size;
int sent;
datasize = BitConverter.GetBytes(size);
sent = s.Send(datasize);
sent = s.Send(data, total, dataleft, SocketFlags.None);
total += sent;
dataleft -= sent;
// MessageBox.Show("D");
return total;
}
This is the server - I'm just receiving a full image in the beginning, and then just deltas:
public void startListening()
{
prev = byteArrayToImage(ReceiveVarData(client.Client));
theImage.Image = prev;
while (true)
{
data = ReceiveVarData(client.Client);
curr = byteArrayToImage(data) ;
merge = Merge(prev, curr);
theImage.Image = merge;
count++;
prev = merge;
}
}
public static Bitmap Merge(Bitmap bmp0, Bitmap bmp1)
{
int Bpp = 3;
Bitmap bmp2 = new Bitmap(bmp0.Width, bmp0.Height, bmp0.PixelFormat);
var bmpData0 = bmp0.LockBits(
new System.Drawing.Rectangle(0, 0, bmp0.Width, bmp0.Height),
ImageLockMode.ReadOnly, bmp0.PixelFormat);
var bmpData1 = bmp1.LockBits(
new System.Drawing.Rectangle(0, 0, bmp1.Width, bmp1.Height),
ImageLockMode.ReadOnly, bmp1.PixelFormat);
var bmpData2 = bmp2.LockBits(
new System.Drawing.Rectangle(0, 0, bmp2.Width, bmp2.Height),
ImageLockMode.ReadWrite, bmp2.PixelFormat);
bmp0.UnlockBits(bmpData0);
bmp1.UnlockBits(bmpData1);
bmp2.UnlockBits(bmpData2);
int len = bmpData0.Height * bmpData0.Stride;
byte[] data0 = new byte[len];
byte[] data1 = new byte[len];
byte[] data2 = new byte[len];
Marshal.Copy(bmpData0.Scan0, data0, 0, len);
Marshal.Copy(bmpData1.Scan0, data1, 0, len);
Marshal.Copy(bmpData2.Scan0, data2, 0, len);
for (int i = 0; i < len; i += Bpp)
{
bool toberestored = (data1[i] != 2 && data1[i + 1] != 3 &&
data1[i + 2] != 7 && data1[i + 2] != 42);
if (toberestored)
{
data2[i] = data1[i];
data2[i + 1] = data1[i + 1];
data2[i + 2] = data1[i + 2];
if (Bpp == 4) data2[i + 3] = data1[i + 3];
}
else
{
data2[i] = data0[i];
data2[i + 1] = data0[i + 1];
data2[i + 2] = data0[i + 2];
if (Bpp == 4) data2[i + 3] = data0[i + 3];
}
}
Marshal.Copy(data2, 0, bmpData2.Scan0, len);
return bmp2;
}
I think it's coded fine, but I'm still unable to get more than 6~7fps (of 8kb-100kb) when running on 2 different computers with a fast and stable internet connection, and a maximum of 11fps when running both client and server on the same computer. I think it's because of the complexity of the delta and merging algorithms, but i dont know.
I would very appreciate if anyone could suggest how could optimize it further.