From 2fb7063e1a0c3d5a5dee51ffc8d636d1a8159e8b Mon Sep 17 00:00:00 2001 From: Lain Date: Tue, 2 Jan 2024 06:20:51 -0600 Subject: [PATCH] libobs: Defer media controls to video thread To prevent sources from having to take too much extra consideration into threading, defer the media control functions that directly affect functionality to the video thread. Getters will still have to take threading into account, but this should make things much more trivial for media controls thread-wise. (Lain note: Context: I noticed that things such as the slide show require mutexes due to their media controls, and I felt that it was largely unnecessary and that libobs should mitigate the threading issue itself and keep it all in the video thread like it should be. Again, the getters are still going to require *some* consideration into threading but in terms of threading, for the type of stuff we're doing, querying state is far more trivial to take into consideration.) --- libobs/obs-internal.h | 22 ++++++++ libobs/obs-source.c | 117 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 124 insertions(+), 15 deletions(-) diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index 3bad6b99a..46ac6e318 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -688,6 +688,24 @@ struct caption_cb_info { void *param; }; +enum media_action_type { + MEDIA_ACTION_NONE, + MEDIA_ACTION_PLAY_PAUSE, + MEDIA_ACTION_RESTART, + MEDIA_ACTION_STOP, + MEDIA_ACTION_NEXT, + MEDIA_ACTION_PREVIOUS, + MEDIA_ACTION_SET_TIME, +}; + +struct media_action { + enum media_action_type type; + union { + bool pause; + int64_t ms; + }; +}; + struct obs_source { struct obs_context_data context; struct obs_source_info info; @@ -874,6 +892,10 @@ struct obs_source { struct audio_monitor *monitor; enum obs_monitoring_type monitoring_type; + /* media action queue */ + DARRAY(struct media_action) media_actions; + pthread_mutex_t media_actions_mutex; + /* private data */ obs_data_t *private_settings; }; diff --git a/libobs/obs-source.c b/libobs/obs-source.c index 87341fdae..69f6dc0c9 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -193,6 +193,7 @@ static bool obs_source_init(struct obs_source *source) pthread_mutex_init_value(&source->audio_buf_mutex); pthread_mutex_init_value(&source->audio_cb_mutex); pthread_mutex_init_value(&source->caption_cb_mutex); + pthread_mutex_init_value(&source->media_actions_mutex); if (pthread_mutex_init_recursive(&source->filter_mutex) != 0) return false; @@ -208,6 +209,8 @@ static bool obs_source_init(struct obs_source *source) return false; if (pthread_mutex_init(&source->caption_cb_mutex, NULL) != 0) return false; + if (pthread_mutex_init(&source->media_actions_mutex, NULL) != 0) + return false; if (is_audio_source(source) || is_composite_source(source)) allocate_audio_output_buffer(source); @@ -737,6 +740,7 @@ static void obs_source_destroy_defer(struct obs_source *source) da_free(source->async_cache); da_free(source->async_frames); da_free(source->filters); + da_free(source->media_actions); pthread_mutex_destroy(&source->filter_mutex); pthread_mutex_destroy(&source->audio_actions_mutex); pthread_mutex_destroy(&source->audio_buf_mutex); @@ -744,6 +748,7 @@ static void obs_source_destroy_defer(struct obs_source *source) pthread_mutex_destroy(&source->audio_mutex); pthread_mutex_destroy(&source->caption_cb_mutex); pthread_mutex_destroy(&source->async_mutex); + pthread_mutex_destroy(&source->media_actions_mutex); obs_data_release(source->private_settings); obs_context_data_free(&source->context); @@ -1210,6 +1215,59 @@ static void filter_frame(obs_source_t *source, *ref_frame = frame; } +void process_media_actions(obs_source_t *source) +{ + struct media_action action = {0}; + + for (;;) { + pthread_mutex_lock(&source->media_actions_mutex); + if (source->media_actions.num) { + action = source->media_actions.array[0]; + da_pop_front(source->media_actions); + } else { + action.type = MEDIA_ACTION_NONE; + } + pthread_mutex_unlock(&source->media_actions_mutex); + + switch (action.type) { + case MEDIA_ACTION_NONE: + return; + case MEDIA_ACTION_PLAY_PAUSE: + source->info.media_play_pause(source->context.data, + action.pause); + + if (action.pause) + obs_source_dosignal(source, NULL, + "media_pause"); + else + obs_source_dosignal(source, NULL, "media_play"); + break; + + case MEDIA_ACTION_RESTART: + source->info.media_restart(source->context.data); + obs_source_dosignal(source, NULL, "media_restart"); + break; + + case MEDIA_ACTION_STOP: + source->info.media_stop(source->context.data); + obs_source_dosignal(source, NULL, "media_stopped"); + break; + case MEDIA_ACTION_NEXT: + source->info.media_next(source->context.data); + obs_source_dosignal(source, NULL, "media_next"); + break; + case MEDIA_ACTION_PREVIOUS: + source->info.media_previous(source->context.data); + obs_source_dosignal(source, NULL, "media_previous"); + break; + case MEDIA_ACTION_SET_TIME: + source->info.media_set_time(source->context.data, + action.ms); + break; + } + } +} + static void async_tick(obs_source_t *source) { uint64_t sys_time = obs->video.video_time; @@ -1253,6 +1311,9 @@ void obs_source_video_tick(obs_source_t *source, float seconds) if ((source->info.output_flags & OBS_SOURCE_ASYNC) != 0) async_tick(source); + if ((source->info.output_flags & OBS_SOURCE_CONTROLLABLE_MEDIA) != 0) + process_media_actions(source); + if (os_atomic_load_long(&source->defer_update_count) > 0) obs_source_deferred_update(source); @@ -6002,12 +6063,14 @@ void obs_source_media_play_pause(obs_source_t *source, bool pause) if (!source->info.media_play_pause) return; - source->info.media_play_pause(source->context.data, pause); + struct media_action action = { + .type = MEDIA_ACTION_PLAY_PAUSE, + .pause = pause, + }; - if (pause) - obs_source_dosignal(source, NULL, "media_pause"); - else - obs_source_dosignal(source, NULL, "media_play"); + pthread_mutex_lock(&source->media_actions_mutex); + da_push_back(source->media_actions, &action); + pthread_mutex_unlock(&source->media_actions_mutex); } void obs_source_media_restart(obs_source_t *source) @@ -6020,9 +6083,13 @@ void obs_source_media_restart(obs_source_t *source) if (!source->info.media_restart) return; - source->info.media_restart(source->context.data); + struct media_action action = { + .type = MEDIA_ACTION_RESTART, + }; - obs_source_dosignal(source, NULL, "media_restart"); + pthread_mutex_lock(&source->media_actions_mutex); + da_push_back(source->media_actions, &action); + pthread_mutex_unlock(&source->media_actions_mutex); } void obs_source_media_stop(obs_source_t *source) @@ -6035,9 +6102,13 @@ void obs_source_media_stop(obs_source_t *source) if (!source->info.media_stop) return; - source->info.media_stop(source->context.data); + struct media_action action = { + .type = MEDIA_ACTION_STOP, + }; - obs_source_dosignal(source, NULL, "media_stopped"); + pthread_mutex_lock(&source->media_actions_mutex); + da_push_back(source->media_actions, &action); + pthread_mutex_unlock(&source->media_actions_mutex); } void obs_source_media_next(obs_source_t *source) @@ -6050,9 +6121,13 @@ void obs_source_media_next(obs_source_t *source) if (!source->info.media_next) return; - source->info.media_next(source->context.data); + struct media_action action = { + .type = MEDIA_ACTION_NEXT, + }; - obs_source_dosignal(source, NULL, "media_next"); + pthread_mutex_lock(&source->media_actions_mutex); + da_push_back(source->media_actions, &action); + pthread_mutex_unlock(&source->media_actions_mutex); } void obs_source_media_previous(obs_source_t *source) @@ -6065,9 +6140,13 @@ void obs_source_media_previous(obs_source_t *source) if (!source->info.media_previous) return; - source->info.media_previous(source->context.data); + struct media_action action = { + .type = MEDIA_ACTION_PREVIOUS, + }; - obs_source_dosignal(source, NULL, "media_previous"); + pthread_mutex_lock(&source->media_actions_mutex); + da_push_back(source->media_actions, &action); + pthread_mutex_unlock(&source->media_actions_mutex); } int64_t obs_source_media_get_duration(obs_source_t *source) @@ -6102,9 +6181,17 @@ void obs_source_media_set_time(obs_source_t *source, int64_t ms) return; if ((source->info.output_flags & OBS_SOURCE_CONTROLLABLE_MEDIA) == 0) return; + if (!source->info.media_set_time) + return; - if (source->info.media_set_time) - source->info.media_set_time(source->context.data, ms); + struct media_action action = { + .type = MEDIA_ACTION_SET_TIME, + .ms = ms, + }; + + pthread_mutex_lock(&source->media_actions_mutex); + da_push_back(source->media_actions, &action); + pthread_mutex_unlock(&source->media_actions_mutex); } enum obs_media_state obs_source_media_get_state(obs_source_t *source)