Versions Compared

Key

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

Table of Contents

Пример стримера c доступом к медиа-устройствам

Данный стример может использоваться для публикации следующих типов потоков с и проигрывания WebRTC потоков на Web Call Server

  • WebRTC
  • RTMFP
  • RTMP

и позволяет выбрать медиа-устройства и параметры для публикуемого видео

...

На скриншоте ниже представлен пример во время публикации потока.

Image RemovedImage Added


На странице вопроизводятся отображаются два видео элемента:

  • 'Local' - видео с камеры
  • 'PreviewPlayer' - видео, которое приходит с сервера

Код примера

Код данного примера находится на WCS-сервере по следующему пути:

...

Тестировать данный пример можно по следующему адресу:

https://host:8888/client2/examples/demo/streaming/media_devices_manager/media_device_manager.html

Здесь host - адрес WCS-сервера.

Работа с кодом примера

Для разбора кода возьмем версию файла manager.js с хэшем ecbadc3, которая находится здесь и доступна для скачивания в соответствующей сборке 2.0.5.28.2747212.

1. Инициализация API.

Flashphoner.init() код code

Code Block
languagejs
themeRDark
        Flashphoner.init({
flashMediaProviderSwfLocation: '../../../../media-provider.swf',
mediaProvidersReadyCallback: function (mediaProviders) {
//hide remote video if current media provider is Flash
if (mediaProviders[0] == "Flash") {
$("#fecForm").hide();
$("#stereoForm").hide();
$("#sendAudioBitrateForm            screenSharingExtensionId: extensionId,
            mediaProvidersReadyCallback: function (mediaProviders) {
                if (Flashphoner.isUsingTemasys()) {
                    $("#audioInputForm").hide();
                    $("#cpuOveruseDetectionForm#videoInputForm").hide();
}
if (Flashphoner.isUsingTemasys()) {
$("#audioInputForm").hide();
$("#videoInputForm").hide();
}
}
}                }
            }
        })

2. Получение списка доступных медиа-устройств . кодввода

Flashphoner.getMediaDevices() код code

При получении списка медиа-устройств заполняются выпадающие списки микрофонов и камер на странице клиента.

Code Block
languagejs
themeRDark
    Flashphoner.getMediaDevices(null, true).then(function (list) {
        list.audio.forEach(function (device) {
var audio = document.getElementById("audioInput");
var i;
var deviceInList = false;
for (i = 0; i < audio..options.length; i++) {
if (audio.options[i].value == device.id) {
deviceInList = true;
break;
}
}
if (!deviceInList) {
var option = document.createElement("option");
option.text = device.label || device.id;
option.value = device.id;
audio.appendChild(option);
}
});
list.video.forEach(function (device) {
console.log(device);
var video = document.getElementById("videoInput");
var i;
var deviceInList = false;
for (i = 0; i < video.options.length; i++) {
if (video.options[i].value == device.id) {
deviceInList = true;
break;
}
}
if (!deviceInList) {
var option = document.createElement("option");
option.text = device.label || device.id;
option.value = device.id;
if (option.text.toLowerCase().indexOf("back") >= 0 && video.children.length > 0) {
video.insertBefore(option, video.children[0]);
} else {
video.appendChild(option);
}
}
});


$("#url").val(setURL() + "/" + createUUID(8));

//set initial button callback
onStopped();

if (list.audio.length === 0) {
$("#sendAudio").prop('checked', false).prop('disabled', true);
}
if (list.video.length === 0) {
$("#sendVideo").prop('checked', false).prop('disabled', true);
}
}).catch(function (error) {
$("#notifyFlash").text("Failed to get media devices");
});

3. Получение граничных параметров для аудио и видео со страницы клиента

getConstraints() код

Code Block
languagejs
function getConstraints() {
constraints = {
audio: $("#sendAudio").is(':checked'),
video: $("#sendVideo").is(':checked'),
customStream: $("#sendCanvasStream").is(':checked')
};

if (constraints.audio) {
constraints.audio = {

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

3. Получение списка доступных медиа-устройств вывода звука

Flashphoner.getMediaDevices() code

При получении списка медиа-устройств заполняются выпадающий список устройств вывода звука на странице клиента.

Code Block
languagejs
themeRDark
    Flashphoner.getMediaDevices(null, true, MEDIA_DEVICE_KIND.OUTPUT).then(function (list) {
        list.audio.forEach(function (device) {
            ...
        });
        ...
    }).catch(function (error) {
        $("#notifyFlash").text("Failed to get media devices");
    });

4. Получение граничных параметров для публикации аудио и видео со страницы клиента

getConstraints() code

Источники публикации:

  • камера (sendVideo)
  • микрофон (sendAudio)
Code Block
languagejs
themeRDark
    constraints = {
        audio: $("#sendAudio").is(':checked'),
        video: $("#sendVideo").is(':checked'),
    };

Параметры аудио:

  • выбор микрофона (deviceId)
  • коррекция ошибок для кодека Opus (fec)
  • режим стерео (stereo)
  • битрейт аудио (bitrate)
Code Block
languagejs
themeRDark
    if (constraints.audio) {
        constraints.audio = {
            deviceId: $('#audioInput').val()
        };
        if ($("#fec").is(':checked'))
constraints            constraints.audio.fec = $("#fec").is(':checked');
        if ($("#sendStereoAudio").is(':checked'))
            constraints.audio.stereo = $("#sendStereoAudio").is(':checked');
        if (parseInt($('#sendAudioBitrate').val()) > 0)
            constraints.audio.bitrate = parseInt($('#sendAudioBitrate').val());
}

if (constraints.video) {
if (constraints.customStream) {
constraints.customStream = canvas.captureStream(30);
constraints.video = false;
} else {
    }

Параметры видео:

  • выбор камеры (deviceId)
  • размеры при публикации (width, height)
  • минимальный и максимальный битрейт видео (minBitrate, maxBitrate)
  • FPS (frameRate)
Code Block
languagejs
themeRDark
            constraints.video = {
                deviceId: {exact: $('#videoInput').val()},
width: parseInt                width: parseInt($('#sendWidth').val()),
                height: parseInt($('#sendHeight').val())
            };
            if (Browser.isSafariWebRTC() && Browser.isiOS() && Flashphoner.getMediaProviders()[0] === "WebRTC") {
                constraints.video.widthdeviceId = {minexact: parseInt($('#sendWidth#videoInput').val()), max: 640};
constraints.video.height = {min: parseInt($('#sendHeight').val()), max: 480};
}
};
            }
            if (parseInt($('#sendVideoMinBitrate').val()) > 0)
                constraints.video.minBitrate = parseInt($('#sendVideoMinBitrate').val());
            if (parseInt($('#sendVideoMaxBitrate').val()) > 0)
                constraints.video.maxBitrate = parseInt($('#sendVideoMaxBitrate').val());
            if (parseInt($('#fps').val()) > 0)
                constraints.video.frameRate = parseInt($('#fps').val());
}
}

return constraints;
}

...


5. Получение доступа к медиаустройствам для локального тестирования

Flashphoner.getMediaAccess() код code

В метод передаются граничные параметры для аудио и видео (constrains), а также localVideo - div-элемент, в котором будет отображаться видео с выбранной камеры.

Code Block
languagejs
themeRDark
    Flashphoner.getMediaAccess(getConstraints(), localVideo).then(function (disp) {
        $("#testBtn").text("Release").off('click').click(function () {
$(this)            $(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;
});

5. Подключение к серверу.

Flashphoner.createSession() код

Code Block
languagejs
Flashphoner.createSession({urlServer: url        ...
        testStarted = true;
    }).catch(function (error) {
        $("#testBtn").prop('disabled', false);
        testStarted = false;
    });

6. Подключение к серверу.

Flashphoner.createSession() code

Code Block
languagejs
themeRDark
    Flashphoner.createSession({urlServer: url, timeout: tm}).on(SESSION_STATUS.ESTABLISHED, function (session) {
        ...
    }).on(SESSION_STATUS.DISCONNECTED, function () {
        ...
    }).on(SESSION_STATUS.FAILED, function () {
        ...
    });

7. Получение от сервера события, подтверждающего успешное соединение.

ConnectionStatusEvent ESTABLISHED code

Code Block
languagejs
themeRDark
    Flashphoner.createSession({urlServer: url, timeout: tm}).on(SESSION_STATUS.ESTABLISHED, function (session) {
//session connected, start streaming
startStreaming(session);
}).on(SESSION_STATUS.DISCONNECTED, function () {
setStatus(SESSION_STATUS.DISCONNECTED);
onStopped();
}).on(SESSION_STATUS.FAILED, function () {
setStatus(SESSION_STATUS.FAILED);
onStopped();
});

6. Получение от сервера события, подтверждающего успешное соединение.

...

        setStatus("#connectStatus", session.status());
        onConnected(session);
        ...
    });

8. Публикация видеопотока

session.createStream(), publishStream.publish() code

Code Block
languagejs
Flashphoner.createSession({urlServer: url}).on(SESSION_STATUS.ESTABLISHED, function (session) {
//session connected, start streaming
startStreaming(session);
}).on(SESSION_STATUS.DISCONNECTED, function () {
setStatus(SESSION_STATUS.DISCONNECTED);
onStopped();
}).on(SESSION_STATUS.FAILED, function () {
setStatus(SESSION_STATUS.FAILED);
onStopped();
});

7. Публикация видеопотока

session.createStream(), publishStream.publish() код

publishStream = session.createStream({ name: streamName, display: localVideo, cacheLocalResources: true, constraints: constraints, mediaConnectionConstraints: mediaConnectionConstraints }).on(STREAM_STATUS.PUBLISHING, function (publishStream) { $("#testBtn").prop('disabled', true); var video = document.getElementById(publishStream.id()); //resize local if resolution is available if (video.videoWidth > 0 && video.videoHeight > 0) { resizeLocalVideo({target: video}); } enableMuteToggles(true); if ($("#muteVideoToggle").is(":checked")) { muteVideo(); } if ($("#muteAudioToggle").is(":checked")) { muteAudio(); } //remove resize listener in case this video was cached earlier video.removeEventListener('resize', resizeLocalVideo); video.addEventListener('resize', resizeLocalVideo); setStatus(STREAM_STATUS.PUBLISHING); //play preview var constraints = { audio: $("#playAudio").is(':checked'), video: $("#playVideo").is(':checked') }; if (constraints.video) { constraints.video = { width: (!$("#receiveDefaultSize").is(":checked")) ? parseInt($('#receiveWidth').val()) : 0, height: (!$("#receiveDefaultSize").is(":checked")) ? parseInt($('#receiveHeight').val()) : 0, bitrate: (!$("#receiveDefaultBitrate
Code Block
languagejs
themeRDark
    publishStream = session.createStream({
        name: streamName,
        display: localVideo,
        cacheLocalResources: true,
        constraints: constraints,
        mediaConnectionConstraints: mediaConnectionConstraints,
        sdpHook: rewriteSdp,
        transport: transportInput,
        cvoExtension: cvo,
        stripCodecs: strippedCodecs,
        videoContentHint: contentHint
        ...
    });
    publishStream.publish();

9. Получение от сервера события, подтверждающего успешную публикацию потока

StreamStatusEvent PUBLISHING code

Code Block
languagejs
themeRDark
    publishStream = session.createStream({
        ...
    }).on(STREAM_STATUS.PUBLISHING, function (stream) {
        $("#testBtn").prop('disabled', true);
        var video = document.getElementById(stream.id());
        //resize local if resolution is available
        if (video.videoWidth > 0 && video.videoHeight > 0) {
            resizeLocalVideo({target: video});
        }
        enablePublishToggles(true);
        if ($("#muteVideoToggle").is(":checked")) {
    ? $("#receiveBitrate").val() : 0,
quality: (!$("#receiveDefaultQuality        muteVideo();
        }
        if ($("#muteAudioToggle").is(":checked")) ? $('#quality').val() : 0
};
}
previewStream = session.createStream({
name: streamName,
display: remoteVideo,
constraints: constraints
}).on(STREAM_STATUS.PLAYING, function (previewStream) {
document.getElementById(previewStream.id()).addEventListener{
            muteAudio();
        }
        //remove resize listener in case this video was cached earlier
        video.removeEventListener('resize', function (event) {
$("#playResolution").text(event.target.videoWidth + "x" + event.target.videoHeight);
resizeVideo(event.target);
});
//enable stop button
onStarted(publishStream, previewStream);
//wait for incoming stream
if (Flashphoner.getMediaProviders()[0] == "WebRTC") {
setTimeout(function () {
detectSpeech(previewStream);
}, 3000);
}

drawSquare();
})resizeLocalVideo);
        video.addEventListener('resize', resizeLocalVideo);
        publishStream.setMicrophoneGain(currentGainValue);
        setStatus("#publishStatus", STREAM_STATUS.PUBLISHING);
        onPublishing(stream);
    }).on(STREAM_STATUS.STOPPEDUNPUBLISHED, function () {
publishStream.stop();
        ...
    }).on(STREAM_STATUS.FAILED, function () {
//preview failed, stop publishStream
if (publishStream.status() == STREAM_STATUS.PUBLISHING) {
setStatus(STREAM_STATUS.FAILED);
publishStream.stop();
}
});
previewStream.play();
}).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();

...

        ...
    });
    publishStream.publish();

10. Воспроизведение потока

session.createStream(), previewStream.play() code

Code Block
languagejs
themeRDark
    previewStream = session.createStream({
        name: streamName,
        display: remoteVideo,
        constraints: constraints,
        transport: transportOutput,
        stripCodecs: strippedCodecs
        ...
    });
    previewStream.play();

11. Получение от сервера события, подтверждающего успешную публикацию успешное воспроизведение потока

StreamStatusEvent PUBLISHING код

При получении данного события создается превью-видеопоток при помощи createStream() и вызывается play() для его воспроизведения.

Code Block
languagejs
publishStream = session.createStream({
name: streamName,
display: localVideo,
cacheLocalResources: true,
constraints: constraints,
mediaConnectionConstraints: mediaConnectionConstraints
}).on(STREAM_STATUS.PUBLISHING, function (publishStream) {
$("#testBtn").prop('disabled', true);
var video = document.getElementById(publishStream.id());
//resize local if resolution is available
if (video.videoWidth > 0 && video.videoHeight > 0) {
resizeLocalVideo({target: video});
}
enableMuteToggles(true);
if ($("#muteVideoToggle").is(":checked")) {
muteVideo();
}
if ($("#muteAudioToggle").is(":checked")) {
muteAudio();
}
//remove resize listener in case this video was cached earlier
video.removeEventListener('resize', resizeLocalVideo);
video.addEventListener('resize', resizeLocalVideo);
setStatus(STREAM_STATUS.PUBLISHING);

//play preview
var constraints = {
audio: $("#playAudio").is(':checked'),
video: $("#playVideo").is(':checked')
};
if (constraints.video) {
constraints.video = {
width: (!$("#receiveDefaultSize").is(":checked")) ? parseInt($('#receiveWidth').val()) : 0,
height: (!$("#receiveDefaultSize").is(":checked")) ? parseInt($('#receiveHeight').val()) : 0,
bitrate: (!$("#receiveDefaultBitrate").is(":checked")) ? $("#receiveBitrate").val() : 0,
quality: (!$("#receiveDefaultQuality").is(":checked")) ? $('#quality').val() : 0
};
}
previewStream = session.createStream({
name: streamName,
display: remoteVideo,
constraints: constraints
}).on(STREAM_STATUS.PLAYING, function (previewStream) {
document.getElementById(previewStream.id()).addEventListener('resize', function (event) {
$("#playResolution").text(event.target.videoWidth + "x" + event.target.videoHeight);
resizeVideo(event.target);
});
//enable stop button
onStarted(publishStream, previewStream);
//wait for incoming stream
if (Flashphoner.getMediaProviders()[0] == "WebRTC") {
setTimeout(function () {
detectSpeech(previewStream);
}, 3000);
}

drawSquare();
}).on(STREAM_STATUS.STOPPED, function () {
publishStream.stop();
}).on(STREAM_STATUS.FAILED, function () {
//preview failed, stop publishStream
if (publishStream.status() == STREAM_STATUS.PUBLISHING) {
setStatus(STREAM_STATUS.FAILED);
publishStream.stop();
}
});
previewStream.play();
}).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();

9. Остановка воспроизведения превью-видеопотока.

previewStream.stop() код

Code Block
languagejs
$("#publishBtn").text("Stop").off('click').click(function () {
$(this).prop('disabled', true);
previewStream.stop();
}).prop('disabled', false);

10. Получение от сервера события, подтверждающего остановку воспроизведения

StreamStatusEvent STOPPED код

Code Block
languagejs
previewStream = session.createStream({
name: streamName,
display: remoteVideo,
constraints: constraints
}).on(STREAM_STATUS.PLAYING, function (previewStream) {
document.getElementById(previewStream.id()).addEventListener('resize', function (event) {
$("#playResolution").text(event.target.videoWidth + "x" + event.target.videoHeight);
resizeVideo(event.target);
});
//enable stop button
onStarted(publishStream, previewStream);
//wait for incoming stream
if (Flashphoner.getMediaProviders()[0] == "WebRTC") {
setTimeout(function () {
detectSpeech(previewStream);
}, 3000);
}

drawSquare();
}).on(STREAM_STATUS.STOPPED, function () {
publishStream.stop();
}).on(STREAM_STATUS.FAILED, function () {
//preview failed, stop publishStream
if (publishStream.status() == STREAM_STATUS.PUBLISHING) {
setStatus(STREAM_STATUS.FAILED);
publishStream.stop();
}
});
previewStream.play();

11. Остановка публикации видеопотока после остановки воспроизведения превью-потока.

publishStream.stop() код

Code Block
languagejs
previewStream = session.createStream({
name: streamName,
display: remoteVideo,
constraints: constraints
}).on(STREAM_STATUS.PLAYING, function (previewStream) {
document.getElementById(previewStream.id()).addEventListener('resize', function (event) {
$("#playResolution").text(event.target.videoWidth + "x" + event.target.videoHeight);
resizeVideo(event.target);
});
//enable stop button
onStarted(publishStream, previewStream);
//wait for incoming stream
if (Flashphoner.getMediaProviders()[0] == "WebRTC") {
setTimeout(function () {
detectSpeech(previewStream);
}, 3000);
}

drawSquare();
}).on(STREAM_STATUS.STOPPED, function () {
publishStream.stop();
}).on(STREAM_STATUS.FAILED, function () {
//preview failed, stop publishStream
if (publishStream.status() == STREAM_STATUS.PUBLISHING) {
setStatus(STREAM_STATUS.FAILED);
publishStream.stop();
}
});
previewStream.play();

12. Получение от сервера события, подтверждающего успешную остановку публикации

StreamStatusEvent UNPUBLISHED код

...

languagejs

...

PLAYING code

Code Block
languagejs
themeRDark
    previewStream = session.createStream({
        ...
    }).on(STREAM_STATUS.PLAYING, function (stream) {
        playConnectionQualityStat.connectionQualityUpdateTimestamp = new Date().valueOf();
        setStatus("#playStatus", stream.status());
        onPlaying(stream);
        document.getElementById(stream.id()).addEventListener('resize', function (event) {
            $("#playResolution").text(event.target.videoWidth + "x" + event.target.videoHeight);
            resizeVideo(event.target);
        });
        //wait for incoming stream
        if (Flashphoner.getMediaProviders()[0] == "WebRTC") {
            setTimeout(function () {
                if(Browser.isChrome()) {
                    detectSpeechChrome(stream);
                } else {
                    detectSpeech(stream);
                }
            }, 3000);
        }
        ...
    });
    previewStream.play();

12. Остановка воспроизведения потока.

stream.stop() code

Code Block
languagejs
themeRDark
    $("#playBtn").text("Stop").off('click').click(function () {
        $(this).prop('disabled', true);
        stream.stop();
    }).prop('disabled', false);

13. Получение от сервера события, подтверждающего остановку воспроизведения

StreamStatusEvent STOPPED code

Code Block
languagejs
themeRDark
    previewStream = session.createStream({
        ...
    }).on(STREAM_STATUS.STOPPED, function () {
        setStatus("#playStatus", STREAM_STATUS.STOPPED);
        onStopped();
        ...
    });
    previewStream.play();

14. Остановка публикации видеопотока

stream.stop() code

Code Block
languagejs
themeRDark
    $("#publishBtn").text("Stop").off('click').click(function () {
        $(this).prop('disabled', true);
        stream.stop();
    }).prop('disabled', false);

15. Получение от сервера события, подтверждающего успешную остановку публикации

StreamStatusEvent UNPUBLISHED code

Code Block
languagejs
themeRDark
    publishStream = session.createStream({
        ...
    }).on(STREAM_STATUS.UNPUBLISHED, function () {
        setStatus("#publishStatus", STREAM_STATUS.UNPUBLISHED);
        onUnpublished();
        ...
    });
    publishStream.publish();

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

stream.muteAudio() code:

Code Block
languagejs
themeRDark
function muteAudio() {
    if (publishStream) {
        publishStream.muteAudio();
    }
}

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

stream.muteVideo() code:

Code Block
languagejs
themeRDark
function muteVideo() {
    if (publishStream) {
        publishStream.muteVideo();
    }
}

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

stream.getStats() code:

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

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

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>";
                        $("#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;
                }
                ...
            }
        });

20. Определение речи при помощи интерфейса ScriptProcessor (любой браузер, кроме Chrome)

audioContext.createMediaStreamSource(), audioContext.createScriptProcessor() code


Code Block
languagejs
themeRDark
function detectSpeech(stream, level, latency) {
    var mediaStream = document.getElementById(stream.id()).srcObject;
    var source = audioContext.createMediaStreamSource(mediaStream);
    var processor = audioContext.createScriptProcessor(512);
    processor.onaudioprocess = handleAudio;
    processor.connect(audioContext.destination);
    processor.clipping = false;
    processor.lastClip = 0;
    // threshold
    processor.threshold = level || 0.10;
    processor.latency = latency || 750;

    processor.isSpeech =
        function () {
            if (!this.clipping) return false;
            if ((this.lastClip + this.latency) < window.performance.now()) this.clipping = false;
            return this.clipping;
        };

    source.connect(processor);

    // Check speech every 500 ms
    speechIntervalID = setInterval(function () {
        if (processor.isSpeech()) {
            $("#talking").css('background-color', 'green');
        } else {
            $("#talking").css('background-color', 'red');
        }
    }, 500);
}

Обработка аудиоданных code

Code Block
languagejs
themeRDark
function handleAudio(event) {
    var buf = event.inputBuffer.getChannelData(0);
    var bufLength = buf.length;
    var x;
    for (var i = 0; i < bufLength; i++) {
        x = buf[i];
        if (Math.abs(x) >= this.threshold) {
            this.clipping = true;
            this.lastClip = window.performance.now();
        }
    }
}

21. Определение речи по WebRTC статистике входящего аудио потока в браузере Chrome

stream.getStats() code

Code Block
languagejs
themeRDark
function detectSpeechChrome(stream, level, latency) {
    statSpeechDetector.threshold = level || 0.010;
    statSpeechDetector.latency = latency || 750;
    statSpeechDetector.clipping = false;
    statSpeechDetector.lastClip = 0;
    speechIntervalID = setInterval(function() {
        stream.getStats(function(stat) {
            let audioStats = stat.inboundStream.audio;
            if(!audioStats) {
                return;
            }
            // Using audioLevel WebRTC stats parameter
            if (audioStats.audioLevel >= statSpeechDetector.threshold) {
                statSpeechDetector.clipping = true;
                statSpeechDetector.lastClip = window.performance.now();
            }
            if ((statSpeechDetector.lastClip + statSpeechDetector.latency) < window.performance.now()) {
                statSpeechDetector.clipping = false;
            }
            if (statSpeechDetector.clipping) {
                $("#talking").css('background-color', 'green');
            } else {
                $("#talking").css('background-color', 'red');
            }
        });
    },500);
}