-
Notifications
You must be signed in to change notification settings - Fork 417
Description
Overview
I'm trying to render a yuvj420p frame as an OpenGL texture. In my testing, frames that do not have padding work great. However, frames with padding are a mess. To prepare data for texture, I'm using the following function which is mostly the same as useful_array function in av/video/frame.pyx.
def _remove_padding(self, plane):
"""Remove padding from a video plane.
Args:
plane (av.video.plane.VideoPlane): the plane to remove padding from
Returns:
numpy.array: an array with proper memory aligned width
"""
buf_width = plane.line_size
bytes_per_pixel = 1
frame_width = plane.width * bytes_per_pixel
arr = np.frombuffer(plane, np.uint8)
if buf_width != frame_width:
arr = arr.reshape(-1, buf_width)[:, :frame_width]
return arr.reshape(-1, frame_width)With frames that have padding, the above function does not work properly.
Expected behavior
The given function should be able to remove any padding from the frame and be rendered as a texture properly.
Actual behavior
The output frame is completely messed up:
Traceback:
N\A
Investigation
I felt that the above behavior may be a result of plane.width being off by some pixels. In my case, the width is 1198 px for which 16 pixel alignment would be 1200 px. So, I adjusted the above function to align the width value:
def _remove_padding(self, plane):
"""Remove padding from a video plane.
Args:
plane (av.video.plane.VideoPlane): the plane to remove padding from
Returns:
numpy.array: an array with proper memory aligned width
"""
buf_width = plane.line_size
bytes_per_pixel = 1
frame_width = plane.width * bytes_per_pixel
arr = np.frombuffer(plane, np.uint8)
if buf_width != frame_width:
align_to = 16
# Frame width that is aligned up with a 16 pixel boundary
# See avcodec_align_dimensions2 function and FFALIGN macro
frame_width = (frame_width + align_to - 1) & ~(align_to - 1)
# Slice (create a view) at the aligned boundary
arr = arr.reshape(-1, buf_width)[:, :frame_width]
return arr.reshape(-1, frame_width)Now at least Y plane seems to be aligned properly, but chroma planes still are not. In my case, the chroma width is 599 which when adjusted by the above function comes out to be 608.

If I adjust chroma planes' width manually to 600, it seems to work:
Research
I have done the following:
- Checked the PyAV documentation
- Searched on Google
- Searched on Stack Overflow
- Looked through old GitHub issues
- Asked on PyAV Gitter
- ... and waited 72 hours for a response.
Additional context
- This answer on SO seems useful.
- I also tried using
frame.to_ndarray()directly with same results.def frame_to_ycbcr(self, frame): frame_data = frame.to_ndarray(format=frame.format.name) start_offset = 0 end_offset = frame.height y_data = frame_data[start_offset:end_offset, :] start_offset = end_offset end_offset = (5 * frame.height) // 4 cb_data = frame_data[start_offset:end_offset, :].reshape(-1, frame.width // 2) start_offset = end_offset end_offset = None cr_data = frame_data[start_offset:, :].reshape(-1, frame.width // 2) return y_data, cb_data, cr_data
- FFALIGN macro
- avcodec_align_dimensions2

