diff --git a/docs/sphinx/reference-libobs-media-io.rst b/docs/sphinx/reference-libobs-media-io.rst index 9378bff3b..2880722f9 100644 --- a/docs/sphinx/reference-libobs-media-io.rst +++ b/docs/sphinx/reference-libobs-media-io.rst @@ -60,6 +60,8 @@ Video Handler - VIDEO_FORMAT_V210 + - VIDEO_FORMAT_R10L + --------------------- .. enum:: video_trc diff --git a/docs/sphinx/reference-outputs.rst b/docs/sphinx/reference-outputs.rst index da53c285a..5433eb057 100644 --- a/docs/sphinx/reference-outputs.rst +++ b/docs/sphinx/reference-outputs.rst @@ -827,6 +827,9 @@ Functions used by outputs /* packed 4:2:2 format, 10 bpp */ VIDEO_FORMAT_V210, + + /* packed uncompressed 10-bit format */ + VIDEO_FORMAT_R10L, }; enum video_colorspace { diff --git a/docs/sphinx/reference-sources.rst b/docs/sphinx/reference-sources.rst index 34ae15fb4..fbe356199 100644 --- a/docs/sphinx/reference-sources.rst +++ b/docs/sphinx/reference-sources.rst @@ -1470,6 +1470,9 @@ Functions used by sources /* packed 4:2:2 format, 10 bpp */ VIDEO_FORMAT_V210, + + /* packed uncompressed 10-bit format */ + VIDEO_FORMAT_R10L, }; struct obs_source_frame { diff --git a/libobs/data/format_conversion.effect b/libobs/data/format_conversion.effect index 28f7160fd..6d1088c75 100644 --- a/libobs/data/format_conversion.effect +++ b/libobs/data/format_conversion.effect @@ -1054,6 +1054,72 @@ float3 PSBGR3_Full(FragPos frag_in) : TARGET return rgb; } +float3 compute_r10l_reverse(float2 pos, bool limited) +{ + float4 xyzw = image.Load(int3(pos, 0)).bgra; + uint4 xyzw255 = uint4(mad(xyzw, 255., 0.5)); + uint r = ((xyzw255.z & 0xC0u) >> 6) | (xyzw255.w << 2); + uint g = ((xyzw255.y & 0xFu) >> 4) | ((xyzw255.z & 0x3Fu) << 4); + uint b = (xyzw255.x >> 2) | ((xyzw255.y & 0xFu) << 6); + float3 rgb = float3(uint3(r, g, b)); + if (limited) + { + rgb = mad(rgb, 1. / 876., -16. / 219.); + } + else + { + rgb /= 1023.; + } + + return rgb; +} + +float4 PSR10L_SRGB_Full_Reverse(FragPos frag_in) : TARGET +{ + float3 rgb = compute_r10l_reverse(frag_in.pos.xy, false); + rgb = srgb_nonlinear_to_linear(rgb); + return float4(rgb, 1.); +} + +float4 PSR10L_PQ_2020_709_Full_Reverse(FragPos frag_in) : TARGET +{ + float3 pq = compute_r10l_reverse(frag_in.pos.xy, false); + float3 hdr2020 = st2084_to_linear_eetf(pq, hdr_lw, hdr_lmax) * maximum_over_sdr_white_nits; + float3 rgb = rec2020_to_rec709(hdr2020); + return float4(rgb, 1.); +} + +float4 PSR10L_HLG_2020_709_Full_Reverse(FragPos frag_in) : TARGET +{ + float3 hlg = compute_r10l_reverse(frag_in.pos.xy, false); + float3 hdr2020 = hlg_to_linear(hlg, hlg_exponent) * maximum_over_sdr_white_nits; + float3 rgb = rec2020_to_rec709(hdr2020); + return float4(rgb, 1.); +} + +float4 PSR10L_SRGB_Limited_Reverse(FragPos frag_in) : TARGET +{ + float3 rgb = compute_r10l_reverse(frag_in.pos.xy, true); + rgb = srgb_nonlinear_to_linear(rgb); + return float4(rgb, 1.); +} + +float4 PSR10L_PQ_2020_709_Limited_Reverse(FragPos frag_in) : TARGET +{ + float3 pq = compute_r10l_reverse(frag_in.pos.xy, true); + float3 hdr2020 = st2084_to_linear_eetf(pq, hdr_lw, hdr_lmax) * maximum_over_sdr_white_nits; + float3 rgb = rec2020_to_rec709(hdr2020); + return float4(rgb, 1.); +} + +float4 PSR10L_HLG_2020_709_Limited_Reverse(FragPos frag_in) : TARGET +{ + float3 hlg = compute_r10l_reverse(frag_in.pos.xy, true); + float3 hdr2020 = hlg_to_linear(hlg, hlg_exponent) * maximum_over_sdr_white_nits; + float3 rgb = rec2020_to_rec709(hdr2020); + return float4(rgb, 1.); +} + technique Planar_Y { pass @@ -1701,3 +1767,57 @@ technique BGR3_Full pixel_shader = PSBGR3_Full(frag_in); } } + +technique R10L_SRGB_Full_Reverse +{ + pass + { + vertex_shader = VSPos(id); + pixel_shader = PSR10L_SRGB_Full_Reverse(frag_in); + } +} + +technique R10L_PQ_2020_709_Full_Reverse +{ + pass + { + vertex_shader = VSPos(id); + pixel_shader = PSR10L_PQ_2020_709_Full_Reverse(frag_in); + } +} + +technique R10L_HLG_2020_709_Full_Reverse +{ + pass + { + vertex_shader = VSPos(id); + pixel_shader = PSR10L_HLG_2020_709_Full_Reverse(frag_in); + } +} + +technique R10L_SRGB_Limited_Reverse +{ + pass + { + vertex_shader = VSPos(id); + pixel_shader = PSR10L_SRGB_Limited_Reverse(frag_in); + } +} + +technique R10L_PQ_2020_709_Limited_Reverse +{ + pass + { + vertex_shader = VSPos(id); + pixel_shader = PSR10L_PQ_2020_709_Limited_Reverse(frag_in); + } +} + +technique R10L_HLG_2020_709_Limited_Reverse +{ + pass + { + vertex_shader = VSPos(id); + pixel_shader = PSR10L_HLG_2020_709_Limited_Reverse(frag_in); + } +} diff --git a/libobs/media-io/video-frame.c b/libobs/media-io/video-frame.c index ebf51dafa..5dc761d8d 100644 --- a/libobs/media-io/video-frame.c +++ b/libobs/media-io/video-frame.c @@ -339,6 +339,13 @@ void video_frame_init(struct video_frame *frame, enum video_format format, frame->linesize[0] = adjusted_width; break; } + + case VIDEO_FORMAT_R10L: + size = width * height * 4; + ALIGN_SIZE(size, alignment); + frame->data[0] = bmalloc(size); + frame->linesize[0] = width * 4; + break; } } @@ -372,6 +379,7 @@ void video_frame_copy(struct video_frame *dst, const struct video_frame *src, case VIDEO_FORMAT_BGR3: case VIDEO_FORMAT_AYUV: case VIDEO_FORMAT_V210: + case VIDEO_FORMAT_R10L: memcpy(dst->data[0], src->data[0], src->linesize[0] * cy); break; diff --git a/libobs/media-io/video-io.h b/libobs/media-io/video-io.h index 9990ed998..41cad67a5 100644 --- a/libobs/media-io/video-io.h +++ b/libobs/media-io/video-io.h @@ -91,6 +91,9 @@ enum video_format { /* packed 4:2:2 format, 10 bpp */ VIDEO_FORMAT_V210, + + /* packed uncompressed 10-bit format */ + VIDEO_FORMAT_R10L, }; enum video_trc { @@ -164,6 +167,7 @@ static inline bool format_is_yuv(enum video_format format) case VIDEO_FORMAT_BGRX: case VIDEO_FORMAT_Y800: case VIDEO_FORMAT_BGR3: + case VIDEO_FORMAT_R10L: return false; } @@ -221,6 +225,8 @@ static inline const char *get_video_format_name(enum video_format format) return "P416"; case VIDEO_FORMAT_V210: return "v210"; + case VIDEO_FORMAT_R10L: + return "R10l"; case VIDEO_FORMAT_NONE:; } diff --git a/libobs/media-io/video-matrices.c b/libobs/media-io/video-matrices.c index 7b3d4b682..78712d431 100644 --- a/libobs/media-io/video-matrices.c +++ b/libobs/media-io/video-matrices.c @@ -264,6 +264,7 @@ bool video_format_get_parameters_for_format(enum video_colorspace color_space, case VIDEO_FORMAT_P010: case VIDEO_FORMAT_I210: case VIDEO_FORMAT_V210: + case VIDEO_FORMAT_R10L: bpc = 10; break; case VIDEO_FORMAT_I412: diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index 4187042dd..ce2fa4dc6 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -963,6 +963,7 @@ convert_video_format(enum video_format format, enum video_trc trc) case VIDEO_FORMAT_P216: case VIDEO_FORMAT_P416: case VIDEO_FORMAT_V210: + case VIDEO_FORMAT_R10L: return GS_RGBA16F; default: return GS_BGRX; diff --git a/libobs/obs-source.c b/libobs/obs-source.c index 6781f1537..6e0c22843 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -1592,6 +1592,7 @@ enum convert_type { CONVERT_I010, CONVERT_P010, CONVERT_V210, + CONVERT_R10L, }; static inline enum convert_type get_convert_type(enum video_format format, @@ -1652,6 +1653,9 @@ static inline enum convert_type get_convert_type(enum video_format format, case VIDEO_FORMAT_V210: return CONVERT_V210; + case VIDEO_FORMAT_R10L: + return CONVERT_R10L; + case VIDEO_FORMAT_P216: case VIDEO_FORMAT_P416: /* Unimplemented */ @@ -1959,6 +1963,16 @@ static inline bool set_v210_sizes(struct obs_source *source, return true; } +static inline bool set_r10l_sizes(struct obs_source *source, + const struct obs_source_frame *frame) +{ + source->async_convert_width[0] = frame->width; + source->async_convert_height[0] = frame->height; + source->async_texture_formats[0] = GS_BGRA_UNORM; + source->async_channel_count = 1; + return true; +} + static inline bool init_gpu_conversion(struct obs_source *source, const struct obs_source_frame *frame) { @@ -2019,6 +2033,9 @@ static inline bool init_gpu_conversion(struct obs_source *source, case CONVERT_V210: return set_v210_sizes(source, frame); + case CONVERT_R10L: + return set_r10l_sizes(source, frame); + case CONVERT_NONE: assert(false && "No conversion requested"); break; @@ -2112,6 +2129,7 @@ static void upload_raw_frame(gs_texture_t *tex[MAX_AV_PLANES], case CONVERT_I010: case CONVERT_P010: case CONVERT_V210: + case CONVERT_R10L: for (size_t c = 0; c < MAX_AV_PLANES; c++) { if (tex[c]) gs_texture_set_image(tex[c], frame->data[c], @@ -2245,6 +2263,20 @@ static const char *select_conversion_technique(enum video_format format, } } + case VIDEO_FORMAT_R10L: { + switch (trc) { + case VIDEO_TRC_PQ: + return full_range ? "R10L_PQ_2020_709_Full_Reverse" + : "R10L_PQ_2020_709_Limited_Reverse"; + case VIDEO_TRC_HLG: + return full_range ? "R10L_HLG_2020_709_Full_Reverse" + : "R10L_HLG_2020_709_Limited_Reverse"; + default: + return full_range ? "R10L_SRGB_Full_Reverse" + : "R10L_SRGB_Limited_Reverse"; + } + } + case VIDEO_FORMAT_BGRA: case VIDEO_FORMAT_BGRX: case VIDEO_FORMAT_RGBA: @@ -3462,6 +3494,7 @@ static void copy_frame_data(struct obs_source_frame *dst, case VIDEO_FORMAT_BGR3: case VIDEO_FORMAT_AYUV: case VIDEO_FORMAT_V210: + case VIDEO_FORMAT_R10L: copy_frame_data_plane(dst, src, 0, dst->height); break; diff --git a/libobs/obs-video.c b/libobs/obs-video.c index b12717a5b..21f7ab17d 100644 --- a/libobs/obs-video.c +++ b/libobs/obs-video.c @@ -787,6 +787,7 @@ static void set_gpu_converted_data(struct video_frame *output, case VIDEO_FORMAT_YA2L: case VIDEO_FORMAT_AYUV: case VIDEO_FORMAT_V210: + case VIDEO_FORMAT_R10L: /* unimplemented */ ; }