Звонки в браузере с поддержкой WebRTC¶
Описание¶
Web Call Server поддерживает аудио и видеозвонки из браузера на SIP устройства, PBX серверы, SIP-GSM-шлюзы, VoIP конференции и другие устройства с поддержкой протокола SIP. Таким образом, веб-приложение в браузере может работать, как программный телефон с поддержкой протокола SIP, принимать и инициировать голосовые и видеозвонки.
Поддерживаемые платформы и браузеры¶
| Chrome | Firefox | Safari | Edge | |
|---|---|---|---|---|
| Windows | ✅ | ✅ | ❌ | ✅ |
| Mac OS | ✅ | ✅ | ✅ | ✅ |
| Android | ✅ | ✅ | ❌ | ✅ |
| iOS | ✅ | ✅ | ✅ | ✅ |
Поддерживаемые протоколы¶
- WebRTC
- RTP
- SIP
Поддерживаемые кодеки¶
- H.264
- VP8
- G.711
- Speex
- G.729
- Opus
Поддерживаемые SIP функции¶
- DTMF
- Удержание звонка
- Перевод звонка
SIP функции управляются при помощи WebSDK.
Схема работы¶
1. SIP-сервер как прокси-сервер для передачи вызовов и RTP медиа¶
- Браузер начинает звонок с помощью WebSDK
- WCS соединяется с SIP-сервером
- SIP-сервер соединяется с SIP-устройством, принимающим звонок
- Браузер и SIP-устройство обмениваются аудио- и видеопотоками
2. SIP-сервер только как сервер для передачи вызовов¶
- Браузер начинает звонок с помощью WebSDK
- WCS соединяется с SIP-сервером
- SIP-сервер соединяется с SIP-устройством, принимающим звонок
- Браузер и SIP-устройство обмениваются аудио- и видеопотоками
Последовательность выполнения операций¶
Ниже описана последовательность вызовов при использовании примера Phone для создания звонка

-
Создание звонка при помощи WebSDK:
Session.createCall(),Call.call()code
-
Отправка
SIP INVITEна SIP сервер -
Отправка
SIP INVITEна SIP устройство -
Получение подтверждения от SIP устройства
-
Получение подтверждения от SIP сервера
-
Получение от сервера события, подтверждающего успешное соединение
CALL_STATUS.ESTABLISHEDcode
var outCall = session.createCall({ ... }).on(CALL_STATUS.RING, function(){ ... }).on(CALL_STATUS.ESTABLISHED, function(){ setStatus("#callStatus", CALL_STATUS.ESTABLISHED); $("#holdBtn").prop('disabled',false); onAnswerOutgoing(); }).on(CALL_STATUS.HOLD, function() { ... }).on(CALL_STATUS.FINISH, function(){ ... }).on(CALL_STATUS.FAILED, function(){ ... }); -
Стороны звонка обмениваются аудио- и видеопотоками
-
Завершение звонка
Call.hangup()code
function onConnected(session) { $("#connectBtn, #connectTokenBtn").text("Disconnect").off('click').click(function(){ $(this).prop('disabled', true); if (currentCall) { showOutgoing(); disableOutgoing(true); setStatus("#callStatus", ""); currentCall.hangup(); } session.disconnect(); }).prop('disabled', false); } -
Отправка
SIP BYEна SIP-сервер -
Отправка
SIP BYEна SIP-устройство -
Получение подтверждения от SIP-устройства
-
Получение подтверждения от SIP-сервера
Тестирование¶
Исходящий звонок из браузера на SIP-устройство¶
-
Для тестирования используем:
- два SIP-аккаунта;
- веб-приложение Phone Video для совершения звонка;
- программный телефон для ответа на звонок.
-
Откройте веб-приложение Phone Video. Введите данные SIP-аккаунта, звонящего из браузера:

-
Запустите программный телефон, введите данные SIP-аккаунта, принимающего звонок:

-
Нажмите в браузере кнопку
Connect, будет установлено соединение с сервером. Затем введите идентификатор SIP-аккаунта, принимающего звонок, и нажмите кнопкуCall:

-
Примите звонок в программном телефоне, нажав кнопку ответа на звонок с использованием видео:


В отдельном окне отобразится видео, транслируемое из браузера:

-
В браузере также отобразится видео:

-
Для завершения звонка нажмите кнопку
Hangupв браузере, либо кнопку завершения звонка в программном телефоне.
Прием входящего звонка с SIP-устройства в браузере¶
-
Для тестирования используем:
- два SIP-аккаунта;
- программный телефон для совершения звонка;
- веб-приложение Phone Video для ответа на звонок.
-
Откройте веб-приложение Phone Video. Введите данные SIP-аккаунта, принимающего звонок в браузере:
Нажмите в браузере кнопку Connect, будет установлено соединение с WCS сервером. -
Запустите программный телефон, введите данные звонящего SIP-аккаунта:

-
В программном телефоне введите идентификатор SIP-аккаунта, принимающего звонок, и нажмите кнопку вызова:

-
Примите звонок в браузере, нажав кнопку
Answer:


-
В браузере отобразится видео:

-
В отдельном окне программного телефона также отобразится видео, транслируемое из браузера:

-
Для завершения звонка нажмите кнопку
Hangupв браузере, либо кнопку завершения звонка в программном телефоне
Управление камерой, микрофоном и устройствами вывода звука¶
Выбор и переключение устройств ввода и вывода¶
Как и при захвате видеопотока, при совершении звонка из браузера можно выбрать камеру, микрофон и (только в браузере Chrome) устройство вывода звука. Кроме того, устройства можно переключать во время звонка.

-
Выбор камеры, микрофона, устройства вывода звука code
Flashphoner.getMediaDevices(null, true, MEDIA_DEVICE_KIND.ALL).then(function (list) { for (var type in list) { if (list.hasOwnProperty(type)) { list[type].forEach(function(device) { if (device.type == "mic") { ... } else if (device.type == "speaker") { ... } else if (device.type == "camera") { ... } }); } } ... }).catch(function (error) { $("#notifyFlash").text("Failed to get media devices "+error); }); -
Переключение устройства вывода звука во время звонка code
-
Переключение микрофона во время звонка code
$("#switchMicBtn").click(function() { if (currentCall) { currentCall.switchMic().then(function(id) { $('#micList option:selected').prop('selected', false); $("#micList option[value='"+ id +"']").prop('selected', true); }).catch(function(e) { console.log("Error " + e); }); } }).prop('disabled', true); -
Переключение камеры во время звонка code
$("#switchCamBtn").click(function() { if (currentCall) { currentCall.switchCam().then(function(id) { $('#cameraList option:selected').prop('selected', false); $("#cameraList option[value='"+ id +"']").prop('selected', true); }).catch(function(e) { console.log("Error " + e); }); } }).prop('disabled', true);
Установка размера видео¶
При создании звонка, может быть указан размер исходящего видео

code:
function getConstraints() {
var constraints = {
...
video: {
deviceId: {exact: $('#cameraList').find(":selected").val()},
width: parseInt($('#sendWidth').val()),
height: parseInt($('#sendHeight').val())
}
};
if (Browser.isSafariWebRTC() && Browser.isiOS() && Flashphoner.getMediaProviders()[0] === "WebRTC") {
constraints.video.width = {min: parseInt($('#sendWidth').val()), max: 640};
constraints.video.height = {min: parseInt($('#sendHeight').val()), max: 480};
}
return constraints;
}
Совершение звонка без микрофона и камеры¶
В некоторых случаях, когда звонок не предполагает двухсторонней коммуникации, например, при звонке на голосовое меню, можно позвонить, не используя микрофон и камеру.
Для этого необходимо отключить таймер активности RTP настройкой в файле flashphoner.properties
и отключить аудио и видео в настройке граничных параметров исходящего звонка в браузерах Chrome, Safari, MS Edge:
var constraints = {
audio: false,
video: false
};
var outCall = session.createCall({
callee: $("#callee").val(),
visibleName: $("#sipLogin").val(),
constraints: constraints,
...
})
В браузере Firefox необходимо создать пустой аудиопоток:
var constraints = {
audio: false,
video: false
};
if(Browser.isFirefox()) {
var audioContext = new AudioContext();
var emptyAudioStream = audioContext.createMediaStreamDestination().stream;
constraints.customStream = emptyAudioStream;
}
var outCall = session.createCall({
callee: $("#callee").val(),
visibleName: $("#sipLogin").val(),
constraints: constraints,
...
})
Отображение WebRTC-статистики¶
Во время SIP-звонка клиентское приложение получает WebRTC-статистику в соответствии со стандартом. Эта статистика может быть отображена в браузере, например:

Отметим, что в браузере Safari отображается только статистика аудио.
Call.getStats() code
currentCall.getStats(function (stats) {
if (stats && stats.outboundStream) {
if (stats.outboundStream.videoStats) {
$('#videoStatBytesSent').text(stats.outboundStream.videoStats.bytesSent);
$('#videoStatPacketsSent').text(stats.outboundStream.videoStats.packetsSent);
$('#videoStatFramesEncoded').text(stats.outboundStream.videoStats.framesEncoded);
} else {
...
}
if (stats.outboundStream.audioStats) {
$('#audioStatBytesSent').text(stats.outboundStream.audioStats.bytesSent);
$('#audioStatPacketsSent').text(stats.outboundStream.audioStats.packetsSent);
} else {
...
}
}
});
Настройка используемых кодеков¶
WCS указывает поддерживаемые кодеки в INVITE SDP согласно следующим параметрам в файле flashphoner.properties.
-
В INVITE SDP включаются кодеки, указанные параметром
codecs, по умолчанию
-
Из INVITE SDP исключаются кодеки, указанные параметром
codecs_exclude_sip, по умолчанию
-
Из INVITE SDP исключаются кодеки, указанные браузером, если установлен параметр
-
Из INVITE SDP исключаются кодеки, указанные параметром
stripCodecsв клиентском приложении, например:
Передача дополнительных параметров в SDP в запросе SIP INVITE и ответе 200 OK¶
При создании звонка при помощи JavaScript API могут быть определены дополнительные параметры для управления пропускной способностью канала через SDP для исходящих (в запросе SIP INVITE)
var sdpAttributes = ["b=AS:3000","b=TIAS:2500000","b=RS:1000","b=RR:3000"];
var outCall = session.createCall({
sipSDP: sdpAttributes,
...
});
и входящих звонков (в ответе 200 OK)
var sdpAttributes = ["b=AS:3000","b=TIAS:2500000","b=RS:1000","b=RR:3000"];
inCall.answer({
sipSDP: sdpAttributes,
...
});
Эти параметры проставляются в SDP после connection information (c=IN IP4) и до time description (t=0 0):
v=0
o=Flashphoner 0 1541068898263 IN IP4 192.168.1.5
s=Flashphoner/1.0
c=IN IP4 192.168.1.5
b=AS:3000
b=TIAS:2500000
b=RS:1000
b=RR:3000
t=0 0
m=audio
Звонки с использованием SIP TLS сигналинга¶
Использование SIP TLS сигналинга включается при помощи настройки
При этом сертификат SIP сервера проверяется с использованием системного хранилища сертификатов. Поэтому для использования SIP TLS на SIP сервере должен быть установлен действительный SSL сертификат, выданный известным удостоверяющим центром.
Звонки через сервер с самоподписанным сертификатом¶
Для того, чтобы совершать звонки через SIP сервер с самоподписанным SSL сертификатом, необходимо этот сертификат добавить в локальное хранилище сертификатов сервера, на который установлен WCS:
- Получите самоподписанный сертификат с SIP сервера
Здесь 192.168.0.153- IP адрес SIP сервера-
5061- порт SIP сигналинга -
Скопируйте сертификаты из ответа сервера
и добавьте их в файлCertificate chain 0 s:/CN=pbx.mycompany.com/O=My Super Company i:/CN=Asterisk Private CA/O=My Super Company -----BEGIN CERTIFICATE----- ... SIP server certificate goes here -----END CERTIFICATE----- 1 s:/CN=Asterisk Private CA/O=My Super Company i:/CN=Asterisk Private CA/O=My Super Company -----BEGIN CERTIFICATE----- ... SIP server CA certificate goes here -----END CERTIFICATE-----pbx.crt. Содержимое файла должно быть таким:
-
Определите каталог установки Java
Например, если ответ был таким:
/usr/java/jdk1.8.0_181/bin/java, то Java установлена в каталоге/usr/java/jdk1.8.0_181/ -
Найдите файл хранилища сертификатов Java, например
-
Импортируйте сертификаты, полученные на шаге 2, в хранилище сертификатов Java
-
Перезапустите WCS.
Подключение к существующей сессии¶
В некоторых случаях необходимо подключиться в браузере к уже существующей сессии и принять входящий звонок. Как правило, это актуально на мобильных устройствах, т.к. при уходе браузера в фон websocket сессия автоматически закрывается через некоторое время, и приложению остаются доступными только push уведомления. Чтобы сохранить сессию активной при отключении, необходимо при создании сессии указать опцию keepAlive
var connectionOptions = {
urlServer: url,
keepAlive: true,
sipOptions: sipOptions
};
...
Flashphoner.createSession(connectionOptions).on(SESSION_STATUS.ESTABLISHED, function(session, connection){
...
});
В этом случае сессия останется активной до истечения интервала в миллисекундах (по умолчанию 3600 секунд, или 1 час)
Периодичность проверки этого интервала задается настройкой в миллисекундах (по умолчанию 300 секунд, или 5 минут)
Чтобы подключиться к этой сессии заново, необходимо запомнить токен сессии
Flashphoner.createSession(connectionOptions).on(SESSION_STATUS.ESTABLISHED, function(session, connection){
authToken = connection.authToken;
...
});
Затем к этой сессии можно подключиться с помощью этого токена (например, при получении push уведомления о входящем звонке):
var connectionOptions = {
urlServer: url,
keepAlive: true
};
if (authToken) {
connectionOptions.authToken = authToken;
} else {
connectionOptions.sipOptions = sipOptions;
}
Flashphoner.createSession(connectionOptions).on(SESSION_STATUS.ESTABLISHED, function(session, connection){
...
});
Поддержка re-INVITE¶
В сборке 5.2.1943 добавлена поддержка re-INVITE для исходящих SIP звонков. В случае, если SIP сервер пришлет SDP в ответе 200 OK на SIP INVITE, этот SDP будет применен для согласования кодеков для передачи медиаданных между WCS и SIP сервером. Изменений в настройках WCS не требуется.
Запись SIP звонков¶
Начиная со сборки 5.2.2183, SIP аудио и видео звонки могут быть записаны при помощи записи нескольких потоков в один файл:
В этом случае, аудио и видео дорожки обоих участников звонка будут записаны в файл и затем смикшированы. Например, так будет выглядеть в микшере аудио звонок между SIP аккаунтами 10006 и 10007

Здесь
10006- звонящий абонент (в данный момент говорит)10007- абонент, принимающий звонок
Один звонок записывается в один файл, поэтому по окончании звонка в каталоге /usr/local/FlashphonerWebCallServer/records появятся два файла, один исходный, другой микшированный, например
-rw-rw-r-- 1 root flashphoner 596453 Mar 11 03:39 stream-31e3aed5-d246-42ad-8423-c81fa92835d8-mockLogin_mixed.mp4
-rw-rw-r-- 1 root flashphoner 761032 Mar 11 03:39 stream-31e3aed5-d246-42ad-8423-c81fa92835d8-mockLogin.mp4
Известные проблемы¶
1. Невозможно совершить SIP-звонок, если поля SIP Login, SIP Authentification name содержат недопустимые символы¶
Симптомы
Звонок не совершается, зависает в статусе PENDING
Решение
Согласно RFC3261, SIP Login и SIP Authentification name не должны содержать неэкранированных пробелов, спецсимволов и не должны заключаться в угловые скобки <>.
Например, такое заполнение полей не соответствует стандарту
sipLogin='Ralf C12441@host.com'
sipAuthenticationName='Ralf C'
sipPassword='demo'
sipVisibleName='null'
а такое соответствует
2. Возможны проблемы со звуком при звонках из браузера Edge¶
Симптомы
a) исходящий звук периодически то резко приглушается, то идет нормально.
b) входящий звук слышен, только если говорить в микрофон.
Решение
Отключить для браузера Edge использование кодеков SILK и G.722 в SIP звонках при помощи опции stripCodecs:
var outCall = session.createCall({
callee: $("#callee").val(),
visibleName: $("#sipLogin").val(),
...
stripCodecs: "silk,g722"
...
});
outCall.call();
или при помощи настройки
3. Не работает переключение микрофона в браузере Safari¶
Симптомы
Не переключается микрофон при помощи метода WebSDK switchMic().
Решение
Использовать другой браузер, поскольку Safari всегда использует микрофон Sound input, выбранный в настройках звука системы Sound menu (для входа необходимо зажать клавишу Option (Alt) и щелкнуть по иконке звука в меню). После выбора другого микрофона в sound menu требуется перезагрузка Mac.
Если не работает микрофон Logitech USB camera (когда выбран в Sound input), может помочь изменение format / sample rate в Audio MIDI Setup и перезагрузка.
4. Не устанавливается исходящий видеозвонок из браузера, если размер INVITE SDP превышает размер MTU¶
Симптомы
При попытке установить исходящий видеозвонок SIP-сторона возвращает 408 Request timeout, аудиозвонки при этом устанавливаются успешно
Решение
Уменьшить количество кодеков в INVITE SDP таким образом, чтобы SDP укладывалось в размер пакета, определенный MTU (как правило, 1500 байт), при помощи настроек
Следует оставить только те кодеки, которые поддерживаются обеими сторонами звонка, в данном случае это VP8 и PCMA (alaw).
5. При аудио+видео звонке, если вызываемый абонент отвечает только с аудио, нет звука в браузере¶
Симптомы
При исходящем аудио+видео звонке (допустим, из примера Phone Video), если вызываемый абонент принимает звонок только с аудио (например, звонок на IVR), в браузере на вызывающей стороне нет звука
6. При аудио+видео звонке на IVR сообщение слышно не с самого начала¶
Симптомы
При исходящем аудио+видео звонке (допустим, из примера Phone Video), звук сообщения IVR появляется не сразу
Решение
Обновить WCS до сборки 5.2.1755 и уменьшить интервал ожидания перед запуском генератора видео кадров
7. При звонках между браузерами включается излишний транскодинг в VP8¶
Симптомы
Со стороны SIP сервера приходит видео в кодеке H264, но в браузере играет VP8