blob: bc09d257efcfd97a03a02bc37903d6c61e4a1b23 [file] [log] [blame]
<!DOCTYPE html>
<html>
<head><title>GetUserMedia test</title></head>
<body>
<script src="third_party/ssim.js"></script>
<script src="third_party/blackframe.js"></script>
<script>
const resolutions = [[640, 480], [1280, 720]];
let isVideoInputFound = false;
let scriptReady = false;
let isTestDone = false;
let enumerateDevicesError = '';
const globalErrors = [];
const results = [];
const logs = [];
function addLog(msg) {
const curTime = new Date().toISOString();
logs.push(`[${curTime}] ${msg}`);
}
function testNextResolution(durationSec) {
addLog('testNextResolution');
const nextResolution = resolutions.shift();
if (nextResolution === undefined) {
reportTestDone();
return;
}
const test = new CameraTest(nextResolution, durationSec);
test.start();
}
function reportTestDone() {
console.log('tests completed');
isTestDone = true;
}
function getResults() {
return results;
}
function getLogs() {
return logs;
}
function resolutionMatchesIndependentOfRotation(aWidth, aHeight,
bWidth, bHeight) {
return (aWidth === bWidth && aHeight === bHeight) ||
(aWidth === bHeight && aHeight === bWidth);
}
function saveResult(resolution, verdict) {
results.push(verdict);
}
// Check if a video input exists
function checkVideoInput() {
addLog('checkVideoInput');
navigator.mediaDevices.enumerateDevices()
.then(findVideoInput)
.catch(gotEnumerateDevicesError);
return isVideoInputFound;
}
function findVideoInput(devices) {
addLog('findVideoInput');
isVideoInputFound = devices.some((dev) => dev.kind == 'videoinput');
}
function gotEnumerateDevicesError(error) {
console.log('navigator.mediaDevices.enumerateDevices error: ', error);
enumerateDevicesError = error.toString();
}
function CameraTest(resolution, durationSec) {
this.resolution = resolution;
this.durationSec = durationSec;
this.localVideo = document.createElement('video');
this.localVideo.id = 'local-video';
this.localVideo.autoplay = true;
document.body.appendChild(this.localVideo);
this.localStream = null;
this.canvas = document.createElement('canvas');
this.context = this.canvas.getContext('2d');
this.previousFrame = [];
this.identicalFrameSsimThreshold = 0.985;
this.frameComparator = new Ssim();
this.results = {
width: resolution[0],
height: resolution[1],
errors: [],
frameStats: {totalFrames: 0, blackFrames: 0, frozenFrames: 0},
};
this.constraints = {
'audio': false,
'video': {
'mandatory': {
'maxWidth': this.resolution[0].toString(),
'maxHeight': this.resolution[1].toString(),
'minWidth': this.resolution[0].toString(),
'minHeight': this.resolution[1].toString(),
},
},
};
}
CameraTest.prototype = {
start: function() {
addLog(`CameraTest.start: ${this.resolution[0]}x${this.resolution[1]}`);
this.localVideo.addEventListener('play',
this.startCheckingVideoFrames.bind(this), false);
navigator.mediaDevices.getUserMedia(this.constraints)
.then(this.gotLocalStream.bind(this))
.catch(this.gotUserMediaError.bind(this));
},
gotLocalStream: function(stream) {
addLog('CameraTest.gotLocalStream');
this.localStream = stream;
this.localVideo.srcObject = stream;
setTimeout(() => {
this.stop();
testNextResolution(this.durationSec);
}, 1000 * this.durationSec);
},
gotUserMediaError: function(error) {
console.log('navigator.mediaDevices.getUserMedia error: ', error);
this.results.errors.push('GetUserMedia error: ' + error.toString());
},
startCheckingVideoFrames: function() {
addLog('CameraTest.startCheckingVideoFrames');
if (!resolutionMatchesIndependentOfRotation(this.localVideo.videoWidth,
this.localVideo.videoHeight, this.resolution[0], this.resolution[1])) {
this.results.errors.push('resolution', 'Got ' +
this.localVideo.videoWidth + 'x' + this.localVideo.videoHeight +
', expected ' + this.resolution[0] + 'x' + this.resolution[1] +
' or rotated version thereof');
}
this.videoFrameChecker = setInterval(this.checkVideoFrame.bind(this), 20);
},
checkVideoFrame: function() {
this.context.drawImage(this.localVideo, 0, 0, this.canvas.width,
this.canvas.height);
const imageData = this.context.getImageData(0, 0, this.canvas.width,
this.canvas.height);
if (isBlackFrame(imageData.data, imageData.data.length)) {
this.results.frameStats.blackFrames++;
}
if (this.frameComparator.calculate(this.previousFrame, imageData.data) >
this.identicalFrameSsimThreshold) {
this.results.frameStats.frozenFrames++;
}
this.previousFrame = imageData.data;
this.results.frameStats.totalFrames++;
},
stop: function() {
addLog('CameraTest.start');
clearInterval(this.videoFrameChecker);
saveResult(this.resolution, this.results);
this.localStream.getTracks().forEach((track) => track.stop());
this.localVideo.srcObject = null;
document.body.removeChild(this.localVideo);
},
};
window.onerror = function(message, filename, lineno, colno, error) {
const msg = 'Something went wrong, here is the stack trace --> ' +
error.stack;
console.log(msg);
globalErrors.push(msg);
};
scriptReady = true;
</script>
</body>
</html>