Pass multiple input files to ffmpeg using a single stream in Node - node.js

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?

Related

Node.js : How can I add meta data to an audio file?

I have a .wav audio file that I would like to add meta data to, in Node.js:
let original = fs.readFileSync('./somewhere/something.wav').toString('base64')
let withMeta = addMeta(original)
fs.writeFileSync('./somewhere/something-more.wav', withMeta)
Is this possible ? Is there some Js library that allows you to write metadata (not just read/extract it) to an existing audio file.
Assuming you have ffmpeg on your system, you could use that in node via fluent-ffmpeg doing something like the following:
const ffmpeg = require('fluent-ffmpeg')
ffmpeg('./somewhere/something.wav')
.audioCodec('copy')
.outputOptions(
'-metadata', 'title=testtitle',
'-metadata', 'artist=testartist'
)
.output('./somewhere/something-more.wav')
.on('end', () => { console.log('done') })
.run()
Acceptable metadata keys for wave files in ffmpeg are: (source)
artist
comment
copyright
date
genre
language
title
album
track
encoder
timecode
encoded_by

first m3u8 ts segment not working after mp4 to m3u8 conversion by node js

index.js
const ffmpegPath = require('#ffmpeg-installer/ffmpeg').path;
const ffmpeg = require('fluent-ffmpeg');
const process = require('process');
const args = process.argv.slice(2);
if (args.length !== 4) {
console.error('Incorrect number of arguments');
process.exit(1);
}
const startTime = args[0];
const timeDuration = args[1];
const inputFile = args[2];
const outputFile=args[3];
ffmpeg.setFfmpegPath(ffmpegPath);
ffmpeg(inputFile)
.setStartTime(startTime)
.setDuration(timeDuration)
.output(outputFile)
.outputOptions('-hls_list_size 0')
.on('end', function(err) {
if(!err) { console.log('conversion Done') }
})
.on('error', function(err){
console.log('error: ', err)
}).run();
Here is the index.js and I'm running it by hitting the command on the terminal
node index.js 5 40 ./input.mp4 ./output.m3u8
Here 5 is for starting time and 40 is the time duration in seconds. The process creates m3u8 with ts files but the first ts file isn't getting created properly. It's been created in kb format while all the other files in mb format.
the output_test0 isn't getting generated properly and so that's why while playing the m3u8 file, the first few seconds is just static picture. This issue has been happening with the first ts output only. Any trick on how to fix it?
Following your comment about how it seems to be caused by the use of input seeking instead of output seeking:
Use seek() or seekOutput() instead of setStartTime(). The documentation describes the difference:
seek(time): seek output
Aliases: seekOutput().
Seeks streams before encoding them into the output. This is different from calling seekInput() in that the offset will only apply to one output. This is also slower, as skipped frames will still be decoded (but dropped).
The time argument may be a number (in seconds) or a timestamp string (with format [[hh:]mm:]ss[.xxx]).
ffmpeg('/path/to/file.avi')
.seekInput('1:00')
.output('from-1m30s.avi')
.seek(30)
.output('from-1m40s.avi')
.seek('0:40');
setStartTime() is an alias for seekInput(). From the same documentation:
seekInput(time): set input start time
Alias: setStartTime().
Seeks an input and only start decoding at given time offset.
Note that seek() or seekOutput() should be applied to the output and not on the input as seekInput(), i.e. after output().

How to convert wav file into 8000hz using Nodejs

I have tried to convert speech wav file to text using nodejs but it displays error like this:
Error:
data: '{\n "error": "This 8000hz audio input requires a narrow band
model."\n}',
Code :
let directory = `File Directory`;
let dirbuf = Buffer.from(directory);
let files = fs.readdirSync(directory);
// Create the stream.
// Pipe in the audio.
files.forEach(wav_files => {
//how can i convert that wav file into 8000hz and use that same wav file for speech to text convert
fs.createReadStream(wav_files).pipe(recognizeStream);
recognizeStream.on('data', function(event) { onEvent('Data:',event,wav_files); });
}
I am not sure whether you've already explored wav package or not. But I created a cheat like this:
const fs = require('fs');
const WaveFile = require('wavefile').WaveFile;
let wav = new WaveFile(fs.readFileSync("source.wav"));
// do it like this
wav.toSampleRate(8000);
// or like following way with your choice method
// wav.toSampleRate(44100, {method: "cubic"});
// write new file
fs.writeFileSync("target-file.wav", wav.toBuffer());
For complete running example clone node-cheat wav-8000hz and run node wav.js followed by npm i wavefile.

How to solve output underflow error using naudiodon / portaudio?

I am writing a small node.js program that will be able to play wav sound files on a chosen audio device.
The sound starts well but it is stoped before the end of the file.
Here is my code :
const fs = require("fs");
const wav = require("wav");
const portAudio = require("naudiodon");
const ao = new portAudio.AudioIO({
outOptions: {
channelCount: 2,
sampleFormat: portAudio.SampleFormat24Bit,
sampleRate: 44100,
}
});
const name = "myfile.wav";
const file = fs.createReadStream(`./sounds/${name}`);
const reader = new wav.Reader();
reader.on("format", () => {
reader.pipe(ao);
ao.start();
});
file.pipe(reader);
process.on("SIGINT", ao.quit);
When I modify the highWaterMark option of fs.createReadStream, it slightly change the cut position in the sound but it never goes until the end of it.
I always get a portAudio status - output underflow log error.
Thanks for any help !
I have been experiencing a similar error, and my solution was to manually write to the AudioIO stream instead of using the pipe commands.
So instead of
reader.on("format", () => {
reader.pipe(ao);
ao.start();
});
You would use
ao.start();
reader.on("data",chunk=>ao.write(chunk));
Output underflow is generally not an issue, but to avoid it I initialised a new instance of PortAudio before playing every file, however that is only applicable if you don't care about slight latency.

How to convert Uint8Array video to frames in nodejs

I want to be able to extract jpegs from a Uint8 array containing the data for a mpeg or avi video.
The module ffmpeg has the function fnExtractFrameToJPG but it only accepts a filename pointing to the video file. I want to be able to extract the frames from the UInt8Array.
One way to do it is to write the UInt8Array to a tmp file and then use the tmp file with ffmpeg to extract the frames:
const tmp = require("tmp");
const ffmpeg_ = require("ffmpeg");
function convert_images(video_bytes_array){
var tmpobj = tmp.fileSync({ postfix: '.avi' })
fs.writeFileSync(tmpobj.name, video_bytes_array);
try {
var process = new ffmpeg(tmpobj.name);
console.log(tmpobj.name)
process.then(function (video) {
// Callback mode
video.fnExtractFrameToJPG('./', { // make sure you defined the directory where you want to save the images
frame_rate : 1,
number : 10,
file_name : 'my_frame_%t_%s'
}, function (error, files) {
if (!error)
tmpobj.removeCallback();
});
});
} catch (e) {
console.log(e.code);
console.log(e.msg);
}
}
Another possibitlity is to use opencv after you save the UInt8Array to a tmp file. Another solution is to use stream and ffmpeg-fluent which would not require using tmp files.

Resources