diff --git a/UI/frontend-plugins/decklink-output-ui/decklink-ui-main.cpp b/UI/frontend-plugins/decklink-output-ui/decklink-ui-main.cpp
index a937255e5..29819de17 100644
--- a/UI/frontend-plugins/decklink-output-ui/decklink-ui-main.cpp
+++ b/UI/frontend-plugins/decklink-output-ui/decklink-ui-main.cpp
@@ -19,11 +19,9 @@ bool shutting_down = false;
bool main_output_running = false;
bool preview_output_running = false;
-obs_output_t *output;
-
constexpr size_t STAGE_BUFFER_COUNT = 3;
-struct preview_output {
+struct decklink_ui_output {
bool enabled;
obs_source_t *current_source;
obs_output_t *output;
@@ -40,7 +38,7 @@ struct preview_output {
obs_video_info ovi;
};
-static struct preview_output context = {0};
+static struct decklink_ui_output context = {0};
OBSData load_settings()
{
@@ -58,10 +56,28 @@ OBSData load_settings()
return nullptr;
}
+static void decklink_ui_tick(void *param, float sec);
+static void decklink_ui_render(void *param);
+
void output_stop()
{
- obs_output_stop(output);
- obs_output_release(output);
+ obs_output_stop(context.output);
+ obs_output_release(context.output);
+
+ obs_remove_main_rendered_callback(decklink_ui_render, &context);
+
+ obs_enter_graphics();
+ for (gs_stagesurf_t *&surf : context.stagesurfaces) {
+ gs_stagesurface_destroy(surf);
+ surf = nullptr;
+ }
+ gs_texrender_destroy(context.texrender);
+ context.texrender = nullptr;
+ obs_leave_graphics();
+
+ video_output_close(context.video_queue);
+ obs_remove_tick_callback(decklink_ui_tick, &context);
+
main_output_running = false;
if (!shutting_down)
@@ -73,10 +89,51 @@ void output_start()
OBSData settings = load_settings();
if (settings != nullptr) {
- output = obs_output_create("decklink_output", "decklink_output",
- settings, NULL);
+ obs_add_tick_callback(decklink_ui_tick, &context);
+ context.output = obs_output_create(
+ "decklink_output", "decklink_output", settings, NULL);
- bool started = obs_output_start(output);
+ obs_get_video_info(&context.ovi);
+
+ const struct video_scale_info *const conversion =
+ obs_output_get_video_conversion(context.output);
+ const uint32_t width = conversion->width;
+ const uint32_t height = conversion->height;
+
+ obs_enter_graphics();
+ context.texrender_premultiplied = nullptr;
+ context.texrender = gs_texrender_create(GS_BGRA, GS_ZS_NONE);
+ for (gs_stagesurf_t *&surf : context.stagesurfaces)
+ surf = gs_stagesurface_create(width, height, GS_BGRA);
+ obs_leave_graphics();
+
+ for (bool &written : context.surf_written)
+ written = false;
+
+ context.stage_index = 0;
+
+ const video_output_info *mainVOI =
+ video_output_get_info(obs_get_video());
+
+ video_output_info vi = {0};
+ vi.format = VIDEO_FORMAT_BGRA;
+ vi.width = width;
+ vi.height = height;
+ vi.fps_den = context.ovi.fps_den;
+ vi.fps_num = context.ovi.fps_num;
+ vi.cache_size = 16;
+ vi.colorspace = mainVOI->colorspace;
+ vi.range = VIDEO_RANGE_FULL;
+ vi.name = "decklink_output";
+
+ video_output_open(&context.video_queue, &vi);
+
+ context.current_source = nullptr;
+ obs_add_main_rendered_callback(decklink_ui_render, &context);
+
+ obs_output_set_media(context.output, context.video_queue,
+ obs_get_audio());
+ bool started = obs_output_start(context.output);
main_output_running = started;
@@ -113,13 +170,12 @@ OBSData load_preview_settings()
}
void on_preview_scene_changed(enum obs_frontend_event event, void *param);
-void render_preview_source(void *param, uint32_t cx, uint32_t cy);
-static void preview_tick(void *param, float sec)
+static void decklink_ui_tick(void *param, float sec)
{
UNUSED_PARAMETER(sec);
- auto ctx = (struct preview_output *)param;
+ auto ctx = (struct decklink_ui_output *)param;
if (ctx->texrender_premultiplied)
gs_texrender_reset(ctx->texrender_premultiplied);
@@ -132,7 +188,7 @@ void preview_output_stop()
obs_output_stop(context.output);
obs_output_release(context.output);
- obs_remove_main_render_callback(render_preview_source, &context);
+ obs_remove_main_rendered_callback(decklink_ui_render, &context);
obs_frontend_remove_event_callback(on_preview_scene_changed, &context);
obs_source_release(context.current_source);
@@ -149,7 +205,7 @@ void preview_output_stop()
obs_leave_graphics();
video_output_close(context.video_queue);
- obs_remove_tick_callback(preview_tick, &context);
+ obs_remove_tick_callback(decklink_ui_tick, &context);
preview_output_running = false;
@@ -162,7 +218,7 @@ void preview_output_start()
OBSData settings = load_preview_settings();
if (settings != nullptr) {
- obs_add_tick_callback(preview_tick, &context);
+ obs_add_tick_callback(decklink_ui_tick, &context);
context.output = obs_output_create("decklink_output",
"decklink_preview_output",
settings, NULL);
@@ -212,7 +268,7 @@ void preview_output_start()
context.current_source =
obs_frontend_get_current_scene();
}
- obs_add_main_render_callback(render_preview_source, &context);
+ obs_add_main_rendered_callback(decklink_ui_render, &context);
obs_output_set_media(context.output, context.video_queue,
obs_get_audio());
@@ -237,7 +293,7 @@ void preview_output_toggle()
void on_preview_scene_changed(enum obs_frontend_event event, void *param)
{
- auto ctx = (struct preview_output *)param;
+ auto ctx = (struct decklink_ui_output *)param;
switch (event) {
case OBS_FRONTEND_EVENT_STUDIO_MODE_ENABLED:
case OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED:
@@ -259,27 +315,33 @@ void on_preview_scene_changed(enum obs_frontend_event event, void *param)
}
}
-void render_preview_source(void *param, uint32_t cx, uint32_t cy)
+static void decklink_ui_render(void *param)
{
- UNUSED_PARAMETER(cx);
- UNUSED_PARAMETER(cy);
+ auto *const ctx = (struct decklink_ui_output *)param;
- auto ctx = (struct preview_output *)param;
+ uint32_t width = 0;
+ uint32_t height = 0;
+ gs_texture_t *tex = nullptr;
- if (!ctx->current_source)
- return;
+ if (main_output_running) {
+ tex = obs_get_main_texture();
+ if (!tex)
+ return;
- const uint32_t width = obs_source_get_base_width(ctx->current_source);
- const uint32_t height = obs_source_get_base_height(ctx->current_source);
+ width = gs_texture_get_width(tex);
+ height = gs_texture_get_height(tex);
+ } else if (preview_output_running) {
+ if (!ctx->current_source)
+ return;
- const struct video_scale_info *const conversion =
- obs_output_get_video_conversion(context.output);
- const uint32_t scaled_width = conversion->width;
- const uint32_t scaled_height = conversion->height;
+ width = obs_source_get_base_width(ctx->current_source);
+ height = obs_source_get_base_height(ctx->current_source);
+
+ gs_texrender_t *const texrender_premultiplied =
+ ctx->texrender_premultiplied;
+ if (!gs_texrender_begin(texrender_premultiplied, width, height))
+ return;
- gs_texrender_t *const texrender_premultiplied =
- ctx->texrender_premultiplied;
- if (gs_texrender_begin(texrender_premultiplied, width, height)) {
struct vec4 background;
vec4_zero(&background);
@@ -295,71 +357,70 @@ void render_preview_source(void *param, uint32_t cx, uint32_t cy)
gs_blend_state_pop();
gs_texrender_end(texrender_premultiplied);
- if (gs_texrender_begin(ctx->texrender, scaled_width,
- scaled_height)) {
- const bool previous = gs_framebuffer_srgb_enabled();
- gs_enable_framebuffer_srgb(true);
- gs_enable_blending(false);
+ tex = gs_texrender_get_texture(texrender_premultiplied);
+ } else {
+ return;
+ }
- gs_texture_t *const tex = gs_texrender_get_texture(
- texrender_premultiplied);
- gs_effect_t *const effect =
- obs_get_base_effect(OBS_EFFECT_DEFAULT);
- gs_effect_set_texture_srgb(
- gs_effect_get_param_by_name(effect, "image"),
- tex);
- while (gs_effect_loop(effect, "DrawAlphaDivide")) {
- gs_draw_sprite(tex, 0, 0, 0);
- }
+ const struct video_scale_info *const conversion =
+ obs_output_get_video_conversion(context.output);
+ const uint32_t scaled_width = conversion->width;
+ const uint32_t scaled_height = conversion->height;
- gs_enable_blending(true);
- gs_enable_framebuffer_srgb(previous);
+ if (!gs_texrender_begin(ctx->texrender, scaled_width, scaled_height))
+ return;
- gs_texrender_end(ctx->texrender);
- }
+ const bool previous = gs_framebuffer_srgb_enabled();
+ gs_enable_framebuffer_srgb(true);
+ gs_enable_blending(false);
- const size_t write_stage_index = ctx->stage_index;
- gs_stage_texture(ctx->stagesurfaces[write_stage_index],
- gs_texrender_get_texture(ctx->texrender));
- ctx->surf_written[write_stage_index] = true;
+ gs_effect_t *const effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
+ gs_effect_set_texture_srgb(gs_effect_get_param_by_name(effect, "image"),
+ tex);
+ while (gs_effect_loop(effect, "DrawAlphaDivide")) {
+ gs_draw_sprite(tex, 0, 0, 0);
+ }
- const size_t read_stage_index =
- (write_stage_index + 1) % STAGE_BUFFER_COUNT;
- if (ctx->surf_written[read_stage_index]) {
- struct video_frame output_frame;
- if (video_output_lock_frame(ctx->video_queue,
- &output_frame, 1,
- os_gettime_ns())) {
- gs_stagesurf_t *const read_surf =
- ctx->stagesurfaces[read_stage_index];
- if (gs_stagesurface_map(read_surf,
- &ctx->video_data,
- &ctx->video_linesize)) {
- uint32_t linesize =
- output_frame.linesize[0];
- for (uint32_t i = 0; i < scaled_height;
- i++) {
- uint32_t dst_offset =
- linesize * i;
- uint32_t src_offset =
- ctx->video_linesize * i;
- memcpy(output_frame.data[0] +
- dst_offset,
- ctx->video_data +
- src_offset,
- linesize);
- }
+ gs_enable_blending(true);
+ gs_enable_framebuffer_srgb(previous);
- gs_stagesurface_unmap(read_surf);
- ctx->video_data = nullptr;
+ gs_texrender_end(ctx->texrender);
+
+ const size_t write_stage_index = ctx->stage_index;
+ gs_stage_texture(ctx->stagesurfaces[write_stage_index],
+ gs_texrender_get_texture(ctx->texrender));
+ ctx->surf_written[write_stage_index] = true;
+
+ const size_t read_stage_index =
+ (write_stage_index + 1) % STAGE_BUFFER_COUNT;
+ if (ctx->surf_written[read_stage_index]) {
+ struct video_frame output_frame;
+ if (video_output_lock_frame(ctx->video_queue, &output_frame, 1,
+ os_gettime_ns())) {
+ gs_stagesurf_t *const read_surf =
+ ctx->stagesurfaces[read_stage_index];
+ if (gs_stagesurface_map(read_surf, &ctx->video_data,
+ &ctx->video_linesize)) {
+ uint32_t linesize = output_frame.linesize[0];
+ for (uint32_t i = 0; i < scaled_height; i++) {
+ uint32_t dst_offset = linesize * i;
+ uint32_t src_offset =
+ ctx->video_linesize * i;
+ memcpy(output_frame.data[0] +
+ dst_offset,
+ ctx->video_data + src_offset,
+ linesize);
}
- video_output_unlock_frame(ctx->video_queue);
+ gs_stagesurface_unmap(read_surf);
+ ctx->video_data = nullptr;
}
- }
- ctx->stage_index = read_stage_index;
+ video_output_unlock_frame(ctx->video_queue);
+ }
}
+
+ ctx->stage_index = read_stage_index;
}
void addOutputUI(void)
diff --git a/UI/frontend-plugins/decklink-output-ui/forms/output.ui b/UI/frontend-plugins/decklink-output-ui/forms/output.ui
index 39971d35d..945bf5eb6 100644
--- a/UI/frontend-plugins/decklink-output-ui/forms/output.ui
+++ b/UI/frontend-plugins/decklink-output-ui/forms/output.ui
@@ -7,7 +7,7 @@
0
0
785
- 497
+ 484
@@ -106,26 +106,6 @@
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
- -
-
-
- Keyer output requires BGRA mode in advanced settings.
-
-
-