diff --git a/ext/pulse/pulsesink.c b/ext/pulse/pulsesink.c index 97ffd4bc00..a6f9f881bf 100644 --- a/ext/pulse/pulsesink.c +++ b/ext/pulse/pulsesink.c @@ -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 pulseaudio api docs. + * + * 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; diff --git a/ext/pulse/pulsesink.h b/ext/pulse/pulsesink.h index 9658c1ea27..83a0bed2af 100644 --- a/ext/pulse/pulsesink.h +++ b/ext/pulse/pulsesink.h @@ -72,6 +72,8 @@ struct _GstPulseSink const gchar *pa_version; + GstStructure *properties; + pa_proplist *proplist; }; struct _GstPulseSinkClass diff --git a/ext/pulse/pulsesrc.c b/ext/pulse/pulsesrc.c index c0a38841d5..0baad9f163 100644 --- a/ext/pulse/pulsesrc.c +++ b/ext/pulse/pulsesrc.c @@ -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 pulseaudio api docs. + * + * 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", diff --git a/ext/pulse/pulsesrc.h b/ext/pulse/pulsesrc.h index be8943475b..fb5006d6f6 100644 --- a/ext/pulse/pulsesrc.h +++ b/ext/pulse/pulsesrc.h @@ -76,6 +76,9 @@ struct _GstPulseSrc gboolean operation_success:1; gboolean paused:1; gboolean in_read:1; + + GstStructure *properties; + pa_proplist *proplist; }; struct _GstPulseSrcClass diff --git a/ext/pulse/pulseutil.c b/ext/pulse/pulseutil.c index 2541459904..c779e1aff9 100644 --- a/ext/pulse/pulseutil.c +++ b/ext/pulse/pulseutil.c @@ -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; +} diff --git a/ext/pulse/pulseutil.h b/ext/pulse/pulseutil.h index 4cafba51b2..75b31120a9 100644 --- a/ext/pulse/pulseutil.h +++ b/ext/pulse/pulseutil.h @@ -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) {