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)