Skip to content

Commit

Permalink
Merge pull request #519 from aboba/main
Browse files Browse the repository at this point in the history
Add automated glass-glass latency calculation via RVFC
  • Loading branch information
jan-ivar authored Jul 18, 2023
2 parents 72763a0 + 0d7ea31 commit 4f04462
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 8 deletions.
10 changes: 7 additions & 3 deletions samples/webcodecs-echo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<meta id="theme-color" name="theme-color" content="#ffffff">
<base target="_blank">

<title>WebTransport Echo with WebCodecs in Worker</title>
<title>WebTransport Echo with WebCodecs in Worker + RVFC</title>
<link rel="stylesheet" href="css/main.css">

<style>
Expand Down Expand Up @@ -57,7 +57,7 @@

<body>
<div>
<h2>WebTransport Echo with WebCodecs in Worker</h2>
<h2>WebTransport Echo with WebCodecs in Worker + RVFC</h2>
<div class="input-line">
<label for="url">URL:</label>
<input type="text" name="url" id="url" size="40"
Expand Down Expand Up @@ -150,17 +150,21 @@ <h2>WebTransport Echo with WebCodecs in Worker</h2>

<div id="chart_div" style="width: 900px; height: 500px;"></div>

<div id="chart2_div" style="width: 900px; height: 500px;"></div>

<div class="select">
<label for="videoSource">Video source: </label><select id="videoSource"></select>
</div>

<video height="50%" id="inputVideo" autoplay muted></video>
<br/>Local Video</br>
<br/></br><video height="50%" id="outputVideo" autoplay muted></video>
<br/></br>
<video height="50%" id="outputVideo" autoplay muted></video>
<br/>Encoded (and Decoded) Video via the WT Echo Server</br>
<br/></br>
<button id="connect">Start</button>
<button id="stop">Stop</button>
<br/></br>
<script src="https://www.gstatic.com/charts/loader.js"></script>
<script src="js/main.js"></script>
</body>
Expand Down
102 changes: 97 additions & 5 deletions samples/webcodecs-echo/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ let latencyPref = "realtime", bitPref = "variable";
let hw = "no-preference";
let streamWorker;
let inputStream, outputStream;
let metrics = {
all: [],
};
let e2e = {
all: [],
};
const rate = document.querySelector('#rate');
const connectButton = document.querySelector('#connect');
const stopButton = document.querySelector('#stop');
Expand All @@ -17,9 +23,13 @@ const resButtons = document.querySelector('#resButtons');
const modeButtons = document.querySelector('#modeButtons');
const hwButtons = document.querySelector('#hwButtons');
const chart_div = document.getElementById('chart_div');
const chart2_div = document.getElementById('chart2_div');
const videoSelect = document.querySelector('select#videoSource');
const outputVideo = document.getElementById('outputVideo');
const inputVideo = document.getElementById('inputVideo');
const selectors = [videoSelect];
chart_div.style.display = "none";
chart2_div.style.display = "none";
connectButton.disabled = false;
stopButton.disabled = true;

Expand All @@ -36,6 +46,34 @@ const cinema4KConstraints = {video: {width: {exact: 4096}, height: {exact: 2160}
const eightKConstraints = {video: {width: {min: 7680}, height: {min: 4320}}};
let constraints = qvgaConstraints;

function metrics_update(data) {
metrics.all.push(data);
}

function metrics_report() {
metrics.all.sort((a, b) => {
return (100000 * (a.mediaTime - b.mediaTime) + a.output - b.output);
});
const len = metrics.all.length;
let j = 0;
for (let i = 0; i < len ; i++ ) {
if (metrics.all[i].output == 1) {
const frameno = metrics.all[i].presentedFrames;
const g2g = metrics.all[i].expectedDisplayTime - metrics.all[i-1].captureTime;
const mediaTime = metrics.all[i].mediaTime;
const captureTime = metrics.all[i-1].captureTime;
const expectedDisplayTime = metrics.all[i].expectedDisplayTime;
const delay = metrics.all[i].expectedDisplayTime - metrics.all[i-1].expectedDisplayTime;
const data = [frameno, g2g];
e2e.all.push(data);
}
}
// addToEventLog('Data dump: ' + JSON.stringify(e2e.all));
return {
count: e2e.all.length
};
}

function addToEventLog(text, severity = 'info') {
let log = document.querySelector('textarea');
log.value += 'log-' + severity + ': ' + text + '\n';
Expand Down Expand Up @@ -147,6 +185,7 @@ function stop() {
stopButton.disabled = true;
connectButton.disabled = true;
chart_div.style.display = "initial";
chart2_div.style.display = "initial";
streamWorker.postMessage({ type: "stop" });
try {
inputStream.cancel();
Expand Down Expand Up @@ -196,11 +235,11 @@ document.addEventListener('DOMContentLoaded', async function(event) {
if (e.data.severity != 'chart'){
addToEventLog('Worker msg: ' + e.data.text, e.data.severity);
} else {
//draw chart
google.charts.load('current', {'packages':['corechart']});
google.charts.setOnLoadCallback(() => {
let data = new google.visualization.DataTable();
//addToEventLog('Data dump: ' + e.data.text);
// draw rtt chart
// addToEventLog('Data dump: ' + e.data.text);
data.addColumn('number', 'Length');
data.addColumn('number', 'RTT');
data.addRows(JSON.parse(e.data.text));
Expand All @@ -213,7 +252,27 @@ document.addEventListener('DOMContentLoaded', async function(event) {
legend: 'none'
};
let chart = new google.visualization.ScatterChart(chart_div);
chart.draw(data, options);
chart.draw(data, options);
});
// draw the glass-glass latency chart
metrics_report();
google.charts.load('current', {'packages':['corechart']});
google.charts.setOnLoadCallback(() => {
let data = new google.visualization.DataTable();
// addToEventLog('Data dump: ' + JSON.stringify(e2e.all));
data.addColumn('number', 'Frame Number');
data.addColumn('number', 'Glass-Glass Latency (ms)');
data.addRows(e2e.all);
let options = {
width: 900,
height: 500,
title: 'Glass-Glass Latency (ms) versus Frame Number',
haxis: {title: 'Frame Number'},
vaxis: {title: 'Glass-Glass Latency'},
legend: 'none'
};
let chart = new google.visualization.ScatterChart(chart2_div);
chart.draw(data, options);
});
}
}, false);
Expand Down Expand Up @@ -258,7 +317,37 @@ document.addEventListener('DOMContentLoaded', async function(event) {
// WritableStream of VideoFrames, using non-standard Chrome API
const generator = new MediaStreamTrackGenerator({kind: 'video'});
outputStream = generator.writable;
document.getElementById('outputVideo').srcObject = new MediaStream([generator]);
outputVideo.srcObject = new MediaStream([generator]);

// Initialize variables
let paint_count = 0;
let start_time = 0.0;

const recordOutputFrames = (now, metadata) => {
metadata.output = 1.;
metadata.time = now;
if( start_time == 0.0 ) start_time = now;
let elapsed = (now - start_time)/1000.;
let fps = (++paint_count / elapsed).toFixed(3);
metadata.fps = fps;
metrics_update(metadata);
outputVideo.requestVideoFrameCallback(recordOutputFrames);
};

outputVideo.requestVideoFrameCallback(recordOutputFrames);

const recordInputFrames = (now, metadata) => {
metadata.output = 0;
metadata.time = now;
if( start_time == 0.0 ) start_time = now;
let elapsed = (now - start_time)/1000.;
let fps = (++paint_count / elapsed).toFixed(3);
metadata.fps = fps;
metrics_update(metadata);
inputVideo.requestVideoFrameCallback(recordInputFrames);
};

inputVideo.requestVideoFrameCallback(recordInputFrames);

//Create video Encoder configuration
const vConfig = {
Expand Down Expand Up @@ -296,7 +385,10 @@ document.addEventListener('DOMContentLoaded', async function(event) {
config.pt = 1;
break;
case "H265":
config.codec = "hvc1.1.6.L123.00"; // Main profile, level 4.1, main Tier
config.codec = "hev1.1.6.L120.B0"; // Main profile, level 4, up to 2048 x 1080@30
// config.codec = "hev1.1.4.L93.B0" Main 10
// config.codec = "hev1.2.4.L120.B0" Main 10, Level 4.0
// config.codec = "hev1.1.6.L93.B0"; // Main profile, level 3.1, up to 1280 x [email protected]
config.hevc = { format: "annexb" };
config.pt = 2;
break;
Expand Down

0 comments on commit 4f04462

Please sign in to comment.