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
File renamed without changes.
5 changes: 1 addition & 4 deletions av/audio/resampler.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ from av.filter.graph cimport Graph


cdef class AudioResampler:

cdef readonly bint is_passthrough

cdef AudioFrame template

# Destination descriptors
Expand All @@ -17,5 +15,4 @@ cdef class AudioResampler:
cdef readonly unsigned int frame_size

cdef Graph graph

cpdef resample(self, AudioFrame)
cpdef list resample(self, AudioFrame)
53 changes: 28 additions & 25 deletions av/audio/resampler.pyx → av/audio/resampler.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,38 @@
from av.filter.context cimport FilterContext
from errno import EAGAIN

import errno
import cython
from cython.cimports.av.filter.context import FilterContext
from cython.cimports.av.filter.graph import Graph

import av.filter
from av.error import FFmpegError


cdef class AudioResampler:

@cython.cclass
class AudioResampler:
"""AudioResampler(format=None, layout=None, rate=None)

:param AudioFormat format: The target format, or string that parses to one
(e.g. ``"s16"``).
:param AudioLayout layout: The target layout, or an int/string that parses
to one (e.g. ``"stereo"``).
:param int rate: The target sample rate.


"""

def __cinit__(self, format=None, layout=None, rate=None, frame_size=None):
if format is not None:
self.format = format if isinstance(format, AudioFormat) else AudioFormat(format)
self.format = (
format if isinstance(format, AudioFormat) else AudioFormat(format)
)

if layout is not None:
self.layout = AudioLayout(layout)
self.rate = int(rate) if rate else 0

self.rate = int(rate) if rate else 0
self.frame_size = int(frame_size) if frame_size else 0

self.graph = None

cpdef resample(self, AudioFrame frame):
@cython.ccall
def resample(self, frame: AudioFrame | None) -> list:
"""resample(frame)

Convert the ``sample_rate``, ``channel_layout`` and/or ``format`` of
Expand Down Expand Up @@ -60,30 +62,31 @@ def __cinit__(self, format=None, layout=None, rate=None, frame_size=None):

# Check if we can passthrough or if there is actually work to do.
if (
frame.format.sample_fmt == self.format.sample_fmt and
frame.layout == self.layout and
frame.sample_rate == self.rate and
self.frame_size == 0
frame.format.sample_fmt == self.format.sample_fmt
and frame.layout == self.layout
and frame.sample_rate == self.rate
and self.frame_size == 0
):
self.is_passthrough = True
return [frame]

# handle resampling with aformat filter
# (similar to configure_output_audio_filter from ffmpeg)
self.graph = av.filter.Graph()
self.graph = Graph()
extra_args = {}
if frame.time_base is not None:
extra_args["time_base"] = str(frame.time_base)
extra_args["time_base"] = f"{frame.time_base}"

abuffer = self.graph.add(
"abuffer",
sample_rate=str(frame.sample_rate),
sample_rate=f"{frame.sample_rate}",
sample_fmt=AudioFormat(frame.format).name,
channel_layout=frame.layout.name,
**extra_args,
)
aformat = self.graph.add(
"aformat",
sample_rates=str(self.rate),
sample_rates=f"{self.rate}",
sample_fmts=self.format.name,
channel_layouts=self.layout.name,
)
Expand All @@ -97,22 +100,22 @@ def __cinit__(self, format=None, layout=None, rate=None, frame_size=None):

if frame is not None:
if (
frame.format.sample_fmt != self.template.format.sample_fmt or
frame.layout != self.template.layout or
frame.sample_rate != self.template.rate
frame.format.sample_fmt != self.template.format.sample_fmt
or frame.layout != self.template.layout
or frame.sample_rate != self.template.rate
):
raise ValueError("Frame does not match AudioResampler setup.")

self.graph.push(frame)

output = []
output: list = []
while True:
try:
output.append(self.graph.pull())
except EOFError:
break
except av.FFmpegError as e:
if e.errno != errno.EAGAIN:
except FFmpegError as e:
if e.errno != EAGAIN:
raise
break

Expand Down
Loading