基于阿里云的混流直播


基于阿里云的混流直播

这里和上次的摄像头与直播共享无区别,但是这里只需要一个阿里云的SDK实例,且底层只需要创建一个video,因此混流无法做小屏拖拽,但是它支持画中画,因此无需小屏拖拽

HTML

<!--
 注意:如果有判断展示与隐藏,这里不要使用v-if,不然底层不会帮你创建video实例,
 阿里云应该是在渲染时给你创建的,v-if是创建与销毁,无法在更新中再创建,可以使用v-show
-->
<div class="content" id="videoContainer">
        <span class="finishLive1" @click="doShare">分享</span>
        <span class="finishLive" @click="closeLive">结束</span>
</div>

Vue

new Vue({
    data() {
        return {
            pushClient:AliRTSPusher.createClient()// 推流实例
            urlTui:'', // 推流地址 
            cameraStreamId:null, // 摄像头
            screenStreamId:null, // 屏幕共享
            micStreamId:null,  // 音频
            videoEffectManager:null //视频管理器实例对象
        }
    },
    methods:{
        // 初始化
        initVideo() {
            // 将容器ID传给SDK,SDK会在其中创建一个video标签并播放预览画面
            const videoEl = this.pushClient.setRenderView('videoContainer');
            // 设置直播清晰度
            this.pushClient.setVideoQuality('720p_1');
        },
        // 推流
        async openLive(){
            // 通过接口获取推流地址
            let res = await syhtAjaxGet('/api/live/xxx');
            // 这个是打印成功的状态码
            this.pushClient.on("connectStatusChange", (event) => {
                console.log(event.status);
            });
            // 这里非常重要,监听错误的状态,10012状态码是用户关闭直播共享,10011状态码是用户取消共享选择
            this.pushClient.on("error", async (err) => {
                if (err.errorCode === "video device not found") {
                    console.log("video device not found")
                }
                // 当用户关闭共享时,我们要关闭屏幕共享,关闭之前的音频摄像头,再次开启第二次的音频摄像头
                if(err.errorCode === 10012) {
                    // 关闭共享,如果不传id,则是关闭全部共享,由于我们这个直播只有一个共享状态,则关闭全部即可
                    await this.pushClient.stopScreenCapture();
                    // 关闭指定的摄像头与音频,传入的值就是指定的
                    this.pushClient.stopCamera(this.cameraStreamId);
                    this.pushClient.stopMicrophone(this.micStreamId);
                    // 重新开启新的摄像头与音频
                    this.cameraStreamId = await this.pushClient.startCamera();
                    this.micStreamId = await this.pushClient.startMicrophone();
                    // 指定页面大小位置
                    this.videoEffectManager.setLayout([
                      { streamId: this.cameraStreamId, x: 1280/2, y: 720/2, width: 1280, height: 720, zOrder: 1 }
                    ]);
                    return // 直接退出函数,因为在第一次的时候已经进行一次推流了
                }
            });
            // 获取视频管理器实例对象
            this.videoEffectManager = this.pushClient.getVideoEffectManager();
            // 开启混流模式
            this.videoEffectManager.enableMixing(true);
            // 指定底层创建的video大小
            this.videoEffectManager.setMixingConfig({
              videoWidth: 1280,
              videoHeight: 720,
              videoFramerate: 30
            })
            // 第一次开启音频,摄像头,并复制,让后续可以关闭指定的音频摄像头
            this.cameraStreamId = await this.pushClient.startCamera();
            this.micStreamId = await this.pushClient.startMicrophone();
            // 由于初次只有摄像头和音频,并无共享,因此只指定第一次的直播流位置
            this.videoEffectManager.setLayout([
              { streamId: this.cameraStreamId, x: 1280/2, y: 720/2, width: 1280, height: 720, zOrder: 1 },
            ]);
            // 开始推流
            await this.pushClient.startPush(res.data.push_url).then(res=>{
                console.log("推流成功")
            })
            // 将推流地址定义,防止后续要推流再去请求接口
            this.urlTui = res.data?.push_url
        },
        //分享
        /*
        当我们点击分享时,我们要做的就是:
        1.关闭之前的摄像头,音频的推流
        2.开启屏幕分享
        3.判断用户是否取消共享,如果取消,让用户回到音频摄像头
        4.开启混流,指定两个位置,让大屏共享屏幕,小屏展示人像
        */
         async doShare() {
             // 关闭所有音频,视频
            this.pushClient.stopCamera();
            this.pushClient.stopMicrophone();
             // 关闭屏幕共享,防止上传屏幕共享存在,用户点击第二遍屏幕共享
            this.pushClient.stopScreenCapture(this.screenStreamId);
             // 开启新的音频,并赋值,后续用于指定位置
            this.cameraStreamId = await this.pushClient.startCamera();
            this.micStreamId = await this.pushClient.startMicrophone();
             // 开启屏幕共享,由于是一个Promise,可以在他成功之后指定位置
            this.screenStreamId = await this.pushClient.startScreenCapture(true).then(res=>{
                // 指定大小屏位置
              this.videoEffectManager.setLayout([
              { streamId: this.screenStreamId, x: 1280/2, y: 720/2, width: 1280, height: 720, zOrder: 1 },
              { streamId: this.cameraStreamId, x: 1190, y: 630, width: 180, height: 180, zOrder: 2 },
            ]);
            }).catch(async error=>{
                // 用户取消 10011
              if(error.errorCode === 10011){
                  // 关闭指定共享
                  this.pushClient.stopScreenCapture(this.screenStreamId)
                  // 开启摄像头音频
                  this.cameraStreamId = await this.pushClient.startCamera();
                  this.micStreamId = await this.pushClient.startMicrophone();
                  //指定位置
                  this.videoEffectManager.setLayout([
                      { streamId: this.cameraStreamId, x: 1280/2, y: 720/2, width: 1280, height: 720, zOrder: 1 }
                  ]);
              }
            })
             // 无论成功失败,都推流一次
             await this.pushClient.startPush(this.urlTui)
          },
    }
})

文章作者: 冷杨威
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 冷杨威 !
  目录
-->