...
Для работы с исходным кодом примера возьмем версию файла two-way-streaming.js, доступную здесь, которую также можно загрузить в сборке 1.0.41
1. Локальные переменные
Объявление локальных переменных для работы с константами, SFU SDK, для отображения видео и работы с конфигурацией клиента
Code Block | ||||
---|---|---|---|---|
| ||||
const constants = SFU.constants; const sfu = SFU; let mainConfig; let localDisplay; let remoteDisplay; let publishState; let playState; const PUBLISH = "publish"; const PLAY = "play"; const STOP = "stop"; const PRELOADER_URL="../commons/media/silence.mp3" |
...
Объявление конфигурации комнаты и публикации потоков по умолчанию, на случай, если нет файла конфигурации config.json
Code Block | ||||
---|---|---|---|---|
| ||||
const defaultConfig = { room: { url: "wss://127.0.0.1:8888", name: "ROOM1", pin: "1234", nickName: "User1" }, media: { audio: { tracks: [ { source: "mic", channels: 1 } ] }, video: { tracks: [ { source: "camera", width: 640, height: 360, codec: "H264", encodings: [ { rid: "360p", active: true, maxBitrate: 500000 }, { rid: "180p", active: true, maxBitrate: 200000, scaleResolutionDownBy: 2 } ] } ] } } }; |
...
Хранит данные Websocket сессии, WebRTC соединения и комнаты, формирует идентификаторы элементов на странице для доступа к ним
Code Block | ||||
---|---|---|---|---|
| ||||
const CurrentState = function(prefix) { let state = { prefix: prefix, pc: null, session: null, room: null, timer: null, set: function(pc, session, room) { state.pc = pc; state.session = session; state.room = room; }, clear: function() { state.stopWaiting(); state.room = null; state.session = null; state.pc = null; }, waitFor: function(div, timeout) { state.stopWaiting(); state.timer = setTimeout(function () { if (div.innerHTML !== "") { // Enable stop button $("#" + state.buttonId()).prop('disabled', false); } else if (state.isConnected()) { setStatus(state.errInfoId(), "No media capturing started in " + timeout + " ms, stopping", "red"); onStopClick(state); } }, timeout); }, stopWaiting: function() { if (state.timer) { clearTimeout(state.timer); state.timer = null; } }, buttonId: function() { return state.prefix + "Btn"; }, buttonText: function() { return (state.prefix.charAt(0).toUpperCase() + state.prefix.slice(1)); }, inputId: function() { return state.prefix + "Name"; }, statusId: function() { return state.prefix + "Status"; }, formId: function() { return state.prefix + "Form"; }, errInfoId: function() { return state.prefix + "ErrorInfo"; }, is: function(value) { return (prefix === value); }, isActive: function() { return (state.room && state.pc); }, isConnected: function() { return (state.session && state.session.state() == constants.SFU_STATE.CONNECTED); } }; return state; } |
4. Инициализация
init() code
Функция init() вызывается после того, как страница загрузится:
...
connect(), SFU.createRoom() code
Функция connect() вызывается по нажатию кнопки Publish или Play:
...
6. Запуск публикации или проигрывания при установке соединения
onConnected() code
Функция onConnected():
- настраивает действия по нажатию кнопки Stop
- подписывается на события об ошибках комнаты
- вызывает функцию публикации или проигрывания
...
publishStreams(), SFURoom.join() code
Функция publishStreams():
...
addTrackToPeerConnection(), PeerConnection.addTransceiver() code
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 }); } |
...
subscribeTrackToEndedEvent(), MediaTrack.addEventListener(), SFURoom.updateState() code
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(); } }); }; |
...
playStreams(), SFURoom.join() code
Функция playStreams():
- инициализирует базовый элемент для отображения входящих медиа потоков
- входит в комнату на сервере
Code Block | ||||
---|---|---|---|---|
| ||||
const playStreams = function(state) { if (state.isConnected() && state.isActive()) { //create remote display item to show remote streams remoteDisplay = initRemoteDisplay({ div: document.getElementById("remoteVideo"), room: state.room, peerConnection: state.pc }); state.room.join(state.pc); } $("#" + state.buttonId()).prop('disabled', false); } |
...
unPublishStreams(), localDisplay.stop() code
Code Block | ||||
---|---|---|---|---|
| ||||
const unPublishStreams = function(state) { if (localDisplay) { localDisplay.stop(); } } |
...
stopStreams(), remoteDisplay.stop() code
Code Block | ||||
---|---|---|---|---|
| ||||
const stopStreams = function(state) { if (remoteDisplay) { remoteDisplay.stop(); } } |
...
onStartClick(), playFirstSound(), connect() code
Функция onStartClick():
- проверяет правильность заполнения полей ввода
- перед стартом воспроизведения, в браузере Safari вызывает функцию playFirstSound() для автоматического проигрывания аудио
- вызывает функцию connect()
...
onStopClick(), Session.disconnect() code
Функция onStopClick():
- останавливает публикацию или воспроизведение
- разрывает Websocket сессию
...
13. Действия при разрыве Websocket сессии
onDisconnected() code
Функция onDisconnected():
...
14.1. Запуск публикации или проигрывания
startStreaming() code
Code Block | ||||
---|---|---|---|---|
| ||||
const startStreaming = function(state) { if (state.is(PUBLISH)) { publishStreams(state); } else if (state.is(PLAY)) { playStreams(state); } } |
14.2. Остановка публикации или проигрывания
stopStreaming() code
Code Block | ||||
---|---|---|---|---|
| ||||
const stopStreaming = function(state) { state.stopWaiting(); if (state.is(PUBLISH)) { unPublishStreams(state); } else if (state.is(PLAY)) { stopStreams(state); } } |