Audio APP (sink-input) bind to the sink with only unplugged hdmi-audio ports on it

Submitted by Hui Wang on Sept. 30, 2018, 8:53 a.m.

Details

Message ID 6c815397-61cd-ab29-01f8-7c82a0e10ed3@canonical.com
State New
Headers show
Series "Audio APP (sink-input) bind to the sink with only unplugged hdmi-audio ports on it" ( rev: 1 ) in PulseAudio

Not browsing as part of any series.

Commit Message

Hui Wang Sept. 30, 2018, 8:53 a.m.
The tentative fix of this problem like this:

When a port is unplugged, it is caught by 
module-switch-on-port-available.c, here we check if the sink of this 
port is default_sink or not, if it is default_sink, it means the sink of 
this port is still the highest priority one, no need to move 
sink-inputs, otherwise, try to move all sink-inputs of this sink to 
default_sink temporarily.

To restore the sink-inputs to its original sink, it depends on 
module-stream-restore.c, but we don't let it restore unconditionally, if 
the target sink has at least one port with avaialbe_yes/unknow, let it 
restore.

So supposing one sink has speaker, the other sink has hdmi-audio, when 
unplugging the hdmi cable, the audio will route to speaker, after 
plugging the hdmi cable again, when app build new sink-input, it will 
restore the sound to original sink.




+           /* now the default sink is the best sink already */
+           move_input_to_default_sink(c, pp.sink, def_sink);
+       }
+       break;
+
+    case PA_DIRECTION_INPUT:
+       break;
+    }
+}

  static pa_hook_result_t port_available_hook_callback(pa_core *c, 
pa_device_port *port, void* userdata) {
      pa_assert(port);
@@ -295,6 +352,7 @@ static pa_hook_result_t 
port_available_hook_callback(pa_core *c, pa_device_port
          break;
      case PA_AVAILABLE_NO:
          switch_from_port(port);
+       check_and_move_sink_inputs_to_default_sink(c, port);
          break;
      default:
          break;



On 2018年09月30日 15:03, Hui Wang wrote:
> This issue is also reported to: 
> https://gitlab.freedesktop.org/pulseaudio/pulseaudio/issues/579
>
> Recently we found a weird issue on many laptops with the ubuntu 18.04, 
> it uses the pulseaudio-11.1 (I guess the PA of the latest version also 
> has this problem). The issue is like this:
>
> 1. boot the system up without plugging a hdmi monitor
> 2. run an audio app to play sound (e.g. $speaker-test)
> 3. the sound outputs from analog-speaker
> 4. plug a monitor with audio capability (through DP or HDMI port)
> 5. the sound still outputs from analog-speaker
> 6. open sound-setting (gnome-control-center --> choose sound), you will
>    see two output devies: speaker and HDMI audio
> 7. choose HDMI audio, the sound will switch to HDIM audio from speaker
>    (pa will remember speaker-test prefer to use hdmi-audio sink)
> 8. unplug the monitor, the default-sink is switching to analog-speaker,
>    but the sound of speaker-test still route to hdmi-audio sink
> 9. run other sound apps, they all route sound to default sink
>    (analog-speaker), but speaker-test always routes to hdmi-audio sink,
>    as a result, speaker-test can't output sound anymore unless we
>    replug a monitor with audio capability then the speaker-test output
>    from hdmi-audio again.
> 10. if we want the speaker-test to route to analog-speaker, two ways:
>    run pacmd move-sink-input or plug a monitor, after two audio devices
>    (hdmi audio and speaker) show up in the sound-setting, select
>    analog-speaker manually.
>
> This issue only happens on the laptops with 2 audio cards, analog 
> devices on one card, hdmi audio on the other card. This kind of 
> laptops are very common, like I+A (Intel graphic + Amd Graphic), 
> I+N(Intel + Nvidia), and A AMD.
>
> This issue will not happen on the laptops with only Intel graphic 
> card, since both analog and hdmi audio belong to one sound card. When 
> hdmi monitor is unplugged, the hdmi sink will be removed from PA, then 
> all sink-inputs will route to the only left sink: analog-sink.
>
> This issue will not happen on BT or USB audio. Unlike hdmi audio, BT 
> and USB audio cards will be removed totally from PA when they are 
> unpluged/unconnected, so they don't have this issue as well.
>
> The root cause of this issue is although the hdmi monitor is 
> unplugged, the hdmi-sink still exists, and sink-input is selected by 
> user to bind to this sink, so the pa doesn't care about if this sink 
> has valid port or not, it bind the sink-input to this sink 
> unconditionally.
>
> Maybe we could improve it like this: if the user selected sink only 
> has available_no ports, the pa will switch all sink-inputs of this 
> sink to other sinks (like default_sink) temporarily, once the selected 
> sink has availble ports, all sink-inputs switch back to this sink.
>
> Any good ideas?
>
> Thanks,
>
> Hui.
>
>
> _______________________________________________
> pulseaudio-discuss mailing list
> pulseaudio-discuss@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss

Patch hide | download patch | download mbox

diff --git a/src/modules/module-stream-restore.c 
b/src/modules/module-stream-restore.c
index 228e9e4..2dd825f 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -1424,6 +1424,7 @@  static void subscribe_callback(pa_core *c, 
pa_subscription_event_type_t t, uint3
  static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, 
pa_sink_input_new_data *new_data, struct userdata *u) {
      char *name;
      struct entry *e;
+    bool good = 0;

      pa_assert(c);
      pa_assert(new_data);
@@ -1450,6 +1451,21 @@  static pa_hook_result_t 
sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
                  s = pa_idxset_first(card->sinks, NULL);
          }

+       /* if all ports on this sink are all available_no, we don't set 
this sink temporarily */
+       if (s) {
+         pa_device_port *i;
+          void *state_p;
+         PA_HASHMAP_FOREACH(i, s->ports, state_p) {
+           if (i->available != PA_AVAILABLE_NO) {
+             good = 1;
+             break;
+           }
+         }
+       }
+
+       if (!good)
+         s = NULL;
+
          /* It might happen that a stream and a sink are set up at the
             same time, in which case we want to make sure we don't
             interfere with that */
diff --git a/src/modules/module-switch-on-port-available.c 
b/src/modules/module-switch-on-port-available.c
index 321db36..acd3252 100644
--- a/src/modules/module-switch-on-port-available.c
+++ b/src/modules/module-switch-on-port-available.c
@@ -272,6 +272,63 @@  static void switch_from_port(pa_device_port *port) {
          switch_to_port(best_port);
  }

+/* This function refers to sink_put_hook_callback() in the 
module-switch-on-connect.c */
+static void move_input_to_default_sink(pa_core *c, pa_sink *ori_sink, 
pa_sink *new_sink)
+{
+    pa_sink_input *i;
+    uint32_t idx;
+
+    /* Now move all old inputs over */
+    if (pa_idxset_size(ori_sink->inputs) <= 0) {
+       pa_log_debug("No sink inputs to move away.");
+       return;
+    }
+
+    PA_IDXSET_FOREACH(i, ori_sink->inputs, idx) {
+      /* don't check i->save_sink here, since we have to move input 
temporarily */
+       if (!PA_SINK_INPUT_IS_LINKED(i->state))
+           continue;
+
+       if (pa_sink_input_move_to(i, new_sink, false) < 0)
+           pa_log_info("Failed to move sink input %u \"%s\" to %s.", 
i->index,
+ pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), 
new_sink->name);
+       else
+           pa_log_info("Successfully moved sink input %u \"%s\" to 
%s.", i->index,
+ pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), 
new_sink->name);
+    }
+}
+
+static void check_and_move_sink_inputs_to_default_sink(pa_core *c, 
pa_device_port *port)
+{
+    struct port_pointers pp = find_port_pointers(port);
+    pa_sink *def_sink, *p_sink = pp.sink;
+
+    switch (port->direction) {
+    case PA_DIRECTION_OUTPUT:
+       def_sink = c->default_sink;
+
+       /* check if there is better sinks to move to, before here,the 
default_sink is already
+           re-selected when the port is changing unavailble, if p_sink 
equals default_sink,
+          it means there is no better sinks to replace the sink 
containing this port */
+       if (p_sink != def_sink) {
+           void *state_p;
+           pa_device_port *i;
+
+           /* check if all ports on this sink are available_no, if not, 
don't need to move the input */
+           PA_HASHMAP_FOREACH(i, p_sink->ports, state_p) {
+             if (i->available != PA_AVAILABLE_NO)
+               return;
+           }
+