FFix.Runner (ffix v0.2.0)

Copy Markdown View Source

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)
end

Use 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

event()

@type event() ::
  {:stdout, binary()}
  | {:stderr, binary()}
  | {:progress, FFix.Runner.Progress.t()}
  | {:exit, FFix.Runner.Result.t()}
  | {:error, FFix.Runner.Error.t()}

option()

@type option() ::
  {:ffmpeg, String.t()}
  | {:stdin, stdin_source()}
  | {:stdout, stdout_mode()}
  | {:stderr, stderr_mode()}
  | {:progress, boolean()}
  | {:on_event, (event() -> any())}

stderr_mode()

@type stderr_mode() :: :discard | :collect | {:tail, pos_integer()}

stdin_source()

@type stdin_source() :: Enumerable.t() | (Collectable.t() -> any())

stdout_mode()

@type stdout_mode() :: :discard | :collect

Functions

run(command, options \\ [])

@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 writable Collectable sink. 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 to false. For command values, also adds -progress pipe:2. Raw argv must request progress itself.
  • :on_event — a function called with every event described in stream/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.

run!(command, options \\ [])

@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.

stream(command, options \\ [])

@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} — a FFix.Runner.Progress, when enabled.
  • {:exit, result} — process finished; inspect its exit_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.

stream!(command, options \\ [])

@spec stream!(FFix.Command.t() | [String.t(), ...], [option()]) :: term()

Like stream/2, raising FFix.Runner.Error for missing executables or non-zero exits.