mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-24 01:00:37 +00:00
pulse: allow setting stream properties
Add a "properties" property to the elements to allow setting extra stream properties. Fixes #537544
This commit is contained in:
parent
956074a3cf
commit
f604e20499
6 changed files with 149 additions and 6 deletions
|
@ -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;
|
||||
|
|
|
@ -72,6 +72,8 @@ struct _GstPulseSink
|
|||
|
||||
const gchar *pa_version;
|
||||
|
||||
GstStructure *properties;
|
||||
pa_proplist *proplist;
|
||||
};
|
||||
|
||||
struct _GstPulseSinkClass
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -76,6 +76,9 @@ struct _GstPulseSrc
|
|||
gboolean operation_success:1;
|
||||
gboolean paused:1;
|
||||
gboolean in_read:1;
|
||||
|
||||
GstStructure *properties;
|
||||
pa_proplist *proplist;
|
||||
};
|
||||
|
||||
struct _GstPulseSrcClass
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in a new issue