提交 c431ac6a 编写于 作者: J jp9000

libobs: Refactor source volume transition design

This changes the way source volume handles transitioning between being
active and inactive states.

The previous way that transitioning handled volume was that it set the
presentation volume of the source and all of its sub-sources to 0.0 if
the source was inactive, and 1.0 if active.  Transition sources would
then also set the presentation volume for sub-sources to whatever their
transitioning volume was.  However, the problem with this is that the
design didn't take in to account if the source or its sub-sources were
active anywhere else, so because of that it would break if that ever
happened, and I didn't realize that when I was designing it.

So instead, this completely overhauls the design of handling
transitioning volume.  Each frame, it'll go through all sources and
check whether they're active or inactive and set the base volume
accordingly.  If transitions are currently active, it will actually walk
the active source tree and check whether the source is in a
transitioning state somewhere.

 - If the source is a sub-source of a transition, and it's not active
   outside of the transition, then the transition will control the
   volume of the source.

 - If the source is a sub-source of a transition, but it's also active
   outside of the transition, it'll defer to whichever is louder.

This also adds a new callback to the obs_source_info structure for
transition sources, get_transition_volume, which is called to get the
transitioning volume of a sub-source.
上级 10f89886
......@@ -311,17 +311,11 @@ struct obs_source {
pthread_mutex_t audio_mutex;
struct obs_audio_data audio_data;
size_t audio_storage_size;
float base_volume;
float user_volume;
float present_volume;
int64_t sync_offset;
/* transition volume is meant to store the sum of transitioning volumes
* of a source, i.e. if a source is within both the "to" and "from"
* targets of a transition, it would add both volumes to this variable,
* and then when the transition frame is complete, is applies the value
* to the presentation volume. */
float transition_volume;
/* async video data */
gs_texture_t *async_texture;
gs_texrender_t *async_convert_texrender;
......@@ -367,6 +361,8 @@ enum view_type {
extern void obs_source_activate(obs_source_t *source, enum view_type type);
extern void obs_source_deactivate(obs_source_t *source, enum view_type type);
extern void obs_source_video_tick(obs_source_t *source, float seconds);
extern float obs_source_get_target_volume(obs_source_t *source,
obs_source_t *target);
/* ------------------------------------------------------------------------- */
......
......@@ -110,7 +110,8 @@ bool obs_source_init(struct obs_source *source,
{
source->refs = 1;
source->user_volume = 1.0f;
source->present_volume = 0.0f;
source->present_volume = 1.0f;
source->base_volume = 0.0f;
source->sync_offset = 0;
pthread_mutex_init_value(&source->filter_mutex);
pthread_mutex_init_value(&source->video_mutex);
......@@ -561,7 +562,6 @@ void obs_source_activate(obs_source_t *source, enum view_type type)
if (os_atomic_inc_long(&source->activate_refs) == 1) {
activate_source(source);
obs_source_enum_tree(source, activate_tree, NULL);
obs_source_set_present_volume(source, 1.0f);
}
}
}
......@@ -579,7 +579,6 @@ void obs_source_deactivate(obs_source_t *source, enum view_type type)
if (os_atomic_dec_long(&source->activate_refs) == 0) {
deactivate_source(source);
obs_source_enum_tree(source, deactivate_tree, NULL);
obs_source_set_present_volume(source, 0.0f);
}
}
}
......@@ -684,8 +683,9 @@ static void source_output_audio_line(obs_source_t *source,
conv_frames_to_time(in.frames);
in.timestamp += source->timing_adjust + source->sync_offset;
in.volume = source->user_volume * source->present_volume *
obs->audio.user_volume * obs->audio.present_volume;
in.volume = source->base_volume * source->user_volume *
source->present_volume * obs->audio.user_volume *
obs->audio.present_volume;
audio_line_output(source->audio_line, &in);
source_signal_audio_data(source, &in);
......@@ -1993,53 +1993,6 @@ void obs_source_remove_child(obs_source_t *parent, obs_source_t *child)
}
}
static void reset_transition_vol(obs_source_t *parent, obs_source_t *child,
void *param)
{
child->transition_volume = 0.0f;
UNUSED_PARAMETER(parent);
UNUSED_PARAMETER(param);
}
static void add_transition_vol(obs_source_t *parent, obs_source_t *child,
void *param)
{
float *vol = param;
child->transition_volume += *vol;
UNUSED_PARAMETER(parent);
}
static void apply_transition_vol(obs_source_t *parent, obs_source_t *child,
void *param)
{
child->present_volume = child->transition_volume;
UNUSED_PARAMETER(parent);
UNUSED_PARAMETER(param);
}
void obs_transition_begin_frame(obs_source_t *transition)
{
if (!transition) return;
obs_source_enum_tree(transition, reset_transition_vol, NULL);
}
void obs_source_set_transition_vol(obs_source_t *source, float vol)
{
if (!source) return;
add_transition_vol(NULL, source, &vol);
obs_source_enum_tree(source, add_transition_vol, &vol);
}
void obs_transition_end_frame(obs_source_t *transition)
{
if (!transition) return;
obs_source_enum_tree(transition, apply_transition_vol, NULL);
}
void obs_source_save(obs_source_t *source)
{
if (!source_valid(source) || !source->info.save) return;
......@@ -2155,3 +2108,67 @@ void obs_source_draw(gs_texture_t *texture, int x, int y, uint32_t cx,
if (change_pos)
gs_matrix_pop();
}
static inline float get_transition_volume(obs_source_t *source,
obs_source_t *child)
{
if (source && child && source->info.get_transition_volume)
return source->info.get_transition_volume(source->context.data,
child);
return 0.0f;
}
static float obs_source_get_target_volume_refs(obs_source_t *source,
obs_source_t *target, int refs);
struct base_vol_enum_info {
obs_source_t *target;
float vol;
};
static void get_transition_child_vol(obs_source_t *parent, obs_source_t *child,
void *param)
{
struct base_vol_enum_info *info = param;
float vol = obs_source_get_target_volume(child, info->target);
info->vol += vol * get_transition_volume(parent, child);
}
static void get_source_base_vol(obs_source_t *parent, obs_source_t *child,
void *param)
{
struct base_vol_enum_info *info = param;
float vol = obs_source_get_target_volume(child, info->target);
if (vol > info->vol)
info->vol = vol;
UNUSED_PARAMETER(parent);
}
/*
* This traverses a source tree for any references to a particular source.
* If the source is found, it'll just return 1.0. However, if the source
* exists within some transition somewhere, the transition source will be able
* to control what the volume of the source will be. If the source is also
* active outside the transition, then it'll just use 1.0.
*/
float obs_source_get_target_volume(obs_source_t *source, obs_source_t *target)
{
struct base_vol_enum_info info = {target, 0.0f};
bool transition = source->info.type == OBS_SOURCE_TYPE_TRANSITION;
if (source == target)
return 1.0f;
if (source->info.enum_sources) {
source->info.enum_sources(source->context.data,
transition ?
get_transition_child_vol :
get_source_base_vol,
&info);
}
return info.vol;
}
......@@ -352,6 +352,15 @@ struct obs_source_info {
*/
void (*key_click)(void *data, const struct obs_key_event *event,
bool key_up);
/**
* Called to transition sources get the volume of a transitioning
* sub-source.
*
* @param data Source data
* @param source Transitioning sub-source to get the volume of
*/
float (*get_transition_volume)(void *data, obs_source_t *source);
};
EXPORT void obs_register_source_s(const struct obs_source_info *info,
......
......@@ -20,9 +20,39 @@
#include "graphics/vec4.h"
#include "media-io/format-conversion.h"
static inline void calculate_base_volume(struct obs_core_data *data,
struct obs_view *view, obs_source_t *target)
{
if (!target->activate_refs) {
target->base_volume = 0.0f;
/* only walk the tree if there are transitions active */
} else if (data->active_transitions) {
float best_vol = 0.0f;
for (size_t i = 0; i < MAX_CHANNELS; i++) {
struct obs_source *source = view->channels[i];
float vol = 0.0f;
if (!source)
continue;
vol = obs_source_get_target_volume(source, target);
if (best_vol < vol)
best_vol = vol;
}
target->base_volume = best_vol;
} else {
target->base_volume = 1.0f;
}
}
static uint64_t tick_sources(uint64_t cur_time, uint64_t last_time)
{
struct obs_core_data *data = &obs->data;
struct obs_view *view = &data->main_view;
struct obs_source *source;
uint64_t delta_time;
float seconds;
......@@ -36,6 +66,7 @@ static uint64_t tick_sources(uint64_t cur_time, uint64_t last_time)
pthread_mutex_lock(&data->sources_mutex);
/* call the tick function of each source */
source = data->first_source;
while (source) {
if (source->refs)
......@@ -43,6 +74,18 @@ static uint64_t tick_sources(uint64_t cur_time, uint64_t last_time)
source = (struct obs_source*)source->context.next;
}
/* calculate source volumes */
pthread_mutex_lock(&view->channels_mutex);
source = data->first_source;
while (source) {
if (source->refs)
calculate_base_volume(data, view, source);
source = (struct obs_source*)source->context.next;
}
pthread_mutex_unlock(&view->channels_mutex);
pthread_mutex_unlock(&data->sources_mutex);
return cur_time;
......
......@@ -855,18 +855,6 @@ EXPORT void obs_source_send_focus(obs_source_t *source, bool focus);
EXPORT void obs_source_send_key_click(obs_source_t *source,
const struct obs_key_event *event, bool key_up);
/** Begins transition frame. Sets all transitioning volume values to 0.0f. */
EXPORT void obs_transition_begin_frame(obs_source_t *transition);
/**
* Adds a transitioning volume value to a source that's being transitioned.
* This value is applied to all the sources within the the source.
*/
EXPORT void obs_source_set_transition_vol(obs_source_t *source, float vol);
/** Ends transition frame and applies new presentation volumes to all sources */
EXPORT void obs_transition_end_frame(obs_source_t *transition);
/* ------------------------------------------------------------------------- */
/* Scenes */
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册