46

Actually, i have a HTML5 page with JavaScript function that allow me to play a wmv video file. I need to take a snapshot when the video is playing (with pause or without) and saved in any image format JPG or BMP whatever. Any help will appreciate. Thank you.

<!DOCTYPE HTML>

    <html>
        <head>
            <title>HTML5 video</title>        
    <script type="text/javascript">
        function checkPlay() {
            var myVideo = document.getElementById("myVid");
            var ytStr = '<iframe width="500" height="500" src="C:\XA0002.wmv" frameborder="0" allowfullscreen></iframe>';
            try {
                var canPlay = document.getElementsByTagName('video')[0].canPlayType("video/wmv");
                if ((canPlay == "no") || (canPlay == "")) {
                    myVideo.innerHTML = ytStr;
                }
                new MediaElement(v, { success: function (media) { media.play(); } });
            } catch (err) {
                myVideo.innerHTML = ytStr;
            }
        }
    </script>
        </head>
        <body onload="checkPlay()">
          <div id="myVid"/>

      </body>
    </html>
Passerby
  • 9,715
  • 2
  • 33
  • 50
Ahmed MEZRI
  • 514
  • 1
  • 5
  • 7
  • Do you want a snapshot of a specific moment in the video player or do you want a full size screenshot? If you are asking for a screenshot of the video player, that might be a little challenging but a screen shot you can look at http://stackoverflow.com/questions/4912092/using-html5-canvas-javascript-to-take-screenshots – aug Dec 07 '12 at 10:05

5 Answers5

87

To copy an video frame to an image file involves properly loading the video, copying the image to a canvas and exporting it to a file. This is totally possible, but there are a few places where you can run into trouble, so let's take it one step at a time.

1) Loading the video

To capture the pixels, you need to load the video into a <video> tag, not an iframe or object or embed. And the file needs to come from a web server, which is the same one the web page itself is on (unless you use cross-origin headers, which is complicated and may not work in your browser. This is restricted by the browser for security reasons. Your code loads the video from the local file system, which won't work.

You also need to load the right file format. IE9+ may play WMV, but it's unlikely that any other browser will. If you can, load up multiple sources, using webm, mp4 and ideally ogg/theora.

var container = document.getElementById("myVid"),
    video = document.createElement('video'),
    canCapture = true;
if (!video.canPlayType('video/wmv')) {
    /* If you don't have multiple sources, you can load up a Flash fallback here
       (like jPlayer), but you won't be able to capture frames */
    canCapture = false;
    return;
}
video.src = 'myvideo.wmv';
container.appendChild(video);
video.play(); //or put this in a button click handler if you want your own controls

2) Next, create the canvas and a drawing context. You actually don't need to even attach it to the DOM, but you do need to set it to the size at which you want to save the resulting image. For this example, we'll assume you have a fixed dimension in mind, but if you want, you can set it to a multiple of the video size. Just make sure you run that inside a 'loadedmetadata' event on the video, because the video dimensions won't be available right away.

var canvas = document.createElement('canvas');
canvas.width = 640;
canvas.height = 480;
var ctx = canvas.getContext('2d');
// if you want to preview the captured image,
// attach the canvas to the DOM somewhere you can see it.

3) Capture the image to the canvas and save it to a file. Put this code in an onclick event on a button or inside a timer - however you want to decided when the image gets captured. Use the drawImage method. ([This article] provides a good explanation, including the security concerns. It's the same for video as for an image.)

//draw image to canvas. scale to target dimensions
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);

//convert to desired file format
var dataURI = canvas.toDataURL('image/jpeg'); // can also use 'image/png'

4) Exporting the image file

The jpg file is now saved as a Data URI, which is a long javascript string representing an encoded version of the full binary file. It's up to you what to do with it. You can place it directly into an img element by just setting it to the src: myImage.src = dataUri;.

If you want to save it to a file, you pretty much need to upload it to a server. Here is a good tutorial on how to do that.

As usual, all of the above are restricted to browsers that support it. If you're going to stick to wmv video, you're pretty much limited to Internet Explorer 9+. 6-8 don't support either the video tag or the canvas. If you can add more video formats, you can use Firefox (3.5+) and Chrome.

brianchirls
  • 7,661
  • 1
  • 32
  • 32
  • 3
    Thanks. I have just made a bookmark script to take snapshot of Youtube Video: [youtube-screenshot](https://github.com/ReeganExE/youtube-screenshot) – ninhjs.dev Jun 22 '17 at 11:24
  • this is awesome, I wonder though how to take a snapshot at some point in the video? I mean the e.g 10th second. – Tomasz Mularczyk Feb 10 '18 at 18:36
  • For anyone having issues with CORS and a security error: http://overengineer.net/enabling-cors-for-html5-video-element-screenshots (add crossOrigin="anonymous" to the video) – Terren Feb 12 '19 at 00:24
  • @TomaszMularczyk call `video.currentTime = 10000` prior to `video.play()` – Petr Bela Jun 07 '20 at 02:07
  • If you need to capture the video at its current resolution, you can use `videoElement.videoHeight` and `videoElement.videoWidth` – Nick Bull Jan 11 '21 at 17:26
20
<html>
<body>

  <video controls>
    <source src="C:/Users/ganesha/Desktop/Aararum.mp4" type="video/mp4"></source>
  </video>
  <canvas id="canvas" width="640" height="480"></canvas>
  <button id="snap" onclick="snap()">Snap Photo</button>

  <!-- start the script ... within that declare variables as follows... -->
  <script>
  var video=document.querySelector('video');
  var canvas=document.querySelector('canvas');
  var context=canvas.getContext('2d');
  var w,h,ratio;

  //add loadedmetadata which will helps to identify video attributes

  video.addEventListener('loadedmetadata', function() {
    ratio = video.videoWidth/video.videoHeight;
    w = video.videoWidth-100;
    h = parseInt(w/ratio,10);
    canvas.width = w;
    canvas.height = h;
  },false);

  function snap() {
    context.fillRect(0,0,w,h);
    context.drawImage(video,0,0,w,h);
  }
  </script>

</body>
</html>
doppelgreener
  • 4,809
  • 10
  • 46
  • 63
Sijin Parambath
  • 309
  • 2
  • 3
  • Works great! But it only works if the video source is hosted on the same server and not cross origin. But thank you for your solution! – Jonathan Marzullo Jan 16 '17 at 14:28
  • so how do you solve that problem?? @JonathanMarzullo – MartianMartian Jun 18 '18 at 02:30
  • @Martian2029 since its a cross origin issue, that means that video must be on the same domain, port and protocol.. please see this for more info on cross origin policy.. https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy – Jonathan Marzullo Jun 18 '18 at 13:06
  • thanks, works perfectly in 2020, too! Combined with this scaling the captured photo can be fitted nicely into responsive pages: https://stackoverflow.com/a/3422425/1838044 – throbi May 21 '20 at 21:13
  • How to hide the canvas until snapshot is taken? – aquatic7 Feb 11 '21 at 02:39
15

You can take a copy of the current video and store it in a canvas element using the drawImage() function.

I have a complete example whose code you can view at Video/canvas screenshot which should set you on the right track.

Ian Devlin
  • 18,534
  • 6
  • 55
  • 73
  • However, this will require the OP to get rid of his `iframe`, and use a `video` tag instead. Not that there's anything wrong with that, on the contrary, just pointing it out. – Cerbrus Dec 07 '12 at 10:14
2
Sample code:
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"> 
</script>

</head>
<body>
<h2>Upload,Save and Download video </h2>
<form method="POST" action="" enctype="multipart/form-data">
<input type="file" name="video" id="upload"/>
</...>

<script>
var input = document.getElementById('upload');

input.addEventListener('change', function(event){
var file = this.files[0];
var url = URL.createObjectURL(file);

var video = document.createElement('video');
video.src = url;

var snapshot = function(){
    var canvas = document.createElement('canvas');
    var ctx = canvas.getContext('2d');

    ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
    var img_data = canvas.toDataURL('image/jpg');
    var filename = file.name;
    var filename = filename.split('.').slice(0, -1).join('.');



    document.getElementById("thumb").value = filename;
    $.post("thumb-saver.php", { 
        base:img_data, 
        output:"thumbnails/"+ filename +  '.jpg'
    }, function( data ) {
            //alert(data);
    });

    video.removeEventListener('canplay', snapshot);
 };

video.addEventListener('canplay', snapshot);
});
</script>
Chirag Patel
  • 559
  • 6
  • 14
-3

video mush contained in canvas tag first.

like this;

<canvas id="target-canvas">
    <video id="video-for-thumbs" autoplay src="http://.....></video> 
</canvas>

and js like this

setTimeout(function(){
    var canvas=$('#target-canvas')[0];
    canvas.getContext('2d').drawImage($('#video-for-thumbs')[0],0,0,1000,300);
    var f=canvas.toDataURL();
    console.log(f);

},200);

data will rendered in base64

and if you want get frame in the middle of video, you can increase delay time to run js.

in additional

you can add play back rate to increase speed of videos to cut waiting time to screenshot just for thumbnail

$('#video-for-thumbs')[0].playbackRate=9;
yuanganteng
  • 171
  • 9