Skip to content

Commit

Permalink
Sync with upstream
Browse files Browse the repository at this point in the history
  • Loading branch information
WyattBlue committed Mar 15, 2024
1 parent 716628e commit 6bdf8ed
Show file tree
Hide file tree
Showing 50 changed files with 291 additions and 1,823 deletions.
9 changes: 4 additions & 5 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- uses: actions/checkout@v4
name: Checkout
- name: Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: 3.12

Expand Down Expand Up @@ -57,7 +57,7 @@ jobs:
name: Checkout

- name: Python ${{ matrix.config.python }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.config.python }}

Expand All @@ -72,7 +72,6 @@ jobs:
libtheora-dev libvorbis-dev libx264-dev
;;
macos-latest)
brew update
brew install automake libtool nasm pkg-config shtool wget
brew install libass libjpeg libpng libvorbis libvpx opus theora x264
;;
Expand Down Expand Up @@ -148,7 +147,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
- uses: actions/setup-python@v5
with:
python-version: 3.9
- name: Build source package
Expand Down Expand Up @@ -182,7 +181,7 @@ jobs:
arch: AMD64
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
- uses: actions/setup-python@v5
with:
python-version: 3.9
- name: Set up QEMU
Expand Down
2 changes: 1 addition & 1 deletion av/about.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "12.0.4"
__version__ = "12.0.5"
6 changes: 5 additions & 1 deletion av/audio/stream.pxd
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
from av.packet cimport Packet
from av.stream cimport Stream

from .frame cimport AudioFrame


cdef class AudioStream(Stream):
pass
cpdef encode(self, AudioFrame frame=?)
cpdef decode(self, Packet packet=?)
14 changes: 11 additions & 3 deletions av/audio/stream.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,19 @@ from av.stream import Stream
from .codeccontext import AudioCodecContext
from .format import AudioFormat
from .frame import AudioFrame
from .layout import AudioLayout

class AudioStream(Stream):
type: Literal["audio"]
format: AudioFormat
codec_context: AudioCodecContext
# From codec context
frame_size: int
sample_rate: int
rate: int
channels: int
channel_layout: int
layout: AudioLayout
format: AudioFormat
type: Literal["audio"]

def encode(self, frame: AudioFrame | None = None) -> list[Packet]: ... # type: ignore[override]
def encode(self, frame: AudioFrame | None = None) -> list[Packet]: ...
def decode(self, packet: Packet | None = None) -> list[AudioFrame]: ...
35 changes: 34 additions & 1 deletion av/audio/stream.pyx
Original file line number Diff line number Diff line change
@@ -1,7 +1,40 @@
from av.packet cimport Packet

from .frame cimport AudioFrame


cdef class AudioStream(Stream):
def __repr__(self):
form = self.format.name if self.format else None
return (
f"<av.{self.__class__.__name__} #{self.index} {self.name} at {self.rate}Hz,"
f"<av.AudioStream #{self.index} {self.name} at {self.rate}Hz,"
f" {self.layout.name}, {form} at 0x{id(self):x}>"
)

cpdef encode(self, AudioFrame frame=None):
"""
Encode an :class:`.AudioFrame` and return a list of :class:`.Packet`.
:rtype: list[Packet]
.. seealso:: This is mostly a passthrough to :meth:`.CodecContext.encode`.
"""

packets = self.codec_context.encode(frame)
cdef Packet packet
for packet in packets:
packet._stream = self
packet.ptr.stream_index = self.ptr.index

return packets

cpdef decode(self, Packet packet=None):
"""
Decode a :class:`.Packet` and return a list of :class:`.AudioFrame`.
:rtype: list[AudioFrame]
.. seealso:: This is a passthrough to :meth:`.CodecContext.decode`.
"""

return self.codec_context.decode(packet)
1 change: 0 additions & 1 deletion av/container/core.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,6 @@ def open(
Honored only when ``file`` is a file-like object. Defaults to 32768 (32k).
:param timeout: How many seconds to wait for data before giving up, as a float, or a
:ref:`(open timeout, read timeout) <timeouts>` tuple.
:type timeout: float or tuple
:param callable io_open: Custom I/O callable for opening files/streams.
This option is intended for formats that need to open additional
file-like objects to ``file`` using custom I/O.
Expand Down
3 changes: 1 addition & 2 deletions av/container/input.pyi
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Any, Iterator, Literal, overload
from typing import Any, Iterator, overload

from av.audio.frame import AudioFrame
from av.audio.stream import AudioStream
Expand Down Expand Up @@ -34,7 +34,6 @@ class InputContainer(Container):
self,
offset: int,
*,
whence: Literal["time"] = "time",
backward: bool = True,
any_frame: bool = False,
stream: Stream | VideoStream | AudioStream | None = None,
Expand Down
14 changes: 4 additions & 10 deletions av/container/input.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -208,10 +208,10 @@ cdef class InputContainer(Container):
for frame in packet.decode():
yield frame

def seek(self, offset, *, str whence="time", bint backward=True,
bint any_frame=False, Stream stream=None,
bint unsupported_frame_offset=False,
bint unsupported_byte_offset=False):
def seek(
self, offset, *, bint backward=True, bint any_frame=False, Stream stream=None,
bint unsupported_frame_offset=False, bint unsupported_byte_offset=False
):
"""seek(offset, *, backward=True, any_frame=False, stream=None)
Seek to a (key)frame nearsest to the given timestamp.
Expand Down Expand Up @@ -252,12 +252,6 @@ cdef class InputContainer(Container):
cdef int flags = 0
cdef int ret

# We used to support whence in 'time', 'frame', and 'byte', but later
# realized that FFmpeg doens't implement the frame or byte ones.
# We don't even document this anymore, but do allow 'time' to pass through.
if whence != "time":
raise ValueError("whence != 'time' is no longer supported")

if backward:
flags |= lib.AVSEEK_FLAG_BACKWARD
if any_frame:
Expand Down
3 changes: 3 additions & 0 deletions av/packet.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ class Packet:
duration: int | None
is_keyframe: bool
is_corrupt: bool
is_discard: bool
is_trusted: bool
is_disposable: bool

def __init__(self, input: int | None = None) -> None: ...
def decode(self) -> Iterator[SubtitleSet]: ...
16 changes: 15 additions & 1 deletion av/packet.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -189,11 +189,25 @@ cdef class Packet(Buffer):
self.ptr.flags &= ~(lib.AV_PKT_FLAG_KEY)

@property
def is_corrupt(self): return bool(self.ptr.flags & lib.AV_PKT_FLAG_CORRUPT)
def is_corrupt(self):
return bool(self.ptr.flags & lib.AV_PKT_FLAG_CORRUPT)

@is_corrupt.setter
def is_corrupt(self, v):
if v:
self.ptr.flags |= lib.AV_PKT_FLAG_CORRUPT
else:
self.ptr.flags &= ~(lib.AV_PKT_FLAG_CORRUPT)

@property
def is_discard(self):
return bool(self.ptr.flags & lib.AV_PKT_FLAG_DISCARD)

@property
def is_trusted(self):
return bool(self.ptr.flags & lib.AV_PKT_FLAG_TRUSTED)

@property
def is_disposable(self):
return bool(self.ptr.flags & lib.AV_PKT_FLAG_DISPOSABLE)

2 changes: 0 additions & 2 deletions av/stream.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,3 @@ class Stream:
frames: int
language: str | None
type: Literal["video", "audio", "data", "subtitle", "attachment"]

def encode(self, frame: Frame | None = None) -> list[Packet]: ...
61 changes: 0 additions & 61 deletions av/stream.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -146,25 +146,6 @@ cdef class Stream:
# Lets just copy what we want.
err_check(lib.avcodec_parameters_from_context(self.ptr.codecpar, self.codec_context.ptr))

def encode(self, frame=None):
"""
Encode an :class:`.AudioFrame` or :class:`.VideoFrame` and return a list
of :class:`.Packet`.
:return: :class:`list` of :class:`.Packet`.
.. seealso:: This is mostly a passthrough to :meth:`.CodecContext.encode`.
"""
if self.codec_context is None:
raise RuntimeError("Stream.encode requires a valid CodecContext")

packets = self.codec_context.encode(frame)
cdef Packet packet
for packet in packets:
packet._stream = self
packet.ptr.stream_index = self.ptr.index
return packets

cdef _get_side_data(self, lib.AVStream *stream):
# Get DISPLAYMATRIX SideData from a lib.AVStream object.
# Returns: tuple[int, dict[str, Any]]
Expand Down Expand Up @@ -236,48 +217,6 @@ cdef class Stream:
"""
to_avrational(value, &self.ptr.time_base)

@property
def average_rate(self):
"""
The average frame rate of this video stream.
This is calculated when the file is opened by looking at the first
few frames and averaging their rate.
:type: :class:`~fractions.Fraction` or ``None``
"""
return avrational_to_fraction(&self.ptr.avg_frame_rate)

@property
def base_rate(self):
"""
The base frame rate of this stream.
This is calculated as the lowest framerate at which the timestamps of
frames can be represented accurately. See :ffmpeg:`AVStream.r_frame_rate`
for more.
:type: :class:`~fractions.Fraction` or ``None``
"""
return avrational_to_fraction(&self.ptr.r_frame_rate)

@property
def guessed_rate(self):
"""The guessed frame rate of this stream.
This is a wrapper around :ffmpeg:`av_guess_frame_rate`, and uses multiple
heuristics to decide what is "the" frame rate.
:type: :class:`~fractions.Fraction` or ``None``
"""
# The two NULL arguments aren't used in FFmpeg >= 4.0
cdef lib.AVRational val = lib.av_guess_frame_rate(NULL, self.ptr, NULL)
return avrational_to_fraction(&val)

@property
def start_time(self):
"""
Expand Down
2 changes: 1 addition & 1 deletion av/subtitles/subtitle.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@ class TextSubtitle(Subtitle):

class AssSubtitle(Subtitle):
type: Literal[b"ass"]
ass: str
ass: bytes
8 changes: 7 additions & 1 deletion av/subtitles/subtitle.pyx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
from cpython cimport PyBuffer_FillInfo


cdef extern from "Python.h":
bytes PyBytes_FromString(char*)


cdef class SubtitleProxy:
def __dealloc__(self):
lib.avsubtitle_free(&self.struct)
Expand Down Expand Up @@ -153,4 +157,6 @@ cdef class AssSubtitle(Subtitle):

@property
def ass(self):
return self.ptr.ass
if self.ptr.ass is not NULL:
return PyBytes_FromString(self.ptr.ass)
return b""
10 changes: 6 additions & 4 deletions av/video/codeccontext.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,29 @@ from typing import Iterator, Literal
from av.codec.context import CodecContext
from av.packet import Packet

from .format import VideoFormat
from .frame import VideoFrame

class VideoCodecContext(CodecContext):
format: VideoFormat
width: int
height: int
bits_per_codec_sample: int
pix_fmt: str
pix_fmt: str | None
framerate: Fraction
rate: Fraction
gop_size: int
sample_aspect_ratio: Fraction
display_aspect_ratio: Fraction
sample_aspect_ratio: Fraction | None
display_aspect_ratio: Fraction | None
has_b_frames: bool
coded_width: int
coded_height: int
color_range: int
color_primaries: int
color_trc: int
colorspace: int

type: Literal["video"]

def encode(self, frame: VideoFrame | None = None) -> list[Packet]: ...
def encode_lazy(self, frame: VideoFrame | None = None) -> Iterator[Packet]: ...
def decode(self, packet: Packet | None = None) -> list[VideoFrame]: ...
19 changes: 19 additions & 0 deletions av/video/codeccontext.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,25 @@ cdef class VideoCodecContext(CodecContext):
self.ptr.height = value
self._build_format()

@property
def bits_per_coded_sample(self):
"""
The number of bits per sample in the codedwords. It's mandatory for this to be set for some formats to decode properly.
Wraps :ffmpeg:`AVCodecContext.bits_per_coded_sample`.
:type: int
"""
return self.ptr.bits_per_coded_sample

@bits_per_coded_sample.setter
def bits_per_coded_sample(self, int value):
if self.is_encoder:
raise ValueError("Not supported for encoders")

self.ptr.bits_per_coded_sample = value
self._build_format()

@property
def pix_fmt(self):
"""
Expand Down
5 changes: 2 additions & 3 deletions av/video/frame.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ from av.frame import Frame

from .format import VideoFormat
from .plane import VideoPlane
from .reformatter import ColorRange, Colorspace

_SupportedNDarray = Union[
np.ndarray[Any, np.dtype[np.uint8]],
Expand Down Expand Up @@ -47,8 +46,8 @@ class VideoFrame(Frame):
width: int | None = None,
height: int | None = None,
format: str | None = None,
src_colorspace: Colorspace | None = None,
dst_colorspace: Colorspace | None = None,
src_colorspace: int | None = None,
dst_colorspace: int | None = None,
interpolation: int | str | None = None,
src_color_range: int | str | None = None,
dst_color_range: int | str | None = None,
Expand Down
Loading

0 comments on commit 6bdf8ed

Please sign in to comment.