21

I am trying to write a nodejs server that will take a time input (possibly part of url path) and then provide a still of the video frame at that time index as a jpeg picture.

I can do this easily in plain Javascript, but I cant see a way to do this in nodejs. I know I will probably need to use a canvas plugin like node-canvas to do the snapshot.

Any ideas welcome.

The following is how I do it in Javascript at the moment:

myjavascript.js

function capture(video, scaleFactor) {
    if(scaleFactor == null){
        scaleFactor = 1;
    }
    var w = video.videoWidth * scaleFactor;
    var h = video.videoHeight * scaleFactor;
    stdout("<br/> w: "+ w+ "<br/> h: "+ h);
    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("videoTag");
 var output = document.getElementById("output");
 var canvas = capture(video, 1);
 output.innerHTML = '';
 output.appendChild(canvas);
}

index.html

<html>
<head>
    <title>video snap</title>   
    <script type="text/javascript" src="myjavascript.js"></script>
</head>
<body>

<div id="video_container" >
        <video id="videoTag" width="640" height="360" autobuffer="" controls="true">
            <source src="frozenplanet.mp4" type="video/mp4">
            <source src="frozenplanet.ogv" type="video/ogg">
        </video>
</div>

<div id="output"></div>

</body>
</html>
MYR
  • 381
  • 1
  • 2
  • 12
  • 3
    Node.js hasn't video procesing tool and it's not recommended to process video by web-application. If you want to process video on the server side, you needs an appropriate tool for that and some queue realization (maybe on node) for it. – Phillip Kovalev Jan 10 '12 at 12:17

3 Answers3

48

node-fluent-ffmpeg has a nice takeScreenshots function.

var proc = new ffmpeg('/path/to/your_movie.avi')
  .takeScreenshots({
      count: 1,
      timemarks: [ '600' ] // number of seconds
    }, '/path/to/thumbnail/folder', function(err) {
    console.log('screenshots were saved')
  });
fent
  • 17,861
  • 15
  • 87
  • 91
  • I would love to try this, seems more elegant than exec ffmpeg, however I cant get npm to install it or anything else at the moment :S – MYR Jan 13 '12 at 16:37
  • npm install fluent-ffmpeg npm http GET https://registry.npmjs.org/fluent-ffmpeg npm ERR! Error: failed to fetch from registry: fluent-ffmpeg ... This is the error I get, checked proxy settings etc, all fine! :( – MYR Jan 13 '12 at 16:43
  • Well then try to fix that problem with npm because it is definitely better to have all the modules in userland available than having to reinvent the wheel. – fent Jan 13 '12 at 16:43
  • Can you see the registry ok via npm, if I go to the registry url on a browser I can seen the json fine, however trying npm on any module gives me that, there are also posts appearing that suggest this may be something else. e.g. https://github.com/nodejitsu/forever/issues/216 – MYR Jan 13 '12 at 16:46
  • It's happening with me too, and I have the latest version. – fent Jan 13 '12 at 16:57
  • there is/was apparently some issue with npm handling of proxys, so when I tried it in an environment that does not have proxy it worked fine. – MYR Jan 16 '12 at 21:32
  • Further investigation has shown that the npm code plays havoc with https over proxy. You can solve this by doing the following: "npm --proxy http://proxy-address:port/ --registry=http://registry.npmjs.org install fluent-ffmpeg" – MYR Feb 17 '12 at 21:17
  • hi when i use node-fluent-ffmpeg i got a err says "meta data contains no durations, aborting screenshot creation" any idea why? – paynestrike Oct 29 '12 at 01:54
  • I'd look at the usage for it and make sure I'm not doing anything the wrong way. If you're still getting the error, open an issue. – fent Oct 29 '12 at 05:15
  • 2
    There is no such API in the latest version of the library. The callback method will not be called. – Siamaster Jun 01 '20 at 10:02
  • where do you specify the size? – d1fficult Aug 18 '20 at 23:53
  • See the answer by Steven Westmoreland for the correct answer in 2021. The API changed – Christian Ayscue May 06 '21 at 16:07
7

You can use the node-fluent-ffmpeg module for this. You'll need to install ffmpeg; on MacOS, install Homebrew then use the brew install ffmpeg command.

var ffmpeg = require('fluent-ffmpeg');

ffmpeg('/path/to/video.mp4')
  .on('end', function() {
    console.log('Screenshots taken');
  })
  .on('error', function(err) {
    console.error(err);
  })
  .screenshots({
    // Will take screenshots at 20%, 40%, 60% and 80% of the video
    count: 4,
    folder: '/path/to/output'
  });
3

As 'node-fluent-ffmpeg' wasn't not working for me for some reason, I figured this out myself - based on the code of video-thumb (which also wasn't working for me). You do need to install FFMPEG before you can use this code, here is tutorial on how to do so for Mac.

var path = require('path'), // Default node module
    pathToFile = path.join(__dirname, 'folder', 'file.mov'),
    pathToSnapshot = path.join(__dirname, 'folder', 'file-snapshot.jpg');

// Also a default node module
require('child_process').exec(('ffmpeg -ss 00:00:25 -i ' + pathToFile + ' -vframes 1 -q:v 2 ' + pathToSnapshot), function () {

    console.log('Saved the thumb to:', pathToSnapshot);

});