В последнее время я работал над простой программой для совместного использования экрана.
На самом деле программа работает с TCP protocol
и использует API дублирования рабочего стола - классный сервис, который поддерживает очень быструю захват экрана, а также предоставляет информацию о MovedRegions
(области, которые только изменили свое положение на экране, но все еще существуют) и UpdatedRegions
(измененные области).
Дублирование рабочего стола имеет 2 улучшающих свойства - массивы 2 байта - массив для массива previouspixels
и NewPixels
. Каждые 4 байта представляют собой пиксель в форме RGBA, так что, например, если мой экран равен 1920 x 1080, размер буфера равен 1920 x 1080 * 4.
Ниже приведены важные моменты моей стратегии.
- В начальном состоянии (в первый раз) я посылаю весь буфер пикселя (в моем случае это 1920 x 1080 * 3) - на экранах всегда присутствует альфа-компонент:)
-
Отныне я перебираю обновленные области (это массив прямоугольников), и я посылаю области границ и 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]); } } }
- я Сжатие буфера с использованием lz4-оболочки, написанной в С# (см. [email protected]). Затем я записываю данные в 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]);
}
}
Из многих мер я видел, что отправленные данные действительно огромны, и иногда для обновления одного кадра данные могут получить до 200 КБ (после сжатия!). Давайте будем честными - 200kb действительно ничего, но если я хочу плавно перетекать на экран и быть в состоянии смотреть с высокой скоростью Fps, мне придется немного поработать над этим - до минимизировать сетевой трафик и использование полосы пропускания.
Я ищу предложения и креативные идеи для повышения эффективности программы - в основном, данные, отправленные в сетевой части (путем их упаковки другими способами или любой другой идеей), я буду благодарен за любую помощь и идеи. Спасибо.