summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Dröge <slomo@circular-chaos.org>2008-08-20 17:17:55 +0000
committerSebastian Dröge <slomo@circular-chaos.org>2008-08-20 17:17:55 +0000
commit4ab89b397e32c041fd8174071a7686b8547643d0 (patch)
treefa7ad90554168289ea1d570b6c40ed3a5d5fc072
parent74314914704609541ca0da9bf1f52469ea5e1079 (diff)
ext/pulse/: If downstream provides no channel layout and >2 channels should be used use the default layout that pulse...
Original commit message from CVS: * ext/pulse/pulsesrc.c: (gst_pulsesrc_class_init), (gst_pulsesrc_create_stream), (gst_pulsesrc_negotiate), (gst_pulsesrc_prepare): * ext/pulse/pulseutil.c: (gst_pulse_gst_to_channel_map), (gst_pulse_channel_map_to_gst): * ext/pulse/pulseutil.h: If downstream provides no channel layout and >2 channels should be used use the default layout that pulseaudio chooses and also add this layout to the caps. Fixes bug #547258.
-rw-r--r--ChangeLog12
-rw-r--r--ext/pulse/pulsesrc.c271
-rw-r--r--ext/pulse/pulseutil.c63
-rw-r--r--ext/pulse/pulseutil.h3
4 files changed, 271 insertions, 78 deletions
diff --git a/ChangeLog b/ChangeLog
index c309f777..cbf9ebe8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2008-08-20 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+
+ * ext/pulse/pulsesrc.c: (gst_pulsesrc_class_init),
+ (gst_pulsesrc_create_stream), (gst_pulsesrc_negotiate),
+ (gst_pulsesrc_prepare):
+ * ext/pulse/pulseutil.c: (gst_pulse_gst_to_channel_map),
+ (gst_pulse_channel_map_to_gst):
+ * ext/pulse/pulseutil.h:
+ If downstream provides no channel layout and >2 channels should be
+ used use the default layout that pulseaudio chooses and also
+ add this layout to the caps. Fixes bug #547258.
+
2008-08-20 Wim Taymans <wim.taymans@collabora.co.uk>
Patch by: Peter Kjellerstedt <pkj at axis com>
diff --git a/ext/pulse/pulsesrc.c b/ext/pulse/pulsesrc.c
index 9302ca3a..6bc198df 100644
--- a/ext/pulse/pulsesrc.c
+++ b/ext/pulse/pulsesrc.c
@@ -84,12 +84,15 @@ static gboolean gst_pulsesrc_close (GstAudioSrc * asrc);
static gboolean gst_pulsesrc_prepare (GstAudioSrc * asrc,
GstRingBufferSpec * spec);
+
static gboolean gst_pulsesrc_unprepare (GstAudioSrc * asrc);
static guint gst_pulsesrc_read (GstAudioSrc * asrc, gpointer data,
guint length);
static guint gst_pulsesrc_delay (GstAudioSrc * asrc);
+static gboolean gst_pulsesrc_negotiate (GstBaseSrc * basesrc);
+
static GstStateChangeReturn gst_pulsesrc_change_state (GstElement *
element, GstStateChange transition);
@@ -200,11 +203,9 @@ gst_pulsesrc_base_init (gpointer g_class)
static void
gst_pulsesrc_class_init (gpointer g_class, gpointer class_data)
{
-
GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
-
GstAudioSrcClass *gstaudiosrc_class = GST_AUDIO_SRC_CLASS (g_class);
-
+ GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (g_class);
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
parent_class = g_type_class_peek_parent (g_class);
@@ -217,6 +218,8 @@ gst_pulsesrc_class_init (gpointer g_class, gpointer class_data)
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_pulsesrc_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_pulsesrc_get_property);
+ gstbasesrc_class->negotiate = GST_DEBUG_FUNCPTR (gst_pulsesrc_negotiate);
+
gstaudiosrc_class->open = GST_DEBUG_FUNCPTR (gst_pulsesrc_open);
gstaudiosrc_class->close = GST_DEBUG_FUNCPTR (gst_pulsesrc_close);
gstaudiosrc_class->prepare = GST_DEBUG_FUNCPTR (gst_pulsesrc_prepare);
@@ -490,80 +493,6 @@ gst_pulsesrc_close (GstAudioSrc * asrc)
}
static gboolean
-gst_pulsesrc_prepare (GstAudioSrc * asrc, GstRingBufferSpec * spec)
-{
- pa_buffer_attr buf_attr;
-
- pa_channel_map channel_map;
-
- GstPulseSrc *pulsesrc = GST_PULSESRC (asrc);
-
- if (!gst_pulse_fill_sample_spec (spec, &pulsesrc->sample_spec)) {
- GST_ELEMENT_ERROR (pulsesrc, RESOURCE, SETTINGS,
- ("Invalid sample specification."), (NULL));
- goto unlock_and_fail;
- }
-
- pa_threaded_mainloop_lock (pulsesrc->mainloop);
-
- if (!pulsesrc->context
- || pa_context_get_state (pulsesrc->context) != PA_CONTEXT_READY) {
- GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED, ("Bad context state: %s",
- pulsesrc->
- context ? pa_strerror (pa_context_errno (pulsesrc->context)) :
- NULL), (NULL));
- goto unlock_and_fail;
- }
-
- if (!(pulsesrc->stream = pa_stream_new (pulsesrc->context,
- "Record Stream",
- &pulsesrc->sample_spec,
- gst_pulse_gst_to_channel_map (&channel_map, spec)))) {
- GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED,
- ("Failed to create stream: %s",
- pa_strerror (pa_context_errno (pulsesrc->context))), (NULL));
- goto unlock_and_fail;
- }
-
- pa_stream_set_state_callback (pulsesrc->stream, gst_pulsesrc_stream_state_cb,
- pulsesrc);
- pa_stream_set_read_callback (pulsesrc->stream, gst_pulsesrc_stream_request_cb,
- pulsesrc);
-
- memset (&buf_attr, 0, sizeof (buf_attr));
- buf_attr.maxlength = spec->segtotal * spec->segsize * 2;
- buf_attr.fragsize = spec->segsize;
-
- if (pa_stream_connect_record (pulsesrc->stream, pulsesrc->device, &buf_attr,
- PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE |
- PA_STREAM_NOT_MONOTONOUS) < 0) {
- GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED,
- ("Failed to connect stream: %s",
- pa_strerror (pa_context_errno (pulsesrc->context))), (NULL));
- goto unlock_and_fail;
- }
-
- /* Wait until the stream is ready */
- pa_threaded_mainloop_wait (pulsesrc->mainloop);
-
- if (pa_stream_get_state (pulsesrc->stream) != PA_STREAM_READY) {
- GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED,
- ("Failed to connect stream: %s",
- pa_strerror (pa_context_errno (pulsesrc->context))), (NULL));
- goto unlock_and_fail;
- }
-
- pa_threaded_mainloop_unlock (pulsesrc->mainloop);
-
- return TRUE;
-
-unlock_and_fail:
-
- pa_threaded_mainloop_unlock (pulsesrc->mainloop);
- return FALSE;
-}
-
-static gboolean
gst_pulsesrc_unprepare (GstAudioSrc * asrc)
{
GstPulseSrc *pulsesrc = GST_PULSESRC (asrc);
@@ -695,6 +624,194 @@ unlock_and_fail:
return 0;
}
+static gboolean
+gst_pulsesrc_create_stream (GstPulseSrc * pulsesrc, GstCaps * caps)
+{
+ pa_channel_map channel_map;
+ GstStructure *s;
+ gboolean need_channel_layout = FALSE;
+ GstRingBufferSpec spec;
+
+ memset (&spec, 0, sizeof (GstRingBufferSpec));
+ spec.latency_time = GST_SECOND;
+ if (!gst_ring_buffer_parse_caps (&spec, caps)) {
+ GST_ELEMENT_ERROR (pulsesrc, RESOURCE, SETTINGS,
+ ("Can't parse caps."), (NULL));
+ goto fail;
+ }
+ /* Keep the refcount of the caps at 1 to make them writable */
+ gst_caps_unref (spec.caps);
+
+ if (!gst_pulse_fill_sample_spec (&spec, &pulsesrc->sample_spec)) {
+ GST_ELEMENT_ERROR (pulsesrc, RESOURCE, SETTINGS,
+ ("Invalid sample specification."), (NULL));
+ goto fail;
+ }
+
+ pa_threaded_mainloop_lock (pulsesrc->mainloop);
+
+ if (!pulsesrc->context
+ || pa_context_get_state (pulsesrc->context) != PA_CONTEXT_READY) {
+ GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED, ("Bad context state: %s",
+ pulsesrc->context ? pa_strerror (pa_context_errno (pulsesrc->
+ context)) : NULL), (NULL));
+ goto unlock_and_fail;
+ }
+
+ s = gst_caps_get_structure (caps, 0);
+ if (!gst_structure_has_field (s, "channel-layout") ||
+ !gst_pulse_gst_to_channel_map (&channel_map, &spec)) {
+ if (spec.channels == 1)
+ pa_channel_map_init_mono (&channel_map);
+ else if (spec.channels == 2)
+ pa_channel_map_init_stereo (&channel_map);
+ else
+ need_channel_layout = TRUE;
+ }
+
+ if (!(pulsesrc->stream = pa_stream_new (pulsesrc->context,
+ "Record Stream",
+ &pulsesrc->sample_spec,
+ (need_channel_layout) ? NULL : &channel_map))) {
+ GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED,
+ ("Failed to create stream: %s",
+ pa_strerror (pa_context_errno (pulsesrc->context))), (NULL));
+ goto unlock_and_fail;
+ }
+
+ if (need_channel_layout) {
+ const pa_channel_map *m = pa_stream_get_channel_map (pulsesrc->stream);
+
+ gst_pulse_channel_map_to_gst (m, &spec);
+ caps = spec.caps;
+ }
+
+ GST_DEBUG_OBJECT (pulsesrc, "Caps are %" GST_PTR_FORMAT, caps);
+
+ pa_stream_set_state_callback (pulsesrc->stream, gst_pulsesrc_stream_state_cb,
+ pulsesrc);
+ pa_stream_set_read_callback (pulsesrc->stream, gst_pulsesrc_stream_request_cb,
+ pulsesrc);
+
+ return TRUE;
+
+unlock_and_fail:
+ pa_threaded_mainloop_unlock (pulsesrc->mainloop);
+
+fail:
+ return FALSE;
+}
+
+/* This is essentially gst_base_src_negotiate_default() but the caps
+ * are guaranteed to have a channel layout for > 2 channels
+ */
+static gboolean
+gst_pulsesrc_negotiate (GstBaseSrc * basesrc)
+{
+ GstCaps *thiscaps;
+ GstCaps *caps = NULL;
+ GstCaps *peercaps = NULL;
+ gboolean result = FALSE;
+
+ /* first see what is possible on our source pad */
+ thiscaps = gst_pad_get_caps (GST_BASE_SRC_PAD (basesrc));
+ GST_DEBUG_OBJECT (basesrc, "caps of src: %" GST_PTR_FORMAT, thiscaps);
+ /* nothing or anything is allowed, we're done */
+ if (thiscaps == NULL || gst_caps_is_any (thiscaps))
+ goto no_nego_needed;
+
+ /* get the peer caps */
+ peercaps = gst_pad_peer_get_caps (GST_BASE_SRC_PAD (basesrc));
+ GST_DEBUG_OBJECT (basesrc, "caps of peer: %" GST_PTR_FORMAT, peercaps);
+ if (peercaps) {
+ GstCaps *icaps;
+
+ /* get intersection */
+ icaps = gst_caps_intersect (thiscaps, peercaps);
+ GST_DEBUG_OBJECT (basesrc, "intersect: %" GST_PTR_FORMAT, icaps);
+ gst_caps_unref (thiscaps);
+ gst_caps_unref (peercaps);
+ if (icaps) {
+ /* take first (and best, since they are sorted) possibility */
+ caps = gst_caps_copy_nth (icaps, 0);
+ gst_caps_unref (icaps);
+ }
+ } else {
+ /* no peer, work with our own caps then */
+ caps = thiscaps;
+ }
+ if (caps) {
+ caps = gst_caps_make_writable (caps);
+ gst_caps_truncate (caps);
+
+ /* now fixate */
+ if (!gst_caps_is_empty (caps)) {
+ gst_pad_fixate_caps (GST_BASE_SRC_PAD (basesrc), caps);
+ GST_DEBUG_OBJECT (basesrc, "fixated to: %" GST_PTR_FORMAT, caps);
+
+ if (gst_caps_is_any (caps)) {
+ /* hmm, still anything, so element can do anything and
+ * nego is not needed */
+ result = TRUE;
+ } else if (gst_caps_is_fixed (caps)) {
+ /* yay, fixed caps, use those then */
+ result = gst_pulsesrc_create_stream (GST_PULSESRC (basesrc), caps);
+ if (result)
+ gst_pad_set_caps (GST_BASE_SRC_PAD (basesrc), caps);
+ result = TRUE;
+ }
+ }
+ gst_caps_unref (caps);
+ }
+ return result;
+
+no_nego_needed:
+ {
+ GST_DEBUG_OBJECT (basesrc, "no negotiation needed");
+ if (thiscaps)
+ gst_caps_unref (thiscaps);
+ return TRUE;
+ }
+}
+
+static gboolean
+gst_pulsesrc_prepare (GstAudioSrc * asrc, GstRingBufferSpec * spec)
+{
+ pa_buffer_attr buf_attr;
+ GstPulseSrc *pulsesrc = GST_PULSESRC (asrc);
+
+ memset (&buf_attr, 0, sizeof (buf_attr));
+ buf_attr.maxlength = spec->segtotal * spec->segsize * 2;
+ buf_attr.fragsize = spec->segsize;
+
+ if (pa_stream_connect_record (pulsesrc->stream, pulsesrc->device, &buf_attr,
+ PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE |
+ PA_STREAM_NOT_MONOTONOUS) < 0) {
+ GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED,
+ ("Failed to connect stream: %s",
+ pa_strerror (pa_context_errno (pulsesrc->context))), (NULL));
+ goto unlock_and_fail;
+ }
+
+ /* Wait until the stream is ready */
+ pa_threaded_mainloop_wait (pulsesrc->mainloop);
+
+ if (pa_stream_get_state (pulsesrc->stream) != PA_STREAM_READY) {
+ GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED,
+ ("Failed to connect stream: %s",
+ pa_strerror (pa_context_errno (pulsesrc->context))), (NULL));
+ goto unlock_and_fail;
+ }
+
+ pa_threaded_mainloop_unlock (pulsesrc->mainloop);
+
+ return TRUE;
+
+unlock_and_fail:
+ pa_threaded_mainloop_unlock (pulsesrc->mainloop);
+ return FALSE;
+}
+
static GstStateChangeReturn
gst_pulsesrc_change_state (GstElement * element, GstStateChange transition)
{
diff --git a/ext/pulse/pulseutil.c b/ext/pulse/pulseutil.c
index 518bce2f..62dbc5b0 100644
--- a/ext/pulse/pulseutil.c
+++ b/ext/pulse/pulseutil.c
@@ -45,6 +45,30 @@ static const pa_channel_position_t gst_pos_to_pa[GST_AUDIO_CHANNEL_POSITION_NUM]
[GST_AUDIO_CHANNEL_POSITION_NONE] = PA_CHANNEL_POSITION_INVALID
};
+/* All index are increased by one because PA_CHANNEL_POSITION_INVALID == -1 */
+static const GstAudioChannelPosition
+ pa_to_gst_pos[GST_AUDIO_CHANNEL_POSITION_NUM]
+ = {
+ [PA_CHANNEL_POSITION_MONO + 1] = GST_AUDIO_CHANNEL_POSITION_FRONT_MONO,
+ [PA_CHANNEL_POSITION_FRONT_LEFT + 1] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
+ [PA_CHANNEL_POSITION_FRONT_RIGHT + 1] =
+ GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
+ [PA_CHANNEL_POSITION_REAR_CENTER + 1] =
+ GST_AUDIO_CHANNEL_POSITION_REAR_CENTER,
+ [PA_CHANNEL_POSITION_REAR_LEFT + 1] = GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
+ [PA_CHANNEL_POSITION_REAR_RIGHT + 1] = GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
+ [PA_CHANNEL_POSITION_LFE + 1] = GST_AUDIO_CHANNEL_POSITION_LFE,
+ [PA_CHANNEL_POSITION_FRONT_CENTER + 1] =
+ GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
+ [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER + 1] =
+ GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
+ [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER + 1] =
+ GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
+ [PA_CHANNEL_POSITION_SIDE_LEFT + 1] = GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT,
+ [PA_CHANNEL_POSITION_SIDE_RIGHT + 1] = GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT,
+ [PA_CHANNEL_POSITION_INVALID + 1] = GST_AUDIO_CHANNEL_POSITION_NONE,
+};
+
gboolean
gst_pulse_fill_sample_spec (GstRingBufferSpec * spec, pa_sample_spec * ss)
{
@@ -95,7 +119,8 @@ gst_pulse_client_name (void)
}
pa_channel_map *
-gst_pulse_gst_to_channel_map (pa_channel_map * map, GstRingBufferSpec * spec)
+gst_pulse_gst_to_channel_map (pa_channel_map * map,
+ const GstRingBufferSpec * spec)
{
int i;
@@ -136,3 +161,39 @@ gst_pulse_gst_to_channel_map (pa_channel_map * map, GstRingBufferSpec * spec)
return map;
}
+
+GstRingBufferSpec *
+gst_pulse_channel_map_to_gst (const pa_channel_map * map,
+ GstRingBufferSpec * spec)
+{
+ int i;
+ GstAudioChannelPosition *pos;
+ gboolean invalid = FALSE;
+
+ g_return_val_if_fail (map->channels == spec->channels, NULL);
+
+ pos = g_new0 (GstAudioChannelPosition, spec->channels + 1);
+
+ for (i = 0; i < spec->channels; i++) {
+ if (map->map[i] == PA_CHANNEL_POSITION_INVALID) {
+ invalid = TRUE;
+ break;
+ } else if (map->map[i] < GST_AUDIO_CHANNEL_POSITION_NUM) {
+ pos[i] = pa_to_gst_pos[map->map[i] + 1];
+ } else {
+ invalid = TRUE;
+ break;
+ }
+ }
+
+ if (invalid) {
+ for (i = 0; i < spec->channels; i++)
+ pos[i] = GST_AUDIO_CHANNEL_POSITION_NONE;
+ }
+
+ gst_audio_set_channel_positions (gst_caps_get_structure (spec->caps, 0), pos);
+
+ g_free (pos);
+
+ return spec;
+}
diff --git a/ext/pulse/pulseutil.h b/ext/pulse/pulseutil.h
index f4689849..8700a979 100644
--- a/ext/pulse/pulseutil.h
+++ b/ext/pulse/pulseutil.h
@@ -32,6 +32,9 @@ gboolean gst_pulse_fill_sample_spec (GstRingBufferSpec * spec,
gchar *gst_pulse_client_name (void);
pa_channel_map *gst_pulse_gst_to_channel_map (pa_channel_map * map,
+ const GstRingBufferSpec * spec);
+
+GstRingBufferSpec *gst_pulse_channel_map_to_gst (const pa_channel_map * map,
GstRingBufferSpec * spec);
#endif