pulse: allow setting stream properties
authorStefan Kost <ensonic@users.sf.net>
Mon, 16 Aug 2010 12:35:51 +0000 (15:35 +0300)
committerStefan Kost <ensonic@users.sf.net>
Tue, 7 Sep 2010 11:20:21 +0000 (14:20 +0300)
Add a "properties" property to the elements to allow setting extra stream
properties.

Fixes #537544

ext/pulse/pulsesink.c
ext/pulse/pulsesink.h
ext/pulse/pulsesrc.c
ext/pulse/pulsesrc.h
ext/pulse/pulseutil.c
ext/pulse/pulseutil.h

index 97ffd4b..a6f9f88 100644 (file)
@@ -80,6 +80,7 @@ enum
   PROP_VOLUME,
   PROP_MUTE,
   PROP_CLIENT,
+  PROP_STREAM_PROPERTIES,
   PROP_LAST
 };
 
@@ -770,7 +771,11 @@ gst_pulseringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec)
 
   /* create a stream */
   GST_LOG_OBJECT (psink, "creating stream with name %s", name);
-  if (!(pbuf->stream = pa_stream_new (pctx->context,
+  if (psink->proplist) {
+    if (!(pbuf->stream = pa_stream_new_with_proplist (pctx->context,
+                name, &pbuf->sample_spec, &channel_map, psink->proplist)))
+      goto stream_failed;
+  } else if (!(pbuf->stream = pa_stream_new (pctx->context,
               name, &pbuf->sample_spec, &channel_map)))
     goto stream_failed;
 
@@ -1853,7 +1858,7 @@ gst_pulsesink_class_init (GstPulseSinkClass * klass)
   /**
    * GstPulseSink:client
    *
-   * The PulseAudio client name to use
+   * The PulseAudio client name to use.
    *
    * Since: 0.10.25
    */
@@ -1863,6 +1868,29 @@ gst_pulsesink_class_init (GstPulseSinkClass * klass)
           "The PulseAudio client name to use", gst_pulse_client_name (),
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
           GST_PARAM_MUTABLE_READY));
+
+  /**
+   * GstPulseSink:stream-properties
+   *
+   * List of pulseaudio stream properties. A list of defined properties can be
+   * found in the <ulink href="http://0pointer.de/lennart/projects/pulseaudio/doxygen/proplist_8h.html">pulseaudio api docs</ulink>.
+   *
+   * Below is an example for registering as a music application to pulseaudio.
+   * |[
+   * GstStructure *props;
+   *
+   * props = gst_structure_from_string ("props,media.role=music", NULL);
+   * g_object_set (pulse, "stream-properties", props, NULL);
+   * gst_structure_free
+   * ]|
+   *
+   * Since: 0.10.26
+   */
+  g_object_class_install_property (gobject_class,
+      PROP_STREAM_PROPERTIES,
+      g_param_spec_boxed ("stream-properties", "stream properties",
+          "list of pulseaudio stream properties",
+          GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 }
 
 /* returns the current time of the sink ringbuffer */
@@ -1926,6 +1954,9 @@ gst_pulsesink_init (GstPulseSink * pulsesink, GstPulseSinkClass * klass)
   /* needed for conditional execution */
   pulsesink->pa_version = pa_get_library_version ();
 
+  pulsesink->properties = NULL;
+  pulsesink->proplist = NULL;
+
   GST_DEBUG_OBJECT (pulsesink, "using pulseaudio version %s",
       pulsesink->pa_version);
 
@@ -1945,6 +1976,11 @@ gst_pulsesink_finalize (GObject * object)
   g_free (pulsesink->device_description);
   g_free (pulsesink->client_name);
 
+  if (pulsesink->properties)
+    gst_structure_free (pulsesink->properties);
+  if (pulsesink->proplist)
+    pa_proplist_free (pulsesink->proplist);
+
   if (pulsesink->probe) {
     gst_pulseprobe_free (pulsesink->probe);
     pulsesink->probe = NULL;
@@ -2385,6 +2421,15 @@ gst_pulsesink_set_property (GObject * object,
       } else
         pulsesink->client_name = g_value_dup_string (value);
       break;
+    case PROP_STREAM_PROPERTIES:
+      if (pulsesink->properties)
+        gst_structure_free (pulsesink->properties);
+      pulsesink->properties =
+          gst_structure_copy (gst_value_get_structure (value));
+      if (pulsesink->proplist)
+        pa_proplist_free (pulsesink->proplist);
+      pulsesink->proplist = gst_pulse_make_proplist (pulsesink->properties);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -2419,6 +2464,9 @@ gst_pulsesink_get_property (GObject * object,
     case PROP_CLIENT:
       g_value_set_string (value, pulsesink->client_name);
       break;
+    case PROP_STREAM_PROPERTIES:
+      gst_value_set_structure (value, pulsesink->properties);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
index 9658c1e..83a0bed 100644 (file)
@@ -72,6 +72,8 @@ struct _GstPulseSink
 
   const gchar *pa_version;
 
+  GstStructure *properties;
+  pa_proplist *proplist;
 };
 
 struct _GstPulseSinkClass
index c0a3884..0baad9f 100644 (file)
@@ -61,6 +61,7 @@ enum
   PROP_SERVER,
   PROP_DEVICE,
   PROP_DEVICE_NAME,
+  PROP_STREAM_PROPERTIES,
   PROP_LAST
 };
 
@@ -244,6 +245,29 @@ gst_pulsesrc_class_init (GstPulseSrcClass * klass)
       g_param_spec_string ("device-name", "Device name",
           "Human-readable name of the sound device", DEFAULT_DEVICE_NAME,
           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GstPulseSrc:stream-properties
+   *
+   * List of pulseaudio stream properties. A list of defined properties can be
+   * found in the <ulink href="http://0pointer.de/lennart/projects/pulseaudio/doxygen/proplist_8h.html">pulseaudio api docs</ulink>.
+   *
+   * Below is an example for registering as a music application to pulseaudio.
+   * |[
+   * GstStructure *props;
+   *
+   * props = gst_structure_from_string ("props,media.role=music", NULL);
+   * g_object_set (pulse, "stream-properties", props, NULL);
+   * gst_structure_free (props);
+   * ]|
+   *
+   * Since: 0.10.26
+   */
+  g_object_class_install_property (gobject_class,
+      PROP_STREAM_PROPERTIES,
+      g_param_spec_boxed ("stream-properties", "stream properties",
+          "list of pulseaudio stream properties",
+          GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 }
 
 static void
@@ -273,6 +297,9 @@ gst_pulsesrc_init (GstPulseSrc * pulsesrc, GstPulseSrcClass * klass)
 
   pulsesrc->mixer = NULL;
 
+  pulsesrc->properties = NULL;
+  pulsesrc->proplist = NULL;
+
   pulsesrc->probe = gst_pulseprobe_new (G_OBJECT (pulsesrc), G_OBJECT_GET_CLASS (pulsesrc), PROP_DEVICE, pulsesrc->server, FALSE, TRUE);        /* FALSE for sinks, TRUE for sources */
 
   /* this should be the default but it isn't yet */
@@ -314,6 +341,11 @@ gst_pulsesrc_finalize (GObject * object)
   g_free (pulsesrc->server);
   g_free (pulsesrc->device);
 
+  if (pulsesrc->properties)
+    gst_structure_free (pulsesrc->properties);
+  if (pulsesrc->proplist)
+    pa_proplist_free (pulsesrc->proplist);
+
   if (pulsesrc->mixer) {
     gst_pulsemixer_ctrl_free (pulsesrc->mixer);
     pulsesrc->mixer = NULL;
@@ -432,6 +464,15 @@ gst_pulsesrc_set_property (GObject * object,
       g_free (pulsesrc->device);
       pulsesrc->device = g_value_dup_string (value);
       break;
+    case PROP_STREAM_PROPERTIES:
+      if (pulsesrc->properties)
+        gst_structure_free (pulsesrc->properties);
+      pulsesrc->properties =
+          gst_structure_copy (gst_value_get_structure (value));
+      if (pulsesrc->proplist)
+        pa_proplist_free (pulsesrc->proplist);
+      pulsesrc->proplist = gst_pulse_make_proplist (pulsesrc->properties);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -455,6 +496,9 @@ gst_pulsesrc_get_property (GObject * object,
     case PROP_DEVICE_NAME:
       g_value_take_string (value, gst_pulsesrc_device_description (pulsesrc));
       break;
+    case PROP_STREAM_PROPERTIES:
+      gst_value_set_structure (value, pulsesrc->properties);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -797,6 +841,7 @@ gst_pulsesrc_create_stream (GstPulseSrc * pulsesrc, GstCaps * caps)
   GstStructure *s;
   gboolean need_channel_layout = FALSE;
   GstRingBufferSpec spec;
+  const gchar *name;
 
   memset (&spec, 0, sizeof (GstRingBufferSpec));
   spec.latency_time = GST_SECOND;
@@ -832,9 +877,19 @@ gst_pulsesrc_create_stream (GstPulseSrc * pulsesrc, GstCaps * caps)
       need_channel_layout = TRUE;
   }
 
-  if (!(pulsesrc->stream = pa_stream_new (pulsesrc->context,
-              "Record Stream",
-              &pulsesrc->sample_spec,
+  name = "Record Stream";
+  if (pulsesrc->proplist) {
+    if (!(pulsesrc->stream = pa_stream_new_with_proplist (pulsesrc->context,
+                name, &pulsesrc->sample_spec,
+                (need_channel_layout) ? NULL : &channel_map,
+                pulsesrc->proplist))) {
+      GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED,
+          ("Failed to create stream: %s",
+              pa_strerror (pa_context_errno (pulsesrc->context))), (NULL));
+      goto unlock_and_fail;
+    }
+  } else if (!(pulsesrc->stream = pa_stream_new (pulsesrc->context,
+              name, &pulsesrc->sample_spec,
               (need_channel_layout) ? NULL : &channel_map))) {
     GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED,
         ("Failed to create stream: %s",
index be89434..fb5006d 100644 (file)
@@ -76,6 +76,9 @@ struct _GstPulseSrc
   gboolean operation_success:1;
   gboolean paused:1;
   gboolean in_read:1;
+
+  GstStructure *properties;
+  pa_proplist *proplist;
 };
 
 struct _GstPulseSrcClass
index 2541459..c779e1a 100644 (file)
@@ -216,3 +216,36 @@ gst_pulse_cvolume_from_linear (pa_cvolume * v, unsigned channels,
 {
   pa_cvolume_set (v, channels, pa_sw_volume_from_linear (volume));
 }
+
+static gboolean
+make_proplist_item (GQuark field_id, const GValue * value, gpointer user_data)
+{
+  pa_proplist *p = (pa_proplist *) user_data;
+  gchar *prop_id = (gchar *) g_quark_to_string (field_id);
+
+  /* http://0pointer.de/lennart/projects/pulseaudio/doxygen/proplist_8h.html */
+
+  /* match prop id */
+
+  /* check type */
+  switch (G_VALUE_TYPE (value)) {
+    case G_TYPE_STRING:
+      pa_proplist_sets (p, prop_id, g_value_get_string (value));
+      break;
+    default:
+      GST_WARNING ("unmapped property type %s", G_VALUE_TYPE_NAME (value));
+      break;
+  }
+
+  return TRUE;
+}
+
+pa_proplist *
+gst_pulse_make_proplist (const GstStructure * properties)
+{
+  pa_proplist *proplist = pa_proplist_new ();
+
+  /* iterate the structure and fill the proplist */
+  gst_structure_foreach (properties, make_proplist_item, proplist);
+  return proplist;
+}
index 4cafba5..75b3112 100644 (file)
@@ -37,7 +37,9 @@ pa_channel_map *gst_pulse_gst_to_channel_map (pa_channel_map * map,
 GstRingBufferSpec *gst_pulse_channel_map_to_gst (const pa_channel_map * map,
     GstRingBufferSpec * spec);
 
-void gst_pulse_cvolume_from_linear(pa_cvolume *v, unsigned channels, gdouble volume);
+void gst_pulse_cvolume_from_linear (pa_cvolume *v, unsigned channels, gdouble volume);
+
+pa_proplist *gst_pulse_make_proplist (const GstStructure *properties);
 
 #if !HAVE_PULSE_0_9_11
 static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) {