Материал: Передача потоковых данных на основе WebRTC

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

Транспорт - компоненты для организации подключения и управления пользовательскими сессиями, представляют из себя переработанные и дополненные компоненты Libjingle. Имеют функционал для обхода NAT, прокси и файерволов путем предварительного согласования соединения через STAN и TURN сервера, передачи потока по протоколам RTP или SRTP, работающим поверх TCP и UDP, шифрования потока при передаче по SRTP, управления полосой пропускания.[18]

Движок обработки аудио - компонент, обеспечивающий передачу аудиосигнала от аудиокарты к сетевому интерфейсу и обратно. Он включает в себя аудио кодеки iSAC и Opus, системы эхоподавления и подавления шума. Кодек iSAC является широкополосным аудио кодеком для VoIP и потокового аудио, использует частоту дискретизации 16/32 кГц с адаптивной передачи данных от 12 до 52 Кбит/с. Opus поддерживает постоянный и переменный битрейт от 6 до 510 Кбит/с и частоты дискретизации от 8 до 48 кГц.

Движок обработки видео - компонент для управления видеосигналом, включающий кодек VP8, систему адаптивного видеобуффераJitterBuffer, автоматически подстраивающегося под текущее состояние соединения и систему коррекции изображения. Кодек VP8 имеет повышенную устойчивость к потере пакетов, механизм фильтрации артефактов, который может применяться по-разному к разным частям кадра в зависимости от их изменения, возможность масштабировать декодирование, имеет профили, оптимизированные для проведения видео-конференций в реальном времени.

C++ API имеет два основных интерфейса: Stream API и PeerConnection, которые, в свою очередь, подразделяются на несколько дочерних интерфейсов. Stream API предназначен для получения медиапотока от аппаратного обеспечения клиента (веб-камера/микрофон) или преобразования в поток данных, которые предоставит пользователь, например, для вещания заготовленного пользователем файла и обработки видео и аудио потоков. PeerConnection API предназначен для согласования и создания соединения, обхода NAT, файервола или прокси,  и передачи потока по сети.[18] Общая схема работы представлена на рисунке 2.

          Рисунок 2 - Схема взаимодействия с C++ API

Разработчики приложений могут внедрить в свои приложения C++ API и обеспечить к нему доступ через высокоуровневое Web API на JavaScript, которое описывается стандартом W3C. Стандарт Web API от W3C на данный момент имеет стабильную версию 1.0. Разработчики конечных клиентских приложений могут использовать Web API для доступа к функциям WebRTC, не вдаваясь в подробности внутренней архитектуры WebRTC и концентрируя внимание на архитектуре своего web-приложения. Далее в данной работе будут описываться методы Web API, практическая часть будет основываться на нем же. Web API, также как и C++ API, имеет два основных интерфейса: MediaStream и RTCPeerConnection.

Одной из интересных особенностей MediaStream, является то, как он работает с аудио и видео, принимая данные от каждого устройства и передавая его в виде потока. При получении аудио и видео MediaStream работает отдельно с каждым потоком. Таким образом, есть возможность отправлять или получать от других клиентов сразу несколько независимых потоков. Это особенно актуально для портативных устройств, таких как планшеты и смартфоны, которые могут иметь более одной web-камеры (задняя и фронтальная) или микрофона. MediaStream не обязательно должен быть отправлены через Интернет. Поток может быть использован локально, преобразован, сконвертирован или же записан в файл.

RTCPeerConnection занимается установкой прямого подключения и управляет потоками между клиентами. Он может быть ассоциирован с двумя наборами потоков: входящих и исходящих. Исходящий поток отправляется в объект RTCPeerConnection от MediaStream и вещается другим клиентам, входящий поток передается обратно в MediaStream для обработки и отображения в пользовательском интерфейсе, если это необходимо.

1.3 Использование Web API

1.3.1 Создание подключения

          Для того, чтобы создать объект WebRTC-подключения, необходимо вызвать конструктор RTCPeerConnection, который принимает два аргумента в качестве параметров: объект со списком ICE серверов и объект с параметрами подключения. Конструктор вернет объект подключения, на котором будут вызываться все остальные описанные ниже методы.[10]

          Несмотря на то, что WebRTC позволяет реализовать прямую передачу данных между браузерами, необходимо использовать промежуточные сервера для координации соединения, обхода NAT и файерволов. Signaling, процесс координации подключения, необходим для того, чтобы браузеры перед началом передачи данных могли обменяться служебной информацией. Эта служебная информация передается от клиента к клиенту в формате SDP и может содержать:

● сообщениями управления сессиями, которые необходимы для открытия или закрытия сеанса связи;

● сообщениями об ошибках;

● метаданными, такими как информация о кодеках, ширине полосы пропускания и типе передаваемых данных;

● ключевыми данными для установки защищенного соединения;

● сетевой информацией, такой как IP-адрес и порт.[13]

Пример SDP при координации WebRTC-соединения для передачи аудио и видео показан на рисунке 3.

            Рисунок 3 - Пример SDP-сообщения

SDP генерируется методами объекта RTCPeerConnectioncreateOfferи createAnswer. Первый создает SDP для запроса подключения, второй - SDP для ответа. Когда клиент получает от другого клиента запрос на подключение, он может отправить ответ, сгенерированный createAnswer. После того как оба клиента сохранят полученные друг от друга SDP методом setRemoteDescriptionобъекта RTCPeerConnection, а свои собственные SDP сохранят методом setLocalDescription, соединение между этими клиентами будет создано.Процесс отправки и принятия SDP-сообщений может быть реализован при помощи серверных сокетов или баз данных реального времени.

Большинство пользователей в Интернет защищены несколькими слоями NAT, могут иметь антивирусное программное обеспечение, блокирующее определенные порты и протоколы или выходят через прокси и файерволы  и поэтому не могут обмениваться информацией. Файервол и NAT может быть реализован одним и тем же устройством, таким как обычный Wi-Fi роутер.  WebRTC приложения могут использовать ICE, чтобы обойти ограничения, наложенные NAT и файерволами.[13] Для этого необходимо задать объекту RTCPeerConnection список STUN и TURN серверов, как это показано на рисунке 4.

            Рисунок 4 - Создание RTCPeerConnection со списком ICE-серверов

ICE будет пытаться найти наилучший путь для подключения. Он попробует все возможности и выбирает наиболее эффективный вариант. ICE сначала попытается установить соединение с использованием адреса, полученного от операционной системы и сетевой карты. Если это не удается (клиент находится за одним или несколькими NAT), ICE получает внешний адрес, используя сервер STUN, и если это также не удается, трафик направляется через TURN сервера. Другими словами:

● STUN сервер используется для получения внешнего сетевого адреса клиента, находящегося за NAT;

● TURN сервер используются для передачи трафика, если прямое соединение не удается.

1.3.2 Получение локального потока

          Получение доступа и получение медиа потоков от пользователя реализуется средствами, которые предоставляет MediaStream API. Основнной метод MediaStream - метод getUserMedia, вызываемый на глобальном объекте navigator. getUserMedia принимает в качестве параметров три аргумента. Первый указывает на то, какие данные необходимо получить - аудио, видео или оба. Второй аргумент - функция, которая будет вызвана при удачном получении потока, аргументом этой функции будет объект потока. Третий аргумент - функция, которая будет вызвана при возникновении ошибки либо в том случае, если пользователь запретит взятие медиа потоков. После вызова getUserMedia браузер запросит у пользователя разрешение на взятие потока. Имя метода getUserMedia отличается в разных браузерах и вызывается с префиксами, потому что WebRTC еще не до конца стандартизирован. Так, в MozillaFirefox, getUserMedia вызывается с префиксом moz, в Chrome - с префиксом webkit.[12] Простой пример вызова getUserMedia для получения аудио и видео на рисунке 5.

            Рисунок 5 - Вызов getUserMedia

После того, как локальный поток получен, он может быть показан пользователю. Это реализуется средствами HTML5. Видео может быть отображено на web-странице путем добавления HTML-элемента video, в который добавлен поток через свойство src как показано на рисунке 6.

            Рисунок 6 - Прикрепление потока

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

1.3.3 Отправка и получение потоков

          Для отправки локального потока, полученного из getUserMedia, другим клиентам, необходимо вызвать метод addStreamобъекта RTCPeerConnection с потоком в качестве параметра.

            Рисунок 7 - Отправка потока в RTCPeerConnection

После координации подключения и создания объекта RTCPeerConnection другой клиент может получить входящий поток, когда сработает событие onaddstreamобъекта RTCPeerConnection. Это событие вернет объект, содержащий поток клиента и информацию о соединении.

            Рисунок 8 - Отлов входящего потока

          Таким образом можно организовать простейший сеанс потоковой передачи данных, используя основные методы Web API WebRTC.

2 ПРОГРАММНАЯ РЕАЛИЗАЦИЯ НА ОСНОВЕ WEB API

          2.1 Web-приложение, внедряемое на сторонний ресурс

          WebRTC позволяет организовывать видеосвязь средствами JavaScript и HTML5, иными словами, конечное клиентское приложение может быть размещено на сайте или web-странице. Первой практической реализацией в рамках данной дипломной работы была попытка создать переносимое web-приложение, которое могло бы быть встроено на сторонний сайт для определенных клиентов. Это было бы полезно, если есть необходимость в потоковой передаче данных между клиентами определенного ресурса, в то время как нет доступа к исходному коду этого ресурса и нет возможности его модифицировать. Например, если функционал реализуется сторонним разработчиком или самими клиентами ресурса. Для реализации этой задачи была выбрана социальная сеть Вконтакте. Чтобы создать некое подобие интеграции приложения на сайт, было выбрано встроить его в браузерное расширение, которое будет запускать основной код при заходе на сайт, а основной исполняемый код WebRTC-приложения разместить на стороннем хранилище, откуда он будет подгружаться в iframe. Весь исходный код находится в приложении Б данной дипломной работы.

Общая структура приложения имеет вид, изображенный на рисунке 9.

Для обмена SDP-сообщениями было решено использовать базу данных Firebase, она будет выполнять роль сигнального сокета. В качестве облачного хранилища для исполняемого JavaScript был выбран GoogleDrive. Это обосновано возможностью получения доступа к файлу по защищенному https протоколу.

          Реализацию подобного приложения можно разбить на основные этапы:

1. написание WebRTC-приложения и размещение на облачном хранилище;

2. создание браузерного расширения, взаимодействующего с сайтом;

3. разбор HTML-верстки необходимых страниц сайта и реализация функций парсинга;

4. интегрирование в расширение кода, взаимодействующего со страницами сайта;

5. подключение внешнего WebRTC-приложения к расширению;

6. внедрение WebRTC-приложения в страницу сайта.

Для реализации WebRTC-приложения была написана JavaScript-библиотека RTCPeerBroadcasting, имеющая функции для создания подключений между клиентами в режиме конференции и передачи потоковых данных. Исходный код библиотеки RTCPeerBroadcasting находится в приложении А.

Для создания объекта подключения в библиотеке реализована функция RTCPeerConnection, принимающая в качестве параметра объект с настройками подключения.

            Рисунок 10 - Объект параметров функции RTCPeerConnection

          Пример такого объекта показан на рисунке 10, где attachStream - объект видео потока, который будет отправлен другому клиенту, onICE - функция-обработчик события подключения нового клиента, отправляющая ему ответ, onRemoteStream - функция-обработчик события получения потока от клиента, onRemoteStreamEnded - функция-обработчик события остановки потока, onOfferSDP и onAnswerSDP - функции отправки запросов и ответов на запрос подключения. Функция RTCPeerConnection при вызове создает объект WebRTC-подключения, отправляет запрос на подключение к другому клиенту и поток, если он задан. После того, как клиент подтвердит подключение и отправит ответ, для установки соединения можно вызвать функцию addAnswerSDP, которая сохраняет SDP клиента.

            Рисунок 11 - Сохранение SDP

Функции onOfferSDP и onAnswerSDP изначально не заданы в коде функции PeerConnection, они задаются при ее вызове. Это сделано для того, чтобы данная библиотека была как можно более переносимой. В WebRTC нет встроенных функций для обмена SDP-сообщениями, поэтому программист может сам выбрать, что использовать для этого: web-сокеты, базу данных или собственный сигнальный сервер.

          Вторая важная функция класса RTCPeerBroadcasting - функция Broadcasting. Эта функция является оберткой для RTCPeerConnection, выполняет роль слоя абстракции и создана для упрощения создания конференц-связи, когда подключенных клиентов более одного. Принимает на вход объект, пример которого показан на рисунке 12.

            Рисунок 12 - Объект параметров Broadcasting

В данном объекте openSocket - функция открытия сокета, в которой необходимо задать функции обмена SDP-сообщениями, onRemoteStream и onRemoteStreamEnded - функции-обработчики начала и завершения потоков, connectToRoom - функция, которая будет вызвана при подключении к уже существующей конференции. При вызове Broadcasting отправляет в сокет SDP и ожидает ответ. Если ответ получен, создается новый объект RTCPeerConnection для нового подключенного клиента и устанавливается соединение. Broadcasting возвращает объект, содержащий методы createRoom, joinRoom, leaveRoom. Метод createRoom создает новую конференцию с указанным именем, после вызова отправляет в сокет сообщение с информацией о новой конференции. Метод joinRoom отправляет запрос на подключение к уже существующей конференции, создает новый объект RTCPeerConnection. Метод leaveRoom запускает цикл по списку всех объектов RTCPeerConnection и на каждом вызывает метод close, который закрывает прямое подключение. В итоге библиотека имеет простой интерфейс для создания конференц-подключений. Эта библиотека является основой для WebRTC-приложения, которое будет подгружаться расширением на страницу Вконтакте.

          Для данного приложения еще необходимо было создать пользовательский интерфейс и функционал для создания некоторого подобия вызовов и ответов на видеозвонок. Так как структура данного приложения предполагает, что приложение будет подгружаться в iframe на странице с другим доменом, то необходимо реализовать механизм обмена данными между родительским окном и содержимым iframe. Для этого был выбран механизм POST-сообщений. Они отправляются в виде строки. Чтобы отправить массив с данными, можно закодировать его в JSON-строку, а при получении декодировать.

            Рисунок 13 - Отлов POST-сообщения

При получении POST-сообщения с необходимыми данными (id звонящего и принимающего, ссылка на фотографию, имя) скрипт запускает новую конференцию, вызывая метод createRoom библиотеки RTCPeerBroadcasting, в качестве имени конференции будет указан id принимающего пользователя. После того, как конференция создана, в базу данных будет записана информация о вызове. Если принимающий вызов пользователь будет в режиме онлайн, то он получит сообщение о вызове из базы данных. Если пользователь отклонит вызов, то в базу данных будет отправлено соответствующее сообщение, которое получит вызывающий. Если пользователь примет вызов, то браузерное расширение откроет iframe и загрузит страницу с WebRTC-приложением. В него также будет отправлено POST-сообщение о принятии вызова, после чего будет вызван метод joinRoom библиотеки RTCPeerBroadcasting. Пользователь будет подключен к конференции и между ним и вызывающим будет установлено прямое подключение.