diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index e36beeb8e..9993707bd 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -193,6 +193,7 @@ struct obs_source { signal_handler_t signals; proc_handler_t procs; + /* signals to call the source update in the video thread */ bool defer_update; /* ensures show/hide are only called once */ @@ -237,9 +238,14 @@ struct obs_source { float transition_volume; /* async video data */ - texture_t output_texture; + texture_t async_texture; + enum video_format async_format; + float async_color_matrix[16]; + bool async_flip; DARRAY(struct source_frame*) video_frames; pthread_mutex_t video_mutex; + uint32_t async_width; + uint32_t async_height; /* filters */ struct obs_source *filter_parent; diff --git a/libobs/obs-module.c b/libobs/obs-module.c index b449c5113..fd4cdbcec 100644 --- a/libobs/obs-module.c +++ b/libobs/obs-module.c @@ -128,8 +128,9 @@ void obs_register_source_s(const struct obs_source_info *info, size_t size) CHECK_REQUIRED_VAL(info, create, obs_register_source); CHECK_REQUIRED_VAL(info, destroy, obs_register_source); - if (info->type == OBS_SOURCE_TYPE_INPUT && - info->output_flags & OBS_SOURCE_VIDEO) { + if (info->type == OBS_SOURCE_TYPE_INPUT && + (info->output_flags & OBS_SOURCE_VIDEO) != 0 && + (info->output_flags & OBS_SOURCE_ASYNC) == 0) { CHECK_REQUIRED_VAL(info, getwidth, obs_register_source); CHECK_REQUIRED_VAL(info, getheight, obs_register_source); } diff --git a/libobs/obs-source.c b/libobs/obs-source.c index 564ccb565..b335a6761 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -240,7 +240,7 @@ static void obs_source_destroy(struct obs_source *source) source_frame_destroy(source->video_frames.array[i]); gs_entercontext(obs->video.graphics); - texture_destroy(source->output_texture); + texture_destroy(source->async_texture); gs_leavecontext(); if (source->data) @@ -529,7 +529,7 @@ static inline void handle_ts_jump(obs_source_t source, uint64_t expected, source->name, diff, expected, ts); /* if has video, ignore audio data until reset */ - if (source->info.output_flags & OBS_SOURCE_ASYNC_VIDEO) + if (source->info.output_flags & OBS_SOURCE_ASYNC) os_atomic_dec_long(&source->audio_reset_ref); else reset_audio_timing(source, ts); @@ -577,21 +577,25 @@ static void source_output_audio_line(obs_source_t source, audio_line_output(source->audio_line, &in); } -static bool set_texture_size(obs_source_t source, struct source_frame *frame) +static inline bool set_async_texture_size(struct obs_source *source, + struct source_frame *frame) { - if (source->output_texture) { - uint32_t width = texture_getwidth(source->output_texture); - uint32_t height = texture_getheight(source->output_texture); - - if (width == frame->width && height == frame->height) + if (source->async_texture) { + if (source->async_width == frame->width && + source->async_height == frame->height) return true; } - texture_destroy(source->output_texture); - source->output_texture = gs_create_texture(frame->width, frame->height, + texture_destroy(source->async_texture); + source->async_texture = gs_create_texture(frame->width, frame->height, GS_RGBA, 1, NULL, GS_DYNAMIC); - return source->output_texture != NULL; + if (!source->async_texture) + return false; + + source->async_width = frame->width; + source->async_height = frame->height; + return true; } enum convert_type { @@ -626,11 +630,18 @@ static inline enum convert_type get_convert_type(enum video_format format) return CONVERT_NONE; } -static bool upload_frame(texture_t tex, const struct source_frame *frame) +static bool update_async_texture(struct obs_source *source, + const struct source_frame *frame) { - void *ptr; - uint32_t linesize; + texture_t tex = source->async_texture; enum convert_type type = get_convert_type(frame->format); + void *ptr; + uint32_t linesize; + + source->async_format = frame->format; + source->async_flip = frame->flip; + memcpy(source->async_color_matrix, frame->color_matrix, + sizeof(frame->color_matrix)); if (type == CONVERT_NONE) { texture_setimage(tex, frame->data[0], frame->linesize[0], @@ -663,44 +674,59 @@ static bool upload_frame(texture_t tex, const struct source_frame *frame) return true; } -static void obs_source_draw_texture(texture_t tex, struct source_frame *frame) +static inline void obs_source_draw_texture(struct obs_source *source, + effect_t effect, float *color_matrix) { - effect_t effect = obs->video.default_effect; - bool yuv = format_is_yuv(frame->format); - const char *type = yuv ? "DrawMatrix" : "Draw"; - technique_t tech; - eparam_t param; + texture_t tex = source->async_texture; + eparam_t param; - if (!upload_frame(tex, frame)) - return; - - tech = effect_gettechnique(effect, type); - technique_begin(tech); - technique_beginpass(tech, 0); - - if (yuv) { + if (color_matrix) { param = effect_getparambyname(effect, "color_matrix"); - effect_setval(effect, param, frame->color_matrix, - sizeof(float) * 16); + effect_setval(effect, param, color_matrix, sizeof(float) * 16); } param = effect_getparambyname(effect, "image"); effect_settexture(effect, param, tex); - gs_draw_sprite(tex, frame->flip ? GS_FLIP_V : 0, 0, 0); + gs_draw_sprite(tex, source->async_flip ? GS_FLIP_V : 0, 0, 0); +} - technique_endpass(tech); - technique_end(tech); +static void obs_source_draw_async_texture(struct obs_source *source) +{ + effect_t effect = gs_geteffect(); + bool yuv = format_is_yuv(source->async_format); + const char *type = yuv ? "DrawMatrix" : "Draw"; + bool def_draw = (!effect); + technique_t tech; + + if (def_draw) { + effect = obs_get_default_effect(); + tech = effect_gettechnique(effect, type); + technique_begin(tech); + technique_beginpass(tech, 0); + } + + obs_source_draw_texture(source, effect, + yuv ? source->async_color_matrix : NULL); + + if (def_draw) { + technique_endpass(tech); + technique_end(tech); + } } static void obs_source_render_async_video(obs_source_t source) { struct source_frame *frame = obs_source_getframe(source); - if (!frame) - return; + if (frame) { + if (!set_async_texture_size(source, frame)) + return; + if (!update_async_texture(source, frame)) + return; + } - if (set_texture_size(source, frame)) - obs_source_draw_texture(source->output_texture, frame); + if (source->async_texture) + obs_source_draw_async_texture(source); obs_source_releaseframe(source, frame); } @@ -731,48 +757,53 @@ static inline void obs_source_default_render(obs_source_t source, static inline void obs_source_main_render(obs_source_t source) { - uint32_t flags = source->info.output_flags; - bool color_matrix = (flags & OBS_SOURCE_COLOR_MATRIX) != 0; + uint32_t flags = source->info.output_flags; + bool color_matrix = (flags & OBS_SOURCE_COLOR_MATRIX) != 0; + bool custom_draw = (flags & OBS_SOURCE_CUSTOM_DRAW) != 0; bool default_effect = !source->filter_parent && source->filters.num == 0 && - (flags & OBS_SOURCE_CUSTOM_DRAW) == 0; + !custom_draw; if (default_effect) obs_source_default_render(source, color_matrix); else - source->info.video_render(source->data, NULL); + source->info.video_render(source->data, + custom_draw ? NULL : gs_geteffect()); } void obs_source_video_render(obs_source_t source) { if (!source) return; - if (source->info.video_render) { - if (source->filters.num && !source->rendering_filter) - obs_source_render_filters(source); - else - obs_source_main_render(source); + if (source->filters.num && !source->rendering_filter) + obs_source_render_filters(source); - } else if (source->filter_target) { + else if (source->info.video_render) + obs_source_main_render(source); + + else if (source->filter_target) obs_source_video_render(source->filter_target); - } else { + else obs_source_render_async_video(source); - } } uint32_t obs_source_getwidth(obs_source_t source) { - if (source && source->info.getwidth) + if (!source) return 0; + + if (source->info.getwidth) return source->info.getwidth(source->data); - return 0; + return source->async_width; } uint32_t obs_source_getheight(obs_source_t source) { - if (source && source->info.getheight) + if (!source) return 0; + + if (source->info.getheight) return source->info.getheight(source->data); - return 0; + return source->async_height; } obs_source_t obs_filter_getparent(obs_source_t filter) @@ -1102,7 +1133,7 @@ void obs_source_output_audio(obs_source_t source, output = filter_async_audio(source, &source->audio_data); if (output) { - bool async = (flags & OBS_SOURCE_ASYNC_VIDEO) != 0; + bool async = (flags & OBS_SOURCE_ASYNC) != 0; pthread_mutex_lock(&source->audio_mutex); diff --git a/libobs/obs-source.h b/libobs/obs-source.h index 55c4f9f3f..1dd297059 100644 --- a/libobs/obs-source.h +++ b/libobs/obs-source.h @@ -23,6 +23,7 @@ extern "C" { #endif + enum obs_source_type { OBS_SOURCE_TYPE_INPUT, OBS_SOURCE_TYPE_FILTER, @@ -31,6 +32,7 @@ enum obs_source_type { OBS_SOURCE_TYPE_SCENE = 0x80000000 }; + /** * @name Source output flags * @@ -55,6 +57,9 @@ enum obs_source_type { */ #define OBS_SOURCE_AUDIO (1<<1) +/** Async video flag (use OBS_SOURCE_ASYNC_VIDEO) */ +#define OBS_SOURCE_ASYNC (1<<2) + /** * Source passes raw video data via RAM. * @@ -66,7 +71,7 @@ enum obs_source_type { * obs_source_getframe to get the current frame data, and * obs_source_releaseframe to release the data when complete. */ -#define OBS_SOURCE_ASYNC_VIDEO ((1<<2) | OBS_SOURCE_VIDEO) +#define OBS_SOURCE_ASYNC_VIDEO (OBS_SOURCE_ASYNC | OBS_SOURCE_VIDEO) /** * Source uses custom drawing, rather than a default effect. @@ -132,10 +137,12 @@ struct obs_source_info { /** Destroys the private data for the source */ void (*destroy)(void *data); - /** Returns the width of the source. Required if input and video */ + /** Returns the width of the source. Required if this is an input + * source and has non-async video */ uint32_t (*getwidth)(void *data); - /** Returns the height of the source. Required if input and video */ + /** Returns the height of the source. Required if this is an input + * source and has non-async video */ uint32_t (*getheight)(void *data); /* ----------------------------------------------------------------- */ diff --git a/test/test-input/test-random.c b/test/test-input/test-random.c index 31b2e38d4..9391cd0dd 100644 --- a/test/test-input/test-random.c +++ b/test/test-input/test-random.c @@ -1,8 +1,13 @@ #include +#include +#include #include struct random_tex { - texture_t texture; + obs_source_t source; + os_event_t stop_signal; + pthread_t thread; + bool initialized; }; static const char *random_getname(const char *locale) @@ -16,19 +21,18 @@ static void random_destroy(void *data) struct random_tex *rt = data; if (rt) { - gs_entercontext(obs_graphics()); + if (rt->initialized) { + os_event_signal(rt->stop_signal); + pthread_join(rt->thread, NULL); + } - texture_destroy(rt->texture); + os_event_destroy(rt->stop_signal); bfree(rt); - - gs_leavecontext(); } } -static void *random_create(obs_data_t settings, obs_source_t source) +static inline void fill_texture(uint32_t *pixels) { - struct random_tex *rt = bzalloc(sizeof(struct random_tex)); - uint32_t *pixels = bmalloc(20*20*4); size_t x, y; for (y = 0; y < 20; y++) { @@ -41,53 +45,62 @@ static void *random_create(obs_data_t settings, obs_source_t source) pixels[y*20 + x] = pixel; } } +} - gs_entercontext(obs_graphics()); +static void *video_thread(void *data) +{ + struct random_tex *rt = data; + uint32_t pixels[20*20]; + uint64_t cur_time = os_gettime_ns(); - rt->texture = gs_create_texture(20, 20, GS_RGBA, 1, - (const void**)&pixels, 0); - bfree(pixels); + struct source_frame frame = { + .data = {[0] = (uint8_t*)pixels}, + .linesize = {[0] = 20*4}, + .width = 20, + .height = 20, + .format = VIDEO_FORMAT_BGRX + }; - if (!rt->texture) { + while (os_event_try(rt->stop_signal) == EAGAIN) { + fill_texture(pixels); + + frame.timestamp = cur_time; + + obs_source_output_video(rt->source, &frame); + + os_sleepto_ns(cur_time += 250000000); + } + + return NULL; +} + +static void *random_create(obs_data_t settings, obs_source_t source) +{ + struct random_tex *rt = bzalloc(sizeof(struct random_tex)); + rt->source = source; + + if (os_event_init(&rt->stop_signal, OS_EVENT_TYPE_MANUAL) != 0) { random_destroy(rt); return NULL; } - gs_leavecontext(); + if (pthread_create(&rt->thread, NULL, video_thread, rt) != 0) { + random_destroy(rt); + return NULL; + } + + rt->initialized = true; UNUSED_PARAMETER(settings); UNUSED_PARAMETER(source); return rt; } -static void random_video_render(void *data, effect_t effect) -{ - struct random_tex *rt = data; - eparam_t image = effect_getparambyname(effect, "image"); - effect_settexture(effect, image, rt->texture); - gs_draw_sprite(rt->texture, 0, 0, 0); -} - -static uint32_t random_getwidth(void *data) -{ - struct random_tex *rt = data; - return texture_getwidth(rt->texture); -} - -static uint32_t random_getheight(void *data) -{ - struct random_tex *rt = data; - return texture_getheight(rt->texture); -} - struct obs_source_info test_random = { .id = "random", .type = OBS_SOURCE_TYPE_INPUT, - .output_flags = OBS_SOURCE_VIDEO, + .output_flags = OBS_SOURCE_ASYNC_VIDEO, .getname = random_getname, .create = random_create, .destroy = random_destroy, - .video_render = random_video_render, - .getwidth = random_getwidth, - .getheight = random_getheight };