Execute commands, collect results, and observe progress.
The top-level FFix.run/2, FFix.run!/2, and FFix.stream/2 functions use
this runner. Pass a command built with FFix.command/2 and choose how to
handle its output.
Run and handle errors
case FFix.run(command) do
{:ok, result} -> IO.puts("Finished in #{result.duration_ms} ms")
{:error, error} -> IO.puts(:stderr, error.message)
endUse run!/2 in scripts when a failed command should raise. For diagnostics,
FFix.Runner.Error carries the execution result and captured stderr.
FFix.Runner.Result describes the available timings, progress, and output fields.
Follow progress
FFix.stream(command, progress: true)
|> Enum.each(fn
{:progress, progress} -> IO.inspect({progress.out_time, progress.speed})
{:stderr, chunk} -> IO.binwrite(:stderr, chunk)
{:exit, result} -> IO.inspect(result.exit_status)
_event -> :ok
end)Execution starts when the stream is enumerated. Use stream!/2 to raise on a
failed process exit; plain stream/2 reports the exit in its result event.
Halting enumeration cancels the process. Enumerating again starts a new run.
Pipe an image
Supply bytes with stdin: and collect an output written to :stdout:
alias FFix.{Encoder, Filter, Muxer}
command =
FFix.input(:stdin, f: "image2pipe")
|> FFix.video(0)
|> Filter.scale(w: 640, h: -2)
|> Encoder.png()
|> Muxer.mux("image2pipe", :stdout)
|> FFix.command()
result = FFix.run!(command, stdin: File.stream!("photo.png", 65_536), stdout: :collect)
File.write!("small.png", result.stdout)Collect only what you need
stdout: :collect and stderr: :collect retain their full output in memory.
For large media, consume stdout chunks with stream/2 and keep stdout's
default :discard capture policy. Live chunks are still emitted.
Progress buffers are unbounded, even with limited stderr capture.
Choose FFmpeg
Executable selection follows this order: the ffmpeg: runner option,
FFMPEG_BIN, then ffmpeg on PATH.
FFix.run(command, ffmpeg: "/usr/local/bin/ffmpeg")For an existing argument list, run/2 also accepts
["ffmpeg", "-version"]. Raw argv uses its own executable and arguments
unchanged. For setup, see FFmpeg downloads
or the development download task Mix.Tasks.Ffix.Ffmpeg.Fetch.
Summary
Functions
Runs a command and returns {:ok, result} or {:error, error}.
Like run/2, returning the result directly and raising FFix.Runner.Error on execution failure.
Returns a lazy stream of process output, progress, and lifecycle events.
Like stream/2, raising FFix.Runner.Error for missing executables or non-zero exits.
Types
@type event() :: {:stdout, binary()} | {:stderr, binary()} | {:progress, FFix.Runner.Progress.t()} | {:exit, FFix.Runner.Result.t()} | {:error, FFix.Runner.Error.t()}
@type option() :: {:ffmpeg, String.t()} | {:stdin, stdin_source()} | {:stdout, stdout_mode()} | {:stderr, stderr_mode()} | {:progress, boolean()} | {:on_event, (event() -> any())}
@type stderr_mode() :: :discard | :collect | {:tail, pos_integer()}
@type stdin_source() :: Enumerable.t() | (Collectable.t() -> any())
@type stdout_mode() :: :discard | :collect
Functions
@spec run(FFix.Command.t() | [String.t(), ...], [option()]) :: {:ok, FFix.Runner.Result.t()} | {:error, FFix.Runner.Error.t()}
Runs a command and returns {:ok, result} or {:error, error}.
A zero exit status succeeds. Missing executables and non-zero exits return
FFix.Runner.Error. Process I/O follows Exile.stream/2 semantics, without
translation to runner errors. Invalid configuration and exceptions in your
option callbacks, event handler, or stdin producer propagate to the caller.
Options
:stdin— an enumerable of bytes, or a function receiving a writableCollectablesink. For example,fn sink -> Enum.into(chunks, sink) end.:stdout—:discard(default) or:collect.:stderr—:discard,:collect, or{:tail, bytes}. The default keeps the last 65,536 bytes.:ffmpeg— executable path/name for command values; see the module guide.:progress— parse FFmpeg progress records; defaults tofalse. For command values, also adds-progress pipe:2. Raw argv must request progress itself.:on_event— a function called with every event described instream/2.
Command execution adds -hide_banner, -nostats, and -loglevel level+warning.
Override these through command global: options. There is no execution deadline;
use a supervised task when your application needs one.
Capture policies control retained stdout and stderr, independently of live
events. Progress fields accumulate until progress=continue or progress=end,
independently of stderr capture. Incomplete updates are not emitted.
@spec run!(FFix.Command.t() | [String.t(), ...], [option()]) :: FFix.Runner.Result.t()
Like run/2, returning the result directly and raising FFix.Runner.Error on execution failure.
@spec stream(FFix.Command.t() | [String.t(), ...], [option()]) :: term()
Returns a lazy stream of process output, progress, and lifecycle events.
Accepts the same options as run/2. Events are:
{:stdout, chunk}and{:stderr, chunk}— raw bytes.{:progress, progress}— aFFix.Runner.Progress, when enabled.{:exit, result}— process finished; inspect itsexit_status.{:error, error}— the executable could not be found.
Quiet commands may emit no events until they exit.
A non-zero exit is still an :exit event; use stream!/2 to raise on failure.
Early halt cancels and cleans up the child, without emitting an exit event for
that cancellation. Exile handles process and stdin cleanup, with a 1,000 ms
timeout per cleanup step. Exceptions from stream consumers propagate normally.
Consume large streams incrementally. Converting all events to a list retains
their payloads even when the runner's capture policy is :discard.
@spec stream!(FFix.Command.t() | [String.t(), ...], [option()]) :: term()
Like stream/2, raising FFix.Runner.Error for missing executables or non-zero exits.