Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

Фреймворк Jitsi

Глава 10 из книги "Архитектура приложений с открытым исходным кодом", том 1.

Оригинал: "Jitsi", глава 10 из 1 тома книги "The Architecture of Open Source Applications"
Автор: Emil Ivov
Перевод: Н.Ромоданов

10.5. Медиасервис

Когда работа со связью осуществляется в режиме реального времени поверх IP, имеется один важный аспект, о котором нужно иметь представление: протоколы, например, SIP и XMPP, хотя и признаются многими как наиболее распространенные протоколы VoIP, являются, на самом деле, не тем, что переносит голос и видео через Интернет. Эта задача выполняется с помощью протокола реального времени Real-time Transport Protocol (RTP). SIP и XMPP только отвечают за подготовку всего того, что требуется для RTP, например, определения адреса, куда должны отправляться пакеты RTP, форматов, в которых должны быть закодированы аудио и видео (например, кодек), и т.д. На них также возлагается обязанность отслеживать присутствие пользователей, поддерживать собственное присутствие, выполнять телефонный вызов (делать звонок) и многое другое. Именно поэтому протоколы, например, SIP и XMPP, часто называют сигнальными протоколами.

Что это значит в контексте Jitsi? Ну, в первую очередь, это означает, что вы не отправитесь искать какой-нибудь код, в котором происходит обработка аудио или видео потоков в каком-нибудь из пакетов jitsi sip или jabber. Такой код находится в нашем медиасервисе MediaService. MediaService и ее реализация находятся в net.java.sip.communicator.service.neomedia и net.java.sip.communicator.impl.neomedia.

Почему "neomedia"?

«neo» в названии пакета NeoMedia указывает, что он заменяет аналогичный пакет, который мы использовали первоначально, и что затем нам потребовалось его полностью переписать. Это фактически одно из наших эмпирических правил, к которому мы пришли: вряд ли стоит тратить много времени на разработку приложения на 100% того, что потребуется в будущем. Просто нет возможности учесть все факторы, поэтому в любом случае вам обязательно позже потребуется делать изменения. Кроме того, вполне вероятно, что при кропотливом проектированию у вас повысится сложность решений, которые вам никогда не придется воспользоваться, поскольку сценарии, к которым вы будете готовы, никогда не возникнут.

Вдобавок к самому MediaService, есть два других интерфейса, которые особенно важны: MediaDevice и MediaStream.

10.5.1. Захват медиа, потоковая обработка и воспроизведение

Устройства MediaDevice представляют собой устройства захвата и воспроизведения медиапотока, которыми мы пользуемся во время разговора (рис. 10.4). Ваш микрофон и колонки, ваша гарнитура и ваша веб-камер являются примерами таких устройств MediaDevice, но они не единственные. Потоковая обработка на настольном компьютере и совместные звонки с захватом видео в Jitsi с вашего рабочего стола при использовании режима конференц-связи используют устройство AudioMixer для смешивания звука, который мы получаем от активных участников. Во всех случаях, устройства MediaDevice представляют собой лишь один тип MediaType. То есть, все они могут быть только аудио или видео , но не обоих типов. Это означает, что если, например, у вас есть веб-камера со встроенным микрофоном, то Jitsi видит его как два устройства: одно, которое может только снимать видео, и еще одно, которое может захватывать только звук.

Однако, только устройств недостаточно для того, чтобы делать телефонные или видео вызовы. В дополнение к воспроизведению и захвату медиапотока, необходимо также иметь возможность отправлять его по сети. Это тот момент, когда используются интерфейсы медиапотоков MediaStream. Интерфейс MediaStream является тем, что подключается к устройству MediaDevice вашего собеседника. Здесь работа осуществляется с входящими и исходящими пакетами, которыми вы обмениваетесь с собеседником в течение вызова.

Точно также как и с устройствами, один поток может быть ответственен только за один тип MediaType. Это означает, что в случае аудио/видео вызовов Jitsi должен создать два отдельных медиапотока, а затем подключить каждый к соответствующему аудио или видео устройству MediaDevice.

Рис.10.4: Медиапотоки для различных устройств

10.5.2. Кодеки

Другим важным понятием в потоковом медиа является то, что в MediaFormats, также известно как кодеки. По умолчанию большинство операционных систем позволяют записывать звук в формате 48 кГц PCM или в некотором подобном формате. Это то, что мы часто называем «необработанном аудио» («raw audio») и это тот вид аудио, который вы получаете в файлах WAV: отличное качество и огромный размер. Довольно непрактично пытаться передавать аудио через Интернет в формате PCM.

Это то, для чего предназначены кодеки: они позволяют вам представлять и транспортировать аудио и видео различным образом. Некоторые аудио кодеки, например, iLBC, 8KHz Speex или G.729, имеют низкие требования к пропускной способности, но звучат несколько приглушенно. Другие, например, широкополосные Speex и G.722, дают великолепное качество звука, но и требуют большей пропускной способности. Есть кодеки, которые пытаются обеспечить хорошее качество, сохраняя при этом требования к пропускной способности на разумном уровне. Хорошим примером такого кодека является популярный видео-кодек H.264. Компромисс здесь состоит в большом объеме расчетов, необходимых в процессе преобразования. Если вы используете Jitsi для видео вызовов в H.264, то вы обеспечите хорошее качество изображения и ваши требования к пропускной способности будут вполне разумными, но ваш процессор будет работать на максимуме.

Все это очень упрощенно, но идея в том, что выбор кодека всегда является выбором компромиссов. Вы либо жертвуете пропускной способностью, качеством, загрузкой процессора, либо их комбинацией. Людям, работающим с VoIP, редко нужно знать о кодеках больше.

10.5.3. Подключение к провайдерам протоколов

Протоколы в Jitsi, которые в настоящее время имеют поддержку аудио/видео, все используют наши медиасервисы Mediaservices одинаковым образом. Сначала они спрашивают MediaService об устройствах, доступных в системе:

public List<MediaDevice> getDevices(MediaType mediaType, MediaUseCase useCase);

Тип MediaType указывает, интересуют ли нас аудио или видео устройства. Параметр MediaUseCase в настоящее время рассматривается только в случае видео устройств. Это сообщает медиа сервису, хотим ли мы получить устройство, которые можно использовать при обычном вызове (MediaUseCase.CALL), в этом случае он возвращает список доступных веб-камер, или совместно используемые сессии настольного компьютера (MediaUseCase.DESKTOP), в этом случае он возвращает ссылки на настольные компьютеры пользователей.

Следующим шагом является получение списка форматов, доступных для конкретного устройства. Мы делаем это с помощью метода MediaDevice.getSupportedFormats:

public List<MediaFormat> getSupportedFormats();

Как только будет получен этот список, реализация протокола отправляет его другой стороне, которая вернет его подмножество с тем, чтобы указать, какие из них она поддерживает. Этот обмен также известен как модель Предложение/Ответ (Offer/Answer Model) и он часто использует протокол описания сеанса Session Description Protocol или некоторую другую его форму.

После обмена форматами и некоторыми номерами портов и IP-адресами, протоколы VoIP создают, настраивают и запускают медиапотоки MediaStreams. Грубо говоря, эта инициализация выполняется в следующих строках:

// Сначала создается коннектор потока, сообщающий медиасервису, какие сокеты
// должны использоваться, когда медиапоток транспортируется с помощью RTP, а
// управление потоком и статистика сообщений осуществляется с помощью RTCP
StreamConnector connector =  new DefaultStreamConnector(rtpSocket, rtcpSocket);
MediaStream stream = mediaService.createMediaStream(connector, device, control);

// MediaStreamTarget указывает адрес и порт, которые наш собеседник использует
// для медиапотока. В различных протоколах VoIP есть свои собственные способы 
// обмена этой информацией
stream.setTarget(target);

// Параметр MediaDirection сообщает потоку, откуда он поступает, куда он направляется,
// или и то и другое
stream.setDirection(direction);

// Затем мы задаем формат потока. Мы используем тот, который пришел первым в 
// списке, вернувшимся в ответе о согласовании сессии.
stream.setFormat(format);

// Наконец, мы готовы действительно начать получать медиапоток от нашего 
// медиаустройства и направлять его в интернет 
stream.start();

Теперь вы можете помахать в веб-камеру рукой, схватить микрофон и сказать: "Привет, мир!"


Продолжение статьи: 10.6. Сервис пользовательского интерфейса.