Skip to content

Commit

Permalink
Sync with upstream
Browse files Browse the repository at this point in the history
  • Loading branch information
WyattBlue committed Jul 21, 2024
1 parent 5dd9db5 commit e3b0645
Show file tree
Hide file tree
Showing 31 changed files with 278 additions and 175 deletions.
12 changes: 12 additions & 0 deletions av/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,15 @@

# Backwards compatibility
AVError = FFmpegError # noqa: F405


def get_include() -> str:
"""
Returns the path to the `include` folder to be used when building extensions to av.
"""
# Installed package
include_path = os.path.join(os.path.dirname(__file__), "include")
if os.path.exists(include_path):
return include_path
# Running from source directory
return os.path.join(os.path.dirname(__file__), os.pardir, "include")
2 changes: 1 addition & 1 deletion av/about.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "12.2.0"
__version__ = "12.3.0"
9 changes: 1 addition & 8 deletions av/audio/frame.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ cdef class AudioFrame(Frame):

def __repr__(self):
return (
f"<av.{self.__class__.__name__} {self.index} pts={self.pts}, {self.samples} "
f"<av.{self.__class__.__name__} pts={self.pts}, {self.samples} "
f"samples at {self.rate}Hz, {self.layout.name}, {self.format.name} at 0x{id(self):x}"
)

Expand Down Expand Up @@ -191,10 +191,3 @@ cdef class AudioFrame(Frame):
count = self.samples * len(self.layout.channels)

return np.vstack([np.frombuffer(x, dtype=dtype, count=count) for x in self.planes])

def __getattribute__(self, attribute):
# This method should be deleted when `frame.index` is removed
if attribute == "index":
warnings.warn("Using `frame.index` is deprecated.", AVDeprecationWarning)

return Frame.__getattribute__(self, attribute)
4 changes: 2 additions & 2 deletions av/audio/resampler.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ class AudioResampler:

def __init__(
self,
format: AudioFormat | None = None,
layout: AudioLayout | None = None,
format: str | int | AudioFormat | None = None,
layout: str | int | AudioLayout | None = None,
rate: int | None = None,
frame_size: int | None = None,
) -> None: ...
Expand Down
109 changes: 32 additions & 77 deletions av/codec/context.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ cdef CodecContext wrap_codec_context(lib.AVCodecContext *c_ctx, const lib.AVCode

cdef CodecContext py_ctx

# TODO: This.
if c_ctx.codec_type == lib.AVMEDIA_TYPE_VIDEO:
from av.video.codeccontext import VideoCodecContext
py_ctx = VideoCodecContext(_cinit_sentinel)
Expand All @@ -45,69 +44,46 @@ cdef CodecContext wrap_codec_context(lib.AVCodecContext *c_ctx, const lib.AVCode

ThreadType = define_enum("ThreadType", __name__, (
("NONE", 0),
("FRAME", lib.FF_THREAD_FRAME,
"""Decode more than one frame at once"""),
("SLICE", lib.FF_THREAD_SLICE,
"""Decode more than one part of a single frame at once"""),
("AUTO", lib.FF_THREAD_SLICE | lib.FF_THREAD_FRAME,
"""Decode using both FRAME and SLICE methods."""),
("FRAME", lib.FF_THREAD_FRAME, "Decode more than one frame at once"),
("SLICE", lib.FF_THREAD_SLICE, "Decode more than one part of a single frame at once"),
("AUTO", lib.FF_THREAD_SLICE | lib.FF_THREAD_FRAME, "Decode using both FRAME and SLICE methods."),
), is_flags=True)

SkipType = define_enum("SkipType", __name__, (
("NONE", lib.AVDISCARD_NONE,
"""Discard nothing"""),
("DEFAULT", lib.AVDISCARD_DEFAULT,
"""Discard useless packets like 0 size packets in AVI"""),
("NONREF", lib.AVDISCARD_NONREF,
"""Discard all non reference"""),
("BIDIR", lib.AVDISCARD_BIDIR,
"""Discard all bidirectional frames"""),
("NONINTRA", lib.AVDISCARD_NONINTRA,
"""Discard all non intra frames"""),
("NONKEY", lib.AVDISCARD_NONKEY,
"""Discard all frames except keyframes"""),
("ALL", lib.AVDISCARD_ALL,
"""Discard all"""),
("NONE", lib.AVDISCARD_NONE, "Discard nothing"),
("DEFAULT", lib.AVDISCARD_DEFAULT, "Discard useless packets like 0 size packets in AVI"),
("NONREF", lib.AVDISCARD_NONREF, "Discard all non reference"),
("BIDIR", lib.AVDISCARD_BIDIR, "Discard all bidirectional frames"),
("NONINTRA", lib.AVDISCARD_NONINTRA, "Discard all non intra frames"),
("NONKEY", lib.AVDISCARD_NONKEY, "Discard all frames except keyframes"),
("ALL", lib.AVDISCARD_ALL, "Discard all"),
))

Flags = define_enum("Flags", __name__, (
("NONE", 0),
("UNALIGNED", lib.AV_CODEC_FLAG_UNALIGNED,
"""Allow decoders to produce frames with data planes that are not aligned
to CPU requirements (e.g. due to cropping)."""),
("QSCALE", lib.AV_CODEC_FLAG_QSCALE,
"""Use fixed qscale."""),
("4MV", lib.AV_CODEC_FLAG_4MV,
"""4 MV per MB allowed / advanced prediction for H.263."""),
("OUTPUT_CORRUPT", lib.AV_CODEC_FLAG_OUTPUT_CORRUPT,
"""Output even those frames that might be corrupted."""),
("QPEL", lib.AV_CODEC_FLAG_QPEL,
"""Use qpel MC."""),
"Allow decoders to produce frames with data planes that are not aligned to CPU requirements (e.g. due to cropping)."
),
("QSCALE", lib.AV_CODEC_FLAG_QSCALE, "Use fixed qscale."),
("4MV", lib.AV_CODEC_FLAG_4MV, "4 MV per MB allowed / advanced prediction for H.263."),
("OUTPUT_CORRUPT", lib.AV_CODEC_FLAG_OUTPUT_CORRUPT, "Output even those frames that might be corrupted."),
("QPEL", lib.AV_CODEC_FLAG_QPEL, "Use qpel MC."),
("DROPCHANGED", 1 << 5,
"""Don't output frames whose parameters differ from first
decoded frame in stream."""),
("PASS1", lib.AV_CODEC_FLAG_PASS1,
"""Use internal 2pass ratecontrol in first pass mode."""),
("PASS2", lib.AV_CODEC_FLAG_PASS2,
"""Use internal 2pass ratecontrol in second pass mode."""),
("LOOP_FILTER", lib.AV_CODEC_FLAG_LOOP_FILTER,
"""loop filter."""),
("GRAY", lib.AV_CODEC_FLAG_GRAY,
"""Only decode/encode grayscale."""),
("PSNR", lib.AV_CODEC_FLAG_PSNR,
"""error[?] variables will be set during encoding."""),
("INTERLACED_DCT", lib.AV_CODEC_FLAG_INTERLACED_DCT,
"""Use interlaced DCT."""),
("LOW_DELAY", lib.AV_CODEC_FLAG_LOW_DELAY,
"""Force low delay."""),
"Don't output frames whose parameters differ from first decoded frame in stream."
),
("PASS1", lib.AV_CODEC_FLAG_PASS1, "Use internal 2pass ratecontrol in first pass mode."),
("PASS2", lib.AV_CODEC_FLAG_PASS2, "Use internal 2pass ratecontrol in second pass mode."),
("LOOP_FILTER", lib.AV_CODEC_FLAG_LOOP_FILTER, "loop filter."),
("GRAY", lib.AV_CODEC_FLAG_GRAY, "Only decode/encode grayscale."),
("PSNR", lib.AV_CODEC_FLAG_PSNR, "error[?] variables will be set during encoding."),
("INTERLACED_DCT", lib.AV_CODEC_FLAG_INTERLACED_DCT, "Use interlaced DCT."),
("LOW_DELAY", lib.AV_CODEC_FLAG_LOW_DELAY, "Force low delay."),
("GLOBAL_HEADER", lib.AV_CODEC_FLAG_GLOBAL_HEADER,
"""Place global headers in extradata instead of every keyframe."""),
("BITEXACT", lib.AV_CODEC_FLAG_BITEXACT,
"""Use only bitexact stuff (except (I)DCT)."""),
("AC_PRED", lib.AV_CODEC_FLAG_AC_PRED,
"""H.263 advanced intra coding / MPEG-4 AC prediction"""),
("INTERLACED_ME", lib.AV_CODEC_FLAG_INTERLACED_ME,
"""Interlaced motion estimation"""),
"Place global headers in extradata instead of every keyframe."
),
("BITEXACT", lib.AV_CODEC_FLAG_BITEXACT, "Use only bitexact stuff (except (I)DCT)."),
("AC_PRED", lib.AV_CODEC_FLAG_AC_PRED, "H.263 advanced intra coding / MPEG-4 AC prediction"),
("INTERLACED_ME", lib.AV_CODEC_FLAG_INTERLACED_ME, "Interlaced motion estimation"),
("CLOSED_GOP", lib.AV_CODEC_FLAG_CLOSED_GOP),
), is_flags=True)

Expand Down Expand Up @@ -168,11 +144,7 @@ cdef class CodecContext:
def _set_flags(self, value):
self.ptr.flags = value

flags = Flags.property(
_get_flags,
_set_flags,
"""Flag property of :class:`.Flags`."""
)
flags = Flags.property(_get_flags, _set_flags, "Flag property of :class:`.Flags`.")

unaligned = flags.flag_property("UNALIGNED")
qscale = flags.flag_property("QSCALE")
Expand All @@ -199,12 +171,7 @@ cdef class CodecContext:
def _set_flags2(self, value):
self.ptr.flags2 = value

flags2 = Flags2.property(
_get_flags2,
_set_flags2,
"""Flag property of :class:`.Flags2`."""
)

flags2 = Flags2.property(_get_flags2, _set_flags2, "Flag property of :class:`.Flags2`.")
fast = flags2.flag_property("FAST")
no_output = flags2.flag_property("NO_OUTPUT")
local_header = flags2.flag_property("LOCAL_HEADER")
Expand Down Expand Up @@ -261,14 +228,6 @@ cdef class CodecContext:
raise ValueError("CodecContext is already open.")
return

# We might pass partial frames.
# TODO: What is this for?! This is causing problems with raw decoding
# as the internal parser doesn't seem to see a frame until it sees
# the next one.
# if self.codec.ptr.capabilities & lib.CODEC_CAP_TRUNCATED:
# self.ptr.flags |= lib.CODEC_FLAG_TRUNCATED

# TODO: Do this better.
cdef _Dictionary options = Dictionary()
options.update(self.options or {})

Expand Down Expand Up @@ -377,7 +336,6 @@ cdef class CodecContext:
in_size -= consumed

if not in_size:
# Aaaand now we're done.
break

return packets
Expand Down Expand Up @@ -522,7 +480,6 @@ cdef class CodecContext:
lib.avcodec_flush_buffers(self.ptr)

cdef _setup_decoded_frame(self, Frame frame, Packet packet):

# Propagate our manual times.
# While decoding, frame times are in stream time_base, which PyAV
# is carrying around.
Expand All @@ -531,8 +488,6 @@ cdef class CodecContext:
if packet is not None:
frame._time_base = packet._time_base

frame.index = self.ptr.frame_number - 1

@property
def name(self):
return self.codec.name
Expand Down
2 changes: 1 addition & 1 deletion av/container/core.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ def open(
:param int buffer_size: Size of buffer for Python input/output operations in bytes.
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.
:ref:`(open timeout, read timeout)` 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
8 changes: 8 additions & 0 deletions av/container/output.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,11 @@ class OutputContainer(Container):
def close(self) -> None: ...
def mux(self, packets: Packet | Sequence[Packet]) -> None: ...
def mux_one(self, packet: Packet) -> None: ...
@property
def default_video_codec(self) -> str: ...
@property
def default_audio_codec(self) -> str: ...
@property
def default_subtitle_codec(self) -> str: ...
@property
def supported_codecs(self) -> set[str]: ...
43 changes: 43 additions & 0 deletions av/container/output.pyx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import logging
import os

cimport libav as lib

from av.codec.codec cimport Codec
from av.codec.context cimport CodecContext, wrap_codec_context
from av.container.streams cimport StreamContainer
Expand Down Expand Up @@ -192,6 +194,47 @@ cdef class OutputContainer(Container):

self._started = True

@property
def supported_codecs(self):
"""
Returns a set of all codecs this format supports.
"""
result = set()
cdef const lib.AVCodec *codec = NULL
cdef void *opaque = NULL

while True:
codec = lib.av_codec_iterate(&opaque)
if codec == NULL:
break

if lib.avformat_query_codec(self.ptr.oformat, codec.id, lib.FF_COMPLIANCE_NORMAL) == 1:
result.add(codec.name)

return result


@property
def default_video_codec(self):
"""
Returns the default video codec this container recommends.
"""
return lib.avcodec_get_name(self.format.optr.video_codec)

@property
def default_audio_codec(self):
"""
Returns the default audio codec this container recommends.
"""
return lib.avcodec_get_name(self.format.optr.audio_codec)

@property
def default_subtitle_codec(self):
"""
Returns the default subtitle codec this container recommends.
"""
return lib.avcodec_get_name(self.format.optr.subtitle_codec)

def close(self):
for stream in self.streams:
if stream.codec_context:
Expand Down
1 change: 1 addition & 0 deletions av/filter/graph.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class Graph:

def __init__(self) -> None: ...
def configure(self, auto_buffer: bool = True, force: bool = False) -> None: ...
def link_nodes(self, *nodes: FilterContext) -> Graph: ...
def add(
self, filter: str | Filter, args: Any = None, **kwargs: str
) -> FilterContext: ...
Expand Down
9 changes: 8 additions & 1 deletion av/filter/graph.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ cdef class Graph:
# We get auto-inserted stuff here.
self._auto_register()

def link_nodes(self, *nodes):
"""
Links nodes together for simple filter graphs.
"""
for c, n in zip(nodes, nodes[1:]):
c.link_to(n)
return self

def add(self, filter, args=None, **kwargs):
cdef Filter cy_filter
Expand All @@ -68,7 +75,7 @@ cdef class Graph:

# There might have been automatic contexts added (e.g. resamplers,
# fifos, and scalers). It is more likely to see them after the graph
# is configured, but we wan't to be safe.
# is configured, but we want to be safe.
self._auto_register()

return ctx
Expand Down
4 changes: 4 additions & 0 deletions av/format.pyi
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
__all__ = ("ContainerFormat", "formats_available")

from typing import Literal

from .enum import EnumFlag

class Flags(EnumFlag):
Expand All @@ -22,10 +24,12 @@ class Flags(EnumFlag):
SEEK_TO_PTS: int

class ContainerFormat:
def __init__(self, name: str, mode: Literal["r", "w"] | None = None) -> None: ...
name: str
long_name: str
is_input: bool
is_output: bool
extensions: set[str]

# flags
no_file: int
Expand Down
7 changes: 0 additions & 7 deletions av/frame.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,10 @@ from av.sidedata.sidedata cimport _SideDataContainer


cdef class Frame:

cdef lib.AVFrame *ptr

# We define our own time.
cdef lib.AVRational _time_base
cdef _rebase_time(self, lib.AVRational)

cdef _SideDataContainer _side_data

cdef readonly int index

cdef _copy_internal_attributes(self, Frame source, bint data_layout=?)

cdef _init_user_attributes(self)
3 changes: 1 addition & 2 deletions av/frame.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,10 @@ cdef class Frame:
lib.av_frame_free(&self.ptr)

def __repr__(self):
return f"av.{self.__class__.__name__} #{self.index} pts={self.pts} at 0x{id(self):x}>"
return f"av.{self.__class__.__name__} pts={self.pts} at 0x{id(self):x}>"

cdef _copy_internal_attributes(self, Frame source, bint data_layout=True):
"""Mimic another frame."""
self.index = source.index
self._time_base = source._time_base
lib.av_frame_copy_props(self.ptr, source.ptr)
if data_layout:
Expand Down
1 change: 1 addition & 0 deletions av/logging.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ CRITICAL: int
def adapt_level(level: int) -> int: ...
def get_level() -> int | None: ...
def set_level(level: int | None) -> None: ...
def set_libav_level(level: int) -> None: ...
def restore_default_callback() -> None: ...
def get_skip_repeated() -> bool: ...
def set_skip_repeated(v: bool) -> None: ...
Expand Down
Loading

0 comments on commit e3b0645

Please sign in to comment.