Skip to content

Commit

Permalink
Make ffmpeg 7 compile
Browse files Browse the repository at this point in the history
  • Loading branch information
WyattBlue committed Jul 25, 2024
1 parent 4cc61f0 commit d436c10
Show file tree
Hide file tree
Showing 19 changed files with 174 additions and 259 deletions.
61 changes: 27 additions & 34 deletions av/audio/codeccontext.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,6 @@ cdef class AudioCodecContext(CodecContext):
cdef _init(self, lib.AVCodecContext *ptr, const lib.AVCodec *codec):
CodecContext._init(self, ptr, codec)

# Sometimes there isn't a layout set, but there are a number of
# channels. Assume it is the default layout.
# TODO: Put this behind `not bare_metal`.
# TODO: Do this more efficiently.
if self.ptr.channels and not self.ptr.channel_layout:
self.ptr.channel_layout = get_audio_layout(self.ptr.channels, 0).layout

cdef _set_default_time_base(self):
self.ptr.time_base.num = 1
self.ptr.time_base.den = self.ptr.sample_rate
Expand Down Expand Up @@ -85,33 +78,33 @@ cdef class AudioCodecContext(CodecContext):
def rate(self, value):
self.sample_rate = value

# TODO: Integrate into AudioLayout.
@property
def channels(self):
return self.ptr.channels

@channels.setter
def channels(self, value):
self.ptr.channels = value
self.ptr.channel_layout = lib.av_get_default_channel_layout(value)
@property
def channel_layout(self):
return self.ptr.channel_layout

@property
def layout(self):
"""
The audio channel layout.
:type: AudioLayout
"""
return get_audio_layout(self.ptr.channels, self.ptr.channel_layout)

@layout.setter
def layout(self, value):
cdef AudioLayout layout = AudioLayout(value)
self.ptr.channel_layout = layout.layout
self.ptr.channels = layout.nb_channels
# # TODO: Integrate into AudioLayout.
# @property
# def channels(self):
# return self.ptr.channels

# @channels.setter
# def channels(self, value):
# self.ptr.channels = value
# self.ptr.channel_layout = lib.av_get_default_channel_layout(value)
# @property
# def channel_layout(self):
# return self.ptr.channel_layout

# @property
# def layout(self):
# """
# The audio channel layout.

# :type: AudioLayout
# """
# return get_audio_layout(self.ptr.channels, self.ptr.channel_layout)

# @layout.setter
# def layout(self, value):
# cdef AudioLayout layout = AudioLayout(value)
# self.ptr.channel_layout = layout.layout
# self.ptr.channels = layout.nb_channels

@property
def format(self):
Expand Down
4 changes: 2 additions & 2 deletions av/audio/fifo.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ cdef class AudioFifo:
# Make sure nothing changed.
elif (
frame.ptr.format != self.template.ptr.format or
frame.ptr.channel_layout != self.template.ptr.channel_layout or
# TODO: frame.ptr.ch_layout != self.template.ptr.ch_layout or
frame.ptr.sample_rate != self.template.ptr.sample_rate or
(frame._time_base.num and self.template._time_base.num and (
frame._time_base.num != self.template._time_base.num or
Expand Down Expand Up @@ -131,7 +131,7 @@ cdef class AudioFifo:
frame._copy_internal_attributes(self.template)
frame._init(
<lib.AVSampleFormat>self.template.ptr.format,
self.template.ptr.channel_layout,
<lib.AVChannelLayout>self.template.ptr.ch_layout,
samples,
1, # Align?
)
Expand Down
2 changes: 1 addition & 1 deletion av/audio/frame.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ cdef class AudioFrame(Frame):
:type: AudioFormat
"""

cdef _init(self, lib.AVSampleFormat format, uint64_t layout, unsigned int nb_samples, unsigned int align)
cdef _init(self, lib.AVSampleFormat format, lib.AVChannelLayout layout, unsigned int nb_samples, unsigned int align)
cdef _init_user_attributes(self)

cdef AudioFrame alloc_audio_frame()
67 changes: 32 additions & 35 deletions av/audio/frame.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,15 @@ cdef class AudioFrame(Frame):
cdef AudioLayout cy_layout = AudioLayout(layout)
self._init(cy_format.sample_fmt, cy_layout.layout, samples, align)

cdef _init(self, lib.AVSampleFormat format, uint64_t layout, unsigned int nb_samples, unsigned int align):
cdef _init(self, lib.AVSampleFormat format, lib.AVChannelLayout layout, unsigned int nb_samples, unsigned int align):

self.ptr.nb_samples = nb_samples
self.ptr.format = <int>format
self.ptr.channel_layout = layout
self.ptr.ch_layout = layout

# Sometimes this is called twice. Oh well.
self._init_user_attributes()

# Audio filters need AVFrame.channels to match number of channels from layout.
self.ptr.channels = self.layout.nb_channels

if self.layout.channels and nb_samples:
# Cleanup the old buffer.
lib.av_freep(&self._buffer)
Expand Down Expand Up @@ -89,7 +86,7 @@ cdef class AudioFrame(Frame):
lib.av_freep(&self._buffer)

cdef _init_user_attributes(self):
self.layout = get_audio_layout(0, self.ptr.channel_layout)
self.layout = get_audio_layout(0, self.ptr.ch_layout)
self.format = get_audio_format(<lib.AVSampleFormat>self.ptr.format)

def __repr__(self):
Expand All @@ -98,35 +95,35 @@ cdef class AudioFrame(Frame):
f"samples at {self.rate}Hz, {self.layout.name}, {self.format.name} at 0x{id(self):x}"
)

@staticmethod
def from_ndarray(array, format="s16", layout="stereo"):
"""
Construct a frame from a numpy array.
"""
import numpy as np

# map avcodec type to numpy type
try:
dtype = np.dtype(format_dtypes[format])
except KeyError:
raise ValueError(
f"Conversion from numpy array with format `{format}` is not yet supported"
)

# check input format
nb_channels = len(AudioLayout(layout).channels)
check_ndarray(array, dtype, 2)
if AudioFormat(format).is_planar:
check_ndarray_shape(array, array.shape[0] == nb_channels)
samples = array.shape[1]
else:
check_ndarray_shape(array, array.shape[0] == 1)
samples = array.shape[1] // nb_channels

frame = AudioFrame(format=format, layout=layout, samples=samples)
for i, plane in enumerate(frame.planes):
plane.update(array[i, :])
return frame
# @staticmethod
# def from_ndarray(array, format="s16", layout="stereo"):
# """
# Construct a frame from a numpy array.
# """
# import numpy as np

# # map avcodec type to numpy type
# try:
# dtype = np.dtype(format_dtypes[format])
# except KeyError:
# raise ValueError(
# f"Conversion from numpy array with format `{format}` is not yet supported"
# )

# # check input format
# nb_channels = len(AudioLayout(layout).channels)
# check_ndarray(array, dtype, 2)
# if AudioFormat(format).is_planar:
# check_ndarray_shape(array, array.shape[0] == nb_channels)
# samples = array.shape[1]
# else:
# check_ndarray_shape(array, array.shape[0] == 1)
# samples = array.shape[1] // nb_channels

# frame = AudioFrame(format=format, layout=layout, samples=samples)
# for i, plane in enumerate(frame.planes):
# plane.update(array[i, :])
# return frame

@property
def planes(self):
Expand Down
25 changes: 4 additions & 21 deletions av/audio/layout.pxd
Original file line number Diff line number Diff line change
@@ -1,26 +1,9 @@
from libc.stdint cimport uint64_t
cimport libav as lib


cdef class AudioLayout:

# The layout for FFMpeg; this is essentially a bitmask of channels.
cdef uint64_t layout
cdef lib.AVChannelLayout layout
cdef int nb_channels
cdef _init(self, lib.AVChannelLayout layout)

cdef readonly tuple channels
"""
A tuple of :class:`AudioChannel` objects.
:type: tuple
"""

cdef _init(self, uint64_t layout)


cdef class AudioChannel:

# The channel for FFmpeg.
cdef uint64_t channel


cdef AudioLayout get_audio_layout(int channels, uint64_t c_layout)
cdef AudioLayout get_audio_layout(int channels, lib.AVChannelLayout c_layout)
2 changes: 0 additions & 2 deletions av/audio/layout.pyi
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
channel_descriptions: dict[str, str]

class AudioLayout:
name: str
layout: int
Expand Down
103 changes: 18 additions & 85 deletions av/audio/layout.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -3,118 +3,51 @@ cimport libav as lib

cdef object _cinit_bypass_sentinel

cdef AudioLayout get_audio_layout(int channels, uint64_t c_layout):
cdef AudioLayout get_audio_layout(int channels, lib.AVChannelLayout c_layout):
"""Get an AudioLayout from Cython land."""
cdef AudioLayout layout = AudioLayout.__new__(AudioLayout, _cinit_bypass_sentinel)
if channels and not c_layout:
c_layout = default_layouts[channels]
layout._init(c_layout)
return layout


# TODO: What about av_get_default_channel_layout(...)?
cdef uint64_t default_layouts[17]
default_layouts[0] = 0
default_layouts[1] = lib.AV_CH_LAYOUT_MONO
default_layouts[2] = lib.AV_CH_LAYOUT_STEREO
default_layouts[3] = lib.AV_CH_LAYOUT_2POINT1
default_layouts[4] = lib.AV_CH_LAYOUT_4POINT0
default_layouts[5] = lib.AV_CH_LAYOUT_5POINT0_BACK
default_layouts[6] = lib.AV_CH_LAYOUT_5POINT1_BACK
default_layouts[7] = lib.AV_CH_LAYOUT_6POINT1
default_layouts[8] = lib.AV_CH_LAYOUT_7POINT1
default_layouts[9] = 0x01FF
default_layouts[10] = 0x03FF
default_layouts[11] = 0x07FF
default_layouts[12] = 0x0FFF
default_layouts[13] = 0x1FFF
default_layouts[14] = 0x3FFF
default_layouts[15] = 0x7FFF
default_layouts[16] = 0xFFFF


cdef dict channel_descriptions = {
"FL": "front left",
"FR": "front right",
"FC": "front center",
"LFE": "low frequency",
"BL": "back left",
"BR": "back right",
"FLC": "front left-of-center",
"FRC": "front right-of-center",
"BC": "back center",
"SL": "side left",
"SR": "side right",
"TC": "top center",
"TFL": "top front left",
"TFC": "top front center",
"TFR": "top front right",
"TBL": "top back left",
"TBC": "top back center",
"TBR": "top back right",
"DL": "downmix left",
"DR": "downmix right",
"WL": "wide left",
"WR": "wide right",
"SDL": "surround direct left",
"SDR": "surround direct right",
"LFE2": "low frequency 2",
}


cdef class AudioLayout:
def __init__(self, layout):
if layout is _cinit_bypass_sentinel:
return

cdef uint64_t c_layout
if isinstance(layout, int):
if layout < 0 or layout > 8:
raise ValueError(f"no layout with {layout} channels")
cdef lib.AVChannelLayout c_layout
# if isinstance(layout, int):
# if layout < 0 or layout > 8:
# raise ValueError(f"no layout with {layout} channels")

c_layout = default_layouts[layout]
elif isinstance(layout, str):
c_layout = lib.av_get_channel_layout(layout)
elif isinstance(layout, AudioLayout):
# c_layout = default_layouts[layout]
# elif isinstance(layout, str):
# c_layout = lib.av_get_channel_layout(layout)
if isinstance(layout, AudioLayout):
c_layout = (<AudioLayout>layout).layout
else:
raise TypeError("layout must be str or int")

if not c_layout:
raise ValueError(f"invalid channel layout: {layout}")

self._init(c_layout)

cdef _init(self, uint64_t layout):
cdef _init(self, lib.AVChannelLayout layout):
self.layout = layout
self.nb_channels = lib.av_get_channel_layout_nb_channels(layout) # This just counts bits.
self.channels = tuple(AudioChannel(self, i) for i in range(self.nb_channels))
# TODO
self.nb_channels = 2 # lib.av_get_channel_layout_nb_channels(layout) # This just counts bits.
# self.channels = tuple(AudioChannel(self, i) for i in range(self.nb_channels))

def __repr__(self):
return f"<av.{self.__class__.__name__} {self.name!r}>"

@property
def name(self):
"""The canonical name of the audio layout."""
cdef char out[32]
# Passing 0 as number of channels... fix this later?
lib.av_get_channel_layout_string(out, 32, 0, self.layout)
return <str>out

return "TODO"
# cdef char out[32]
# # Passing 0 as number of channels... fix this later?
# lib.av_get_channel_layout_string(out, 32, 0, self.layout)
# return <str>out

cdef class AudioChannel:
def __cinit__(self, AudioLayout layout, int index):
self.channel = lib.av_channel_layout_extract_channel(layout.layout, index)

def __repr__(self):
return f"<av.{self.__class__.__name__} {self.name!r} ({self.description})>"

@property
def name(self):
"""The canonical name of the audio channel."""
return lib.av_get_channel_name(self.channel)

@property
def description(self):
"""A human description of the audio channel."""
return channel_descriptions.get(self.name)
Loading

0 comments on commit d436c10

Please sign in to comment.