From 7ea3f9bb2bc80cb070651b16bb532ab3924f2cf4 Mon Sep 17 00:00:00 2001 From: WyattBlue Date: Tue, 27 Oct 2026 20:44:48 -0400 Subject: [PATCH 1/2] Deprecate VideoStream.thread_type passthrough VideoStream.thread_type is now a real property that warns and points to cctx. It was only reachable through __getattr__, so stubtest reported the stub's property as missing at runtime. add_stream() keyword arguments set thread_type on the codec context, so they don't warn. --- CHANGELOG.rst | 2 +- av/container/output.py | 6 +++--- av/stream.py | 2 +- av/video/stream.py | 31 +++++++++++++++++++++++++++---- av/video/stream.pyi | 2 ++ examples/basics/hw_decode.py | 2 +- examples/basics/thread_type.py | 2 +- examples/numpy/barcode.py | 2 +- tests/test_streams.py | 31 ++++++++++++++++++++++++------- 9 files changed, 61 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a862fcc9a..7871aae70 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -35,7 +35,7 @@ Features: - Add ``VideoFrame.solid()`` to create a frame filled with one color, in any non-paletted pixel format. - Add ``Stream.cctx`` as the name for a stream's codec context; ``Stream.codec_context`` remains as an alias. -- Deprecate ``AudioStream.format``, ``AudioStream.layout``, and ``VideoStream.format``, which pass through to the codec context; use ``stream.cctx.format`` and ``stream.cctx.layout`` instead. Passing ``format`` or ``layout`` to ``add_stream()`` is not deprecated. +- Deprecate ``AudioStream.format``, ``AudioStream.layout``, ``VideoStream.format``, and ``VideoStream.thread_type``, which pass through to the codec context; use ``stream.cctx.format``, ``stream.cctx.layout``, and ``stream.cctx.thread_type`` instead. Passing ``format``, ``layout``, or ``thread_type`` to ``add_stream()`` is not deprecated. Fixes: diff --git a/av/container/output.py b/av/container/output.py index 46c1a2d5b..0185e93fe 100644 --- a/av/container/output.py +++ b/av/container/output.py @@ -38,10 +38,10 @@ def _set_codecpar_extradata( @cython.cfunc def _set_stream_attrs(stream: Stream, ctx: CodecContext, kwargs: dict) -> cython.void: - # format and layout go straight to the codec context, since the stream - # passthroughs for them are deprecated. + # format, layout, and thread_type go straight to the codec context, since + # the stream passthroughs for them are deprecated. for k, v in kwargs.items(): - if k == "format" or k == "layout": + if k in ("format", "layout", "thread_type"): setattr(ctx, k, v) else: setattr(stream, k, v) diff --git a/av/stream.py b/av/stream.py index 2c324c2a5..d6edf0e6c 100644 --- a/av/stream.py +++ b/av/stream.py @@ -164,7 +164,7 @@ def __setattr__(self, name, value): if name == "time_base": to_avrational(value, cython.address(self.ptr.time_base)) return - if name == "format" or name == "layout": + if name in ("format", "layout", "thread_type"): prop = getattr(type(self), name, None) if prop is not None: prop.__set__(self, value) diff --git a/av/video/stream.py b/av/video/stream.py index e60ade5a3..ba994abeb 100644 --- a/av/video/stream.py +++ b/av/video/stream.py @@ -10,9 +10,9 @@ from cython.cimports.libc.string import memcpy -def _warn_format_passthrough(): +def _warn_passthrough(name): warnings.warn( - "VideoStream.format is deprecated; use VideoStream.cctx.format instead", + f"VideoStream.{name} is deprecated; use VideoStream.cctx.{name} instead", DeprecationWarning, # Cython functions have no frames, so 1 is the caller. stacklevel=1, @@ -58,7 +58,7 @@ def format(self): :type: VideoFormat | None """ - _warn_format_passthrough() + _warn_passthrough("format") if self.cctx is None: raise AttributeError( f"'{type(self).__name__}' object has no attribute 'format'" @@ -67,10 +67,33 @@ def format(self): @format.setter def format(self, value): - _warn_format_passthrough() + _warn_passthrough("format") if self.cctx is not None: self.cctx.format = value + @property + def thread_type(self): + """ + The threading type of the codec context. + + .. deprecated:: 19.1 + Use ``stream.cctx.thread_type`` instead. + + :type: ThreadType + """ + _warn_passthrough("thread_type") + if self.cctx is None: + raise AttributeError( + f"'{type(self).__name__}' object has no attribute 'thread_type'" + ) + return self.cctx.thread_type + + @thread_type.setter + def thread_type(self, value): + _warn_passthrough("thread_type") + if self.cctx is not None: + self.cctx.thread_type = value + @cython.ccall def encode(self, frame: VideoFrame | None = None): """ diff --git a/av/video/stream.pyi b/av/video/stream.pyi index 43a12d20a..983539d70 100644 --- a/av/video/stream.pyi +++ b/av/video/stream.pyi @@ -50,8 +50,10 @@ class VideoStream(Stream): def format(self, value: VideoFormat) -> None: ... thread_count: int @property + @deprecated("Use VideoStream.cctx.thread_type instead") def thread_type(self) -> ThreadType: ... @thread_type.setter + @deprecated("Use VideoStream.cctx.thread_type instead") def thread_type(self, value: int | str | ThreadType) -> None: ... width: int height: int diff --git a/examples/basics/hw_decode.py b/examples/basics/hw_decode.py index 605ee1841..bdc695e92 100644 --- a/examples/basics/hw_decode.py +++ b/examples/basics/hw_decode.py @@ -40,7 +40,7 @@ container = av.open(test_file_path) -container.streams.video[0].thread_type = "AUTO" +container.streams.video[0].cctx.thread_type = "AUTO" start_time = time.time() frame_count = 0 diff --git a/examples/basics/thread_type.py b/examples/basics/thread_type.py index fab539b26..23066a4a3 100644 --- a/examples/basics/thread_type.py +++ b/examples/basics/thread_type.py @@ -26,7 +26,7 @@ ) # !!! This is the only difference. -container.streams.video[0].thread_type = "AUTO" +container.streams.video[0].cctx.thread_type = "AUTO" start_time = time.time() for packet in container.demux(): diff --git a/examples/numpy/barcode.py b/examples/numpy/barcode.py index bd40f17a3..4b2fafa1f 100644 --- a/examples/numpy/barcode.py +++ b/examples/numpy/barcode.py @@ -7,7 +7,7 @@ container = av.open( av.datasets.curated("pexels/time-lapse-video-of-sunset-by-the-sea-854400.mp4") ) -container.streams.video[0].thread_type = "AUTO" # Go faster! +container.streams.video[0].cctx.thread_type = "AUTO" # Go faster! columns = [] for frame in container.decode(video=0): diff --git a/tests/test_streams.py b/tests/test_streams.py index 1fd705df2..af163caae 100644 --- a/tests/test_streams.py +++ b/tests/test_streams.py @@ -114,14 +114,14 @@ def test_selection(self) -> None: ) video = container.streams.video[0] - video.thread_type = av.codec.context.ThreadType.AUTO - assert video.thread_type == av.codec.context.ThreadType.AUTO + video.cctx.thread_type = av.codec.context.ThreadType.AUTO + assert video.cctx.thread_type == av.codec.context.ThreadType.AUTO - video.thread_type = 0x03 - assert video.thread_type == av.codec.context.ThreadType.AUTO + video.cctx.thread_type = 0x03 + assert video.cctx.thread_type == av.codec.context.ThreadType.AUTO - video.thread_type = "AUTO" - assert video.thread_type == av.codec.context.ThreadType.AUTO + video.cctx.thread_type = "AUTO" + assert video.cctx.thread_type == av.codec.context.ThreadType.AUTO audio = container.streams.audio[0] @@ -482,6 +482,19 @@ def test_video_format_passthrough_deprecated() -> None: assert stream.format is not None +def test_video_thread_type_passthrough_deprecated() -> None: + with av.open(io.BytesIO(), "w", format="mp4") as output: + stream = output.add_stream("mpeg4", rate=24) + assert isinstance(stream, av.VideoStream) + + with pytest.warns(DeprecationWarning, match=r"cctx\.thread_type"): + stream.thread_type = "AUTO" + assert stream.cctx.thread_type == av.codec.context.ThreadType.AUTO + + with pytest.warns(DeprecationWarning, match=r"cctx\.thread_type"): + assert stream.thread_type == av.codec.context.ThreadType.AUTO + + @pytest.mark.filterwarnings("error::DeprecationWarning") def test_add_stream_format_layout_kwargs_not_deprecated() -> None: with av.open(io.BytesIO(), "w", format="mp4") as output: @@ -489,9 +502,13 @@ def test_add_stream_format_layout_kwargs_not_deprecated() -> None: assert astream.cctx.layout.name == "mono" vstream = output.add_stream( - "mpeg4", rate=24, format=av.VideoFormat("yuv420p", 32, 32) + "mpeg4", + rate=24, + format=av.VideoFormat("yuv420p", 32, 32), + thread_type="AUTO", ) assert vstream.cctx.width == 32 + assert vstream.cctx.thread_type == av.codec.context.ThreadType.AUTO graph = av.filter.Graph() graph.add_buffer(template=vstream, time_base=Fraction(1, 24)) From 98697e3eed5eb2d4779c843fa1536b6746cc8b16 Mon Sep 17 00:00:00 2001 From: WyattBlue Date: Tue, 27 Oct 2026 20:58:06 -0400 Subject: [PATCH 2/2] Deprecate VideoStream.thread_count passthrough VideoStream.thread_count is now a real property that warns and points to cctx, like thread_type. add_stream() keyword arguments set it on the codec context, so they don't warn. --- CHANGELOG.rst | 2 +- av/container/output.py | 6 +++--- av/stream.py | 2 +- av/video/stream.py | 23 +++++++++++++++++++++++ av/video/stream.pyi | 7 ++++++- tests/test_streams.py | 15 +++++++++++++++ 6 files changed, 49 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 7871aae70..ef9e71bb6 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -35,7 +35,7 @@ Features: - Add ``VideoFrame.solid()`` to create a frame filled with one color, in any non-paletted pixel format. - Add ``Stream.cctx`` as the name for a stream's codec context; ``Stream.codec_context`` remains as an alias. -- Deprecate ``AudioStream.format``, ``AudioStream.layout``, ``VideoStream.format``, and ``VideoStream.thread_type``, which pass through to the codec context; use ``stream.cctx.format``, ``stream.cctx.layout``, and ``stream.cctx.thread_type`` instead. Passing ``format``, ``layout``, or ``thread_type`` to ``add_stream()`` is not deprecated. +- Deprecate ``AudioStream.format``, ``AudioStream.layout``, ``VideoStream.format``, ``VideoStream.thread_count``, and ``VideoStream.thread_type``, which pass through to the codec context; use ``stream.cctx.format``, ``stream.cctx.layout``, ``stream.cctx.thread_count``, and ``stream.cctx.thread_type`` instead. Passing these to ``add_stream()`` is not deprecated. Fixes: diff --git a/av/container/output.py b/av/container/output.py index 0185e93fe..5b823d7c1 100644 --- a/av/container/output.py +++ b/av/container/output.py @@ -38,10 +38,10 @@ def _set_codecpar_extradata( @cython.cfunc def _set_stream_attrs(stream: Stream, ctx: CodecContext, kwargs: dict) -> cython.void: - # format, layout, and thread_type go straight to the codec context, since - # the stream passthroughs for them are deprecated. + # These go straight to the codec context, since the stream passthroughs + # for them are deprecated. for k, v in kwargs.items(): - if k in ("format", "layout", "thread_type"): + if k in ("format", "layout", "thread_count", "thread_type"): setattr(ctx, k, v) else: setattr(stream, k, v) diff --git a/av/stream.py b/av/stream.py index d6edf0e6c..cd6e297ba 100644 --- a/av/stream.py +++ b/av/stream.py @@ -164,7 +164,7 @@ def __setattr__(self, name, value): if name == "time_base": to_avrational(value, cython.address(self.ptr.time_base)) return - if name in ("format", "layout", "thread_type"): + if name in ("format", "layout", "thread_count", "thread_type"): prop = getattr(type(self), name, None) if prop is not None: prop.__set__(self, value) diff --git a/av/video/stream.py b/av/video/stream.py index ba994abeb..ebdc1fa68 100644 --- a/av/video/stream.py +++ b/av/video/stream.py @@ -71,6 +71,29 @@ def format(self, value): if self.cctx is not None: self.cctx.format = value + @property + def thread_count(self): + """ + The number of threads the codec context uses. + + .. deprecated:: 19.1 + Use ``stream.cctx.thread_count`` instead. + + :type: int + """ + _warn_passthrough("thread_count") + if self.cctx is None: + raise AttributeError( + f"'{type(self).__name__}' object has no attribute 'thread_count'" + ) + return self.cctx.thread_count + + @thread_count.setter + def thread_count(self, value): + _warn_passthrough("thread_count") + if self.cctx is not None: + self.cctx.thread_count = value + @property def thread_type(self): """ diff --git a/av/video/stream.pyi b/av/video/stream.pyi index 983539d70..cfe656ead 100644 --- a/av/video/stream.pyi +++ b/av/video/stream.pyi @@ -48,7 +48,12 @@ class VideoStream(Stream): @format.setter @deprecated("Use VideoStream.cctx.format instead") def format(self, value: VideoFormat) -> None: ... - thread_count: int + @property + @deprecated("Use VideoStream.cctx.thread_count instead") + def thread_count(self) -> int: ... + @thread_count.setter + @deprecated("Use VideoStream.cctx.thread_count instead") + def thread_count(self, value: int) -> None: ... @property @deprecated("Use VideoStream.cctx.thread_type instead") def thread_type(self) -> ThreadType: ... diff --git a/tests/test_streams.py b/tests/test_streams.py index af163caae..f400e9320 100644 --- a/tests/test_streams.py +++ b/tests/test_streams.py @@ -495,6 +495,19 @@ def test_video_thread_type_passthrough_deprecated() -> None: assert stream.thread_type == av.codec.context.ThreadType.AUTO +def test_video_thread_count_passthrough_deprecated() -> None: + with av.open(io.BytesIO(), "w", format="mp4") as output: + stream = output.add_stream("mpeg4", rate=24) + assert isinstance(stream, av.VideoStream) + + with pytest.warns(DeprecationWarning, match=r"cctx\.thread_count"): + stream.thread_count = 2 + assert stream.cctx.thread_count == 2 + + with pytest.warns(DeprecationWarning, match=r"cctx\.thread_count"): + assert stream.thread_count == 2 + + @pytest.mark.filterwarnings("error::DeprecationWarning") def test_add_stream_format_layout_kwargs_not_deprecated() -> None: with av.open(io.BytesIO(), "w", format="mp4") as output: @@ -506,8 +519,10 @@ def test_add_stream_format_layout_kwargs_not_deprecated() -> None: rate=24, format=av.VideoFormat("yuv420p", 32, 32), thread_type="AUTO", + thread_count=2, ) assert vstream.cctx.width == 32 + assert vstream.cctx.thread_count == 2 assert vstream.cctx.thread_type == av.codec.context.ThreadType.AUTO graph = av.filter.Graph()