mirror of
https://github.com/obsproject/obs-studio.git
synced 2024-07-14 23:34:08 +00:00
libobs: Buffer scene item visibility actions
This buffers scene item visibility actions so that if obs_sceneitem_set_visible to true or false, that it will ensure that the action is mapped to the exact sample point time in which obs_sceneitem_set_visible is called. Mapping to the exact sample point isn't necessary, but it's a nice thing to have.
This commit is contained in:
parent
247a42c2a1
commit
3c68196c5c
|
@ -142,7 +142,7 @@ static void scene_enum_sources(void *data,
|
|||
next = item->next;
|
||||
|
||||
obs_sceneitem_addref(item);
|
||||
if (item->visible)
|
||||
if (os_atomic_load_long(&item->active_refs) > 0)
|
||||
enum_callback(scene->source, item->source, param);
|
||||
obs_sceneitem_release(item);
|
||||
|
||||
|
@ -347,7 +347,7 @@ static void scene_video_render(void *data, gs_effect_t *effect)
|
|||
if (source_size_changed(item))
|
||||
update_item_transform(item);
|
||||
|
||||
if (item->visible) {
|
||||
if (item->user_visible) {
|
||||
gs_matrix_push();
|
||||
gs_matrix_mul(&item->draw_transform);
|
||||
obs_source_video_render(item->source);
|
||||
|
@ -364,11 +364,33 @@ static void scene_video_render(void *data, gs_effect_t *effect)
|
|||
UNUSED_PARAMETER(effect);
|
||||
}
|
||||
|
||||
static inline void set_visibility(struct obs_scene_item *item, bool vis)
|
||||
{
|
||||
pthread_mutex_lock(&item->actions_mutex);
|
||||
|
||||
da_resize(item->audio_actions, 0);
|
||||
|
||||
if (os_atomic_load_long(&item->active_refs) > 0) {
|
||||
if (!vis)
|
||||
obs_source_remove_active_child(item->parent->source,
|
||||
item->source);
|
||||
} else if (vis) {
|
||||
obs_source_add_active_child(item->parent->source, item->source);
|
||||
}
|
||||
|
||||
os_atomic_set_long(&item->active_refs, vis ? 1 : 0);
|
||||
item->visible = vis;
|
||||
item->user_visible = vis;
|
||||
|
||||
pthread_mutex_unlock(&item->actions_mutex);
|
||||
}
|
||||
|
||||
static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)
|
||||
{
|
||||
const char *name = obs_data_get_string(item_data, "name");
|
||||
obs_source_t *source = obs_get_source_by_name(name);
|
||||
struct obs_scene_item *item;
|
||||
bool visible;
|
||||
|
||||
if (!source) {
|
||||
blog(LOG_WARNING, "[scene_load_item] Source %s not found!",
|
||||
|
@ -391,12 +413,11 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)
|
|||
|
||||
item->rot = (float)obs_data_get_double(item_data, "rot");
|
||||
item->align = (uint32_t)obs_data_get_int(item_data, "align");
|
||||
item->visible = obs_data_get_bool(item_data, "visible");
|
||||
visible = obs_data_get_bool(item_data, "visible");
|
||||
obs_data_get_vec2(item_data, "pos", &item->pos);
|
||||
obs_data_get_vec2(item_data, "scale", &item->scale);
|
||||
|
||||
if (!item->visible)
|
||||
obs_source_remove_active_child(scene->source, source);
|
||||
set_visibility(item, visible);
|
||||
|
||||
item->bounds_type =
|
||||
(enum obs_bounds_type)obs_data_get_int(item_data,
|
||||
|
@ -437,7 +458,7 @@ static void scene_save_item(obs_data_array_t *array,
|
|||
const char *name = obs_source_get_name(item->source);
|
||||
|
||||
obs_data_set_string(item_data, "name", name);
|
||||
obs_data_set_bool (item_data, "visible", item->visible);
|
||||
obs_data_set_bool (item_data, "visible", item->user_visible);
|
||||
obs_data_set_double(item_data, "rot", item->rot);
|
||||
obs_data_set_vec2 (item_data, "pos", &item->pos);
|
||||
obs_data_set_vec2 (item_data, "scale", &item->scale);
|
||||
|
@ -482,6 +503,101 @@ static uint32_t scene_getheight(void *data)
|
|||
return obs->video.base_height;
|
||||
}
|
||||
|
||||
static void apply_scene_item_audio_actions(struct obs_scene_item *item,
|
||||
float **p_buf, uint64_t ts, size_t sample_rate)
|
||||
{
|
||||
bool cur_visible = item->visible;
|
||||
uint64_t frame_num = 0;
|
||||
size_t deref_count = 0;
|
||||
float *buf;
|
||||
|
||||
if (!*p_buf)
|
||||
*p_buf = malloc(AUDIO_OUTPUT_FRAMES * sizeof(float));
|
||||
buf = *p_buf;
|
||||
|
||||
pthread_mutex_lock(&item->actions_mutex);
|
||||
|
||||
for (size_t i = 0; i < item->audio_actions.num; i++) {
|
||||
struct item_action action = item->audio_actions.array[i];
|
||||
uint64_t timestamp = action.timestamp;
|
||||
uint64_t new_frame_num;
|
||||
|
||||
if (timestamp < ts)
|
||||
timestamp = ts;
|
||||
|
||||
new_frame_num = (timestamp - ts) * (uint64_t)sample_rate /
|
||||
1000000000ULL;
|
||||
|
||||
if (new_frame_num >= AUDIO_OUTPUT_FRAMES)
|
||||
break;
|
||||
|
||||
da_erase(item->audio_actions, i--);
|
||||
|
||||
item->visible = action.visible;
|
||||
if (!item->visible)
|
||||
deref_count++;
|
||||
|
||||
if (new_frame_num > frame_num) {
|
||||
for (; frame_num < new_frame_num; frame_num++)
|
||||
buf[frame_num] = cur_visible ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
cur_visible = item->visible;
|
||||
}
|
||||
|
||||
for (; frame_num < AUDIO_OUTPUT_FRAMES; frame_num++)
|
||||
buf[frame_num] = cur_visible ? 1.0f : 0.0f;
|
||||
|
||||
pthread_mutex_unlock(&item->actions_mutex);
|
||||
|
||||
while (deref_count--) {
|
||||
if (os_atomic_dec_long(&item->active_refs) == 0) {
|
||||
obs_source_remove_active_child(item->parent->source,
|
||||
item->source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool apply_scene_item_volume(struct obs_scene_item *item,
|
||||
float **buf, uint64_t ts, size_t sample_rate)
|
||||
{
|
||||
bool actions_pending;
|
||||
struct item_action action;
|
||||
|
||||
pthread_mutex_lock(&item->actions_mutex);
|
||||
|
||||
actions_pending = item->audio_actions.num > 0;
|
||||
if (actions_pending)
|
||||
action = item->audio_actions.array[0];
|
||||
|
||||
pthread_mutex_unlock(&item->actions_mutex);
|
||||
|
||||
if (actions_pending) {
|
||||
uint64_t duration = (uint64_t)AUDIO_OUTPUT_FRAMES *
|
||||
1000000000ULL / (uint64_t)sample_rate;
|
||||
|
||||
if (action.timestamp < (ts + duration)) {
|
||||
apply_scene_item_audio_actions(item, buf, ts,
|
||||
sample_rate);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void mix_audio_with_buf(float *p_out, float *p_in, float *buf_in,
|
||||
size_t pos, size_t count)
|
||||
{
|
||||
register float *out = p_out;
|
||||
register float *buf = buf_in + pos;
|
||||
register float *in = p_in + pos;
|
||||
register float *end = in + count;
|
||||
|
||||
while (in < end)
|
||||
*(out++) += *(in++) * *(buf++);
|
||||
}
|
||||
|
||||
static inline void mix_audio(float *p_out, float *p_in,
|
||||
size_t pos, size_t count)
|
||||
{
|
||||
|
@ -498,6 +614,7 @@ static bool scene_audio_render(void *data, uint64_t *ts_out,
|
|||
size_t channels, size_t sample_rate)
|
||||
{
|
||||
uint64_t timestamp = 0;
|
||||
float *buf = NULL;
|
||||
struct obs_source_audio_mix child_audio;
|
||||
struct obs_scene *scene = data;
|
||||
struct obs_scene_item *item;
|
||||
|
@ -526,6 +643,10 @@ static bool scene_audio_render(void *data, uint64_t *ts_out,
|
|||
while (item) {
|
||||
uint64_t source_ts;
|
||||
size_t pos, count;
|
||||
bool apply_buf;
|
||||
|
||||
apply_buf = apply_scene_item_volume(item, &buf, timestamp,
|
||||
sample_rate);
|
||||
|
||||
if (obs_source_audio_pending(item->source)) {
|
||||
item = item->next;
|
||||
|
@ -537,13 +658,25 @@ static bool scene_audio_render(void *data, uint64_t *ts_out,
|
|||
source_ts - timestamp);
|
||||
count = AUDIO_OUTPUT_FRAMES - pos;
|
||||
|
||||
if (!apply_buf && !item->visible) {
|
||||
item = item->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
obs_source_get_audio_mix(item->source, &child_audio);
|
||||
for (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) {
|
||||
if ((mixers & (1 << mix)) == 0)
|
||||
continue;
|
||||
|
||||
for (size_t ch = 0; ch < channels; ch++) {
|
||||
float *out = audio_output->output[mix].data[ch];
|
||||
float *in = child_audio.output[mix].data[ch];
|
||||
|
||||
mix_audio(out, in, pos, count);
|
||||
if (apply_buf)
|
||||
mix_audio_with_buf(out, in, buf, pos,
|
||||
count);
|
||||
else
|
||||
mix_audio(out, in, pos, count);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -552,6 +685,8 @@ static bool scene_audio_render(void *data, uint64_t *ts_out,
|
|||
|
||||
*ts_out = timestamp;
|
||||
audio_unlock(scene);
|
||||
|
||||
free(buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -596,10 +731,13 @@ obs_scene_t *obs_scene_duplicate(obs_scene_t *scene, const char *name)
|
|||
struct obs_scene_item *new_item =
|
||||
obs_scene_add(new_scene, source);
|
||||
|
||||
new_item->visible = item->visible;
|
||||
if (!new_item->visible)
|
||||
obs_source_remove_active_child(
|
||||
new_scene->source, source);
|
||||
if (!new_item) {
|
||||
item = item->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!item->user_visible)
|
||||
set_visibility(new_item, false);
|
||||
|
||||
new_item->selected = item->selected;
|
||||
new_item->pos = item->pos;
|
||||
|
@ -718,7 +856,7 @@ static bool hotkey_show_sceneitem(void *data, obs_hotkey_pair_id id,
|
|||
UNUSED_PARAMETER(hotkey);
|
||||
|
||||
obs_sceneitem_t *si = sceneitem_get_ref(data);
|
||||
if (pressed && si && !si->visible) {
|
||||
if (pressed && si && !si->user_visible) {
|
||||
obs_sceneitem_set_visible(si, true);
|
||||
obs_sceneitem_release(si);
|
||||
return true;
|
||||
|
@ -735,7 +873,7 @@ static bool hotkey_hide_sceneitem(void *data, obs_hotkey_pair_id id,
|
|||
UNUSED_PARAMETER(hotkey);
|
||||
|
||||
obs_sceneitem_t *si = sceneitem_get_ref(data);
|
||||
if (pressed && si && si->visible) {
|
||||
if (pressed && si && si->user_visible) {
|
||||
obs_sceneitem_set_visible(si, false);
|
||||
obs_sceneitem_release(si);
|
||||
return true;
|
||||
|
@ -775,11 +913,23 @@ static void init_hotkeys(obs_scene_t *scene, obs_sceneitem_t *item,
|
|||
dstr_free(&hide_desc);
|
||||
}
|
||||
|
||||
static inline bool source_has_audio(obs_source_t *source)
|
||||
{
|
||||
return (source->info.output_flags &
|
||||
(OBS_SOURCE_AUDIO | OBS_SOURCE_COMPOSITE)) != 0;
|
||||
}
|
||||
|
||||
obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source)
|
||||
{
|
||||
struct obs_scene_item *last;
|
||||
struct obs_scene_item *item;
|
||||
struct calldata params = {0};
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
struct item_action action = {
|
||||
.visible = true,
|
||||
.timestamp = os_gettime_ns()
|
||||
};
|
||||
|
||||
if (!scene)
|
||||
return NULL;
|
||||
|
@ -789,24 +939,39 @@ obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (pthread_mutex_init(&mutex, NULL) != 0) {
|
||||
blog(LOG_WARNING, "Failed to create scene item mutex");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!obs_source_add_active_child(scene->source, source)) {
|
||||
blog(LOG_WARNING, "Failed to add source to scene due to "
|
||||
"infinite source recursion");
|
||||
pthread_mutex_destroy(&mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
item = bzalloc(sizeof(struct obs_scene_item));
|
||||
item->source = source;
|
||||
item->visible = true;
|
||||
item->parent = scene;
|
||||
item->ref = 1;
|
||||
item->align = OBS_ALIGN_TOP | OBS_ALIGN_LEFT;
|
||||
item->actions_mutex = mutex;
|
||||
item->user_visible = true;
|
||||
os_atomic_set_long(&item->active_refs, 1);
|
||||
vec2_set(&item->scale, 1.0f, 1.0f);
|
||||
matrix4_identity(&item->draw_transform);
|
||||
matrix4_identity(&item->box_transform);
|
||||
|
||||
obs_source_addref(source);
|
||||
|
||||
if (source_has_audio(source)) {
|
||||
item->visible = false;
|
||||
da_push_back(item->audio_actions, &action);
|
||||
} else {
|
||||
item->visible = true;
|
||||
}
|
||||
|
||||
full_lock(scene);
|
||||
|
||||
last = scene->first_item;
|
||||
|
@ -837,8 +1002,10 @@ static void obs_sceneitem_destroy(obs_sceneitem_t *item)
|
|||
{
|
||||
if (item) {
|
||||
obs_hotkey_pair_unregister(item->toggle_visibility);
|
||||
pthread_mutex_destroy(&item->actions_mutex);
|
||||
if (item->source)
|
||||
obs_source_release(item->source);
|
||||
da_free(item->audio_actions);
|
||||
bfree(item);
|
||||
}
|
||||
}
|
||||
|
@ -880,8 +1047,8 @@ void obs_sceneitem_remove(obs_sceneitem_t *item)
|
|||
|
||||
assert(scene != NULL);
|
||||
assert(scene->source != NULL);
|
||||
if (item->visible)
|
||||
obs_source_remove_active_child(scene->source, item->source);
|
||||
|
||||
set_visibility(item, false);
|
||||
|
||||
signal_item_remove(item);
|
||||
detach_sceneitem(item);
|
||||
|
@ -1155,32 +1322,37 @@ void obs_sceneitem_get_box_transform(const obs_sceneitem_t *item,
|
|||
|
||||
bool obs_sceneitem_visible(const obs_sceneitem_t *item)
|
||||
{
|
||||
return item ? item->visible : false;
|
||||
return item ? item->user_visible : false;
|
||||
}
|
||||
|
||||
bool obs_sceneitem_set_visible(obs_sceneitem_t *item, bool visible)
|
||||
{
|
||||
struct calldata cd = {0};
|
||||
struct item_action action = {
|
||||
.visible = visible,
|
||||
.timestamp = os_gettime_ns()
|
||||
};
|
||||
|
||||
if (!item)
|
||||
return false;
|
||||
|
||||
if (item->visible == visible)
|
||||
if (item->user_visible == visible)
|
||||
return false;
|
||||
|
||||
if (!item->parent)
|
||||
return false;
|
||||
|
||||
if (visible) {
|
||||
if (!obs_source_add_active_child(item->parent->source,
|
||||
item->source))
|
||||
return false;
|
||||
} else {
|
||||
obs_source_remove_active_child(item->parent->source,
|
||||
item->source);
|
||||
if (os_atomic_inc_long(&item->active_refs) == 1) {
|
||||
if (!obs_source_add_active_child(item->parent->source,
|
||||
item->source)) {
|
||||
os_atomic_dec_long(&item->active_refs);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item->visible = visible;
|
||||
item->user_visible = visible;
|
||||
|
||||
calldata_set_ptr(&cd, "scene", item->parent);
|
||||
calldata_set_ptr(&cd, "item", item);
|
||||
|
@ -1188,8 +1360,15 @@ bool obs_sceneitem_set_visible(obs_sceneitem_t *item, bool visible)
|
|||
|
||||
signal_handler_signal(item->parent->source->context.signals,
|
||||
"item_visible", &cd);
|
||||
|
||||
calldata_free(&cd);
|
||||
|
||||
if (source_has_audio(item->source)) {
|
||||
pthread_mutex_lock(&item->actions_mutex);
|
||||
da_push_back(item->audio_actions, &action);
|
||||
pthread_mutex_unlock(&item->actions_mutex);
|
||||
} else {
|
||||
set_visibility(item, visible);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,12 +23,19 @@
|
|||
|
||||
/* how obs scene! */
|
||||
|
||||
struct item_action {
|
||||
bool visible;
|
||||
uint64_t timestamp;
|
||||
};
|
||||
|
||||
struct obs_scene_item {
|
||||
volatile long ref;
|
||||
volatile bool removed;
|
||||
|
||||
struct obs_scene *parent;
|
||||
struct obs_source *source;
|
||||
volatile long active_refs;
|
||||
bool user_visible;
|
||||
bool visible;
|
||||
bool selected;
|
||||
|
||||
|
@ -51,6 +58,9 @@ struct obs_scene_item {
|
|||
|
||||
obs_hotkey_pair_id toggle_visibility;
|
||||
|
||||
pthread_mutex_t actions_mutex;
|
||||
DARRAY(struct item_action) audio_actions;
|
||||
|
||||
/* would do **prev_next, but not really great for reordering */
|
||||
struct obs_scene_item *prev;
|
||||
struct obs_scene_item *next;
|
||||
|
|
Loading…
Reference in a new issue