...
Тестировать данный пример можно по следующему адресу:
https://host:8888/client2/examples/demo/streaming/media_devices_manager/media_device_manager.html
Здесь host - адрес WCS-сервера.
...
Для разбора кода возьмем версию файла manager.js, которая находится здесь и доступна для скачивания в соответствующей сборке 0.5.28.27472753.126.
1. Инициализация API.
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.init({ screenSharingExtensionId: extensionId, 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").hide(); $("#cpuOveruseDetectionForm").hide(); } if (Flashphoner.isUsingTemasys()) { $("#audioInputForm").hide(); $("#videoInputForm").hide(); } } }) |
2. Получение списка доступных медиа-устройств . кодввода
Flashphoner.getMediaDevices() код code
При получении списка медиа-устройств заполняются выпадающие списки микрофонов и камер на странице клиента.
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.getMediaDevices(null, true).then(function (list) { list.audio.forEach(function (device) { ... }); list.video.forEach(function (device) { ... }); ... }).catch(function (error) { $("#notifyFlash").text("Failed to get media devices"); }); |
3. Получение граничных параметров для публикации аудио и видео со страницы клиента
getConstraints() код
Источники публикации:
- камера (sendVideo)
- микрофон (sendAudio)
- HTML5 Canvas (sendCanvasStream)
списка доступных медиа-устройств вывода звука
Flashphoner.getMediaDevices() code
При получении списка медиа-устройств заполняются выпадающий список устройств вывода звука на странице клиента.
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.getMediaDevices(null, constraints =true, MEDIA_DEVICE_KIND.OUTPUT).then(function (list) { audio: $("#sendAudio").is(':checked'), list.audio.forEach(function (device) { video: $("#sendVideo").is(':checked'), ... }); ... }).catch(function (error) { customStream: $("#sendCanvasStream#notifyFlash").is(':checked')text("Failed to get media devices"); }); |
Параметры аудио:
...
4. Получение граничных параметров для публикации аудио и видео со страницы клиента
getConstraints() code
Источники публикации:
- камера (sendVideo)
- микрофон (sendAudio)
Code Block | ||||
---|---|---|---|---|
| ||||
if (constraints.audio)constraints = { constraints.audio = {: $("#sendAudio").is(':checked'), deviceIdvideo: $('#audioInput'"#sendVideo").valis(':checked'), }; |
Параметры аудио:
- выбор микрофона (deviceId)
- коррекция ошибок для кодека Opus (fec)
- режим стерео (stereo)
- битрейт аудио (bitrate)
Code Block | ||||
---|---|---|---|---|
| ||||
};if (constraints.audio) { if ($("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()); } |
...
Code Block | ||||
---|---|---|---|---|
| ||||
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.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()); |
45. Получение доступа к медиаустройствам для локального тестирования
Flashphoner.getMediaAccess() код code
В метод передаются граничные параметры для аудио и видео (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); ... testStarted = true; }).catch(function (error) { $("#testBtn").prop('disabled', false); testStarted = false; }); |
56. Подключение к серверу.
Flashphoner.createSession() код code
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.createSession({urlServer: url, timeout: tm}).on(SESSION_STATUS.ESTABLISHED, function (session) { //session connected, start streamingsetStatus("#connectStatus", session.status()); startStreamingonConnected(session); }).on(SESSION_STATUS.DISCONNECTED, function () { setStatus("#connectStatus", SESSION_STATUS.DISCONNECTED); onStoppedonDisconnected(); }).on(SESSION_STATUS.FAILED, function () { setStatus("#connectStatus", SESSION_STATUS.FAILED); onStoppedonDisconnected(); }); |
67. Получение от сервера события, подтверждающего успешное соединение.
ConnectionStatusEvent ESTABLISHED кодESTABLISHED code
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.createSession({urlServer: url, timeout: tm}).on(SESSION_STATUS.ESTABLISHED, function (session) { //session connected, start streamingsetStatus("#connectStatus", session.status()); startStreamingonConnected(session); }).on(SESSION_STATUS.DISCONNECTED, function () { ...... }).on(SESSION_STATUS.FAILED, function () { ... }); |
...
; |
8. Публикация видеопотока
session.createStream(), publishStream.publish() код code
Code Block | ||||
---|---|---|---|---|
| ||||
publishStream = session.createStream({ name: streamName, display: localVideo, cacheLocalResources: true, constraints: constraints, mediaConnectionConstraints: mediaConnectionConstraints, ...sdpHook: rewriteSdp, }); publishStream.publish(); |
8. Получение от сервера события, подтверждающего успешную публикацию потока
StreamStatusEvent PUBLISHING код
При получении данного события создается превью-видеопоток при помощи createStream() и вызывается play() для его воспроизведения.
Code Block | ||||
---|---|---|---|---|
| ||||
publishStream = session.createStream({transport: transportInput, namecvoExtension: streamNamecvo, displaystripCodecs: localVideo,strippedCodecs cacheLocalResources: true,... }); publishStream.publish(); |
9. Получение от сервера события, подтверждающего успешную публикацию потока
StreamStatusEvent PUBLISHING code
Code Block | ||||
---|---|---|---|---|
| ||||
constraints: constraints,publishStream = session.createStream({ mediaConnectionConstraints: mediaConnectionConstraints... }).on(STREAM_STATUS.PUBLISHING, function (publishStreamstream) { $("#testBtn").prop('disabled', true); var video = document.getElementById(publishStreamstream.id()); //resize local if resolution is available if (video.videoWidth > 0 && video.videoHeight > 0) { resizeLocalVideo({target: video}); } enableMuteTogglesenablePublishToggles(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); publishStream.setMicrophoneGain(currentGainValue); setStatus("#publishStatus", STREAM_STATUS.PUBLISHING); //play previewonPublishing(stream); }).on(STREAM_STATUS.UNPUBLISHED, function var constraints = () { ... audio: $("#playAudio"}).is(':checked'),on(STREAM_STATUS.FAILED, function () { ... }); video: $("#playVideo").is(':checked') publishStream.publish(); |
10. Воспроизведение потока
session.createStream(), previewStream.play() code
Code Block | ||||
---|---|---|---|---|
| ||||
previewStream };= session.createStream({ if (constraints.video) { name: streamName, display: remoteVideo, constraints.video = { constraints: constraints, transport: transportOutput, width: (!$("#receiveDefaultSize").is(":checked")) ? parseInt($('#receiveWidth').val()) : 0, stripCodecs: strippedCodecs ... }); height: (!$("#receiveDefaultSize").is(":checked")) ? parseInt($('#receiveHeight').val()) : 0, previewStream.play(); |
11. Получение от сервера события, подтверждающего успешное воспроизведение потока
StreamStatusEvent PLAYING code
Code Block | ||||
---|---|---|---|---|
| ||||
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 () {
detectSpeech(stream);
}, 3000);
}
...
});
previewStream.play(); |
12. Остановка воспроизведения потока.
stream.stop() code
Code Block | ||||
---|---|---|---|---|
| ||||
$("#playBtn").text("Stop").off('click').click(function () {
$(this).prop('disabled', true);
stream.stop();
}).prop('disabled', false); |
13. Получение от сервера события, подтверждающего остановку воспроизведения
StreamStatusEvent STOPPED code
Code Block | ||||
---|---|---|---|---|
| ||||
previewStream = session.createStream({
...
}).on(STREAM_STATUS.STOPPED, function () {
setStatus("#playStatus", STREAM_STATUS.STOPPED);
onStopped();
...
});
previewStream.play(); |
14. Остановка публикации видеопотока
stream.stop() code
Code Block | ||||
---|---|---|---|---|
| ||||
$("#publishBtn").text("Stop").off('click').click(function () {
$(this).prop('disabled', true);
stream.stop();
}).prop('disabled', false); |
15. Получение от сервера события, подтверждающего успешную остановку публикации
StreamStatusEvent UNPUBLISHED code
Code Block | ||||
---|---|---|---|---|
| ||||
publishStream = session.createStream({
...
}).on(STREAM_STATUS.UNPUBLISHED, function () {
setStatus("#publishStatus", STREAM_STATUS.UNPUBLISHED);
onUnpublished();
...
});
publishStream.publish(); |
16. Отключение микрофона
code:
Code Block | ||||
---|---|---|---|---|
| ||||
if ($("#muteAudioToggle").is(":checked")) {
muteAudio();
} |
17. Отключение камеры
code:
Code Block | ||||
---|---|---|---|---|
| ||||
if ($("#muteVideoToggle").is(":checked")) {
muteVideo();
} |
18. Отображение статистики при публикации потока
stream.getStats() code:
Code Block | ||||
---|---|---|---|---|
| ||||
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; } } bitrate: (!$("#receiveDefaultBitrate").is(":checked")) ? $("#receiveBitrate").val() : 0,... }); |
19. Отображение статистики при воспроизведении потока
stream.getStats() code:
Code Block | ||||
---|---|---|---|---|
| ||||
quality: (!$("#receiveDefaultQuality").is(":checked")) ? $('#quality').val() : 0 previewStream.getStats(function (stats) { }; if (stats && stats.inboundStream) { } previewStream = session.createStream({ if (stats.inboundStream.video) { name: streamName, display: remoteVideo, showStat(stats.inboundStream.video, "inVideoStat"); constraints: constraints let vBitrate = (stats.inboundStream.video.bytesReceived - videoBytesReceived) * 8; }); previewStream.play(); }).on(STREAM_STATUS.UNPUBLISHED, function (if ($('#inVideoStatBitrate').length == 0) { ... }).on(STREAM_STATUS.FAILED, function () { ... let html }); publishStream.publish(); |
9. Остановка воспроизведения превью-видеопотока.
previewStream.stop() код
Code Block | ||||
---|---|---|---|---|
| ||||
$("#publishBtn").text("Stop").off('click').click(function () { = "<div>Bitrate: " + "<span id='inVideoStatBitrate' style='font-weight: normal'>" + vBitrate + "</span>" + "</div>"; $(this).prop('disabled', true"#inVideoStat").append(html); previewStream.stop(); }).prop('disabled', false); |
10. Получение от сервера события, подтверждающего остановку воспроизведения
StreamStatusEvent STOPPED код
Code Block | ||||
---|---|---|---|---|
| ||||
} previewStream = session.createStream(else { name: streamName, display: remoteVideo,$('#inVideoStatBitrate').text(vBitrate); constraints: constraints }).on(STREAM_STATUS.PLAYING, function (previewStream) { } ... videoBytesReceived }).on(STREAM_STATUS.STOPPED, function () {= stats.inboundStream.video.bytesReceived; publishStream.stop(); }).on(STREAM_STATUS.FAILED, function () { ... ...} }); if previewStream.play(); |
11. Остановка публикации видеопотока после остановки воспроизведения превью-потока.
publishStream.stop() код
Code Block | ||||
---|---|---|---|---|
| ||||
(stats.inboundStream.audio) { previewStream = session.createStream({ showStat(stats.inboundStream.audio, "inAudioStat"); name: streamName, let aBitrate = (stats.inboundStream.audio.bytesReceived - audioBytesReceived) display: remoteVideo,* 8; constraints: constraints }).on(STREAM_STATUS.PLAYING, function (previewStreamif ($('#inAudioStatBitrate').length == 0) { ... }).on(STREAM_STATUS.STOPPED, function () { let publishStream.stop(); }).on(STREAM_STATUS.FAILED, function () { html = "<div style='font-weight: bold'>Bitrate: " + "<span id='inAudioStatBitrate' style='font-weight: normal'>" + aBitrate + "</span>" + "</div>"; ... }); previewStream.play(); |
12. Получение от сервера события, подтверждающего успешную остановку публикации
StreamStatusEvent UNPUBLISHED код
Code Block | ||||
---|---|---|---|---|
| ||||
publishStream = session.createStream({ $("#inAudioStat").append(html); name: streamName, } display:else localVideo, { cacheLocalResources: true, constraints: constraints, $('#inAudioStatBitrate').text(aBitrate); mediaConnectionConstraints: mediaConnectionConstraints }).on(STREAM_STATUS.PUBLISHING, function (publishStream) { } ... }).on(STREAM_STATUS.UNPUBLISHED, function () { audioBytesReceived setStatus(STREAM_STATUS.UNPUBLISHED)= stats.inboundStream.audio.bytesReceived; //enable start button } onStopped(); }).on(STREAM_STATUS.FAILED, function () { ... }); } publishStream.publish( }); |