[pulseaudio] 01/02: Backport from upstream to fix audio switching away from HDMI monitor when it sleeps

Luke Yelavich themuso-guest at moszumanska.debian.org
Wed Jan 18 05:30:31 UTC 2017


This is an automated email from the git hooks/post-receive script.

themuso-guest pushed a commit to branch ubuntu-xenial
in repository pulseaudio.

commit d8f501fd12c0443487725f02f339e27212ceb944
Author: Luke Yelavich <themuso at ubuntu.com>
Date:   Tue Nov 22 11:16:36 2016 +1100

    Backport from upstream to fix audio switching away from HDMI monitor when it sleeps
---
 debian/changelog                                   |  11 +
 ...0101-card-add-preferred-input-output-port.patch | 156 ++++++++++
 ...ort-available-prefer-ports-that-have-been.patch | 316 +++++++++++++++++++++
 ...0203-card-Add-hook-before-profile-changes.patch |   6 +-
 debian/patches/series                              |   2 +
 5 files changed, 488 insertions(+), 3 deletions(-)

diff --git a/debian/changelog b/debian/changelog
index 4ad5468..c2db4ff 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,14 @@
+pulseaudio (1:8.0-0ubuntu3.2) UNRELEASED; urgency=medium
+
+  * 0101-card-add-preferred-input-output-port.patch,
+    0102-switch-on-port-available-prefer-ports-that-have-been.patch,
+    0103-card-Add-hook-before-profile-changes.patch:
+    - Backport from upstream to fix a bug in PulseAudio 8 where audio switches
+      to another audio device when an HDMI monitor goes to sleep, thanks to
+      Cristian Klein <cristiklein at gmail.com> for the backport (LP: #1641954)
+
+ -- Luke Yelavich <themuso at ubuntu.com>  Tue, 22 Nov 2016 11:13:00 +1100
+
 pulseaudio (1:8.0-0ubuntu3.1) xenial; urgency=medium
 
   * debian/pulseaudio.maintscript: Bump versions for files that need to be
diff --git a/debian/patches/0101-card-add-preferred-input-output-port.patch b/debian/patches/0101-card-add-preferred-input-output-port.patch
new file mode 100644
index 0000000..9b4b1b2
--- /dev/null
+++ b/debian/patches/0101-card-add-preferred-input-output-port.patch
@@ -0,0 +1,156 @@
+From 04040c522f5f62dda50ac927e92453381d419f09 Mon Sep 17 00:00:00 2001
+From: Tanu Kaskinen <tanuk at iki.fi>
+Date: Fri, 4 Mar 2016 15:23:30 +0200
+Subject: card: add preferred_{input, output}_port
+
+I will modify module-switch-on-port-available so that it will keep
+track of which input and output port the user prefers on the card,
+based on the user's profile and port switches. The preference needs
+to be saved on disk, for which I will use module-card-restore.
+
+To facilitate communication between the two modules, this patch adds
+preferred_input_port and preferred_output_port fields to pa_card, and
+a hook for monitoring the variable changes. It would be nice if the
+two modules would communicate directly with each other, but
+implementing that would be somewhat complicated, so I chose this time
+for adding the functionality to the core. In theory some other routing
+module might want to manage the new variables instead of
+module-switch-on-port-available, but admittedly that's not very likely
+to happen...
+
+diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c
+index b6cbbf7..0eaccff 100644
+--- a/src/pulsecore/card.c
++++ b/src/pulsecore/card.c
+@@ -103,6 +103,15 @@ void pa_card_new_data_set_profile(pa_card_new_data *data, const char *profile) {
+     data->active_profile = pa_xstrdup(profile);
+ }
+ 
++void pa_card_new_data_set_preferred_port(pa_card_new_data *data, pa_direction_t direction, pa_device_port *port) {
++    pa_assert(data);
++
++    if (direction == PA_DIRECTION_INPUT)
++        data->preferred_input_port = port;
++    else
++        data->preferred_output_port = port;
++}
++
+ void pa_card_new_data_done(pa_card_new_data *data) {
+ 
+     pa_assert(data);
+@@ -169,6 +178,9 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
+     PA_HASHMAP_FOREACH(port, c->ports, state)
+         port->card = c;
+ 
++    c->preferred_input_port = data->preferred_input_port;
++    c->preferred_output_port = data->preferred_output_port;
++
+     if (data->active_profile)
+         if ((c->active_profile = pa_hashmap_get(c->profiles, data->active_profile)))
+             c->save_profile = data->save_profile;
+@@ -309,6 +321,40 @@ int pa_card_set_profile(pa_card *c, pa_card_profile *profile, bool save) {
+     return 0;
+ }
+ 
++void pa_card_set_preferred_port(pa_card *c, pa_direction_t direction, pa_device_port *port) {
++    pa_device_port *old_port;
++    const char *old_port_str;
++    const char *new_port_str;
++    pa_card_preferred_port_changed_hook_data data;
++
++    pa_assert(c);
++
++    if (direction == PA_DIRECTION_INPUT) {
++        old_port = c->preferred_input_port;
++        old_port_str = c->preferred_input_port ? c->preferred_input_port->name : "(unset)";
++    } else {
++        old_port = c->preferred_output_port;
++        old_port_str = c->preferred_output_port ? c->preferred_output_port->name : "(unset)";
++    }
++
++    if (port == old_port)
++        return;
++
++    new_port_str = port ? port->name : "(unset)";
++
++    if (direction == PA_DIRECTION_INPUT) {
++        c->preferred_input_port = port;
++        pa_log_debug("%s: preferred_input_port: %s -> %s", c->name, old_port_str, new_port_str);
++    } else {
++        c->preferred_output_port = port;
++        pa_log_debug("%s: preferred_output_port: %s -> %s", c->name, old_port_str, new_port_str);
++    }
++
++    data.card = c;
++    data.direction = direction;
++    pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CARD_PREFERRED_PORT_CHANGED], &data);
++}
++
+ int pa_card_suspend(pa_card *c, bool suspend, pa_suspend_cause_t cause) {
+     pa_sink *sink;
+     pa_source *source;
+diff --git a/src/pulsecore/card.h b/src/pulsecore/card.h
+index 30bfc0e..79966f3 100644
+--- a/src/pulsecore/card.h
++++ b/src/pulsecore/card.h
+@@ -79,6 +79,8 @@ struct pa_card {
+     pa_card_profile *active_profile;
+ 
+     pa_hashmap *ports;
++    pa_device_port *preferred_input_port;
++    pa_device_port *preferred_output_port;
+ 
+     bool save_profile:1;
+ 
+@@ -98,12 +100,19 @@ typedef struct pa_card_new_data {
+     char *active_profile;
+ 
+     pa_hashmap *ports;
++    pa_device_port *preferred_input_port;
++    pa_device_port *preferred_output_port;
+ 
+     bool namereg_fail:1;
+ 
+     bool save_profile:1;
+ } pa_card_new_data;
+ 
++typedef struct {
++    pa_card *card;
++    pa_direction_t direction;
++} pa_card_preferred_port_changed_hook_data;
++
+ pa_card_profile *pa_card_profile_new(const char *name, const char *description, size_t extra);
+ void pa_card_profile_free(pa_card_profile *c);
+ 
+@@ -113,6 +122,7 @@ void pa_card_profile_set_available(pa_card_profile *c, pa_available_t available)
+ pa_card_new_data *pa_card_new_data_init(pa_card_new_data *data);
+ void pa_card_new_data_set_name(pa_card_new_data *data, const char *name);
+ void pa_card_new_data_set_profile(pa_card_new_data *data, const char *profile);
++void pa_card_new_data_set_preferred_port(pa_card_new_data *data, pa_direction_t direction, pa_device_port *port);
+ void pa_card_new_data_done(pa_card_new_data *data);
+ 
+ pa_card *pa_card_new(pa_core *c, pa_card_new_data *data);
+@@ -122,6 +132,8 @@ void pa_card_add_profile(pa_card *c, pa_card_profile *profile);
+ 
+ int pa_card_set_profile(pa_card *c, pa_card_profile *profile, bool save);
+ 
++void pa_card_set_preferred_port(pa_card *c, pa_direction_t direction, pa_device_port *port);
++
+ int pa_card_suspend(pa_card *c, bool suspend, pa_suspend_cause_t cause);
+ 
+ #endif
+diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
+index 1a3c490..69ab4d0 100644
+--- a/src/pulsecore/core.h
++++ b/src/pulsecore/core.h
+@@ -120,6 +120,7 @@ typedef enum pa_core_hook {
+     PA_CORE_HOOK_CARD_NEW,
+     PA_CORE_HOOK_CARD_PUT,
+     PA_CORE_HOOK_CARD_UNLINK,
++    PA_CORE_HOOK_CARD_PREFERRED_PORT_CHANGED,
+     PA_CORE_HOOK_CARD_PROFILE_CHANGED,
+     PA_CORE_HOOK_CARD_PROFILE_ADDED,
+     PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED,
+-- 
+cgit v0.10.2
+
diff --git a/debian/patches/0102-switch-on-port-available-prefer-ports-that-have-been.patch b/debian/patches/0102-switch-on-port-available-prefer-ports-that-have-been.patch
new file mode 100644
index 0000000..aff34c1
--- /dev/null
+++ b/debian/patches/0102-switch-on-port-available-prefer-ports-that-have-been.patch
@@ -0,0 +1,316 @@
+Index: pulseaudio-8.0/src/modules/module-switch-on-port-available.c
+===================================================================
+--- pulseaudio-8.0.orig/src/modules/module-switch-on-port-available.c
++++ pulseaudio-8.0/src/modules/module-switch-on-port-available.c
+@@ -29,45 +29,97 @@
+ 
+ #include "module-switch-on-port-available-symdef.h"
+ 
+-static bool profile_good_for_output(pa_card_profile *profile, unsigned prio) {
++struct card_info {
++    struct userdata *userdata;
++    pa_card *card;
++
++    /* We need to cache the active profile, because we want to compare the old
++     * and new profiles in the PROFILE_CHANGED hook. Without this we'd only
++     * have access to the new profile. */
++    pa_card_profile *active_profile;
++};
++
++struct userdata {
++    pa_hashmap *card_infos; /* pa_card -> struct card_info */
++};
++
++static void card_info_new(struct userdata *u, pa_card *card) {
++    struct card_info *info;
++
++    info = pa_xnew0(struct card_info, 1);
++    info->userdata = u;
++    info->card = card;
++    info->active_profile = card->active_profile;
++
++    pa_hashmap_put(u->card_infos, card, info);
++}
++
++static void card_info_free(struct card_info *info) {
++    pa_hashmap_remove(info->userdata->card_infos, info->card);
++    pa_xfree(info);
++}
++
++static bool profile_good_for_output(pa_card_profile *profile, pa_device_port *port) {
++    pa_card *card;
+     pa_sink *sink;
+     uint32_t idx;
+ 
+     pa_assert(profile);
+ 
+-    if (!pa_safe_streq(profile->card->active_profile->input_name, profile->input_name))
++    card = profile->card;
++
++    if (!pa_safe_streq(card->active_profile->input_name, profile->input_name))
+         return false;
+ 
+-    if (profile->card->active_profile->n_sources != profile->n_sources)
++    if (card->active_profile->n_sources != profile->n_sources)
+         return false;
+ 
+-    if (profile->card->active_profile->max_source_channels != profile->max_source_channels)
++    if (card->active_profile->max_source_channels != profile->max_source_channels)
+         return false;
+ 
+-    /* Try not to switch to HDMI sinks from analog when HDMI is becoming available */
+-    PA_IDXSET_FOREACH(sink, profile->card->sinks, idx) {
++    if (port == card->preferred_output_port)
++        return true;
++
++    PA_IDXSET_FOREACH(sink, card->sinks, idx) {
+         if (!sink->active_port)
+             continue;
+ 
+-        if ((sink->active_port->available != PA_AVAILABLE_NO) && (sink->active_port->priority >= prio))
++        if ((sink->active_port->available != PA_AVAILABLE_NO) && (sink->active_port->priority >= port->priority))
+             return false;
+     }
+ 
+     return true;
+ }
+ 
+-static bool profile_good_for_input(pa_card_profile *profile) {
++static bool profile_good_for_input(pa_card_profile *profile, pa_device_port *port) {
++    pa_card *card;
++    pa_source *source;
++    uint32_t idx;
++
+     pa_assert(profile);
+ 
+-    if (!pa_safe_streq(profile->card->active_profile->output_name, profile->output_name))
++    card = profile->card;
++
++    if (!pa_safe_streq(card->active_profile->output_name, profile->output_name))
+         return false;
+ 
+-    if (profile->card->active_profile->n_sinks != profile->n_sinks)
++    if (card->active_profile->n_sinks != profile->n_sinks)
+         return false;
+ 
+-    if (profile->card->active_profile->max_sink_channels != profile->max_sink_channels)
++    if (card->active_profile->max_sink_channels != profile->max_sink_channels)
+         return false;
+ 
++    if (port == card->preferred_input_port)
++        return true;
++
++    PA_IDXSET_FOREACH(source, card->sources, idx) {
++        if (!source->active_port)
++            continue;
++
++        if ((source->active_port->available != PA_AVAILABLE_NO) && (source->active_port->priority >= port->priority))
++            return false;
++    }
++
+     return true;
+ }
+ 
+@@ -88,12 +140,12 @@ static int try_to_switch_profile(pa_devi
+         switch (port->direction) {
+             case PA_DIRECTION_OUTPUT:
+                 name = profile->output_name;
+-                good = profile_good_for_output(profile, port->priority);
++                good = profile_good_for_output(profile, port);
+                 break;
+ 
+             case PA_DIRECTION_INPUT:
+                 name = profile->input_name;
+-                good = profile_good_for_input(profile);
++                good = profile_good_for_input(profile, port);
+                 break;
+         }
+ 
+@@ -182,7 +234,7 @@ static bool switch_to_port(pa_device_por
+     pa_log_debug("Trying to switch to port %s", port->name);
+     if (!pp.is_preferred_profile_active) {
+         if (try_to_switch_profile(port) < 0) {
+-            if (pp.is_possible_profile_active)
++            if (!pp.is_possible_profile_active)
+                 return false;
+         }
+         else
+@@ -307,9 +359,142 @@ static pa_hook_result_t source_new_hook_
+     return PA_HOOK_OK;
+ }
+ 
++static pa_hook_result_t card_put_hook_callback(pa_core *core, pa_card *card, struct userdata *u) {
++    card_info_new(u, card);
++
++    return PA_HOOK_OK;
++}
++
++static pa_hook_result_t card_unlink_hook_callback(pa_core *core, pa_card *card, struct userdata *u) {
++    card_info_free(pa_hashmap_get(u->card_infos, card));
++
++    return PA_HOOK_OK;
++}
++
++static void update_preferred_input_port(pa_card *card, pa_card_profile *old_profile, pa_card_profile *new_profile) {
++    pa_source *source;
++
++    /* If the profile change didn't affect input, it doesn't indicate change in
++     * the user's input port preference. */
++    if (pa_safe_streq(old_profile->input_name, new_profile->input_name))
++        return;
++
++    /* If there are more than one source, we don't know which of those the user
++     * prefers. If there are no sources, then the user doesn't seem to care
++     * about input at all. */
++    if (pa_idxset_size(card->sources) != 1) {
++        pa_card_set_preferred_port(card, PA_DIRECTION_INPUT, NULL);
++        return;
++    }
++
++    /* If the profile change modified the set of sinks, then it's unclear
++     * whether the user wanted to activate some specific input port, or was the
++     * input change only a side effect of activating some output. If the new
++     * profile contains no sinks, though, then we know the user only cares
++     * about input. */
++    if (pa_idxset_size(card->sinks) > 0 && !pa_safe_streq(old_profile->output_name, new_profile->output_name)) {
++        pa_card_set_preferred_port(card, PA_DIRECTION_INPUT, NULL);
++        return;
++    }
++
++    source = pa_idxset_first(card->sources, NULL);
++
++    /* We know the user wanted to activate this source. The user might not have
++     * wanted to activate the port that was selected by default, but if that's
++     * the case, the user will change the port manually, and we'll update the
++     * port preference at that time. If no port change occurs, we can assume
++     * that the user likes the port that is now active. */
++    pa_card_set_preferred_port(card, PA_DIRECTION_INPUT, source->active_port);
++}
++
++static void update_preferred_output_port(pa_card *card, pa_card_profile *old_profile, pa_card_profile *new_profile) {
++    pa_sink *sink;
++
++    /* If the profile change didn't affect output, it doesn't indicate change in
++     * the user's output port preference. */
++    if (pa_safe_streq(old_profile->output_name, new_profile->output_name))
++        return;
++
++    /* If there are more than one sink, we don't know which of those the user
++     * prefers. If there are no sinks, then the user doesn't seem to care about
++     * output at all. */
++    if (pa_idxset_size(card->sinks) != 1) {
++        pa_card_set_preferred_port(card, PA_DIRECTION_OUTPUT, NULL);
++        return;
++    }
++
++    /* If the profile change modified the set of sources, then it's unclear
++     * whether the user wanted to activate some specific output port, or was
++     * the output change only a side effect of activating some input. If the
++     * new profile contains no sources, though, then we know the user only
++     * cares about output. */
++    if (pa_idxset_size(card->sources) > 0 && !pa_safe_streq(old_profile->input_name, new_profile->input_name)) {
++        pa_card_set_preferred_port(card, PA_DIRECTION_OUTPUT, NULL);
++        return;
++    }
++
++    sink = pa_idxset_first(card->sinks, NULL);
++
++    /* We know the user wanted to activate this sink. The user might not have
++     * wanted to activate the port that was selected by default, but if that's
++     * the case, the user will change the port manually, and we'll update the
++     * port preference at that time. If no port change occurs, we can assume
++     * that the user likes the port that is now active. */
++    pa_card_set_preferred_port(card, PA_DIRECTION_OUTPUT, sink->active_port);
++}
++
++static pa_hook_result_t card_profile_changed_callback(pa_core *core, pa_card *card, struct userdata *u) {
++    struct card_info *info;
++    pa_card_profile *old_profile;
++    pa_card_profile *new_profile;
++
++    info = pa_hashmap_get(u->card_infos, card);
++    old_profile = info->active_profile;
++    new_profile = card->active_profile;
++    info->active_profile = new_profile;
++
++    /* This profile change wasn't initiated by the user, so it doesn't signal
++     * a change in the user's port preferences. */
++    if (!card->save_profile)
++        return PA_HOOK_OK;
++
++    update_preferred_input_port(card, old_profile, new_profile);
++    update_preferred_output_port(card, old_profile, new_profile);
++
++    return PA_HOOK_OK;
++}
++
++static pa_hook_result_t source_port_changed_callback(pa_core *core, pa_source *source, void *userdata) {
++    if (!source->save_port)
++        return PA_HOOK_OK;
++
++    pa_card_set_preferred_port(source->card, PA_DIRECTION_INPUT, source->active_port);
++
++    return PA_HOOK_OK;
++}
++
++static pa_hook_result_t sink_port_changed_callback(pa_core *core, pa_sink *sink, void *userdata) {
++    if (!sink->save_port)
++        return PA_HOOK_OK;
++
++    pa_card_set_preferred_port(sink->card, PA_DIRECTION_OUTPUT, sink->active_port);
++
++    return PA_HOOK_OK;
++}
++
+ int pa__init(pa_module*m) {
++    struct userdata *u;
++    pa_card *card;
++    uint32_t idx;
++
+     pa_assert(m);
+ 
++    u = m->userdata = pa_xnew0(struct userdata, 1);
++    u->card_infos = pa_hashmap_new(NULL, NULL);
++
++    PA_IDXSET_FOREACH(card, m->core->cards, idx)
++        card_info_new(u, card);
++
+     /* Make sure we are after module-device-restore, so we can overwrite that suggestion if necessary */
+     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_NEW],
+                            PA_HOOK_NORMAL, (pa_hook_cb_t) sink_new_hook_callback, NULL);
+@@ -317,8 +502,35 @@ int pa__init(pa_module*m) {
+                            PA_HOOK_NORMAL, (pa_hook_cb_t) source_new_hook_callback, NULL);
+     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED],
+                            PA_HOOK_LATE, (pa_hook_cb_t) port_available_hook_callback, NULL);
++    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PUT],
++                           PA_HOOK_NORMAL, (pa_hook_cb_t) card_put_hook_callback, u);
++    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_UNLINK],
++                           PA_HOOK_NORMAL, (pa_hook_cb_t) card_unlink_hook_callback, u);
++    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_CHANGED],
++                           PA_HOOK_NORMAL, (pa_hook_cb_t) card_profile_changed_callback, u);
++    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_PORT_CHANGED],
++                           PA_HOOK_NORMAL, (pa_hook_cb_t) source_port_changed_callback, NULL);
++    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_PORT_CHANGED],
++                           PA_HOOK_NORMAL, (pa_hook_cb_t) sink_port_changed_callback, NULL);
+ 
+     handle_all_unavailable(m->core);
+ 
+     return 0;
+ }
++
++void pa__done(pa_module *module) {
++    struct userdata *u;
++    struct card_info *info;
++
++    pa_assert(module);
++
++    if (!(u = module->userdata))
++        return;
++
++    while ((info = pa_hashmap_last(u->card_infos)))
++        card_info_free(info);
++
++    pa_hashmap_free(u->card_infos);
++
++    pa_xfree(u);
++}
diff --git a/debian/patches/0203-card-Add-hook-before-profile-changes.patch b/debian/patches/0203-card-Add-hook-before-profile-changes.patch
index 5af5223..f85ef01 100644
--- a/debian/patches/0203-card-Add-hook-before-profile-changes.patch
+++ b/debian/patches/0203-card-Add-hook-before-profile-changes.patch
@@ -14,7 +14,7 @@ Index: pulseaudio/src/pulsecore/card.c
 ===================================================================
 --- pulseaudio.orig/src/pulsecore/card.c
 +++ pulseaudio/src/pulsecore/card.c
-@@ -291,6 +291,8 @@ int pa_card_set_profile(pa_card *c, pa_c
+@@ -303,6 +303,8 @@ int pa_card_set_profile(pa_card *c, pa_c
          return 0;
      }
  
@@ -27,9 +27,9 @@ Index: pulseaudio/src/pulsecore/core.h
 ===================================================================
 --- pulseaudio.orig/src/pulsecore/core.h
 +++ pulseaudio/src/pulsecore/core.h
-@@ -121,6 +121,7 @@ typedef enum pa_core_hook {
-     PA_CORE_HOOK_CARD_PUT,
+@@ -122,6 +122,7 @@ typedef enum pa_core_hook {
      PA_CORE_HOOK_CARD_UNLINK,
+     PA_CORE_HOOK_CARD_PREFERRED_PORT_CHANGED,
      PA_CORE_HOOK_CARD_PROFILE_CHANGED,
 +    PA_CORE_HOOK_CARD_PROFILE_CHANGING,
      PA_CORE_HOOK_CARD_PROFILE_ADDED,
diff --git a/debian/patches/series b/debian/patches/series
index 646bf2a..0cd4be0 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -8,6 +8,8 @@
 
 # Cherry-picked from upstream
 0100-switch-on-port-available-Switch-from-HDMI-to-analog-.patch
+0101-card-add-preferred-input-output-port.patch
+0102-switch-on-port-available-prefer-ports-that-have-been.patch
 
 # Ubuntu touch stuff
 0202-dont-probe-ucm.patch

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-pulseaudio/pulseaudio.git



More information about the pkg-pulseaudio-devel mailing list