283 lines
9.5 KiB
HTML
283 lines
9.5 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en" dir="ltr">
|
|
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no">
|
|
<meta content="telephone=no" name="format-detection">
|
|
<title>头歌考试系统</title>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
#id_local_video {
|
|
position: absolute;
|
|
width: 100%;
|
|
height: 100%;
|
|
z-index: 3;
|
|
display: none;
|
|
|
|
}
|
|
|
|
#id_local_video video {
|
|
height: 100%;
|
|
width: 100%;
|
|
object-fit: cover !important;
|
|
}
|
|
|
|
button {
|
|
position: relative;
|
|
width: 69%;
|
|
height: 44px;
|
|
line-height: 44px;
|
|
font-size: 16px;
|
|
text-align: center;
|
|
margin-top: 60px;
|
|
background: linear-gradient(90deg, #74C2FF 0%, #2EA4FF 100%);
|
|
-webkit-appearance: none;
|
|
border-radius: 44px;
|
|
border: none;
|
|
color: #FFF;
|
|
outline: none;
|
|
}
|
|
|
|
.wrp {
|
|
text-align: center;
|
|
}
|
|
|
|
.wrp img {
|
|
width: 69%;
|
|
margin-bottom: 25px;
|
|
margin-top: 60px;
|
|
}
|
|
|
|
#model {
|
|
display: none;
|
|
position: fixed;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: rgba(0, 0, 0, .6);
|
|
z-index: 200;
|
|
|
|
}
|
|
|
|
#model>div {
|
|
width: 80%;
|
|
height: 162px;
|
|
background: #FFF;
|
|
margin-left: 10%;
|
|
position: absolute;
|
|
top: 50%;
|
|
margin-top: -81px;
|
|
border-radius: 6px;
|
|
|
|
}
|
|
|
|
#model .con {
|
|
height: 120px;
|
|
padding: 0 15px;
|
|
text-align: center;
|
|
color: #333;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 16px;
|
|
|
|
}
|
|
|
|
#model .btn {
|
|
font-size: 14px;
|
|
height: 40px;
|
|
line-height: 40px;
|
|
text-align: center;
|
|
border-top: 1px solid #eee;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<script src="https://imgcache.qq.com/open/qcloud/live/webrtc/js/TXLivePusher-1.0.2.min.js" charset="utf-8"></script>
|
|
<div id="model">
|
|
<div>
|
|
<div class="con" id="text"></div>
|
|
<div class="btn" onclick="location.reload()">确定</div>
|
|
</div>
|
|
</div>
|
|
<div id="id_local_video" style="width:100%;display:none;align-items:center;justify-content:center;">
|
|
</div>
|
|
<div class="wrp">
|
|
<img src="/images/user_camera.png" />
|
|
<div class="tip">请按图示调整手机录制角度!</div>
|
|
<button onclick="liveVideo()">开始录制</button>
|
|
</div>
|
|
<script>
|
|
let liveUrl;
|
|
let timer;
|
|
let API_SERVER = "";
|
|
if (document.domain === "www.educoder.net" || document.domain === "kepukehuan.educoder.net" || document.domain === "localhost") {
|
|
API_SERVER = "data"
|
|
} else {
|
|
API_SERVER = document.domain.split(".")[0] + "-data"
|
|
}
|
|
var Cookie = {
|
|
get: function (k) {
|
|
return ((new RegExp(["(?:; )?", k, "=([^;]*);?"].join(""))).test(document.cookie) && RegExp["$1"]) || "";
|
|
},
|
|
set: function (k, v, e, d) {
|
|
var date = new Date();
|
|
var expiresDays = e;
|
|
date.setTime(date.getTime() + expiresDays * 24 * 3600 * 1000);
|
|
document.cookie = k + "=" + v + "; expires=" + (e != '' ? date.toGMTString() : "GMT_String") + ";path=/;domain=" + (d || document.domain);
|
|
|
|
},
|
|
del: function (k, d) {
|
|
document.cookie = k + "=; expires=Thu, 01 Jan 1970 00:00:01 GMT;path=/;domain=" + (d || _options.domain);
|
|
document.cookie = k + "=v; expires=Thu, 01 Jan 1970 00:00:01 GMT;path=/";
|
|
}
|
|
};
|
|
var pattern = /(\w+)=([^\#&]*)/ig;
|
|
var parames = {};
|
|
location.href.replace(pattern, function (attr, key, value) {
|
|
parames[key] = decodeURI(value);
|
|
});
|
|
Cookie.set("_educoder_session", parames.cookie)
|
|
Cookie.set("_educoder_session", parames.cookie, 1, "educoder.net")
|
|
console.log("parames:", parames)
|
|
async function getData() {
|
|
const res = await fetch("https://" + API_SERVER + ".educoder.net" + "/api/exercises/" + parames.id + "/video_push_url.json?login=" + parames.login, {
|
|
method: 'GET',
|
|
credentials: "include",
|
|
mode: "cors"
|
|
|
|
}).then(r => r.json())
|
|
liveUrl = res.url;
|
|
// window.liveVideo(res.url);
|
|
console.log("res:", res)
|
|
}
|
|
getData()
|
|
|
|
async function saveData() {
|
|
const res = await fetch("https://" + API_SERVER + ".educoder.net" + "/api/exercises/" + parames.id + "/save_video_url.json?login=" + parames.login, {
|
|
method: 'POST',
|
|
credentials: "include",
|
|
body: JSON.stringify({
|
|
video_url: "https://live-stream.educoder.net/live/" + parames.id + "-" + parames.login + ".m3u8",
|
|
exercise_user_id: parames.login
|
|
}),
|
|
headers: {
|
|
Accept: 'application/json',
|
|
'Content-Type': 'application/json; charset=utf-8',
|
|
},
|
|
mode: "cors"
|
|
|
|
}).then(r => r.json())
|
|
}
|
|
</script>
|
|
<script type="text/javascript">
|
|
var dom = document.getElementById("model");
|
|
var text = document.getElementById("text");
|
|
|
|
|
|
const pushError = function (e) {
|
|
switch (e) {
|
|
case -1:
|
|
text.innerHTML = ("WebRTC 接口调用失败");
|
|
break;
|
|
case -2:
|
|
text.innerHTML = ("请求服务器推流接口返回报错");
|
|
break;
|
|
case -1001:
|
|
text.innerHTML = ("打开摄像头失败")
|
|
break;
|
|
case -1002:
|
|
text.innerHTML = ("打开麦克风失败")
|
|
break;
|
|
case -1005:
|
|
text.innerHTML = ("摄像头被中断(设备被拔出或者权限被用户取消)")
|
|
break;
|
|
case -1006:
|
|
text.innerHTML = ("麦克风被中断(设备被拔出或者权限被用户取消)")
|
|
break;
|
|
default:
|
|
text.innerHTML = ("推流错误,请刷新后再试")
|
|
break;
|
|
|
|
}
|
|
dom.style.display = "block"
|
|
}
|
|
|
|
window.addEventListener("offline", function () {
|
|
text.innerHTML = ("您的网络已断开,请连接网络后再试")
|
|
dom.style.display = "block"
|
|
})
|
|
|
|
window.addEventListener("netchange",function(){
|
|
text.innerHTML = ("您的网络已切换,点击确定后重新开始录制")
|
|
dom.style.display = "block"
|
|
})
|
|
|
|
function liveVideo(url) {
|
|
// liveUrl = 'live-stream-push.educoder.net/live/11?txSecret=0b401937c270954c127c8ad249d9030c&txTime=6243F6AD'
|
|
url = url || liveUrl
|
|
if (!liveUrl) {
|
|
alert("未获取到推流地址")
|
|
return;
|
|
}
|
|
|
|
try {
|
|
window.livePusher = new TXLivePusher();
|
|
livePusher.setRenderView('id_local_video');
|
|
livePusher.setVideoQuality('180p');
|
|
livePusher.setAudioQuality('standard');
|
|
livePusher.setProperty('setVideoFPS', 25);
|
|
livePusher.startCamera();
|
|
livePusher.startMicrophone();
|
|
var hasVideo = false;
|
|
var hasAudio = false;
|
|
var isPush = false;
|
|
livePusher.setObserver({
|
|
onError: function (e) {
|
|
// pushError(e)
|
|
livePusher.startPush('webrtc://' + url);
|
|
setTimeout(() => {
|
|
// console.log("重新推流1")
|
|
// livePusher.startPush('webrtc://' + url);
|
|
}, 1500)
|
|
},
|
|
onCaptureFirstAudioFrame: function () {
|
|
hasAudio = true;
|
|
if (hasVideo && !isPush) {
|
|
isPush = true;
|
|
saveData()
|
|
livePusher.startPush('webrtc://' + url);
|
|
}
|
|
},
|
|
onCaptureFirstVideoFrame: function () {
|
|
hasVideo = true;
|
|
if (hasAudio && !isPush) {
|
|
isPush = true;
|
|
saveData()
|
|
livePusher.startPush('webrtc://' + url);
|
|
}
|
|
}
|
|
});
|
|
document.getElementById("id_local_video").style.display = "block";
|
|
timer = setInterval(() => {
|
|
if (!livePusher.isVideoPushing) {
|
|
console.log("重新推流")
|
|
livePusher.stopPush();
|
|
livePusher.startPush('webrtc://' + url);
|
|
}
|
|
}, 10 * 1000)
|
|
} catch (e) {
|
|
debugger
|
|
alert(e)
|
|
}
|
|
}
|
|
</script>
|
|
</body>
|
|
|
|
</html> |