Звонки в браузере с поддержкой 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.ESTABLISHED
code
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 не требуется.
Известные проблемы¶
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