fluent-ffmpeg / node-fluent-ffmpeg

A fluent API to FFMPEG (http://www.ffmpeg.org)

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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
CleanShot 2023-08-24 at 14 14 24

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

How to get fps | duration when use stream | buffer input?