diff --git a/src/modules/module-filter-apply.c b/src/modules/module-filter-apply.c index 1c1278218..bd4a9d176 100644 --- a/src/modules/module-filter-apply.c +++ b/src/modules/module-filter-apply.c @@ -293,7 +293,10 @@ static bool find_paired_master(struct userdata *u, struct filter *filter, pa_obj if (pa_streq(module_name, si->sink->module->name)) { /* Make sure we're not routing to another instance of * the same filter. */ - filter->sink_master = si->sink->input_to_master->sink; + if (si->sink->vsink) + filter->sink_master = si->sink->vsink->input_to_master->sink; + else + filter->sink_master = si->sink->input_to_master->sink; } else { filter->sink_master = si->sink; } @@ -461,7 +464,10 @@ static void find_filters_for_module(struct userdata *u, pa_module *m, const char if (sink->module == m) { pa_assert(pa_sink_is_filter(sink)); - fltr = filter_new(name, parameters, sink->input_to_master->sink, NULL); + if (sink->vsink) + fltr = filter_new(name, parameters, sink->vsink->input_to_master->sink, NULL); + else + fltr = filter_new(name, parameters, sink->input_to_master->sink, NULL); fltr->module_index = m->index; fltr->sink = sink; diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 4380087ca..c780d7b37 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -1784,10 +1784,20 @@ bool pa_sink_input_may_move(pa_sink_input *i) { static bool find_filter_sink_input(pa_sink_input *target, pa_sink *s) { unsigned PA_UNUSED i = 0; - while (s && s->input_to_master) { - if (s->input_to_master == target) - return true; - s = s->input_to_master->sink; + + /* During consolidation, we have to support s->input_to_master and + * s->vsink->input_to_master. The first will disappear after all + * virtual sinks use the new code. */ + while (s && (s->input_to_master || (s->vsink && s->vsink->input_to_master))) { + if (s->vsink) { + if (s->vsink->input_to_master == target) + return true; + s = s->vsink->input_to_master->sink; + } else { + if (s->input_to_master == target) + return true; + s = s->input_to_master->sink; + } pa_assert(i++ < 100); } return false; @@ -1799,8 +1809,11 @@ static bool is_filter_sink_moving(pa_sink_input *i) { if (!sink) return false; - while (sink->input_to_master) { - sink = sink->input_to_master->sink; + while (sink->input_to_master || (sink->vsink && sink->vsink->input_to_master)) { + if (sink->vsink) + sink = sink->vsink->input_to_master->sink; + else + sink = sink->input_to_master->sink; if (!sink) return true; diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 0f0dc56fc..e87a16a04 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -287,6 +287,7 @@ pa_sink* pa_sink_new( s->inputs = pa_idxset_new(NULL, NULL); s->n_corked = 0; s->input_to_master = NULL; + s->vsink = NULL; s->reference_volume = s->real_volume = data->volume; pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels); @@ -1692,11 +1693,19 @@ bool pa_sink_has_filter_attached(pa_sink *s) { pa_sink *pa_sink_get_master(pa_sink *s) { pa_sink_assert_ref(s); + /* During consolidation, we have to support s->input_to_master and + * s->vsink->input_to_master. The first will disappear after all + * virtual sinks use the new code. */ while (s && (s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) { - if (PA_UNLIKELY(!s->input_to_master)) + if (PA_UNLIKELY(s->vsink && !s->vsink->input_to_master)) + return NULL; + if (PA_UNLIKELY(!s->vsink && !s->input_to_master)) return NULL; - s = s->input_to_master->sink; + if (s->input_to_master) + s = s->input_to_master->sink; + else + s = s->vsink->input_to_master->sink; } return s; @@ -1706,7 +1715,7 @@ pa_sink *pa_sink_get_master(pa_sink *s) { bool pa_sink_is_filter(pa_sink *s) { pa_sink_assert_ref(s); - return (s->input_to_master != NULL); + return ((s->vsink != NULL) || (s->input_to_master != NULL)); } /* Called from main context */ diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index 383edacb5..f4db8158d 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -56,6 +56,90 @@ typedef void(*pa_sink_cb_t)(pa_sink *s); typedef int (*pa_sink_get_mute_cb_t)(pa_sink *s, bool *mute); +/* Virtual sink structure */ +typedef struct pa_vsink { + pa_msgobject parent; /* Message object */ + pa_sink *sink; /* A pointer to the virtual sink */ + pa_sink_input *input_to_master; /* Sink input to the master sink */ + pa_memblockq *memblockq; /* Memblockq of the virtual sink, may be NULL */ + size_t drop_bytes; /* Number of bytes to drop during sink_input_pop() + * in sink input sample speci. Used during rewind + * of fixed block size filters */ + + bool auto_desc; /* Automatically adapt description on move */ + const char *desc_head; /* Leading part of description string used for the + * sink and sink input when auto_desc is true */ + const char *sink_type; /* Name for the type of sink, used as suffix for + * the sink name if the name is derived from the + * master sink. */ + bool autoloaded; /* True if the sink 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. */ + size_t max_request_frames_min; /* Minimum value for max_request in frames, 0 if unused */ + pa_usec_t max_latency; /* Maximum latency allowed for the sink, 0 if unused */ + int max_rewind; /* Maximum number of frames that the sink can rewind. + * 0 means unlimited, -1 disables rewinding */ + + /* Callback to rewind the filter when pulseaudio requests it. Called from + * I/O thread context. May be NULL */ + void (*rewind_filter)(pa_sink *s, size_t amount); + + /* 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 communicate the max_rewind value to the filter. Called from + * I/O thread context whenever the max_rewind value changes. May be NULL */ + void (*set_filter_max_rewind)(pa_sink_input *i, size_t amount); + + /* 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_sink *s); + + /* If defined, this function is called from the sink-input pop() 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_sink_input *i); + + /* If set and dest is valid, this function is called in the moving() callback + * to change the description of sink and sink_input. Called from main context. + * May be NULL */ + void (*set_description)(pa_sink_input *i, pa_sink *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_vsink; + struct pa_sink { pa_msgobject parent; @@ -88,6 +172,7 @@ struct pa_sink { pa_idxset *inputs; unsigned n_corked; pa_source *monitor_source; + pa_vsink *vsink; /* non-NULL only for filter sinks */ pa_sink_input *input_to_master; /* non-NULL only for filter sinks */ pa_volume_t base_volume; /* shall be constant */