Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

WCS позволяет настраивать камеру и микрофон в браузере. Рассмотрим, как и какими параметрами можно управлять при захвате аудио- и видеопотока, на примере веб-приложения Media Devices:

media_device_manager.html

manager.js

Настройки микрофона

1.Выбор микрофона из списка


кодcode:

Code Block
languagejs
themeRDark
titleКод выбора микрофона из списка устройств
    Flashphoner.getMediaDevices(null, true, MEDIA_DEVICE_KIND.INPUT).then(function (list) {
        list.audio.forEach(function (device) {
            var audio = document.getElementById("audioInput");...
        });
    var i;...
    }).catch(function (error) {
      var deviceInList = false;
            for (i = 0; i < audio.options.length; i++ $("#notifyFlash").text("Failed to get media devices");
    });

2. Переключение микрофона во время трансляции

Image Added

code:

Code Block
languagejs
themeRDark
    $("#switchMicBtn").click(function (){
        stream.switchMic().then(function(id) {
                if (audio.options[i].value == device.id) {$('#audioInput option:selected').prop('selected', false);
            $("#audioInput option[value='"+       deviceInList = trueid +"']").prop('selected', true);
        }).catch(function(e) {
           break;
       console.log("Error " + e);
          });
    }).prop('disabled', !($('#sendAudio').is(':checked')));

3. Регулировка усиления микрофона (работает только в браузере Chrome)

Image Added

code:

Code Block
languagejs
themeRDark
       }$("#micGainControl").slider({
        range: "min",
   if (!deviceInList) {
   min: 0,
        max: 100,
   var  option = document.createElement("option");
   value: currentGainValue,
        step: 10,
     option.text = device.label || device.id; animate: true,
        slide: function (event,      option.value = device.id;ui) {
            currentGainValue =   audio.appendChild(option)ui.value;
            }if(previewStream) {
        });
        ...publishStream.setMicrophoneGain(currentGainValue);
    }).catch(function (error) {
        }
    $("#notifyFlash").text("Failed to get media devices");}
    });

2. Регулировка усиления микрофона (работает только в браузере Chrome)код4. Включение коррекции ошибок (только для кодека Opus)

Image Added

code:

Code Block
languagejs
themeRDark
 $("#micGainControl").slider({
   if (constraints.audio) {
   range: "min",
     constraints.audio = {
   min: 0,
        maxdeviceId: 100,$('#audioInput').val()
        value: currentGainValue,
};
         step: 10,if ($("#fec").is(':checked'))
        animate: true,
   constraints.audio.fec     slide: function (event, ui) {= $("#fec").is(':checked');
            currentGainValue = ui.value;...
    }

5. Установка стерео / моно режима.

Image Added

code:

Code Block
languagejs
themeRDark
    if    if(previewStream(constraints.audio) {
        constraints.audio = {
      publishStream.setMicrophoneGain(currentGainValue);
            }deviceId: $('#audioInput').val()
        };
    });

3. Включение коррекции ошибок (только для кодека Opus)

Image Removed

код:

Code Block
languagejs
themeRDark
    if (constraints.audio) {
...
         constraints.audio = {
if ($("#sendStereoAudio").is(':checked'))
            constraints.audio.stereo = deviceId: $('#audioInput'"#sendStereoAudio").valis(':checked');
        };...
    }

6. Установка битрейта звука в бит/с

Image Added

code:

Code Block
languagejs
themeRDark
    if ($("#fec").is(':checked'))constraints.audio) {
            constraints.audio.fec = $("#fec").is(':checked');{
        if ($("#sendStereoAudio").is(':checked'))    deviceId: $('#audioInput').val()
        };
       constraints .audio.stereo = $("#sendStereoAudio").is(':checked');
        if (parseInt($('#sendAudioBitrate').val()) > 0)
            constraints.audio.bitrate = parseInt($('#sendAudioBitrate').val());
    }

4. Установка стерео / моно режима7. Отключение микрофона.

Image RemovedImage Added

кодcode:

Code Block
languagejs
themeRDark
        if (constraints.audio$("#muteAudioToggle").is(":checked")) {
        constraints.audio = {
  muteAudio();
          deviceId: $('#audioInput').val()}

Настройки камеры

1. Выбор камеры

Image Added

code:

Code Block
languagejs
themeRDark
    Flashphoner.getMediaDevices(null, true, MEDIA_DEVICE_KIND.INPUT).then(function (list) {
        };...
        iflist.video.forEach(function ($("#fec").is(':checked'))
device) {
             constraints.audio.fec = $("#fec").is(':checked');
        if ($("#sendStereoAudio").is(':checked'))});
    }).catch(function (error) {
      constraints.audio.stereo = $("#sendStereoAudio#notifyFlash").is(':checked');
    text("Failed to get media devices");
    if (parseInt($('#sendAudioBitrate').val()) > 0)
            constraints.audio.bitrate = parseInt($('#sendAudioBitrate').val());
    }

...

Image Removed

код:

Code Block
languagejs
themeRDark
    if (constraints.audio});

Если необходимо выбрать только камеру, не запрашивая доступ к аудиоустройствам, необходимо вызвать функцию getMediaDevices() с явным указанием ограничений

Code Block
languagejs
themeRDark
    Flashphoner.getMediaDevices(null, true, MEDIA_DEVICE_KIND.INPUT, {video: true, audio: false}).then(function (list) {
        ...
        list.video.forEach(function (device) {
        constraints.audio   = {...
        });
    deviceId: $('#audioInput').val()
}).catch(function (error) {
         }$("#notifyFlash").text("Failed to get media devices");
    });

2. Переключение камер.

Image Added

code:

Code Block
languagejs
themeRDark
    if ($$("#switchBtn").text("#fecSwitch").isoff(':checkedclick').click(function () {
            constraints.audio.fec = $("#fec").is(':checked');stream.switchCam().then(function(id) {
        if    ($("#sendStereoAudio"'#videoInput option:selected').isprop(':checkedselected', false));
            constraints.audio.stereo = $("#sendStereoAudio$("#videoInput option[value='"+ id +"']").isprop(':checkedselected', true);
        if (parseInt($('#sendAudioBitrate').val()) > 0)}).catch(function(e) {
            constraints.audio.bitrate = parseInt($('#sendAudioBitrate').val()console.log("Error " + e);
    }

6. Отключение микрофона.

Image Removed

код:

Code Block
languagejs
themeRDark
    });
    if ($("#muteAudioToggle").is(":checked")) {
            muteAudio();
        }

Настройки камеры

1. Выбор камеры

Image Removed

код:

Code Block
});

Переключение камеры может осуществляться "на лету", во время трансляции потока. Переключение работает в следующем порядке:

  • На ПК камеры переключаются в том порядке, в каком они определены в менеджере устройств операционной системы.
  • На Android при использовании браузера Chrome по умолчанию выбирается фронтальная камера, при использовании браузера Firefox - тыловая камера
  • На iOS в браузере Safari по умолчанию выбирается фронтальная камера, но в выпадающем списке при выборе камеры первой указана тыловая камера.

3. Установка разрешения видео

Image Added

code:

Code Block
languagejs
themeRDark
Flashphoner.getMediaDevices(null, true).then(function (list)        constraints.video = {
     ...
        list.video.forEach(function (device) {
deviceId: $('#videoInput').val(),
             console.log(device);width: parseInt($('#sendWidth').val()),
            var video = document.getElementById("videoInput");height: parseInt($('#sendHeight').val())
            var i};
        if (Browser.isSafariWebRTC() &&  var deviceInList = false;Browser.isiOS() && Flashphoner.getMediaProviders()[0] === "WebRTC") {
            for (iconstraints.video.deviceId = 0; i < video.options.length; i++) {
{exact: $('#videoInput').val()};
        }

4. Установка FPS

Image Added

code:

Code Block
languagejs
themeRDark
   if (constraints.video) {
     if   (video.options[i].value == device.id) {
        if (parseInt($('#fps').val())           deviceInList = true;> 0)
             constraints.video.frameRate       break= parseInt($('#fps').val());
       }

5. Установка битрейта видео в кбит/с

Image Added

code:

Code Block
languagejs
themeRDark
    if (constraints.video) {
   }
     ...
       }
 if (parseInt($('#sendVideoMinBitrate').val()) > 0)
        if (!deviceInList) {
  constraints.video.minBitrate = parseInt($('#sendVideoMinBitrate').val());
            var option = document.createElement("option");
  if (parseInt($('#sendVideoMaxBitrate').val()) > 0)
              option.textconstraints.video.maxBitrate = device.label || device.idparseInt($('#sendVideoMaxBitrate').val());
                option.value = device.id;...
        }

6. Установка CPU Overuse Detection

Image Added

code:

Code Block
languagejs
themeRDark
        if (option.text.toLowerCase(!$("#cpuOveruseDetection").indexOf("back") >= 0 && video.children.length > 0is(':checked')) {
        mediaConnectionConstraints = {
          video.insertBefore(option, video.children[0]);  "mandatory": {
                } else {googCpuOveruseDetection: false
            }
        video.appendChild(option);}
    }

7. Отключение камеры

Image Added

code:

Code Block
languagejs
themeRDark
        if    }($("#muteVideoToggle").is(":checked")) {
            }
        }muteVideo();
    }).catch(function (error) {
        $("#notifyFlash").text("Failed to get media devices");
    });

2. Переключение камер.

Image Removed

...

   }

Тестирование захвата с камеры и микрофона локально

Локальное тестирование захвата с микрофона и камеры предназначено для того, чтобы проверить работоспособность микрофона и камеры в браузере, не отправляя поток на сервер.

Image Added

code:

Code Block
languagejs
themeRDark
function $startTest("#switchBtn").text("Switch) {
    Flashphoner.getMediaAccess(getConstraints(), localVideo).then(function (disp) {
        $("#testBtn").text("Release").off('click').click(function () {
           publishStream.switchCam $(this).prop('disabled', true);
            stopTest();
        }).prop('disabled', $('#sendCanvasStream').is(':checked'));

Переключение камеры может осуществляться "на лету", во время трансляции потока. Переключение работает в следующем порядке:

  • На ПК камеры переключаются в том порядке, в каком они определены в менеджере устройств операционной системы.
  • На Android при использовании браузера Chrome по умолчанию выбирается фронтальная камера, при использовании браузера Firefox - тыловая камера
  • На iOS в браузере Safari по умолчанию выбирается фронтальная камера, но в выпадающем списке при выборе камеры первой указана тыловая камера.

3. Установка разрешения видео

Image Removed

код:

Code Block
languagejs
themeRDark
function resizeLocalVideo(event) {
    var requested = constraints.video;
    if (requested.width != event.target.videoWidth || requested.height != event.target.videoHeightfalse);

        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) {
        console.warn("Camera does not support requested resolution, actual resolution is " + event.target.videoWidth +var "x"stream += event.target.videoHeight)localVideo.children[i].srcObject;
    }
     $("#publishResolution").text(event.target.videoWidth + "x" + event.target.videoHeight);
       resizeVideo(event.target);
}

4. Установка FPS

Image Removed

код:

Code Block
languagejs
themeRDark
   if (constraints.video) { audioContextForTest = new AudioContext();
        if (constraints.customStream) {
           var constraints.customStreammicrophone = canvasaudioContextForTest.captureStreamcreateMediaStreamSource(30stream);
            constraints.video = false;
      var javascriptNode } else {
= audioContextForTest.createScriptProcessor(1024, 1, 1);
               constraints.video = {
     microphone.connect(javascriptNode);
                  deviceId: {exact: $('#videoInput').val()},
 javascriptNode.connect(audioContextForTest.destination);
                   width: parseInt($('#sendWidth').val()),
 javascriptNode.onaudioprocess = function (event) {
                   height: parseInt($('#sendHeight').val())     var inpt_L = event.inputBuffer.getChannelData(0);
            };
            if (Browser.isSafariWebRTC() && Browser.isiOS() && Flashphoner.getMediaProviders()[0] === "WebRTC") {
var sum_L = 0.0;
                      constraints.video.width = {min:for parseInt($('#sendWidth').val()), max: 640};(var i = 0; i < inpt_L.length; ++i) {
                constraints.video.height = {min: parseInt($('#sendHeight').val()), max: 480};
        sum_L += inpt_L[i]  }* inpt_L[i];
            if (parseInt($('#sendVideoMinBitrate').val()) > 0)
            }
         constraints.video.minBitrate = parseInt($('#sendVideoMinBitrate').val());
            if (parseInt($('#sendVideoMaxBitrate').val()) > 0)$("#micLevel").text(Math.floor(Math.sqrt(sum_L / inpt_L.length) * 100));
                constraints.video.maxBitrate = parseInt($('#sendVideoMaxBitrate').val());   }
            if (parseInt($('#fps').val()) > 0)
    }
            }
        } constraints.video.frameRate = parseInt($('#fps').val());else if (Flashphoner.getMediaProviders()[0] == "Flash") {
        }
    micLevelInterval  }

...

Image Removed

код:

Code Block
languagejs
themeRDark
if (constraints.video= setInterval(function () {
        if (constraints.customStream) {
            constraints.customStream = canvas.captureStream(30$("#micLevel").text(disp.children[0].getMicrophoneLevel());
            constraints.video = false}, 500);
        } else {
        testStarted    constraints.video = {= true;
                deviceId: {exact: $('#videoInput').val()},
   }).catch(function (error) {
             width: parseInt($('#sendWidth'"#testBtn").val()),prop('disabled', false);
        testStarted = false;
      height: parseInt($('#sendHeight').val())
            };});
}

Замена отдельных параметров SDP

При публикации потока предусмотрена возможность замены параметров SDP. В поле 'SDP replace' указывается шаблон поиска параметра, который нужно заменить, в поле 'with' указывается новое значение параметра.

Image Added

Для замены параметров SDP используется callback-функция, которая должна быть указана при создании потока в параметре sdpHook метода createStream():

создание потока code

Code Block
languagejs
themeRDark
    publishStream = session.createStream({
        name: streamName,
   if (Browser.isSafariWebRTC() && Browser.isiOS() && Flashphoner.getMediaProviders()[0] === "WebRTC") {
 display: localVideo,
        cacheLocalResources: true,
        constraints: constraints.video.width = {min: parseInt($('#sendWidth').val()), max: 640};
,
        mediaConnectionConstraints: mediaConnectionConstraints,
        sdpHook: rewriteSdp,
         constraints..video.height
  = {min: parseInt($('#sendHeight').val()), max: 480}; })

функция rewriteSdp code

Code Block
languagejs
themeRDark
function rewriteSdp(sdp) {
    var sdpStringFind       }= $("#sdpStringFind").val().replace('\\r\\n','\r\n');
    var sdpStringReplace       if (parseInt($('#sendVideoMinBitrate'= $("#sdpStringReplace").val().replace('\\r\\n','\r\n');
 > 0)
  if (sdpStringFind != 0 && sdpStringReplace != 0) {
       constraints.video.minBitrate var newSDP = parseInt($('#sendVideoMinBitrate').valsdp.sdpString.toString());
        newSDP =   if (parseInt($('#sendVideoMaxBitrate').val()) > 0)newSDP.replace(new RegExp(sdpStringFind,"g"), sdpStringReplace);
        return newSDP;
    }
   constraints.video.maxBitrate = parseInt($('#sendVideoMaxBitrate').val());
            if (parseInt($('#fps').val()) > 0)
                constraints.video.frameRate = parseInt($('#fps').val());
        }
    }

6. Установка CPU Overuse Detection

Image Removed

код:

Code Block
languagejs
themeRDark
    if (!$("#cpuOveruseDetection").is(':checked')) {
        mediaConnectionConstraints = {
            "mandatory": {
                googCpuOveruseDetection: false
            }
        }
    }

7. Отключение камеры

Image Removed

код:

Code Block
return sdp.sdpString;
}

Увеличение битрейта публикуемого видео в браузере Chrome

Замена параметров SDP позволяет увеличить битрейт публикуемого видео. Для этого необходимо при публикации H264 заменить параметр 'a'  по шаблону

Code Block
themeRDark
a=fmtp:(.*) (.*)

на

Code Block
themeRDark
a=fmtp:$1 $2;x-google-min-bitrate=2500

Здесь 2500 - битрейт в килобитах в секунду.

Подобным образом можно указать битрейт видео на старте (атрибут x-google-start-bitrate) и ограничить максимальный битрейт (атрибут x-google-max-bitrate). Отметим, что, если указать только минимальный битрейт, то выше 2500 кбит/с битрейт поднять не удается, возможно, по умолчанию в Chrome зафиксирован максимальный битрейт на уровне 2500 кбит/с. Если необходимо использовать более высокие значения, например, для трансляции потока высокого разрешения, должны быть указаны и минимальное, и максимальное значения:

Code Block
themeRDark
a=fmtp:$1 $2;x-google-max-bitrate=7000;x-google-min-bitrate=3000

В этом случае браузер будет держать битрейт при публикации потока  в пределах от 3000 до 7000 кбит/с.

При публикации потока VP8 необходимо заменить

Code Block
themeRDark
a=rtpmap:(.*) VP8/90000\r\n

на

Code Block
themeRDark
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
themeRDark
c=IN (.*)\r\n

на

Code Block
themeRDark
c=IN $1\r\nb=AS:10000\r\n

Установка используемых кодеков

При публикации потока предусмотрена возможность убрать из WebRTC SDP кодеки, которые не должны использоваться при публикации данного потока, например:

Code Block
languagejs
themeRDark
    publishStream = session.createStream({
     if ($("#muteVideoToggle").is(":checked"))   ...
        stripCodecs: "h264,H264,flv,mpv"
    }).on(STREAM_STATUS.PUBLISHING, function (publishStream) {
        ...
    muteVideo(});
        }

Тестирование захвата с камеры и микрофона локально

Локальное тестирование захвата с микрофона и камеры предназначено для того, чтобы проверить работоспособность микрофона и камеры в браузере, не отправляя поток на сервер.

Image Removed

...

publishStream.publish();

Данная возможность полезна, в частности, для обхода багов браузера с каким-либо кодеком. Например, если в браузере не работает H.264, можно отключить его и перейти на VP8 при работе по WebRTC.

Управление выводом звука

При воспроизведении потока можно выбрать (и переключить "на лету") устройство вывода звука в браузерах Chrome и MS Edge.

Image Added

code:

Code Block
languagejs
themeRDark
function startTest() {
    if (Browser.isSafariWebRTC())Flashphoner.getMediaDevices(null, true, MEDIA_DEVICE_KIND.OUTPUT).then(function (list) {
        Flashphonerlist.audio.playFirstVideoforEach(localVideo,function true(device);
 {
            Flashphoner.playFirstVideo(remoteVideo, false);
...
        });
    Flashphoner.getMediaAccess(getConstraints(), localVideo).then}).catch(function (disperror) {
        $("#testBtn"'#audioOutputForm').textremove("Release").off('click').click(function () {
);
            $(this).prop('disabled', true);
            stopTest();
        }).prop('disabled', false);

        window.AudioContext = window.AudioContext || window.webkitAudioContext;
});

Отметим, что в браузерах Firefox и Safari нельзя получить список устройств вывода, поэтому данная функция а них не работает

Отображение WebRTC-статистики

При публикации и воспроизведении потока клиентское приложение может получить WebRTC-статистику в соответствии со стандартом. Эта статистика может быть отображена в браузере, например:

Image Added

Отметим, что в браузере Safari отображается только статистика аудио.

1. Отображение статистики при публикации потока

stream.getStats() code:

Code Block
languagejs
themeRDark
        ifpublishStream.getStats(function (Flashphoner.getMediaProviders()[0] == "WebRTC"stats) {
            if (stats && windowstats.AudioContextoutboundStream) {
            for (i = 0; i < localVideo.children.length; i++if (stats.outboundStream.video) {
                if (localVideo.children[i] && localVideo.children[i].id.indexOf("-LOCAL_CACHED_VIDEO") != -1) {    showStat(stats.outboundStream.video, "outVideoStat");
                    varlet streamvBitrate = localVideo.children[i].srcObject(stats.outboundStream.video.bytesSent - videoBytesSent) * 8;
                    audioContextForTest = new AudioContext();
   if ($('#outVideoStatBitrate').length == 0) {
                 var microphone = audioContextForTest.createMediaStreamSource(stream);
    let html = "<div>Bitrate: " + "<span id='outVideoStatBitrate' style='font-weight: normal'>" + vBitrate + "</span>" + "</div>";
 var javascriptNode = audioContextForTest.createScriptProcessor(1024, 1, 1);
                    microphone.connect(javascriptNode$("#outVideoStat").append(html);
                    javascriptNode.connect(audioContextForTest.destination);
} else {
                     javascriptNode.onaudioprocess = function (event) {
$('#outVideoStatBitrate').text(vBitrate);
                    }
                   var inpt_LvideoBytesSent = eventstats.outboundStream.inputBuffer.getChannelData(0)video.bytesSent;
                    ...
    var sum_L = 0.0;
          }

              for (var i = 0; i < inpt_L.length; ++iif (stats.outboundStream.audio) {
                            sum_L += inpt_L[i] * inpt_L[i]showStat(stats.outboundStream.audio, "outAudioStat");
                    let aBitrate =  }
   (stats.outboundStream.audio.bytesSent - audioBytesSent) * 8;
                    if ($("#micLevel").text(Math.floor(Math.sqrt(sum_L / inpt_L.length) * 100));
'#outAudioStatBitrate').length == 0) {
                       }
 let html = "<div>Bitrate: " + "<span id='outAudioStatBitrate' style='font-weight: normal'>" + aBitrate + "</span>"  }+ "</div>";
            }
        } else  if $(Flashphoner.getMediaProviders()[0] == "Flash") {
"#outAudioStat").append(html);
                  micLevelInterval = setInterval(function ()} else {
                        $("#micLevel"'#outAudioStatBitrate').text(disp.children[0].getMicrophoneLevel())aBitrate);
            }, 500);
        }
        testStarted = true;
    }).catch(function (error) {
    audioBytesSent    $("#testBtn").prop('disabled', false);
= stats.outboundStream.audio.bytesSent;
          testStarted = false;
    });

    drawSquare();
}

Установка используемых кодеков

При публикации потока предусмотрена возможность убрать из WebRTC SDP кодеки, которые не должны использоваться при публикации данного потока, например:

Code Block
languagejs
themeRDark
 publishStream = session.createStream({
     }
   name: streamName,
        display: localVideo,...
        cacheLocalResources: true,
});

2. Отображение статистики при воспроизведении потока

stream.getStats() code:

Code Block
languagejs
themeRDark
         constraints: constraints,previewStream.getStats(function (stats) {
        mediaConnectionConstraints: mediaConnectionConstraints,    if (stats && stats.inboundStream) {
        stripCodecs: "h264,H264,flv,mpv"
       }).on(STREAM_STATUS.PUBLISHING, function (publishStream if (stats.inboundStream.video) {
        $("#testBtn").prop('disabled', true);
        var video = document.getElementById(publishStream.id())showStat(stats.inboundStream.video, "inVideoStat");
        //resize local if resolution is available
       let ifvBitrate = (stats.inboundStream.video.videoWidthbytesReceived > 0 && video.videoHeight > 0) {
            resizeLocalVideo({target: video});
    - videoBytesReceived) * 8;
    }
        enableMuteToggles(true);
        if ($("#muteVideoToggle").is(":checked"))'#inVideoStatBitrate').length == 0) {
            muteVideo();
          }
  let html =    if ($("#muteAudioToggle").is(":checked")) {
            muteAudio();
        }
        //remove resize listener in case this video was cached earlier"<div>Bitrate: " + "<span id='inVideoStatBitrate' style='font-weight: normal'>" + vBitrate + "</span>" + "</div>";
                        $("#inVideoStat").append(html);
                    } else {
                        $('#inVideoStatBitrate').text(vBitrate);
                    }
                    videoBytesReceived = stats.inboundStream.video.bytesReceived;
                    ...
                }

                if (stats.inboundStream.audio) {
                    showStat(stats.inboundStream.audio, "inAudioStat");
                    let aBitrate = (stats.inboundStream.audio.bytesReceived - audioBytesReceived) * 8;
                    if ($('#inAudioStatBitrate').length == 0) {
                        let html = "<div style='font-weight: bold'>Bitrate: " + "<span id='inAudioStatBitrate' style='font-weight: normal'>" + aBitrate + "</span>" + "</div>";
                        $("#inAudioStat").append(html);
                    } else {
                        $('#inAudioStatBitrate').text(aBitrate);
                    }
                    audioBytesReceived = stats.inboundStream.audio.bytesReceived;
                }
                ...
            }
        });

Управление параметрами картинки при публикации потока

При публикации видео потока с помощью граничных условий (constraints) можно управлять разрешением картинки и частотой кадров

Управление разрешением картинки

Разрешение картинки можно указать точно

Code Block
languagejs
themeRDark
constraints = {audio:true, video:{width:320,height:240}}

Однако, в некоторых случаях требуется указать диапазон для ширины и высоты

Code Block
languagejs
themeRDark
constraints = {audio:true, video:{width:{min:160,max:320},height:{min:120,max:240}}}

Для некоторых браузеров, например, iOS Safari, необходимо указать точные значения в виде диапазона (в последних версиях это обрабатывается на уровне WebSDK)

Code Block
languagejs
themeRDark
constraints = {audio:true, video:{width:{min:320,max:320},height:{min:240,max:240}}}

Управление частотой кадров

Частота кадров может быть указана точно

Code Block
languagejs
themeRDark
constraints = {audio:true, video:{frameRate:30}

или в виде диапазона

Code Block
languagejs
themeRDark
constraints = {audio:true, video:{frameRate:{min:15,max:30}}

В некоторых случаях, например, если веб-камера поддерживает 24 fps, при точном указании 30 fps публикация может завершиться ошибкой. В таком случае нужно задать частоту кадров как идеальную

Code Block
languagejs
themeRDark
constraints = {audio:true, video:{frameRate:{ideal:30}}

Переключение между потоками с веб-камеры и с экрана во время трансляции

При организации вебинаров возникает необходимость переключаться между потоками, захваченными с веб-камеры ведущего и с экрана. во время трансляции. В идеале, переключение должно быть бесшовным и с сохранением звуковой дорожки с микрофона ведущего. В последних версиях WebSDK реализована такая возможность для браузеров Chrome и Firefox, рассмотрим пример использования в приложении Media Devices.

Image Added

Image Added

1. Во время трансляции потока с выбранных камеры и микрофона, при установке переключателя 'Screen share' в положение 'on' будет вызвана функция switchToScreen

stream.switchToScreen code

Code Block
languagejs
themeRDark
function switchToScreen() {
    if (publishStream) {
        video.removeEventListener$('#switchBtn').prop('resizedisabled', resizeLocalVideotrue);
        video.addEventListener$('#videoInput').prop('resizedisabled', resizeLocalVideotrue);
        setStatus(STREAM_STATUS.PUBLISHING);

        //play preview
        var constraints = publishStream.switchToScreen($('#mediaSource').val()).catch(function () {
            audio: $("#playAudio#screenShareToggle").isremoveAttr(':"checked'"),;
            video: $("#playVideo"'#switchBtn').isprop(':checkeddisabled', false);
        };
        if (constraints.video) {
            constraints.video = {
   $('#videoInput').prop('disabled', false);
        });
     width: (!$("#receiveDefaultSize").is(":checked")) ? parseInt($('#receiveWidth').val()) : 0,
                height: (!$("#receiveDefaultSize").is(":checked")) ? parseInt($('#receiveHeight').val()) : 0,}
}

В данную функцию передается источник потока (экран).

2. Затем пользователь в браузере Chrome при помощи расширения, а в браузере Firefox средствами браузера должен выбрать весь экран или окно программы для трансляции:

Image Added

3. На сервер транслируется поток с экрана

Image Added

При этом источник трансляции звука не меняется.

4. Для возврата к трансляции потока с веб-камеры вызывается функция switch ToCam

stream.switchToCam code

Code Block
languagejs
themeRDark
function switchToCam() {
    if (publishStream) {
        publishStream.switchToCam();
        bitrate: (!$("#receiveDefaultBitrate"'#switchBtn').is(":checked")) ? $("#receiveBitrate").val() : 0,prop('disabled', false);
                quality: (!$("#receiveDefaultQuality").is(":checked")) ? $('#quality').val() : 0
            };
        }
    }).on(STREAM_STATUS.UNPUBLISHED, function () {
        setStatus(STREAM_STATUS.UNPUBLISHED);
        //enable start button
        onStopped();
    }).on(STREAM_STATUS.FAILED, function () {
        setStatus(STREAM_STATUS.FAILED);
        //enable start button
        onStopped();
    });
    publishStream.publish();

...

$('#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

Code Block
languagebash
themeRDark
disable_streaming_proxy=true

или указав фиксированное разрешение для плеера при воспроизведении

Code Block
languagejs
themeRDark
session.createStream({constraints:{audio:true,video:{width:320,height:240}}}).play();