Creating A MP4 clip with Node js - node.js

Can I create video clips from an mp4 video with node js streams? I am sure there are npms for this task, but is this something that can be done with just streams?
When I create a server, I can pipe a brief clip from the beginning of a video to an HttpResponse stream and pipe it to a file Stream with the following code: (It works!)
const fs = require('fs');
const http= require('http');
http.createServer(async (req, res) => {
// Creating clip from the beginning to 5% of the video
var { size } = fs.statSync('./Fun.mp4');
var start = 0
var end = .05*size;
var videoClip = fs.createReadStream('./Fun.mp4', { start, end })
var fileCopy = fs.createWriteStream('./Fun-Copy.mp4')
res.writeHead(200, {'Content-Type': 'video/mp4'})
videoClip.pipe(res)
videoClip.pipe(fileCopy)
}).listen(3000);
However, if I change the start position to the middle of the video, it doesn't work. I don't see the clip in the web browser, and Qucktime cannot play the copy that was produced.
// Attempting to create a clip from 50% to 60% of the video
var { size } = fs.statSync('./Fun.mp4');
var start = 0.5*size;
var end = 0.6*size;
It seems like mp4 is incomplete without the beginning. Is there a way that I can create clips from a larger video file with streams. Is there some meta elements or something, or a specific number of bytes that need to be copied from the beginning of an mp4 file?
Does node js handle audio the same way? Can I build audio clips from a larger file with node js streams?

There is not a specific number of bytes needed. MP4 uses an index like structure to organize the files. If you modify the file at all, the index (called the moov box) needs to be rewritten in its entirety.

Related

Write multiple files to http response with streams in nodejs

I have an array of files that I have to pack into a gzip archive and send them through http response on the fly. That means I can't store the whole file in the memory yet I have to synchronously pipe them into tar.entry or everything is going to break.
const tar = require('tar-stream'); //lib for tar stream
const { createGzip } = require('zlib'); //lib for gzip stream
//large list of huge files.
const files = [ 'file1', 'file2', 'file3', ..., 'file99999' ];
...
//http request handler:
const pack = tar.pack(); //tar stream, creates .tar
const gzipStream = createGzip(); //gzip stream so we could reduce the size
//pipe archive data trough gzip stream
//and send it to the client on the fly
pack.pipe(gzipStream).pipe(response);
//The issue comes here, when I need to pass multiple files to pack.entry
files.forEach(name => {
const src = fs.createReadStream(name); //create stream from file
const size = fs.statSync(name).size; //determine it's size
const entry = pack.entry({ name, size }); //create tar entry
//and this ruins everything because if two different streams
//writes smth into entry, it'll fail and throw an error
src.pipe(entry);
});
Basically I need for the pipe to complete sending data (smth like await src.pipe(entry);), but pipes in nodejs don't do that. So is there any way I could get around it?
Nevermind, just don't use forEach in this case

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 convert a node gd image to a stream that I can pipe?

I'm using node-gd to process images, but I'd like to do a few things before saving them to the disk. Right now I save the file with the .savePng() and .saveJpeg() functions.
I'd like to convert it to a stream which can be piped to an FS stream.
I tried the module streamifier because it sounds like it would do what I need, but when running the code below, the exported image is unreadable (though the same size as exporting via node-gd).
Here is what I attempted to do:
var gd = require("node-gd");
var fs = require("fs");
const streamifier = require('streamifier');
var inputImage = gd.createFromPng('input.png');
var writeStream = fs.createWriteStream('output.png');
var pngstream = inputImage.pngPtr();
streamifier.createReadStream(pngstream).pipe(writeStream);
Is there something I'm missing?
The png pointer needs to first be converted to a buffer like so
var pngstream = Buffer.from(inputImage.pngPtr(), 'binary');

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 pipe one readable stream into two writable streams at once in Node.js?

The goal is to:
Create a file read stream.
Pipe it to gzip (zlib.createGzip())
Then pipe the read stream of zlib output to:
1) HTTP response object
2) and writable file stream to save the gzipped output.
Now I can do down to 3.1:
var gzip = zlib.createGzip(),
sourceFileStream = fs.createReadStream(sourceFilePath),
targetFileStream = fs.createWriteStream(targetFilePath);
response.setHeader('Content-Encoding', 'gzip');
sourceFileStream.pipe(gzip).pipe(response);
... which works fine, but I need to also save the gzipped data to a file so that I don't need to regzip every time and be able to directly stream the gzipped data as a response.
So how do I pipe one readable stream into two writable streams at once in Node?
Would sourceFileStream.pipe(gzip).pipe(response).pipe(targetFileStream); work in Node 0.8.x?
Pipe chaining/splitting doesn't work like you're trying to do here, sending the first to two different subsequent steps:
sourceFileStream.pipe(gzip).pipe(response);
However, you can pipe the same readable stream into two writeable streams, eg:
var fs = require('fs');
var source = fs.createReadStream('source.txt');
var dest1 = fs.createWriteStream('dest1.txt');
var dest2 = fs.createWriteStream('dest2.txt');
source.pipe(dest1);
source.pipe(dest2);
I found that zlib returns a readable stream which can be later piped into multiple other streams. So I did the following to solve the above problem:
var sourceFileStream = fs.createReadStream(sourceFile);
// Even though we could chain like
// sourceFileStream.pipe(zlib.createGzip()).pipe(response);
// we need a stream with a gzipped data to pipe to two
// other streams.
var gzip = sourceFileStream.pipe(zlib.createGzip());
// This will pipe the gzipped data to response object
// and automatically close the response object.
gzip.pipe(response);
// Then I can pipe the gzipped data to a file.
gzip.pipe(fs.createWriteStream(targetFilePath));
you can use "readable-stream-clone" package
const fs = require("fs");
const ReadableStreamClone = require("readable-stream-clone");
const readStream = fs.createReadStream('text.txt');
const readStream1 = new ReadableStreamClone(readStream);
const readStream2 = new ReadableStreamClone(readStream);
const writeStream1 = fs.createWriteStream('sample1.txt');
const writeStream2 = fs.createWriteStream('sample2.txt');
readStream1.pipe(writeStream1)
readStream2.pipe(writeStream2)

Resources