七牛云web-rtc封装

// https://developer.qiniu.com/rtc/9059/WebQNRemoteUser  七牛文档

// https://developer.qiniu.com/rtc/9059/WebQNRemoteUser  七牛文档

// https://developer.qiniu.com/rtc/9059/WebQNRemoteUser  七牛文档

import QNRTC, {
    QNConnectionState,
    QNConnectionDisconnectedReason,
    QNVideoOptimizationMode,
    QNRemoteUser as RTCUser,
} from "qnweb-rtc";
import User from "@/views/teacher/live/qiniu/models/user.js";
import Track from "@/views/teacher/live/qiniu/models/Track.js";

class Room {
    rtcToken = ''; // 加入房间的token
    liveType = ''; // 选择的类型
    users = new Map(); // 房间中的用户
    usersArr = []; // 用户数组 用来获取用户列表
    userFlag = '';  // 用来判断用户是离开了还是加入
    currentUserId = ''; // 当前加入房间的用户的id 
    roomUserArr = []; // 学生进入房间 离开房间信息
    publishedTracks = new Map();//已发布的Tracks
    state = QNConnectionState.DISCONNECTED; // 当前房间链接状态
    publishTracksReport = { // 当前发布 Track 的实时状态,每隔 5 秒更新
        audio: null,
        video: null,
        screen: null,
    };
    session = null; // client 实例
    remoteTracks = new Map(); // 房间内学生已发布的Tracks
    localTracks = []; // 本地的Tracks
    statusInterval;
    shouldResumedTracks = []; // 播放失败的 sracks
    subscribedTracks = new Map(); // 已订阅的 Track Map
    NetworkLv = '未知';  // 网络等级
    screenStatus = false; // 分享屏幕状态 
    errorState = ''; // 直播错误状态
    cameraList = []; // 可用视频设备
    microphonesList = []; // 可用麦克风设备
    currentDevices = {
        microphoneId:'',
        cameraId:''
    }
    constructor() {
        // 创建rtc实例
        this.session = QNRTC.createClient();
        // 监听房间连接状态 修改this指向
        this.session.on('connection-state-changed', this.setState.bind(this));
        // 获取枚举可用的视频输入设备,比如摄像头
        QNRTC.getCameras().then(res => {
            this.cameraList = res
            this.currentDevices.cameraId =  this.cameraList[0].deviceId
        }).catch(res => {
            // 没有摄像头的话 提示没有摄像头设备
            ELEMENT.Message.warning("没有视频播放设备,请接入摄像头");
            // console.log(res,'error');
        })
        // 获取枚举可用的音频输入设备,比如麦克风    
        QNRTC.getMicrophones().then(res => {
            this.microphonesList = res
            this.currentDevices.microphoneId =  this.microphonesList[0].deviceId

        }).catch(res => {
            // 没有麦克风的话 提示没有麦克风设备
            ELEMENT.Message.warning("没有麦克风设备,请接入麦克风");
        })
        // 即将离开当前页面(刷新或关闭)时执行
        window.onbeforeunload = () => {
            try {
                this.leaveRoom();

            } catch { }
        }
    }
    // 发送消息
    sendMessage(id, message) {
        return this.session.sendMessage(id, message)
    }
    // 麦克风设备插入或拔出
    microphoneChange(info) {
        if (info.state == 'ACTIVE') {
            ELEMENT.Message.warning("麦克风设备插入");
        } else if (info.state == 'INACTIVE') {
            ELEMENT.Message.warning("麦克风设备拔出");
        }
    }
    // 视频设备插入或拔出 
    cameraChange(info) {
        if (info.state == 'ACTIVE') {
            ELEMENT.Message.warning("麦克风设备插入");
        } else if (info.state == 'INACTIVE') {
            ELEMENT.Message.warning("麦克风设备拔出");
        }
    }
    // 音频设备插入或拔出
    audioChange(info) {
        if (info.state == 'ACTIVE') {
            ELEMENT.Message.warning("音频设备插入");
        } else if (info.state == 'INACTIVE') {
            ELEMENT.Message.warning("音频设备拔出");
        }
    }


    // 获取本地网络情况
    getNetworkQuality() {
        let res = this.session.getNetworkQuality()
        let lv
        switch (res) {
            case 'UNKNOWN':
                lv = '未知'
                break;
            case 'EXCELLENT':
                lv = '极好'
                break;
            case 'GOOD':
                lv = '良好'
                break;
            case 'FAIR':
                lv = '一般'
                break;
            case 'POOR':
                lv = '极差'
                break;
        }
        this.NetworkLv = lv
    }
    // 进入房间先获取在房间的远端的学生 把在房间里的学生重新添加订阅
    getRemoteUser() {
        // 获取远端的学生信息
        let remoteUserList = this.session.remoteUsers
        console.log(remoteUserList);
        remoteUserList.forEach((item) => {
            // 添加学生到本地
            this.addUser(item.userID);
            //   获取当前学生的音频和视频teacks
            let tracks = [
                ...(item.getVideoTracks()),
                ...(item.getAudioTracks()),
            ];
            //   重新订阅该学生
            this.addTracks(item.userID, tracks);
        });

    }

    //离开房间触发,释放所有房间内的 Track
    async leaveRoom() {
        // 清除rtc实例
        this.session.leave();

        // 释放本地tracks
        this.releaseLocalTracks()
        // 销毁 已发布的tracks
        this.publishedTracks.forEach(t => (t.rtcTrack).destroy());
        // 移除 已发布的map数据
        this.publishedTracks.clear();
        // 移除 已订阅学生 tracks 的map数据
        this.subscribedTracks.clear();
        // 移除 远端的 tracks 的map数据
        this.remoteTracks.clear();
        // 移除 学生信息的 map数据
        this.users.clear();
        // 清空 房间的rtcToken
        this.rtcToken = '';
        // 如果有直播的状态 清空直播的状态
        if (this.statusInterval) clearInterval(this.statusInterval);

        console.log('------------离开房间-----------');
    }
    // 选择直播的类型
    liveType(type) {
        this.liveType = type
    }
    // 获取学生列表
    getStudentList(flag) {
        this.usersArr = Array.from(this.users.values());
        this.userFlag = flag
        this.userFlag = ''
    }
    //  获取已经发布的 audioTrack
    publishedAudioTracks() {
        return Array.from(this.publishedTracks.values())
            .filter(v => v.rtcTrack.isAudio());
    }
    //  获取已发布的 VideoTrack 
    publishedCameraTracks() {
        return Array.from(this.publishedTracks.values())
            .filter(v => v.rtcTrack.tag === 'camera');
    }
    // 获取已发布的共享屏幕
    publishedScreenTracks() {
        return Array.from(this.publishedTracks.values())
            .filter(v => v.rtcTrack.tag === 'screen');
    }
    // 是否显示重新播放的对话框
    showResumePlayDialog() {
        return this.shouldResumedTracks.length !== 0;
    }
    // 添加需要恢复的轨道
    addShouldResumedTracks(track) {
        this.shouldResumedTracks.push(track);
    }
    // 清除需要恢复的轨道
    clearShouldResumedTracks() {
        this.shouldResumedTracks = []
    }
    // 加入房间token
    setToken(rtcToken) {
        this.rtcToken = rtcToken
    }
    // 设置本地的Tracks
    setLocalTracks(tracks) {
        this.localTracks = tracks
    }
    // 创建本地预览 Tracks
    async PreviewLocalTracks() {
        this.localTracks = await QNRTC.createMicrophoneAndCameraTracks(
            { tag: "microphone",microphoneId: this.microphonesList[0].deviceId },
            { tag: "camera",  cameraId:this.cameraList[0].deviceId , optimizationMode: QNVideoOptimizationMode.DETAIL }
        );
        return this.localTracks
    }
    // 根据uid改变学生举手的状态
    changeHandStatus(userID, status) {
        const currentUser = this.users.get(userID)
        currentUser.changeHandStatus(status)
    }
    // 添加轨道 添加学生的音视频track 
    addTracks(userID, tracks) {
        // 是否有用户id
        if (this.users.has(userID)) {
            const user = this.users.get(userID);
            for (const track of tracks) {
                // 添加用户track
                user.addPublishedTrack(track);
                // 添加远程订阅信息
                this.remoteTracks.set(track.trackID, track);
            }
        }
        this.subscribe(tracks.map(v => v.trackID), userID);

    }
    // 删除轨道
    removeTracks(userID, tracks) {
        if (this.users.has(userID)) {
            const user = this.users.get(userID);
            for (const track of tracks) {
                // 移除学生的音视频的 track 
                user.removePublishedTrack(track);
                user.tracks.delete(track.trackID);
                this.remoteTracks.delete(track.trackID);
            }

        }
    }
    // 切换已发布的 VideoTrack(Camera) Mute状态 视频和共享屏幕
    toggleMutePublishedCamera() {
        this.publishedCameraTracks().forEach(track => track.toggleMute());
    }
    //切换已发布的 VideoTrack(Screen) Mute状态  共享屏幕
    toggleMutePublishedScreen() {
        this.publishedScreenTracks().forEach(track => track.toggleMute());
    }
    // 切换已发布的 AudioTrack Mute状态
    toggleMutePublishedAudio() {
        this.publishedAudioTracks().forEach(track => track.toggleMute());
    }

    // 播放需要回复的轨道
    playShouldResumedTracks() {
        for (const t of this.shouldResumedTracks) {
            if (t.rtcTrack && t.rtcTrack.mediaElement) {
                t.rtcTrack.mediaElement.play();
            }
        }
        this.clearShouldResumedTracks()
    }
    // 设置直播状态 
    setState(state, info) {
        this.state = state;
        if (state !== QNConnectionState.DISCONNECTED) return;
        if (!info) return;
        this.errorState = info.reason
        switch (info.reason) {
            case QNConnectionDisconnectedReason.LEAVE: {
                ELEMENT.Message.warning("主动退出房间");
                return;
            }
            case QNConnectionDisconnectedReason.KICKED_OUT: {
                this.leaveRoom()
                ELEMENT.Message.warning("您被踢出房间");
                return;
            }
            case QNConnectionDisconnectedReason.ERROR: {
                this.leaveRoom()
                ELEMENT.Message.warning(`错误: ErrorCode: ${info.errorCode}, ErorMessage: ${info.errorMessage}`)
                return;
            }
        }
    }
    // 加入房间
    async joinRoom(token, userData, role) {
        if (!token) return;
        await this.session.join(token, userData)
            .then(res => {
                console.log('登入成功!!!!!!!!!', res);
                // 清除订阅,已发布的轨道 学生信息
                this.subscribedTracks.clear();
                this.remoteTracks.clear();
                this.users.clear();

                // 监听学生加入房间
                this.session.on('user-joined', this.addUser.bind(this));
                // 监听学生离开房间
                this.session.on('user-left', this.removeUser.bind(this));
                // 监听学生添加媒体轨道
                this.session.on('user-published', this.addTracks.bind(this));
                // 监听学生移除媒体轨道
                this.session.on('user-unpublished', this.removeTracks.bind(this));
                // 监听音频设备移除或者添加 
                QNRTC.onPlaybackDeviceChanged = this.audioChange
                // 监听麦克风设备移除或者添加
                QNRTC.onMicrophoneChanged = this.microphoneChange
                // 监听视频设备移除或者添加
                QNRTC.onCameraChanged = this.cameraChange
            })
            .catch(res => {
                console.log(res, 'error');
                console.log(res.code, 'code');
                switch (res.code) {
                    case 10001:
                        ELEMENT.Message.warning("房间 Token 错误");
                        break;
                    case 10002:
                        ELEMENT.Message.warning("房间 Token 过期");
                        break;
                    case 10022:
                        ELEMENT.Message.warning("房间内已经有同名用户");
                        break;
                    case 10054:
                        ELEMENT.Message.warning("设备不支持指定的音视频格式,无法进行连麦的操作");
                        break;
                    case 21001:
                        ELEMENT.Message.warning("加入房间认证失败");
                        break;
                    case 21003:
                        ELEMENT.Message.warning("30s 内未能重连成功或者重连时房间已被销毁");
                        break;
                    case 21005:
                        ELEMENT.Message.warning("需要检查接口调用、设备状态、网络状态等是否正常");
                        break;
                }
            });

    }
    // 学生进入房间
    addUser(userID) {
        // // 获取学生的数据
        const rtcuser = this.session.getRemoteUser(userID);
        let userName = rtcuser.userData
        this.roomUserArr.push(userName + '加入了房间')
        // console.log(rtcuser);
        if (!rtcuser) return;
        if (this.users.has(userID)) return;
        // 设置学生的信息
        this.users.set(userID, new User(rtcuser));
        // this.currentUserId = userID
        // 更新学生列表
        this.usersArr = Array.from(this.users.values());

    }
    // 学生离开房间
    removeUser(userID) {
        let userName = this.users.get(userID).rtcUser.userData
        this.roomUserArr.push(userName + '离开了房间')
        this.users.delete(userID);
        this.getStudentList('remove')
    }
    // 发布
    async publish(tracks) {
        try {
            await this.session.publish(tracks);
            for (const track of tracks) {
                if (track.trackID) this.publishedTracks.set(track.trackID, new Track(track));
            }
            if (this.statusInterval) {
                window.clearInterval(this.statusInterval);
            }
            // 修改内部的this指向
            // 5秒监听一次 直播的设备网络情况
            this.statusInterval = window.setInterval(this.updateTrackStatusReport.bind(this), 5000);
        } catch (e) {
            tracks.map(t => t.destroy());
            throw e;
        }
    }
    // 取消发布 传入需要取消的设备名 例如 camera
    async unpublish(tag) {
        const tracks = Array.from(this.publishedTracks.values()).filter(v => v.tag == tag)
            .map(t => (t.rtcTrack));
        await this.session.unpublish(tracks);
        // tracks.forEach(t => t.destroy());
        tracks[0].destroy();
        // this.publishedTracks.clear();
        this.publishedTracks.delete(tracks[0].trackID)
        console.log('--------------取消发布-----------------');
    }
    // 创建麦克风Track
    async createMicrophoneTrack(deviceId) {
        let tracks = [];
        tracks = [
            await QNRTC.createMicrophoneAudioTrack({
                tag: 'microphone',
                optimizationMode: QNVideoOptimizationMode.DETAIL,
                microphoneId : deviceId ? deviceId : this.microphonesList[0].deviceId
            })
        ]
        return tracks
    }
    // 创建视频Track
    async createVideoTrack(deviceId) {
        let tracks = [];
        tracks = [
            await QNRTC.createCameraVideoTrack({
                tag: 'camera',
                optimizationMode: QNVideoOptimizationMode.DETAIL,
                cameraId: deviceId ? deviceId : this.cameraList[0].deviceId
            })
        ]
        return tracks
    }
    setMicrophoneDevice(deviceId){
        this.currentDevices.microphoneId = deviceId
    }
    setCameraDevice(deviceId){
        this.currentDevices.cameraId = deviceId
    }
    // 创建共享屏幕 Track
    async createScreenTrack() {
        let tracks = [];
        tracks = [await QNRTC.createScreenVideoTrack({
            screenVideoTag: "screen",
            optimizationMode: QNVideoOptimizationMode.DETAIL
        })]
        return tracks
    }
    // 设置选择的选项
    async getSelectTracks(liveType) {

        let tracks = [];
        switch (liveType) {
            // 音频通话
            case 'microphone': {
                tracks = [await QNRTC.createMicrophoneAudioTrack(
                    {
                        tag: "microphone",
                        microphoneId: this.microphonesList[0].deviceId
                    }
                )];
                break;
            }
            // 音视频通话
            case 'AudioAndVideo': {
                tracks = await QNRTC.createMicrophoneAndCameraTracks(
                    { tag: "microphone",microphoneId: this.microphonesList[0].deviceId },
                    { tag: "camera",cameraId:this.cameraList[0].deviceId, optimizationMode: QNVideoOptimizationMode.DETAIL, encoderConfig: "1080p", }
                );
                break;
            }
            // 音视频 + 屏幕共享
            case 'AudioAndvidoeAndShareScreen': {
                tracks = [
                    ...await QNRTC.createMicrophoneAndCameraTracks(
                        { tag: "microphone",microphoneId: this.microphonesList[0].deviceId },
                        { tag: "camera",cameraId:this.cameraList[0].deviceId, optimizationMode: QNVideoOptimizationMode.DETAIL }
                    ),
                    (await QNRTC.createScreenVideoTrack({
                        screenVideoTag: "screen",
                        optimizationMode: QNVideoOptimizationMode.DETAIL,
                        encoderConfig: "1080p"
                    }))
                ];
                break;
            }
            // 屏幕共享 + 系统声音
            case 'audioAndShareScreen': {
                const ts = await QNRTC.createScreenVideoTrack({
                    optimizationMode: QNVideoOptimizationMode.DETAIL,
                    screenAudioTag: "system-audio",
                    screenVideoTag: "screen",
                    encoderConfig: "1080p",
                }, "auto");
                if (Array.isArray(ts)) {
                    tracks = ts;
                } else {
                    tracks = [ts];
                }
                break;
            }
        }
        this.handleTrackEnded(tracks);
        this.setLocalTracks([...this.localTracks, ...tracks]);
        return tracks;
    }
    //处理 Track 结束事件:如果是麦克风 Track,重新创建一个麦克风 Track,并发布;如果是屏幕分享 Track,直接从已发布的 Track 中删除
    async handleTrackEnded(tracks) {
        for (const track of tracks) {
            if (track.tag === "microphone") {
                track.on("ended", async () => {
                    const tracks = [await QNRTC.createMicrophoneAudioTrack({ tag: "microphone" })];
                    this.handleTrackEnded(tracks);
                    this.setLocalTracks([...this.localTracks, ...tracks]);
                    this.publish(tracks);
                });
            } else if (track.tag === "screen") {
                track.on("ended", async () => {
                    this.screenStatus = false
                    track.trackID && this.publishedTracks.delete(track.trackID);
                    console.log('---------------handleTrackEnded---------------------');
                });
            }
        }
    }

    // 订阅
    async subscribe(trackids, userID) {
        // 初始化一个空数组,用于存放需要订阅的轨道
        let targetTracks = [];
        // 遍历传入的 trackids,获取对应的远程轨道,并将其添加到 targetTracks 数组中
        trackids.forEach(trackID => {
            // 订阅的track从remoteTracks 取得
            const targetTrack = this.remoteTracks.get(trackID);
            if (targetTrack) {
                targetTracks.push(targetTrack);
            }
        });
        let innerfunc;
        // 定义一个 removePromise 变量,用于存放一个 Promise,以便在订阅时监听是否有轨道被移除的事件
        const removePromise = new Promise((resovle, reject) => {
            // 定义一个函数 innerfunc,用于处理轨道移除事件
            innerfunc = (_, tracks) => {
                for (const track of tracks) {
                    // 如果移除的轨道包含在要订阅的轨道列表中,则认为订阅失败
                    if (trackids.includes((track.trackID))) {
                        const error = new Error('订阅失败,订阅的track已移除');
                        reject(error);
                    }
                }
            };
            // 监听会话的 user-unpublished 事件,并将 innerfunc 函数作为回调函数
            this.session.on('user-unpublished', innerfunc);
        });
        try {
            // 订阅远程用户的音视频轨道,等待订阅结果返回
            const rtctracks = await Promise.race([removePromise, this.session.subscribe(targetTracks)]);
            // 如果有 innerfunc 函数,则将其从事件监听器中移除
            if (innerfunc) {
                this.session.off('user-unpublished', innerfunc);
            }
            // 如果 rtctracks 是数组,则说明订阅失败,直接返回
            if (Array.isArray(rtctracks)) return;
            // 遍历 rtctracks 中的音视频轨道,并将其添加到 subscribedTracks 和 users 对象中
            for (const rtctrack of [...rtctracks.videoTracks, ...rtctracks.audioTracks]) {
                const track = new Track(rtctrack);
                this.subscribedTracks.set(rtctrack.trackID, track);
                const user = this.users.get(rtctrack.userID);
                if (user) {
                    user.tracks.set(rtctrack.trackID, track);
                }
            }
            this.currentUserId = userID
            // 更新学生列表
            this.getStudentList('add')

        } catch (e) {
            console.warn(e);
            throw e;
        }
    }
    // 取消订阅
    async unsubscribe(trackids) {
        let targetTracks = [];
        trackids.forEach(trackID => {
            const targetTrack = this.remoteTracks.get(trackID);
            if (targetTrack) {
                targetTracks.push(targetTrack);
            }
        });
        await this.session.unsubscribe(targetTracks);

        for (const trackid of trackids) {
            const track = this.subscribedTracks.get(trackid);
            if (!track) { return; }
            this.subscribedTracks.delete(trackid);
            const user = this.users.get(track.userID);
            if (!user) { return; }
            user.tracks.delete(trackid);
        }


    }
    //释放本地 Track
    releaseLocalTracks() {
        if (this.localTracks.length === 0) return;
        this.localTracks.forEach(t => {
            t.destroy();
        });
        this.localTracks = [];
    }

    // 每隔 5 秒获取当前发布 Track 的状态
    updateTrackStatusReport() {
        // console.log(that);
        const publishedTracksList = Array.from(this.publishedTracks.values());

        const audioTrack = publishedTracksList.find(t => t.rtcTrack.isAudio());
        const videoTrack = publishedTracksList.find(t => t.tag === "camera");
        const screenTrack = publishedTracksList.find(t => t.tag === "screen");

        this.publishTracksReport.audio = null;
        if (audioTrack) {
            this.publishTracksReport.audio = (audioTrack.rtcTrack).getStats();
        }

        this.publishTracksReport.video = null;
        if (videoTrack) {
            this.publishTracksReport.video = (videoTrack.rtcTrack).getStats()[0];
        }

        this.publishTracksReport.screen = null;
        if (screenTrack) {
            this.publishTracksReport.screen = (screenTrack.rtcTrack).getStats()[0];
        }
        this.getNetworkQuality()
    }



}

export default new Room()

// 以下是vue中的方法

 //  开始直播
 // 如果有定时器 就清除定时器
      if (this.classCountdown != null) {
        clearInterval(this.classCountdown);
        this.classCountdown = null;
      }
      this.startLive = true;
      this.readeLive = false;
      // 显示 直播的计时 和开始计算
      this.isShowCountTime = true;
      this.classTimeFn();
      // 改变状态 上课
      await this.attendFn();
      // 释放本地 Tracks
      await room.releaseLocalTracks();

      await room.joinRoom(this.rtcToken, this.$store.getters.userInfo.nickName);

      // 获取直播类型
      let liveType = this.$store.getters.liveType;
      // 设置直播 创建直播 tracks
      let tracks = await this.selectTracks(liveType);
      // console.log(tracks);
      if (tracks) {
        // 将所有的tracks 推送到房间里
        await room.publish(tracks);
      }
      this.selectTypeTracks(liveType);
      // 先获取在房间内的用户 去重新订阅
      this.getRemoteRoomUser();
      this.videoRecordFn();

  // 切换视频的播放设备
    async changeVideoDevice(deviceId) {
      await room.unpublish("camera");
      const rtcTracks = await room.createVideoTrack(deviceId);
      await room.publish(rtcTracks);
      room.setCameraDevice(deviceId)
      this.$nextTick(() => {
        let teacherVideoEleSmall =
          this.$refs.studentVideoRef.$refs.teacherVideoRef;
        let videoTracksed = this.getFirstRtcTrack(room.publishedCameraTracks());
        videoTracksed.play(teacherVideoEleSmall);
        teacherVideoEleSmall.querySelector("video").style["object-fit"] =
          "cover";
      });
    },
    // 切换音频的播放设备
    async changeMicrophoneDevice(deviceId) {
      await room.unpublish("microphone");
      const rtcTracks = await room.createMicrophoneTrack(deviceId);
      await room.publish(rtcTracks);
      room.setMicrophoneDevice(deviceId)
    },
    // 进入房间先获取在房间的远端的学生 把在房间里的学生显示出来
    getRemoteRoomUser() {
      room.getRemoteUser();
    },
 // 根据直播的类型判断 是否渲染视频
 // 根据直播的类型 添加不同的video
      this.$nextTick(() => {
        let teacherVideoEleSmall =
          this.$refs.studentVideoRef.$refs.teacherVideoRef;
        let tracherVideoEleBig =
          this.$refs.startLiveMain.$refs.startLiveVideoRef;

        switch (liveType) {
          case "audioAndShareScreen":
            screenRracks.play(tracherVideoEleBig);
            tracherVideoEleBig.querySelector("video").style["object-fit"] =
              "cover";
            room.screenStatus = true;
            this.screenStatus = true;
            break;
          case "AudioAndvidoeAndShareScreen":
            videoTracks.play(teacherVideoEleSmall);
            screenRracks.play(tracherVideoEleBig);
            teacherVideoEleSmall.querySelector("video").style["object-fit"] =
              "cover";
            tracherVideoEleBig.querySelector("video").style["object-fit"] =
              "cover";
            this.videoStatus = true;
            this.screenStatus = true;
            room.screenStatus = true;
            break;
          case "AudioAndVideo":
            videoTracks.play(teacherVideoEleSmall);
            teacherVideoEleSmall.querySelector("video").style["object-fit"] =
              "cover";
            this.videoStatus = true;
            break;
        }
      });
    // 获取已发布的Track
    getFirstRtcTrack(publishedTracks) {
      let rtcTrack;
      if (publishedTracks.length > 0) {
        const firstTrack = publishedTracks[0];
        if (firstTrack) {
          rtcTrack = firstTrack.rtcTrack;
        }
      }
      return rtcTrack;
    },
    // 选择 Tracks
    selectTracks(liveType) {
      // 获取创建的所有的 tracks
      return room.getSelectTracks(liveType).catch((e) => {
        console.log(e);
        switch (e.code) {
          case 11010:
            ELEMENT.Message.warning(
              "获取摄像头/麦克风权限被拒绝,请手动打开摄像头/麦克风权限后重新进入房间"
            );
            break;
          case 11008:
            ELEMENT.Message.warning(
              "抱歉,您的浏览器不支持屏幕共享,请使用 Chrome 或者 Firefox"
            );
            break;
          case 11013:
            ELEMENT.Message.warning("请刷新页面手动重新发布");
            break;
          case 11018:
            ELEMENT.Message.warning("请手动选择系统声音");
            break;
          default:
            ELEMENT.Message.warning(
              `无法获取摄像头/麦克风数据,错误代码: ${e.name}`
            );
        }
        return undefined;
      });
    },
   //  按钮 不同按钮执行事件
    async toolsFn(toolsBtn) {
      switch (toolsBtn) {
        case "microphone":
          room.toggleMutePublishedAudio();
          this.audioStatus = !this.audioStatus
          break;
        case "camera":
          // 获取已发布的视频的 tracks
          let videoTracks = this.getFirstRtcTrack(room.publishedCameraTracks());
          if (videoTracks) {
            room.toggleMutePublishedCamera();
          } else {
            let track = await room.createVideoTrack();
            // console.log(track);
            await room.publish(track);
            this.$nextTick(() => {
              let teacherVideoEleSmall =
                this.$refs.studentVideoRef.$refs.teacherVideoRef;
              let videoTracksed = this.getFirstRtcTrack(
                room.publishedCameraTracks()
              );
              videoTracksed.play(teacherVideoEleSmall);
              teacherVideoEleSmall.querySelector('video').style["object-fit"] = "cover";
            });
          }
          this.videoStatus = !this.videoStatus
          break;
        case "shareScreen":
          // 获取已发布的共享屏幕
          let screenRracks = this.getFirstRtcTrack(
            room.publishedScreenTracks()
          );
          room.screenStatus = true
          // 如果有分享视频的track
          if (screenRracks) {
            room.toggleMutePublishedScreen();
          } else {
            let track = await room.createScreenTrack();
            await room.publish(track);
            this.$nextTick(() => {
              let tracherVideoEleBig =
                this.$refs.startLiveMain.$refs.startLiveVideoRef;
              let screenRracked = this.getFirstRtcTrack(
                room.publishedScreenTracks()
              );
              screenRracked.play(tracherVideoEleBig);
              tracherVideoEleBig.querySelector('video').style["object-fit"] = "cover";
              // 监听分享屏幕结束事件,以便关闭共享可以重新开启
              room.handleTrackEnded(track);
            });
          }
          this.screenStatus = !this.screenStatus
          break;
       case "changeDevice":
          if(deviceType == 'micDevice'){
           this.changeMicrophoneDevice(deviceId)
          }else if(deviceType == 'camDevice'){
              this.changeVideoDevice(deviceId)
          }
          break;
      }
    },

watch 中的监听事件

   "room.usersArr": {
      // 监听学生加入
      handler() {
        // 学生加入  添加学生的音视频到页面中
        // 根据当前学生的id 从学生列表中过滤出当前学生
        this.room.usersArr
          .filter((item) => item.id == this.room.currentUserId)
          .map((user) => {
            this.$nextTick(() => {
              let camera = undefined;
              let share = undefined;
              let audioTrack = undefined;
              // 根据学生列表的id 去播放学生的音视频
              let StuId = "#id-" + user.id;
              // 获取当前学生的 tracks 并区分开音视频和共享屏幕来
              let tracks = Array.from(user.tracks.values());
              // console.log(tracks,'-------------------------');
              let StuRef = document.querySelector(StuId);
              for (const track of tracks) {
                const tag = track.rtcTrack.tag;
                if (tag === "camera") {
                  camera = track;
                } else if (tag === "screen") {
                  share = track;
                } else if (track.rtcTrack.isAudio()) {
                  audioTrack = track;
                }
              }
              if (audioTrack != undefined) {
                audioTrack.rtcTrack.play(StuRef);
              }
              if (camera != undefined) {
                camera.rtcTrack.play(StuRef);
                // 将视频铺满
                StuRef.querySelector("video").style["object-fit"] = "cover";
              }
            });
          });
      },
    },
    "room.NetworkLv": {
      // 监听网络情况
      handler(values) {
        if (values == "极差") {
          // this.isShowNewWorkDialog = true;
        }
      },
    },
    "room.screenStatus": {
      // 屏幕分享状态
      handler(value) {
        if (value == false) {
          this.screenStatus = false;
        }
      },
    },
    "room.roomUserArr": {
      // 房间用户信息进出房间
      handler(val) {
        this.roomUserArr.push(val[val.length - 1]);
      },
    },
    "room.errorState": {
      // 监听用户直播状态 错误状态显示出错信息
      handler(val) {
        if (val == "KICKED_OUT" || val == "ERROR") {
          this.liveErrorStatus = true;
          // 错误信息文本没写 如果有需要再添加
          // this.liveStateText = ''
        }
      },
    },
    "im.cmdMessageMsg": {
      // 消息透传 这样就能监听到im中消息的变化了
      handler(values) {
        console.log(values, "监听命令消息1111");
        let type = values.ext.type;
        let stuId = values.from;
        let stuName = this.getUserName(stuId);
        switch (type) {
          // 举手
          case "raiseHand":
            room.changeHandStatus(stuId, true);
            this.roomUserArr.push(stuName + "发起举手");
            break;
          // 放下手
          case "dropHand":
            room.changeHandStatus(stuId, false);
            this.roomUserArr.push(stuName + "结束举手");
            break;
        }
      },
    },
引入 room 后
import room from "@/views/teacher/live/qiniu/models/room.js";
要在data中添加 添加后才能用watch监听到room里的数据变化
room: room,


/**
 * 包裹 qnweb-rtc 的 QNTrack 属性
 */
 
export default class Track {
   rtcTrack;

   muted;

   trackID;

   tag;

   userID;

   constructor(track) {
    this.rtcTrack = track;
    this.muted = track.isMuted();
    this.trackID = track.trackID;
    this.tag = track.tag;
    this.userID = track.userID;
  }

   updateTrack() {
    this.muted = this.rtcTrack.isMuted();
    this.trackID = this.rtcTrack.trackID;
    this.tag = this.rtcTrack.tag;
    this.userID = this.rtcTrack.userID;
  }

   toggleMute() {
    if (!(this.rtcTrack)) return;
    this.rtcTrack.setMuted(!this.muted);
    this.updateTrack();
  }
}

/**
 * 包裹 qnweb-rtc 的 QNRemoteUser 对象
 */
export default class User {
    rtcUser;

    id;

    tracks = new Map();


    publishedTracks = new Map();

    constructor(user) {
        this.rtcUser = user;
        this.id = user.userID;
    }


    addPublishedTrack(track) {
        this.publishedTracks.set(track.trackID, track);
    }


    removePublishedTrack(track) {
        this.publishedTracks.delete(track.trackID);
    }


    updateTracks(tracks) {
        for (const track of tracks) {
            this.tracks.set(track.trackID, track);
        }
    }


    updateTrack(key, v) {
        this.tracks.delete(key);
        this.tracks.set(key, v);
    }

}

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值