pulsesink: Add property to find out the device currently in use
authorOlivier Crête <olivier.crete@collabora.com>
Mon, 19 Aug 2013 03:32:22 +0000 (23:32 -0400)
committerOlivier Crête <olivier.crete@collabora.com>
Thu, 22 Aug 2013 19:03:29 +0000 (15:03 -0400)
https://bugzilla.gnome.org/show_bug.cgi?id=590768

ext/pulse/pulsesink.c
ext/pulse/pulsesink.h

index d7394a59aa93c3af7ef5920a1a10ccac34348e50..9967c8d5f13ba26ddcd2001d4f6af2ce50be4b1d 100644 (file)
@@ -67,6 +67,7 @@ GST_DEBUG_CATEGORY_EXTERN (pulse_debug);
 
 #define DEFAULT_SERVER          NULL
 #define DEFAULT_DEVICE          NULL
+#define DEFAULT_CURRENT_DEVICE  NULL
 #define DEFAULT_DEVICE_NAME     NULL
 #define DEFAULT_VOLUME          1.0
 #define DEFAULT_MUTE            FALSE
@@ -77,6 +78,7 @@ enum
   PROP_0,
   PROP_SERVER,
   PROP_DEVICE,
+  PROP_CURRENT_DEVICE,
   PROP_DEVICE_NAME,
   PROP_VOLUME,
   PROP_MUTE,
@@ -1418,6 +1420,7 @@ gst_pulseringbuffer_commit (GstAudioRingBuffer * buf, guint64 * sample,
   if (g_atomic_int_compare_and_exchange (&psink->notify, 1, 0)) {
     g_object_notify (G_OBJECT (psink), "volume");
     g_object_notify (G_OBJECT (psink), "mute");
+    g_object_notify (G_OBJECT (psink), "current-device");
   }
 
   /* make sure the ringbuffer is started */
@@ -1873,6 +1876,11 @@ gst_pulsesink_class_init (GstPulseSinkClass * klass)
           "The PulseAudio sink device to connect to", DEFAULT_DEVICE,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+  g_object_class_install_property (gobject_class, PROP_CURRENT_DEVICE,
+      g_param_spec_string ("current-device", "Current Device",
+          "The current PulseAudio sink device", DEFAULT_CURRENT_DEVICE,
+          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
   g_object_class_install_property (gobject_class,
       PROP_DEVICE_NAME,
       g_param_spec_string ("device-name", "Device name",
@@ -2447,6 +2455,7 @@ gst_pulsesink_finalize (GObject * object)
   g_free (pulsesink->server);
   g_free (pulsesink->device);
   g_free (pulsesink->client_name);
+  g_free (pulsesink->current_sink_name);
 
   free_device_info (&pulsesink->device_info);
 
@@ -2620,6 +2629,7 @@ gst_pulsesink_sink_input_info_cb (pa_context * c, const pa_sink_input_info * i,
   if (i->index == pa_stream_get_index (pbuf->stream)) {
     psink->volume = pa_sw_volume_to_linear (pa_cvolume_max (&i->volume));
     psink->mute = i->mute;
+    psink->current_sink_idx = i->sink;
 
     if (psink->volume > MAX_VOLUME) {
       GST_WARNING_OBJECT (psink, "Clipped volume from %f to %f", psink->volume,
@@ -2705,6 +2715,90 @@ info_failed:
   }
 }
 
+static void
+gst_pulsesink_current_sink_info_cb (pa_context * c, const pa_sink_info * i,
+    int eol, void *userdata)
+{
+  GstPulseSink *psink;
+
+  psink = GST_PULSESINK_CAST (userdata);
+
+  if (!i)
+    goto done;
+
+  /* If the index doesn't match our current stream,
+   * it implies we just recreated the stream (caps change)
+   */
+  if (i->index == psink->current_sink_idx) {
+    g_free (psink->current_sink_name);
+    psink->current_sink_name = g_strdup (i->name);
+  }
+
+done:
+  pa_threaded_mainloop_signal (mainloop, 0);
+}
+
+static gchar *
+gst_pulsesink_get_current_device (GstPulseSink * pulsesink)
+{
+  pa_operation *o = NULL;
+  GstPulseRingBuffer *pbuf;
+  gchar *current_sink;
+
+  if (!mainloop)
+    goto no_mainloop;
+
+  pbuf =
+      GST_PULSERING_BUFFER_CAST (GST_AUDIO_BASE_SINK (pulsesink)->ringbuffer);
+  if (pbuf == NULL || pbuf->stream == NULL)
+    goto no_buffer;
+
+  gst_pulsesink_get_sink_input_info (pulsesink, NULL, NULL);
+
+  pa_threaded_mainloop_lock (mainloop);
+
+  if (!(o = pa_context_get_sink_info_by_index (pbuf->context,
+              pulsesink->current_sink_idx, gst_pulsesink_current_sink_info_cb,
+              pulsesink)))
+    goto info_failed;
+
+  while (pa_operation_get_state (o) == PA_OPERATION_RUNNING) {
+    pa_threaded_mainloop_wait (mainloop);
+    if (gst_pulsering_is_dead (pulsesink, pbuf, TRUE))
+      goto unlock;
+  }
+
+unlock:
+
+  current_sink = g_strdup (pulsesink->current_sink_name);
+
+  if (o)
+    pa_operation_unref (o);
+
+  pa_threaded_mainloop_unlock (mainloop);
+
+  return current_sink;
+
+  /* ERRORS */
+no_mainloop:
+  {
+    GST_DEBUG_OBJECT (pulsesink, "we have no mainloop");
+    return NULL;
+  }
+no_buffer:
+  {
+    GST_DEBUG_OBJECT (pulsesink, "we have no ringbuffer");
+    goto unlock;
+  }
+info_failed:
+  {
+    GST_ELEMENT_ERROR (pulsesink, RESOURCE, FAILED,
+        ("pa_context_get_sink_input_info() failed: %s",
+            pa_strerror (pa_context_errno (pbuf->context))), (NULL));
+    goto unlock;
+  }
+}
+
 static gchar *
 gst_pulsesink_device_description (GstPulseSink * psink)
 {
@@ -2881,6 +2975,15 @@ gst_pulsesink_get_property (GObject * object,
     case PROP_DEVICE:
       g_value_set_string (value, pulsesink->device);
       break;
+    case PROP_CURRENT_DEVICE:
+    {
+      gchar *current_device = gst_pulsesink_get_current_device (pulsesink);
+      if (current_device)
+        g_value_take_string (value, current_device);
+      else
+        g_value_set_string (value, "");
+      break;
+    }
     case PROP_DEVICE_NAME:
       g_value_take_string (value, gst_pulsesink_device_description (pulsesink));
       break;
index dfed7be5f3d9d52e7f24c79808a4b097edbf0b0a..de3b93281538b1bd9a23b4920ce02a0bac48868e 100644 (file)
@@ -71,6 +71,8 @@ struct _GstPulseSink
   gboolean volume_set:1;
   gboolean mute:1;
   gboolean mute_set:1;
+  guint32 current_sink_idx;
+  gchar *current_sink_name;
 
   guint defer_pending;