CLI tool
AOMediaCodec/libavif avatar
AOMediaCodec/libavif

AOMediaCodec/libavif: an AV1 image library that ships with no codec enabled

libavif - Library for encoding and decoding .avif files

2,185 stars313 forksCNOASSERTION

At a glance

What is it?
The reference C implementation of the AV1 image file format, with encoder and decoder command line tools, four example programs and a fuzzing harness. The build has no AV1 codec by default and a three-value dependency scheme in which one value tells CMake to download and compile your dependencies for you.
Who is it for?
Adopt libavif if you need AV1 image encode or decode in C and want a reference implementation with a fuzzing harness behind it, because the decoder has been run through oss-fuzz and that is the strongest available argument for trusting a parser of untrusted image data.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The configuration model, and why an empty default is deliberate

The first fact an integrator needs is that no AV1 codec is enabled by default. A fresh build produces a library that cannot encode or decode anything, and you must opt into at least one by setting a CMake option to either LOCAL or SYSTEM, depending on whether you want a locally built or a system installed version. Each of those options takes exactly three values, and the same three apply to every dependency in the project. OFF disables it. SYSTEM expects the dependency to already be installed and discoverable. LOCAL means the dependency is built locally, and in most cases CMake will download and build it for you. That is a coherent model for a library that has to link against five different codecs, three decoders and encoders from different upstream projects, and it puts you in control of which one you ship. The cost is that there is no such thing as a default build of libavif, so two projects both on version 1.4.2 can encode with completely different code, produce different file sizes at the same quality setting, and differ in their threading behaviour. If you are reviewing a project that uses libavif, the CMake flags are part of its identity and belong in the same document as the compiler version. One trap in the SYSTEM path is worth naming: those libraries must be available in their C API form and discoverable by CMake's find-library step, or, if libavif is a child CMake project, the target must already exist by the time its scripts run. A system libaom built only as a C++ library does not satisfy that.

LOCAL means CMake downloads and compiles your codec for you

The first time you build libavif from source with LOCAL, the build machine fetches source and compiles it. That is convenient and it is also the thing most likely to break in a controlled environment, so the mechanism is worth being precise about. In most cases CMake handles downloading automatically. For some dependencies you have to run the associated script in the ext/ subdirectory yourself. And if a directory with the dependency already exists in ext/, CMake uses it instead of downloading a new copy, which is the documented way to pin a version or apply patches. Three consequences follow. First, a network-restricted CI runner will fail on a first build of a clean checkout unless the dependencies are vendored into ext/ or installed as system packages. Second, ext/ is a supply-chain surface, because a directory there takes precedence over a download, so what is in that tree matters as much as what CMake fetches. Third, build times differ enormously between SYSTEM and LOCAL, and a LOCAL build of libaom plus the sharp and JPEG dependencies is not a fast operation. The recommended path when your dependencies are already installed looks like this:

sh
cmake -S libavif -B libavif/build -DAVIF_CODEC_AOM=SYSTEM -DAVIF_BUILD_APPS=ON
cmake --build libavif/build --config Release --parallel

That is the two-line shape of a normal build, and it is the one to copy if you have libaom, libjpeg, libpng and libyuv present. On Linux and macOS you probably will not build it, because the project is packaged for most major systems. On Debian-based distributions the package is

sh
sudo apt install libavif-dev

and on macOS Homebrew serves it with

sh
brew install libavif

Windows users can run

sh
vcpkg install libavif

or take the statically linked command line binaries from the releases page, and MSYS2 has a package for the default UCRT64 environment. CMake itself is the only hard build requirement when you do build it.

Two decoders, three encoders, one library that does both

The codec list is asymmetric, and the asymmetry is the decision. AVIF_CODEC_AOM selects libaom and covers encoding and decoding. AVIF_CODEC_DAV1D and AVIF_CODEC_LIBGAV1 are decoders only, from VideoLAN and Chromium respectively. AVIF_CODEC_RAV1E and AVIF_CODEC_SVT selects rav1e and SVT-AV1, both encoders. So the choice depends on which direction you need, and that is the easy half. If you only decode, which is the common case for an image format in a build pipeline or a browser-facing service, you have two decoder choices and no encoder to pay for. If you encode, you have three, and the README does not say which to pick. That is a real gap, because the encoders differ substantially in speed, in how many threads they use and in output quality at a given setting, and there is no table here to help you choose. Worse, the failure mode of choosing badly is quiet: an encoder that is slow does not error, it just makes your pipeline slow, and quality differences only show up in file size. The workaround is empirical and cheap. Build with one encoder, encode a representative corpus at the same quality value, and measure time and size before committing. avifenc exposes a quality flag in its documented invocation, which is the control you would use for that comparison.

libyuv is optional, strongly recommended, and expected to be installed

The libyuv dependency is described in a sentence that contains a mild contradiction, and the resolution matters for container builds. It is called an optional but strongly recommended dependency, it speeds up colour space conversions, and it is enabled by default with a value of SYSTEM, which means the library expects to find it installed on the system. So a dependency described as optional is on by default, and its default state is to be found on the machine. The practical effect is a build failure on a minimal image that has no libyuv, and the error will name a missing system library rather than a missing feature. There are three ways out and the README names all three: install the system package, switch to a local build with AVIF_LIBYUV=LOCAL, or disable it with AVIF_LIBYUV=OFF. Disabling is legitimate if you do not care about conversion throughput, since nothing about correctness depends on it, but you are trading speed for build simplicity and the README does not quantify that trade. The same three-value treatment applies to the other supporting dependencies visible in the from-scratch build line, which turns on local builds of the JPEG and PNG image libraries as well. That command line is the clearest illustration of the whole model, since it sets five dependencies to LOCAL and asks for a static, debug build with the command line applications enabled.

The build examples still clone v1.2.1 while the release is v1.4.2

The two documented build command lines begin with a clone of a specific tag, and the tag is v1.2.1. The current release is v1.4.2, published on 2026-05-26, following v1.4.1 in March 2026 and v1.4.0 in the same month. So a reader who copies the from-scratch build line gets a tree two minor versions behind the one they are reading the documentation for, and the README gives no indication that the example is pinned to an older release. It is a small documentation defect with a real cost, because these are exactly the lines people paste. Two positive things about the examples, though. The contrast between them is the best documentation of the dependency model in the project, since one is a Release build linking installed dependencies and the other is a Debug build with static libraries and everything local. And the second line of the pair, cmake --build with a config and a parallel flag, is correct for both. If you are building this yourself, replace the tag with the release you actually want and check the CHANGELOG in the repository root between the two. The README's own advice is the right one for consumers: use the tagged releases rather than the main branch, because bug fixes and features are added in new versions on a regular cadence.

Four examples, four layers of tests, and a fuzzing harness

The examples directory is small and its shape tells you the API surface: one program for encoding, and three for decoding from a file, from memory and in a streaming fashion. That is the whole public entry point list, and having a distinct streaming decode example means large images and streaming sources are a designed case rather than an accident. The testing story is layered in a way that matters for a parser of untrusted input. A few tests written in C are built by enabling AVIF_BUILD_TESTS. The remaining tests need GoogleTest, built by enabling the same option and setting AVIF_GTEST to SYSTEM or LOCAL. Fuzzing tests require fuzztest, with the instructions in ext/oss-fuzz/README.md, which places this project inside the oss-fuzz continuous fuzzing effort. Code coverage is available through AVIF_ENABLE_COVERAGE and an avif_coverage target, built as make avif_coverage -j, and it requires compiling with clang and having LLVM installed. For a C library whose decoder takes bytes from strangers, a fuzzing harness is not a nice-to-have; it is the reason you can consider the decoder at all. The development notes add two more signals. The library is written in C99 while most tests are in C++14, so the test code can use modern conveniences the library cannot. And there is a Release Checklist in the wiki, which is what a project with downstream packagers in most major distributions usually has.

The README says BSD and the licence field says something else

There is a licence discrepancy to resolve before you ship this. The licence field on the project page is one that cannot be classified automatically, while the README states plainly that libavif is released under the BSD License and reproduces the notice, with a 2019 copyright to Joe Drago. Those are consistent in substance and inconsistent in form, and the form matters because automated licence detection in your own tooling will read the metadata, not the README. Open the LICENSE file and use what it says. Three adjacent facts belong in the same review. There is a third_party directory, so vendored code is present and its terms are separate from the library's. There is an android_jni directory with its own CMakeLists, and the formatting instructions list a contrib/gdk-pixbuf CMake file, so the project ships platform bindings and a desktop image-loader integration in-tree rather than leaving them to downstream packaging. And there is a SECURITY.md. For a parser that will be pointed at untrusted uploads, a security policy and an oss-fuzz membership together are the two signals that matter more than any feature, and this project has both. Where it is the wrong tool is anything requiring a pipeline: libavif encodes and decodes single files and gives you two command line tools, so batch conversion, resizing and format negotiation still need to be assembled around it.

Editorial conclusion

Adopt libavif if you need AV1 image encode or decode in C and want a reference implementation with a fuzzing harness behind it, because the decoder has been run through oss-fuzz and that is the strongest available argument for trusting a parser of untrusted image data. Do not adopt it expecting a turnkey build, because no codec is enabled by default, a value of LOCAL makes CMake fetch and compile your dependencies, and libyuv is expected to be installed on the system even though it is described as optional. Three checks come before that. Check whether the codec choice is symmetric, since libaom does both directions while dav1d and libgav1 decode only and rav1e and SVT-AV1 encode only. Confirm the version you clone, because the build examples in the README still pin v1.2.1 while the current release is v1.4.2. And read the LICENSE file, since the licence field on the project page is one that cannot be classified automatically, while the README states BSD.

Frequently asked questions

How do I encode and decode an AVIF file with the command line tools?

Use avifenc with a quality flag for encoding, such as avifenc -q 75 input.[jpg|png|y4m] output.avif, and avifdec to decode, such as avifdec output.avif decoded.png. Run avifenc --help for the full option list.

Why does my libavif build have no AV1 codec?

No codec is enabled by default. You must set at least one of AVIF_CODEC_AOM, AVIF_CODEC_DAV1D, AVIF_CODEC_LIBGAV1, AVIF_CODEC_RAV1E or AVIF_CODEC_SVT to LOCAL or SYSTEM. libaom does both encoding and decoding, the other two decoders decode only, and rav1e and SVT-AV1 encode only.

What do the SYSTEM, LOCAL and OFF values mean in libavif CMake options?

OFF disables the dependency, SYSTEM expects it to be installed and discoverable, and LOCAL builds it locally, with CMake usually downloading it automatically. For some dependencies you run the script in the ext/ directory yourself, and an existing directory in ext/ is used instead of downloading a copy.

How do I install libavif on Linux and macOS?

It is packaged for most major systems. On Debian-based distributions use sudo apt install libavif-dev, on Red Hat-based ones sudo yum -y install libavif, on macOS brew install libavif or sudo port install libavif, on Windows vcpkg install libavif or the prebuilt binaries from the releases page, and under MSYS2 UCRT64 pacman -S mingw-w64-ucrt-x86_64-libavif.

How is libavif tested?

A few C tests build with AVIF_BUILD_TESTS, the rest need GoogleTest with AVIF_GTEST set to SYSTEM or LOCAL, and fuzzing tests need fuzztest with instructions in ext/oss-fuzz/README.md. Code coverage is available through AVIF_ENABLE_COVERAGE and an avif_coverage target, and requires clang.

Official sources

  1. AOMediaCodec/libavif on GitHub
  2. Issues
  3. README
  4. Releases
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/aomediacodec-libavif.svg)](https://hysenlabs.com/projects/aomediacodec-libavif)