obs-transitions: Add HDR support to stinger

Regular SDR/HDR stingers, and SDR track matte should work. HDR track
matte might work, but would take a carefully crafted video that takes
the SDR white level into account, and this hasn't been tested.
This commit is contained in:
jpark37 2022-07-28 00:48:07 -07:00 committed by Jim
parent 8cb57aac1f
commit 6a871d3f66
2 changed files with 133 additions and 13 deletions

View File

@ -33,9 +33,9 @@ float3 srgb_nonlinear_to_linear(float3 v)
return float3(srgb_nonlinear_to_linear_channel(v.r), srgb_nonlinear_to_linear_channel(v.g), srgb_nonlinear_to_linear_channel(v.b));
}
float4 PSStingerMatte(VertData v_in) : TARGET
float4 StingerMatte(VertData f_in)
{
float2 uv = v_in.uv;
float2 uv = f_in.uv;
float4 a_color = a_tex.Sample(textureSampler, uv);
float4 b_color = b_tex.Sample(textureSampler, uv);
float4 matte_color = matte_tex.Sample(textureSampler, uv);
@ -51,15 +51,36 @@ float4 PSStingerMatte(VertData v_in) : TARGET
matte_luma = (invert_matte ? (1.0 - matte_luma) : matte_luma);
float4 rgba = lerp(a_color, b_color, matte_luma);
return rgba;
}
float4 PSStingerMatte(VertData f_in) : TARGET
{
float4 rgba = StingerMatte(f_in);
rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb);
return rgba;
}
float4 PSStingerMatteLinear(VertData f_in) : TARGET
{
float4 rgba = StingerMatte(f_in);
return rgba;
}
technique StingerMatte
{
pass
{
vertex_shader = VSDefault(v_in);
pixel_shader = PSStingerMatte(v_in);
pixel_shader = PSStingerMatte(f_in);
}
}
technique StingerMatteLinear
{
pass
{
vertex_shader = VSDefault(v_in);
pixel_shader = PSStingerMatteLinear(f_in);
}
}

View File

@ -256,7 +256,16 @@ static void stinger_matte_render(void *data, gs_texture_t *a, gs_texture_t *b,
float scale_x = (float)cx / matte_cx;
float scale_y = (float)cy / matte_cy;
if (gs_texrender_begin(s->matte_tex, cx, cy)) {
const enum gs_color_space space =
obs_source_get_color_space(matte_source, 0, NULL);
enum gs_color_format format = gs_get_format_from_space(space);
if (gs_texrender_get_format(s->matte_tex) != format) {
gs_texrender_destroy(s->matte_tex);
s->matte_tex = gs_texrender_create(format, GS_ZS_NONE);
}
if (gs_texrender_begin_with_color_space(s->matte_tex, cx, cy,
space)) {
gs_matrix_scale3f(scale_x, scale_y, 1.0f);
gs_matrix_translate3f(width_offset, height_offset,
0.0f);
@ -273,13 +282,23 @@ static void stinger_matte_render(void *data, gs_texture_t *a, gs_texture_t *b,
const bool previous = gs_framebuffer_srgb_enabled();
gs_enable_framebuffer_srgb(true);
gs_effect_set_texture(s->ep_a_tex, a);
gs_effect_set_texture(s->ep_b_tex, b);
/* texture setters look reversed, but they aren't */
const char *tech_name = "StingerMatte";
if (gs_get_color_space() == GS_CS_SRGB) {
/* users want nonlinear fade */
gs_effect_set_texture(s->ep_a_tex, a);
gs_effect_set_texture(s->ep_b_tex, b);
} else {
/* nonlinear fade is too wrong, so use linear fade */
gs_effect_set_texture_srgb(s->ep_a_tex, a);
gs_effect_set_texture_srgb(s->ep_b_tex, b);
tech_name = "StingerMatteLinear";
}
gs_effect_set_texture(s->ep_matte_tex,
gs_texrender_get_texture(s->matte_tex));
gs_effect_set_bool(s->ep_invert_matte, s->invert_matte);
while (gs_effect_loop(s->matte_effect, "StingerMatte"))
while (gs_effect_loop(s->matte_effect, tech_name))
gs_draw_sprite(NULL, 0, cx, cy);
gs_enable_framebuffer_srgb(previous);
@ -289,9 +308,16 @@ static void stinger_matte_render(void *data, gs_texture_t *a, gs_texture_t *b,
static void stinger_texrender(struct stinger_info *s, uint32_t source_cx,
uint32_t source_cy, uint32_t media_cx,
uint32_t media_cy)
uint32_t media_cy, enum gs_color_space space)
{
if (gs_texrender_begin(s->stinger_tex, source_cx, source_cy)) {
enum gs_color_format format = gs_get_format_from_space(space);
if (gs_texrender_get_format(s->stinger_tex) != format) {
gs_texrender_destroy(s->stinger_tex);
s->stinger_tex = gs_texrender_create(format, GS_ZS_NONE);
}
if (gs_texrender_begin_with_color_space(s->stinger_tex, source_cx,
source_cy, space)) {
float cx = (float)media_cx / s->matte_width_factor;
float cy = (float)media_cy / s->matte_height_factor;
@ -306,6 +332,50 @@ static void stinger_texrender(struct stinger_info *s, uint32_t source_cx,
}
}
static const char *
get_tech_name_and_multiplier(enum gs_color_space current_space,
enum gs_color_space source_space,
float *multiplier)
{
const char *tech_name = "Draw";
*multiplier = 1.f;
switch (source_space) {
case GS_CS_SRGB:
case GS_CS_SRGB_16F:
switch (current_space) {
case GS_CS_709_SCRGB:
tech_name = "DrawMultiply";
*multiplier = obs_get_video_sdr_white_level() / 80.0f;
}
break;
case GS_CS_709_EXTENDED:
switch (current_space) {
case GS_CS_SRGB:
case GS_CS_SRGB_16F:
tech_name = "DrawTonemap";
break;
case GS_CS_709_SCRGB:
tech_name = "DrawMultiply";
*multiplier = obs_get_video_sdr_white_level() / 80.0f;
}
break;
case GS_CS_709_SCRGB:
switch (current_space) {
case GS_CS_SRGB:
case GS_CS_SRGB_16F:
tech_name = "DrawMultiplyTonemap";
*multiplier = 80.0f / obs_get_video_sdr_white_level();
break;
case GS_CS_709_EXTENDED:
tech_name = "DrawMultiply";
*multiplier = 80.0f / obs_get_video_sdr_white_level();
}
}
return tech_name;
}
static void stinger_video_render(void *data, gs_effect_t *effect)
{
struct stinger_info *s = data;
@ -353,21 +423,38 @@ static void stinger_video_render(void *data, gs_effect_t *effect)
return;
if (s->do_texrender) {
stinger_texrender(s, source_cx, source_cy, media_cx, media_cy);
const enum gs_color_space space =
obs_source_get_color_space(s->media_source, 0, NULL);
stinger_texrender(s, source_cx, source_cy, media_cx, media_cy,
space);
const bool previous = gs_framebuffer_srgb_enabled();
gs_enable_framebuffer_srgb(true);
float multiplier;
const char *technique = get_tech_name_and_multiplier(
gs_get_color_space(), space, &multiplier);
gs_effect_t *e = obs_get_base_effect(OBS_EFFECT_DEFAULT);
gs_eparam_t *p = gs_effect_get_param_by_name(e, "image");
gs_eparam_t *p_image = gs_effect_get_param_by_name(e, "image");
gs_eparam_t *p_multiplier =
gs_effect_get_param_by_name(e, "multiplier");
gs_texture_t *tex = gs_texrender_get_texture(s->stinger_tex);
gs_effect_set_texture(p, tex);
while (gs_effect_loop(e, "Draw"))
gs_effect_set_texture_srgb(p_image, tex);
gs_effect_set_float(p_multiplier, multiplier);
while (gs_effect_loop(e, technique))
gs_draw_sprite(NULL, 0, source_cx, source_cy);
gs_enable_framebuffer_srgb(previous);
} else {
const bool previous = gs_set_linear_srgb(true);
gs_matrix_push();
gs_matrix_scale3f(source_cxf / (float)media_cx,
source_cyf / (float)media_cy, 1.0f);
obs_source_video_render(s->media_source);
gs_matrix_pop();
gs_set_linear_srgb(previous);
}
UNUSED_PARAMETER(effect);
@ -792,6 +879,17 @@ static obs_missing_files_t *stinger_missing_files(void *data)
return files;
}
static enum gs_color_space
stinger_get_color_space(void *data, size_t count,
const enum gs_color_space *preferred_spaces)
{
UNUSED_PARAMETER(count);
UNUSED_PARAMETER(preferred_spaces);
struct stinger_info *s = data;
return obs_transition_video_get_color_space(s->source);
}
struct obs_source_info stinger_transition = {
.id = "obs_stinger_transition",
.type = OBS_SOURCE_TYPE_TRANSITION,
@ -809,4 +907,5 @@ struct obs_source_info stinger_transition = {
.enum_all_sources = stinger_enum_all_sources,
.transition_start = stinger_transition_start,
.transition_stop = stinger_transition_stop,
.video_get_color_space = stinger_get_color_space,
};