diff options
author | Sebastian Dröge <slomo@circular-chaos.org> | 2008-08-20 17:17:55 +0000 |
---|---|---|
committer | Sebastian Dröge <slomo@circular-chaos.org> | 2008-08-20 17:17:55 +0000 |
commit | 4ab89b397e32c041fd8174071a7686b8547643d0 (patch) | |
tree | fa7ad90554168289ea1d570b6c40ed3a5d5fc072 /ext/pulse | |
parent | 74314914704609541ca0da9bf1f52469ea5e1079 (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.
Diffstat (limited to 'ext/pulse')
-rw-r--r-- | ext/pulse/pulsesrc.c | 271 | ||||
-rw-r--r-- | ext/pulse/pulseutil.c | 63 | ||||
-rw-r--r-- | ext/pulse/pulseutil.h | 3 |
3 files changed, 259 insertions, 78 deletions
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 |