streamBuffers output missing duration
hoangqwe159 opened this issue · comments
Version information
- fluent-ffmpeg version: 2.1.2
- "@ffmpeg-installer/ffmpeg": "^1.1.0",
"ffmpeg-static": "^5.1.0",
"ffprobe-static": "^3.1.0"
ffmpeg version: 5.0.1 - OS: MacOS Sonoma 14.0
Code to reproduce
import { Readable } from 'stream';
import Ffmpeg, { FfmpegCommand } from 'fluent-ffmpeg';
import * as ffmpegPath from 'ffmpeg-static';
import * as ffprobe from 'ffprobe-static';
import * as fs from 'fs';
import * as path from 'path';
function createImageStream(folderPath: string): Readable {
const imageStream = new Readable();
const files = fs.readdirSync(folderPath);
const imageFiles = files.filter(file => {
const extname = path.extname(file).toLowerCase();
return extname === '.jpg' || extname === '.jpeg' || extname === '.png';
});
for (const file of imageFiles) {
const filePath = path.join(folderPath, file);
const imageBuffer = fs.readFileSync(filePath);
imageStream.push(imageBuffer);
}
imageStream.push(null);
return imageStream;
}
function encode({ stream }: { stream: Readable}): Promise<{ filename: string }> {
return new Promise((resolve, reject) => {
try {
const filePath = '/Users/hoangqwe159/viet-desygner/temp/output.mp4'; // Replace with your desired file path
const outputStream = fs.createWriteStream(filePath, {});
const command: FfmpegCommand = Ffmpeg()
.setFfmpegPath(ffmpegPath.default)
.setFfprobePath(ffprobe.path)
.input(stream)
.inputFPS(30)
.outputOptions(
[
// '-t 27',
'-preset medium',
'-pix_fmt yuv420p',
`-vf scale=-2:1080`,
`-f mp4`,
'-vcodec libx264',
'-movflags frag_keyframe+empty_moov',
'-movflags +faststart',
]
)
.output(outputStream);
command.on('start', function (commandLine) {
console.log('Spawned Ffmpeg with command: ' + commandLine);
});
command.on('end', () => {
resolve({ filename: filePath });
});
command.on('error', (err: Error) => {
reject(new Error(err.message));
});
command.run();
} catch (err) {
this.logger.error('Error encoding animation', err);
reject('Error encoding animation sequence');
}
});
}
const stream: Readable = createImageStream('/Users/hoangqwe159/viet-desygner/temp/frames'); // 811 frames as PNG
encode({ stream });
Expected results
Related question: https://superuser.com/questions/980272/what-movflags-frag-keyframeempty-moov-flag-means
When encoding video with FFMPEG I am using -movflags frag_keyframe+empty_moov - I found it somewhere on the net - and it allows me to get mp4 video stream (without it I get Could not write header for output file #0 (incorrect codec parameters ?) error if I want to stream output somewhere, but it works if I pass output file name out.mp4 for example). I am asking because this flag also messes with my video somehow and I can't upload it to Twitter for example when using this flag (without it Twitter accepts my video). So it messes up with encoding/format or something else... is there any workaround?
Output video has correct 27 seconds duration when open it in Android device or upload to check in https://www.metadata2go.com/view-metadata
Observed results
FFMPEG command
ffmpeg -r 30 -i pipe:0 -t 27 -preset medium -pix_fmt yuv420p -vf scale=-2:1080 -f mp4 -vcodec libx264 -movflags frag_keyframe+empty_moov -movflags +faststart pipe:1
Duration is displayed as 0 second
Spawned Ffmpeg with command: ffmpeg -r 30 -i pipe:0 -t 27 -preset medium -pix_fmt yuv420p -vf scale=-2:1080 -f mp4 -vcodec libx264 -movflags frag_keyframe+empty_moov -movflags +faststart pipe:1
Stderr output: ffmpeg version 5.0.1 Copyright (c) 2000-2022 the FFmpeg developers
Stderr output: built with Apple clang version 13.0.0 (clang-1300.0.29.30)
Stderr output: configuration: --prefix=/Volumes/tempdisk/sw --extra-cflags=-fno-stack-check --arch=arm64 --cc=/usr/bin/clang --enable-gpl --enable-libopenjpeg --enable-libopus --enable-libmp3lame --enable-libx264 --enable-libx265 --enable-libvpx --enable-libwebp --enable-libass --enable-libfreetype --enable-libtheora --enable-libvorbis --enable-libsnappy --enable-libaom --enable-libvidstab --enable-libzimg --enable-libsvtav1 --enable-version3 --pkg-config-flags=--static --disable-ffplay --enable-postproc --enable-nonfree --enable-neon --enable-runtime-cpudetect --disable-indev=qtkit --disable-indev=x11grab_xcb
Stderr output: libavutil 57. 17.100 / 57. 17.100
Stderr output: libavcodec 59. 18.100 / 59. 18.100
Stderr output: libavformat 59. 16.100 / 59. 16.100
Stderr output: libavdevice 59. 4.100 / 59. 4.100
Stderr output: libavfilter 8. 24.100 / 8. 24.100
Stderr output: libswscale 6. 4.100 / 6. 4.100
Stderr output: libswresample 4. 3.100 / 4. 3.100
Stderr output: libpostproc 56. 3.100 / 56. 3.100
Stderr output: Input #0, png_pipe, from 'pipe:0':
Stderr output: Duration: N/A, bitrate: N/A
Stderr output: Stream #0:0: Video: png, rgba(pc), 764x1080, 25 fps, 25 tbr, 25 tbn
Stderr output: Stream mapping:
Stderr output: Stream #0:0 -> #0:0 (png (native) -> h264 (libx264))
Stderr output: [libx264 @ 0x14b6113b0] using cpu capabilities: ARMv8 NEON
Stderr output: [libx264 @ 0x14b6113b0] profile High, level 3.1, 4:2:0, 8-bit
Stderr output: [libx264 @ 0x14b6113b0] 264 - core 164 r3075 66a5bc1 - H.264/MPEG-4 AVC codec - Copyleft 2003-2021 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=15 lookahead_threads=2 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=25 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=crf mbtree=1 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00
Stderr output: Output #0, mp4, to 'pipe:1':
Stderr output: Metadata:
Stderr output: encoder : Lavf59.16.100
Stderr output: Stream #0:0: Video: h264 (avc1 / 0x31637661), yuv420p(tv, progressive), 764x1080, q=2-31, 30 fps, 15360 tbn
Stderr output: Metadata:
Stderr output: encoder : Lavc59.18.100 libx264
Stderr output: Side data:
Stderr output: cpb: bitrate max/min/avg: 0/0/0 buffer size: 0 vbv_delay: N/A
Stderr output: frame= 1 fps=0.0 q=0.0 size= 1kB time=00:00:00.00 bitrate=N/A speed= 0x
Stderr output: frame= 156 fps=0.0 q=29.0 size= 30kB time=00:00:03.13 bitrate= 79.5kbits/s speed=6.18x
Stderr output: frame= 203 fps=201 q=29.0 size= 35kB time=00:00:04.70 bitrate= 61.2kbits/s speed=4.65x
Stderr output: frame= 246 fps=163 q=29.0 size= 80kB time=00:00:06.13 bitrate= 106.4kbits/s speed=4.06x
Stderr output: frame= 289 fps=143 q=29.0 size= 80kB time=00:00:07.56 bitrate= 86.2kbits/s speed=3.75x
Stderr output: frame= 334 fps=133 q=29.0 size= 712kB time=00:00:09.06 bitrate= 643.4kbits/s speed= 3.6x
Stderr output: frame= 380 fps=125 q=29.0 size= 712kB time=00:00:10.60 bitrate= 550.3kbits/s speed= 3.5x
Stderr output: frame= 433 fps=123 q=29.0 size= 712kB time=00:00:12.36 bitrate= 471.7kbits/s speed= 3.5x
Stderr output: frame= 491 fps=122 q=29.0 size= 1417kB time=00:00:14.30 bitrate= 811.5kbits/s speed=3.54x
Stderr output: frame= 548 fps=121 q=29.0 size= 1417kB time=00:00:16.20 bitrate= 716.3kbits/s speed=3.57x
Stderr output: frame= 602 fps=119 q=29.0 size= 1417kB time=00:00:18.00 bitrate= 644.7kbits/s speed=3.57x
Stderr output: frame= 653 fps=118 q=29.0 size= 1417kB time=00:00:19.70 bitrate= 589.1kbits/s speed=3.55x
Stderr output: frame= 703 fps=116 q=29.0 size= 2319kB time=00:00:21.36 bitrate= 889.2kbits/s speed=3.53x
Stderr output: frame= 753 fps=115 q=29.0 size= 2319kB time=00:00:23.03 bitrate= 824.8kbits/s speed=3.51x
Stderr output: frame= 803 fps=114 q=29.0 size= 3002kB time=00:00:24.70 bitrate= 995.8kbits/s speed= 3.5x
Stderr output: frame= 810 fps=110 q=-1.0 Lsize= 3672kB time=00:00:26.90 bitrate=1118.4kbits/s speed=3.67x
Stderr output: video:3664kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.227935%
Stderr output: [libx264 @ 0x14b6113b0] frame I:10 Avg QP:11.44 size: 41065
Stderr output: [libx264 @ 0x14b6113b0] frame P:281 Avg QP:21.92 size: 7154
Stderr output: [libx264 @ 0x14b6113b0] frame B:519 Avg QP:25.96 size: 2563
Stderr output: [libx264 @ 0x14b6113b0] consecutive B-frames: 10.5% 9.1% 9.3% 71.1%
Stderr output: [libx264 @ 0x14b6113b0] mb I I16..4: 54.1% 32.4% 13.5%
Stderr output: [libx264 @ 0x14b6113b0] mb P I16..4: 4.6% 5.3% 1.1% P16..4: 23.2% 6.4% 4.5% 0.0% 0.0% skip:54.9%
Stderr output: [libx264 @ 0x14b6113b0] mb B I16..4: 1.7% 1.8% 0.5% B16..8: 25.8% 2.2% 0.4% direct: 0.5% skip:67.1% L0:49.2% L1:47.5% BI: 3.3%
Stderr output: [libx264 @ 0x14b6113b0] 8x8 transform intra:44.1% inter:73.5%
Stderr output: [libx264 @ 0x14b6113b0] coded y,uvDC,uvAC intra: 28.6% 25.4% 9.3% inter: 4.9% 3.1% 0.3%
Stderr output: [libx264 @ 0x14b6113b0] i16 v,h,dc,p: 67% 26% 3% 5%
Stderr output: [libx264 @ 0x14b6113b0] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 23% 14% 36% 5% 4% 4% 4% 5% 5%
Stderr output: [libx264 @ 0x14b6113b0] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 31% 20% 19% 6% 5% 5% 5% 5% 4%
Stderr output: [libx264 @ 0x14b6113b0] i8c dc,h,v,p: 73% 11% 14% 2%
Stderr output: [libx264 @ 0x14b6113b0] Weighted P-Frames: Y:6.4% UV:3.9%
Stderr output: [libx264 @ 0x14b6113b0] ref P L0: 57.8% 8.2% 22.3% 10.8% 1.0%
Stderr output: [libx264 @ 0x14b6113b0] ref B L0: 78.6% 17.2% 4.3%
Stderr output: [libx264 @ 0x14b6113b0] ref B L1: 93.3% 6.7%
Stderr output: [libx264 @ 0x14b6113b0] kb/s:1111.49
Stderr output:
[Nest] 20049 - 08/24/2023, 2:32:13 PM LOG Processing complete... /Users/hoangqwe159/viet-desygner/temp/kEBhHlSFjm8/16.pdf.1080p.mp4
I am also facing this issue with input as a buffer.
// The function return N/A on duration
async convertOggBufferToWavFile(inputBuffer, outputPath, durationLimit = 30) {
const wavFileDestination = outputPath ?? path.join(__dirname, `${uuidv4}.wav`);
const inputStream = new Readable({
// encoding: 'base64',
read() {
this.push(inputBuffer);
this.push(null);
}
});
return new Promise((resolve, reject) => {
let audioData = { 'path': wavFileDestination };
ffmpeg(inputStream)
.outputOptions('-acodec', 'pcm_s16le')
.duration(durationLimit)
.on('codecData', function (data) {
if (data.format && data.audio) { // Check that format and audio codec are present
audioData = { ...audioData, data };
// Convert duration from HH:mm:ss.sss to seconds
if (data.duration) {
const durationRegex = /^(\d+):(\d{2}):(\d{2}\.\d{3})$/;
const matches = durationRegex.exec(data.duration);
if (matches) {
const [_, hours, minutes, secondsAndMillis] = matches;
const totalSeconds = (Number(hours) * 3600) + (Number(minutes) * 60) + Number(secondsAndMillis);
audioData.duration_s = totalSeconds;
} else {
console.warn(`Warning: invalid duration format "${data.duration}"`);
}
}
}
})
.on('error', (error) => reject(error))
.on('end', () => resolve(audioData))
.saveToFile(wavFileDestination);
});
}
// Calling the function from a test case
const inputBuffer = fs.readFileSync(testOggFilePath);
const outputPath = `./tests/${uuidv4()}.wav`;
const result = await convertOggBufferToWavFile(inputBuffer, outputPath);
The .on('codecData', function (data) {
gives data with duration = N/A. I am using the buffer in the test cases because the application processes an audio that comes in as a buffer.
Same issue+1