main.js - основная логика приложения¶
Данный модуль описывает основную лоргику приложения
1. Локальные переменные¶
Объявление локальных переменных для работы с константами, SFU SDK, для отображения локального видео и работы с конфигурацией клиента
2. Конфигурация по умолчанию¶
Объявление конфигурации комнаты и публикации потоков по умолчанию, на случай, если нет файла конфигурации config.json
Клиент настраивается на соединение с сервером по WSS по адресу localhost для входа в комнату ROOM1
с пин-кодом 1234
под именем Alice
. Секция media задает публикацию аудио и видео дорожек. Видео публикуется двумя дорожками с качествами high (h) и medium (m)
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
или конфигурацию по умолчанию, создает элемент для отображения локального видео и открывает модальное окно входа
/**
* 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
в модальном окне входа
connect
/**
* 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();
});
}
Скрытие модального окна входа и отключение полей ввода до установки соединения с сервером
Создание объекта PeerConnection
и подготовка объекта конфигурации комнаты
//create peer connection
const pc = new RTCPeerConnection();
//get config object for room creation
const roomConfig = cControls.roomConfig();
roomConfig.pc = pc;
Создание сессии и установка соединения с сервером
Подписка на событие сессии CONNECTED
Инициализация чата после установки соединения
//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());
Добавление каждого потока в объект localDisplay
для отображения и в объект PeerConnection
для публикации
//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);
});
});
Добавление слушателя, чтобы определить, когда клиент добавляет новые потоки для публикации. Получив новый поток, необходимо добавить его в localDisplay
для отображения, добавить в PeerConnection
для публикации и обновить состояние комнаты
//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();
});
Вход пользователя в комнату
5. Завершение публикации потока¶
subscribeTrackToEndedEvent()
code
Вспомогательная функция, которая подписывается на событие ended
для локального потока. При получении события поток удаляется из PeerConnection
, и состояние комнаты обновляется.
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
для публикации