diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 19ac4ed87..0c86d12e4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -28,16 +28,18 @@ We are operating with `semantic versioning `_. - Bug fixes (PATCH) go here. - $CHANGE by :gh-user:`mikeboers` in (:pr:`1`). -v18.0.0 (next) --------------- +v18.0.0 +------- Breaking: - Remove Python 3.10 -- Support HW encoding via a ``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 :gh-user:`WyattBlue` (:issue:`2156`). Features: +- Support HW encoding via a ``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 :gh-user:`WyattBlue` (:issue:`2156`). +- Use FFmpeg 8.1.2 in the binary wheels by :gh-user:`WyattBlue`. +- Expose ``sw_format`` on ``VideoCodecContext``, and allow configuring the software format of hardware encoders by :gh-user:`WyattBlue`. - Add ``options`` parameter to ``AudioResampler`` for passing ``libswresample`` options (e.g. ``resampler``, ``filter_size``, ``cutoff``) by :gh-user:`WyattBlue` (:issue:`2262`). - Support ``yuv420p10le`` in ``VideoFrame.to_ndarray`` and ``VideoFrame.from_ndarray`` by :gh-user:`WyattBlue` (:issue:`1981`). - Add ``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 :gh-user:`WyattBlue`. @@ -50,6 +52,8 @@ Fixes: - Fix ``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 :gh-user:`WyattBlue` (:issue:`2208`). - Fix ``add_mux_stream`` producing unwritable Matroska files by extracting codec extradata from the bitstream before the header is written by :gh-user:`WyattBlue` (:issue:`2198`). - Encode GPU frames (e.g. CUDA frames from DLPack) directly with ``pix_fmt="cuda"`` by adopting the frame's ``hw_frames_ctx`` before opening the encoder by :gh-user:`WyattBlue` (:issue:`2199`). +- Fix a crash when reading ``VideoCodecContext.pix_fmt`` before it is set; it now returns ``None`` by :gh-user:`CarlosRDomin`. +- Preserve frame attributes (``pts``, ``time_base``, colorspace, etc.) when downloading hardware frames to system memory by :gh-user:`CarlosRDomin`. v17.1.0 ------- diff --git a/av/about.py b/av/about.py index e12ca9a2e..c6a8b8ed8 100644 --- a/av/about.py +++ b/av/about.py @@ -1 +1 @@ -__version__ = "18.0.0pre" +__version__ = "18.0.0" diff --git a/av/codec/hwaccel.py b/av/codec/hwaccel.py index 392bd39ac..93e7accec 100644 --- a/av/codec/hwaccel.py +++ b/av/codec/hwaccel.py @@ -94,6 +94,8 @@ def is_supported(self): @cython.ccall def hwdevices_available(): + """Return the names of the hardware device types FFmpeg was built with, + e.g. ``["cuda", "videotoolbox"]``.""" result: list = [] x: lib.AVHWDeviceType = lib.AV_HWDEVICE_TYPE_NONE while True: @@ -107,6 +109,29 @@ def hwdevices_available(): @cython.final @cython.cclass class HWAccel: + """HWAccel(device_type, device=None, allow_software_fallback=True, options=None, flags=None, is_hw_owned=False) + + Settings for hardware-accelerated decoding and encoding. Pass an instance to + :func:`av.open` to decode on the device, or to + :meth:`OutputContainer.add_stream ` + to encode on it. + + :param device_type: The kind of device, e.g. ``"cuda"``, ``"vaapi"``, + ``"videotoolbox"``. See :func:`hwdevices_available` for the types + supported by the loaded FFmpeg. + :type device_type: str or HWDeviceType + :param str device: An optional device identifier, e.g. a DRI render node path + (``"/dev/dri/renderD128"``) for VAAPI or a device index (``"1"``) for CUDA. + Uses the default device if ``None``. + :param bool allow_software_fallback: Whether decoding falls back to a software + decoder when the hardware decoder cannot handle the stream. + :param dict options: Options passed to ``av_hwdevice_ctx_create``. + :param int flags: Flags passed to ``av_hwdevice_ctx_create``. + :param bool is_hw_owned: If True, decoded frames stay on the device (for + consumption via DLPack, for example) instead of being downloaded to + system memory. + """ + def __init__( self, device_type, diff --git a/av/filter/graph.py b/av/filter/graph.py index 7463c5a5c..3e4898ab6 100644 --- a/av/filter/graph.py +++ b/av/filter/graph.py @@ -240,6 +240,15 @@ def set_audio_frame_size(self, frame_size): ) def push(self, frame, at: cython.int = -1): + """Push a frame into the graph's buffer source(s). + + :param frame: An :class:`.AudioFrame` or :class:`.VideoFrame` to push into + the matching buffer sources, or ``None`` to signal end of stream to + every buffer source. + :param int at: Index of a single buffer source to push to, for graphs with + multiple inputs (e.g. ``overlay``). The default of ``-1`` pushes to + every buffer source matching the frame's type. + """ if frame is None: contexts = self._video_sources + self._audio_sources elif isinstance(frame, VideoFrame): @@ -263,7 +272,7 @@ def push(self, frame, at: cython.int = -1): ctx.push(frame) def vpush(self, frame: VideoFrame | None, at: cython.int = -1): - """Like `push`, but only for VideoFrames.""" + """Like :meth:`push`, but only for :class:`.VideoFrame`.""" contexts = self._video_sources if at >= 0: if at >= len(contexts): diff --git a/docs/api/codec.rst b/docs/api/codec.rst index 7b6c261e3..a3f9b27a2 100644 --- a/docs/api/codec.rst +++ b/docs/api/codec.rst @@ -128,3 +128,37 @@ Enums and Flags .. enumtable:: av.codec.context.ThreadType +Hardware Acceleration +--------------------- + +.. currentmodule:: av.codec.hwaccel +.. automodule:: av.codec.hwaccel + +.. autoclass:: HWAccel + +.. autofunction:: hwdevices_available + +To decode on a hardware device, pass an :class:`HWAccel` to :func:`av.open`:: + + import av + from av.codec.hwaccel import HWAccel + + hwaccel = HWAccel(device_type="videotoolbox") + with av.open("input.mp4", hwaccel=hwaccel) as container: + for frame in container.decode(video=0): + ... # Frames are downloaded to system memory by default. + +To encode on a hardware device, pass one to :meth:`OutputContainer.add_stream +` with a hardware encoder. Software +frames passed to ``encode`` are uploaded to the device automatically:: + + with av.open("output.mp4", "w") as container: + stream = container.add_stream( + "h264_videotoolbox", rate=30, hwaccel=HWAccel(device_type="videotoolbox") + ) + ... + +See ``examples/basics/hw_decode.py`` for a complete example, including +recommended device types per platform. + +