...
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");
}); |
...
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.getMediaDevices(null, true, nullMEDIA_DEVICE_KIND.INPUT, {video: true, audio: false}).then(function (list) { ... 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 () { publishStreamstream.switchCam();.then(function(id) { } $('#videoInput option:selected').prop('disabledselected', false); $('#sendCanvasStream').is(':checked')); |
...
"#videoInput option[value='"+ id +"']").prop('selected', true);
}).catch(function(e) {
console.log("Error " + e);
});
}); |
Переключение камеры может осуществляться "на лету", во время трансляции потока. Переключение работает в следующем порядке:
...
3. Установка разрешения видео
code:
Code Block | ||||
---|---|---|---|---|
| ||||
constraints.video = { deviceId: $('#videoInput').val(), width: parseInt($('#sendWidth').val()), height: parseInt($('#sendHeight').val()) }}; if (Browser.isSafariWebRTC() && Browser.isiOS() && Flashphoner.getMediaProviders()[0] === "WebRTC") { constraints.video.deviceId = {exact: $('#videoInput').val()}; } |
4. Установка FPS
code:
Code Block | ||||
---|---|---|---|---|
| ||||
if (constraints.video) { constraints..video.width = {min: . if (parseInt($('#sendWidth#fps').val()), max: 640};> 0) constraints.video.heightframeRate = {min: parseInt($('#sendHeight#fps').val()), max: 480}; } |
45. Установка FPSбитрейта видео в кбит/с
code:
Code Block | ||||
---|---|---|---|---|
| ||||
if (constraints.video) { if (constraints.customStream) {... if (parseInt($('#sendVideoMinBitrate').val()) ...> 0) } else { constraints.video.minBitrate = parseInt($('#sendVideoMinBitrate').val()); ... if (parseInt($('#fps#sendVideoMaxBitrate').val()) > 0) constraints.video.frameRatemaxBitrate = parseInt($('#fps#sendVideoMaxBitrate').val()); }... } |
56. Установка битрейта видео в кбит/сCPU Overuse Detection
code:
Code Block | ||||
---|---|---|---|---|
| ||||
if (constraints.video(!$("#cpuOveruseDetection").is(':checked')) { if (constraints.customStream)mediaConnectionConstraints = { ..."mandatory": { } else { googCpuOveruseDetection: false ... } if (parseInt($('#sendVideoMinBitrate').val()) > 0) } } |
7. Отключение камеры
code:
Code Block | ||||
---|---|---|---|---|
| ||||
constraints.video.minBitrate = parseIntif ($('#sendVideoMinBitrate'"#muteVideoToggle").valis(":checked")); { if (parseInt($('#sendVideoMaxBitrate').val()) > 0)muteVideo(); } |
Тестирование захвата с камеры и микрофона локально
Локальное тестирование захвата с микрофона и камеры предназначено для того, чтобы проверить работоспособность микрофона и камеры в браузере, не отправляя поток на сервер.
code:
Code Block | ||||
---|---|---|---|---|
| ||||
function startTest() { constraints.video.maxBitrate = parseInt($('#sendVideoMaxBitrate').val());Flashphoner.getMediaAccess(getConstraints(), localVideo).then(function (disp) { ... $("#testBtn").text("Release").off('click').click(function () { } } |
6. Установка CPU Overuse Detection
code:
Code Block | ||||
---|---|---|---|---|
| ||||
if (!$("#cpuOveruseDetection"this).isprop(':checked')) {disabled', true); mediaConnectionConstraints = { stopTest(); }).prop('disabled', "mandatory": { false); window.AudioContext = window.AudioContext || window.webkitAudioContext; googCpuOveruseDetection: false if (Flashphoner.getMediaProviders()[0] == "WebRTC" && window.AudioContext) { } for } (i = 0; } |
7. Отключение камеры
code:
Code Block | ||||
---|---|---|---|---|
| ||||
i < localVideo.children.length; i++) { if ($("#muteVideoToggle").is(":checked")localVideo.children[i] && localVideo.children[i].id.indexOf("-LOCAL_CACHED_VIDEO") != -1) { muteVideo(); } |
Тестирование захвата с камеры и микрофона локально
Локальное тестирование захвата с микрофона и камеры предназначено для того, чтобы проверить работоспособность микрофона и камеры в браузере, не отправляя поток на сервер.
code:
Code Block | ||||
---|---|---|---|---|
| ||||
function startTest() { var stream = localVideo.children[i].srcObject; if (Browser.isSafariWebRTC()) { Flashphoner.playFirstVideo(localVideo, true); audioContextForTest = new Flashphoner.playFirstVideo(remoteVideo, falseAudioContext(); } Flashphoner.getMediaAccess(getConstraints(), localVideo).then(function (disp) { var microphone $("#testBtn").text("Release").off('click').click(function () { = audioContextForTest.createMediaStreamSource(stream); $(this).prop('disabled', true var javascriptNode = audioContextForTest.createScriptProcessor(1024, 1, 1); stopTest(); })microphone.prop('disabled', falseconnect(javascriptNode); window.AudioContext = window.AudioContext || window.webkitAudioContext; if javascriptNode.connect(FlashphoneraudioContextForTest.getMediaProviders()[0] == "WebRTC" && window.AudioContext) { destination); for (i = 0; i < localVideo.children.length; i++javascriptNode.onaudioprocess = function (event) { if (localVideo.children[i] && localVideo.children[i].id.indexOf("-LOCAL_CACHED_VIDEO") != -1) { var inpt_L = event.inputBuffer.getChannelData(0); var streamsum_L = localVideo.children[i].srcObject; 0.0; for (var audioContextForTesti = new AudioContext();0; i < inpt_L.length; ++i) { var microphone = audioContextForTest.createMediaStreamSource(stream); sum_L += inpt_L[i] * inpt_L[i]; var javascriptNode = audioContextForTest.createScriptProcessor(1024, 1, 1); } microphone.connect(javascriptNode); $("#micLevel").text(Math.floor(Math.sqrt(sum_L / inpt_L.length) javascriptNode.connect(audioContextForTest.destination* 100)); javascriptNode.onaudioprocess} = function (event) { } ...} } else if (Flashphoner.getMediaProviders()[0] == } } } } else if (Flashphoner.getMediaProviders()[0] == "Flash") "Flash") { micLevelInterval = setInterval(function () { $("#micLevel").text(disp.children[0].getMicrophoneLevel()); }, 500); } testStarted = true; }).catch(function (error) { $("#testBtn").prop('disabled', false); testStarted = false; }); drawSquare(); } |
Замена отдельных параметров SDP
...
Для замены параметров SDP используется callback-функция, которая должна быть указана при создании потока в параметре sdpHook метода createStream():
создание потока code
Code Block | ||||
---|---|---|---|---|
| ||||
publishStream previewStream = session.= session.createStream({ name: streamName, display: remoteVideolocalVideo, 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(sdpStringFind,new RegExp(sdpStringFind,"g"), sdpStringReplace); return newSDP; } return sdp.sdpString; } |
...
При воспроизведении потока можно выбрать (и переключить "на лету") устройство вывода звука в браузерах Chrome и MS Edge.
code:
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.getMediaDevices(null, true, MEDIA_DEVICE_KIND.OUTPUT).then(function (list) { list.audio.forEach(function (device) { ... }); }).catch(function (error) { $('#audioOutputForm').remove(); }); |
...
1. Отображение статистики при публикации потока
stream.getStats() code:
Code Block | ||||
---|---|---|---|---|
| ||||
publishStream.getStats(function (stats) {.getStats(function (stats) { if (stats && stats.outboundStream) { if (stats.outboundStream.video) { showStat(stats.outboundStream.video, "outVideoStat"); let vBitrate = (stats.outboundStream.video.bytesSent - videoBytesSent) * 8; if ($('#outVideoStatBitrate').length == 0) { let html = "<div>Bitrate: " + "<span id='outVideoStatBitrate' style='font-weight: normal'>" + vBitrate + "</span>" + "</div>"; $("#outVideoStat").append(html); } else { $('#outVideoStatBitrate').text(vBitrate); } videoBytesSent = stats.outboundStream.video.bytesSent; ... } if (stats.outboundStream.audio) { showStat(stats.outboundStream.audio, "outAudioStat"); let aBitrate = (stats.outboundStream.audio.bytesSent - audioBytesSent) * 8; if ($('#outAudioStatBitrate').length == 0) { let html = "<div>Bitrate: " + "<span id='outAudioStatBitrate' style='font-weight: normal'>" + aBitrate + "</span>" + "</div>"; $("#outAudioStat").append(html); } else { $('#outAudioStatBitrate').text(aBitrate); } audioBytesSent = stats.outboundStream.audio.bytesSent; } } ... }); |
2. Отображение статистики при воспроизведении потока
stream.getStats() code:
Code Block | ||||
---|---|---|---|---|
| ||||
previewStream.getStats(function (stats) { if (stats && stats.inboundStream) { if (stats.inboundStream.video) { showStat(stats.inboundStream.video, "inVideoStat"); let vBitrate = (stats.inboundStream.video.bytesReceived - videoBytesReceived) * 8; if ($('#inVideoStatBitrate').length == 0) { 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').text(if (stats.outboundStreaminboundStream.audioStats.bytesSent); audio) { $('#outAudioStatPacketsSent').textshowStat(stats.outboundStream.audioStats.packetsSentinboundStream.audio, "inAudioStat"); } else { let aBitrate ... } = (stats.inboundStream.audio.bytesReceived - audioBytesReceived) * 8; } }); |
2. Отображение статистики при воспроизведении потока
stream.getStats() code:
Code Block | ||||
---|---|---|---|---|
| ||||
previewStream.getStats(function (statsif ($('#inAudioStatBitrate').length == 0) { 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); } } |
...