mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-11 09:55:36 +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>
|
2008-08-20 Wim Taymans <wim.taymans@collabora.co.uk>
|
||||||
|
|
||||||
Patch by: Peter Kjellerstedt <pkj at axis com>
|
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,
|
static gboolean gst_pulsesrc_prepare (GstAudioSrc * asrc,
|
||||||
GstRingBufferSpec * spec);
|
GstRingBufferSpec * spec);
|
||||||
|
|
||||||
static gboolean gst_pulsesrc_unprepare (GstAudioSrc * asrc);
|
static gboolean gst_pulsesrc_unprepare (GstAudioSrc * asrc);
|
||||||
|
|
||||||
static guint gst_pulsesrc_read (GstAudioSrc * asrc, gpointer data,
|
static guint gst_pulsesrc_read (GstAudioSrc * asrc, gpointer data,
|
||||||
guint length);
|
guint length);
|
||||||
static guint gst_pulsesrc_delay (GstAudioSrc * asrc);
|
static guint gst_pulsesrc_delay (GstAudioSrc * asrc);
|
||||||
|
|
||||||
|
static gboolean gst_pulsesrc_negotiate (GstBaseSrc * basesrc);
|
||||||
|
|
||||||
static GstStateChangeReturn gst_pulsesrc_change_state (GstElement *
|
static GstStateChangeReturn gst_pulsesrc_change_state (GstElement *
|
||||||
element, GstStateChange transition);
|
element, GstStateChange transition);
|
||||||
|
|
||||||
|
@ -200,11 +203,9 @@ gst_pulsesrc_base_init (gpointer g_class)
|
||||||
static void
|
static void
|
||||||
gst_pulsesrc_class_init (gpointer g_class, gpointer class_data)
|
gst_pulsesrc_class_init (gpointer g_class, gpointer class_data)
|
||||||
{
|
{
|
||||||
|
|
||||||
GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
|
GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
|
||||||
|
|
||||||
GstAudioSrcClass *gstaudiosrc_class = GST_AUDIO_SRC_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);
|
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
|
||||||
|
|
||||||
parent_class = g_type_class_peek_parent (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->set_property = GST_DEBUG_FUNCPTR (gst_pulsesrc_set_property);
|
||||||
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_pulsesrc_get_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->open = GST_DEBUG_FUNCPTR (gst_pulsesrc_open);
|
||||||
gstaudiosrc_class->close = GST_DEBUG_FUNCPTR (gst_pulsesrc_close);
|
gstaudiosrc_class->close = GST_DEBUG_FUNCPTR (gst_pulsesrc_close);
|
||||||
gstaudiosrc_class->prepare = GST_DEBUG_FUNCPTR (gst_pulsesrc_prepare);
|
gstaudiosrc_class->prepare = GST_DEBUG_FUNCPTR (gst_pulsesrc_prepare);
|
||||||
|
@ -489,80 +492,6 @@ gst_pulsesrc_close (GstAudioSrc * asrc)
|
||||||
return TRUE;
|
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
|
static gboolean
|
||||||
gst_pulsesrc_unprepare (GstAudioSrc * asrc)
|
gst_pulsesrc_unprepare (GstAudioSrc * asrc)
|
||||||
{
|
{
|
||||||
|
@ -695,6 +624,194 @@ unlock_and_fail:
|
||||||
return 0;
|
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
|
static GstStateChangeReturn
|
||||||
gst_pulsesrc_change_state (GstElement * element, GstStateChange transition)
|
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
|
[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
|
gboolean
|
||||||
gst_pulse_fill_sample_spec (GstRingBufferSpec * spec, pa_sample_spec * ss)
|
gst_pulse_fill_sample_spec (GstRingBufferSpec * spec, pa_sample_spec * ss)
|
||||||
{
|
{
|
||||||
|
@ -95,7 +119,8 @@ gst_pulse_client_name (void)
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_channel_map *
|
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;
|
int i;
|
||||||
|
|
||||||
|
@ -136,3 +161,39 @@ gst_pulse_gst_to_channel_map (pa_channel_map * map, GstRingBufferSpec * spec)
|
||||||
|
|
||||||
return map;
|
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);
|
gchar *gst_pulse_client_name (void);
|
||||||
|
|
||||||
pa_channel_map *gst_pulse_gst_to_channel_map (pa_channel_map * map,
|
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);
|
GstRingBufferSpec * spec);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue