Well, maybe not totally, but I couldn’t find a good existing way of doing this.
I wanted to delay a stream by an arbitrary amount of time. The specs talk about ‘time shifting live streams’ but I don’t think there are any implementations around yet in any of the browsers for this.
I tried ‘recording’ the streams – again something that is coming, but not around yet. There are a couple of open js libraries that’ll do it, like this and this , but I needed to be able to constantly buffer dozens of streams at once and these solutions were slow and would crash the browser.
So my solution can be seen here – basically I store a grab of each frame as it comes in, and then draw these every frame back to a canvas object, after a delay. Works great, performance is great, looks like video. Only drawbacks are obviously no audio (wasn’t a problem for my purposes) and it doesn’t render unless the tab is in focus (again, fine for me).
code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script type="text/javascript">
'use strict';
(function(){
var frameStore = {};
function toggle_visibility() {
function toggle(id){
var el = document.getElementById(id);
el.style.display = el.style.display==='none' ? 'block' : 'none';
}
for(var i=0; i<arguments.length; ++i){
toggle(arguments[i]);
}
}
function startRecording(stream, videoElementId, canvasElementId) {
frameStore[videoElementId] = [];
var mediaElement = document.querySelector(videoElementId)
mediaElement.src = URL.createObjectURL(stream);
var canvas = document.querySelector(canvasElementId)
toggle_visibility(canvasElementId.substr(1));
setTimeout(function(){toggle_visibility(canvasElementId.substr(1));}, 4000);
var context = canvas.getContext('2d');
mediaElement.addEventListener('play', function(){
draw(this,context,320,180, frameStore[videoElementId]);
},false);
}
function draw(v,c,w,h,store) {
if(v.paused || v.ended) return false;
c.drawImage(v,0,0);
var data = c.getImageData(0, 0, w, h);
store.push(data);
if(store.length > 100) {
c.putImageData(store[store.length-100], 0, 0);
store.shift();
};
setTimeout(draw,40,v,c,w,h,store);
}
function init() {
navigator.getMedia = ( navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia);
navigator.getMedia (
// constraints
{
video: {
mandatory: {
maxFrameRate: 25,
maxWidth: 320,
maxHeight: 180
}
},
audio: false
},
// successCallback
function(localMediaStream) {
startRecording(localMediaStream, "#myvideo1", "#c1");
startRecording(localMediaStream, "#myvideo2", "#c2");
startRecording(localMediaStream, "#myvideo3", "#c3");
startRecording(localMediaStream, "#myvideo4", "#c4");
startRecording(localMediaStream, "#myvideo5", "#c5");
startRecording(localMediaStream, "#myvideo6", "#c6");
startRecording(localMediaStream, "#myvideo7", "#c7");
startRecording(localMediaStream, "#myvideo8", "#c8");
startRecording(localMediaStream, "#myvideo9", "#c9");
startRecording(localMediaStream, "#myvideo0", "#c0");
},
// errorCallback
function(err) {
console.log("The following error occured: " + err);
console.log(err);
}
);
}
document.addEventListener('DOMContentLoaded', init);
})();
</script>
<style type="text/css">
.video-holder {
position:relative;
width:100%;
}
.thevideo {
position:absolute;
width:320px;
height:180px;
}
.c {
/*position:absolute;*/
top: 0;
bottom: 0;
left: 320px;
right: 0;
margin-left: 320px;
width: 320px;
height: 180px;
}
</style>
</head>
<body>
<p>Accept permission to use camera</p>
<div class="video-holder" >
<video id="myvideo1" class="thevideo" autoplay ></video>
<canvas id='c1' width="320" height="180" class="c" />
</div>
<div class="video-holder">
<video id="myvideo2" class="thevideo" autoplay ></video>
<canvas id='c2' width="320" height="180" class="c" />
</div>
<div class="video-holder">
<video id="myvideo3" class="thevideo" autoplay ></video>
<canvas id='c3' width="320" height="180" class="c" />
</div>
<div class="video-holder">
<video id="myvideo4" class="thevideo" autoplay ></video>
<canvas id='c4' width="320" height="180" class="c" />
</div>
<div class="video-holder">
<video id="myvideo5" class="thevideo" autoplay ></video>
<canvas id='c5' width="320" height="180" class="c" />
</div>
<div class="video-holder">
<video id="myvideo6" class="thevideo" autoplay ></video>
<canvas id='c6' width="320" height="180" class="c" />
</div>
<div class="video-holder">
<video id="myvideo7" class="thevideo" autoplay ></video>
<canvas id='c7' width="320" height="180" class="c" />
</div>
<div class="video-holder">
<video id="myvideo8" class="thevideo" autoplay ></video>
<canvas id='c8' width="320" height="180" class="c" />
</div>
<div class="video-holder">
<video id="myvideo9" class="thevideo" autoplay ></video>
<canvas id='c9' width="320" height="180" class="c" />
</div>
<div class="video-holder">
<video id="myvideo0" class="thevideo" autoplay ></video>
<canvas id='c0' width="320" height="180" class="c" />
</div>
</body>
</html>