|
|
@@ -26,37 +26,14 @@
|
|
|
|
|
|
static void obs_source_destroy(obs_source_t source);
|
|
|
|
|
|
-bool load_source_info(void *module, const char *module_name,
|
|
|
- const char *id, struct source_info *info)
|
|
|
-{
|
|
|
- LOAD_MODULE_SUBFUNC(getname, true);
|
|
|
- LOAD_MODULE_SUBFUNC(create, true);
|
|
|
- LOAD_MODULE_SUBFUNC(destroy, true);
|
|
|
- LOAD_MODULE_SUBFUNC(get_output_flags, true);
|
|
|
-
|
|
|
- LOAD_MODULE_SUBFUNC(properties, false);
|
|
|
- LOAD_MODULE_SUBFUNC(update, false);
|
|
|
- LOAD_MODULE_SUBFUNC(activate, false);
|
|
|
- LOAD_MODULE_SUBFUNC(deactivate, false);
|
|
|
- LOAD_MODULE_SUBFUNC(video_tick, false);
|
|
|
- LOAD_MODULE_SUBFUNC(video_render, false);
|
|
|
- LOAD_MODULE_SUBFUNC(getwidth, false);
|
|
|
- LOAD_MODULE_SUBFUNC(getheight, false);
|
|
|
- LOAD_MODULE_SUBFUNC(filter_video, false);
|
|
|
- LOAD_MODULE_SUBFUNC(filter_audio, false);
|
|
|
-
|
|
|
- info->id = id;
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
-static inline const struct source_info *find_source(struct darray *list,
|
|
|
+static inline const struct obs_source_info *find_source(struct darray *list,
|
|
|
const char *id)
|
|
|
{
|
|
|
size_t i;
|
|
|
- struct source_info *array = list->array;
|
|
|
+ struct obs_source_info *array = list->array;
|
|
|
|
|
|
for (i = 0; i < list->num; i++) {
|
|
|
- struct source_info *info = array+i;
|
|
|
+ struct obs_source_info *info = array+i;
|
|
|
if (strcmp(info->id, id) == 0)
|
|
|
return info;
|
|
|
}
|
|
|
@@ -64,16 +41,25 @@ static inline const struct source_info *find_source(struct darray *list,
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
-static const struct source_info *get_source_info(enum obs_source_type type,
|
|
|
+static const struct obs_source_info *get_source_info(enum obs_source_type type,
|
|
|
const char *id)
|
|
|
{
|
|
|
struct darray *list = NULL;
|
|
|
|
|
|
switch (type) {
|
|
|
- case SOURCE_INPUT: list = &obs->input_types.da; break;
|
|
|
- case SOURCE_FILTER: list = &obs->filter_types.da; break;
|
|
|
- case SOURCE_TRANSITION: list = &obs->transition_types.da; break;
|
|
|
- case SOURCE_SCENE:
|
|
|
+ case OBS_SOURCE_TYPE_INPUT:
|
|
|
+ list = &obs->input_types.da;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case OBS_SOURCE_TYPE_FILTER:
|
|
|
+ list = &obs->filter_types.da;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case OBS_SOURCE_TYPE_TRANSITION:
|
|
|
+ list = &obs->transition_types.da;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case OBS_SOURCE_TYPE_SCENE:
|
|
|
default:
|
|
|
blog(LOG_WARNING, "get_source_info: invalid source type");
|
|
|
return NULL;
|
|
|
@@ -95,22 +81,21 @@ bool obs_source_init_handlers(struct obs_source *source)
|
|
|
const char *obs_source_getdisplayname(enum obs_source_type type,
|
|
|
const char *id, const char *locale)
|
|
|
{
|
|
|
- const struct source_info *info = get_source_info(type, id);
|
|
|
+ const struct obs_source_info *info = get_source_info(type, id);
|
|
|
return (info != NULL) ? info->getname(locale) : NULL;
|
|
|
}
|
|
|
|
|
|
/* internal initialization */
|
|
|
-bool obs_source_init(struct obs_source *source, const struct source_info *info)
|
|
|
+bool obs_source_init(struct obs_source *source,
|
|
|
+ const struct obs_source_info *info)
|
|
|
{
|
|
|
- uint32_t flags = info->get_output_flags(source->data);
|
|
|
-
|
|
|
source->refs = 1;
|
|
|
source->volume = 1.0f;
|
|
|
pthread_mutex_init_value(&source->filter_mutex);
|
|
|
pthread_mutex_init_value(&source->video_mutex);
|
|
|
pthread_mutex_init_value(&source->audio_mutex);
|
|
|
|
|
|
- memcpy(&source->callbacks, info, sizeof(struct source_info));
|
|
|
+ memcpy(&source->info, info, sizeof(struct obs_source_info));
|
|
|
|
|
|
if (pthread_mutex_init(&source->filter_mutex, NULL) != 0)
|
|
|
return false;
|
|
|
@@ -119,7 +104,7 @@ bool obs_source_init(struct obs_source *source, const struct source_info *info)
|
|
|
if (pthread_mutex_init(&source->video_mutex, NULL) != 0)
|
|
|
return false;
|
|
|
|
|
|
- if (flags & SOURCE_AUDIO) {
|
|
|
+ if (info->output_flags & OBS_SOURCE_AUDIO) {
|
|
|
source->audio_line = audio_output_createline(obs->audio.audio,
|
|
|
source->name);
|
|
|
if (!source->audio_line) {
|
|
|
@@ -148,7 +133,7 @@ obs_source_t obs_source_create(enum obs_source_type type, const char *id,
|
|
|
{
|
|
|
struct obs_source *source;
|
|
|
|
|
|
- const struct source_info *info = get_source_info(type, id);
|
|
|
+ const struct obs_source_info *info = get_source_info(type, id);
|
|
|
if (!info) {
|
|
|
blog(LOG_WARNING, "Source '%s' not found", id);
|
|
|
return NULL;
|
|
|
@@ -268,7 +253,7 @@ static void obs_source_destroy(obs_source_t source)
|
|
|
gs_leavecontext();
|
|
|
|
|
|
if (source->data)
|
|
|
- source->callbacks.destroy(source->data);
|
|
|
+ source->info.destroy(source->data);
|
|
|
|
|
|
for (i = 0; i < MAX_AUDIO_PLANES; i++)
|
|
|
bfree(source->audio_data.data[i]);
|
|
|
@@ -279,6 +264,7 @@ static void obs_source_destroy(obs_source_t source)
|
|
|
proc_handler_destroy(source->procs);
|
|
|
signal_handler_destroy(source->signals);
|
|
|
|
|
|
+ texrender_destroy(source->filter_texrender);
|
|
|
da_free(source->video_frames);
|
|
|
da_free(source->filters);
|
|
|
pthread_mutex_destroy(&source->filter_mutex);
|
|
|
@@ -338,41 +324,45 @@ bool obs_source_removed(obs_source_t source)
|
|
|
obs_properties_t obs_source_properties(enum obs_source_type type,
|
|
|
const char *id, const char *locale)
|
|
|
{
|
|
|
- const struct source_info *info = get_source_info(type, id);
|
|
|
- if (info && info->properties)
|
|
|
- return info->properties(locale);
|
|
|
+ const struct obs_source_info *info = get_source_info(type, id);
|
|
|
+ if (info && info->get_properties)
|
|
|
+ return info->get_properties(locale);
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
uint32_t obs_source_get_output_flags(obs_source_t source)
|
|
|
{
|
|
|
- return source->callbacks.get_output_flags(source->data);
|
|
|
+ return source->info.output_flags;
|
|
|
}
|
|
|
|
|
|
void obs_source_update(obs_source_t source, obs_data_t settings)
|
|
|
{
|
|
|
obs_data_replace(&source->settings, settings);
|
|
|
|
|
|
- if (source->callbacks.update)
|
|
|
- source->callbacks.update(source->data, source->settings);
|
|
|
+ if (source->info.update)
|
|
|
+ source->info.update(source->data, source->settings);
|
|
|
}
|
|
|
|
|
|
void obs_source_activate(obs_source_t source)
|
|
|
{
|
|
|
- if (source->callbacks.activate)
|
|
|
- source->callbacks.activate(source->data);
|
|
|
+ if (source->info.activate)
|
|
|
+ source->info.activate(source->data);
|
|
|
}
|
|
|
|
|
|
void obs_source_deactivate(obs_source_t source)
|
|
|
{
|
|
|
- if (source->callbacks.deactivate)
|
|
|
- source->callbacks.deactivate(source->data);
|
|
|
+ if (source->info.deactivate)
|
|
|
+ source->info.deactivate(source->data);
|
|
|
}
|
|
|
|
|
|
void obs_source_video_tick(obs_source_t source, float seconds)
|
|
|
{
|
|
|
- if (source->callbacks.video_tick)
|
|
|
- source->callbacks.video_tick(source->data, seconds);
|
|
|
+ /* reset the filter render texture information once every frame */
|
|
|
+ if (source->filter_texrender)
|
|
|
+ texrender_reset(source->filter_texrender);
|
|
|
+
|
|
|
+ if (source->info.video_tick)
|
|
|
+ source->info.video_tick(source->data, seconds);
|
|
|
}
|
|
|
|
|
|
/* unless the value is 3+ hours worth of frames, this won't overflow */
|
|
|
@@ -399,13 +389,11 @@ static inline void reset_audio_timing(obs_source_t source, uint64_t timetamp)
|
|
|
static inline void handle_ts_jump(obs_source_t source, uint64_t ts,
|
|
|
uint64_t diff)
|
|
|
{
|
|
|
- uint32_t flags = source->callbacks.get_output_flags(source->data);
|
|
|
-
|
|
|
blog(LOG_DEBUG, "Timestamp for source '%s' jumped by '%lld', "
|
|
|
"resetting audio timing", source->name, diff);
|
|
|
|
|
|
/* if has video, ignore audio data until reset */
|
|
|
- if (flags & SOURCE_ASYNC_VIDEO)
|
|
|
+ if (source->info.output_flags & OBS_SOURCE_ASYNC_VIDEO)
|
|
|
source->audio_reset_ref--;
|
|
|
else
|
|
|
reset_audio_timing(source, ts);
|
|
|
@@ -555,7 +543,7 @@ static void obs_source_draw_texture(texture_t tex, struct source_frame *frame)
|
|
|
sizeof(float) * 16);
|
|
|
}
|
|
|
|
|
|
- param = effect_getparambyname(effect, "diffuse");
|
|
|
+ param = effect_getparambyname(effect, "image");
|
|
|
effect_settexture(effect, param, tex);
|
|
|
|
|
|
gs_draw_sprite(tex, frame->flip ? GS_FLIP_V : 0, 0, 0);
|
|
|
@@ -583,17 +571,18 @@ static inline void obs_source_render_filters(obs_source_t source)
|
|
|
source->rendering_filter = false;
|
|
|
}
|
|
|
|
|
|
-static inline void obs_source_default_render(obs_source_t source, bool yuv)
|
|
|
+static inline void obs_source_default_render(obs_source_t source,
|
|
|
+ bool color_matrix)
|
|
|
{
|
|
|
effect_t effect = obs->video.default_effect;
|
|
|
- const char *tech_name = yuv ? "DrawMatrix" : "Draw";
|
|
|
+ const char *tech_name = color_matrix ? "DrawMatrix" : "Draw";
|
|
|
technique_t tech = effect_gettechnique(effect, tech_name);
|
|
|
size_t passes, i;
|
|
|
|
|
|
passes = technique_begin(tech);
|
|
|
for (i = 0; i < passes; i++) {
|
|
|
technique_beginpass(tech, i);
|
|
|
- source->callbacks.video_render(source->data);
|
|
|
+ source->info.video_render(source->data, effect);
|
|
|
technique_endpass(tech);
|
|
|
}
|
|
|
technique_end(tech);
|
|
|
@@ -601,20 +590,21 @@ static inline void obs_source_default_render(obs_source_t source, bool yuv)
|
|
|
|
|
|
static inline void obs_source_main_render(obs_source_t source)
|
|
|
{
|
|
|
- uint32_t flags = source->callbacks.get_output_flags(source->data);
|
|
|
+ uint32_t flags = source->info.output_flags;
|
|
|
+ bool color_matrix = (flags & OBS_SOURCE_COLOR_MATRIX) != 0;
|
|
|
bool default_effect = !source->filter_parent &&
|
|
|
source->filters.num == 0 &&
|
|
|
- (flags & SOURCE_DEFAULT_EFFECT) != 0;
|
|
|
+ (flags & OBS_SOURCE_CUSTOM_DRAW) == 0;
|
|
|
|
|
|
if (default_effect)
|
|
|
- obs_source_default_render(source, (flags & SOURCE_YUV) != 0);
|
|
|
+ obs_source_default_render(source, color_matrix);
|
|
|
else
|
|
|
- source->callbacks.video_render(source->data);
|
|
|
+ source->info.video_render(source->data, NULL);
|
|
|
}
|
|
|
|
|
|
void obs_source_video_render(obs_source_t source)
|
|
|
{
|
|
|
- if (source->callbacks.video_render) {
|
|
|
+ if (source->info.video_render) {
|
|
|
if (source->filters.num && !source->rendering_filter)
|
|
|
obs_source_render_filters(source);
|
|
|
else
|
|
|
@@ -630,15 +620,15 @@ void obs_source_video_render(obs_source_t source)
|
|
|
|
|
|
uint32_t obs_source_getwidth(obs_source_t source)
|
|
|
{
|
|
|
- if (source->callbacks.getwidth)
|
|
|
- return source->callbacks.getwidth(source->data);
|
|
|
+ if (source->info.getwidth)
|
|
|
+ return source->info.getwidth(source->data);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
uint32_t obs_source_getheight(obs_source_t source)
|
|
|
{
|
|
|
- if (source->callbacks.getheight)
|
|
|
- return source->callbacks.getheight(source->data);
|
|
|
+ if (source->info.getheight)
|
|
|
+ return source->info.getheight(source->data);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
@@ -747,8 +737,8 @@ static inline struct source_frame *filter_async_video(obs_source_t source,
|
|
|
size_t i;
|
|
|
for (i = source->filters.num; i > 0; i--) {
|
|
|
struct obs_source *filter = source->filters.array[i-1];
|
|
|
- if (filter->callbacks.filter_video) {
|
|
|
- in = filter->callbacks.filter_video(filter->data, in);
|
|
|
+ if (filter->info.filter_video) {
|
|
|
+ in = filter->info.filter_video(filter->data, in);
|
|
|
if (!in)
|
|
|
return NULL;
|
|
|
}
|
|
|
@@ -842,8 +832,8 @@ static inline struct filtered_audio *filter_async_audio(obs_source_t source,
|
|
|
size_t i;
|
|
|
for (i = source->filters.num; i > 0; i--) {
|
|
|
struct obs_source *filter = source->filters.array[i-1];
|
|
|
- if (filter->callbacks.filter_audio) {
|
|
|
- in = filter->callbacks.filter_audio(filter->data, in);
|
|
|
+ if (filter->info.filter_audio) {
|
|
|
+ in = filter->info.filter_audio(filter->data, in);
|
|
|
if (!in)
|
|
|
return NULL;
|
|
|
}
|
|
|
@@ -952,11 +942,13 @@ void obs_source_output_audio(obs_source_t source,
|
|
|
output = filter_async_audio(source, &source->audio_data);
|
|
|
|
|
|
if (output) {
|
|
|
+ bool async = (flags & OBS_SOURCE_ASYNC_VIDEO) == 0;
|
|
|
+
|
|
|
pthread_mutex_lock(&source->audio_mutex);
|
|
|
|
|
|
/* wait for video to start before outputting any audio so we
|
|
|
* have a base for sync */
|
|
|
- if (source->timing_set || (flags & SOURCE_ASYNC_VIDEO) == 0) {
|
|
|
+ if (source->timing_set || async) {
|
|
|
struct audio_data data;
|
|
|
|
|
|
for (int i = 0; i < MAX_AUDIO_PLANES; i++)
|
|
|
@@ -1091,15 +1083,15 @@ void obs_source_gettype(obs_source_t source, enum obs_source_type *type,
|
|
|
const char **id)
|
|
|
{
|
|
|
if (type) *type = source->type;
|
|
|
- if (id) *id = source->callbacks.id;
|
|
|
+ if (id) *id = source->info.id;
|
|
|
}
|
|
|
|
|
|
static inline void render_filter_bypass(obs_source_t target, effect_t effect,
|
|
|
- uint32_t width, uint32_t height, bool yuv)
|
|
|
+ uint32_t width, uint32_t height, bool use_matrix)
|
|
|
{
|
|
|
- const char *tech_name = yuv ? "DrawMatrix" : "Draw";
|
|
|
+ const char *tech_name = use_matrix ? "DrawMatrix" : "Draw";
|
|
|
technique_t tech = effect_gettechnique(effect, tech_name);
|
|
|
- eparam_t diffuse = effect_getparambyname(effect, "diffuse");
|
|
|
+ eparam_t image = effect_getparambyname(effect, "image");
|
|
|
size_t passes, i;
|
|
|
|
|
|
passes = technique_begin(tech);
|
|
|
@@ -1112,14 +1104,14 @@ static inline void render_filter_bypass(obs_source_t target, effect_t effect,
|
|
|
}
|
|
|
|
|
|
static inline void render_filter_tex(texture_t tex, effect_t effect,
|
|
|
- uint32_t width, uint32_t height, bool yuv)
|
|
|
+ uint32_t width, uint32_t height, bool use_matrix)
|
|
|
{
|
|
|
- const char *tech_name = yuv ? "DrawMatrix" : "Draw";
|
|
|
+ const char *tech_name = use_matrix ? "DrawMatrix" : "Draw";
|
|
|
technique_t tech = effect_gettechnique(effect, tech_name);
|
|
|
- eparam_t diffuse = effect_getparambyname(effect, "diffuse");
|
|
|
+ eparam_t image = effect_getparambyname(effect, "image");
|
|
|
size_t passes, i;
|
|
|
|
|
|
- effect_settexture(effect, diffuse, tex);
|
|
|
+ effect_settexture(effect, image, tex);
|
|
|
|
|
|
passes = technique_begin(tech);
|
|
|
for (i = 0; i < passes; i++) {
|
|
|
@@ -1130,8 +1122,8 @@ static inline void render_filter_tex(texture_t tex, effect_t effect,
|
|
|
technique_end(tech);
|
|
|
}
|
|
|
|
|
|
-void obs_source_process_filter(obs_source_t filter, texrender_t texrender,
|
|
|
- effect_t effect, uint32_t width, uint32_t height,
|
|
|
+void obs_source_process_filter(obs_source_t filter, effect_t effect,
|
|
|
+ uint32_t width, uint32_t height, enum gs_color_format format,
|
|
|
enum allow_direct_render allow_direct)
|
|
|
{
|
|
|
obs_source_t target = obs_filter_gettarget(filter);
|
|
|
@@ -1140,8 +1132,8 @@ void obs_source_process_filter(obs_source_t filter, texrender_t texrender,
|
|
|
uint32_t parent_flags = obs_source_get_output_flags(parent);
|
|
|
int cx = obs_source_getwidth(target);
|
|
|
int cy = obs_source_getheight(target);
|
|
|
- bool yuv = (target_flags & SOURCE_YUV) != 0;
|
|
|
- bool expects_def = (parent_flags & SOURCE_DEFAULT_EFFECT) != 0;
|
|
|
+ bool use_matrix = !!(target_flags & OBS_SOURCE_COLOR_MATRIX);
|
|
|
+ bool expects_def = !(parent_flags & OBS_SOURCE_CUSTOM_DRAW);
|
|
|
bool can_directly = allow_direct == ALLOW_DIRECT_RENDERING;
|
|
|
|
|
|
/* if the parent does not use any custom effects, and this is the last
|
|
|
@@ -1149,23 +1141,27 @@ void obs_source_process_filter(obs_source_t filter, texrender_t texrender,
|
|
|
* using the filter effect instead of rendering to texture to reduce
|
|
|
* the total number of passes */
|
|
|
if (can_directly && expects_def && target == parent) {
|
|
|
- render_filter_bypass(target, effect, width, height, yuv);
|
|
|
+ render_filter_bypass(target, effect, width, height, use_matrix);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (texrender_begin(texrender, cx, cy)) {
|
|
|
+ if (!filter->filter_texrender)
|
|
|
+ filter->filter_texrender = texrender_create(format,
|
|
|
+ GS_ZS_NONE);
|
|
|
+
|
|
|
+ if (texrender_begin(filter->filter_texrender, cx, cy)) {
|
|
|
gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);
|
|
|
if (expects_def && parent == target)
|
|
|
- obs_source_default_render(parent, yuv);
|
|
|
+ obs_source_default_render(parent, use_matrix);
|
|
|
else
|
|
|
obs_source_video_render(target);
|
|
|
- texrender_end(texrender);
|
|
|
+ texrender_end(filter->filter_texrender);
|
|
|
}
|
|
|
|
|
|
/* --------------------------- */
|
|
|
|
|
|
- render_filter_tex(texrender_gettexture(texrender), effect,
|
|
|
- width, height, yuv);
|
|
|
+ render_filter_tex(texrender_gettexture(filter->filter_texrender),
|
|
|
+ effect, width, height, use_matrix);
|
|
|
}
|
|
|
|
|
|
signal_handler_t obs_source_signalhandler(obs_source_t source)
|