导入数据库表和音视频demo

This commit is contained in:
2023-09-14 14:59:57 +08:00
parent c0ca154d31
commit 736c5376e0
157 changed files with 11044 additions and 0 deletions

View File

@@ -0,0 +1,286 @@
<html>
<head>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" type="text/css">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="stylesheet" href="https://code.getmdl.io/1.3.0/material.indigo-pink.min.css">
<script defer src="https://code.getmdl.io/1.3.0/material.min.js"></script>
<script src="../js/dialog-polyfill.js" type="text/javascript"></script>
<style>
html {
zoom: 90%;
}
body {
background: #e2e1e0;
text-align: center;
margin: 0px;
padding: 0px;
font-size: 9px;
color: #555;
font-family: Roboto;
text-align: -webkit-center;
}
table {
margin: 10px;
position: relative;
}
video {
object-fit: cover;
float: left;
background: #fff;
border-radius: 2px;
display: inline-block;
margin: 1rem;
position: relative;
width: 420px;
box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
transition: all 0.5s cubic-bezier(.25,.8,.25,1);
padding:1px;
bottom: 0px;
height: 315px;
}
#container {
top: 10px;
left: 10px;
margin: 0px;
padding: 0px;
width: 900px;
}
.container-video {
width: 50%;
float: left;
display: inline-block;
position: relative;
font-size: 24px;
top: 5px;
margin-bottom: 10px;
}
.gaugeChartContainer {
position: relative;
width: 190px;
height: 120px;
float: left;
padding: 10px;
}
.gaugeChart {
position: relative;
text-align: center;
}
.gaugeChart canvas {
position: absolute;
top: 0;
left: 0;
z-index: 0;
}
.gaugeChartLabel {
display: inline-block;
position: absolute;
float: left;
left: 0;
top: 55px;
width: 100%;
text-align: center;
color: #FFFFFF;
font-size: 24px;
font-weight: bold;
z-index: 1;
text-shadow: #333 0px 0px 2px;
}
.gaugeChartContainer {
position: relative;
font-size: 9px;
}
.gaugeChartTitle {
display: inline-block;
position: absolute;
float: left;
top: 0px;
left: 0;
width: 100%;
text-align: center;
color: #888;
font-weight: bold;
font-size: 12px;
}
.gaugeChartMin {
display: inline-block;
position: absolute;
float: left;
left: 0;
bottom: 10%;
width: 92%;
margin-left: 8%;
text-align: left;
color: #888;
font-weight: bold;
}
.gaugeChartMax {
display: inline-block;
position: absolute;
float: left;
left: 0;
bottom: 10%;
width: 95%;
margin-right: 5%;
text-align: right;
color: #888;
font-weight: bold;
}
td {
margin: 5px;
padding: 5px;
text-align: center;
}
.ready-dialog
{
width: 780px;
text-align: left;
}
.ready-dialog p
{
color: black;
font-size: 12pt;
}
.ready-dialog code
{
font-size: 12pt;
}
</style>
<script src="../js/gauge.min.js" type="text/javascript"></script>
</head>
<body>
<div id="container">
<div class="container-video">LOCAL
<video id="local"></video>
</div>
<div class="container-video">REMOTE
<video id="remote"></video>
</div>
<table>
<tr>
<td></td>
<td>FPS/4</td>
<td>FPS/2</td>
<td>FPS</td>
</tr>
<tr data-rid="a">
<td>High</td>
<td><button data-rid="a" data-tid="0" class="mdl-button mdl-js-button mdl-button--raised">Layer RID A T0</button></td>
<td><button data-rid="a" data-tid="1" class="mdl-button mdl-js-button mdl-button--raised">Layer RID A T1</button></td>
<td><button data-rid="a" data-tid="2" class="mdl-button mdl-js-button mdl-button--raised">Layer RID A T2</button></td>
</tr>
<tr data-rid="b">
<td>Medium</td>
<td><button data-rid="b" data-tid="0" class="mdl-button mdl-js-button mdl-button--raised">Layer RID B T0</button></td>
<td><button data-rid="b" data-tid="1" class="mdl-button mdl-js-button mdl-button--raised">Layer RID B T1</button></td>
<td><button data-rid="b" data-tid="2" class="mdl-button mdl-js-button mdl-button--raised">Layer RID B T2</button></td>
</tr>
<tr data-rid="c">
<td>Low</td>
<td><button data-rid="c" data-tid="0" class="mdl-button mdl-js-button mdl-button--raised">Layer RID C T0</button></td>
<td><button data-rid="c" data-tid="1" class="mdl-button mdl-js-button mdl-button--raised">Layer RID C T1</button></td>
<td><button data-rid="c" data-tid="2" class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored">Layer RID C T2</button></td>
</tr>
</table>
<div class="gaugeChartContainer">
<canvas id="g_s_w" class="gaugeChart" width="192" height="135" style="width: 160px; height: 100px;"></canvas>
<span id="s_s_w"class="gaugeChartLabel">- -</span>
<span class="gaugeChartTitle">Sent Width</span>
<span class="gaugeChartMin">0</span>
<span class="gaugeChartMax">640</span>
</div>
<div class="gaugeChartContainer">
<canvas id="g_s_h" class="gaugeChart" width="192" height="135" style="width: 160px; height: 100px;"></canvas>
<span id="s_s_h" class="gaugeChartLabel">- -</span>
<span class="gaugeChartTitle">Sent Height</span>
<span class="gaugeChartMin">0</span>
<span class="gaugeChartMax">480</span>
</div>
<div class="gaugeChartContainer">
<canvas id="g_s_f" class="gaugeChart" width="192" height="135" style="width: 160px; height: 100px;"></canvas>
<span id="s_s_f" class="gaugeChartLabel">- -</span>
<span class="gaugeChartTitle">Sent FPS</span>
<span class="gaugeChartMin">0</span>
<span class="gaugeChartMax">30</span>
</div>
<div class="gaugeChartContainer">
<canvas id="g_s_b" class="gaugeChart" width="192" height="135" style="width: 160px; height: 100px;"></canvas>
<span id="s_s_b" class="gaugeChartLabel">- -</span>
<span class="gaugeChartTitle">Sent kbps</span>
<span class="gaugeChartMin">0</span>
<span class="gaugeChartMax">1024</span>
</div>
<div class="gaugeChartContainer">
<canvas id="r_s_w" class="gaugeChart" width="192" height="135" style="width: 160px; height: 100px;"></canvas>
<span id="s_s_w" class="gaugeChartLabel">- -</span>
<span class="gaugeChartTitle">Recv Width</span>
<span class="gaugeChartMin">0</span>
<span class="gaugeChartMax">640</span>
</div>
<div class="gaugeChartContainer">
<canvas id="r_s_h" class="gaugeChart" width="192" height="135" style="width: 160px; height: 100px;"></canvas>
<span id="s_s_h" class="gaugeChartLabel">- -</span>
<span class="gaugeChartTitle">Recv Heigth</span>
<span class="gaugeChartMin">0</span>
<span class="gaugeChartMax">480</span>
</div>
<div class="gaugeChartContainer">
<canvas id="r_s_f" class="gaugeChart" width="192" height="135" style="width: 160px; height: 100px;"></canvas>
<span id="s_s_f" class="gaugeChartLabel">- -</span>
<span class="gaugeChartTitle">Recv FPS</span>
<span class="gaugeChartMin">0</span>
<span class="gaugeChartMax">30</span>
</div>
<div class="gaugeChartContainer">
<canvas id="r_s_b" class="gaugeChart" width="192" height="135" style="width: 160px; height: 100px;"></canvas>
<span id="s_s_b" class="gaugeChartLabel">- -</span>
<span class="gaugeChartTitle">Recv kbps</span>
<span class="gaugeChartMin">0</span>
<span class="gaugeChartMax">1024</span>
</div>
</div>
<dialog class="ready-dialog mdl-dialog">
<h4 class="mdl-dialog__title">Ready to test VP8 Simulcast layer selection?</h4>
<div class="mdl-dialog__content">
<p>
This demo allows you to test the P8 Simulcast layer selection</a>.
</p>
<p>
This uses standard WebRTC rid/encodings api, so it only works on firefox currently
</p>
</div>
<div class="mdl-dialog__actions">
<button type="button" class="ready mdl-button mdl-button mdl-button--raised mdl-button--accent">Ready!</button>
</div>
</dialog>
</body>
</html>
<script src="js/simulcast.js" type="text/javascript"></script>

View File

@@ -0,0 +1,427 @@
const url = "wss://"+window.location.hostname+":"+window.location.port;
let videoResolution = true;
//Get our url
const href = new URL (window.location.href);
if (href.searchParams.has ("video"))
switch (href.searchParams.get ("video").toLowerCase ())
{
case "1080p":
videoResolution = {
width: {min: 1920, max: 1920},
height: {min: 1080, max: 1080},
};
break;
case "720p":
videoResolution = {
width: {min: 1280, max: 1280},
height: {min: 720, max: 720},
};
break;
case "576p":
videoResolution = {
width: {min: 720, max: 720},
height: {min: 576, max: 576},
};
break;
case "480p":
videoResolution = {
width: {min: 640, max: 640},
height: {min: 480, max: 480},
};
break;
case "no":
videoResolution = false;
break;
}
var opts = {
lines: 12, // The number of lines to draw
angle: 0.15, // The length of each line
lineWidth: 0.44, // 0.44 The line thickness
pointer: {
length: 0.8, // 0.9 The radius of the inner circle
strokeWidth: 0.035, // The rotation offset
color: '#A0A0A0' // Fill color
},
limitMax: true,
colorStart: '#28c1d1', // Colors
colorStop: '#28c1d1', // just experiment with them
strokeColor: '#F0F0F0', // to see which ones work best for you
generateGradient: false,
gradientType: 0
};
var targets = document.querySelectorAll('.gaugeChart'); // your canvas element
var gauges = [];
for (var i=0;i<targets.length;++i)
{
gauges[i] = new Gauge(targets[i]).setOptions (opts); // create sexy gauge!
gauges[i].animationSpeed = 10000; // set animation speed (32 is default value)
gauges[i].set (0); // set actual value
}
var texts = document.querySelectorAll('.gaugeChartLabel');
var max = document.querySelectorAll('.gaugeChartMax');
max[0].innerText = gauges[0].maxValue = videoResolution.width ? videoResolution.width.max : 640;
max[1].innerText = gauges[1].maxValue = videoResolution.height ? videoResolution.height.max : 480;
max[2].innerText = gauges[2].maxValue = 30;
max[3].innerText = gauges[3].maxValue = 2048;
max[4].innerText = gauges[4].maxValue = videoResolution.width ? videoResolution.width.max : 640;
max[5].innerText = gauges[5].maxValue = videoResolution.height ? videoResolution.height.max : 480;
max[6].innerText = gauges[6].maxValue = 30;
max[7].innerText = gauges[7].maxValue = 2048;
var ssrcs;
function addVideoForStream(stream,muted)
{
//Create new video element
const video = document.querySelector (muted ? "#local" : "#remote");
//Set same id
video.streamid = stream.id;
//Set src stream
video.srcObject = stream;
//Set other properties
video.autoplay = true;
video.muted = muted;
}
//Get user media promise based
function getUserMedia(constrains)
{
return new Promise(function(resolve,reject) {
//Get it
navigator.getUserMedia(constrains,
function(stream){
resolve(stream);
},
function(error){
reject(error);
});
});
}
var pc;
let simulcast_03 = false;
let sdpMungling = false;
function connect()
{
//Create PC
pc = new RTCPeerConnection({sdpSemantics : "plan-b"});
var ws = new WebSocket(url,"simulcast");
pc.ontrack = function(event) {
var prev = 0,prevFrames = 0,prevBytes = 0;
console.debug("ontrack",event);
const stream = event.streams[0];
//Play it
addVideoForStream(stream);
//Get track
var track = stream.getVideoTracks()[0];
//Update stats
setInterval(async function(){
var results;
try {
//For ff
results = await pc.getStats(track);
} catch(e) {
//For chrome
results = await pc.getStats();
}
var width = track.width || remote.videoWidth;
var height = track.height || remote.videoHeight;
//Get results
for (let result of results.values())
{
if (result.type==="inbound-rtp")
{
//Get timestamp delta
var delta = result.timestamp-prev;
//Store this ts
prev = result.timestamp;
//Get values
var fps = (result.framesDecoded-prevFrames)*1000/delta;
var kbps = (result.bytesReceived-prevBytes)*8/delta;
//Store last values
prevFrames = result.framesDecoded;
prevBytes = result.bytesReceived;
//If first
if (delta==result.timestamp || isNaN(fps) || isNaN (kbps))
return;
for (var i=4;i<targets.length;++i)
gauges[i].animationSpeed = 10000000; // set animation speed (32 is default value)
gauges[6].set(Math.min(Math.floor(fps) ,30));
gauges[7].set(Math.min(Math.floor(kbps) ,gauges[7].maxValue));
texts[6].innerText = Math.floor(fps);
texts[7].innerText = Math.floor(kbps);
} else if (result.type==="track") {
//Update stats
width = result.frameWidth;
height = result.frameHeight;
}
}
gauges[4].set(width);
gauges[5].set(height);
texts[4].innerText = width;
texts[5].innerText = height;
},1000);
};
ws.onopen = function(){
console.log("opened");
navigator.mediaDevices.getUserMedia({
audio: false,
video: videoResolution
})
.then(function(stream){
var prev = 0;
var prevFrames = 0;
var prevBytes = 0;
var track = stream.getVideoTracks()[0];
console.debug("getUserMedia sucess",stream);
//Play it
addVideoForStream(stream,true);
//Update stats
setInterval(async function(){
var results;
try {
//For ff
results = await pc.getStats(track);
} catch(e) {
//For chrome
results = await pc.getStats();
}
var width = track.width || local.videoWidth;//result.stat("googFrameWidthReceived");
var height = track.height || local.videoHeight;//result.stat("googFrameHeightReceived");
//Get results
for (let result of results.values())
{
if (result.type==="outbound-rtp")
{
//Get timestamp delta
var delta = result.timestamp-prev;
//Store this ts
prev = result.timestamp;
//Get values
var fps = ((result.framesEncoded-prevFrames)*1000/delta);
var kbps = (result.bytesSent-prevBytes)*8/delta;
//Store last values
prevFrames = result.framesEncoded;
prevBytes = result.bytesSent;
//If first
if (delta==result.timestamp || isNaN(fps) || isNaN (kbps))
return;
for (var i=0;i<4;++i)
gauges[i].animationSpeed = 10000000; // set animation speed (32 is default value)
gauges[2].set(Math.min(Math.floor(fps) ,30));
gauges[3].set(Math.min(Math.floor(kbps) ,gauges[3].maxValue));
texts[2].innerText = Math.floor(fps);
texts[3].innerText = Math.floor(kbps);
} else if (result.type==="track") {
//Update stats
width = result.frameWidth;
height = result.frameHeight;
}
}
},1000);
window.s = stream;
//Add stream tracks to peer connection
stream.getTracks().forEach(track => pc.addTrack(track, stream));
//Check API "compatibility"
if (pc.getSenders()[0].setParameters)
{
try {
//Enable simulcast
pc.getSenders()[0].setParameters({
encodings: [
{ rid: "a"},
{ rid: "b" , scaleDownResolutionBy: 2.0 },
{ rid: "c" , scaleDownResolutionBy: 4.0 }
]
});
} catch(e) {
}
}
//Create new offer
return pc.createOffer();
})
.then(function(offer){
console.debug("createOffer sucess",offer);
//Get offer
let sdp = offer.sdp;
//Check simulcast 04 format
if (sdp.indexOf(": send rid"))
{
//Convert from simulcast_03 to simulcast
sdp = sdp.replace(": send rid=",":send ");
//We need to modify answer too
simulcast_03 = true;
}
//If offer doesn't have simulcast
if (sdp.indexOf("simulcast")==-1)
try {
//OK, chrome way
const reg1 = RegExp("m=video.*\?a=ssrc:(\\d*) cname:(.+?)\\r\\n","s");
const reg2 = RegExp("m=video.*\?a=ssrc:(\\d*) mslabel:(.+?)\\r\\n","s");
const reg3 = RegExp("m=video.*\?a=ssrc:(\\d*) msid:(.+?)\\r\\n","s");
const reg4 = RegExp("m=video.*\?a=ssrc:(\\d*) label:(.+?)\\r\\n","s");
//Get ssrc and cname
let res = reg1.exec(sdp);
const ssrc = res[1];
const cname = res[2];
//Get other params
const mslabel = reg2.exec(sdp)[2];
const msid = reg3.exec(sdp)[2];
const label = reg4.exec(sdp)[2];
//Add simulcasts ssrcs
const num = 2;
const ssrcs = [ssrc];
for (let i=0;i<num;++i)
{
//Create new ssrcs
const ssrc = 100+i*2;
const rtx = ssrc+1;
//Add to ssrc list
ssrcs.push(ssrc);
//Add sdp stuff
sdp += "a=ssrc-group:FID " + ssrc + " " + rtx + "\r\n" +
"a=ssrc:" + ssrc + " cname:" + cname + "\r\n" +
"a=ssrc:" + ssrc + " msid:" + msid + "\r\n" +
"a=ssrc:" + ssrc + " mslabel:" + mslabel + "\r\n" +
"a=ssrc:" + ssrc + " label:" + label + "\r\n" +
"a=ssrc:" + rtx + " cname:" + cname + "\r\n" +
"a=ssrc:" + rtx + " msid:" + msid + "\r\n" +
"a=ssrc:" + rtx + " mslabel:" + mslabel + "\r\n" +
"a=ssrc:" + rtx + " label:" + label + "\r\n";
}
//Conference flag
sdp += "a=x-google-flag:conference\r\n";
//Add SIM group
sdp += "a=ssrc-group:SIM " + ssrcs.join(" ") + "\r\n";
//Update sdp in offer without the rid stuff
offer.sdp = sdp;
//Add RID equivalent to send it to the sfu
sdp += "a=simulcast:send a;b;c\r\n";
sdp += "a=rid:a send ssrc="+ssrcs[2]+"\r\n";
sdp += "a=rid:b send ssrc="+ssrcs[1]+"\r\n";
sdp += "a=rid:c send ssrc="+ssrcs[0]+"\r\n";
//Disable third row
//document.querySelector("tr[data-rid='c']").style.display = 'none';
//Doing mungling
sdpMungling = true;
} catch(e) {
console.error(e);
}
//Set it
pc.setLocalDescription(offer);
console.log(sdp);
//Create room
ws.send(JSON.stringify({
cmd : "OFFER",
offer : sdp
}));
//Select simulcast layer
ws.send(JSON.stringify({
cmd : "SELECT_LAYER",
rid : "b",
spatialLayerId : 0,
temporalLayerId : 2
}));
})
.catch(function(error){
console.error("Error",error);
});
};
ws.onmessage = function(event){
console.log(event);
//Get protocol message
const msg = JSON.parse(event.data);
//Get sdp
let sdp = msg.answer;
//If offer was simulcast 04
if (simulcast_03)
//Conver it back
sdp = sdp.replace(": recv rid=",":recv ");
//if doing mungling
if (sdpMungling)
//Add custom flag and remove simulcast attirbute
sdp = sdp.replace(/a=sim.*\r\n/,"") + "a=x-google-flag:conference\r\n";
console.log(sdp);
pc.setRemoteDescription(new RTCSessionDescription({
type:'answer',
//Convert from simulcast to simulcast_03
sdp: sdp
}), function () {
console.log("JOINED");
}, function (err) {
console.error("Error joining",err);
}
);
var old = document.querySelector ('.mdl-button--colored');
var listener = function(event)
{
//Get data
var rid = event.target.dataset["rid"];
var temporalLayerId = event.target.dataset["tid"];
//Select simulcast layer
ws.send(JSON.stringify({
cmd : "SELECT_LAYER",
rid : rid,
spatialLayerId : 0,
temporalLayerId : temporalLayerId
}));
//Remove
event.target.classList.add("mdl-button--colored");
old.classList.remove("mdl-button--colored");
old = event.target;
};
var buttons = document.querySelectorAll('button');
for (var i = 0; i < buttons.length; i++)
buttons[i].addEventListener("click",listener);
};
}
var dialog = document.querySelector('dialog');
if (dialog.showModal)
{
dialog.showModal();
dialog.querySelector('.ready').addEventListener('click', function() {
dialog.close();
connect();
});
} else {
connect();
}