Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ v19.1.0 (Unreleased)
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.

Fixes:

Expand Down
74 changes: 66 additions & 8 deletions av/audio/stream.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import warnings

import cython
from cython.cimports import libav as lib
from cython.cimports.av.audio.frame import AudioFrame
from cython.cimports.av.packet import Packet


def _warn_passthrough(name):
warnings.warn(
f"AudioStream.{name} is deprecated; use AudioStream.cctx.{name} instead",
DeprecationWarning,
# Cython functions have no frames, so 1 is the caller.
stacklevel=1,
)


@cython.final
@cython.cclass
class AudioStream(Stream):
Expand All @@ -13,20 +24,67 @@ def __repr__(self):
f"<av.{self.__class__.__name__} (container closed) at 0x{id(self):x}>"
)

if self.codec_context is None:
if self.cctx is None:
return f"<av.AudioStream #{self.index} audio/<nocodec> at 0x{id(self):x}>"
form = self.format.name if self.format else None
cc = self.cctx
form = cc.format.name if cc.format else None
return (
f"<av.AudioStream #{self.index} {self.name} at {self.rate}Hz,"
f" {self.layout.name}, {form} at 0x{id(self):x}>"
f" {cc.layout.name}, {form} at 0x{id(self):x}>"
)

def __getattr__(self, name):
if self.codec_context is None:
if self.cctx is None:
raise AttributeError(
f"'{type(self).__name__}' object has no attribute '{name}'"
)
return getattr(self.codec_context, name)
return getattr(self.cctx, name)

@property
def format(self):
"""
The audio sample format of the codec context.

.. deprecated:: 19.1
Use ``stream.cctx.format`` instead.

:type: AudioFormat | None
"""
_warn_passthrough("format")
if self.cctx is None:
raise AttributeError(
f"'{type(self).__name__}' object has no attribute 'format'"
)
return self.cctx.format

@format.setter
def format(self, value):
_warn_passthrough("format")
if self.cctx is not None:
self.cctx.format = value

@property
def layout(self):
"""
The audio channel layout of the codec context.

.. deprecated:: 19.1
Use ``stream.cctx.layout`` instead.

:type: AudioLayout
"""
_warn_passthrough("layout")
if self.cctx is None:
raise AttributeError(
f"'{type(self).__name__}' object has no attribute 'layout'"
)
return self.cctx.layout

@layout.setter
def layout(self, value):
_warn_passthrough("layout")
if self.cctx is not None:
self.cctx.layout = value

@cython.ccall
def encode(self, frame: AudioFrame | None = None):
Expand All @@ -38,7 +96,7 @@ def encode(self, frame: AudioFrame | None = None):
.. seealso:: This is mostly a passthrough to :meth:`.CodecContext.encode`.
"""
self._assert_has_codec_context(lib.AVERROR_ENCODER_NOT_FOUND)
packets = self.codec_context.encode(frame)
packets = self.cctx.encode(frame)
packet: Packet
for packet in packets:
packet._stream = self
Expand All @@ -54,7 +112,7 @@ def encode_lazy(self, frame: AudioFrame | None = None):
"""
self._assert_has_codec_context(lib.AVERROR_ENCODER_NOT_FOUND)
packet: Packet
for packet in self.codec_context.encode_lazy(frame):
for packet in self.cctx.encode_lazy(frame):
packet._stream = self
packet.ptr.stream_index = self.ptr.index
yield packet
Expand All @@ -69,4 +127,4 @@ def decode(self, packet: Packet | None = None):
.. seealso:: This is a passthrough to :meth:`.CodecContext.decode`.
"""
self._assert_has_codec_context()
return self.codec_context.decode(packet)
return self.cctx.decode(packet)
7 changes: 7 additions & 0 deletions av/audio/stream.pyi
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from collections.abc import Iterator
from typing import Any, Literal, final

from typing_extensions import deprecated

from av.codec.codec import Codec
from av.packet import Packet
from av.stream import Stream
Expand All @@ -12,6 +14,7 @@ from .layout import AudioLayout

@final
class AudioStream(Stream):
cctx: AudioCodecContext
codec_context: AudioCodecContext
def encode(self, frame: AudioFrame | None = None) -> list[Packet[AudioStream]]: ...
def encode_lazy(
Expand All @@ -32,10 +35,14 @@ class AudioStream(Stream):
channels: int
type: Literal["audio"]
@property
@deprecated("Use AudioStream.cctx.format instead")
def format(self) -> AudioFormat | None: ...
@format.setter
@deprecated("Use AudioStream.cctx.format instead")
def format(self, value: AudioFormat | str) -> None: ...
@property
@deprecated("Use AudioStream.cctx.layout instead")
def layout(self) -> AudioLayout: ...
@layout.setter
@deprecated("Use AudioStream.cctx.layout instead")
def layout(self, value: AudioLayout | str) -> None: ...
4 changes: 2 additions & 2 deletions av/bitstream.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ def __cinit__(
err_check(res)
# codecpar carries everything the muxer needs; a mux-only stream
# (add_mux_stream) has no context to keep in sync.
if out_stream.codec_context is not None:
if out_stream.cctx is not None:
lib.avcodec_parameters_to_context(
out_stream.codec_context.ptr, out_stream.ptr.codecpar
out_stream.cctx.ptr, out_stream.ptr.codecpar
)

def __dealloc__(self):
Expand Down
2 changes: 1 addition & 1 deletion av/container/input.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,6 @@ def flush_buffers(self) -> cython.void:
codec_context: CodecContext

for stream in self.streams:
codec_context = stream.codec_context
codec_context = stream.cctx
if codec_context:
codec_context.flush_buffers()
29 changes: 19 additions & 10 deletions av/container/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,17 @@ def _set_codecpar_extradata(
stream.codecpar.extradata_size = size


@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.
for k, v in kwargs.items():
if k == "format" or k == "layout":
setattr(ctx, k, v)
else:
setattr(stream, k, v)


@cython.cfunc
def arm_write_timeout(self: OutputContainer) -> cython.void:
"""Start the write deadline, unless the output is a seekable file.
Expand Down Expand Up @@ -257,8 +268,7 @@ def add_stream(
if options:
py_stream.options.update(options)

for k, v in kwargs.items():
setattr(py_stream, k, v)
_set_stream_attrs(py_stream, py_codec_context, kwargs)

return py_stream

Expand Down Expand Up @@ -366,14 +376,14 @@ def add_stream_from_template(
if opaque is None:
opaque = template.type != "video"

if template.codec_context is None:
if template.cctx is None:
return self._add_stream_without_codec_from_template(template, **kwargs)

codec_obj: Codec
if opaque: # Copy ctx from template.
codec_obj = template.codec_context.codec
codec_obj = template.cctx.codec
else: # Construct new codec object.
codec_obj = Codec(template.codec_context.codec.name, "w")
codec_obj = Codec(template.cctx.codec.name, "w")

codec: cython.pointer[cython.const[lib.AVCodec]] = codec_obj.ptr

Expand Down Expand Up @@ -410,7 +420,7 @@ def add_stream_from_template(
# Copy flags If we're creating a new codec object. This fixes some
# muxing issues. Overwriting the flag set just above is intentional.
if not opaque:
ctx.flags = template.codec_context.flags
ctx.flags = template.cctx.flags

# Initialize stream codec parameters to populate the codec type.
# Subsequent changes to the codec context will be applied just
Expand All @@ -427,8 +437,7 @@ def add_stream_from_template(
py_stream: Stream = wrap_stream(self, stream, py_codec_context)
self.streams.add_stream(py_stream)

for k, v in kwargs.items():
setattr(py_stream, k, v)
_set_stream_attrs(py_stream, py_codec_context, kwargs)

return py_stream

Expand Down Expand Up @@ -602,7 +611,7 @@ def start_encoding(self):

# Finalize and open all streams.
for stream in self.streams:
ctx = stream.codec_context
ctx = stream.cctx
if ctx is not None and not ctx.is_open:
for k, v in self.options.items():
ctx.options.setdefault(k, v)
Expand Down Expand Up @@ -798,7 +807,7 @@ def _buffer_for_extradata(self, packet: Packet):
stream: Stream
for stream in self.streams:
if (
stream.codec_context is not None
stream.cctx is not None
or stream.ptr.codecpar.extradata != cython.NULL
):
continue
Expand Down
13 changes: 10 additions & 3 deletions av/filter/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@
from cython.cimports.av.video.frame import VideoFrame


@cython.cfunc
def _template_ctx(template):
# A stream's format and layout passthroughs are deprecated, so read them
# from its codec context. Frames and codec contexts are used directly.
return getattr(template, "cctx", template)


@cython.final
@cython.cclass
class Graph:
Expand Down Expand Up @@ -174,7 +181,7 @@ def add_buffer(
if height is None:
height = template.height
if format is None:
format = template.format
format = _template_ctx(template).format
if time_base is None:
time_base = template.time_base

Expand Down Expand Up @@ -219,9 +226,9 @@ def add_abuffer(
if sample_rate is None:
sample_rate = template.sample_rate
if format is None:
format = template.format
format = _template_ctx(template).format
if layout is None:
layout = template.layout.name
layout = _template_ctx(template).layout.name
if channels is None:
channels = template.channels
if time_base is None:
Expand Down
2 changes: 1 addition & 1 deletion av/stream.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ cdef class Stream:
cdef lib.AVStream *ptr
cdef readonly Container container
cdef readonly dict metadata
cdef readonly CodecContext codec_context
cdef readonly CodecContext cctx
cdef void _init(self, Container, lib.AVStream*, CodecContext)
cdef bint _is_open(self)
cdef void _assert_open(self)
Expand Down
Loading
Loading