Описание
Пример демонстрирует сценарий презентации: публикация потоков с веб-камеры и с экрана на одной странице с возможностью микширования этих двух потоков на сервере.
Параметры публикации экрана:
- 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();
}
...
}
