alsa-ucm: fix persistent port names with alsa-lib >= 1.2.5
authorIgor V. Kovalenko <igor.v.kovalenko@gmail.com>
Wed, 30 Jun 2021 19:12:55 +0000 (22:12 +0300)
committerTanu Kaskinen <tanuk@iki.fi>
Mon, 12 Jul 2021 10:49:21 +0000 (13:49 +0300)
Alsa UCM device string can contain private configuration prefix required to make
correct device open call. Private prefix is dynamically generated by UCM manager
depending on internal state. Since pulseaudio sink/source port names currently
contain device string, these may change between runs breaking volume database
and module arguments referring to sink/source.

Fix this by skipping UCM private prefix available via `_alibpref` key while
creating UCM mapping name. Mapping object will still contain unmodified
device string for device open call.

See also https://github.com/alsa-project/alsa-ucm-conf/issues/104

Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/598>

src/modules/alsa/alsa-ucm.c

index e34e466..fe6873c 100644 (file)
@@ -1538,6 +1538,32 @@ static void alsa_mapping_add_ucm_modifier(pa_alsa_mapping *m, pa_alsa_ucm_modifi
         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 *device_str, bool is_sink) {
+    pa_alsa_mapping *m;
+    char *mapping_name;
+    size_t ucm_alibpref_len = 0;
+    const char *value;
+
+    /* find private alsa-lib's configuration device prefix */
+    if (snd_use_case_get(ucm->ucm_mgr, "_alibpref", &value) == 0) {
+        if (value[0] && pa_startswith(device_str, value))
+            ucm_alibpref_len = strlen(value);
+
+        free((void *)value);
+    }
+
+    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);
+
+    if (!m)
+        pa_log("No mapping for %s", mapping_name);
+
+    pa_xfree(mapping_name);
+
+    return m;
+}
+
 static int ucm_create_mapping_direction(
         pa_alsa_ucm_config *ucm,
         pa_alsa_profile_set *ps,
@@ -1549,19 +1575,14 @@ static int ucm_create_mapping_direction(
         bool is_sink) {
 
     pa_alsa_mapping *m;
-    char *mapping_name;
     unsigned priority, rate, channels;
 
-    mapping_name = pa_sprintf_malloc("Mapping %s: %s: %s", verb_name, device_str, is_sink ? "sink" : "source");
+    m = ucm_alsa_mapping_get(ucm, ps, verb_name, device_str, is_sink);
 
-    m = pa_alsa_mapping_get(ps, mapping_name);
-    if (!m) {
-        pa_log("No mapping for %s", mapping_name);
-        pa_xfree(mapping_name);
+    if (!m)
         return -1;
-    }
-    pa_log_debug("UCM mapping: %s dev %s", mapping_name, device_name);
-    pa_xfree(mapping_name);
+
+    pa_log_debug("UCM mapping: %s dev %s", m->name, device_name);
 
     priority = is_sink ? device->playback_priority : device->capture_priority;
     rate = is_sink ? device->playback_rate : device->capture_rate;
@@ -1606,18 +1627,13 @@ static int ucm_create_mapping_for_modifier(
         bool is_sink) {
 
     pa_alsa_mapping *m;
-    char *mapping_name;
 
-    mapping_name = pa_sprintf_malloc("Mapping %s: %s: %s", verb_name, device_str, is_sink ? "sink" : "source");
+    m = ucm_alsa_mapping_get(ucm, ps, verb_name, device_str, is_sink);
 
-    m = pa_alsa_mapping_get(ps, mapping_name);
-    if (!m) {
-        pa_log("no mapping for %s", mapping_name);
-        pa_xfree(mapping_name);
+    if (!m)
         return -1;
-    }
-    pa_log_info("ucm mapping: %s modifier %s", mapping_name, mod_name);
-    pa_xfree(mapping_name);
+
+    pa_log_info("UCM mapping: %s modifier %s", m->name, mod_name);
 
     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);