Table of Contents |
---|
Пример стримера c доступом к медиа-устройствам
Данный стример может использоваться для публикации следующих типов потоков с и проигрывания WebRTC потоков на Web Call Server
- WebRTC
- RTMFP
- RTMP
и позволяет выбрать медиа-устройства и параметры для публикуемого видео
...
На скриншоте ниже представлен пример во время публикации потока.
На странице вопроизводятся отображаются два видео элемента:
- '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.
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.init({ flashMediaProviderSwfLocationscreenSharingExtensionId: '../../../../media-provider.swf'extensionId, mediaProvidersReadyCallback: function (mediaProviders) { //hide remote video if current media provider is Flash (Flashphoner.isUsingTemasys()) { if (mediaProviders[0] == "Flash") { $("#fecForm$("#audioInputForm").hide(); $("#stereoForm#videoInputForm").hide(); $("#sendAudioBitrateForm").hide();} } $("#cpuOveruseDetectionForm").hide(); }) |
2. Получение списка доступных медиа-устройств ввода
Flashphoner.getMediaDevices() code
При получении списка медиа-устройств заполняются выпадающие списки микрофонов и камер на странице клиента.
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.getMediaDevices(null, true).then(function (list) { } list.audio.forEach(function (device) { if (Flashphoner.isUsingTemasys()) {... }); $("#audioInputForm").hide();list.video.forEach(function (device) { ... $("#videoInputForm").hide(}); ... }).catch(function }(error) { $("#notifyFlash").text("Failed to get media } devices"); }); |
23. Получение списка доступных медиа-устройств . кодвывода звука
Flashphoner.getMediaDevices() код code
При получении списка медиа-устройств заполняются выпадающие списки выпадающий список устройств вывода звука на странице клиента.
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.getMediaDevices(null, true, MEDIA_DEVICE_KIND.OUTPUT).then(function (list) { list.audio.forEach(function (device) { ... var audio = document.getElementById("audioInput" }); ... var i; }).catch(function (error) { $("#notifyFlash").text("Failed to get media devices"); var deviceInList = false; }); |
4. Получение граничных параметров для публикации аудио и видео со страницы клиента
getConstraints() code
Источники публикации:
- камера (sendVideo)
- микрофон (sendAudio)
Code Block | ||||
---|---|---|---|---|
| ||||
constraints = { for (i = 0; i < audio.options.length; i++) {audio: $("#sendAudio").is(':checked'), if (audio.options[i].value == device.id) {video: $("#sendVideo").is(':checked'), 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 | ||||
---|---|---|---|---|
| ||||
function getConstraints() {
constraints = {
audio: $("#sendAudio").is(':checked'),
video: $("#sendVideo").is(':checked'),
customStream: $("#sendCanvasStream").is(':checked')
};
if (constraints.audio) {
constraints.audio = {
deviceId: $('#audioInput').val()
};
if ($("#fec").is(':checked'))
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 {
constraints.video = {
deviceId: {exact: $('#videoInput').val()},
width: parseInt($('#sendWidth').val()),
height: parseInt($('#sendHeight').val())
};
if (Browser.isSafariWebRTC() && Browser.isiOS() && Flashphoner.getMediaProviders()[0] === "WebRTC") {
constraints.video.width = {min: parseInt($('#sendWidth').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;
} |
4. Получение доступа к медиаустройствам для локального тестирования
Flashphoner.getMediaAccess() код
В метод передаются граничные параметры для аудио и видео (constrains), а также localVideo - div-элемент, в котором будет отображаться видео с выбранной камеры.
Code Block | ||||
---|---|---|---|---|
| ||||
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;
}); |
5. Подключение к серверу.
Flashphoner.createSession() код
Code Block | ||||
---|---|---|---|---|
| ||||
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();
}); |
6. Получение от сервера события, подтверждающего успешное соединение.
ConnectionStatusEvent ESTABLISHED код
Code Block | ||||
---|---|---|---|---|
| ||||
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() код
Code Block | ||||
---|---|---|---|---|
| ||||
publishStream = session.createStream({ name: streamName, display: localVideo, cacheLocalResources: true, constraints: constraints,}; |
Параметры аудио:
- выбор микрофона (deviceId)
- коррекция ошибок для кодека Opus (fec)
- режим стерео (stereo)
- битрейт аудио (bitrate)
Code Block | ||||
---|---|---|---|---|
| ||||
if (constraints.audio) { mediaConnectionConstraints: mediaConnectionConstraints constraints.audio = { }).on(STREAM_STATUS.PUBLISHING, function (publishStream) { deviceId: $("#testBtn").prop('disabled#audioInput', true); var video = document.getElementById(publishStream.id()); .val() //resize local if resolution is available}; if (video.videoWidth > 0 && video.videoHeight > 0) { ($("#fec").is(':checked')) constraints.audio.fec = $("#fec").is(':checked'); resizeLocalVideo({target: video});if ($("#sendStereoAudio").is(':checked')) } constraints.audio.stereo enableMuteToggles(true= $("#sendStereoAudio").is(':checked'); if (parseInt($("#muteVideoToggle"'#sendAudioBitrate').isval(":checked")) > {0) constraints.audio.bitrate muteVideo(= parseInt($('#sendAudioBitrate').val()); } |
Параметры видео:
- выбор камеры (deviceId)
- размеры при публикации (width, height)
- минимальный и максимальный битрейт видео (minBitrate, maxBitrate)
- FPS (frameRate)
Code Block | ||||
---|---|---|---|---|
| ||||
} if ($("#muteAudioToggle").is(":checked"))constraints.video = { muteAudio(); deviceId: } {exact: $('#videoInput').val()}, //remove resize listener in case this video was cached earlier video.removeEventListenerwidth: parseInt($('resize#sendWidth', resizeLocalVideo); video.addEventListener('resize', resizeLocalVideo);.val()), setStatus(STREAM_STATUS.PUBLISHING); //play previewheight: parseInt($('#sendHeight').val()) var constraints = {}; audio:if $("#playAudio").is(':checked'), video: $("#playVideo").is(':checked') Browser.isSafariWebRTC() && Browser.isiOS() && Flashphoner.getMediaProviders()[0] === "WebRTC") { }; if (constraints.video).deviceId = {exact: $('#videoInput').val()}; constraints.video = {} width: (!$("#receiveDefaultSize").is(":checked")) ? if (parseInt($('#receiveWidth#sendVideoMinBitrate').val()) :> 0,) height: (!$("#receiveDefaultSize").is(":checked")) ?constraints.video.minBitrate = parseInt($('#receiveHeight#sendVideoMinBitrate').val()) : 0,; bitrate: (!$("#receiveDefaultBitrate").is(":checked")) ? $("#receiveBitrate"if (parseInt($('#sendVideoMaxBitrate').val()) :> 0,) quality: (!$("#receiveDefaultQuality").is(":checked")) ? constraints.video.maxBitrate = parseInt($('#quality#sendVideoMaxBitrate').val() : 0 }; )); } if (parseInt($('#fps').val()) > 0) previewStream = session.createStream({ name: streamName, display: remoteVideo, constraints: constraints }).on(STREAM_STATUS.PLAYING, function (previewStreamconstraints.video.frameRate = parseInt($('#fps').val()); |
5. Получение доступа к медиаустройствам для локального тестирования
Flashphoner.getMediaAccess() code
В метод передаются граничные параметры для аудио и видео (constrains), а также localVideo - div-элемент, в котором будет отображаться видео с выбранной камеры.
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.getMediaAccess(getConstraints(), localVideo).then(function (disp) { document.getElementById(previewStream.id()).addEventListener('resize', function (event$("#testBtn").text("Release").off('click').click(function () { $("#playResolution"this).text(event.target.videoWidth + "x" + event.target.videoHeight); prop('disabled', true); stopTest(); resizeVideo(event.target}).prop('disabled', false); });... testStarted = true; //enable stop button }).catch(function (error) { onStarted(publishStream, previewStream$("#testBtn").prop('disabled', false); testStarted //wait for incoming stream= false; }); |
6. Подключение к серверу.
Flashphoner.createSession() code
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.createSession({urlServer: url, if (Flashphoner.getMediaProviders()[0] == "WebRTC"timeout: tm}).on(SESSION_STATUS.ESTABLISHED, function (session) { ... setTimeout(}).on(SESSION_STATUS.DISCONNECTED, function () { ... detectSpeech(previewStream);}).on(SESSION_STATUS.FAILED, function () { ... }, 3000); ); |
7. Получение от сервера события, подтверждающего успешное соединение.
ConnectionStatusEvent ESTABLISHED code
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.createSession({urlServer: url, timeout: } tm}).on(SESSION_STATUS.ESTABLISHED, function (session) { drawSquare(setStatus("#connectStatus", session.status()); }).on(STREAM_STATUS.STOPPED, function () {onConnected(session); ... }); |
8. Публикация видеопотока
session.createStream(), publishStream.
...
publish()
...
Code Block | ||||
---|---|---|---|---|
| ||||
publishStream = }).on(STREAM_STATUS.FAILED, function () session.createStream({ //preview failed, stop publishStream name: streamName, display: localVideo, if (publishStream.status() == STREAM_STATUS.PUBLISHING) { cacheLocalResources: true, constraints: constraints, setStatus(STREAM_STATUS.FAILED); mediaConnectionConstraints: mediaConnectionConstraints, sdpHook: rewriteSdp, publishStream.stop(); transport: transportInput, cvoExtension: }cvo, }); stripCodecs: strippedCodecs, previewStream.play(); }).on(STREAM_STATUS.UNPUBLISHED, function () {videoContentHint: contentHint ... }); setStatus(STREAM_STATUS.UNPUBLISHED); publishStream.publish(); |
9. Получение от сервера события, подтверждающего успешную публикацию потока
StreamStatusEvent PUBLISHING code
Code Block | ||||
---|---|---|---|---|
| ||||
publishStream //enable start button= session.createStream({ onStopped();... }).on(STREAM_STATUS.FAILEDPUBLISHING, function (stream) { $("#testBtn").prop('disabled', true); var video = setStatus(STREAM_STATUS.FAILEDdocument.getElementById(stream.id()); //enable start button resize local if resolution is available if onStopped(); }); publishStream.publish(); |
8. Получение от сервера события, подтверждающего успешную публикацию потока
StreamStatusEvent PUBLISHING код
При получении данного события создается превью-видеопоток при помощи createStream() и вызывается play() для его воспроизведения.
Code Block | ||||
---|---|---|---|---|
| ||||
publishStream = session.createStream({ (video.videoWidth > 0 && video.videoHeight > 0) { nameresizeLocalVideo({target: streamName,video}); display: localVideo,} cacheLocalResources: true,enablePublishToggles(true); constraints: constraints, if ($("#muteVideoToggle").is(":checked")) { mediaConnectionConstraints: mediaConnectionConstraints muteVideo(); }).on(STREAM_STATUS.PUBLISHING, function (publishStream) {} if ($("#testBtn#muteAudioToggle").prop('disabled', true); is(":checked")) { var video = document.getElementById(publishStream.id());muteAudio(); } //remove resize local if resolution is available listener in case this video was cached earlier if (video.videoWidth > 0 && video.videoHeight > 0) { removeEventListener('resize', resizeLocalVideo); video.addEventListener('resize', resizeLocalVideo); resizeLocalVideo({target: video}publishStream.setMicrophoneGain(currentGainValue); } setStatus("#publishStatus", STREAM_STATUS.PUBLISHING); enableMuteTogglesonPublishing(truestream); if ($("#muteVideoToggle").is(":checked")}).on(STREAM_STATUS.UNPUBLISHED, function () { ... }).on(STREAM_STATUS.FAILED, function muteVideo(); { }... }); if ($("#muteAudioToggle").is(":checked")) { publishStream.publish(); |
10. Воспроизведение потока
session.createStream(), previewStream.play() code
Code Block | ||||
---|---|---|---|---|
| ||||
previewStream muteAudio(); = session.createStream({ name: }streamName, //remove resize listener in case this video was cached earlierdisplay: remoteVideo, constraints: constraints, video.removeEventListener('resize', resizeLocalVideo); transport: transportOutput, video.addEventListener('resize', resizeLocalVideo);stripCodecs: strippedCodecs setStatus(STREAM_STATUS.PUBLISHING); ... //play preview }); previewStream.play(); |
11. Получение от сервера события, подтверждающего успешное воспроизведение потока
StreamStatusEvent PLAYING code
Code Block | ||||
---|---|---|---|---|
| ||||
var constraintspreviewStream = session.createStream({ ... audio: $("#playAudio"}).is(':checked'), on(STREAM_STATUS.PLAYING, function (stream) { playConnectionQualityStat.connectionQualityUpdateTimestamp = video:new $Date("#playVideo").isvalueOf(':checked'); }setStatus("#playStatus", stream.status()); if (constraints.video) {onPlaying(stream); document.getElementById(stream.id()).addEventListener('resize', constraints.video =function (event) { width: (!$("#receiveDefaultSize#playResolution").is(":checked")) ? parseInt($('#receiveWidth').val()) : 0, text(event.target.videoWidth + "x" + event.target.videoHeight); resizeVideo(event.target); height: (!$("#receiveDefaultSize").is(":checked")) ? parseInt($('#receiveHeight').val()) : 0, }); //wait for incoming stream bitrate:if (!$("#receiveDefaultBitrate").is(":checked")) ? $("#receiveBitrate").val() : 0,Flashphoner.getMediaProviders()[0] == "WebRTC") { setTimeout(function () { quality: (!$("#receiveDefaultQuality").is(":checked")) ? $('#quality').val() : 0 if(Browser.isChrome()) { }detectSpeechChrome(stream); } previewStream} =else session.createStream({ name: streamName, detectSpeech(stream); display: remoteVideo,} constraints: constraints}, 3000); }).on(STREAM_STATUS.PLAYING, function (previewStream) { ... }); previewStream.play(); |
12. Остановка воспроизведения потока.
stream.stop() code
Code Block | ||||
---|---|---|---|---|
| ||||
document.getElementById(previewStream.id()).addEventListener('resize', function (event) { $("#playBtn").text("Stop").off('click').click(function () { $("#playResolution"this).text(event.target.videoWidth + "x" + event.target.videoHeight); prop('disabled', true); stream.stop(); }).prop('disabled', false); |
13. Получение от сервера события, подтверждающего остановку воспроизведения
StreamStatusEvent STOPPED code
Code Block | ||||
---|---|---|---|---|
| ||||
previewStream = resizeVideo(event.target);session.createStream({ ... }); .on(STREAM_STATUS.STOPPED, function () { //enable stop buttonsetStatus("#playStatus", STREAM_STATUS.STOPPED); onStarted(publishStream, previewStreamonStopped(); ... //wait for incoming stream}); previewStream.play(); |
14. Остановка публикации видеопотока
stream.stop() code
Code Block | ||||
---|---|---|---|---|
| ||||
if (Flashphoner.getMediaProviders()[0] == "WebRTC"$("#publishBtn").text("Stop").off('click').click(function () { $(this).prop('disabled', true); setTimeout(function stream.stop() {; }).prop('disabled', false); |
15. Получение от сервера события, подтверждающего успешную остановку публикации
StreamStatusEvent UNPUBLISHED code
Code Block | ||||
---|---|---|---|---|
| ||||
publishStream detectSpeech(previewStream);= session.createStream({ ... }, 3000); } }).on(STREAM_STATUS.UNPUBLISHED, function () { drawSquare(setStatus("#publishStatus", STREAM_STATUS.UNPUBLISHED); }).on(STREAM_STATUS.STOPPED, function onUnpublished() {; ... publishStream.stop(}); publishStream.publish(); |
16. Отключение микрофона
stream.muteAudio() code:
Code Block | ||||
---|---|---|---|---|
| ||||
function muteAudio() { }).on(STREAM_STATUS.FAILED, function if (publishStream) { publishStream.muteAudio(); //preview failed, stop publishStream } } |
17. Отключение камеры
stream.muteVideo() code:
Code Block | ||||
---|---|---|---|---|
| ||||
function muteVideo() { if (publishStream.status() == STREAM_STATUS.PUBLISHING) { setStatus(STREAM_STATUS.FAILEDpublishStream.muteVideo(); } } |
18. Отображение статистики при публикации потока
stream.getStats() code:
Code Block | ||||
---|---|---|---|---|
| ||||
publishStream.stopgetStats(function (stats); { if } (stats && stats.outboundStream) { }); previewStream.play(); if }).on(STREAM_STATUS.UNPUBLISHED, function ((stats.outboundStream.video) { setStatus(STREAM_STATUS.UNPUBLISHED); //enable start button showStat(stats.outboundStream.video, "outVideoStat"); onStopped(); }).on(STREAM_STATUS.FAILED, function () { let vBitrate = setStatus(STREAM_STATUS.FAILED); //enable start button (stats.outboundStream.video.bytesSent - videoBytesSent) * 8; onStopped(); }); publishStream.publish(); |
9. Остановка воспроизведения превью-видеопотока.
previewStream.stop() код
Code Block | ||||
---|---|---|---|---|
| ||||
$("#publishBtn").text("Stop").off('click').click(function () { if ($('#outVideoStatBitrate').length == 0) { $(this).prop('disabled', true); let html = previewStream.stop(); }).prop('disabled', false); |
10. Получение от сервера события, подтверждающего остановку воспроизведения
StreamStatusEvent STOPPED код
Code Block | ||||
---|---|---|---|---|
| ||||
previewStream = session.createStream({ "<div>Bitrate: " + "<span id='outVideoStatBitrate' style='font-weight: normal'>" + vBitrate + "</span>" + "</div>"; name: streamName, $("#outVideoStat").append(html); display: remoteVideo, } constraints:else constraints{ }).on(STREAM_STATUS.PLAYING, function (previewStream) { document.getElementById(previewStream.id()).addEventListener$('resize', function (event) {#outVideoStatBitrate').text(vBitrate); $("#playResolution").text(event.target.videoWidth + "x" + event.target.videoHeight); } videoBytesSent resizeVideo(event.target); = stats.outboundStream.video.bytesSent; }); ... //enable stop button} onStarted(publishStream, previewStream); if (stats.outboundStream.audio) { //wait for incoming stream if (Flashphoner.getMediaProviders()[0] == "WebRTC") { showStat(stats.outboundStream.audio, "outAudioStat"); let aBitrate = setTimeout(function () {(stats.outboundStream.audio.bytesSent - audioBytesSent) * 8; if detectSpeech(previewStream); ($('#outAudioStatBitrate').length == 0) { }, 3000); let html } drawSquare()= "<div>Bitrate: " + "<span id='outAudioStatBitrate' style='font-weight: normal'>" + aBitrate + "</span>" + "</div>"; }).on(STREAM_STATUS.STOPPED, function () { publishStream.stop(); }).on(STREAM_STATUS.FAILED, function () { $("#outAudioStat").append(html); //preview failed, stop publishStream } else { if (publishStream.status() == STREAM_STATUS.PUBLISHING) { setStatus(STREAM_STATUS.FAILED$('#outAudioStatBitrate').text(aBitrate); publishStream.stop(); } } }); audioBytesSent = previewStream.play(); |
11. Остановка публикации видеопотока после остановки воспроизведения превью-потока.
publishStream.stop() код
Code Block | ||||
---|---|---|---|---|
| ||||
previewStream = session.createStream({ stats.outboundStream.audio.bytesSent; name: streamName,} display: remoteVideo,} constraints: constraints... }).on(STREAM_STATUS.PLAYING, function (previewStream) { ; |
19. Отображение статистики при воспроизведении потока
stream.getStats() code:
Code Block | ||||
---|---|---|---|---|
| ||||
previewStream.getStats(function (stats) { if (stats && stats.inboundStream) { if document.getElementById(previewStream.id()).addEventListener('resize', function (eventstats.inboundStream.video) { $("#playResolution").text(event.target.videoWidth + "x" + event.target.videoHeight); showStat(stats.inboundStream.video, "inVideoStat"); let vBitrate = resizeVideo(event.target); stats.inboundStream.video.bytesReceived - videoBytesReceived) * 8; }); if //enable stop button ($('#inVideoStatBitrate').length == 0) { onStarted(publishStream, previewStream); let html //wait for incoming stream if (Flashphoner.getMediaProviders()[0] == "WebRTC") { = "<div>Bitrate: " + "<span id='inVideoStatBitrate' style='font-weight: normal'>" + vBitrate + "</span>" + "</div>"; setTimeout(function () { $("#inVideoStat").append(html); } else detectSpeech(previewStream); { }, 3000 $('#inVideoStatBitrate').text(vBitrate); } drawSquare(); videoBytesReceived = }).on(STREAM_STATUS.STOPPED, function () {stats.inboundStream.video.bytesReceived; publishStream.stop(); }).on(STREAM_STATUS.FAILED, function () { ... } //preview failed, stop publishStream if (publishStream.status() == STREAM_STATUS.PUBLISHINGstats.inboundStream.audio) { setStatus(STREAM_STATUS.FAILEDshowStat(stats.inboundStream.audio, "inAudioStat"); publishStream.stop(); let aBitrate = (stats.inboundStream.audio.bytesReceived - audioBytesReceived) * 8; } }); if previewStream.play(); |
12. Получение от сервера события, подтверждающего успешную остановку публикации
StreamStatusEvent UNPUBLISHED код
Code Block | ||||
---|---|---|---|---|
| ||||
publishStream = session.createStream({ ($('#inAudioStatBitrate').length == 0) { name: streamName, let display: localVideo, cacheLocalResources: true, constraints: constraints,html = "<div style='font-weight: bold'>Bitrate: " + "<span id='inAudioStatBitrate' style='font-weight: normal'>" + aBitrate + "</span>" + "</div>"; mediaConnectionConstraints: mediaConnectionConstraints }).on(STREAM_STATUS.PUBLISHING, function (publishStream) { $("#testBtn#inAudioStat").prop('disabled', trueappend(html); var video = document.getElementById(publishStream.id()); //resize local if resolution} iselse available{ if (video.videoWidth > 0 && video.videoHeight > 0) { resizeLocalVideo({target: video}$('#inAudioStatBitrate').text(aBitrate); } enableMuteToggles(true);} if ($("#muteVideoToggle").is(":checked")) { audioBytesReceived muteVideo()= stats.inboundStream.audio.bytesReceived; } } if ($("#muteAudioToggle").is(":checked")) { muteAudio(); ... } }); |
20. Определение речи при помощи интерфейса ScriptProcessor (любой браузер, кроме Chrome)
audioContext.createMediaStreamSource(), audioContext.createScriptProcessor() code
Code Block | ||||
---|---|---|---|---|
| ||||
function detectSpeech(stream, level, //remove resize listener in case this video was cached earlierlatency) { var mediaStream = document.getElementById(stream.id()).srcObject; var source = videoaudioContext.removeEventListener('resize', resizeLocalVideocreateMediaStreamSource(mediaStream); var processor = videoaudioContext.addEventListener('resize', resizeLocalVideocreateScriptProcessor(512); processor.onaudioprocess = handleAudio; setStatus(STREAM_STATUS.PUBLISHINGprocessor.connect(audioContext.destination); processor.clipping = false; //play previewprocessor.lastClip = 0; // threshold var constraintsprocessor.threshold = { level || 0.10; processor.latency = latency || 750; audio: $("#playAudio").is(':checked'),processor.isSpeech = video: $("#playVideo").is(':checked') function () { }; if (constraints!this.videoclipping) return {false; if constraints((this.videolastClip = { + this.latency) < window.performance.now()) this.clipping = false; width: (!$("#receiveDefaultSize").is(":checked")) ? parseInt($('#receiveWidth').val()) : 0, return this.clipping; }; source.connect(processor); height: (!$("#receiveDefaultSize").is(":checked")) ? parseInt($('#receiveHeight').val()) : 0,// Check speech every 500 ms speechIntervalID = setInterval(function () { bitrate:if (!$("#receiveDefaultBitrate").is(":checked")) ? $("#receiveBitrate").val() : 0, processor.isSpeech()) { quality: (!$("#receiveDefaultQuality#talking").is(":checked")) ? $('#quality').val() : 0 css('background-color', 'green'); } else { }$("#talking").css('background-color', 'red'); } }, 500); } |
Обработка аудиоданных code
Code Block | ||||
---|---|---|---|---|
| ||||
function handleAudio(event) { var previewStreambuf = sessionevent.inputBuffer.createStreamgetChannelData({0); var bufLength = buf.length; name: streamName,var x; for (var i = 0; i < display: remoteVideo,bufLength; i++) { x constraints: constraints = buf[i]; if })(Math.on(STREAM_STATUS.PLAYING, function (previewStreamabs(x) >= this.threshold) { document.getElementById(previewStream.id()).addEventListener('resize', function (event) { this.clipping = true; $("#playResolution").text(event.target.videoWidth + "x" + event.target.videoHeight this.lastClip = window.performance.now(); } } } |
21. Определение речи по WebRTC статистике входящего аудио потока в браузере Chrome
stream.getStats() code
Code Block | ||||
---|---|---|---|---|
| ||||
function detectSpeechChrome(stream, level, latency) { resizeVideo(event.target); statSpeechDetector.threshold = level || 0.010; statSpeechDetector.latency = latency || })750; statSpeechDetector.clipping = false; statSpeechDetector.lastClip = 0; //enable stop button speechIntervalID = setInterval(function() { onStarted(publishStream, previewStream);stream.getStats(function(stat) { //waitlet foraudioStats incoming stream= stat.inboundStream.audio; if (Flashphoner.getMediaProviders()[0] == "WebRTC"(!audioStats) { setTimeout(function () { return; detectSpeech(previewStream);} // Using audioLevel WebRTC }, 3000);stats parameter } if (audioStats.audioLevel >= statSpeechDetector.threshold) { drawSquare(); }).on(STREAM_STATUS.STOPPED, function () { statSpeechDetector.clipping = true; publishStream.stop(); statSpeechDetector.lastClip = }).on(STREAM_STATUS.FAILED, function () { window.performance.now(); //preview failed, stop publishStream} if (publishStream.status() == STREAM_STATUS.PUBLISHING((statSpeechDetector.lastClip + statSpeechDetector.latency) < window.performance.now()) { setStatus(STREAM_STATUS.FAILED)statSpeechDetector.clipping = false; } publishStream.stop(); if (statSpeechDetector.clipping) }{ }); previewStream.play(); }).on(STREAM_STATUS.UNPUBLISHED, function () {$("#talking").css('background-color', 'green'); setStatus(STREAM_STATUS.UNPUBLISHED); } else { //enable start button onStopped(); }$("#talking").on(STREAM_STATUS.FAILED, function () { css('background-color', 'red'); setStatus(STREAM_STATUS.FAILED); //enable start button} onStopped(}); },500); publishStream.publish();} |