merge multiple videos to a single .mp4 files using fluent-ffmpeg - node.js

Version information
fluent-ffmpeg version: 2.1.2
ffmpeg version:4
OS:linux mint
Code to reproduce
var fluent_ffmpeg = require("fluent-ffmpeg");
var mergedVideo = fluent_ffmpeg();
mergedVideo
.mergeAdd('./Video1.mp4')
.mergeAdd('./Video2.mp4')
// .inputOptions(['-loglevel error','-hwaccel vdpau'])
// .outputOptions('-c:v h264_nvenc')
.on('error', function(err) {
console.log('Error ' + err.message);
})
.on('end', function() {
console.log('Finished!');
})
.mergeToFile('./mergedVideo8.mp4', '/tmp');
When I run this code, then I get conversion failed error.
Observed results
Error ffmpeg exited with code 1: Error reinitializing filters!
Failed to inject frame into filter network: Invalid argument
Error while processing the decoded data for stream #3:0
Conversion failed!
I have tried the same conversion using the command line:
ffmpeg -f concat -i textfile -c copy -fflags +genpts merged8.mp4
Where textfile has the following content-
file 'Video1.mp4'
file 'Video2.mp4'
And I was able to concatenate the video file. But I want to get the same result using fluent-ffmpeg.

Related

ffmpeg nodejs lambda crop issue

I have a lambda function to crop videos with ffmpeg
I am installing the layer this way
#!/bin/bash
mkdir -p layer
cd layer
rm -rf *
curl -O https://johnvansickle.com/ffmpeg/builds/ffmpeg-git-amd64-static.tar.xz
tar -xf ffmpeg-git-amd64-static.tar.xz
mv ffmpeg-git-*-amd64-static ffmpeg
rm ffmpeg-git-amd64-static.tar.xz
I do not really which version but should be recent as I did it today for the last time
Then my node js lambda function is running with the following nodejs module https://github.com/fluent-ffmpeg/node-fluent-ffmpeg
return new Promise((resolve, reject) => {
ffmpeg(inputFile.name)
.videoFilters(`crop=${width}:${height}:${x}:${y}`)
.format('mp4')
.on('error', reject)
.on('end', () => resolve(fs.readFileSync(outputFile.name)))
.save(outputFile.name);
with for example videoFilters('crop=500:500:20:20')
And I have the folowing error
ffmpeg exited with code 1: Error reinitializing filters!
Failed to inject frame into filter network: Invalid argument
Error while processing the decoded data for stream #0:0
Conversion failed!
On my local computer I am running the following command on the exact same image
ffmpeg -i in.mp4 -filter:v "crop=500:500:20:20" out.mp4
my version of ffmpeg is 4.2.2 and this is working great
I do not have the issue with all videos, here one video which is causing me the issue https://ajouve-util.s3.amazonaws.com/earth.mp4
Here is slightly modified code that works:
var ffmpeg = require('fluent-ffmpeg');
var reject = function(something) {
console.error("got error", something);
}
var resolve = function() {
console.info("Good, job done. Now read output file");
}
var width = 500;
var height = 500;
var x = 20;
var y = 20;
var filter_string = `crop=${width}:${height}:${x}:${y}`;
console.log("filter_string", filter_string);
var onStart = function(commandLine) {
console.log('Spawned Ffmpeg with command: ' + commandLine);
}
var command = ffmpeg('earth.mp4')
.videoFilters(filter_string)
.format('mp4')
.on('error', reject)
.on('end', resolve)
.on('start', onStart)
.output('earth_cropped.mp4');
console.log("running..");
command.run();
console.log("Done");
With earth.mp4 downloaded from your link in same dir output is:
node main.js
filter_string crop=500:500:20:20
running..
Done
Spawned Ffmpeg with command: ffmpeg -i earth.mp4 -y -filter:v crop=500:500:20:20 -f mp4 earth_cropped.mp4
Good, job done. Now read output file
And we get earth_cropped.mp4 file as expected.
I bet the error is in input params, so add .on('start', onStart) in your code and check logs to see exact command used to validate it ;)

Pass multiple input files to ffmpeg using a single stream in Node

I'm trying to use ffmpeg to merge multiple video files. Every file has the same encoding, and they just need to be stitched together. The problem I'm having is that I'd like to do this using streams, but ffmpeg only supports one input stream per command.
Since the files have the same encoding, I thought I could merge them into a single stream, and feed it as an input to ffmpeg.
const CombinedStream = require("combined-stream")
const ffmpeg = require("fluent-ffmpeg")
const AWS = require("aws-sdk")
const s3 = new AWS.S3()
const merge = ({ videos }) => {
const combinedStream = CombinedStream.create();
videos //I take my videos from S3 and merge them
.map((video => {
return s3
.getObject({
Bucket: "myAWSBucketName",
Key: video
})
.createReadStream()
}))
.forEach(stream => {
combinedStream.append(stream)
})
ffmpeg()
.input(combinedStream)
.save("/tmp/file.mp4")
}
merge({ videos: ["video1.mp4", "video2.mp4"]})
I was hoping ffmpeg could read the files from the single stream and output them together, but I got this error instead:
Error: ffmpeg exited with code 1: pipe:0: Invalid data found when processing input
Cannot determine format of input stream 0:0 after EOF
Error marking filters as finished
Conversion failed!
Can anyone help me?

Piping ffmpeg thumbail output to another program

I'm trying to capture frames from a live video stream (h.264) and pipe the resulting JPG images to a Node JS script, instead of saving these individual frames directly to .jpg files.
As a test, I created the following Node JS script, to simply capture the incoming piped data, then dump it to a file:
// pipe.js - test pipe output
var fs = require('fs');
var data = '';
process.stdin.resume();
process.stdin.setEncoding('utf8');
var filename = process.argv[2];
process.stdin.on('data', (chunk) => {
console.log('Received data chunk via pipe.');
data += chunk;
});
process.stdin.on('end', () => {
console.log('Data ended.');
fs.writeFile(filename, data, err => {
if (err) {
console.log('Error writing file: error #', err);
}
});
console.log('Saved file.');
});
console.log('Started... Filename = ' + filename);
Here's the ffmpeg command I used:
ffmpeg -vcodec h264_mmal -i "rtsp://[stream url]" -vframes 1 -f image2pipe - | node pipe.js test.jpg
This generated the following output, and also produced a 175kB file which contains garbage (unreadable as a jpg file anyway). FYI using ffmpeg to export directly to a jpg file produced files around 25kB in size.
...
Press [q] to stop, [?] for help
[h264_mmal # 0x130d3f0] Changing output format.
Input stream #0:0 frame changed from size:1280x720 fmt:yuvj420p to size:1280x720 fmt:yuv420p
[swscaler # 0x1450ca0] deprecated pixel format used, make sure you did set range correctly
Received data chunk via pipe.
Received data chunk via pipe.
frame= 1 fps=0.0 q=7.7 Lsize= 94kB time=00:00:00.40 bitrate=1929.0kbits/s speed=1.18x
video:94kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.000000%
Received data chunk via pipe.
Data ended.
Saved file.
You can see that the Node JS script is receiving piped data (per the "Received data via pipe" messages above. However, it doesn't seem to be outputting a valid JPG file. I can't find a way to specifically request that ffmpeg output JPG format, since there is no -vcodec option for JPG. I tried using -vcodec png and outputting to a .png file, but the resulting file was about 2MB in size and also unreadable as a png file.
Is this a problem caused by using utf8 encoding, or am I doing something else wrong?
Thanks for any advice.
UPDATE: OK I got it to send a single jpg image correctly. The issue was in the way Node JS was capturing the stream data. Here's a working script:
// pipe.js - capture piped binary input and write to file
var fs = require('fs');
var filename = process.argv[2];
console.log("Opening " + filename + " for binary writing...");
var wstream = fs.createWriteStream(filename);
process.stdin.on('readable', () => {
var chunk = '';
while ((chunk = process.stdin.read()) !== null) {
wstream.write(chunk); // Write the binary data to file
console.log("Writing chunk to file...");
}
});
process.stdin.on('end', () => {
// Close the file
wstream.end();
});
However, now the problem is this: when piping the output of ffmpeg to this script, how can I tell when one JPG file ends and another one begins?
ffmpeg command:
ffmpeg -vcodec h264_mmal -i "[my rtsp stream]" -r 1 -q:v 2 -f singlejpeg - | node pipe.js test_output.jpg
The test_output.jpg file continues to grow as long as the script runs. How can I instead know when the data for one jpg is complete and another one has started?
According to this, jpeg files always start with FF D8 FF and end with FF D9, so I guess I can check for this ending signature and start a new file at that point... any other suggestions?

Video - extract image from DASH m4s file using ffmpeg

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.

fluent-ffmpeg thumbnail creation error

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/");

Resources