Peering

Peering / Блог

Как Peering передаёт экран Mac на iPhone

Удалённый рабочий стол это видеопоток с одним необычным требованием: тот, кто смотрит, ещё и управляет. Все решения ниже вытекают из желания держать эту петлю короткой.

Захват: просим у системы картинку

Приложение на Mac получает кадры через ScreenCaptureKit от Apple. Источником может быть весь дисплей, все окна одного приложения или одно окно, и между ними можно переключаться, не разрывая соединение. Поэтому macOS при первом запуске и спрашивает разрешение «Запись экрана»: без него Mac может отдать только чёрную картинку.

Рабочий стол Mac с открытым редактором кода, который в реальном времени передаётся на iPhone с Peering.
Картинка на iPhone это закодированный видеопоток, а не серия снимков экрана.

Кодирование: малая задержка, без перестановки кадров

Кадры попадают в кодировщик VideoToolbox от Apple на Mac, как H.264 или HEVC, в зависимости от того, что iPhone умеет декодировать. Настройки противоположны тем, что у стриминговых сервисов:

Служебная информация для декодера (наборы параметров) отправляется один раз отдельным сообщением и не повторяется в каждом кадре. iPhone, который подключился посреди потока, получает это сообщение, а затем ключевой кадр.

Отправка: кадры можно отбрасывать, ключевые кадры нельзя

Каждый кадр идёт с небольшим 25-байтным заголовком: ключевой ли он, его размер в пикселях и время показа. В релейном или прямом соединении он нарезается на датаграммы примерно по 1080 байт, и каждая запечатывается ChaCha20-Poly1305 ключом, который знают только два устройства. Счётчик в каждой датаграмме питает окно защиты от повторов на 64 записи, так что перехваченный пакет нельзя воспроизвести позже.

Правило при перегрузке прямолинейно: видео можно отбрасывать, ключевые кадры нельзя. Если новый кадр готов, пока старый ещё ждёт отправки, старый заменяется, кроме одного случая: недоставленный ключевой кадр никогда не заменяется обычным. Показать экран таким, какой он сейчас, важнее, чем показать каждый кадр, который когда-либо существовал.

В домашней сети картинка идёт по собственному TCP-соединению, где сеть достаточно надёжна, и это правило срабатывает редко. На каждом сокете включён TCP_NODELAY. Поведение по умолчанию, когда мелкие записи склеиваются в пакет, добавляет десятки миллисекунд на каждом отрезке пути. При скачивании файла это незаметно, а когда вы двигаете курсор, очень заметно.

Восстановление: за потери платим картинкой, а не задержкой

UDP не пересылает потерянное, поэтому потерянная датаграмма означает повреждённый кадр. iPhone её не ждёт. Он продолжает декодировать, и искажение пропадает на следующем ключевом кадре, не позже чем через две секунды. Если после неудачного участка Wi-Fi вы заметили смазанность, это она и есть. Пауза и возобновление картинки немедленно вызывают ключевой кадр.

Если быстрый путь перестаёт доставлять вообще, поток переходит на управляющее соединение с меньшим битрейтом, как описано в статье о соединении, и возвращается, когда быстрый путь оживает.

Показ: под iPhone, а не под Mac

iPhone выбирает уровень качества и может запросить меньше кадров в секунду, а Mac приведёт это к разумному диапазону. Приложение может уместить весь рабочий стол на экране или заполнить им экран, а ещё попросить у Mac виртуальный дисплей по форме iPhone, чтобы широкий монитор не сжимался в вертикальный экран. Касания картинки идут в обратную сторону, об этом рассказывает статья как касание становится кликом.

Вопросы

Какой видеокодек использует Peering?

H.264 или HEVC, кодирование на Mac через VideoToolbox, без B-кадров и с настройками малой задержки. iPhone сообщает Mac, что он умеет декодировать.

Почему картинка иногда на миг смазывается?

Потерянный сетевой пакет повреждает кадр, и картинка восстанавливается на следующем ключевом кадре, который приходит не реже чем раз в две секунды. Peering предпочитает короткую смазанность ожиданию повторной отправки.