Пример стримера с автоматическим восстановлением публикации/проигрывания
Данный пример показывает
...
различные способы восстановления публикации и проигрывания:
- при проблемах с публикацией H264 потока из браузера с заменой кодека на VP8
- при остановке отправки медиапакетов, с контролем по снижению битрейта публикации до 0
...
Код примера
Код данного примера находится на WCS-сервере по следующему пути:
/usr/local/FlashphonerWebCallServer/client2/examples/demo/streaming/stream-auto-restore
stream-auto-restore.css - файл стилей
stream-auto-restore.html - страница клиента
stream-auto-restore.js - скрипт, обеспечивающий работу примера
Тестировать данный пример можно по следующему адресу:
https://host:8888/client2/examples/demo/streaming/stream_filter/stream_filter.html
Здесь host - адрес WCS-сервера.
Работа с кодом примера
Для разбора кода возьмем версию файла stream-auto-restore.js с хешем f2862b9, которая находится здесь и доступна для скачивания в соответствующей сборке 2.0.207.
1. Инициализация API.
Flashphoner.init() code
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.init(); |
2. Подключение к серверу.
Flashphoner.createSession() code
Code Block | ||||
---|---|---|---|---|
| ||||
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();
}); |
3. Получение от сервера события, подтверждающего успешное соединение.
...
- при переключении с одной сети на другую (с Wi-Fi на LTE и обратно)
- разрыве и последующем восстановлении соединения (в том числе при остановке публикации играемого потока или при перезапуске сервера)
Параметры контроля битрейта
- Check bitrate - контролировать снижение битрейта публикации до 0
- Change codec - менять кодек с H264 на VP8 при остановке публикации по снижению битрейта до 0
- Bitrate check interval - интервал проверки текущего значения битрейта публикации
- Max tries - максимальное количество последовательных попыток проверки битрейта, при котором зафиксировано снижение до 0
Параметры восстановления соединения
- Restore connection - восстанавливать соединение при обрыве сессии или при остановке проигрывания потока с признаком ошибки
- Timeout - интервал между попытками восстановить соединение
- Max tries - максимальное количество попыток восстановить соединение
- Missing pings - максимальное количество ping пакетов, не полученных от сервера (0 отключает проверку)
- Pings check period - интервал проверки количества пропущенных ping пакетов
Код примера
Код данного примера находится на WCS-сервере по следующему пути:
/usr/local/FlashphonerWebCallServer/client2/examples/demo/streaming/stream-auto-restore
stream-auto-restore.css - файл стилей
stream-auto-restore.html - страница клиента
stream-auto-restore.js - скрипт, обеспечивающий работу примера
Тестировать данный пример можно по следующему адресу:
https://host:8888/client2/examples/demo/streaming/stream-auto-restore/stream-auto-restore.html
Здесь host - адрес WCS-сервера.
Работа с кодом примера
Для разбора кода возьмем версию файла stream-auto-restore.js с хешем 2035db9, которая находится здесь и доступна для скачивания в соответствующей сборке 2.0.209.
1. Действия при открытии страницы
1.1. Инициализация API.
Flashphoner.init() code
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.init(); |
1.2. Инициализация объектов для хранения текущего состояния сессии и публикуемого/играемого потоков
Code Block | ||||
---|---|---|---|---|
| ||||
currentSession = sessionState();
streamPublishing = streamState();
streamPlaying = streamState(); |
1.3. Инициализация объекта для контроля битрейта
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.createSession({urlServer: url}).on(SESSION_STATUS.ESTABLISHED, function(session){
setStatus("#connectStatus", session.status());
onConnected(session);
}).on(SESSION_STATUS.DISCONNECTED, function(){
...
}).on(SESSION_STATUS.FAILED, function(){
...
}); |
4. Публикация видеопотока.
session.createStream(), publish() code
При создании передаются:
- имя видеопотока streamName
- localVideo - div-элемент, в котором будет отображаться видео с камеры
- stripCodecs - кодек, который нужно исключить для успешной публикации
h264PublishFailureDetector = codecPublishingFailureDetector(); |
1.4. Инициализация объекта для восстановления соединения
объекту передается функция, которая должна выполниться при срабатывании интервального таймера восстановления соединения
Code Block | ||||
---|---|---|---|---|
| ||||
streamingRestarter = streamRestarter(function() {
if (streamPublishing.wasActive) {
onPublishRestart();
}
if (streamPlaying.wasActive && streamPlaying.name != streamPublishing.name) {
onPlayRestart();
}
}); |
1.5. Запуск детектора изменения сети
Code Block | ||||
---|---|---|---|---|
| ||||
session.createStream({ name: streamName, display: localVideo, cacheLocalResources: true receiveVideo: false, receiveAudio: false, stripCodecs: stripCodecs ... }).publish networkChangeDetector(); |
5. Получение от сервера события, подтверждающего успешную публикацию потока.
StreamStatusEvent PUBLISHING code
2. Действия при подключении к серверу/отключении от сервера
2.1. Подключение к серверу.
Flashphoner.createSession() code
При создании сессии передаются следующие параметры:
- url - Websocket URL сервера
- receiveProbes - максимальное количество ping пакетов, не полученных от сервера (0 отключает проверку)
- probesInterval - интервал проверки количества пропущенных ping пакетов (0 отключает проверку)
Code Block | ||||
---|---|---|---|---|
| ||||
session.createStream({ Flashphoner.createSession({ urlServer: url, ... receiveProbes: receiveProbes, probesInterval: probesInterval }).on(STREAMSESSION_STATUS.PUBLISHINGESTABLISHED, function (streamsession) { setStatus("#publishStatus", STREAM_STATUS.PUBLISHING); ... onPublishing(stream); })}).on(STREAMSESSION_STATUS.UNPUBLISHEDDISCONNECTED, function () { ... }).on(STREAMSESSION_STATUS.FAILED, function () { ... }).publish(); |
6. Воспроизведение видеопотока.
session.createStream(), play() code.
При создании передается имя видеопотока streamName (в том числе, это может быть имя потока, опубликованного выше), а также remoteVideo - div-элемент, в котором будет отображаться видео.2.2. Получение от сервера события, подтверждающего успешное соединение.
ConnectionStatusEvent ESTABLISHED code
При успешной установке соединения:
- запоминаются параметры текущей сессии для последующего восстановления
- если в предыдущей сессии был опубликован или игрался поток, публикация/проигрывание перезапускается
Code Block | ||||
---|---|---|---|---|
| ||||
session.createStream({
name: streamName,
display: remoteVideo
...
}).play(); |
7. Получение от сервера события, подтверждающего успешное воспроизведение потока.
StreamStatusEvent PLAYING code
Code Block | ||||
---|---|---|---|---|
| ||||
session.createStream({
name: streamName,
display: remoteVideo
...
}).on(STREAM_STATUS.PLAYING, function(stream) {
setStatus("#playStatus", stream.status());
onPlaying(stream);
}).on(STREAM_STATUS.STOPPED, function() {
...
}).on(STREAM_STATUS.FAILED, function() {
...
}).play(); |
8. Остановка воспроизведения видеопотока.
stream.stop() code
Code Block | ||||
---|---|---|---|---|
| ||||
function onPlaying(stream) {
$("#playBtn").text("Stop").off('click').click(function(){
$(this).prop('disabled', true);
stream.stop();
}).prop('disabled', false);
$("#playInfo").text("");
} |
9. Получение от сервера события, подтверждающего успешную остановку воспроизведения потока.
StreamStatusEvent STOPPED code
Code Block | ||||
---|---|---|---|---|
| ||||
session.createStream({
...
}).on(STREAM_STATUS.PLAYING, function(stream) {
...
}).on(STREAM_STATUS.STOPPED, function() {
setStatus("#playStatus", STREAM_STATUS.STOPPED);
onStopped();
}).on(STREAM_STATUS.FAILED, function() {
...
}).play(); |
10. Остановка публикации видеопотока
stream.stop() code
Code Block | ||||
---|---|---|---|---|
| ||||
function onPublishing(stream) {
$("#publishBtn").text("Stop").off('click').click(function(){
$(this).prop('disabled', true);
stream.stop();
}).prop('disabled', false);
$("#publishInfo").text("");
...
} |
11. Получение от сервера события, подтверждающего успешную остановку публикации потока.
StreamStatusEvent UNPUBLISHED code
Code Block | ||||
---|---|---|---|---|
| ||||
session.createStream({
...
}).on(STREAM_STATUS.PUBLISHING, function(stream){
...
}).on(STREAM_STATUS.UNPUBLISHED, function(){
setStatus("#publishStatus", STREAM_STATUS.UNPUBLISHED);
onUnpublished();
}).on(STREAM_STATUS.FAILED, function(){
...
}).publish(); |
...
Flashphoner.createSession({
urlServer: url,
receiveProbes: receiveProbes,
probesInterval: probesInterval
}).on(SESSION_STATUS.ESTABLISHED, function (session) {
setStatus("#connectStatus", session.status());
currentSession.set(url, session);
onConnected(session);
if(restoreConnection) {
if(streamPublishing.wasActive) {
console.log("A stream was published before disconnection, restart publishing");
onPublishRestart();
return;
}
if(streamPlaying.wasActive) {
console.log("A stream was played before disconnection, restart playback");
onPlayRestart();
}
}
}).on(SESSION_STATUS.DISCONNECTED, function () {
...
}).on(SESSION_STATUS.FAILED, function () {
...
}); |
2.3. Закрытие соединения при нажатии на кнопку Disconnect
session.disconnect() code
Code Block | ||||
---|---|---|---|---|
| ||||
function onConnected(session) {
$("#connectBtn").text("Disconnect").off('click').click(function () {
$(this).prop('disabled', true);
currentSession.isManuallyDisconnected = true;
session.disconnect();
}).prop('disabled', false);
...
} |
2.4. Получение события при закрытии соединения
ConnectionStatusEvent DISCONNECTED code
Если соединение было закрыто вручную при нажатии на кнопку Disconnect:
- объекты состояния очищаются
- таймер восстановления соединения останавливается
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.createSession({
urlServer: url,
receiveProbes: receiveProbes,
probesInterval: probesInterval
}).on(SESSION_STATUS.ESTABLISHED, function (session) {
...
}).on(SESSION_STATUS.DISCONNECTED, function () {
setStatus("#connectStatus", SESSION_STATUS.DISCONNECTED);
onDisconnected();
// Prevent streaming restart if session is manually disconnected
if (currentSession.isManuallyDisconnected) {
streamPublishing.clear();
streamPlaying.clear();
streamingRestarter.reset();
currentSession.clear();
}
}).on(SESSION_STATUS.FAILED, function () {
...
}); |
2.5. Получение события при разрыве соединения
ConnectionStatusEvent FAILED code
Если на момент разрыва соединения публиковался или проигрывался поток, запускается таймер восстановления соединения
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.createSession({
urlServer: url,
receiveProbes: receiveProbes,
probesInterval: probesInterval
}).on(SESSION_STATUS.ESTABLISHED, function (session) {
...
}).on(SESSION_STATUS.DISCONNECTED, function () {
...
}).on(SESSION_STATUS.FAILED, function () {
setStatus("#connectStatus", SESSION_STATUS.FAILED);
onDisconnected();
if(restoreConnection
&& (streamPublishing.wasActive || streamPlaying.wasActive)) {
streamingRestarter.restart($("#restoreTimeout").val(), $("#restoreMaxTries").val());
}
}); |
3. Действия при публикации видеопотока
3.1 Публикация видеопотока
session.createStream(), publish() code
При создании передаются:
- имя видеопотока streamName
- localVideo - div-элемент, в котором будет отображаться видео с камеры
- stripCodecs - кодек, который нужно исключить, если при предыдущей публикации битрейт упал до 0
Code Block | ||||
---|---|---|---|---|
| ||||
session.createStream({
name: streamName,
display: localVideo,
cacheLocalResources: true,
receiveVideo: false,
receiveAudio: false,
stripCodecs: stripCodecs
...
}).publish(); |
3.2. Получение от сервера события, подтверждающего успешную публикацию потока.
StreamStatusEvent PUBLISHING code
При успешной публикации:
- запускается контроль битрейта
- запоминаются текущие параметры публикуемого потока для последующего восстановления публикации
- останавливается таймер восстановления соединения
- если в предыдущей сессии игрался поток, запускается проигрывание этого потока
Code Block | ||||
---|---|---|---|---|
| ||||
session.createStream({
...
}).on(STREAM_STATUS.PUBLISHING, function (stream) {
setStatus("#publishStatus", STREAM_STATUS.PUBLISHING);
onPublishing(stream);
streamPublishing.set(streamName, stream);
streamingRestarter.reset();
if ($("#restoreConnection").is(':checked')
&& streamPlaying.wasActive) {
console.log("A stream was played before, restart playback");
onPlayRestart();
}
}).on(STREAM_STATUS.UNPUBLISHED, function () {
...
}).on(STREAM_STATUS.FAILED, function (stream) {
...
}).publish(); |
3.3. Запуск контроля битрейта при успешной публикации
Code Block | ||||
---|---|---|---|---|
| ||||
function onPublishing(stream) {
...
// Start publish failure detector by bitrate #WCS-3382
if($("#checkBitrate").is(':checked')) {
h264PublishFailureDetector.startDetection(stream, $("#bitrateInteval").val(), $("#bitrateMaxTries").val());
}
} |
3.4. Остановка публикации при нажатии на Stop
stream.stop() code
Code Block | ||||
---|---|---|---|---|
| ||||
function onPublishing(stream) {
$("#publishBtn").text("Stop").off('click').click(function () {
$(this).prop('disabled', true);
streamPublishing.isManuallyStopped = true;
stream.stop();
}).prop('disabled', false);
...
} |
3.5. Получение от сервера события, подтверждающего успешную остановку публикации потока при нажатии на Stop
StreamStatusEvent UNPUBLISHED code
При успешной остановке публикации:
- останавливается контроль битрейта
- останавливается таймер восстановления соединения
- очищается объект состояния публикуемого потока
Code Block | ||||
---|---|---|---|---|
| ||||
session.createStream({
...
}).on(STREAM_STATUS.PUBLISHING, function (stream) {
...
}).on(STREAM_STATUS.UNPUBLISHED, function () {
setStatus("#publishStatus", STREAM_STATUS.UNPUBLISHED);
onUnpublished();
if (!streamPlaying.wasActive) {
// No stream playback< we don't need restart any more
streamingRestarter.reset();
} else if (streamPlaying.wasActive && streamPlaying.name == streamPublishing.name) {
// Prevent playback restart for the same stream
streamingRestarter.reset();
}
streamPublishing.clear();
}).on(STREAM_STATUS.FAILED, function (stream) {
...
}).publish(); |
3.6. Получение от сервера события об остановке публикации в связи с ошибкой
StreamStatusEvent FAILED code
При остановке публикации в связи с ошибкой
- останавливается контроль битрейта
- если не зафиксирована локальная ошибка в браузере (например, по недоступности устройств захвата), запускается таймер восстановления соединенимя
Code Block | ||||
---|---|---|---|---|
| ||||
session.createStream({
...
}).on(STREAM_STATUS.PUBLISHING, function (stream) {
...
}).on(STREAM_STATUS.UNPUBLISHED, function () {
...
}).on(STREAM_STATUS.FAILED, function (stream) {
setStatus("#publishStatus", STREAM_STATUS.FAILED, stream);
onUnpublished();
if ($("#restoreConnection").is(':checked') && stream.getInfo() != ERROR_INFO.LOCAL_ERROR) {
streamingRestarter.restart($("#restoreTimeout").val(), $("#restoreMaxTries").val());
}
}).publish(); |
3.7. Остановка контроля битрейта при остановке публикации
Code Block | ||||
---|---|---|---|---|
| ||||
function onUnpublished() {
...
h264PublishFailureDetector.stopDetection(streamPublishing.isManuallyStopped || currentSession.isManuallyDisconnected);
...
}
|
4. Действия при воспроизведении видеопотока
4.1. Воспроизведение видеопотока
session.createStream(), play() code.
При создании передается имя видеопотока streamName (в том числе, это может быть имя потока, опубликованного выше), а также remoteVideo - div-элемент, в котором будет отображаться видео.
Code Block | ||||
---|---|---|---|---|
| ||||
session.createStream({
name: streamName,
display: remoteVideo
...
}).play(); |
4.2. Получение от сервера события, подтверждающего успешное воспроизведение потока.
StreamStatusEvent PLAYING code
При успешном воспроизведении потока:
- запоминаются текущие параметры потока для последующего восстановления
- останавливается таймер восстановления воспроизведения
Code Block | ||||
---|---|---|---|---|
| ||||
session.createStream({
name: streamName,
display: remoteVideo
}).on(STREAM_STATUS.PENDING, function (stream) {
...
}).on(STREAM_STATUS.PLAYING, function (stream) {
setStatus("#playStatus", stream.status());
onPlaying(stream);
streamingRestarter.reset();
streamPlaying.set(streamName, stream);
}).on(STREAM_STATUS.STOPPED, function () {
...
}).on(STREAM_STATUS.FAILED, function (stream) {
...
}).play(); |
4.3 Остановка воспроизведения видеопотока при нажатии на Stop
stream.stop() code
Code Block | ||||
---|---|---|---|---|
| ||||
function onPlaying(stream) {
$("#playBtn").text("Stop").off('click').click(function(){
$(this).prop('disabled', true);
stream.stop();
}).prop('disabled', false);
$("#playInfo").text("");
} |
4.4. Получение от сервера события, подтверждающего успешную остановку воспроизведения потока.
StreamStatusEvent STOPPED code
При успешной остановке воспроизведения:
- останавливается таймер восстановления соединения
- очищается объект состояния играемого потока
Code Block | ||||
---|---|---|---|---|
| ||||
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();
streamingRestarter.reset();
streamPlaying.clear();
}).on(STREAM_STATUS.FAILED, function (stream) {
...
}).play(); |
4.5. Получение события об ошибке воспроизведения потока
StreamStatusEvent FAILED code
При остановке воспроизведения в связи с ошибкой запускается таймер восстановления соединения
Code Block | ||||
---|---|---|---|---|
| ||||
session.createStream({
name: streamName,
display: remoteVideo
}).on(STREAM_STATUS.PENDING, function (stream) {
...
}).on(STREAM_STATUS.PLAYING, function (stream) {
...
}).on(STREAM_STATUS.STOPPED, function () {
...
}).on(STREAM_STATUS.FAILED, function (stream) {
setStatus("#playStatus", STREAM_STATUS.FAILED, stream);
onStopped();
if ($("#restoreConnection").is(':checked')) {
streamingRestarter.restart($("#restoreTimeout").val(), $("#restoreMaxTries").val());
}
}).play(); |
5. Контроль битрейта и возобновление публикации при снижении битрейта до 0
5.1. Получение WebRTC статистики от браузера, определение текущего кодека и битрейта публикации, остановка публикации при устойчивом падении битрейта до 0
Code Block | ||||
---|---|---|---|---|
| ||||
stream.getStats(function(stat) {
let videoStats = stat.outboundStream.video;
if(!videoStats) {
return;
}
let stats_codec = videoStats.codec;
let bytesSent = videoStats.bytesSent;
let bitrate = (bytesSent - detector.lastBytesSent) * 8;
if (bitrate == 0) {
detector.counter.inc();
console.log("Bitrate is 0 (" + detector.counter.getCurrent() + ")");
if (detector.counter.exceeded()) {
detector.failed = true;
console.log("Publishing seems to be failed, stop the stream");
stream.stop();
}
} else {
detector.counter.reset();
}
detector.lastBytesSent = bytesSent;
detector.codec = stats_codec;
$("#publishInfo").text(detector.codec);
}); |
5.2. Остановка таймера проверки битрейта
Code Block | ||||
---|---|---|---|---|
| ||||
if (detector.publishFailureIntervalID) {
clearInterval(detector.publishFailureIntervalID);
detector.publishFailureIntervalID = null;
} |
5.3. Перезапуск публикации
Code Block | ||||
---|---|---|---|---|
| ||||
if (detector.failed) {
$("#publishInfo").text("Failed to publish " + detector.codec);
if($("#changeCodec").is(':checked')) {
// Try to change codec from H264 to VP8 #WCS-3382
if (detector.codec == "H264") {
console.log("H264 publishing seems to be failed, trying VP8 by stripping H264");
let stripCodecs = "H264";
publishBtnClick(stripCodecs);
} else if (detector.codec == "VP8") {
console.log("VP8 publishing seems to be failed, giving up");
}
} else {
// Try to republish with the same codec #WCS-3410
publishBtnClick();
}
} |
6. Восстановление соединения
6.1. Запуск таймера восстановления соединения
Таймер вызывает функцию, в которой выполняются необходимые действия по восстановлению
Code Block | ||||
---|---|---|---|---|
| ||||
restarter.restartTimerId = setInterval(function(){
if (restarter.counter.exceeded()) {
logger.info("Tried to restart for " + restartMaxTimes + " times with " +restartTimeout + " ms interval, cancelled");
restarter.reset();
return;
}
onRestart();
restarter.counter.inc();
}, restartTimeout); |
6.2. Остановка таймера восстановления соединения
Code Block | ||||
---|---|---|---|---|
| ||||
if (restarter.restartTimerId) {
clearInterval(restarter.restartTimerId);
logger.info("Timer " + restarter.restartTimerId + " stopped");
restarter.restartTimerId = null;
}
restarter.counter.reset(); |
6.3. Создание новой сессии
Code Block | ||||
---|---|---|---|---|
| ||||
let sessions = Flashphoner.getSessions();
if (!sessions.length || sessions[0].status() == SESSION_STATUS.FAILED) {
logger.info("Restart session to publish");
click("connectBtn");
} else {
...
} |
6.4. Повторная публикация
Code Block | ||||
---|---|---|---|---|
| ||||
let streams = sessions[0].getStreams();
let stream = null;
let clickButton = false;
if (streams.length == 0) {
// No streams in session, try to restart publishing
logger.info("No streams in session, restart publishing");
clickButton = true;
} else {
// If there is already a stream, check its state and restart publishing if needed
for (let i = 0; i < streams.length; i++) {
if (streams[i].name() === $('#publishStream').val()) {
stream = streams[i];
if (!isStreamPublishing(stream)) {
logger.info("Restart stream " + stream.name() + " publishing");
clickButton = true;
}
break;
}
}
if (!stream) {
logger.info("Restart stream publishing");
clickButton = true;
}
}
if (clickButton) {
click("publishBtn");
} |
6.5. Повторное проигрывание
Code Block | ||||
---|---|---|---|---|
| ||||
var statPublishFailureDetector let streams = {sessions[0].getStreams(); failed: false, let codec: "",stream = null; lastBytesSent: 0, let clickButton counter:= {false; value: 0, if (streams.length == 0) { threshold: PUBLISH_FAILURE_DETECTOR_MAX_TRIES } }; |
13. Запуск процедуры контроля текущего битрейта публикации по событию STREAM_STATUS.PUBLISHING
Code Block | ||||
---|---|---|---|---|
| ||||
function onPublishing(stream) { ... if(Browser.isChrome()) { // No streams in session, try to restart playing detectPublishFailure(stream, PUBLISH_FAILURE_DETECTOR_INTERVAL, PUBLISH_FAILURE_DETECTOR_MAX_TRIESlogger.info("No streams in session, restart playback"); } } |
14. Получение WebRTC статистики от браузера, определение текущего кодека и битрейта публикации, остановка публикации при устойчивом падении битрейта до 0
Code Block | ||||
---|---|---|---|---|
| ||||
clickButton = true; stream.getStats(function(stat)} else { let// videoStatsIf = stat.outboundStream.video; if(!videoStats) { there is already a stream, check its state and restart playing if needed for (let i = 0; i < return;streams.length; i++) { } if (streams[i].name() === $('#playStream').val()) { let codec = videoStats.codec; let bytesSentstream = videoStats.bytesSentstreams[i]; let bitrate = (bytesSent - statPublishFailureDetector.lastBytesSent) * 8; if (!isStreamPlaying(stream)) { if (bitrate == 0) { logger.info("Restart stream " + statPublishFailureDetector.counter.value++stream.name() + " playback"); console.log("Bitrate is 0 (" + statPublishFailureDetector.counter.value + ")"); clickButton = true; } if (statPublishFailureDetector.counter.value >= statPublishFailureDetector.counter.threshold) { break; statPublishFailureDetector.failed = true; } console.log("Publishing seems to be failed, stop the stream"); } if (!stream) { streamlogger.stop(info("Restart stream playback"); clickButton = }true; } else { } statPublishFailureDetector.counter.value = 0;if (clickButton) { }click("playBtn"); } |
7. Контроль смены сети
7.1. Обработка события, сигнализирующего о смене сети на устройстве
connection.onchange code
Code Block | ||||
---|---|---|---|---|
| ||||
if (Browser.isChrome() || statPublishFailureDetector.lastBytesSent = bytesSent;(Browser.isFirefox() && Browser.isAndroid())) { connection = navigator.connection || statPublishFailureDetectornavigator.codecmozConnection = codec|| navigator.webkitConnection; if $("#publishInfo").text(statPublishFailureDetector.codec);connection) { }); |
15. Остановка таймера проверки битрейта и вызов функции перезапуска публикации по событию STREAM_STATUS.UNPUBLISHED
Code Block | ||||
---|---|---|---|---|
| ||||
function onUnpublished() { ... if (publishFailureIntervalID connectionType = connection.type; if (Browser.isFirefox()) { clearInterval(publishFailureIntervalID); publishFailureIntervalIDconnection.ontypechange = nullonNetworkChange; } checkPublishFailureConditions(); } |
16. Перезапуск публикации с кодеком VP8
Code Block | ||||
---|---|---|---|---|
| ||||
function checkPublishFailureConditions() else { if (statPublishFailureDetector.failed) { connection.onchange = onNetworkChange; $("#publishInfo").text("Failed to publish " + statPublishFailureDetector.codec); } } } |
7.2. Закрытие соединения при смене сети
Code Block | ||||
---|---|---|---|---|
| ||||
if (isNetworkConnected() if (statPublishFailureDetector.codec == "H264"&& connection.type != connectionType) { console.log("H264 publishing seems to be failed, trying VP8 by stripping H264"); if (currentSession.getStatus() == SESSION_STATUS.ESTABLISHED) { let stripCodecslogger = "H264"; Flashphoner.getLogger(); publishBtnClick(stripCodecs); logger.info("Close session due to network change }from else" if (statPublishFailureDetector.codec == "VP8") { + connectionType + " to " + connection.type); console.log("VP8 publishing seems to be failed, giving up" currentSession.sdkSession.disconnect(); } } } |