Skip to content

Commit 0eca76e

Browse files
committed
Add yuv422p10le pix_fmt
1 parent ebb839d commit 0eca76e

2 files changed

Lines changed: 47 additions & 4 deletions

File tree

av/video/frame.pyx

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ supported_np_pix_fmts = {
2020
"gbrapf32be", "gbrapf32le", "gbrp", "gbrp10be", "gbrp10le", "gbrp12be", "gbrp12le",
2121
"gbrp14be", "gbrp14le", "gbrp16be", "gbrp16le", "gbrpf32be", "gbrpf32le", "gray",
2222
"gray16be", "gray16le", "gray8", "grayf32be", "grayf32le", "nv12", "pal8", "rgb24",
23-
"rgb48be", "rgb48le", "rgb8", "rgba", "rgba64be", "rgba64le", "yuv420p", "yuv444p",
24-
"yuv444p16be", "yuv444p16le", "yuva444p16be", "yuva444p16le", "yuvj420p",
25-
"yuvj444p", "yuyv422",
23+
"rgb48be", "rgb48le", "rgb8", "rgba", "rgba64be", "rgba64le", "yuv420p",
24+
"yuv422p10le", "yuv444p", "yuv444p16be", "yuv444p16le", "yuva444p16be",
25+
"yuva444p16le", "yuvj420p", "yuvj444p", "yuyv422",
2626
}
2727

2828
cdef VideoFrame alloc_video_frame():
@@ -322,7 +322,7 @@ cdef class VideoFrame(Frame):
322322
import numpy as np
323323

324324
# check size
325-
if frame.format.name in {"yuv420p", "yuvj420p", "yuyv422"}:
325+
if frame.format.name in {"yuv420p", "yuvj420p", "yuyv422", "yuv422p10le"}:
326326
assert frame.width % 2 == 0, "the width has to be even for this pixel format"
327327
assert frame.height % 2 == 0, "the height has to be even for this pixel format"
328328

@@ -408,6 +408,18 @@ cdef class VideoFrame(Frame):
408408
useful_array(frame.planes[1]),
409409
useful_array(frame.planes[2]),
410410
]).reshape(-1, frame.width)
411+
if frame.format.name == "yuv422p10le":
412+
# Read planes as uint16 at their original width
413+
y = useful_array(frame.planes[0], 2, "uint16").reshape(frame.height, frame.width)
414+
u = useful_array(frame.planes[1], 2, "uint16").reshape(frame.height, frame.width // 2)
415+
v = useful_array(frame.planes[2], 2, "uint16").reshape(frame.height, frame.width // 2)
416+
417+
# Double the width of U and V by repeating each value
418+
u_full = np.repeat(u, 2, axis=1)
419+
v_full = np.repeat(v, 2, axis=1)
420+
if channel_last:
421+
return np.stack([y, u_full, v_full], axis=2)
422+
return np.stack([y, u_full, v_full], axis=0)
411423
if frame.format.name == "pal8":
412424
image = useful_array(frame.planes[0]).reshape(frame.height, frame.width)
413425
palette = np.frombuffer(frame.planes[1], "i4").astype(">i4").reshape(-1, 1).view(np.uint8)
@@ -641,6 +653,28 @@ cdef class VideoFrame(Frame):
641653
copy_array_to_plane(flat[u_start:v_start], frame.planes[1], 1)
642654
copy_array_to_plane(flat[v_start:], frame.planes[2], 1)
643655
return frame
656+
elif format == "yuv422p10le":
657+
if not isinstance(array, np.ndarray) or array.dtype != np.uint16:
658+
raise ValueError("Array must be uint16 type")
659+
660+
# Convert to channel-first if needed
661+
if channel_last and array.shape[2] == 3:
662+
array = np.moveaxis(array, 2, 0)
663+
elif not (array.shape[0] == 3):
664+
raise ValueError("Array must have shape (3, height, width) or (height, width, 3)")
665+
666+
height, width = array.shape[1:]
667+
if width % 2 != 0 or height % 2 != 0:
668+
raise ValueError("Width and height must be even")
669+
670+
frame = VideoFrame(width, height, format)
671+
copy_array_to_plane(array[0], frame.planes[0], 2)
672+
# Subsample U and V by taking every other column
673+
u = array[1, :, ::2].copy() # Need copy to ensure C-contiguous
674+
v = array[2, :, ::2].copy() # Need copy to ensure C-contiguous
675+
copy_array_to_plane(u, frame.planes[1], 2)
676+
copy_array_to_plane(v, frame.planes[2], 2)
677+
return frame
644678
elif format == "yuyv422":
645679
check_ndarray(array, "uint8", 3)
646680
check_ndarray_shape(array, array.shape[0] % 2 == 0)

tests/test_videoframe.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,15 @@ def test_ndarray_yuv444p16() -> None:
485485
assertNdarraysEqual(frame.to_ndarray(), array)
486486

487487

488+
def test_ndarray_yuv422p10le() -> None:
489+
array = numpy.random.randint(0, 65536, size=(3, 480, 640), dtype=numpy.uint16)
490+
for format in ("yuv422p10le",):
491+
frame = VideoFrame.from_ndarray(array, format=format)
492+
assert frame.width == 640 and frame.height == 480
493+
assert frame.format.name == format
494+
assert format in av.video.frame.supported_np_pix_fmts
495+
496+
488497
def test_ndarray_yuv444p16_align() -> None:
489498
array = numpy.random.randint(0, 65536, size=(238, 318, 3), dtype=numpy.uint16)
490499
for format in ("yuv444p16be", "yuv444p16le"):

0 commit comments

Comments
 (0)