fluent-ffmpeg/node-fluent-ffmpeg

Error: Output format mp4 is not available

alpacaPwaa opened this issue · 14 comments

Version information

  • fluent-ffmpeg version: 7.0-full_build-www.gyan.dev
  • ffmpeg version: 2.1.2
  • OS: Windows 11

Code to reproduce

createVideo: privateProcedure
    .input(
      z.object({
        fileId: z.string(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const { getUser } = getKindeServerSession();
      const user = await getUser();

      if (!user || !user.id || !user.email) {
        throw new TRPCError({ code: "UNAUTHORIZED" });
      }

      const dbUser = await db.user.findFirst({
        where: {
          id: user.id,
        },
      });

      if (!dbUser) {
        throw new TRPCError({
          code: "UNAUTHORIZED",
          message: "User not found in the database.",
        });
      }

      const putObjectCommand = new PutObjectCommand({
        Bucket: process.env.AWS_BUCKET_NAME!,
        Key: generateFileName(),
      });

      const s3 = new S3Client({
        region: process.env.AWS_BUCKET_REGION!,
        credentials: {
          accessKeyId: process.env.AWS_ACCESS_KEY!,
          secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
        },
      });

      const signedURL = await getSignedUrl(s3, putObjectCommand, {
        expiresIn: 60,
      });

      const ffmpeg = require("fluent-ffmpeg");
      const passthroughStream = new PassThrough();

      ffmpeg({ source: "./template1.mp4" })
        .on("end", async () => {
          console.log("Job done");
          await uploadToS3(passthroughStream);
        })
        .on("error", (error: string) => {
          console.error("Error:", error);
        })
        .videoFilter({
          filter: "drawtext",
          options: {
            text: "hi",
            fontsize: 100,
            fontcolor: "white",
            x: "(w-text_w)/2",
            y: "(h-text_h)/2",
            box: 1,
            boxcolor: "black@0.5",
            boxborderw: 5,
            fontfile: "/Windows/fonts/calibri.ttf",
          },
        })
        .noAudio()
        .addOutputOption("-f", "mp4")
        .videoCodec("libx264")
        .outputFormat("mp4")
        .outputOptions(["-movflags frag_keyframe+empty_moov"])
        .pipe(passthroughStream, { end: true });

      const uploadToS3 = async (stream: PassThrough) => {
        const upload = new Upload({
          client: s3,
          params: {
            Bucket: process.env.AWS_BUCKET_NAME!,
            Key: generateFileName(),
            Body: stream,
          },
        });
        await upload.done();
      };

      const createdVideo = await db.video.create({
        data: {
          name: "Test Name",
          url: signedURL.split("?")[0],
          key: signedURL,
          fileId: input.fileId,
        },
      });

      return createdVideo;
    }),

(note: if the problem only happens with some inputs, include a link to such an input file)

Expected results

I should be able to save the video to s3

Observed results

Error: Output format mp4 is not available
at eval (webpack-internal:///(rsc)/./node_modules/fluent-ffmpeg/lib/capabilities.js:589:21)
at nextTask (webpack-internal:///(rsc)/./node_modules/async/dist/async.mjs:5892:9)
at next (webpack-internal:///(rsc)/./node_modules/async/dist/async.mjs:5900:9)
at eval (webpack-internal:///(rsc)/./node_modules/async/dist/async.mjs:430:16)
at eval (webpack-internal:///(rsc)/./node_modules/fluent-ffmpeg/lib/capabilities.js:549:7)
at handleExit (webpack-internal:///(rsc)/./node_modules/fluent-ffmpeg/lib/processor.js:170:11)
at ChildProcess.eval (webpack-internal:///(rsc)/./node_modules/fluent-ffmpeg/lib/processor.js:184:11)
at ChildProcess.emit (node:events:518:28)
at ChildProcess._handle.onexit (node:internal/child_process:294:12)
at Process.callbackTrampoline (node:internal/async_hooks:130:17)

same issue with me

I have a similar issue (although all the formats seem broken for me)
Recently upgraded my ffmpeg version. Looks like newer versions of ffmpeg -formats have added a new info column for devices, which adds a space between the mux/demux columns and the format name:

New (build from March 2024):

Formats:
 D.. = Demuxing supported
 .E. = Muxing supported
 ..d = Is a device
 ---
  E  mp4             MP4 (MPEG-4 Part 14)

Old (build from June 2023):

File formats:
 D. = Demuxing supported
 .E = Muxing supported
 --
  E mp4             MP4 (MPEG-4 Part 14)

(Obviously, mp4 is just an example, I'm not going to paste the whole format list here.)

My guess at what is happening:
The regex that Fluent-FFmpeg uses to read FFmpeg's format list and determine its format support doesn't know how to read the extra space, so Fluent thinks there aren't any valid formats.
Regex is here:

var formatRegexp = /^\s*([D ])([E ]) ([^ ]+) +(.*)$/;

Then, when you try to run a command, the error checking at line 589 compares its (empty) list of valid formats to your format, determines it can't support the format, and throws the error.

@benharp I try running ffmpeg -format and I also had the same column for devices. Does this mean I have to downgrade my ffmpeg version? What version works for you?

Formats:
 D.. = Demuxing supported
 .E. = Muxing supported
 ..d = Is a device
 ---

@benharp followed your comment and downgraded my ffmpeg to ffmpeg version 6.1-full now it can finish the stream. Thanks your hint helps a lot!

@alpacaPwaa should this issue be closed? I mean, you can fix it by downgrading the version, but now Homebrew installs version 7 by default so maybe this should be taken into account now.

We should fix the regexpr and make a new fluent-ffmpeg version? Is that possible?
Any maintainer?

It seems like I have to re-open the issue, everyone here's right although there's a workaround for this issue it's still better to address the root cause.

Use patch-package until it gets fixed. This worked in my case:

diff --git a/node_modules/fluent-ffmpeg/lib/capabilities.js b/node_modules/fluent-ffmpeg/lib/capabilities.js
index 3722ff1..3434210 100644
--- a/node_modules/fluent-ffmpeg/lib/capabilities.js
+++ b/node_modules/fluent-ffmpeg/lib/capabilities.js
@@ -15,7 +15,7 @@ var ffCodecRegexp = /^\s*([D\.])([E\.])([VAS])([I\.])([L\.])([S\.]) ([^ ]+) +(.*
 var ffEncodersRegexp = /\(encoders:([^\)]+)\)/;
 var ffDecodersRegexp = /\(decoders:([^\)]+)\)/;
 var encodersRegexp = /^\s*([VAS\.])([F\.])([S\.])([X\.])([B\.])([D\.]) ([^ ]+) +(.*)$/;
-var formatRegexp = /^\s*([D ])([E ]) ([^ ]+) +(.*)$/;
+var formatRegexp = /^\s*([D ])([E ])([d ]) ([^ ]+) +(.*)$/;
 var lineBreakRegexp = /\r\n|\r|\n/;
 var filterRegexp = /^(?: [T\.][S\.][C\.] )?([^ ]+) +(AA?|VV?|\|)->(AA?|VV?|\|) +(.*)$/;
 
@@ -527,7 +527,7 @@ module.exports = function(proto) {
       lines.forEach(function(line) {
         var match = line.match(formatRegexp);
         if (match) {
-          match[3].split(',').forEach(function(format) {
+          match[4].split(',').forEach(function(format) {
             if (!(format in data)) {
               data[format] = {
                 description: match[4],

Fixed here #1266

You can also install package from: https://www.npmjs.com/package/fluent-ffmpeg-7 and just change the import to from 'fluent-ffmpeg-7'

I did a quick version there till official version gets fixed.

Is there a way to skip format checks? I want my code to work on both version of ffmpeg

You can use my code, it works on both versions!

You can directly install from the npm with npm i fluent-ffmpeg-7. I called like that just to reinforce that it also works on FFmpeg 7

@njoyard Old issue

This has been fixed in #1266