WCS позволяет настраивать камеру и микрофон в браузере. Рассмотрим, как и какими параметрами можно управлять при захвате аудио- и видеопотока, на примере веб-приложения Media Devices:
1.Выбор микрофона из списка
code:
Flashphoner.getMediaDevices(null, true).then(function (list) { list.audio.forEach(function (device) { ... }); ... }).catch(function (error) { $("#notifyFlash").text("Failed to get media devices"); }); |
2. Переключение микрофона во время трансляции
code:
$("#switchMicBtn").click(function (){ publishStream.switchMic(); }).prop('disabled', !($('#sendAudio').is(':checked'))); |
3. Регулировка усиления микрофона (работает только в браузере Chrome)
code:
$("#micGainControl").slider({ range: "min", min: 0, max: 100, value: currentGainValue, step: 10, animate: true, slide: function (event, ui) { currentGainValue = ui.value; if(previewStream) { publishStream.setMicrophoneGain(currentGainValue); } } }); |
4. Включение коррекции ошибок (только для кодека Opus)
code:
if (constraints.audio) { constraints.audio = { deviceId: $('#audioInput').val() }; if ($("#fec").is(':checked')) constraints.audio.fec = $("#fec").is(':checked'); ... } |
5. Установка стерео / моно режима.
code:
if (constraints.audio) { constraints.audio = { deviceId: $('#audioInput').val() }; ... if ($("#sendStereoAudio").is(':checked')) constraints.audio.stereo = $("#sendStereoAudio").is(':checked'); ... } |
6. Установка битрейта звука в кбит/с
code:
if (constraints.audio) { constraints.audio = { deviceId: $('#audioInput').val() }; ... if (parseInt($('#sendAudioBitrate').val()) > 0) constraints.audio.bitrate = parseInt($('#sendAudioBitrate').val()); } |
7. Отключение микрофона.
code:
if ($("#muteAudioToggle").is(":checked")) { muteAudio(); } |
1. Выбор камеры
code:
Flashphoner.getMediaDevices(null, true).then(function (list) { ... list.video.forEach(function (device) { ... }); }).catch(function (error) { $("#notifyFlash").text("Failed to get media devices"); }); |
2. Переключение камер.
code:
$("#switchBtn").text("Switch").off('click').click(function () { publishStream.switchCam(); }).prop('disabled', $('#sendCanvasStream').is(':checked')); |
Переключение камеры может осуществляться "на лету", во время трансляции потока. Переключение работает в следующем порядке:
3. Установка разрешения видео
code:
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()}; constraints.video.width = {min: parseInt($('#sendWidth').val()), max: 640}; constraints.video.height = {min: parseInt($('#sendHeight').val()), max: 480}; } |
4. Установка FPS
code:
if (constraints.video) { if (constraints.customStream) { ... } else { ... if (parseInt($('#fps').val()) > 0) constraints.video.frameRate = parseInt($('#fps').val()); } } |
5. Установка битрейта видео в кбит/с
code:
if (constraints.video) { if (constraints.customStream) { ... } else { ... 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:
if (!$("#cpuOveruseDetection").is(':checked')) { mediaConnectionConstraints = { "mandatory": { googCpuOveruseDetection: false } } } |
7. Отключение камеры
code:
if ($("#muteVideoToggle").is(":checked")) { muteVideo(); } |
Локальное тестирование захвата с микрофона и камеры предназначено для того, чтобы проверить работоспособность микрофона и камеры в браузере, не отправляя поток на сервер.
code:
function startTest() { if (Browser.isSafariWebRTC()) { Flashphoner.playFirstVideo(localVideo, true); Flashphoner.playFirstVideo(remoteVideo, false); } 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) { ... } } } } 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; }); drawSquare(); } |
При публикации потока предусмотрена возможность замены параметров SDP. В поле 'SDP replace' указывается шаблон поиска параметра, который нужно заменить, в поле 'with' указывается новое значение параметра.
Для замены параметров SDP используется callback-функция, которая должна быть указана при создании потока в параметре sdpHook метода createStream():
создание потока code
previewStream = session.createStream({ name: streamName, display: remoteVideo, constraints: constraints, sdpHook: rewriteSdp ... }) |
функция rewriteSdp code
function rewriteSdp(sdp) { var sdpStringFind = $("#sdpStringFind").val(); var sdpStringReplace = $("#sdpStringReplace").val(); if (sdpStringFind != 0 && sdpStringReplace != 0) { var newSDP = sdp.sdpString.toString(); newSDP = newSDP.replace(sdpStringFind, sdpStringReplace); return newSDP; } return sdp.sdpString; } |
Замена параметров SDP позволяет увеличить битрейт публикуемого видео. Для этого необходимо при публикации заменить параметр 'a' по шаблону
a=fmtp:(.*) (.*) |
на
a=fmtp:$1 $2;x-google-min-bitrate=2500 |
Здесь 2500 - битрейт в килобитах в секунду.
Подобным образом можно указать битрейт видео на старте (атрибут x-google-start-bitrate) и ограничить максимальный битрейт (атрибут x-google-max-bitrate).
Отметим, что такая возможность управления битрейтом доступна только в браузере Chrome.
При публикации потока предусмотрена возможность убрать из WebRTC SDP кодеки, которые не должны использоваться при публикации данного потока, например:
publishStream = session.createStream({ ... stripCodecs: "h264,H264,flv,mpv" }).on(STREAM_STATUS.PUBLISHING, function (publishStream) { ... }); publishStream.publish(); |
Данная возможность полезна, в частности, для обхода багов браузера с каким-либо кодеком. Например, если в браузере не работает H.264, можно отключить его и перейти на VP8 при работе по WebRTC.
При воспроизведении потока можно выбрать (и переключить "на лету") устройство вывода звука в браузере Chrome.
code:
Flashphoner.getMediaDevices(null, true, MEDIA_DEVICE_KIND.OUTPUT).then(function (list) { list.audio.forEach(function (device) { ... }); }).catch(function (error) { $('#audioOutputForm').remove(); }); |
При публикации и воспроизведении потока клиентское приложение может получить WebRTC-статистику в соответствии со стандартом. Эта статистика может быть отображена в браузере, например:
Отметим, что в браузере Safari отображается только статистика аудио.
1. Отображение статистики при публикации потока
stream.getStats() code:
publishStream.getStats(function (stats) { if (stats && stats.outboundStream) { if(stats.outboundStream.videoStats) { $('#outVideoStatBytesSent').text(stats.outboundStream.videoStats.bytesSent); $('#outVideoStatPacketsSent').text(stats.outboundStream.videoStats.packetsSent); $('#outVideoStatFramesEncoded').text(stats.outboundStream.videoStats.framesEncoded); } else { ... } if(stats.outboundStream.audioStats) { $('#outAudioStatBytesSent').text(stats.outboundStream.audioStats.bytesSent); $('#outAudioStatPacketsSent').text(stats.outboundStream.audioStats.packetsSent); } else { ... } } }); |
2. Отображение статистики при воспроизведении потока
stream.getStats() code:
previewStream.getStats(function (stats) { if (stats && stats.inboundStream) { if(stats.inboundStream.videoStats) { $('#inVideoStatBytesReceived').text(stats.inboundStream.videoStats.bytesReceived); $('#inVideoStatPacketsReceived').text(stats.inboundStream.videoStats.packetsReceived); $('#inVideoStatFramesDecoded').text(stats.inboundStream.videoStats.framesDecoded); } else { ... } if(stats.inboundStream.audioStats) { $('#inAudioStatBytesReceived').text(stats.inboundStream.audioStats.bytesReceived); $('#inAudioStatPacketsReceived').text(stats.inboundStream.audioStats.packetsReceived); } else { ... } } }); |
При публикации видео потока с помощью граничных условий (constraints) можно управлять разрешением картинки и частотой кадров
Разрешение картинки можно указать точно
constraints = {audio:true, video:{width:320,height:240}} |
Однако, в некоторых случаях требуется указать диапазон для ширины и высоты
constraints = {audio:true, video:{width:{min:160,max:320},height:{min:120,max:240}}} |
Для некоторых браузеров, например, iOS Safari, необходимо указать точные значения в виде диапазона (в последних версиях это обрабатывается на уровне WebSDK)
constraints = {audio:true, video:{width:{min:320,max:320},height:{min:240,max:240}}} |
Частота кадров может быть указана точно
constraints = {audio:true, video:{frameRate:30} |
или в виде диапазона
constraints = {audio:true, video:{frameRate:{min:15,max:30}} |
В некоторых случаях, например, если веб-камера поддерживает 24 fps, при точном указании 30 fps публикация может завершиться ошибкой. В таком случае нужно задать частоту кадров как идеальную
constraints = {audio:true, video:{frameRate:{ideal:30}} |
При организации вебинаров возникает необходимость переключаться между потоками, захваченными с веб-камеры ведущего и с экрана. во время трансляции. В идеале, переключение должно быть бесшовным и с сохранением звуковой дорожки с микрофона ведущего. В последних версиях WebSDK реализована такая возможность для браузеров Chrome и Firefox, рассмотрим пример использования в приложении Media Devices.
1. Во время трансляции потока с выбранных камеры и микрофона, при установке переключателя 'Screen share' в положение 'on' будет вызвана функция switchToScreen
stream.switchToScreen code
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); }); } } |
В данную функцию передается источник потока (экран).
2. Затем пользователь в браузере Chrome при помощи расширения, а в браузере Firefox средствами браузера должен выбрать весь экран или окно программы для трансляции:
3. На сервер транслируется поток с экрана
При этом источник трансляции звука не меняется.
4. Для возврата к трансляции потока с веб-камеры вызывается функция switch ToCam
stream.switchToCam code
function switchToCam() { if (publishStream) { publishStream.switchToCam(); $('#switchBtn').prop('disabled', false); $('#videoInput').prop('disabled', false); } } |
1. Переключение трансляции работает только в браузерах Chrome и Firefox.
2. Невозможно переключить веб-камеру в то время, когда транслируется экран.
3. Для публикации экрана в браузере Chrome необходимо расширение.
4. Переключение работает только в том случае, если первым опубликован поток с веб-камеры.
1. Не работает переключение микрофона в браузере Safari.
Симптомы: не переключается микрофон при помощи метода switchMic() WCS WebSDK.
Решение: использовать другой браузер, поскольку Safari всегда использует микрофон sound input, выбранный в настройках звука системы sound menu (для входа необходимо зажать клавишу Option (Alt) и щелкнуть по иконке звука в меню). После выбора другого микрофона в sound menu требуется перезагрузка Mac.
Если не работает микрофон Logitech USB camera (когда выбран в sound input), может помочь изменение format / sample rate в Audio MIDI Setup и перезагрузка.
2. iOS Safari зависает на воспроизведении, если публикующий переключает камеру.
Симптомы: при переключении камеры воспроизведение публикуемого потока в браузере iOS Safari зависает.
Решение: включить транскодинг при помощи параметра в файле flashphoner.properties
disable_streaming_proxy=true |
или указав фиксированное разрешение для плеера при воспроизведении
session.createStream({constraints:{audio:true,video:{width:320,height:240}}}).play(); |