How to Take a Snapshot of HTML5-Javascript-Based Video Player

HTML5 video screenshot

Hm...it seems like it actually is possible to draw an image from a paused video. Just keep the interval going from the beginning.

Snapshot video as a preview

Without seeing all your code, here is an example which gets 10 screens.

var screens = [];
var video = document.getElementById('video');var video_preview = document.getElementById('video_preview');
var loadingContainer = document.getElementById('loading-container')
function loadVideo(event) { loadingContainer.style.display = 'block' screens = [];
var reader = new FileReader() reader.onload = function(e) { video.src = video_preview.src = e.target.result; video.autoplay = video_preview.autoplay = true; video.hasLoaded = video_preview.hasLoaded = false;
video_preview.addEventListener('canplay', function() { video_preview.hasLoaded = true; video.play(); })
video.addEventListener('canplay', function() {
// first time if (!video.hasLoaded) { console.log('Loaded: video duration: ', this.duration, event.target.files[0].size)
loadingContainer.innerText = 'Generating screens...'
video.hasLoaded = true;
var self = this;
(function repeat(i) { setTimeout(function() { var timestamp = ((self.duration / 10) * i) / 1.1; // fudge abit so dont get start/end frames
console.log('seeking to:', timestamp) self.currentTime = timestamp
if (--i) { // next repeat(i); } else { // loadingContainer.style.display = 'none'
// all screens grabbed var str = '<div style="position:relative;width:calc(100% + .25rem)">' screens.reverse().forEach(function(screen) { str += '<img src="' + screen + '" style="width:32%" class="img-thumbnail m-1" />'; }); str += '</div>'; document.getElementById("screens-container").innerHTML = str; } }, 500) // how fast to attempt to grab screens })(11); // iterations i.e how many screens } }, false);
video.addEventListener('seeked', function() { console.log('grabbing screen for', this.currentTime) takeScreen(); }, false); }
reader.readAsDataURL(event.target.files[0]);}
function takeScreen() { var filename = video.src; var w = video.videoWidth; var h = video.videoHeight; var canvas = document.createElement('canvas'); canvas.width = w; canvas.height = h; var ctx = canvas.getContext('2d'); ctx.drawImage(video, 0, 0, w, h); var data = canvas.toDataURL("image/jpg");
screens.push(data)
loadingContainer.innerText = 'Generated ' + screens.length + ' out of 10 screens...'}
function failed(e) { switch (e.target.error.code) { case e.target.error.MEDIA_ERR_ABORTED: console.log('You aborted the video playback.'); break; case e.target.error.MEDIA_ERR_NETWORK: console.log('A network error caused the video download to fail part-way.'); break; case e.target.error.MEDIA_ERR_DECODE: console.log('The video playback was aborted due to a corruption problem or because the video used features your browser did not support.'); break; case e.target.error.MEDIA_ERR_SRC_NOT_SUPPORTED: console.log('The video could not be loaded, either because the server or network failed or because the format is not supported.'); break; default: console.log('An unknown error occurred.'); break; }}
body,html {  margin: 0;  padding: 0}
li { display: inline-block; list-style-type: none}
.jumbotron { margin-bottom: 1rem;}
<div class="container">
<div class="card"> <div class="card-header" style="background-color: #e9ecef"> <h5 class="card-title">Video Preview</h5> <video id="video_preview" onerror="failed(event)" controls="controls" preload="none" muted style="width: 100%"></video> <video id="video" onerror="failed(event)" controls="controls" preload="none" muted style="display:none"></video> <div class="card-body"> <a href="javascript:void(0)" class="btn btn-sm btn-primary btn-block" onClick="$('[type=\'file\']').trigger('click')"> <i class="fa fa-upload"></i> Load Video </a> <form id="uploadForm" ref="uploadForm" action='/upload' method='post' enctype="multipart/form-data"> <input type="file" name="file" accept="video/*" style="display: none" onchange="loadVideo(event)"> </form> <div id="loading-container" style="display:none">Loading...</div> </div>
<div class="card-body"> <h5 class="card-title">Screens</h5> <div id="screens-container"></div> </div> </div> </div>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>

How can we get the screenshot of the video in html5 using canvas

Looks like it is possible here: http://html5doctor.com/video-canvas-magic/

Capture frames from video with HTML5 and JavaScript

Cause

The problem is that seeking video (by setting it's currentTime) is asynchronous.

You need to listen to the seeked event or else it will risk take the actual current frame which is likely your old value.

As it is asynchronous you must not use the setInterval() as it is asynchronous too and you will not be able to properly synchronize when the next frame is seeked to. There is no need to use setInterval() as we will utilize the seeked event instead which will keep everything is sync.

Solution

By re-writing the code a little you can use the seeked event to go through the video to capture the correct frame as this event ensures us that we are actually at the frame we requested by setting the currentTime property.

Example

// global or parent scope of handlers
var video = document.getElementById("video"); // added for clarity: this is needed
var i = 0;

video.addEventListener('loadeddata', function() {
this.currentTime = i;
});

Add this event handler to the party:

video.addEventListener('seeked', function() {

// now video has seeked and current frames will show
// at the time as we expect
generateThumbnail(i);

// when frame is captured, increase here by 5 seconds
i += 5;

// if we are not past end, seek to next interval
if (i <= this.duration) {
// this will trigger another seeked event
this.currentTime = i;
}
else {
// Done!, next action
}
});

Using HTML5/Canvas/JavaScript to take in-browser screenshots

JavaScript can read the DOM and render a fairly accurate representation of that using canvas. I have been working on a script which converts HTML into a canvas image. Decided today to make an implementation of it into sending feedbacks like you described.

The script allows you to create feedback forms which include a screenshot, created on the client's browser, along with the form. The screenshot is based on the DOM and as such may not be 100% accurate to the real representation as it does not make an actual screenshot, but builds the screenshot based on the information available on the page.

It does not require any rendering from the server, as the whole image is created on the client's browser. The HTML2Canvas script itself is still in a very experimental state, as it does not parse nearly as much of the CSS3 attributes I would want it to, nor does it have any support to load CORS images even if a proxy was available.

Still quite limited browser compatibility (not because more couldn't be supported, just haven't had time to make it more cross browser supported).

For more information, have a look at the examples here:

http://hertzen.com/experiments/jsfeedback/

edit
The html2canvas script is now available separately here and some examples here.

edit 2
Another confirmation that Google uses a very similar method (in fact, based on the documentation, the only major difference is their async method of traversing/drawing) can be found in this presentation by Elliott Sprehn from the Google Feedback team:
http://www.elliottsprehn.com/preso/fluentconf/

HTML5 video screenshot via canvas using CORS

I have answered my own question.

What a horrible headache I now have.

The problem lies somewhere in the nuanced specification of the HTML5 video crossorigin/CORS specification.

I only tested in Chrome and Edge, but here is what you need to know at the time of writing:

Chrome

Loading your HTML5 video will fail if you have crossOrigin set, but your video is being served from any port other than 80 and is not using https:

THIS WILL FAIL

Client at http://www.myapp.com/player.html:

<video crossOrigin="anonymous" src="http://cdn.myapp.com:81/video.mp4"></video>

THIS WILL SUCCEED

Client at http://www.myapp.com/player.html:

<video crossOrigin="anonymous" src="https://cdn.myapp.com:81/video.mp4"></video>

Chrome and Edge

getImageData() and toDataURL() will be security blocked unless:

  • crossorigin is set to anonymous or use-credentials (as defined here) before the video is loaded. If you do this too late, it will still fail.

All

Finally, if you are going to set crossOrigin in javascript, be sure to use the correct casing for the javascript property: crossOrigin (NOT crossorigin)

I have written this up in a little more detail in my blog.

HTML5 Capture image of paused video stream and save it

function capture() {  var canvas = document.getElementById("c");  var video = document.getElementById("v");    if (video.paused) {      canvas.getContext('2d').drawImage(video, 0, 0);  }}
<video id="v" autoplay="autoplay" >    <source src="http://html5doctor.com/demos/video-canvas-magic/video.webm" type="video/ogg"></video><input type="button" value="Capture" onclick="capture()"/><canvas id="c" width="500" height="500"></canvas>

Make a HTML5 video player play a different file

After you set the src property, call video.load().

Actually, if you're only going to have one single source (presumably, because you know you're going to be using a browser that will play mp3), then you can just simplify and set the src property on the video tag itself.

HTML:

<video id="video" src="file:///C:/Users/Ruurd/Music/Far%20East%20Movement%20-%20Turn%20Up%20The%20Love%20ft.%20Cover%20Drive.mp3"></video>

Javascript:

var video = document.getElementById('video');
function change(s){
video.pause();
video.src = s;
video.load();
video.currentTime = 0;
video.play();
}


Related Topics



Leave a reply



Submit