I am working on one Meteor App where I am using CollectionFS to upload Files.
I am able to upload and generate thumbnails for Images.
But my Issue is : How should I create thumbnails for Videos?
I can see that it is possible via command line: https://superuser.com/questions/599348/can-imagemagick-make-thumbnails-from-video
But how can I apply this to my Meteor code.
Here is what I am doing:
VideoFileCollection = new FS.Collection("VideoFileCollection", {
stores: [
new FS.Store.FileSystem("videos", {path: "/uploads/videos"}),
new FS.Store.FileSystem("videosthumbs", {path: "/uploads/videosthumbs",
beforeWrite: function(fileObj) {
// We return an object, which will change the
// filename extension and type for this store only.
return {
extension: 'png',
type: 'image/png'
};
},
transformWrite: function(fileObj, readStream, writeStream) {
gm(readStream, fileObj.name()).stream('PNG').pipe(writeStream);
}
})
]
});
What is happening here that video is getting Uploaded to "videos" folder and one PNG is created under "videosthumbs" with 0 Bytes and thumbnail is not getting generated.
I have also read at : https://github.com/aheckmann/gm#custom-arguments
that we can use : gm().command() - Custom command such as identify or convert
Can Anybody advise me on what can be done to handle this situation?
Thanks and Regards
Checked the link that you have added and here is a rough solution that might help you
ffmpeg -ss 600 -i input.mp4 -vframes 1 -s 420x270 -filter:v 'yadif' output.png
Here is a function that i have made.
var im = require('imagemagick');
var args = [
"ffmpeg", "-ss", "600", "-i", "input.mp4", "-vframes", " 1", "-s", "420x270", "-filter:v", "'yadif'", "output.png"
];
// Function to convert and
im.convert(args, function(err)
if (err) throw err;
});
Related
I am creating a server-side video renderer in node.js. I need to add images on an existing video file every frame. I need to set the specific position of each frame in the rendered video. I am holding all the frames of the images in Readable. This is my code that works, but does not take into account the position of the images. How can I modify it? Of course I have a list with images coordinates - but I don't know how to make a filter out of this.
this.stream is a list of images.
const filter = ["[1:v]format=argb,setpts=PTS+" + 0 + "/TB[out]",
{
filter: "overlay",
options: {
enable: "between(t," + 0 + "," + 9 + ")",
x: "0",
y: "0",
},
inputs: "[0:v][out]",
outputs: "tmp",
}
]
const Options = [
"-crf 16",
"-f mp4",
"-vcodec libx264",
"-movflags frag_keyframe+empty_moov",
"-pix_fmt yuv420p",
];
var ffmpeg = require('fluent-ffmpeg');
ffmpeg.setFfmpegPath(ffm.path);
var command = ffmpeg();
const outputStream = fs.createWriteStream(this.Path + "test.mp4");
command.input(this.Path + "input.mp4");
command.input(this.stream).inputFPS(23);
command.outputOptions(Options);
command.fps(23);
command.complexFilter(filter, "tmp");
command.output(outputStream);
Instead of using const filter I suggest using the build-in fluent-ffmpeg functions. This code will add an image positioned (50, 50):
video_path and image_path are file paths to video and image.
overlay=50:50 will place your image at position x=50, y=50 from top-left.
Then you save the file and handle errors
const command = ffmpeg(video_path)
.input(image_path)
.complexFilter([
`overlay=50:50`,
])
.saveToFile("./public/vid.mp4")
.on("error", (err) => {
console.log(err);
})
.on("end", () => {
console.log("File saved.");
});
For more info see github issue: https://github.com/fluent-ffmpeg/node-fluent-ffmpeg/issues/770
I'm trying to create a thumbnail from a dash stream m4s file.
I have the mpd, init.mp4 file and the m4s files.
I have the code using nodeJS ffmpeg package that extracts image from an mp4 file:
try {
var process = new ffmpeg('video.mp4');
process.then(function (video) {
// Callback mode
video.fnExtractFrameToJPG('C:\\files\\nodejs', {
start_time: `1:50:30`,
frame_rate : 1,
file_name : 'my_frame_%t_%s'
}, function (error, files) {
if (!error)
console.log('Frames: ' + files);
});
}, function (err) {
console.log('Error: ' + err);
});
} catch (e) {
console.log(e.code);
console.log(e.msg);
}
But because i'm reading my files from a dash-stream i'm getting an m4s files.
I've tried to convert the m4s format into mp4 and then use the code above, but the ffmpeg( fluent-ffmpeg to be exact) is returning an error message
an error occured: ffmpeg exited with code 1:
C:\files\nodejs\testFiles\000000.m4s: Invalid data found when
processing input
The code i used to convert is:
var proc = new fluent({source: "C:\\files\\nodejs\\testFiles\\000000.m4s",
nolog: true})
//useless i think - not working
//proc.setFfmpegPath("C:\\files\\ffmpeg-20170620-ae6f6d4-win64-static\\bin")
proc.withSize('50%').withFps(24).toFormat('mp4')
.on('end', function(){
console.log('file has been converted successfully');
})
.on('error', function(err){
console.log('an error occured: ' + err.message);
})
.saveToFile("C:\\files\\nodejs\\new.mp4");
Is it possible to convert a single m4s file to mp4?
If not, what is the right way of converting m4s to mp4 using ffmpeg with nodejs?
I couldn't find any reference for that, but if it is possible to extract an image directly from the m4s file i think it will solve the problem faster.
It is possible to use this site to download all the *.m4s files, mpd and init.mp4 files using the network section (f12 in Chrome browser) and check the code.
I created a API in node that you can upload a video (.mp4, .avi, etc). Then, the video is request by a iOS app in swift.
I would like to use HTTP Live streaming from the app. Can you help me how can I transform the video file to chunks .ts and generate the playlist file (m3u8) to be consumed by the app?
This is the correct flow?
What it's the best solution?
Thanks!
Finally I have a solution, I use fluent-ffmpeg like this :
var ffmpeg = require('fluent-ffmpeg');
ffmpeg(video, { timeout: 432000 })
.addOption('-level', 3.0)
// size
.addOption('-s','640x360')
// start_number
.addOption('-start_number', 0)
// set hls segments time
.addOption('-hls_time', 10)
// include all the segments in the list
.addOption('-hls_list_size', 0)
// format -f
.format('hls')
// setup event handlers
.on('start', function(cmd) {
console.log('Started ' + cmd);
})
.on('error', function(err) {
logger.error('an error happened: ' + err.message);
})
.on('end', function() {
logger.debug('File has been converted succesfully');
})
.save(outputDir)
ffmpeg comes with HLS streaming capability.
ffmpeg -i "input" output.m3u8
For more information visit: ffmpeg hls documentation
i try to create a video thumbnail with fluent-ffmpeg here is my code
var ffmpeg = require('fluent-ffmpeg');
exports.thumbnail = function(){
var proc = new ffmpeg({ source: 'Video/express2.mp4',nolog: true })
.withSize('150x100')
.takeScreenshots({ count: 1, timemarks: [ '00:00:02.000' ] }, 'Video/', function(err, filenames) {
console.log(filenames);
console.log('screenshots were saved');
});
}
but i keep getting this error
"mate data contains no duration, aborting screenshot creation"
any idea why,
by the way am on windows, and i put the ffmpeg folder in c/ffmpeg ,and i added the ffmpeg/bin in to my environment varableļ¼ i dont know if fluent-ffmpeg need to know the path of ffmpeg,but i can successfully create a thumbnail with the code below
exec("C:/ffmpeg/bin/ffmpeg -i Video/" + Name + " -ss 00:01:00.00 -r 1 -an -vframes 1 -s 300x200 -f mjpeg Video/" + Name + ".jpg")
please help me!!!
I think the issue can be caused by the .withSize('...') method call.
The doc says:
It doesn't interract well with filters. In particular, don't use the size() method to resize thumbnails, use the size option instead.
And the size() method is an alias of withSize().
Also - but this is not the problem in your case - you don't need to set either the count and the timemarks at the same time. The doc says:
count is ignored when timemarks or timestamps is specified.
Then you probably could solve with:
const ffmpeg = require('fluent-ffmpeg');
exports.thumbnail = function(){
const proc = new ffmpeg({ source: 'Video/express2.mp4',nolog: true })
.takeScreenshots({ timemarks: [ '00:00:02.000' ], size: '150x100' }, 'Video/', function(err, filenames) {
console.log(filenames);
console.log('screenshots were saved');
});
}
Have a look at the doc:
https://github.com/fluent-ffmpeg/node-fluent-ffmpeg#screenshotsoptions-dirname-generate-thumbnails
FFmpeg needs to know the duration of a video file, while most videos have this information in the file header some file don't, mostly raw videos like a raw H.264 stream.
A simple solution could be to remux the video prior to take the snapshot, the FFmpeg 0.5 command for this task it's quite simple:
ffmpeg -i input.m4v -acodec copy -vcodec copy output.m4v
This command tells FFmpeg to read the "input.m4v" file, to use the same audio encoder and video encoder (no encoding at all) for the output, and to output the data into the file output.m4v.
FFmpeg automatically adds all extra metadata/header information needed to take the snapshot later.
Try this code to create thumbnails from Video
// You have to Install Below packages First
var ffmpegPath = require('#ffmpeg-installer/ffmpeg').path;
var ffprobePath = require('#ffprobe-installer/ffprobe').path;
var ffmpeg = require('fluent-ffmpeg');
ffmpeg.setFfmpegPath(ffmpegPath);
ffmpeg.setFfprobePath(ffprobePath);
var proc = ffmpeg(sourceFilePath)
.on('filenames', function(filenames) {
console.log('screenshots are ' + filenames.join(', '));
})
.on('end', function() {
console.log('screenshots were saved');
})
.on('error', function(err) {
console.log('an error happened: ' + err.message);
})
// take 1 screenshots at predefined timemarks and size
.takeScreenshots({ count: 1, timemarks: [ '00:00:01.000' ], size: '200x200' }, "Video/");
I am building an app with node.js, I successfully uploaded the video, but I need to generate a video thumbnail for it. Currently I use node exec to execute a system command of ffmpeg to make the thumbnail.
exec("C:/ffmpeg/bin/ffmpeg -i Video/" + Name + " -ss 00:01:00.00 -r 1 -an -vframes 1 -f mjpeg Video/" + Name + ".jpg")
This code is coming from a tutorial from http://net.tutsplus.com/tutorials/javascript-ajax/how-to-create-a-resumable-video-uploade-in-node-js/
the code above did generate a jpg file but it's not a thumbnail but a video screen shot, I wonder is there any other method to generate video thumbnail, or how to exec the ffmpeg command to make a real thumbnail (resized), and I prefer png file.
Reference to GitHub fluent-ffmpeg project.
Repeating example from original StackOverflow answer:
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')
});
Resize by adding a -s widthxheight option to your command.
There is a node module for this:
video-thumb
It basically just wraps a call to exec ffmpeg
I recommend using https://www.npmjs.com/package/fluent-ffmpeg to call ffmpeg from Node.js
Using media-thumbnail, you can easily generate thumbnails from your videos. The module basically wraps the ffmpeg thumbnail functionality.
const mt = require('media-thumbnail')
mt.forVideo(
'./path/to/video.mp4',
'./path/to/thumbnail.png', {
width: 200
})
.then(() => console.log('Success'), err => console.error(err))
You can also create thumbnails from your images using this package.
Instead I would recommend using thumbsupply. In addition to provide you with thumbnails, it caches them to improve performance significantly.
npm install --save thumbsupply
After installing the module, you can use it in a following way.
const thumbsupply = require('thumbsupply')("com.example.application");
thumbsupply.generateThumbnail('some-video.mp4')
.then(thumb => {
// serve thumbnail
})
app.post('/convert', upload.any(), (req, res) => {
console.log("calling", req.files)
let thumbNailName = req.files[0].filename.split('.')
var gm = require('gm');
gm('./src/Upload/'+req.files[0].filename)// get pdf file from storage folder
.thumb(
50, // Width
50, // Height
'./src/thumbnail/'+thumbNailName[0]+'.png', // Output file name
80, // Quality from 0 to 100
function (error, stdout, stderr, command) {
if (!error) {
console.log("processing");
} else {
console.log("error")
}
}
);
})