...
Note that audio tracks are playing in a separate audio
tags.
Example source code
To analyze the example source code, take the version available here, which can be downloaded with build 1.0.1.36
The source code consists of the following modules:
...
To analyze the example source code, take the file two-way-streaming.js version availablehere, which can be downloaded with build 1.0.41
1. Local variables
Local variables declaration to work with constants, SFU SDK, to display video and to work with client configuration
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" |
...
Default room configuration and stream publishing configuration to use if there is no config.json
file found
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 } ] } ] } } }; |
...
The object should keep Websocket session data, WebRTC connection data and room data, and shoukd form HTML tags ids to access them from code.
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. Initialization
init() code
The init() function is called on page load and:
...
connect(), SFU.createRoom() code
The connect() function is called by Publish or Play click:
...
6. Publishing or playback start after session establishing
onConnected() code
The onConnected() function:
...
publishStreams(), SFURoom.join() code
The publishStreams() function:
...
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
The playStreams() function:
...
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
The onStartClick() function:
...
onStopClick(), Session.disconnect() code
The onStopClick() function:
...
13. Websocket session disconnection actions
onDisconnected() code
The onDisconnected() functions:
...
14.1. Start publishing or playback
startStreaming() code
Code Block | ||||
---|---|---|---|---|
| ||||
const startStreaming = function(state) { if (state.is(PUBLISH)) { publishStreams(state); } else if (state.is(PLAY)) { playStreams(state); } } |
14.2. Stop publishing or playback
stopStreaming() code
Code Block | ||||
---|---|---|---|---|
| ||||
const stopStreaming = function(state) { state.stopWaiting(); if (state.is(PUBLISH)) { unPublishStreams(state); } else if (state.is(PLAY)) { stopStreams(state); } } |