// 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);
}
}