WebRTC на Android: как разработать нативный видеочат
Об особенностях технологии разработки видео чатов и конференций WebRTC на Android.
Поможет разработчикам, начинающим интересоваться темой. Полезно управляющим и владельцам бизнеса, задумавшимся о разработке Android приложения с видеосвязью. В коде не разберетесь, но узнаете варианты разработки с плюсами и минусами, получите представление о задачах для программиста.
Вкратце о WebRTC
WebRTC - технология разработки видео чатов и конференций. Она позволяет создавать peer-to-peer соединение между мобильными устройствами и браузерами для передачи медиа потоков. Более подробную информацию, как она устроена и об общих принципах работы можно найти в нашей статье про WebRTC простым языком.
2 способа реализации видеосвязи с WebRTC на Android
- Самый простой и быстрый вариант - воспользоваться одним из множества коммерческих проектов, например, Twilio или LiveSwitch. Они предоставляют свои SDK для различных платформ и реализуют функциональность из коробки, но имеют минусы. Они платные и функционал ограничен: вы сможете сделать только те функции, которые в них есть, а не любые, какие придумаете.
- Другой вариант - использовать одну из существующих библиотек. Этот подход требует большего количества кода, но сэкономит деньги и даст большую гибкость в реализации функциональности. В этой статье мы рассмотрим второй вариант, а в качестве библиотеки используем https://webrtc.github.io/webrtc-org/native-code/android/
Создание соединения
Создание WebRTC соединения состоит из двух этапов:
- Установка логического соединения - устройства должны договориться о формате данных, кодеках и т.п.
- Установка физического соединения - устройства должны знать адреса друг друга
Для начала отметим, что на этапе инициирования соединения, для обмена данными между устройствами, используется сигнальный механизм. В качестве сигнального механизма может выступать любой канал передачи данных - например, сокеты.
Предположим, мы хотим установить видеосвязь между двумя устройствами. Для этого нужно установить логическое соединение между ними.
Логическое соединение
Логическое соединение устанавливается с использованием Session Description Protocol (SDP), для этого один пир:
- Создает объект PeerConnection
- Формирует на SDP-offer - объект, содержащий данные о предстоящей сессии, и при помощи сигнального механизма отправляет его собеседнику.
В свою очередь, другой пир:
- Также создает объект PeerConnection
- При помощи сигнального механизма получает SDP-offer, отравленный первым пиром, и сохраняет его у себя
- Формирует SDP-answer и отправляет его обратно, также при помощи сигнального механизма
Первый пир, получив SDP-answer, сохраняет его у себя.
После успешного обмена объектами SessionDescription, логическое соединение считается установленным.
Физическое соединение
Теперь необходимо установить физическое соединение между устройствами, что чаще всего является нетривиальной задачей. Обычно устройства в интернете не имеют публичных адресов, поскольку располагаются за роутерами и межсетевыми экранами. Для решения этой проблемы в WebRTC используется технология ICE (Interactive Connectivity Establishment).
Важной составляющей ICE являются Stun и Turn сервера. Они служат одной цели - установке связи между устройствами, которые не имеют публичных адресов.
Stun-сервер
Устройство делает запрос к Stun-серверу и в ответ получает свой публичный адрес. Далее, при помощи сигнального механизма отправляет его собеседнику. После того, как собеседник проделает то же самое, устройства узнают расположение друг друга в сети и будут готовы к передаче данных друг другу.
Turn-сервер
В некоторых случаях роутер может иметь ограничение “Symmetric NAT”. Это ограничение не позволит установить прямое соединение между устройствами. В этом случае используется Turn-сервер. Он выступает в качестве посредника и все данные идут через него. Подробнее можно почитать в документации по WebRTC от Mozilla.
Как мы увидели, STUN и TURN сервера играют важную роль в установке физического соединения между устройствами. Именно для этой цели мы, при создании объекта PeerConnection, передаем список с доступными ICE-серверами.
Для установки физического соединения один пир генерирует ICE-кандидаты - объекты, содержащие информацию о том, как устройство может быть найдено в сети, и отправляет их через сигнальный механизм собеседнику
Далее второй пир, через сигнальный механизм получая ICE-кандидаты первого пира, сохраняет их у себя. При этом он также генерирует свои ICE-кандидаты и отправляет их обратно
Теперь, когда пиры обменялись своими адресами, можно приступать к передаче и приему данных.
Получение данных
Библиотека, после установки логического и физического соединений с собеседником, вызовет колбэк onAddTrack и передаст в него объект MediaStream, содержащий VideoTrack и AudioTrack собеседника
Дальше получаем VideoTrack из MediaStream и отобразить его на экране.
Для отображения VideoTrack, необходимо передать ему объект, реализующий интерфейс VideoSink. Для этих целей библиотека предоставляет класс SurfaceViewRenderer.
Для получения звука собеседника не нужно делать ничего дополнительного - библиотека все сделает за нас. Но все же, если хочется более тонкой настройки звука, можно получить объект AudioTrack и через него менять настройки аудио.
Например, так можно замьютить собеседника
Отправка данных
Отправка видео и аудио со своего устройства также начинается с создания объекта PeerConnection и отправки IceCandidate-ов. Но в отличие от создания SDPOffer при получении видеопотока собеседника, в данном случае мы должны сначала создать объект MediaStream, который включает в себя AudioTrack и VideoTrack.
Для отправки своих аудио и видеопотоков, нужно создать объект PeerConnection, после чего при помощи сигнального механизма обменяться IceCandidate и SDP пакетами. Но вместо того, чтобы получить от библиотеки медиапоток собеседника, мы должны получить медиапоток нашего устройства и передать его в библиотеку, чтобы она передала его собеседнику
Теперь нам нужно создать объект MediaStream и передать в него объекты AudioTrack и VideoTrack
Получить AudioTrack можно следующим образом
Получение VideoTrack происходит чуть сложнее. Сначала получаем список всех камер устройства:
Далее нужно создать объект CameraVideoCapturer, который и будет захватывать изображение
Теперь, после получения CameraVideoCapturer начинаем захватывать изображение и добавляем его в MediaStream
После создания MediaStream и добавления его в PeerConnection, библиотека формирует SDP-offer и происходит описанный выше обмен SDP-пакетами через сигнальный механизм. По завершению этого процесса собеседник начнет получать наш видео поток. Поздравляю, с этого момента соединение установлено.
Many-to-Many
Мы рассмотрели вариант подключения one-to-one. WebRTC также позволяет создавать подключения many-to-many. В самом простом варианте делается это точно также как и при подключении one-to-one. Разница в том, что объект PeerConnection, так же как и обмен SDP-пакетами и ICE-кандидатами, делается не один раз, а для каждого участника. У этого подхода есть минусы:
- Устройство сильно нагружается, поскольку ему необходимо отправлять одинаковый поток данных каждому собеседнику
- Реализация дополнительных фич, таких как запись видео, транскодинг и т.д. сложна либо вообще невозможна
В таком случае WebRTC может использоваться вместе с медиасервером, который и берет на себя вышеуказанные задачи. Для клиентской стороны процесс выглядит точно также, как и при прямом подключении к устройствам собеседников, только медиапоток отправляется не всем участниками, а только на медиасервер. Медиасервер уже ретранслирует его другим участникам.
Заключение
Мы рассмотрели самый простой вариант создания WebRTC соединения на Android. Если после прочтения у вас осталось непонимание, просто пройдитесь еще раз по всем шагам и попробуйте реализовать их самостоятельно - как только вы усвоите ключевые моменты, применение этой технологии на практике уже не вызовет затруднений.
Также, для лучшего понимания работы WebRTC можно обратиться к следующим ресурсам:
Документация по WebRTC от Mozilla
Статья Fora Soft о WebRTC простым языком
Статья Fora Soft о безопасности WebRTC.
Михаил, Android-разработчик.
