Having some issues running ffmpeg via Process

I'm having some issues with a script I'm trying to write to automate some of my ffmpeg video commands. I need to run ffmpeg via Process and in doing so, I've noticed that once it starts running it never does anything (as it never even creates the output file) and never stops running. I'm not entirely sure how to fix this, to get the full command to run and then exit once finished. Any insight on this would be much appreciated.

ffmpeg is installed on my system through Homebrew:

brew tap slhck/ffmpeg
brew install slhck/ffmpeg/ffmpeg --with-aom --with-fdk-aac

Here is the video I'm testing the script with: https://www.dropbox.com/s/vyo5oxm82hy7xqd/Big%20Buck%20Bunny.mkv?dl=0

Here is a simplified version of my script that still encounters the issue:

To test, replace the placeholders in the script with the path to the video and the path to it's output (usually, I just specify the same path as the input, but with an .mp4 extension instead) and run in Terminal using swift ~/path/to/FfmpegCommand.swift.

Possibly thread deadlock?

As a quick experiment, try running the shell command through this packaged method instead, since it does nothing multithreaded.

(Also, have you verified how the same ffmpeg command behaves when you call it manually in a terminal?)

Have you tried using the -nostdin option of ffmpeg?

ffmpeg enables interaction with stdin by default. On Mac OS X and Linux systems, this causes an ffmpeg job running in the background to suspend. Adding option -nostdin to the invocation causes ffmpeg to not enable stdin interaction, and so avoids suspending the background process.

Yes! This made it work with the original implementation! Thank you so much! I looked all over for flag options that might impact it, but ffmpeg has so many.

I tried using that package and it couldn't figure out how to get it up and running.

Given

let ffmpegShell = Shell(at: URL(fileURLWithPath: "/usr/local/bin/ffmpeg"))
let bashShell = Shell(at: URL(fileURLWithPath: "/bin/bash"))

I tried the following and got errors for each I couldn't figure out how to resolve after an hour of working with them:

// Error: Trailing options were found on the commandline
try ffmpegShell.run(command: [
    "-nostdin",
    "-i",
    "'\(inputFilePath)'",
    "-c:v copy",
    "-c:a copy",
    "'\(outputFilePath)'"
])

// Error: /bin/bash: ffmpeg: command not found
try bashShell.run(command: [
    "ffmpeg",
    "-nostdin",
    "-i",
    "'\(inputFilePath)'",
    "-c:v copy",
    "-c:a copy",
    "'\(outputFilePath)'"
])

// Error: /bin/bash: - : invalid option
try bashShell.run(command: [
    "-c",
    "ffmpeg -nostdin -i '\(inputFilePath)' -c:v copy -c:a copy '\(outputFilePath)'"
])

And, yes, I have verified that the command works manually in terminal.

Assuming the command you would use in terminal is...

$ ffmpeg -nostdin -i '...' -c:v copy -c:a copy '...'

...this was what I meant:

let output = try Shell.default.run(
    command: [
        "ffmpeg",
        "-nostdin",
        "-i", "'\(inputFilePath)'",
        "-c:v", "copy",
        "-c:a", "copy",
        "'\(outputFilePath)'"
    ],
    autoquote: false,
    reportProgress: { print($0) })

However, based on the “command not found” error you got, it does not look like ffmpeg is registered in $PATH in whatever environment you are in. The variant that bypasses the shell and interacts directly with the executable at a given path is this:

let ffmpeg = ExternalProcess(at: URL(fileURLWithPath: "/usr/local/bin/ffmpeg"))
let processOutput = try ffmpeg.run([
    "-nostdin",
    "-i", "\(inputFilePath)",
    "-c:v", "copy",
    "-c:a", "copy",
    "\(outputFilePath)"
    ], reportProgress: { print($0) })

Note that since the arguments are passed directly, there is no intervening shell to interpret the quotation marks, so leave them out. (Also note that there is no shell to split the arguments at spaces, so ffmpeg will receive the arguments in exactly the form specified by the array.)

Never mind. For some reason the form never showed the intervening posts until after I responded. Sorry for the noise. Just ignore what I said. -nostdin was the real culprit; no sense experimenting with the threading.

I'm considering replacing my hodgepodge shell solution with your library, so this was all still helpful, thanks! I didn't realize until today that I don't know much about shells and processes outside of Bash :sweat_smile:

1 Like

Glad I could help! I had the same issue some weeks ago.

BTW, ffmpeg is not a good candidate for swift bash functions that take an array of arguments. For some reason I failed to write the following command in the form bash.run(command: [...]):

ffmpeg -i in.mp4 -vf "scale=(iw*sar)*max(720/(iw*sar)\,480/ih):ih*max(720/(iw*sar)\,480/ih), crop=720:480" -an -ss 800 -t 4 out.mp4

I suppose that the special characters are not allowed here.

IMHO there should be a specialised implementation for ffmpeg using the new string interpolation syntax of Swift 5 to escape this stringly-typed hell!"

1 Like
Terms of Service

Privacy Policy

Cookie Policy