我正在尝试优化我的屏幕共享应用。我已经使用了几种方法使它更快更稳定,例如只在两帧之间发送增量,并使用Gzip压缩数据。
这是我的客户代码:
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;
}
}
截图:
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;
}
Difference
方法:
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;
}
和SendVarData
函数:
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;
}
这是服务器 - 我刚开始收到一张完整的图片,然后就是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;
}
我认为它的编码很好,但是当我在两台不同的计算机上运行时,我仍然无法获得超过6~7fps(8kb-100kb)的快速稳定的互联网连接,并且在运行两者时最多只能达到11fps客户端和服务器在同一台计算机上。我认为这是因为delta和合并算法的复杂性,但我不知道。
如果有人能建议如何进一步优化它,我将非常感激。
答案 0 :(得分:1)
您可以以不同的方式组织data2以发送更少的数据。 以下“压缩”算法非常基本,但与您的实施相比,它将提供更好的压缩。
通过识别连续差异的开始和结束来搜索差异。当您找到所有像素不同的间隔时,使用2个字节在该间隔之前存储相同数据的长度,然后存储连续差异的数量,最后为每个不同的像素写入3个RGB字节。
在65535个不同像素的情况下,将最大间隔长度阻止为65535并存储间隔值。在存储的间隔之后开始的下一个差异间隔,下一个间隔的相同计数将为0。
在65535个相同像素的情况下,只需写入$ FFFF,然后写入$ 0000,表示不同像素的空序列。
澄清:我的意思是“识别连续差异的开始和结束”?
在上面的例子中,字母标识颜色,即W(白色),P(粉红色),O(橙色): “相同”一词是指数据0和数据1之间的比较(不将data1 [i]与data1 [i-1] 进行比较)。
Data0 = WWPW WOW OOWOW OO OPP
Data1 = WWPW PWP OOWOW PP OPP
你有4个相同的像素(WWPW),后跟一个间隔(长度3),从第4个字符开始,到6日结束,所有像素都不同。然后是五个相同的像素,接着是一个有2个差异的新区间。最后,有几个常见的像素。
data2的输出将是(括号中的文本不是缓冲区的一部分,并解释了之前的缓冲区值:
04 00 (4 identical pixels) 03 00 (3 different pixels coded in next 9 bytes)
Pr Pg Pb (3 bytes RGB code for P) Wr Wg Wb (RGB code for W) Pr Pg Pb (RGB code for P)
05 00 (5 identical pixels) 03 00 (2 different pixels coded in next 6 bytes)
Pr Pg Pb (RGB code for P) ...
答案 1 :(得分:0)
编写差异的代码如下所示。 我让你构建解决相反操作的代码,即读取差异以更新上一张图像。
Data2Index = 0 ; // next index for additions in data2 ;
int idcount = 0 ;
int diffstart = -1 ;
int diffstart = -1 ;
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]));
if (!changed)
{
if (idcount==ushort.MaxValue)
{ // still identical, but there is a limitation on count
// write to data2 the identical count + differencecount equals to 0
AddIdCountDiffCountAndDifferences(idcount,0,0) ;
idcount = 0 ;
}
if (diffstart>0)
{ // after 0 or more identical values, a change was found
// write to data2 the identical count + difference count + different pixels
AddIdCountDiffCountAndDifferences(idcount,diffcount,diffstart) ;
idcount = 0 ;
diffcount= 0 ;
diffstart=-1 ;
}
else identicalcount++ ; // still identical, continue until difference found
}
else if (diffstart<0)
{ // a difference is found after a sequence of identical pixels, store the index of first difference
diffstart=i ; diffcount=1 ;
}
else
{ // different pixel follows another difference (and limitation not reached)
if (diffcount<ushort.MaxVakue) diffcount++ ;
}
else
{ // limitation reached, i.e. diffcount equals 65535
AddIdCountDiffCountAndDifferences(0,diffcount,diffstart) ;
diffstart+=diffcount ;
diffcount=0 ;
}
此处用于填充数据2的过程:
private int Data2Index = 0 ; // to be reset before
private void AddIdCountDiffCountAndDifferences(int idcount,int diffcount,int diffstart)
{
data2[Data2Index++]=(byte)(idcount && 0xFF) ; // low byte of the int
data2[Data2Index++]=(byte)(idcount >> 8 && 0xFF) ; // second byte of the int
data2[Data2Index++]=(byte)(diffcount && 0xFF) ; // low byte of the int
data2[Data2Index++]=(byte)(diffcount >> 8 && 0xFF) ; // second byte of the int
for (int i=0;i<diffcount;i++)
{
data2[Data2Index++]=data1[diffstart+Bpp*i ] ;
data2[Data2Index++]=data1[diffstart+Bpp*i+1] ;
data2[Data2Index++]=data1[diffstart+Bpp*i+2] ;
}
}