libobs: Round up chroma sizes for odd resolutions

When playing video, OBS can overflow and crash, or render video edges
incorrectly if the video resolution does not divide into 2 for trivial
chroma subsampling. This is fixed by rounding chroma plane sizes up
instead of down, and maintaining that through the video pipeline.
This commit is contained in:
jpark37 2021-07-21 23:01:14 -07:00 committed by Jim
parent d6c77964c0
commit 8440a53da3
2 changed files with 121 additions and 72 deletions

View File

@ -37,34 +37,40 @@ void video_frame_init(struct video_frame *frame, enum video_format format,
case VIDEO_FORMAT_NONE:
return;
case VIDEO_FORMAT_I420:
case VIDEO_FORMAT_I420: {
size = width * height;
ALIGN_SIZE(size, alignment);
offsets[0] = size;
size += (width / 2) * (height / 2);
const uint32_t half_width = (width + 1) / 2;
const uint32_t half_height = (height + 1) / 2;
const uint32_t quarter_area = half_width * half_height;
size += quarter_area;
ALIGN_SIZE(size, alignment);
offsets[1] = size;
size += (width / 2) * (height / 2);
size += quarter_area;
ALIGN_SIZE(size, alignment);
frame->data[0] = bmalloc(size);
frame->data[1] = (uint8_t *)frame->data[0] + offsets[0];
frame->data[2] = (uint8_t *)frame->data[0] + offsets[1];
frame->linesize[0] = width;
frame->linesize[1] = width / 2;
frame->linesize[2] = width / 2;
frame->linesize[1] = half_width;
frame->linesize[2] = half_width;
break;
}
case VIDEO_FORMAT_NV12:
case VIDEO_FORMAT_NV12: {
size = width * height;
ALIGN_SIZE(size, alignment);
offsets[0] = size;
size += (width / 2) * (height / 2) * 2;
const uint32_t cbcr_width = (width + 1) & (UINT32_MAX - 1);
size += cbcr_width * ((height + 1) / 2);
ALIGN_SIZE(size, alignment);
frame->data[0] = bmalloc(size);
frame->data[1] = (uint8_t *)frame->data[0] + offsets[0];
frame->linesize[0] = width;
frame->linesize[1] = width;
frame->linesize[1] = cbcr_width;
break;
}
case VIDEO_FORMAT_Y800:
size = width * height;
@ -75,12 +81,15 @@ void video_frame_init(struct video_frame *frame, enum video_format format,
case VIDEO_FORMAT_YVYU:
case VIDEO_FORMAT_YUY2:
case VIDEO_FORMAT_UYVY:
size = width * height * 2;
case VIDEO_FORMAT_UYVY: {
const uint32_t double_width =
((width + 1) & (UINT32_MAX - 1)) * 2;
size = double_width * height;
ALIGN_SIZE(size, alignment);
frame->data[0] = bmalloc(size);
frame->linesize[0] = width * 2;
frame->linesize[0] = double_width;
break;
}
case VIDEO_FORMAT_RGBA:
case VIDEO_FORMAT_BGRA:
@ -110,31 +119,37 @@ void video_frame_init(struct video_frame *frame, enum video_format format,
frame->linesize[0] = width * 3;
break;
case VIDEO_FORMAT_I422:
case VIDEO_FORMAT_I422: {
size = width * height;
ALIGN_SIZE(size, alignment);
offsets[0] = size;
size += (width / 2) * height;
const uint32_t half_width = (width + 1) / 2;
const uint32_t half_area = half_width * height;
size += half_area;
ALIGN_SIZE(size, alignment);
offsets[1] = size;
size += (width / 2) * height;
size += half_area;
ALIGN_SIZE(size, alignment);
frame->data[0] = bmalloc(size);
frame->data[1] = (uint8_t *)frame->data[0] + offsets[0];
frame->data[2] = (uint8_t *)frame->data[0] + offsets[1];
frame->linesize[0] = width;
frame->linesize[1] = width / 2;
frame->linesize[2] = width / 2;
frame->linesize[1] = half_width;
frame->linesize[2] = half_width;
break;
}
case VIDEO_FORMAT_I40A:
case VIDEO_FORMAT_I40A: {
size = width * height;
ALIGN_SIZE(size, alignment);
offsets[0] = size;
size += (width / 2) * (height / 2);
const uint32_t half_width = (width + 1) / 2;
const uint32_t half_height = (height + 1) / 2;
const uint32_t quarter_area = half_width * half_height;
size += quarter_area;
ALIGN_SIZE(size, alignment);
offsets[1] = size;
size += (width / 2) * (height / 2);
size += quarter_area;
ALIGN_SIZE(size, alignment);
offsets[2] = size;
size += width * height;
@ -144,19 +159,22 @@ void video_frame_init(struct video_frame *frame, enum video_format format,
frame->data[2] = (uint8_t *)frame->data[0] + offsets[1];
frame->data[3] = (uint8_t *)frame->data[0] + offsets[2];
frame->linesize[0] = width;
frame->linesize[1] = width / 2;
frame->linesize[2] = width / 2;
frame->linesize[1] = half_width;
frame->linesize[2] = half_width;
frame->linesize[3] = width;
break;
}
case VIDEO_FORMAT_I42A:
case VIDEO_FORMAT_I42A: {
size = width * height;
ALIGN_SIZE(size, alignment);
offsets[0] = size;
size += (width / 2) * height;
const uint32_t half_width = (width + 1) / 2;
const uint32_t half_area = half_width * height;
size += half_area;
ALIGN_SIZE(size, alignment);
offsets[1] = size;
size += (width / 2) * height;
size += half_area;
ALIGN_SIZE(size, alignment);
offsets[2] = size;
size += width * height;
@ -166,10 +184,11 @@ void video_frame_init(struct video_frame *frame, enum video_format format,
frame->data[2] = (uint8_t *)frame->data[0] + offsets[1];
frame->data[3] = (uint8_t *)frame->data[0] + offsets[2];
frame->linesize[0] = width;
frame->linesize[1] = width / 2;
frame->linesize[2] = width / 2;
frame->linesize[1] = half_width;
frame->linesize[2] = half_width;
frame->linesize[3] = width;
break;
}
case VIDEO_FORMAT_YUVA:
size = width * height;

View File

@ -1554,8 +1554,11 @@ static inline enum convert_type get_convert_type(enum video_format format,
static inline bool set_packed422_sizes(struct obs_source *source,
const struct obs_source_frame *frame)
{
source->async_convert_width[0] = frame->width / 2;
source->async_convert_height[0] = frame->height;
const uint32_t width = frame->width;
const uint32_t height = frame->height;
const uint32_t half_width = (width + 1) / 2;
source->async_convert_width[0] = half_width;
source->async_convert_height[0] = height;
source->async_texture_formats[0] = GS_BGRA;
source->async_channel_count = 1;
return true;
@ -1611,12 +1614,16 @@ set_planar444_alpha_sizes(struct obs_source *source,
static inline bool set_planar420_sizes(struct obs_source *source,
const struct obs_source_frame *frame)
{
source->async_convert_width[0] = frame->width;
source->async_convert_width[1] = frame->width / 2;
source->async_convert_width[2] = frame->width / 2;
source->async_convert_height[0] = frame->height;
source->async_convert_height[1] = frame->height / 2;
source->async_convert_height[2] = frame->height / 2;
const uint32_t width = frame->width;
const uint32_t height = frame->height;
const uint32_t half_width = (width + 1) / 2;
const uint32_t half_height = (height + 1) / 2;
source->async_convert_width[0] = width;
source->async_convert_width[1] = half_width;
source->async_convert_width[2] = half_width;
source->async_convert_height[0] = height;
source->async_convert_height[1] = half_height;
source->async_convert_height[2] = half_height;
source->async_texture_formats[0] = GS_R8;
source->async_texture_formats[1] = GS_R8;
source->async_texture_formats[2] = GS_R8;
@ -1628,14 +1635,18 @@ static inline bool
set_planar420_alpha_sizes(struct obs_source *source,
const struct obs_source_frame *frame)
{
source->async_convert_width[0] = frame->width;
source->async_convert_width[1] = frame->width / 2;
source->async_convert_width[2] = frame->width / 2;
source->async_convert_width[3] = frame->width;
source->async_convert_height[0] = frame->height;
source->async_convert_height[1] = frame->height / 2;
source->async_convert_height[2] = frame->height / 2;
source->async_convert_height[3] = frame->height;
const uint32_t width = frame->width;
const uint32_t height = frame->height;
const uint32_t half_width = (width + 1) / 2;
const uint32_t half_height = (height + 1) / 2;
source->async_convert_width[0] = width;
source->async_convert_width[1] = half_width;
source->async_convert_width[2] = half_width;
source->async_convert_width[3] = width;
source->async_convert_height[0] = height;
source->async_convert_height[1] = half_height;
source->async_convert_height[2] = half_height;
source->async_convert_height[3] = height;
source->async_texture_formats[0] = GS_R8;
source->async_texture_formats[1] = GS_R8;
source->async_texture_formats[2] = GS_R8;
@ -1647,12 +1658,15 @@ set_planar420_alpha_sizes(struct obs_source *source,
static inline bool set_planar422_sizes(struct obs_source *source,
const struct obs_source_frame *frame)
{
source->async_convert_width[0] = frame->width;
source->async_convert_width[1] = frame->width / 2;
source->async_convert_width[2] = frame->width / 2;
source->async_convert_height[0] = frame->height;
source->async_convert_height[1] = frame->height;
source->async_convert_height[2] = frame->height;
const uint32_t width = frame->width;
const uint32_t height = frame->height;
const uint32_t half_width = (width + 1) / 2;
source->async_convert_width[0] = width;
source->async_convert_width[1] = half_width;
source->async_convert_width[2] = half_width;
source->async_convert_height[0] = height;
source->async_convert_height[1] = height;
source->async_convert_height[2] = height;
source->async_texture_formats[0] = GS_R8;
source->async_texture_formats[1] = GS_R8;
source->async_texture_formats[2] = GS_R8;
@ -1664,14 +1678,17 @@ static inline bool
set_planar422_alpha_sizes(struct obs_source *source,
const struct obs_source_frame *frame)
{
source->async_convert_width[0] = frame->width;
source->async_convert_width[1] = frame->width / 2;
source->async_convert_width[2] = frame->width / 2;
source->async_convert_width[3] = frame->width;
source->async_convert_height[0] = frame->height;
source->async_convert_height[1] = frame->height;
source->async_convert_height[2] = frame->height;
source->async_convert_height[3] = frame->height;
const uint32_t width = frame->width;
const uint32_t height = frame->height;
const uint32_t half_width = (width + 1) / 2;
source->async_convert_width[0] = width;
source->async_convert_width[1] = half_width;
source->async_convert_width[2] = half_width;
source->async_convert_width[3] = width;
source->async_convert_height[0] = height;
source->async_convert_height[1] = height;
source->async_convert_height[2] = height;
source->async_convert_height[3] = height;
source->async_texture_formats[0] = GS_R8;
source->async_texture_formats[1] = GS_R8;
source->async_texture_formats[2] = GS_R8;
@ -1683,10 +1700,14 @@ set_planar422_alpha_sizes(struct obs_source *source,
static inline bool set_nv12_sizes(struct obs_source *source,
const struct obs_source_frame *frame)
{
source->async_convert_width[0] = frame->width;
source->async_convert_width[1] = frame->width / 2;
source->async_convert_height[0] = frame->height;
source->async_convert_height[1] = frame->height / 2;
const uint32_t width = frame->width;
const uint32_t height = frame->height;
const uint32_t half_width = (width + 1) / 2;
const uint32_t half_height = (height + 1) / 2;
source->async_convert_width[0] = width;
source->async_convert_width[1] = half_width;
source->async_convert_height[0] = height;
source->async_convert_height[1] = half_height;
source->async_texture_formats[0] = GS_R8;
source->async_texture_formats[1] = GS_R8G8;
source->async_channel_count = 2;
@ -2746,16 +2767,22 @@ static void copy_frame_data(struct obs_source_frame *dst,
}
switch (src->format) {
case VIDEO_FORMAT_I420:
copy_frame_data_plane(dst, src, 0, dst->height);
copy_frame_data_plane(dst, src, 1, dst->height / 2);
copy_frame_data_plane(dst, src, 2, dst->height / 2);
case VIDEO_FORMAT_I420: {
const uint32_t height = dst->height;
const uint32_t half_height = (height + 1) / 2;
copy_frame_data_plane(dst, src, 0, height);
copy_frame_data_plane(dst, src, 1, half_height);
copy_frame_data_plane(dst, src, 2, half_height);
break;
}
case VIDEO_FORMAT_NV12:
copy_frame_data_plane(dst, src, 0, dst->height);
copy_frame_data_plane(dst, src, 1, dst->height / 2);
case VIDEO_FORMAT_NV12: {
const uint32_t height = dst->height;
const uint32_t half_height = (height + 1) / 2;
copy_frame_data_plane(dst, src, 0, height);
copy_frame_data_plane(dst, src, 1, half_height);
break;
}
case VIDEO_FORMAT_I444:
case VIDEO_FORMAT_I422:
@ -2777,12 +2804,15 @@ static void copy_frame_data(struct obs_source_frame *dst,
copy_frame_data_plane(dst, src, 0, dst->height);
break;
case VIDEO_FORMAT_I40A:
copy_frame_data_plane(dst, src, 0, dst->height);
copy_frame_data_plane(dst, src, 1, dst->height / 2);
copy_frame_data_plane(dst, src, 2, dst->height / 2);
copy_frame_data_plane(dst, src, 3, dst->height);
case VIDEO_FORMAT_I40A: {
const uint32_t height = dst->height;
const uint32_t half_height = (height + 1) / 2;
copy_frame_data_plane(dst, src, 0, height);
copy_frame_data_plane(dst, src, 1, half_height);
copy_frame_data_plane(dst, src, 2, half_height);
copy_frame_data_plane(dst, src, 3, height);
break;
}
case VIDEO_FORMAT_I42A:
case VIDEO_FORMAT_YUVA: