# PyAV: Pythonic FFmpeg bindings for when the ffmpeg command is not enough

> PyAV exposes FFmpeg containers, streams, packets, codecs and frames directly to Python, and bundles FFmpeg in its wheels. It is a tool for precise media work, not a convenience wrapper, and the README says so.

**PyAV-Org/PyAV** — ﻿﻿Pythonic bindings for FFmpeg's libraries.

- Repository: https://github.com/PyAV-Org/PyAV
- Website: https://pyav.basswood.io
- Stars: 3,294 · Forks: 453
- Language: Python
- License: BSD-3-Clause
- Published: 2026-09-24 · Updated: 2026-09-24 · Language: en
- Canonical page: https://hysenlabs.com/projects/pyav-org-pyav

## What PyAV is for, and who should not use it

PyAV is a Pythonic binding for the FFmpeg libraries. The README describes the goal plainly: provide all the power and control of the underlying library while managing the gritty details as much as possible. The project positions itself for direct and precise access to media via containers, streams, packets, codecs and frames, and it exposes a few transformations of that data plus paths to and from packages such as NumPy and Pillow.

The audience is narrow on purpose. Developers who need to inspect individual packets, decode selected frames, or move raw audio and video buffers into array code will find the object model useful. Developers who simply want to transcode a file will not. The README is unusually direct about this: if the ffmpeg command does the job without you bending over backwards, PyAV is likely going to be more of a hindrance than a help. That sentence is the best adoption test the project offers, and it is worth taking literally rather than treating as modesty.

The reason for the warning is that PyAV does not decide things for you. Working with media is complicated, and the library refuses to abstract that away. You choose the container, the stream, the codec, the pixel format and the resampling parameters. That control is the product.

## The mechanism: containers, streams, packets, frames

PyAV mirrors FFmpeg's own layering rather than hiding it. A container is opened, streams are read from it, packets are pulled from a stream, and frames are decoded from those packets. The same objects run in the other direction for writing. This is why the README lists containers, streams, packets, codecs and frames as separate concepts: they are separate objects in the API, and each one carries its own state and its own failure modes.

The repository layout reflects that structure. The av package sits at the top level next to docs, examples, include, scripts and tests. The examples directory is split into audio, basics, numpy and subtitles, which maps closely to the four things people actually do with the library: decode audio, learn the object model, hand frames to array code, and handle subtitle streams. The include directory and the setup.py file show the other half of the design: PyAV is a Cython extension that links against avformat, avcodec, avdevice, avutil, avfilter, swscale and swresample. There is no pure-Python fallback, and no partial build. If those libraries are not present or not compatible, the extension does not load.

That architecture explains the trade-off at the centre of the project. You get FFmpeg's full capability and its full surface area. Nothing in the README suggests PyAV tries to smooth over codec quirks, timestamp handling or format negotiation. It gives you the objects and lets you make the decisions.

## Installing PyAV and decoding a first frame

PyAV requires Python 3.12 or later. Binary wheels are published on PyPI for Linux, macOS and Windows with FFmpeg bundled, so the common case needs no system FFmpeg at all. The README gives this command:

```bash
pip install av
```

Conda users get the same result from conda-forge, which the README also documents:

```bash
conda install av -c conda-forge
```

After installation, the package imports as av. The examples directory in the repository is the place to look for complete scripts, including examples/basics, examples/numpy, examples/audio and examples/subtitles. A first real use is opening a container, selecting a video stream and decoding one frame, which is the sequence the object model is built around: open, pick a stream, decode, inspect. If you have a file on disk, the container is opened by path and the stream is chosen from the container's stream list.

The main thing to watch during installation is the source route. The README warns that PyAV is not always the easiest Python package to install from source because of the complexity of the dependencies. Building the source distribution against an existing FFmpeg on Linux or macOS uses:

```bash
pip install av --no-binary av
```

The README states that FFmpeg's development files and pkg-config must both be available for that to work. This release supports FFmpeg 8.x. If your distribution ships an older FFmpeg, the bundled wheel is the safer path.

## Building from a Git checkout when you need a specific FFmpeg

The source build exists for people who cannot use the wheels, usually because they need a particular FFmpeg configuration. On Linux or macOS the README walks through a checkout, an activation script, an optional dependency build, the extension build, the test suite and a final install:

```bash
git clone https://github.com/PyAV-Org/PyAV.git
cd PyAV
source scripts/activate.sh

# Build ffmpeg from source. You can skip this step if ffmpeg 8.x is already installed.
./scripts/build-deps

make
make test
```

The scripts/build-deps step is the expensive one and it is skippable when ffmpeg 8.x is already installed. The Makefile shows what make actually does: it installs a pre-release Cython and setuptools, then runs setup.py build_ext --inplace --debug with CFLAGS set to -O0 -Wno-unreachable-code. That is a development build, not a release build, which is worth knowing if you plan to benchmark anything. The Makefile also defines a lint target that runs ruff, isort and mypy, and a fate-suite target that rsyncs the FFmpeg sample corpus into tests/assets/fate-suite for the test suite.

Windows takes a different route. The README uses a Conda environment with the FFmpeg development files maintained by the PyAV project, then fetches the vendor files and builds the extension in place:

```powershell
git clone https://github.com/PyAV-Org/PyAV.git
cd PyAV
conda create --name pyav-dev --channel conda-forge python=3.12 cython setuptools numpy pillow pytest
conda activate pyav-dev
$ffmpegDir = Join-Path $env:CONDA_PREFIX "Library"
python scripts\fetch-vendor.py --config-file scripts\ffmpeg-latest.json $ffmpegDir
python setup.py build_ext --inplace --ffmpeg-dir=$ffmpegDir
python -m pytest
```

Note that the Windows path uses setup.py build_ext directly with an explicit --ffmpeg-dir, while the Unix path goes through make. The two are not interchangeable.

## Where PyAV is the wrong tool

The clearest limitation is stated by the project itself. If the ffmpeg command does the job without you bending over backwards, PyAV is likely going to be more of a hindrance than a help. That covers a large share of real tasks: format conversion, simple trimming, thumbnail extraction, loudness normalisation, muxing a subtitle track into an existing file. All of those have a one-line command form, and rewriting them against the packet and frame API adds code without adding capability.

The second limitation is installation weight. PyAV is a Cython extension linked against seven FFmpeg libraries, and the build system requires setuptools 78 or later and Cython 3.3 or later. Source builds need FFmpeg development files and pkg-config, and the README notes that installing from source is not always easy because of the dependency complexity. A project that ships as a wheel has none of this; a project that must compile on an unusual architecture inherits all of it.

The third is version coupling. This release supports FFmpeg 8.x, so a system with an older FFmpeg will not satisfy a source build, and the bundled wheels pin you to whatever FFmpeg the maintainers shipped. If your pipeline depends on a specific encoder or a patched FFmpeg, the wheel is not an option and you are maintaining a build.

Finally, PyAV is not a media framework. It does not schedule work, manage a queue, or provide a transcoding service. It gives you objects and expects you to drive them. The README's phrase about responsibility is accurate: the library hands you the decisions.

## PyAV compared with calling the ffmpeg binary

The realistic alternative for most people is subprocess, running the ffmpeg command and parsing its output. The difference in approach is not speed or quality, it is where the data lives. With the binary, frames pass through a pipe or a file, and your Python code sees bytes, exit codes and stderr text. With PyAV, frames are Python objects with typed attributes, and you can read or write individual planes without serialising anything.

That matters in a specific case: when you need to look at pixel data. Moving decoded frames into NumPy arrays is a first-class path in PyAV, which is why examples/numpy exists as its own directory alongside examples/basics. Doing the same through the binary means writing raw frames to a pipe in a known pixel format and reshaping the buffer yourself, which works but puts format negotiation in your hands anyway.

The binary wins in the opposite case. It handles the whole pipeline, including the decisions PyAV refuses to make, and it is present on most systems already. If your task can be expressed as flags, the flags are shorter than the code. The honest split is that PyAV is for reading and writing media inside a Python program, and the binary is for transforming media as a job.

## Maintenance, licensing and upgrade cost

The repository is not archived, and the last push was on 2026-09-23. Recent releases are v17.1.0 on 2026-06-07, v18.0.0 on 2026-07-02 and v18.1.0 on 2026-08-12. The version is read at build time from av.about.__version__, so the package version and the release tag are the same string. The classifiers list Development Status 5 - Production/Stable and supported Python versions from 3.12 through 3.15.

The upgrade cost is tied to FFmpeg, not to Python. This release supports FFmpeg 8.x, and the README notes that the source build can fetch and build FFmpeg itself via scripts/build-deps or, on Windows, scripts/fetch-vendor.py with scripts/ffmpeg-latest.json. That means a major PyAV bump can also be a major FFmpeg bump, and encoder behaviour or format support can shift underneath you. Pinning the wheel version is the cheapest way to control that; building from source means owning it.

Licensing is straightforward to state and not legal advice. PyAV is BSD-3-Clause, as declared in pyproject.toml and LICENSE.txt. FFmpeg, which the wheels bundle, is a separate project with its own licensing, and FFmpeg builds can be configured with components under different terms depending on which codecs are enabled. If you redistribute a PyAV wheel, the FFmpeg build inside it is the part to check with your own counsel.

## Conclusion

Adopt PyAV when you need frame-level or packet-level control that shelling out to ffmpeg cannot give you, and you are willing to manage FFmpeg's complexity yourself. Do not adopt it if a plain ffmpeg command already does the job, since the README states PyAV is then more of a hindrance than a help. Before committing, verify that your Python is 3.12 or later, that a bundled wheel exists for your platform, and that the FFmpeg version you need matches the 8.x line this release supports.

## FAQ

### How do I install PyAV using pip?

Run pip install av. The README states that binary wheels are provided on PyPI for Linux, macOS and Windows with FFmpeg bundled, and that PyAV requires Python 3.12 or later.

### What is the purpose of using FFmpeg with Python?

PyAV provides direct and precise access to media via containers, streams, packets, codecs and frames, and helps move that data to and from packages such as NumPy and Pillow. It is aimed at developers who need control the ffmpeg command does not give them.

### Can I pip install FFmpeg?

The README does not describe a pip package for FFmpeg itself. What it does say is that PyAV's PyPI wheels ship with FFmpeg bundled, so installing PyAV gives you the libraries it links against without a separate system install.

### What is FFmpeg used for?

The README describes FFmpeg as the library that PyAV binds to, and lists the components PyAV links against: avformat, avcodec, avdevice, avutil, avfilter, swscale and swresample. Those cover containers, codecs, filtering, scaling and audio resampling.

## Sources

- [License: BSD-3-Clause](https://github.com/PyAV-Org/PyAV/blob/main/LICENSE)
- [Project website](https://pyav.basswood.io)
- [PyAV-Org/PyAV on GitHub](https://github.com/PyAV-Org/PyAV)
- [README](https://github.com/PyAV-Org/PyAV/blob/main/README.md)
- [Releases](https://github.com/PyAV-Org/PyAV/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/pyav-org-pyav
