【发布时间】:2022-01-21 21:47:06
【问题描述】:
我的项目涉及捕获桌面帧并通过 TCP 套接字发送到接收设备进行渲染,我希望尽可能减少帧之间的延迟,最好总共低于 50 毫秒。
发送的数据是一个字节数组的DirectX DataStream,它只是代表每个像素的颜色。
对于 1920x1080 的显示,这会产生一个长度为 8294400 的字节数组,使用 Stream.CopyTo 需要不到 10 毫秒,这是合理的。
使用 LZ4 压缩生成的字节数组将其长度减少到 631126 字节,需要额外的 10 毫秒左右。
我希望通过仅发送更改的像素来进一步减小此大小。
我的第一个想法是使用Dictionary<int,int> 来存储上一帧发送的像素的缓存,然后与新帧进行比较:
private Dictionary<int, int> CachedPixels = new Dictionary<int, int>();
...
DataRectangle dataRect = surface.Map(SharpDX.DXGI.MapFlags.Read, out DataStream dataStream);
using(MemoryStream ms = new MemoryStream()) {
using(BinaryReader br = new BinaryReader(dataStream))
int pixels = (int)(dataStream.Length / 4); //4 bytes per BGRA colour
Dictionary<int, int> changedPixels = new Dictionary<int, int>();
for(int i = 0; i < pixels; i++) {
int col = br.ReadInt32();
if(!CachedPixels.ContainsKey(i)) {
CachedPixels.Add(i, col);
changedPixels.Add(i, col);
} else {
if(col != CachedPixels[i]) {
CachedPixels[i] = col;
changedPixels.Add(i, col);
}
}
}
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(ms, newCache);
return LZ4.Compress(ms.ToArray());
但这真的很慢。
如果我使用StopWatch 计算所用时间:
所有 2073600 像素的第一帧循环耗时 170 毫秒,使用BinaryFormatter 进行序列化耗时 2100 毫秒。
然后下一帧更新 379902 像素,循环需要 75 毫秒,序列化需要 356 毫秒。
我该如何优化这个?
【问题讨论】:
-
几年前,我做了类似的东西,但不是为了图像。我会将整个区域视为许多小块(例如,64x64 像素)。对于每一帧,计算每个块的哈希值,然后将它们保存在矩阵中。此时,您可以检测哈希是否更改并发送相关的块内容。不过,总的来说,这不是一件容易的事。
-
为什么不直接让 ffmpeg 捕获您的桌面并进行流式传输呢?这样你就不会最终重新实现 ffmpeg
-
简单来说,由于gdigrab/dshow的限制,我的用例不支持ffmpeg。
标签: c# arrays dictionary optimization binaryformatter