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>
<span class="streamType" style="display: none;">
<label for="cameraPublisher">推视频流</label>
<input
id="cameraPublisher"
value="cameraPublisher"
type="checkbox"
name="streamType"
checked
/>
<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 +" <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>