I wrote a simple script that take a frame from a video and draw it to a canvas. My problem is that the colors are changing between the video and the drawn image.
I put here the result next to the original to make it easier to see. The original one is on the left. It's seems to be way more visible on chrome browser btw. All the test I made where on OSX.
Here a snippet, canvas on left, video on right:
// Get our mask image
var canvas = document.querySelector(".canvas");
var video = document.querySelector(".video");
var ctx = canvas.getContext('2d');
function drawMaskedVideo() {
ctx.drawImage(video, 0, 0, video.videoWidth/2, video.videoHeight, 0,0, video.videoWidth/2, video.videoHeight);
}
requestAnimationFrame(function loop() {
drawMaskedVideo();
requestAnimationFrame(loop.bind(this));
});
html, body {
margin: 0 auto;
}
.video, .canvas {
width: 100%;
}
.canvas {
position: absolute;
top: 0;
left: 0;
}
<video class="video" autoplay="autoplay" muted="muted" preload="auto" loop="loop">
<source src="http://mazwai.com/system/posts/videos/000/000/214/original/finn-karstens_winter-wonderland-kiel.mp4" type="video/mp4">
</video>
<canvas class='canvas' width='1280' height='720'></canvas>
I'd like to know why this thing happen, and if it possible to get rid of it in a cross browser way ?
Here the simple script I wrote:
let video = document.querySelector('#my-video') // .mp4 file used
let w = video.videoWidth;
let h = video.videoHeight;
let canvas = document.createElement('canvas');
canvas.width = w;
canvas.height = h;
let ctx = canvas.getContext('2d');
ctx.drawImage(video, 0, 0, w, h)
document.querySelector('.canvas-container').appendChild(canvas);
The solution might be as simple as setting a css filter to the video element:
.video {
-webkit-filter: contrast(100%);
}
I can't reason about this since it was discovered accident (playing with your demo and reading related answers), so I leave the technical explanation for someone else and leave you with some magic for now.
Any sufficiently advanced technology is indistinguishable from magic.
— Arthur C. Clarke
// Get our mask image
var canvas = document.querySelector(".canvas");
var video = document.querySelector(".video");
var ctx = canvas.getContext('2d');
function drawMaskedVideo() {
ctx.drawImage(video, 0, 0, video.videoWidth/2, video.videoHeight, 0,0, video.videoWidth/2, video.videoHeight);
}
requestAnimationFrame(function loop() {
drawMaskedVideo();
requestAnimationFrame(loop.bind(this));
});
html, body {
margin: 0 auto;
}
.video, .canvas {
width: 100%;
}
.video {
-webkit-filter: contrast(100%);
}
.canvas {
position: absolute;
top: 0;
left: 0;
}
<video class="video" autoplay="autoplay" muted="muted" preload="auto" loop="loop">
<source src="http://mazwai.com/system/posts/videos/000/000/214/original/finn-karstens_winter-wonderland-kiel.mp4" type="video/mp4">
</video>
<canvas class='canvas' width='1280' height='720'></canvas>
Note:
Running this on a Macbook Pro (2,3 GHz Intel Core i5) I could see no difference performance wise. Tracked CPU during the video playback and both demos idle at around 28%.
Related
i am building a video hosting site. my video player generates video player screenshots by the onclick() function. i am also not sure if these images are deleted or stored after generating.
i have tried surfing around here trying to find a definitive assistance as i lack in javascript knowledge and it's so frustrating. please help me. ask me if you need any more info. :)
here is my html:
<body>
<div id="videoPanel">
<div id="instructions">
<video id="my_video_1" class="video-js vjs-default-skin" width="720px" height="400px" controls preload="none" poster='' autoplay data-setup='{ "aspectRatio":"640:500", "playbackRates": [1, 1.5, 2] }'>
<source src="/Custom-Video-Player/Masamune kun no Revenge - 07.mp4" type='video/mp4' />
</video>
</div>
<button id="clicker" onclick="shoot()">Capture</button><br />
<button id="download" onclick=""> download</button> <br/>
<div id="output" style="display: inline-block; top: 4px; position: relative; border: dotted 1px #ccc; padding: 2px;"></div>
</div>
</body>
this is the javascript for the screenshot generation:
var videoId = 'my_video_1';
var scaleFactor = 0.25;
var snapshots = [];
var w = 720 * scaleFactor;
var h = 400 * scaleFactor;
function capture(video, scaleFactor) {
if (scaleFactor == null) {
scaleFactor = 1;
}
var canvas = document.createElement('canvas');
canvas.width = w;
canvas.height = h;
var ctx = canvas.getContext('2d');
ctx.drawImage(video, 0, 0, w, h);
return canvas;
}
function shoot() {
var video = document.getElementById(videoId);
var output = document.getElementById('output');
var canvas = capture(video, scaleFactor);
canvas.onclick = function () {
window.open(this.toDataURL());
};
snapshots.unshift(canvas);
output.innerHTML = '';
for (var i = 0; i < 4; i++) {
output.appendChild(snapshots[i]);
}
}
theres also a download button that i want to be able to function as such:
it will number all images in accordance to their generation in regard to the 'capture' button, put them all in a zip file and download when download is pressed.
I know how to copy on frame by frame basis from a playing HTML5 video to a canvas using 2D context.
But I want to work with a paused video, change dinamically its currentTime and copy current frame of video to canvas.
My guess is some process is not called yet when video position is set with currentTime property, although the video itself does update the image it shows (but not to the canvas).
I've found that it's posible to overcome this by setting a setTimeout to do the canvas ´drawImage´ in the next step.
You can see here a jsfiddle that proves the point.
As you can see in the fiddle you can play the video, and canvas updates, but if you pause the video, mouse scroll moves currentTime of video. There, a ´seTimeout´ is needed to update the canvas, if I call the drawImage method directly, canvas doesn't update.
In short, my question is:
Is there a better way to do this? Is it posible to do this without setTimeout and inside de loop itself? Pros & Cons?
Thank you very much for reading through here!
Every time you change the currentTime of your VideoElement, a seeked Event will trigger when the video actually changed its position.
var vid = document.getElementById("v");
var canvas = document.getElementById("c");
var context = canvas.getContext('2d');
var targetFrame = document.getElementById('t');
var cw = canvas.width = 200;
var ch = canvas.height = Math.round(cw / 1.7777);
var targetOffset = 0;
window.addEventListener('wheel', function(e) {
e.preventDefault();
targetOffset = targetOffset + (e.deltaY / 1000);
targetFrame.value = targetOffset;
seek(); // for demo purpose, we only listen to wheel
return false;
});
// that's all is needed
vid.addEventListener('seeked', function() {
context.drawImage(vid, 0, 0, cw, ch);
});
// for demo
// removed the rendering loop
// now it only changes the video's currentTime property
function seek() {
targetOffset = targetOffset * 0.9;
targetFrame.value = Math.round(targetOffset * 100) / 100;
var vct = vid.currentTime - targetOffset;
if (vct < 0) {
vct = vid.duration + vct;
} else if (vct > vid.duration) {
vct = vct - vid.duration;
}
vid.currentTime = vct;
}
.column {
float: left;
width: 50%;
}
.row:after {
content: "";
display: table;
clear: both;
}
#c {
border: 1px solid black;
}
<h3>
scroll up is forward
</h3>
<div class="row">
<div class="column">
<div>
Video element:
</div>
<video controls height="120" id="v" tabindex="-1" autobuffer="auto" preload="auto">
<source type="video/webm" src="https://www.html5rocks.com/tutorials/video/basics/Chrome_ImF.webm"/>
</video>
</div>
<div class="column">
<div>
Canvas element:
</div>
<canvas id="c"></canvas>
<div>
Momentum: <input type=text id="t">
</div>
</div>
</div>
I am trying to create a visualiser using HTML5 Audio and frequencies based on a canvas. It is working fine on chrome and safari. But I want to make it working on IE and Firefox as well.
// Create a new instance of an audio object and adjust some of its properties
var audio = new Audio();
audio.crossOrigin = "anonymous";
audio.src = 'http://www.streaming507.com:8072/stream';
audio.controls = true;
audio.loop = true;
// Establish all variables that your Analyser will use
var canvas, ctx, source, context, analyser, fbc_array, bars, bar_x, bar_width, bar_height;
// Initialize the MP3 player after the page loads all of its HTML into the window
window.addEventListener("load", initMp3Player, false);
function initMp3Player() {
document.getElementById('audio_box').appendChild(audio);
context = new webkitAudioContext(); // AudioContext object instance
analyser = context.createAnalyser(); // AnalyserNode method
canvas = document.getElementById('analyser_render');
ctx = canvas.getContext('2d');
// Re-route audio playback into the processing graph of the AudioContext
source = context.createMediaElementSource(audio);
source.connect(analyser);
analyser.connect(context.destination);
//frameLooper();
}
// frameLooper() animates any style of graphics you wish to the audio frequency
// Looping at the default frame rate that the browser provides(approx. 60 FPS)
function frameLooper() {
window.webkitRequestAnimationFrame(frameLooper);
fbc_array = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(fbc_array);
ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas
var my_gradient = ctx.createLinearGradient(0, 0, 170, 0);
my_gradient.addColorStop(0, "black");
my_gradient.addColorStop(0.5, "red");
my_gradient.addColorStop(1, "white");
ctx.fillStyle = my_gradient; // Color of the bars
bars = 100;
for (var i = 0; i < bars; i++) {
bar_x = i * 3;
bar_width = 2;
bar_height = -(fbc_array[i] / 2);
// fillRect( x, y, width, height ) // Explanation of the parameters below
ctx.fillRect(bar_x, canvas.height, bar_width, bar_height);
}
}
div#mp3_player {
width: 500px;
height: 60px;
background: #000;
padding: 5px;
margin: 50px auto;
}
div#mp3_player > div > audio {
width: 500px;
background: #000;
float: left;
}
div#mp3_player > canvas {
width: 500px;
height: 30px;
background: #002D3C;
float: left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="mp3_player">
<div id="audio_box"></div>
<canvas id="analyser_render"></canvas>
</div>
Please note, it does not show the visualisation on stack overflow, but it work on local host.
Basically the issue that it does not show on IE and Moz is webkitAudioContext(), and webkitRequestAnimationFrame(). removing webkit from the first one makes it working on chrome and IE but stops working on Safari.
Is there any idea how to make it working in current browsers like Chrome, Safari, Firefox and IE.
Try to allocate the AudioContext this way:
var Actx;
try {
Actx = (AudioContext || webkitAudioContext);
}
catch(err) {
// sorry, no visualizer for you...
}
if (Actx) {
context = new Actx();
// ... continue here
}
else {
// optionally rub it in here instead of in catch...
}
For requestAnimationFrame you can use this polyfill, then use it without prefix:
function frameLooper() {
window.requestAnimationFrame(frameLooper);
...
You probably also want to wait for the audio element's "canplay" event.
I have a HTML5 video banner at the top of my page. It has the autoplay and loop attributes added. When I begin to scroll down the page the video stops and never restarts when I scroll back upwards. I'd like the video to carry on playing no matter if the user scrolls or not.
There doesn't seem to be any attributes listed in the W3C spec that suggests a solution, so is there another way of disabling this functionality?
The HTML and CSS is very simple so I'm wondering if I need some JS to help along the way:
HTML:
<video preload="none" autoplay loop>
<source src="/media/test.mp4" type="video/mp4">
</video>
CSS:
video {
width: 100%;
height: auto;
}
Any help would be much appreciated.
You can use isInViewport jQuery plugin as below:
$('video').each(function(){
if ($(this).is(":in-viewport")) {
$(this)[0].play();
} else {
$(this)[0].pause();
}
})
it works fine for me,, if you want to change yiur viewport then change fraction = 0.5; in my code
var videos = document.getElementsByTagName("video"),
fraction = 0.5;
function checkScroll() {
for(var i = 0; i < videos.length; i++) {
var video = videos[i];
var x = video.offsetLeft, y = video.offsetTop, w = video.offsetWidth, h = video.offsetHeight, r = x + w, //right
b = y + h, //bottom
visibleX, visibleY, visible;
visibleX = Math.max(0, Math.min(w, window.pageXOffset + window.innerWidth - x, r - window.pageXOffset));
visibleY = Math.max(0, Math.min(h, window.pageYOffset + window.innerHeight - y, b - window.pageYOffset));
visible = visibleX * visibleY / (w * h);
if (visible > fraction) {
video.play();
} else {
video.pause();
}
}
}
window.addEventListener('scroll', checkScroll, false);
window.addEventListener('resize', checkScroll, false);
div {padding-top:300px; width:320px;}
video {
padding-bottom:300px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<video id="video1" preload="auto" loop="loop">
<source src="http://www.w3schools.com/html/movie.mp4" type="video/mp4">>
bgvideo
</video>
</div>
This can be disabled by using the attribute data-keepplaying.
<video preload="none" autoplay loop>
<source src="/media/test.mp4" type="video/mp4">
</video>
if using fullpage.js
<video autoplay muted loop data-keepplaying>
<source src="myvide.mp4" type="video/mp4">
</video>
works
I have the following code:
<!DOCTYPE html>
<title>Video/Canvas Gray Effect</title>
<script>
document.addEventListener('DOMContentLoaded', function(){
var v = document.getElementById('v');
var canvas = document.getElementById('c');
var context = canvas.getContext('2d');
var back = document.createElement('canvas');
var backcontext = back.getContext('2d');
var cw,ch;
v.addEventListener('play', function(){
cw = v.clientWidth;
ch = v.clientHeight;
canvas.width = cw;
canvas.height = ch;
back.width = cw;
back.height = ch;
draw(v,context,backcontext,cw,ch);
},false);
},false);
function draw(v,c,bc,w,h) {
if(v.paused || v.ended) return false;
// First, draw it into the backing canvas
bc.drawImage(v,0,0,w,h);
// Grab the pixel data from the backing canvas
var idata = bc.getImageData(0,0,w,h);
var data = idata.data;
// Loop through the pixels, turning them grayscale
for(var i = 0; i < data.length; i+=4) {
var r = data[i];
var g = data[i+1];
var b = data[i+2];
var brightness = (3*r+4*g+b)>>>3;
data[i] = brightness;
data[i+1] = brightness;
data[i+2] = brightness;
}
idata.data = data;
// Draw the pixels onto the visible canvas
c.putImageData(idata,0,0);
// Start over!
setTimeout(draw,20,v,c,bc,w,h);
}
</script>
<video id=v controls loop>
<source src=video.webm type=video/webm>
</video>
<canvas id=c></canvas>
<style>
#c {
position: absolute;
top: 50%;
left: 50%;
margin: -180px 0 0 20px;
}
#v {
position: absolute;
top: 50%;
left: 50%;
margin: -180px 0 0 -500px;
}
</style>
The canvas work only if the video hosted in my site, but if a external video the canvas not working only work if make a proxy using the same domain, for example:
Working if a hosted the video on my site:
<video id=v controls loop>
<source src=video.webm type=video/webm>
</video>
Working if a use a proxy, but I will not use a proxy:
<video id=v controls loop>
<source src=./proxy.php?url_toexternal_video=http://host.com/video.webm type=video/webm>
</video>
Not working if a use the direct link to the video:
<video id=v controls loop>
<source src=http://host.com/video.webm type=video/webm>
</video>
any way to make it work if it is from an external video? Thanks! ;)
Nope, there's no way to use getImageData on a canvas that has been tainted by cross-origin content.