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. Установка битрейта звука в кбит/с

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");
    });

...

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

Image Added


code:

Code Block
languagejs
themeRDark
   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. Установка FPSImage Removedбитрейта видео в кбит/с

Image Added


code:

Code Block
languagejs
themeRDark
    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. Установка битрейта видео в кбит/сImage RemovedCPU Overuse Detection

Image Added


code:

Code Block
languagejs
themeRDark
    if (constraints.video(!$("#cpuOveruseDetection").is(':checked')) {
        if (constraints.customStream)mediaConnectionConstraints = {
            ..."mandatory": {
        }  else {
     googCpuOveruseDetection: false
      ...
      }
      if (parseInt($('#sendVideoMinBitrate').val()) > 0) }
    }

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

Image Added


code:

Code Block
languagejs
themeRDark
            constraints.video.minBitrate = parseIntif ($('#sendVideoMinBitrate'"#muteVideoToggle").valis(":checked")); {
            if (parseInt($('#sendVideoMaxBitrate').val()) > 0)muteVideo();
        }

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

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

Image Added


code:

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

Image Removed

code:

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

Image Removed

code:

Code Block
languagejs
themeRDark
i < localVideo.children.length; i++) {
                if ($("#muteVideoToggle").is(":checked")localVideo.children[i] && localVideo.children[i].id.indexOf("-LOCAL_CACHED_VIDEO") != -1) {
            muteVideo();
         }

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

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

Image Removed

code:

Code Block
languagejs
themeRDark
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
languagejs
themeRDark
    publishStream    previewStream = session.= session.createStream({
            name: streamName,
            display: remoteVideolocalVideo,
        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(sdpStringFind,new RegExp(sdpStringFind,"g"), sdpStringReplace);
        return newSDP;
    }
    return sdp.sdpString;
}

...

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

code:

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

...