diff --git a/src/modules/module-filter-apply.c b/src/modules/module-filter-apply.c index f6c6929c7..480d3614f 100644 --- a/src/modules/module-filter-apply.c +++ b/src/modules/module-filter-apply.c @@ -272,7 +272,10 @@ static bool find_paired_master(struct userdata *u, struct filter *filter, pa_obj } /* Make sure we're not routing to another instance of * the same filter. */ - filter->source_master = so->source->output_from_master->source; + if (so->source->vsource) + filter->source_master = so->source->vsource->output_from_master->source; + else + filter->source_master = so->source->output_from_master->source; } else { filter->source_master = so->source; } @@ -474,12 +477,18 @@ static void find_filters_for_module(struct userdata *u, pa_module *m, const char pa_assert(pa_source_is_filter(source)); if (!fltr) { - fltr = filter_new(name, parameters, NULL, source->output_from_master->source); + if (source->vsource) + fltr = filter_new(name, parameters, NULL, source->vsource->output_from_master->source); + else + fltr = filter_new(name, parameters, NULL, source->output_from_master->source); fltr->module_index = m->index; fltr->source = source; } else { fltr->source = source; - fltr->source_master = source->output_from_master->source; + if (source->vsource) + fltr->source_master = source->vsource->output_from_master->source; + else + fltr->source_master = source->output_from_master->source; } break; diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c index 5e84493e7..cfe322e82 100644 --- a/src/pulsecore/core.c +++ b/src/pulsecore/core.c @@ -529,6 +529,7 @@ void pa_core_update_default_sink(pa_core *core) { * a > b -> return 1 */ static int compare_sources(pa_source *a, pa_source *b, bool ignore_configured_virtual_default) { pa_core *core; + bool a_is_vsource, b_is_vsource; core = a->core; @@ -572,10 +573,25 @@ static int compare_sources(pa_source *a, pa_source *b, bool ignore_configured_vi if (a->priority > b->priority) return 1; - /* Let sources like pipe source or null source win against filter sources */ - if (a->output_from_master && !b->output_from_master) + /* Let sources like pipe source or null source win against filter sources + During consolidation, we have to detect the presence of the vsource or + output_to_master variable. When the virtual sources have been migrated, + this will simplify. */ + a_is_vsource = false; + if (a->vsource) + a_is_vsource = true; + else if (a->output_from_master) + a_is_vsource = true; + + b_is_vsource = false; + if (b->vsource) + b_is_vsource = true; + else if (b->output_from_master) + b_is_vsource = true; + + if (a_is_vsource && !b_is_vsource) return -1; - if (!a->output_from_master && b->output_from_master) + if (!a_is_vsource && b_is_vsource) return 1; /* If the sources are monitors, we can compare the monitored sinks. */ diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index 7e0a925fd..98f50fd80 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -1306,10 +1306,20 @@ bool pa_source_output_may_move(pa_source_output *o) { bool pa_source_output_is_filter_loop(pa_source_output *target, pa_source *s) { unsigned PA_UNUSED i = 0; - while (s && s->output_from_master) { - if (s->output_from_master == target) - return true; - s = s->output_from_master->source; + + /* During consolidation, we have to support s->output_from_master and + * s->vsource->output_from_master. The first will disappear after all + * virtual sources use the new code. */ + while (s && (s->output_from_master || (s->vsource && s->vsource->output_from_master))) { + if (s->vsource) { + if (s->vsource->output_from_master == target) + return true; + s = s->vsource->output_from_master->source; + } else { + if (s->output_from_master == target) + return true; + s = s->output_from_master->source; + } pa_assert(i++ < 100); } return false; @@ -1321,8 +1331,11 @@ static bool is_filter_source_moving(pa_source_output *o) { if (!source) return false; - while (source->output_from_master) { - source = source->output_from_master->source; + while (source->output_from_master || (source->vsource && source->vsource->output_from_master)) { + if (source->vsource) + source = source->vsource->output_from_master->source; + else + source = source->output_from_master->source; if (!source) return true; diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 226a717ca..cbffc9b93 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -1234,11 +1234,19 @@ bool pa_source_flat_volume_enabled(pa_source *s) { pa_source *pa_source_get_master(pa_source *s) { pa_source_assert_ref(s); + /* During consolidation, we have to support s->output_from_master and + * s->vsource->output_from_master. The first will disappear after all + * virtual sources use the new code. */ while (s && (s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) { - if (PA_UNLIKELY(!s->output_from_master)) + if (PA_UNLIKELY(s->vsource && !s->vsource->output_from_master)) return NULL; + if (PA_UNLIKELY(!s->vsource && !s->output_from_master)) + return NULL; - s = s->output_from_master->source; + if (s->output_from_master) + s = s->output_from_master->source; + else + s = s->vsource->output_from_master->source; } return s; @@ -1248,7 +1256,7 @@ pa_source *pa_source_get_master(pa_source *s) { bool pa_source_is_filter(pa_source *s) { pa_source_assert_ref(s); - return (s->output_from_master != NULL); + return ((s->output_from_master != NULL || s->vsource->output_from_master != NULL)); } /* Called from main context */ diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index aa71ee829..41dab0046 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -57,6 +57,78 @@ typedef void(*pa_source_cb_t)(pa_source *s); typedef int (*pa_source_get_mute_cb_t)(pa_source *s, bool *mute); +/* Virtual source structure */ +typedef struct pa_vsource { + pa_msgobject parent; /* Message object */ + pa_core *core; /* Pointer to core */ + pa_source *source; /* A pointer to the virtual source */ + pa_source_output *output_from_master; /* source output from the master source */ + pa_memblockq *memblockq; /* Memblockq of the virtual source, may be NULL */ + + bool auto_desc; /* Automatically adapt description on move */ + bool source_moving; /* Set when master source changes to preserve volume */ + const char *desc_head; /* Leading part of description string used for the + * source and source input when auto_desc is true */ + const char *source_type; /* Name for the type of source, used as suffix for + * the source name if the name is derived from the + * master source. */ + bool autoloaded; /* True if the source was not loaded manually */ + size_t max_chunk_size; /* Maximum chunk size in bytes that the filter will + * accept, set to pa_mempool_block_size_max() by default */ + size_t fixed_block_size; /* Block size in frames for fixed block size filters, + * 0 if block size is controlled by pulseaudio. */ + size_t fixed_input_block_size; /* Input block size in frames. If not 0, input data for + * process_chunk() will always have the same size. + * If not enough new data is available, the remaining + * samples will be filled with history. */ + size_t overlap_frames; /* Some filters require old input samples in addtion to + * the current data. The variable contains the number of + * previous frames that will be passed to process_chunk(). + * The actual number of history frames may be variable if + * the filter defines the get_current_overlap() function. + * In this case, overlap_frames contains the maximum + * number of history frames. */ + pa_usec_t max_latency; /* Maximum latency allowed for the source, 0 if unused */ + + /* Callback to process a chunk of data by the filter. Called from I/O thread + * context. May be NULL */ + void (*process_chunk)(uint8_t *src, uint8_t *dst, unsigned in_count, unsigned out_count, void *userdata); + + /* Callback to retrieve additional latency caused by the filter. Called from + * I/O thread context. May be NULL */ + pa_usec_t (*get_extra_latency)(pa_source *s); + + /* If defined, this function is called from the source-output push() callback + * to retrieve the current number of history frames to include in the next + * chunk. Called from I/O thread. */ + size_t (*get_current_overlap)(pa_source_output *o); + + /* If set and dest is valid, this function is called in the moving() callback + * to change the description of source and source-output. Called from main context. + * May be NULL */ + void (*set_description)(pa_source_output *o, pa_source *dest); + + /* If set, this function will be called after update_filter_parameters() to + * inform the filter of the block sizes that will be used. These may differ + * from the sizes set in update_filter_parameters() if the function tries to + * set an invalid combination of block sizes. Called from I/O thread. */ + void (*update_block_sizes)(size_t fixed_block_size, size_t fixed_input_block_size, size_t overlap_frames, void *userdata); + + /* If set, this function is called in I/O thread context when an update of the + * filter parameters is requested. May be NULL. The function must replace + * the currently used parameter structure by the new structure in parameters + * and return a pointer to the old structure so that it can be freed in the + * main thread using free_filter_parameters(). If the old structure can be + * re-used, the function may return NULL. update_filter_parameters() may + * also modify the block sizes. */ + void *(*update_filter_parameters)(void *parameters, void *userdata); + + /* Frees a parameter structure. May only be NULL, if update_filter_parameters() + * is also NULL or if update_filter_parameters() always returns NULL. Called + * from main thread. */ + void (*free_filter_parameters)(void *parameters); +} pa_vsource; + struct pa_source { pa_msgobject parent; @@ -90,6 +162,7 @@ struct pa_source { unsigned n_corked; pa_sink *monitor_of; /* may be NULL */ pa_source_output *output_from_master; /* non-NULL only for filter sources */ + pa_vsource *vsource; /* non-NULL only for filter sources */ pa_volume_t base_volume; /* shall be constant */ unsigned n_volume_steps; /* shall be constant */