v19.0.0
Major
- Drop support for Python 3.11. Binary wheels are now built for Python 3.12 and later.
- Remove the undocumented
CodecContext.hwaccelattribute. It held theHWAccelsettings object passed in, not the live device context; useCodecContext.is_hwaccelto check whether hardware acceleration is in use. - Rational attributes (
time_base,average_rate,base_rate,guessed_rate,framerate,rate,sample_aspect_ratio, anddisplay_aspect_ratio) now returnav.AVRationalrather thanfractions.Fraction, and are neverNone: an unset value is the falsyAVRational(0, 1). Test them withif not stream.time_base:instead ofis None. Setters still accept afractions.Fraction. - Closing an
OutputContainernow frees its context, so using one afterwards raisesContainer is not openinstead of continuing against a finished file. Muxing has noavformat_close_input()to do this for it, soadd_stream(),mux(),start_encoding()and the rest kept working afterclose(), and streams held from it stayed readable. - Remove
Capabilities.hwaccel,Capabilities.hwaccel_vdpau, andCapabilities.neg_linesizes, none of which FFmpeg defines any more. - Remove the
metadata_encodingandmetadata_errorsarguments toav.open, and the matching attributes. Metadata is now always read and written as UTF-8 withsurrogateescape, which is byte exact: a tag in another encoding survives as surrogates and is recovered per key withvalue.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. - Remove the
stream_optionsargument toav.openand the matching attribute. They only ever reachedavformat_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. Passoptionsfor every stream, setstream.codec_context.optionsfor one, andContainer.add_stream(..., options={})when writing.
Features
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, matchingffmpeg -codecs. Its legend gains the..D...and..T...media types.ContainerFormat.fixed_framesizereports whether a format wants fixed size audio frames.CodecContextexposes more ofAVCodecContext:pkt_timebase,frame_num,active_thread_type,bits_per_raw_sample,compression_level,rc_buffer_size,min_bit_rate, a setter formax_bit_rate, the audioinitial_padding,trailing_padding, andseek_preroll, andstats_in/stats_outfor two-pass encoding.VideoCodecContextgainschroma_sample_location,refs, andmb_decision;AudioCodecContextgainsblock_align.CodecContext.coded_side_dataandCodecContext.decoded_side_dataexpose the context's global side data as dicts ofbytes, keyed by packet side data name andTyperespectively. Stream wide HDR metadata, such as mastering display and content light level, arrives indecoded_side_dataonce a frame has been decoded.VideoFrame.chroma_locationexposesAVFrame.chroma_location, the position of the chroma samples relative to the luma samples, and the newChromaLocationenum names its values. Only the codec context side of the field was wrapped, asVideoCodecContext.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.- Enums gained the members FFmpeg has since added:
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, theLCEVC,VIEW_ID,THREE_D_REFERENCE_DISPLAYS, andEXIFmembers ofsidedata.Type, and theexif,dynamic_hdr_smpte_2094_app5, andhevc_confpacket side data names.
Fixes
Frame.side_dataandPacketSideData.data_typeno 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, theAV_PKT_DATA_HEVC_CONFan HEVC stream in MP4 or Matroska carries raisedIndexError. A frame side data type that nosidedata.Typemember names, which is anything a newer FFmpeg than PyAV was built against added, now becomes anUNKNOWN_<value>member rather than raisingValueError.CodecContext.bit_rate_tolerancereturns its value instead of alwaysNone; the getter was missing itsreturn.- A rejected
add_stream()oradd_mux_stream()no longer breaks the container. InputContainer.sizereturnsNonewhen the size cannot be determined rather than the negativeAVERRORit 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 namesh264,hevc,av1,dirac, andilbc, each of which was overwritten by the row of whichever encoder it resolved to.FilterLink.inputandFilterLink.outputnow follow the filters the graph auto-inserts while configuring. They cached the pad they first resolved, so reading one beforeGraph.configure()reported the filter the link no longer pointed at.- Fix a segfault when a
FilterLinkoutlives itsGraph. It held the graph by weak reference and dereferencedAVFilterLinkbefore consulting it, solink.inputandlink.outputread freed memory. It now holds the graph, as aFilterContextalready did. - Reading a
Streamor itsindex_entriesafter the container is closed now raises instead of reading freed memory, sinceavformat_close_input()frees the underlyingAVStream. Holding anindex_entriesalso keeps its container alive, and anIndexEntryis a copy, so it stays readable after the close and is unaffected by the demuxer reallocating the index. - Attaching one object to the
opaqueof more than one frame or packet no longer loses it. The objects were keyed byid(), so every holder shared an entry and whichever was freed first took it away from the rest. Frame.side_datanow satisfies theMappingprotocol it advertises: iteration yieldsTypekeys, soitems(),keys(), andvalues()work instead of raisingKeyError. Values remain reachable positionally by an integer or, for the first time, a slice. Its type stub was aTypedDictwith a single literal key, and is nowSideDataContainer.- Fix crashes from indexes that were turned into C pointer arithmetic without being range checked.
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);VideoFormatComponentandAudioPlaneaccepted any index at all; andBitmapSubtitlePlaneandVideoBlockParamswere missing their lower bounds. - Frames returned by flushing a codec context directly (
CodecContext.decode()with no packet) now carry the stream'stime_baseinstead ofNone. VideoFrame.reformat()(and soto_ndarray(format=...),to_rgb(),to_image()) now shares oneSwsContextper 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.- Writing to a network URL no longer blocks every other Python thread, and
timeoutnow applies to opening an output container.avio_open(),avformat_write_header(),av_write_trailer(), andavio_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.closenow raises rather than freeing a context another thread is still muxing or closing. By @adrianrfreedman in (#2412). timeoutnow 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 leftav_interleaved_write_frame()andav_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.createnow passesflagson to the context it creates. They were dropped, soav_hwdevice_ctx_create()always got0and CUDA'sAV_CUDA_USE_PRIMARY_CONTEXTcould not be requested through them.
New Contributors
- @threexc made their first contribution in #2398
- @dylanpulver made their first contribution in #2405
- @happyme531 made their first contribution in #2404
- @adrianrfreedman made their first contribution in #2412
- @Robolightning made their first contribution in #2415
Full Changelog: v18.1.0...v19.0.0