Node-fluent-ffmpeg: multiple screenshots very slow

Created on 19 Sep 2015  路  11Comments  路  Source: fluent-ffmpeg/node-fluent-ffmpeg

I use

handle.thumbnails({
  filename: output,
  count: 3,
  timemarks: ['20%', '40%', '60%']
}, outputDir);

The resulting fluent-ffmpeg command is something like this

ffmpeg -ss 508.1740876 -i /files/6ad6f3c5bd59cee74fdacb56eec45519.mp4
 -y -filter_complex split=3[screen0][screen1][screen2] -vframes 1 -map [screen0] 
/files/thumbs/6ad6f3c5bd59cee74fdacb56eec45519.mp4_1.png -vframes 1 -map [screen1] -ss 
508.1740876 /files/thumbs/6ad6f3c5bd59cee74fdacb56eec45519.mp4_2.png -vframes 1 -map [screen2] 
-ss 1016.3481752000001 /files/thumbs/6ad6f3c5bd59cee74fdacb56eec45519.mp4_3.png

This seems to be _really_ slow with large files (~1gb).
Instead, if i launch the same ffmpeg executable like this (only asking 1 screenshot)

ffmpeg -ss 1016.3481752000001 -i /files/6ad6f3c5bd59cee74fdacb56eec45519.mp4 -y 
 -vframes 1 /files/thumbs/6ad6f3c5bd59cee74fdacb56eec45519.mp4_3.png

then it's SUPER fast. I am no expert when it comes to ffmpeg cli. Do you see something that is obviously wrong with the former ?

Most helpful comment

@aravindnc Oh, that's Great!
I write simpler codes by using your idea, like this:

const fs = require("fs");
const path = require("path");
const ffmpeg = require("fluent-ffmpeg");

const count = 10;
const timestamps = [];
const startPositionPercent = 5;
const endPositionPercent = 95;
const addPercent = (endPositionPercent - startPositionPercent) / (count - 1);
let i = 0;

if (!timestamps.length) {
    let i = 0;
    while (i < count) {
        timestamps.push(`${startPositionPercent + addPercent * i}%`);
        i = i + 1;
    }
}

function takeScreenshots(file) {
    ffmpeg(file)
        .on("start", () => {
            if (i < 1) {
                console.log(`start taking screenshots`);
            }
        })
        .on("end", () => {
            i = i + 1;
            console.log(`taken screenshot: ${i}`);

            if (i < count) {
                takeScreenshots(file);
            }
        })
        .screenshots({
            count: 1,
            timemarks: [timestamps[i]],
            filename: `%b-${i + 1}.jpg`
        }, path.join(path.dirname(file), `screenshots`));
}

All 11 comments

I think i got it. If i use -ss before -i then the seek is very fast. If you do it after -i the seek will decode all the frames of the files. I don't think there is a way to do multiple screenshots with -ss before -i. So i have to create multiple and separate commands.

Better ways ?

Yes it's because -ss is not before -i and you need to add that before each input.

So here's a working example that takes it out super fast.

ffmpeg -ss 10 -i test.avi -frames:v 1 -f image2 -map 0:v:0 thumbnails/output_0.png -ss 800 -i test.avi -     frames:v 1 -f image2 -map 1:v:0 thumbnails/output_1.png -ss 2400 -i test.avi -frames:v 1 -f image2 -map 2:v:0 thumbnails/output_2.png
  • So the 0 : v : 0 means 1st input, and select video streams, first videostream (0)
  • 1 : v : 0 means 2nd input, and select video streams, first videostream (0)
  • 2 : v : 0 means 3rd input, and select video streams, first videostream (0)

It adds the screenshots to a thumbnails folder.

Hope that helps :)

.thumbnails() is a crude approach that does the job but is not guaranteed to be the best way.
For specific needs use your own solution, @Muqito 's idea of using the same input multiple times is a good way to go. You can code that with fluent-ffmpeg without any issues.

Closing this because it has not had any activity for months. Feel free to reopen if you still have issues/questions :)

The best thing to do call the ffmpeg function after each onEnd event like below. This helped me to generate thumbnails < 1 sec which was 1/100th time before what I was using.

ffmpeg(DirectoryPath + FileName)
    .on('end', function (files) {   
        ffmpeg(values.DirectoryPath + values.FileName)
        .on('end', function (files) {   
            // place next ffmpeg function here
        })
        .screenshots({
            count: 1,
            timestamps: ['75%'],
            filename: VideoUniqId + '_2' + thumbnailExtension,
            folder: _thumbSaveLocation
        });
    })                                      
})
.screenshots({
    count: 1,
    timestamps: ['25%'],
    filename: VideoUniqId + '_1' + thumbnailExtension,
    folder: _thumbSaveLocation
});

@aravindnc Oh, that's Great!
I write simpler codes by using your idea, like this:

const fs = require("fs");
const path = require("path");
const ffmpeg = require("fluent-ffmpeg");

const count = 10;
const timestamps = [];
const startPositionPercent = 5;
const endPositionPercent = 95;
const addPercent = (endPositionPercent - startPositionPercent) / (count - 1);
let i = 0;

if (!timestamps.length) {
    let i = 0;
    while (i < count) {
        timestamps.push(`${startPositionPercent + addPercent * i}%`);
        i = i + 1;
    }
}

function takeScreenshots(file) {
    ffmpeg(file)
        .on("start", () => {
            if (i < 1) {
                console.log(`start taking screenshots`);
            }
        })
        .on("end", () => {
            i = i + 1;
            console.log(`taken screenshot: ${i}`);

            if (i < count) {
                takeScreenshots(file);
            }
        })
        .screenshots({
            count: 1,
            timemarks: [timestamps[i]],
            filename: `%b-${i + 1}.jpg`
        }, path.join(path.dirname(file), `screenshots`));
}

@matori Awesome ;)

Thank you @davibe for the question, everyone for the discussion, and @matori for a ready-to-use solution. I spent over a day trying different things and even started figuring out a way to use ffmpeg directly from my node project abandoning fluent-ffmpeg. I would highly recommend (@njoyard) sharing a link to this discussion as a caveat under screenshots to save others the headache 馃憤

Just in case anyone end here from googling "ffmpeg slow to take screenshot" this is super fast.

ffmpeg -i "$1" -ss 00:28:0.0 -vframes 1 "${1%.*}".screenshot1.png

@matori Great solution, thanks!

@aravindnc Oh, that's Great!
I write simpler codes by using your idea, like this:

const fs = require("fs");
const path = require("path");
const ffmpeg = require("fluent-ffmpeg");

const count = 10;
const timestamps = [];
const startPositionPercent = 5;
const endPositionPercent = 95;
const addPercent = (endPositionPercent - startPositionPercent) / (count - 1);
let i = 0;

if (!timestamps.length) {
    let i = 0;
    while (i < count) {
        timestamps.push(`${startPositionPercent + addPercent * i}%`);
        i = i + 1;
    }
}

function takeScreenshots(file) {
    ffmpeg(file)
        .on("start", () => {
            if (i < 1) {
                console.log(`start taking screenshots`);
            }
        })
        .on("end", () => {
            i = i + 1;
            console.log(`taken screenshot: ${i}`);

            if (i < count) {
                takeScreenshots(file);
            }
        })
        .screenshots({
            count: 1,
            timemarks: [timestamps[i]],
            filename: `%b-${i + 1}.jpg`
        }, path.join(path.dirname(file), `screenshots`));
}

Hi @matori, sorry to ask, but I am really newbie in cmd commands. So, how can I run this script in win10? I have a folder with several videos files. I want to take 6 screenshots of each video file, starting with 3 minutes and 30 seconds distance each others and save in a desktop folder.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

judemorrissey picture judemorrissey  路  5Comments

mStirner picture mStirner  路  4Comments

echo66 picture echo66  路  3Comments

odigity picture odigity  路  4Comments

LauraWebdev picture LauraWebdev  路  3Comments