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
    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
languagejs
themeRDark
    $("#switchMicBtn").click(function (){
        publishStreamstream.switchMic();.then(function(id) {
    }        $('#audioInput option:selected').prop('disabledselected', !($('#sendAudio').is(':checked')));

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

Image Removed

code:

Code Block
languagejs
themeRDark
    $("#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)

Image Added

code:

Code Block
languagejs
themeRDark
    $("#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
languagejs
themeRDark
    if (constraints.audio) {
        constraints.audio = {
            deviceId: $('#audioInput').val()
        };
        if ($("#fec").is(':checked'))
            constraints.audio.fec = $("#fec").is(':checked');
        ...
    }

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

code:

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

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

Image RemovedImage Added

code:

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

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

code:

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

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

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

code:

Code Block
languagejs
themeRDark
    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. Переключение камер.

Image Removed

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

Code Block
languagejs
themeRDark
    $("#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. Установка разрешения видео

Image Removed

code:

Code Block
languagejs
themeRDark
      list.video.forEach(function (device) {
            ...
        });
    }).catch(function (error) {
        $("#notifyFlash").text("Failed to get media devices");
    });

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

Image Added

code:

Code Block
languagejs
themeRDark
    $("#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. Установка разрешения видео

Image Added

code:

Code Block
languagejs
themeRDark
        constraints.video = {
          constraints.video.height = {mindeviceId: parseInt($$('#sendHeight#videoInput').val()), max: 480};
            }

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

Image Removed

code:

Code Block
languagejs
themeRDark
   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

Image Added

code:

Code Block
languagejs
themeRDark
    if (constraints.video) {
        if (constraints.customStream) {...
        if (parseInt($('#fps').val())   ...
 > 0)
       } else {
    constraints.video.frameRate = parseInt($('#fps').val());
   }

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

Image Added

code:

Code Block
languagejs
themeRDark
   ...
 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

Image Added

code:

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

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

Image Added

code:

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

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

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

Image Added

code:

Code Block
languagejs
themeRDark
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' указывается новое значение параметра.

Image Added

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

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

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

функция rewriteSdp code

Code Block
languagejs
themeRDark
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
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 = parseInt($('#sendVideoMinBitrate').val());
            if (parseInt($('#sendVideoMaxBitrate').val()) > 0)
                constraints.video.maxBitrate = parseInt($('#sendVideoMaxBitrate').val());
            ...
        }
    }

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

Image Removed

code:

Code Block
languagejs
themeRDark
    if (!$("#cpuOveruseDetection").is(':checked')) {
        mediaConnectionConstraints = {
            "mandatory": {
                googCpuOveruseDetection: false
            }$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({
        }...
    }

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

Image Removed

code:

Code Block
languagejs
themeRDark
    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.

Image Added

code:

function startTest() { if (Browser.isSafariWebRTC()) { Flashphoner.playFirstVideo(localVideo, true); Flashphoner.playFirstVideo(remoteVideo, false); } Flashphoner.getMediaAccess(getConstraints(), localVideo
Code Block
languagejs
themeRDark
RDark
    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-статистику в соответствии со стандартом. Эта статистика может быть отображена в браузере, например:

Image Added

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

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

stream.getStats() code:

Code Block
languagejs
themeRDark
        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' указывается новое значение параметра.

Image Removed

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

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

Code Block
languagejs
themeRDark
      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
languagejs
themeRDark
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
languagebash
themeRDark
a=fmtp:(.*) (.*)

на

Code Block
languagebash
themeRDark
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
languagejs
themeRDark
    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.

Image Removed

code:

Code Block
languagejs
themeRDark
    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-статистику в соответствии со стандартом. Эта статистика может быть отображена в браузере, например:

Image Removed

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

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

stream.getStats() code:

Code Block
languagejs
themeRDark
    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
languagejs
themeRDark
    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
languagejs
themeRDark
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
languagejs
themeRDark
function switchToCam() {
    if (publishStream) {
        publishStream.switchToCam();
        $('#switchBtn').prop('disabled', false);
        $('#videoInput').prop('disabled', false);
    }
}

...

3. Для публикации экрана в браузере Chrome необходимо расширение.

4. Переключение работает только в том случае, если первым опубликован поток с веб-камеры.

Известные проблемы

1. Не работает переключение микрофона в браузере Safari.

...