...
Code Block | ||||
---|---|---|---|---|
| ||||
const createOneToOneParticipantModel = function (userId, nickname, participantView, remoteTrackFactory, abrFactory, displayOptions) { const instance = { ... addAudioTrack: function (track) { this.audioTracks.set(track.mid, track); const self = this; remoteTrackFactory.getAudioTrack().then((remoteTrack) => { if (remoteTrack) { if (self.disposed || !self.audioTracks.get(track.mid)) { remoteTrack.dispose(); return; } this.remoteAudioTracks.set(track.mid, remoteTrack); remoteTrack.demandTrack(track.id).then(() => { if (!self.audioTracks.get(track.mid)) { remoteTrack.dispose(); self.remoteAudioTracks.delete(track.mid); return; } participantView.addAudioTrack(track, remoteTrack.track, displayOptions.showAudio); }, (ex) => { console.log("Failed demand track " + ex); remoteTrack.dispose(); self.remoteAudioTracks.delete(track.mid); }); } }, (ex) => { console.log("Failed to get audio track " + ex); }); }, ... }; ... return instance; } |
8.4. Удаление отображаемой аудио дорожки
removeAudioTrack() code
Code Block | ||||
---|---|---|---|---|
| ||||
const createOneToOneParticipantModel = function (userId, nickname, participantView, remoteTrackFactory, abrFactory, displayOptions) {
const instance = {
...
removeAudioTrack: function (track) {
if (!this.audioTracks.delete(track.mid)) {
return
}
participantView.removeAudioTrack(track);
const remoteTrack = this.remoteAudioTracks.get(track.mid);
if (remoteTrack) {
this.remoteAudioTracks.delete(track.mid);
remoteTrack.dispose();
}
},
...
};
...
return instance;
} |
8.5. Запрос видео дорожки для отображения
requestVideoTrack() code
Code Block | ||||
---|---|---|---|---|
| ||||
const createOneToOneParticipantModel = function (userId, nickname, participantView, remoteTrackFactory, abrFactory, displayOptions) {
const instance = {
...
requestVideoTrack: async function (track, remoteTrack) {
return new Promise((resolve, reject) => {
if (!remoteTrack || !track) {
reject(new Error("Remote and local track must be defined"));
return;
}
const self = this;
remoteTrack.demandTrack(track.id).then(() => {
if (!self.videoTracks.get(track.mid)) {
reject(new Error("Video track already removed from model"));
return;
}
let abrManager = self.abrManagers.get(track.id);
if (abrManager) {
abrManager.clearQualityState();
} else if (abrFactory) {
abrManager = abrFactory.createAbrManager();
self.abrManagers.set(track.id, abrManager);
}
if (abrManager) {
abrManager.setTrack(remoteTrack);
abrManager.stop();
if (track.quality.length > 0) {
participantView.addQuality(track, "Auto", true, async () => {
const manager = self.abrManagers.get(track.id);
if (!manager) {
return;
}
manager.start();
manager.setAuto();
participantView.pickQuality(track, "Auto");
});
if (displayOptions.autoAbr) {
abrManager.setAuto();
abrManager.start();
participantView.pickQuality(track, "Auto");
}
}
}
for (const qualityDescriptor of track.quality) {
if (abrManager) {
abrManager.addQuality(qualityDescriptor.quality);
abrManager.setQualityAvailable(qualityDescriptor.quality, qualityDescriptor.available);
}
if (displayOptions.quality) {
participantView.addQuality(track, qualityDescriptor.quality, qualityDescriptor.available, async () => {
const manager = self.abrManagers.get(track.id);
if (manager) {
manager.setManual();
manager.setQuality(qualityDescriptor.quality);
}
return self.pickQuality(track, qualityDescriptor.quality);
});
}
}
self.remoteVideoTracks.delete(track.mid);
self.remoteVideoTracks.set(track.mid, remoteTrack);
resolve();
}, (ex) => {
reject(ex);
});
});
},
...
};
...
return instance;
} |
8.6. Обновление информации о доступных качествах потока
updateQualityInfo() code
Code Block | ||||
---|---|---|---|---|
| ||||
const createOneToOneParticipantModel = function (userId, nickname, participantView, remoteTrackFactory, abrFactory, displayOptions) {
const instance = {
...
updateQualityInfo: function (remoteTracks) {
for (const remoteTrackQuality of remoteTracks) {
const track = this.videoTracks.get(remoteTrackQuality.mid);
if (!track) {
continue;
}
if (!this.remoteVideoTracks.get(track.mid)) {
// update model and return, view not changed
for (const remoteQualityInfo of remoteTrackQuality.quality) {
const quality = track.quality.find((q) => q.quality === remoteQualityInfo.quality);
if (quality) {
quality.available = remoteQualityInfo.available;
} else {
track.quality.push(remoteQualityInfo);
}
}
return;
}
let abrManager = this.abrManagers.get(track.id);
if (abrManager && track.quality.length === 0 && remoteTrackQuality.quality.length > 0) {
const self = this;
participantView.addQuality(track, "Auto", true, async () => {
const manager = self.abrManagers.get(track.id);
if (!manager) {
return;
}
manager.start();
manager.setAuto();
participantView.pickQuality(track, "Auto");
})
if (displayOptions.autoAbr) {
abrManager.setAuto();
abrManager.start();
participantView.pickQuality(track, "Auto");
}
}
for (const remoteQualityInfo of remoteTrackQuality.quality) {
const localQuality = track.quality.find((q) => q.quality === remoteQualityInfo.quality);
if (localQuality) {
localQuality.available = remoteQualityInfo.available;
if (abrManager) {
abrManager.setQualityAvailable(remoteQualityInfo.quality, remoteQualityInfo.available);
}
if (displayOptions.quality) {
participantView.updateQuality(track, localQuality.quality, localQuality.available);
}
} else {
track.quality.push(remoteQualityInfo);
if (abrManager) {
abrManager.addQuality(remoteQualityInfo.quality);
abrManager.setQualityAvailable(remoteQualityInfo.quality, remoteQualityInfo.available)
}
if (displayOptions.quality) {
const self = this;
participantView.addQuality(track, remoteQualityInfo.quality, remoteQualityInfo.available, async () => {
const manager = self.abrManagers.get(track.id);
if (manager) {
manager.setManual();
manager.setQuality(remoteQualityInfo.quality);
}
return self.pickQuality(track, remoteQualityInfo.quality);
});
}
}
}
}
},
...
};
...
return instance;
} |
8.7. Выбор качества для проигрывания
pickQuality() code
Code Block | ||||
---|---|---|---|---|
| ||||
const createOneToOneParticipantModel = function (userId, nickname, participantView, remoteTrackFactory, abrFactory, displayOptions) {
const instance = {
...
pickQuality: async function (track, qualityName) {
let remoteVideoTrack = this.remoteVideoTracks.get(track.mid);
if (remoteVideoTrack) {
return remoteVideoTrack.setPreferredQuality(qualityName).then(() => {
participantView.pickQuality(track, qualityName);
});
}
},
...
};
...
return instance;
} |
8.9. Заглушить видео участника
muteVideo() code
Code Block | ||||
---|---|---|---|---|
| ||||
const createOneToOneParticipantModel = function (userId, nickname, participantView, remoteTrackFactory, abrFactory, displayOptions) {
const instance = {
...
muteVideo: async function (track) {
const remoteTrack = this.remoteVideoTracks.get(track.mid);
if (remoteTrack) {
return remoteTrack.mute();
} else {
return new Promise((resolve, reject) => {
reject(new Error("Remote track not defined"));
});
}
},
...
};
...
return instance;
} |
8.10. Возобновить видео участника
unmuteVideo() code
Code Block | ||||
---|---|---|---|---|
| ||||
const createOneToOneParticipantModel = function (userId, nickname, participantView, remoteTrackFactory, abrFactory, displayOptions) {
const instance = {
...
unmuteVideo: async function (track) {
const remoteTrack = this.remoteVideoTracks.get(track.mid);
if (remoteTrack) {
return remoteTrack.unmute();
} else {
return new Promise((resolve, reject) => {
reject(new Error("Remote track not defined"));
});
}
}
};
...
return instance;
} |
8.11. Завершение работы
dispose() code
Code Block | ||||
---|---|---|---|---|
| ||||
const createOneToOneParticipantModel = function (userId, nickname, participantView, remoteTrackFactory, abrFactory, displayOptions) {
const instance = {
...
dispose: async function () {
this.disposed = true;
participantView.dispose();
this.remoteVideoTracks.forEach((track, id) => {
track.dispose();
})
this.remoteVideoTracks.clear();
this.remoteAudioTracks.forEach((track, id) => {
track.dispose();
})
this.remoteAudioTracks.clear();
this.abrManagers.forEach((abrManager, id) => {
abrManager.stop();
})
this.abrManagers.clear();
},
};
...
return instance;
} |
9. Создание объекта модели участника в комнате со многими участниками
createOneToManyParticipantModel() code
Code Block | ||||
---|---|---|---|---|
| ||||
const createOneToManyParticipantModel = function (userId, nickname, participantView, remoteTrackFactory, abrFactory, displayOptions) {
...
const instance = {
userId: userId,
nickname: nickname,
videoEnabled: false,
currentTrack: null,
remoteVideoTrack: null,
remoteAudioTracks: new Map(),
audioTracks: new Map(),
videoTracks: new Map(),
abr: null,
disposed: false,
dispose: async function () {
...
},
addVideoTrack: function (track) {
...
},
removeVideoTrack: function (track) {
...
},
addAudioTrack: function (track) {
...
},
removeAudioTrack: function (track) {
...
},
setUserId: function (userId) {
...
},
setNickname: function (nickname) {
...
},
updateQualityInfo: function (remoteTracks) {
...
},
requestVideoTrack: async function (track, remoteTrack) {
...
},
pickQuality: async function (track, qualityName) {
...
},
muteVideo: async function (track) {
...
},
unmuteVideo: async function (track) {
...
}
};
instance.setUserId(userId);
instance.setNickname(nickname);
if (abrFactory) {
instance.abr = abrFactory.createAbrManager();
}
return instance;
} |
9.1. Добавление видео дорожки для отображения
addVideoTrack() code
Code Block | ||||
---|---|---|---|---|
| ||||
const createOneToManyParticipantModel = function (userId, nickname, participantView, remoteTrackFactory, abrFactory, displayOptions) {
...
const instance = {
...
addVideoTrack: function (track) {
this.videoTracks.set(track.mid, track);
if (!track.quality) {
track.quality = [];
}
const self = this;
participantView.addVideoTrack(track, () => {
if (self.disposed) {
return new Promise((resolve, reject) => {
reject(new Error("Model disposed"));
});
}
if (self.remoteVideoTrack) {
return new Promise((resolve, reject) => {
self.requestVideoTrack(track, self.remoteVideoTrack).then(() => {
resolve();
}, (ex) => {
reject(ex);
});
});
} else {
return new Promise((resolve, reject) => {
reject(new Error("Remote track is null"));
requestTrackAndPick(self, track);
});
}
});
requestTrackAndPick(this, track);
},
...
};
...
return instance;
} |
9.2. Удаление отображаемой видео дорожки
removeVideoTrack() code
Code Block | ||||
---|---|---|---|---|
| ||||
const createOneToManyParticipantModel = function (userId, nickname, participantView, remoteTrackFactory, abrFactory, displayOptions) {
...
const instance = {
...
removeVideoTrack: function (track) {
this.videoTracks.delete(track.mid);
participantView.removeVideoTrack(track);
if (this.currentTrack && this.currentTrack.mid === track.mid) {
repickTrack(this, track);
}
},
...
};
...
return instance;
} |
9.3. Добавление аудио дорожки для отображения
addAudioTrack() code
Code Block | ||||
---|---|---|---|---|
| ||||
const createOneToManyParticipantModel = function (userId, nickname, participantView, remoteTrackFactory, abrFactory, displayOptions) {
...
const instance = {
...
addAudioTrack: async function (track) {
this.audioTracks.set(track.mid, track);
const self = this;
remoteTrackFactory.getAudioTrack().then((remoteTrack) => {
if (!remoteTrack) {
return;
}
if (self.disposed || !self.audioTracks.get(track.mid)) {
remoteTrack.dispose();
return;
}
this.remoteAudioTracks.set(track.mid, remoteTrack);
remoteTrack.demandTrack(track.id).then(() => {
if (!self.audioTracks.get(track.mid)) {
remoteTrack.dispose();
self.remoteAudioTracks.delete(track.mid);
return;
}
participantView.addAudioTrack(track, remoteTrack.track, displayOptions.showAudio);
}, (ex) => {
console.log("Failed demand track " + ex);
remoteTrack.dispose();
self.remoteAudioTracks.delete(track.mid);
});
}, (ex) => {
console.log("Failed to get audio track " + ex);
});
},
...
};
...
return instance;
} |
9.4. Удаление отображаемой аудио дорожки
removeAudioTrack() code
Code Block | ||||
---|---|---|---|---|
| ||||
const createOneToManyParticipantModel = function (userId, nickname, participantView, remoteTrackFactory, abrFactory, displayOptions) {
...
const instance = {
...
removeAudioTrack: function (track) {
if (!this.audioTracks.delete(track.mid)) {
return
}
participantView.removeAudioTrack(track);
const remoteTrack = this.remoteAudioTracks.get(track.mid);
if (remoteTrack) {
this.remoteAudioTracks.delete(track.mid);
remoteTrack.dispose();
}
},
...
};
...
return instance;
} |
9.5. Запрос видео дорожки для отображения
requestVideoTrack() code
Code Block | ||||
---|---|---|---|---|
| ||||
const createOneToManyParticipantModel = function (userId, nickname, participantView, remoteTrackFactory, abrFactory, displayOptions) {
...
const instance = {
...
requestVideoTrack: async function (track, remoteTrack) {
return new Promise((resolve, reject) => {
if (!remoteTrack || !track) {
reject(new Error("Remote and local track must be defined"));
return;
}
const self = this;
remoteTrack.demandTrack(track.id).then(() => {
// channels reordering case, must be removed after channels unification
if (!self.videoTracks.get(track.mid)) {
reject(new Error("Video track already removed from model"));
return;
}
self.currentTrack = track;
participantView.clearQualityState(track);
if (self.abr) {
self.abr.stop();
self.abr.clearQualityState();
self.abr.setTrack(remoteTrack);
if (track.quality.length > 0) {
participantView.addQuality(track, "Auto", true, async () => {
if (!self.abr) {
return;
}
self.abr.start();
self.abr.setAuto();
participantView.pickQuality(track, "Auto");
})
}
if (displayOptions.autoAbr) {
self.abr.setAuto();
self.abr.start();
participantView.pickQuality(track, "Auto");
}
}
for (const qualityDescriptor of track.quality) {
if (self.abr) {
self.abr.addQuality(qualityDescriptor.quality);
self.abr.setQualityAvailable(qualityDescriptor.quality, qualityDescriptor.available);
}
if (displayOptions.quality) {
participantView.addQuality(track, qualityDescriptor.quality, qualityDescriptor.available, async () => {
if (self.abr) {
self.abr.setManual();
self.abr.setQuality(qualityDescriptor.quality);
}
return self.pickQuality(track, qualityDescriptor.quality);
});
}
}
resolve();
}, (ex) => reject(ex));
});
},
...
};
...
return instance;
} |