NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
PyPI · #615 most downloaded on PyPI
Pythonic bindings for FFmpeg's libraries.
Last release 2 days ago
03 Oct 2026
Ships fairly regularly
a new release about every 2 months
Rarely documented
notes for 9 of 59 stable releases
Nothing withdrawn
no release was ever pulled
13 years old
59 releases · first in 2013
Fix #2425 by @WyattBlue in #2426
Full Changelog: v19.0.0...v19.0.1
Fixes:
Reading .Frame.side_data no longer holds the frame in a reference cycle, which kept its picture buffer alive until gc ran. By WyattBlue.
.MotionVectors no longer memoizes the vectors it hands out, a cache that never evicted and put every one of them in a reference cycle with the side data, retaining 1640 objects for a single 640x360 frame. By WyattBlue.
.Stream.index_entries is now a view built on access rather than an object allocated for every stream whether or not anything reads it, which also takes the stream out of a reference cycle with it. Fixes (2425) by WyattBlue.
.Filter and .FilterContext pads are now built on access rather than cached, which takes a filter out of a reference cycle with its pads and makes a pad compare equal by what it denotes. By WyattBlue.
One column per quarter.
Drop support for Python 3.11. Binary wheels are now built for Python 3.12 and later.
CodecContext.hwaccel attribute. It held the HWAccel settings object passed in, not the live device context; use CodecContext.is_hwaccel to check whether hardware acceleration is in use.time_base, average_rate, base_rate, guessed_rate, framerate, rate, sample_aspect_ratio, and display_aspect_ratio) now return av.AVRational rather than fractions.Fraction, and are never None: an unset value is the falsy AVRational(0, 1). Test them with if not stream.time_base: instead of is None. Setters still accept a fractions.Fraction.OutputContainer now frees its context, so using one afterwards raises Container is not open instead of continuing against a finished file. Muxing has no avformat_close_input() to do this for it, so add_stream(), mux(), start_encoding() and the rest kept working after close(), and streams held from it stayed readable.Capabilities.hwaccel, Capabilities.hwaccel_vdpau, and Capabilities.neg_linesizes, none of which FFmpeg defines any more.metadata_encoding and metadata_errors arguments to av.open, and the matching attributes. Metadata is now always read and written as UTF-8 with surrogateescape, which is byte exact: a tag in another encoding survives as surrogates and is recovered per key with value.encode("utf-8", "surrogateescape").decode("cp1251"). Previously one encoding had to be chosen for a whole container, so a file mixing encodings across tags could not be represented at all.stream_options argument to av.open and the matching attribute. They only ever reached avformat_find_stream_info(), and only for formats that expose their streams before it runs, so they raised for MPEG and friends; output containers rejected them outright. Pass options for every stream, set stream.codec_context.options for one, and Container.add_stream(..., options={}) when writing.av.dump_codecs() now lists every codec FFmpeg knows of rather than only those with an encoder or a decoder, so data and attachment codecs appear, matching ffmpeg -codecs. Its legend gains the ..D... and ..T... media types.ContainerFormat.fixed_framesize reports whether a format wants fixed size audio frames.CodecContext exposes more of AVCodecContext: pkt_timebase, frame_num, active_thread_type, bits_per_raw_sample, compression_level, rc_buffer_size, min_bit_rate, a setter for max_bit_rate, the audio initial_padding, trailing_padding, and seek_preroll, and stats_in/stats_out for two-pass encoding. VideoCodecContext gains chroma_sample_location, refs, and mb_decision; AudioCodecContext gains block_align.CodecContext.coded_side_data and CodecContext.decoded_side_data expose the context's global side data as dicts of bytes, keyed by packet side data name and Type respectively. Stream wide HDR metadata, such as mastering display and content light level, arrives in decoded_side_data once a frame has been decoded.VideoFrame.chroma_location exposes AVFrame.chroma_location, the position of the chroma samples relative to the luma samples, and the new ChromaLocation enum names its values. Only the codec context side of the field was wrapped, as VideoCodecContext.chroma_sample_location, so the siting a decoder actually reported per frame could not be read at all. Each property mirrors its C field name, which FFmpeg spells differently on the two structs.Properties.FIELDS, Properties.ENHANCEMENT, PixFmtLoss.EXCESS_RESOLUTION, PixFmtLoss.EXCESS_DEPTH, Flags2.icc_profiles, format.Flags.experimental, Interpolation.STRICT, Interpolation.UNSTABLE, ColorTrc.V_LOG, ColorPrimaries.V_GAMUT, the LCEVC, VIEW_ID, THREE_D_REFERENCE_DISPLAYS, and EXIF members of sidedata.Type, and the exif, dynamic_hdr_smpte_2094_app5, and hevc_conf packet side data names.Frame.side_data and PacketSideData.data_type no longer raise on side data types FFmpeg has added since PyAV last listed them. The packet side data names were missing three, so reading, say, the AV_PKT_DATA_HEVC_CONF an HEVC stream in MP4 or Matroska carries raised IndexError. A frame side data type that no sidedata.Type member names, which is anything a newer FFmpeg than PyAV was built against added, now becomes an UNKNOWN_<value> member rather than raising ValueError.CodecContext.bit_rate_tolerance returns its value instead of always None; the getter was missing its return.add_stream() or add_mux_stream() no longer breaks the container.InputContainer.size returns None when the size cannot be determined rather than the negative AVERROR it was passing through, which read as a plausible byte count. A non-seekable input, such as a pipe, reported -78.av.dump_codecs() no longer drops the canonical names h264, hevc, av1, dirac, and ilbc, each of which was overwritten by the row of whichever encoder it resolved to.FilterLink.input and FilterLink.output now follow the filters the graph auto-inserts while configuring. They cached the pad they first resolved, so reading one before Graph.configure() reported the filter the link no longer pointed at.FilterLink outlives its Graph. It held the graph by weak reference and dereferenced AVFilterLink before consulting it, so link.input and link.output read freed memory. It now holds the graph, as a FilterContext already did.Stream or its index_entries after the container is closed now raises instead of reading freed memory, since avformat_close_input() frees the underlying AVStream. Holding an index_entries also keeps its container alive, and an IndexEntry is a copy, so it stays readable after the close and is unaffected by the demuxer reallocating the index.opaque of more than one frame or packet no longer loses it. The objects were keyed by id(), so every holder shared an entry and whichever was freed first took it away from the rest.Frame.side_data now satisfies the Mapping protocol it advertises: iteration yields Type keys, so items(), keys(), and values() work instead of raising KeyError. Values remain reachable positionally by an integer or, for the first time, a slice. Its type stub was a TypedDict with a single literal key, and is now SideDataContainer.MotionVectors[i] only checked the upper bound, so a negative index read off the front of the buffer (mvs[-1] now returns the last vector, as with any sequence); VideoFormatComponent and AudioPlane accepted any index at all; and BitmapSubtitlePlane and VideoBlockParams were missing their lower bounds.CodecContext.decode() with no packet) now carry the stream's time_base instead of None.VideoFrame.reformat() (and so to_ndarray(format=...), to_rgb(), to_image()) now shares one SwsContext per thread instead of allocating one per frame. FFmpeg 8's swscale retains megabytes of graph state per context, which showed up as large RSS growth when many frames were alive at once.timeout now applies to opening an output container. avio_open(), avformat_write_header(), av_write_trailer(), and avio_closep() held the GIL, so an unreachable RTMP server froze the whole process, and the interrupt callback was only installed for demuxing, so nothing could end the wait. OutputContainer.close now raises rather than freeing a context another thread is still muxing or closing. By @adrianrfreedman in (#2412).timeout now applies to muxing and closing an output container, not just to opening it. Only opening armed the interrupt callback, so a peer that accepted the connection and then stopped reading left av_interleaved_write_frame() and av_write_trailer() blocked forever. Each mux gets the full timeout, and a close shares one across writing the trailer and flushing, so neither can outlast it. Only unseekable outputs are covered: writing a seekable file takes as long as the file is big, so a deadline meant for a peer would abandon it part-written. By @adrianrfreedman in (#2414).HWAccel.create now passes flags on to the context it creates. They were dropped, so av_hwdevice_ctx_create() always got 0 and CUDA's AV_CUDA_USE_PRIMARY_CONTEXT could not be requested through them.Full Changelog: v18.1.0...v19.0.0
Infer the specific stream and codec context types for all FFmpeg encoder names in type-checked code by @WyattBlue .
CodecContext.supported_options to discover generic and codec-specific options by @WyattBlue (#2032).Packet.rescale_ts() to rescale packet PTS, DTS, and duration to a new AVRational time base by @WyattBlue.current_ctx flag on CudaContext and VideoFrame.from_dlpack, for interop with libraries like PyTorch that initialize CUDA first by @Yozer (#2339).VideoFrame.from_dlpack no longer requires restating primary_ctx/current_ctx when passing an explicit cuda_context; the flags are only validated when explicitly given by @WyattBlue.cuda_stream parameter on CudaContext; currently limited to logical CUDA device 0 by @Yozer (#2360).av.AVRational, an exact rational number stored as two int32s that mirrors FFmpeg's AVRational. Codec.frame_rates now holds these rather than fractions.Fraction, and every setter that accepts a Fraction also accepts an AVRational. Unset rational attributes are falsy, so test them with if not stream.time_base: rather than is None by @WyattBlue.Frame.metadata, the metadata FFmpeg attaches to a frame, by @WyattBlue.CodecContext.level by @WyattBlue.VideoCodecContext.field_order by @WyattBlue.VideoFrame.save now forwards keyword arguments to the encoder, letting callers trade file size for speed (e.g. pred="none" or compression_level=1 for PNG, qscale=2 for JPG) by @WyattBlue.InputContainer.start_time_realtime, the stream's start time in microseconds since the Unix epoch, which RTSP derives from RTCP sender reports, by @WyattBlue (#2365).VideoFrame.save raising AttributeError when given a Path rather than a str by @WyattBlue.VideoStream after add_stream being dropped from the output; the codec context's frame rate is now copied to the stream before the header is written by @WyattBlue (#2301).CodecContext (a demuxed stream with no available decoder, such as one from a truncated file, or a stream created by add_mux_stream); decoding now raises DecoderNotFoundError, encoding now raises EncoderNotFoundError, and BitStreamFilterContext accepts such a stream as out_stream by @WyattBlue, reported by @justinrmiller (#2344).Full Changelog: v18.0.0...v18.1.0
Upgrade binary wheels to ffmpeg 8.1.2
hwaccel parameter on OutputContainer.add_stream (e.g. h264_vaapi, h264_nvenc, h264_videotoolbox); software frames passed to encode are uploaded to the device automatically by @WyattBlue (#2156).sw_format on VideoCodecContext, and allow configuring the software format of hardware encoders by @WyattBlue.options parameter to AudioResampler for passing libswresample options (e.g. resampler, filter_size, cutoff) by @WyattBlue (#2262).yuv420p10le in VideoFrame.to_ndarray and VideoFrame.from_ndarray by @WyattBlue (#1981).at parameter to Graph.push and Graph.vpush to push a frame to a single buffer source by index, for multi-input filters like overlay by @WyattBlue.find_best_pix_fmt_of_list now returns the loss as a PixFmtLoss enum.IntFlag instead of a plain int by @WyattBlue (#2300).Colorspace.BT2020 by @mark-oshea.BitStreamFilterContext now accepts a Codec or a codec-name str as in_stream to pin the input codec without a full Stream by @WyattBlue.VideoFrame.reformat (and to_ndarray/to_rgb/to_image) raising OSError Operation not supported on frames tagged with reserved or otherwise unsupported color_primaries/color_trc values (e.g. VP9 and NVDEC output); a transfer/primaries conversion is now only performed when explicitly requested by @WyattBlue (#2208).add_mux_stream producing unwritable Matroska files by extracting codec extradata from the bitstream before the header is written by @WyattBlue (#2198).pix_fmt="cuda" by adopting the frame's hw_frames_ctx before opening the encoder by @WyattBlue (#2199).VideoCodecContext.pix_fmt before it is set; it now returns None by @CarlosRDomin.pts, time_base, colorspace, etc.) when downloading hardware frames to system memory by @CarlosRDomin.Full Changelog: v17.1.0...v18.0.0
Remove the undertested av.option and av.descriptor APIs, along with the related Codec and Filter descriptor accessors.
av.option and av.descriptor APIs, along with the related Codec and Filter descriptor accessors.AVCodecContext.global_quality by @WyattBlue in #2246.Stream.discard so demuxing and seeking can skip unwanted streams by @WyattBlue (#2272).Stream.set_display_matrix() and Stream.set_display_rotation() to write the container display (rotation) matrix on output streams by @hmaarrfk in #2287.Container.video_codec_id to force a specific video codec on a container by @WyattBlue (#2243).cython.final to leaf classes, ensuring that they are not subclassed by @WyattBlue.CodecContext.decode() is not memory safe in some cases.FFmpegError, AudioLayout channel layouts, and Frame.opaque, and break a reference cycle between FilterLink and Graph by @lgeiger.Stream from multiple threads under FFmpeg 8.1 by @WyattBlue (#2247).InputContainer initialization by @WyattBlue (#2010).enumerate_input_devices and enumerate_output_devices raising AttributeError by @WyattBlue and @kazuki (#2264).HTTPTooManyRequestsError instead of UndefinedError by @WyattBlue (#2267).VideoFrame.to_ndarray() and to_image() on bottom-up frames with a negative line_size by @WyattBlue (#2213).Disposition an IntFlag so Stream.disposition can be assigned without raising TypeError by @HotariTobu.pts, dts, and duration to packets from CodecContext.parse() by @WyattBlue (#1919).time_base in add_stream_from_template() by @daveisfera in #2249.malloc size calculations by @WyattBlue.Full Changelog: v17.0.1...v17.1.0
Adjust tests so they work with ffmpeg 8.1 by @strophy in #2204 and #2205
StreamContainer.get() by @lgeiger in #2221threads in filter graph by @lgeiger in #2211_send_packet_and_recv to simplify decode() by @lgeiger in #2233AVPacket read buffer when demuxing by @lgeiger in #2234AVCodecContext.frame_num instead of counting ourselves by @WyattBlue in #2236layout_compare() for AudioLayout.__eq__() by @WyattBlue in #2236Full Changelog: v17.0.0...v17.0.1
Limited API binary wheels are now built.
OutputContainer.add_mux_stream() for creating codec-context-free streams, enabling muxing of pre-encoded packets without an encoder, addressing #1970 by @WyattBlue.ColorTrc and ColorPrimaries enums; add color_trc and color_primaries properties to VideoFrame; add dst_color_trc and dst_color_primaries parameters to VideoFrame.reformat(), addressing #1968 by @WyattBlue in (#2175).sws_scale with sws_scale_frame, skipping unnecessary reformats, and minimizing Python interactions by @lgeiger.VideoFrame.to_ndarray() for padded frames by @lgeiger in (#2190).add_stream_from_template by skipping avcodec_open2 for template-initialized codec contexts, addressing #2135 by @WyattBlue).scripts/build-deps) to prevent accidental OpenSSL linkage that breaks FIPS-enabled systems, addressing #1972.SWS_SPLINE interpolation by @lgeiger in (#2188).Full Changelog: v16.1.0...v17.0.0
Add support for Intel QSV codecs by @ladaapp .
Features:
avcodec_find_best_pix_fmt_of_list() by @ndeybach in #2058.Fixes:
Full Changelog: v16.0.1...v16.1.0
Nothing published for this version
Nothing published for this version
Build is failing because it can't find libcurl: ValueError: Cannot repair wheel, because required library "libssl.so.10" could not be located
Build is failing because it can't find libcurl:
ValueError: Cannot repair wheel, because required library
"libssl.so.10" could not be located
Someone who cares about this can fix it
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →