2018-03-22 11:52:23 -04:00
|
|
|
(function () {
|
2018-03-30 14:54:58 -04:00
|
|
|
const BENCHMARK_THROTTLE = 250;
|
|
|
|
const INTERVAL = 33;
|
|
|
|
|
2018-03-22 11:52:23 -04:00
|
|
|
const video = document.createElement('video');
|
|
|
|
navigator.getUserMedia({
|
|
|
|
audio: false,
|
|
|
|
video: {
|
|
|
|
width: {min: 480, ideal: 640},
|
|
|
|
height: {min: 360, ideal: 480}
|
|
|
|
}
|
|
|
|
}, stream => {
|
|
|
|
video.autoplay = true;
|
|
|
|
video.src = window.URL.createObjectURL(stream);
|
|
|
|
// Get the track to hint to the browser the stream needs to be running
|
|
|
|
// even though we don't add the video tag to the DOM.
|
|
|
|
stream.getTracks();
|
|
|
|
video.addEventListener('play', () => {
|
|
|
|
video.width = video.videoWidth;
|
|
|
|
video.height = video.videoHeight;
|
|
|
|
});
|
|
|
|
}, err => {
|
2018-03-30 14:54:58 -04:00
|
|
|
// eslint-disable-next-line no-console
|
2018-03-22 11:52:23 -04:00
|
|
|
console.log(err);
|
|
|
|
});
|
|
|
|
|
2018-03-30 14:54:58 -04:00
|
|
|
const VideoMotion = window.Scratch3VideoSensingDebug.VideoMotion;
|
|
|
|
const VideoMotionView = window.Scratch3VideoSensingDebug.VideoMotionView;
|
2018-03-22 11:52:23 -04:00
|
|
|
|
|
|
|
// Create motion detector
|
|
|
|
const motion = new VideoMotion();
|
|
|
|
|
|
|
|
// Create debug views that will render different slices of how the detector
|
2018-03-30 14:54:58 -04:00
|
|
|
// uses a frame of input.
|
2018-03-22 11:52:23 -04:00
|
|
|
const OUTPUT = VideoMotionView.OUTPUT;
|
|
|
|
const outputKeys = Object.keys(OUTPUT);
|
|
|
|
const outputValues = Object.values(OUTPUT);
|
|
|
|
const views = outputValues
|
|
|
|
.map(output => new VideoMotionView(motion, output));
|
|
|
|
const view = views[0];
|
|
|
|
|
2018-03-27 18:11:46 -04:00
|
|
|
const defaultViews = [OUTPUT.INPUT, OUTPUT.XY_CELL, OUTPUT.T_CELL, OUTPUT.UV_CELL];
|
2018-03-22 11:52:23 -04:00
|
|
|
|
2018-03-30 14:54:58 -04:00
|
|
|
// Add activation toggles for each debug view.
|
2018-03-22 11:52:23 -04:00
|
|
|
const activators = document.createElement('div');
|
|
|
|
activators.style.userSelect = 'none';
|
|
|
|
outputValues.forEach((output, index) => {
|
|
|
|
const checkboxLabel = document.createElement('label');
|
|
|
|
const checkbox = document.createElement('input');
|
|
|
|
checkbox.type = 'checkbox';
|
|
|
|
checkbox.checked = defaultViews.indexOf(output) !== -1;
|
|
|
|
const checkboxSpan = document.createElement('span');
|
|
|
|
checkboxSpan.innerText = outputKeys[index];
|
|
|
|
checkboxLabel.appendChild(checkbox);
|
|
|
|
checkboxLabel.appendChild(checkboxSpan);
|
|
|
|
|
|
|
|
const _view = views[index];
|
|
|
|
_view.canvas.style.display = checkbox.checked ? '' : 'none';
|
|
|
|
_view.active = checkbox.checked;
|
|
|
|
checkbox.onchange = event => {
|
|
|
|
_view.canvas.style.display = checkbox.checked ? '' : 'none';
|
|
|
|
_view.active = checkbox.checked;
|
|
|
|
event.preventDefault();
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
activators.appendChild(checkboxLabel);
|
|
|
|
});
|
|
|
|
document.body.appendChild(activators);
|
|
|
|
|
|
|
|
// Add a text line to display milliseconds per frame, motion value, and
|
|
|
|
// motion direction
|
2018-03-30 14:54:58 -04:00
|
|
|
const textContainer = document.createElement('div');
|
|
|
|
const textHeader = document.createElement('div');
|
|
|
|
textHeader.innerText = 'duration (us) :: motion amount :: motion direction';
|
|
|
|
textContainer.appendChild(textHeader);
|
2018-03-22 11:52:23 -04:00
|
|
|
const textEl = document.createElement('div');
|
2018-03-30 14:54:58 -04:00
|
|
|
textEl.innerText = `0 :: 0 :: 0`;
|
|
|
|
textContainer.appendChild(textEl);
|
|
|
|
document.body.appendChild(textContainer);
|
2018-03-22 11:52:23 -04:00
|
|
|
let textTimer = Date.now();
|
|
|
|
|
|
|
|
// Add the motion debug views to the dom after the text line, so the text
|
|
|
|
// appears first.
|
|
|
|
views.forEach(_view => document.body.appendChild(_view.canvas));
|
|
|
|
|
|
|
|
// Create a temporary canvas the video will be drawn to so the video's
|
|
|
|
// bitmap data can be transformed into a TypeArray.
|
|
|
|
const tempCanvas = document.createElement('canvas');
|
|
|
|
tempCanvas.width = view.canvas.width;
|
|
|
|
tempCanvas.height = view.canvas.height;
|
|
|
|
const ctx = tempCanvas.getContext('2d');
|
|
|
|
|
|
|
|
const loop = function () {
|
2018-03-30 14:54:58 -04:00
|
|
|
const timeoutId = setTimeout(loop, INTERVAL);
|
2018-03-22 11:52:23 -04:00
|
|
|
|
|
|
|
try {
|
|
|
|
// Get the bitmap data for the video frame
|
|
|
|
ctx.scale(-1, 1);
|
|
|
|
ctx.drawImage(
|
|
|
|
video,
|
|
|
|
0, 0, video.width || video.clientWidth, video.height || video.clientHeight,
|
2018-03-30 14:54:58 -04:00
|
|
|
-tempCanvas.width, 0, tempCanvas.width, tempCanvas.height
|
2018-03-22 11:52:23 -04:00
|
|
|
);
|
|
|
|
ctx.resetTransform();
|
|
|
|
const data = ctx.getImageData(0, 0, tempCanvas.width, tempCanvas.height);
|
|
|
|
|
2018-03-30 14:54:58 -04:00
|
|
|
// Analyze the latest frame.
|
2018-03-22 11:52:23 -04:00
|
|
|
const b = performance.now();
|
|
|
|
motion.addFrame(data.data);
|
|
|
|
motion.analyzeFrame();
|
2018-03-30 14:54:58 -04:00
|
|
|
|
|
|
|
// Every so often update the visible debug numbers with duration in
|
|
|
|
// microseconds, the amount of motion and the direction of the
|
|
|
|
// motion.
|
|
|
|
if (Date.now() - textTimer > BENCHMARK_THROTTLE) {
|
2018-03-22 11:52:23 -04:00
|
|
|
const e = performance.now();
|
|
|
|
const analyzeDuration = ((e - b) * 1000).toFixed(0);
|
|
|
|
const motionAmount = motion.motionAmount.toFixed(1);
|
|
|
|
const motionDirection = motion.motionDirection.toFixed(1);
|
|
|
|
textEl.innerText = `${analyzeDuration} :: ${motionAmount} :: ${motionDirection}`;
|
|
|
|
textTimer = Date.now();
|
|
|
|
}
|
|
|
|
views.forEach(_view => _view.active && _view.draw());
|
|
|
|
} catch (error) {
|
2018-03-30 14:54:58 -04:00
|
|
|
// eslint-disable-next-line no-console
|
2018-03-22 11:52:23 -04:00
|
|
|
console.error(error.stack || error);
|
|
|
|
clearTimeout(timeoutId);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
loop();
|
|
|
|
}());
|