mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-11 01:45:33 +00:00
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.
This commit is contained in:
parent
7431491470
commit
4ab89b397e
4 changed files with 271 additions and 78 deletions
12
ChangeLog
12
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>
|
||||
|
|
|
@ -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);
|
||||
|
@ -489,80 +492,6 @@ gst_pulsesrc_close (GstAudioSrc * asrc)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue