Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

2.15. Сбросить таймер использования текущего качества

2. Обработка событий комнаты

2.1. ADD_TRACKS

initRemoteDisplayabr.stopKeeping() codeЗдесь:

  • новый участник добавляется в список
  • добавляется информация о качестве потоков
  • создается элемент для отображения видео и аудио потоков участника
Code Block
languagejs
themeRDark
const abrManagerFactory = function room.on(constants.SFU_ROOM_EVENT.ADD_TRACKS, function(e(room, abrOptions) {
        console.log("Received ADD_TRACKS");return {
        let participant = remoteParticipants[e.info.nickName];
        if (!participantcreateAbrManager: function () {
            let participantabr = {};
            participant.nickName   = e..info.nickName;
             participant.tracks = [];
  stopKeeping: function () {
              participant.displays = [];
    if (abr.keepGoodTimer) {
      remoteParticipants[participant.nickName] = participant;
        }
        participant.tracks.push.apply(participant.tracks, e.info.infoclearTimeout(abr.keepGoodTimer);
        for (const pTrack of e.info.info) {
            let createDisplayabr.keepGoodTimer = truenull;
            for (let i = 0; i < participant.displays.length; i++) { }
                let},
  display = participant.displays[i];
            ...
    if (pTrack.type === "VIDEO") {
    }
            return abr;
   if (display.hasVideo()) {
   }
    }
}

2.16. Переключиться на более высокое качество на заданное время

abr.tryUpper() code

Code Block
languagejs
themeRDark
const abrManagerFactory = function (room, abrOptions) {
    return {
      continue;
  createAbrManager: function () {
            let abr = }{
                    display.videoMid = pTrack.mid;...
                tryUpper: function   display.setTrackInfo(pTrack); {
                    let createDisplayquality = falseabr.getUpperQuality(abr.currentQualityName);
                    break;
       if (abr.tryUpperTimeout && !abr.tryUpperTimer && quality) {
         } else if (pTrack.type === "AUDIO") {
         abr.tryUpperTimer           if (display.hasAudio())= setTimeout(() => {
                        continue;
    abr.setQualityGood(quality.name, true);
               }
             abr.stopTrying();
       display.audioMid = pTrack.mid;
                    display.setTrackInfo(pTrack}, abr.tryUpperTimeout);
                    createDisplay = false;}
                    break;},
                }...
            }
            if (!createDisplay) {return abr;
        }
        continue;
    }
}

2.17. Остановить таймер тестирования более высокого качества

abr.stopTrying() code

Code Block
languagejs
themeRDark
const abrManagerFactory = function (room, abrOptions) {
    return {
        }createAbrManager: function () {
            let displayabr = createRemoteDisplay(participant.nickName, participant.nickName, mainDiv, displayOptions);
 {
                participant..displays.push(display);

               if (pTrack.type === "VIDEO" stopTrying: function () {
                display.videoMid = pTrack.mid;
  if (abr.tryUpperTimer) {
            display.setTrackInfo(pTrack);
            } else if (pTrack.type === "AUDIO") {
clearTimeout(abr.tryUpperTimer);
                        displayabr.audioMidtryUpperTimer = pTrack.midnull;
                 display.setTrackInfo(pTrack);
   }
                },
        }
        ...
    });        }
            return abr;
        }
    }
}

2.

...

18. Переключиться на указанное качество

abr.setQuality() code

Здесь:

  • удаляются элементы, в которых проигрывались потоки
  • информация о потоках удаляется из списка
Code Block
languagejs
themeRDark
const abrManagerFactory = function room.on(constants.SFU_ROOM_EVENT.ADD_TRACKS, function(e(room, abrOptions) {
    return {
   ...
     }).on(constants.SFU_ROOM_EVENT.REMOVE_TRACKS,createAbrManager: function (e) {
            console.log("Received REMOVE_TRACKS");let abr = {
        const      participant = remoteParticipants[e..info.nickName];
        if  (!participant) {
     setQuality: async function (name) {
   return;
        }
        for console.log(const"set rTrack of e.info.info) {quality name");
            for (let i = 0; i < participant.tracks.length; i++) {
                if (rTrack.mid === participant.tracks[i].mid) { // Pause switching until a new quality is received
                    participantabr.tracks.splice(i, 1pause();
                    breakabr.currentQualityName = name;
                }
    abr.track.setPreferredQuality(abr.currentQualityName);
        }
         }
   for (let i = 0; i < participant.displays.length; i++) {}
                let found = falsereturn abr;
        }
        const display = participant.displays[i];
                if (display.audioMid === rTrack.mid) {}
}

3. Создание объекта для управления комнатой

createDefaultMeetingController() code

Code Block
languagejs
themeRDark
const createDefaultMeetingController = function (room, meetingModel) {
    ...

    return {
        stop: stop
      display.setAudio(null);
      }
}

3.1. Обработка события PARTICIPANT_LIST

createDefaultMeetingController() code

Code Block
languagejs
themeRDark
const createDefaultMeetingController = function (room, meetingModel) {
    const constants = SFU.constants;
       found = true;room.on(constants.SFU_ROOM_EVENT.PARTICIPANT_LIST, async function (e) {
        for (const idName      } else if (display.videoMid === rTrack.midof e.participants) {
                    display.setVideo(nullmeetingModel.addParticipant(idName.userId, idName.name);
        }
        ...
    found = true});
    ...
}

3.2. Обработка события JOINED

createDefaultMeetingController() code

Code Block
languagejs
themeRDark
const createDefaultMeetingController = function (room, meetingModel) {
    const constants }
 = SFU.constants;
    room.on(constants.SFU_ROOM_EVENT.PARTICIPANT_LIST, async          if (foundfunction (e) {
        ...
            if (!display.hasAudio() && !display.hasVideo())}).on(constants.SFU_ROOM_EVENT.JOINED, async function (e) {
                        display.dispose(meetingModel.addParticipant(e.userId, e.name);
        ...
    });
            participant.displays.splice(i, 1);
       ...
}

3.3. Обработка события LEFT

createDefaultMeetingController() code

Code Block
languagejs
themeRDark
const createDefaultMeetingController = function (room, meetingModel) {
    const constants        }= SFU.constants;
    room.on(constants.SFU_ROOM_EVENT.PARTICIPANT_LIST, async function (e) {
            break;...
    }).on(constants.SFU_ROOM_EVENT.LEFT, function (e) {
         }meetingModel.removeParticipant(e.userId);
            }
    ...
    });
        ...
    });

2.3. LEFT

...

}

3.4. Обработка события ADD_TRACKS

createDefaultMeetingController() code

Здесь:

  • участник удаляется из списка
  • удаляются элементы, в которых проигрывались потоки
Code Block
languagejs
themeRDark
const createDefaultMeetingController = function (room, meetingModel) {
    const constants = SFU.constants;
    room.on(constants.SFU_ROOM_EVENT.ADDPARTICIPANT_TRACKSLIST, async function (e) {
        ...
    }).on(constants.SFU_ROOM_EVENT.LEFTADD_TRACKS, async function (e) {
        consolemeetingModel.log("Received LEFT"addTracks(e.info.userId, e.info.info);
        let participant = remoteParticipants[e.name];...
        if (!participant) {});
            return;...
}

3.4. Обработка события REMOVE_TRACKS

createDefaultMeetingController() code

Code Block
languagejs
themeRDark
const createDefaultMeetingController = function (room, meetingModel) {
    const constants   }= SFU.constants;
    room.on(constants.SFU_ROOM_EVENT.PARTICIPANT_LIST, async function  participant.displays.forEach(function(display)(e) {
        ...
    display.dispose();}).on(constants.SFU_ROOM_EVENT.REMOVE_TRACKS, async function (e) {
        })meetingModel.removeTracks(e.info.userId, e.info.info);
        delete remoteParticipants[e.name];
...
     });
    ...
    });

...

3.4. Обработка события TRACK_QUALITY_STATE

initRemoteDisplaycreateDefaultMeetingController() code

Здесь:

  • обновляется информация о качествах потоков
Code Block
languagejs
themeRDark
const createDefaultMeetingController = function (room, meetingModel) {
    const constants = SFU.constants;
    room.on(constants.SFU_ROOM_EVENT.ADDPARTICIPANT_TRACKSLIST, async function (e) {
        ... 
    }).on(constants.SFU_ROOM_EVENT.TRACK_QUALITY_STATE, async function (e) {
        consolemeetingModel.log("Received track quality state"updateQualityInfo(e.info.userId, e.info.tracks);
        const participant = remoteParticipants[e.info.nickName];...
        if (!participant) {});
            return;
        }

        for (const rTrack of e.info.tracks) {
            const mid = rTrack.mid...
}

3.5. Обработка события ENDED

createDefaultMeetingController() code

Code Block
languagejs
themeRDark
const createDefaultMeetingController = function (room, meetingModel) {
    const constants = SFU.constants;
            for (let i = 0; i < participant.displays.length; i++) {
                const display = participant.displays[i];
                if (display.videoMid === mid) {
                    display.updateQualityInfo(rTrack.quality);
                    break;
                }
            }
        }
    });

3. Создание элементов для отображения потоков участников

3.1. Создание контейнера

createRemoteDisplay() code

Здесь:

  • настраиваются параметры отображения
  • создается контейнер для потоков участника
  • создается контейнер для конкретного потока
  • создается контейнер для кнопок переключения качества
Code Block
languagejs
themeRDark
        const cell = document.createElement("div");
        cell.setAttribute("class", "text-center");
        cell.id = id;
        mainDiv.appendChild(cell);
        let publisherNameDisplay;
        let currentQualityDisplay;
        let videoTypeDisplay;
        let abrQualityCheckPeriod = ABR_QUALITY_CHECK_PERIOD;
        let abrKeepOnGoodQuality = ABR_KEEP_ON_QUALITY;
        let abrTryForUpperQuality = ABR_TRY_UPPER_QUALITY;
        if (displayOptions.abrQualityCheckPeriod !== undefined) {
            abrQualityCheckPeriod = displayOptions.abrQualityCheckPeriod;
        }
        if (displayOptions.abrKeepOnGoodQuality !== undefined) {
            abrKeepOnGoodQuality = displayOptions.abrKeepOnGoodQuality;
        }
        if (displayOptions.abrTryForUpperQuality !== undefined) {
            abrTryForUpperQuality = displayOptions.abrTryForUpperQuality;
        }
        if (!displayOptions.abr) {
            abrQualityCheckPeriod = 0;
            abrKeepOnGoodQuality = 0;
            abrTryForUpperQuality = 0;
        }
        if (displayOptions.publisher) {
            publisherNameDisplay = createInfoDisplay(cell, "Published by: " + name);
        }
        if (displayOptions.quality) {
            currentQualityDisplay = createInfoDisplay(cell, "");
        }
        if (displayOptions.type) {
            videoTypeDisplay = createInfoDisplay(cell, "");
        }
        const qualitySwitchDisplay = createInfoDisplay(cell, "");

        let qualityDivs = [];
        let contentType = "";

        const rootDisplay = createContainer(cell);
        const streamDisplay = createContainer(rootDisplay);
        const audioDisplay = createContainer(rootDisplay);
        const audioTypeDisplay = createInfoDisplay(audioDisplay);
        const audioTrackDisplay = createContainer(audioDisplay);
        const audioStateButton = AudioStateButton();

        hideItem(streamDisplay);
        hideItem(audioDisplay);
        hideItem(publisherNameDisplay);
        hideItem(currentQualityDisplay);
        hideItem(videoTypeDisplay);
        hideItem(qualitySwitchDisplay);

3.2. Инициализация ABR

createRemoteDisplay() code

Здесь задаются параметры качества получения потока от сервера, по которым отображение будет переключаться на более высокое или более низкое качество

Code Block
languagejs
themeRDark
        const abr = ABR(abrQualityCheckPeriod, [
            {parameter: "nackCount", maxLeap: 10},
            {parameter: "freezeCount", maxLeap: 10},
            {parameter: "packetsLost", maxLeap: 10}
        ], abrKeepOnGoodQuality, abrTryForUpperQuality);

3.3. Добавление видео элемента

setVideo() code

Code Block
languagejs
themeRDark
            setVideo: function(stream) {
                if (video) {
                    video.remove();
                }

                if (stream == null) {
                    video = null;
                    this.videoMid = undefined;
                    qualityDivs.forEach(function(div) {
                        div.remove();
                    });
                    qualityDivs = [];
                    return;
                }
                showItem(streamDisplay);
                video = document.createElement("video");
                video.controls = "controls";
                video.muted = true;
                video.autoplay = true;
                if (Browser().isSafariWebRTC()) {
                    video.setAttribute("playsinline", "");
                    video.setAttribute("webkit-playsinline", "");
                    this.setWebkitEventHandlers(video);
                } else {
                    this.setEventHandlers(video);
                }
                streamDisplay.appendChild(video);
                video.srcObject = stream;
                this.setResizeHandler(video);
                abr.start();
            },

3.4. Добавление аудио элемента

setAudio() code

Code Block
languagejs
themeRDark
            setAudio: function(stream) {
                if (audio) {
                    audio.remove();
                }
                if (!stream) {
                    audio = null;
                    this.audioMid = undefined;
                    return;
                }
                showItem(audioDisplay);
                audio = document.createElement("audio");
                audio.controls = "controls";
                audio.muted = true;
                audio.autoplay = true;
                if (Browser().isSafariWebRTC()) {
                    audio.setAttribute("playsinline", "");
                    audio.setAttribute("webkit-playsinline", "");
                    this.setWebkitEventHandlers(audio);
                } else {
                    this.setEventHandlers(audio);
                }
                audioTrackDisplay.appendChild(audio);
                audioStateButton.makeButton(audioTypeDisplay, audio);
                audio.srcObject = stream;
                audio.onloadedmetadata = function (e) {
                    audio.play().then(function() {
                        if (Browser().isSafariWebRTC() && Browser().isiOS()) {
                            console.warn("Audio track should be manually unmuted in iOS Safari");
                        } else {
                            audio.muted = false;
                            audioStateButton.setButtonState();
                        }
                    });
                };
            },

3.5. Настройка обработчиков событий для аудио и видео элементов

setResizeHandler(), setEventHandlers(), setWebkitEventHandlers() code

Code Block
languagejs
themeRDark
            setEventHandlers: function(video) {
                // Ignore play/pause button
                video.addEventListener("pause", function () {
                    console.log("Media paused by click, continue...");
                    video.play();
                });
            },
            setWebkitEventHandlers: function(video) {
                let needRestart = false;
                let isFullscreen = false;
                // Use webkitbeginfullscreen event to detect full screen mode in iOS Safari
                video.addEventListener("webkitbeginfullscreen", function () {
                    isFullscreen = true;
                });                
                video.addEventListener("pause", function () {
                    if (needRestart) {
                        console.log("Media paused after fullscreen, continue...");
                        video.play();
                        needRestart = false;
                    } else {
                        console.log("Media paused by click, continue...");
                        video.play();
                    }
                });
                video.addEventListener("webkitendfullscreen", function () {
                    video.play();
                    needRestart = true;
                    isFullscreen = false;
                });
            },

3.6. Добавление информации о дорожке в ABR

setVideoABRTrack() code

Code Block
languagejs
themeRDark
            setVideoABRTrack: function(track) {
                abr.setTrack(track);
            },

3.7. Настройка переключения качества

setTrackInfo() code

Здесь:

  • настраиваются кнопки переключения качества
  • информация о заявленных качествах публикации добавляется в ABR
  • скрываются или отображаются элементы для показа текущего качества и источника видео/аудио
Code Block
languagejs
themeRDark
            setTrackInfo: function(trackInfo) {
                if (trackInfo) { 
                    if (trackInfo.quality) {
                        showItem(qualitySwitchDisplay);
                        if (abr.isEnabled()) {
                            const autoDiv = createQualityButton("Auto", qualityDivs, qualitySwitchDisplay);
                            autoDiv.style.color = QUALITY_COLORS.SELECTED;
                            autoDiv.addEventListener('click', function() {
                                setQualityButtonsColor(qualityDivs);
                                autoDiv.style.color = QUALITY_COLORS.SELECTED;
                                abr.setAuto();
                            });
                        }
                        for (let i = 0; i < trackInfo.quality.length; i++) {
                            abr.addQuality(trackInfo.quality[i]);
                            const qualityDiv = createQualityButton(trackInfo.quality[i], qualityDivs, qualitySwitchDisplay);
                            qualityDiv.addEventListener('click', function() {
                                console.log("Clicked on quality " + trackInfo.quality[i] + " trackId " + trackInfo.id);
                                if (qualityDiv.style.color === QUALITY_COLORS.UNAVAILABLE) {
                                    return;
                                }
                                setQualityButtonsColor(qualityDivs);
                                qualityDiv.style.color = QUALITY_COLORS.SELECTED;
                                abr.setManual();
                                abr.setQuality(trackInfo.quality[i]);
                            });
                        }
                    } else {
                        hideItem(qualitySwitchDisplay);
                    }
                    if (trackInfo.type) {
                        contentType = trackInfo.contentType || "";
                        if (trackInfo.type == "VIDEO" && displayOptions.type && contentType !== "") {
                            showItem(videoTypeDisplay);room.on(constants.SFU_ROOM_EVENT.PARTICIPANT_LIST, async function (e) {
        ...
    }).on(constants.SFU_ROOM_EVENT.ENDED, function (e) {
             videoTypeDisplay.innerHTML = contentTypemeetingModel.end();
    });
    ...
}

3.6. Остановка комнаты

createDefaultMeetingController() code

Code Block
languagejs
themeRDark
const createDefaultMeetingController = function (room, meetingModel) {
          }...
    const stop = function () {
        meetingModel.end();
    };

   if (trackInfo.type == "AUDIO") return {
        stop: stop
    }
}

4. Создание модели комнаты

createDefaultMeetingModel() code

Code Block
languagejs
themeRDark
const createDefaultMeetingModel = function (meetingView, participantFactory, displayOptions, abrFactory) {
    ...
}

4.1. Добавление участника

addParticipant() code

Code Block
languagejs
themeRDark
const createDefaultMeetingModel = audioStateButton.setContentType(contentType);
function (meetingView, participantFactory, displayOptions, abrFactory) {
    return {
        ...
        addParticipant: function (userId, participantName) }{
            if (this.participants.get(userId)) {
            }
    return;
            }
            },

3.8. Добавление информации о доступных качествах видео в ABR

updateQualityInfo() code

Code Block
languagejs
themeRDark
const [participantModel, participantView, participant] = participantFactory.createParticipant(userId, participantName, displayOptions, abrFactory);
            updateQualityInfo: function(videoQuality) {this.participants.set(userId, participant);
            meetingView.addParticipant(userId,    showItem(qualitySwitchDisplayparticipantName, participantView.rootDiv);
        },
        for (const qualityInfo of videoQuality) {
         ...
    }
}

4.2. Удаление участника

removeParticipant() code

Code Block
languagejs
themeRDark
const createDefaultMeetingModel = function (meetingView, participantFactory, displayOptions, abrFactory) {
    return {
      let qualityColor = QUALITY_COLORS.UNAVAILABLE; ...
        removeParticipant:            if (qualityInfo.available === truefunction (userId) {
                        qualityColorconst participant = QUALITY_COLORS.AVAILABLEthis.participants.get(userId);
            if        }(participant) {
                this.participants.delete(userId);
    for (const qualityDiv of qualityDivs) {
       meetingView.removeParticipant(userId);
                 if (qualityDiv.innerText === qualityInfo.quality){participant.dispose();
            }
        },
        qualityDiv..style.color = qualityColor;
    }
}

4.3. Переименование участника

renameParticipant() code

Code Block
languagejs
themeRDark
const createDefaultMeetingModel = function (meetingView, participantFactory, displayOptions, abrFactory) {
    return {
        ...
   break;
     renameParticipant: function (userId, newNickname) {
            const participant  }= this.participants.get(userId);
            if        }(participant) {
                    abr.setQualityAvailable(qualityInfo.quality, qualityInfo.availableparticipant.setNickname(newNickname);
            }
        },
        ...
    }
},

...

4.

...

4. Добавление треков участника для отображения

addTracks() code

Code Block
languagejs
themeRDark
const createDefaultMeetingModel = function (meetingView, participantFactory,       dispose: function(displayOptions, abrFactory) {
    return {
           abr.stop();...
        addTracks: function (userId,      cell.remove();tracks) {
            },

4. Подписка на добавление дорожек в WebRTC соединение

PeerConnection.ontrack(), setAudio(), setVideo(), setVideoABRTrack() code

Здесь:

  • при получении видео или аудио потока, добавляется элемент для его проигрывания
Code Block
languagejs
themeRDark
       const peerConnection.ontrackparticipant = ({transceiver}) =>this.participants.get(userId);
            if (!participant) {
        let rParticipant        return;
           console.log("Attach remote track " + transceiver.receiver.track.id + " kind " + transceiver.receiver.track.kind + " mid " + transceiver.mid); }

            for (const track of tracks) {
        for (const [nickName, participant] of Object.entries(remoteParticipants))        if (track.type === "VIDEO") {
            for (const pTrack of        participant.tracksaddVideoTrack(track);
 {
                console.log("Participant " + participant.nickName + " track " + pTrack.id + " mid " + pTrack.mid} else if (track.type === "AUDIO") {
                    participant.addAudioTrack(track);
                if (pTrack.mid === transceiver.mid) {
}
            }
        },
        ...
    }
}

4.5. Удаление отображаемых треков участника

removeTracks() code

Code Block
languagejs
themeRDark
const createDefaultMeetingModel rParticipant = participant;
     function (meetingView, participantFactory, displayOptions, abrFactory) {
    return {
          break;...
        removeTracks: function (userId, tracks) {
    }
        const participant   }= this.participants.get(userId);
            if (rParticipant!participant) {
                breakreturn;
            }
        }
        if (rParticipant) {
            for (const displaytrack of rParticipant.displays) {
                if (transceiver.receiver.track.kind === "video"tracks) {
                    if (displaytrack.videoMidtype === transceiver.mid"VIDEO") {
                    participant.removeVideoTrack(track);
           let stream = new MediaStream();
        } else if (track.type === "AUDIO") {
                     streamparticipant.addTrackremoveAudioTrack(transceiver.receiver.track);
                }
            display.setVideoABRTrack(transceiver.receiver.track);}
        },
        ...
    }
}

4.6. Обновление информации о качестве треков участника

updateQualityInfo() code

Code Block
languagejs
themeRDark
const createDefaultMeetingModel = function display.setVideo(stream);(meetingView, participantFactory, displayOptions, abrFactory) {
    return {
        ...
        updateQualityInfo: function (userId, tracksInfo) break;{
            const participant = this.participants.get(userId);
     }
       if (!participant) {
          } else if (transceiver.receiver.track.kind === "audio") { return;
            }
        if  (display.audioMid === transceiverparticipant.mid) {updateQualityInfo(tracksInfo);
        },
        ...
        let stream}
}

4.7. Завершение отображения участников при остановке комнаты

end() code

Code Block
languagejs
themeRDark
const createDefaultMeetingModel = newfunction MediaStream();
    meetingView, participantFactory, displayOptions, abrFactory) {
    return {
               stream.addTrack(transceiver.receiver.track);
        end: function () {
             displayconsole.setAudio(stream);
      log("Meeting " + this.meetingName + " ended")
                  breakmeetingView.end();
            this.participants.forEach((participant, id)       }=> {
                }participant.dispose();
            });
        } else {
            console.warn("Failed to find participant for track " + transceiverthis.receiver.track.idparticipants.clear();
        },
        ...
    }
}

5. Остановка воспроизведения

...

4.8. Переименование комнаты

setMeetingName() code

Code Block
languagejs
themeRDark
const createDefaultMeetingModel = function const stop = function((meetingView, participantFactory, displayOptions, abrFactory) {
    return {
   for (const [nickName, participant] of Object.entries(remoteParticipants)) {...
        setMeetingName: function   participant.displays.forEach(function(display)(id) {
            this.meetingName    display.dispose()= id;
            }meetingView.setMeetingName(id);
            delete remoteParticipants[nickName];
    }
    }
    }