Build and run FFmpeg commands in Elixir.

Use FFix to convert media, extract tracks, choose codecs and containers, or process audio and video with filters. Start with inputs and outputs; add a filter pipeline when you need to change the picture or sound.

You will need FFmpeg on your PATH to run these examples. See FFix.Runner to choose another executable.

Your first command

Extract the first audio track from an interview into a WAV file:

command =
  FFix.input("interview.mp4")
  |> FFix.audio(0)
  |> FFix.output("interview.wav")
  |> FFix.command()

FFix.run!(command)

An input file can contain several streams: video, audio tracks, subtitles, and more. audio(input, 0) selects the first audio stream; indexes start at zero. FFmpeg chooses the WAV format and its default encoder from the filename.

There are two deliberate steps at the end. An output describes what to write and where. A command brings the outputs and their inputs together. Building either is just preparation; run!/2 starts FFmpeg.

Choose a codec and a container

An encoder compresses the media. A muxer packages streams into a container, such as MP4 or Matroska. Here, AAC is the audio codec and MP4 is the container used for the .m4a file:

alias FFix.{Encoder, Muxer}

command =
  FFix.input("interview.wav")
  |> FFix.audio(0)
  |> Encoder.aac(b: "128k")
  |> Muxer.mp4("interview.m4a")
  |> FFix.command()

FFix.run!(command)

b: "128k" sets the audio bitrate to 128 kbit/s. An encoder can be used directly on an input stream: filtering is optional. See FFix.Encoder for quality and bitrate settings, and FFix.Muxer for format-specific options.

Copy streams without re-encoding

When the existing codecs suit the destination, stream copy is faster and preserves the encoded media. This example puts an MP4's first video track and all its audio tracks into a Matroska file:

source = FFix.input("interview.mp4")

output =
  FFix.output(
    [
      FFix.stream_copy(FFix.video(source, 0)),
      FFix.stream_copy(FFix.audio(source, :all))
    ],
    "interview.mkv"
  )

FFix.command(output) |> FFix.run!()

:all selects every stream of that media type. See stream_copy/1 for format compatibility and FFix.Command.Input for selecting optional tracks.

Add a filter

Filters work on decoded pictures or sound. Apply them before choosing the output encoder. Here we resize a video while copying its audio:

alias FFix.{Encoder, Filter, Muxer}
source = FFix.input("interview.mp4")

picture =
  source
  |> FFix.video(0)
  |> Filter.scale(w: 1280, h: -2)
  |> Encoder.libx264(crf: 23, preset: "medium")

output =
  Muxer.mp4(
    [picture, FFix.stream_copy(FFix.audio(source, 0))],
    "interview-small.mp4",
    movflags: [:faststart]
  )

FFix.command(output) |> FFix.run!()

h: -2 keeps the aspect ratio and gives the height an even number of pixels. crf controls video quality; lower values mean higher quality and usually larger files. faststart helps an MP4 begin playing before its download finishes. This example assumes an audio track compatible with MP4; use FFix.Encoder.aac/2 instead of stream copy when you need AAC audio.

Explore FFix.Filter for cropping, overlays, audio processing, and combining streams.

Write several outputs

Pass an ordered list of outputs to command/2. Reuse an input declaration to read it once, even when outputs use different encoders or settings.

This video-only example scales once, then splits the filtered pictures into a full-size rendition and a small preview:

alias FFix.{Encoder, Filter, Muxer}
source = FFix.input("interview.mp4")

[main, preview] =
  source
  |> FFix.video(0)
  |> Filter.scale(w: 1280, h: -2)
  |> Filter.split(outputs: 2)

main_output = Muxer.mp4(Encoder.libx264(main, crf: 20), "main.mp4")

preview_output =
  preview
  |> Filter.scale(w: 320, h: -2)
  |> Encoder.libx264(crf: 28)
  |> Muxer.mp4("preview.mp4")

FFix.command([main_output, preview_output]) |> FFix.run!()

A filtered stream needs a split to feed two branches. Direct input streams can be reused as they are. Outputs can also use entirely different inputs. They still run in one FFmpeg process; use separate commands when jobs need independent cancellation or retries.

Inspect, run, and learn more

Inspect a command before executing it:

FFix.to_shell_string(command)
FFix.to_argv(command)

run/2 returns {:ok, result} or {:error, error}. run!/2 returns the result or raises on execution failure. Both accept runner options such as ffmpeg: "/usr/local/bin/ffmpeg" and stderr: :collect.

The FFmpeg command guide is a useful companion when you need the meaning of a particular FFmpeg option.

Summary

Building

Selects an audio stream by zero-based audio index, or all audio streams with :all.

Assembles and validates one output or an ordered list of outputs as a command.

Builds a filter with an explicit name and ordered output media.

Groups filter outputs into a reusable graph.

Declares a media source and its input options.

Declares an output from one source or an ordered list, a target, and output options.

Selects by absolute stream index, :all, or an FFmpeg stream specifier.

Copies the selected encoded streams into an output, saving encoding time and quality loss.

Selects a subtitle track by zero-based subtitle index, or all subtitle tracks with :all.

Selects a video stream by zero-based video index, or all video streams with :all.

Inspecting

Returns a command as an argument list beginning with "ffmpeg".

Validates a graph and returns its FFmpeg filtergraph text. See FFix.Graph.to_filtergraph/1.

Returns a shell-quoted command for logs and debugging. Use to_argv/1 for process execution.

Checks graph or command structure and returns the original value.

Running

Runs a command, returning {: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 execution events. See FFix.Runner.stream/2 for progress and piping examples.

Like stream/2, raising FFix.Runner.Error on execution failure.

Building

audio(input, index, options \\ [])

Selects an audio stream by zero-based audio index, or all audio streams with :all.

FFix.audio(source, 0)
FFix.audio(source, :all, optional: true)

optional: true lets FFmpeg omit missing matches from an output. One audio stream may contain several channels; stereo is usually one stream. See FFix.Command.Input for selection examples.

command(outputs, options \\ [])

Assembles and validates one output or an ordered list of outputs as a command.

FFix.command(output)
FFix.command([main_output, preview_output], global: [n: :flag])

The command collects the inputs used by its outputs. Reusing an input declaration opens it once; separate declarations open separate inputs.

Options:

  • :global — FFmpeg options for the whole invocation.
  • :inputs — a complete, ordered input list. Use this for positional graph inputs, additional metadata inputs, or a specific input order. Include every input used by the outputs, with the same configuration.
  • :terminals — sink branches to execute alongside the outputs.
  • :settings — graph settings, currently sws_flags.

With explicit inputs:, selections follow their input declarations even when the order changes. Configure sources before selecting streams; see FFix.Command.Input for examples. Output callbacks run during serialization.

Overwriting files

global: [y: :flag] permits replacing existing output files. Use global: [n: :flag] to refuse overwrites.

For direct construction and manipulation of command data, see FFix.Command.

filter(inputs, name, output_media, options \\ [])

Builds a filter with an explicit name and ordered output media.

Use the named FFix.Filter helpers for common filters. See FFix.Filter.filter/4 for custom names and output shapes.

graph(options)

@spec graph(keyword()) :: FFix.Graph.t()

Groups filter outputs into a reusable graph.

graph = FFix.graph(outputs: [main: video, sound: audio])
graph[:main]

Supply output: for a single unnamed output or outputs: for an ordered list, optionally named. terminals: includes sink branches and settings: accepts sws_flags. See FFix.Graph for templates, binding, and parsing.

input(source, options \\ [])

Declares a media source and its input options.

source = FFix.input("interview.mp4", ss: 30)

Input options apply before reading this source. Here, ss seeks to 30 seconds. For file, URL, and pipe sources, see FFix.Command.Input.new/2.

output(sources, target, options \\ [])

Declares an output from one source or an ordered list, a target, and output options.

FFix.output([video, audio], "interview.mp4", t: 30)

Bare streams use FFmpeg's default encoding. Apply FFix.Encoder or stream_copy/1 to choose it explicitly. FFix.Muxer helpers provide format-specific options. See FFix.Command.Output for named mappings and callbacks.

select(input, selector, options \\ [])

Selects by absolute stream index, :all, or an FFmpeg stream specifier.

FFix.select(source, 3)
FFix.select(source, :all)
FFix.select(source, "a:m:language:eng", optional: true)

An absolute index counts every stream in the file. Prefer video/3, audio/3, and subtitle/3 when selecting by media type. Strings and optional selections are output queries; use an indexed media helper to select a filter input. See FFix.Command.Input and FFmpeg's stream specifiers.

stream_copy(source)

@spec stream_copy(FFix.Command.source()) :: FFix.Command.Mapping.t()

Copies the selected encoded streams into an output, saving encoding time and quality loss.

audio = source |> FFix.audio(:all) |> FFix.stream_copy()
FFix.output(audio, "audio.mka")

Check the destination format

The output container must support the copied codecs. Stream copy also requires an unfiltered source: after filtering, choose an encoder instead.

subtitle(input, index, options \\ [])

Selects a subtitle track by zero-based subtitle index, or all subtitle tracks with :all.

Use optional: true to allow a missing track. These selections add subtitle streams to outputs. To draw subtitles onto the video itself, use FFix.Filter.subtitles/2.

video(input, index, options \\ [])

Selects a video stream by zero-based video index, or all video streams with :all.

FFix.video(source, 0)
FFix.video(source, :all, optional: true)
FFix.video(source, 0, attached_pictures: false)

Options:

  • :optional — allow a missing match when mapping an output; defaults to false.
  • :attached_pictures — include cover art and thumbnails in matching; defaults to true. Set it to false to use FFmpeg's V selector.

An indexed, required selection can feed a filter. :all and optional selections go directly to outputs. See FFix.Command.Input for the selection rules.

Inspecting

to_argv(command)

@spec to_argv(FFix.Command.t()) :: [String.t()]

Returns a command as an argument list beginning with "ffmpeg".

Prefer this list when passing a command to another process library. Arguments are escaped for FFmpeg and do not need shell quoting. The runner chooses its executable separately through FFix.Runner.run/2 options.

to_filtergraph(graph)

@spec to_filtergraph(FFix.Graph.t()) :: String.t()

Validates a graph and returns its FFmpeg filtergraph text. See FFix.Graph.to_filtergraph/1.

to_shell_string(command)

@spec to_shell_string(FFix.Command.t()) :: String.t()

Returns a shell-quoted command for logs and debugging. Use to_argv/1 for process execution.

validate!(graph)

@spec validate!(FFix.Graph.t() | FFix.Command.t()) ::
  FFix.Graph.t() | FFix.Command.t()

Checks graph or command structure and returns the original value.

Raises ArgumentError for invalid connections or configuration. Validation checks the instructions; FFmpeg checks actual files and available codecs when the command runs. Deferred output option values are checked during serialization.

Running

run(command, options \\ [])

@spec run(FFix.Command.t() | [String.t(), ...], [FFix.Runner.option()]) ::
  {:ok, FFix.Runner.Result.t()} | {:error, FFix.Runner.Error.t()}

Runs a command, returning {:ok, result} or {:error, error}.

See FFix.Runner.run/2 for executable selection, input/output capture, and progress options, and FFix.Runner.Result for result fields.

run!(command, options \\ [])

@spec run!(FFix.Command.t() | [String.t(), ...], [FFix.Runner.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(), ...], [FFix.Runner.option()]) ::
  Enumerable.t()

Returns a lazy stream of execution events. See FFix.Runner.stream/2 for progress and piping examples.

stream!(command, options \\ [])

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

Like stream/2, raising FFix.Runner.Error on execution failure.

Types

output_media()

@type output_media() :: :audio | :video | :unknown

stream_index()

@type stream_index() :: non_neg_integer() | :all

video_option()

@type video_option() ::
  FFix.Command.Input.selection_option() | {:attached_pictures, boolean()}