akash-akya

akash-akya

FFix - build ffmpeg commands and filter graphs in Elixir

Hi everyone,

I made a small Elixir library called FFix.

FFix helps you build ffmpeg commands and filter graphs using Elixir data and functions, instead of manually joining long -filter_complex, -map, and option strings.

It does not try to hide ffmpeg. Filter names, options, stream mappings, codecs, and muxer options are still ffmpeg-shaped. FFix just gives you a nicer way to compose and inspect them from Elixir.

Examples

Examples assume import FFix and import FFix.Filter.

cmd =
  command(
    "input.mp4",
    fn src ->
      src[:video]
      |> crop(w: 720, h: 720)
    end,
    fn cropped, src ->
      output("square.mp4", video: cropped, audio: src[:audio])
    end
  )

FFix.to_argv(cmd)

This builds a command like:

ffmpeg -i input.mp4 \
  -filter_complex '[0:v]crop=w=720:h=720[out0]' \
  -map '[out0]' \
  -map 0:a \
  square.mp4

A more useful example is when the filter graph starts branching.

This creates a vertical video from a normal landscape video. It splits the video into two branches: one branch becomes a blurred background, and the other is scaled and placed on top.

cmd =
  command(
    "input.mp4",
    fn src ->
      [bg_src, fg_src] = split(src[:video], outputs: 2)

      bg =
        bg_src
        |> scale(w: 1080, h: 1920, force_original_aspect_ratio: :increase)
        |> crop(w: 1080, h: 1920)
        |> gblur(sigma: 30)

      fg =
        fg_src
        |> scale(w: 1080, h: -1)

      bg
      |> overlay(fg, x: expr("(W-w)/2"), y: expr("(H-h)/2"))
    end,
    fn video, src ->
      output("vertical.mp4",
        video: video,
        audio: src[:audio],
        "c:v": :libx264,
        "c:a": :copy,
        shortest: true
      )
    end
  )

The same thing in raw ffmpeg form is roughly:

ffmpeg -i input.mp4 \
  -filter_complex "\
[0:v]split=outputs=2[bg_src][fg_src];\
[bg_src]scale=w=1080:h=1920:force_original_aspect_ratio=increase,crop=w=1080:h=1920,gblur=sigma=30[bg];\
[fg_src]scale=w=1080:h=-1[fg];\
[bg][fg]overlay=x=(W-w)/2:y=(H-h)/2[outv]" \
  -map "[outv]" \
  -map 0:a \
  -c:v libx264 \
  -c:a copy \
  -shortest \
  vertical.mp4

FFix is mostly useful when these commands are not static strings, and you need to build them from user input, stored config, templates, or application logic.


It also supports streaming. For example, this reads WAV data through stdin and streams MP3 data back through stdout:

cmd =
  command(
    input(:stdin, f: :wav),
    fn src ->
      src[:audio]
    end,
    fn audio ->
      output(:stdout,
        audio: audio,
        f: :mp3,
        "c:a": :libmp3lame
      )
    end
  )

cmd
|> FFix.stream!(stdin: File.stream!("input.wav", [], 8192))
|> Stream.filter(fn
  {:stdout, _chunk} -> true
  _event -> false
end)
|> Stream.map(fn {:stdout, chunk} -> chunk end)
|> Enum.into(File.stream!("output.mp3", [:write, :binary]))

And one command can write multiple outputs. For example, generate MP4 and WebM versions from the same input:

cmd =
  command(
    "input.mov",
    fn src ->
      [src[:video], src[:audio]]
    end,
    fn [video, audio] ->
      [
        output("video.mp4",
          video: video,
          audio: audio,
          "c:v": :libx264,
          "c:a": :aac,
          movflags: [:faststart]
        ),

        output("video.webm",
          video: video,
          audio: audio,
          "c:v": :"libvpx-vp9",
          "c:a": :libopus
        )
      ]
    end
  )

Some things FFix supports today:

  • Build ffmpeg commands and filter graphs with Elixir data/functions instead of raw string assembly.
  • Generated helpers and docs for ffmpeg filters like scale, crop, overlay, drawtext, fps, etc.
  • Named graph outputs, multiple inputs/outputs, and stream selectors like src[:video], src[:audio], src[audio: 1].
  • Parsing and serializing filter graphs.
  • to_argv/1 for the command boundary, plus a thin runner for stdin/stdout streaming, collected results, progress events, and ffmpeg logs.

For more details, the docs have the full API and more examples. There is an intro Livebook too.

The goal is to stay close to ffmpeg and keep the core simple. FFix models commands and filter graphs as normal Elixir data, and only serializes them to ffmpeg syntax at the boundary. I’ve used some version of this library for my own ffmpeg tasks for a few years. It was sitting in my backlog for a long time, so I finally cleaned it up and organised it with some help from an LLM agent.

Feedback is very welcome, especially on the API shape and scope.

Install:

def deps do
  [
    {:ffix, "~> 0.1.0"}
  ]
end

Note: ffmpeg must be available at compile time, because FFix generates filter helpers/docs from local ffmpeg metadata.

Links:

https://github.com/akash-akya/ffix

Most Liked

manhvu

manhvu

Uses pipes to pass arguments to ffmpeg is a nice way.

Where Next?

Popular in Announcing Top

danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 29603 241
New
dbern
I’m excited to announce that TaxJar has developed and open-sourced DateTimeParser. We developed it because we found a need to parse user ...
New
aesmail
Hello guys, I have finally made it. I created an admin interface for a framework. It’s been on my todo list for years and with the curre...
New
zorbash
I created Kitto a framework for dashboards inspired by Dashing. The distributed characteristics of Elixir and the low memory footprint...
New
kip
ex_cldr provides localisation and internationalisation support based upon the data from the Unicode CLDR project. Unicode released CLDR ...
407 13014 120
New
nikokozak
Hello all, I’ve been working on Svonix - a library for quickly integrating Svelte components into Phoenix views. It’s a much-needed succ...
New
wojtekmach
Hey everyone! Req is an HTTP client for Elixir that I’ve been working on for quite some time. There is already a lot of HTTP clients out...
New
Qqwy
TypeCheck: Fast and flexible runtime type-checking for your Elixir projects. Core ideas Type- and function specifications are const...
336 14482 100
New
scohen
Lexical Lexical is a next-generation language server for the Elixir programming language. Features Context aware code completion As-you...
New
mattludwigs
Grizzly is a library for working with Z-Wave devices. Z-Wave is a low-frequency radio protocol for controlling smart home devices on a me...
New

Other popular topics Top

danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 29603 241
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
Nvim
Anybody knows a comprehensive comparison of Django and Phoenix, thanks for the help. Where are they similar? Where do they differ the m...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
greenz1
I have a phoenix application from which a user can download multiple(5-6) files of size 1MB. I couldn’t find anything related to sending ...
New
josevalim
Hi everyone, One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 52673 488
New
vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement