...
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) {
if (pTrack.type === "VIDEO") {
const displayObj = {
display: createRemoteDisplay(participant.nickName, participant.nickName, null, pTrack),
mediaStream: new MediaStream(),
mids: {
audio: [],
video: undefined
},
audioStreams: {},
audioElements: {}
};
const video = displayObj.display.getElementsByTagName("video")[0];
video.srcObject = displayObj.mediaStream;
displayObj.mids.video = pTrack.mid;
participant.displays.push(displayObj);
}
}
}).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.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;
}
}
}
}).on(constants.SFU_ROOM_EVENT.LEFT, function(e) {
let participant = remoteParticipants[e.name];
if (!participant) {
return;
}
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;
let vDisplay;
for (let i = 0; i < participant.displays.length; i++) {
const display = participant.displays[i];
if (display.mids.video === mid) {
vDisplay = display;
break;
}
}
//todo rework loops
if (vDisplay) {
for (const qualityInfo of rTrack.quality) {
for (const child of vDisplay.display.childNodes) {
if (child.childNodes.length > 0) {
for (const cChild of child.childNodes) {
if (cChild.innerHTML === qualityInfo.quality) {
if (qualityInfo.available === true) {
cChild.style.color = "gray";
} else {
cChild.style.color = "red";
}
break;
}
}
}
}
}
}
}
}); |
...
SFU_ROOM_EVENT.ADD_TRACKS
Find participant. If not found create a new one.
...
Code Block |
---|
|
participant.tracks.push.apply(participant.tracks, e.info.info); |
Create display for every video track that is beeing added.
Code Block |
---|
|
for (const pTrack of e.info.info) {
if (pTrack.type === "VIDEO") {
const displayObj = {
display: createRemoteDisplay(participant.nickName, participant.nickName, null, pTrack),
mediaStream: new MediaStream(),
mids: {
audio: [],
video: undefined
},
audioStreams: {},
audioElements: {}
};
const video = displayObj.display.getElementsByTagName("video")[0];
video.srcObject = displayObj.mediaStream;
displayObj.mids.video = pTrack.mid;
participant.displays.push(displayObj);
}
} |
SFU_ROOM_EVENT.REMOVE_TRACKS
Find remote participant. If not found return.
Code Block |
---|
|
const participant = remoteParticipants[e.info.nickName];
if (!participant) {
return;
} |
Walk through tracks
Code Block |
---|
|
for (const rTrack of e.info.info) { |
Find and remove participant's track that has the same mid as track that is beeing removed.
Code Block |
---|
|
for (let i = 0; i < participant.tracks.length; i++) {
if (rTrack.mid === participant.tracks[i].mid) {
participant.tracks.splice(i, 1);
break;
}
} |
Find display that corresponds to track and remove track from the display. If display has no active tracks remove display as well.
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;
}
} |
SFU_ROOM_EVENT.LEFT
Find and remove participant.
Code Block |
---|
|
let participant = remoteParticipants[e.name];
if (!participant) {
return;
}
delete remoteParticipants[e.name]; |
SFU_ROOM_EVENT.TRACK_QUALITY_STATE
Find participant. Return if not found.
Code Block |
---|
|
console.log("Received track quality state");
const participant = remoteParticipants[e.info.nickName];
if (!participant) {
return;
} |
Walk through tracks
Code Block |
---|
|
for (const rTrack of e.info.tracks) { |
Find corresponding display
Code Block |
---|
|
const mid = rTrack.mid;
let vDisplay;
for (let i = 0; i < participant.displays.length; i++) {
const display = participant.displays[i];
if (display.mids.video === mid) {
vDisplay = display;
break;
}
} |
Update quality state
Code Block |
---|
|
if (vDisplay) {
for (const qualityInfo of rTrack.quality) {
for (const child of vDisplay.display.childNodes) {
if (child.childNodes.length > 0) {
for (const cChild of child.childNodes) {
if (cChild.innerHTML === qualityInfo.quality) {
if (qualityInfo.available === true) {
cChild.style.color = "gray";
} else {
cChild.style.color = "red";
}
break;
}
}
}
}
}
} |
Helper function which will create display based on stream and track info.
Code Block |
---|
|
const createRemoteDisplay = function(id, name, stream, trackInfo) {
const cell = document.createElement('div');
cell.setAttribute("class", "grid-item");
cell.id = id;
mainDiv.appendChild(cell);
const streamNameDisplay = document.createElement("div");
streamNameDisplay.innerHTML = "Name: " + name;
streamNameDisplay.setAttribute("style","width:auto; height:20px");
cell.appendChild(streamNameDisplay);
const qualityDisplay = document.createElement("div");
qualityDisplay.setAttribute("style","width:auto; height:20px");
cell.appendChild(qualityDisplay);
const tidDisplay = document.createElement("div");
tidDisplay.setAttribute("style","width:auto; height:20px");
cell.appendChild(tidDisplay);
const qualityDivs = [];
const tidDivs = [];
if (trackInfo && trackInfo.quality) {
for (let i = 0; i < trackInfo.quality.length; i++) {
const qualityDiv = document.createElement("button");
qualityDivs.push(qualityDiv);
qualityDiv.innerText = trackInfo.quality[i];
qualityDiv.setAttribute("style", "display:inline-block; border: solid; border-width: 1px");
qualityDiv.style.color = "red";
qualityDiv.addEventListener('click', function(){
console.log("Clicked on quality " + trackInfo.quality[i] + " trackId " + trackInfo.id);
if (qualityDiv.style.color === "red") {
return;
}
for (let c = 0; c < qualityDivs.length; c++) {
if (qualityDivs[c].style.color !== "red") {
qualityDivs[c].style.color = "gray";
}
}
qualityDiv.style.color = "blue";
room.changeQuality(trackInfo.id, trackInfo.quality[i]);
});
qualityDisplay.appendChild(qualityDiv);
}
for (let i = 0; i < 3; i++) {
const tidDiv = document.createElement("button");
tidDivs.push(tidDiv);
tidDiv.innerText = "TID"+i;
tidDiv.setAttribute("style", "display:inline-block; border: solid; border-width: 1px");
tidDiv.style.color = "gray";
tidDiv.addEventListener('click', function(){
console.log("Clicked on TID " + i + " trackId " + trackInfo.id);
for (let c = 0; c < tidDivs.length; c++) {
tidDivs[c].style.color = "gray";
}
tidDiv.style.color = "blue";
room.changeQuality(trackInfo.id, null, i);
});
tidDisplay.appendChild(tidDiv);
}
}
const rootDisplay = document.createElement('div');
rootDisplay.setAttribute("style","width:auto; height:auto");
cell.appendChild(rootDisplay);
const streamDisplay = document.createElement('div');
streamDisplay.setAttribute("style","width:auto; height:auto");
rootDisplay.appendChild(streamDisplay);
const video = document.createElement("video");
streamDisplay.appendChild(video);
video.srcObject = stream;
video.onloadedmetadata = function (e) {
video.play();
};
video.addEventListener("ended", function() {
console.log("VIDEO ENDED");
});
video.addEventListener('resize', function (event) {
streamNameDisplay.innerHTML = "Name: " + name + " " + video.videoWidth + "x" + video.videoHeight;
resizeVideo(event.target);
});
return cell;
} |
PeerConnection code
Subscribe to PeerConnection's "ontrack" event.
Code Block |
---|
|
peerConnection.ontrack = ({transceiver}) => {
let rParticipant;
console.log("Attach remote track " + transceiver.receiver.track.id + " kind " + transceiver.receiver.track.kind + " mid " + transceiver.mid);
for (const [nickName, participant] of Object.entries(remoteParticipants)) {
for (const pTrack of participant.tracks) {
console.log("Participant " + participant.nickName + " track " + pTrack.id + " mid " + pTrack.mid);
if (pTrack.mid === transceiver.mid) {
rParticipant = participant;
break;
}
}
if (rParticipant) {
break;
}
}
if (rParticipant) {
for (const display of rParticipant.displays) {
if (transceiver.receiver.track.kind === "video") {
if (display.mids.video === transceiver.mid) {
display.mediaStream.addTrack(transceiver.receiver.track);
display.display.getElementsByTagName("video")[0].play();
break;
}
} else if (transceiver.receiver.track.kind === "audio") {
if (display.mids.audio.includes(transceiver.mid)) {
break;
}
display.mids.audio.push(transceiver.mid);
let aStream = new MediaStream();
aStream.addTrack(transceiver.receiver.track);
display.audioStreams[transceiver.mid] = aStream;
let audio = document.createElement("audio");
audio.controls = "controls";
display.audioElements[transceiver.mid] = audio;
display.display.appendChild(audio);
audio.srcObject = aStream;
audio.play();
break;
}
}
} else {
console.warn("Failed to find participant for track " + transceiver.receiver.track.id);
}
} |
Find participant based on track's mid
Code Block |
---|
|
let rParticipant;
console.log("Attach remote track " + transceiver.receiver.track.id + " kind " + transceiver.receiver.track.kind + " mid " + transceiver.mid);
for (const [nickName, participant] of Object.entries(remoteParticipants)) {
for (const pTrack of participant.tracks) {
console.log("Participant " + participant.nickName + " track " + pTrack.id + " mid " + pTrack.mid);
if (pTrack.mid === transceiver.mid) {
rParticipant = participant;
break;
}
}
if (rParticipant) {
break;
}
} |
Find corresponding display among participant's displays and add track.
Code Block |
---|
|
for (const display of rParticipant.displays) {
if (transceiver.receiver.track.kind === "video") {
if (display.mids.video === transceiver.mid) {
display.mediaStream.addTrack(transceiver.receiver.track);
display.display.getElementsByTagName("video")[0].play();
break;
}
} else if (transceiver.receiver.track.kind === "audio") {
if (display.mids.audio.includes(transceiver.mid)) {
break;
}
display.mids.audio.push(transceiver.mid);
let aStream = new MediaStream();
aStream.addTrack(transceiver.receiver.track);
display.audioStreams[transceiver.mid] = aStream;
let audio = document.createElement("audio");
audio.controls = "controls";
display.audioElements[transceiver.mid] = audio;
display.display.appendChild(audio);
audio.srcObject = aStream;
audio.play();
break;
}
} |