Хабрахабр

[Перевод] Небольшое расследование: как YouTube использует WebRTC для стриминга

WebRTC — это JavaScript API в современных браузерах для видеозвонков. А еще для голосовых звонков, шаринга экрана, пробития NAT, раскрытия локального адреса и других интересных штук. В последние пару лет крупные игроки начинают переходить с пропиетарных API и расширений браузеров на WebRTC: с его помощью работает Skype for Web, частично — Hangouts, а теперь и возможности YouTube по броадкасту прямо из браузера. Пока только из хрома и с пятисекундной задержкой — но велика беда начало. Под катом мы предлагаем адаптированный для Хабра перевод детективной истории, где эксперты по WebRTC разбирают код клиентской части YouTube и рассказывают нам что и как сделали разработчики из Гугла.
Прошлый Четверг. Залогинившисть в свой YouTube аккаунт, я обнаружил новую иконку камеры с подсказкой «Go Live» в правом-верхнем углу (примечание переводчика: судя по всему, пока раскатано не для всех пользователей. В комментах отметились подписчики YouTube Red, у них есть). Естественно я сразу же ее кликнул, и, похоже, теперь мы можем стримить прямо из браузера. Попахивало WebRTC, так что я привычно открыл chrome://webrtc-internals/ — и таки да, это было WebRTC. Нас как разработчиков всегда интересовали масштабные использования технологии, так что я сразу связался с мастером-реверсером Филипом «Фип» Ханкелем и попросил его покопаться во внутренностях YouTube. Дальше мы можем ознакомиться с результатами его работы.

Служебная страница Хрома, webrtc-internals, сослужила нам хорошую службу еще в далеком 2014 году, когда мы изучали как работает Hangouts, и ничего не мешало нам снова ей воспользоваться. Так как новая регистрация на YouTube недоступна для броадкастов в течении 24 часов, то мы воспользовались дампом, любезно предоставленным Цахи Левент-Леви (примечание переводчика: да-да, тот самый Цахи который выступал у нас на Intercom и которого мы регулярно переводим). Вы можете воспользоваться вот этой тулзой, чтобы загрузить дамп себе в Хром и посмотреть на происходящее глазами WebRTC.

А со стороны сервера у них что-то свое. Судя по тому, что мы увидели, новая фича YouTube использует WebRTC только на стороне клиента для захвата потока видеокамеры. Значит не realtime. Что это значит? Очень ждем, что он вытащит наружу какие-нибудь интересные технические детали. Хотя наш давний и хороший знакомый Крис Кранки говорит, что задержка составляет менее пяти секунд.

А пока углубимся в технические детали, которые смогли вытащить мы…

Вызовы getUserMedia

После импортирования дампа, в самом его начале мы видим вызовы JavaScript API getUserMedia, которые совершает YouTube. По вызовам видно, что сервис скромно хочет камеру в разрешении 1080p:

А еще они делают отдельный вызов getUserMedia для получения микрофона.

На этом скриншоте не видно самого первого вызова getUserMedia, который запрашивает сразу камеру и микрофон, чтобы пользователь увидел только одно оконо подтверждения бразуера вместо двух.

Вызовы RTCPeerConnection

Осмотрев вызовы getUserMedia, можно переходить к вызовам RTCPeerConnection. Если вы хотите узнать больше о WebRTC, рекомендую почитать результаты предыдущего исследования "Как работает Hangouts" или более общую информацию о webrtc-internals на нашем блоге TestRTC blog.

Сервера ICE

По логу видно, что объект RTCPeerConnection создан с пустым списокм ICE серверов (примечание переводчика: неудивительно, что это пока работает только в Хроме. Ёжик бы вообще не дал такой объект создать).

{ iceServers: [], iceTransportPolicy: all, bundlePolicy: balanced, rtcpMuxPolicy: require, iceCandidatePoolSize: 0
}

Далее будет ясно, почему для такого варианта использования не нужны TURN-сервера (примечание переводчика: ICE это «фреймворк», текстовая инструкция как делать peer-to-peer с печальными IP адресами 192.168..., TURN сервера в фреймворке не самое главное. Самое главное это STUN сервера, которые отвечают на фундаментальный вопрос «а какой у меня внешний IP адрес?». Без указания как минимум одного STUN сервера большинство реализаций WebRTC просто не будет работать).

Забавно, что это API объявлено depricated. Далее клиент добавляет MediaStream с помощью API addSteam. Странно, что авторы не используют новое API addTrack, которое доступно начиная с 64-й версии Google Chrome, а в более старых версиях — с помощью полифила adapter.js

Сигнализация и setLocalDescription

После создания объекта RTCPeerConnection клиент создает WebRTC «оффер» со списком всех аудио и видео кодеков, доступных Хрому. Оффер без модификаций устанавливается как описание локального эндпоинта с помощью setLocalDescription. Кстати, отсутствие модификаций означает, что simulcast (одновременное транслирование нескольких потоков с разным качеством видео, позволяет не перекодировать все на сервере, уменьшает задержки и нагрузку) не используется.

Скорее всего они не используются, так как подключаться будет клиент (Хром) к серверу (бэкенду YouTube). В соответствии с логикой работы WebRTC, после вызова setLocalDescription хром предлагает несколько «кандидатов» — вариантов как удаленный компьютер может попробовать подключиться к локальному.

Фильтр по ключевому слову «realtimemediaservice» сетевого лога Хрома показывают нам HTTP запрос и ответ на него. Апдейт: Найти сервер сигналинга и используемый протокол оказалось не очень сложно. Никаких сложные схем, trickle-ice оптимизаций скорости установки соединения и другой магии, все настолько просто, насколько вообще возможно.

setRemoteDescription

Следующим шагом идет вызов setRemoteDescription на основании информации, полученной от сервера. Где, как мы помним, WebRTC не используется. И здесь все становится интересным! SDP, используемый в setRemoteDescription, выглядит так, как будто на другой стороне его сделал Хром или сишная WebRTC-библиотека с полным списком кодеков наперевес. Причем мы точно знаем, что YouTube не использует «ice-lite», как это делает Hangouts.

264 указан как предпочтительный (число 102, см здесь, если интересно, как устроены текстовые пакеты SDP): В полученном со стороны сервера SDP пакете кодек H.

m=video 9 UDP/TLS/RTP/SAVPF 102 96 97 98 99 123 108 109 124

264, кому любопытно можете поискать в дампе по ключевому слову «send-googCodecName». Изучение статистики (частично отображается после загрузки дампа) подтверждает, что используется кодек H.

Кроме SDP ответа, сервер передает Хрому несколько кандидатов для установки подключения:

a=candidate:3757856892 1 udp 2113939711 2a00:1450:400c:c06::7f 19305 typ host generation 0 network-cost 50
a=candidate:1687053168 1 tcp 2113939711 2a00:1450:400c:c06::7f 19305 typ host tcptype passive generation 0 network-cost 50
a=candidate:1545990220 1 ssltcp 2113939711 2a00:1450:400c:c06::7f 443 typ host generation 0 network-cost 50
a=candidate:4158478555 1 udp 2113937151 66.102.1.127 19305 typ host generation 0 network-cost 50
a=candidate:1286562775 1 tcp 2113937151 66.102.1.127 19305 typ host tcptype passive generation 0 network-cost 50
a=candidate:3430656991 1 ssltcp 2113937151 66.102.1.127 443 typ host generation 0 network-cost 50

Мы можем наблюдать IPv4 и IPv6 UDP кандидаты, «ICE-TCP» кандидаты (да, в случае засухи WebRTC может ходить по TCP, хотя очень этого не любит делать) и пропиетарные для Хрома «SSL-TCP», которые мы раньше видели в Hangouts. При таком раскладе TURN сервер никак не улучшит шансы установить подключение, так как в обоих случаях это будет подключения Хрома к реальному IP адресу. Видимо, поэтому TURN сервер и не используется.

Кодеки

Симулкаста нет. Что, вообщем-то, ожидаемо: в хроме нет H264-simulcast кодека. Зато есть баг репорт с печальным отсутствием фидбека. В целом H.264 разумный вабор: кодирующая сторона может использовать видеокарту для облегчения процесса, а большинство плееров смогут воспроизвести этот формат без перекодирования.

Скорее всего функция перекодирования у YouTube уже есть, как часть инфраструктуры, которую они давно используют для стриминга. Тем не менее, совсем без перекодирования обойтись не удастся, так как без симулкаста серверу придется создавать потоки с меньшим битрейтом и разрешением для «слабых» клиентов.

Статистика WebRTC

Сама по себе статистика ничего нового не раскрывает. Самый интересный график это «picture loss indications», PLI — данные, которые присылает сервер (от переводчика: статистика WebRTC интересна тем, что на каждом конце соединения собирается как локальная статистика, так и принимается удаленная. Мы об этом писали на прошлой неделе):

image

Возможно, это сделано, чтобы облегчить серверам YouTube запись или перекодирование видео. pliCount увеличивается каждые 10 секунд и, соответственно, каждые 10 секунд клиент отсылает серверу опорный кадр (keyframe).

Итого

YouTube использует WebRTC как дружественный пользователю способ получить видеопоток с камеры для стриминга. Скорее всего это никак не повлияет на профессиональных стримеров с дорогими настроенными ригами, но вот барьер входа для новичков понизит значительно.

Это один из примеров запуска Гуглом решений, которые работают только в Хроме. К сожалению, фича не работает в Firefox. Тем не менее, с точки зрения WebRTC все должно работать, так что мы еще вернемся к этому вопросу после починки багов фронтенда. Нилс Охлмейер из Mozilla попробовал заставить его работать подделав user agent, но столкнулся с использованием в JavaScript устаревшего API registerElement.

С нетерпением ждем, когда в Хроме уберут префикс. Обновление К сожалению, дополнительное изучение показало, что JavaScript код этой фичи также использует легаси API webkitRTCPeerConnection вместо современного RTCPeerConection.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть