Contains This module contains code related to displaying local and remote media. Code is separated in two closures - one for local media and another one for remote
Display local media
1. Wrapper function
initLocalDisplay() code
Define top level closure for local media code
Code Block |
---|
|
const initLocalDisplay = function(localDisplayElement){ |
code
Define local variables
Code Block |
---|
|
const localDisplayDiv = localDisplayElement;
const localDisplays = {}; |
removeLocalDisplay() code
Define function that will remove local display once track is ended
Code Block |
---|
|
const removeLocalDisplay = function(id) {
delete localDisplays[id];
$('#' + id).remove();
reassembleLocalLayout();
} |
getAudioContainer() code
Define function that will find video element without audio track.
Code Block |
---|
|
const getAudioContainer = function() {
for (const [key, value] of Object.entries(localDisplays)) {
let video = value.getElementsByTagName("video");
if (video && video[0]) {
let audioStateButton = value.getElementsByTagName("button");
let audioTracks = video[0].srcObject.getAudioTracks();
if (!audioTracks || audioTracks.length === 0) {
return {
id: value.id,
video: video[0],
audioStateDisplay: audioStateButton[0]
}
}
}
}
}; |
add() code
Define add function which will add new track to local display
...
Code Block |
---|
|
localDisplays[id] = coreDisplay;
reassembleLocalLayout();
return coreDisplay; |
reassembleLocalLayout() code
...
Code Block |
---|
|
const reassembleLocalLayout = function() {
let gridWidth = gridSize(Object.keys(localDisplays).length).x;
let container = document.createElement('div');
let row;
let rowI = 1;
let colI = 0;
for (const [key, value] of Object.entries(localDisplays)) {
if (row) {
if (colI >= gridWidth) {
row = createRow(container);
rowI++;
colI = 0;
}
} else {
row = createRow(container);
}
$("#" + key).detach();
let col = createCol(row);
col.appendChild(value);
colI++;
}
$(localDisplayDiv).empty();
localDisplayDiv.appendChild(container);
} |
7. Export add function for main code
code
Code Block |
---|
|
return {
add: add
} |
Display remote media
1. Wrapper function
initRemoteDisplay() code
Define top level closure for remote media code
Code Block |
---|
|
const initRemoteDisplay = function(room, mainDiv, peerConnection) { |
code
Define local variables
Code Block |
---|
|
const constants = SFU.constants;
const remoteParticipants = {}; |
code
Subscribe to related room events
Code Block |
---|
|
room.on(constants.SFU_ROOM_EVENT.ADD_TRACKS, function(e) {
let participant = remoteParticipants[e.info.nickName];
if (!participant) {
participant = {};
participant.nickName = e.info.nickName;
participant.tracks = [];
participant.displays = [];
remoteParticipants[participant.nickName] = participant;
}
participant.tracks.push.apply(participant.tracks, e.info.info);
for (const pTrack of e.info.info) {
let createDisplay = true;
for (let i = 0; i < participant.displays.length; i++) {
let display = participant.displays[i];
if (pTrack.type === "VIDEO") {
if (display.hasVideo()) {
continue;
}
display.videoMid = pTrack.mid;
display.setTrackInfo(pTrack);
createDisplay = false;
break;
} else if (pTrack.type === "AUDIO") {
if (display.hasAudio()) {
continue;
}
display.audioMid = pTrack.mid;
createDisplay = false;
break;
}
}
if (!createDisplay) {
continue;
}
let display = createRemoteDisplay(participant.nickName, participant.nickName, mainDiv);
participant.displays.push(display);
if (pTrack.type === "VIDEO") {
display.videoMid = pTrack.mid;
display.setTrackInfo(pTrack);
} else if (pTrack.type === "AUDIO") {
display.audioMid = pTrack.mid;
}
}
}).on(constants.SFU_ROOM_EVENT.REMOVE_TRACKS, function(e) {
const participant = remoteParticipants[e.info.nickName];
if (!participant) {
return;
}
for (const rTrack of e.info.info) {
for (let i = 0; i < participant.tracks.length; i++) {
if (rTrack.mid === participant.tracks[i].mid) {
participant.tracks.splice(i, 1);
break;
}
}
for (let i = 0; i < participant.displays.length; i++) {
let found = false;
const display = participant.displays[i];
if (display.audioMid === rTrack.mid) {
display.setAudio(null);
found = true;
} else if (display.videoMid === rTrack.mid) {
display.setVideo(null);
found = true;
}
if (found) {
if (!display.hasAudio() && !display.hasVideo()) {
display.dispose();
participant.displays.splice(i, 1);
}
break;
}
}
}
}).on(constants.SFU_ROOM_EVENT.LEFT, function(e) {
let participant = remoteParticipants[e.name];
if (!participant) {
return;
}
participant.displays.forEach(function(display){
display.dispose();
})
delete remoteParticipants[e.name];
}).on(constants.SFU_ROOM_EVENT.TRACK_QUALITY_STATE, function(e){
console.log("Received track quality state");
const participant = remoteParticipants[e.info.nickName];
if (!participant) {
return;
}
for (const rTrack of e.info.tracks) {
const mid = rTrack.mid;
for (let i = 0; i < participant.displays.length; i++) {
const display = participant.displays[i];
if (display.videoMid === mid) {
display.updateQualityInfo(rTrack.quality);
break;
}
}
}
}); |
Find participant. If not found create a new one.
...
Code Block |
---|
|
for (const pTrack of e.info.info) {
let createDisplay = true;
for (let i = 0; i < participant.displays.length; i++) {
let display = participant.displays[i];
if (pTrack.type === "VIDEO") {
if (display.hasVideo()) {
continue;
}
display.videoMid = pTrack.mid;
display.setTrackInfo(pTrack);
createDisplay = false;
break;
} else if (pTrack.type === "AUDIO") {
if (display.hasAudio()) {
continue;
}
display.audioMid = pTrack.mid;
createDisplay = false;
break;
}
}
if (!createDisplay) {
continue;
}
let display = createRemoteDisplay(participant.nickName, participant.nickName, mainDiv);
participant.displays.push(display);
if (pTrack.type === "VIDEO") {
display.videoMid = pTrack.mid;
display.setTrackInfo(pTrack);
} else if (pTrack.type === "AUDIO") {
display.audioMid = pTrack.mid;
}
}
} |
Find remote participant. If not found return.
...
Code Block |
---|
|
for (let i = 0; i < participant.displays.length; i++) {
let found = false;
const display = participant.displays[i];
if (display.mids.audio.includes(rTrack.mid)) {
//remove from mids array
display.mids.audio.splice(display.mids.audio.indexOf(rTrack.mid), 1);
//stop track and remove stream
display.audioStreams[rTrack.mid].getAudioTracks()[0].stop();
delete display.audioStreams[rTrack.mid];
//remove audio element
display.display.removeChild(display.audioElements[rTrack.mid]);
delete display.audioElements[rTrack.mid];
found = true;
} else if (display.mids.video === rTrack.mid) {
display.mids.video = undefined;
display.mediaStream.getVideoTracks()[0].stop();
found = true;
}
if (display.mids.audio.length === 0 && display.mids.video === undefined) {
const video = display.display.getElementsByTagName("video")[0]
video.pause();
video.srcObject = null;
display.display.remove();
participant.displays.splice(i, 1);
}
if (found) {
break;
}
} |
Find and remove participant.
...
Code Block |
---|
|
let participant = remoteParticipants[e.name];
if (!participant) {
return;
}
participant.displays.forEach(function(display){
display.dispose();
})
delete remoteParticipants[e.name]; |
Find participant. Return if not found.
...
Code Block |
---|
|
const mid = rTrack.mid;
for (let i = 0; i < participant.displays.length; i++) {
const display = participant.displays[i];
if (display.videoMid === mid) {
display.updateQualityInfo(rTrack.quality);
break;
}
} |
createRemoteDisplay() code
...
Code Block |
---|
|
updateQualityInfo: function(videoQuality) {
for (const qualityInfo of videoQuality) {
for (const qualityDiv of qualityDivs) {
if (qualityDiv.innerText === qualityInfo.quality){
if (qualityInfo.available === true) {
qualityDiv.style.color = "gray";
} else {
qualityDiv.style.color = "red";
}
break;
}
}
}
} |
code
Subscribe to PeerConnection's "ontrack" event.
...