В браузере по WebRTC¶
Описание¶
Поддерживаемые платформы и браузеры¶
Chrome | Firefox | Safari | Edge | |
---|---|---|---|---|
Windows | ✅ | ✅ | ❌ | ✅ |
Mac OS | ✅ | ✅ | ✅ | ✅ |
Android | ✅ | ✅ | ❌ | ✅ |
iOS | ✅ | ✅ | ✅ | ✅ |
Поддерживаемые кодеки¶
- Video: H.264, VP8
- Audio: Opus, PCMA, PCMU, G722, G729
Схема работы¶
- Браузер соединяется с сервером по протоколу Websocket и отправляет команду
publishStream
. - Браузер захватывает микрофон и камеру и отправляет WebRTC поток на сервер.
- Второй браузер устанавливает соединение также по Websocket и отправляет команду
playStream
. - Второй браузер получает WebRTC поток и воспроизводит этот поток на странице.
Краткое руководство по тестированию¶
-
Для теста используем демо-сервер
demo.flashphoner.com
и веб-приложение Two Way Streaminghttps://demo.flashphoner.com/client2/examples/demo/streaming/two_way_streaming/two_way_streaming.html
-
Установите соединение с сервером по кнопке
Connect
-
Нажмите
Publish
. Браузер захватывает камеру и отправляет поток на сервер
-
Откройте Two Way Streaming в отдельном окне, нажмите
Connect
и укажите идентификатор потока, затем нажмитеPlay
-
Графики воспроизведения
chrome://webrtc-internals
Последовательность выполнения операций¶
Ниже описана последовательность вызовов при использовании примера Two Way Streaming для воспроизведения потока
-
Установка соединения с сервером
Flashphoner.createSession()
code
Flashphoner.createSession({urlServer: url}).on(SESSION_STATUS.ESTABLISHED, function (session) { setStatus("#connectStatus", session.status()); onConnected(session); }).on(SESSION_STATUS.DISCONNECTED, function () { setStatus("#connectStatus", SESSION_STATUS.DISCONNECTED); onDisconnected(); }).on(SESSION_STATUS.FAILED, function () { setStatus("#connectStatus", SESSION_STATUS.FAILED); onDisconnected(); });
-
Получение от сервера события, подтверждающего успешное соединение
SESSION_STATUS.ESTABLISHED
code
-
Воспроизведение потока
Stream.play()
code
-
Получение от сервера события, подтверждающего успешное воспроизведение потока
STREAM_STATUS.PLAYING
code
session.createStream({ name: streamName, display: remoteVideo }).on(STREAM_STATUS.PENDING, function(stream) { ... }).on(STREAM_STATUS.PLAYING, function (stream) { setStatus("#playStatus", stream.status()); onPlaying(stream); }).on(STREAM_STATUS.STOPPED, function () { ... }).on(STREAM_STATUS.FAILED, function (stream) { ... }).play();
-
Прием аудио-видео потока по WebRTC
-
Остановка воспроизведения потока
Stream.stop()
code
-
Получение от сервера события, подтверждающего остановку воспроизведения потока
STREAM_STATUS.STOPPED
code
session.createStream({ name: streamName, display: remoteVideo }).on(STREAM_STATUS.PENDING, function(stream) { ... }).on(STREAM_STATUS.PLAYING, function (stream) { ... }).on(STREAM_STATUS.STOPPED, function () { setStatus("#playStatus", STREAM_STATUS.STOPPED); onStopped(); }).on(STREAM_STATUS.FAILED, function (stream) { ... }).play();
Воспроизведение двух и более потоков на одной странице¶
WCS предоставляет возможность воспроизведения двух и более потоков на одной странице. С точки зрения схемы работы и последовательности выполнения операций воспроизведение любого числа потоков не отличается от воспроизведения одного.
- Для теста используем:
- демо-сервер
demo.flashphoner.com
; - веб-приложение Two Way Streaming для публикации потоков
-
веб-приложение 2 Players для воспроизведения потоков
-
Откройте веб-приложение Two Way Streaming, нажмите
Connect
, затемPublish
. Скопируйте идентификатор первого потока из окнаLocal
:
-
В другой вкладке откройте веб-приложение Two Way Streaming, нажмите
Connect
, затемPublish
. Скопируйте идентификатор второго потока из окнаLocal
:
-
Откройте веб-приложение 2 Players, укажите идентификаторы первого (слева) и второго (справа) потоков:
-
Нажмите
Play
под правым и левым окнами плеера:
-
Графики
chrome://webrtc-internals
для первого потока:
-
Графики
chrome://webrtc-internals
для второго потока:
Максимальное количество потоков, которые можно играть на одной странице¶
Максимальное количество потоков, которое можно воспроизвести на одной странице с приемлемым качеством, зависит от следующих параметров:
- параметры одного потока (разрешение и битрейт)
- пропускная способность канала от сервера до клиента
- используемый транспорт (UDP или TCP)
- производительность клиентского устройства
Например, для потока 1920x1080 с битрейтом 2 Мбит/с, с использованием TCP транспорта для канала пропускной способностью 30-35 Мбит/с экспериментально получены следующие максимально возможные значения:
- ПК на базе Intel Core i5 8 gen и новее, от 8 Гб RAM: до 15 потоков аудио+видео, или 6 потоков аудио+видео и 14 потоков только с аудио
- Флагманское Android/iOS устройство 2018 года и новее (Samsung S серии, Apple iPhone Pro): до 15 потоков аудио+видео, или 6 потоков аудио+видео и 14 потоков только с аудио
- Устройство среднего и ниже класса или устаревшее Android/iOS устройство (Nokia 5, Apple iPhone 7): до 6 потоков аудио+видео, или только с аудио
Таким образом, для потока 1920x1080 с битрейтом 2 Мбит/с оптимальным будет проигрывание не более чем 6 потоков на одной странице, чтобы воспроизведение работало у любых клиентов.
Рассмотрим случай вебинара: один поток 1920x1080 с битрейтом 2 Мбит/с и несколько потоков 640x360 с битрейтом 500 кбит/с. В тех же условиях:
- ПК на базе Intel Core i5 8 gen и новее, от 8 Гб RAM: до 25 потоков аудио+видео, или 6 потоков аудио+видео и 25 потоков только с аудио
- Флагманское Android/iOS устройство 2018 года и новее (Samsung S серии, Apple iPhone Pro): до 20 потоков аудио+видео, или 6 потоков аудио+видео и 25 потоков только с аудио
- Устройство среднего и ниже класса или устаревшее Android/iOS устройство 2017 года и новее: до 10 потоков аудио+видео, или 6 потоков аудио+видео и 15 потоков только с аудио
Таким образом, для вебинара с трансляцией рабочего стола и нескольких веб камер оптимальным будет проигрывание не более чем 10 потоков на одной странице, чтобы воспроизведение работало у любых клиентов.
Воспроизведение WebRTC потока в стороннем плеере¶
Поток, опубликованный на WCS сервере, можно проиграть по WebRTC в стороннем браузерном плеере, например, в VR плеере. Для этого элемент страницы video
, в котором должен быть проигран поток, необходимо передать как параметр remoteVideo
в функцию WebSDK session.createStream()
session.createStream({
name: document.getElementById('playStream').value,
display: display,
remoteVideo: video
})
...
Тестирование¶
- Для теста используем:
- WCS сервер
- Пример Two Way Streaming для публикации потока
-
Delight VR player для проигрывания потока
-
Пубо=ликация WebRTC потока на сервере
-
Воспроизведение WebRTC потока в VR плеере
Пример кода для использования стороннего плеера¶
-
Элемент страницы для проигрывания видео, поле ввода имени потока и кнопки для запуска и остановки проигрывания
<div style="width: 50%;" id="display"> <dl8-live-video id="remoteVideo" format="STEREO_TERPON"> <source> </dl8-live-video> </div> <input class="form-control" type="text" id="playStream" placeholder="Stream Name"> <button id="playBtn" type="button" class="btn btn-default" disabled>Play</button> <button id="stopBtn" type="button" class="btn btn-default" disabled>Stop</button>
-
Обработка готовности плеера к проигрыванию
-
Установка соединения с WCS сервером и создание объекта потока
var video = dl8video.contentElement; Flashphoner.createSession({urlServer: url}).on(SESSION_STATUS.ESTABLISHED, function (session) { var session = Flashphoner.getSessions()[0]; session.createStream({ name: document.getElementById('playStream').value, display: display, remoteVideo: video }).on(STREAM_STATUS.PLAYING, function (stream) { ... }).play(); })
-
Запуск проигрывания в VR плеере и обработка нажатия кнопки
Stop
... Flashphoner.createSession({urlServer: url}).on(SESSION_STATUS.ESTABLISHED, function (session) { var session = Flashphoner.getSessions()[0]; session.createStream({ ... }).on(STREAM_STATUS.PLAYING, function (stream) { dl8video.start(); $('#stopBtn').prop('disabled', false).click(function() { $('#playBtn').prop('disabled', false); $('#stopBtn').prop('disabled', true); stream.stop(); dl8video.exit(); }); }).play(); })
Full custom player page code sample
<!DOCTYPE html>
<html>
<head>
<title>WebRTC Delight</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="text/javascript" src="../../../../flashphoner.js"></script>
<script type="text/javascript" src="../../dependencies/jquery/jquery-1.12.0.js"></script>
<script type="text/javascript" src="../../dependencies/js/utils.js"></script>
<script src="dl8-66b250447635476d123a44a391c80b09887e831e.js" async></script>
<meta name="dl8-custom-format" content='{"name": "STEREO_TERPON","base":"STEREO_MESH","params":{"uri": "03198702.json"}}'>
</head>
<body>
<div style="width: 50%;" id="display">
<dl8-live-video id="remoteVideo" format="STEREO_TERPON">
<source>
</dl8-live-video>
</div>
<input class="form-control" type="text" id="playStream" placeholder="Stream Name">
<button id="playBtn" type="button" class="btn btn-default" disabled>Play</button>
<button id="stopBtn" type="button" class="btn btn-default" disabled>Stop</button>
<script>
Flashphoner.init({flashMediaProviderSwfLocation: '../../../../media-provider.swf'});
var SESSION_STATUS = Flashphoner.constants.SESSION_STATUS;
var STREAM_STATUS = Flashphoner.constants.STREAM_STATUS;
var STREAM_STATUS_INFO = Flashphoner.constants.STREAM_STATUS_INFO;
var playBtn = document.getElementById('playBtn');
var display = document.getElementById('display');
var dl8video = null;
var url = setURL();
document.addEventListener('x-dl8-evt-ready', function () {
dl8video = document.getElementById('remoteVideo');
$('#playBtn').prop('disabled', false).click(function() {
playStream();
});
});
function playStream() {
$('#playBtn').prop('disabled', true);
$('#stopBtn').prop('disabled', false);
var video = dl8video.contentElement;
Flashphoner.createSession({urlServer: url}).on(SESSION_STATUS.ESTABLISHED, function (session) {
var session = Flashphoner.getSessions()[0];
session.createStream({
name: document.getElementById('playStream').value,
display: display,
remoteVideo: video
}).on(STREAM_STATUS.PLAYING, function (stream) {
dl8video.start();
$('#stopBtn').prop('disabled', false).click(function() {
$('#playBtn').prop('disabled', false);
$('#stopBtn').prop('disabled', true);
stream.stop();
dl8video.exit();
});
}).play();
})
}
</script>
</body>
</html>
Автозапуск воспроизведения¶
Примеры Player и Embed Player поддерживают автозапуск воспроизведения при помощи параметра
например
https://hostname:8888/embed_player?urlServer=wss://hostname:8443&streamName=stream1&autoplay=true&mediaProviders=WebRTC
Здесь
hostname
- имя WCS-сервераstream1
- имя потока на сервере
Особенности автозапуска воспроизведения в браузерах¶
Chrome¶
В последних версиях браузера Chrome (71 и выше) была изменена политика автозапуска воспроизведения контента на веб-страницах. Теперь для запуска воспроизведения видео необходимо, чтобы пользователь совершил какое-либо действие, например, нажатие на кнопку.
Это изменение влияет на создание аудиоконтекста, который необходим, чтобы работала регулировка громкости. Согласно новой политике, создание аудиоконтекста также требует действия от пользователя.
В связи с этим, в Chrome 71, а также в других браузерах на базе Chromium, поддерживающих изменение политики автозапуска, видео при автозапуске может проигрываться без звука. Для того, чтобы включить звук, пользователь должен использовать подходящий элемент управления в окне Embed Player.
Firefox и MacOS Safari¶
Как и в Chrome, автозапуск воспроизведения работает без звука, для включения звука требуется действие пользователя.
iOS Safari¶
Автозапуск воспроизведения работает, начиная с iOS 12.2. При этом политика автозапуска требует, чтобы пользователь переместил регулятор громкости для воспроизведения звука.
В версиях iOS 12.2-12.3 звук может не начать воспроизводиться и при движении регулятора громкости. В таких случаях необходимо повторно запустить воспроизведение видео, не обновляя страницу.
При включенном Low Power Mode автозапуск воспроизведения в iOS Safari не работает.
Тонкая настройка воспроизведения звука в iOS Safari¶
В случае воспроизведения и последующей публикации видео на одной странице (например, видеочат) в iOS Safari уровень звука для проигрываемого потока может меняться. Избежать этого можно двумя способами:
-
Запрашивать доступ к медиаустройствам при создании сессии перед проигрыванием
Flashphoner.createSession({urlServer: url}).on(SESSION_STATUS.ESTABLISHED, function (session) { ... if (Browser.isSafariWebRTC() && Browser.isiOS() && Flashphoner.getMediaProviders()[0] === "WebRTC") { Flashphoner.playFirstVideo(localVideo, true, PRELOADER_URL).then(function () { Flashphoner.getMediaAccess(null, localVideo).then(function (disp) { }); }); } ... });
-
Через 1-1.5 секунды после получения статуса потока
PLAYING
, отключить и снова включить звук и/или видео
session.createStream({ name: streamName, display: remoteVideo }).on(STREAM_STATUS.PENDING, function (stream) { ... }).on(STREAM_STATUS.PLAYING, function (stream) { setStatus("#playStatus", stream.status()); onPlaying(stream); if (Browser.isSafariWebRTC() && Browser.isiOS() && Flashphoner.getMediaProviders()[0] === "WebRTC") { setTimeout(function () { stream.muteRemoteAudio(); stream.unmuteRemoteAudio(); }, 1500); } ... }).play();
Воспроизведение стерео звука в браузере¶
Как и для публикации стерео звука в кодеке Opus, для проигрывания в браузере необходимо установить параметры кодека на стороне сервера
В этом случае браузер Firefox играет стерео звук без дополнительных настроек.
При проигрывании в браузере потока, захваченного из RTMP, RTSP или VOD источника, как правило, звук транскодируется в кодек Opus. По умолчанию, кодировщик Opus настроен на передачу речи и монофонического аудио. Для проигрывания стерео звука в браузере, необходимо повысить битрейт кодирования Opus до 60 кбит/с или выше
Браузеры на основе Chrome¶
По умолчанию, из-за бага в движке браузер Chrome играет WebRTC поток со стерео звуком в кодеке Opus как моно. В связи с этим необходимы дополнительные настройки на стороне клиента, в зависимости от реализации.
С использованием Web SDK¶
В сборке Web SDK 0.5.28.2753.151 добавлена настройка для проигрывания стерео звука
например
session.createStream({
name: streamName,
display: remoteVideo,
constraints: {
audio: {
stereo: true
}
}
...
}).play();
С использованием Websocket API¶
Если в проекте используется только Websocket API, необходимо изменить параметры кодека Opus в исходящем (offer) SDP, непосредственно после его создания
var connection = new RTCPeerConnection(connectionConfig, connectionConstraints);
...
connection.createOffer(constraints).then(function (offer) {
offer.sdp = offer.sdp.replace('minptime=10', 'minptime=10;stereo=1;sprop-stereo=1');
connection.setLocalDescription(offer).then(function () {
...
});
});
Дополнительная задержка при воспроизведении видеопотока¶
В некоторых случаев при воспроизведении видеопотока необходимо добавить заданную задержку относительно трансляции. В сборке WebSDK 0.5.28.2753.142, поставляемой вместе со сборкой WCS 5.2.708, с этой целью добавлена опция playoutDelay
:
session.createStream({
name: streamName,
display: remoteVideo,
playoutDelay: 10
}).on(STREAM_STATUS.PENDING, function (stream) {
...
}).play();
Задержка задается в секундах.
Данная опция поддерживается только в браузерах на основе Chromium, которые поддерживают атрибут
Задержка не применяется к аудио дорожкам в потоке, а также к аудио потокам без видео.
Поддержка избыточности при проигрывании аудио¶
В сборке 5.2.1969 добавлена поддержка избыточности при проигрывании аудио данных с сервера (RED, RFC2198). Это позволяет снизить влияние потерь при проигрывании аудио в кодеке opus. Данная возможность настраивается так же, как для публикации аудио.
Чтобы включить поддержку избыточности при проигрывании opus аудио, используется настройка
Настройка указывает количество дополнительных данных. При публикации WebRTC библиотека в браузере обычно использует двойное резервирование, поэтому для проигрывания рекомендуется ставить такое же значение.
Известные проблемы¶
1. Возможный баг в браузере Safari на iOS приводит к фризам при¶
воспроизведении WebRTC
Симптомы
Останавливается воспроизведение видео, звуковая дорожка при этом может продолжать играть, для восстановления требуется перезагрузка страницы либо перезапуск браузера.
Решение
a) включить транскодер на сервере, указав в файле flashphoner.properties
b) при воспроизведении потока с iOS Safari явно указать ширину и высоту,например:
2. При публикации потока по RTMP и воспроизведении в браузере по WebRTC вместо аудиокодека Opus используется PCMU¶
Симптомы
В chrome://webrtc-internals
отображается кодек PCMU
Решение
Отключить алгоритм избегания транскодинга (Avoid Transcoding Alhorithm) при помощи опции в файле flashphoner.properties
3. Проигрывание аудио в RTMP потоке может останавливаться¶
При публикации потока при помощи Flash Streaming, воспроизведении этого потока в iOS Safari по WebRTC и одновременной публикации потока по WebRTC из Safari перестает воспроизводиться звук.
Симптомы
a) Публикация потока stream1
из приложения Flash Streaming в браузере Chrome под Windows
b) Воспроизведение потока stream1
на iOS Safari в приложении Two Way Streaming. Звук и видео воспроизводятся нормально.
c) Публикация потока stream2
из iOS Safari в приложении Two Way Streaming. Воспроизведение звука пропадает.
d) Остановка публикации в iOS Safari. Воспроизведение звука восстанавливается.
Решение
Отключить алгоритм избегания транскодинга (Avoid Transcoding Alhorithm) на сервере при помощи опции в файле flashphoner.properties
4. Проигрывание RTMp потока в браузере по WebRTC может останавливаться по keep alive¶
При публикации потока по RTMP и отключении Keep Alive для всех протоколов воспроизведение в браузере по WebRTC останавливается по истечению WebSocket-таймаута
Симптомы
При публикации потока по RTMP воспроизведение в браузере по WebRTC останавливается без явного указания ошибки
Решение
Если Keep Alive отключен для всех протоколов при помощи настройки в файле flashphoner.properties
Необходимо также отключить таймаут на чтение WebSocket настройкой
5. Кодек G722 не работает в браузере Edge¶
Симптомы
Поток со звуком G722 не воспроизводится в браузере Edge или воспроизводится без звука, с фризами
Решение
Использовать другой кодек или другой браузер. В случае, если использование другого браузера невозможно, исключить кодек G722 при помощи настройки
6. Некоторые браузеры, основанные на Chromium, в зависимости от версии и ОС не поддерживают кодек H264¶
Симптомы
Не работает публикация, не работает воспроизведение частично (только звук) или полностью при трансляции WebRTC потока H264
Решение
Разрешить поддержку vp8 на стороне сервера
исключить H264 для трансляции или воспроизведения на стороне клиента
Attention
При трансляции H264 потока и воспроизведении его как VP8 на сервере
включается транскодинг.
7. Включение Flash в Chrome может приводить к ошибке Cross origin content¶
Если в настройках браузера Chrome 71 и выше для сайта разрешен Flash, при воспроизведении по WebRTC в консоль браузера может выводиться ошибка Cross-origin content must have visible size large than 400 x 300 pixels, or it will be blocked
Симптомы
При воспроизведении по WebRTC в консоль браузера Chrome выводится сообщение ёCross-origin content must have visible size large than 400 x 300 pixels, or it will be blockedё, при этом воспроизведение работает
Решение
Использовать WebSDK без поддержки Flash (по умолчанию поддержка отключена в последних сборках)
8. При большом количестве подписчиков наблюдаются задержки в воспроизводимом потоке¶
Симптомы
При большом количестве подписчиков (более 200 для потока 720p) наблюдаются задержки и фризы видео, аудио продолжает играть
Решение
Включить многопоточную отсылку кадров подписчикам
Эта настройка действует только на потоки, которые не транскодируются на данном сервере
9. При проигрывании потока в iOS Safari по умолчанию звук идет в голосовой динамик¶
Симптомы
При проигрывании WebRTC потока, например, при входе в чат-комнату с iOS устройства тихий звук
Решение
Отключить и снова включить звук при старте проигрывания потока, например
10. При проигрывании потока в iOS Safari резко возрастает нагрузка на сервер, при условии использования JDK 11¶
Симптомы
При подключении подписчика с iOS Safari резко возрастает нагрузка на процессор сервера
Решение
Обновить JDK до одной из рекомендованных версий: 8, 12, 14.
11. При воспроизведении двух и более потоков на одной странице вбраузере Chrome на некоторых устройствах Xiaomi с MIUI 12 картинка в первом потоке подергивается¶
Симптомы
При воспроизведении двух потоков на одной странице в примере 2 Players картинка первого потока подергивается, мелькает изображение второго потока
Решение
a) использовать на устройстве Xioami MIUI 11
b) использовать микшер для проигрывания двух и более потоков на одной странице