go-astiav: Go bindings for ffmpeg, pinned to one release
Golang ffmpeg and libav C bindings
At a glance
- What is it?
- astiav is a Go library with C bindings for ffmpeg, written to give the C library a more idiomatic Go surface, to port the upstream ffmpeg examples, and to be fully tested. It targets ffmpeg n9.0 only, and it introduces breaking changes without the usual major version suffix, with a dedicated file listing them.
- Who is it for?
- astiav fits a Go developer who needs ffmpeg's codec, filter, and container machinery as a library rather than through the command line, and who can pin a specific ffmpeg build. It does not fit a project that wants to support several ffmpeg versions at once, because the library declares compatibility with n9.0 only, and it does not fit a team that treats a major version bump as the signal to upgrade.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 30 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A binding, not a wrapper
astiav is a Go library providing C bindings for ffmpeg, and the version constraint is stated in one short sentence: it is only compatible with ffmpeg n9.0.
That single sentence defines the project. A binding for a C library that is rewritten and renamed between major releases is a binding you have to rebuild often, and the author has chosen to target one release rather than to abstract over the differences. For a user that means the ffmpeg build on your machine has to be the one the library expects, which is a real constraint, and it is the constraint that a self hosted build solves.
The three goals are given as a checklist, and they describe the shape of the library.
The first is to provide a better Go idiomatic API, and the sub-items are specific. A standard error pattern, so the Go error idiom is respected rather than translated from the C return codes. Typed constants and flags, so the integer magic numbers that dominate the ffmpeg headers become named Go constants. And struct based functions, so the C functions that take a pointer to a context become methods on a Go type.
The second goal is to provide the Go version of the ffmpeg examples, which is what the examples directory is, and it is the most useful part of the repository for learning the library.
The third goal is to be fully tested, and the repository layout backs that up, because nearly every source file has a test file next to it with the same name, and the test data includes a video sample you can use in your own tests.
The project is MIT licensed, the last push was on 2026-09-23, and there are no GitHub releases, so the version you depend on is the commit you pin.
The examples table is a map of the library
The examples directory mirrors the upstream ffmpeg examples as closely as possible, and the README puts them in a table with two columns, one linking to the Go example and one linking to the corresponding ffmpeg example, with an X where there is no equivalent.
The list covers the whole pipeline. Bit stream filtering has no ffmpeg equivalent in the table. Custom IO demuxing has one. Custom IO muxing does not, which is a hint that the Go version is doing something the C example does not. Demuxing and decoding, filtering, hardware decoding and filtering, hardware encoding, remuxing, resampling audio, scaling video, and transcoding all have both.
The X column is the interesting part of the table, because it marks the places where the Go library does something on its own rather than copying. Frame data manipulation and metadata are the two that stand out, since both are areas where the C API is awkward to use and a Go shaped wrapper can offer more.
The tip at the end of the section matters for anyone writing tests against this library. You can use the video sample located in the testdata directory for your tests, which means you do not have to download a video to exercise a decode path, which is the difference between a test suite that runs in continuous integration and one that does not.
The example directories are the same list as the table, one directory per example, each with a main file. There are thirteen of them, and reading them in order is close to reading the ffmpeg documentation with the C turned into Go.
Alloc, Unref, Free, and the closure that makes it work
The Patterns section is the part of the README that is genuinely hard to get right in a binding, and it starts by saying that all use cases are different and that it is impossible to provide patterns that work in every situation, and that the ffmpeg documentation or source code should be your ultimate source of truth.
The method that every user of this library has to learn is when to call Alloc, when to call Unref, and when to call Free, and the example takes the frame reading pattern.
// You can allocate the packet once and reuse the same object in the for loop below
pkt := astiav.AllocPacket()
// However, once you're done using the packet, you need to make sure to free it
defer pkt.Free()
// Loop
for {
// We'll use a closure to ease unreferencing the packet
func() {
// Read frame using the same packet every time
formatContext.ReadFrame(pkt)
// However make sure to unreference the packet once you're done with what
// have been "injected" by the .ReadFrame() method
defer pkt.Unref()
// Here you can do whatever you feel like with your packet
}()
}The three operations are three different things. Alloc creates a packet that the library owns. The read method injects a reference into the packet, and the Unref releases that reference. Free releases the object itself, and it is deferred to the end of the enclosing scope so that it happens exactly once.
The closure is the trick, and it is worth understanding rather than copying. The Unref has to happen after the use of the packet contents and before the next loop iteration, and a defer inside the loop body would run at the end of the function instead. Wrapping the body in a closure puts the defer at the end of the closure, which is the end of the iteration.
The note above the examples says that errors are not checked there for readability purposes, and that you should. For a library that is mostly reference counting, that instruction matters, because a missed Unref is a leak and a missed Free is worse.
Building against a source ffmpeg
Because the library targets one specific ffmpeg release, the installation section is about building that ffmpeg yourself and pointing the Go toolchain at it.
The first instruction is to check out the ffmpeg section in the Devcontainer Dockerfile to see how to install ffmpeg from source, which is the practical way to get the right build, and the devcontainer in the repository is there for that purpose.
Then the environment variables, which are the part you will need on every build machine.
export CGO_CFLAGS="-I{{ path to your ffmpeg directory }}/include/",
export CGO_LDFLAGS="-L{{ path to your ffmpeg directory }}/lib/",
export PKG_CONFIG_PATH="{{ path to your ffmpeg directory }}/lib/pkgconfig",Three variables, and they do three different things. The compile flags tell cgo where the headers are, the link flags tell it where the libraries are, and the pkg-config path is what lets the build find the package configuration file that describes the library and its dependencies. You have to replace the placeholders with the absolute path to your ffmpeg directory, and the README is explicit about that.
The Go module itself is deliberately thin. It requires the Go 1.21 version, and it depends on the author's own utility library, go-astikit, and on testify for the tests, with the usual indirect dependencies and a commented out replace directive left in the file for local development of the kit.
The two stage build, the Go sources and the C glue, is visible in the file listing. There are Go files for every type in the library, and alongside them are the C and header files that the cgo layer needs, for example the class and the class category, the channel layout, and the codec context.
Windows, and the toolchain that has to be there first
There is a dedicated section for building on Windows, and it starts by saying that building there requires the msys2 or mingw64 gcc toolchain, with a link to the msys2 quickstart guide.
Once msys2 is installed, you run the mingw64 shell from the installation folder and run a set of commands. The first updates the packages, and the second installs the requirements to build, which is a list that includes git, diffutils, the mingw64 toolchain, pkg-config, make, and nasm.
# Update Packages
pacman -Syu
# Install Requirements to Build
pacman -S --noconfirm --needed git diffutils mingw-w64-x86_64-toolchain pkg-config make nasm
# Clone the repository using git
git clone https://github.com/asticode/go-astiav
cd go-astiavAfter the clone, the instructions are to follow the build instructions above, and there are two notes at the end. For pkg-config, use the pkgconfiglite package from choco. And remember to set the CGO and PKG_CONFIG environment variables properly to point at the folder where ffmpeg was built.
The second note is the one that catches people. On Windows the variables have to point at the mingw64 environment rather than at a system ffmpeg, and the pkg-config detail is there because the msys2 package of pkg-config is not always the one that finds the right file.
The nasm requirement is for the assembler, which ffmpeg's build needs, so a missing nasm shows up as a failure in the ffmpeg build rather than in the Go build, which is a confusing way to discover a missing dependency.
Why astiav exists, and the breaking changes policy
The last section explains the origin, and it is a short story with a clear conclusion.
After maintaining for several years the most starred fork of goav, which is a Go binding library for the same C libraries, the author decided to write from scratch his own C bindings, in order to fix most of the problems he was still encountering while using goav.
So astiav is a second attempt, and it is worth judging it as one. The goals on the list in the README are the problems of the first attempt restated as objectives. A standard error pattern, because the C error convention does not translate well. Typed constants and flags, because the headers are full of integers that mean nothing at the call site. Struct based functions, because a context pointer is not an object. And the Go versions of the ffmpeg examples, because the upstream documentation is the best tutorial available and the bindings are only useful if you can read it against something familiar.
The breaking changes policy is the last thing, and it is unusual enough to be worth reading twice. Breaking changes will be introduced without using the v2 Golang pattern, and the list of them is kept in a file in the repository root.
That is a deliberate trade. A library that mirrors a C API which changes between releases has to change too, and the author has decided that the cost of a major version suffix on every incompatible bump is higher than the cost of the surprise. The mitigation is documentation rather than tooling, which is weaker, and the reason it is acceptable is that the library is already pinned to one ffmpeg release, so the audience is small and attentive.
If you depend on it, the practical habit is to read that file before every upgrade, and to pin the ffmpeg build as carefully as you pin the Go module.
Editorial conclusion
astiav fits a Go developer who needs ffmpeg's codec, filter, and container machinery as a library rather than through the command line, and who can pin a specific ffmpeg build. It does not fit a project that wants to support several ffmpeg versions at once, because the library declares compatibility with n9.0 only, and it does not fit a team that treats a major version bump as the signal to upgrade. Verify first that you have ffmpeg n9.0 built from source and that the CGO and pkg-config environment variables point at it, that you read the breaking changes file before pinning a version, and that you follow the allocation pattern in the documentation, since the reference counting of the C API is where a binding like this either works or leaks.
Frequently asked questions
What is the astiav library?
It is a Go library providing C bindings for ffmpeg, with goals of a more idiomatic Go API with a standard error pattern, typed constants and flags, and struct based functions, a Go version of the upstream ffmpeg examples, and full test coverage. It is only compatible with ffmpeg n9.0.
How do I install ffmpeg for astiav?
Build it from source, following the ffmpeg section in the repository's devcontainer Dockerfile, and then point the Go toolchain at it with three environment variables: the cgo compile flags with the include path, the cgo link flags with the library path, and the pkg-config path.
When do I call Alloc, Unref, and Free in astiav?
Alloc creates an object the library owns, the read method injects a reference that Unref releases, and Free releases the object itself and is deferred to the end of the scope. The documentation shows a closure inside the loop so that the deferred Unref runs at the end of each iteration rather than at the end of the function.
Does astiav follow semantic versioning?
Not in the usual way. The README warns that breaking changes will be introduced without using the v2 Golang pattern, and the list of them is kept in a file named BREAKING_CHANGES.md in the repository root, so that file is what to read before upgrading.
What do the astiav examples cover?
Thirteen examples mirroring the upstream ffmpeg ones where one exists, including bit stream filtering, custom IO demuxing and muxing, demuxing and decoding, filtering, hardware decoding and filtering, hardware encoding, metadata, remuxing, resampling audio, scaling video, and transcoding, with a table linking each Go example to its ffmpeg counterpart.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/asticode-go-astiav)