diff --git a/plugins/image-source/obs-slideshow.c b/plugins/image-source/obs-slideshow.c index e8316e93f..f5b20ee29 100644 --- a/plugins/image-source/obs-slideshow.c +++ b/plugins/image-source/obs-slideshow.c @@ -57,6 +57,9 @@ /* ------------------------------------------------------------------------- */ +#define MOD(a,b) ((((a)%(b))+(b))%(b)) +#define MAX_LOADED 11 /* needs to be an odd number */ + struct image_file_data { char *path; obs_source_t *source; @@ -75,7 +78,6 @@ struct slideshow { bool loop; bool restart_on_activate; bool pause_on_deactivate; - bool restart; bool manual; bool hide; bool use_cut; @@ -87,13 +89,14 @@ struct slideshow { obs_source_t *transition; float elapsed; - size_t cur_item; + int cur_item; uint32_t cx; uint32_t cy; pthread_mutex_t mutex; DARRAY(struct image_file_data) files; + DARRAY(char*) paths; enum behavior behavior; @@ -164,7 +167,18 @@ static void free_files(struct darray *array) static inline size_t random_file(struct slideshow *ss) { - return (size_t)rand() % ss->files.num; + return (size_t)rand() % ss->paths.num; +} + +static void free_paths(struct darray *array) +{ + DARRAY(char*) paths; + paths.da = *array; + + for (size_t i = 0; i < paths.num; i++) + bfree(paths.array[i]); + + da_free(paths); } /* ------------------------------------------------------------------------- */ @@ -176,7 +190,7 @@ static const char *ss_getname(void *unused) } static void add_file(struct slideshow *ss, struct darray *array, - const char *path, uint32_t *cx, uint32_t *cy) + const char *path, uint32_t *cx, uint32_t *cy, bool next) { DARRAY(struct image_file_data) new_files; struct image_file_data data; @@ -199,7 +213,11 @@ static void add_file(struct slideshow *ss, struct darray *array, data.path = bstrdup(path); data.source = new_source; - da_push_back(new_files, &data); + + if (next) + da_push_back(new_files, &data); + else + da_insert(new_files, 0, &data); if (new_cx > *cx) *cx = new_cx; if (new_cy > *cy) *cy = new_cy; @@ -208,6 +226,48 @@ static void add_file(struct slideshow *ss, struct darray *array, *array = new_files.da; } +static void clear_buffer(struct slideshow *ss, bool next) +{ + if (ss->paths.num <= MAX_LOADED || !ss->paths.num || !ss->files.num) + return; + + if (next) { + bfree(ss->files.array[0].path); + obs_source_release(ss->files.array[0].source); + da_erase(ss->files, 0); + } else { + bfree(ss->files.array[ss->files.num - 1].path); + obs_source_release(ss->files.array[ss->files.num - 1].source); + da_pop_back(ss->files); + } + + size_t index = 0; + + if (ss->randomize) + index = random_file(ss); + else if (!ss->randomize && next) + index = MOD((ss->cur_item + ((MAX_LOADED / 2) + 1)), + ss->paths.num); + else if (!ss->randomize && !next) + index = MOD((ss->cur_item - ((MAX_LOADED / 2) + 1)), + ss->paths.num); + + uint32_t cx, cy; + add_file(ss, &ss->files.da, ss->paths.array[index], &cx, &cy, + next); +} + +static void add_path(struct darray *array, const char *path) +{ + DARRAY(char*) new_paths; + new_paths.da = *array; + + const char *new_path = bstrdup(path); + da_push_back(new_paths, &new_path); + + *array = new_paths.da; +} + static bool valid_extension(const char *ext) { if (!ext) @@ -222,35 +282,54 @@ static bool valid_extension(const char *ext) static inline bool item_valid(struct slideshow *ss) { - return ss->files.num && ss->cur_item < ss->files.num; + return ss->files.num && ss->paths.num && + (size_t)ss->cur_item < ss->paths.num; } -static void do_transition(void *data, bool to_null) +static void do_transition(void *data, bool to_null, bool next) { struct slideshow *ss = data; bool valid = item_valid(ss); - if (valid && ss->use_cut) - obs_transition_set(ss->transition, - ss->files.array[ss->cur_item].source); - - else if (valid && !to_null) - obs_transition_start(ss->transition, - OBS_TRANSITION_MODE_AUTO, - ss->tr_speed, - ss->files.array[ss->cur_item].source); - - else - obs_transition_start(ss->transition, - OBS_TRANSITION_MODE_AUTO, + if (to_null) { + obs_transition_start(ss->transition, OBS_TRANSITION_MODE_AUTO, ss->tr_speed, NULL); + return; + } + + if (!valid) + return; + + clear_buffer(ss, next); + + obs_source_t *source; + + if (next && ss->paths.num > MAX_LOADED) + source = ss->files.array[(MAX_LOADED / 2) + 1].source; + else if (!next && ss->paths.num > MAX_LOADED) + source = ss->files.array[(MAX_LOADED / 2) - 1].source; + else if (ss->paths.num <= MAX_LOADED) + source = ss->files.array[(size_t)ss->cur_item].source; + + if (!source) + return; + + if (ss->use_cut) + obs_transition_set(ss->transition, source); + + else if (!to_null) + obs_transition_start(ss->transition, + OBS_TRANSITION_MODE_AUTO, + ss->tr_speed, source); } static void ss_update(void *data, obs_data_t *settings) { DARRAY(struct image_file_data) new_files; DARRAY(struct image_file_data) old_files; + DARRAY(char*) new_paths; + DARRAY(char*) old_paths; obs_source_t *new_tr = NULL; obs_source_t *old_tr = NULL; struct slideshow *ss = data; @@ -260,6 +339,8 @@ static void ss_update(void *data, obs_data_t *settings) uint32_t new_speed; uint32_t cx = 0; uint32_t cy = 0; + uint32_t last_cx = 0; + uint32_t last_cy = 0; size_t count; const char *behavior; const char *mode; @@ -268,6 +349,7 @@ static void ss_update(void *data, obs_data_t *settings) /* get settings data */ da_init(new_files); + da_init(new_paths); behavior = obs_data_get_string(settings, S_BEHAVIOR); @@ -333,14 +415,13 @@ static void ss_update(void *data, obs_data_t *settings) dstr_copy(&dir_path, path); dstr_cat_ch(&dir_path, '/'); dstr_cat(&dir_path, ent->d_name); - add_file(ss, &new_files.da, dir_path.array, - &cx, &cy); + add_path(&new_paths.da, dir_path.array); } dstr_free(&dir_path); os_closedir(dir); } else { - add_file(ss, &new_files.da, path, &cx, &cy); + add_path(&new_paths.da, path); } obs_data_release(item); @@ -353,6 +434,8 @@ static void ss_update(void *data, obs_data_t *settings) old_files.da = ss->files.da; ss->files.da = new_files.da; + old_paths.da = ss->paths.da; + ss->paths.da = new_paths.da; if (new_tr) { old_tr = ss->transition; ss->transition = new_tr; @@ -369,12 +452,31 @@ static void ss_update(void *data, obs_data_t *settings) pthread_mutex_unlock(&ss->mutex); + if (ss->paths.num > MAX_LOADED && !ss->randomize) { + for (int i = -(MAX_LOADED / 2); i <= (MAX_LOADED / 2); i++) { + size_t index = MOD(i, ss->paths.num); + add_file(ss, &ss->files.da, ss->paths.array[index], + &cx, &cy, true); + } + } else if (ss->paths.num > MAX_LOADED && ss->randomize) { + for (size_t i = 0; i < MAX_LOADED; i++) { + size_t index = random_file(ss); + add_file(ss, &ss->files.da, ss->paths.array[index], + &cx, &cy, true); + } + } else if (ss->paths.num <= MAX_LOADED) { + for (size_t i = 0; i < ss->paths.num; i++) + add_file(ss, &ss->files.da, ss->paths.array[i], + &cx, &cy, true); + } + /* ------------------------------------- */ /* clean up and restart transition */ if (old_tr) obs_source_release(old_tr); free_files(&old_files.da); + free_paths(&old_paths.da); /* ------------------------- */ @@ -418,21 +520,32 @@ static void ss_update(void *data, obs_data_t *settings) /* ------------------------- */ + obs_data_t *priv_settings = obs_source_get_private_settings(ss->source); + last_cx = (uint32_t)obs_data_get_int(priv_settings, "last_cx"); + last_cy = (uint32_t)obs_data_get_int(priv_settings, "last_cy"); + + if (ss->randomize && last_cx > 0 && last_cy > 0) { + cx = last_cx; + cy = last_cy; + } + + obs_data_set_int(priv_settings, "last_cx", cx); + obs_data_set_int(priv_settings, "last_cy", cy); + obs_data_release(priv_settings); + ss->cx = cx; ss->cy = cy; - ss->cur_item = 0; ss->elapsed = 0.0f; + ss->cur_item = 0; obs_transition_set_size(ss->transition, cx, cy); obs_transition_set_alignment(ss->transition, OBS_ALIGN_CENTER); obs_transition_set_scale_type(ss->transition, OBS_TRANSITION_SCALE_ASPECT); - if (ss->randomize && ss->files.num) - ss->cur_item = random_file(ss); if (new_tr) obs_source_add_active_child(ss->source, new_tr); if (ss->files.num) - do_transition(ss, false); + do_transition(ss, false, true); obs_data_array_release(array); } @@ -449,14 +562,15 @@ static void ss_restart(void *data) { struct slideshow *ss = data; - ss->elapsed = 0.0f; - ss->cur_item = 0; - - obs_transition_set(ss->transition, - ss->files.array[ss->cur_item].source); - ss->stop = false; - ss->paused = false; + ss->use_cut = true; + ss->restart_on_activate = false; + + obs_data_t *settings = obs_source_get_settings(ss->source); + ss_update(ss, settings); + obs_data_release(settings); + + ss->use_cut = false; } static void ss_stop(void *data) @@ -466,7 +580,7 @@ static void ss_stop(void *data) ss->elapsed = 0.0f; ss->cur_item = 0; - do_transition(ss, true); + do_transition(ss, true, true); ss->stop = true; ss->paused = false; } @@ -475,28 +589,26 @@ static void ss_next_slide(void *data) { struct slideshow *ss = data; - if (!ss->files.num) + if (!ss->paths.num) return; - if (++ss->cur_item >= ss->files.num) + if (++ss->cur_item >= (int)ss->paths.num) ss->cur_item = 0; - do_transition(ss, false); + do_transition(ss, false, true); } static void ss_previous_slide(void *data) { struct slideshow *ss = data; - if (!ss->files.num) + if (!ss->paths.num) return; - if (ss->cur_item == 0) - ss->cur_item = ss->files.num - 1; - else - --ss->cur_item; + if (--ss->cur_item < 0) + ss->cur_item = (int)(ss->paths.num - 1); - do_transition(ss, false); + do_transition(ss, false, false); } static void play_pause_hotkey(void *data, obs_hotkey_id id, @@ -571,6 +683,7 @@ static void ss_destroy(void *data) obs_source_release(ss->transition); free_files(&ss->files.da); + free_paths(&ss->paths.da); pthread_mutex_destroy(&ss->mutex); bfree(ss); } @@ -644,13 +757,8 @@ static void ss_video_tick(void *data, float seconds) if (!ss->transition || !ss->slide_time) return; - if (ss->restart_on_activate && !ss->randomize && ss->use_cut) { - ss->elapsed = 0.0f; - ss->cur_item = 0; - do_transition(ss, false); - ss->restart_on_activate = false; - ss->use_cut = false; - ss->stop = false; + if (ss->restart_on_activate && ss->use_cut) { + ss_restart(ss); return; } @@ -659,13 +767,13 @@ static void ss_video_tick(void *data, float seconds) /* ----------------------------------------------------- */ /* fade to transparency when the file list becomes empty */ - if (!ss->files.num) { + if (!ss->paths.num) { obs_source_t* active_transition_source = obs_transition_get_active_source(ss->transition); if (active_transition_source) { obs_source_release(active_transition_source); - do_transition(ss, true); + do_transition(ss, true, true); } } @@ -676,29 +784,17 @@ static void ss_video_tick(void *data, float seconds) if (ss->elapsed > ss->slide_time) { ss->elapsed -= ss->slide_time; - if (!ss->loop && ss->cur_item == ss->files.num - 1) { + if (!ss->loop && ss->cur_item == (int)ss->paths.num - 1 && + !ss->randomize) { if (ss->hide) - do_transition(ss, true); + do_transition(ss, true, true); else - do_transition(ss, false); + do_transition(ss, false, true); return; } - if (ss->randomize) { - size_t next = ss->cur_item; - if (ss->files.num > 1) { - while (next == ss->cur_item) - next = random_file(ss); - } - ss->cur_item = next; - - } else if (++ss->cur_item >= ss->files.num) { - ss->cur_item = 0; - } - - if (ss->files.num) - do_transition(ss, false); + ss_next_slide(ss); } } @@ -861,11 +957,14 @@ static obs_properties_t *ss_properties(void *data) if (ss) { pthread_mutex_lock(&ss->mutex); - if (ss->files.num) { - struct image_file_data *last = da_end(ss->files); + if (ss->paths.num) { + char **p_last_path = da_end(ss->paths); + const char *last_path; const char *slash; - dstr_copy(&path, last->path); + last_path = p_last_path ? *p_last_path : ""; + + dstr_copy(&path, last_path); dstr_replace(&path, "\\", "/"); slash = strrchr(path.array, '/'); if (slash)