在过去的几个月里,我一直在开发一个小屏幕共享项目,它是一个桌面应用程序,基于pct协议。我将展示逻辑和代码,并希望得到审查。我想听听每一个建议。
首先,我使用了一个名为桌面复制API桌面复制API的服务。下面是 C#实现。
使用它,我可以快速访问屏幕缓冲区并访问屏幕的脏区域(矩形将被更新和更改),这样我就不必处理两个镜头(前一个和当前位图)并扫描它们的差异。
在使用了一段时间的桌面API之后,我注意到DirtyRegions并不完全是纯DirtyRegions。这些矩形包含的数据比他们想象的要多。(这可能与桌面刷新有关。我不知道为什么)。例如,如果我只是打开一个小视频,很少有像素在视频中移动,有时它甚至会给我整个视频播放器的界限,甚至更多!
我注意到它对网络流量的大小有很大的影响,所以我决定编写一个小算法来为每个区域本身找到精确的变化的边界。(实际上,我依赖于DirtyRegions属性,我不需要扫描整个位图区域,但每个区域扫描是必要的,以防止发送额外的不必要数据。)
在找到更改后,我只需从当前桌面位图(完整的)中裁剪(使用更改的矩形边界),将其压缩为jpeg,然后发送。
这是查找代码的不同之处:
private unsafe Rectangle GetBoundingBoxForChanges(Bitmap _prevBitmap, Bitmap _newBitmap)
{
BitmapData bmNewData = _newBitmap.LockBits(
new Rectangle(0, 0, _newBitmap.Width, _newBitmap.Height),
ImageLockMode.ReadOnly, _newBitmap.PixelFormat);
BitmapData bmPrevData = _prevBitmap.LockBits(
new Rectangle(0, 0, _prevBitmap.Width, _prevBitmap.Height),
ImageLockMode.ReadOnly, _prevBitmap.PixelFormat);
var strideNew = bmNewData.Width;
var scanNew0 = bmNewData.Scan0;
var scanPrev0 = bmPrevData.Scan0;
foreach (var region in frame.UpdatedRegions)
{
var width = region.Width;
var height = region.Height;
var left = width;
var right = 0;
var top = height;
var bottom = 0;
var pNew = (int*)scanNew0.ToPointer();
var pPrev = (int*)scanPrev0.ToPointer();
for (var y = 0; y < height; ++y)
{
// For pixels up to the current bound (left to right)
//
for (var x = 0; x < left; ++x)
{
// Use pointer arithmetic to index the
// next pixel in this row.
//
if ((pNew + x)[0] != (pPrev + x)[0])
{
// Found a change.
//
if (x < left)
{
left = x;
}
if (x > right)
{
right = x;
}
if (y < top)
{
top = y;
}
if (y > bottom)
{
bottom = y;
}
}
}
// Move the pointers to the next row.
//
pNew += strideNew;
pPrev += strideNew;
}
// Second Pass - The first pass found at
// least one different pixel and has set
// the left & top bounds. In addition, the
// right & bottom bounds have been initialized.
// Adapt the number of pixels scanned from right
// to left so we only scan up to the current bound.
// In addition, there is no need to scan past
// the top bound.
//
// Set the pointers to the first element of the
// bottom row.
//
pNew = (int*)(void*)scanNew0;
pPrev = (int*)(void*)scanPrev0;
pNew += (_newBitmap.Height - 1) * strideNew;
pPrev += (_prevBitmap.Height - 1) * strideNew;
// For each row (bottom to top)
//
for (int y = _newBitmap.Height - 1; y > top; y--)
{
// For each column (right to left)
//
for (int x = _newBitmap.Width - 1; x > right; x--)
{
// Use pointer arithmetic to index the
// next pixel in this row.
//
if ((pNew + x)[0] != (pPrev + x)[0])
{
// Found a change.
//
if (x > right)
{
right = x;
}
if (y > bottom)
{
bottom = y;
}
}
pNew -= strideNew;
pPrev -= strideNew;
}
}
var diffImgWidth = right - left + 1;
var diffImgHeight = bottom - top + 1;
if (diffImgHeight < 0 || diffImgWidth < 0)
{
// Nothing changed
continue;
}
// Return the bounding box.
//
_newBitmap.UnlockBits(bmNewData);
_prevBitmap.UnlockBits(bmPrevData);
Rectangle rect= new Rectangle(left, top, diffImgWidth, diffImgHeight);
return rect;
}在第二方面,客户端接收这些块并将它们合并到初始的大位图上--这就是更新后的屏幕显示的方式。
我做了很多工作,对于差异处理部分,.jpeg压缩,客户端位图的处理(以及屏幕部分的绘制),这些操作都非常快,在我的LAN网络中运行这个项目非常快。
这一切都给我一个简单的结论,这里的主要瓶颈是网络部分。当屏幕上没有大的变化时,甚至不会有网络流量,但是当屏幕上有很大的变化时,每个帧大小(在jpeg压缩之后)都可以达到150 to!
我正在寻找更多的方法或技术,以提高项目的效率,因为它是一种实时项目,它必须工作得相当快。
为了达到这个目标,我已经做了一些事情:
我正在寻找减少网络流量的方法(主要是)。
有人建议我搬到UDP,但实际上不明白为什么和如何(它承诺更快地传输数据吗?)它不是有缓冲区大小限制吗?上一次尝试时,我无法发送大于65k的缓冲区)。
我不是要重新发明轮子什么的。这只是我为自己开发的一个小项目。
发布于 2016-08-26 19:43:51
主回路的最后一部分在我看来不太对。
_newBitmap.UnlockBits(bmNewData);_prevBitmap.UnlockBits(bmPrevData);矩形rect =新矩形(左、顶、diffImgWidth、diffImgHeight);
在循环中释放资源可能是危险的,因为如果您更改算法,您可能会过早地释放它们并遇到异常。
考虑使用一个try/finally,它更清楚地显示您正在做的事情:
BitmapData bmNewData = _newBitmap.LockBits(...);
BitmapData bmPrevData = _prevBitmap.LockBits(...);
try
{
for (...)
{
Rectangle rect = new Rectangle(left, top, diffImgWidth, diffImgHeight);
return rect;
}
}
finally
{
_newBitmap.UnlockBits(bmNewData);
_prevBitmap.UnlockBits(bmPrevData);
}这还保证在出现异常情况下将释放资源。
https://codereview.stackexchange.com/questions/139345
复制相似问题