mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-11 18:05: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_VOLUME,
|
||||||
PROP_MUTE,
|
PROP_MUTE,
|
||||||
PROP_CLIENT,
|
PROP_CLIENT,
|
||||||
|
PROP_STREAM_PROPERTIES,
|
||||||
PROP_LAST
|
PROP_LAST
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -770,7 +771,11 @@ gst_pulseringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec)
|
||||||
|
|
||||||
/* create a stream */
|
/* create a stream */
|
||||||
GST_LOG_OBJECT (psink, "creating stream with name %s", name);
|
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)))
|
name, &pbuf->sample_spec, &channel_map)))
|
||||||
goto stream_failed;
|
goto stream_failed;
|
||||||
|
|
||||||
|
@ -1853,7 +1858,7 @@ gst_pulsesink_class_init (GstPulseSinkClass * klass)
|
||||||
/**
|
/**
|
||||||
* GstPulseSink:client
|
* GstPulseSink:client
|
||||||
*
|
*
|
||||||
* The PulseAudio client name to use
|
* The PulseAudio client name to use.
|
||||||
*
|
*
|
||||||
* Since: 0.10.25
|
* Since: 0.10.25
|
||||||
*/
|
*/
|
||||||
|
@ -1863,6 +1868,29 @@ gst_pulsesink_class_init (GstPulseSinkClass * klass)
|
||||||
"The PulseAudio client name to use", gst_pulse_client_name (),
|
"The PulseAudio client name to use", gst_pulse_client_name (),
|
||||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
|
||||||
GST_PARAM_MUTABLE_READY));
|
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 */
|
/* returns the current time of the sink ringbuffer */
|
||||||
|
@ -1926,6 +1954,9 @@ gst_pulsesink_init (GstPulseSink * pulsesink, GstPulseSinkClass * klass)
|
||||||
/* needed for conditional execution */
|
/* needed for conditional execution */
|
||||||
pulsesink->pa_version = pa_get_library_version ();
|
pulsesink->pa_version = pa_get_library_version ();
|
||||||
|
|
||||||
|
pulsesink->properties = NULL;
|
||||||
|
pulsesink->proplist = NULL;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (pulsesink, "using pulseaudio version %s",
|
GST_DEBUG_OBJECT (pulsesink, "using pulseaudio version %s",
|
||||||
pulsesink->pa_version);
|
pulsesink->pa_version);
|
||||||
|
|
||||||
|
@ -1945,6 +1976,11 @@ gst_pulsesink_finalize (GObject * object)
|
||||||
g_free (pulsesink->device_description);
|
g_free (pulsesink->device_description);
|
||||||
g_free (pulsesink->client_name);
|
g_free (pulsesink->client_name);
|
||||||
|
|
||||||
|
if (pulsesink->properties)
|
||||||
|
gst_structure_free (pulsesink->properties);
|
||||||
|
if (pulsesink->proplist)
|
||||||
|
pa_proplist_free (pulsesink->proplist);
|
||||||
|
|
||||||
if (pulsesink->probe) {
|
if (pulsesink->probe) {
|
||||||
gst_pulseprobe_free (pulsesink->probe);
|
gst_pulseprobe_free (pulsesink->probe);
|
||||||
pulsesink->probe = NULL;
|
pulsesink->probe = NULL;
|
||||||
|
@ -2385,6 +2421,15 @@ gst_pulsesink_set_property (GObject * object,
|
||||||
} else
|
} else
|
||||||
pulsesink->client_name = g_value_dup_string (value);
|
pulsesink->client_name = g_value_dup_string (value);
|
||||||
break;
|
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:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
|
@ -2419,6 +2464,9 @@ gst_pulsesink_get_property (GObject * object,
|
||||||
case PROP_CLIENT:
|
case PROP_CLIENT:
|
||||||
g_value_set_string (value, pulsesink->client_name);
|
g_value_set_string (value, pulsesink->client_name);
|
||||||
break;
|
break;
|
||||||
|
case PROP_STREAM_PROPERTIES:
|
||||||
|
gst_value_set_structure (value, pulsesink->properties);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -72,6 +72,8 @@ struct _GstPulseSink
|
||||||
|
|
||||||
const gchar *pa_version;
|
const gchar *pa_version;
|
||||||
|
|
||||||
|
GstStructure *properties;
|
||||||
|
pa_proplist *proplist;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstPulseSinkClass
|
struct _GstPulseSinkClass
|
||||||
|
|
|
@ -61,6 +61,7 @@ enum
|
||||||
PROP_SERVER,
|
PROP_SERVER,
|
||||||
PROP_DEVICE,
|
PROP_DEVICE,
|
||||||
PROP_DEVICE_NAME,
|
PROP_DEVICE_NAME,
|
||||||
|
PROP_STREAM_PROPERTIES,
|
||||||
PROP_LAST
|
PROP_LAST
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -244,6 +245,29 @@ gst_pulsesrc_class_init (GstPulseSrcClass * klass)
|
||||||
g_param_spec_string ("device-name", "Device name",
|
g_param_spec_string ("device-name", "Device name",
|
||||||
"Human-readable name of the sound device", DEFAULT_DEVICE_NAME,
|
"Human-readable name of the sound device", DEFAULT_DEVICE_NAME,
|
||||||
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
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
|
static void
|
||||||
|
@ -273,6 +297,9 @@ gst_pulsesrc_init (GstPulseSrc * pulsesrc, GstPulseSrcClass * klass)
|
||||||
|
|
||||||
pulsesrc->mixer = NULL;
|
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 */
|
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 */
|
/* 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->server);
|
||||||
g_free (pulsesrc->device);
|
g_free (pulsesrc->device);
|
||||||
|
|
||||||
|
if (pulsesrc->properties)
|
||||||
|
gst_structure_free (pulsesrc->properties);
|
||||||
|
if (pulsesrc->proplist)
|
||||||
|
pa_proplist_free (pulsesrc->proplist);
|
||||||
|
|
||||||
if (pulsesrc->mixer) {
|
if (pulsesrc->mixer) {
|
||||||
gst_pulsemixer_ctrl_free (pulsesrc->mixer);
|
gst_pulsemixer_ctrl_free (pulsesrc->mixer);
|
||||||
pulsesrc->mixer = NULL;
|
pulsesrc->mixer = NULL;
|
||||||
|
@ -432,6 +464,15 @@ gst_pulsesrc_set_property (GObject * object,
|
||||||
g_free (pulsesrc->device);
|
g_free (pulsesrc->device);
|
||||||
pulsesrc->device = g_value_dup_string (value);
|
pulsesrc->device = g_value_dup_string (value);
|
||||||
break;
|
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:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
|
@ -455,6 +496,9 @@ gst_pulsesrc_get_property (GObject * object,
|
||||||
case PROP_DEVICE_NAME:
|
case PROP_DEVICE_NAME:
|
||||||
g_value_take_string (value, gst_pulsesrc_device_description (pulsesrc));
|
g_value_take_string (value, gst_pulsesrc_device_description (pulsesrc));
|
||||||
break;
|
break;
|
||||||
|
case PROP_STREAM_PROPERTIES:
|
||||||
|
gst_value_set_structure (value, pulsesrc->properties);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
|
@ -797,6 +841,7 @@ gst_pulsesrc_create_stream (GstPulseSrc * pulsesrc, GstCaps * caps)
|
||||||
GstStructure *s;
|
GstStructure *s;
|
||||||
gboolean need_channel_layout = FALSE;
|
gboolean need_channel_layout = FALSE;
|
||||||
GstRingBufferSpec spec;
|
GstRingBufferSpec spec;
|
||||||
|
const gchar *name;
|
||||||
|
|
||||||
memset (&spec, 0, sizeof (GstRingBufferSpec));
|
memset (&spec, 0, sizeof (GstRingBufferSpec));
|
||||||
spec.latency_time = GST_SECOND;
|
spec.latency_time = GST_SECOND;
|
||||||
|
@ -832,9 +877,19 @@ gst_pulsesrc_create_stream (GstPulseSrc * pulsesrc, GstCaps * caps)
|
||||||
need_channel_layout = TRUE;
|
need_channel_layout = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(pulsesrc->stream = pa_stream_new (pulsesrc->context,
|
name = "Record Stream";
|
||||||
"Record Stream",
|
if (pulsesrc->proplist) {
|
||||||
&pulsesrc->sample_spec,
|
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))) {
|
(need_channel_layout) ? NULL : &channel_map))) {
|
||||||
GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED,
|
GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED,
|
||||||
("Failed to create stream: %s",
|
("Failed to create stream: %s",
|
||||||
|
|
|
@ -76,6 +76,9 @@ struct _GstPulseSrc
|
||||||
gboolean operation_success:1;
|
gboolean operation_success:1;
|
||||||
gboolean paused:1;
|
gboolean paused:1;
|
||||||
gboolean in_read:1;
|
gboolean in_read:1;
|
||||||
|
|
||||||
|
GstStructure *properties;
|
||||||
|
pa_proplist *proplist;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstPulseSrcClass
|
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));
|
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 *gst_pulse_channel_map_to_gst (const pa_channel_map * map,
|
||||||
GstRingBufferSpec * spec);
|
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
|
#if !HAVE_PULSE_0_9_11
|
||||||
static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) {
|
static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) {
|
||||||
|
|
Loading…
Reference in a new issue