腾讯云实时音视频

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";//验证
}

 

posted on 2024-06-05 10:16  夕颜博客  阅读(11)  评论(0编辑  收藏  举报

导航