diff --git a/spa/plugins/alsa/acp/acp.c b/spa/plugins/alsa/acp/acp.c index f21eec6c9..7599328a6 100644 --- a/spa/plugins/alsa/acp/acp.c +++ b/spa/plugins/alsa/acp/acp.c @@ -675,7 +675,7 @@ struct temp_port_avail { 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); + snd_hctl_elem_t **_elem = snd_mixer_elem_get_private(melem), *elem; snd_ctl_elem_value_t *elem_value; bool plugged_in, any_input_port_available; void *state; @@ -685,6 +685,8 @@ static int report_jack_state(snd_mixer_elem_t *melem, unsigned int mask) 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 @@ -953,13 +955,17 @@ static pa_device_port* find_port_with_eld_device(pa_card *impl, int device) 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); - int device = snd_hctl_elem_get_device(elem); + snd_hctl_elem_t **_elem = snd_mixer_elem_get_private(melem), *elem; + int device; 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; diff --git a/spa/plugins/alsa/acp/alsa-util.c b/spa/plugins/alsa/acp/alsa-util.c index 38ae934eb..8d53fdd66 100644 --- a/spa/plugins/alsa/acp/alsa-util.c +++ b/spa/plugins/alsa/acp/alsa-util.c @@ -1604,10 +1604,11 @@ static snd_mixer_elem_t *pa_alsa_mixer_find(snd_mixer_t *mixer, snd_mixer_elem_t *elem; for (elem = snd_mixer_first_elem(mixer); elem; elem = snd_mixer_elem_next(elem)) { - snd_hctl_elem_t *helem; + snd_hctl_elem_t **_helem, *helem; if (snd_mixer_elem_get_type(elem) != SND_MIXER_ELEM_PULSEAUDIO) continue; - helem = snd_mixer_elem_get_private(elem); + _helem = snd_mixer_elem_get_private(elem); + helem = *_helem; if (snd_hctl_elem_get_interface(helem) != iface) continue; if (!pa_streq(snd_hctl_elem_get_name(helem), name)) @@ -1635,15 +1636,25 @@ static int mixer_class_compare(const snd_mixer_elem_t *c1, const snd_mixer_elem_ 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 defined as '~0U`. if (mask == SND_CTL_EVENT_MASK_REMOVE) { // 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. + _helem = snd_mixer_elem_get_private(melem); + *_helem = NULL; 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); @@ -1655,24 +1666,48 @@ static int mixer_class_event(snd_mixer_class_t *class, unsigned int mask, const int device = snd_hctl_elem_get_device(helem); snd_mixer_elem_t *new_melem; + bool found = true; + 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, NULL)) < 0) { + if ((err = snd_mixer_elem_new(&new_melem, SND_MIXER_ELEM_PULSEAUDIO, 0, _helem, mixer_melem_free)) < 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 ((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 (!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; + } } } }