基于阿里云的混流直播
这里和上次的摄像头与直播共享无区别,但是这里只需要一个阿里云的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)
},
}
})