1. Локальные переменные
Объявление локальных переменных для работы с константами, SFU SDK, для отображения локального видео и работы с конфигурацией клиента
code
Code Block |
---|
|
const constants = SFU.constants;
const sfu = SFU;
let localDisplay;
let cControls; |
2. Конфигурация по умолчанию
Объявление конфигурации комнаты и публикации потоков по умолчанию, на случай, если нет файла конфигурации config.json
code
Клиент настраивается на соединение с сервером по WSS по адресу localhost для входа в комнату "ROOM1" с пин-кодом "1234" под именем "Alice". Секция media задает публикацию аудио и видео дорожек. Видео публикуется двумя дорожками с качествами high (h) и medium (m)
Code Block |
---|
|
const defaultConfig = {
room: {
url: "wss://127.0.0.1:8888",
name: "ROOM1",
pin: "1234",
nickName: "Alice"
},
media: {
audio: {
tracks: [
{
source: "mic",
channels: 1
}
]
},
video: {
tracks: [
{
source: "camera",
width: 1280,
height: 720,
codec: "H264",
encodings: [
{ rid: "h", active: true, maxBitrate: 900000 },
{ rid: "m", active: true, maxBitrate: 300000, scaleResolutionDownBy: 2 }
]
}
]
}
}
}; |
3. Инициализация
init() code
Функция init() вызывается после того, как страница загрузится. Функция загружает config.json или конфигурацию по умолчанию, создает элемент для отображения локального видео и открывает модальное окно входа
Code Block |
---|
|
/**
* load config and show entrance modal
*/
const init = function() {
//read config
$.getJSON("config.json", function(config){
cControls = createControls(config);
}).fail(function(){
//use default config
cControls = createControls(defaultConfig);
});
//create local display to show local streams
localDisplay = initLocalDisplay(document.getElementById("localDisplay"));
//open entrance modal
$('#entranceModal').modal('show');
} |
4. Соединение с сервером и создание либо вход в комнату
connect() code
Функция вызывается по щелчку пользователя по кнопке Enter в модальном окне входа
Code Block |
---|
|
/**
* connect to server
*/
function connect() {
//hide modal
$('#entranceModal').modal('hide');
//disable controls
cControls.muteInput();
//create peer connection
const pc = new RTCPeerConnection();
//get config object for room creation
const roomConfig = cControls.roomConfig();
roomConfig.pc = pc;
//kick off connect to server and local room creation
const session = sfu.createRoom(roomConfig);
session.on(constants.SFU_EVENT.CONNECTED, function(room) {
//connected to server
const chatDiv = document.getElementById('messages');
const chatInput = document.getElementById('localMessage');
const chatButton = document.getElementById('sendMessage');
//create and bind chat to the new room
createChat(room, chatDiv, chatInput, chatButton);
room.on(constants.SFU_ROOM_EVENT.FAILED, function(e) {
const errField = document.getElementById("errorMsg");
errField.style.color = "red";
errField.innerText = e;
}).on(constants.SFU_ROOM_EVENT.OPERATION_FAILED, function (e) {
const errField = document.getElementById("errorMsg");
errField.style.color = "red";
errField.innerText = e.operation + " failed: " + e.error;
})
//setup remote display for showing remote audio/video tracks
const remoteDisplay = document.getElementById("display");
initRemoteDisplay(room, remoteDisplay, pc);
//get configured local video streams
let streams = cControls.getVideoStreams();
//combine local video streams with audio streams
streams.push.apply(streams, cControls.getAudioStreams());
//add our local streams to the room (to PeerConnection)
streams.forEach(function (s) {
//add local stream to local display
localDisplay.add(s.stream.id, "local", s.stream);
//add each track to PeerConnection
s.stream.getTracks().forEach((track) => {
addTrackToPeerConnection(pc, s.stream, track, s.encodings);
subscribeTrackToEndedEvent(room, track, pc);
});
});
//add callback for the new local stream to the local controls
cControls.onTrack(function (s) {
//add local stream to local display
localDisplay.add(s.stream.id, "local", s.stream);
//add each track to PeerConnection
s.stream.getTracks().forEach((track) => {
addTrackToPeerConnection(pc, s.stream, track, s.encodings);
subscribeTrackToEndedEvent(room, track, pc);
});
//kickoff renegotiation
room.updateState();
});
//join room
room.join();
});
} |
5. Подробнее о функции connect()
Скрытие модального окна входа и отключение полей ввода до установки соединения с сервером
code
Code Block |
---|
|
//hide modal
$('#entranceModal').modal('hide');
//disable controls
cControls.muteInput(); |
Создание объекта PeerConnection и подготовка объекта конфигурации комнаты
code
Code Block |
---|
|
//create peer connection
const pc = new RTCPeerConnection();
//get config object for room creation
const roomConfig = cControls.roomConfig();
roomConfig.pc = pc; |
Создание сессии и установка соединения с сервером
code
Code Block |
---|
|
const session = sfu.createRoom(roomConfig); |
Подписка на событие сессии "CONNECTED"
code
Code Block |
---|
|
session.on(constants.SFU_EVENT.CONNECTED, function(room) { |
Инициализация чата после установки соединения
code
Code Block |
---|
|
//connected to server
const chatDiv = document.getElementById('messages');
const chatInput = document.getElementById('localMessage');
const chatButton = document.getElementById('sendMessage');
//create and bind chat to the new room
createChat(room, chatDiv, chatInput, chatButton); |
Подписка на сообщения об ошибках комнаты
code
Code Block |
---|
|
room.on(constants.SFU_ROOM_EVENT.FAILED, function(e) {
const errField = document.getElementById("errorMsg");
errField.style.color = "red";
errField.innerText = e;
}).on(constants.SFU_ROOM_EVENT.OPERATION_FAILED, function (e) {
const errField = document.getElementById("errorMsg");
errField.style.color = "red";
errField.innerText = e.operation + " failed: " + e.error;
}) |
Инициализация объекта для отображения потоков от других участников
code
Code Block |
---|
|
//setup remote display for showing remote audio/video tracks
const remoteDisplay = document.getElementById("display");
initRemoteDisplay(room, remoteDisplay, pc); |
Получение настроек публикации локального медиа
code
Code Block |
---|
|
//get configured local video streams
let streams = cControls.getVideoStreams();
//combine local video streams with audio streams
streams.push.apply(streams, cControls.getAudioStreams()); |
Добавление каждого потока в объект localDisplay для отображения и в объект PeerConnection для публикации
code
Code Block |
---|
|
//add our local streams to the room (to PeerConnection)
streams.forEach(function (s) {
//add local stream to local display
localDisplay.add(s.stream.id, "local", s.stream);
//add each track to PeerConnection
s.stream.getTracks().forEach((track) => {
if (s.source === "screen") {
config[track.id] = s.source;
}
addTrackToPeerConnection(pc, s.stream, track, s.encodings);
subscribeTrackToEndedEvent(room, track, pc);
});
}); |
Добавление слушателя, чтобы определить, когда клиент добавляет новые потоки для публикации. Получив новый поток, необходимо добавить его в localDisplay для отображения, добавить в PeerConnection для публикации и обновить состояние комнаты
code
Code Block |
---|
|
//add callback for the new local stream to the local controls
cControls.onTrack(function (s) {
let config = {};
//add local stream to local display
localDisplay.add(s.stream.id, "local", s.stream);
//add each track to PeerConnection
s.stream.getTracks().forEach((track) => {
if (s.source === "screen") {
config[track.id] = s.source;
}
addTrackToPeerConnection(pc, s.stream, track, s.encodings);
subscribeTrackToEndedEvent(room, track, pc);
});
//kickoff renegotiation
room.updateState(config);
}); |
Настройка WebRTC соединения в комнате
code
Code Block |
---|
|
//join room
room.join(pc, null, config); |
6. Завершение публикации потока
subscribeTrackToEndedEvent() code
Вспомогательная функция, которая подписывается на событие "ended" для локального потока. При получении события поток удаляется из PeerConnection, и состояние комнаты обновляется.
Code Block |
---|
|
const subscribeTrackToEndedEvent = function(room, track, pc) {
track.addEventListener("ended", function() {
//track ended, see if we need to cleanup
let negotiate = false;
for (const sender of pc.getSenders()) {
if (sender.track === track) {
pc.removeTrack(sender);
//track found, set renegotiation flag
negotiate = true;
break;
}
}
if (negotiate) {
//kickoff renegotiation
room.updateState();
}
});
}; |
7. Добавление новой дорожки в PeerConnection
addTrackToPeerConnection() code
Вспомогательная функция, которая добавляет новую дорожку в PeerConnection для публикации
Code Block |
---|
|
const addTrackToPeerConnection = function(pc, stream, track, encodings) {
pc.addTransceiver(track, {
direction: "sendonly",
streams: [stream],
sendEncodings: encodings ? encodings : [] //passing encoding types for video simulcast tracks
});
} |