Node-fluent-ffmpeg: Transcoding to Wav file causes problems with Duration

Created on 29 Nov 2018  路  6Comments  路  Source: fluent-ffmpeg/node-fluent-ffmpeg

Version information

  • fluent-ffmpeg version: 2.1.2
  • ffmpeg version: 4.1
  • OS: Mac OS Mojave

Code to reproduce

[Node.js] Codeconvert to wav

const ffmpeg = require('fluent-ffmpeg');
const ffmpeg_static = require('ffmpeg-static');

const command = ffmpeg()
  .setFfmpegPath(ffmpeg_static.path)
  .input(inputStream)
  .outputOptions('-f wav')
  .outputOptions('-ac 1')
  .outputOptions('-ar 11025')

[Command line] convert to wav

$ ffmpeg -i input -ac 1 -ar 11025 output.wav 

Expected results

I am sending this to external API.
I thought this would work.

It worked on the Wav file generated on the command line.

Observed results

But it did not work.
Although it is possible to play sound without problems. . .

The result of executing the wav file generated by fluent-ffmpeg with the ffmpeg command.

ffmpeg version 4.1 Copyright (c) 2000-2018 the FFmpeg developers
  built with Apple LLVM version 10.0.0 (clang-1000.11.45.5)
  configuration: --prefix=/usr/local/Cellar/ffmpeg/4.1 --enable-shared --enable-pthreads --enable-version3 --enable-hardcoded-tables --enable-avresample --cc=clang --host-cflags= --host-ldflags= --enable-ffplay --enable-gpl --enable-libmp3lame --enable-libopus --enable-libsnappy --enable-libtheora --enable-libvorbis --enable-libvpx --enable-libx264 --enable-libx265 --enable-libxvid --enable-lzma --enable-opencl --enable-videotoolbox
  libavutil      56. 22.100 / 56. 22.100
  libavcodec     58. 35.100 / 58. 35.100
  libavformat    58. 20.100 / 58. 20.100
  libavdevice    58.  5.100 / 58.  5.100
  libavfilter     7. 40.101 /  7. 40.101
  libavresample   4.  0.  0 /  4.  0.  0
  libswscale      5.  3.100 /  5.  3.100
  libswresample   3.  3.100 /  3.  3.100
  libpostproc    55.  3.100 / 55.  3.100
[wav @ 0x7fec37004a00] Ignoring maximum wav data size, file may be invalid
[wav @ 0x7fec37004a00] Estimating duration from bitrate, this may be inaccurate
Guessed Channel Layout for Input Stream #0.0 : mono
Input #0, wav, from 'output.wav':
  Metadata:
    encoder         : Lavf58.20.100
  Duration: 00:00:02.04, bitrate: 176 kb/s
    Stream #0:0: Audio: pcm_s16le ([1][0][0][0] / 0x0001), 11025 Hz, mono, s16, 176 kb/s
At least one output file must be specified

I think this output is a problem.
This is not output in the wav file generated by the ffmpeg command.

[wav @ 0x7fec37004a00] Ignoring maximum wav data size, file may be invalid
[wav @ 0x7fec37004a00] Estimating duration from bitrate, this may be inaccurate

The wav file generated by fluent-ffmpeg is displayed on Mac's Finder and Duration becomes 0.
I think this also represents a problem.

As with command line execution, what is necessary to run with fluent-ffmpeg?

Most helpful comment

@345ml This works for me.

stream = Buffer.alloc(0)
let mediaURL = '../../../Downloads/bbb_sunflower_1080p_60fps_normal.mp4'
ffmpeg(mediaURL)
  .outputOptions('-f wav')
  .outputOptions('-ac 1')
  .outputOptions('-ar 11025')
  .pipe(stream, { end: true })
  .on('data', function(chunk) {
      console.log('I got a Chunk')
      stream = Buffer.concat([stream, chunk])
  })
  .on('end', () => {
      console.log(stream.length)
  });

@gvsro your first line says that stream is Buffer

stream = Buffer.alloc(0)

Why stream can be an argument in ffmpeg.pipe(stream, { end: true}) ?

.pipe(stream, { end: true })

Shouldn't it be a writable stream or empty argument for pipe?

And I try it but still not works for me... (still got 0 duration)

I try another example (see below), but still got the same problem

const data = [];
ffmpeg(amr_audio_url)
  .format('wav')
  .audioFrequency(16000)
  .audioChannels(1)
  .pipe()
  .on('data', (chunk) => {
    data.push(chunk);
  })
  .on('end', () => {
    const wav = Buffer.concat(data)
    // if I fs.writeFileSync this as output.wav, it can be played correctly
    // but duration is missing (00:00, mismatch with original one)
  });

Only when I call ffmpeg.save(), the duration of wav file is correct and same as original one.

can anyone give minimal working example?

All 6 comments

I found a pattern that works
What is the difference between these?

A pattern that work.

const command = ffmpeg()
  .setFfmpegPath(ffmpeg_static.path)
  .input(inputStream)
  .outputOptions('-f wav')
  .outputOptions('-ac 1')
  .outputOptions('-ar 11025')
  .save('output.wav')

A pattern that does not work.

const writeStream = fs.createWriteStream('output.wav');

const command = ffmpeg()
  .setFfmpegPath(ffmpeg_static.path)
  .input(inputStream)
  .outputOptions('-f wav')
  .outputOptions('-ac 1')
  .outputOptions('-ar 11025')
  .output(writeStream)
  .run()

What I really want to do

I want to handle output buffer data as below.

let buffer = [];

const command = ffmpeg()
  .setFfmpegPath(ffmpeg_static.path)
  .input(inputStream)
  .outputOptions('-f wav')
  .outputOptions('-ac 1')
  .outputOptions('-ar 11025')

const ffstream = command.pipe({ end: true });
  ffstream.on('data', function(chunk) {
    buffer.push(chunk);
  });
  ffstream.on('end', function() {
    Buffer.concat(buffer);
  })

@345ml This works for me.

stream = Buffer.alloc(0)
let mediaURL = '../../../Downloads/bbb_sunflower_1080p_60fps_normal.mp4'
ffmpeg(mediaURL)
  .outputOptions('-f wav')
  .outputOptions('-ac 1')
  .outputOptions('-ar 11025')
  .pipe(stream, { end: true })
  .on('data', function(chunk) {
      console.log('I got a Chunk')
      stream = Buffer.concat([stream, chunk])
  })
  .on('end', () => {
      console.log(stream.length)
  });

@345ml This works for me.

stream = Buffer.alloc(0)
let mediaURL = '../../../Downloads/bbb_sunflower_1080p_60fps_normal.mp4'
ffmpeg(mediaURL)
  .outputOptions('-f wav')
  .outputOptions('-ac 1')
  .outputOptions('-ar 11025')
  .pipe(stream, { end: true })
  .on('data', function(chunk) {
      console.log('I got a Chunk')
      stream = Buffer.concat([stream, chunk])
  })
  .on('end', () => {
      console.log(stream.length)
  });

@gvsro your first line says that stream is Buffer

stream = Buffer.alloc(0)

Why stream can be an argument in ffmpeg.pipe(stream, { end: true}) ?

.pipe(stream, { end: true })

Shouldn't it be a writable stream or empty argument for pipe?

And I try it but still not works for me... (still got 0 duration)

I try another example (see below), but still got the same problem

const data = [];
ffmpeg(amr_audio_url)
  .format('wav')
  .audioFrequency(16000)
  .audioChannels(1)
  .pipe()
  .on('data', (chunk) => {
    data.push(chunk);
  })
  .on('end', () => {
    const wav = Buffer.concat(data)
    // if I fs.writeFileSync this as output.wav, it can be played correctly
    // but duration is missing (00:00, mismatch with original one)
  });

Only when I call ffmpeg.save(), the duration of wav file is correct and same as original one.

can anyone give minimal working example?

I tried to compare the 2 files with https://www.npmjs.com/package/wavefile.
It looks the difference is the chunksize.
I didn't find a fix yet.
It does sound reasonable that ffmpeg cannot set the chunk size when streaming.

I found an app called "RIFFVIEW",
A good file showing: <data> (waveform data - 302400 bytes)
ffmpeg streamed file showing: <data> (waveform data - -1 bytes)
Check the note at the end of this section.
ffmpeg cannot write the length while streaming. It should be done only after the file is completed.
It's interesting to try to fix the file by using wavefile module

Hey guys, I had the same issue and created a solution for that. Here's the package I developed and am actually using. https://www.npmjs.com/package/@wpdas/wave-header

Was this page helpful?
0 / 5 - 0 ratings

Related issues

LauraWebdev picture LauraWebdev  路  3Comments

danielbanfield picture danielbanfield  路  5Comments

judemorrissey picture judemorrissey  路  5Comments

baoxiangyang picture baoxiangyang  路  3Comments

xyk2 picture xyk2  路  3Comments