腾讯云实时音视频
import { Injectable } from '@angular/core'; import { Observable, Subject, BehaviorSubject } from 'rxjs'; import { UserService } from './user.service'; declare var $: any; declare var TRTC: any; @Injectable({ providedIn: 'root' }) export class TrtcService { sdkAppId: number; userSig: string; cameras: Array<any> = []; microphones: Array<any> = []; cameraId: any; microphoneId: any; client: any = null; public isJoined: boolean = false; public IsInitDevice: boolean = false; public initDeviceMsg: string = "正在检测设备中,请稍后再试"; config: TRTCConfig; constructor(private userService: UserService) { this.config = new TRTCConfig(); } /* * 监听进房/退房的用户 */ socketSub: Subject<any> = new BehaviorSubject<any>(null); /* * 初始化配置 */ init() { this.initDevice(); } /* * 生成用户签名 */ generateUserSig() { return new Promise((resolve, reject) => { var that = this; this.userService.getTrtcUserSig({ Id: this.config.userId, Remark: this.config.fkTb }).subscribe(result => { if (result.code == 100 && result.data) { that.sdkAppId = result.data.SdkAppId; that.userSig = result.data.UserSig; } else { alert.bind('生成签名失败,请联系管理员'); } resolve(true); }); }); } /* * 创建 Client 对象 */ async createClient() { if (this.client) { return; } try { this.client = TRTC.create(); this.installEventHandlers(); console.log(`Client [${this.config.userId}] created.`); } catch (e) { alert(`Failed to create Client [${this.config.userId}].`); } } /* * 进房 */ async join() { if (this.isJoined) { console.warn(`has been joined ${this.config.userId}`); return; } if (this.client == null) { await this.createClient(); } if (!this.userSig) { await this.generateUserSig(); if (!this.userSig) { alert.bind('生成签名失败,请联系管理员'); return; } } try { await this.client.enterRoom({ strRoomId: this.config.roomId, sdkAppId: this.sdkAppId, userId: this.config.userId, userSig: this.userSig }); this.isJoined = true; if (this.config.video) this.setLocalVideo(true); if (this.config.audio) this.setLocalAudio(true); } catch (e) { console.error(`Join room ${this.config.roomId} failed, please check your params. Error: ${e.message}`); this.reportTrtcflow('Join room failed, please check your params. Error:', '2joinRoom', e); } } /* * 退房 */ async leave() { if (!this.isJoined) { console.warn('leave() - please join() firstly'); return; } try { await this.client.exitRoom(); this.isJoined = false; if (this.config.video) this.setLocalVideo(false); if (this.config.audio) this.setLocalAudio(false); } catch (error) { console.error(`Leave room failed. Error: ${error.message_}`); this.reportTrtcflow('leaveRoom:', '5leaveRoom', error); } } /* * 监听事件 * https://web.sdk.qcloud.com/trtc/webrtc/v5/doc/zh-cn/module-EVENT.html */ installEventHandlers() { this.client.on(TRTC.EVENT.KICKED_OUT, this.handleKickedOut.bind(this)); this.client.on(TRTC.EVENT.REMOTE_USER_ENTER, this.handleRemoteUserEnter.bind(this)); this.client.on(TRTC.EVENT.REMOTE_USER_EXIT, this.handleRemoteUserExit.bind(this)); this.client.on(TRTC.EVENT.REMOTE_VIDEO_AVAILABLE, this.handleRemoteVideoAvailable.bind(this)); this.client.on(TRTC.EVENT.REMOTE_VIDEO_UNAVAILABLE, this.handleRemoteVideoUnavailable.bind(this)); this.client.on(TRTC.EVENT.REMOTE_AUDIO_AVAILABLE, this.handleRemoteAudioAvailable.bind(this)); this.client.on(TRTC.EVENT.REMOTE_AUDIO_UNAVAILABLE, this.handleRemoteAudioUnavailable.bind(this)); this.client.on(TRTC.EVENT.SCREEN_SHARE_STOPPED, this.handleScreenShareStopped.bind(this)); } uninstallEventHandlers(offAll: boolean = false) { if (offAll) { this.client.off('*'); } else { this.client.off(TRTC.EVENT.KICKED_OUT, this.handleKickedOut.bind(this)); this.client.off(TRTC.EVENT.REMOTE_USER_ENTER, this.handleRemoteUserEnter.bind(this)); this.client.off(TRTC.EVENT.REMOTE_USER_EXIT, this.handleRemoteUserExit.bind(this)); this.client.off(TRTC.EVENT.REMOTE_VIDEO_AVAILABLE, this.handleRemoteVideoAvailable.bind(this)); this.client.off(TRTC.EVENT.REMOTE_VIDEO_UNAVAILABLE, this.handleRemoteVideoUnavailable.bind(this)); this.client.off(TRTC.EVENT.REMOTE_AUDIO_AVAILABLE, this.handleRemoteAudioAvailable.bind(this)); this.client.off(TRTC.EVENT.REMOTE_AUDIO_UNAVAILABLE, this.handleRemoteAudioUnavailable.bind(this)); this.client.off(TRTC.EVENT.SCREEN_SHARE_STOPPED, this.handleScreenShareStopped.bind(this)); } } handleKickedOut(event) { const { userId } = event; this.socketSub.next({ Id: this.config.userId, IsJoined: false }); console.log('KickedOut ' + userId); } handleRemoteUserEnter(event) { const { userId } = event; this.socketSub.next({ Id: userId, IsJoined: true }); console.log("handleRemoteUserEnter " + event); } handleRemoteUserExit(event) { const { userId } = event; this.socketSub.next({ Id: userId, IsJoined: false }); console.log('UserExit ' + userId); } handleRemoteVideoAvailable(event) { var that = this; const { userId, streamType } = event; if (that.config.subscribeUser.length == 0 || that.config.subscribeUser.indexOf(userId) > -1) { setTimeout(function () { that.setRemoteVideo(userId, true, streamType); console.log('RemoteVideoAvailable ' + userId); }, 10); } } handleRemoteVideoUnavailable(event) { const { userId, streamType } = event; this.setRemoteVideo(userId, false, streamType); console.log('RemoteVideoUnavailable ' + userId); } handleRemoteAudioUnavailable(event) { } handleRemoteAudioAvailable(event) { } handleScreenShareStopped() { } /* * 设置本地音频(开/关) * https://web.sdk.qcloud.com/trtc/webrtc/v5/doc/zh-cn/tutorial-15-basic-dynamic-add-video.html */ setLocalAudio(mute: boolean = true) { if (mute) { this.client.startLocalAudio(); } else { this.client.stopLocalAudio(); } } /* * 设置本地视频(开/关) */ setLocalVideo(mute: boolean = true) { if (mute) { this.client.startLocalVideo({ view: `video-${this.config.userId}`}); } else { this.client.stopLocalVideo(); } } /* * 设置本地视频流编码,120p为小流 * @param isSmall 表示是否拉取小流 */ setLocalVideoProfile(profile: number = 120) { if (profile == 120) { this.client.updateLocalVideo({ option: { small: `${profile}p` } }); } else { this.client.updateLocalVideo({ option: { small: false, profile: `${profile}p` } }); } } /* * 设置远端视频(开/关) */ setRemoteVideo(userId: string, mute: boolean = true, streamType: string = "main") { if (mute) { this.client.startRemoteVideo({ userId, streamType, view: `video-${userId}`, option: { small: this.config.remoteVideoSmall } }); } else { this.client.stopRemoteVideo({ userId, streamType }); } } /* * 静音远端用户 * @param userId 用户ID,*表示所有用户 * @param mute 表示是否静音 */ muteRemoteAudio(userId: string, mute: boolean) { this.client.muteRemoteAudio(userId, mute); } /* * 设置远端用户音量 * @param userId 用户ID,*表示所有用户 * @param volume 音量大小,取值范围为0 - 100,默认值为 100 */ setRemoteAudioVolume(userId: string, volume: number) { this.client.setRemoteAudioVolume(userId, volume); } /* * 动态开关远端视频小流 * @param userId 用户ID,*表示所有用户 * @param isSmall 表示是否拉取小流 */ smallRemoteVideo(userId: string, isSmall: boolean = false) { this.client.updateRemoteVideo({ userId, streamType: "main", option: { small: isSmall } }); } /* * 检查设备 */ async initDevice() { try { let that = this; await navigator.mediaDevices.getUserMedia({ audio: this.config.audio, video: this.config.video }).then(function (stream) { stream.getTracks().forEach(track => track.stop()); that.reportTrtcflow('', '0initDevice', null); }); this.IsInitDevice = true; console.log("检查设备完成"); } catch (err) { this.IsInitDevice = false; console.error(`${err.name}: ${err.message}`); switch (err.name) { case "NotFoundError": case "DevicesNotFoundError": this.initDeviceMsg = "未找到麦克风或摄像头,无法开启视频会议。"; break; case "NotReadableError": case "TrackStartError": this.initDeviceMsg = "麦克风和摄像头被使用,开启视频将会失败。"; break; case "OverconstrainedError": case "ConstraintNotSatisfiedErrror": this.initDeviceMsg = "获取麦克风或摄像头失败,请联系系统管理员。"; break; case "NotAllowedError": case "PermissionDeniedError": this.initDeviceMsg = "如果不允许当前页面访问麦克风和摄像头权限,您在发布本地流的时候可能会失败。"; break; default: this.initDeviceMsg = "如果不允许当前页面访问麦克风和摄像头权限,您在发布本地流的时候可能会失败。"; break; } confirm.bind(this.initDeviceMsg); this.reportTrtcflow(`摄像头或麦克风权限出错. Error: `, '0initDevice', err); } } /* * 更新设备 */ async updateDevice() { try { const updateDevice = async () => { this.cameras = await TRTC.getCameraList(); this.microphones = await TRTC.getMicrophoneList(); console.log("cameras", this.cameras); console.log("microphones", this.microphones); } await updateDevice(); // 设备更新 navigator.mediaDevices.addEventListener('devicechange', async () => { await updateDevice(); }); } catch (e) { console.error('get device failed', e); } } /* * 切换视频设备 */ async switchDeviceVideo(videoId) { if (videoId) { try { await this.client.updateLocalVideo({ option: { cameraId: videoId } }); console.log('Switch video device success'); } catch (error) { console.error('switchDevice failed', error); } } } /* * 切换音频设备 */ async switchDeviceAudio(audioId) { if (audioId) { try { await this.client.updateLocalAudio({ option: { microphoneId: audioId } }); console.log('Switch audio device success'); } catch (error) { console.error('switchDevice failed', error); } } } /** * 上报事件到自己的日志平台,如果是正式发布 * @param msg * @param mode 类型:初始化本地语音流、视频流等等 */ reportTrtcflow(msg: string, mode: string, error: any) { if (mode && mode.length) { let errmsg = ''; if (error) { errmsg = error.message_ ? error.message_ : error.message; } let errcode = ''; if (error) { if (error.getCode) { errcode = error.getCode(); } } let name = ''; if (error) { if (error.name) { name = error.name; } } let fixedRoom = ''; if (this.config) { fixedRoom = `${this.config.roomId}::${this.config.userId}`; } let data = `${fixedRoom}::${mode}::${msg} ${errmsg} :: ${errcode} :: ${name}`; this.userService.addReportTrtc({ NotTitle: data }); } } } @Injectable({ providedIn: 'root' }) export class TRTCConfig { userId: string;//用户id(必填) roomId: string;//房间号(必填) audio: boolean = true;// true 表示开启语音 video: boolean = true;// true 表示开启视频 isSubscribe: boolean = true;// true 表示订阅远端视频流 subscribeUser: Array<any> = [];//为空表示订阅全部,有值表示订阅指定部分 remoteVideoSmall: boolean = true;//拉流端选择拉大流或小流 true 表示拉小流,false 表示拉大流 fkTb: string = "notice";//验证 }