Closed Jourdelune closed 3 years ago
No, I look at the implementation there are no options to save it as buffer.
And do you know any alternative that can do it in js? I'm looking and I can't find it
What actually do you want to achieve? I can profide some example.
You want to compress the image? Or else?
I make a small application to play music via files, I want to get the thumbnail of a mp4 file to be able to display it but since there can be a lot of music, I will not save the image in a png file, get the buffer, create a url and send it to the html then delete the image and this for each music I would have to recover the buffer directly
Okay then it is should be like this. I just scribbled here please read the comments, basically fs.readFileSync
will create the buffer and you can sent it to the user.
const tmp = require('tmp'); // install this with npm first
const ffmpeg = require('ffmpeg')
const http = require('http');
const fs = require('fs');
const path = require('path')
http.createServer(function(req, res) {
// create temp directory
const tmpobj = tmp.dirSync();
// process the screenshot
ffmpeg('/path/to/video.avi')
.screenshots({
timestamps: [30.5, '50%', '01:10.123'],
filename: 'thumbnail.png',
folder: tmpobj.name, // path to tmp folder
size: '320x240'
});
// create buffer
const buf = fs.readFileSync('./package.json');
const bufString = buf.toString('hex');
// delete your file
tmpobj.removeCallback();
return bufString
}).listen(8080);
Hi, thanks for your answer and really thank you for taking the time to develop this script! On the other hand I did not understand why read package.json "const buf = fs.readFileSync('./package.json');"
lol I just copy paste the usage, it should be fs.readfilesync(path.join(tmpobj.name, 'thumbnail.png'))
On Sat, 4 Sep 2021, 21:33 Jourdelune, @.***> wrote:
Hi, thanks for your answer and really thank you for taking the time to develop this script! On the other hand I did not understand why read package.json "const buf = fs.readFileSync('./package.json');"
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/fluent-ffmpeg/node-fluent-ffmpeg/issues/1144#issuecomment-912984203, or unsubscribe https://github.com/notifications/unsubscribe-auth/AFSC6X7BR4FFPDYXU2UMZLLUAIU4DANCNFSM5DME2K4Q .
Okay, I have do that
const tmpobj = tmp.dirSync();
ffmpeg(`${testFolder}${file}`)
.takeScreenshots({
timestamps: ['50%'],
filename: 'thumbnail.png',
folder: tmpobj.name,
size: '320x240'
});
const buf = fs.readFileSync(path.join(tmpobj.name,'thumbnail.png'))
const bufString = buf.toString('hex');
tmpobj.removeCallback();
But I have this error
"Uncaught (in promise) Error: ENOENT: no such file or directory, open '/tmp/tmp-19974-ueHIfWHyyFNj/thumbnail.png'", source: fs.js (476)
however the file with the image exists (I checked), you have an idea of the reason (maybe it looks for this file in the current directory) ?
Finally I have make that and that work, thanks for help me^^
const tmpobj = tmp.dirSync();
ffmpeg(`${testFolder}${file}`).takeScreenshots({
timestamps: ['50%'],
filename: 'thumbnail.png',
folder: tmpobj.name,
size: '320x240'
}).on('end', async function() {
const buf = await fs.readFileSync(path.join(tmpobj.name,'thumbnail.png'), {encoding: 'base64'})
const format = "image/png";
addHtml('liste-track', `
<tr class="track-choice">
<td class="track-name" style="max-width: 200px;"><img src="data:image/gif;base64,${buf}" alt="" width="40px" height="40px" class="logo-align"/><i class="fas fa-play lg play-in-survol" onclick="PlayMusic(this, '${testFolder}${file}')"></i>${metadata.common.title}</td>
<td class="artist-name">${metadata.common.artist}</td>
<td class="album">${tag.tags.album==="undefined"? tag.tags.album:"None"}</td>
<td class="timer-table">${duration}</td>
</tr>
`)
fs.rmdirSync(tmpobj.name, { recursive: true });
})
}
The original intent (to retreive a screenshot/thumbnail directly in a buffer) is actually possible:
const noop = () => {}
ffmpeg
.input('D:/videos/input.mp4')
.inputOptions(['-ss', 5])
.output({
writable: true,
write: data => {
data // = image buffer
},
on: noop, once: noop, pipe: noop, emit: noop, end: noop,
})
.outputOptions([
'-vframes 1',
'-f image2pipe'
])
.run();
.output()
can be a stream which will receive buffered data, so I've just mocked a writable stream here to get it.
If you want to take a dependency on PNG here's the code I've been using to parse PNG's and emitting them
import ffmpeg from "fluent-ffmpeg"
import { Transform } from "stream"
const { PNG } = require('pngjs');
// magic string to detect end of png from stream
const pngMagicNumber = "IEND®B`".split("").map((val) => val.charCodeAt(0));
function parseFFMpegPng(ffmpeg: ffmpeg.FfmpegCommand) {
let incompleteFrame = [] as number[];
let frames = 1;
return ffmpeg.output(
new Transform({
write(data: Buffer, _encoding, cb) {
let buffers = [] as Buffer[];
let found = [] as number[];
({ incompleteFrame, buffers } = data.reduce((last, current) => {
last.incompleteFrame.push(current);
if (found.length >= 8) {
found.shift();
}
found.push(current);
if (pngMagicNumber.every((val, index) => val === found[index])) {
last.buffers.push(Buffer.from(last.incompleteFrame));
found = [];
last.incompleteFrame = [];
}
return last;
}, { incompleteFrame, buffers: [] as Buffer[] }));
if (buffers.length) {
this.emit('png', buffers)
}
cb();
},
}).on('png', function (buffers: Buffer[]) {
buffers.forEach((buffer) => {
const dst = new PNG;
const frame = frames++;
dst.on('parsed', function (this: { width: number, height: number, data: Buffer }) {
const { width, height, data } = this;
ffmpeg.emit('parsed', { width, height, data, frame });
}).parse(buffer);
});
}))
.videoCodec('png')
.addOutputOption('-vsync', '0')
.format("image2pipe");
}
Then to use, just pass the function an ffmpeg command
const reader = parseFFMpegPng(ffmpeg(url, {
stdoutLines: 0,
preset: 'ultrafast',
logger: {
info: console.log,
warn: console.warn,
error: console.error,
debug: console.info,
}
})))
To get the PNG data.
reader.on('parsed', (png: { width: number, height: number, data: Buffer, frame: number }) => {
// Use PNGjs data here
})
Maybe you need this
const readStream = fs.createReadStream('demo.mp4')
const frames: Buffer[] = []
let currentBuffer: Buffer[] = []
const PNG_HEADER = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])
ffmpeg().input(readStream)
.outputOptions(['-movflags frag_keyframe+empty_moov', '-f image2pipe', '-vcodec png'])
.output(
new Transform({
write(data: Buffer, _encoding, cb) {
const currentIndex = data.indexOf(PNG_HEADER)
const isPngHeaderStart = currentIndex !== -1
if(isPngHeaderStart) {
const first = data.subarray(0, currentIndex)
const last = data.subarray(currentIndex)
if (currentBuffer.length !== 0) {
frames.push(Buffer.concat([...currentBuffer, first]))
}
currentBuffer = [last]
} else {
currentBuffer.push(data)
}
cb()
},
}), { end: true }
)
.on('end', () => {
frames.push(Buffer.concat(currentBuffer))
currentBuffer = []
// do something, frames is you need Buffers
console.log(frames.length, 'frames')
// clear Buffer
frames.length = 0;
})
.run()
Hello, I would like to take a picture of my video but without saving it in an image and instead retrieve the image data stored in a variable. Is this possible?