Versions Compared

Key

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

...

Для анализа исходного кода возьмем версию модуля display.js, которая находится здесь и доступна в сборке 1.0.1.36

Захват и отображение локального видео

1. Инициализация

initLocalDisplay() code

Функция initLocalDisplay() возвращает объект для работы с HTML5 элементами захвата и отображения локального видео

...

2.1. Добавления аудио дорожки к HTML5 элементу

add() code

Здесь:

  • добавляется аудио дорожка к video элементу
  • создается обработчик события onended для аудио дорожкивидео дорожки
  • добавляется обработчик нажатия кнопки включения/отключения аудио
Code Block
languagejs
themeRDark
        if (stream.getAudioTracks().length > 0) {
            let videoElement = getAudioContainer();
            if (videoElement) {
                let track = stream.getAudioTracks()[0];
                videoElement.video.srcObject.addTrack(track);
                videoElement.audioStateDisplay.innerHTML = audioStateText(stream) + " " + type;
                trackvideoElement.audioStateDisplay.addEventListener("endedclick", function() {
                    onMuteClick(videoElement.video.srcObject.removeTrack(trackaudioStateDisplay, stream, type);
                    videoElement.audioStateDisplay.innerHTML = "No audio"});
                track.addEventListener("ended",    //check video element has no tracks leftfunction() {
                    videoElement.video.srcObject.removeTrack(track);
                    videoElement.audioStateDisplay.innerHTML = "No audio";
                    //check video element has no tracks left
                    for (const [key, vTrack] of Object.entries(videoElement.video.srcObject.getTracks())) {
                        if (vTrack.readyState !== "ended") {
                            return;
                        }
                    }
                    removeLocalDisplay(videoElement.id);
                });
                return;
            }
        }

2.2. Создание контейнера для отображения локального видео

add() code

Здесь:

  • создается контейнер для элементов отображения локального видео
  • создается элемент для отображения информации о публикуемом видео
Code Block
languagejs
themeRDark
        const coreDisplay = document.createElement('div'createContainer(null);
        coreDisplay.setAttribute("class","text-center");
        coreDisplay.setAttribute("style","width: auto; height: auto;");
        coreDisplay.id = stream.id;
        const streamNameDisplaypublisherNameDisplay = document.createElement("div");
        streamNameDisplay.innerHTML = "Name:createInfoDisplay(coreDisplay, name + " " + name;
        streamNameDisplay.setAttribute("class","text-center");
        streamNameDisplay.setAttribute("style","width: auto; height: auto;");
        coreDisplay.appendChild(streamNameDisplay);

...

type);

2.3. Создание кнопки для включения/отключения локального аудио

add() code

Здесь:

  • создается кнопка для включения/отключения локального аудио
  • добавляется обработчик нажатия этой кнопки
Code Block
languagejs
themeRDark
        const audioStateDisplay = document.createElement("button");
        audioStateDisplay.innerHTML = audioStateText(stream);
        audioStateDisplay.addEventListener('click', function(){
coreDisplay.appendChild(audioStateDisplay);

2.4. Создание элемента для отображения локального видео

add() code

Здесь:

  • создается элемент-контейнер, размеры которого можно менять в зависимости от размеров родительского элемента
  • создается HTML5 video  элемент, с учетом публикации в Safari
Code Block
languagejs
themeRDark
        const streamDisplay = createContainer(coreDisplay);
   if (stream.getAudioTracks().length > 0) {
 streamDisplay.id = "stream-" + id;
        const video = streamdocument.getAudioTrackscreateElement()[0].enabled = !(stream.getAudioTracks()[0].enabled)"video");
        video.muted = true;
        if(Browser().isSafariWebRTC()) {
          audioStateDisplay.innerHTML = audioStateText(streamvideo.setAttribute("playsinline", "");
            } video.setAttribute("webkit-playsinline", "");
        });
        coreDisplaystreamDisplay.appendChild(audioStateDisplay)video);
        video.srcObject = stream;

2.

...

5. Создание

...

обработчиков событий video элемента

add() code

Здесь:

...

  • запускается проигрывание локального видео
  • настраивается обработчик события onended  для видео дорожки
  • настраивается обработчик события onresize  для локального видео, в котором размеры видео меняются под размеры контейнера
Code Block
languagejs
themeRDark
        constvideo.onloadedmetadata streamDisplay= =function document.createElement('div');(e) {
        streamDisplay.id = "stream-" + idvideo.play();
        streamDisplay.setAttribute("class","text-center")};
        streamDisplay.setAttribute("style","width: auto; height: auto;");
stream.getTracks().forEach(function(track){
            coreDisplay.appendChild(streamDisplay);track.addEventListener("ended", function() {
        const video = document.createElement("video");
        video.muted = truesrcObject.removeTrack(track);
        if(Browser().isSafariWebRTC()) {
       //check video element has  video.setAttribute("playsinline", "");no tracks left
            video.setAttribute("webkit-playsinline", "");
    for (const [key, vTrack] of Object.entries(video.srcObject.getTracks())) {
                    if (vTrack.readyState !== "ended") {
                }
        streamDisplay.appendChild(video)return;
               video.srcObject = stream;

2.5. Создание обработчиков событий video элемента

add() code

Здесь:

  • запускается проигрывание локального видео
  • настраивается обработчик события onended  для видео дорожки
  • настраивается обработчик события onresize  для локального видео, в котором размеры видео меняются под размеры контейнера
Code Block
languagejs
themeRDark
    }
            video.onloadedmetadata = function (e) {}
                video.playremoveLocalDisplay(id);
            });
        stream.getTracks().forEach(function(track){});
            track.addEventListener("ended", function(if (stream.getVideoTracks().length > 0) {
            // Resize only if video.srcObject.removeTrack(track); displayed
            video.addEventListener('resize', function (event) {
 //check video element has no tracks left
         publisherNameDisplay.innerHTML = name + " " + fortype (const [key, vTrack] of Object.entries(video.srcObject.getTracks())) {
     + " " + video.videoWidth + "x" + video.videoHeight;
               if resizeVideo(vTrack.readyState !== "ended") {event.target);
            });
        } else {
  return;
          // Hide audio only container
      }
      hideItem(streamDisplay);
          }
  // Set up mute button for audio only stream
      removeLocalDisplay(id);
      audioStateDisplay.innerHTML = audioStateText(stream) + " " + })type;
        });
        videoaudioStateDisplay.addEventListener('resize'"click", function (event) {
            streamNameDisplay.innerHTML  = "Name: " + name + "<br/>Max.resolution: " + video.videoWidth + "x" + video.videoHeight onMuteClick(audioStateDisplay, stream, type);
            resizeVideo(event.target});
        });

2.6. Добавление видео контейнера в элемент HTML страницы

add() code

Code Block
languagejs
themeRDark
        localDisplays[id] = coreDisplay;
        localDisplayDiv.appendChild(coreDisplay);
        return coreDisplay;

3. Остановка захвата видео и аудио

stop() code

Code Block
languagejs
themeRDark
    const stop = function () {
        for (const [key, value] of Object.entries(localDisplays)) {
            removeLocalDisplay(value.id);
        }
    }

...

1. Инициализация

initRemoteDisplay() code

Функция initLocalDisplayinitRemoteDisplay() возвращает объект для работы с HTML5 элементами отображения видео и аудио потоков, опубликованных в комнате

Code Block
languagejs
themeRDark
const initRemoteDisplay = function(mainDiv, room, peerConnectionoptions) {
    const constants = SFU.constants;
    const remoteParticipants = {};
    ...
    const createRemoteDisplay = function(id, name, mainDiv // Validate options first
    if (!options.div) {
        ...
throw new Error("Main div }

to place all  const stop = function(the media tag is not defined");
    }
    if (!options.room) {
        throw new Error("Room is  ...not defined");
    }

    peerConnection.ontrackif = ({transceiver}) =>(!options.peerConnection) {
        throw ...
    }

new Error("PeerConnection is not defined");
    return {}

    let mainDiv = options.div;
 stop: stop
  let  }
}

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

2.1. ADD_TRACKS

initRemoteDisplay() code

Здесь:

  • новый участник добавляется в список
  • добавляется информация о качестве потоков
  • создается элемент для отображения видео и аудио потоков участника
Code Block
languagejs
themeRDark
    room.on(constants.SFU_ROOM_EVENT.ADD_TRACKS, function(e) {
        console.log("Received ADD_TRACKS")room = options.room;
    let peerConnection = options.peerConnection;
    let displayOptions = options.displayOptions || {publisher: true, quality: true, type: true};
    ...
    const createRemoteDisplay = function(id, name, mainDiv, displayOptions) {
        ...
    }

    const stop = function() {
        ...
    }

    peerConnection.ontrack = ({transceiver}) => {
        ...
    }

    return {
        stop: stop
    }
}

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

2.1. ADD_TRACKS

initRemoteDisplay() code

Здесь:

  • новый участник добавляется в список
  • добавляется информация о качестве потоков
  • создается элемент для отображения видео и аудио потоков участника
Code Block
languagejs
themeRDark
    room.on(constants.SFU_ROOM_EVENT.ADD_TRACKS, function(e) {
        console.log("Received ADD_TRACKS");
        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;
                    display.setTrackInfo(pTrack);
                    createDisplay = false;
                    break;
                }
            }
            if (!createDisplay) {
                continue;
            }
            let display = createRemoteDisplay(participant.nickName, participant.nickName, mainDiv, displayOptions);
            participant.displays.push(display);
            if (pTrack.type === "VIDEO") {
                display.videoMid = pTrack.mid;
                display.setTrackInfo(pTrack);
            } else if (pTrack.type === "AUDIO") {
                display.audioMid = pTrack.mid;
                display.setTrackInfo(pTrack);
            }
        }
        ...
    });

2.2. REMOVE_TRACKS

initRemoteDisplay() code

Здесь:

  • удаляются элементы, в которых проигрывались потоки
  • информация о потоках удаляется из списка
Code Block
languagejs
themeRDark
    room.on(constants.SFU_ROOM_EVENT.ADD_TRACKS, function(e) {
        ...
    }).on(constants.SFU_ROOM_EVENT.REMOVE_TRACKS, function(e) {
        console.log("Received REMOVE_TRACKS");
        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;
                }
            }
        }
        ...
    });

2.3. LEFT

initRemoteDisplay() code

Здесь:

  • участник удаляется из списка
  • удаляются элементы, в которых проигрывались потоки
Code Block
languagejs
themeRDark
    room.on(constants.SFU_ROOM_EVENT.ADD_TRACKS, function(e) {
        ...
    }).on(constants.SFU_ROOM_EVENT.LEFT, function(e) {
        console.log("Received LEFT");
        let participant = remoteParticipants[e.name];
        if (!participant) {
            return;
        }
        participant.displays.forEach(function(display){
            display.dispose();
        })
        delete remoteParticipants[e.name];
        ...
    });

2.4. TRACK_QUALITY_STATE

initRemoteDisplay() code

Здесь:

  • обновляется информация о качествах потоков
Code Block
languagejs
themeRDark
    room.on(constants.SFU_ROOM_EVENT.ADD_TRACKS, function(e) {
        ... 
    }).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;
                }
            }
        }
    });

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;
        let participant        video.autoplay = remoteParticipants[e.info.nickName]true;
                if (!participant(Browser().isSafariWebRTC()) {
            participant = {};
        video.setAttribute("playsinline", "");
              participant.nickName = e.info.nickName      video.setAttribute("webkit-playsinline", "");
            participant.tracks = [];
      this.setWebkitEventHandlers(video);
      participant.displays = [];
        } else {
  remoteParticipants[participant.nickName] = participant;
        }
        participant.tracks.push.apply(participant.tracks, e.info.infothis.setEventHandlers(video);
        for (const pTrack of e.info.info) {
            ...}
            let display = createRemoteDisplay(participant.nickName, participant.nickName, mainDiv streamDisplay.appendChild(video);
               participant.displays.push(display) video.srcObject = stream;
            if  (pTrack.type === "VIDEO") { this.setResizeHandler(video);
                display.videoMid = pTrack.mid;
   abr.start();
            },

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

setAudio() code

Code Block
languagejs
themeRDark
            setAudio: display.setTrackInfofunction(pTrackstream); {
            } else if (pTrack.type === "AUDIO" if (audio) {
                  display.audioMid = pTrackaudio.midremove();
                }
           }
     if (!stream) {
          ...
    });

2.2. REMOVE_TRACKS

initRemoteDisplay() code

Здесь:

  • удаляются элементы, в которых проигрывались потоки
  • информация о потоках удаляется из списка
Code Block
languagejs
themeRDark
    }).on(constants.SFU_ROOM_EVENT.REMOVE_TRACKS, function(e) {  audio = null;
        console.log("Received REMOVE_TRACKS");
           const participantthis.audioMid = remoteParticipants[e.info.nickName]undefined;
        if (!participant) {
            return;
                }
        for (const rTrack of e.info.info) {        showItem(audioDisplay);
            for (let i = 0;audio i= < participant.tracks.length; i++) {document.createElement("audio");
                if (rTrack.midaudio.controls === participant.tracks[i].mid) { "controls";
                audio.muted    participant.tracks.splice(i, 1)= true;
                audio.autoplay =   breaktrue;
                }
  if (Browser().isSafariWebRTC()) {
          }
            for (let i = 0; i < participant.displays.length; i++) {
audio.setAttribute("playsinline", "");
                  let found = false audio.setAttribute("webkit-playsinline", "");
                const  display = participant.displays[i]this.setWebkitEventHandlers(audio);
                if (display.audioMid === rTrack.mid) } else {
                    displaythis.setAudiosetEventHandlers(nullaudio);
                    found = true;}
                } else if (display.videoMid === rTrack.mid) {audioTrackDisplay.appendChild(audio);
                    display.setVideo(nullaudioStateButton.makeButton(audioTypeDisplay, audio);
                audio.srcObject = stream;
  found = true;
            audio.onloadedmetadata = function (e) }{
                  if  (foundaudio.play().then(function() {
                        if (Browser(!display).hasAudioisSafariWebRTC() && !display.hasVideoBrowser().isiOS()) {
                         display.dispose();
           console.warn("Audio track should be manually unmuted in iOS Safari");
                participant.displays.splice(i, 1);
        } else {
          }
                  audio.muted = breakfalse;
                }
            }audioStateButton.setButtonState();
        }
    });

2.3. LEFT

initRemoteDisplay() code

Здесь:

  • участник удаляется из списка
  • удаляются элементы, в которых проигрывались потоки
Code Block
languagejs
themeRDark
    }).on(constants.SFU_ROOM_EVENT.LEFT, function(e) {
        console.log("Received LEFT");}
        let participant = remoteParticipants[e.name];
        if (!participant) {});
            return;
    };
    }
        participant.displays.forEach(function(display){
},

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

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

Code Block
languagejs
themeRDark
            setEventHandlers: display.disposefunction(video); {
        })
        delete remoteParticipants[e.name];
    });

2.4. TRACK_QUALITY_STATE

initRemoteDisplay() code

Здесь:

  • обновляется информация о качествах потоков
Code Block
languagejs
themeRDark
    }).on(constants.SFU_ROOM_EVENT.TRACK_QUALITY_STATE, function(e){
// Ignore play/pause button
                video.addEventListener("pause", function () {
                    console.log("ReceivedMedia paused trackby quality stateclick, continue...");
        const participant = remoteParticipants[e.info.nickName];
        if video.play(!participant) {
;
                return});
            },

         for (const rTrack of e.info.trackssetWebkitEventHandlers: function(video) {
            const mid    let needRestart = rTrack.midfalse;
               for (let iisFullscreen = 0false;
    i < participant.displays.length; i++) {
        // Use webkitbeginfullscreen event to detect full screen constmode displayin = participant.displays[i];iOS Safari
                if (display.videoMid === midvideo.addEventListener("webkitbeginfullscreen", function () {
                     display.updateQualityInfo(rTrack.quality)isFullscreen = true;
                });    break;            
                }
   video.addEventListener("pause", function () {
         }
        }
    });

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

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

createRemoteDisplay() code

Здесь:

  • создается контейнер для потоков участника
  • создается контейнер для конкретного потока
  • создается контейнер для кнопок переключения качества
Code Block
languagejs
themeRDark
if (needRestart) {
             const cell = document.createElement("div");
        cellconsole.setAttributelog("class", "text-centerMedia paused after fullscreen, continue...");
        cell.id = id;
        mainDiv.appendChild(cell);
        const streamNameDisplay = document.createElement("div"video.play();
           streamNameDisplay.innerHTML = "Published by: " + name;
        streamNameDisplay.setAttribute("style","width:auto; height:auto;");
needRestart = false;
          streamNameDisplay.setAttribute("class","text-center");
        cell.appendChild(streamNameDisplay);
  } else {
    const qualityDisplay = document.createElement("div");
        qualityDisplay.setAttribute("style","width:auto; height:auto;");
        qualityDisplayconsole.setAttributelog("class","text-centerMedia paused by click, continue...");
          cell.appendChild(qualityDisplay);

           let qualityDivs = [] video.play();

        const rootDisplay = document.createElement("div");
        rootDisplay.setAttribute("style","width:auto; height:auto;"); }
        rootDisplay.setAttribute("class","text-center");
        cell.appendChild(rootDisplay});
        const      streamDisplay = documentvideo.createElementaddEventListener("divwebkitendfullscreen");
, function () {
          streamDisplay.setAttribute("style","width:auto; height:auto;");
          streamDisplayvideo.setAttribute("class","text-center"play();
        rootDisplay.appendChild(streamDisplay);

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

setVideo() code

Code Block
languagejs
themeRDark
            setVideo: function(stream) {needRestart = true;
                if (video) {
  isFullscreen = false;
                video.remove(});
                }

},

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

setVideoABRTrack() code

Code Block
languagejs
themeRDark
                 if (stream == nullsetVideoABRTrack: function(track) {
                    video = nullabr.setTrack(track);
                    this.videoMid = undefined;
},

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

setTrackInfo() code

Здесь:

  • настраиваются кнопки переключения качества
  • информация о заявленных качествах публикации добавляется в ABR
  • скрываются или отображаются элементы для показа текущего качества и источника видео/аудио
Code Block
languagejs
themeRDark
                    qualityDivs.forEach(setTrackInfo: function(divtrackInfo) {
                if (trackInfo) { 
     div.remove();
               if (trackInfo.quality) {
   });
                    qualityDivs = [] showItem(qualitySwitchDisplay);
                    return;
       if (abr.isEnabled()) {
             }
               const videoautoDiv = document.createElementcreateQualityButton("video");
                video.muted = trueAuto", qualityDivs, qualitySwitchDisplay);
                if(Browser().isSafariWebRTC()) {
           autoDiv.style.color = QUALITY_COLORS.SELECTED;
       video.setAttribute("playsinline", "");
                    videoautoDiv.setAttribute("webkit-playsinline", "");addEventListener('click', function() {
                }
                streamDisplay.appendChildsetQualityButtonsColor(videoqualityDivs);
                video.srcObject = stream;
                videoautoDiv.style.onloadedmetadatacolor = function (e) {
    QUALITY_COLORS.SELECTED;
                video.play().then(function() {
               abr.setAuto();
         video.muted = false;
                    });
                };
        }
        video.addEventListener("resize", function (event) {
             for (let i = 0; i < streamNameDisplay.innerHTML = "Published by: " + name + "<br/>Current resolution: " + video.videoWidth + "x" + video.videoHeight;
trackInfo.quality.length; i++) {
                            resizeVideoabr.addQuality(event.targettrackInfo.quality[i]);
                }            const qualityDiv = createQualityButton(trackInfo.quality[i], qualityDivs, qualitySwitchDisplay);
            },

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

setAudio() code

Code Block
languagejs
themeRDark
                setAudio:qualityDiv.addEventListener('click', function(stream) {
                if (audio) {
                console.log("Clicked on quality " + trackInfo.quality[i] + " trackId " + audiotrackInfo.remove(id);
                }
                if (!streamqualityDiv.style.color === QUALITY_COLORS.UNAVAILABLE) {
                    audio = null;
                    this.audioMid = undefinedreturn;
                    return;
                }
                audio = document.createElement("audio");
                audio.controls = "controls"setQualityButtonsColor(qualityDivs);
                audio.muted = true;
                audioqualityDiv.style.autoplaycolor = trueQUALITY_COLORS.SELECTED;
                cell.appendChild(audio);
                audio.srcObject = streamabr.setManual();
                audio.onloadedmetadata = function (e) {
                    audio.play().then(function() {
abr.setQuality(trackInfo.quality[i]);
                          audio.muted = false});
                    });
    }
            };
        }    },

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

setTrackInfo() code

Code Block
languagejs
themeRDark
else {
                setTrackInfo: function(trackInfo) {
      hideItem(qualitySwitchDisplay);
          if (trackInfo && trackInfo.quality) {
      }
              for (let i = 0; i <if (trackInfo.quality.length; i++type) {
                        constcontentType qualityDiv = document.createElement("button")trackInfo.contentType || "";
                        if (trackInfo.type == "VIDEO" && qualityDivs.push(qualityDiv);displayOptions.type && contentType !== "") {
                        qualityDiv.innerText   = trackInfo.quality[i] showItem(videoTypeDisplay);
                        qualityDiv.setAttribute("style", "display:inline-block; border: solid; border-width: 1px")    videoTypeDisplay.innerHTML = contentType;
                        qualityDiv.style.color = "red";
}
                        if  qualityDiv.addEventListener('click', function(){
        (trackInfo.type == "AUDIO") {
                    console.log("Clicked on quality " + trackInfo.quality[i] + " trackId " + trackInfoaudioStateButton.idsetContentType(contentType);
                        }
    if (qualityDiv.style.color === "red") {
            }
                }
    return;
        },

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

updateQualityInfo() code

Code Block
languagejs
themeRDark
            updateQualityInfo: function(videoQuality) {
           }
     showItem(qualitySwitchDisplay);
                for (const qualityInfo of videoQuality) {
  for (let c = 0; c < qualityDivs.length; c++) {
         let qualityColor = QUALITY_COLORS.UNAVAILABLE;
                    if (qualityDivs[c].style.color !qualityInfo.available === "red"true) {
                        qualityColor = QUALITY_COLORS.AVAILABLE;
          qualityDivs[c].style.color = "gray";
          }
                    for (const qualityDiv of qualityDivs) }{
                        if (qualityDiv.innerText   }=== qualityInfo.quality){
                            qualityDiv.style.color = "blue"qualityColor;
                            room.changeQuality(trackInfo.id, trackInfo.quality[i])  break;
                        });
                    }
    qualityDisplay.appendChild(qualityDiv);
                    }abr.setQualityAvailable(qualityInfo.quality, qualityInfo.available);
                }
            },

3.

...

9. Удаление контейнера с видео и аудио

dispose() code

Code Block
languagejs
themeRDark
            dispose: function() {
                abr.stop();
                cell.remove();
            },

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

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

Здесь:

  • при получении видео или аудио потока, добавляется элемент для его проигрывания
Code Block
languagejs
themeRDark
    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.videoMid === transceiver.mid) {
                        let stream = new MediaStream();
                        stream.addTrack(transceiver.receiver.track);
                        display.setVideoABRTrack(transceiver.receiver.track);
                        display.setVideo(stream);
                        break;
                    }
                } else if (transceiver.receiver.track.kind === "audio") {
                    if (display.audioMid === transceiver.mid) {
                        let stream = new MediaStream();
                        stream.addTrack(transceiver.receiver.track);
                        display.setAudio(stream);
                        break;
                    }
                }
            }
        } else {
            console.warn("Failed to find participant for track " + transceiver.receiver.track.id);
        }
    }

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

stop() code

Code Block
languagejs
themeRDark
    const stop = function() {
        for (const [nickName, participant] of Object.entries(remoteParticipants)) {
            participant.displays.forEach(function(display){
                display.dispose();
            });
            delete remoteParticipants[nickName];
        }
    }