AliRTC2.5/Web/RtcSample/index.html (697 lines of code) (raw):

<!DOCTYPE html> <html> <head> <title>AliWebRTC Demo</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> <link rel="stylesheet" href="./index.css" /> <script src="https://code.jquery.com/jquery-1.10.2.min.js"></script> <!-- 请从官网下载最新sdk,添加到同层目录,打开下方注释 --> <!-- <script src="./aliyun-webrtc-sdk.js"></script> --> <script src="./sha256.js"></script> <script src="./utils.js"></script> </head> <body> <div class="login"> <div class="main"> <div class="main-title"> <span>音视频通信</span> </div> <div class="main-input"> <input type="text" placeholder="请输入房间号"> </div> <div class="main-button"> <button>进入房间</button> </div> </div> </div> <div class="main-web" style="display: none;"> <div class="remote-user-list"> <h2>房间成员</h2> <ul class="user-ul"></ul> </div> <div class="container-box"> <div class="local-display-name"> <span class="username">User: <b></b></span> <span class="channelid">Channel Id: <b></b></span> <span class="streamstate">推流状态:<b></b></span> </div> <div class="publisher"> <button class="select-preview">关闭预览</button> <button class="push-stream">处理中...</button>&nbsp;&nbsp; <span class="streamType" style="display: none;"> <label for="cameraPublisher">推视频流</label> <input id="cameraPublisher" value="cameraPublisher" type="checkbox" name="streamType" checked />&nbsp; <label for="screenPublish">推共享流</label> <input id="screenPublish" value="screenPublish" type="checkbox" name="streamType" /> </span> </div> <div class="local-video"> <video autoplay playsinline></video> </div> </div> <div class="video-container"></div> </div> <div class="alert"></div> </body> </html> <script> /** * 请找到 GenerateAliRtcAuthInfo方法,配置appId、key */ var channelId; var userName = Math.random().toString(36).replace(/[^a-z]+/g, "").substr(0, 5); var publisherList = []; var aliWebrtc; try{ aliWebrtc = new AliRtcEngine(); support(); }catch(error){ console.log(error) alert("请从官网下载最新SDK,添加到同层目录,取消第11行注释") } /** * isSupport webrtc能力检测 */ function support() { aliWebrtc.isSupport().then(re => { console.log(re); init(); }).catch(error => { alert(error.message); }) } function init() { /** * remote用户加入房间 onJoin * 更新在线用户列表 */ aliWebrtc.on("onJoin", (publisher) => { if(publisher.userId){ updateUserList(); } //重置订阅状态 //默认订阅远端音频和视频大流,但需要调用subscribe才能生效 //这里取消默认订阅,根据需求进行订阅 aliWebrtc.configRemoteAudio(publisher.userId, false); aliWebrtc.configRemoteCameraTrack(publisher.userId, false, false); showAlert(publisher.displayName + "加入房间","success"); }); /** * remote流发布事件 onPublish * 将该用户新增到推流列表 * 若该用户已存在推流列表,则进行状态更新 */ aliWebrtc.on("onPublisher", (publisher) => { console.log("onPublisher", publisher); let index = publisherList.getIndexByProprety(publisher.userId, "userId"); if (index === -1) { //新增 publisherList.push(publisher); } else { //流状态更新 updatePublisherStream(publisher, index); } }); /** * remote流结束发布事件 onUnPublisher * 推流列表删除该用户 * 移除用户视图 * 初始化订阅状态 */ aliWebrtc.on("onUnPublisher", (publisher) => { console.log("onUnPublisher",publisher); detelePublisher(publisher.userId); removeDom(publisher.userId); initialization(publisher.userId); }); /** * 被服务器踢出或者频道关闭时回调 onBye */ aliWebrtc.on("onBye",(message) =>{ //1:被服务器踢出 //2:频道关闭 //3:同一个ID在其他端登录,被服务器踢出 var msg; switch (message.code) { case 1: msg = "被服务器踢出"; break; case 2: msg = "频道关闭"; break; case 3: msg = "同一个ID在其他端登录,被服务器踢出"; break; default: msg = "onBye"; } showAlert(msg,"danger"); }); /** * 错误信息 */ aliWebrtc.on("onError", (error) => { console.log(error) var msg = error && error.message ? error.message : error; if (msg && msg.indexOf("no session") > -1) { msg = "请重新登录:" + msg; } if (error.errorCode === 10011 || error.errorCode === 10012) { msg = error.errorCode === 10011 ? "屏幕共享被禁止" : "屏幕共享已取消"; setTimeout(() => { $("#screenPublish").removeAttr("checked"); getPublishState("danger"); }, 2000); } if(error.code == 15) { msg = "没有开启H5兼容"; } if(error.type === "publish") { // 提示用户网络状态不佳 console.log("推流断开 需要停止推流,然后重新推流"); // 先记录当前推流状态 var pubAudio = aliWebrtc.configLocalAudioPublish; var pubCamera = aliWebrtc.configLocalCameraPublish; var pubScreen = aliWebrtc.configLocalScreenPublish; // 设置取消推流 aliWebrtc.configLocalAudioPublish = false; aliWebrtc.configLocalCameraPublish = false; aliWebrtc.configLocalScreenPublish = false; aliWebrtc.publish().then(()=>{ console.log("推流断开取消推流成功"); aliWebrtc.configLocalAudioPublish = pubAudio; aliWebrtc.configLocalCameraPublish = pubCamera; aliWebrtc.configLocalScreenPublish = pubScreen; aliWebrtc.publish().then(()=>{ console.log("推流断开重新推流成功"); }).catch(err => { console.log("推流断开重新推流失败"); }) }).catch(err => { console.log("推流断开取消推流失败"); }) } if(error.type === "subscribe") { console.log("订阅断开 取消订阅该userId的所有订阅并移除所有该userId的dom"); //先记录当前用户的订阅状态 var subInfo = getSubscribeInfo(error.userId); //取消订阅状态 initialization(error.userId); aliWebrtc.subscribe(error.userId).then(re => { console.log("订阅断开 取消订阅成功"); aliWebrtc.configRemoteAudio(error.userId,subInfo.isSubAudio); aliWebrtc.configRemoteCameraTrack(error.userId,subInfo.isSubLarge,subInfo.isSubCamera); aliWebrtc.configRemoteScreenTrack(error.userId,subInfo.isSubScreen); aliWebrtc.subscribe(error.userId).then(re => { console.log("订阅断开 重新订阅成功") if($("#" + error.userId + "_camera")){ aliWebrtc.setDisplayRemoteVideo(error.userId,$("#" + error.userId + "_camera video")[0], 1) } if($("#" + error.userId + "_screen")){ aliWebrtc.setDisplayRemoteVideo(error.userId,$("#" + error.userId + "_screen video")[0], 2) } }).catch(err=>{ console.log("订阅断开 重新订阅失败"); detelePublisher(error.userId); removeDom(error.userId); }) }).catch(err => { console.log("订阅断开 取消订阅失败", err) detelePublisher(error.userId); removeDom(error.userId); }); } showAlert(msg,"danger") }); /** * 检测到用户离开频道 * 更新用户列表 * 移除用户视图 */ aliWebrtc.on("onLeave", (publisher) => { initialization(publisher.userId); updateUserList(); removeDom(publisher.userId); showAlert(publisher.displayName + "离开房间","success"); }) } /** * 加入房间 * 触发:输入房间号、单击加入房间按钮 * 更新页面信息 * 默认开启预览 * 获取鉴权信息 * 加入房间 * 本地默认自动推视频流(视频流 + 音频流) * 发布本地流 */ function joinroom() { $(".local-display-name .username b").text(userName); $(".local-display-name .channelid b").text(channelId); $(".local-display-name .streamstate b").text("当前未推流"); //1.预览 var localVideo = $(".local-video video"); aliWebrtc.startPreview(localVideo[0]).then((obj) => { }).catch((error) => { showAlert("[开启预览失败]" + error.message,"danger"); }); //2. 获取频道鉴权令牌参数 为了防止被盗用建议该方法在服务端获取 var authInfo = GenerateAliRtcAuthInfo(channelId); //3. 加入房间 默认推音频视频流 aliWebrtc.joinChannel(authInfo, userName).then(() => { showAlert( "加入房间成功", "success"); // 4. 发布本地流 aliWebrtc.configLocalAudioPublish = true; aliWebrtc.configLocalCameraPublish = true; aliWebrtc.publish().then((res) => { setTimeout(() => { console.log("发布流成功"); $(".push-stream").text("停止推流"); $(".streamType").show(); $(".local-display-name .streamstate b").text("视频流"); },2000) }, (error) => { $(".streamType").show(); showAlert("[推流失败]" + error.message, "danger"); }); }).catch((error) => { showAlert("[加入房间失败]" + error.message, "danger"); }) } /** * 更新在线用户列表 */ var updateUserList = () => { $(".user-ul").empty(); let userList = aliWebrtc.getUserList(); let frg = document.createDocumentFragment(); userList.map((user) => { let html = $("<li class='user-ul-li'>" + user.displayName + "<ul class='menu'></ul></li>"); $(html).bind("mouseover",user.userId,showUserMenu).bind("mouseleave",hideUserMenu); frg.append(html[0]); }) $(".user-ul").append($(frg)); } /* * 获取测试token * @param {*} channelId 频道号 * @return {object} authinfo */ var GenerateAliRtcAuthInfo = (channelId) => { var appId = "yourAppId"; // 修改为自己的appid 该方案仅为开发测试使用,正式上线需要使用服务端的AppServer var key = "yourKey"; // 修改为自己的key 该方案仅为开发测试使用,正式上线需要使用服务端的AppServer userId = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); });// 可以自定义 var timestamp = parseInt(new Date().getTime() / 1000 + 48 * 60 * 60); var nonce = 'AK-' + timestamp; var token = sha256(appId + key + channelId + userId + nonce + timestamp); return { appid:appId, userid:userId, timestamp:timestamp, nonce:nonce, token:token, gslb: ["https://rgslb.rtc.aliyuncs.com"], channel:channelId }; } /** * 获取当前remote用户的流菜单 */ var showUserMenu = (evt) => { let userId = evt.data; if(!$(event.target).eq(0).hasClass("user-ul-li")){ return } $(".menu").hide(); $(event.target).find(".menu").empty().show(); let userInfo = aliWebrtc.getUserInfo(userId); let streamTypeList = userInfo.streamConfigs.filter(item => { return item.state === "active"; }); var html = ""; if(streamTypeList.length == 0){ html = $("<li>该用户未推流</li>"); $(event.target).find(".menu").append(html[0]); }else{ var frg = document.createDocumentFragment() streamTypeList.map(item => { item.userId = userId; var labelName = ""; if(item.type === "video"){ switch (item.label) { case "sophon_video_camera_large": labelName = "视频流"; break; case "sophon_video_screen_share": labelName = "共享流"; break; case "sophon_audio": labelName = ""; break; default: labelName = ""; } } else { labelName = ""; } //将音频流或小流的标签不显示 if(labelName !== ""){ let subState = item.subscribed === true ? "取消订阅" : "订阅"; html = $("<li>"+ labelName +"&nbsp;<span>"+ subState +"</span></li>"); $(html).find("span").off("click").on("click", item, unSub); frg.append(html[0]); } }) $(event.target).find(".menu").append($(frg)); } } /** * 隐藏当前remote用户的流菜单 */ var hideUserMenu = () => { $(event.currentTarget).find(".menu").hide(); } /** * 订阅&取消订阅 */ var unSub = (evt) => { let v = evt.data; if(v.subscribed){ setConfigRemote(v.userId, v.label).then(re => { removeDom(v.userId, v.label); console.log("取消订阅"); }); }else { receivePublishManual(v).then(re => { creatDomAndshowRemoteVideo(v); console.log("订阅成功"); }); } $(".menu").hide(); } /** * 获取dom标签 设置video */ var creatDomAndshowRemoteVideo = (v) => { var dom = getDisplayRemoteVideo(v.userId, v.label); if (v.label != "sophon_video_screen_share") { aliWebrtc.setDisplayRemoteVideo(v.userId, dom, 1); } else { aliWebrtc.setDisplayRemoteVideo(v.userId, dom, 2); } } /** * 创建获取订阅的remote的video标签 */ var getDisplayRemoteVideo = function (userId, label) { var label = label === "sophon_video_camera_large" ? "camera" : "screen"; var id = userId + "_" + label; var videoWrapper = $("#" + id); if (videoWrapper.length == 0) { var userInfo = aliWebrtc.getUserList().filter(item => { return item.userId === userId; }) var displayName = userInfo[0].displayName; videoWrapper = $("<div class='remote-subscriber' id=" + id + "> <video autoplay playsinline></video><div class='display-name'></div></div>"); $(".video-container").append(videoWrapper); } videoWrapper.find(".display-name").text(displayName + "—" + label); return videoWrapper.find("video")[0]; } /** * 移除dom */ var removeDom = (userId, label) => { if(label === "sophon_audio") return if(userId) { if(!label){ $("#" + userId + "_camera").remove(); $("#" + userId + "_screen").remove(); }else { label = label === "sophon_video_camera_large" ? "camera" : "screen"; $("#" + userId + "_" + label).remove(); } } } /** * 取消订阅设置 */ var setConfigRemote = (userId, label) => { return new Promise((resolve, reject) => { //demo中只订阅大流 if (label === "sophon_video_camera_large") { aliWebrtc.configRemoteCameraTrack(userId, false, false); aliWebrtc.configRemoteAudio(userId,false); } else if (label === "sophon_video_screen_share") { aliWebrtc.configRemoteScreenTrack(userId, false); } aliWebrtc.subscribe(userId).then(re => { resolve(); }).catch(error => console.log("取消订阅失败", error)) }); } /** * 订阅设置 */ var receivePublishManual = (v) =>{ console.log("receivePublishManual订阅", v); return new Promise((resolve, reject) => { if (v.label === "sophon_video_camera_large") { console.log("订阅固定视频流"); aliWebrtc.configRemoteCameraTrack(v.userId, true, true); aliWebrtc.configRemoteAudio(v.userId, true); } else if (v.label === "sophon_video_screen_share") { console.log("订阅屏幕共享流"); aliWebrtc.configRemoteScreenTrack(v.userId, true); } aliWebrtc.subscribe(v.userId).then(re => { resolve(); }).catch((error) => { reject(error); showAlert("[subscribe失败]" + error.message, "danger"); }); }) } /** * 更新推流状态 * 当远端流发生变化时,通过onPublisher回调接收到信息 * 远端流不可用时其state值为inactive * 通过对比本地维护的publisherList来进行dom的删除 * 并且更新本地维护的publisherList */ var updatePublisherStream = (publisher,index) => { let oldStreamConfigs = JSON.parse(JSON.stringify(publisherList[index].streamConfigs)); let newStreamConfigs = publisher.streamConfigs; let subscribeInfo = getSubscribeInfo(publisher.userId); oldStreamConfigs.forEach((v, i, a) => { let newStream = newStreamConfigs.getObjByProprety(v.label, "label"); // 判断流状态改变了 但不确定我们是否订阅了该流 if (v.state != newStream.state) { console.log("流的状态变了" + v.label, v, v.type, ">"+ v.state + ">>" + newStream.state + ">", newStream, subscribeInfo); //并且要取消订阅某个流,不然就不能再次订阅了 subscribeInfo.subscribeInfoArr.forEach(sv => { if (v.label === sv.label) { console.log("setConfigRemote取消订阅调用[api]:subscribe", publisher.userId, sv.type, sv.label); setConfigRemote(publisher.userId, sv.type, sv.label).then(re => { // 移除dom removeDom(publisher.userId, v.label); }).catch(error => { console.error("流的状态变了重新订阅出问题", error); }); } }); } }); publisherList.splice(index, 1, publisher); } /** * 用户停止推流时 删除用户列表中该用户 */ var detelePublisher = (userId) => { let index = publisherList.getIndexByProprety(userId, "userId"); if (index != -1) { publisherList.splice(index, 1); this.detelePublisher(userId); } else { console.log("未找到之前的推流数据"); //删除推流用户 } } /** * 正在推流时,热切换进行republish操作 */ var rePublish = () => { if($(".publisher .push-stream").text() === "停止推流"){ $(".publisher .push-stream").text("处理中..."); $(".streamType").hide(); aliWebrtc.publish().then(re => { setTimeout(() => { getPublishState("success"); },2000); }).catch(error => { setTimeout(() => { getPublishState("danger"); }, 2000); }); } } /** * 根据推流状态设置当前推流UI */ var getPublishState = (type) => { var streamstate = $(".streamstate b").text() if(aliWebrtc.configLocalAudioPublish || aliWebrtc.configLocalCameraPublish || aliWebrtc.configLocalScreenPublish){ $(".publisher .push-stream").text("停止推流"); if(aliWebrtc.configLocalScreenPublish && aliWebrtc.configLocalCameraPublish){ streamstate = "视频流 + 共享流"; } else { if(aliWebrtc.configLocalScreenPublish) { streamstate = "共享流"; } else if(aliWebrtc.configLocalCameraPublish) { streamstate = "视频流"; } } } else { $(".publisher .push-stream").text("开始推流"); streamstate = "当前未推流"; } showAlert("推流状态:" + streamstate, type); $(".streamstate b").text(streamstate); $(".streamType").show(); } /** * 进入房间 */ $(".main-button button").click(() => { var value = $(".main-input input").val(); if(!value){ showAlert("请输入房间号","danger"); return } if(!aliWebrtc){ showAlert("isSupport失败,未能初始化aliWebrtc","danger"); return } channelId = value; joinroom(); $(".login").hide(); $(".main-web").show(); }) /** * 控制预览 */ $(".publisher .select-preview").click(function(e) { var localVideo = $(".local-video video"); if($(this).text() === "开启预览"){ $(this).text("处理中..."); aliWebrtc.startPreview(localVideo[0]).then((obj) => { setTimeout(() => { $(this).text("关闭预览"); },2500); }).catch((error) => { setTimeout(() => { $(this).text("开启预览"); },2500); showAlert("[关闭预览失败]" + error.message, "danger"); }); }else if($(this).text() === "关闭预览") { $(this).text("处理中..."); aliWebrtc.stopPreview().then((re) => { setTimeout(() => { $(this).text("开启预览"); },2500); }).catch((error) => { setTimeout(() => { $(this).text("关闭预览"); },2500); showAlert("[开启预览失败]" + error.message, "danger"); }); }else { return } }) /** * 控制推流选项 */ $(".publisher .streamType input").click(function(e) { var config = $(this).val(); var isChecked = $(this).prop("checked"); if(config === "cameraPublisher"){ if(isChecked){ aliWebrtc.configLocalAudioPublish = true; aliWebrtc.configLocalCameraPublish = true; } else { aliWebrtc.configLocalAudioPublish = false; aliWebrtc.configLocalCameraPublish = false; } }else { if(isChecked){ aliWebrtc.configLocalScreenPublish = true; } else { aliWebrtc.configLocalScreenPublish = false; } } //正在推流时可以热切换 rePublish(); }) /** * 处理推流 */ $(".publisher .push-stream").click(function(e) { if($(this).text() === "开始推流") { $(this).text("处理中...") $(".streamType").hide() aliWebrtc .publish() .then(re => { setTimeout(() => { getPublishState("success"); },2000); }) .catch(error => { $(this).text("开始推流"); $(".streamType").show(); showAlert( "[开始推流失败]" + error.message, "danger"); }); }else if($(this).text() === "停止推流") { $(this).text("处理中..."); $(".streamType").hide(); aliWebrtc.configLocalAudioPublish = false; aliWebrtc.configLocalCameraPublish = false; aliWebrtc.configLocalScreenPublish = false; aliWebrtc.publish().then((re)=>{ setTimeout(() => { $("input[type='checkbox']").removeAttr("checked"); $(".streamType").show(); $(this).text("开始推流"); $(".local-display-name .streamstate b").text("当前未推流"); },2000); } ,(error)=>{ $(".streamType").show(); showAlert( "[停止推流失败]" + error.message, "danger"); }); }else { return } }); /** * 页面刷新时调用离会 */ window.onbeforeunload = function (e) { if(!aliWebrtc){ showAlert("isSupport失败,未能初始化aliWebrtc","danger"); return } aliWebrtc.leaveChannel(); aliWebrtc.dispose(); }; </script>