diff options
Diffstat (limited to 'src/pulsecore')
-rw-r--r-- | src/pulsecore/atomic.h | 2 | ||||
-rw-r--r-- | src/pulsecore/core-util.c | 50 | ||||
-rw-r--r-- | src/pulsecore/core-util.h | 3 | ||||
-rw-r--r-- | src/pulsecore/core.c | 77 | ||||
-rw-r--r-- | src/pulsecore/core.h | 8 | ||||
-rw-r--r-- | src/pulsecore/device-port.c | 50 | ||||
-rw-r--r-- | src/pulsecore/device-port.h | 4 | ||||
-rw-r--r-- | src/pulsecore/macro.h | 38 | ||||
-rw-r--r-- | src/pulsecore/meson.build | 2 | ||||
-rw-r--r-- | src/pulsecore/resampler.c | 4 | ||||
-rw-r--r-- | src/pulsecore/resampler.h | 3 | ||||
-rw-r--r-- | src/pulsecore/rtpoll.c | 63 | ||||
-rw-r--r-- | src/pulsecore/rtpoll.h | 9 | ||||
-rw-r--r-- | src/pulsecore/sink-input.c | 57 | ||||
-rw-r--r-- | src/pulsecore/sink-input.h | 19 | ||||
-rw-r--r-- | src/pulsecore/sink.c | 53 | ||||
-rw-r--r-- | src/pulsecore/sink.h | 6 | ||||
-rw-r--r-- | src/pulsecore/source-output.c | 57 | ||||
-rw-r--r-- | src/pulsecore/source-output.h | 19 | ||||
-rw-r--r-- | src/pulsecore/source.c | 52 | ||||
-rw-r--r-- | src/pulsecore/source.h | 6 |
21 files changed, 468 insertions, 114 deletions
diff --git a/src/pulsecore/atomic.h b/src/pulsecore/atomic.h index a82ca83..e5c1401 100644 --- a/src/pulsecore/atomic.h +++ b/src/pulsecore/atomic.h @@ -117,7 +117,7 @@ static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) { } static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void* p) { - __atomic_store_n(&a->value, p, __ATOMIC_SEQ_CST); + __atomic_store_n(&a->value, (unsigned long) p, __ATOMIC_SEQ_CST); } #else diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index 270acd8..601b1d1 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -784,12 +784,16 @@ void pa_reset_priority(void) { #endif } +/* Check whenever any substring in v matches the provided regex. */ int pa_match(const char *expr, const char *v) { #if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) int k; regex_t re; int r; + pa_assert(expr); + pa_assert(v); + if (regcomp(&re, expr, REG_NOSUB|REG_EXTENDED) != 0) { errno = EINVAL; return -1; @@ -814,6 +818,22 @@ int pa_match(const char *expr, const char *v) { #endif } +/* Check whenever the provided regex pattern is valid. */ +bool pa_is_regex_valid(const char *expr) { +#if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) + regex_t re; + + if (expr == NULL || regcomp(&re, expr, REG_NOSUB|REG_EXTENDED) != 0) { + return false; + } + + regfree(&re); + return true; +#else + return false; +#endif +} + /* Try to parse a boolean string value.*/ int pa_parse_boolean(const char *v) { pa_assert(v); @@ -859,7 +879,7 @@ int pa_parse_volume(const char *v, pa_volume_t *volume) { len = strlen(v); - if (len >= 64) + if (len <= 0 || len >= 64) return -1; memcpy(str, v, len + 1); @@ -1428,7 +1448,7 @@ static int check_ours(const char *p) { return -errno; #ifdef HAVE_GETUID - if (st.st_uid != getuid()) + if (st.st_uid != getuid() && st.st_uid != 0) return -EACCES; #endif @@ -2873,6 +2893,32 @@ bool pa_str_in_list_spaces(const char *haystack, const char *needle) { return false; } +char* pa_str_strip_suffix(const char *str, const char *suffix) { + size_t str_l, suf_l, prefix; + char *ret; + + pa_assert(str); + pa_assert(suffix); + + str_l = strlen(str); + suf_l = strlen(suffix); + + if (str_l < suf_l) + return NULL; + + prefix = str_l - suf_l; + + if (!pa_streq(&str[prefix], suffix)) + return NULL; + + ret = pa_xmalloc(prefix + 1); + + strncpy(ret, str, prefix); + ret[prefix] = '\0'; + + return ret; +} + char *pa_get_user_name_malloc(void) { ssize_t k; char *u; diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h index cfb9eb2..9440af9 100644 --- a/src/pulsecore/core-util.h +++ b/src/pulsecore/core-util.h @@ -158,6 +158,7 @@ size_t pa_vsnprintf(char *str, size_t size, const char *format, va_list ap); char *pa_truncate_utf8(char *c, size_t l); int pa_match(const char *expr, const char *v); +bool pa_is_regex_valid(const char *expr); char *pa_getcwd(void); char *pa_make_path_absolute(const char *p); @@ -231,6 +232,8 @@ static inline bool pa_safe_streq(const char *a, const char *b) { bool pa_str_in_list_spaces(const char *needle, const char *haystack); bool pa_str_in_list(const char *haystack, const char *delimiters, const char *needle); +char* pa_str_strip_suffix(const char *str, const char *suffix); + char *pa_get_host_name_malloc(void); char *pa_get_user_name_malloc(void); diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c index e5bb2e4..335f802 100644 --- a/src/pulsecore/core.c +++ b/src/pulsecore/core.c @@ -144,7 +144,8 @@ pa_core* pa_core_new(pa_mainloop_api *m, bool shared, bool enable_memfd, size_t c->realtime_priority = 5; c->disable_remixing = false; c->remixing_use_all_sink_channels = true; - c->disable_lfe_remixing = true; + c->remixing_produce_lfe = false; + c->remixing_consume_lfe = false; c->lfe_crossover_freq = 0; c->deferred_volume = true; c->resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 1; @@ -349,6 +350,10 @@ void pa_core_update_default_sink(pa_core *core) { pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SERVER | PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX); pa_hook_fire(&core->hooks[PA_CORE_HOOK_DEFAULT_SINK_CHANGED], core->default_sink); + + /* try to move the streams from old_default_sink to the new default_sink conditionally */ + if (old_default_sink) + pa_sink_move_streams_to_default_sink(core, old_default_sink, true); } /* a < b -> return -1 @@ -430,6 +435,10 @@ void pa_core_update_default_source(pa_core *core) { old_default_source ? old_default_source->name : "(unset)", best ? best->name : "(unset)"); pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SERVER | PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX); pa_hook_fire(&core->hooks[PA_CORE_HOOK_DEFAULT_SOURCE_CHANGED], core->default_source); + + /* try to move the streams from old_default_source to the new default_source conditionally */ + if (old_default_source) + pa_source_move_streams_to_default_source(core, old_default_source, true); } void pa_core_set_exit_idle_time(pa_core *core, int time) { @@ -519,6 +528,72 @@ void pa_core_rttime_restart(pa_core *c, pa_time_event *e, pa_usec_t usec) { c->mainloop->time_restart(e, pa_timeval_rtstore(&tv, usec, true)); } +void pa_core_move_streams_to_newly_available_preferred_sink(pa_core *c, pa_sink *s) { + pa_sink_input *si; + uint32_t idx; + + pa_assert(c); + pa_assert(s); + + PA_IDXSET_FOREACH(si, c->sink_inputs, idx) { + if (si->sink == s) + continue; + + if (!si->sink) + continue; + + /* Skip this sink input if it is connecting a filter sink to + * the master */ + if (si->origin_sink) + continue; + + /* It might happen that a stream and a sink are set up at the + same time, in which case we want to make sure we don't + interfere with that */ + if (!PA_SINK_INPUT_IS_LINKED(si->state)) + continue; + + if (pa_safe_streq(si->preferred_sink, s->name)) + pa_sink_input_move_to(si, s, false); + } + +} + +void pa_core_move_streams_to_newly_available_preferred_source(pa_core *c, pa_source *s) { + pa_source_output *so; + uint32_t idx; + + pa_assert(c); + pa_assert(s); + + PA_IDXSET_FOREACH(so, c->source_outputs, idx) { + if (so->source == s) + continue; + + if (so->direct_on_input) + continue; + + if (!so->source) + continue; + + /* Skip this source output if it is connecting a filter source to + * the master */ + if (so->destination_source) + continue; + + /* It might happen that a stream and a source are set up at the + same time, in which case we want to make sure we don't + interfere with that */ + if (!PA_SOURCE_OUTPUT_IS_LINKED(so->state)) + continue; + + if (pa_safe_streq(so->preferred_source, s->name)) + pa_source_output_move_to(so, s, false); + } + +} + + /* Helper macro to reduce repetition in pa_suspend_cause_to_string(). * Parameters: * char *p: the current position in the write buffer diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index d03897c..57924b9 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -213,6 +213,7 @@ struct pa_core { int exit_idle_time, scache_idle_time; bool flat_volumes:1; + bool rescue_streams:1; bool disallow_module_loading:1; bool disallow_exit:1; bool running_as_daemon:1; @@ -220,7 +221,8 @@ struct pa_core { bool avoid_resampling:1; bool disable_remixing:1; bool remixing_use_all_sink_channels:1; - bool disable_lfe_remixing:1; + bool remixing_produce_lfe:1; + bool remixing_consume_lfe:1; bool deferred_volume:1; pa_resample_method_t resample_method; @@ -278,4 +280,8 @@ static const size_t PA_SUSPEND_CAUSE_TO_STRING_BUF_SIZE = * provided buffer. The same buffer is the return value of this function. */ const char *pa_suspend_cause_to_string(pa_suspend_cause_t cause, char buf[PA_SUSPEND_CAUSE_TO_STRING_BUF_SIZE]); +void pa_core_move_streams_to_newly_available_preferred_sink(pa_core *c, pa_sink *s); + +void pa_core_move_streams_to_newly_available_preferred_source(pa_core *c, pa_source *s); + #endif diff --git a/src/pulsecore/device-port.c b/src/pulsecore/device-port.c index fb12772..b104c49 100644 --- a/src/pulsecore/device-port.c +++ b/src/pulsecore/device-port.c @@ -100,6 +100,30 @@ void pa_device_port_set_available(pa_device_port *p, pa_available_t status) { else pa_core_update_default_source(p->core); + if (p->direction == PA_DIRECTION_OUTPUT) { + pa_sink *sink; + + sink = pa_device_port_get_sink(p); + if (sink && p == sink->active_port) { + if (sink->active_port->available == PA_AVAILABLE_NO) { + if (p->core->rescue_streams) + pa_sink_move_streams_to_default_sink(p->core, sink, false); + } else + pa_core_move_streams_to_newly_available_preferred_sink(p->core, sink); + } + } else { + pa_source *source; + + source = pa_device_port_get_source(p); + if (source && p == source->active_port) { + if (source->active_port->available == PA_AVAILABLE_NO) { + if (p->core->rescue_streams) + pa_source_move_streams_to_default_source(p->core, source, false); + } else + pa_core_move_streams_to_newly_available_preferred_source(p->core, source); + } + } + pa_subscription_post(p->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, p->card->index); pa_hook_fire(&p->core->hooks[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED], p); } @@ -224,3 +248,29 @@ pa_device_port *pa_device_port_find_best(pa_hashmap *ports) return best; } + +pa_sink *pa_device_port_get_sink(pa_device_port *p) { + pa_sink *rs = NULL; + pa_sink *sink; + uint32_t state; + + PA_IDXSET_FOREACH(sink, p->card->sinks, state) + if (p == pa_hashmap_get(sink->ports, p->name)) { + rs = sink; + break; + } + return rs; +} + +pa_source *pa_device_port_get_source(pa_device_port *p) { + pa_source *rs = NULL; + pa_source *source; + uint32_t state; + + PA_IDXSET_FOREACH(source, p->card->sources, state) + if (p == pa_hashmap_get(source->ports, p->name)) { + rs = source; + break; + } + return rs; +} diff --git a/src/pulsecore/device-port.h b/src/pulsecore/device-port.h index fbdce1a..41198f6 100644 --- a/src/pulsecore/device-port.h +++ b/src/pulsecore/device-port.h @@ -87,4 +87,8 @@ void pa_device_port_set_preferred_profile(pa_device_port *p, const char *new_pp) pa_device_port *pa_device_port_find_best(pa_hashmap *ports); +pa_sink *pa_device_port_get_sink(pa_device_port *p); + +pa_source *pa_device_port_get_source(pa_device_port *p); + #endif diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h index bb15b7f..ec8d31c 100644 --- a/src/pulsecore/macro.h +++ b/src/pulsecore/macro.h @@ -34,16 +34,6 @@ #error "Please include config.h before including this file!" #endif -#ifndef PA_LIKELY -#ifdef __GNUC__ -#define PA_LIKELY(x) (__builtin_expect(!!(x),1)) -#define PA_UNLIKELY(x) (__builtin_expect(!!(x),0)) -#else -#define PA_LIKELY(x) (x) -#define PA_UNLIKELY(x) (x) -#endif -#endif - /* Rounds down */ static inline void* PA_ALIGN_PTR(const void *p) { return (void*) (((size_t) p) & ~(sizeof(void*) - 1)); @@ -100,34 +90,6 @@ static inline size_t PA_ALIGN(size_t l) { #endif #ifdef __GNUC__ -#define PA_CLAMP(x, low, high) \ - __extension__ ({ \ - typeof(x) _x = (x); \ - typeof(low) _low = (low); \ - typeof(high) _high = (high); \ - ((_x > _high) ? _high : ((_x < _low) ? _low : _x)); \ - }) -#else -#define PA_CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) -#endif - -#ifdef __GNUC__ -#define PA_CLAMP_UNLIKELY(x, low, high) \ - __extension__ ({ \ - typeof(x) _x = (x); \ - typeof(low) _low = (low); \ - typeof(high) _high = (high); \ - (PA_UNLIKELY(_x > _high) ? _high : (PA_UNLIKELY(_x < _low) ? _low : _x)); \ - }) -#else -#define PA_CLAMP_UNLIKELY(x, low, high) (PA_UNLIKELY((x) > (high)) ? (high) : (PA_UNLIKELY((x) < (low)) ? (low) : (x))) -#endif - -/* We don't define a PA_CLAMP_LIKELY here, because it doesn't really - * make sense: we cannot know if it is more likely that the values is - * lower or greater than the boundaries.*/ - -#ifdef __GNUC__ #define PA_ROUND_UP(a, b) \ __extension__ ({ \ typeof(a) _a = (a); \ diff --git a/src/pulsecore/meson.build b/src/pulsecore/meson.build index 19f6b9e..0bd596c 100644 --- a/src/pulsecore/meson.build +++ b/src/pulsecore/meson.build @@ -198,7 +198,7 @@ libpulsecore = shared_library('pulsecore-' + pa_version_major_minor, install_rpath : privlibdir, install_dir : privlibdir, link_with : libpulsecore_simd_lib, - dependencies : [libm_dep, libpulsecommon_dep, libpulse_dep, ltdl_dep, shm_dep, sndfile_dep, database_dep, dbus_dep, libatomic_ops_dep, orc_dep, samplerate_dep, soxr_dep, speex_dep, x11_dep], + dependencies : [libm_dep, libpulsecommon_dep, libpulse_dep, ltdl_dep, shm_dep, sndfile_dep, database_dep, dbus_dep, libatomic_ops_dep, orc_dep, samplerate_dep, soxr_dep, speex_dep, x11_dep, libintl_dep], implicit_include_directories : false) libpulsecore_dep = declare_dependency(link_with: libpulsecore) diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c index ff9795e..58463f1 100644 --- a/src/pulsecore/resampler.c +++ b/src/pulsecore/resampler.c @@ -1099,7 +1099,7 @@ static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_remixed) * right input channel. Something is really wrong in this * case anyway. */ - } else if (on_lfe(b) && !(r->flags & PA_RESAMPLER_NO_LFE)) { + } else if (on_lfe(b) && (r->flags & PA_RESAMPLER_PRODUCE_LFE)) { /* We are not connected and an LFE. Let's average all * channels for LFE. */ @@ -1150,7 +1150,7 @@ static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_remixed) m->map_table_f[oc][ic] = (1.f/9.f) / (float) ic_unconnected_center; ic_unconnected_center_mixed_in = true; - } else if (on_lfe(a) && !(r->flags & PA_RESAMPLER_NO_LFE)) + } else if (on_lfe(a) && (r->flags & PA_RESAMPLER_CONSUME_LFE)) m->map_table_f[oc][ic] = .375f / (float) ic_unconnected_lfe; } } diff --git a/src/pulsecore/resampler.h b/src/pulsecore/resampler.h index 5d3171f..5a264b3 100644 --- a/src/pulsecore/resampler.h +++ b/src/pulsecore/resampler.h @@ -68,8 +68,9 @@ typedef enum pa_resample_flags { PA_RESAMPLER_VARIABLE_RATE = 0x0001U, PA_RESAMPLER_NO_REMAP = 0x0002U, /* implies NO_REMIX */ PA_RESAMPLER_NO_REMIX = 0x0004U, - PA_RESAMPLER_NO_LFE = 0x0008U, PA_RESAMPLER_NO_FILL_SINK = 0x0010U, + PA_RESAMPLER_PRODUCE_LFE = 0x0020U, + PA_RESAMPLER_CONSUME_LFE = 0x0040U, } pa_resample_flags_t; struct pa_resampler { diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c index 98cf88f..1085bf9 100644 --- a/src/pulsecore/rtpoll.c +++ b/src/pulsecore/rtpoll.c @@ -77,7 +77,9 @@ struct pa_rtpoll_item { int (*work_cb)(pa_rtpoll_item *i); int (*before_cb)(pa_rtpoll_item *i); void (*after_cb)(pa_rtpoll_item *i); - void *userdata; + void *work_userdata; + void *before_userdata; + void *after_userdata; PA_LLIST_FIELDS(pa_rtpoll_item); }; @@ -411,7 +413,9 @@ pa_rtpoll_item *pa_rtpoll_item_new(pa_rtpoll *p, pa_rtpoll_priority_t prio, unsi i->pollfd = NULL; i->priority = prio; - i->userdata = NULL; + i->work_userdata = NULL; + i->before_userdata = NULL; + i->work_userdata = NULL; i->before_cb = NULL; i->after_cb = NULL; i->work_cb = NULL; @@ -458,42 +462,39 @@ struct pollfd *pa_rtpoll_item_get_pollfd(pa_rtpoll_item *i, unsigned *n_fds) { return i->pollfd; } -void pa_rtpoll_item_set_before_callback(pa_rtpoll_item *i, int (*before_cb)(pa_rtpoll_item *i)) { +void pa_rtpoll_item_set_before_callback(pa_rtpoll_item *i, int (*before_cb)(pa_rtpoll_item *i), void *userdata) { pa_assert(i); pa_assert(i->priority < PA_RTPOLL_NEVER); i->before_cb = before_cb; + i->before_userdata = userdata; } -void pa_rtpoll_item_set_after_callback(pa_rtpoll_item *i, void (*after_cb)(pa_rtpoll_item *i)) { +void pa_rtpoll_item_set_after_callback(pa_rtpoll_item *i, void (*after_cb)(pa_rtpoll_item *i), void *userdata) { pa_assert(i); pa_assert(i->priority < PA_RTPOLL_NEVER); i->after_cb = after_cb; + i->after_userdata = userdata; } -void pa_rtpoll_item_set_work_callback(pa_rtpoll_item *i, int (*work_cb)(pa_rtpoll_item *i)) { +void pa_rtpoll_item_set_work_callback(pa_rtpoll_item *i, int (*work_cb)(pa_rtpoll_item *i), void *userdata) { pa_assert(i); pa_assert(i->priority < PA_RTPOLL_NEVER); i->work_cb = work_cb; + i->work_userdata = userdata; } -void pa_rtpoll_item_set_userdata(pa_rtpoll_item *i, void *userdata) { +void* pa_rtpoll_item_get_work_userdata(pa_rtpoll_item *i) { pa_assert(i); - i->userdata = userdata; -} - -void* pa_rtpoll_item_get_userdata(pa_rtpoll_item *i) { - pa_assert(i); - - return i->userdata; + return i->work_userdata; } static int fdsem_before(pa_rtpoll_item *i) { - if (pa_fdsem_before_poll(i->userdata) < 0) + if (pa_fdsem_before_poll(i->before_userdata) < 0) return 1; /* 1 means immediate restart of the loop */ return 0; @@ -503,7 +504,7 @@ static void fdsem_after(pa_rtpoll_item *i) { pa_assert(i); pa_assert((i->pollfd[0].revents & ~POLLIN) == 0); - pa_fdsem_after_poll(i->userdata); + pa_fdsem_after_poll(i->after_userdata); } pa_rtpoll_item *pa_rtpoll_item_new_fdsem(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_fdsem *f) { @@ -520,9 +521,8 @@ pa_rtpoll_item *pa_rtpoll_item_new_fdsem(pa_rtpoll *p, pa_rtpoll_priority_t prio pollfd->fd = pa_fdsem_get(f); pollfd->events = POLLIN; - i->before_cb = fdsem_before; - i->after_cb = fdsem_after; - i->userdata = f; + pa_rtpoll_item_set_before_callback(i, fdsem_before, f); + pa_rtpoll_item_set_after_callback(i, fdsem_after, f); return i; } @@ -530,7 +530,7 @@ pa_rtpoll_item *pa_rtpoll_item_new_fdsem(pa_rtpoll *p, pa_rtpoll_priority_t prio static int asyncmsgq_read_before(pa_rtpoll_item *i) { pa_assert(i); - if (pa_asyncmsgq_read_before_poll(i->userdata) < 0) + if (pa_asyncmsgq_read_before_poll(i->before_userdata) < 0) return 1; /* 1 means immediate restart of the loop */ return 0; @@ -540,7 +540,7 @@ static void asyncmsgq_read_after(pa_rtpoll_item *i) { pa_assert(i); pa_assert((i->pollfd[0].revents & ~POLLIN) == 0); - pa_asyncmsgq_read_after_poll(i->userdata); + pa_asyncmsgq_read_after_poll(i->after_userdata); } static int asyncmsgq_read_work(pa_rtpoll_item *i) { @@ -552,11 +552,11 @@ static int asyncmsgq_read_work(pa_rtpoll_item *i) { pa_assert(i); - if (pa_asyncmsgq_get(i->userdata, &object, &code, &data, &offset, &chunk, 0) == 0) { + if (pa_asyncmsgq_get(i->work_userdata, &object, &code, &data, &offset, &chunk, 0) == 0) { int ret; if (!object && code == PA_MESSAGE_SHUTDOWN) { - pa_asyncmsgq_done(i->userdata, 0); + pa_asyncmsgq_done(i->work_userdata, 0); /* Requests the loop to exit. Will cause the next iteration of * pa_rtpoll_run() to return 0 */ i->rtpoll->quit = true; @@ -564,7 +564,7 @@ static int asyncmsgq_read_work(pa_rtpoll_item *i) { } ret = pa_asyncmsgq_dispatch(object, code, data, offset, &chunk); - pa_asyncmsgq_done(i->userdata, ret); + pa_asyncmsgq_done(i->work_userdata, ret); return 1; } @@ -584,10 +584,9 @@ pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_read(pa_rtpoll *p, pa_rtpoll_priori pollfd->fd = pa_asyncmsgq_read_fd(q); pollfd->events = POLLIN; - i->before_cb = asyncmsgq_read_before; - i->after_cb = asyncmsgq_read_after; - i->work_cb = asyncmsgq_read_work; - i->userdata = q; + pa_rtpoll_item_set_before_callback(i, asyncmsgq_read_before, q); + pa_rtpoll_item_set_after_callback(i, asyncmsgq_read_after, q); + pa_rtpoll_item_set_work_callback(i, asyncmsgq_read_work, q); return i; } @@ -595,7 +594,7 @@ pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_read(pa_rtpoll *p, pa_rtpoll_priori static int asyncmsgq_write_before(pa_rtpoll_item *i) { pa_assert(i); - pa_asyncmsgq_write_before_poll(i->userdata); + pa_asyncmsgq_write_before_poll(i->before_userdata); return 0; } @@ -603,7 +602,7 @@ static void asyncmsgq_write_after(pa_rtpoll_item *i) { pa_assert(i); pa_assert((i->pollfd[0].revents & ~POLLIN) == 0); - pa_asyncmsgq_write_after_poll(i->userdata); + pa_asyncmsgq_write_after_poll(i->after_userdata); } pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_write(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q) { @@ -619,10 +618,8 @@ pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_write(pa_rtpoll *p, pa_rtpoll_prior pollfd->fd = pa_asyncmsgq_write_fd(q); pollfd->events = POLLIN; - i->before_cb = asyncmsgq_write_before; - i->after_cb = asyncmsgq_write_after; - i->work_cb = NULL; - i->userdata = q; + pa_rtpoll_item_set_before_callback(i, asyncmsgq_write_before, q); + pa_rtpoll_item_set_after_callback(i, asyncmsgq_write_after, q); return i; } diff --git a/src/pulsecore/rtpoll.h b/src/pulsecore/rtpoll.h index 8f0715a..121b51e 100644 --- a/src/pulsecore/rtpoll.h +++ b/src/pulsecore/rtpoll.h @@ -80,19 +80,18 @@ struct pollfd *pa_rtpoll_item_get_pollfd(pa_rtpoll_item *i, unsigned *n_fds); /* Set the callback that shall be called when there's time to do some work: If the * callback returns a value > 0, the poll is skipped and the next * iteration of the loop will start immediately. */ -void pa_rtpoll_item_set_work_callback(pa_rtpoll_item *i, int (*work_cb)(pa_rtpoll_item *i)); +void pa_rtpoll_item_set_work_callback(pa_rtpoll_item *i, int (*work_cb)(pa_rtpoll_item *i), void *userdata); /* Set the callback that shall be called immediately before entering * the sleeping poll: If the callback returns a value > 0, the poll is * skipped and the next iteration of the loop will start immediately. */ -void pa_rtpoll_item_set_before_callback(pa_rtpoll_item *i, int (*before_cb)(pa_rtpoll_item *i)); +void pa_rtpoll_item_set_before_callback(pa_rtpoll_item *i, int (*before_cb)(pa_rtpoll_item *i), void *userdata); /* Set the callback that shall be called immediately after having * entered the sleeping poll */ -void pa_rtpoll_item_set_after_callback(pa_rtpoll_item *i, void (*after_cb)(pa_rtpoll_item *i)); +void pa_rtpoll_item_set_after_callback(pa_rtpoll_item *i, void (*after_cb)(pa_rtpoll_item *i), void *userdata); -void pa_rtpoll_item_set_userdata(pa_rtpoll_item *i, void *userdata); -void* pa_rtpoll_item_get_userdata(pa_rtpoll_item *i); +void* pa_rtpoll_item_get_work_userdata(pa_rtpoll_item *i); pa_rtpoll_item *pa_rtpoll_item_new_fdsem(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_fdsem *s); pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_read(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q); diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index d3ead2c..12e3c8b 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -190,7 +190,10 @@ bool pa_sink_input_new_data_set_sink(pa_sink_input_new_data *data, pa_sink *s, b if (!data->req_formats) { /* We're not working with the extended API */ data->sink = s; - data->save_sink = save; + if (save) { + pa_xfree(data->preferred_sink); + data->preferred_sink = pa_xstrdup(s->name); + } data->sink_requested_by_application = requested_by_application; } else { /* Extended API: let's see if this sink supports the formats the client can provide */ @@ -199,7 +202,10 @@ bool pa_sink_input_new_data_set_sink(pa_sink_input_new_data *data, pa_sink *s, b if (formats && !pa_idxset_isempty(formats)) { /* Sink supports at least one of the requested formats */ data->sink = s; - data->save_sink = save; + if (save) { + pa_xfree(data->preferred_sink); + data->preferred_sink = pa_xstrdup(s->name); + } data->sink_requested_by_application = requested_by_application; if (data->nego_formats) pa_idxset_free(data->nego_formats, (pa_free_cb_t) pa_format_info_free); @@ -226,7 +232,7 @@ bool pa_sink_input_new_data_set_formats(pa_sink_input_new_data *data, pa_idxset if (data->sink) { /* Trigger format negotiation */ - return pa_sink_input_new_data_set_sink(data, data->sink, data->save_sink, data->sink_requested_by_application); + return pa_sink_input_new_data_set_sink(data, data->sink, (data->preferred_sink != NULL), data->sink_requested_by_application); } return true; @@ -250,6 +256,9 @@ void pa_sink_input_new_data_done(pa_sink_input_new_data *data) { if (data->volume_factor_sink_items) pa_hashmap_free(data->volume_factor_sink_items); + if (data->preferred_sink) + pa_xfree(data->preferred_sink); + pa_proplist_free(data->proplist); } @@ -467,7 +476,8 @@ int pa_sink_input_new( ((data->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | (core->disable_remixing || (data->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) | (core->remixing_use_all_sink_channels ? 0 : PA_RESAMPLER_NO_FILL_SINK) | - (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) { + (core->remixing_produce_lfe ? PA_RESAMPLER_PRODUCE_LFE : 0) | + (core->remixing_consume_lfe ? PA_RESAMPLER_CONSUME_LFE : 0)))) { pa_log_warn("Unsupported resampling operation."); return -PA_ERR_NOTSUPPORTED; } @@ -518,7 +528,7 @@ int pa_sink_input_new( pa_cvolume_reset(&i->real_ratio, i->sample_spec.channels); i->volume_writable = data->volume_writable; i->save_volume = data->save_volume; - i->save_sink = data->save_sink; + i->preferred_sink = pa_xstrdup(data->preferred_sink); i->save_muted = data->save_muted; i->muted = data->muted; @@ -776,6 +786,9 @@ static void sink_input_free(pa_object *o) { if (i->volume_factor_sink_items) pa_hashmap_free(i->volume_factor_sink_items); + if (i->preferred_sink) + pa_xfree(i->preferred_sink); + pa_xfree(i->driver); pa_xfree(i); } @@ -1914,7 +1927,16 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, bool save) { i->moving(i, dest); i->sink = dest; - i->save_sink = save; + /* save == true, means user is calling the move_to() and want to + save the preferred_sink */ + if (save) { + pa_xfree(i->preferred_sink); + if (dest == dest->core->default_sink) + i->preferred_sink = NULL; + else + i->preferred_sink = pa_xstrdup(dest->name); + } + pa_idxset_put(dest->inputs, pa_sink_input_ref(i), NULL); PA_HASHMAP_FOREACH(v, i->volume_factor_sink_items, state) @@ -1957,6 +1979,12 @@ void pa_sink_input_fail_move(pa_sink_input *i) { if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL], i) == PA_HOOK_STOP) return; + /* Can we move the sink input to the default sink? */ + if (i->core->rescue_streams && pa_sink_input_may_move_to(i, i->core->default_sink)) { + if (pa_sink_input_finish_move(i, i->core->default_sink, false) >= 0) + return; + } + if (i->moving) i->moving(i, NULL); @@ -2292,7 +2320,8 @@ int pa_sink_input_update_resampler(pa_sink_input *i) { ((i->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | (i->core->disable_remixing || (i->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) | (i->core->remixing_use_all_sink_channels ? 0 : PA_RESAMPLER_NO_FILL_SINK) | - (i->core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)); + (i->core->remixing_produce_lfe ? PA_RESAMPLER_PRODUCE_LFE : 0) | + (i->core->remixing_consume_lfe ? PA_RESAMPLER_CONSUME_LFE : 0)); if (!new_resampler) { pa_log_warn("Unsupported resampling operation."); @@ -2404,3 +2433,17 @@ void pa_sink_input_set_reference_ratio(pa_sink_input *i, const pa_cvolume *ratio pa_cvolume_snprint_verbose(old_ratio_str, sizeof(old_ratio_str), &old_ratio, &i->channel_map, true), pa_cvolume_snprint_verbose(new_ratio_str, sizeof(new_ratio_str), ratio, &i->channel_map, true)); } + +/* Called from the main thread. */ +void pa_sink_input_set_preferred_sink(pa_sink_input *i, pa_sink *s) { + pa_assert(i); + + pa_xfree(i->preferred_sink); + if (s) { + i->preferred_sink = pa_xstrdup(s->name); + pa_sink_input_move_to(i, s, false); + } else { + i->preferred_sink = NULL; + pa_sink_input_move_to(i, i->core->default_sink, false); + } +} diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index f7f9237..d3de6e3 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -119,11 +119,16 @@ struct pa_sink_input { bool muted:1; - /* if true then the sink we are connected to and/or the volume - * set is worth remembering, i.e. was explicitly chosen by the - * user and not automatically. module-stream-restore looks for + /* if true then the volume and the mute state of this sink-input + * are worth remembering, module-stream-restore looks for * this.*/ - bool save_sink:1, save_volume:1, save_muted:1; + bool save_volume:1, save_muted:1; + + /* if users move the sink-input to a sink, and the sink is not default_sink, + * the sink->name will be saved in preferred_sink. And later if sink-input + * is moved to other sinks for some reason, it still can be restored to the + * preferred_sink at an appropriate time */ + char *preferred_sink; pa_resample_method_t requested_resample_method, actual_resample_method; @@ -315,7 +320,9 @@ typedef struct pa_sink_input_new_data { bool volume_writable:1; - bool save_sink:1, save_volume:1, save_muted:1; + bool save_volume:1, save_muted:1; + + char *preferred_sink; } pa_sink_input_new_data; pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data); @@ -454,6 +461,8 @@ void pa_sink_input_set_volume_direct(pa_sink_input *i, const pa_cvolume *volume) * i->reference_ratio and logs a message if the value changes. */ void pa_sink_input_set_reference_ratio(pa_sink_input *i, const pa_cvolume *ratio); +void pa_sink_input_set_preferred_sink(pa_sink_input *i, pa_sink *s); + #define pa_sink_input_assert_io_context(s) \ pa_assert(pa_thread_mq_get() || !PA_SINK_INPUT_IS_LINKED((s)->state)) diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 55e9af3..11c3849 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -724,9 +724,14 @@ void pa_sink_put(pa_sink* s) { pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index); pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PUT], s); - /* This function must be called after the PA_CORE_HOOK_SINK_PUT hook, - * because module-switch-on-connect needs to know the old default sink */ + /* It's good to fire the SINK_PUT hook before updating the default sink, + * because module-switch-on-connect will set the new sink as the default + * sink, and if we were to call pa_core_update_default_sink() before that, + * the default sink might change twice, causing unnecessary stream moving. */ + pa_core_update_default_sink(s->core); + + pa_core_move_streams_to_newly_available_preferred_sink(s->core, s); } /* Called from main context */ @@ -757,6 +762,9 @@ void pa_sink_unlink(pa_sink* s) { pa_core_update_default_sink(s->core); + if (linked && s->core->rescue_streams) + pa_sink_move_streams_to_default_sink(s->core, s, false); + if (s->card) pa_idxset_remove_by_data(s->card->sinks, s, NULL); @@ -3931,3 +3939,44 @@ void pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume) { pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_VOLUME_CHANGED], s); } + +void pa_sink_move_streams_to_default_sink(pa_core *core, pa_sink *old_sink, bool default_sink_changed) { + pa_sink_input *i; + uint32_t idx; + + pa_assert(core); + pa_assert(old_sink); + + if (core->state == PA_CORE_SHUTDOWN) + return; + + if (core->default_sink == NULL || core->default_sink->unlink_requested) + return; + + if (old_sink == core->default_sink) + return; + + PA_IDXSET_FOREACH(i, old_sink->inputs, idx) { + if (!PA_SINK_INPUT_IS_LINKED(i->state)) + continue; + + if (!i->sink) + continue; + + /* If default_sink_changed is false, the old sink became unavailable, so all streams must be moved. */ + if (pa_safe_streq(old_sink->name, i->preferred_sink) && default_sink_changed) + continue; + + if (!pa_sink_input_may_move_to(i, core->default_sink)) + continue; + + if (default_sink_changed) + pa_log_info("The sink input %u \"%s\" is moving to %s due to change of the default sink.", + i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), core->default_sink->name); + else + pa_log_info("The sink input %u \"%s\" is moving to %s, because the old sink became unavailable.", + i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), core->default_sink->name); + + pa_sink_input_move_to(i, core->default_sink, false); + } +} diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index b9dd64f..1778eb7 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -558,6 +558,12 @@ int64_t pa_sink_get_latency_within_thread(pa_sink *s, bool allow_negative); * s->reference_volume and fires change notifications. */ void pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume); +/* When the default_sink is changed or the active_port of a sink is changed to + * PA_AVAILABLE_NO, this function is called to move the streams of the old + * default_sink or the sink with active_port equals PA_AVAILABLE_NO to the + * current default_sink conditionally*/ +void pa_sink_move_streams_to_default_sink(pa_core *core, pa_sink *old_sink, bool default_sink_changed); + /* Verify that we called in IO context (aka 'thread context), or that * the sink is not yet set up, i.e. the thread not set up yet. See * pa_assert_io_context() in thread-mq.h for more information. */ diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index f5005ab..92f74d4 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -134,7 +134,10 @@ bool pa_source_output_new_data_set_source(pa_source_output_new_data *data, pa_so if (!data->req_formats) { /* We're not working with the extended API */ data->source = s; - data->save_source = save; + if (save) { + pa_xfree(data->preferred_source); + data->preferred_source = pa_xstrdup(s->name); + } data->source_requested_by_application = requested_by_application; } else { /* Extended API: let's see if this source supports the formats the client would like */ @@ -143,7 +146,10 @@ bool pa_source_output_new_data_set_source(pa_source_output_new_data *data, pa_so if (formats && !pa_idxset_isempty(formats)) { /* Source supports at least one of the requested formats */ data->source = s; - data->save_source = save; + if (save) { + pa_xfree(data->preferred_source); + data->preferred_source = pa_xstrdup(s->name); + } data->source_requested_by_application = requested_by_application; if (data->nego_formats) pa_idxset_free(data->nego_formats, (pa_free_cb_t) pa_format_info_free); @@ -170,7 +176,7 @@ bool pa_source_output_new_data_set_formats(pa_source_output_new_data *data, pa_i if (data->source) { /* Trigger format negotiation */ - return pa_source_output_new_data_set_source(data, data->source, data->save_source, + return pa_source_output_new_data_set_source(data, data->source, (data->preferred_source != NULL), data->source_requested_by_application); } @@ -189,6 +195,9 @@ void pa_source_output_new_data_done(pa_source_output_new_data *data) { if (data->format) pa_format_info_free(data->format); + if (data->preferred_source) + pa_xfree(data->preferred_source); + pa_proplist_free(data->proplist); } @@ -414,7 +423,8 @@ int pa_source_output_new( ((data->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | (core->disable_remixing || (data->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) | (core->remixing_use_all_sink_channels ? 0 : PA_RESAMPLER_NO_FILL_SINK) | - (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) { + (core->remixing_produce_lfe ? PA_RESAMPLER_PRODUCE_LFE : 0) | + (core->remixing_consume_lfe ? PA_RESAMPLER_CONSUME_LFE : 0)))) { pa_log_warn("Unsupported resampling operation."); return -PA_ERR_NOTSUPPORTED; } @@ -459,7 +469,7 @@ int pa_source_output_new( pa_cvolume_reset(&o->real_ratio, o->sample_spec.channels); o->volume_writable = data->volume_writable; o->save_volume = data->save_volume; - o->save_source = data->save_source; + o->preferred_source = pa_xstrdup(data->preferred_source); o->save_muted = data->save_muted; o->muted = data->muted; @@ -651,6 +661,9 @@ static void source_output_free(pa_object* mo) { if (o->proplist) pa_proplist_free(o->proplist); + if (o->preferred_source) + pa_xfree(o->preferred_source); + pa_xfree(o->driver); pa_xfree(o); } @@ -1544,7 +1557,16 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, bool save o->moving(o, dest); o->source = dest; - o->save_source = save; + /* save == true, means user is calling the move_to() and want to + save the preferred_source */ + if (save) { + pa_xfree(o->preferred_source); + if (dest == dest->core->default_source) + o->preferred_source = NULL; + else + o->preferred_source = pa_xstrdup(dest->name); + } + pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL); pa_cvolume_remap(&o->volume_factor_source, &o->channel_map, &o->source->channel_map); @@ -1584,6 +1606,12 @@ void pa_source_output_fail_move(pa_source_output *o) { if (pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FAIL], o) == PA_HOOK_STOP) return; + /* Can we move the source output to the default source? */ + if (o->core->rescue_streams && pa_source_output_may_move_to(o, o->core->default_source)) { + if (pa_source_output_finish_move(o, o->core->default_source, false) >= 0) + return; + } + if (o->moving) o->moving(o, NULL); @@ -1755,7 +1783,8 @@ int pa_source_output_update_resampler(pa_source_output *o) { ((o->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | (o->core->disable_remixing || (o->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) | (o->core->remixing_use_all_sink_channels ? 0 : PA_RESAMPLER_NO_FILL_SINK) | - (o->core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)); + (o->core->remixing_produce_lfe ? PA_RESAMPLER_PRODUCE_LFE : 0) | + (o->core->remixing_consume_lfe ? PA_RESAMPLER_CONSUME_LFE : 0)); if (!new_resampler) { pa_log_warn("Unsupported resampling operation."); @@ -1867,3 +1896,17 @@ void pa_source_output_set_reference_ratio(pa_source_output *o, const pa_cvolume pa_cvolume_snprint_verbose(old_ratio_str, sizeof(old_ratio_str), &old_ratio, &o->channel_map, true), pa_cvolume_snprint_verbose(new_ratio_str, sizeof(new_ratio_str), ratio, &o->channel_map, true)); } + +/* Called from the main thread. */ +void pa_source_output_set_preferred_source(pa_source_output *o, pa_source *s) { + pa_assert(o); + + pa_xfree(o->preferred_source); + if (s) { + o->preferred_source = pa_xstrdup(s->name); + pa_source_output_move_to(o, s, false); + } else { + o->preferred_source = NULL; + pa_source_output_move_to(o, o->core->default_source, false); + } +} diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h index 16853c0..2bf5682 100644 --- a/src/pulsecore/source-output.h +++ b/src/pulsecore/source-output.h @@ -106,11 +106,15 @@ struct pa_source_output { bool muted:1; - /* if true then the source we are connected to and/or the volume - * set is worth remembering, i.e. was explicitly chosen by the - * user and not automatically. module-stream-restore looks for - * this.*/ - bool save_source:1, save_volume:1, save_muted:1; + /* if true then the volume and the mute state of this source-output + * are worth remembering, module-stream-restore looks for this. */ + bool save_volume:1, save_muted:1; + + /* if users move the source-output to a source, and the source is not + * default_source, the source->name will be saved in preferred_source. And + * later if source-output is moved to other sources for some reason, it + * still can be restored to the preferred_source at an appropriate time */ + char *preferred_source; pa_resample_method_t requested_resample_method, actual_resample_method; @@ -277,7 +281,8 @@ typedef struct pa_source_output_new_data { bool volume_writable:1; - bool save_source:1, save_volume:1, save_muted:1; + bool save_volume:1, save_muted:1; + char *preferred_source; } pa_source_output_new_data; pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data); @@ -397,6 +402,8 @@ void pa_source_output_set_volume_direct(pa_source_output *o, const pa_cvolume *v * o->reference_ratio and logs a message if the value changes. */ void pa_source_output_set_reference_ratio(pa_source_output *o, const pa_cvolume *ratio); +void pa_source_output_set_preferred_source(pa_source_output *o, pa_source *s); + #define pa_source_output_assert_io_context(s) \ pa_assert(pa_thread_mq_get() || !PA_SOURCE_OUTPUT_IS_LINKED((s)->state)) diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 855d3de..1cb83f9 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -666,9 +666,13 @@ void pa_source_put(pa_source *s) { pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index); pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PUT], s); - /* This function must be called after the PA_CORE_HOOK_SOURCE_PUT hook, - * because module-switch-on-connect needs to know the old default source */ + /* It's good to fire the SOURCE_PUT hook before updating the default source, + * because module-switch-on-connect will set the new source as the default + * source, and if we were to call pa_core_update_default_source() before that, + * the default source might change twice, causing unnecessary stream moving. */ pa_core_update_default_source(s->core); + + pa_core_move_streams_to_newly_available_preferred_source(s->core, s); } /* Called from main context */ @@ -698,6 +702,9 @@ void pa_source_unlink(pa_source *s) { pa_core_update_default_source(s->core); + if (linked && s->core->rescue_streams) + pa_source_move_streams_to_default_source(s->core, s, false); + if (s->card) pa_idxset_remove_by_data(s->card->sources, s, NULL); @@ -2988,3 +2995,44 @@ void pa_source_set_reference_volume_direct(pa_source *s, const pa_cvolume *volum pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_VOLUME_CHANGED], s); } + +void pa_source_move_streams_to_default_source(pa_core *core, pa_source *old_source, bool default_source_changed) { + pa_source_output *o; + uint32_t idx; + + pa_assert(core); + pa_assert(old_source); + + if (core->state == PA_CORE_SHUTDOWN) + return; + + if (core->default_source == NULL || core->default_source->unlink_requested) + return; + + if (old_source == core->default_source) + return; + + PA_IDXSET_FOREACH(o, old_source->outputs, idx) { + if (!PA_SOURCE_OUTPUT_IS_LINKED(o->state)) + continue; + + if (!o->source) + continue; + + /* If default_source_changed is false, the old source became unavailable, so all streams must be moved. */ + if (pa_safe_streq(old_source->name, o->preferred_source) && default_source_changed) + continue; + + if (!pa_source_output_may_move_to(o, core->default_source)) + continue; + + if (default_source_changed) + pa_log_info("The source output %u \"%s\" is moving to %s due to change of the default source.", + o->index, pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME)), core->default_source->name); + else + pa_log_info("The source output %u \"%s\" is moving to %s, because the old source became unavailable.", + o->index, pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME)), core->default_source->name); + + pa_source_output_move_to(o, core->default_source, false); + } +} diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index e9462c6..12e37d6 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -479,6 +479,12 @@ int64_t pa_source_get_latency_within_thread(pa_source *s, bool allow_negative); * sets s->reference_volume and fires change notifications. */ void pa_source_set_reference_volume_direct(pa_source *s, const pa_cvolume *volume); +/* When the default_source is changed or the active_port of a source is changed to + * PA_AVAILABLE_NO, this function is called to move the streams of the old + * default_source or the source with active_port equals PA_AVAILABLE_NO to the + * current default_source conditionally*/ +void pa_source_move_streams_to_default_source(pa_core *core, pa_source *old_source, bool default_source_changed); + #define pa_source_assert_io_context(s) \ pa_assert(pa_thread_mq_get() || !PA_SOURCE_IS_LINKED((s)->state)) |