Аудио конференции бывают разные, как и задачи, которые они решают: централизованные (на сервере), клиентские, распределенные. В нашем случае мы рассмотрим первые два варианта — централизованные на стороне облака VoxImplant и клиентские, сделанные прямо в браузере с использованием WebAudio и WebRTC (да-да, и такое уже стало возможно!). У обоих вариантов есть свои плюсы и минусы, которые мы рассмотрим подробнее под катом, а также расскажем о том как их использовать и о подводных камнях (куда же без них!).
Серверные конференции
Из названия следует, что микширование аудио потоков происходит на стороне сервера. Для каждого участника конференции создается свой микс, в котором есть все участники кроме него самого (вы же не хотите слушать свое эхо). К тому же, у конференций есть еще ряд параметров, которые влияют на качество звука. Например, частота дискретизации, на которой она работает. В случае VoxImplant у нас есть 2 варианта — обычные и HD. В обычных частота 8KHz и они лучше всего подходят для объединения звонков из телефонной сети, там выше 8KHz все равно не получится. В случае HD мы пошли по пути создания максимального качества, и поэтому в данном случае миксуем уже на 48KHz (максимум для WebRTC в браузере). Так как используются серверные ресурсы, то сделать такие конференции бесплатными сложно, железо и трафик пока еще что-то стоят 🙂
Во время создания серверных конференций пришлось использовать всякие разные инновационные технологии, которые хорошо давят шум (NR), эффективно определяют говорящих (VAD) и так далее, все это самым прямым образом влияет как на качество звука, так и на масштабируемость: кодирование и декодирование потоков никто не отменял (микширование и ресэмплинг — не самые сложные задачи). Мы в первую очередь ориентируемся на WebRTC, поэтому основной ходовой кодек у нас Opus, но подключиться можно и из SIP с любым из следующих на выбор: G.711, Speex (и Opus).
Конференция на стороне VoxImplant создается следующим образом (сценарий VoxEngine):
// Подключаем модуль конференций require(Modules.Conference); var conf = null, calls = []; // При старте сессии создаем конференцию VoxEngine.addEventListener(AppEvents.Started, function(e) { if (conf === null) { // hd_audio определяет будет HD или не-HD конференция conf = VoxEngine.createConference({ hd_audio: true }); } }); // Входящие звонки подключаем к конференции VoxEngine.addEventListener(AppEvents.CallAlerting, function(e) { e.call.addEventListener(CallEvents.Connected, handleCallConnected); e.call.addEventListener(CallEvents.Disconnected, handleCallDisconnected); e.call.answer(); }); // Соедияем аудио поток конференции и звонка function handleCallConnected(e) { VoxEngine.sendMediaBetween(conf, e.call); calls.push(e.call); } // Если все звонки отключатся, то можно убить сессию function handleCallDisconnected(e) { var index = calls.indexOf(e.call); if (index > -1) calls.splice(index, 1); if (calls.length === 0) VoxEngine.terminate(); }
Звонки туда направляются с помощью функции callConference, поэтому придется сделать отдельный сценарий, который форвадит звонки в конференцию из разных источников (PSTN, WebSDK, MobileSDK или SIP) и прописать соответствующее правило (Pattern) приложения. Более подробно про работу с конференциями в VoxImplant можно прочитать по данной ссылке.
Чем же хороши серверные конференции? Много участников (по умолчанию до 100 в случае VoxImplant), управление конференцией на стороне сервера (это может быть весьма полезно в ряде случаев), лучшее качество звука. Минусы мы уже перечислили — это не бесплатно, так как требуются серверные ресурсы.
Poor man’s conferencing: конференции на клиенте
Все мы знакомы со Skype и его прекрасной возможностью аудио-конференций. Это тот самый client-side conferencing, хостом выступает создающий конференцию пользователь, соответственно на его компьютере все будут микшироваться. Если интернет или железо у этого товарища окажется не очень, то все будут страдать, но зато это бесплатно! 🙂
После недавних значительных обновлений WebRTC и Web Audio в Chrome и Firefox стало возможно такой же сценарий реализовать прямо на уровне браузера. Я был очень воодушевлен, когда приступил к реализации этой идеи. Но мой пыл несколько поугас после того, как пришлось изрядно повозиться, чтобы это все завелось без лишних эффектов и независимо от браузеров участников (WebRTC пока есть в Chrome/Chromium и Firefox). Начнем с теории…
RTCPeerConnection
Этот прекрасный класс (далее по тексту будем называть его PC) от WebRTC дает нам возможность передавать звук (и видео, но в этот раз без него) в реальном времени, подключая к нему стрим (local stream) от микрофона, через сеть кому-то на другом конце и оттуда получать чужой стрим (remote stream). Изначально в WebRTC все крутилось около MediaStream’ов (тот самый локальный стрим от микрофона это объект данного класса), но сейчас стандарт немного эволюционировал и все сдвинулось в сторону Audio/VideoTracks (для более лучших видео конференций, но про это в другой раз). Что не отменяет работы с классом MediaStream, когда мы переходим в плоскость Web Audio. Мы не будем рассматривать как сделать P2P звонок с помощью WebRTC, про это есть много других статей + на VoxImplant это делается совсем уж просто. Итак, что же мы должны сделать, чтобы смиксовать звук из разных PC и своего микрофона? Начнем с простого:
// предположим, что у нас есть MediaStream от микрофона - такой код будет воспроизводить наш локальный поток средствами Web Audio function gotStream(stream) { // А вот и Web Audio - создаем контекст window.AudioContext = window.AudioContext || window.webkitAudioContext; var audioContext = new AudioContext(); // Web Audio работает с так называемыми audio nodes , которые можно стыковать разными способами var mediaStreamSource = audioContext.createMediaStreamSource( stream ); // Отправляем поток на проигрывание аудио устройству mediaStreamSource.connect( audioContext.destination ); } navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia; navigator.getUserMedia( {audio:true}, gotStream,function(){});
Для того, чтобы объединять разные потоки нам потребуется ChannelMergerNode, это и есть наш микшер, нам таких потребуется столько сколько у нас участников в конференции и каждый участник будет получать микс остальных за исключением себя, выглядит оно как-то так:
window.AudioContext = window.AudioContext || window.webkitAudioContext; var audioContext = new AudioContext(); var mediaStreamSource = audioContext.createMediaStreamSource( local_stream ), participant1 = audioContext.createMediaStreamSource( participant1_stream ), participantN = audioContext.createMediaStreamSource( participantN_stream ); // Микшер var merger = audioContext.createChannelMerger(); // Отправляем поток в MediaStream, который надо подключить к PC var destination_participant1 = audioContext.createMediaStreamDestination(); mediaStreamSource.connect(merger, 0, 0); // Отправляем локальный стрим в микшер participantN.connect(merger, 0, 0); // добавляем в микс всех участников кроме того для кого этот микс и сделан mediaStreamSource.connect(merger, 0, 1); // необходимо для стерео в FF participantN.connect(merger, 0, 0); // необходимо для стерео в FF // Микс отправляем в destination_participant1 merger.connect( destination_participant1 ); // Добавляем в PC для participant1 результирующий поток , скорее всего, предыдущий надо будет отключить через removeStream pc.addStream( destination_participant1.stream );
Ничего гениального, но поверьте, что разработчикам браузеров пришлось изрядно повозиться, чтобы это работало. Вам не показалось, что все как-то слишком просто? 🙂 Вот и мне так казалось, пока дело не дошло до тестирования. Проверка отправки микса с Chrome на Firefox выявила, что проигрывается только 1 из всех медиа-потоков, отправленных в микс, при том что в случаях Chrome->Chrome, Firefox->Chrome, Firefox->Firefox все работает нормально. Попытка осмыслить причину такого поведения пока не привела к успеху, мы написали об этом коллегам в Google и Mozilla, но на момент написания текущей статьи ответа еще не получили. Как только появится понимание проблемы или способ решения проблемы, то мы обязательно напишем об этом в P.S.
Демки
Напоследок предлагаем вам ознакомиться с демками, быстро собранными нами на VoxImplant: первая использует клиентский подход (+github) — в ней нужно выбрать кому звоним для подключения к клиентской конференции, а вторая использует серверные конференции (+github) — тут всех желающих просто подключает к одной конференции. Всегда рады ознакомиться с вашими мыслями и комментариями, всем удачного конференсинга!
ссылка на оригинал статьи https://habrahabr.ru/post/279313/
Добавить комментарий