Date: Thu, 28 Mar 2024 14:39:03 +0100 (CET) Message-ID: <1740898606.47119.1711633143786@docs.flashphoner.com> Subject: Exported From Confluence MIME-Version: 1.0 Content-Type: multipart/related; boundary="----=_Part_47118_94900511.1711633143786" ------=_Part_47118_94900511.1711633143786 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Content-Location: file:///C:/exported.html
Declare local variables for constants, SFU SDK, local display and contro= ls
const constants =3D= SFU.constants; const sfu =3D SFU; let localDisplay; let cControls;=20
Declare default room and publishing configuration which will be used if = there was no config.json file available
With this config client will be preconfigured to connect to localhost ov= er WSS, enter room "ROOM1" with pin "1234" and nickname "Alice". Media sect= ion directs client to publish audio and video tracks. Video will have two s= ub-tracks - high (h) and medium (m).
const defaultConfig= =3D { 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, scale= ResolutionDownBy: 2 } ] } ] } } };=20
init() code
Init function that is called when page is finished loading. The function= will load config.json or default config, create local display and open ent= rance modal window.
/** * load config and show entrance modal */ const init =3D function() { //read config $.getJSON("config.json", function(config){ cControls =3D createControls(config); }).fail(function(){ //use default config cControls =3D createControls(defaultConfig); }); //create local display to show local streams localDisplay =3D initLocalDisplay(document.getElementById("localDisplay= ")); //open entrance modal $('#entranceModal').modal('show'); }=20
connect() code
Connect function that is called once user clicks Enter in entrance modal= window.
/** * connect to server */ function connect() { //hide modal $('#entranceModal').modal('hide'); //disable controls cControls.muteInput(); //create peer connection const pc =3D new RTCPeerConnection(); //get config object for room creation const roomConfig =3D cControls.roomConfig(); roomConfig.pc =3D pc; //kick off connect to server and local room creation const session =3D sfu.createRoom(roomConfig); session.on(constants.SFU_EVENT.CONNECTED, function(room) { //connected to server const chatDiv =3D document.getElementById('messages'); const chatInput =3D document.getElementById('localMessage'); const chatButton =3D 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 =3D document.getElementById("errorMsg"); errField.style.color =3D "red"; errField.innerText =3D e; }).on(constants.SFU_ROOM_EVENT.OPERATION_FAILED, function (e) { const errField =3D document.getElementById("errorMsg"); errField.style.color =3D "red"; errField.innerText =3D e.operation + " failed: " + e.error; }) //setup remote display for showing remote audio/video tracks const remoteDisplay =3D document.getElementById("display"); initRemoteDisplay(room, remoteDisplay, pc); //get configured local video streams let streams =3D 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) =3D> { 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) =3D> { addTrackToPeerConnection(pc, s.stream, track, s.encodings); subscribeTrackToEndedEvent(room, track, pc); }); //kickoff renegotiation room.updateState(); }); //join room room.join(); }); }=20
Hide modal as we don't need it anymore and mute controls before connect = is established
//hide modal $('#entranceModal').modal('hide'); //disable controls cControls.muteInput();=20
Create PeerConnection and prepare the room config for the creation of se= ssion and room
//create peer conne= ction const pc =3D new RTCPeerConnection(); //get config object for room creation const roomConfig =3D cControls.roomConfig(); roomConfig.pc =3D pc;=20
Create session (which will automatically connect to the server)
const session =3D s= fu.createRoom(roomConfig);=20
Subscribe to session's "CONNECTED" event
session.on(constant= s.SFU_EVENT.CONNECTED, function(room) {=20
Once we are connected initialize room chat
//connected to serv= er const chatDiv =3D document.getElementById('messages'); const chatInput =3D document.getElementById('localMessage'); const chatButton =3D document.getElementById('sendMessage'); //create and bind chat to the new room createChat(room, chatDiv, chatInput, chatButton);=20
Subscribe to room error events
room.on(constants.S= FU_ROOM_EVENT.FAILED, function(e) { const errField =3D document.getElementById("errorMsg"); errField.style.color =3D "red"; errField.innerText =3D e; }).on(constants.SFU_ROOM_EVENT.OPERATION_FAILED, function (e) { const errField =3D document.getElementById("errorMsg"); errField.style.color =3D "red"; errField.innerText =3D e.operation + " failed: " + e.error; })=20
Initialize remote display
//setup remote disp= lay for showing remote audio/video tracks const remoteDisplay =3D document.getElementById("display"); initRemoteDisplay(room, remoteDisplay, pc);=20
Get preconfigured local media from controls
//get configured lo= cal video streams let streams =3D cControls.getVideoStreams(); //combine local video streams with audio streams streams.push.apply(streams, cControls.getAudioStreams());=20
Add each stream to local display (so we can see it on page) and peer con= nection
//add our l= ocal 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) =3D> { if (s.source =3D=3D=3D "screen") { config[track.id] =3D s.source; } addTrackToPeerConnection(pc, s.stream, track, s.encodings); subscribeTrackToEndedEvent(room, track, pc); }); });=20
Add listener to controls so we know if user adds new local streams. Once= we have a new stream we will need to add it to local display, add it to pe= er connection and kickoff renegotiation
//add callb= ack for the new local stream to the local controls cControls.onTrack(function (s) { let config =3D {}; //add local stream to local display localDisplay.add(s.stream.id, "local", s.stream); //add each track to PeerConnection s.stream.getTracks().forEach((track) =3D> { if (s.source =3D=3D=3D "screen") { config[track.id] =3D s.source; } addTrackToPeerConnection(pc, s.stream, track, s.encodings); subscribeTrackToEndedEvent(room, track, pc); }); //kickoff renegotiation room.updateState(config); });=20
WebRTC negotiation in the room
//join room room.join(pc, null, config);=20
subscribeTrackToEndedEvent() code
This is a helper function that subscribes new local track to "ended" eve= nt. Once event fired we remove track from peer connection and kickoff reneg= otiation.
const subscribeTrac= kToEndedEvent =3D function(room, track, pc) { track.addEventListener("ended", function() { //track ended, see if we need to cleanup let negotiate =3D false; for (const sender of pc.getSenders()) { if (sender.track =3D=3D=3D track) { pc.removeTrack(sender); //track found, set renegotiation flag negotiate =3D true; break; } } if (negotiate) { //kickoff renegotiation room.updateState(); } }); };=20
addTrackToPeerConnection() code
This is a helper function which adds new local track to peer connection.=
const addTrackToPee= rConnection =3D function(pc, stream, track, encodings) { pc.addTransceiver(track, { direction: "sendonly", streams: [stream], sendEncodings: encodings ? encodings : [] //passing encoding types = for video simulcast tracks }); }=20