C#屏幕流媒体节目
最近我一直在做一个简单的屏幕共享程序。
实际上该程序适用于TCP protocol
并使用桌面复制API – 一种支持非常快速的屏幕捕获的酷服务,还提供有关MovedRegions
(仅改变其在屏幕上的位置但仍然存在的区域)和UpdatedRegions
(更改区域)的信息。
桌面复制有2个重要属性 – 2个字节数组,一个用于previouspixels
一个NewPixels
数组和一个NewPixels
数组。 每4个字节代表一个RGBAforms的像素,例如,如果我的屏幕是1920 x 1080,则缓冲区大小为1920 x 1080 * 4。
以下是我的策略的重要亮点
- 在初始状态(第一次)我发送整个像素缓冲区(在我的情况下它是1920 x 1080 * 3) – alpha组件在屏幕上始终为255 🙂
-
从现在开始,我遍历UpdatedRegions(它是一个矩形数组),我发送区域边界和Xo’r像素,如下所示:
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]); } } }
- 我使用用c#编写的lz4包装器压缩缓冲区(参见lz4.NET@github )。 然后,我在NetworkStream上写入数据。
- 我合并了接收器端的区域以获得更新的图像 – 这不是我们今天的问题:)
‘writer’是我编写的’QuickBinaryWriter’类的一个实例(只是为了再次重用相同的缓冲区)。
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]); } }
从许多方面来看,我已经看到发送的数据非常庞大,有时对于单帧更新,数据可能达到200kb(压缩后!)。 说实话 – 200kb真的没什么,但如果我想平滑地流式传输屏幕并且能够以高Fps速率观看,我将不得不对此进行一点点工作 – 最小化网络流量和带宽使用 。
我正在寻找建议和创意,以提高计划的效率 – 主要是在网络部分发送的数据(通过其他方式包装或任何其他想法)我会感激任何帮助和想法。谢谢。
对于屏幕为1920 x 1080的4字节颜色,您每帧大约需要8 MB。 使用20 FPS,您可以达到160 MB / s。 因此从8 MB到200 KB(4 MB / s @ 20 FPS)是一个很大的改进。
我想提醒你注意我不确定你关注的某些方面,希望它有所帮助。
- 压缩屏幕图像越多,可能需要的处理就越多
- 实际上,您需要专注于为一系列连续变化的图像设计的压缩机制,类似于video编解码器(尽管没有音频)。 例如:H.264
- 请记住,您需要使用某种实时协议来传输数据。 这背后的想法是,如果你的一个框架使其延迟到目的地机器,你可以放下接下来的几帧来追赶。 否则你将处于一个长期滞后的境地,我怀疑用户会喜欢。
- 你总是可以牺牲质量来提高性能。 您在类似技术(如MS远程桌面,VNC等)中看到的最简单的此类机制是发送8位颜色(每个2位的ARGB)而不是您正在使用的3字节颜色。
- 改善您的情况的另一种方法是关注屏幕上您要流式传输的特定矩形,而不是流式传输整个桌面。 这将减小框架本身的尺寸。
- 另一种方法是在传输之前将屏幕图像缩放为较小的图像,然后在显示之前将其缩放回正常。
- 发送初始屏幕后,您始终可以在
newpixels
和previouspixels
之间发送差异。 不用说原始屏幕和差异屏幕都将被LZ4压缩/解压缩。 如果你使用一些有损算法来压缩差异,你应该经常发送完整的数组而不是diff。 - UpdRegions是否有重叠区域? 可以优化以不发送重复的像素信息吗?
上述想法可以一个接一个地应用,以获得更好的用户体验。 最终,它取决于您的应用程序和最终用户的具体情况。
编辑:
-
颜色量化可用于减少用于颜色的位数。 以下是颜色量化的具体实现的一些链接
- 优化图像颜色量化
- nQuant图书馆
-
通常,量化的颜色存储在调色板中,并且只有该调色板的索引被提供给解码逻辑
Slashy,
由于您使用的是高分辨率帧,并且您希望获得良好的帧速率,因此您可能会考虑采用H.264编码。 我已经完成了HD / SDI广播video的一些工作,这完全取决于H.264,现在有点转向H.265。 广播中使用的大多数库都是用C ++编写的,以提高速度。
我建议看看这样的东西https://msdn.microsoft.com/en-us/library/windows/desktop/dd797816(v=vs.85).aspx