Описание
Пример демонстрирует сценарий презентации: публикация потоков с веб-камеры и с экрана на одной странице с возможностью микширования этих двух потоков на сервере.
Параметры публикации экрана:
- FPS - частота кадров в секунду
- Width - ширина картинки
- Height - высота картинки
Параметры микширования:
- Add to mixer - добавлять ли потоки автоматически в микшер
- Mixer - имя микшера
Параметры соединения:
- Websocket URL WCS сервера
Код примера
Код данного примера находится на WCS-сервере по следующему пути:
/usr/local/FlashphonerWebCallServer/client2/examples/demo/streaming/screen-camera-mixer
screen-camera-mixer.css - файл стилей
screen-camera-mixer.html - HTML страница примера
screen-camera-mixer.js - скрипт, обеспечивающий работу примера
Тестировать данный пример можно по следующему адресу:
https://host:8888/client2/examples/demo/streaming/screen-camera-mixer/screen-camera-mixer.html
Здесь host - адрес WCS-сервера.
Работа с кодом примера
Для разбора кода возьмем версию файла screen-camera-mixer.js с хешем 42b55d1, которая находится здесь и доступна для скачивания в соответствующей сборке 2.0.247.
1. Инициализация API.
Flashphoner.init() code
Flashphoner.init();
2. Подключение к серверу
Flashphoner.createSession() code
const connect = function() { ... let url = field("url"); ... console.log("Create new session with url " + url); Flashphoner.createSession({urlServer: url}).on(SESSION_STATUS.ESTABLISHED, function(session){ ... }).on(SESSION_STATUS.DISCONNECTED, function(){ ... }).on(SESSION_STATUS.FAILED, function(){ ... }); }
3. Получение от сервера события, подтверждающего успешное соединение
STREAM_STATUS.ESTABLISHED code
const connect = function() { ... let url = field("url"); ... console.log("Create new session with url " + url); Flashphoner.createSession({urlServer: url}).on(SESSION_STATUS.ESTABLISHED, function(session){ //session connected, start streaming setStatus("screen", SESSION_STATUS.ESTABLISHED); setStatus("camera", SESSION_STATUS.ESTABLISHED); onConnected(session); }).on(SESSION_STATUS.DISCONNECTED, function(){ ... }).on(SESSION_STATUS.FAILED, function(){ ... }); }
4. Публикация видеопотока с экрана
Session.createStream(), Stream.publish() code
Методу createStream() передаются следующие параметры:
- streamName - имя публикуемого потока
- constraints.video.width - ширина картинки
- constraints.video.height - высота картинки
- constraints.video.frameRate - FPS публикации
- constraints.video.type: "screen" - тип потока: публикация экрана
- constraints.video.withoutExtension: true - публикация экрана из браузера без использования расширения
- constraints.video.mediaSource: "screen" - только для публикации из Firefox
- localVideoScreen - элемент для локального отображения потока
- disableConstraintsNormalization = true - отключение нормализации параметров (только для MacOS Safari)
const startStreamingScreen = function(session) { let streamName = getStreamName("screen", field("url")); let constraints = { video: { width: parseInt($('#width').val()), height: parseInt($('#height').val()), frameRate: parseInt($('#fps').val()), type: "screen", withoutExtension: true } }; if (Browser.isFirefox()) { constraints.video.mediaSource = "screen"; } let options = { name: streamName, display: localVideoScreen, constraints: constraints } if (isSafariMacOS()) { options.disableConstraintsNormalization = true; } session.createStream(options).on(STREAM_STATUS.PUBLISHING, function(screenStream) { ... }).on(STREAM_STATUS.UNPUBLISHED, function() { ... }).on(STREAM_STATUS.FAILED, function(stream) { ... }).publish(); }
5. Получение от сервера события, подтверждающего успешную публикацию экрана
STREAM_STATUS.PUBLISHING code
По этому событию начинаются сбор статистики WebRTC для публикации экрана и публикация потока с камеры
const startStreamingScreen = function(session) { ... session.createStream(options).on(STREAM_STATUS.PUBLISHING, function(screenStream) { /* * User can stop sharing screen capture using Chrome "stop" button. * Catch onended video track event and stop publishing. */ document.getElementById(screenStream.id()).srcObject.getVideoTracks()[0].onended = function (e) { screenStream.stop(); }; document.getElementById(screenStream.id()).addEventListener('resize', function(event){ resizeVideo(event.target); }); setStatus("screen", STREAM_STATUS.PUBLISHING, screenStream); screenStats = StreamStats("screen", screenStream, STAT_INTERVAL); startStreamingCamera(session, screenStream); }).on(STREAM_STATUS.UNPUBLISHED, function() { ... }).on(STREAM_STATUS.FAILED, function(stream) { ... }).publish(); }
6. Публикация видеопотока с камеры
Session.createStream(), Stream.publish() code
Методу createStream() передаются следующие параметры:
- streamName - имя публикуемого потока
- localVideoCamera - элемент для локального отображения потока
const startStreamingCamera = function(session, screenStream) { let streamName = getStreamName("camera", field("url")); let options = { name: streamName, display: localVideoCamera } session.createStream(options).on(STREAM_STATUS.PUBLISHING, function(cameraStream) { ... }).on(STREAM_STATUS.UNPUBLISHED, function() { ... }).on(STREAM_STATUS.FAILED, function(stream) { ... }).publish(); }
7. Получение от сервера события, подтверждающего успешную публикацию камеры
STREAM_STATUS.PUBLISHING code
По этому событию начинаются сбор статистики WebRTC для публикации камеры
const startStreamingCamera = function(session, screenStream) { ... session.createStream(options).on(STREAM_STATUS.PUBLISHING, function(cameraStream) { document.getElementById(cameraStream.id()).addEventListener('resize', function(event){ resizeVideo(event.target); }); setStatus("camera", STREAM_STATUS.PUBLISHING, cameraStream); cameraStats = StreamStats("camera", cameraStream, STAT_INTERVAL); onStarted(cameraStream); }).on(STREAM_STATUS.UNPUBLISHED, function() { ... }).on(STREAM_STATUS.FAILED, function(stream) { ... }).publish(); }
8. Сбор и отображение WebRTC статистики
Stream.getStats(), stats.outboundStream.video.bytesSent, stats.otherStats.availableOutgoingBitrate code
Отображаются следующие параметры:
- текущий битрейт публикации
- максимально возможный битрейт публикации для потока с заданными параметрами, по оценке браузера
const StreamStats = function(type, stream, interval) { const streamStats = { type: type, timer: null, stream: stream, bytesSent: 0, start: function(interval) { if (!streamStats.timer) { streamStats.timer = setInterval(streamStats.displayStats, interval); } }, stop: function() { if (streamStats.timer) { clearInterval(streamStats.timer); streamStats.timer = null; } setBitrate(streamStats.type, ""); setAvailableBitrate(streamStats.type, ""); }, displayStats: function() { if (streamStats.stream) { streamStats.stream.getStats((stats) => { if (stats) { if (stats.outboundStream && stats.outboundStream.video) { let vBitrate = (stats.outboundStream.video.bytesSent - streamStats.bytesSent) * 8; setBitrate(streamStats.type, vBitrate); streamStats.bytesSent = stats.outboundStream.video.bytesSent; } if (stats.otherStats && stats.otherStats.availableOutgoingBitrate !== undefined) { setAvailableBitrate(streamStats.type, stats.otherStats.availableOutgoingBitrate); } } }); } } }; streamStats.start(interval); return streamStats; }
9. Остановка публикации камеры
Stream.stop() code
const setPublishButton = function(action, session, cameraStream) { $("#publishBtn").text(action).off('click').click(function(){ if (action == "Start") { ... } else if (action === "Stop") { $(this).prop('disabled', true); cameraStream.stop(); } }).prop('disabled', false); }
10. Остановка публикации экрана
Stream.stop() code
const startStreamingCamera = function(session, screenStream) { ... session.createStream(options).on(STREAM_STATUS.PUBLISHING, function(cameraStream) { ... }).on(STREAM_STATUS.UNPUBLISHED, function() { setStatus("camera", STREAM_STATUS.UNPUBLISHED); screenStream.stop(); }).on(STREAM_STATUS.FAILED, function(stream) { ... }).publish(); }
11. Остановка сбора статистики WebRTC
const onStopped = function(session) { if (cameraStats) { cameraStats.stop(); } if (screenStats) { screenStats.stop(); } ... }