From fb95e1d1e929bbeccac19f8bf15771a2c58ffdad Mon Sep 17 00:00:00 2001 From: VodBox Date: Tue, 18 Feb 2020 21:06:16 +1300 Subject: [PATCH] libobs: Add missing file API to sources --- libobs/CMakeLists.txt | 2 + libobs/obs-missing-files.c | 151 ++++++++++++++++++++++++ libobs/obs-missing-files.h | 60 ++++++++++ libobs/obs-source.c | 22 ++++ libobs/obs-source.h | 3 + libobs/obs.h | 8 ++ plugins/image-source/image-source.c | 32 +++++ plugins/image-source/obs-slideshow.c | 66 +++++++++++ plugins/obs-ffmpeg/obs-ffmpeg-source.c | 32 +++++ plugins/obs-text/gdiplus/obs-text.cpp | 39 ++++++ plugins/text-freetype2/text-freetype2.c | 40 +++++++ plugins/text-freetype2/text-freetype2.h | 2 + plugins/vlc-video/vlc-video-source.c | 66 +++++++++++ 13 files changed, 523 insertions(+) create mode 100644 libobs/obs-missing-files.c create mode 100644 libobs/obs-missing-files.h diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt index 3d3733f2d..327cbd0c8 100644 --- a/libobs/CMakeLists.txt +++ b/libobs/CMakeLists.txt @@ -409,6 +409,7 @@ set(libobs_libobs_SOURCES obs.c obs-properties.c obs-data.c + obs-missing-files.c obs-hotkey.c obs-hotkey-name-map.c obs-module.c @@ -430,6 +431,7 @@ set(libobs_libobs_HEADERS obs-ui.h obs-properties.h obs-data.h + obs-missing-files.h obs-interaction.h obs-hotkey.h obs-hotkeys.h diff --git a/libobs/obs-missing-files.c b/libobs/obs-missing-files.c new file mode 100644 index 000000000..494ef301d --- /dev/null +++ b/libobs/obs-missing-files.c @@ -0,0 +1,151 @@ +/****************************************************************************** + Copyright (C) 2019 by Dillon Pentz + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +******************************************************************************/ + +#include "util/threading.h" +#include "util/dstr.h" +#include "obs-missing-files.h" +#include "obs.h" + +struct obs_missing_file { + volatile long ref; + char *file_path; + obs_missing_file_cb callback; + int src_type; + void *src; + char *src_name; + void *data; +}; + +struct obs_missing_files { + DARRAY(struct obs_missing_file *) files; +}; + +obs_missing_files_t *obs_missing_files_create() +{ + struct obs_missing_files *files = + bzalloc(sizeof(struct obs_missing_files)); + + return files; +} + +void obs_missing_files_destroy(obs_missing_files_t *files) +{ + for (size_t i = 0; i < files->files.num; i++) { + obs_missing_file_release(files->files.array[i]); + } + + da_free(files->files); + bfree(files); +} + +void obs_missing_files_add_file(obs_missing_files_t *files, + obs_missing_file_t *file) +{ + da_insert(files->files, files->files.num, &file); +} + +size_t obs_missing_files_count(obs_missing_files_t *files) +{ + return files->files.num; +} + +obs_missing_file_t *obs_missing_files_get_file(obs_missing_files_t *files, + int idx) +{ + return files->files.array[idx]; +} + +void obs_missing_files_append(obs_missing_files_t *dst, + obs_missing_files_t *src) +{ + for (size_t i = 0; i < src->files.num; i++) { + obs_missing_file_t *file = src->files.array[i]; + obs_missing_files_add_file(dst, file); + os_atomic_inc_long(&file->ref); + } +} + +obs_missing_file_t *obs_missing_file_create(const char *path, + obs_missing_file_cb callback, + int src_type, void *src, void *data) +{ + struct obs_missing_file *file = + bzalloc(sizeof(struct obs_missing_file)); + + file->file_path = bstrdup(path); + file->callback = callback; + file->src_type = src_type; + file->src = src; + file->data = data; + file->ref = 1; + + switch (src_type) { + case OBS_MISSING_FILE_SOURCE: + file->src_name = bstrdup(obs_source_get_name(src)); + break; + case OBS_MISSING_FILE_SCRIPT: + break; + } + + return file; +} + +void obs_missing_file_release(obs_missing_file_t *file) +{ + if (!file) + return; + + if (os_atomic_dec_long(&file->ref) == 0) + obs_missing_file_destroy(file); +} + +void obs_missing_file_destroy(obs_missing_file_t *file) +{ + switch (file->src_type) { + case OBS_MISSING_FILE_SOURCE: + bfree(file->src_name); + break; + case OBS_MISSING_FILE_SCRIPT: + break; + } + bfree(file->file_path); + bfree(file); +} + +void obs_missing_file_issue_callback(obs_missing_file_t *file, + const char *new_path) +{ + switch (file->src_type) { + case OBS_MISSING_FILE_SOURCE: + obs_source_replace_missing_file(file->callback, + (obs_source_t *)file->src, + new_path, file->data); + break; + case OBS_MISSING_FILE_SCRIPT: + break; + } +} + +const char *obs_missing_file_get_path(obs_missing_file_t *file) +{ + return file->file_path; +} + +const char *obs_missing_file_get_source_name(obs_missing_file_t *file) +{ + return file->src_name; +} diff --git a/libobs/obs-missing-files.h b/libobs/obs-missing-files.h new file mode 100644 index 000000000..6e626ff35 --- /dev/null +++ b/libobs/obs-missing-files.h @@ -0,0 +1,60 @@ +/****************************************************************************** + Copyright (C) 2019 by Dillon Pentz + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +******************************************************************************/ + +#pragma once + +#include "util/c99defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*obs_missing_file_cb)(void *src, const char *new_path, + void *data); + +struct obs_missing_file; +struct obs_missing_files; +typedef struct obs_missing_file obs_missing_file_t; +typedef struct obs_missing_files obs_missing_files_t; + +enum obs_missing_file_src { OBS_MISSING_FILE_SOURCE, OBS_MISSING_FILE_SCRIPT }; + +EXPORT obs_missing_files_t *obs_missing_files_create(); +EXPORT obs_missing_file_t *obs_missing_file_create(const char *path, + obs_missing_file_cb callback, + int src_type, void *src, + void *data); + +EXPORT void obs_missing_files_add_file(obs_missing_files_t *files, + obs_missing_file_t *file); +EXPORT size_t obs_missing_files_count(obs_missing_files_t *files); +EXPORT obs_missing_file_t * +obs_missing_files_get_file(obs_missing_files_t *files, int idx); +EXPORT void obs_missing_files_destroy(obs_missing_files_t *files); +EXPORT void obs_missing_files_append(obs_missing_files_t *dst, + obs_missing_files_t *src); + +EXPORT void obs_missing_file_issue_callback(obs_missing_file_t *file, + const char *new_path); +EXPORT const char *obs_missing_file_get_path(obs_missing_file_t *file); +EXPORT const char *obs_missing_file_get_source_name(obs_missing_file_t *file); +EXPORT void obs_missing_file_release(obs_missing_file_t *file); +EXPORT void obs_missing_file_destroy(obs_missing_file_t *file); + +#ifdef __cplusplus +} +#endif diff --git a/libobs/obs-source.c b/libobs/obs-source.c index 9aea45c73..421b0dc91 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -844,6 +844,28 @@ obs_properties_t *obs_get_source_properties(const char *id) return NULL; } +obs_missing_files_t *obs_source_get_missing_files(const obs_source_t *source) +{ + if (!obs_source_valid(source, "obs_source_get_missing_files")) + return obs_missing_files_create(); + + if (source->info.missing_files) { + return source->info.missing_files(source->context.data); + } + + return obs_missing_files_create(); +} + +void obs_source_replace_missing_file(obs_missing_file_cb cb, + obs_source_t *source, const char *new_path, + void *data) +{ + if (!obs_source_valid(source, "obs_source_replace_missing_file")) + return; + + cb(source->context.data, new_path, data); +} + bool obs_is_source_configurable(const char *id) { const struct obs_source_info *info = get_source_info(id); diff --git a/libobs/obs-source.h b/libobs/obs-source.h index ec4194fc2..2ae0b182f 100644 --- a/libobs/obs-source.h +++ b/libobs/obs-source.h @@ -532,6 +532,9 @@ struct obs_source_info { /* version-related stuff */ uint32_t version; /* increment if needed to specify a new version */ const char *unversioned_id; /* set internally, don't set manually */ + + /** Missing files **/ + obs_missing_files_t *(*missing_files)(void *data); }; EXPORT void obs_register_source_s(const struct obs_source_info *info, diff --git a/libobs/obs.h b/libobs/obs.h index 6ad9712f3..4599ce62b 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -68,6 +68,7 @@ typedef struct obs_weak_output obs_weak_output_t; typedef struct obs_weak_encoder obs_weak_encoder_t; typedef struct obs_weak_service obs_weak_service_t; +#include "obs-missing-files.h" #include "obs-source.h" #include "obs-encoder.h" #include "obs-output.h" @@ -906,6 +907,13 @@ EXPORT obs_data_t *obs_get_source_defaults(const char *id); /** Returns the property list, if any. Free with obs_properties_destroy */ EXPORT obs_properties_t *obs_get_source_properties(const char *id); +EXPORT obs_missing_files_t * +obs_source_get_missing_files(const obs_source_t *source); + +EXPORT void obs_source_replace_missing_file(obs_missing_file_cb cb, + obs_source_t *source, + const char *new_path, void *data); + /** Returns whether the source has custom properties or not */ EXPORT bool obs_is_source_configurable(const char *id); diff --git a/plugins/image-source/image-source.c b/plugins/image-source/image-source.c index aa911e634..3f809a3ee 100644 --- a/plugins/image-source/image-source.c +++ b/plugins/image-source/image-source.c @@ -252,6 +252,37 @@ uint64_t image_source_get_memory_usage(void *data) return s->if2.mem_usage; } +static void missing_file_callback(void *src, const char *new_path, void *data) +{ + struct image_source *s = src; + + obs_source_t *source = s->source; + obs_data_t *settings = obs_source_get_settings(source); + obs_data_set_string(settings, "file", new_path); + obs_source_update(source, settings); + obs_data_release(settings); + + UNUSED_PARAMETER(data); +} + +static obs_missing_files_t *image_source_missingfiles(void *data) +{ + struct image_source *s = data; + obs_missing_files_t *files = obs_missing_files_create(); + + if (strcmp(s->file, "") != 0) { + if (!os_file_exists(s->file)) { + obs_missing_file_t *file = obs_missing_file_create( + s->file, missing_file_callback, + OBS_MISSING_FILE_SOURCE, s->source, NULL); + + obs_missing_files_add_file(files, file); + } + } + + return files; +} + static struct obs_source_info image_source_info = { .id = "image_source", .type = OBS_SOURCE_TYPE_INPUT, @@ -267,6 +298,7 @@ static struct obs_source_info image_source_info = { .get_height = image_source_getheight, .video_render = image_source_render, .video_tick = image_source_tick, + .missing_files = image_source_missingfiles, .get_properties = image_source_properties, .icon_type = OBS_ICON_TYPE_IMAGE, }; diff --git a/plugins/image-source/obs-slideshow.c b/plugins/image-source/obs-slideshow.c index 41727d7cd..27dde2570 100644 --- a/plugins/image-source/obs-slideshow.c +++ b/plugins/image-source/obs-slideshow.c @@ -958,6 +958,71 @@ static void ss_deactivate(void *data) ss->pause_on_deactivate = true; } +static void missing_file_callback(void *src, const char *new_path, void *data) +{ + struct slideshow *s = src; + const char *orig_path = data; + + obs_source_t *source = s->source; + obs_data_t *settings = obs_source_get_settings(source); + obs_data_array_t *files = obs_data_get_array(settings, S_FILES); + + size_t l = obs_data_array_count(files); + for (size_t i = 0; i < l; i++) { + obs_data_t *file = obs_data_array_item(files, i); + const char *path = obs_data_get_string(file, "value"); + + if (strcmp(path, orig_path) == 0) { + obs_data_set_string(file, "value", new_path); + + obs_data_release(file); + break; + } + + obs_data_release(file); + } + + obs_source_update(source, settings); + + obs_data_array_release(files); + obs_data_release(settings); +} + +static obs_missing_files_t *ss_missingfiles(void *data) +{ + struct slideshow *s = data; + obs_missing_files_t *missing_files = obs_missing_files_create(); + + obs_source_t *source = s->source; + obs_data_t *settings = obs_source_get_settings(source); + obs_data_array_t *files = obs_data_get_array(settings, S_FILES); + + size_t l = obs_data_array_count(files); + for (size_t i = 0; i < l; i++) { + obs_data_t *item = obs_data_array_item(files, i); + const char *path = obs_data_get_string(item, "value"); + + if (strcmp(path, "") != 0) { + if (!os_file_exists(path)) { + obs_missing_file_t *file = + obs_missing_file_create( + path, missing_file_callback, + OBS_MISSING_FILE_SOURCE, source, + (void *)path); + + obs_missing_files_add_file(missing_files, file); + } + } + + obs_data_release(item); + } + + obs_data_array_release(files); + obs_data_release(settings); + + return missing_files; +} + struct obs_source_info slideshow_info = { .id = "slideshow", .type = OBS_SOURCE_TYPE_INPUT, @@ -977,6 +1042,7 @@ struct obs_source_info slideshow_info = { .get_height = ss_height, .get_defaults = ss_defaults, .get_properties = ss_properties, + .missing_files = ss_missingfiles, .icon_type = OBS_ICON_TYPE_SLIDESHOW, .media_play_pause = ss_play_pause, .media_restart = ss_restart, diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-source.c b/plugins/obs-ffmpeg/obs-ffmpeg-source.c index 6781e16f9..685747fd6 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-source.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-source.c @@ -727,6 +727,37 @@ static enum obs_media_state ffmpeg_source_get_state(void *data) return s->state; } +static void missing_file_callback(void *src, const char *new_path, void *data) +{ + struct ffmpeg_source *s = src; + + obs_source_t *source = s->source; + obs_data_t *settings = obs_source_get_settings(source); + obs_data_set_string(settings, "local_file", new_path); + obs_source_update(source, settings); + obs_data_release(settings); + + UNUSED_PARAMETER(data); +} + +static obs_missing_files_t *ffmpeg_source_missingfiles(void *data) +{ + struct ffmpeg_source *s = data; + obs_missing_files_t *files = obs_missing_files_create(); + + if (s->is_local_file && strcmp(s->input, "") != 0) { + if (!os_file_exists(s->input)) { + obs_missing_file_t *file = obs_missing_file_create( + s->input, missing_file_callback, + OBS_MISSING_FILE_SOURCE, s->source, NULL); + + obs_missing_files_add_file(files, file); + } + } + + return files; +} + struct obs_source_info ffmpeg_source = { .id = "ffmpeg_source", .type = OBS_SOURCE_TYPE_INPUT, @@ -741,6 +772,7 @@ struct obs_source_info ffmpeg_source = { .activate = ffmpeg_source_activate, .deactivate = ffmpeg_source_deactivate, .video_tick = ffmpeg_source_tick, + .missing_files = ffmpeg_source_missingfiles, .update = ffmpeg_source_update, .icon_type = OBS_ICON_TYPE_MEDIA, .media_play_pause = ffmpeg_source_play_pause, diff --git a/plugins/obs-text/gdiplus/obs-text.cpp b/plugins/obs-text/gdiplus/obs-text.cpp index d28e1f091..7c8efd51e 100644 --- a/plugins/obs-text/gdiplus/obs-text.cpp +++ b/plugins/obs-text/gdiplus/obs-text.cpp @@ -1088,6 +1088,19 @@ static void defaults(obs_data_t *settings, int ver) obs_data_release(font_obj); }; +static void missing_file_callback(void *src, const char *new_path, void *data) +{ + TextSource *s = reinterpret_cast(src); + + obs_source_t *source = s->source; + obs_data_t *settings = obs_source_get_settings(source); + obs_data_set_string(settings, S_FILE, new_path); + obs_source_update(source, settings); + obs_data_release(settings); + + UNUSED_PARAMETER(data); +} + bool obs_module_load(void) { obs_source_info si = {}; @@ -1121,6 +1134,32 @@ bool obs_module_load(void) si.video_render = [](void *data, gs_effect_t *) { reinterpret_cast(data)->Render(); }; + si.missing_files = [](void *data) { + TextSource *s = reinterpret_cast(data); + obs_missing_files_t *files = obs_missing_files_create(); + + obs_source_t *source = s->source; + obs_data_t *settings = obs_source_get_settings(source); + + bool read = obs_data_get_bool(settings, S_USE_FILE); + const char *path = obs_data_get_string(settings, S_FILE); + + if (read && strcmp(path, "") != 0) { + if (!os_file_exists(path)) { + obs_missing_file_t *file = + obs_missing_file_create( + path, missing_file_callback, + OBS_MISSING_FILE_SOURCE, + s->source, NULL); + + obs_missing_files_add_file(files, file); + } + } + + obs_data_release(settings); + + return files; + }; obs_source_info si_v2 = si; si_v2.version = 2; diff --git a/plugins/text-freetype2/text-freetype2.c b/plugins/text-freetype2/text-freetype2.c index fec89b94a..08b49412d 100644 --- a/plugins/text-freetype2/text-freetype2.c +++ b/plugins/text-freetype2/text-freetype2.c @@ -70,6 +70,7 @@ static struct obs_source_info freetype2_source_info_v2 = { .video_render = ft2_source_render, .video_tick = ft2_video_tick, .get_properties = ft2_source_properties, + .missing_files = ft2_missing_files, .icon_type = OBS_ICON_TYPE_TEXT, }; @@ -551,3 +552,42 @@ static void *ft2_source_create_v2(obs_data_t *settings, obs_source_t *source) { return ft2_source_create(settings, source, 2); } + +static void missing_file_callback(void *src, const char *new_path, void *data) +{ + struct ft2_source *s = src; + + obs_source_t *source = s->src; + obs_data_t *settings = obs_source_get_settings(source); + obs_data_set_string(settings, "text_file", new_path); + obs_source_update(source, settings); + obs_data_release(settings); + + UNUSED_PARAMETER(data); +} + +static obs_missing_files_t *ft2_missing_files(void *data) +{ + struct ft2_source *s = data; + obs_missing_files_t *files = obs_missing_files_create(); + + obs_source_t *source = s->src; + obs_data_t *settings = obs_source_get_settings(source); + + bool read = obs_data_get_bool(settings, "from_file"); + const char *path = obs_data_get_string(settings, "text_file"); + + if (read && strcmp(path, "") != 0) { + if (!os_file_exists(path)) { + obs_missing_file_t *file = obs_missing_file_create( + path, missing_file_callback, + OBS_MISSING_FILE_SOURCE, s->src, NULL); + + obs_missing_files_add_file(files, file); + } + } + + obs_data_release(settings); + + return files; +} diff --git a/plugins/text-freetype2/text-freetype2.h b/plugins/text-freetype2/text-freetype2.h index bae0cbb91..ff7e5b4ca 100644 --- a/plugins/text-freetype2/text-freetype2.h +++ b/plugins/text-freetype2/text-freetype2.h @@ -88,6 +88,8 @@ static obs_properties_t *ft2_source_properties(void *unused); static const char *ft2_source_get_name(void *unused); +static obs_missing_files_t *ft2_missing_files(void *data); + uint32_t get_ft2_text_width(wchar_t *text, struct ft2_source *srcdata); time_t get_modified_timestamp(char *filename); diff --git a/plugins/vlc-video/vlc-video-source.c b/plugins/vlc-video/vlc-video-source.c index dce6d471d..c663601c3 100644 --- a/plugins/vlc-video/vlc-video-source.c +++ b/plugins/vlc-video/vlc-video-source.c @@ -1086,6 +1086,71 @@ static obs_properties_t *vlcs_properties(void *data) return ppts; } +static void missing_file_callback(void *src, const char *new_path, void *data) +{ + struct vlc_source *s = src; + const char *orig_path = data; + + obs_source_t *source = s->source; + obs_data_t *settings = obs_source_get_settings(source); + obs_data_array_t *files = obs_data_get_array(settings, S_PLAYLIST); + + size_t l = obs_data_array_count(files); + for (size_t i = 0; i < l; i++) { + obs_data_t *file = obs_data_array_item(files, i); + const char *path = obs_data_get_string(file, "value"); + + if (strcmp(path, orig_path) == 0) { + obs_data_set_string(file, "value", new_path); + + obs_data_release(file); + break; + } + + obs_data_release(file); + } + + obs_source_update(source, settings); + + obs_data_array_release(files); + obs_data_release(settings); +} + +static obs_missing_files_t *vlcs_missingfiles(void *data) +{ + struct vlc_source *s = data; + obs_missing_files_t *missing_files = obs_missing_files_create(); + + obs_source_t *source = s->source; + obs_data_t *settings = obs_source_get_settings(source); + obs_data_array_t *files = obs_data_get_array(settings, S_PLAYLIST); + + size_t l = obs_data_array_count(files); + for (size_t i = 0; i < l; i++) { + obs_data_t *item = obs_data_array_item(files, i); + const char *path = obs_data_get_string(item, "value"); + + if (strcmp(path, "") != 0) { + if (!os_file_exists(path)) { + obs_missing_file_t *file = + obs_missing_file_create( + path, missing_file_callback, + OBS_MISSING_FILE_SOURCE, source, + (void *)path); + + obs_missing_files_add_file(missing_files, file); + } + } + + obs_data_release(item); + } + + obs_data_array_release(files); + obs_data_release(settings); + + return missing_files; +} + struct obs_source_info vlc_source_info = { .id = "vlc_source", .type = OBS_SOURCE_TYPE_INPUT, @@ -1100,6 +1165,7 @@ struct obs_source_info vlc_source_info = { .get_properties = vlcs_properties, .activate = vlcs_activate, .deactivate = vlcs_deactivate, + .missing_files = vlcs_missingfiles, .icon_type = OBS_ICON_TYPE_MEDIA, .media_play_pause = vlcs_play_pause, .media_restart = vlcs_restart,