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.)
This commit is contained in:
Lain 2024-01-02 06:20:51 -06:00
parent 42bda5fb67
commit 2fb7063e1a
2 changed files with 124 additions and 15 deletions

View file

@ -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;
};

View file

@ -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)