Как стать автором
Обновить

Пишем свой протокол поверх UDP

Время на прочтение 24 мин
Количество просмотров 40K
Всего голосов 55: ↑53 и ↓2 +51
Комментарии 44

Комментарии 44

НЛО прилетело и опубликовало эту надпись здесь
> webrtc умеет восстанавливать потерянные пакеты rtcp nack pli
PLI — несоколько не о том (запрос кейфрейма),
NACK у меня не заработал (но я не сильно старался)

А вот RTX вполне есть как минимум в хроме.
НЛО прилетело и опубликовало эту надпись здесь
Добрый день, у нас не было цели «изобрести» что-то новое ;)
мы решали задачу мобильного стриминга с низкой задержкой в FullHD, и то что из это вышло описано в докладе

очевидно, что на TCP хорошо это делать не получится: head-of-line-blocking, и накопление буфера, отсутсвие приоритезации…

от webRTC отказались, потому что:
1) webRTC сложный протокол для звонков, в котором очень много всего лишнего, что не требуется для ведения трансляций: например AEC (Acoustic Echo Cancellation), STUN/TURN…

2) в webRTC не возможна смена разрешения не лету в одностороннем порядке, нет приоритезации audio

3) webRTC умеет восстанавливать потерянные пакеты, но звонки это прежде всего это история про lowLatency и нам не удалось избавится от эффектов («рассыпания» или «замораживания») изображения на webRTC при изменения пропускной способности канала (а блоггеры часто ведут свои трансляции в движении), поэтому OKTP аккуратнее работает с каналом: делает промер канала избыточными пакетами и обратную связь по packetloss и по latency на канале

4) в webRTC не просто сделать стриминг 1 к N (где N ~ 1 млн, если вникнуть в сам протокол и вспомнить, что каждый зритель, так может попросить персональный опорный кадр)

5) мы поддержали IP migration, т.е. смена IP стримера происходит прозрачно (это важная вещь для мобильного стриминга в движении)

другие предложения:
5) VP{8,9} на iOS реализованы софтварно и съедают аккумулятор больше чем h264 (по независимым тестам, OKLive использует батарею меньше конкурентов)

6) HLS, для стриминга с клиента кажется не лучшей затеей: если внутри MPEG-TS (то пакеты по 188 байт ведут к росту нагрузки на CPU клиента и сервера), в HLS нельзя сменить разрешение на лету
> т.е. не надо ничего ждать, все сразу можно получать без задержек
в HLS, если вы пришли на стрим между опорными кадрами, то у вас выбор или играть с прошлого или подождать следующего (т.е. выбор или ждать или отставать)

7) OkLive вышел в сторы более 2х лет назад, тогда еще у QUIC даже не было 1ой версии драфта
datatracker.ietf.org/doc/draft-ietf-quic-transport
при этом QUIC не является протоколом стриминга с потяреми!
конечно стримить через него можно
но в OKTP обратная связь по потерянным пакетам и по latency (как у BBR), которая тюнит кодировщик (bitrate, fps, resolution)
очень буду признателен, если перечислите протоколы у которых есть возможность этим управлять ;)

8) udt — это высокопроизводительный протокол передачи данных предназначенный для передачи наборов данных большого объёма на высокой скорости по глобальным сетям
а у нас приложение про мобильный стриминг ;)

9) srt — в начале 2016ого публичной информации про srt не было, в 2017ом появилась первая версия на github
reversecode, как у SRT обратная связь на кодировщик реализована? можно ли менять resolution,fps,bitrate?

из всего выше описанного, следует, что OK сделали правильный выбор, реализовав свой протокол поверх UDP
для решения узкой задачи: стриминг в мобильных сетях в высоком качестве и с низкой задержкой
и мой призыв, что если у кого-то есть узкая задача, то сделать свой протокол будет хорошим решением

буду рад провести тесты с любым приложением стриминга в условиях мобильной сети с переменным качеством (например в поезде )
НЛО прилетело и опубликовало эту надпись здесь
Дополню насчёт решения использовать своё решение вместо webrtc
WebRTC — технология основанная на открытых стандартах которым >20 лет. RFC для RTP был опубликован в 1996, SDP в 1998. Для современных приложений конечно «базовые» варианты протоколов не годятся поэтому было добавлено много расширений, какие-то обязательные, другие опциональные. Со списком расширений можно ознакомиться здесь tools.ietf.org/html/draft-ietf-rtcweb-rtp-usage-26.
В результате наслоений расширений получился протокол, который не совместим со старыми приложениями но при этом тянет много legacy.

Вот пример эволюции webrtc:
RFC 6464: добавлен заголовок в аудио пакеты который указывает уровень звука
RFC 6904: оказалось что этот заголовок передавать в открытом виде несекьюрно, добавили расширение для шифрования предыдущего расширения.
Клиентский код теперь должен поддерживать все возможные комбинации этих расширений, чтобы правильно работать и со старыми и с новыми пирами.

В OK Live нет необходимости поддерживать легаси, поэтому мы решили не использовать этот бутерброд расширений а сделать свой протокол с учётом опыта webrtc.

Но ещё более важным барьером было то что при реализации лайф стриминга нужно решать ещё такой компромис:

задержка <=> заполнение сети

WebRTC настроен под то чтобы уменьшить задержку любой ценой в то время как для OK Live допустим буферинг 1-2 секунды в условиях плохой сети. OKMP позволяет задавать максимальную желаемую задержку, чтобы позволяет добиться оптимальных параметров для текущего сценария/состояния сети.

Для иллюстрации: на некоторых андроид устройствах RTT на wifi плавает от 10 до 1500 мс во время стриминга, при этом процент потерь низкий. WebRTC в таком сценарии понижает качество до самого дна и проталкивает 50-100 kbps, тогда как OK Live выдаёт 1100 kbps.
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
и meerkat ;) но они уже умерли

Очень крутой доклад. alatobol вы не планируете выкладывать протокол в open source?

НЛО прилетело и опубликовало эту надпись здесь

Я прочитал транскрипцию

НЛО прилетело и опубликовало эту надпись здесь

Во-первых, само по себе круто, что сделали свой протокол, а не взяли существующий, в отличие от того же Periscope. Очередное доказательство что инженерия в России зачастую сильнее чем даже в долине.


Во-вторых, доходчиво изложены проблемы / задачи протокола и решения

НЛО прилетело и опубликовало эту надпись здесь
Планируете ли выложить исходный код протокола?
Делаем самый простой вариант — Diffie-Hellman на эллиптических кривых

Вы потом выводите для каждого пакета новый ключ или просто мешинг делаете? Тогда как клиенты выводят ключи при потерях пакетов? Или каждый пакет независимо шифруете на сессионном ключе?

Если последнее, то, насколько я понимаю, управляющие пакеты довольно похожи и, с учетом меняющейся синхропосылки, можно довольно быстро подобрать ключ.
НЛО прилетело и опубликовало эту надпись здесь
Тут не хватает реализации ProtocolBase и UDPPacket. Видимо там скрыта работа с шифрованием пакетов.
НЛО прилетело и опубликовало эту надпись здесь
Я хотел узнать как шифруются пакеты, как часто меняется ключ, как считается нагрузка на ключ, что происходит при потере пакетов, есть ли защита от replay attack, ну и все такое )

Просто у меня сложилось впечатление (возможно неверное), что шифрование тут сделано для галочки в отсутствии сформулированной модели угроз и не защищает от несанкционированного просмотра видео, например.

Использование функций шифрования не означает автоматического получения защиты данных.
НЛО прилетело и опубликовало эту надпись здесь
AES — это просто алгоритм, а шифрование происходит в конкретном режиме (они описаны, например, в NIST SP 800-38A).

На вскидку что с AES можно сделать не так:
1. Использовать режим AES-ECB (поищите фразу «ecb penguin» в интернете).
2. Использовать одинаковую синхропосылку для всех пакетов.
3. Не считать целостность пакетов.
4. Шифровать только данные, которые не обязательны для восстановления видео потока. Например, шифровать только заголовки.
5. Не защищаться от replay attack. Например, если номер пакета не шифруется, то злоумышленник легко сможет повторно посылать старые (даже зашифрованные) данные вместо новых. Как в фильмах, где посылается видео пустого коридора, а в это время там кто-то ходит.

Если вы не защищаетесь от нарушителя уровня спецслужб, то это пока все для начала.
НЛО прилетело и опубликовало эту надпись здесь
1. Ок, но я доклад не смотрел. А в расшифровке доклада упустили про режим шифрования.
2. Синхропосылка — это «nonce» в терминах AES-CTR. Должно быть уникально.
3. crc32 защищает только от случайных искажений. Для защиты от нарушителя нужно считать имитовставку, либо использовать режим AES-GCM, где имитовставка считается одновременно с шифрованием пакета.
4. Для публичного стрима достаточно считать имитовставку (шифровать вообще не нужно) для защиты от подмены в канале, если такая угроза рассматривается. Для непубличного необходимо шифровать все данные и тогда предположительно генерировать групповой ключ, чтобы не перешифровывать для каждого пира. Из текста я так понял, что сами фреймы не шифруются.
5. Понятно.
НЛО прилетело и опубликовало эту надпись здесь
Почему выбран именно crc32?
НЛО прилетело и опубликовало эту надпись здесь
пока из всего описанного выше, не вижу предпосылок, чтобы бежать переделывать то, что больше 2х лет работает в продакшене
НЛО прилетело и опубликовало эту надпись здесь
Вы правы, с шифрованием нужно быть осторожным чтобы не наделать уязвимостей.
В случае OKMP считается хеш от данных, в том числе тех заголовков которые не шифруются. Сам хеш затем шифруется.
Для защиты от «пингвина» используется CTR и Initialization Vector. IV уникален для каждого пакета.
Отличный доклад. Мне нравится, что прежде чем изобретать свой протокол, они разобрали существующие, провели измерения итд. Ничего плохого в этом нет, имхо, если свой протокол обеспечивает лучшее качество.

Что касается потерь пакетов «пачками», то я подозреваю, это как-то связано с организацией сотовой сети. Может быть модем передает несколько пакетов в одной группе или в одном тайм слоте и при помехах они все теряются разом. Ну или это может быть связано с перегрузкой оборудования на БС, которое начинает дропать пакеты в этом случае, пока ситуация не выправится.
Я не испытываю иллюзий, что в какой-то перспективе официально опубликуют код.
В докладе вскользь упомянули, что в качестве congestion control они используют гугловский BBR, вот уж что интересно было бы глянуть. Кроме ядра линкуса и QUIC-a никто так и не сделал в открытых проектах. А выглядит очень любопытно.
НЛО прилетело и опубликовало эту надпись здесь
Потеря даже 0,001% пакетов приводит к снижению пропускной способности на 30%.

Один пакет на сто тысяч потерялся — и всё, TCP неэффективен? О_о

Это опечатка, packet loss 0,001 или 0.1% ;) ну и конечно проседание TCP будет зависеть от RTT
Круто. Жаль только, такие усилия и на это: youtu.be/FnODfLelQg4?t=4m
Ну, во-первых, с трудом представляется реализация серверов доступа, раздающих чанки hls пакетами mpeg-ts. Не представлю, как вообще можно было насчитать заметный pps при переходе от mpeg-ts к fmp4 чанкам. Раздают-то чанки а не пакеты.
Во вторых, выигрыш от монолитного манифеста mpeg-dash c учетом общего количества обращений к бекенду находится в пределах ошибки измерений. Его плюс вовсе не в монолитности, а в механизме шаблонизации имени чанка, что при соответствующем устройстве плеера позволит загружать чанк, не ожидая изменения манифеста. Похожая логика реализуется в релизациях плееров под HLS с низкой задержкой.
В третьих, константный битрейт в 2018-м году, ну это прям архаика. Вы проигрываете гораздо больше на транскодинге, хранении и картинке, нежели выигрываете в нескольких частных случаях.
Раз уж разговор зашел за битрейт, и, очевидно, про экономию битрейта то четвертых, про B-фреймы: временное расстояние между ними и зависимыми фреймами в рамках остальных задержек остального тракта находятся в рамках 5-10%. Да, понятно, в этом случае возникают дополнительные требования к транспорту. Но это же не повод просто сказать, что вредная и ненужная часть. А так бывает, что транслировать надо не только говорящую голову, отснятую на камеру с кристально чистой картинкой.
В четвертых, часть статьи про udp, коррекцию ошибок и т.п. кране напоминает протокол kcp (https://github.com/xtaci/kcp-go) и даже был какая-то реализация rtmp поверх этого протокола (https://github.com/LiveStudioSolution/joy4)


НЛО прилетело и опубликовало эту надпись здесь
Хорошая подача и интересный топик, прочитал с удовольствием.

На клиентах (Android/iOS) шарили реализацию протокола на плюсах или делали отдельные реализации?

Интересно еще насколько в таких применениях заметна разница в производительности клиента если его писать на Java/etc для Android против C++/etc. По идее, протягивать байтики сквозь сокет должно быть норм и там и там, разве что перенос битмап кадров обратно в нативный хип для рендеринга может дать задержку, но это просто мои рассуждения, может вы сравнивали?
Но мы получим такую картину: если мы начинаем беспорядочно слать UDP пакеты в socket, то по статистике к 21-му пакету вероятность того, что он дойдет, будет всего лишь 85%. То есть packet loss уже будет 15%, что никуда не годится. Это нужно исправлять.

А какова причина этого, почему так происходит? Столкнулся с UDP-протоколами еще в 2010 на примере uTP, вряд ли они стали бы делать, будь такие большие потери.


Исправляется это стандартно. На рисунке проиллюстрирована жизнь без Pacer и жизнь с Pacer.
Pacer — это такая штука, которая раздвигает пакеты во времени и контролирует их потерю; смотрит, какой сейчас packet loss, в зависимости от этого адаптируется под скорость канала.

Что конкретно скрывается за этим названием? Это некая библиотека, чье-то готовое решение? И ведь чтоб делать что-то в зависимости от packet loss, нужно уже иметь некий протокол поверх, который как-то будет его замерять.


Если вы будете писать свой UDP протокол, например, с двухсторонней связью, то нужно понимать, что есть NAT Unbinding и шанс, что вы не сможете обратно с сервера найти клиента. На слайде как раз времена, когда не удалось достучаться до клиента с сервера по UDP. Многие скептики говорят, что маршрутизаторы устроены так, что NAT Unbinding вытесняет в первую очередь именно UDP маршруты. Но выше видно, что если Keep-Alive или ping будет меньше 30 секунд, то с вероятностью 99% будет возможно достичь клиента.

А можно ли увидеть левый нижний угол этого слайда подробнее? Похоже, что потери есть и ниже этого порога; причем еще нужно убедиться, что потеря произошла именно по причине NAT unbinding, а не другой...

Можно чуть поподробнее про Pacer. Как организована равномерность отправки пакетов?

подозреваю что коллеги либо SO_MAX_PACING_RATE на сокет ставят либо SO_TXTIME и потом управляют временем отправки каждого пакета через SCM_TXTIME…

Зарегистрируйтесь на Хабре , чтобы оставить комментарий