Skip to content

Commit

Permalink
Sync with upstream
Browse files Browse the repository at this point in the history
  • Loading branch information
WyattBlue committed Sep 4, 2024
1 parent 2b2d380 commit 18e2e75
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 17 deletions.
10 changes: 4 additions & 6 deletions .github/workflows/smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,13 @@ jobs:
ubuntu-latest)
sudo apt-get update
sudo apt-get install autoconf automake build-essential cmake \
libtool mercurial pkg-config texinfo wget yasm zlib1g-dev
sudo apt-get install libfreetype6-dev libjpeg-dev \
libtheora-dev libvorbis-dev libx264-dev
libtool pkg-config yasm zlib1g-dev libvorbis-dev libx264-dev
if [[ "${{ matrix.config.extras }}" ]]; then
sudo apt-get install doxygen
sudo apt-get install doxygen wget
fi
;;
macos-12)
brew install automake libtool nasm pkg-config shtool texi2html wget
brew install libjpeg libpng libvorbis libvpx opus theora x264
brew install automake libtool nasm pkg-config libpng libvorbis libvpx opus x264
;;
esac
Expand Down Expand Up @@ -138,6 +135,7 @@ jobs:
. $CONDA/etc/profile.d/conda.sh
conda activate pyav
python scripts\\fetch-vendor.py --config-file scripts\\ffmpeg-${{ matrix.config.ffmpeg }}.json $CONDA_PREFIX\\Library
python scripts\\comptime.py ${{ matrix.config.ffmpeg }}
python setup.py build_ext --inplace --ffmpeg-dir=$CONDA_PREFIX\\Library
- name: Test
Expand Down
2 changes: 1 addition & 1 deletion av/about.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "13.0.0rc1"
__version__ = "13.0.0"
8 changes: 8 additions & 0 deletions av/audio/layout.pyi
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
from dataclasses import dataclass

class AudioLayout:
name: str
nb_channels: int
channels: tuple[AudioChannel, ...]
def __init__(self, layout: str | AudioLayout): ...

@dataclass
class AudioChannel:
name: str
description: str
38 changes: 35 additions & 3 deletions av/audio/layout.pyx
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
cimport libav as lib
from cpython.bytes cimport PyBytes_FromStringAndSize

from dataclasses import dataclass


@dataclass
class AudioChannel:
name: str
description: str

def __repr__(self):
return f"<av.AudioChannel '{self.name}' ({self.description})>"

cdef object _cinit_bypass_sentinel

Expand Down Expand Up @@ -40,13 +51,34 @@ cdef class AudioLayout:
return self.layout.nb_channels

@property
def name(self):
def channels(self):
cdef lib.AVChannel channel
cdef char buf[16]
cdef char buf2[128]

results = []

for index in range(self.layout.nb_channels):
channel = lib.av_channel_layout_channel_from_index(&self.layout, index);
size = lib.av_channel_name(buf, sizeof(buf), channel) - 1
size2 = lib.av_channel_description(buf2, sizeof(buf2), channel) - 1
results.append(
AudioChannel(
PyBytes_FromStringAndSize(buf, size).decode("utf-8"),
PyBytes_FromStringAndSize(buf2, size2).decode("utf-8"),
)
)

return tuple(results)

@property
def name(self) -> str:
"""The canonical name of the audio layout."""
cdef char layout_name[128] # Adjust buffer size as needed
cdef char layout_name[128]
cdef int ret

ret = lib.av_channel_layout_describe(&self.layout, layout_name, sizeof(layout_name))
if ret < 0:
raise RuntimeError(f"Failed to get layout name: {ret}")

return layout_name
return layout_name
9 changes: 9 additions & 0 deletions av/video/frame.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,12 @@ class VideoFrame(Frame):
) -> VideoFrame: ...
@staticmethod
def from_ndarray(array: _SupportedNDarray, format: str = "rgb24") -> VideoFrame: ...
@staticmethod
def from_bytes(
data: bytes,
width: int,
height: int,
format: str = "rgba",
flip_horizontal: bool = False,
flip_vertical: bool = False,
) -> VideoFrame: ...
40 changes: 34 additions & 6 deletions av/video/frame.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,8 @@ cdef byteswap_array(array, bint big_endian):
return array


cdef copy_array_to_plane(array, VideoPlane plane, unsigned int bytes_per_pixel):
cdef bytes imgbytes = array.tobytes()
cdef const uint8_t[:] i_buf = imgbytes
cdef copy_bytes_to_plane(img_bytes, VideoPlane plane, unsigned int bytes_per_pixel, bint flip_horizontal, bint flip_vertical):
cdef const uint8_t[:] i_buf = img_bytes
cdef size_t i_pos = 0
cdef size_t i_stride = plane.width * bytes_per_pixel
cdef size_t i_size = plane.height * i_stride
Expand All @@ -51,12 +50,33 @@ cdef copy_array_to_plane(array, VideoPlane plane, unsigned int bytes_per_pixel):
cdef size_t o_pos = 0
cdef size_t o_stride = abs(plane.line_size)

while i_pos < i_size:
o_buf[o_pos:o_pos + i_stride] = i_buf[i_pos:i_pos + i_stride]
i_pos += i_stride
cdef int start_row, end_row, step
if flip_vertical:
start_row = plane.height - 1
end_row = -1
step = -1
else:
start_row = 0
end_row = plane.height
step = 1

cdef int i, j
for row in range(start_row, end_row, step):
i_pos = row * i_stride
if flip_horizontal:
for i in range(0, i_stride, bytes_per_pixel):
for j in range(bytes_per_pixel):
o_buf[o_pos + i + j] = i_buf[i_pos + i_stride - i - bytes_per_pixel + j]
else:
o_buf[o_pos:o_pos + i_stride] = i_buf[i_pos:i_pos + i_stride]
o_pos += o_stride


cdef copy_array_to_plane(array, VideoPlane plane, unsigned int bytes_per_pixel):
cdef bytes imgbytes = array.tobytes()
copy_bytes_to_plane(imgbytes, plane, bytes_per_pixel, False, False)


cdef useful_array(VideoPlane plane, unsigned int bytes_per_pixel=1, str dtype="uint8"):
"""
Return the useful part of the VideoPlane as a single dimensional array.
Expand Down Expand Up @@ -570,3 +590,11 @@ cdef class VideoFrame(Frame):
copy_array_to_plane(array, frame.planes[0], 1 if array.ndim == 2 else array.shape[2])

return frame

def from_bytes(img_bytes: bytes, width: int, height: int, format="rgba", flip_horizontal=False, flip_vertical=False):
frame = VideoFrame(width, height, format)
if format == "rgba":
copy_bytes_to_plane(img_bytes, frame.planes[0], 4, flip_horizontal, flip_vertical)
else:
raise NotImplementedError(f"Format '{format}' is not supported.")
return frame
3 changes: 3 additions & 0 deletions include/libavcodec/avcodec.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ cdef extern from "libavutil/channel_layout.h":
void av_channel_layout_uninit(AVChannelLayout *channel_layout)
int av_channel_layout_copy(AVChannelLayout *dst, const AVChannelLayout *src)
int av_channel_layout_describe(const AVChannelLayout *channel_layout, char *buf, size_t buf_size)
int av_channel_name(char *buf, size_t buf_size, AVChannel channel_id)
int av_channel_description(char *buf, size_t buf_size, AVChannel channel_id)
AVChannel av_channel_layout_channel_from_index(AVChannelLayout *channel_layout, unsigned int idx)


cdef extern from "libavcodec/avcodec.h" nogil:
Expand Down
5 changes: 4 additions & 1 deletion tests/test_audiolayout.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ def test_stereo_from_layout(self):
layout2 = AudioLayout(layout)
self._test_stereo(layout2)

def _test_stereo(self, layout):
def _test_stereo(self, layout: AudioLayout) -> None:
self.assertEqual(layout.name, "stereo")
self.assertEqual(layout.nb_channels, 2)
self.assertEqual(repr(layout), "<av.AudioLayout 'stereo'>")

# Re-enable when FFmpeg 6.0 is dropped.

# self.assertEqual(layout.channels[0].name, "FL")
# self.assertEqual(layout.channels[0].description, "front left")
# self.assertEqual(
Expand Down

0 comments on commit 18e2e75

Please sign in to comment.