From d3a8ef7128fe1b8035ea3eb13535a668685ab7ac Mon Sep 17 00:00:00 2001 From: mvji <33432858+mvji@users.noreply.github.com> Date: Tue, 19 Apr 2022 19:37:07 +0200 Subject: [PATCH] libobs: Add support for YUV422P10LE, YUV444P12LE, YUVA444P12LE --- libobs/data/format_conversion.effect | 67 +++++++++++++++++++ libobs/media-io/video-frame.c | 60 +++++++++++++++++ libobs/media-io/video-io.h | 18 ++++++ libobs/media-io/video-matrices.c | 6 ++ libobs/media-io/video-scaler-ffmpeg.c | 10 +++ libobs/obs-internal.h | 3 + libobs/obs-source.c | 92 ++++++++++++++++++++++++++- libobs/obs-video.c | 3 + libobs/obs.c | 3 + 9 files changed, 261 insertions(+), 1 deletion(-) diff --git a/libobs/data/format_conversion.effect b/libobs/data/format_conversion.effect index 2688e76ae..6b59a74be 100644 --- a/libobs/data/format_conversion.effect +++ b/libobs/data/format_conversion.effect @@ -501,6 +501,19 @@ float3 PSPlanar422_Reverse(FragPosWide frag_in) : TARGET return rgb; } +float4 PSPlanar422_10LE_Reverse(FragPosWide frag_in) : TARGET +{ + float y = image.Load(int3(frag_in.pos_wide.xz, 0)).x; + int3 xy0_chroma = int3(frag_in.pos_wide.yz, 0); + float cb = image1.Load(xy0_chroma).x; + float cr = image2.Load(xy0_chroma).x; + float3 yuv = float3(y, cb, cr); + yuv *= 65535. / 1023.; + float3 rgb = YUV_to_RGB(yuv); + rgb = srgb_nonlinear_to_linear(rgb); + return float4(rgb, 1.0); +} + float4 PSPlanar422A_Reverse(FragPosWide frag_in) : TARGET { int3 xy0_luma = int3(frag_in.pos_wide.xz, 0); @@ -525,6 +538,19 @@ float3 PSPlanar444_Reverse(FragPos frag_in) : TARGET return rgb; } +float4 PSPlanar444_12LE_Reverse(FragPos frag_in) : TARGET +{ + int3 xy0 = int3(frag_in.pos.xy, 0); + float y = image.Load(xy0).x; + float cb = image1.Load(xy0).x; + float cr = image2.Load(xy0).x; + float3 yuv = float3(y, cb, cr); + yuv *= 65535. / 4095.; + float3 rgb = YUV_to_RGB(yuv); + rgb = srgb_nonlinear_to_linear(rgb); + return float4(rgb, 1.0); +} + float4 PSPlanar444A_Reverse(FragPos frag_in) : TARGET { int3 xy0 = int3(frag_in.pos.xy, 0); @@ -537,6 +563,20 @@ float4 PSPlanar444A_Reverse(FragPos frag_in) : TARGET return rgba; } +float4 PSPlanar444A_12LE_Reverse(FragPos frag_in) : TARGET +{ + int3 xy0 = int3(frag_in.pos.xy, 0); + float y = image.Load(xy0).x; + float cb = image1.Load(xy0).x; + float cr = image2.Load(xy0).x; + float alpha = image3.Load(xy0).x * 16.; + float3 yuv = float3(y, cb, cr); + yuv *= 65535. / 4095.; + float3 rgb = YUV_to_RGB(yuv); + rgb = srgb_nonlinear_to_linear(rgb); + return float4(rgb, alpha); +} + float4 PSAYUV_Reverse(FragPos frag_in) : TARGET { float4 yuva = image.Load(int3(frag_in.pos.xy, 0)); @@ -921,6 +961,15 @@ technique I422_Reverse } } +technique I210_Reverse +{ + pass + { + vertex_shader = VSPosWide_Reverse(id); + pixel_shader = PSPlanar422_10LE_Reverse(frag_in); + } +} + technique I42A_Reverse { pass @@ -939,6 +988,15 @@ technique I444_Reverse } } +technique I412_Reverse +{ + pass + { + vertex_shader = VSPos(id); + pixel_shader = PSPlanar444_12LE_Reverse(frag_in); + } +} + technique YUVA_Reverse { pass @@ -948,6 +1006,15 @@ technique YUVA_Reverse } } +technique YA2L_Reverse +{ + pass + { + vertex_shader = VSPos(id); + pixel_shader = PSPlanar444A_12LE_Reverse(frag_in); + } +} + technique AYUV_Reverse { pass diff --git a/libobs/media-io/video-frame.c b/libobs/media-io/video-frame.c index 9cfa31a19..b5a7e6b95 100644 --- a/libobs/media-io/video-frame.c +++ b/libobs/media-io/video-frame.c @@ -112,6 +112,17 @@ void video_frame_init(struct video_frame *frame, enum video_format format, frame->linesize[2] = width; break; + case VIDEO_FORMAT_I412: + size = width * height * 2; + ALIGN_SIZE(size, alignment); + frame->data[0] = bmalloc(size * 3); + frame->data[1] = (uint8_t *)frame->data[0] + size; + frame->data[2] = (uint8_t *)frame->data[1] + size; + frame->linesize[0] = width * 2; + frame->linesize[1] = width * 2; + frame->linesize[2] = width * 2; + break; + case VIDEO_FORMAT_BGR3: size = width * height * 3; ALIGN_SIZE(size, alignment); @@ -139,6 +150,27 @@ void video_frame_init(struct video_frame *frame, enum video_format format, break; } + case VIDEO_FORMAT_I210: { + size = width * height * 2; + ALIGN_SIZE(size, alignment); + offsets[0] = size; + const uint32_t half_width = (width + 1) / 2; + const uint32_t half_area = half_width * height; + const uint32_t half_area_size = 2 * half_area; + size += half_area_size; + ALIGN_SIZE(size, alignment); + offsets[1] = size; + size += half_area_size; + 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 * 2; + frame->linesize[1] = half_width * 2; + frame->linesize[2] = half_width * 2; + break; + } + case VIDEO_FORMAT_I40A: { size = width * height; ALIGN_SIZE(size, alignment); @@ -212,6 +244,31 @@ void video_frame_init(struct video_frame *frame, enum video_format format, frame->linesize[3] = width; break; + case VIDEO_FORMAT_YA2L: { + const uint32_t linesize = width * 2; + const uint32_t plane_size = linesize * height; + size = plane_size; + ALIGN_SIZE(size, alignment); + offsets[0] = size; + size += plane_size; + ALIGN_SIZE(size, alignment); + offsets[1] = size; + size += plane_size; + ALIGN_SIZE(size, alignment); + offsets[2] = size; + size += plane_size; + 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->data[3] = (uint8_t *)frame->data[0] + offsets[2]; + frame->linesize[0] = linesize; + frame->linesize[1] = linesize; + frame->linesize[2] = linesize; + frame->linesize[3] = linesize; + break; + } + case VIDEO_FORMAT_I010: { size = width * height * 2; ALIGN_SIZE(size, alignment); @@ -283,6 +340,8 @@ void video_frame_copy(struct video_frame *dst, const struct video_frame *src, case VIDEO_FORMAT_I444: case VIDEO_FORMAT_I422: + case VIDEO_FORMAT_I210: + case VIDEO_FORMAT_I412: memcpy(dst->data[0], src->data[0], src->linesize[0] * cy); memcpy(dst->data[1], src->data[1], src->linesize[1] * cy); memcpy(dst->data[2], src->data[2], src->linesize[2] * cy); @@ -297,6 +356,7 @@ void video_frame_copy(struct video_frame *dst, const struct video_frame *src, case VIDEO_FORMAT_I42A: case VIDEO_FORMAT_YUVA: + case VIDEO_FORMAT_YA2L: memcpy(dst->data[0], src->data[0], src->linesize[0] * cy); memcpy(dst->data[1], src->data[1], src->linesize[1] * cy); memcpy(dst->data[2], src->data[2], src->linesize[2] * cy); diff --git a/libobs/media-io/video-io.h b/libobs/media-io/video-io.h index 7453d921f..97985a941 100644 --- a/libobs/media-io/video-io.h +++ b/libobs/media-io/video-io.h @@ -73,6 +73,15 @@ enum video_format { /* planar 4:2:0 format, 10 bpp */ VIDEO_FORMAT_I010, /* three-plane */ VIDEO_FORMAT_P010, /* two-plane, luma and packed chroma */ + + /* planar 4:2:2 10 bits */ + VIDEO_FORMAT_I210, // Little Endian + + /* planar 4:4:4 12 bits */ + VIDEO_FORMAT_I412, // Little Endian + + /* planar 4:4:4 12 bits with alpha */ + VIDEO_FORMAT_YA2L, // Little Endian }; enum video_trc { @@ -123,13 +132,16 @@ static inline bool format_is_yuv(enum video_format format) case VIDEO_FORMAT_I420: case VIDEO_FORMAT_NV12: case VIDEO_FORMAT_I422: + case VIDEO_FORMAT_I210: case VIDEO_FORMAT_YVYU: case VIDEO_FORMAT_YUY2: case VIDEO_FORMAT_UYVY: case VIDEO_FORMAT_I444: + case VIDEO_FORMAT_I412: case VIDEO_FORMAT_I40A: case VIDEO_FORMAT_I42A: case VIDEO_FORMAT_YUVA: + case VIDEO_FORMAT_YA2L: case VIDEO_FORMAT_AYUV: case VIDEO_FORMAT_I010: case VIDEO_FORMAT_P010: @@ -155,6 +167,8 @@ static inline const char *get_video_format_name(enum video_format format) return "NV12"; case VIDEO_FORMAT_I422: return "I422"; + case VIDEO_FORMAT_I210: + return "I210"; case VIDEO_FORMAT_YVYU: return "YVYU"; case VIDEO_FORMAT_YUY2: @@ -169,6 +183,8 @@ static inline const char *get_video_format_name(enum video_format format) return "BGRX"; case VIDEO_FORMAT_I444: return "I444"; + case VIDEO_FORMAT_I412: + return "I412"; case VIDEO_FORMAT_Y800: return "Y800"; case VIDEO_FORMAT_BGR3: @@ -179,6 +195,8 @@ static inline const char *get_video_format_name(enum video_format format) return "I42A"; case VIDEO_FORMAT_YUVA: return "YUVA"; + case VIDEO_FORMAT_YA2L: + return "YA2L"; case VIDEO_FORMAT_AYUV: return "AYUV"; case VIDEO_FORMAT_I010: diff --git a/libobs/media-io/video-matrices.c b/libobs/media-io/video-matrices.c index a008fe35a..4e9239b2c 100644 --- a/libobs/media-io/video-matrices.c +++ b/libobs/media-io/video-matrices.c @@ -264,7 +264,13 @@ bool video_format_get_parameters_for_format(enum video_colorspace color_space, switch (format) { case VIDEO_FORMAT_I010: case VIDEO_FORMAT_P010: + case VIDEO_FORMAT_I210: bpc = 10; + break; + case VIDEO_FORMAT_I412: + case VIDEO_FORMAT_YA2L: + bpc = 12; + break; } return video_format_get_parameters_for_bpc(color_space, range, matrix, diff --git a/libobs/media-io/video-scaler-ffmpeg.c b/libobs/media-io/video-scaler-ffmpeg.c index 2d8db8b3c..7c357a470 100644 --- a/libobs/media-io/video-scaler-ffmpeg.c +++ b/libobs/media-io/video-scaler-ffmpeg.c @@ -51,16 +51,26 @@ get_ffmpeg_video_format(enum video_format format) return AV_PIX_FMT_GRAY8; case VIDEO_FORMAT_I444: return AV_PIX_FMT_YUV444P; + case VIDEO_FORMAT_I412: + return AV_PIX_FMT_YUV444P12LE; case VIDEO_FORMAT_BGR3: return AV_PIX_FMT_BGR24; case VIDEO_FORMAT_I422: return AV_PIX_FMT_YUV422P; + case VIDEO_FORMAT_I210: + return AV_PIX_FMT_YUV422P10LE; case VIDEO_FORMAT_I40A: return AV_PIX_FMT_YUVA420P; case VIDEO_FORMAT_I42A: return AV_PIX_FMT_YUVA422P; case VIDEO_FORMAT_YUVA: return AV_PIX_FMT_YUVA444P; + case VIDEO_FORMAT_YA2L: +#if LIBAVUTIL_BUILD >= AV_VERSION_INT(56, 31, 100) + return AV_PIX_FMT_YUVA444P12LE; +#else + return AV_PIX_FMT_NONE; +#endif case VIDEO_FORMAT_I010: return AV_PIX_FMT_YUV420P10LE; case VIDEO_FORMAT_P010: diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index 1ced6fe1c..28221e150 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -890,6 +890,9 @@ convert_video_format(enum video_format format) return GS_BGRA; case VIDEO_FORMAT_I010: case VIDEO_FORMAT_P010: + case VIDEO_FORMAT_I210: + case VIDEO_FORMAT_I412: + case VIDEO_FORMAT_YA2L: return GS_RGBA16F; default: return GS_BGRX; diff --git a/libobs/obs-source.c b/libobs/obs-source.c index bb2f73ba8..3baf10d66 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -1548,10 +1548,13 @@ enum convert_type { CONVERT_420, CONVERT_420_A, CONVERT_422, + CONVERT_422P10LE, CONVERT_422_A, CONVERT_422_PACK, CONVERT_444, + CONVERT_444P12LE, CONVERT_444_A, + CONVERT_444P12LE_A, CONVERT_444_A_PACK, CONVERT_800, CONVERT_RGB_LIMITED, @@ -1574,8 +1577,12 @@ static inline enum convert_type get_convert_type(enum video_format format, return CONVERT_NV12; case VIDEO_FORMAT_I444: return CONVERT_444; + case VIDEO_FORMAT_I412: + return CONVERT_444P12LE; case VIDEO_FORMAT_I422: return CONVERT_422; + case VIDEO_FORMAT_I210: + return CONVERT_422P10LE; case VIDEO_FORMAT_YVYU: case VIDEO_FORMAT_YUY2: @@ -1603,6 +1610,9 @@ static inline enum convert_type get_convert_type(enum video_format format, case VIDEO_FORMAT_YUVA: return CONVERT_444_A; + case VIDEO_FORMAT_YA2L: + return CONVERT_444P12LE_A; + case VIDEO_FORMAT_AYUV: return CONVERT_444_A_PACK; @@ -1672,6 +1682,22 @@ static inline bool set_planar444_sizes(struct obs_source *source, return true; } +static inline bool set_planar444_16_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; + source->async_convert_width[2] = 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_texture_formats[0] = GS_R16; + source->async_texture_formats[1] = GS_R16; + source->async_texture_formats[2] = GS_R16; + source->async_channel_count = 3; + return true; +} + static inline bool set_planar444_alpha_sizes(struct obs_source *source, const struct obs_source_frame *frame) @@ -1692,6 +1718,26 @@ set_planar444_alpha_sizes(struct obs_source *source, return true; } +static inline bool +set_planar444_16_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; + source->async_convert_width[2] = frame->width; + 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; + source->async_texture_formats[0] = GS_R16; + source->async_texture_formats[1] = GS_R16; + source->async_texture_formats[2] = GS_R16; + source->async_texture_formats[3] = GS_R16; + source->async_channel_count = 4; + return true; +} + static inline bool set_planar420_sizes(struct obs_source *source, const struct obs_source_frame *frame) { @@ -1754,6 +1800,24 @@ static inline bool set_planar422_sizes(struct obs_source *source, source->async_channel_count = 3; return true; } +static inline bool set_planar422_16_sizes(struct obs_source *source, + const struct obs_source_frame *frame) +{ + 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_R16; + source->async_texture_formats[1] = GS_R16; + source->async_texture_formats[2] = GS_R16; + source->async_channel_count = 3; + return true; +} static inline bool set_planar422_alpha_sizes(struct obs_source *source, @@ -1876,12 +1940,18 @@ static inline bool init_gpu_conversion(struct obs_source *source, case CONVERT_422: return set_planar422_sizes(source, frame); + case CONVERT_422P10LE: + return set_planar422_16_sizes(source, frame); + case CONVERT_NV12: return set_nv12_sizes(source, frame); case CONVERT_444: return set_planar444_sizes(source, frame); + case CONVERT_444P12LE: + return set_planar444_16_sizes(source, frame); + case CONVERT_800: return set_y800_sizes(source, frame); @@ -1900,6 +1970,9 @@ static inline bool init_gpu_conversion(struct obs_source *source, case CONVERT_444_A: return set_planar444_alpha_sizes(source, frame); + case CONVERT_444P12LE_A: + return set_planar444_16_alpha_sizes(source, frame); + case CONVERT_444_A_PACK: return set_packed444_alpha_sizes(source, frame); @@ -1992,11 +2065,14 @@ static void upload_raw_frame(gs_texture_t *tex[MAX_AV_PLANES], case CONVERT_BGR3: case CONVERT_420: case CONVERT_422: + case CONVERT_422P10LE: case CONVERT_NV12: case CONVERT_444: + case CONVERT_444P12LE: case CONVERT_420_A: case CONVERT_422_A: case CONVERT_444_A: + case CONVERT_444P12LE_A: case CONVERT_444_A_PACK: case CONVERT_I010_SRGB: case CONVERT_I010_PQ_2020_709: @@ -2039,6 +2115,9 @@ static const char *select_conversion_technique(enum video_format format, case VIDEO_FORMAT_I444: return "I444_Reverse"; + case VIDEO_FORMAT_I412: + return "I412_Reverse"; + case VIDEO_FORMAT_Y800: return full_range ? "Y800_Full" : "Y800_Limited"; @@ -2048,6 +2127,9 @@ static const char *select_conversion_technique(enum video_format format, case VIDEO_FORMAT_I422: return "I422_Reverse"; + case VIDEO_FORMAT_I210: + return "I210_Reverse"; + case VIDEO_FORMAT_I40A: return "I40A_Reverse"; @@ -2057,6 +2139,9 @@ static const char *select_conversion_technique(enum video_format format, case VIDEO_FORMAT_YUVA: return "YUVA_Reverse"; + case VIDEO_FORMAT_YA2L: + return "YA2L_Reverse"; + case VIDEO_FORMAT_AYUV: return "AYUV_Reverse"; @@ -2097,7 +2182,9 @@ static const char *select_conversion_technique(enum video_format format, static bool need_linear_output(enum video_format format) { - return (format == VIDEO_FORMAT_I010) || (format == VIDEO_FORMAT_P010); + return (format == VIDEO_FORMAT_I010) || (format == VIDEO_FORMAT_P010) || + (format == VIDEO_FORMAT_I210) || (format == VIDEO_FORMAT_I412) || + (format == VIDEO_FORMAT_YA2L); } static inline void set_eparam(gs_effect_t *effect, const char *name, float val) @@ -3192,6 +3279,8 @@ static void copy_frame_data(struct obs_source_frame *dst, case VIDEO_FORMAT_I444: case VIDEO_FORMAT_I422: + case VIDEO_FORMAT_I210: + case VIDEO_FORMAT_I412: copy_frame_data_plane(dst, src, 0, dst->height); copy_frame_data_plane(dst, src, 1, dst->height); copy_frame_data_plane(dst, src, 2, dst->height); @@ -3222,6 +3311,7 @@ static void copy_frame_data(struct obs_source_frame *dst, case VIDEO_FORMAT_I42A: case VIDEO_FORMAT_YUVA: + case VIDEO_FORMAT_YA2L: copy_frame_data_plane(dst, src, 0, dst->height); copy_frame_data_plane(dst, src, 1, dst->height); copy_frame_data_plane(dst, src, 2, dst->height); diff --git a/libobs/obs-video.c b/libobs/obs-video.c index a9c2527d0..657b79ab9 100644 --- a/libobs/obs-video.c +++ b/libobs/obs-video.c @@ -727,10 +727,13 @@ static void set_gpu_converted_data(struct obs_core_video *video, case VIDEO_FORMAT_BGRX: case VIDEO_FORMAT_Y800: case VIDEO_FORMAT_BGR3: + case VIDEO_FORMAT_I412: case VIDEO_FORMAT_I422: + case VIDEO_FORMAT_I210: case VIDEO_FORMAT_I40A: case VIDEO_FORMAT_I42A: case VIDEO_FORMAT_YUVA: + case VIDEO_FORMAT_YA2L: case VIDEO_FORMAT_AYUV: /* unimplemented */ ; diff --git a/libobs/obs.c b/libobs/obs.c index 913277fe0..9789b7716 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -379,6 +379,9 @@ static bool obs_init_textures(struct obs_video_info *ovi) switch (ovi->output_format) { case VIDEO_FORMAT_I010: case VIDEO_FORMAT_P010: + case VIDEO_FORMAT_I210: + case VIDEO_FORMAT_I412: + case VIDEO_FORMAT_YA2L: format = GS_RGBA16F; }