Projects
Essentials
pipewire-aptx
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 26
View file
pipewire-aptx.changes
Changed
@@ -1,4 +1,9 @@ ------------------------------------------------------------------- +Fri Apr 14 13:35:34 UTC 2023 - Bjørn Lie <zaitor@opensuse.org> + +- Update to version 0.3.69 + +------------------------------------------------------------------- Sat Apr 8 17:49:24 UTC 2023 - Bjørn Lie <zaitor@opensuse.org> - Update to version 0.3.68
View file
pipewire-aptx.spec
Changed
@@ -7,7 +7,7 @@ %define soversion 0_2 Name: pipewire-aptx -Version: 0.3.68 +Version: 0.3.69 Release: 0 Summary: PipeWire Bluetooth aptX codec plugin License: MIT @@ -16,7 +16,7 @@ BuildRequires: c++_compiler BuildRequires: c_compiler -BuildRequires: meson >= 0.59.0 +BuildRequires: meson >= 0.61.1 BuildRequires: pkgconfig BuildRequires: pkgconfig(bluez) BuildRequires: pkgconfig(dbus-1)
View file
pipewire-0.3.68.tar.gz/NEWS -> pipewire-0.3.69.tar.gz/NEWS
Changed
@@ -1,3 +1,59 @@ +# PipeWire 0.3.69 (2023-04-13) + +This is a quick bugfix release that is API and ABI compatible with previous +0.3.x releases. + +## Highlights + - Reverted the UCM changes, they seem to cause regressions causing audio + to be muted in some cases. + - Fix a regression in the scheduler where a driver node might not be marked + runnable in some cases, like when echo-cancel is used. (#3145) + - Handle links from the driver to itself. This makes the midi bridge work + again. (#3153) + - ALSA rate matching for sources was fixed. It would previously wait too + long for rate matching and then cause drift. This should reduce + crackling and stuttering whan capturing in low latency. + - Fix the GStreamer clock to make cheese video recording work again. (#3149) + - More fixes and improvements. + +## PipeWire + - Fix a regression in the scheduler where a driver node might not be marked + runnable in some cases, like when echo-cancel is used. (#3145) + - Handle links from the driver to itself. This makes the midi bridge work + again. (#3153) + - Some man pages were improved. + - Fix a potential crash when thread-loop is destroyed before the loop. + (#3150) + +## Modules + - A new raw biquad filter was added to filter-chain. You can manually set the + 6 parameters and you can use this to create custom filters per sample rate. + (#3139) + - The echo-canceler now supports different channels for the capture and playback + streams. + +## SPA + - A SB Audigy specific profile set was added to make better use of the + controls. (#2934) + - More ALSA IRQ based scheduling improvements. + - ALSA rate matching for sources was fixed. It would previously wait too + long for rate matching and then cause drift. This should reduce + crackling and stuttering whan capturing in low latency. + - The echo-cancel plugin API has a new method to make it possible to have + different channels for capture, source and playback. + - Reverted the UCM changes, they seem to cause regressions causing audio + to be muted in some cases. + +## Bluetooth + - Many more BAP fixes and improvements. Devices are now created as a set + and can be combined into one device by the session manager. + +## GStreamer + - Fix the GStreamer clock to make cheese video recording work again. (#3149) + + +Older versions: + # PipeWire 0.3.68 (2023-04-06) This is a bugfix release that is API and ABI compatible with previous @@ -135,9 +191,6 @@ ## GStreamer - Sort the device by priority in deviceprovider. (#3072) - -Older versions: - # PipeWire 0.3.67 (2023-03-09) This is a bugfix release that is API and ABI compatible with previous
View file
pipewire-0.3.68.tar.gz/man/pipewire.conf.5.rst.in -> pipewire-0.3.69.tar.gz/man/pipewire.conf.5.rst.in
Changed
@@ -69,8 +69,7 @@ of use, a relaxed format may be used where: * ``:`` to delimit keys and values can be substuted by ``=`` or a space. - * ``"`` around keys and string can be omited as long as no special characters - are used in the strings. + * ``"`` around keys and string can be omited as long as no special characters are used in the strings. * ``,`` to separate objects can be replaced with a whitespace character. * ``#`` can be used to start a comment until the line end
View file
pipewire-0.3.68.tar.gz/man/pw-cli.1.rst.in -> pipewire-0.3.69.tar.gz/man/pw-cli.1.rst.in
Changed
@@ -174,10 +174,6 @@ send-command *object-id* Send a command to an object. - -EXAMPLES -======== - AUTHORS =======
View file
pipewire-0.3.68.tar.gz/man/pw-metadata.1.rst.in -> pipewire-0.3.69.tar.gz/man/pw-metadata.1.rst.in
Changed
@@ -48,11 +48,23 @@ Keeps running and log the changes to the metadata. -d | --delete + Delete all metadata for *id* or for the specified *key* of object *id*. + Without any option, all metadata is removed. - Delete all metadata for *id* or for the - specified *key* of object *id* +-n | --name + Metadata name (Default: "default"). - Without any option, all metadata is removed +EXAMPLES +======== + +**pw-metadata** + Show metadata in default name. + +**pw-metadata** -n settings 0 + Display settings. + +**pw-metadata** -n settings 0 clock.quantum 1024 + Change clock.quantum to 1024. AUTHORS =======
View file
pipewire-0.3.68.tar.gz/meson.build -> pipewire-0.3.69.tar.gz/meson.build
Changed
@@ -1,5 +1,5 @@ project('pipewire', 'c' , - version : '0.3.68', + version : '0.3.69', license : 'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' , meson_version : '>= 0.61.1', default_options : 'warning_level=3',
View file
pipewire-0.3.68.tar.gz/pipewire-jack/src/pipewire-jack.c -> pipewire-0.3.69.tar.gz/pipewire-jack/src/pipewire-jack.c
Changed
@@ -1361,10 +1361,16 @@ return c->sample_rate == sample_rate; } +static inline uint64_t get_time_ns(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return SPA_TIMESPEC_TO_NSEC(&ts); +} + static inline uint32_t cycle_run(struct client *c) { uint64_t cmd; - struct timespec ts; int fd = c->socket_source->fd; struct spa_io_position *pos = c->rt.position; struct pw_node_activation *activation = c->activation; @@ -1383,9 +1389,8 @@ if (SPA_UNLIKELY(cmd > 1)) pw_log_info("%p: missed %"PRIu64" wakeups", c, cmd - 1); - clock_gettime(CLOCK_MONOTONIC, &ts); activation->status = PW_NODE_ACTIVATION_AWAKE; - activation->awake_time = SPA_TIMESPEC_TO_NSEC(&ts); + activation->awake_time = get_time_ns(); if (SPA_UNLIKELY(c->first)) { if (c->thread_init_callback) @@ -1442,15 +1447,13 @@ static inline void signal_sync(struct client *c) { - struct timespec ts; uint64_t cmd, nsec; struct link *l; struct pw_node_activation *activation = c->activation; complete_process(c, c->buffer_frames); - clock_gettime(CLOCK_MONOTONIC, &ts); - nsec = SPA_TIMESPEC_TO_NSEC(&ts); + nsec = get_time_ns(); activation->status = PW_NODE_ACTIVATION_FINISHED; activation->finish_time = nsec; @@ -5700,7 +5703,6 @@ { struct client *c = (struct client *) client; struct spa_io_position *pos; - struct timespec ts; uint64_t diff; spa_return_val_if_fail(c != NULL, 0); @@ -5708,17 +5710,14 @@ if (SPA_UNLIKELY((pos = c->rt.position) == NULL)) return 0; - clock_gettime(CLOCK_MONOTONIC, &ts); - diff = SPA_TIMESPEC_TO_NSEC(&ts) - pos->clock.nsec; + diff = get_time_ns() - pos->clock.nsec; return (jack_nframes_t) floor(((double)c->sample_rate * diff) / SPA_NSEC_PER_SEC); } SPA_EXPORT jack_nframes_t jack_frame_time (const jack_client_t *client) { - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return jack_time_to_frames(client, SPA_TIMESPEC_TO_USEC(&ts)); + return jack_time_to_frames(client, jack_get_time()); } SPA_EXPORT @@ -5801,9 +5800,7 @@ SPA_EXPORT jack_time_t jack_get_time(void) { - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return SPA_TIMESPEC_TO_USEC(&ts); + return get_time_ns()/SPA_NSEC_PER_USEC; } SPA_EXPORT @@ -6000,9 +5997,7 @@ running = pos->clock.position - pos->offset; if (pos->state == SPA_IO_POSITION_STATE_RUNNING) { - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - uint64_t nsecs = SPA_TIMESPEC_TO_NSEC(&ts) - pos->clock.nsec; + uint64_t nsecs = get_time_ns() - pos->clock.nsec; running += (uint64_t)floor((((double) c->sample_rate) / SPA_NSEC_PER_SEC) * nsecs); } seg = &pos->segments0;
View file
pipewire-0.3.68.tar.gz/spa/include/spa/interfaces/audio/aec.h -> pipewire-0.3.69.tar.gz/spa/include/spa/interfaces/audio/aec.h
Changed
@@ -40,7 +40,7 @@ }; struct spa_audio_aec_methods { -#define SPA_VERSION_AUDIO_AEC_METHODS 2 +#define SPA_VERSION_AUDIO_AEC_METHODS 3 uint32_t version; int (*add_listener) (void *object, @@ -60,6 +60,12 @@ int (*enum_props) (void* object, int index, struct spa_pod_builder* builder); int (*get_params) (void* object, struct spa_pod_builder* builder); int (*set_params) (void *object, const struct spa_pod *args); + + /* version 1:3 */ + int (*init2) (void *object, const struct spa_dict *args, + struct spa_audio_info_raw *play_info, + struct spa_audio_info_raw *rec_info, + struct spa_audio_info_raw *out_info); }; #define spa_audio_aec_method(o,method,version,...) \ @@ -81,6 +87,7 @@ #define spa_audio_aec_enum_props(o,...) spa_audio_aec_method(o, enum_props, 2, __VA_ARGS__) #define spa_audio_aec_get_params(o,...) spa_audio_aec_method(o, get_params, 2, __VA_ARGS__) #define spa_audio_aec_set_params(o,...) spa_audio_aec_method(o, set_params, 2, __VA_ARGS__) +#define spa_audio_aec_init2(o,...) spa_audio_aec_method(o, init2, 3, __VA_ARGS__) #ifdef __cplusplus } /* extern "C" */
View file
pipewire-0.3.68.tar.gz/spa/plugins/aec/aec-webrtc.cpp -> pipewire-0.3.69.tar.gz/spa/plugins/aec/aec-webrtc.cpp
Changed
@@ -22,7 +22,9 @@ struct spa_log *log; std::unique_ptr<webrtc::AudioProcessing> apm; - spa_audio_info_raw info; + spa_audio_info_raw rec_info; + spa_audio_info_raw out_info; + spa_audio_info_raw play_info; std::unique_ptr<float *> play_buffer, rec_buffer, out_buffer; }; @@ -38,9 +40,12 @@ return default_value; } -static int webrtc_init(void *object, const struct spa_dict *args, const struct spa_audio_info_raw *info) +static int webrtc_init2(void *object, const struct spa_dict *args, + struct spa_audio_info_raw *rec_info, struct spa_audio_info_raw *out_info, + struct spa_audio_info_raw *play_info) { auto impl = static_cast<struct impl_data*>(object); + int res; bool extended_filter = webrtc_get_spa_bool(args, "webrtc.extended_filter", true); bool delay_agnostic = webrtc_get_spa_bool(args, "webrtc.delay_agnostic", true); @@ -67,16 +72,16 @@ config.Set<webrtc::ExperimentalNs>(new webrtc::ExperimentalNs(experimental_ns)); webrtc::ProcessingConfig pconfig = {{ - webrtc::StreamConfig(info->rate, info->channels, false), /* input stream */ - webrtc::StreamConfig(info->rate, info->channels, false), /* output stream */ - webrtc::StreamConfig(info->rate, info->channels, false), /* reverse input stream */ - webrtc::StreamConfig(info->rate, info->channels, false), /* reverse output stream */ + webrtc::StreamConfig(rec_info->rate, rec_info->channels, false), /* input stream */ + webrtc::StreamConfig(out_info->rate, out_info->channels, false), /* output stream */ + webrtc::StreamConfig(play_info->rate, play_info->channels, false), /* reverse input stream */ + webrtc::StreamConfig(play_info->rate, play_info->channels, false), /* reverse output stream */ }}; auto apm = std::unique_ptr<webrtc::AudioProcessing>(webrtc::AudioProcessing::Create(config)); - if (apm->Initialize(pconfig) != webrtc::AudioProcessing::kNoError) { - spa_log_error(impl->log, "Error initialising webrtc audio processing module"); - return -1; + if ((res = apm->Initialize(pconfig)) != webrtc::AudioProcessing::kNoError) { + spa_log_error(impl->log, "Error initialising webrtc audio processing module: %d", res); + return -EINVAL; } apm->high_pass_filter()->Enable(high_pass_filter); @@ -94,48 +99,72 @@ apm->gain_control()->set_mode(webrtc::GainControl::kAdaptiveDigital); apm->gain_control()->Enable(gain_control); impl->apm = std::move(apm); - impl->info = *info; - impl->play_buffer = std::make_unique<float *>(info->channels); - impl->rec_buffer = std::make_unique<float *>(info->channels); - impl->out_buffer = std::make_unique<float *>(info->channels); + impl->rec_info = *rec_info; + impl->out_info = *out_info; + impl->play_info = *play_info; + impl->play_buffer = std::make_unique<float *>(play_info->channels); + impl->rec_buffer = std::make_unique<float *>(rec_info->channels); + impl->out_buffer = std::make_unique<float *>(out_info->channels); return 0; } +static int webrtc_init(void *object, const struct spa_dict *args, + const struct spa_audio_info_raw *info) +{ + int res; + struct spa_audio_info_raw rec_info = *info; + struct spa_audio_info_raw out_info = *info; + struct spa_audio_info_raw play_info = *info; + res = webrtc_init2(object, args, &rec_info, &out_info, &play_info); + if (rec_info.channels != out_info.channels) + res = -EINVAL; + return res; +} + static int webrtc_run(void *object, const float *rec, const float *play, float *out, uint32_t n_samples) { auto impl = static_cast<struct impl_data*>(object); - webrtc::StreamConfig config = - webrtc::StreamConfig(impl->info.rate, impl->info.channels, false); - unsigned int num_blocks = n_samples * 1000 / impl->info.rate / 10; + int res; + + webrtc::StreamConfig play_config = + webrtc::StreamConfig(impl->play_info.rate, impl->play_info.channels, false); + webrtc::StreamConfig rec_config = + webrtc::StreamConfig(impl->rec_info.rate, impl->rec_info.channels, false); + webrtc::StreamConfig out_config = + webrtc::StreamConfig(impl->out_info.rate, impl->out_info.channels, false); + unsigned int num_blocks = n_samples * 1000 / impl->play_info.rate / 10; - if (n_samples * 1000 / impl->info.rate % 10 != 0) { + if (n_samples * 1000 / impl->play_info.rate % 10 != 0) { spa_log_error(impl->log, "Buffers must be multiples of 10ms in length (currently %u samples)", n_samples); - return -1; + return -EINVAL; } for (size_t i = 0; i < num_blocks; i ++) { - for (size_t j = 0; j < impl->info.channels; j++) { - impl->play_bufferj = const_cast<float *>(playj) + config.num_frames() * i; - impl->rec_bufferj = const_cast<float *>(recj) + config.num_frames() * i; - impl->out_bufferj = outj + config.num_frames() * i; - } + for (size_t j = 0; j < impl->play_info.channels; j++) + impl->play_bufferj = const_cast<float *>(playj) + play_config.num_frames() * i; + for (size_t j = 0; j < impl->rec_info.channels; j++) + impl->rec_bufferj = const_cast<float *>(recj) + rec_config.num_frames() * i; + for (size_t j = 0; j < impl->out_info.channels; j++) + impl->out_bufferj = outj + out_config.num_frames() * i; + /* FIXME: ProcessReverseStream may change the playback buffer, in which * case we should use that, if we ever expose the intelligibility * enhancer */ - if (impl->apm->ProcessReverseStream(impl->play_buffer.get(), config, config, impl->play_buffer.get()) != + if ((res = impl->apm->ProcessReverseStream(impl->play_buffer.get(), + play_config, play_config, impl->play_buffer.get())) != webrtc::AudioProcessing::kNoError) { - spa_log_error(impl->log, "Processing reverse stream failed"); + spa_log_error(impl->log, "Processing reverse stream failed: %d", res); } // Extra delay introduced by multiple frames impl->apm->set_stream_delay_ms((num_blocks - 1) * 10); - if (impl->apm->ProcessStream(impl->rec_buffer.get(), config, config, impl->out_buffer.get()) != + if ((res = impl->apm->ProcessStream(impl->rec_buffer.get(), + rec_config, out_config, impl->out_buffer.get())) != webrtc::AudioProcessing::kNoError) { - spa_log_error(impl->log, "Processing stream failed"); + spa_log_error(impl->log, "Processing stream failed: %d", res); } } - return 0; } @@ -144,6 +173,7 @@ .add_listener = NULL, .init = webrtc_init, .run = webrtc_run, + .init2 = webrtc_init2, }; static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface)
View file
pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/acp.c -> pipewire-0.3.69.tar.gz/spa/plugins/alsa/acp/acp.c
Changed
@@ -362,7 +362,7 @@ devstr, NULL, &m->sample_spec, &m->channel_map, SND_PCM_STREAM_PLAYBACK, &try_period_size, &try_buffer_size, - 0, NULL, NULL, NULL, NULL, false))) { + 0, NULL, NULL, false))) { pa_alsa_init_proplist_pcm(NULL, m->output_proplist, m->output_pcm); pa_proplist_setf(m->output_proplist, "clock.name", "api.alsa.%u", index); pa_alsa_close(&m->output_pcm); @@ -392,7 +392,7 @@ devstr, NULL, &m->sample_spec, &m->channel_map, SND_PCM_STREAM_CAPTURE, &try_period_size, &try_buffer_size, - 0, NULL, NULL, NULL, NULL, false))) { + 0, NULL, NULL, false))) { pa_alsa_init_proplist_pcm(NULL, m->input_proplist, m->input_pcm); pa_proplist_setf(m->input_proplist, "clock.name", "api.alsa.%u", index); pa_alsa_close(&m->input_pcm); @@ -449,8 +449,8 @@ pa_dynarray_append(&impl->out.devices, dev); } if (impl->use_ucm) { - if (m->ucm_context.ucm_device) { - pa_alsa_ucm_add_port(NULL, &m->ucm_context, + if (m->ucm_context.ucm_devices) { + pa_alsa_ucm_add_ports_combination(NULL, &m->ucm_context, true, impl->ports, ap, NULL); pa_alsa_ucm_add_ports(&dev->ports, m->proplist, &m->ucm_context, true, impl, dev->pcm_handle, impl->profile_set->ignore_dB); @@ -473,8 +473,8 @@ } if (impl->use_ucm) { - if (m->ucm_context.ucm_device) { - pa_alsa_ucm_add_port(NULL, &m->ucm_context, + if (m->ucm_context.ucm_devices) { + pa_alsa_ucm_add_ports_combination(NULL, &m->ucm_context, false, impl->ports, ap, NULL); pa_alsa_ucm_add_ports(&dev->ports, m->proplist, &m->ucm_context, false, impl, dev->pcm_handle, impl->profile_set->ignore_dB); @@ -608,7 +608,7 @@ static int report_jack_state(snd_mixer_elem_t *melem, unsigned int mask) { pa_card *impl = snd_mixer_elem_get_callback_private(melem); - snd_hctl_elem_t **_elem = snd_mixer_elem_get_private(melem), *elem; + snd_hctl_elem_t *elem = snd_mixer_elem_get_private(melem); snd_ctl_elem_value_t *elem_value; bool plugged_in, any_input_port_available; void *state; @@ -618,8 +618,6 @@ enum acp_available active_available = ACP_AVAILABLE_UNKNOWN; size_t size; - pa_assert(_elem); - elem = *_elem; #if 0 /* Changing the jack state may cause a port change, and a port change will * make the sink or source change the mixer settings. If there are multiple @@ -888,17 +886,13 @@ static int hdmi_eld_changed(snd_mixer_elem_t *melem, unsigned int mask) { pa_card *impl = snd_mixer_elem_get_callback_private(melem); - snd_hctl_elem_t **_elem = snd_mixer_elem_get_private(melem), *elem; - int device; + snd_hctl_elem_t *elem = snd_mixer_elem_get_private(melem); + int device = snd_hctl_elem_get_device(elem); const char *old_monitor_name; pa_device_port *p; pa_hdmi_eld eld; bool changed = false; - pa_assert(_elem); - elem = *_elem; - device = snd_hctl_elem_get_device(elem); - if (mask == SND_CTL_EVENT_MASK_REMOVE) return 0; @@ -1259,7 +1253,8 @@ * will be NULL, but the UCM device enable sequence will still need to be * executed. */ if (dev->active_port && dev->ucm_context) { - if ((res = pa_alsa_ucm_set_port(dev->ucm_context, dev->active_port)) < 0) + if ((res = pa_alsa_ucm_set_port(dev->ucm_context, dev->active_port, + dev->direction == PA_ALSA_DIRECTION_OUTPUT)) < 0) return res; } @@ -1434,7 +1429,8 @@ /* if UCM is available for this card then update the verb */ if (impl->use_ucm && !(np->profile.flags & ACP_PROFILE_PRO)) { if ((res = pa_alsa_ucm_set_profile(&impl->ucm, impl, - np->profile.flags & ACP_PROFILE_OFF ? NULL : np, op)) < 0) { + np->profile.flags & ACP_PROFILE_OFF ? NULL : np->profile.name, + op ? op->profile.name : NULL)) < 0) { return res; } } @@ -1443,8 +1439,8 @@ PA_IDXSET_FOREACH(am, np->output_mappings, idx) { if (impl->use_ucm) { /* Update ports priorities */ - if (am->ucm_context.ucm_device) { - pa_alsa_ucm_add_port(am->output.ports, &am->ucm_context, + if (am->ucm_context.ucm_devices) { + pa_alsa_ucm_add_ports_combination(am->output.ports, &am->ucm_context, true, impl->ports, np, NULL); } } @@ -1456,8 +1452,8 @@ PA_IDXSET_FOREACH(am, np->input_mappings, idx) { if (impl->use_ucm) { /* Update ports priorities */ - if (am->ucm_context.ucm_device) { - pa_alsa_ucm_add_port(am->input.ports, &am->ucm_context, + if (am->ucm_context.ucm_devices) { + pa_alsa_ucm_add_ports_combination(am->input.ports, &am->ucm_context, false, impl->ports, np, NULL); } } @@ -1848,7 +1844,8 @@ mixer_volume_init(impl, d); sync_mixer(d, p); - res = pa_alsa_ucm_set_port(d->ucm_context, p); + res = pa_alsa_ucm_set_port(d->ucm_context, p, + dev->direction == ACP_DIRECTION_PLAYBACK); } else { pa_alsa_port_data *data;
View file
pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/alsa-mixer.c -> pipewire-0.3.69.tar.gz/spa/plugins/alsa/acp/alsa-mixer.c
Changed
@@ -5000,7 +5000,7 @@ handle = pa_alsa_open_by_template( m->device_strings, dev_id, NULL, &try_ss, &try_map, mode, &try_period_size, - &try_buffer_size, 0, NULL, NULL, NULL, NULL, exact_channels); + &try_buffer_size, 0, NULL, NULL, exact_channels); if (handle && !exact_channels && m->channel_map.channels != try_map.channels) { char bufPA_CHANNEL_MAP_SNPRINT_MAX; pa_log_debug("Channel map for mapping '%s' permanently changed to '%s'", m->name,
View file
pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/alsa-mixer.h -> pipewire-0.3.69.tar.gz/spa/plugins/alsa/acp/alsa-mixer.h
Changed
@@ -354,7 +354,7 @@ pa_alsa_device output; pa_alsa_device input; - /* ucm device context */ + /* ucm device context*/ pa_alsa_ucm_mapping_context ucm_context; }; @@ -381,9 +381,6 @@ pa_idxset *input_mappings; pa_idxset *output_mappings; - /* ucm device context */ - pa_alsa_ucm_profile_context ucm_context; - struct { pa_dynarray devices; } out;
View file
pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/alsa-ucm.c -> pipewire-0.3.69.tar.gz/spa/plugins/alsa/acp/alsa-ucm.c
Changed
@@ -72,8 +72,9 @@ static void ucm_port_data_init(pa_alsa_ucm_port_data *port, pa_alsa_ucm_config *ucm, pa_device_port *core_port, - pa_alsa_ucm_device *device); + pa_alsa_ucm_device **devices, unsigned n_devices); static void ucm_port_data_free(pa_device_port *port); +static void ucm_port_update_available(pa_alsa_ucm_port_data *port); static struct ucm_type types = { {"None", PA_DEVICE_PORT_TYPE_UNKNOWN}, @@ -169,6 +170,17 @@ return (char *)value; } +static int ucm_device_exists(pa_idxset *idxset, pa_alsa_ucm_device *dev) { + pa_alsa_ucm_device *d; + uint32_t idx; + + PA_IDXSET_FOREACH(d, idxset, idx) + if (d == dev) + return 1; + + return 0; +} + static void ucm_add_devices_to_idxset( pa_idxset *idxset, pa_alsa_ucm_device *me, @@ -494,10 +506,10 @@ n_confdev = snd_use_case_get_list(uc_mgr, id, &devices); pa_xfree(id); - device->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); if (n_confdev <= 0) pa_log_debug("No %s for device %s", "_conflictingdevs", device_name); else { + device->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); ucm_add_devices_to_idxset(device->conflicting_devices, device, verb->devices, devices, n_confdev); snd_use_case_free_list(devices, n_confdev); } @@ -506,10 +518,10 @@ n_suppdev = snd_use_case_get_list(uc_mgr, id, &devices); pa_xfree(id); - device->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); if (n_suppdev <= 0) pa_log_debug("No %s for device %s", "_supporteddevs", device_name); else { + device->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); ucm_add_devices_to_idxset(device->supported_devices, device, verb->devices, devices, n_suppdev); snd_use_case_free_list(devices, n_suppdev); } @@ -518,16 +530,10 @@ }; /* Create a property list for this ucm modifier */ -static int ucm_get_modifier_property( - pa_alsa_ucm_modifier *modifier, - snd_use_case_mgr_t *uc_mgr, - pa_alsa_ucm_verb *verb, - const char *modifier_name) { +static int ucm_get_modifier_property(pa_alsa_ucm_modifier *modifier, snd_use_case_mgr_t *uc_mgr, const char *modifier_name) { const char *value; char *id; int i; - const char **devices; - int n_confdev, n_suppdev; for (i = 0; itemi.id; i++) { int err; @@ -544,28 +550,16 @@ } id = pa_sprintf_malloc("%s/%s", "_conflictingdevs", modifier_name); - n_confdev = snd_use_case_get_list(uc_mgr, id, &devices); + modifier->n_confdev = snd_use_case_get_list(uc_mgr, id, &modifier->conflicting_devices); pa_xfree(id); - - modifier->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - if (n_confdev <= 0) + if (modifier->n_confdev < 0) pa_log_debug("No %s for modifier %s", "_conflictingdevs", modifier_name); - else { - ucm_add_devices_to_idxset(modifier->conflicting_devices, NULL, verb->devices, devices, n_confdev); - snd_use_case_free_list(devices, n_confdev); - } id = pa_sprintf_malloc("%s/%s", "_supporteddevs", modifier_name); - n_suppdev = snd_use_case_get_list(uc_mgr, id, &devices); + modifier->n_suppdev = snd_use_case_get_list(uc_mgr, id, &modifier->supported_devices); pa_xfree(id); - - modifier->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - if (n_suppdev <= 0) + if (modifier->n_suppdev < 0) pa_log_debug("No %s for modifier %s", "_supporteddevs", modifier_name); - else { - ucm_add_devices_to_idxset(modifier->supported_devices, NULL, verb->devices, devices, n_suppdev); - snd_use_case_free_list(devices, n_suppdev); - } return 0; }; @@ -602,59 +596,6 @@ return 0; }; -static long ucm_device_status(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *dev) { - const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); - char *devstatus; - long status = 0; - - devstatus = pa_sprintf_malloc("_devstatus/%s", dev_name); - if (snd_use_case_geti(ucm->ucm_mgr, devstatus, &status) < 0) { - pa_log_debug("Failed to get status for UCM device %s", dev_name); - status = -1; - } - pa_xfree(devstatus); - - return status; -} - -static int ucm_device_disable(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *dev) { - const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); - - /* If any of dev's conflicting devices is enabled, trying to disable - * dev gives an error despite the fact that it's already disabled. - * Check that dev is enabled to avoid this error. */ - if (ucm_device_status(ucm, dev) == 0) { - pa_log_debug("UCM device %s is already disabled", dev_name); - return 0; - } - - pa_log_debug("Disabling UCM device %s", dev_name); - if (snd_use_case_set(ucm->ucm_mgr, "_disdev", dev_name) < 0) { - pa_log("Failed to disable UCM device %s", dev_name); - return -1; - } - - return 0; -} - -static int ucm_device_enable(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *dev) { - const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); - - /* We don't need to enable devices that are already enabled */ - if (ucm_device_status(ucm, dev) > 0) { - pa_log_debug("UCM device %s is already enabled", dev_name); - return 0; - } - - pa_log_debug("Enabling UCM device %s", dev_name); - if (snd_use_case_set(ucm->ucm_mgr, "_enadev", dev_name) < 0) { - pa_log("Failed to enable UCM device %s", dev_name); - return -1; - } - - return 0; -} - static int ucm_get_modifiers(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) { const char **mod_list; int num_mod, i; @@ -685,57 +626,6 @@ return 0; }; -static long ucm_modifier_status(pa_alsa_ucm_config *ucm, pa_alsa_ucm_modifier *mod) { - const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME); - char *modstatus; - long status = 0; - - modstatus = pa_sprintf_malloc("_modstatus/%s", mod_name); - if (snd_use_case_geti(ucm->ucm_mgr, modstatus, &status) < 0) { - pa_log_debug("Failed to get status for UCM modifier %s", mod_name); - status = -1; - } - pa_xfree(modstatus); - - return status; -} - -static int ucm_modifier_disable(pa_alsa_ucm_config *ucm, pa_alsa_ucm_modifier *mod) { - const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME); - - /* We don't need to disable modifiers that are already disabled */ - if (ucm_modifier_status(ucm, mod) == 0) { - pa_log_debug("UCM modifier %s is already disabled", mod_name); - return 0; - } - - pa_log_debug("Disabling UCM modifier %s", mod_name); - if (snd_use_case_set(ucm->ucm_mgr, "_dismod", mod_name) < 0) { - pa_log("Failed to disable UCM modifier %s", mod_name); - return -1; - } - - return 0; -} - -static int ucm_modifier_enable(pa_alsa_ucm_config *ucm, pa_alsa_ucm_modifier *mod) { - const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME); - - /* We don't need to enable modifiers that are already enabled */ - if (ucm_modifier_status(ucm, mod) > 0) { - pa_log_debug("UCM modifier %s is already enabled", mod_name); - return 0; - } - - pa_log_debug("Enabling UCM modifier %s", mod_name); - if (snd_use_case_set(ucm->ucm_mgr, "_enamod", mod_name) < 0) { - pa_log("Failed to enable UCM modifier %s", mod_name); - return -1; - } - - return 0; -} - static void add_role_to_device(pa_alsa_ucm_device *dev, const char *dev_name, const char *role_name, const char *role) { const char *cur = pa_proplist_gets(dev->proplist, role_name); @@ -752,19 +642,27 @@ role_name)); } -static void add_media_role(pa_alsa_ucm_device *dev, const char *role_name, const char *role, bool is_sink) { - const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); - const char *sink = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SINK); - const char *source = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SOURCE); +static void add_media_role(const char *name, pa_alsa_ucm_device *list, const char *role_name, const char *role, bool is_sink) { + pa_alsa_ucm_device *d; + + PA_LLIST_FOREACH(d, list) { + const char *dev_name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME); + + if (pa_streq(dev_name, name)) { + const char *sink = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SINK); + const char *source = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SOURCE); - if (is_sink && sink) - add_role_to_device(dev, dev_name, role_name, role); - else if (!is_sink && source) - add_role_to_device(dev, dev_name, role_name, role); + if (is_sink && sink) + add_role_to_device(d, dev_name, role_name, role); + else if (!is_sink && source) + add_role_to_device(d, dev_name, role_name, role); + break; + } + } } static char *modifier_name_to_role(const char *mod_name, bool *is_sink) { - char *sub = NULL, *tmp, *pos; + char *sub = NULL, *tmp; *is_sink = false; @@ -774,32 +672,26 @@ } else if (pa_startswith(mod_name, "Capture")) sub = pa_xstrdup(mod_name + 7); - pos = sub; - while (pos && *pos == ' ') pos++; - - if (!pos || !*pos) { + if (!sub || !*sub) { pa_xfree(sub); pa_log_warn("Can't match media roles for modifier %s", mod_name); return NULL; } - tmp = pos; + tmp = sub; do { *tmp = tolower(*tmp); } while (*(++tmp)); - tmp = pa_xstrdup(pos); - pa_xfree(sub); - return tmp; + return sub; } -static void ucm_set_media_roles(pa_alsa_ucm_modifier *modifier, const char *mod_name) { - pa_alsa_ucm_device *dev; +static void ucm_set_media_roles(pa_alsa_ucm_modifier *modifier, pa_alsa_ucm_device *list, const char *mod_name) { + int i; bool is_sink = false; char *sub = NULL; const char *role_name; - uint32_t idx; sub = modifier_name_to_role(mod_name, &is_sink); if (!sub) @@ -809,11 +701,11 @@ modifier->media_role = sub; role_name = is_sink ? PA_ALSA_PROP_UCM_PLAYBACK_ROLES : PA_ALSA_PROP_UCM_CAPTURE_ROLES; - PA_IDXSET_FOREACH(dev, modifier->supported_devices, idx) { + for (i = 0; i < modifier->n_suppdev; i++) { /* if modifier has no specific pcm, we add role intent to its supported devices */ if (!pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_SINK) && !pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_SOURCE)) - add_media_role(dev, role_name, sub, is_sink); + add_media_role(modifier->supported_devicesi, list, role_name, sub, is_sink); } } @@ -821,17 +713,29 @@ uint32_t idx; pa_alsa_ucm_device *d; - PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx) - if (pa_idxset_put(d->conflicting_devices, dev, NULL) == 0) - pa_log_warn("Add lost conflicting device %s to %s", - pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME), - pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME)); + if (dev->conflicting_devices) { + PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx) { + if (!d->conflicting_devices) + d->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + + if (pa_idxset_put(d->conflicting_devices, dev, NULL) == 0) + pa_log_warn("Add lost conflicting device %s to %s", + pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME), + pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME)); + } + } + + if (dev->supported_devices) { + PA_IDXSET_FOREACH(d, dev->supported_devices, idx) { + if (!d->supported_devices) + d->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - PA_IDXSET_FOREACH(d, dev->supported_devices, idx) - if (pa_idxset_put(d->supported_devices, dev, NULL) == 0) - pa_log_warn("Add lost supported device %s to %s", - pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME), - pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME)); + if (pa_idxset_put(d->supported_devices, dev, NULL) == 0) + pa_log_warn("Add lost supported device %s to %s", + pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME), + pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME)); + } + } } int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index) { @@ -883,7 +787,7 @@ free((void *)value); } - /* get a list of all UCM verbs for this card */ + /* get a list of all UCM verbs (profiles) for this card */ num_verbs = snd_use_case_verb_list(ucm->ucm_mgr, &verb_list); if (num_verbs < 0) { pa_log("UCM verb list not found for %s", card_name); @@ -972,11 +876,11 @@ const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME); /* Modifier properties */ - ucm_get_modifier_property(mod, uc_mgr, verb, mod_name); + ucm_get_modifier_property(mod, uc_mgr, mod_name); /* Set PA_PROP_DEVICE_INTENDED_ROLES property to devices */ pa_log_debug("Set media roles for verb %s, modifier %s", verb_name, mod_name); - ucm_set_media_roles(mod, mod_name); + ucm_set_media_roles(mod, verb->devices, mod_name); } *p_verb = verb; @@ -995,27 +899,43 @@ pa_device_port *port; pa_alsa_ucm_port_data *data; pa_alsa_ucm_device *dev; + const char *eld_mixer_device_name; void *state; + int idx, eld_device; PA_HASHMAP_FOREACH(port, hash, state) { data = PA_DEVICE_PORT_DATA(port); - dev = data->device; - data->eld_device = dev->eld_device; - pa_xfree(data->eld_mixer_device_name); - data->eld_mixer_device_name = pa_xstrdup(dev->eld_mixer_device_name); + eld_mixer_device_name = NULL; + eld_device = -1; + PA_DYNARRAY_FOREACH(dev, data->devices, idx) { + if (dev->eld_device >= 0 && dev->eld_mixer_device_name) { + if (eld_device >= 0 && eld_device != dev->eld_device) { + pa_log_error("The ELD device is already set!"); + } else if (eld_mixer_device_name && pa_streq(dev->eld_mixer_device_name, eld_mixer_device_name)) { + pa_log_error("The ELD mixer device is already set (%s, %s)!", dev->eld_mixer_device_name, dev->eld_mixer_device_name); + } else { + eld_mixer_device_name = dev->eld_mixer_device_name; + eld_device = dev->eld_device; + } + } + } + data->eld_device = eld_device; + if (data->eld_mixer_device_name) + pa_xfree(data->eld_mixer_device_name); + data->eld_mixer_device_name = pa_xstrdup(eld_mixer_device_name); } } -static void update_mixer_paths(pa_hashmap *ports, const char *verb_name) { +static void update_mixer_paths(pa_hashmap *ports, const char *profile) { pa_device_port *port; pa_alsa_ucm_port_data *data; void *state; /* select volume controls on ports */ PA_HASHMAP_FOREACH(port, ports, state) { - pa_log_info("Updating mixer path for %s: %s", verb_name, port->name); + pa_log_info("Updating mixer path for %s: %s", profile, port->name); data = PA_DEVICE_PORT_DATA(port); - data->path = pa_hashmap_get(data->paths, verb_name); + data->path = pa_hashmap_get(data->paths, profile); } } @@ -1025,29 +945,39 @@ pa_alsa_ucm_port_data *data; pa_alsa_ucm_device *dev; snd_mixer_t *mixer_handle; - const char *verb_name, *mdev; + const char *profile, *mdev, *mdev2; void *state, *state2; + int idx; PA_HASHMAP_FOREACH(port, hash, state) { data = PA_DEVICE_PORT_DATA(port); - dev = data->device; - mdev = get_mixer_device(dev, is_sink); + mdev = NULL; + PA_DYNARRAY_FOREACH(dev, data->devices, idx) { + mdev2 = get_mixer_device(dev, is_sink); + if (mdev && mdev2 && !pa_streq(mdev, mdev2)) { + pa_log_error("Two mixer device names found ('%s', '%s'), using s/w volume", mdev, mdev2); + goto fail; + } + if (mdev2) + mdev = mdev2; + } + if (mdev == NULL || !(mixer_handle = pa_alsa_open_mixer_by_name(mixers, mdev, true))) { pa_log_error("Failed to find a working mixer device (%s).", mdev); goto fail; } - PA_HASHMAP_FOREACH_KV(verb_name, path, data->paths, state2) { + PA_HASHMAP_FOREACH_KV(profile, path, data->paths, state2) { if (pa_alsa_path_probe(path, NULL, mixer_handle, ignore_dB) < 0) { pa_log_warn("Could not probe path: %s, using s/w volume", path->name); - pa_hashmap_remove(data->paths, verb_name); + pa_hashmap_remove(data->paths, profile); } else if (!path->has_volume && !path->has_mute) { pa_log_warn("Path %s is not a volume or mute control", path->name); - pa_hashmap_remove(data->paths, verb_name); + pa_hashmap_remove(data->paths, profile); } else pa_log_debug("Set up h/w %s using '%s' for %s:%s", path->has_volume ? "volume" : "mute", - path->name, verb_name, port->name); + path->name, profile, port->name); } } @@ -1061,141 +991,91 @@ } } -static char *devset_name(pa_idxset *devices, const char *sep) { - int i = 0; - int num = pa_idxset_size(devices); - pa_alsa_ucm_device *sortednum, *dev; - char *dev_names = NULL; - char *tmp = NULL; - uint32_t idx; - - PA_IDXSET_FOREACH(dev, devices, idx) { - sortedi = dev; - i++; - } - - /* Sort by alphabetical order so as to have a deterministic naming scheme */ - qsort(&sorted0, num, sizeof(pa_alsa_ucm_device *), pa_alsa_ucm_device_cmp); - - for (i = 0; i < num; i++) { - dev = sortedi; - const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); - - if (!dev_names) { - dev_names = pa_xstrdup(dev_name); - } else { - tmp = pa_sprintf_malloc("%s%s%s", dev_names, sep, dev_name); - pa_xfree(dev_names); - dev_names = tmp; - } - } - - return dev_names; -} - -PA_UNUSED static char *devset_description(pa_idxset *devices, const char *sep) { - int i = 0; - int num = pa_idxset_size(devices); - pa_alsa_ucm_device *sortednum, *dev; - char *dev_descs = NULL; - char *tmp = NULL; - uint32_t idx; - - PA_IDXSET_FOREACH(dev, devices, idx) { - sortedi = dev; - i++; - } - - /* Sort by alphabetical order to match devset_name() */ - qsort(&sorted0, num, sizeof(pa_alsa_ucm_device *), pa_alsa_ucm_device_cmp); - - for (i = 0; i < num; i++) { - dev = sortedi; - const char *dev_desc = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_DESCRIPTION); - - if (!dev_descs) { - dev_descs = pa_xstrdup(dev_desc); - } else { - tmp = pa_sprintf_malloc("%s%s%s", dev_descs, sep, dev_desc); - pa_xfree(dev_descs); - dev_descs = tmp; - } - } - - return dev_descs; -} - -/* If invert is true, uses the formula 1/p = 1/p1 + 1/p2 + ... 1/pn. - * This way, the result will always be less than the individual components, - * yet higher components will lead to higher result. */ -static unsigned devset_playback_priority(pa_idxset *devices, bool invert) { - pa_alsa_ucm_device *dev; - uint32_t idx; - double priority = 0; - - PA_IDXSET_FOREACH(dev, devices, idx) { - if (dev->playback_priority > 0 && invert) - priority += 1.0 / dev->playback_priority; - else - priority += dev->playback_priority; - } - - if (priority > 0 && invert) - return 1.0 / priority; - - return (unsigned) priority; -} - -static unsigned devset_capture_priority(pa_idxset *devices, bool invert) { - pa_alsa_ucm_device *dev; - uint32_t idx; - double priority = 0; - - PA_IDXSET_FOREACH(dev, devices, idx) { - if (dev->capture_priority > 0 && invert) - priority += 1.0 / dev->capture_priority; - else - priority += dev->capture_priority; - } - - if (priority > 0 && invert) - return 1.0 / priority; - - return (unsigned) priority; -} - -void pa_alsa_ucm_add_port( +static void ucm_add_port_combination( pa_hashmap *hash, pa_alsa_ucm_mapping_context *context, bool is_sink, + pa_alsa_ucm_device **pdevices, + int num, pa_hashmap *ports, pa_card_profile *cp, pa_core *core) { pa_device_port *port; + int i; unsigned priority; + double prio2; char *name, *desc; const char *dev_name; const char *direction; - const char *verb_name; - pa_alsa_ucm_device *dev; + const char *profile; + pa_alsa_ucm_device *sortednum, *dev; pa_alsa_ucm_port_data *data; pa_alsa_ucm_volume *vol; - pa_alsa_jack *jack; - pa_device_port_type_t type; + pa_alsa_jack *jack, *jack2; + pa_device_port_type_t type, type2; void *state; - dev = context->ucm_device; - if (!dev) - return; + for (i = 0; i < num; i++) + sortedi = pdevicesi; + + /* Sort by alphabetical order so as to have a deterministic naming scheme + * for combination ports */ + qsort(&sorted0, num, sizeof(pa_alsa_ucm_device *), pa_alsa_ucm_device_cmp); + dev = sorted0; dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); + name = pa_sprintf_malloc("%s%s", is_sink ? PA_UCM_PRE_TAG_OUTPUT : PA_UCM_PRE_TAG_INPUT, dev_name); - desc = pa_xstrdup(pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_DESCRIPTION)); + desc = num == 1 ? pa_xstrdup(pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_DESCRIPTION)) + : pa_sprintf_malloc("Combination port for %s", dev_name); + priority = is_sink ? dev->playback_priority : dev->capture_priority; + prio2 = (priority == 0 ? 0 : 1.0/priority); jack = ucm_get_jack(context->ucm, dev); type = dev->type; + for (i = 1; i < num; i++) { + char *tmp; + + dev = sortedi; + dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); + + tmp = pa_sprintf_malloc("%s+%s", name, dev_name); + pa_xfree(name); + name = tmp; + + tmp = pa_sprintf_malloc("%s,%s", desc, dev_name); + pa_xfree(desc); + desc = tmp; + + priority = is_sink ? dev->playback_priority : dev->capture_priority; + if (priority != 0 && prio2 > 0) + prio2 += 1.0/priority; + + jack2 = ucm_get_jack(context->ucm, dev); + if (jack2) { + if (jack && jack != jack2) + pa_log_warn("Multiple jacks per combined device '%s': '%s' '%s'", name, jack->name, jack2->name); + jack = jack2; + } + + type2 = dev->type; + if (type2 != PA_DEVICE_PORT_TYPE_UNKNOWN) { + if (type != PA_DEVICE_PORT_TYPE_UNKNOWN && type != type2) + pa_log_warn("Multiple device types per combined device '%s': %d %d", name, type, type2); + type = type2; + } + } + + /* Make combination ports always have lower priority, and use the formula + 1/p = 1/p1 + 1/p2 + ... 1/pn. + This way, the result will always be less than the individual components, + yet higher components will lead to higher result. */ + + if (num > 1) + priority = prio2 > 0 ? 1.0/prio2 : 0; + port = pa_hashmap_get(ports, name); if (!port) { pa_device_port_new_data port_data; @@ -1212,32 +1092,37 @@ pa_device_port_new_data_done(&port_data); data = PA_DEVICE_PORT_DATA(port); - ucm_port_data_init(data, context->ucm, port, dev); + ucm_port_data_init(data, context->ucm, port, pdevices, num); port->impl_free = ucm_port_data_free; pa_hashmap_put(ports, port->name, port); pa_log_debug("Add port %s: %s", port->name, port->description); - PA_HASHMAP_FOREACH_KV(verb_name, vol, is_sink ? dev->playback_volumes : dev->capture_volumes, state) { - pa_alsa_path *path = pa_alsa_path_synthesize(vol->mixer_elem, - is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT); - - if (!path) - pa_log_warn("Failed to set up volume control: %s", vol->mixer_elem); - else { - if (vol->master_elem) { - pa_alsa_element *e = pa_alsa_element_get(path, vol->master_elem, false); - e->switch_use = PA_ALSA_SWITCH_MUTE; - e->volume_use = PA_ALSA_VOLUME_MERGE; - } + if (num == 1) { + /* To keep things simple and not worry about stacking controls, we only support hardware volumes on non-combination + * ports. */ + PA_HASHMAP_FOREACH_KV(profile, vol, is_sink ? dev->playback_volumes : dev->capture_volumes, state) { + pa_alsa_path *path = pa_alsa_path_synthesize(vol->mixer_elem, + is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT); + + if (!path) + pa_log_warn("Failed to set up volume control: %s", vol->mixer_elem); + else { + if (vol->master_elem) { + pa_alsa_element *e = pa_alsa_element_get(path, vol->master_elem, false); + e->switch_use = PA_ALSA_SWITCH_MUTE; + e->volume_use = PA_ALSA_VOLUME_MERGE; + } - pa_hashmap_put(data->paths, pa_xstrdup(verb_name), path); + pa_hashmap_put(data->paths, pa_xstrdup(profile), path); - /* Add path also to already created empty path set */ - if (is_sink) - pa_hashmap_put(dev->playback_mapping->output_path_set->paths, pa_xstrdup(vol->mixer_elem), path); - else - pa_hashmap_put(dev->capture_mapping->input_path_set->paths, pa_xstrdup(vol->mixer_elem), path); + /* Add path also to already created empty path set */ + dev = sorted0; + if (is_sink) + pa_hashmap_put(dev->playback_mapping->output_path_set->paths, pa_xstrdup(vol->mixer_elem), path); + else + pa_hashmap_put(dev->capture_mapping->input_path_set->paths, pa_xstrdup(vol->mixer_elem), path); + } } } } @@ -1258,127 +1143,113 @@ if (hash) { pa_hashmap_put(hash, port->name, port); } - - /* ELD devices */ - set_eld_devices(ports); } -static bool devset_supports_device(pa_idxset *devices, pa_alsa_ucm_device *dev) { - const char *sink, *sink2, *source, *source2; - pa_alsa_ucm_device *d; - uint32_t idx; - - pa_assert(devices); - pa_assert(dev); - - /* Can add anything to empty group */ - if (pa_idxset_isempty(devices)) - return true; - - /* Device already selected */ - if (pa_idxset_contains(devices, dev)) - return true; +static int ucm_port_contains(const char *port_name, const char *dev_name, bool is_sink) { + int ret = 0; + const char *r; + const char *state = NULL; + size_t len; - /* No conflicting device must already be selected */ - if (!pa_idxset_isdisjoint(devices, dev->conflicting_devices)) + if (!port_name || !dev_name) return false; - /* No already selected device must be unsupported */ - if (!pa_idxset_isempty(dev->supported_devices)) - if (!pa_idxset_issubset(devices, dev->supported_devices)) - return false; - - sink = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SINK); - source = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SOURCE); - - PA_IDXSET_FOREACH(d, devices, idx) { - /* Must not be unsupported by any selected device */ - if (!pa_idxset_isempty(d->supported_devices)) - if (!pa_idxset_contains(d->supported_devices, dev)) - return false; + port_name += is_sink ? strlen(PA_UCM_PRE_TAG_OUTPUT) : strlen(PA_UCM_PRE_TAG_INPUT); - /* PlaybackPCM must not be the same as any selected device */ - sink2 = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SINK); - if (sink && sink2 && pa_streq(sink, sink2)) - return false; - - /* CapturePCM must not be the same as any selected device */ - source2 = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SOURCE); - if (source && source2 && pa_streq(source, source2)) - return false; + while ((r = pa_split_in_place(port_name, "+", &len, &state))) { + if (strlen(dev_name) == len && !strncmp(r, dev_name, len)) { + ret = 1; + break; + } } - return true; + return ret; } -/* Iterates nonempty subsets of UCM devices that can be simultaneously - * used, including subsets of previously returned subsets. At start, - * *state should be NULL. It's not safe to modify the devices argument - * until iteration ends. The returned idxsets must be freed by the - * caller. */ -static pa_idxset *iterate_device_subsets(pa_idxset *devices, void **state) { - uint32_t idx; - pa_alsa_ucm_device *dev; +static int ucm_check_conformance( + pa_alsa_ucm_mapping_context *context, + pa_alsa_ucm_device **pdevices, + int dev_num, + pa_alsa_ucm_device *dev) { - pa_assert(devices); - pa_assert(state); + uint32_t idx; + pa_alsa_ucm_device *d; + int i; - if (*state == NULL) { - /* First iteration, start adding from first device */ - *state = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - dev = pa_idxset_first(devices, &idx); + pa_assert(dev); - } else { - /* Backtrack the most recent device we added and skip it */ - dev = pa_idxset_steal_last(*state, NULL); - pa_idxset_get_by_data(devices, dev, &idx); - if (dev) - dev = pa_idxset_next(devices, &idx); + pa_log_debug("Check device %s conformance with %d other devices", + pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME), dev_num); + if (dev_num == 0) { + pa_log_debug("First device in combination, number 1"); + return 1; } - /* Try adding devices we haven't decided on yet */ - for (; dev; dev = pa_idxset_next(devices, &idx)) { - if (devset_supports_device(*state, dev)) - pa_idxset_put(*state, dev, NULL); + if (dev->conflicting_devices) { /* the device defines conflicting devices */ + PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx) { + for (i = 0; i < dev_num; i++) { + if (pdevicesi == d) { + pa_log_debug("Conflicting device found"); + return 0; + } + } + } + } else if (dev->supported_devices) { /* the device defines supported devices */ + for (i = 0; i < dev_num; i++) { + if (!ucm_device_exists(dev->supported_devices, pdevicesi)) { + pa_log_debug("Supported device not found"); + return 0; + } + } + } else { /* not support any other devices */ + pa_log_debug("Not support any other devices"); + return 0; } - if (pa_idxset_isempty(*state)) { - /* No more choices to backtrack on, therefore no more subsets to - * return after this. Don't return the empty set, instead clean - * up and end iteration. */ - pa_idxset_free(*state, NULL); - *state = NULL; - return NULL; - } + pa_log_debug("Device added to combination, number %d", dev_num + 1); + return 1; +} + +static inline pa_alsa_ucm_device *get_next_device(pa_idxset *idxset, uint32_t *idx) { + pa_alsa_ucm_device *dev; + + if (*idx == PA_IDXSET_INVALID) + dev = pa_idxset_first(idxset, idx); + else + dev = pa_idxset_next(idxset, idx); - return pa_idxset_copy(*state, NULL); + return dev; } -/* This a wrapper around iterate_device_subsets() that only returns the - * biggest possible groups and not any of their subsets. */ -static pa_idxset *iterate_maximal_device_subsets(pa_idxset *devices, void **state) { - uint32_t idx; +static void ucm_add_ports_combination( + pa_hashmap *hash, + pa_alsa_ucm_mapping_context *context, + bool is_sink, + pa_alsa_ucm_device **pdevices, + int dev_num, + uint32_t map_index, + pa_hashmap *ports, + pa_card_profile *cp, + pa_core *core) { + pa_alsa_ucm_device *dev; - pa_idxset *subset; + uint32_t idx = map_index; - pa_assert(devices); - pa_assert(state); - - subset = iterate_device_subsets(devices, state); - if (!subset) - return subset; - - /* Skip this group if it's incomplete, by checking if we can add any - * other device. If we can, this iteration is a subset of another - * group that we already returned or eventually return. */ - PA_IDXSET_FOREACH(dev, devices, idx) { - if (!pa_idxset_contains(subset, dev) && devset_supports_device(subset, dev)) { - pa_idxset_free(subset, NULL); - return iterate_maximal_device_subsets(devices, state); - } + if ((dev = get_next_device(context->ucm_devices, &idx)) == NULL) + return; + + /* check if device at map_index can combine with existing devices combination */ + if (ucm_check_conformance(context, pdevices, dev_num, dev)) { + /* add device at map_index to devices combination */ + pdevicesdev_num = dev; + /* add current devices combination as a new port */ + ucm_add_port_combination(hash, context, is_sink, pdevices, dev_num + 1, ports, cp, core); + /* try more elements combination */ + ucm_add_ports_combination(hash, context, is_sink, pdevices, dev_num + 1, idx, ports, cp, core); } - return subset; + /* try other device with current elements number */ + ucm_add_ports_combination(hash, context, is_sink, pdevices, dev_num, idx, ports, cp, core); } static char* merge_roles(const char *cur, const char *add) { @@ -1410,6 +1281,28 @@ return ret; } +void pa_alsa_ucm_add_ports_combination( + pa_hashmap *p, + pa_alsa_ucm_mapping_context *context, + bool is_sink, + pa_hashmap *ports, + pa_card_profile *cp, + pa_core *core) { + + pa_alsa_ucm_device **pdevices; + + pa_assert(context->ucm_devices); + + if (pa_idxset_size(context->ucm_devices) > 0) { + pdevices = pa_xnew(pa_alsa_ucm_device *, pa_idxset_size(context->ucm_devices)); + ucm_add_ports_combination(p, context, is_sink, pdevices, 0, PA_IDXSET_INVALID, ports, cp, core); + pa_xfree(pdevices); + } + + /* ELD devices */ + set_eld_devices(ports); +} + void pa_alsa_ucm_add_ports( pa_hashmap **p, pa_proplist *proplist, @@ -1419,6 +1312,7 @@ snd_pcm_t *pcm_handle, bool ignore_dB) { + uint32_t idx; char *merged_roles; const char *role_name = is_sink ? PA_ALSA_PROP_UCM_PLAYBACK_ROLES : PA_ALSA_PROP_UCM_CAPTURE_ROLES; pa_alsa_ucm_device *dev; @@ -1429,39 +1323,34 @@ pa_assert(*p); /* add ports first */ - pa_alsa_ucm_add_port(*p, context, is_sink, card->ports, NULL, card->core); + pa_alsa_ucm_add_ports_combination(*p, context, is_sink, card->ports, NULL, card->core); /* now set up volume paths if any */ probe_volumes(*p, is_sink, pcm_handle, context->ucm->mixers, ignore_dB); - /* probe_volumes() removes per-verb paths from ports if probing them - * fails. The path for the current verb is cached in + /* probe_volumes() removes per-profile paths from ports if probing them + * fails. The path for the current profile is cached in * pa_alsa_ucm_port_data.path, which is not cleared by probe_volumes() if * the path gets removed, so we have to call update_mixer_paths() here to * unset the cached path if needed. */ - if (context->ucm->active_verb) { - const char *verb_name; - verb_name = pa_proplist_gets(context->ucm->active_verb->proplist, PA_ALSA_PROP_UCM_NAME); - update_mixer_paths(*p, verb_name); - } + if (card->card.active_profile_index < card->card.n_profiles) + update_mixer_paths(*p, card->card.profilescard->card.active_profile_index->name); /* then set property PA_PROP_DEVICE_INTENDED_ROLES */ merged_roles = pa_xstrdup(pa_proplist_gets(proplist, PA_PROP_DEVICE_INTENDED_ROLES)); - - dev = context->ucm_device; - if (dev) { + PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) { const char *roles = pa_proplist_gets(dev->proplist, role_name); tmp = merge_roles(merged_roles, roles); pa_xfree(merged_roles); merged_roles = tmp; } - mod = context->ucm_modifier; - if (mod) { - tmp = merge_roles(merged_roles, mod->media_role); - pa_xfree(merged_roles); - merged_roles = tmp; - } + if (context->ucm_modifiers) + PA_IDXSET_FOREACH(mod, context->ucm_modifiers, idx) { + tmp = merge_roles(merged_roles, mod->media_role); + pa_xfree(merged_roles); + merged_roles = tmp; + } if (merged_roles) pa_proplist_sets(proplist, PA_PROP_DEVICE_INTENDED_ROLES, merged_roles); @@ -1471,81 +1360,85 @@ } /* Change UCM verb and device to match selected card profile */ -int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, pa_alsa_profile *new_profile, pa_alsa_profile *old_profile) { +int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, const char *new_profile, const char *old_profile) { int ret = 0; - const char *verb_name, *profile_name; + const char *profile; pa_alsa_ucm_verb *verb; - pa_alsa_mapping *map; - uint32_t idx; if (new_profile == old_profile) - return 0; + return ret; + else if (new_profile == NULL || old_profile == NULL) + profile = new_profile ? new_profile : SND_USE_CASE_VERB_INACTIVE; + else if (!pa_streq(new_profile, old_profile)) + profile = new_profile; + else + return ret; - if (new_profile == NULL) { - verb = NULL; - profile_name = SND_USE_CASE_VERB_INACTIVE; - verb_name = SND_USE_CASE_VERB_INACTIVE; - } else { - verb = new_profile->ucm_context.verb; - profile_name = new_profile->name; - verb_name = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME); + /* change verb */ + pa_log_info("Set UCM verb to %s", profile); + if ((ret = snd_use_case_set(ucm->ucm_mgr, "_verb", profile)) < 0) { + pa_log("Failed to set verb %s: %s", profile, snd_strerror(ret)); } - pa_log_info("Set profile to %s", profile_name); - if (ucm->active_verb != verb) { - /* change verb */ - pa_log_info("Set UCM verb to %s", verb_name); - if ((snd_use_case_set(ucm->ucm_mgr, "_verb", verb_name)) < 0) { - pa_log("Failed to set verb %s", verb_name); - ret = -1; + /* find active verb */ + ucm->active_verb = NULL; + PA_LLIST_FOREACH(verb, ucm->verbs) { + const char *verb_name; + verb_name = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME); + if (pa_streq(verb_name, profile)) { + ucm->active_verb = verb; + break; } - - } else if (ucm->active_verb) { - /* Disable modifiers not in new profile. Has to be done before - * devices, because _dismod fails if a modifier's supported - * devices are disabled. */ - PA_IDXSET_FOREACH(map, old_profile->input_mappings, idx) - if (new_profile && !pa_idxset_contains(new_profile->input_mappings, map)) - if (map->ucm_context.ucm_modifier && ucm_modifier_disable(ucm, map->ucm_context.ucm_modifier) < 0) - ret = -1; - - PA_IDXSET_FOREACH(map, old_profile->output_mappings, idx) - if (new_profile && !pa_idxset_contains(new_profile->output_mappings, map)) - if (map->ucm_context.ucm_modifier && ucm_modifier_disable(ucm, map->ucm_context.ucm_modifier) < 0) - ret = -1; - - /* Disable devices not in new profile */ - PA_IDXSET_FOREACH(map, old_profile->input_mappings, idx) - if (new_profile && !pa_idxset_contains(new_profile->input_mappings, map)) - if (map->ucm_context.ucm_device && ucm_device_disable(ucm, map->ucm_context.ucm_device) < 0) - ret = -1; - - PA_IDXSET_FOREACH(map, old_profile->output_mappings, idx) - if (new_profile && !pa_idxset_contains(new_profile->output_mappings, map)) - if (map->ucm_context.ucm_device && ucm_device_disable(ucm, map->ucm_context.ucm_device) < 0) - ret = -1; } - ucm->active_verb = verb; - - update_mixer_paths(card->ports, verb_name); + update_mixer_paths(card->ports, profile); return ret; } -int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port) { +int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink) { + int i; + int ret = 0; pa_alsa_ucm_config *ucm; + const char **enable_devs; + int enable_num = 0; + uint32_t idx; pa_alsa_ucm_device *dev; - pa_alsa_ucm_port_data *data; pa_assert(context && context->ucm); ucm = context->ucm; pa_assert(ucm->ucm_mgr); - data = PA_DEVICE_PORT_DATA(port); - dev = data->device; + enable_devs = pa_xnew(const char *, pa_idxset_size(context->ucm_devices)); + + /* first disable then enable */ + PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) { + const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); + + if (ucm_port_contains(port->name, dev_name, is_sink)) + enable_devsenable_num++ = dev_name; + else { + pa_log_debug("Disable ucm device %s", dev_name); + if (snd_use_case_set(ucm->ucm_mgr, "_disdev", dev_name) > 0) { + pa_log("Failed to disable ucm device %s", dev_name); + ret = -1; + break; + } + } + } - return ucm_device_enable(ucm, dev); + for (i = 0; i < enable_num; i++) { + pa_log_debug("Enable ucm device %s", enable_devsi); + if (snd_use_case_set(ucm->ucm_mgr, "_enadev", enable_devsi) < 0) { + pa_log("Failed to enable ucm device %s", enable_devsi); + ret = -1; + break; + } + } + + pa_xfree(enable_devs); + + return ret; } static void ucm_add_mapping(pa_alsa_profile *p, pa_alsa_mapping *m) { @@ -1581,7 +1474,7 @@ const char *new_desc, *mdev; bool is_sink = m->direction == PA_ALSA_DIRECTION_OUTPUT; - m->ucm_context.ucm_device = device; + pa_idxset_put(m->ucm_context.ucm_devices, device, NULL); new_desc = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_DESCRIPTION); cur_desc = m->description; @@ -1610,7 +1503,7 @@ const char *new_desc, *mod_name, *channel_str; uint32_t channels = 0; - m->ucm_context.ucm_modifier = modifier; + pa_idxset_put(m->ucm_context.ucm_modifiers, modifier, NULL); new_desc = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_DESCRIPTION); cur_desc = m->description; @@ -1651,11 +1544,17 @@ pa_channel_map_init(&m->channel_map); } -static pa_alsa_mapping* ucm_alsa_mapping_get(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps, const char *verb_name, const char *ucm_name, bool is_sink) { +static pa_alsa_mapping* ucm_alsa_mapping_get(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps, const char *verb_name, const char *device_str, bool is_sink) { pa_alsa_mapping *m; char *mapping_name; + size_t ucm_alibpref_len = 0; - mapping_name = pa_sprintf_malloc("Mapping %s: %s: %s", verb_name, ucm_name, is_sink ? "sink" : "source"); + /* find private alsa-lib's configuration device prefix */ + + if (ucm->alib_prefix && pa_startswith(device_str, ucm->alib_prefix)) + ucm_alibpref_len = strlen(ucm->alib_prefix); + + mapping_name = pa_sprintf_malloc("Mapping %s: %s: %s", verb_name, device_str + ucm_alibpref_len, is_sink ? "sink" : "source"); m = pa_alsa_mapping_get(ps, mapping_name); @@ -1670,6 +1569,7 @@ static int ucm_create_mapping_direction( pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps, + pa_alsa_profile *p, pa_alsa_ucm_device *device, const char *verb_name, const char *device_name, @@ -1679,7 +1579,7 @@ pa_alsa_mapping *m; unsigned priority, rate, channels; - m = ucm_alsa_mapping_get(ucm, ps, verb_name, device_name, is_sink); + m = ucm_alsa_mapping_get(ucm, ps, verb_name, device_str, is_sink); if (!m) return -1; @@ -1690,7 +1590,8 @@ rate = is_sink ? device->playback_rate : device->capture_rate; channels = is_sink ? device->playback_channels : device->capture_channels; - if (!m->ucm_context.ucm_device) { /* new mapping */ + if (!m->ucm_context.ucm_devices) { /* new mapping */ + m->ucm_context.ucm_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); m->ucm_context.ucm = ucm; m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT; @@ -1698,6 +1599,7 @@ m->device_strings0 = pa_xstrdup(device_str); m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT; + ucm_add_mapping(p, m); if (rate) m->sample_spec.rate = rate; pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA); @@ -1719,6 +1621,7 @@ static int ucm_create_mapping_for_modifier( pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps, + pa_alsa_profile *p, pa_alsa_ucm_modifier *modifier, const char *verb_name, const char *mod_name, @@ -1727,14 +1630,16 @@ pa_alsa_mapping *m; - m = ucm_alsa_mapping_get(ucm, ps, verb_name, mod_name, is_sink); + m = ucm_alsa_mapping_get(ucm, ps, verb_name, device_str, is_sink); if (!m) return -1; pa_log_info("UCM mapping: %s modifier %s", m->name, mod_name); - if (!m->ucm_context.ucm_device && !m->ucm_context.ucm_modifier) { /* new mapping */ + if (!m->ucm_context.ucm_devices && !m->ucm_context.ucm_modifiers) { /* new mapping */ + m->ucm_context.ucm_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + m->ucm_context.ucm_modifiers = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); m->ucm_context.ucm = ucm; m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT; @@ -1743,7 +1648,10 @@ m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT; /* Modifier sinks should not be routed to by default */ m->priority = 0; - } + + ucm_add_mapping(p, m); + } else if (!m->ucm_context.ucm_modifiers) /* share pcm with device */ + m->ucm_context.ucm_modifiers = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); alsa_mapping_add_ucm_modifier(m, modifier); @@ -1753,6 +1661,7 @@ static int ucm_create_mapping( pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps, + pa_alsa_profile *p, pa_alsa_ucm_device *device, const char *verb_name, const char *device_name, @@ -1767,9 +1676,9 @@ } if (sink) - ret = ucm_create_mapping_direction(ucm, ps, device, verb_name, device_name, sink, true); + ret = ucm_create_mapping_direction(ucm, ps, p, device, verb_name, device_name, sink, true); if (ret == 0 && source) - ret = ucm_create_mapping_direction(ucm, ps, device, verb_name, device_name, source, false); + ret = ucm_create_mapping_direction(ucm, ps, p, device, verb_name, device_name, source, false); return ret; } @@ -1842,28 +1751,27 @@ pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps, pa_alsa_ucm_verb *verb, - pa_idxset *mappings, - const char *profile_name, - const char *profile_desc, - unsigned int profile_priority) { + const char *verb_name, + const char *verb_desc) { pa_alsa_profile *p; - pa_alsa_mapping *map; - uint32_t idx; + pa_alsa_ucm_device *dev; + pa_alsa_ucm_modifier *mod; + int i = 0; + const char *name, *sink, *source; + unsigned int priority; pa_assert(ps); - if (pa_hashmap_get(ps->profiles, profile_name)) { - pa_log("Profile %s already exists", profile_name); + if (pa_hashmap_get(ps->profiles, verb_name)) { + pa_log("Verb %s already exists", verb_name); return -1; } p = pa_xnew0(pa_alsa_profile, 1); p->profile_set = ps; - p->name = pa_xstrdup(profile_name); - p->description = pa_xstrdup(profile_desc); - p->priority = profile_priority; - p->ucm_context.verb = verb; + p->name = pa_xstrdup(verb_name); + p->description = pa_xstrdup(verb_desc); p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); @@ -1871,36 +1779,10 @@ p->supported = true; pa_hashmap_put(ps->profiles, p->name, p); - PA_IDXSET_FOREACH(map, mappings, idx) - ucm_add_mapping(p, map); - - pa_alsa_profile_dump(p); - - return 0; -} - -static int ucm_create_verb_profiles( - pa_alsa_ucm_config *ucm, - pa_alsa_profile_set *ps, - pa_alsa_ucm_verb *verb, - const char *verb_name, - const char *verb_desc) { - - pa_idxset *verb_devices, *p_devices, *p_mappings; - pa_alsa_ucm_device *dev; - pa_alsa_ucm_modifier *mod; - int i = 0; - int n_profiles = 0; - const char *name, *sink, *source; - char *p_name, *p_desc, *tmp; - unsigned int verb_priority, p_priority; - uint32_t idx; - void *state = NULL; - /* TODO: get profile priority from policy management */ - verb_priority = verb->priority; + priority = verb->priority; - if (verb_priority == 0) { + if (priority == 0) { char *verb_cmp, *c; c = verb_cmp = pa_xstrdup(verb_name); while (*c) { @@ -1909,13 +1791,15 @@ } for (i = 0; verb_infoi.id; i++) { if (strcasecmp(verb_infoi.id, verb_cmp) == 0) { - verb_priority = verb_infoi.priority; + priority = verb_infoi.priority; break; } } pa_xfree(verb_cmp); } + p->priority = priority; + PA_LLIST_FOREACH(dev, verb->devices) { pa_alsa_jack *jack; const char *jack_hw_mute; @@ -1925,7 +1809,7 @@ sink = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SINK); source = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SOURCE); - ucm_create_mapping(ucm, ps, dev, verb_name, name, sink, source); + ucm_create_mapping(ucm, ps, p, dev, verb_name, name, sink, source); jack = ucm_get_jack(ucm, dev); if (jack) @@ -1976,74 +1860,12 @@ source = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_SOURCE); if (sink) - ucm_create_mapping_for_modifier(ucm, ps, mod, verb_name, name, sink, true); + ucm_create_mapping_for_modifier(ucm, ps, p, mod, verb_name, name, sink, true); else if (source) - ucm_create_mapping_for_modifier(ucm, ps, mod, verb_name, name, source, false); + ucm_create_mapping_for_modifier(ucm, ps, p, mod, verb_name, name, source, false); } - verb_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - PA_LLIST_FOREACH(dev, verb->devices) - pa_idxset_put(verb_devices, dev, NULL); - - while ((p_devices = iterate_maximal_device_subsets(verb_devices, &state))) { - p_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - - /* Add the mappings that include our selected devices */ - PA_IDXSET_FOREACH(dev, p_devices, idx) { - if (dev->playback_mapping) - pa_idxset_put(p_mappings, dev->playback_mapping, NULL); - if (dev->capture_mapping) - pa_idxset_put(p_mappings, dev->capture_mapping, NULL); - } - - /* Add mappings only for the modifiers that can work with our - * device selection */ - PA_LLIST_FOREACH(mod, verb->modifiers) - if (pa_idxset_isempty(mod->supported_devices) || pa_idxset_issubset(mod->supported_devices, p_devices)) - if (pa_idxset_isdisjoint(mod->conflicting_devices, p_devices)) { - if (mod->playback_mapping) - pa_idxset_put(p_mappings, mod->playback_mapping, NULL); - if (mod->capture_mapping) - pa_idxset_put(p_mappings, mod->capture_mapping, NULL); - } - - /* If we'll have multiple profiles for this verb, their names - * must be unique. Use a list of chosen devices to disambiguate - * them. If the profile contains all devices of a verb, we'll - * generate only onle profile whose name should be the verb - * name. GUIs usually show the profile description instead of - * the name, add the device names to those as well. */ - tmp = devset_name(p_devices, ", "); - if (pa_idxset_equals(p_devices, verb_devices)) { - p_name = pa_xstrdup(verb_name); - p_desc = pa_xstrdup(verb_desc); - } else { - p_name = pa_sprintf_malloc("%s (%s)", verb_name, tmp); - p_desc = pa_sprintf_malloc("%s (%s)", verb_desc, tmp); - } - - /* Make sure profiles with higher-priority devices are - * prioritized. */ - p_priority = verb_priority + devset_playback_priority(p_devices, false) + devset_capture_priority(p_devices, false); - - if (ucm_create_profile(ucm, ps, verb, p_mappings, p_name, p_desc, p_priority) == 0) { - pa_log_debug("Created profile %s for UCM verb %s", p_name, verb_name); - n_profiles++; - } - - pa_xfree(tmp); - pa_xfree(p_name); - pa_xfree(p_desc); - pa_idxset_free(p_mappings, NULL); - pa_idxset_free(p_devices, NULL); - } - - pa_idxset_free(verb_devices, NULL); - - if (n_profiles == 0) { - pa_log("UCM verb %s created no profiles", verb_name); - return -1; - } + pa_alsa_profile_dump(p); return 0; } @@ -2052,6 +1874,7 @@ { pa_alsa_ucm_mapping_context *context = &m->ucm_context; pa_alsa_ucm_device *dev; + uint32_t idx; char *mdev, *alib_prefix; snd_pcm_info_t *info; int pcm_card, pcm_device; @@ -2067,12 +1890,13 @@ alib_prefix = context->ucm->alib_prefix; - dev = context->ucm_device; - mdev = pa_sprintf_malloc("%shw:%i", alib_prefix ? alib_prefix : "", pcm_card); - if (mdev == NULL) - return; - dev->eld_mixer_device_name = mdev; - dev->eld_device = pcm_device; + PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) { + mdev = pa_sprintf_malloc("%shw:%i", alib_prefix ? alib_prefix : "", pcm_card); + if (mdev == NULL) + continue; + dev->eld_mixer_device_name = mdev; + dev->eld_device = pcm_device; + } } static snd_pcm_t* mapping_open_pcm(pa_alsa_ucm_config *ucm, pa_alsa_mapping *m, int mode) { @@ -2094,7 +1918,7 @@ try_buffer_size = ucm->default_n_fragments * try_period_size; pcm = pa_alsa_open_by_device_string(m->device_strings0, NULL, &try_ss, - &try_map, mode, &try_period_size, &try_buffer_size, 0, NULL, NULL, NULL, NULL, exact_channels); + &try_map, mode, &try_period_size, &try_buffer_size, 0, NULL, NULL, exact_channels); if (pcm) { if (!exact_channels) @@ -2136,39 +1960,38 @@ snd_mixer_t *mixer_handle; pa_alsa_ucm_mapping_context *context = &m->ucm_context; pa_alsa_ucm_device *dev; - bool has_control; + uint32_t idx; - dev = context->ucm_device; - if (!dev->jack || !dev->jack->mixer_device_name) - return; + PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) { + bool has_control; - mixer_handle = pa_alsa_open_mixer_by_name(mixers, dev->jack->mixer_device_name, true); - if (!mixer_handle) { - pa_log_error("Unable to determine open mixer device '%s' for jack %s", dev->jack->mixer_device_name, dev->jack->name); - return; - } + if (!dev->jack || !dev->jack->mixer_device_name) + continue; + + mixer_handle = pa_alsa_open_mixer_by_name(mixers, dev->jack->mixer_device_name, true); + if (!mixer_handle) { + pa_log_error("Unable to determine open mixer device '%s' for jack %s", dev->jack->mixer_device_name, dev->jack->name); + continue; + } - has_control = pa_alsa_mixer_find_card(mixer_handle, &dev->jack->alsa_id, 0) != NULL; - pa_alsa_jack_set_has_control(dev->jack, has_control); - pa_log_info("UCM jack %s has_control=%d", dev->jack->name, dev->jack->has_control); + has_control = pa_alsa_mixer_find_card(mixer_handle, &dev->jack->alsa_id, 0) != NULL; + pa_alsa_jack_set_has_control(dev->jack, has_control); + pa_log_info("UCM jack %s has_control=%d", dev->jack->name, dev->jack->has_control); + } } static void ucm_probe_profile_set(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps) { void *state; pa_alsa_profile *p; pa_alsa_mapping *m; - const char *verb_name; uint32_t idx; PA_HASHMAP_FOREACH(p, ps->profiles, state) { - pa_log_info("Probing profile %s", p->name); - /* change verb */ - verb_name = pa_proplist_gets(p->ucm_context.verb->proplist, PA_ALSA_PROP_UCM_NAME); - pa_log_info("Set ucm verb to %s", verb_name); + pa_log_info("Set ucm verb to %s", p->name); - if ((snd_use_case_set(ucm->ucm_mgr, "_verb", verb_name)) < 0) { - pa_log("Failed to set verb %s", verb_name); + if ((snd_use_case_set(ucm->ucm_mgr, "_verb", p->name)) < 0) { + pa_log("Failed to set verb %s", p->name); p->supported = false; continue; } @@ -2238,7 +2061,7 @@ (pa_free_cb_t) pa_alsa_profile_free); ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); - /* create profiles for each verb */ + /* create a profile for each verb */ PA_LLIST_FOREACH(verb, ucm->verbs) { const char *verb_name; const char *verb_desc; @@ -2250,7 +2073,7 @@ continue; } - ucm_create_verb_profiles(ucm, ps, verb, verb_name, verb_desc); + ucm_create_profile(ucm, ps, verb, verb_name, verb_desc); } ucm_probe_profile_set(ucm, ps); @@ -2279,8 +2102,10 @@ pa_proplist_free(di->proplist); - pa_idxset_free(di->conflicting_devices, NULL); - pa_idxset_free(di->supported_devices, NULL); + if (di->conflicting_devices) + pa_idxset_free(di->conflicting_devices, NULL); + if (di->supported_devices) + pa_idxset_free(di->supported_devices, NULL); pa_xfree(di->eld_mixer_device_name); @@ -2290,8 +2115,10 @@ PA_LLIST_FOREACH_SAFE(mi, mn, verb->modifiers) { PA_LLIST_REMOVE(pa_alsa_ucm_modifier, verb->modifiers, mi); pa_proplist_free(mi->proplist); - pa_idxset_free(mi->conflicting_devices, NULL); - pa_idxset_free(mi->supported_devices, NULL); + if (mi->n_suppdev > 0) + snd_use_case_free_list(mi->supported_devices, mi->n_suppdev); + if (mi->n_confdev > 0) + snd_use_case_free_list(mi->conflicting_devices, mi->n_confdev); pa_xfree(mi->media_role); pa_xfree(mi); } @@ -2339,22 +2166,29 @@ void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) { pa_alsa_ucm_device *dev; pa_alsa_ucm_modifier *mod; + uint32_t idx; - dev = context->ucm_device; - if (dev) { + if (context->ucm_devices) { /* clear ucm device pointer to mapping */ - if (context->direction == PA_DIRECTION_OUTPUT) - dev->playback_mapping = NULL; - else - dev->capture_mapping = NULL; + PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) { + if (context->direction == PA_DIRECTION_OUTPUT) + dev->playback_mapping = NULL; + else + dev->capture_mapping = NULL; + } + + pa_idxset_free(context->ucm_devices, NULL); } - mod = context->ucm_modifier; - if (mod) { - if (context->direction == PA_DIRECTION_OUTPUT) - mod->playback_mapping = NULL; - else - mod->capture_mapping = NULL; + if (context->ucm_modifiers) { + PA_IDXSET_FOREACH(mod, context->ucm_modifiers, idx) { + if (context->direction == PA_DIRECTION_OUTPUT) + mod->playback_mapping = NULL; + else + mod->capture_mapping = NULL; + } + + pa_idxset_free(context->ucm_modifiers, NULL); } } @@ -2368,7 +2202,12 @@ PA_LLIST_FOREACH(mod, ucm->active_verb->modifiers) { if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) { if (mod->enabled_counter == 0) { - ucm_modifier_enable(ucm, mod); + const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME); + + pa_log_info("Enable ucm modifier %s", mod_name); + if (snd_use_case_set(ucm->ucm_mgr, "_enamod", mod_name) < 0) { + pa_log("Failed to enable ucm modifier %s", mod_name); + } } mod->enabled_counter++; @@ -2388,14 +2227,27 @@ if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) { mod->enabled_counter--; - if (mod->enabled_counter == 0) - ucm_modifier_disable(ucm, mod); + if (mod->enabled_counter == 0) { + const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME); + + pa_log_info("Disable ucm modifier %s", mod_name); + if (snd_use_case_set(ucm->ucm_mgr, "_dismod", mod_name) < 0) { + pa_log("Failed to disable ucm modifier %s", mod_name); + } + } break; } } } +static void device_add_ucm_port(pa_alsa_ucm_device *device, pa_alsa_ucm_port_data *port) { + pa_assert(device); + pa_assert(port); + + pa_dynarray_append(device->ucm_ports, port); +} + static void device_set_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack) { pa_assert(device); pa_assert(jack); @@ -2428,7 +2280,7 @@ device->available = available; PA_DYNARRAY_FOREACH(port, device->ucm_ports, idx) - pa_device_port_set_available(port->core_port, port->device->available); + ucm_port_update_available(port); } void pa_alsa_ucm_device_update_available(pa_alsa_ucm_device *device) { @@ -2452,21 +2304,26 @@ } static void ucm_port_data_init(pa_alsa_ucm_port_data *port, pa_alsa_ucm_config *ucm, pa_device_port *core_port, - pa_alsa_ucm_device *device) { + pa_alsa_ucm_device **devices, unsigned n_devices) { + unsigned i; + pa_assert(ucm); pa_assert(core_port); - pa_assert(device); + pa_assert(devices); port->ucm = ucm; port->core_port = core_port; + port->devices = pa_dynarray_new(NULL); port->eld_device = -1; - port->device = device; - pa_dynarray_append(device->ucm_ports, port); + for (i = 0; i < n_devices; i++) { + pa_dynarray_append(port->devices, devicesi); + device_add_ucm_port(devicesi, port); + } port->paths = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree, NULL); - pa_device_port_set_available(port->core_port, port->device->available); + ucm_port_update_available(port); } static void ucm_port_data_free(pa_device_port *port) { @@ -2476,12 +2333,34 @@ ucm_port = PA_DEVICE_PORT_DATA(port); + if (ucm_port->devices) + pa_dynarray_free(ucm_port->devices); + if (ucm_port->paths) pa_hashmap_free(ucm_port->paths); pa_xfree(ucm_port->eld_mixer_device_name); } +static void ucm_port_update_available(pa_alsa_ucm_port_data *port) { + pa_alsa_ucm_device *device; + unsigned idx; + pa_available_t available = PA_AVAILABLE_YES; + + pa_assert(port); + + PA_DYNARRAY_FOREACH(device, port->devices, idx) { + if (device->available == PA_AVAILABLE_UNKNOWN) + available = PA_AVAILABLE_UNKNOWN; + else if (device->available == PA_AVAILABLE_NO) { + available = PA_AVAILABLE_NO; + break; + } + } + + pa_device_port_set_available(port->core_port, available); +} + #else /* HAVE_ALSA_UCM */ /* Dummy functions for systems without UCM support */ @@ -2495,7 +2374,7 @@ return NULL; } -int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, pa_alsa_profile *new_profile, pa_alsa_profile *old_profile) { +int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, const char *new_profile, const char *old_profile) { return -1; } @@ -2513,7 +2392,7 @@ bool ignore_dB) { } -void pa_alsa_ucm_add_port( +void pa_alsa_ucm_add_ports_combination( pa_hashmap *hash, pa_alsa_ucm_mapping_context *context, bool is_sink, @@ -2522,7 +2401,7 @@ pa_core *core) { } -int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port) { +int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink) { return -1; }
View file
pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/alsa-ucm.h -> pipewire-0.3.69.tar.gz/spa/plugins/alsa/acp/alsa-ucm.h
Changed
@@ -142,13 +142,12 @@ typedef struct pa_alsa_ucm_device pa_alsa_ucm_device; typedef struct pa_alsa_ucm_config pa_alsa_ucm_config; typedef struct pa_alsa_ucm_mapping_context pa_alsa_ucm_mapping_context; -typedef struct pa_alsa_ucm_profile_context pa_alsa_ucm_profile_context; typedef struct pa_alsa_ucm_port_data pa_alsa_ucm_port_data; typedef struct pa_alsa_ucm_volume pa_alsa_ucm_volume; int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index); pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map); -int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, pa_alsa_profile *new_profile, pa_alsa_profile *old_profile); +int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, const char *new_profile, const char *old_profile); int pa_alsa_ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const char *verb_name, const char *verb_desc, pa_alsa_ucm_verb **p_verb); @@ -160,14 +159,14 @@ pa_card *card, snd_pcm_t *pcm_handle, bool ignore_dB); -void pa_alsa_ucm_add_port( +void pa_alsa_ucm_add_ports_combination( pa_hashmap *hash, pa_alsa_ucm_mapping_context *context, bool is_sink, pa_hashmap *ports, pa_card_profile *cp, pa_core *core); -int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port); +int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink); void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm); void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context); @@ -224,8 +223,11 @@ pa_proplist *proplist; - pa_idxset *conflicting_devices; - pa_idxset *supported_devices; + int n_confdev; + int n_suppdev; + + const char **conflicting_devices; + const char **supported_devices; pa_direction_t action_direction; @@ -268,23 +270,21 @@ pa_alsa_ucm_config *ucm; pa_direction_t direction; - pa_alsa_ucm_device *ucm_device; - pa_alsa_ucm_modifier *ucm_modifier; -}; - -struct pa_alsa_ucm_profile_context { - pa_alsa_ucm_verb *verb; + pa_idxset *ucm_devices; + pa_idxset *ucm_modifiers; }; struct pa_alsa_ucm_port_data { pa_alsa_ucm_config *ucm; pa_device_port *core_port; - pa_alsa_ucm_device *device; + /* A single port will be associated with multiple devices if it represents + * a combination of devices. */ + pa_dynarray *devices; /* pa_alsa_ucm_device */ - /* verb name -> pa_alsa_path for volume control */ + /* profile name -> pa_alsa_path for volume control */ pa_hashmap *paths; - /* Current path, set when activating verb */ + /* Current path, set when activating profile */ pa_alsa_path *path; /* ELD info */
View file
pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/alsa-util.c -> pipewire-0.3.69.tar.gz/spa/plugins/alsa/acp/alsa-util.c
Changed
@@ -505,8 +505,6 @@ snd_pcm_uframes_t tsched_size, bool *use_mmap, bool *use_tsched, - pa_sample_format_t **query_supported_formats, - unsigned int **query_supported_rates, pa_alsa_profile_set *ps, pa_alsa_mapping **mapping) { @@ -545,8 +543,6 @@ tsched_size, use_mmap, use_tsched, - query_supported_formats, - query_supported_rates, m); if (pcm_handle) { @@ -574,8 +570,6 @@ tsched_size, use_mmap, use_tsched, - query_supported_formats, - query_supported_rates, m); if (pcm_handle) { @@ -600,8 +594,6 @@ tsched_size, use_mmap, use_tsched, - query_supported_formats, - query_supported_rates, false); pa_xfree(d); @@ -623,8 +615,6 @@ snd_pcm_uframes_t tsched_size, bool *use_mmap, bool *use_tsched, - pa_sample_format_t **query_supported_formats, - unsigned int **query_supported_rates, pa_alsa_mapping *m) { snd_pcm_t *pcm_handle; @@ -654,8 +644,6 @@ tsched_size, use_mmap, use_tsched, - query_supported_formats, - query_supported_rates, pa_channel_map_valid(&m->channel_map) /* Query the channel count if we don't know what we want */); if (!pcm_handle) @@ -693,8 +681,6 @@ snd_pcm_uframes_t tsched_size, bool *use_mmap, bool *use_tsched, - pa_sample_format_t **query_supported_formats, - unsigned int **query_supported_rates, bool require_exact_channel_number) { int err; @@ -722,12 +708,6 @@ pa_log_info("ALSA device open '%s' %s: %p", d, mode == SND_PCM_STREAM_CAPTURE ? "capture" : "playback", pcm_handle); - if (query_supported_formats) - *query_supported_formats = pa_alsa_get_supported_formats(pcm_handle, ss->format); - - if (query_supported_rates) - *query_supported_rates = pa_alsa_get_supported_rates(pcm_handle, ss->rate); - if ((err = pa_alsa_set_hw_params( pcm_handle, ss, @@ -801,8 +781,6 @@ snd_pcm_uframes_t tsched_size, bool *use_mmap, bool *use_tsched, - pa_sample_format_t **query_supported_formats, - unsigned int **query_supported_rates, bool require_exact_channel_number) { snd_pcm_t *pcm_handle; @@ -824,8 +802,6 @@ tsched_size, use_mmap, use_tsched, - query_supported_formats, - query_supported_rates, require_exact_channel_number); pa_xfree(d); @@ -1435,24 +1411,6 @@ return pa_sprintf_malloc("Audio%i", i); } -#endif - -static void dump_supported_rates(unsigned int* values) -{ - pa_strbuf *buf; - char *str; - int i; - - buf = pa_strbuf_new(); - - for (i = 0; valuesi; i++) { - pa_strbuf_printf(buf, " %u", valuesi); - } - - str = pa_strbuf_to_string_free(buf); - pa_log_debug("Supported rates:%s", str); - pa_xfree(str); -} unsigned int *pa_alsa_get_supported_rates(snd_pcm_t *pcm, unsigned int fallback_rate) { static unsigned int all_rates = { 8000, 11025, 12000, @@ -1460,8 +1418,7 @@ 32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000, - 352800, 384000, - 705600, 768000 }; + 384000 }; bool supportedPA_ELEMENTSOF(all_rates) = { false, }; snd_pcm_hw_params_t *hwparams; unsigned int i, j, n, *rates = NULL; @@ -1503,40 +1460,39 @@ rates1 = 0; } - dump_supported_rates(rates); return rates; } pa_sample_format_t *pa_alsa_get_supported_formats(snd_pcm_t *pcm, pa_sample_format_t fallback_format) { - static const snd_pcm_format_t format_trans_to_pcm = { - PA_SAMPLE_U8 = SND_PCM_FORMAT_U8, - PA_SAMPLE_ALAW = SND_PCM_FORMAT_A_LAW, - PA_SAMPLE_ULAW = SND_PCM_FORMAT_MU_LAW, - PA_SAMPLE_S16LE = SND_PCM_FORMAT_S16_LE, - PA_SAMPLE_S16BE = SND_PCM_FORMAT_S16_BE, - PA_SAMPLE_FLOAT32LE = SND_PCM_FORMAT_FLOAT_LE, - PA_SAMPLE_FLOAT32BE = SND_PCM_FORMAT_FLOAT_BE, - PA_SAMPLE_S32LE = SND_PCM_FORMAT_S32_LE, - PA_SAMPLE_S32BE = SND_PCM_FORMAT_S32_BE, - PA_SAMPLE_S24LE = SND_PCM_FORMAT_S24_3LE, - PA_SAMPLE_S24BE = SND_PCM_FORMAT_S24_3BE, - PA_SAMPLE_S24_32LE = SND_PCM_FORMAT_S24_LE, - PA_SAMPLE_S24_32BE = SND_PCM_FORMAT_S24_BE, + static const snd_pcm_format_t format_trans_to_pa = { + SND_PCM_FORMAT_U8 = PA_SAMPLE_U8, + SND_PCM_FORMAT_A_LAW = PA_SAMPLE_ALAW, + SND_PCM_FORMAT_MU_LAW = PA_SAMPLE_ULAW, + SND_PCM_FORMAT_S16_LE = PA_SAMPLE_S16LE, + SND_PCM_FORMAT_S16_BE = PA_SAMPLE_S16BE, + SND_PCM_FORMAT_FLOAT_LE = PA_SAMPLE_FLOAT32LE, + SND_PCM_FORMAT_FLOAT_BE = PA_SAMPLE_FLOAT32BE, + SND_PCM_FORMAT_S32_LE = PA_SAMPLE_S32LE, + SND_PCM_FORMAT_S32_BE = PA_SAMPLE_S32BE, + SND_PCM_FORMAT_S24_3LE = PA_SAMPLE_S24LE, + SND_PCM_FORMAT_S24_3BE = PA_SAMPLE_S24BE, + SND_PCM_FORMAT_S24_LE = PA_SAMPLE_S24_32LE, + SND_PCM_FORMAT_S24_BE = PA_SAMPLE_S24_32BE, }; - static const pa_sample_format_t all_formats = { - PA_SAMPLE_U8, - PA_SAMPLE_ALAW, - PA_SAMPLE_ULAW, - PA_SAMPLE_S16LE, - PA_SAMPLE_S16BE, - PA_SAMPLE_FLOAT32LE, - PA_SAMPLE_FLOAT32BE, - PA_SAMPLE_S32LE, - PA_SAMPLE_S32BE, - PA_SAMPLE_S24LE, - PA_SAMPLE_S24BE, - PA_SAMPLE_S24_32LE, - PA_SAMPLE_S24_32BE, + static const snd_pcm_format_t all_formats = { + SND_PCM_FORMAT_U8, + SND_PCM_FORMAT_A_LAW, + SND_PCM_FORMAT_MU_LAW, + SND_PCM_FORMAT_S16_LE, + SND_PCM_FORMAT_S16_BE, + SND_PCM_FORMAT_FLOAT_LE, + SND_PCM_FORMAT_FLOAT_BE, + SND_PCM_FORMAT_S32_LE, + SND_PCM_FORMAT_S32_BE, + SND_PCM_FORMAT_S24_3LE, + SND_PCM_FORMAT_S24_3BE, + SND_PCM_FORMAT_S24_LE, + SND_PCM_FORMAT_S24_BE, }; bool supportedPA_ELEMENTSOF(all_formats) = { false, @@ -1554,7 +1510,7 @@ } for (i = 0, n = 0; i < PA_ELEMENTSOF(all_formats); i++) { - if (snd_pcm_hw_params_test_format(pcm, hwparams, format_trans_to_pcmall_formatsi) == 0) { + if (snd_pcm_hw_params_test_format(pcm, hwparams, all_formatsi) == 0) { supportedi = true; n++; } @@ -1565,7 +1521,7 @@ for (i = 0, j = 0; i < PA_ELEMENTSOF(all_formats); i++) { if (supportedi) - formatsj++ = all_formatsi; + formatsj++ = format_trans_to_paall_formatsi; } formatsj = PA_SAMPLE_MAX; @@ -1573,7 +1529,7 @@ formats = pa_xnew(pa_sample_format_t, 2); formats0 = fallback_format; - if ((ret = snd_pcm_hw_params_set_format(pcm, hwparams, format_trans_to_pcmformats0)) < 0) { + if ((ret = snd_pcm_hw_params_set_format(pcm, hwparams, format_trans_to_paformats0)) < 0) { pa_log_debug("snd_pcm_hw_params_set_format() failed: %s", pa_alsa_strerror(ret)); pa_xfree(formats); return NULL; @@ -1584,6 +1540,7 @@ return formats; } +#endif bool pa_alsa_pcm_is_hw(snd_pcm_t *pcm) { snd_pcm_info_t* info; @@ -1643,16 +1600,14 @@ snd_ctl_elem_iface_t iface, const char *name, unsigned int index, - unsigned int device, - unsigned int subdevice) { + unsigned int device) { snd_mixer_elem_t *elem; for (elem = snd_mixer_first_elem(mixer); elem; elem = snd_mixer_elem_next(elem)) { - snd_hctl_elem_t **_helem, *helem; + snd_hctl_elem_t *helem; if (snd_mixer_elem_get_type(elem) != SND_MIXER_ELEM_PULSEAUDIO) continue; - _helem = snd_mixer_elem_get_private(elem); - helem = *_helem; + helem = snd_mixer_elem_get_private(elem); if (snd_hctl_elem_get_interface(helem) != iface) continue; if (!pa_streq(snd_hctl_elem_get_name(helem), name)) @@ -1661,19 +1616,17 @@ continue; if (snd_hctl_elem_get_device(helem) != device) continue; - if (snd_hctl_elem_get_subdevice(helem) != subdevice) - continue; return elem; } return NULL; } snd_mixer_elem_t *pa_alsa_mixer_find_card(snd_mixer_t *mixer, struct pa_alsa_mixer_id *alsa_id, unsigned int device) { - return pa_alsa_mixer_find(mixer, SND_CTL_ELEM_IFACE_CARD, alsa_id->name, alsa_id->index, device, 0); + return pa_alsa_mixer_find(mixer, SND_CTL_ELEM_IFACE_CARD, alsa_id->name, alsa_id->index, device); } snd_mixer_elem_t *pa_alsa_mixer_find_pcm(snd_mixer_t *mixer, const char *name, unsigned int device) { - return pa_alsa_mixer_find(mixer, SND_CTL_ELEM_IFACE_PCM, name, 0, device, 0); + return pa_alsa_mixer_find(mixer, SND_CTL_ELEM_IFACE_PCM, name, 0, device); } static int mixer_class_compare(const snd_mixer_elem_t *c1, const snd_mixer_elem_t *c2) @@ -1682,26 +1635,15 @@ return c1 == c2 ? 0 : (c1 > c2 ? 1 : -1); } -static void mixer_melem_free(snd_mixer_elem_t *elem) -{ - snd_hctl_elem_t **_helem; - _helem = snd_mixer_elem_get_private(elem); - pa_xfree(_helem); -} - static int mixer_class_event(snd_mixer_class_t *class, unsigned int mask, snd_hctl_elem_t *helem, snd_mixer_elem_t *melem) { int err; const char *name = snd_hctl_elem_get_name(helem); - snd_hctl_elem_t **_helem; - /* NOTE: The remove event is defined as '~0U`. */ + // NOTE: The remove event defined as '~0U`. if (mask == SND_CTL_EVENT_MASK_REMOVE) { - /* NOTE: Unless we remove the pointer to melem from the linked-list at - * private_data of helem, an assertion will be hit in alsa-lib since - * the list is not empty. */ - _helem = snd_mixer_elem_get_private(melem); - *_helem = NULL; + // NOTE: unless remove pointer to melem from link-list at private_data of helem, hits + // assersion in alsa-lib since the list is not empty. snd_mixer_elem_detach(melem, helem); } else if (mask & SND_CTL_EVENT_MASK_ADD) { snd_ctl_elem_iface_t iface = snd_hctl_elem_get_interface(helem); @@ -1711,50 +1653,26 @@ const char *name = snd_hctl_elem_get_name(helem); const int index = snd_hctl_elem_get_index(helem); const int device = snd_hctl_elem_get_device(helem); - const int subdevice = snd_hctl_elem_get_subdevice(helem); snd_mixer_elem_t *new_melem; - bool found = true; - new_melem = pa_alsa_mixer_find(mixer, iface, name, index, device, subdevice); + new_melem = pa_alsa_mixer_find(mixer, iface, name, index, device); if (!new_melem) { - _helem = pa_xmalloc(sizeof(snd_hctl_elem_t *)); - *_helem = helem; /* Put the hctl pointer as our private data - it will be useful for callbacks */ - if ((err = snd_mixer_elem_new(&new_melem, SND_MIXER_ELEM_PULSEAUDIO, 0, _helem, mixer_melem_free)) < 0) { + if ((err = snd_mixer_elem_new(&new_melem, SND_MIXER_ELEM_PULSEAUDIO, 0, helem, NULL)) < 0) { pa_log_warn("snd_mixer_elem_new failed: %s", pa_alsa_strerror(err)); return 0; } - found = false; - } else { - _helem = snd_mixer_elem_get_private(new_melem); - if (_helem) { - char *s1, *s2; - snd_ctl_elem_id_t *id1, *id2; - snd_ctl_elem_id_alloca(&id1); - snd_ctl_elem_id_alloca(&id2); - snd_hctl_elem_get_id(helem, id1); - snd_hctl_elem_get_id(*_helem, id2); - s1 = snd_ctl_ascii_elem_id_get(id1); - s2 = snd_ctl_ascii_elem_id_get(id2); - pa_log_warn("mixer_class_event - duplicate mixer controls: %s | %s", s1, s2); - free(s2); - free(s1); - return 0; - } - *_helem = helem; } if ((err = snd_mixer_elem_attach(new_melem, helem)) < 0) { pa_log_warn("snd_mixer_elem_attach failed: %s", pa_alsa_strerror(err)); - snd_mixer_elem_free(melem); + snd_mixer_elem_free(melem); return 0; } - if (!found) { - if ((err = snd_mixer_elem_add(new_melem, class)) < 0) { - pa_log_warn("snd_mixer_elem_add failed: %s", pa_alsa_strerror(err)); - return 0; - } + if ((err = snd_mixer_elem_add(new_melem, class)) < 0) { + pa_log_warn("snd_mixer_elem_add failed: %s", pa_alsa_strerror(err)); + return 0; } } }
View file
pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/alsa-util.h -> pipewire-0.3.69.tar.gz/spa/plugins/alsa/acp/alsa-util.h
Changed
@@ -64,8 +64,6 @@ snd_pcm_uframes_t tsched_size, bool *use_mmap, /* modified at return */ bool *use_tsched, /* modified at return */ - pa_sample_format_t **query_supported_formats, /* modified at return */ - unsigned int **query_supported_rates, /* modified at return */ pa_alsa_profile_set *ps, pa_alsa_mapping **mapping); /* modified at return */ #endif @@ -82,8 +80,6 @@ snd_pcm_uframes_t tsched_size, bool *use_mmap, /* modified at return */ bool *use_tsched, /* modified at return */ - pa_sample_format_t **query_supported_formats, /* modified at return */ - unsigned int **query_supported_rates, /* modified at return */ pa_alsa_mapping *mapping); /* Opens the explicit ALSA device */ @@ -98,8 +94,6 @@ snd_pcm_uframes_t tsched_size, bool *use_mmap, /* modified at return */ bool *use_tsched, /* modified at return */ - pa_sample_format_t **query_supported_formats, /* modified at return */ - unsigned int **query_supported_rates, /* modified at return */ bool require_exact_channel_number); /* Opens the explicit ALSA device with a fallback list */ @@ -115,8 +109,6 @@ snd_pcm_uframes_t tsched_size, bool *use_mmap, /* modified at return */ bool *use_tsched, /* modified at return */ - pa_sample_format_t **query_supported_formats, /* modified at return */ - unsigned int **query_supported_rates, /* modified at return */ bool require_exact_channel_number); #if 0
View file
pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/compat.h -> pipewire-0.3.69.tar.gz/spa/plugins/alsa/acp/compat.h
Changed
@@ -47,12 +47,10 @@ #define PA_LIKELY(x) (__builtin_expect(!!(x),1)) #define PA_UNLIKELY(x) (__builtin_expect(!!(x),0)) #define PA_PRINTF_FUNC(fmt, arg1) __attribute__((format(printf, fmt, arg1))) -#define PA_UNUSED __attribute__ ((unused)) #else #define PA_LIKELY(x) (x) #define PA_UNLIKELY(x) (x) #define PA_PRINTF_FUNC(fmt, arg1) -#define PA_UNUSED #endif #define PA_MIN(a,b) \ @@ -98,7 +96,7 @@ PA_AVAILABLE_YES = 2, } pa_available_t; -#define PA_RATE_MAX (48000U*16U) +#define PA_RATE_MAX (48000U*8U) typedef enum pa_sample_format { PA_SAMPLE_U8, /**< Unsigned 8 Bit PCM */
View file
pipewire-0.3.68.tar.gz/spa/plugins/alsa/acp/idxset.h -> pipewire-0.3.69.tar.gz/spa/plugins/alsa/acp/idxset.h
Changed
@@ -130,25 +130,13 @@ return count; } -static inline pa_idxset_item *pa_idxset_search(pa_idxset *s, uint32_t *idx) +static inline void *pa_idxset_search(pa_idxset *s, uint32_t *idx) { pa_idxset_item *item; for (item = pa_array_get_unchecked(&s->array, *idx, pa_idxset_item); pa_array_check(&s->array, item); item++, (*idx)++) { if (item->ptr != NULL) - return item; - } - *idx = PA_IDXSET_INVALID; - return NULL; -} - -static inline pa_idxset_item *pa_idxset_reverse_search(pa_idxset *s, uint32_t *idx) -{ - pa_idxset_item *item; - for (item = pa_array_get_unchecked(&s->array, *idx, pa_idxset_item); - pa_array_check(&s->array, item); item--, (*idx)--) { - if (item->ptr != NULL) - return item; + return item->ptr; } *idx = PA_IDXSET_INVALID; return NULL; @@ -156,93 +144,29 @@ static inline void *pa_idxset_next(pa_idxset *s, uint32_t *idx) { - pa_idxset_item *item; (*idx)++;; - item = pa_idxset_search(s, idx); - return item ? item->ptr : NULL; + return pa_idxset_search(s, idx); } static inline void* pa_idxset_first(pa_idxset *s, uint32_t *idx) { uint32_t i = 0; - pa_idxset_item *item = pa_idxset_search(s, &i); + void *ptr = pa_idxset_search(s, &i); if (idx) *idx = i; - return item ? item->ptr : NULL; -} - -static inline void* pa_idxset_last(pa_idxset *s, uint32_t *idx) -{ - uint32_t i = pa_array_get_len(&s->array, pa_idxset_item) - 1; - pa_idxset_item *item = pa_idxset_reverse_search(s, &i); - if (idx) - *idx = i; - return item ? item->ptr : NULL; -} - -static inline void* pa_idxset_steal_last(pa_idxset *s, uint32_t *idx) -{ - uint32_t i = pa_array_get_len(&s->array, pa_idxset_item) - 1; - void *ptr = NULL; - pa_idxset_item *item = pa_idxset_reverse_search(s, &i); - if (idx) - *idx = i; - if (item) { - ptr = item->ptr; - item->ptr = NULL; - pa_array_remove(&s->array, item); - } return ptr; } static inline void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx) { pa_idxset_item *item = pa_idxset_find(s, p); - if (item == NULL) { - if (idx) - *idx = PA_IDXSET_INVALID; + if (item == NULL) return NULL; - } if (idx) *idx = item - (pa_idxset_item*)s->array.data; return item->ptr; } -static inline bool pa_idxset_contains(pa_idxset *s, const void *p) -{ - return pa_idxset_get_by_data(s, p, NULL) == p; -} - -static inline bool pa_idxset_isdisjoint(pa_idxset *s, pa_idxset *t) -{ - pa_idxset_item *item; - pa_array_for_each(item, &s->array) { - if (item->ptr && pa_idxset_contains(t, item->ptr)) - return false; - } - return true; -} - -static inline bool pa_idxset_issubset(pa_idxset *s, pa_idxset *t) -{ - pa_idxset_item *item; - pa_array_for_each(item, &s->array) { - if (item->ptr && !pa_idxset_contains(t, item->ptr)) - return false; - } - return true; -} - -static inline bool pa_idxset_issuperset(pa_idxset *s, pa_idxset *t) -{ - return pa_idxset_issubset(t, s); -} - -static inline bool pa_idxset_equals(pa_idxset *s, pa_idxset *t) -{ - return pa_idxset_issubset(s, t) && pa_idxset_issuperset(s, t); -} - static inline void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx) { pa_idxset_item *item;
View file
pipewire-0.3.68.tar.gz/spa/plugins/alsa/alsa-pcm.c -> pipewire-0.3.69.tar.gz/spa/plugins/alsa/alsa-pcm.c
Changed
@@ -1567,10 +1567,13 @@ is_batch = snd_pcm_hw_params_is_batch(params) && !state->disable_batch; + /* no period size specified. If we are batch or not using timers, + * use the graph duration as the period */ + if (period_size == 0 && (is_batch || state->disable_tsched)) + period_size = state->position ? state->position->clock.duration : DEFAULT_PERIOD; + if (is_batch) { if (period_size == 0) - period_size = state->position ? state->position->clock.duration : DEFAULT_PERIOD; - if (period_size == 0) period_size = DEFAULT_PERIOD; /* batch devices get their hw pointers updated every period. Make * the period smaller and add one period of headroom. Limit the @@ -1616,10 +1619,18 @@ } state->headroom = state->default_headroom; - /* If tsched is disabled, we know the pointers are updated when we wake - * up, so we don't need the additional headroom */ - if (is_batch && !state->disable_tsched) - state->headroom += period_size; + if (!state->disable_tsched) { + /* When using timers, we might miss the pointer update for batch + * devices so add some extra headroom. With IRQ, we know the pointers + * are updated when we wake up and we don't need the headroom. */ + if (is_batch) + state->headroom += period_size; + /* Add 32 extra samples of headroom to handle jitter in capture. + * For IRQ, we don't need this because when we wake up, we have + * exactly enough samples to read or write. */ + if (state->stream == SND_PCM_STREAM_CAPTURE) + state->headroom = SPA_MAX(state->headroom, 32u); + } state->max_delay = state->buffer_frames / 2; if (spa_strstartswith(state->props.device, "a52") || @@ -1921,9 +1932,7 @@ *delay = state->buffer_frames - avail; } else { *delay = avail; - *target = SPA_MAX(*target, state->read_size); - if (state->matching) - *target += 32; + *target = SPA_MAX(*target, state->read_size + state->headroom); } *target = SPA_CLAMP(*target, state->min_delay, state->max_delay); return 0; @@ -1935,10 +1944,15 @@ double err, corr; int32_t diff; - if (state->stream == SND_PCM_STREAM_PLAYBACK) - err = delay - target; - else - err = target - delay; + if (state->disable_tsched && !follower) { + err = (int64_t)(current_time - state->next_time); + err = err / 1e9 * state->rate; + } else { + if (state->stream == SND_PCM_STREAM_PLAYBACK) + err = delay - target; + else + err = target - delay; + } if (SPA_UNLIKELY(state->dll.bw == 0.0)) { spa_dll_set_bw(&state->dll, SPA_DLL_BW_MAX, state->threshold, state->rate); @@ -1955,12 +1969,14 @@ state->alsa_sync = true; state->alsa_sync_warning = false; } - if (err > state->max_error) { - err = state->max_error; + if (err > state->max_resync) { state->alsa_sync = true; - } else if (err < -state->max_error) { - err = -state->max_error; + if (err > state->max_error) + err = state->max_error; + } else if (err < -state->max_resync) { state->alsa_sync = true; + if (err < -state->max_error) + err = -state->max_error; } if (!follower || state->matching) @@ -2002,8 +2018,8 @@ state->clock->next_nsec = state->next_time; } - spa_log_trace_fp(state->log, "%p: follower:%d %"PRIu64" %f %ld %f %f %u", - state, follower, current_time, corr, delay, err, state->threshold * corr, + spa_log_trace_fp(state->log, "%p: follower:%d %"PRIu64" %f %ld %ld %f %f %u", + state, follower, current_time, corr, delay, target, err, state->threshold * corr, state->threshold); return 0; @@ -2065,6 +2081,7 @@ return -EIO; state->threshold = SPA_SCALE32_UP(state->duration, state->rate, state->rate_denom); state->max_error = SPA_MAX(256.0f, state->threshold / 2.0f); + state->max_resync = SPA_MIN(state->threshold, state->max_error); state->resample = ((uint32_t)state->rate != state->rate_denom) || state->matching; state->alsa_sync = true; } @@ -2478,14 +2495,15 @@ int res; struct spa_io_buffers *io; - if (SPA_UNLIKELY(delay < target)) { - spa_log_trace(state->log, "%p: early wakeup %ld %ld", state, delay, target); + if (SPA_UNLIKELY(delay < state->read_size)) { + spa_log_trace(state->log, "%p: early wakeup %ld %ld %d", state, delay, target, + state->read_size); state->next_time = current_time + (target - delay) * SPA_NSEC_PER_SEC / state->rate; return -EAGAIN; } - if (SPA_UNLIKELY(res = update_time(state, current_time, delay, target, false)) < 0) + if (SPA_UNLIKELY((res = update_time(state, current_time, delay, target, false)) < 0)) return res; if ((res = spa_alsa_read(state)) < 0) @@ -2514,6 +2532,14 @@ return 0; } +static uint64_t get_time_ns(struct state *state) +{ + struct timespec now; + if (spa_system_clock_gettime(state->data_system, CLOCK_MONOTONIC, &now) < 0) + return 0; + return SPA_TIMESPEC_TO_NSEC(&now); +} + static void alsa_wakeup_event(struct spa_source *source) { struct state *state = source->data; @@ -2526,6 +2552,8 @@ int err; unsigned short revents; + current_time = get_time_ns(state); + for (int i = 0; i < state->n_fds; i++) { state->pfdsi.revents = state->sourcei.rmask; /* Reset so that we only handle all our sources' events once */ @@ -2543,17 +2571,20 @@ spa_log_trace_fp(state->log, "Woken up with no work to do"); return; } - } else if (SPA_LIKELY(state->started)) { - if (SPA_UNLIKELY((res = spa_system_timerfd_read(state->data_system, + } else { + if (SPA_LIKELY(state->started)) { + if (SPA_UNLIKELY((res = spa_system_timerfd_read(state->data_system, state->timerfd, &expire)) < 0)) { /* we can get here when the timer is changed since the last - * timerfd wakeup, for example by do_reassign_follower() executed - * in the same epoll wakeup cycle */ - if (res != -EAGAIN) - spa_log_warn(state->log, "%p: error reading timerfd: %s", - state, spa_strerror(res)); - return; + * timerfd wakeup, for example by do_reassign_follower() executed + * in the same epoll wakeup cycle */ + if (res != -EAGAIN) + spa_log_warn(state->log, "%p: error reading timerfd: %s", + state, spa_strerror(res)); + return; + } } + current_time = state->next_time; } if (SPA_UNLIKELY((res = check_position_config(state)) < 0)) { @@ -2562,8 +2593,6 @@ return; } - current_time = state->next_time; - if (SPA_UNLIKELY(get_status(state, current_time, &delay, &target) < 0)) { spa_log_error(state->log, "get_status error"); state->next_time += state->threshold * 1e9 / state->rate; @@ -2572,11 +2601,7 @@ #ifndef FASTPATH if (SPA_UNLIKELY(spa_log_level_topic_enabled(state->log, SPA_LOG_TOPIC_DEFAULT, SPA_LOG_LEVEL_TRACE))) { - struct timespec now; - uint64_t nsec; - if (spa_system_clock_gettime(state->data_system, CLOCK_MONOTONIC, &now) < 0) - return; - nsec = SPA_TIMESPEC_TO_NSEC(&now); + uint64_t nsec = get_time_ns(state); spa_log_trace_fp(state->log, "%p: wakeup %lu %lu %"PRIu64" %"PRIu64" %"PRIi64 " %d %"PRIi64, state, delay, target, nsec, nsec, nsec - current_time, state->threshold, state->sample_count); @@ -2636,12 +2661,7 @@ static int setup_sources(struct state *state) { - struct timespec now; - int res; - - if ((res = spa_system_clock_gettime(state->data_system, CLOCK_MONOTONIC, &now)) < 0) - return res; - state->next_time = SPA_TIMESPEC_TO_NSEC(&now); + state->next_time = get_time_ns(state); if (state->following) { /* Disable wakeups from this node */
View file
pipewire-0.3.68.tar.gz/spa/plugins/alsa/alsa-pcm.h -> pipewire-0.3.69.tar.gz/spa/plugins/alsa/alsa-pcm.h
Changed
@@ -213,6 +213,7 @@ struct spa_dll dll; double max_error; + double max_resync; struct spa_latency_info latency2; struct spa_process_latency_info process_latency;
View file
pipewire-0.3.69.tar.gz/spa/plugins/alsa/mixer/paths/audigy-analog-output-mirror.conf
Added
@@ -0,0 +1,56 @@ +; Mixer path for the Sound Blaster Audigy series, which uses the EMU10K2 DSP. +; We target 'Wave' and other non-'PCM' controls as a special case for when +; the device's stereo-to-all-speakers mirroring mode is in use. (For example, +; the Analog Stereo Output profile.) +; https://docs.kernel.org/sound/cards/audigy-mixer.html +; +; See analog-output.conf.common for an explanation on the directives + +General +priority = 99 +description-key = analog-output + +Element Master +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +Element Wave +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +# The following elements also exist in analog-output.conf. We list them here +# instead of including that file, for ideal positioning of the Wave element: +# Placing Wave below the Master element prevents Master from reaching its +# loudest until the user raises the unified volume control to maximum. +# (This should reduce the chance of a surprise speaker blow-out.) +# Placing Wave above the per-channel elements yields even steps at low volume. + +Element Front +volume = merge +override-map.1 = all-front +override-map.2 = front-left,front-right + +Element Surround +volume = merge +override-map.1 = all-rear +override-map.2 = rear-left,rear-right + +Element Side +volume = merge +override-map.1 = all-side +override-map.2 = side-left,side-right + +Element Center +volume = merge +override-map.1 = all-center +override-map.2 = all-center,all-center + +Element LFE +volume = merge +override-map.1 = lfe +override-map.2 = lfe,lfe + +.include analog-output.conf.common
View file
pipewire-0.3.69.tar.gz/spa/plugins/alsa/mixer/paths/audigy-analog-output.conf
Added
@@ -0,0 +1,44 @@ +; Mixer path for the Sound Blaster Audigy series, which uses the EMU10K2 DSP. +; We target 'PCM Front' and similarly named controls instead of 'Front' et al. +; because the latter affect volume only in the device's stereo-to-all-speakers +; mirroring mode, which is not used by most profiles. +; https://docs.kernel.org/sound/cards/audigy-mixer.html +; +; See analog-output.conf.common for an explanation on the directives + +General +priority = 99 +description-key = analog-output + +Element Master +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +Element PCM Front +volume = merge +override-map.1 = all-front +override-map.2 = front-left,front-right + +Element PCM Surround +volume = merge +override-map.1 = all-rear +override-map.2 = rear-left,rear-right + +Element PCM Side +volume = merge +override-map.1 = all-side +override-map.2 = side-left,side-right + +Element PCM Center +volume = merge +override-map.1 = all-center +override-map.2 = all-center,all-center + +Element PCM LFE +volume = merge +override-map.1 = lfe +override-map.2 = lfe,lfe + +.include analog-output.conf.common
View file
pipewire-0.3.68.tar.gz/spa/plugins/alsa/mixer/profile-sets/audigy.conf -> pipewire-0.3.69.tar.gz/spa/plugins/alsa/mixer/profile-sets/audigy.conf
Changed
@@ -15,10 +15,11 @@ ; Creative Sound Blaster Audigy product line ; -; These are just copies of the mappings we find in default.conf, with the -; small change of making analog-stereo and analog-mono non-fallback mappings. -; This is needed because these cards only support duplex profiles with mono -; inputs, and in the default configuration, with stereo being a fallback +; These are copies of the mappings we find in default.conf, but with analog +; mixer paths targeting appropriate Audigy driver controls, and the small +; change of making analog-stereo and analog-mono non-fallback mappings. +; The latter is needed because these cards only support duplex profiles with +; mono inputs, and in the default configuration, with stereo being a fallback ; mapping, the mono mapping is never tried. ; ; See default.conf for an explanation on the directives used here. @@ -30,7 +31,7 @@ Mapping analog-stereo device-strings = hw:%f channel-map = front-left,front-right -paths-output = analog-output analog-output-lineout analog-output-speaker analog-output-headphones analog-output-headphones-2 +paths-output = audigy-analog-output-mirror analog-output-lineout analog-output-speaker analog-output-headphones analog-output-headphones-2 paths-input = analog-input-front-mic analog-input-rear-mic analog-input-internal-mic analog-input-dock-mic analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line analog-input-headphone-mic analog-input-headset-mic priority = 1 @@ -38,43 +39,42 @@ Mapping analog-mono device-strings = hw:%f channel-map = mono -paths-output = analog-output analog-output-lineout analog-output-speaker analog-output-headphones analog-output-headphones-2 analog-output-mono +paths-output = audigy-analog-output-mirror analog-output-lineout analog-output-speaker analog-output-headphones analog-output-headphones-2 analog-output-mono paths-input = analog-input-front-mic analog-input-rear-mic analog-input-internal-mic analog-input-dock-mic analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line analog-input-headset-mic priority = 1 -# The rest of these are identical to what's in default.conf Mapping analog-surround-21 device-strings = surround21:%f channel-map = front-left,front-right,lfe -paths-output = analog-output analog-output-lineout analog-output-speaker +paths-output = audigy-analog-output analog-output-lineout analog-output-speaker priority = 13 direction = output Mapping analog-surround-40 device-strings = surround40:%f channel-map = front-left,front-right,rear-left,rear-right -paths-output = analog-output analog-output-lineout analog-output-speaker +paths-output = audigy-analog-output analog-output-lineout analog-output-speaker priority = 12 direction = output Mapping analog-surround-41 device-strings = surround41:%f channel-map = front-left,front-right,rear-left,rear-right,lfe -paths-output = analog-output analog-output-lineout analog-output-speaker +paths-output = audigy-analog-output analog-output-lineout analog-output-speaker priority = 13 direction = output Mapping analog-surround-50 device-strings = surround50:%f channel-map = front-left,front-right,rear-left,rear-right,front-center -paths-output = analog-output analog-output-lineout analog-output-speaker +paths-output = audigy-analog-output analog-output-lineout analog-output-speaker priority = 12 direction = output Mapping analog-surround-51 device-strings = surround51:%f channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe -paths-output = analog-output analog-output-lineout analog-output-speaker +paths-output = audigy-analog-output analog-output-lineout analog-output-speaker priority = 13 direction = output @@ -82,7 +82,7 @@ device-strings = surround71:%f channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right description = Analog Surround 7.1 -paths-output = analog-output analog-output-lineout analog-output-speaker +paths-output = audigy-analog-output analog-output-lineout analog-output-speaker priority = 12 direction = output
View file
pipewire-0.3.68.tar.gz/spa/plugins/bluez5/backend-native.c -> pipewire-0.3.69.tar.gz/spa/plugins/bluez5/backend-native.c
Changed
@@ -1062,6 +1062,7 @@ rfcomm_send_reply(rfcomm, "OK"); } else if (sscanf(buf, "AT+BIEV=%u,%u", &indicator, &indicator_value) == 2) { process_hfp_hf_indicator(rfcomm, indicator, indicator_value); + rfcomm_send_reply(rfcomm, "OK"); } else if (sscanf(buf, "AT+XAPL=%04x-%04x-%*^,,%u", &xapl_vendor, &xapl_product, &xapl_features) == 3) { if (xapl_features & SPA_BT_HFP_HF_XAPL_FEATURE_BATTERY_REPORTING) { /* claim, that we support battery status reports */ @@ -1083,6 +1084,7 @@ process_iphoneaccev_indicator(rfcomm, key, value); buf += r; } + rfcomm_send_reply(rfcomm, "OK"); } else if (spa_strstartswith(buf, "AT+APLSIRI?")) { // This command is sent when we activate Apple extensions rfcomm_send_reply(rfcomm, "OK");
View file
pipewire-0.3.68.tar.gz/spa/plugins/bluez5/bap-codec-caps.h -> pipewire-0.3.69.tar.gz/spa/plugins/bluez5/bap-codec-caps.h
Changed
@@ -83,14 +83,6 @@ #define LC3_MAX_CHANNELS 28 -typedef struct { - uint8_t rate; - uint8_t frame_duration; - uint32_t channels; - uint16_t framelen; - uint8_t n_blks; -} __attribute__ ((packed)) bap_lc3_t; - #define BT_ISO_QOS_CIG_UNSET 0xff #define BT_ISO_QOS_CIS_UNSET 0xff @@ -98,6 +90,12 @@ #define BT_ISO_QOS_TARGET_LATENCY_BALANCED 0x02 #define BT_ISO_QOS_TARGET_LATENCY_RELIABILITY 0x03 +struct __attribute__((packed)) ltv { + uint8_t len; + uint8_t type; + uint8_t value; +}; + struct bap_endpoint_qos { uint8_t framing; uint8_t phy;
View file
pipewire-0.3.68.tar.gz/spa/plugins/bluez5/bap-codec-lc3.c -> pipewire-0.3.69.tar.gz/spa/plugins/bluez5/bap-codec-lc3.c
Changed
@@ -34,17 +34,19 @@ unsigned int codesize; }; -struct __attribute__((packed)) ltv { - uint8_t len; - uint8_t type; - uint8_t value; -}; - struct pac_data { const uint8_t *data; size_t size; }; +typedef struct { + uint8_t rate; + uint8_t frame_duration; + uint32_t channels; + uint16_t framelen; + uint8_t n_blks; +} bap_lc3_t; + static int write_ltv(uint8_t *dest, uint8_t type, void* value, size_t len) { struct ltv *ltv = (struct ltv *)dest; @@ -82,6 +84,7 @@ data += write_ltv_uint8(data, LC3_TYPE_DUR, LC3_DUR_ANY); data += write_ltv_uint8(data, LC3_TYPE_CHAN, LC3_CHAN_1 | LC3_CHAN_2); data += write_ltv(data, LC3_TYPE_FRAMELEN, framelen, sizeof(framelen)); + /* XXX: we support only one frame block -> max 2 frames per SDU */ data += write_ltv_uint8(data, LC3_TYPE_BLKS, 2); return data - caps; @@ -119,9 +122,26 @@ return pac + 1; } -static bool parse_capabilities(bap_lc3_t *conf, const uint8_t *data, size_t data_size) +static uint8_t get_num_channels(uint32_t channels) +{ + uint8_t num; + + if (channels == 0) + return 1; /* MONO */ + + for (num = 0; channels; channels >>= 1) + if (channels & 0x1) + ++num; + + return num; +} + +static bool select_config(bap_lc3_t *conf, const struct pac_data *pac) { + const uint8_t *data = pac->data; + size_t data_size = pac->size; uint16_t framelen_min = 0, framelen_max = 0; + int max_frames = -1; if (!data_size) return false; @@ -129,6 +149,9 @@ conf->frame_duration = 0xFF; + /* XXX: we always use one frame block */ + conf->n_blks = 1; + while (data_size > 0) { struct ltv *ltv = (struct ltv *)data; @@ -168,12 +191,11 @@ spa_return_val_if_fail(ltv->len == 2, false); { uint8_t channels = ltv->value0; - /* Only mono or stereo streams are currently supported, - * in both case Audio location is defined as both Front Left - * and Front Right, difference is done by the n_blks parameter. - */ - if ((channels & LC3_CHAN_2) || (channels & LC3_CHAN_1)) + /* XXX: we hardcode mono or stereo stream */ + if (channels & LC3_CHAN_2) conf->channels = LC3_CONFIG_CHNL_FR | LC3_CONFIG_CHNL_FL; + else if (channels & LC3_CHAN_1) + conf->channels = 0; /* mono (omit Audio_Channel_Allocation) */ else return false; } @@ -185,9 +207,7 @@ break; case LC3_TYPE_BLKS: spa_return_val_if_fail(ltv->len == 2, false); - conf->n_blks = ltv->value0; - if (!conf->n_blks) - return false; + max_frames = ltv->value0; break; default: return false; @@ -196,37 +216,42 @@ data += ltv->len + 1; } + /* Default: 1 per channel (BAP v1.0.1 Sec 4.3.1) */ + if (max_frames < 0) + max_frames = get_num_channels(conf->channels); + if (max_frames < get_num_channels(conf->channels)) + return false; + if (framelen_min < LC3_MIN_FRAME_BYTES || framelen_max > LC3_MAX_FRAME_BYTES) return false; if (conf->frame_duration == 0xFF || !conf->rate) return false; - if (!conf->channels) - conf->channels = LC3_CONFIG_CHNL_FL; + /* BAP v1.0.1 Table 5.2; high-reliability */ switch (conf->rate) { case LC3_CONFIG_FREQ_48KHZ: if (conf->frame_duration == LC3_CONFIG_DURATION_7_5) - conf->framelen = 117; + conf->framelen = 117; /* 48_5_2 */ else - conf->framelen = 120; + conf->framelen = 120; /* 48_4_2 */ break; case LC3_CONFIG_FREQ_24KHZ: if (conf->frame_duration == LC3_CONFIG_DURATION_7_5) - conf->framelen = 45; + conf->framelen = 45; /* 24_1_2 */ else - conf->framelen = 60; + conf->framelen = 60; /* 24_2_2 */ break; case LC3_CONFIG_FREQ_16KHZ: if (conf->frame_duration == LC3_CONFIG_DURATION_7_5) - conf->framelen = 30; + conf->framelen = 30; /* 16_1_2 */ else - conf->framelen = 40; + conf->framelen = 40; /* 16_2_2 */ break; case LC3_CONFIG_FREQ_8KHZ: if (conf->frame_duration == LC3_CONFIG_DURATION_7_5) - conf->framelen = 26; + conf->framelen = 26; /* 8_1_2 */ else - conf->framelen = 30; + conf->framelen = 30; /* 8_2_2 */ break; default: return false; @@ -243,6 +268,9 @@ conf->frame_duration = 0xFF; + /* Absent Codec_Frame_Blocks_Per_SDU means 0x1 (BAP v1.0.1 Sec 4.3.2) */ + conf->n_blks = 1; + while (data_size > 0) { struct ltv *ltv = (struct ltv *)data; @@ -269,7 +297,8 @@ case LC3_TYPE_BLKS: spa_return_val_if_fail(ltv->len == 2, false); conf->n_blks = ltv->value0; - if (!conf->n_blks) + /* XXX: we only support 1 frame block for now */ + if (conf->n_blks != 1) return false; break; default: @@ -325,8 +354,8 @@ bap_lc3_t conf1, conf2; int res1, res2; - res1 = parse_capabilities(&conf1, pac1->data, pac1->size) ? (int)sizeof(bap_lc3_t) : -EINVAL; - res2 = parse_capabilities(&conf2, pac2->data, pac2->size) ? (int)sizeof(bap_lc3_t) : -EINVAL; + res1 = select_config(&conf1, pac1) ? (int)sizeof(bap_lc3_t) : -EINVAL; + res2 = select_config(&conf2, pac2) ? (int)sizeof(bap_lc3_t) : -EINVAL; return conf_cmp(&conf1, res1, &conf2, res2); } @@ -353,12 +382,16 @@ qsort(pacs, npacs, sizeof(struct pac_data), pac_cmp); - if (!parse_capabilities(&conf, pacs0.data, pacs0.size)) + if (!select_config(&conf, &pacs0)) return -ENOTSUP; data += write_ltv_uint8(data, LC3_TYPE_FREQ, conf.rate); data += write_ltv_uint8(data, LC3_TYPE_DUR, conf.frame_duration); - data += write_ltv_uint32(data, LC3_TYPE_CHAN, htobl(conf.channels)); + + /* Indicate MONO with absent Audio_Channel_Allocation (BAP v1.0.1 Sec. 4.3.2) */ + if (conf.channels != 0) + data += write_ltv_uint32(data, LC3_TYPE_CHAN, htobl(conf.channels)); + data += write_ltv_uint16(data, LC3_TYPE_FRAMELEN, htobs(conf.framelen)); data += write_ltv_uint8(data, LC3_TYPE_BLKS, conf.n_blks); @@ -378,19 +411,14 @@ return conf_cmp(&conf1, res1, &conf2, res2); } -static uint8_t channels_to_positions(uint32_t channels, uint8_t n_channels, uint32_t *position) +static uint8_t channels_to_positions(uint32_t channels, uint32_t *position) { + uint8_t n_channels = get_num_channels(channels); uint8_t n_positions = 0; spa_assert(n_channels <= SPA_AUDIO_MAX_CHANNELS); - /* First check if stream is configure for Mono, i.e. 1 block for both Front - * Left anf Front Right, - * else map LE Audio locations to PipeWire locations in the ascending order - * which will be used as block order in stream. - */ - if ((channels & (LC3_CONFIG_CHNL_FR | LC3_CONFIG_CHNL_FL)) == (LC3_CONFIG_CHNL_FR | LC3_CONFIG_CHNL_FL) && - n_channels == 1) { + if (channels == 0) { position0 = SPA_AUDIO_CHANNEL_MONO; n_positions = 1; } else { @@ -428,6 +456,9 @@ #undef CHANNEL_2_SPACHANNEL } + if (n_positions != n_channels) + return 0; /* error */ + return n_positions; } @@ -485,7 +516,7 @@ choice->body.type = SPA_CHOICE_Enum; spa_pod_builder_pop(b, &f1); - res = channels_to_positions(conf.channels, conf.n_blks, position); + res = channels_to_positions(conf.channels, position); if (res == 0) return -EINVAL; spa_pod_builder_add(b, @@ -533,7 +564,7 @@ return -EINVAL; } - res = channels_to_positions(conf.channels, conf.n_blks, info->info.raw.position); + res = channels_to_positions(conf.channels, info->info.raw.position); if (res == 0) return -EINVAL; info->info.raw.channels = res; @@ -568,32 +599,35 @@ qos->phy = 0x1; else qos->phy = 0x2; - qos->retransmission = 2; /* default */ - qos->sdu = conf.framelen * conf.n_blks; - qos->latency = 20; /* default */ - qos->delay = 40000U; + qos->sdu = conf.framelen * conf.n_blks * get_num_channels(conf.channels); qos->interval = (conf.frame_duration == LC3_CONFIG_DURATION_7_5 ? 7500 : 10000); - qos->target_latency = BT_ISO_QOS_TARGET_LATENCY_BALANCED; + qos->target_latency = BT_ISO_QOS_TARGET_LATENCY_RELIABILITY; + + /* Default values from BAP v1.0.1 Table 5.2; high-reliability */ + qos->delay = 40000U; + qos->retransmission = 13; switch (conf.rate) { - case LC3_CONFIG_FREQ_8KHZ: - case LC3_CONFIG_FREQ_16KHZ: - case LC3_CONFIG_FREQ_24KHZ: - case LC3_CONFIG_FREQ_32KHZ: - qos->retransmission = 2; - qos->latency = (conf.frame_duration == LC3_CONFIG_DURATION_7_5 ? 8 : 10); - break; - case LC3_CONFIG_FREQ_48KHZ: - qos->retransmission = 5; - qos->latency = (conf.frame_duration == LC3_CONFIG_DURATION_7_5 ? 15 : 20); - break; + case LC3_CONFIG_FREQ_8KHZ: + case LC3_CONFIG_FREQ_16KHZ: + case LC3_CONFIG_FREQ_24KHZ: + case LC3_CONFIG_FREQ_32KHZ: + /* F_1_2, F_2_2 */ + qos->latency = (conf.frame_duration == LC3_CONFIG_DURATION_7_5 ? 75 : 95); + break; + case LC3_CONFIG_FREQ_48KHZ: + /* 48_5_2, 48_4_2 */ + qos->latency = (conf.frame_duration == LC3_CONFIG_DURATION_7_5 ? 75 : 100); + break; + default: + qos->latency = 100; + break; } - /* Clamp to ASE values */ + /* Clamp to ASE values (if known) */ if (endpoint_qos->latency >= 0x0005 && endpoint_qos->latency <= 0x0FA0) /* Values outside the range are RFU */ - qos->latency = SPA_MAX(qos->latency, endpoint_qos->latency); - + qos->latency = endpoint_qos->latency; if (endpoint_qos->delay_min) qos->delay = SPA_MAX(qos->delay, endpoint_qos->delay_min); if (endpoint_qos->delay_max) @@ -651,7 +685,7 @@ res = -EINVAL; goto error; } - this->codesize = this->samples * this->channels * sizeof(int32_t); + this->codesize = this->samples * this->channels * conf.n_blks * sizeof(int32_t); if (!(flags & MEDIA_CODEC_FLAG_SINK)) { for (ich = 0; ich < this->channels; ich++) {
View file
pipewire-0.3.68.tar.gz/spa/plugins/bluez5/bluez5-dbus.c -> pipewire-0.3.69.tar.gz/spa/plugins/bluez5/bluez5-dbus.c
Changed
@@ -10,6 +10,7 @@ #include <sys/stat.h> #include <sys/socket.h> #include <fcntl.h> +#include <limits.h> #include <bluetooth/bluetooth.h> @@ -38,6 +39,8 @@ #include "iso-io.h" #include "defs.h" +#include "bap-codec-caps.h" + static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5"); #undef SPA_LOG_TOPIC_DEFAULT #define SPA_LOG_TOPIC_DEFAULT &log_topic @@ -1266,6 +1269,7 @@ spa_list_init(&d->remote_endpoint_list); spa_list_init(&d->transport_list); spa_list_init(&d->codec_switch_list); + spa_list_init(&d->set_membership_list); spa_hook_list_init(&d->listener_list); @@ -1292,6 +1296,7 @@ struct spa_bt_media_codec_switch *sw; struct spa_bt_transport *t, *tt; struct spa_bt_monitor *monitor = device->monitor; + struct spa_bt_set_membership *s; spa_log_debug(monitor->log, "%p", device); @@ -1321,6 +1326,13 @@ spa_list_consume(sw, &device->codec_switch_list, device_link) media_codec_switch_free(sw); + spa_list_consume(s, &device->set_membership_list, link) { + spa_list_remove(&s->link); + spa_list_remove(&s->others); + free(s->path); + free(s); + } + spa_list_remove(&device->link); free(device->path); free(device->alias); @@ -1332,6 +1344,84 @@ free(device); } +static struct spa_bt_set_membership *device_set_find(struct spa_bt_monitor *monitor, const char *path) +{ + struct spa_bt_device *d; + + spa_list_for_each(d, &monitor->device_list, link) { + struct spa_bt_set_membership *s; + + spa_list_for_each(s, &d->set_membership_list, link) { + if (spa_streq(s->path, path)) + return s; + } + } + + return NULL; +} + +static int device_add_device_set(struct spa_bt_device *device, const char *path, uint8_t rank) +{ + struct spa_bt_monitor *monitor = device->monitor; + struct spa_bt_set_membership *s, *set; + + spa_list_for_each(s, &device->set_membership_list, link) { + if (spa_streq(s->path, path)) { + if (rank) + s->rank = rank; + return 0; + } + } + + s = calloc(1, sizeof(struct spa_bt_set_membership)); + if (s == NULL) + return -ENOMEM; + + s->path = strdup(path); + if (!s->path) { + free(s); + return -ENOMEM; + } + + s->device = device; + s->rank = rank; + + spa_list_init(&s->others); + + /* Join with other set members, if any */ + set = device_set_find(monitor, path); + if (set) + spa_list_append(&set->others, &s->others); + + spa_list_append(&device->set_membership_list, &s->link); + + spa_log_debug(monitor->log, "device %p: add %s to device set %s", device, + device->path, path); + + return 1; +} + +static bool device_remove_device_set(struct spa_bt_device *device, const char *path) +{ + struct spa_bt_monitor *monitor = device->monitor; + struct spa_bt_set_membership *s; + + spa_list_for_each(s, &device->set_membership_list, link) { + if (spa_streq(s->path, path)) { + spa_log_debug(monitor->log, + "device %p: remove %s from device set %s", device, + device->path, path); + spa_list_remove(&s->link); + spa_list_remove(&s->others); + free(s->path); + free(s); + return true; + } + } + + return false; +} + int spa_bt_format_vendor_product_id(uint16_t source_id, uint16_t vendor_id, uint16_t product_id, char *vendor_str, int vendor_str_size, char *product_str, int product_str_size) { @@ -1668,6 +1758,7 @@ int spa_bt_device_check_profiles(struct spa_bt_device *device, bool force) { struct spa_bt_monitor *monitor = device->monitor; + struct spa_bt_set_membership *s, *set; uint32_t connected_profiles = device->connected_profiles; uint32_t connectable_profiles = device->adapter ? adapter_connectable_profiles(device->adapter) : 0; @@ -1677,6 +1768,7 @@ SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY, }; bool direction_connected = false; + bool set_connected = true; bool all_connected; size_t i; @@ -1693,14 +1785,19 @@ all_connected = (device->profiles & connected_profiles) == device->profiles; - spa_log_debug(monitor->log, "device %p: profiles %08x %08x connectable:%08x added:%d all:%d dir:%d", + spa_list_for_each(set, &device->set_membership_list, link) + spa_bt_for_each_set_member(s, set) + if ((s->device->connected_profiles & s->device->profiles) != s->device->profiles) + set_connected = false; + + spa_log_debug(monitor->log, "device %p: profiles %08x %08x connectable:%08x added:%d all:%d dir:%d set:%d", device, device->profiles, connected_profiles, connectable_profiles, - device->added, all_connected, direction_connected); + device->added, all_connected, direction_connected, set_connected); if (connected_profiles == 0 && spa_list_is_empty(&device->codec_switch_list)) { device_stop_timer(device); device_connected(monitor, device, BT_DEVICE_DISCONNECTED); - } else if (force || direction_connected || all_connected) { + } else if (force || ((direction_connected || all_connected) && set_connected)) { device_stop_timer(device); device_connected(monitor, device, BT_DEVICE_CONNECTED); } else { @@ -1734,10 +1831,14 @@ } } +static void device_update_set_status(struct spa_bt_device *device, bool force, const char *path); + int spa_bt_device_connect_profile(struct spa_bt_device *device, enum spa_bt_profile profile) { uint32_t prev_connected = device->connected_profiles; device->connected_profiles |= profile; + if ((prev_connected ^ device->connected_profiles) & SPA_BT_PROFILE_BAP_DUPLEX) + device_update_set_status(device, true, NULL); spa_bt_device_check_profiles(device, false); if (device->connected_profiles != prev_connected) spa_bt_device_emit_profiles_changed(device, device->profiles, prev_connected); @@ -1761,6 +1862,214 @@ spa_log_debug(monitor->log, "hw-volume-profiles:%08x", (int)device->hw_volume_profiles); } +static bool device_set_update_leader(struct spa_bt_set_membership *set) +{ + struct spa_bt_set_membership *s, *leader; + int min_rank = INT_MAX; + int leader_rank = INT_MAX; + + leader = NULL; + + spa_bt_for_each_set_member(s, set) { + if (!(s->device->connected_profiles & SPA_BT_PROFILE_BAP_DUPLEX)) + continue; + min_rank = SPA_MIN(min_rank, s->rank); + if (s->leader) { + leader_rank = s->rank; + leader = s; + } + } + + if (min_rank >= leader_rank && leader) + return false; + + spa_bt_for_each_set_member(s, set) { + if (leader == NULL && s->rank == min_rank && + (s->device->connected_profiles & SPA_BT_PROFILE_BAP_DUPLEX)) { + s->leader = true; + leader = s; + } else { + s->leader = false; + } + } + + if (leader) { + struct spa_bt_monitor *monitor = leader->device->monitor; + + spa_log_debug(monitor->log, "device set %s: leader is %s", + leader->path, leader->device->path); + } + + return true; +} + +static void device_update_set_status(struct spa_bt_device *device, bool force, const char *path) +{ + struct spa_bt_set_membership *s, *set; + + spa_list_for_each(set, &device->set_membership_list, link) { + if (path && !spa_streq(set->path, path)) + continue; + + if (device_set_update_leader(set) || force) { + spa_bt_for_each_set_member(s, set) + if (!s->leader) + spa_bt_device_emit_device_set_changed(s->device); + spa_bt_for_each_set_member(s, set) + if (s->leader) + spa_bt_device_emit_device_set_changed(s->device); + } + } +} + +static int device_set_update_props(struct spa_bt_monitor *monitor, + const char *path, DBusMessageIter *props_iter, DBusMessageIter *invalidated_iter) +{ + struct spa_bt_device *old256; + struct spa_bt_device *new256; + struct spa_bt_set_membership *set; + size_t num_old = 0, num_new = 0; + size_t i; + + if (!props_iter) + goto done; + + /* Find current devices */ + while (dbus_message_iter_get_arg_type(props_iter) != DBUS_TYPE_INVALID) { + DBusMessageIter it2; + const char *key; + + dbus_message_iter_recurse(props_iter, &it0); + dbus_message_iter_get_basic(&it0, &key); + dbus_message_iter_next(&it0); + dbus_message_iter_recurse(&it0, &it1); + + if (spa_streq(key, "Devices")) { + DBusMessageIter iter; + int i = 0; + + if (!check_iter_signature(&it1, "ao")) + goto next; + + dbus_message_iter_recurse(&it1, &iter); + + while (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID) { + struct spa_bt_device *d; + const char *dev_path; + + dbus_message_iter_get_basic(&iter, &dev_path); + + spa_log_debug(monitor->log, "device set %s: Devices%d=%s", + path, i++, dev_path); + + if (num_new >= SPA_N_ELEMENTS(new)) + break; + d = spa_bt_device_find(monitor, dev_path); + if (d) + newnum_new++ = d; + + dbus_message_iter_next(&iter); + } + + } + else + spa_log_debug(monitor->log, "device set %s: unhandled key %s", + path, key); + +next: + dbus_message_iter_next(props_iter); + } + +done: + /* Find devices to remove */ + set = device_set_find(monitor, path); + if (set) { + struct spa_bt_set_membership *s; + + spa_bt_for_each_set_member(s, set) { + for (i = 0; i < num_new; ++i) + if (s->device == newi) + break; + if (i == num_new) { + if (num_old >= SPA_N_ELEMENTS(old)) + break; + oldnum_old++ = s->device; + } + } + } + + /* Remove old devices */ + for (i = 0; i < num_old; ++i) + device_remove_device_set(oldi, path); + + /* Add new devices */ + for (i = 0; i < num_new; ++i) + device_add_device_set(newi, path, 0); + + /* Emit signals & update set leader */ + for (i = 0; i < num_old; ++i) + spa_bt_device_emit_device_set_changed(oldi); + + if (num_new > 0) + device_update_set_status(new0, true, path); + + return 0; +} + +static int device_update_device_sets_prop(struct spa_bt_device *device, + DBusMessageIter *iter) +{ + struct spa_bt_monitor *monitor = device->monitor; + DBusMessageIter it5; + bool changed = false; + + if (!check_iter_signature(iter, "a{oa{sv}}")) + return -EINVAL; + + dbus_message_iter_recurse(iter, &it0); + + while (dbus_message_iter_get_arg_type(&it0) != DBUS_TYPE_INVALID) { + uint8_t rank = 0; + const char *set_path; + + dbus_message_iter_recurse(&it0, &it1); + dbus_message_iter_get_basic(&it1, &set_path); + dbus_message_iter_next(&it1); + dbus_message_iter_recurse(&it1, &it2); + + while (dbus_message_iter_get_arg_type(&it2) != DBUS_TYPE_INVALID) { + const char *key; + int type; + + dbus_message_iter_recurse(&it2, &it3); + dbus_message_iter_get_basic(&it3, &key); + dbus_message_iter_next(&it3); + dbus_message_iter_recurse(&it3, &it4); + + type = dbus_message_iter_get_arg_type(&it4); + + if (spa_streq(key, "Rank") && type == DBUS_TYPE_BYTE) + dbus_message_iter_get_basic(&it4, &rank); + + dbus_message_iter_next(&it2); + } + + spa_log_debug(monitor->log, "device %p: path %s device set %s rank %d", + device, device->path, set_path, (int)rank); + + /* Only add. Removals are handled in device set updates. */ + if (device_add_device_set(device, set_path, rank) == 1) + changed = true; + + dbus_message_iter_next(&it0); + } + + /* Emit change signals */ + device_update_set_status(device, changed, NULL); + + return 0; +} + static int device_update_props(struct spa_bt_device *device, DBusMessageIter *props_iter, DBusMessageIter *invalidated_iter) @@ -1909,6 +2218,9 @@ spa_bt_device_emit_profiles_changed( device, prev_profiles, device->connected_profiles); } + else if (spa_streq(key, "Sets")) { + device_update_device_sets_prop(device, &it1); + } else spa_log_debug(monitor->log, "device %p: unhandled key %s type %d", device, key, type); @@ -2332,8 +2644,11 @@ spa_list_remove(&transport->device_link); } - if (device && device->connected_profiles != prev_connected) + if (device && device->connected_profiles != prev_connected) { + if ((prev_connected ^ device->connected_profiles) & SPA_BT_PROFILE_BAP_DUPLEX) + device_update_set_status(device, true, NULL); spa_bt_device_emit_profiles_changed(device, device->profiles, prev_connected); + } spa_list_remove(&transport->bap_transport_linked); @@ -2836,6 +3151,7 @@ dbus_message_iter_get_basic(&it1, &value); spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value); + transport->bap_interval = value; } else if (spa_streq(key, "Framing")) { dbus_bool_t value; @@ -2878,6 +3194,16 @@ else transport->bap_cis = value; } + else if (spa_streq(key, "Location")) { + uint32_t value; + + if (type != DBUS_TYPE_UINT32) + goto next; + dbus_message_iter_get_basic(&it1, &value); + + spa_log_debug(monitor->log, "transport %p: %s=%d", transport, key, (int)value); + transport->bap_location = value; + } next: dbus_message_iter_next(props_iter); } @@ -2992,6 +3318,7 @@ spa_log_debug(monitor->log, "transport %p: new ISO IO", transport); transport->iso_io = spa_bt_iso_io_create(transport->fd, sink, + transport->bap_cig, transport->bap_interval, monitor->log, monitor->data_loop, monitor->data_system); if (transport->iso_io == NULL) return -errno; @@ -3845,6 +4172,64 @@ return spa_bt_backend_supports_codec(monitor->backend, device, codec); } +static void bap_update_codec_location(struct spa_bt_transport *t) +{ + uint8_t *data = t->configuration; + size_t size = t->configuration_len; + struct ltv *ltv; + uint32_t location; + int i; + + if (!t->bap_location) + return; + + /* + * Append channel location from BAP transport location, if no channel + * configuration is present in the configuration. + * + * XXX: The codec select_configuration should set the location + * XXX: for mono channels from the device location. We have to do + * XXX: this here because transport location is not know + * XXX: in SelectProperties (TODO: should be fixed in bluez). + */ + + while (size > 0) { + ltv = (struct ltv *)data; + + if (ltv->len < sizeof(struct ltv) || ltv->len >= size) + return; + + if (ltv->type == LC3_TYPE_CHAN) + return; /* already has the channel info */ + + size -= ltv->len + 1; + data += ltv->len + 1; + } + + /* Pick the first location bit set */ + location = t->bap_location; + for (i = 0; i < 32; ++i) { + if (location & (1 << i)) { + location = (1 << i); + break; + } + } + + /* Append LTV value to transport configuration */ + size = t->configuration_len + sizeof(struct ltv) + sizeof(uint32_t); + data = realloc(t->configuration, size); + if (!data) + return; + + ltv = SPA_PTROFF(data, t->configuration_len, struct ltv); + ltv->len = 5; + ltv->type = LC3_TYPE_CHAN; + memcpy(ltv->value, &location, sizeof(uint32_t)); + + t->configuration = data; + t->configuration_len = size; +} + static DBusHandlerResult endpoint_set_configuration(DBusConnection *conn, const char *path, DBusMessage *m, void *userdata) { @@ -3929,6 +4314,9 @@ |= transport->device->a2dp_volume_activeSPA_BT_VOLUME_ID_TX; } + if (codec->bap) + bap_update_codec_location(transport); + if (codec->validate_config) { struct spa_audio_info info; if (codec->validate_config(codec, sink ? MEDIA_CODEC_FLAG_SINK : 0, @@ -4699,6 +5087,9 @@ * profile connection handlers can receive per-device settings during profile negotiation. */ spa_bt_device_add_profile(d, SPA_BT_PROFILE_NULL); } + else if (spa_streq(interface_name, BLUEZ_DEVICE_SET_INTERFACE)) { + device_set_update_props(monitor, object_path, props_iter, NULL); + } else if (spa_streq(interface_name, BLUEZ_MEDIA_ENDPOINT_INTERFACE)) { struct spa_bt_remote_endpoint *ep; struct spa_bt_device *d; @@ -4766,6 +5157,8 @@ d = spa_bt_device_find(monitor, object_path); if (d != NULL) device_free(d); + } else if (spa_streq(interface_name, BLUEZ_DEVICE_SET_INTERFACE)) { + device_set_update_props(monitor, object_path, NULL, NULL); } else if (spa_streq(interface_name, BLUEZ_ADAPTER_INTERFACE) || spa_streq(interface_name, BLUEZ_MEDIA_INTERFACE)) { struct spa_bt_adapter *a; @@ -5008,6 +5401,9 @@ spa_bt_device_add_profile(d, SPA_BT_PROFILE_NULL); } + else if (spa_streq(iface, BLUEZ_DEVICE_SET_INTERFACE)) { + device_set_update_props(monitor, path, &it1, NULL); + } else if (spa_streq(iface, BLUEZ_MEDIA_ENDPOINT_INTERFACE)) { struct spa_bt_remote_endpoint *ep; struct spa_bt_device *d; @@ -5099,6 +5495,10 @@ dbus_bus_add_match(this->conn, "type='signal',sender='" BLUEZ_SERVICE "'," "interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'," + "arg0='" BLUEZ_DEVICE_SET_INTERFACE "'", &err); + dbus_bus_add_match(this->conn, + "type='signal',sender='" BLUEZ_SERVICE "'," + "interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'," "arg0='" BLUEZ_MEDIA_ENDPOINT_INTERFACE "'", &err); dbus_bus_add_match(this->conn, "type='signal',sender='" BLUEZ_SERVICE "',"
View file
pipewire-0.3.68.tar.gz/spa/plugins/bluez5/bluez5-device.c -> pipewire-0.3.69.tar.gz/spa/plugins/bluez5/bluez5-device.c
Changed
@@ -42,6 +42,8 @@ #define DEVICE_ID_SOURCE 0 #define DEVICE_ID_SINK 1 +#define DEVICE_ID_SOURCE_SET 2 +#define DEVICE_ID_SINK_SET 3 #define DYNAMIC_NODE_ID_FLAG 0x1000 static struct spa_i18n *_i18n; @@ -98,6 +100,22 @@ bool a2dp_duplex; }; +struct device_set_member { + struct impl *impl; + struct spa_bt_transport *transport; + struct spa_hook listener; +}; + +struct device_set { + struct impl *impl; + char *path; + bool leader; + uint32_t sinks; + uint32_t sources; + struct device_set_member sinkSPA_AUDIO_MAX_CHANNELS; + struct device_set_member sourceSPA_AUDIO_MAX_CHANNELS; +}; + struct impl { struct spa_handle handle; struct spa_device device; @@ -126,6 +144,8 @@ unsigned int save_profile:1; uint32_t prev_bt_connected_profiles; + struct device_set device_set; + const struct media_codec **supported_codecs; size_t supported_codec_count; @@ -138,7 +158,7 @@ struct spa_dict_item setting_itemsMAX_SETTINGS; struct spa_dict setting_dict; - struct node nodes2; + struct node nodes4; }; static void init_node(struct impl *this, struct node *node, uint32_t id) @@ -359,7 +379,7 @@ struct spa_bt_transport_volume *t_volume; float prev_hw_volume; - if (!node->transport || !spa_bt_transport_volume_enabled(node->transport)) + if (!node->active || !node->transport || !spa_bt_transport_volume_enabled(node->transport)) return false; /* PW is the controller for remote device. */ @@ -463,14 +483,146 @@ info.info.raw.channels * sizeof(uint32_t)); } +static const char *get_channel_name(uint32_t channel) +{ + int i; + for (i = 0; spa_type_audio_channeli.name; i++) { + if (spa_type_audio_channeli.type == channel) + return spa_debug_type_short_name(spa_type_audio_channeli.name); + } + return NULL; +} + +static int channel_position_cmp(const void *pa, const void *pb) +{ + uint32_t a = *(uint32_t *)pa, b = *(uint32_t *)pb; + return (int)a - (int)b; +} + +static void emit_device_set_node(struct impl *this, uint32_t id) +{ + struct spa_bt_device *device = this->bt_dev; + struct node *node = &this->nodesid; + struct spa_device_object_info info; + struct spa_dict_item items7; + char str_id32, members_json8192, channels_json512; + struct device_set_member *members; + uint32_t n_members; + uint32_t n_items = 0; + struct spa_strbuf json; + unsigned int i; + + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_BLUEZ5_ADDRESS, device->address); + itemsn_items++ = SPA_DICT_ITEM_INIT("api.bluez5.set", this->device_set.path); + itemsn_items++ = SPA_DICT_ITEM_INIT("api.bluez5.set.leader", "true"); + snprintf(str_id, sizeof(str_id), "%d", id); + itemsn_items++ = SPA_DICT_ITEM_INIT("card.profile.device", str_id); + + if (id == DEVICE_ID_SOURCE_SET) { + itemsn_items++ = SPA_DICT_ITEM_INIT("media.class", "Audio/Source"); + members = this->device_set.source; + n_members = this->device_set.sources; + } else if (id == DEVICE_ID_SINK_SET) { + itemsn_items++ = SPA_DICT_ITEM_INIT("media.class", "Audio/Sink"); + members = this->device_set.sink; + n_members = this->device_set.sinks; + } else { + spa_assert_not_reached(); + } + + node->impl = this; + node->active = true; + node->transport = NULL; + node->a2dp_duplex = false; + node->offload_acquired = false; + node->mute = false; + node->save = false; + node->latency_offset = 0; + + /* Form channel map from members */ + node->n_channels = 0; + for (i = 0; i < n_members; ++i) { + struct spa_bt_transport *t = membersi.transport; + unsigned int j; + + for (j = 0; j < t->n_channels; ++j) { + unsigned int k; + + if (!get_channel_name(t->channelsj)) + continue; + + for (k = 0; k < node->n_channels; ++k) { + if (node->channelsk == t->channelsj) + break; + } + if (k == node->n_channels && node->n_channels < SPA_AUDIO_MAX_CHANNELS) + node->channelsnode->n_channels++ = t->channelsj; + } + } + + qsort(node->channels, node->n_channels, sizeof(uint32_t), channel_position_cmp); + + for (i = 0; i < node->n_channels; ++i) { + /* Session manager will override this, so put in some safe number */ + node->volumesi = node->soft_volumesi = 0.064; + } + + /* Produce member info json */ + spa_strbuf_init(&json, members_json, sizeof(members_json)); + spa_strbuf_append(&json, ""); + for (i = 0; i < n_members; ++i) { + struct spa_bt_transport *t = membersi.transport; + char object_path512; + unsigned int j; + int member_id = (id == DEVICE_ID_SINK_SET) ? DEVICE_ID_SINK : DEVICE_ID_SOURCE; + + if (i > 0) + spa_strbuf_append(&json, ","); + spa_scnprintf(object_path, sizeof(object_path), "%s/%s-%d", + this->device_set.path, t->device->address, member_id); + spa_strbuf_append(&json, "{\"object.path\":\"%s\",\"channels\":", object_path); + for (j = 0; j < t->n_channels; ++j) { + if (j > 0) + spa_strbuf_append(&json, ","); + spa_strbuf_append(&json, "\"%s\"", get_channel_name(t->channelsj)); + } + spa_strbuf_append(&json, "}"); + } + spa_strbuf_append(&json, ""); + json.bufferSPA_MIN(json.pos, json.maxsize-1) = 0; + itemsn_items++ = SPA_DICT_ITEM_INIT("api.bluez5.set.members", members_json); + + spa_strbuf_init(&json, channels_json, sizeof(channels_json)); + spa_strbuf_append(&json, ""); + for (i = 0; i < node->n_channels; ++i) { + if (i > 0) + spa_strbuf_append(&json, ","); + spa_strbuf_append(&json, "\"%s\"", get_channel_name(node->channelsi)); + } + spa_strbuf_append(&json, ""); + json.bufferSPA_MIN(json.pos, json.maxsize-1) = 0; + itemsn_items++ = SPA_DICT_ITEM_INIT("api.bluez5.set.channels", channels_json); + + /* Emit */ + info = SPA_DEVICE_OBJECT_INFO_INIT(); + info.type = SPA_TYPE_INTERFACE_Node; + info.factory_name = (id == DEVICE_ID_SOURCE_SET) ? "source" : "sink"; + info.change_mask = SPA_DEVICE_OBJECT_CHANGE_MASK_PROPS; + info.props = &SPA_DICT_INIT(items, n_items); + + spa_device_emit_object_info(&this->hooks, id, &info); + + emit_node_props(this, &this->nodesid, true); +} + static void emit_node(struct impl *this, struct spa_bt_transport *t, uint32_t id, const char *factory_name, bool a2dp_duplex) { struct spa_bt_device *device = this->bt_dev; struct spa_device_object_info info; - struct spa_dict_item items8; + struct spa_dict_item items11; uint32_t n_items = 0; - char transport32, str_id32; + char transport32, str_id32, object_path512; bool is_dyn_node = SPA_FLAG_IS_SET(id, DYNAMIC_NODE_ID_FLAG); snprintf(transport, sizeof(transport), "pointer:%p", t); @@ -480,9 +632,9 @@ items3 = SPA_DICT_ITEM_INIT(SPA_KEY_API_BLUEZ5_ADDRESS, device->address); items4 = SPA_DICT_ITEM_INIT("device.routes", "1"); n_items = 5; - if (!is_dyn_node) { + if (!is_dyn_node && !this->device_set.path) { snprintf(str_id, sizeof(str_id), "%d", id); - items5 = SPA_DICT_ITEM_INIT("card.profile.device", str_id); + itemsn_items = SPA_DICT_ITEM_INIT("card.profile.device", str_id); n_items++; } if (spa_streq(spa_bt_profile_name(t->profile), "headset-head-unit")) { @@ -493,6 +645,18 @@ itemsn_items = SPA_DICT_ITEM_INIT("api.bluez5.a2dp-duplex", "true"); n_items++; } + if (this->device_set.path) { + itemsn_items = SPA_DICT_ITEM_INIT("api.bluez5.set", this->device_set.path); + n_items++; + itemsn_items = SPA_DICT_ITEM_INIT("api.bluez5.internal", "true"); + n_items++; + + /* object.path can be used in match rules with only basic node props */ + spa_scnprintf(object_path, sizeof(object_path), "%s/%s-%d", + this->device_set.path, device->address, id); + itemsn_items = SPA_DICT_ITEM_INIT("object.path", object_path); + n_items++; + } info = SPA_DEVICE_OBJECT_INFO_INIT(); info.type = SPA_TYPE_INTERFACE_Node; @@ -503,6 +667,16 @@ SPA_FLAG_CLEAR(id, DYNAMIC_NODE_ID_FLAG); spa_device_emit_object_info(&this->hooks, id, &info); + if (this->device_set.path) { + /* Device set member nodes don't have their own routes */ + this->nodesid.impl = this; + this->nodesid.active = false; + if (this->nodesid.transport) + spa_hook_remove(&this->nodesid.transport_listener); + this->nodesid.transport = NULL; + return; + } + if (!is_dyn_node) { uint32_t prev_channels = this->nodesid.n_channels; float boost; @@ -540,9 +714,9 @@ } } -static struct spa_bt_transport *find_transport(struct impl *this, int profile, enum spa_bluetooth_audio_codec codec) +static struct spa_bt_transport *find_device_transport(struct spa_bt_device *device, int profile, + enum spa_bluetooth_audio_codec codec) { - struct spa_bt_device *device = this->bt_dev; struct spa_bt_transport *t; spa_list_for_each(t, &device->transport_list, device_link) { @@ -559,6 +733,11 @@ return NULL; } +static struct spa_bt_transport *find_transport(struct impl *this, int profile, enum spa_bluetooth_audio_codec codec) +{ + return find_device_transport(this->bt_dev, profile, codec); +} + static void dynamic_node_transport_destroy(void *data) { struct dynamic_node *this = data; @@ -631,8 +810,8 @@ SPA_PROP_volume, SPA_POD_Float(t_volume->volume)); event = spa_pod_builder_pop(&b, &f0); - spa_log_debug(impl->log, "dynamic node %p: volume %d changed %f, profile %d", - node, volume_id, t_volume->volume, node->transport->profile); + spa_log_debug(impl->log, "dynamic node %d: volume %d changed %f, profile %d", + node->id, volume_id, t_volume->volume, node->transport->profile); /* Dynamic node doesn't has route, we can only set volume on adaptar node. */ spa_device_emit_event(&impl->hooks, event); @@ -686,10 +865,97 @@ this->factory_name = NULL; } +static void device_set_clear(struct impl *impl) +{ + struct device_set *set = &impl->device_set; + unsigned int i; + + for (i = 0; i < SPA_N_ELEMENTS(set->sink); ++i) { + if (set->sinki.transport) + spa_hook_remove(&set->sinki.listener); + if (set->sourcei.transport) + spa_hook_remove(&set->sourcei.listener); + } + + free(set->path); + spa_zero(*set); + set->impl = impl; +} + +static void device_set_transport_destroy(void *data) +{ + struct device_set_member *member = data; + + member->transport = NULL; + spa_hook_remove(&member->listener); +} + +static const struct spa_bt_transport_events device_set_transport_events = { + SPA_VERSION_BT_DEVICE_EVENTS, + .destroy = device_set_transport_destroy, +}; + +static void device_set_update(struct impl *this) +{ + struct spa_bt_device *device = this->bt_dev; + struct device_set *dset = &this->device_set; + struct spa_bt_set_membership *set; + + device_set_clear(this); + + if (!is_bap_client(this)) + return; + + spa_list_for_each(set, &device->set_membership_list, link) { + struct spa_bt_set_membership *s; + int num_devices = 0; + + spa_bt_for_each_set_member(s, set) { + struct spa_bt_transport *t; + bool active = false; + + if (!(s->device->connected_profiles & SPA_BT_PROFILE_BAP_DUPLEX)) + continue; + + t = find_device_transport(s->device, SPA_BT_PROFILE_BAP_SOURCE, 0); + if (t && t->bap_initiator) { + active = true; + dset->sourcedset->sources.transport = t; + spa_bt_transport_add_listener(t, &dset->sourcedset->sources.listener, + &device_set_transport_events, &dset->sourcedset->sources); + ++dset->sources; + } + + t = find_device_transport(s->device, SPA_BT_PROFILE_BAP_SINK, this->props.codec); + if (t && t->bap_initiator) { + active = true; + dset->sinkdset->sinks.transport = t; + spa_bt_transport_add_listener(t, &dset->sinkdset->sinks.listener, + &device_set_transport_events, &dset->sinkdset->sinks); + ++dset->sinks; + } + + if (active) + ++num_devices; + } + + if (num_devices <= 1 || (dset->sinks <= 1 && dset->sources <= 1)) { + device_set_clear(this); + continue; + } + + dset->path = strdup(set->path); + dset->leader = set->leader; + break; + } +} + static int emit_nodes(struct impl *this) { struct spa_bt_transport *t; + device_set_update(this); + switch (this->profile) { case DEVICE_PROFILE_OFF: break; @@ -765,6 +1031,9 @@ emit_dynamic_node(&this->dyn_media_source, this, t, DEVICE_ID_SOURCE, SPA_NAME_API_BLUEZ5_MEDIA_SOURCE, false); } + + if (this->device_set.leader && this->device_set.sources > 0) + emit_device_set_node(this, DEVICE_ID_SOURCE_SET); } if (this->bt_dev->connected_profiles & (SPA_BT_PROFILE_BAP_SINK)) { @@ -777,6 +1046,9 @@ emit_dynamic_node(&this->dyn_media_sink, this, t, DEVICE_ID_SINK, SPA_NAME_API_BLUEZ5_MEDIA_SINK, false); } + + if (this->device_set.leader && this->device_set.sinks > 0) + emit_device_set_node(this, DEVICE_ID_SINK_SET); } if (get_supported_media_codec(this, this->props.codec, NULL) == NULL) @@ -832,7 +1104,7 @@ remove_dynamic_node (&this->dyn_sco_source); remove_dynamic_node (&this->dyn_sco_sink); - for (uint32_t i = 0; i < 2; i++) { + for (uint32_t i = 0; i < SPA_N_ELEMENTS(this->nodes); i++) { struct node * node = &this->nodesi; node_offload_set_active(node, false); if (node->transport) { @@ -864,7 +1136,7 @@ if (this->profile == profile && (this->profile != DEVICE_PROFILE_A2DP || codec == this->props.codec) && - (this->profile != DEVICE_PROFILE_BAP || codec == this->props.codec) && + (this->profile != DEVICE_PROFILE_BAP || codec == this->props.codec || this->device_set.path) && (this->profile != DEVICE_PROFILE_HSP_HFP || codec == this->props.codec)) return 0; @@ -1036,13 +1308,35 @@ emit_info(this, false); } +static void device_set_changed(void *userdata) +{ + struct impl *this = userdata; + + if (this->profile != DEVICE_PROFILE_BAP) + return; + if (!is_bap_client(this)) + return; + + spa_log_debug(this->log, "%p: device set changed", this); + + emit_remove_nodes(this); + emit_nodes(this); + + this->info.change_mask |= SPA_DEVICE_CHANGE_MASK_PARAMS; + this->paramsIDX_Profile.flags ^= SPA_PARAM_INFO_SERIAL; + this->paramsIDX_EnumProfile.flags ^= SPA_PARAM_INFO_SERIAL; + this->paramsIDX_Route.flags ^= SPA_PARAM_INFO_SERIAL; + this->paramsIDX_EnumRoute.flags ^= SPA_PARAM_INFO_SERIAL; + emit_info(this, false); +} + static void set_initial_profile(struct impl *this); static void device_connected(void *userdata, bool connected) { struct impl *this = userdata; - spa_log_debug(this->log, "connected: %d", connected); + spa_log_debug(this->log, "%p: connected: %d", this, connected); if (connected ^ (this->profile != DEVICE_PROFILE_OFF)) { emit_remove_nodes(this); @@ -1055,6 +1349,7 @@ .connected = device_connected, .codec_switched = codec_switched, .profiles_changed = profiles_changed, + .device_set_changed = device_set_changed, }; static int impl_add_listener(void *object, @@ -1101,14 +1396,19 @@ switch (index) { case DEVICE_PROFILE_A2DP: - case DEVICE_PROFILE_BAP: - if (device->connected_profiles & SPA_BT_PROFILE_MEDIA_SINK) + if (device->connected_profiles & SPA_BT_PROFILE_A2DP_SINK) have_output = true; media_codec = get_supported_media_codec(this, codec, NULL); if (media_codec && media_codec->duplex_codec) have_input = true; break; + case DEVICE_PROFILE_BAP: + if (device->connected_profiles & SPA_BT_PROFILE_BAP_SINK) + have_output = true; + if (device->connected_profiles & SPA_BT_PROFILE_BAP_SOURCE) + have_input = true; + break; case DEVICE_PROFILE_HSP_HFP: if (device->connected_profiles & SPA_BT_PROFILE_HEADSET_HEAD_UNIT) have_output = have_input = true; @@ -1400,6 +1700,14 @@ } priority = 128; } + + if (this->device_set.leader) { + n_sink = this->device_set.sinks ? 1 : 0; + n_source = this->device_set.sinks ? 1 : 0; + } else if (this->device_set.path) { + n_sink = 0; + n_source = 0; + } break; } case DEVICE_PROFILE_HSP_HFP: @@ -1498,6 +1806,7 @@ const char *name_prefix, *description, *hfp_description, *port_type; enum spa_bt_form_factor ff; enum spa_bluetooth_audio_codec codec; + enum spa_param_availability available; char name128; uint32_t i, j, mask, next; uint32_t dev = SPA_ID_INVALID, enum_dev; @@ -1573,7 +1882,7 @@ direction = SPA_DIRECTION_INPUT; snprintf(name, sizeof(name), "%s-input", name_prefix); enum_dev = DEVICE_ID_SOURCE; - if (profile == DEVICE_PROFILE_A2DP) + if (profile == DEVICE_PROFILE_A2DP || profile == DEVICE_PROFILE_BAP) dev = enum_dev; else if (profile != SPA_ID_INVALID) enum_dev = SPA_ID_INVALID; @@ -1582,7 +1891,7 @@ direction = SPA_DIRECTION_OUTPUT; snprintf(name, sizeof(name), "%s-output", name_prefix); enum_dev = DEVICE_ID_SINK; - if (profile == DEVICE_PROFILE_A2DP) + if (profile == DEVICE_PROFILE_A2DP || profile == DEVICE_PROFILE_BAP) dev = enum_dev; else if (profile != SPA_ID_INVALID) enum_dev = SPA_ID_INVALID; @@ -1607,6 +1916,32 @@ else if (profile != SPA_ID_INVALID) enum_dev = SPA_ID_INVALID; break; + case 4: + if (!this->device_set.leader) { + errno = EINVAL; + return NULL; + } + direction = SPA_DIRECTION_INPUT; + snprintf(name, sizeof(name), "%s-set-input", name_prefix); + enum_dev = DEVICE_ID_SOURCE_SET; + if (profile == DEVICE_PROFILE_BAP) + dev = enum_dev; + else if (profile != SPA_ID_INVALID) + enum_dev = SPA_ID_INVALID; + break; + case 5: + if (!this->device_set.leader) { + errno = EINVAL; + return NULL; + } + direction = SPA_DIRECTION_OUTPUT; + snprintf(name, sizeof(name), "%s-set-output", name_prefix); + enum_dev = DEVICE_ID_SINK_SET; + if (profile == DEVICE_PROFILE_BAP) + dev = enum_dev; + else if (profile != SPA_ID_INVALID) + enum_dev = SPA_ID_INVALID; + break; default: errno = EINVAL; return NULL; @@ -1617,6 +1952,10 @@ return NULL; } + available = SPA_PARAM_AVAILABILITY_yes; + if (this->device_set.path && !(port == 4 || port == 5)) + available = SPA_PARAM_AVAILABILITY_no; + spa_pod_builder_push_object(b, &f0, SPA_TYPE_OBJECT_ParamRoute, id); spa_pod_builder_add(b, SPA_PARAM_ROUTE_index, SPA_POD_Int(port), @@ -1624,7 +1963,7 @@ SPA_PARAM_ROUTE_name, SPA_POD_String(name), SPA_PARAM_ROUTE_description, SPA_POD_String(description), SPA_PARAM_ROUTE_priority, SPA_POD_Int(0), - SPA_PARAM_ROUTE_available, SPA_POD_Id(SPA_PARAM_AVAILABILITY_yes), + SPA_PARAM_ROUTE_available, SPA_POD_Id(available), 0); spa_pod_builder_prop(b, SPA_PARAM_ROUTE_info, 0); spa_pod_builder_push_struct(b, &f1); @@ -1643,6 +1982,8 @@ if (j == DEVICE_PROFILE_A2DP && !(port == 0 || port == 1)) continue; + if (j == DEVICE_PROFILE_BAP && !(port == 0 || port == 1 || port == 4 || port == 5)) + continue; if (j == DEVICE_PROFILE_HSP_HFP && !(port == 2 || port == 3)) continue; @@ -1882,7 +2223,7 @@ case SPA_PARAM_EnumRoute: { switch (result.index) { - case 0: case 1: case 2: case 3: + case 0: case 1: case 2: case 3: case 4: case 5: param = build_route(this, &b, id, result.index, SPA_ID_INVALID); if (param == NULL) goto next; @@ -1895,7 +2236,7 @@ case SPA_PARAM_Route: { switch (result.index) { - case 0: case 1: case 2: case 3: + case 0: case 1: case 2: case 3: case 4: case 5: param = build_route(this, &b, id, result.index, this->profile); if (param == NULL) goto next; @@ -1959,7 +2300,7 @@ if (n_volumes == 0) return -EINVAL; - spa_log_info(this->log, "node %p volume %f", node, volumes0); + spa_log_info(this->log, "node %d volume %f", node->id, volumes0); for (i = 0; i < node->n_channels; i++) { if (node->volumesi == volumesi % n_volumes) @@ -1973,7 +2314,7 @@ if (t_volume && t_volume->active && spa_bt_transport_volume_enabled(node->transport)) { float hw_volume = node_get_hw_volume(node); - spa_log_debug(this->log, "node %p hardware volume %f", node, hw_volume); + spa_log_debug(this->log, "node %d hardware volume %f", node->id, hw_volume); node_update_soft_volumes(node, hw_volume); spa_bt_transport_set_volume(node->transport, node->id, hw_volume); @@ -1996,7 +2337,7 @@ struct spa_pod_frame f1; int changed = 0; - spa_log_info(this->log, "node %p mute %d", node, mute); + spa_log_info(this->log, "node %d mute %d", node->id, mute); changed = (node->mute != mute); node->mute = mute; @@ -2027,7 +2368,7 @@ struct spa_pod_frame f1; int changed = 0; - spa_log_info(this->log, "node %p latency offset %"PRIi64" nsec", node, latency_offset); + spa_log_info(this->log, "node %d latency offset %"PRIi64" nsec", node->id, latency_offset); changed = (node->latency_offset != latency_offset); node->latency_offset = latency_offset; @@ -2108,10 +2449,11 @@ static void apply_prop_offload_active(struct impl *this, bool active) { bool old_value = this->props.offload_active; + unsigned int i; this->props.offload_active = active; - for (int i = 0; i < 2; i++) { + for (i = 0; i < SPA_N_ELEMENTS(this->nodes); i++) { node_offload_set_active(&this->nodesi, active); if (!this->nodesi.offload_acquired) this->props.offload_active = false; @@ -2157,7 +2499,8 @@ if (profile == SPA_ID_INVALID) return -EINVAL; - spa_log_debug(this->log, "setting profile %d codec:%d save:%d", profile, codec, (int)save); + spa_log_debug(this->log, "%p: setting profile %d codec:%d save:%d", this, + profile, codec, (int)save); return set_profile(this, profile, codec, save); } case SPA_PARAM_Route: @@ -2180,7 +2523,7 @@ spa_debug_log_pod(this->log, SPA_LOG_LEVEL_DEBUG, 0, NULL, param); return res; } - if (device > 1 || !this->nodesdevice.active) + if (device >= SPA_N_ELEMENTS(this->nodes) || !this->nodesdevice.active) return -EINVAL; node = &this->nodesdevice; @@ -2288,6 +2631,7 @@ free((void *)it->value); } + device_set_clear(this); return 0; } @@ -2373,6 +2717,8 @@ init_node(this, &this->nodes0, 0); init_node(this, &this->nodes1, 1); + init_node(this, &this->nodes2, 2); + init_node(this, &this->nodes3, 3); this->info = SPA_DEVICE_INFO_INIT(); this->info_all = SPA_DEVICE_CHANGE_MASK_PROPS | @@ -2389,6 +2735,8 @@ spa_bt_device_add_listener(this->bt_dev, &this->bt_dev_listener, &bt_dev_events, this); + this->device_set.impl = this; + set_initial_profile(this); return 0;
View file
pipewire-0.3.68.tar.gz/spa/plugins/bluez5/defs.h -> pipewire-0.3.69.tar.gz/spa/plugins/bluez5/defs.h
Changed
@@ -27,6 +27,7 @@ #define BLUEZ_PROFILE_INTERFACE BLUEZ_SERVICE ".Profile1" #define BLUEZ_ADAPTER_INTERFACE BLUEZ_SERVICE ".Adapter1" #define BLUEZ_DEVICE_INTERFACE BLUEZ_SERVICE ".Device1" +#define BLUEZ_DEVICE_SET_INTERFACE BLUEZ_SERVICE ".DeviceSet1" #define BLUEZ_MEDIA_INTERFACE BLUEZ_SERVICE ".Media1" #define BLUEZ_MEDIA_ENDPOINT_INTERFACE BLUEZ_SERVICE ".MediaEndpoint1" #define BLUEZ_MEDIA_TRANSPORT_INTERFACE BLUEZ_SERVICE ".MediaTransport1" @@ -440,12 +441,27 @@ /** Profile configuration changed */ void (*profiles_changed) (void *data, uint32_t prev_profiles, uint32_t prev_connected); + /** Device set configuration changed */ + void (*device_set_changed) (void *data); + /** Device freed */ void (*destroy) (void *data); }; struct media_codec; +struct spa_bt_set_membership { + struct spa_list link; + struct spa_list others; + struct spa_bt_device *device; + char *path; + uint8_t rank; + bool leader; +}; + +#define spa_bt_for_each_set_member(s, set) \ + for ((s) = (set); (s); (s) = spa_list_next((s), others), (s) = (s) != (set) ? (s) : NULL) + struct spa_bt_device { struct spa_list link; struct spa_bt_monitor *monitor; @@ -477,6 +493,7 @@ struct spa_list remote_endpoint_list; struct spa_list transport_list; struct spa_list codec_switch_list; + struct spa_list set_membership_list; uint8_t battery; int has_battery; @@ -518,6 +535,7 @@ #define spa_bt_device_emit_connected(d,...) spa_bt_device_emit(d, connected, 0, __VA_ARGS__) #define spa_bt_device_emit_codec_switched(d,...) spa_bt_device_emit(d, codec_switched, 0, __VA_ARGS__) #define spa_bt_device_emit_profiles_changed(d,...) spa_bt_device_emit(d, profiles_changed, 0, __VA_ARGS__) +#define spa_bt_device_emit_device_set_changed(d) spa_bt_device_emit(d, device_set_changed, 0) #define spa_bt_device_emit_destroy(d) spa_bt_device_emit(d, destroy, 0) #define spa_bt_device_add_listener(d,listener,events,data) \ spa_hook_list_append(&(d)->listener_list, listener, events, data) @@ -612,6 +630,8 @@ unsigned int latency_us; uint8_t bap_cig; uint8_t bap_cis; + uint32_t bap_location; + uint32_t bap_interval; struct spa_bt_iso_io *iso_io; struct spa_bt_sco_io *sco_io;
View file
pipewire-0.3.68.tar.gz/spa/plugins/bluez5/iso-io.c -> pipewire-0.3.69.tar.gz/spa/plugins/bluez5/iso-io.c
Changed
@@ -16,8 +16,6 @@ #include <spa/utils/result.h> #include <spa/node/io.h> -#include <bluetooth/bluetooth.h> - #include "config.h" #include "iso-io.h" @@ -25,7 +23,7 @@ #undef SPA_LOG_TOPIC_DEFAULT #define SPA_LOG_TOPIC_DEFAULT &log_topic -#define IDLE_TIME (100 * SPA_NSEC_PER_MSEC) +#define IDLE_TIME (200 * SPA_NSEC_PER_MSEC) struct group { struct spa_log *log; @@ -159,9 +157,8 @@ if (!stream->sink) continue; - if (stream->idle) - continue; - if (group->paused) { + if (stream->idle || group->paused) { + stream->this.resync = true; stream->this.size = 0; continue; } @@ -195,19 +192,12 @@ set_timeout(group, group->next); } -static struct group *group_create(int fd, struct spa_log *log, struct spa_loop *data_loop, - struct spa_system *data_system) +static struct group *group_create(uint8_t cig, uint32_t interval, + struct spa_log *log, struct spa_loop *data_loop, struct spa_system *data_system) { -#if defined(HAVE_BLUETOOTH_BAP) && defined(BT_ISO_QOS) struct group *group; - struct bt_iso_qos qos; - socklen_t len; - - len = sizeof(qos); - if (getsockopt(fd, SOL_BLUETOOTH, BT_ISO_QOS, &qos, &len) < 0) - return NULL; - if (qos.out.interval <= 5000) { + if (interval <= 5000) { errno = EINVAL; return NULL; } @@ -218,11 +208,11 @@ spa_log_topic_init(log, &log_topic); - group->cig = qos.cig; + group->cig = cig; group->log = log; group->data_loop = data_loop; group->data_system = data_system; - group->duration = qos.out.interval * SPA_NSEC_PER_USEC; + group->duration = interval * SPA_NSEC_PER_USEC; spa_list_init(&group->streams); @@ -243,10 +233,6 @@ spa_loop_add_source(group->data_loop, &group->source); return group; -#else - errno = EOPNOTSUPP; - return NULL; -#endif } static int do_remove_source(struct spa_loop *loop, bool async, uint32_t seq, @@ -286,6 +272,7 @@ stream->fd = fd; stream->sink = sink; stream->group = group; + stream->idle = true; stream->this.duration = group->duration; stream_link(group, stream); @@ -293,13 +280,13 @@ return stream; } -struct spa_bt_iso_io *spa_bt_iso_io_create(int fd, bool sink, struct spa_log *log, - struct spa_loop *data_loop, struct spa_system *data_system) +struct spa_bt_iso_io *spa_bt_iso_io_create(int fd, bool sink, uint8_t cig, uint32_t interval, + struct spa_log *log, struct spa_loop *data_loop, struct spa_system *data_system) { struct stream *stream; struct group *group; - group = group_create(fd, log, data_loop, data_system); + group = group_create(cig, interval, log, data_loop, data_system); if (group == NULL) return NULL; @@ -366,12 +353,11 @@ else if (enabled && !was_enabled) set_timers(stream->group); + stream->idle = true; + stream->this.resync = true; + if (pull == NULL) { stream->this.size = 0; return; } - - /* Pull data now for the next interval */ - stream->this.now = stream->group->next; - stream->pull(&stream->this); }
View file
pipewire-0.3.68.tar.gz/spa/plugins/bluez5/iso-io.h -> pipewire-0.3.69.tar.gz/spa/plugins/bluez5/iso-io.h
Changed
@@ -18,20 +18,22 @@ */ struct spa_bt_iso_io { - uint64_t now; - uint64_t duration; + uint64_t now; /**< Reference time position of next packet (read-only) */ + uint64_t duration; /**< ISO interval duration in ns (read-only) */ + bool resync; /**< Resync position for next packet; (pull callback sets to + * false when done) */ - uint32_t timestamp; - uint8_t buf4096; - size_t size; + uint32_t timestamp; /**< Packet timestamp (set by pull callback) */ + uint8_t buf4096; /**< Packet data (set by pull callback) */ + size_t size; /**< Packet size (set by pull callback) */ void *user_data; }; typedef void (*spa_bt_iso_io_pull_t)(struct spa_bt_iso_io *io); -struct spa_bt_iso_io *spa_bt_iso_io_create(int fd, bool sink, struct spa_log *log, - struct spa_loop *data_loop, struct spa_system *data_system); +struct spa_bt_iso_io *spa_bt_iso_io_create(int fd, bool sink, uint8_t cig, uint32_t interval, + struct spa_log *log, struct spa_loop *data_loop, struct spa_system *data_system); struct spa_bt_iso_io *spa_bt_iso_io_attach(struct spa_bt_iso_io *io, int fd, bool sink); void spa_bt_iso_io_destroy(struct spa_bt_iso_io *io); void spa_bt_iso_io_set_cb(struct spa_bt_iso_io *io, spa_bt_iso_io_pull_t pull, void *user_data);
View file
pipewire-0.3.68.tar.gz/spa/plugins/bluez5/media-sink.c -> pipewire-0.3.69.tar.gz/spa/plugins/bluez5/media-sink.c
Changed
@@ -58,6 +58,10 @@ #define BUFFER_SIZE (8192*8) #define RATE_CTL_DIFF_MAX 0.005 +/* Wait for two cycles before trying to sync ISO. On start/driver reassign, + * first cycle may have strange number of samples. */ +#define RESYNC_CYCLES 2 + struct buffer { uint32_t id; #define BUFFER_FLAG_OUT (1<<0) @@ -129,8 +133,10 @@ unsigned int following:1; unsigned int is_output:1; unsigned int flush_pending:1; + unsigned int iso_pending:1; unsigned int is_duplex:1; + unsigned int is_internal:1; struct spa_source source; int timerfd; @@ -159,8 +165,7 @@ int need_flush; bool fragment; - bool resync; - bool have_iso_packet; + uint32_t resync; uint32_t block_size; uint8_t bufferBUFFER_SIZE; uint32_t buffer_used; @@ -307,7 +312,7 @@ bool following; if (this->position != info->position || this->clock != info->clock) - this->resync = true; + this->resync = RESYNC_CYCLES; this->position = info->position; this->clock = info->clock; @@ -460,6 +465,7 @@ { struct port *port = &this->port; uint64_t t, duration_ns; + bool resampling; if (!this->process_rate || !this->process_duration) { if (this->position) { @@ -481,9 +487,12 @@ / port->current_format.info.raw.rate); /* Account for resampling delay */ - if (port->rate_match && this->clock && SPA_FLAG_IS_SET(port->rate_match->flags, SPA_IO_RATE_MATCH_FLAG_ACTIVE)) + resampling = (port->current_format.info.raw.rate != this->process_rate) || this->following; + if (port->rate_match && this->clock && resampling) { t -= (uint64_t)port->rate_match->delay * SPA_NSEC_PER_SEC / this->clock->rate.denom; + t += SPA_NSEC_PER_SEC / port->current_format.info.raw.rate; + } return t; } @@ -715,6 +724,9 @@ if (!this->flush_timer_source.loop && !this->transport->iso_io) return -EIO; + if (this->transport->iso_io && !this->iso_pending) + return 0; + total_frames = 0; again: written = 0; @@ -787,7 +799,7 @@ if (this->transport->iso_io) { struct spa_bt_iso_io *iso_io = this->transport->iso_io; - if (this->need_flush && !this->have_iso_packet) { + if (this->need_flush) { size_t avail = SPA_MIN(this->buffer_used, sizeof(iso_io->buf)); spa_log_trace(this->log, "%p: ISO put fd:%d size:%u sn:%u ts:%u now:%"PRIu64, @@ -798,7 +810,7 @@ memcpy(iso_io->buf, this->buffer, avail); iso_io->size = avail; iso_io->timestamp = this->timestamp; - this->have_iso_packet = true; + this->iso_pending = false; reset_buffer(this); } @@ -953,9 +965,7 @@ struct port *port = &this->port; const double period = 0.1 * SPA_NSEC_PER_SEC; uint64_t duration_ns; - double value, target, err; - - this->have_iso_packet = false; + double value, target, err, max_err; if (this->resync || !this->position) { spa_bt_rate_control_init(&port->ratectl, 0); @@ -978,19 +988,28 @@ value = (int64_t)iso_io->now - (int64_t)get_reference_time(this, &duration_ns); target = iso_io->duration * 3/2; err = value - target; + max_err = iso_io->duration; - if (err > iso_io->duration) { - uint32_t req = err * port->current_format.info.raw.rate / SPA_NSEC_PER_SEC; - - spa_log_debug(this->log, "%p: ISO sync reset frames:%u", this, (unsigned int)req); - - spa_bt_rate_control_init(&port->ratectl, 0); - drop_frames(this, req); - } else if (-err > iso_io->duration) { - uint32_t req = -err * port->current_format.info.raw.rate / SPA_NSEC_PER_SEC; + if (err > max_err || (iso_io->resync && err > 0)) { + unsigned int req = err * port->current_format.info.raw.rate / SPA_NSEC_PER_SEC; - spa_log_debug(this->log, "%p: ISO sync skip flush frames:%u", this, (unsigned int)req); - return; + if (req > 0) { + spa_bt_rate_control_init(&port->ratectl, 0); + drop_frames(this, req); + spa_log_debug(this->log, "%p: ISO sync skip frames:%u resync:%d", + this, req, iso_io->resync); + } + } else if (-err > max_err || (iso_io->resync && -err > 0)) { + unsigned int req = -err * port->current_format.info.raw.rate / SPA_NSEC_PER_SEC; + static const uint8_t empty8192 = {0}; + + if (req > 0) { + spa_bt_rate_control_init(&port->ratectl, 0); + req = SPA_MIN(req, sizeof(empty) / port->frame_size); + add_data(this, empty, req * port->frame_size); + spa_log_debug(this->log, "%p: ISO sync pad frames:%u resync:%d", + this, req, iso_io->resync); + } } else { spa_bt_rate_control_update(&port->ratectl, err, 0, iso_io->duration, period, RATE_CTL_DIFF_MAX); @@ -1002,7 +1021,10 @@ port->ratectl.corr); } + iso_io->resync = false; + done: + this->iso_pending = true; flush_data(this, this->current_time); } @@ -1215,9 +1237,9 @@ this->flush_source.rmask = 0; spa_loop_add_source(this->data_loop, &this->flush_source); - this->resync = true; - + this->resync = RESYNC_CYCLES; this->flush_pending = false; + this->iso_pending = false; this->transport_started = true; @@ -1375,7 +1397,8 @@ { struct spa_dict_item node_info_items = { { SPA_KEY_DEVICE_API, "bluez5" }, - { SPA_KEY_MEDIA_CLASS, this->is_output ? "Audio/Sink" : "Stream/Input/Audio" }, + { SPA_KEY_MEDIA_CLASS, this->is_internal ? "Audio/Sink/Internal" : + this->is_output ? "Audio/Sink" : "Stream/Input/Audio" }, { "media.name", ((this->transport && this->transport->device->name) ? this->transport->device->name : this->codec->bap ? "BAP" : "A2DP" ) }, { SPA_KEY_NODE_DRIVER, this->is_output ? "true" : "false" }, @@ -1796,6 +1819,8 @@ if (io->status == SPA_STATUS_HAVE_DATA && io->buffer_id < port->n_buffers) { struct buffer *b = &port->buffersio->buffer_id; + struct spa_data *d = b->buf->datas; + unsigned int frames; if (!SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUT)) { spa_log_warn(this->log, "%p: buffer %u in use", this, io->buffer_id); @@ -1803,7 +1828,8 @@ return -EINVAL; } - spa_log_trace(this->log, "%p: queue buffer %u", this, io->buffer_id); + frames = d ? d0.chunk->size / port->frame_size : 0; + spa_log_trace(this->log, "%p: queue buffer %u frames:%u", this, io->buffer_id, frames); spa_list_append(&port->ready, &b->link); SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT); @@ -1831,7 +1857,8 @@ } this->process_time = this->current_time; - this->resync = false; + if (this->resync) + --this->resync; setup_matching(this); @@ -2056,6 +2083,9 @@ if (info && (str = spa_dict_lookup(info, "api.bluez5.a2dp-duplex")) != NULL) this->is_duplex = spa_atob(str); + if (info && (str = spa_dict_lookup(info, "api.bluez5.internal")) != NULL) + this->is_internal = spa_atob(str); + if (info && (str = spa_dict_lookup(info, SPA_KEY_API_BLUEZ5_TRANSPORT))) sscanf(str, "pointer:%p", &this->transport);
View file
pipewire-0.3.68.tar.gz/spa/plugins/bluez5/media-source.c -> pipewire-0.3.69.tar.gz/spa/plugins/bluez5/media-source.c
Changed
@@ -122,6 +122,7 @@ unsigned int is_input:1; unsigned int is_duplex:1; + unsigned int is_internal:1; unsigned int use_duplex_source:1; unsigned int node_latency; @@ -901,7 +902,8 @@ struct spa_dict_item node_info_items = { { SPA_KEY_DEVICE_API, "bluez5" }, - { SPA_KEY_MEDIA_CLASS, this->is_input ? "Audio/Source" : "Stream/Output/Audio" }, + { SPA_KEY_MEDIA_CLASS, this->is_internal ? "Audio/Source/Internal" : + this->is_input ? "Audio/Source" : "Stream/Output/Audio" }, { SPA_KEY_NODE_LATENCY, this->is_input ? "" : latency }, { "media.name", ((this->transport && this->transport->device->name) ? this->transport->device->name : this->codec->bap ? "BAP" : "A2DP") }, @@ -1738,6 +1740,8 @@ this->is_input = spa_streq(str, "input"); if ((str = spa_dict_lookup(info, "api.bluez5.a2dp-duplex")) != NULL) this->is_duplex = spa_atob(str); + if ((str = spa_dict_lookup(info, "api.bluez5.internal")) != NULL) + this->is_internal = spa_atob(str); } if (this->transport == NULL) {
View file
pipewire-0.3.68.tar.gz/src/gst/gstpipewireclock.c -> pipewire-0.3.69.tar.gz/src/gst/gstpipewireclock.c
Changed
@@ -91,6 +91,7 @@ void gst_pipewire_clock_reset (GstPipeWireClock * clock, GstClockTime time) { +#if 0 GstClockTimeDiff time_offset; if (clock->last_time >= time) @@ -104,4 +105,5 @@ "reset clock to %" GST_TIME_FORMAT ", last %" GST_TIME_FORMAT ", offset %" GST_STIME_FORMAT, GST_TIME_ARGS (time), GST_TIME_ARGS (clock->last_time), GST_STIME_ARGS (time_offset)); +#endif }
View file
pipewire-0.3.68.tar.gz/src/modules/module-echo-cancel.c -> pipewire-0.3.69.tar.gz/src/modules/module-echo-cancel.c
Changed
@@ -183,7 +183,9 @@ struct spa_hook core_proxy_listener; struct spa_hook core_listener; - struct spa_audio_info_raw info; + struct spa_audio_info_raw rec_info; + struct spa_audio_info_raw out_info; + struct spa_audio_info_raw play_info; struct pw_properties *capture_props; struct pw_stream *capture; @@ -247,10 +249,13 @@ if (impl->wav_file == NULL) { struct wav_file_info info; - info.info.info.raw = impl->info; - info.info.info.raw.channels *= 3; + spa_zero(info); info.info.media_type = SPA_MEDIA_TYPE_audio; info.info.media_subtype = SPA_MEDIA_SUBTYPE_raw; + info.info.info.raw.format = SPA_AUDIO_FORMAT_F32P; + info.info.info.raw.rate = impl->rec_info.rate; + info.info.info.raw.channels = impl->play_info.channels + + impl->rec_info.channels + impl->out_info.channels; impl->wav_file = wav_file_open(impl->wav_path, "w", &info); @@ -259,14 +264,17 @@ impl->wav_path); } if (impl->wav_file) { - uint32_t i, c = impl->info.channels; - const float *datac * 3; + uint32_t i, n, c = impl->play_info.channels + + impl->rec_info.channels + impl->out_info.channels; + const float *datac; + + for (i = n = 0; i < impl->play_info.channels; i++) + datan++ = playi; + for (i = 0; i < impl->rec_info.channels; i++) + datan++ = reci; + for (i = 0; i < impl->out_info.channels; i++) + datan++ = outi; - for (i = 0; i < c; i++) { - datai = playi; - datai + c = reci; - datai + 2*c = outi; - } wav_file_write(impl->wav_file, (void*)data, n_samples); } else { spa_zero(impl->wav_path); @@ -281,18 +289,17 @@ { struct pw_buffer *cout; struct pw_buffer *pout = NULL; - float rec_bufimpl->info.channelsimpl->aec_blocksize / sizeof(float); - float play_bufimpl->info.channelsimpl->aec_blocksize / sizeof(float); - float play_delayed_bufimpl->info.channelsimpl->aec_blocksize / sizeof(float); - float out_bufimpl->info.channelsimpl->aec_blocksize / sizeof(float); - const float *recimpl->info.channels; - const float *playimpl->info.channels; - const float *play_delayedimpl->info.channels; - float *outimpl->info.channels; + float rec_bufimpl->rec_info.channelsimpl->aec_blocksize / sizeof(float); + float play_bufimpl->play_info.channelsimpl->aec_blocksize / sizeof(float); + float play_delayed_bufimpl->play_info.channelsimpl->aec_blocksize / sizeof(float); + float out_bufimpl->out_info.channelsimpl->aec_blocksize / sizeof(float); + const float *recimpl->rec_info.channels; + const float *playimpl->play_info.channels; + const float *play_delayedimpl->play_info.channels; + float *outimpl->out_info.channels; struct spa_data *dd; uint32_t i, size; uint32_t rindex, pindex, oindex, pdindex, avail; - int32_t stride = 0; if (impl->playback != NULL && (pout = pw_stream_dequeue_buffer(impl->playback)) == NULL) { pw_log_debug("out of playback buffers: %m"); @@ -302,32 +309,37 @@ size = impl->aec_blocksize; /* First read a block from the playback and capture ring buffers */ - spa_ringbuffer_get_read_index(&impl->rec_ring, &rindex); - spa_ringbuffer_get_read_index(&impl->play_ring, &pindex); - spa_ringbuffer_get_read_index(&impl->play_delayed_ring, &pdindex); - for (i = 0; i < impl->info.channels; i++) { + for (i = 0; i < impl->rec_info.channels; i++) { /* captured samples, with echo from sink */ reci = &rec_bufi0; - /* echo from sink */ - playi = &play_bufi0; - /* echo from sink delayed */ - play_delayedi = &play_delayed_bufi0; - /* filtered samples, without echo from sink */ - outi = &out_bufi0; - stride = 0; spa_ringbuffer_read_data(&impl->rec_ring, impl->rec_bufferi, impl->rec_ringsize, rindex % impl->rec_ringsize, (void*)reci, size); - stride = 0; + } + spa_ringbuffer_read_update(&impl->rec_ring, rindex + size); + + for (i = 0; i < impl->out_info.channels; i++) { + /* filtered samples, without echo from sink */ + outi = &out_bufi0; + } + + spa_ringbuffer_get_read_index(&impl->play_ring, &pindex); + spa_ringbuffer_get_read_index(&impl->play_delayed_ring, &pdindex); + + for (i = 0; i < impl->play_info.channels; i++) { + /* echo from sink */ + playi = &play_bufi0; + /* echo from sink delayed */ + play_delayedi = &play_delayed_bufi0; + spa_ringbuffer_read_data(&impl->play_ring, impl->play_bufferi, impl->play_ringsize, pindex % impl->play_ringsize, (void *)playi, size); - stride = 0; spa_ringbuffer_read_data(&impl->play_delayed_ring, impl->play_bufferi, impl->play_ringsize, pdindex % impl->play_ringsize, (void *)play_delayedi, size); @@ -339,11 +351,9 @@ dd->chunk->offset = 0; dd->chunk->size = size; - dd->chunk->stride = stride; + dd->chunk->stride = sizeof(float); } } - - spa_ringbuffer_read_update(&impl->rec_ring, rindex + size); spa_ringbuffer_read_update(&impl->play_ring, pindex + size); spa_ringbuffer_read_update(&impl->play_delayed_ring, pdindex + size); @@ -357,19 +367,20 @@ /* don't run the canceller until play_buffer has been filled, * copy silence to output in the meantime */ silence_size = SPA_MIN(size, delay_left * sizeof(float)); - for (i = 0; i < impl->info.channels; i++) + for (i = 0; i < impl->out_info.channels; i++) memset(outi, 0, silence_size); impl->current_delay += silence_size / sizeof(float); pw_log_debug("current_delay %d", impl->current_delay); if (silence_size != size) { - const float *pdimpl->info.channels; - float *oimpl->info.channels; + const float *pdimpl->play_info.channels; + float *oimpl->out_info.channels; - for (i = 0; i < impl->info.channels; i++) { + for (i = 0; i < impl->play_info.channels; i++) pdi = play_delayedi + delay_left; + for (i = 0; i < impl->out_info.channels; i++) oi = outi + delay_left; - } + aec_run(impl, rec, pd, o, size / sizeof(float) - delay_left); } } else { @@ -393,7 +404,7 @@ avail += drop; } - for (i = 0; i < impl->info.channels; i++) { + for (i = 0; i < impl->out_info.channels; i++) { /* captured samples, with echo from sink */ spa_ringbuffer_write_data(&impl->out_ring, impl->out_bufferi, impl->out_ringsize, oindex % impl->out_ringsize, @@ -412,14 +423,14 @@ break; } - for (i = 0; i < impl->info.channels; i++) { + for (i = 0; i < impl->out_info.channels; i++) { dd = &cout->buffer->datasi; spa_ringbuffer_read_data(&impl->out_ring, impl->out_bufferi, impl->out_ringsize, oindex % impl->out_ringsize, (void *)dd->data, size); dd->chunk->offset = 0; dd->chunk->size = size; - dd->chunk->stride = 0; + dd->chunk->stride = sizeof(float); } pw_stream_queue_buffer(impl->source, cout); @@ -482,7 +493,7 @@ pw_log_debug("Setting AEC block size to %u", impl->aec_blocksize); } - for (i = 0; i < impl->info.channels; i++) { + for (i = 0; i < impl->rec_info.channels; i++) { /* captured samples, with echo from sink */ d = &buf->buffer->datasi; @@ -573,11 +584,12 @@ spa_ringbuffer_init(&impl->play_delayed_ring); spa_ringbuffer_init(&impl->out_ring); - for (i = 0; i < impl->info.channels; i++) { + for (i = 0; i < impl->rec_info.channels; i++) memset(impl->rec_bufferi, 0, impl->rec_ringsize); + for (i = 0; i < impl->play_info.channels; i++) memset(impl->play_bufferi, 0, impl->play_ringsize); + for (i = 0; i < impl->out_info.channels; i++) memset(impl->out_bufferi, 0, impl->out_ringsize); - } spa_ringbuffer_get_write_index(&impl->play_ring, &index); spa_ringbuffer_write_update(&impl->play_ring, index + (sizeof(float) * (impl->buffer_delay))); @@ -856,7 +868,7 @@ pw_log_debug("Setting AEC block size to %u", impl->aec_blocksize); } - for (i = 0; i < impl->info.channels; i++) { + for (i = 0; i < impl->play_info.channels; i++) { /* echo from sink */ d = &buf->buffer->datasi; @@ -1034,14 +1046,15 @@ spa_pod_dynamic_builder_clean(&b); - impl->rec_ringsize = sizeof(float) * impl->max_buffer_size * impl->info.rate / 1000; - impl->play_ringsize = sizeof(float) * ((impl->max_buffer_size * impl->info.rate / 1000) + impl->buffer_delay); - impl->out_ringsize = sizeof(float) * impl->max_buffer_size * impl->info.rate / 1000; - for (i = 0; i < impl->info.channels; i++) { + impl->rec_ringsize = sizeof(float) * impl->max_buffer_size * impl->rec_info.rate / 1000; + impl->play_ringsize = sizeof(float) * ((impl->max_buffer_size * impl->play_info.rate / 1000) + impl->buffer_delay); + impl->out_ringsize = sizeof(float) * impl->max_buffer_size * impl->out_info.rate / 1000; + for (i = 0; i < impl->rec_info.channels; i++) impl->rec_bufferi = malloc(impl->rec_ringsize); + for (i = 0; i < impl->play_info.channels; i++) impl->play_bufferi = malloc(impl->play_ringsize); + for (i = 0; i < impl->out_info.channels; i++) impl->out_bufferi = malloc(impl->out_ringsize); - } reset_buffers(impl); @@ -1101,14 +1114,12 @@ pw_properties_free(impl->playback_props); pw_properties_free(impl->sink_props); - for (i = 0; i < impl->info.channels; i++) { - if (impl->rec_bufferi) - free(impl->rec_bufferi); - if (impl->play_bufferi) - free(impl->play_bufferi); - if (impl->out_bufferi) - free(impl->out_bufferi); - } + for (i = 0; i < impl->rec_info.channels; i++) + free(impl->rec_bufferi); + for (i = 0; i < impl->play_info.channels; i++) + free(impl->play_bufferi); + for (i = 0; i < impl->out_info.channels; i++) + free(impl->out_bufferi); free(impl); } @@ -1189,6 +1200,7 @@ { struct pw_context *context = pw_impl_module_get_context(module); struct pw_properties *props, *aec_props; + struct spa_audio_info_raw info; struct impl *impl; uint32_t id = pw_global_get_id(pw_impl_module_get_global(module)); uint32_t pid = getpid(); @@ -1243,12 +1255,12 @@ if (pw_properties_get(props, "resample.prefill") == NULL) pw_properties_set(props, "resample.prefill", "true"); - parse_audio_info(props, &impl->info); + parse_audio_info(props, &info); - impl->capture_info = impl->info; - impl->source_info = impl->info; - impl->sink_info = impl->info; - impl->playback_info = impl->info; + impl->capture_info = info; + impl->source_info = info; + impl->sink_info = info; + impl->playback_info = info; if ((str = pw_properties_get(props, "capture.props")) != NULL) pw_properties_update_string(impl->capture_props, str, strlen(str)); @@ -1296,6 +1308,48 @@ pw_properties_set(impl->sink_props, PW_KEY_STREAM_CAPTURE_SINK, "true"); } + copy_props(impl, props, PW_KEY_NODE_GROUP); + copy_props(impl, props, PW_KEY_NODE_LINK_GROUP); + copy_props(impl, props, PW_KEY_NODE_VIRTUAL); + copy_props(impl, props, SPA_KEY_AUDIO_CHANNELS); + copy_props(impl, props, SPA_KEY_AUDIO_POSITION); + copy_props(impl, props, "resample.prefill"); + + impl->max_buffer_size = pw_properties_get_uint32(props,"buffer.max_size", MAX_BUFSIZE_MS); + + if ((str = pw_properties_get(props, "buffer.play_delay")) != NULL) { + int req_num, req_denom; + if (sscanf(str, "%u/%u", &req_num, &req_denom) == 2) { + if (req_denom != 0) { + impl->buffer_delay = (info.rate * req_num) / req_denom; + } else { + impl->buffer_delay = DELAY_MS * info.rate / 1000; + pw_log_warn("Sample rate for buffer.play_delay is 0 using default"); + } + } else { + impl->buffer_delay = DELAY_MS * info.rate / 1000; + pw_log_warn("Wrong value/format for buffer.play_delay using default"); + } + } else { + impl->buffer_delay = DELAY_MS * info.rate / 1000; + } + + if ((str = pw_properties_get(impl->capture_props, SPA_KEY_AUDIO_POSITION)) != NULL) { + parse_position(&impl->capture_info, str, strlen(str)); + } + if ((str = pw_properties_get(impl->source_props, SPA_KEY_AUDIO_POSITION)) != NULL) { + parse_position(&impl->source_info, str, strlen(str)); + } + if ((str = pw_properties_get(impl->sink_props, SPA_KEY_AUDIO_POSITION)) != NULL) { + parse_position(&impl->sink_info, str, strlen(str)); + impl->playback_info = impl->sink_info; + } + if ((str = pw_properties_get(impl->playback_props, SPA_KEY_AUDIO_POSITION)) != NULL) { + parse_position(&impl->playback_info, str, strlen(str)); + if (impl->playback_info.channels != impl->sink_info.channels) + impl->playback_info = impl->sink_info; + } + if ((str = pw_properties_get(props, "aec.method")) != NULL) pw_log_warn("aec.method is not supported anymore use library.name"); @@ -1313,12 +1367,12 @@ return -EINVAL; } - struct spa_dict_item info_items = { + struct spa_dict_item dict_items = { { SPA_KEY_LIBRARY_NAME, path }, }; - struct spa_dict info = SPA_DICT_INIT_ARRAY(info_items); + struct spa_dict dict = SPA_DICT_INIT_ARRAY(dict_items); - handle = spa_plugin_loader_load(impl->loader, SPA_NAME_AEC, &info); + handle = spa_plugin_loader_load(impl->loader, SPA_NAME_AEC, &dict); if (handle == NULL) { pw_log_error("aec plugin %s not available library.name %s", SPA_NAME_AEC, path); return -ENOENT; @@ -1346,7 +1400,39 @@ else aec_props = pw_properties_new(NULL, NULL); - res = spa_audio_aec_init(impl->aec, &aec_props->dict, &impl->info); + + if (spa_interface_callback_check(&impl->aec->iface, struct spa_audio_aec_methods, init2, 3)) { + impl->rec_info = impl->capture_info; + impl->out_info = impl->source_info; + impl->play_info = impl->sink_info; + + res = spa_audio_aec_init2(impl->aec, &aec_props->dict, + &impl->rec_info, &impl->out_info, &impl->play_info); + + if (impl->sink_info.channels != impl->play_info.channels) + impl->sink_info = impl->play_info; + if (impl->playback_info.channels != impl->play_info.channels) + impl->playback_info = impl->play_info; + if (impl->capture_info.channels != impl->rec_info.channels) + impl->capture_info = impl->rec_info; + if (impl->source_info.channels != impl->out_info.channels) + impl->source_info = impl->out_info; + } else { + if (impl->source_info.channels != impl->sink_info.channels) + impl->source_info = impl->sink_info; + if (impl->capture_info.channels != impl->source_info.channels) + impl->capture_info = impl->source_info; + if (impl->playback_info.channels != impl->sink_info.channels) + impl->playback_info = impl->sink_info; + + info = impl->playback_info; + + res = spa_audio_aec_init(impl->aec, &aec_props->dict, &info); + + impl->rec_info.channels = info.channels; + impl->out_info.channels = info.channels; + impl->play_info.channels = info.channels; + } pw_properties_free(aec_props); @@ -1372,17 +1458,19 @@ if (factor == 0 || new_num == 0) { pw_log_info("Setting node latency to %s", impl->aec->latency); pw_properties_set(props, PW_KEY_NODE_LATENCY, impl->aec->latency); - impl->aec_blocksize = sizeof(float) * impl->info.rate * num / denom; + impl->aec_blocksize = sizeof(float) * info.rate * num / denom; } else { pw_log_info("Setting node latency to %u/%u", new_num, req_denom); pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", new_num, req_denom); - impl->aec_blocksize = sizeof(float) * impl->info.rate * num / denom * factor; + impl->aec_blocksize = sizeof(float) * info.rate * num / denom * factor; } } else { /* Implementation doesn't care about the block size */ impl->aec_blocksize = 0; } + copy_props(impl, props, PW_KEY_NODE_LATENCY); + impl->core = pw_context_get_object(impl->context, PW_TYPE_INTERFACE_Core); if (impl->core == NULL) { str = pw_properties_get(props, PW_KEY_REMOTE_NAME); @@ -1399,51 +1487,6 @@ goto error; } - copy_props(impl, props, PW_KEY_NODE_GROUP); - copy_props(impl, props, PW_KEY_NODE_LINK_GROUP); - copy_props(impl, props, PW_KEY_NODE_VIRTUAL); - copy_props(impl, props, PW_KEY_NODE_LATENCY); - copy_props(impl, props, SPA_KEY_AUDIO_CHANNELS); - copy_props(impl, props, SPA_KEY_AUDIO_POSITION); - copy_props(impl, props, "resample.prefill"); - - if ((str = pw_properties_get(impl->capture_props, SPA_KEY_AUDIO_POSITION)) != NULL) - parse_position(&impl->capture_info, str, strlen(str)); - if ((str = pw_properties_get(impl->source_props, SPA_KEY_AUDIO_POSITION)) != NULL) - parse_position(&impl->source_info, str, strlen(str)); - if ((str = pw_properties_get(impl->sink_props, SPA_KEY_AUDIO_POSITION)) != NULL) - parse_position(&impl->sink_info, str, strlen(str)); - if ((str = pw_properties_get(impl->playback_props, SPA_KEY_AUDIO_POSITION)) != NULL) - parse_position(&impl->playback_info, str, strlen(str)); - - if (impl->capture_info.channels != impl->info.channels) - impl->capture_info = impl->info; - if (impl->source_info.channels != impl->info.channels) - impl->source_info = impl->info; - if (impl->sink_info.channels != impl->info.channels) - impl->sink_info = impl->info; - if (impl->playback_info.channels != impl->info.channels) - impl->playback_info = impl->info; - - impl->max_buffer_size = pw_properties_get_uint32(props,"buffer.max_size", MAX_BUFSIZE_MS); - - if ((str = pw_properties_get(props, "buffer.play_delay")) != NULL) { - int req_num, req_denom; - if (sscanf(str, "%u/%u", &req_num, &req_denom) == 2) { - if (req_denom != 0) { - impl->buffer_delay = (impl->info.rate*req_num)/req_denom; - } else { - impl->buffer_delay = DELAY_MS * impl->info.rate / 1000; - pw_log_warn("Sample rate for buffer.play_delay is 0 using default"); - } - } else { - impl->buffer_delay = DELAY_MS * impl->info.rate / 1000; - pw_log_warn("Wrong value/format for buffer.play_delay using default"); - } - } else { - impl->buffer_delay = DELAY_MS * impl->info.rate / 1000; - } - pw_properties_free(props); pw_proxy_add_listener((struct pw_proxy*)impl->core,
View file
pipewire-0.3.68.tar.gz/src/modules/module-filter-chain.c -> pipewire-0.3.69.tar.gz/src/modules/module-filter-chain.c
Changed
@@ -162,7 +162,11 @@ * * All biquad filters have an input port "In" and an output port "Out". They have * a "Freq", "Q" and "Gain" control. Their meaning depends on the particular biquad that - * is used. The following labels can be used: + * is used. The biquads also have "b0", "b1", "b2", "a0", "a1" and "a2" ports that + * are read-only except for the bq_raw biquad, which can configure default values + * depending on the graph rate and change those at runtime. + * + * The following labels can be used: * * - `bq_lowpass` a lowpass filter. * - `bq_highpass` a highpass filter. @@ -172,6 +176,30 @@ * - `bq_peaking` a peaking filter. * - `bq_notch` a notch filter. * - `bq_allpass` an allpass filter. + * - `bq_raw` a raw biquad filter. You need a config section to specify coefficients + * per sample rate. The coefficients of the sample rate closest to the + * graph rate are selected: + * + *\code{.unparsed} + * filter.graph = { + * nodes = + * { + * type = builtin + * name = ... + * label = bq_raw + * config = { + * coefficients = + * { rate = 44100, b0=.., b1=.., b2=.., a0=.., a1=.., a2=.. }, + * { rate = 48000, b0=.., b1=.., b2=.., a0=.., a1=.., a2=.. }, + * { rate = 192000, b0=.., b1=.., b2=.., a0=.., a1=.., a2=.. } + * + * } + * ... + * } + * } + * ... + * } + *\endcode * * ### Convolver * @@ -1006,6 +1034,20 @@ node->control_changed = false; } +static void update_props_param(struct impl *impl) +{ + struct graph *graph = &impl->graph; + uint8_t buffer1024; + struct spa_pod_dynamic_builder b; + const struct spa_pod *params1; + + spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096); + params0 = get_props_param(graph, &b.b); + + pw_stream_update_params(impl->capture, params, 1); + spa_pod_dynamic_builder_clean(&b); +} + static void param_props_changed(struct impl *impl, const struct spa_pod *param) { struct spa_pod_object *obj = (struct spa_pod_object *) param; @@ -1018,19 +1060,12 @@ changed += parse_params(graph, &prop->value); } if (changed > 0) { - uint8_t buffer1024; - struct spa_pod_dynamic_builder b; - const struct spa_pod *params1; struct node *node; spa_list_for_each(node, &graph->node_list, link) node_control_changed(node); - spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096); - params0 = get_props_param(graph, &b.b); - - pw_stream_update_params(impl->capture, params, 1); - spa_pod_dynamic_builder_clean(&b); + update_props_param(impl); } } @@ -1847,6 +1882,7 @@ d->control_changed(node->hndli); } } + update_props_param(impl); return 0; error: graph_cleanup(graph);
View file
pipewire-0.3.68.tar.gz/src/modules/module-filter-chain/builtin_plugin.c -> pipewire-0.3.69.tar.gz/src/modules/module-filter-chain/builtin_plugin.c
Changed
@@ -32,10 +32,13 @@ unsigned long rate; float *port64; + int type; struct biquad bq; float freq; float Q; float gain; + float b0, b1, b2; + float a0, a1, a2; }; static void *builtin_instantiate(const struct fc_descriptor * Descriptor, @@ -215,6 +218,159 @@ .cleanup = builtin_cleanup, }; +/** biquads */ +static int bq_type_from_name(const char *name) +{ + if (spa_streq(name, "bq_lowpass")) + return BQ_LOWPASS; + if (spa_streq(name, "bq_highpass")) + return BQ_HIGHPASS; + if (spa_streq(name, "bq_bandpass")) + return BQ_BANDPASS; + if (spa_streq(name, "bq_lowshelf")) + return BQ_LOWSHELF; + if (spa_streq(name, "bq_highshelf")) + return BQ_HIGHSHELF; + if (spa_streq(name, "bq_peaking")) + return BQ_PEAKING; + if (spa_streq(name, "bq_notch")) + return BQ_NOTCH; + if (spa_streq(name, "bq_allpass")) + return BQ_ALLPASS; + if (spa_streq(name, "bq_raw")) + return BQ_NONE; + return BQ_NONE; +} + +static void bq_raw_update(struct builtin *impl, float b0, float b1, float b2, + float a0, float a1, float a2) +{ + struct biquad *bq = &impl->bq; + impl->b0 = b0; + impl->b1 = b1; + impl->b2 = b2; + impl->a0 = a0; + impl->a1 = a1; + impl->a2 = a2; + if (a0 != 0.0f) + a0 = 1.0f / a0; + bq->b0 = impl->b0 * a0; + bq->b1 = impl->b1 * a0; + bq->b2 = impl->b2 * a0; + bq->a1 = impl->a1 * a0; + bq->a2 = impl->a2 * a0; + bq->x1 = bq->x2 = bq->y1 = bq->y2 = 0.0; +} + +/* + * config = { + * coefficients = + * { rate = 44100, b0=.., b1=.., b2=.., a0=.., a1=.., a2=.. }, + * { rate = 48000, b0=.., b1=.., b2=.., a0=.., a1=.., a2=.. }, + * { rate = 192000, b0=.., b1=.., b2=.., a0=.., a1=.., a2=.. } + * + * } + */ +static void *bq_instantiate(const struct fc_descriptor * Descriptor, + unsigned long SampleRate, int index, const char *config) +{ + struct builtin *impl; + struct spa_json it4; + const char *val; + char key256; + uint32_t best_rate = 0; + + impl = calloc(1, sizeof(*impl)); + if (impl == NULL) + return NULL; + + impl->rate = SampleRate; + impl->b0 = impl->a0 = 1.0f; + impl->type = bq_type_from_name(Descriptor->name); + + if (config == NULL) + goto error; + + spa_json_init(&it0, config, strlen(config)); + if (spa_json_enter_object(&it0, &it1) <= 0) + goto error; + + while (spa_json_get_string(&it1, key, sizeof(key)) > 0) { + if (spa_streq(key, "coefficients")) { + if (spa_json_enter_array(&it1, &it2) <= 0) { + pw_log_error("biquads:coefficients require an array"); + goto error; + } + while (spa_json_enter_object(&it2, &it3) > 0) { + int32_t rate = 0; + float b0 = 1.0f, b1 = 0.0f, b2 = 0.0f; + float a0 = 1.0f, a1 = 0.0f, a2 = 0.0f; + + while (spa_json_get_string(&it3, key, sizeof(key)) > 0) { + if (spa_streq(key, "rate")) { + if (spa_json_get_int(&it3, &rate) <= 0) { + pw_log_error("biquads:rate requires a number"); + goto error; + } + } + else if (spa_streq(key, "b0")) { + if (spa_json_get_float(&it3, &b0) <= 0) { + pw_log_error("biquads:b0 requires a float"); + goto error; + } + } + else if (spa_streq(key, "b1")) { + if (spa_json_get_float(&it3, &b1) <= 0) { + pw_log_error("biquads:b1 requires a float"); + goto error; + } + } + else if (spa_streq(key, "b2")) { + if (spa_json_get_float(&it3, &b2) <= 0) { + pw_log_error("biquads:b2 requires a float"); + goto error; + } + } + else if (spa_streq(key, "a0")) { + if (spa_json_get_float(&it3, &a0) <= 0) { + pw_log_error("biquads:a0 requires a float"); + goto error; + } + } + else if (spa_streq(key, "a1")) { + if (spa_json_get_float(&it3, &a1) <= 0) { + pw_log_error("biquads:a1 requires a float"); + goto error; + } + } + else if (spa_streq(key, "a2")) { + if (spa_json_get_float(&it3, &a2) <= 0) { + pw_log_error("biquads:a0 requires a float"); + goto error; + } + } + else if (spa_json_next(&it1, &val) < 0) + break; + } + if (labs((long)rate - (long)SampleRate) < + labs((long)best_rate - (long)SampleRate)) { + best_rate = rate; + bq_raw_update(impl, b0, b1, b2, a0, a1, a2); + } + } + } + else if (spa_json_next(&it1, &val) < 0) + break; + } + + return impl; +error: + free(impl); + errno = EINVAL; + return NULL; +} + +#define BQ_NUM_PORTS 11 static struct fc_port bq_ports = { { .index = 0, .name = "Out", @@ -240,176 +396,225 @@ .flags = FC_PORT_INPUT | FC_PORT_CONTROL, .def = 0.0f, .min = -120.0f, .max = 20.0f, }, + { .index = 5, + .name = "b0", + .flags = FC_PORT_INPUT | FC_PORT_CONTROL, + .def = 1.0f, .min = -10.0f, .max = 10.0f, + }, + { .index = 6, + .name = "b1", + .flags = FC_PORT_INPUT | FC_PORT_CONTROL, + .def = 0.0f, .min = -10.0f, .max = 10.0f, + }, + { .index = 7, + .name = "b2", + .flags = FC_PORT_INPUT | FC_PORT_CONTROL, + .def = 0.0f, .min = -10.0f, .max = 10.0f, + }, + { .index = 8, + .name = "a0", + .flags = FC_PORT_INPUT | FC_PORT_CONTROL, + .def = 1.0f, .min = -10.0f, .max = 10.0f, + }, + { .index = 9, + .name = "a1", + .flags = FC_PORT_INPUT | FC_PORT_CONTROL, + .def = 0.0f, .min = -10.0f, .max = 10.0f, + }, + { .index = 10, + .name = "a2", + .flags = FC_PORT_INPUT | FC_PORT_CONTROL, + .def = 0.0f, .min = -10.0f, .max = 10.0f, + }, + }; -static void bq_run(struct builtin *impl, unsigned long samples, int type) +static void bq_freq_update(struct builtin *impl, int type, float freq, float Q, float gain) { struct biquad *bq = &impl->bq; - float *out = impl->port0; - float *in = impl->port1; - float freq = impl->port20; - float Q = impl->port30; - float gain = impl->port40; - - if (impl->freq != freq || impl->Q != Q || impl->gain != gain) { - impl->freq = freq; - impl->Q = Q; - impl->gain = gain; - biquad_set(bq, type, freq * 2 / impl->rate, Q, gain); + impl->freq = freq; + impl->Q = Q; + impl->gain = gain; + biquad_set(bq, type, freq * 2 / impl->rate, Q, gain); + impl->port50 = impl->b0 = bq->b0; + impl->port60 = impl->b1 = bq->b1; + impl->port70 = impl->b2 = bq->b2; + impl->port80 = impl->a0 = 1.0f; + impl->port90 = impl->a1 = bq->a1; + impl->port100 = impl->a2 = bq->a2; +} + +static void bq_activate(void * Instance) +{ + struct builtin *impl = Instance; + if (impl->type == BQ_NONE) { + impl->port50 = impl->b0; + impl->port60 = impl->b1; + impl->port70 = impl->b2; + impl->port80 = impl->a0; + impl->port90 = impl->a1; + impl->port100 = impl->a2; + } else { + float freq = impl->port20; + float Q = impl->port30; + float gain = impl->port40; + bq_freq_update(impl, impl->type, freq, Q, gain); } - dsp_ops_biquad_run(dsp_ops, bq, out, in, samples); } -/** bq_lowpass */ -static void bq_lowpass_run(void * Instance, unsigned long SampleCount) +static void bq_run(void *Instance, unsigned long samples) { struct builtin *impl = Instance; - bq_run(impl, SampleCount, BQ_LOWPASS); + struct biquad *bq = &impl->bq; + float *out = impl->port0; + float *in = impl->port1; + + if (impl->type == BQ_NONE) { + float b0, b1, b2, a0, a1, a2; + b0 = impl->port50; + b1 = impl->port60; + b2 = impl->port70; + a0 = impl->port80; + a1 = impl->port90; + a2 = impl->port100; + if (impl->b0 != b0 || impl->b1 != b1 || impl->b2 != b2 || + impl->a0 != a0 || impl->a1 != a1 || impl->a2 != a2) { + bq_raw_update(impl, b0, b1, b2, a0, a1, a2); + } + } else { + float freq = impl->port20; + float Q = impl->port30; + float gain = impl->port40; + if (impl->freq != freq || impl->Q != Q || impl->gain != gain) + bq_freq_update(impl, impl->type, freq, Q, gain); + } + dsp_ops_biquad_run(dsp_ops, bq, out, in, samples); } +/** bq_lowpass */ static const struct fc_descriptor bq_lowpass_desc = { .name = "bq_lowpass", - .n_ports = 5, + .n_ports = BQ_NUM_PORTS, .ports = bq_ports, - .instantiate = builtin_instantiate, + .instantiate = bq_instantiate, .connect_port = builtin_connect_port, - .run = bq_lowpass_run, + .activate = bq_activate, + .run = bq_run, .cleanup = builtin_cleanup, }; /** bq_highpass */ -static void bq_highpass_run(void * Instance, unsigned long SampleCount) -{ - struct builtin *impl = Instance; - bq_run(impl, SampleCount, BQ_HIGHPASS); -} - static const struct fc_descriptor bq_highpass_desc = { .name = "bq_highpass", - .n_ports = 5, + .n_ports = BQ_NUM_PORTS, .ports = bq_ports, - .instantiate = builtin_instantiate, + .instantiate = bq_instantiate, .connect_port = builtin_connect_port, - .run = bq_highpass_run, + .activate = bq_activate, + .run = bq_run, .cleanup = builtin_cleanup, }; /** bq_bandpass */ -static void bq_bandpass_run(void * Instance, unsigned long SampleCount) -{ - struct builtin *impl = Instance; - bq_run(impl, SampleCount, BQ_BANDPASS); -} - static const struct fc_descriptor bq_bandpass_desc = { .name = "bq_bandpass", - .n_ports = 5, + .n_ports = BQ_NUM_PORTS, .ports = bq_ports, - .instantiate = builtin_instantiate, + .instantiate = bq_instantiate, .connect_port = builtin_connect_port, - .run = bq_bandpass_run, + .activate = bq_activate, + .run = bq_run, .cleanup = builtin_cleanup, }; /** bq_lowshelf */ -static void bq_lowshelf_run(void * Instance, unsigned long SampleCount) -{ - struct builtin *impl = Instance; - bq_run(impl, SampleCount, BQ_LOWSHELF); -} - static const struct fc_descriptor bq_lowshelf_desc = { .name = "bq_lowshelf", - .n_ports = 5, + .n_ports = BQ_NUM_PORTS, .ports = bq_ports, - .instantiate = builtin_instantiate, + .instantiate = bq_instantiate, .connect_port = builtin_connect_port, - .run = bq_lowshelf_run, + .activate = bq_activate, + .run = bq_run, .cleanup = builtin_cleanup, }; /** bq_highshelf */ -static void bq_highshelf_run(void * Instance, unsigned long SampleCount) -{ - struct builtin *impl = Instance; - bq_run(impl, SampleCount, BQ_HIGHSHELF); -} - static const struct fc_descriptor bq_highshelf_desc = { .name = "bq_highshelf", - .n_ports = 5, + .n_ports = BQ_NUM_PORTS, .ports = bq_ports, - .instantiate = builtin_instantiate, + .instantiate = bq_instantiate, .connect_port = builtin_connect_port, - .run = bq_highshelf_run, + .activate = bq_activate, + .run = bq_run, .cleanup = builtin_cleanup, }; /** bq_peaking */ -static void bq_peaking_run(void * Instance, unsigned long SampleCount) -{ - struct builtin *impl = Instance; - bq_run(impl, SampleCount, BQ_PEAKING); -} - static const struct fc_descriptor bq_peaking_desc = { .name = "bq_peaking", - .n_ports = 5, + .n_ports = BQ_NUM_PORTS, .ports = bq_ports, - .instantiate = builtin_instantiate, + .instantiate = bq_instantiate, .connect_port = builtin_connect_port, - .run = bq_peaking_run, + .activate = bq_activate, + .run = bq_run, .cleanup = builtin_cleanup, }; /** bq_notch */ -static void bq_notch_run(void * Instance, unsigned long SampleCount) -{ - struct builtin *impl = Instance; - bq_run(impl, SampleCount, BQ_NOTCH); -} - static const struct fc_descriptor bq_notch_desc = { .name = "bq_notch", - .n_ports = 5, + .n_ports = BQ_NUM_PORTS, .ports = bq_ports, - .instantiate = builtin_instantiate, + .instantiate = bq_instantiate, .connect_port = builtin_connect_port, - .run = bq_notch_run, + .activate = bq_activate, + .run = bq_run, .cleanup = builtin_cleanup, }; /** bq_allpass */ -static void bq_allpass_run(void * Instance, unsigned long SampleCount) -{ - struct builtin *impl = Instance; - bq_run(impl, SampleCount, BQ_ALLPASS); -} - static const struct fc_descriptor bq_allpass_desc = { .name = "bq_allpass", - .n_ports = 5, + .n_ports = BQ_NUM_PORTS, .ports = bq_ports, - .instantiate = builtin_instantiate, + .instantiate = bq_instantiate, + .connect_port = builtin_connect_port, + .activate = bq_activate, + .run = bq_run, + .cleanup = builtin_cleanup, +}; + +/* bq_raw */ +static const struct fc_descriptor bq_raw_desc = { + .name = "bq_raw", + + .n_ports = BQ_NUM_PORTS, + .ports = bq_ports, + + .instantiate = bq_instantiate, .connect_port = builtin_connect_port, - .run = bq_allpass_run, + .activate = bq_activate, + .run = bq_run, .cleanup = builtin_cleanup, }; @@ -958,6 +1163,7 @@ .cleanup = delay_cleanup, }; +/* invert */ static void invert_run(void * Instance, unsigned long SampleCount) { struct builtin *impl = Instance; @@ -1019,6 +1225,8 @@ return &delay_desc; case 12: return &invert_desc; + case 13: + return &bq_raw_desc; } return NULL; }
View file
pipewire-0.3.68.tar.gz/src/pipewire/context.c -> pipewire-0.3.69.tar.gz/src/pipewire/context.c
Changed
@@ -885,13 +885,16 @@ pw_impl_link_prepare(l); - if (!l->prepared || t->visited) + if (!l->prepared || (t != n && t->visited)) continue; if (!l->passive) t->runnable = true; - t->visited = true; - spa_list_append(&queue, &t->sort_link); + + if (!t->visited) { + t->visited = true; + spa_list_append(&queue, &t->sort_link); + } } } spa_list_for_each(p, &n->output_ports, link) { @@ -903,13 +906,16 @@ pw_impl_link_prepare(l); - if (!l->prepared || t->visited) + if (!l->prepared || (t != n && t->visited)) continue; if (!l->passive) t->runnable = true; - t->visited = true; - spa_list_append(&queue, &t->sort_link); + + if (!t->visited) { + t->visited = true; + spa_list_append(&queue, &t->sort_link); + } } } /* now go through all the nodes that have the same group and @@ -943,6 +949,8 @@ spa_list_consume(n, nodes, sort_link) { spa_list_remove(&n->sort_link); + driver->runnable |= n->runnable; + pw_log_debug(" follower: %p %s runnable:%u driver-runnable:%u", n, n->name, n->runnable, driver->runnable); pw_impl_node_set_driver(n, driver);
View file
pipewire-0.3.68.tar.gz/src/pipewire/thread-loop.c -> pipewire-0.3.69.tar.gz/src/pipewire/thread-loop.c
Changed
@@ -240,6 +240,7 @@ pw_thread_loop_stop(loop); + pw_loop_set_callbacks(loop->loop, NULL, NULL); spa_hook_remove(&loop->hook); spa_hook_list_clean(&loop->listener_list);
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.