...
WCS позволяет настраивать камеру и микрофон в браузере. Рассмотрим, как и какими параметрами можно управлять при захвате аудио- и видеопотока, на примере веб-приложения Media Devices:
Настройки микрофона
1.Выбор микрофона из списка
code:
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.getMediaDevices(null, true, MEDIA_DEVICE_KIND.INPUT).then(function (list) {
list.audio.forEach(function (device) {
...
});
...
}).catch(function (error) {
$("#notifyFlash").text("Failed to get media devices");
}); |
2. Переключение микрофона во время трансляции
code:
Code Block | ||||
---|---|---|---|---|
| ||||
$("#switchMicBtn").click(function (){ publishStreamstream.switchMic();.then(function(id) { } $('#audioInput option:selected').prop('disabledselected', !($('#sendAudio').is(':checked'))); |
3. Регулировка усиления микрофона (работает только в браузере Chrome)
code:
Code Block | ||||
---|---|---|---|---|
| ||||
$("#micGainControl").slider(false); $("#audioInput option[value='"+ id +"']").prop('selected', true); }).catch(function(e) { range: "min", console.log("Error " min: 0,+ e); max: 100,}); value: currentGainValue,}).prop('disabled', !($('#sendAudio').is(':checked'))); |
3. Регулировка усиления микрофона (работает только в браузере Chrome)
code:
Code Block | ||||
---|---|---|---|---|
| ||||
$("#micGainControl").slider({ steprange: 10"min", animatemin: true0, slidemax: function 100, value: currentGainValue, step: 10, animate: true, slide: function (event, ui) { currentGainValue = ui.value; if(previewStream) { publishStream.setMicrophoneGain(currentGainValue); } } }); |
4. Включение коррекции ошибок (только для кодека Opus)
code:
Code Block | ||||
---|---|---|---|---|
| ||||
if (constraints.audio) { constraints.audio = { deviceId: $('#audioInput').val() }; if ($("#fec").is(':checked')) constraints.audio.fec = $("#fec").is(':checked'); ... } |
5. Установка стерео / моно режима.
code:
Code Block | ||||
---|---|---|---|---|
| ||||
if (constraints.audio) { constraints.audio = { deviceId: $('#audioInput').val() }; ... if ($("#sendStereoAudio").is(':checked')) constraints.audio.stereo = $("#sendStereoAudio").is(':checked'); ... } |
6. Установка битрейта звука в кбитбит/с
code:
Code Block | ||||
---|---|---|---|---|
| ||||
if (constraints.audio) { constraints.audio = { deviceId: $('#audioInput').val() }; ... if (parseInt($('#sendAudioBitrate').val()) > 0) constraints.audio.bitrate = parseInt($('#sendAudioBitrate').val()); } |
7. Отключение микрофона.
code:
Code Block | ||||
---|---|---|---|---|
| ||||
if ($("#muteAudioToggle").is(":checked")) { muteAudio(); } |
Настройки камеры
1. Выбор камеры
code:
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.getMediaDevices(null, true, MEDIA_DEVICE_KIND.INPUT).then(function (list) {
...
list.video.forEach(function (device) {
...
});
}).catch(function (error) {
$("#notifyFlash").text("Failed to get media devices");
}); |
2. Переключение камер.
code:Если необходимо выбрать только камеру, не запрашивая доступ к аудиоустройствам, необходимо вызвать функцию getMediaDevices() с явным указанием ограничений
Code Block | ||||
---|---|---|---|---|
| ||||
$("#switchBtn").text("Switch").off('click').clickFlashphoner.getMediaDevices(null, true, MEDIA_DEVICE_KIND.INPUT, {video: true, audio: false}).then(function (list) { publishStream.switchCam();... }).prop('disabled', $('#sendCanvasStream').is(':checked')); |
Переключение камеры может осуществляться "на лету", во время трансляции потока. Переключение работает в следующем порядке:
- На ПК камеры переключаются в том порядке, в каком они определены в менеджере устройств операционной системы.
- На Android при использовании браузера Chrome по умолчанию выбирается фронтальная камера, при использовании браузера Firefox - тыловая камера
- На iOS в браузере Safari по умолчанию выбирается фронтальная камера, но в выпадающем списке при выборе камеры первой указана тыловая камера.
3. Установка разрешения видео
code:
Code Block | ||||
---|---|---|---|---|
| ||||
list.video.forEach(function (device) { ... }); }).catch(function (error) { $("#notifyFlash").text("Failed to get media devices"); }); |
2. Переключение камер.
code:
Code Block | ||||
---|---|---|---|---|
| ||||
$("#switchBtn").text("Switch").off('click').click(function () { constraints.video = stream.switchCam().then(function(id) { deviceId: $$('#videoInput option:selected').val(),prop('selected', false); $("#videoInput option[value='"+ width: parseInt($('#sendWidth').val()),id +"']").prop('selected', true); height: parseInt($('#sendHeight').val())}).catch(function(e) { }; console.log("Error " + e); if (Browser.isSafariWebRTC() && Browser.isiOS() && Flashphoner.getMediaProviders()[0] === "WebRTC") { constraints.video.deviceId = {exact: $('#videoInput').val()}; constraints.video.width = {min: parseInt($('#sendWidth').val()), max: 640}; }); }); |
Переключение камеры может осуществляться "на лету", во время трансляции потока. Переключение работает в следующем порядке:
- На ПК камеры переключаются в том порядке, в каком они определены в менеджере устройств операционной системы.
- На Android при использовании браузера Chrome по умолчанию выбирается фронтальная камера, при использовании браузера Firefox - тыловая камера
- На iOS в браузере Safari по умолчанию выбирается фронтальная камера, но в выпадающем списке при выборе камеры первой указана тыловая камера.
3. Установка разрешения видео
code:
Code Block | ||||
---|---|---|---|---|
| ||||
constraints.video = { constraints.video.height = {mindeviceId: parseInt($$('#sendHeight#videoInput').val()), max: 480}; } |
4. Установка FPS
code:
Code Block | ||||
---|---|---|---|---|
| ||||
if (constraints.video) { width: parseInt($('#sendWidth').val()), ifheight: (constraints.customStream) {parseInt($('#sendHeight').val()) }; ... if } else(Browser.isSafariWebRTC() && Browser.isiOS() && Flashphoner.getMediaProviders()[0] === "WebRTC") { constraints.video.. deviceId = if (parseInt({exact: $('#fps#videoInput').val()) > 0)}; constraints.video.frameRate = parseInt($('#fps').val()); } } |
5. Установка битрейта видео в кбит/с
...
} |
4. Установка FPS
code:
Code Block | ||||
---|---|---|---|---|
| ||||
if (constraints.video) { if (constraints.customStream) {... if (parseInt($('#fps').val()) ... > 0) } else { constraints.video.frameRate = parseInt($('#fps').val()); } |
5. Установка битрейта видео в кбит/с
code:
Code Block | ||||
---|---|---|---|---|
| ||||
... if (constraints.video) { ... if (parseInt($('#sendVideoMinBitrate').val()) > 0) constraints.video.minBitrate = parseInt($('#sendVideoMinBitrate').val()); if (parseInt($('#sendVideoMaxBitrate').val()) > 0) constraints.video.maxBitrate = parseInt($('#sendVideoMaxBitrate').val()); ... } |
6. Установка CPU Overuse Detection
code:
Code Block | ||||
---|---|---|---|---|
| ||||
if (!$("#cpuOveruseDetection").is(':checked')) {
mediaConnectionConstraints = {
"mandatory": {
googCpuOveruseDetection: false
}
}
} |
7. Отключение камеры
code:
Code Block | ||||
---|---|---|---|---|
| ||||
if ($("#muteVideoToggle").is(":checked")) {
muteVideo();
} |
Тестирование захвата с камеры и микрофона локально
Локальное тестирование захвата с микрофона и камеры предназначено для того, чтобы проверить работоспособность микрофона и камеры в браузере, не отправляя поток на сервер.
code:
Code Block | ||||
---|---|---|---|---|
| ||||
function startTest() {
Flashphoner.getMediaAccess(getConstraints(), localVideo).then(function (disp) {
$("#testBtn").text("Release").off('click').click(function () {
$(this).prop('disabled', true);
stopTest();
}).prop('disabled', false);
window.AudioContext = window.AudioContext || window.webkitAudioContext;
if (Flashphoner.getMediaProviders()[0] == "WebRTC" && window.AudioContext) {
for (i = 0; i < localVideo.children.length; i++) {
if (localVideo.children[i] && localVideo.children[i].id.indexOf("-LOCAL_CACHED_VIDEO") != -1) {
var stream = localVideo.children[i].srcObject;
audioContextForTest = new AudioContext();
var microphone = audioContextForTest.createMediaStreamSource(stream);
var javascriptNode = audioContextForTest.createScriptProcessor(1024, 1, 1);
microphone.connect(javascriptNode);
javascriptNode.connect(audioContextForTest.destination);
javascriptNode.onaudioprocess = function (event) {
var inpt_L = event.inputBuffer.getChannelData(0);
var sum_L = 0.0;
for (var i = 0; i < inpt_L.length; ++i) {
sum_L += inpt_L[i] * inpt_L[i];
}
$("#micLevel").text(Math.floor(Math.sqrt(sum_L / inpt_L.length) * 100));
}
}
}
} else if (Flashphoner.getMediaProviders()[0] == "Flash") {
micLevelInterval = setInterval(function () {
$("#micLevel").text(disp.children[0].getMicrophoneLevel());
}, 500);
}
testStarted = true;
}).catch(function (error) {
$("#testBtn").prop('disabled', false);
testStarted = false;
});
} |
Замена отдельных параметров SDP
При публикации потока предусмотрена возможность замены параметров SDP. В поле 'SDP replace' указывается шаблон поиска параметра, который нужно заменить, в поле 'with' указывается новое значение параметра.
Для замены параметров SDP используется callback-функция, которая должна быть указана при создании потока в параметре sdpHook метода createStream():
создание потока code
Code Block | ||||
---|---|---|---|---|
| ||||
publishStream = session.createStream({
name: streamName,
display: localVideo,
cacheLocalResources: true,
constraints: constraints,
mediaConnectionConstraints: mediaConnectionConstraints,
sdpHook: rewriteSdp,
...
}) |
функция rewriteSdp code
Code Block | ||||
---|---|---|---|---|
| ||||
function rewriteSdp(sdp) {
var sdpStringFind = $("#sdpStringFind").val().replace('\\r\\n','\r\n');
var sdpStringReplace = $("#sdpStringReplace").val().replace('\\r\\n','\r\n');
if (sdpStringFind != 0 && sdpStringReplace != 0) {
var newSDP = sdp.sdpString.toString();
newSDP = newSDP.replace(new RegExp(sdpStringFind,"g"), sdpStringReplace);
return newSDP;
}
return sdp.sdpString;
} |
Увеличение битрейта публикуемого видео в браузере Chrome
Замена параметров SDP позволяет увеличить битрейт публикуемого видео. Для этого необходимо при публикации H264 заменить параметр 'a' по шаблону
Code Block | ||
---|---|---|
| ||
a=fmtp:(.*) (.*) |
на
Code Block | ||
---|---|---|
| ||
a=fmtp:$1 $2;x-google-min-bitrate=2500 |
Здесь 2500 - битрейт в килобитах в секунду.
Подобным образом можно указать битрейт видео на старте (атрибут x-google-start-bitrate) и ограничить максимальный битрейт (атрибут x-google-max-bitrate). Отметим, что, если указать только минимальный битрейт, то выше 2500 кбит/с битрейт поднять не удается, возможно, по умолчанию в Chrome зафиксирован максимальный битрейт на уровне 2500 кбит/с. Если необходимо использовать более высокие значения, например, для трансляции потока высокого разрешения, должны быть указаны и минимальное, и максимальное значения:
Code Block | ||
---|---|---|
| ||
a=fmtp:$1 = parseInt($('#sendVideoMinBitrate').val()); if (parseInt($('#sendVideoMaxBitrate').val()) > 0) constraints.video.maxBitrate = parseInt($('#sendVideoMaxBitrate').val()); ... } } |
6. Установка CPU Overuse Detection
code:
Code Block | ||||
---|---|---|---|---|
| ||||
if (!$("#cpuOveruseDetection").is(':checked')) { mediaConnectionConstraints = { "mandatory": { googCpuOveruseDetection: false }$2;x-google-max-bitrate=7000;x-google-min-bitrate=3000 |
В этом случае браузер будет держать битрейт при публикации потока в пределах от 3000 до 7000 кбит/с.
При публикации потока VP8 необходимо заменить
Code Block | ||
---|---|---|
| ||
a=rtpmap:(.*) VP8/90000\r\n |
на
Code Block | ||
---|---|---|
| ||
a=rtpmap:$1 VP8/90000\r\na=fmtp:$1 x-google-min-bitrate=3000;x-google-max-bitrate=7000\r\n |
Возможность управления битрейтом доступна только в браузере Chrome.
Задание пропускной способности канала
Замена параметров SDP позволяет задать пропускную способность канала при публикации потока. Для этого необходимо при публикации заменить параметр 'c' по шаблону
Code Block | ||
---|---|---|
| ||
c=IN (.*)\r\n |
на
Code Block | ||
---|---|---|
| ||
c=IN $1\r\nb=AS:10000\r\n |
Установка используемых кодеков
При публикации потока предусмотрена возможность убрать из WebRTC SDP кодеки, которые не должны использоваться при публикации данного потока, например:
Code Block | ||||
---|---|---|---|---|
| ||||
publishStream = session.createStream({ }... } |
7. Отключение камеры
code:
Code Block | ||||
---|---|---|---|---|
| ||||
stripCodecs: "h264,H264,flv,mpv" if ($("#muteVideoToggle").is(":checked"))}).on(STREAM_STATUS.PUBLISHING, function (publishStream) { muteVideo();... }); } |
Тестирование захвата с камеры и микрофона локально
Локальное тестирование захвата с микрофона и камеры предназначено для того, чтобы проверить работоспособность микрофона и камеры в браузере, не отправляя поток на сервер.
...
publishStream.publish(); |
Данная возможность полезна, в частности, для обхода багов браузера с каким-либо кодеком. Например, если в браузере не работает H.264, можно отключить его и перейти на VP8 при работе по WebRTC.
Управление выводом звука
При воспроизведении потока можно выбрать (и переключить "на лету") устройство вывода звука в браузерах Chrome и MS Edge.
code:
Code Block | |||||||
---|---|---|---|---|---|---|---|
| function startTest() {
if (Browser.isSafariWebRTC()) {
Flashphoner.playFirstVideo(localVideo, true);
Flashphoner.playFirstVideo(remoteVideo, false);
}
Flashphoner.getMediaAccess(getConstraints(), localVideo
| ||||||
Flashphoner.getMediaDevices(null, true, MEDIA_DEVICE_KIND.OUTPUT).then(function (displist) { $("#testBtn").text("Release").off('click').click(list.audio.forEach(function (device) { $(this).prop('disabled', true... }); }).catch(function (error) { stopTest$('#audioOutputForm').remove(); }).prop('disabled', false); window.AudioContext = window.AudioContext || window.webkitAudioContext; }); |
Отметим, что в браузерах Firefox и Safari нельзя получить список устройств вывода, поэтому данная функция а них не работает
Отображение WebRTC-статистики
При публикации и воспроизведении потока клиентское приложение может получить WebRTC-статистику в соответствии со стандартом. Эта статистика может быть отображена в браузере, например:
Отметим, что в браузере Safari отображается только статистика аудио.
1. Отображение статистики при публикации потока
stream.getStats() code:
Code Block | ||||
---|---|---|---|---|
| ||||
if (Flashphoner.getMediaProviders()[0] == "WebRTC" && window.AudioContextpublishStream.getStats(function (stats) { forif (istats = 0; i < localVideo.children.length; i++&& stats.outboundStream) { if (localVideo.children[i] && localVideo.children[i].id.indexOf("-LOCAL_CACHED_VIDEO") != -1stats.outboundStream.video) { var stream = localVideo.children[i].srcObjectshowStat(stats.outboundStream.video, "outVideoStat"); audioContextForTestlet vBitrate = new AudioContext() (stats.outboundStream.video.bytesSent - videoBytesSent) * 8; var microphoneif ($('#outVideoStatBitrate').length == audioContextForTest.createMediaStreamSource(stream);0) { var javascriptNode = audioContextForTest.createScriptProcessor(1024, 1, 1); let html = "<div>Bitrate: " + "<span id='outVideoStatBitrate' style='font-weight: normal'>" + vBitrate + "</span>" + "</div>"; microphone.connect(javascriptNode); javascriptNode.connect(audioContextForTest.destination$("#outVideoStat").append(html); javascriptNode.onaudioprocess} = function (event) else { ...$('#outVideoStatBitrate').text(vBitrate); } } videoBytesSent = stats.outboundStream.video.bytesSent; } } else if (Flashphoner.getMediaProviders()[0] == "Flash") {... micLevelInterval = setInterval(function () {} $("#micLevel").text(disp.children[0].getMicrophoneLevel());if (stats.outboundStream.audio) { }, 500); } showStat(stats.outboundStream.audio, "outAudioStat"); testStarted = true; }).catch(function (error) { let aBitrate = $("#testBtn").prop('disabled', false)(stats.outboundStream.audio.bytesSent - audioBytesSent) * 8; testStarted = false; }); drawSquare(); } |
Замена отдельных параметров SDP
При публикации потока предусмотрена возможность замены параметров SDP. В поле 'SDP replace' указывается шаблон поиска параметра, который нужно заменить, в поле 'with' указывается новое значение параметра.
Для замены параметров SDP используется callback-функция, которая должна быть указана при создании потока в параметре sdpHook метода createStream():
создание потока code
Code Block | ||||
---|---|---|---|---|
| ||||
if ($('#outAudioStatBitrate').length == 0) { previewStream = session.createStream({ let html = "<div>Bitrate: " + "<span name: streamName, id='outAudioStatBitrate' style='font-weight: normal'>" + aBitrate + "</span>" + "</div>"; display: remoteVideo, $("#outAudioStat").append(html); constraints: constraints, sdpHook: rewriteSdp } else { ... }) |
функция rewriteSdp code
Code Block | ||||
---|---|---|---|---|
| ||||
function rewriteSdp(sdp) { var sdpStringFind = $("#sdpStringFind"'#outAudioStatBitrate').valtext(aBitrate); var sdpStringReplace = $("#sdpStringReplace").val(); } if (sdpStringFind != 0 && sdpStringReplace != 0) { var newSDPaudioBytesSent = sdpstats.outboundStream.sdpStringaudio.toString()bytesSent; newSDP = newSDP.replace(sdpStringFind, sdpStringReplace); } return newSDP; } } return sdp.sdpString; } |
Увеличение битрейта публикуемого видео в браузере Chrome
Замена параметров SDP позволяет увеличить битрейт публикуемого видео. Для этого необходимо при публикации заменить параметр 'a' по шаблону
Code Block | ||||
---|---|---|---|---|
| ||||
a=fmtp:(.*) (.*) |
на
Code Block | ||||
---|---|---|---|---|
| ||||
a=fmtp:$1 $2;x-google-min-bitrate=2500 |
Здесь 2500 - битрейт в килобитах в секунду.
Подобным образом можно указать битрейт видео на старте (атрибут x-google-start-bitrate) и ограничить максимальный битрейт (атрибут x-google-max-bitrate).
Отметим, что такая возможность управления битрейтом доступна только в браузере Chrome.
Установка используемых кодеков
...
...
}); |
2. Отображение статистики при воспроизведении потока
stream.getStats() code:
Code Block | ||||
---|---|---|---|---|
| ||||
publishStream = session.createStream({ ...previewStream.getStats(function (stats) { stripCodecs: "h264,H264,flv,mpv" if }).on(STREAM_STATUS.PUBLISHING, function (publishStream(stats && stats.inboundStream) { ... }); publishStream.publish(); |
Данная возможность полезна, в частности, для обхода багов браузера с каким-либо кодеком. Например, если в браузере не работает H.264, можно отключить его и перейти на VP8 при работе по WebRTC.
Управление выводом звука в браузере Chrome
При воспроизведении потока можно выбрать (и переключить "на лету") устройство вывода звука в браузере Chrome.
code:
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.getMediaDevices(null, true, MEDIA_DEVICE_KIND.OUTPUT).then(function (list) { if (stats.inboundStream.video) { showStat(stats.inboundStream.video, "inVideoStat"); let vBitrate = (stats.inboundStream.video.bytesReceived - videoBytesReceived) * 8; list.audio.forEach(function (device if ($('#inVideoStatBitrate').length == 0) { ... }); }).catch(function (error) { $('#audioOutputForm').remove(); }); |
Отображение WebRTC-статистики
При публикации и воспроизведении потока клиентское приложение может получить WebRTC-статистику в соответствии со стандартом. Эта статистика может быть отображена в браузере, например:
Отметим, что в браузере Safari отображается только статистика аудио.
1. Отображение статистики при публикации потока
stream.getStats() code:
Code Block | ||||
---|---|---|---|---|
| ||||
publishStream.getStats(function (stats) {let html = "<div>Bitrate: " + "<span id='inVideoStatBitrate' style='font-weight: normal'>" + vBitrate + "</span>" + "</div>"; if (stats && stats.outboundStream) { if(stats.outboundStream.videoStats) {$("#inVideoStat").append(html); $('#outVideoStatBytesSent').text(stats.outboundStream.videoStats.bytesSent); } else { $('#outVideoStatPacketsSent').text(stats.outboundStream.videoStats.packetsSent); $('#outVideoStatFramesEncoded#inVideoStatBitrate').text(stats.outboundStream.videoStats.framesEncodedvBitrate); } else { videoBytesReceived = stats.inboundStream.video.bytesReceived; } ... if(stats.outboundStream.audioStats) { } $('#outAudioStatBytesSent').textif (stats.outboundStreaminboundStream.audioStats.bytesSent);audio) { $('#outAudioStatPacketsSent').text showStat(stats.outboundStream.audioStats.packetsSentinboundStream.audio, "inAudioStat"); } else { let aBitrate = (stats.inboundStream.audio.bytesReceived - audioBytesReceived) * 8; ... } if ($('#inAudioStatBitrate').length }== 0) { }); |
2. Отображение статистики при воспроизведении потока
stream.getStats() code:
Code Block | ||||
---|---|---|---|---|
| ||||
previewStream.getStats(function (stats) { if (stats && stats.inboundStream) { let html = "<div style='font-weight: bold'>Bitrate: " + if(stats.inboundStream.videoStats) { "<span id='inAudioStatBitrate' style='font-weight: normal'>" + aBitrate + "</span>" + "</div>"; $('#inVideoStatBytesReceived').text(stats.inboundStream.videoStats.bytesReceived); $('#inVideoStatPacketsReceived'"#inAudioStat").text(stats.inboundStream.videoStats.packetsReceivedappend(html); $('#inVideoStatFramesDecoded').text(stats.inboundStream.videoStats.framesDecoded); } else { ... } $('#inAudioStatBitrate').text(aBitrate); if(stats.inboundStream.audioStats) { } $('#inAudioStatBytesReceived').text(stats.inboundStream.audioStats.bytesReceived); audioBytesReceived $('#inAudioStatPacketsReceived').text(= stats.inboundStream.audioStatsaudio.packetsReceived)bytesReceived; } else {} ... } } }); |
Управление параметрами картинки при публикации потока
...
1. Во время трансляции потока с выбранных камеры и микрофона, при установке переключателя 'Screen share' в положение 'on' будет вызвана функция switchToScreen
stream.switchToScreen code
Code Block | ||||
---|---|---|---|---|
| ||||
function switchToScreen() { if (publishStream) { $('#switchBtn').prop('disabled', true); $('#videoInput').prop('disabled', true); publishStream.switchToScreen($('#mediaSource').val()).catch(function () { $("#screenShareToggle").removeAttr("checked"); $('#switchBtn').prop('disabled', false); $('#videoInput').prop('disabled', false); }); } } |
...
4. Для возврата к трансляции потока с веб-камеры вызывается функция switch ToCam
stream.switchToCam code
Code Block | ||||
---|---|---|---|---|
| ||||
function switchToCam() { if (publishStream) { publishStream.switchToCam(); $('#switchBtn').prop('disabled', false); $('#videoInput').prop('disabled', false); } } |
...
3. Для публикации экрана в браузере Chrome необходимо расширение.
4. Переключение работает только в том случае, если первым опубликован поток с веб-камеры.
Известные проблемы
1. Не работает переключение микрофона в браузере Safari.
...