mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 04:01:08 +00:00
osxaudio: Overhaul of probing caps
- Probing caps is unified between source and sink - Hardware stream format is now reported as preferred capabilities (dynamically updated when hardware configuration changes) - Get hardware channel layout from Remote IO just like from HAL - More comprehensive mapping between AudioChannelLabel and GstAudioChannelPosition - Support for unpositioned channel layouts - Announce stereo-mono upmixing/downmixing in caps https://bugzilla.gnome.org/show_bug.cgi?id=743758
This commit is contained in:
parent
8a8884e150
commit
0e5d698c6f
11 changed files with 614 additions and 373 deletions
|
@ -220,10 +220,9 @@ gst_osx_audio_ring_buffer_acquire (GstAudioRingBuffer * buf,
|
||||||
GST_DEBUG_OBJECT (osxbuf, "Format: " CORE_AUDIO_FORMAT,
|
GST_DEBUG_OBJECT (osxbuf, "Format: " CORE_AUDIO_FORMAT,
|
||||||
CORE_AUDIO_FORMAT_ARGS (format));
|
CORE_AUDIO_FORMAT_ARGS (format));
|
||||||
|
|
||||||
if (GST_IS_OSX_AUDIO_SINK (GST_OBJECT_PARENT (buf))) {
|
/* gst_audio_ring_buffer_set_channel_positions is not called
|
||||||
gst_audio_ring_buffer_set_channel_positions (buf,
|
* since the AUs perform channel reordering themselves.
|
||||||
GST_OSX_AUDIO_SINK (GST_OBJECT_PARENT (buf))->channel_positions);
|
* (see gst_core_audio_set_channel_layout) */
|
||||||
}
|
|
||||||
|
|
||||||
buf->size = spec->segtotal * spec->segsize;
|
buf->size = spec->segtotal * spec->segsize;
|
||||||
buf->memory = g_malloc0 (buf->size);
|
buf->memory = g_malloc0 (buf->size);
|
||||||
|
|
|
@ -116,7 +116,6 @@ gst_osx_audio_sink_change_state (GstElement * element,
|
||||||
|
|
||||||
static gboolean gst_osx_audio_sink_query (GstBaseSink * base, GstQuery * query);
|
static gboolean gst_osx_audio_sink_query (GstBaseSink * base, GstQuery * query);
|
||||||
|
|
||||||
static gboolean gst_osx_audio_sink_stop (GstBaseSink * base);
|
|
||||||
static GstCaps *gst_osx_audio_sink_getcaps (GstBaseSink * base,
|
static GstCaps *gst_osx_audio_sink_getcaps (GstBaseSink * base,
|
||||||
GstCaps * filter);
|
GstCaps * filter);
|
||||||
static gboolean gst_osx_audio_sink_acceptcaps (GstOsxAudioSink * sink,
|
static gboolean gst_osx_audio_sink_acceptcaps (GstOsxAudioSink * sink,
|
||||||
|
@ -128,7 +127,6 @@ static GstAudioRingBuffer
|
||||||
* gst_osx_audio_sink_create_ringbuffer (GstAudioBaseSink * sink);
|
* gst_osx_audio_sink_create_ringbuffer (GstAudioBaseSink * sink);
|
||||||
static void gst_osx_audio_sink_osxelement_init (gpointer g_iface,
|
static void gst_osx_audio_sink_osxelement_init (gpointer g_iface,
|
||||||
gpointer iface_data);
|
gpointer iface_data);
|
||||||
static void gst_osx_audio_sink_probe_caps (GstOsxAudioSink * sink);
|
|
||||||
static void gst_osx_audio_sink_set_volume (GstOsxAudioSink * sink);
|
static void gst_osx_audio_sink_set_volume (GstOsxAudioSink * sink);
|
||||||
|
|
||||||
static OSStatus gst_osx_audio_sink_io_proc (GstOsxAudioRingBuffer * buf,
|
static OSStatus gst_osx_audio_sink_io_proc (GstOsxAudioRingBuffer * buf,
|
||||||
|
@ -191,7 +189,6 @@ gst_osx_audio_sink_class_init (GstOsxAudioSinkClass * klass)
|
||||||
0, 1.0, 1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
0, 1.0, 1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_osx_audio_sink_getcaps);
|
gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_osx_audio_sink_getcaps);
|
||||||
gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_osx_audio_sink_stop);
|
|
||||||
|
|
||||||
gstaudiobasesink_class->create_ringbuffer =
|
gstaudiobasesink_class->create_ringbuffer =
|
||||||
GST_DEBUG_FUNCPTR (gst_osx_audio_sink_create_ringbuffer);
|
GST_DEBUG_FUNCPTR (gst_osx_audio_sink_create_ringbuffer);
|
||||||
|
@ -210,19 +207,10 @@ gst_osx_audio_sink_class_init (GstOsxAudioSinkClass * klass)
|
||||||
static void
|
static void
|
||||||
gst_osx_audio_sink_init (GstOsxAudioSink * sink)
|
gst_osx_audio_sink_init (GstOsxAudioSink * sink)
|
||||||
{
|
{
|
||||||
gint i;
|
|
||||||
|
|
||||||
GST_DEBUG ("Initialising object");
|
GST_DEBUG ("Initialising object");
|
||||||
|
|
||||||
sink->device_id = kAudioDeviceUnknown;
|
sink->device_id = kAudioDeviceUnknown;
|
||||||
sink->cached_caps = NULL;
|
|
||||||
|
|
||||||
sink->volume = DEFAULT_VOLUME;
|
sink->volume = DEFAULT_VOLUME;
|
||||||
|
|
||||||
sink->channels = 0;
|
|
||||||
for (i = 0; i < GST_OSX_AUDIO_MAX_CHANNEL; i++) {
|
|
||||||
sink->channel_positions[i] = GST_AUDIO_CHANNEL_POSITION_INVALID;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -330,28 +318,14 @@ gst_osx_audio_sink_query (GstBaseSink * base, GstQuery * query)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_osx_audio_sink_stop (GstBaseSink * base)
|
|
||||||
{
|
|
||||||
GstOsxAudioSink *sink = GST_OSX_AUDIO_SINK (base);
|
|
||||||
|
|
||||||
if (sink->cached_caps) {
|
|
||||||
gst_caps_unref (sink->cached_caps);
|
|
||||||
sink->cached_caps = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return GST_CALL_PARENT_WITH_DEFAULT (GST_BASE_SINK_CLASS, stop, (base), TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static GstCaps *
|
static GstCaps *
|
||||||
gst_osx_audio_sink_getcaps (GstBaseSink * sink, GstCaps * filter)
|
gst_osx_audio_sink_getcaps (GstBaseSink * sink, GstCaps * filter)
|
||||||
{
|
{
|
||||||
GstElementClass *gstelement_class;
|
|
||||||
GstOsxAudioSink *osxsink;
|
GstOsxAudioSink *osxsink;
|
||||||
GstAudioRingBuffer *buf;
|
GstAudioRingBuffer *buf;
|
||||||
GstCaps *ret = NULL;
|
GstOsxAudioRingBuffer *osxbuf;
|
||||||
|
GstCaps *caps, *filtered_caps;
|
||||||
|
|
||||||
gstelement_class = GST_ELEMENT_GET_CLASS (sink);
|
|
||||||
osxsink = GST_OSX_AUDIO_SINK (sink);
|
osxsink = GST_OSX_AUDIO_SINK (sink);
|
||||||
|
|
||||||
GST_OBJECT_LOCK (osxsink);
|
GST_OBJECT_LOCK (osxsink);
|
||||||
|
@ -360,33 +334,51 @@ gst_osx_audio_sink_getcaps (GstBaseSink * sink, GstCaps * filter)
|
||||||
gst_object_ref (buf);
|
gst_object_ref (buf);
|
||||||
GST_OBJECT_UNLOCK (osxsink);
|
GST_OBJECT_UNLOCK (osxsink);
|
||||||
|
|
||||||
if (buf) {
|
if (!buf) {
|
||||||
GST_OBJECT_LOCK (buf);
|
GST_DEBUG_OBJECT (sink, "no ring buffer, returning NULL caps");
|
||||||
|
return GST_BASE_SINK_CLASS (parent_class)->get_caps (sink, filter);
|
||||||
if (buf->open && !osxsink->cached_caps) {
|
|
||||||
/* Device is open, let's probe its caps */
|
|
||||||
gst_osx_audio_sink_probe_caps (osxsink);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (osxsink->cached_caps)
|
osxbuf = GST_OSX_AUDIO_RING_BUFFER (buf);
|
||||||
ret = gst_caps_ref (osxsink->cached_caps);
|
|
||||||
|
/* protect against cached_caps going away */
|
||||||
|
GST_OBJECT_LOCK (buf);
|
||||||
|
|
||||||
|
if (osxbuf->core_audio->cached_caps) {
|
||||||
|
GST_LOG_OBJECT (sink, "Returning cached caps");
|
||||||
|
caps = gst_caps_ref (osxbuf->core_audio->cached_caps);
|
||||||
|
} else if (buf->open) {
|
||||||
|
GstCaps *template_caps;
|
||||||
|
|
||||||
|
/* Get template caps */
|
||||||
|
template_caps =
|
||||||
|
gst_pad_get_pad_template_caps (GST_AUDIO_BASE_SINK_PAD (osxsink));
|
||||||
|
|
||||||
|
/* Device is open, let's probe its caps */
|
||||||
|
caps = gst_core_audio_probe_caps (osxbuf->core_audio, template_caps);
|
||||||
|
gst_caps_replace (&osxbuf->core_audio->cached_caps, caps);
|
||||||
|
|
||||||
|
gst_caps_unref (template_caps);
|
||||||
|
} else {
|
||||||
|
GST_DEBUG_OBJECT (sink, "ring buffer not open, returning NULL caps");
|
||||||
|
caps = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
GST_OBJECT_UNLOCK (buf);
|
GST_OBJECT_UNLOCK (buf);
|
||||||
|
|
||||||
gst_object_unref (buf);
|
gst_object_unref (buf);
|
||||||
}
|
|
||||||
|
|
||||||
if (!ret)
|
if (!caps)
|
||||||
ret = gst_pad_get_pad_template_caps (GST_AUDIO_BASE_SINK_PAD (osxsink));
|
return NULL;
|
||||||
|
|
||||||
if (filter) {
|
if (!filter)
|
||||||
GstCaps *tmp;
|
return caps;
|
||||||
tmp = gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST);
|
|
||||||
gst_caps_unref (ret);
|
|
||||||
ret = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
/* Take care of filtered caps */
|
||||||
|
filtered_caps =
|
||||||
|
gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
|
||||||
|
gst_caps_unref (caps);
|
||||||
|
return filtered_caps;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -586,85 +578,3 @@ gst_osx_audio_sink_set_volume (GstOsxAudioSink * sink)
|
||||||
|
|
||||||
gst_core_audio_set_volume (osxbuf->core_audio, sink->volume);
|
gst_core_audio_set_volume (osxbuf->core_audio, sink->volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
gst_osx_audio_sink_probe_caps (GstOsxAudioSink * osxsink)
|
|
||||||
{
|
|
||||||
gint i, channels;
|
|
||||||
gboolean spdif_allowed;
|
|
||||||
AudioChannelLayout *layout;
|
|
||||||
GstElementClass *element_class;
|
|
||||||
GstPadTemplate *pad_template;
|
|
||||||
GstCaps *caps, *in_caps;
|
|
||||||
guint64 channel_mask = 0;
|
|
||||||
GstAudioChannelPosition *pos = osxsink->channel_positions;
|
|
||||||
|
|
||||||
/* First collect info about the HW capabilites and preferences */
|
|
||||||
spdif_allowed =
|
|
||||||
gst_core_audio_audio_device_is_spdif_avail (osxsink->device_id);
|
|
||||||
layout = gst_core_audio_audio_device_get_channel_layout (osxsink->device_id,
|
|
||||||
TRUE);
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (osxsink, "Selected device ID: %u SPDIF allowed: %d",
|
|
||||||
(unsigned) osxsink->device_id, spdif_allowed);
|
|
||||||
|
|
||||||
if (layout) {
|
|
||||||
channels = MIN (layout->mNumberChannelDescriptions,
|
|
||||||
GST_OSX_AUDIO_MAX_CHANNEL);
|
|
||||||
} else {
|
|
||||||
GST_WARNING_OBJECT (osxsink, "This driver does not support "
|
|
||||||
"kAudioDevicePropertyPreferredChannelLayout.");
|
|
||||||
channels = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!gst_core_audio_parse_channel_layout (layout, channels, &channel_mask,
|
|
||||||
pos)) {
|
|
||||||
GST_WARNING_OBJECT (osxsink, "Failed to parse channel layout");
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free (layout);
|
|
||||||
|
|
||||||
if (!gst_audio_channel_positions_to_mask (pos, channels, TRUE, &channel_mask)) {
|
|
||||||
GST_WARNING_OBJECT (osxsink, "Probably unsupported channel order");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Recover the template caps */
|
|
||||||
element_class = GST_ELEMENT_GET_CLASS (osxsink);
|
|
||||||
pad_template = gst_element_class_get_pad_template (element_class, "sink");
|
|
||||||
in_caps = gst_pad_template_get_caps (pad_template);
|
|
||||||
|
|
||||||
/* Create the allowed subset */
|
|
||||||
caps = gst_caps_new_empty ();
|
|
||||||
for (i = 0; i < gst_caps_get_size (in_caps); i++) {
|
|
||||||
GstStructure *in_s, *out_s;
|
|
||||||
|
|
||||||
in_s = gst_caps_get_structure (in_caps, i);
|
|
||||||
|
|
||||||
if (gst_structure_has_name (in_s, "audio/x-ac3") ||
|
|
||||||
gst_structure_has_name (in_s, "audio/x-dts")) {
|
|
||||||
if (spdif_allowed) {
|
|
||||||
gst_caps_append_structure (caps, gst_structure_copy (in_s));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
out_s = gst_structure_copy (in_s);
|
|
||||||
|
|
||||||
gst_structure_remove_fields (out_s, "channels", NULL);
|
|
||||||
gst_structure_set (out_s, "channels", G_TYPE_INT, channels, NULL);
|
|
||||||
|
|
||||||
if (channel_mask) {
|
|
||||||
gst_structure_remove_fields (out_s, "channel-mask", NULL);
|
|
||||||
gst_structure_set (out_s, "channel-mask", GST_TYPE_BITMASK,
|
|
||||||
channel_mask, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_caps_append_structure (caps, out_s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (osxsink->cached_caps) {
|
|
||||||
gst_caps_unref (osxsink->cached_caps);
|
|
||||||
}
|
|
||||||
|
|
||||||
osxsink->cached_caps = caps;
|
|
||||||
osxsink->channels = channels;
|
|
||||||
}
|
|
||||||
|
|
|
@ -77,10 +77,8 @@ struct _GstOsxAudioSink
|
||||||
|
|
||||||
AudioUnit audiounit;
|
AudioUnit audiounit;
|
||||||
double volume;
|
double volume;
|
||||||
GstCaps *cached_caps;
|
|
||||||
|
|
||||||
guint channels;
|
guint channels;
|
||||||
GstAudioChannelPosition channel_positions[GST_OSX_AUDIO_MAX_CHANNEL];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstOsxAudioSinkClass
|
struct _GstOsxAudioSinkClass
|
||||||
|
|
|
@ -97,7 +97,6 @@ static GstStateChangeReturn
|
||||||
gst_osx_audio_src_change_state (GstElement * element,
|
gst_osx_audio_src_change_state (GstElement * element,
|
||||||
GstStateChange transition);
|
GstStateChange transition);
|
||||||
|
|
||||||
static void gst_osx_audio_src_probe_caps (GstOsxAudioSrc * src);
|
|
||||||
static GstCaps *gst_osx_audio_src_get_caps (GstBaseSrc * src, GstCaps * filter);
|
static GstCaps *gst_osx_audio_src_get_caps (GstBaseSrc * src, GstCaps * filter);
|
||||||
|
|
||||||
static GstAudioRingBuffer *gst_osx_audio_src_create_ringbuffer (GstAudioBaseSrc
|
static GstAudioRingBuffer *gst_osx_audio_src_create_ringbuffer (GstAudioBaseSrc
|
||||||
|
@ -171,7 +170,6 @@ gst_osx_audio_src_init (GstOsxAudioSrc * src)
|
||||||
gst_base_src_set_live (GST_BASE_SRC (src), TRUE);
|
gst_base_src_set_live (GST_BASE_SRC (src), TRUE);
|
||||||
|
|
||||||
src->device_id = kAudioDeviceUnknown;
|
src->device_id = kAudioDeviceUnknown;
|
||||||
src->cached_caps = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -241,72 +239,14 @@ out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
gst_osx_audio_src_probe_caps (GstOsxAudioSrc * osxsrc)
|
|
||||||
{
|
|
||||||
GstOsxAudioRingBuffer *ringbuffer =
|
|
||||||
GST_OSX_AUDIO_RING_BUFFER (GST_AUDIO_BASE_SRC (osxsrc)->ringbuffer);
|
|
||||||
GstCoreAudio *core_audio = ringbuffer->core_audio;
|
|
||||||
GstCaps *caps;
|
|
||||||
gint channels;
|
|
||||||
AudioChannelLayout *layout;
|
|
||||||
AudioStreamBasicDescription asbd_in;
|
|
||||||
UInt32 propertySize;
|
|
||||||
OSStatus status;
|
|
||||||
|
|
||||||
propertySize = sizeof (asbd_in);
|
|
||||||
status = AudioUnitGetProperty (core_audio->audiounit,
|
|
||||||
kAudioUnitProperty_StreamFormat,
|
|
||||||
kAudioUnitScope_Input, 1, &asbd_in, &propertySize);
|
|
||||||
if (status)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
layout = gst_core_audio_audio_device_get_channel_layout (osxsrc->device_id,
|
|
||||||
FALSE);
|
|
||||||
|
|
||||||
if (layout) {
|
|
||||||
channels = MIN (layout->mNumberChannelDescriptions,
|
|
||||||
GST_OSX_AUDIO_MAX_CHANNEL);
|
|
||||||
} else {
|
|
||||||
GST_WARNING_OBJECT (osxsrc, "This driver does not support "
|
|
||||||
"kAudioDevicePropertyPreferredChannelLayout.");
|
|
||||||
channels = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
caps = gst_core_audio_asbd_to_caps (&asbd_in, layout);
|
|
||||||
if (!caps) {
|
|
||||||
GST_WARNING_OBJECT (osxsrc, "Could not get caps from stream description");
|
|
||||||
g_free (layout);
|
|
||||||
goto fail;
|
|
||||||
} else {
|
|
||||||
GST_DEBUG_OBJECT (osxsrc, "Got caps on device: %p", caps);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free (layout);
|
|
||||||
|
|
||||||
if (osxsrc->cached_caps)
|
|
||||||
gst_caps_unref (osxsrc->cached_caps);
|
|
||||||
|
|
||||||
osxsrc->cached_caps = caps;
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
fail:
|
|
||||||
AudioComponentInstanceDispose (core_audio->audiounit);
|
|
||||||
core_audio->audiounit = NULL;
|
|
||||||
GST_WARNING_OBJECT (osxsrc,
|
|
||||||
"Unable to obtain device properties: %d", (int) status);
|
|
||||||
}
|
|
||||||
|
|
||||||
static GstCaps *
|
static GstCaps *
|
||||||
gst_osx_audio_src_get_caps (GstBaseSrc * src, GstCaps * filter)
|
gst_osx_audio_src_get_caps (GstBaseSrc * src, GstCaps * filter)
|
||||||
{
|
{
|
||||||
GstElementClass *gstelement_class;
|
|
||||||
GstOsxAudioSrc *osxsrc;
|
GstOsxAudioSrc *osxsrc;
|
||||||
GstAudioRingBuffer *buf;
|
GstAudioRingBuffer *buf;
|
||||||
GstCaps *ret = NULL;
|
GstOsxAudioRingBuffer *osxbuf;
|
||||||
|
GstCaps *caps, *filtered_caps;
|
||||||
|
|
||||||
gstelement_class = GST_ELEMENT_GET_CLASS (src);
|
|
||||||
osxsrc = GST_OSX_AUDIO_SRC (src);
|
osxsrc = GST_OSX_AUDIO_SRC (src);
|
||||||
|
|
||||||
GST_OBJECT_LOCK (osxsrc);
|
GST_OBJECT_LOCK (osxsrc);
|
||||||
|
@ -315,31 +255,51 @@ gst_osx_audio_src_get_caps (GstBaseSrc * src, GstCaps * filter)
|
||||||
gst_object_ref (buf);
|
gst_object_ref (buf);
|
||||||
GST_OBJECT_UNLOCK (osxsrc);
|
GST_OBJECT_UNLOCK (osxsrc);
|
||||||
|
|
||||||
if (buf) {
|
if (!buf) {
|
||||||
|
GST_DEBUG_OBJECT (src, "no ring buffer, using template caps");
|
||||||
|
return GST_BASE_SRC_CLASS (parent_class)->get_caps (src, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
osxbuf = GST_OSX_AUDIO_RING_BUFFER (buf);
|
||||||
|
|
||||||
|
/* protect against cached_caps going away */
|
||||||
GST_OBJECT_LOCK (buf);
|
GST_OBJECT_LOCK (buf);
|
||||||
|
|
||||||
if (buf->open && !osxsrc->cached_caps) {
|
if (osxbuf->core_audio->cached_caps) {
|
||||||
|
GST_LOG_OBJECT (src, "Returning cached caps");
|
||||||
|
caps = gst_caps_ref (osxbuf->core_audio->cached_caps);
|
||||||
|
} else if (buf->open) {
|
||||||
|
GstCaps *template_caps;
|
||||||
|
|
||||||
|
/* Get template caps */
|
||||||
|
template_caps =
|
||||||
|
gst_pad_get_pad_template_caps (GST_AUDIO_BASE_SRC_PAD (osxsrc));
|
||||||
|
|
||||||
/* Device is open, let's probe its caps */
|
/* Device is open, let's probe its caps */
|
||||||
gst_osx_audio_src_probe_caps (osxsrc);
|
caps = gst_core_audio_probe_caps (osxbuf->core_audio, template_caps);
|
||||||
|
gst_caps_replace (&osxbuf->core_audio->cached_caps, caps);
|
||||||
|
|
||||||
|
gst_caps_unref (template_caps);
|
||||||
|
} else {
|
||||||
|
GST_DEBUG_OBJECT (src, "ring buffer not open, using template caps");
|
||||||
|
caps = GST_BASE_SRC_CLASS (parent_class)->get_caps (src, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
GST_OBJECT_UNLOCK (buf);
|
GST_OBJECT_UNLOCK (buf);
|
||||||
|
|
||||||
gst_object_unref (buf);
|
gst_object_unref (buf);
|
||||||
}
|
|
||||||
|
|
||||||
if (osxsrc->cached_caps)
|
if (!caps)
|
||||||
ret = gst_caps_ref (osxsrc->cached_caps);
|
return NULL;
|
||||||
else
|
|
||||||
ret = gst_pad_get_pad_template_caps (GST_AUDIO_BASE_SRC_PAD (osxsrc));
|
|
||||||
|
|
||||||
if (filter) {
|
if (!filter)
|
||||||
GstCaps *tmp;
|
return caps;
|
||||||
tmp = gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST);
|
|
||||||
gst_caps_unref (ret);
|
|
||||||
ret = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
/* Take care of filtered caps */
|
||||||
|
filtered_caps =
|
||||||
|
gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
|
||||||
|
gst_caps_unref (caps);
|
||||||
|
return filtered_caps;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstAudioRingBuffer *
|
static GstAudioRingBuffer *
|
||||||
|
|
|
@ -68,8 +68,6 @@ struct _GstOsxAudioSrc
|
||||||
GstAudioBaseSrc src;
|
GstAudioBaseSrc src;
|
||||||
|
|
||||||
AudioDeviceID device_id;
|
AudioDeviceID device_id;
|
||||||
|
|
||||||
GstCaps *cached_caps;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstOsxAudioSrcClass
|
struct _GstOsxAudioSrcClass
|
||||||
|
|
|
@ -48,6 +48,7 @@ gst_core_audio_init (GstCoreAudio * core_audio)
|
||||||
core_audio->device_id = kAudioDeviceUnknown;
|
core_audio->device_id = kAudioDeviceUnknown;
|
||||||
core_audio->is_src = FALSE;
|
core_audio->is_src = FALSE;
|
||||||
core_audio->audiounit = NULL;
|
core_audio->audiounit = NULL;
|
||||||
|
core_audio->cached_caps = NULL;
|
||||||
#ifndef HAVE_IOS
|
#ifndef HAVE_IOS
|
||||||
core_audio->hog_pid = -1;
|
core_audio->hog_pid = -1;
|
||||||
core_audio->disabled_mixing = FALSE;
|
core_audio->disabled_mixing = FALSE;
|
||||||
|
@ -65,6 +66,7 @@ gst_core_audio_new (GstObject * osxbuf)
|
||||||
|
|
||||||
core_audio = g_object_new (GST_TYPE_CORE_AUDIO, NULL);
|
core_audio = g_object_new (GST_TYPE_CORE_AUDIO, NULL);
|
||||||
core_audio->osxbuf = osxbuf;
|
core_audio->osxbuf = osxbuf;
|
||||||
|
core_audio->cached_caps = NULL;
|
||||||
return core_audio;
|
return core_audio;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +83,9 @@ gst_core_audio_close (GstCoreAudio * core_audio)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* core_audio->osxbuf is already locked at this point */
|
||||||
|
gst_caps_replace (&core_audio->cached_caps, NULL);
|
||||||
|
|
||||||
AudioComponentInstanceDispose (core_audio->audiounit);
|
AudioComponentInstanceDispose (core_audio->audiounit);
|
||||||
core_audio->audiounit = NULL;
|
core_audio->audiounit = NULL;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
@ -191,79 +196,143 @@ gst_core_audio_audio_device_is_spdif_avail (AudioDeviceID device_id)
|
||||||
return gst_core_audio_audio_device_is_spdif_avail_impl (device_id);
|
return gst_core_audio_audio_device_is_spdif_avail_impl (device_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Does the channel have at least one positioned channel?
|
||||||
|
* (GStreamer is more strict than Core Audio, in that it requires either
|
||||||
|
* all channels to be positioned, or all unpositioned.) */
|
||||||
|
static gboolean
|
||||||
|
_is_core_audio_layout_positioned (AudioChannelLayout * layout)
|
||||||
|
{
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
g_assert (layout->mChannelLayoutTag ==
|
||||||
|
kAudioChannelLayoutTag_UseChannelDescriptions);
|
||||||
|
|
||||||
|
for (i = 0; i < layout->mNumberChannelDescriptions; ++i) {
|
||||||
|
GstAudioChannelPosition p =
|
||||||
|
gst_core_audio_channel_label_to_gst
|
||||||
|
(layout->mChannelDescriptions[i].mChannelLabel, i, FALSE);
|
||||||
|
|
||||||
|
if (p >= 0) /* not special positition */
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_core_audio_parse_channel_descriptions (AudioChannelLayout * layout,
|
||||||
|
guint * channels, guint64 * channel_mask, GstAudioChannelPosition * pos)
|
||||||
|
{
|
||||||
|
gboolean positioned;
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
g_assert (layout->mChannelLayoutTag ==
|
||||||
|
kAudioChannelLayoutTag_UseChannelDescriptions);
|
||||||
|
|
||||||
|
positioned = _is_core_audio_layout_positioned (layout);
|
||||||
|
*channel_mask = 0;
|
||||||
|
|
||||||
|
/* Go over all labels, either taking only positioned or only
|
||||||
|
* unpositioned channels, up to GST_OSX_AUDIO_MAX_CHANNEL channels.
|
||||||
|
*
|
||||||
|
* The resulting 'pos' array will contain either:
|
||||||
|
* - only regular (>= 0) positions
|
||||||
|
* - only GST_AUDIO_CHANNEL_POSITION_NONE positions
|
||||||
|
* in a compact form, skipping over all unsupported positions.
|
||||||
|
*/
|
||||||
|
*channels = 0;
|
||||||
|
for (i = 0; i < layout->mNumberChannelDescriptions; ++i) {
|
||||||
|
GstAudioChannelPosition p =
|
||||||
|
gst_core_audio_channel_label_to_gst
|
||||||
|
(layout->mChannelDescriptions[i].mChannelLabel, i, TRUE);
|
||||||
|
|
||||||
|
/* In positioned layouts, skip all unpositioned channels.
|
||||||
|
* In unpositioned layouts, skip all invalid channels. */
|
||||||
|
if ((positioned && p >= 0) ||
|
||||||
|
(!positioned && p == GST_AUDIO_CHANNEL_POSITION_NONE)) {
|
||||||
|
|
||||||
|
if (pos)
|
||||||
|
pos[*channels] = p;
|
||||||
|
*channel_mask |= G_GUINT64_CONSTANT (1) << p;
|
||||||
|
++(*channels);
|
||||||
|
|
||||||
|
if (*channels == GST_OSX_AUDIO_MAX_CHANNEL)
|
||||||
|
break; /* not to overflow */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
gst_core_audio_parse_channel_layout (AudioChannelLayout * layout,
|
gst_core_audio_parse_channel_layout (AudioChannelLayout * layout,
|
||||||
gint channels, guint64 * channel_mask, GstAudioChannelPosition * pos)
|
guint * channels, guint64 * channel_mask, GstAudioChannelPosition * pos)
|
||||||
{
|
{
|
||||||
gint i;
|
g_assert (channels != NULL);
|
||||||
gboolean ret = TRUE;
|
g_assert (channel_mask != NULL);
|
||||||
|
g_assert (layout != NULL);
|
||||||
|
|
||||||
g_return_val_if_fail (channels <= GST_OSX_AUDIO_MAX_CHANNEL, FALSE);
|
if (layout->mChannelLayoutTag !=
|
||||||
|
kAudioChannelLayoutTag_UseChannelDescriptions) {
|
||||||
|
GST_ERROR
|
||||||
|
("Only kAudioChannelLayoutTag_UseChannelDescriptions is supported.");
|
||||||
|
*channels = 0;
|
||||||
|
*channel_mask = 0;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
switch (channels) {
|
switch (layout->mNumberChannelDescriptions) {
|
||||||
case 0:
|
case 0:
|
||||||
|
if (pos)
|
||||||
pos[0] = GST_AUDIO_CHANNEL_POSITION_NONE;
|
pos[0] = GST_AUDIO_CHANNEL_POSITION_NONE;
|
||||||
break;
|
*channels = 0;
|
||||||
|
*channel_mask = 0;
|
||||||
|
return TRUE;
|
||||||
case 1:
|
case 1:
|
||||||
|
if (pos)
|
||||||
pos[0] = GST_AUDIO_CHANNEL_POSITION_MONO;
|
pos[0] = GST_AUDIO_CHANNEL_POSITION_MONO;
|
||||||
break;
|
*channels = 1;
|
||||||
|
*channel_mask = 0;
|
||||||
|
return TRUE;
|
||||||
case 2:
|
case 2:
|
||||||
|
if (pos) {
|
||||||
pos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
|
pos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
|
||||||
pos[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
|
pos[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
|
||||||
*channel_mask |= GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_LEFT);
|
|
||||||
*channel_mask |= GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_RIGHT);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
for (i = 0; i < channels; i++) {
|
|
||||||
switch (layout->mChannelDescriptions[i].mChannelLabel) {
|
|
||||||
case kAudioChannelLabel_Left:
|
|
||||||
pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
|
|
||||||
break;
|
|
||||||
case kAudioChannelLabel_Right:
|
|
||||||
pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
|
|
||||||
break;
|
|
||||||
case kAudioChannelLabel_Center:
|
|
||||||
pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER;
|
|
||||||
break;
|
|
||||||
case kAudioChannelLabel_LFEScreen:
|
|
||||||
pos[i] = GST_AUDIO_CHANNEL_POSITION_LFE1;
|
|
||||||
break;
|
|
||||||
case kAudioChannelLabel_LeftSurround:
|
|
||||||
pos[i] = GST_AUDIO_CHANNEL_POSITION_REAR_LEFT;
|
|
||||||
break;
|
|
||||||
case kAudioChannelLabel_RightSurround:
|
|
||||||
pos[i] = GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT;
|
|
||||||
break;
|
|
||||||
case kAudioChannelLabel_RearSurroundLeft:
|
|
||||||
pos[i] = GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT;
|
|
||||||
break;
|
|
||||||
case kAudioChannelLabel_RearSurroundRight:
|
|
||||||
pos[i] = GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT;
|
|
||||||
break;
|
|
||||||
case kAudioChannelLabel_CenterSurround:
|
|
||||||
pos[i] = GST_AUDIO_CHANNEL_POSITION_REAR_CENTER;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
GST_WARNING ("unrecognized channel: %d",
|
|
||||||
(int) layout->mChannelDescriptions[i].mChannelLabel);
|
|
||||||
*channel_mask = 0;
|
|
||||||
ret = FALSE;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
*channels = 2;
|
||||||
|
*channel_mask =
|
||||||
|
GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_LEFT) |
|
||||||
|
GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_RIGHT);
|
||||||
|
return TRUE;
|
||||||
|
default:
|
||||||
|
_core_audio_parse_channel_descriptions (layout, channels, channel_mask,
|
||||||
|
pos);
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
/* Converts an AudioStreamBasicDescription to preferred caps.
|
||||||
}
|
*
|
||||||
|
* These caps will indicate the AU element's canonical format, which won't
|
||||||
|
* make Core Audio resample nor convert.
|
||||||
|
*
|
||||||
|
* NOTE ON MULTI-CHANNEL AUDIO:
|
||||||
|
*
|
||||||
|
* If layout is not NULL, resulting caps will only include the subset
|
||||||
|
* of channels supported by GStreamer. If the Core Audio layout contained
|
||||||
|
* ANY positioned channels, then ONLY positioned channels will be included
|
||||||
|
* in the resulting caps. Otherwise, resulting caps will be unpositioned,
|
||||||
|
* and include only unpositioned channels.
|
||||||
|
* (Channels with unsupported AudioChannelLabel will be skipped either way.)
|
||||||
|
*
|
||||||
|
* Naturally, the number of channels indicated by 'channels' can be lower
|
||||||
|
* than the AU element's total number of channels.
|
||||||
|
*/
|
||||||
GstCaps *
|
GstCaps *
|
||||||
gst_core_audio_asbd_to_caps (AudioStreamBasicDescription * asbd,
|
gst_core_audio_asbd_to_caps (AudioStreamBasicDescription * asbd,
|
||||||
AudioChannelLayout * layout)
|
AudioChannelLayout * layout)
|
||||||
{
|
{
|
||||||
GstAudioInfo info;
|
GstAudioInfo info;
|
||||||
GstAudioFormat format = GST_AUDIO_FORMAT_UNKNOWN;
|
GstAudioFormat format = GST_AUDIO_FORMAT_UNKNOWN;
|
||||||
GstAudioChannelPosition pos[64] = { GST_AUDIO_CHANNEL_POSITION_INVALID, };
|
guint rate, channels, bps, endianness;
|
||||||
gint rate, channels, bps, endianness;
|
|
||||||
guint64 channel_mask;
|
guint64 channel_mask;
|
||||||
gboolean sign, interleaved;
|
gboolean sign, interleaved;
|
||||||
|
|
||||||
|
@ -283,13 +352,9 @@ gst_core_audio_asbd_to_caps (AudioStreamBasicDescription * asbd,
|
||||||
}
|
}
|
||||||
|
|
||||||
rate = asbd->mSampleRate;
|
rate = asbd->mSampleRate;
|
||||||
if (rate == kAudioStreamAnyRate)
|
if (rate == kAudioStreamAnyRate) {
|
||||||
rate = GST_AUDIO_DEF_RATE;
|
GST_WARNING ("No sample rate");
|
||||||
|
goto error;
|
||||||
channels = asbd->mChannelsPerFrame;
|
|
||||||
if (channels == 0) {
|
|
||||||
/* The documentation says this should not happen! */
|
|
||||||
channels = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bps = asbd->mBitsPerChannel;
|
bps = asbd->mBitsPerChannel;
|
||||||
|
@ -321,16 +386,206 @@ gst_core_audio_asbd_to_caps (AudioStreamBasicDescription * asbd,
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!gst_core_audio_parse_channel_layout (layout, channels, &channel_mask,
|
if (layout) {
|
||||||
|
GstAudioChannelPosition pos[GST_OSX_AUDIO_MAX_CHANNEL];
|
||||||
|
|
||||||
|
if (!gst_core_audio_parse_channel_layout (layout, &channels, &channel_mask,
|
||||||
pos)) {
|
pos)) {
|
||||||
GST_WARNING ("Failed to parse channel layout");
|
GST_WARNING ("Failed to parse channel layout");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The AU can have arbitrary channel order, but we're using GstAudioInfo
|
||||||
|
* which supports only the GStreamer channel order.
|
||||||
|
* Also, we're eventually producing caps, which only have channel-mask
|
||||||
|
* (whose implied order is the GStreamer channel order). */
|
||||||
|
gst_audio_channel_positions_to_valid_order (pos, channels);
|
||||||
|
|
||||||
gst_audio_info_set_format (&info, format, rate, channels, pos);
|
gst_audio_info_set_format (&info, format, rate, channels, pos);
|
||||||
|
} else {
|
||||||
|
channels = MIN (asbd->mChannelsPerFrame, GST_OSX_AUDIO_MAX_CHANNEL);
|
||||||
|
gst_audio_info_set_format (&info, format, rate, channels, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
return gst_audio_info_to_caps (&info);
|
return gst_audio_info_to_caps (&info);
|
||||||
|
|
||||||
error:
|
error:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
_core_audio_get_property (GstCoreAudio * core_audio, gboolean outer,
|
||||||
|
AudioUnitPropertyID inID, void *inData, UInt32 * inDataSize)
|
||||||
|
{
|
||||||
|
OSStatus status;
|
||||||
|
AudioUnitScope scope;
|
||||||
|
AudioUnitElement element;
|
||||||
|
|
||||||
|
scope = outer ?
|
||||||
|
CORE_AUDIO_OUTER_SCOPE (core_audio) : CORE_AUDIO_INNER_SCOPE (core_audio);
|
||||||
|
element = CORE_AUDIO_ELEMENT (core_audio);
|
||||||
|
|
||||||
|
status =
|
||||||
|
AudioUnitGetProperty (core_audio->audiounit, inID, scope, element, inData,
|
||||||
|
inDataSize);
|
||||||
|
|
||||||
|
return status == noErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
_core_audio_get_stream_format (GstCoreAudio * core_audio,
|
||||||
|
AudioStreamBasicDescription * asbd, gboolean outer)
|
||||||
|
{
|
||||||
|
UInt32 size;
|
||||||
|
|
||||||
|
size = sizeof (AudioStreamBasicDescription);
|
||||||
|
return _core_audio_get_property (core_audio, outer,
|
||||||
|
kAudioUnitProperty_StreamFormat, asbd, &size);
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioChannelLayout *
|
||||||
|
gst_core_audio_get_channel_layout (GstCoreAudio * core_audio, gboolean outer)
|
||||||
|
{
|
||||||
|
UInt32 size;
|
||||||
|
AudioChannelLayout *layout;
|
||||||
|
|
||||||
|
if (core_audio->is_src) {
|
||||||
|
GST_WARNING_OBJECT (core_audio,
|
||||||
|
"gst_core_audio_get_channel_layout not supported on source.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_core_audio_get_property (core_audio, outer,
|
||||||
|
kAudioUnitProperty_AudioChannelLayout, NULL, &size)) {
|
||||||
|
GST_WARNING_OBJECT (core_audio, "unable to get channel layout");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
layout = g_malloc (size);
|
||||||
|
if (!_core_audio_get_property (core_audio, outer,
|
||||||
|
kAudioUnitProperty_AudioChannelLayout, layout, &size)) {
|
||||||
|
GST_WARNING_OBJECT (core_audio, "unable to get channel layout");
|
||||||
|
g_free (layout);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
GstCaps *
|
||||||
|
gst_core_audio_probe_caps (GstCoreAudio * core_audio, GstCaps * in_caps)
|
||||||
|
{
|
||||||
|
guint i, channels;
|
||||||
|
gboolean spdif_allowed;
|
||||||
|
AudioChannelLayout *layout;
|
||||||
|
AudioStreamBasicDescription outer_asbd;
|
||||||
|
gboolean got_outer_asbd;
|
||||||
|
GstCaps *caps = NULL;
|
||||||
|
guint64 channel_mask;
|
||||||
|
|
||||||
|
/* Get the ASBD of the outer scope (i.e. input scope of Input,
|
||||||
|
* output scope of Output).
|
||||||
|
* This ASBD indicates the hardware format. */
|
||||||
|
got_outer_asbd =
|
||||||
|
_core_audio_get_stream_format (core_audio, &outer_asbd, TRUE);
|
||||||
|
|
||||||
|
/* Collect info about the HW capabilites and preferences */
|
||||||
|
spdif_allowed =
|
||||||
|
gst_core_audio_audio_device_is_spdif_avail (core_audio->device_id);
|
||||||
|
layout = gst_core_audio_get_channel_layout (core_audio, TRUE);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (core_audio, "Selected device ID: %u SPDIF allowed: %d",
|
||||||
|
(unsigned) core_audio->device_id, spdif_allowed);
|
||||||
|
|
||||||
|
if (layout) {
|
||||||
|
if (!gst_core_audio_parse_channel_layout (layout, &channels, &channel_mask,
|
||||||
|
NULL)) {
|
||||||
|
GST_WARNING_OBJECT (core_audio, "Failed to parse channel layout");
|
||||||
|
channel_mask = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If available, start with the preferred caps. */
|
||||||
|
if (got_outer_asbd)
|
||||||
|
caps = gst_core_audio_asbd_to_caps (&outer_asbd, layout);
|
||||||
|
|
||||||
|
g_free (layout);
|
||||||
|
} else if (got_outer_asbd) {
|
||||||
|
channels = outer_asbd.mChannelsPerFrame;
|
||||||
|
channel_mask = 0;
|
||||||
|
/* If available, start with the preferred caps */
|
||||||
|
caps = gst_core_audio_asbd_to_caps (&outer_asbd, NULL);
|
||||||
|
} else {
|
||||||
|
GST_ERROR_OBJECT (core_audio,
|
||||||
|
"Unable to get any information about hardware");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Append the allowed subset based on the template caps */
|
||||||
|
if (!caps)
|
||||||
|
caps = gst_caps_new_empty ();
|
||||||
|
for (i = 0; i < gst_caps_get_size (in_caps); i++) {
|
||||||
|
GstStructure *in_s;
|
||||||
|
|
||||||
|
in_s = gst_caps_get_structure (in_caps, i);
|
||||||
|
|
||||||
|
if (gst_structure_has_name (in_s, "audio/x-ac3") ||
|
||||||
|
gst_structure_has_name (in_s, "audio/x-dts")) {
|
||||||
|
if (spdif_allowed) {
|
||||||
|
gst_caps_append_structure (caps, gst_structure_copy (in_s));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
GstStructure *out_s;
|
||||||
|
|
||||||
|
out_s = gst_structure_copy (in_s);
|
||||||
|
gst_structure_set (out_s, "channels", G_TYPE_INT, channels, NULL);
|
||||||
|
if (channel_mask != 0) {
|
||||||
|
/* positioned layout */
|
||||||
|
gst_structure_set (out_s,
|
||||||
|
"channel-mask", GST_TYPE_BITMASK, channel_mask, NULL);
|
||||||
|
} else {
|
||||||
|
/* unpositioned layout */
|
||||||
|
gst_structure_remove_field (out_s, "channel-mask");
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_caps_append_structure (caps, out_s);
|
||||||
|
|
||||||
|
/* Special cases for upmixing and downmixing.
|
||||||
|
* Other than that, the AUs don't upmix or downmix multi-channel audio,
|
||||||
|
* e.g. if you push 5.1-surround audio to a stereo configuration,
|
||||||
|
* the left and right channels will be played accordingly,
|
||||||
|
* and the rest will be dropped. */
|
||||||
|
|
||||||
|
if (channels >= 2 &&
|
||||||
|
(channel_mask & GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_LEFT)) &&
|
||||||
|
(channel_mask & GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_RIGHT))) {
|
||||||
|
|
||||||
|
/* If have stereo channels, then also offer mono since CoreAudio
|
||||||
|
* upmixes it */
|
||||||
|
|
||||||
|
out_s = gst_structure_copy (out_s);
|
||||||
|
|
||||||
|
gst_structure_set (out_s, "channels", G_TYPE_INT, 1, NULL);
|
||||||
|
/* Mono has no channel-mask */
|
||||||
|
gst_structure_remove_field (out_s, "channel-mask");
|
||||||
|
|
||||||
|
gst_caps_append_structure (caps, out_s);
|
||||||
|
|
||||||
|
} else if (channels == 1 && channel_mask == 0) {
|
||||||
|
|
||||||
|
/* If mono, then also offer stereo since CoreAudio downmixes to it */
|
||||||
|
|
||||||
|
out_s = gst_structure_copy (out_s);
|
||||||
|
|
||||||
|
gst_structure_set (out_s,
|
||||||
|
"channels", G_TYPE_INT, 1,
|
||||||
|
"channel-mask", GST_TYPE_BITMASK,
|
||||||
|
GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_LEFT) |
|
||||||
|
GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_RIGHT), NULL);
|
||||||
|
|
||||||
|
gst_caps_append_structure (caps, out_s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return caps;
|
||||||
|
}
|
||||||
|
|
|
@ -63,6 +63,7 @@ G_BEGIN_DECLS
|
||||||
#define GST_IS_CORE_AUDIO_CLASS(klass) \
|
#define GST_IS_CORE_AUDIO_CLASS(klass) \
|
||||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CORE_AUDIO))
|
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CORE_AUDIO))
|
||||||
|
|
||||||
|
/* TODO: Consider raising to 64 */
|
||||||
#define GST_OSX_AUDIO_MAX_CHANNEL (9)
|
#define GST_OSX_AUDIO_MAX_CHANNEL (9)
|
||||||
|
|
||||||
#define CORE_AUDIO_FORMAT_IS_SPDIF(f) ((f).mFormat.mFormatID == 'IAC3' || (f).mFormat.mFormatID == 'iac3' || (f).mFormat.mFormatID == kAudioFormat60958AC3 || (f).mFormat.mFormatID == kAudioFormatAC3)
|
#define CORE_AUDIO_FORMAT_IS_SPDIF(f) ((f).mFormat.mFormatID == 'IAC3' || (f).mFormat.mFormatID == 'iac3' || (f).mFormat.mFormatID == kAudioFormat60958AC3 || (f).mFormat.mFormatID == kAudioFormatAC3)
|
||||||
|
@ -70,6 +71,10 @@ G_BEGIN_DECLS
|
||||||
#define CORE_AUDIO_FORMAT "FormatID: %" GST_FOURCC_FORMAT " rate: %f flags: 0x%x BytesPerPacket: %u FramesPerPacket: %u BytesPerFrame: %u ChannelsPerFrame: %u BitsPerChannel: %u"
|
#define CORE_AUDIO_FORMAT "FormatID: %" GST_FOURCC_FORMAT " rate: %f flags: 0x%x BytesPerPacket: %u FramesPerPacket: %u BytesPerFrame: %u ChannelsPerFrame: %u BitsPerChannel: %u"
|
||||||
#define CORE_AUDIO_FORMAT_ARGS(f) GST_FOURCC_ARGS((unsigned int)(f).mFormatID),(f).mSampleRate,(unsigned int)(f).mFormatFlags,(unsigned int)(f).mBytesPerPacket,(unsigned int)(f).mFramesPerPacket,(unsigned int)(f).mBytesPerFrame,(unsigned int)(f).mChannelsPerFrame,(unsigned int)(f).mBitsPerChannel
|
#define CORE_AUDIO_FORMAT_ARGS(f) GST_FOURCC_ARGS((unsigned int)(f).mFormatID),(f).mSampleRate,(unsigned int)(f).mFormatFlags,(unsigned int)(f).mBytesPerPacket,(unsigned int)(f).mFramesPerPacket,(unsigned int)(f).mBytesPerFrame,(unsigned int)(f).mChannelsPerFrame,(unsigned int)(f).mBitsPerChannel
|
||||||
|
|
||||||
|
#define CORE_AUDIO_INNER_SCOPE(core_audio) ((core_audio)->is_src ? kAudioUnitScope_Output : kAudioUnitScope_Input)
|
||||||
|
#define CORE_AUDIO_OUTER_SCOPE(core_audio) ((core_audio)->is_src ? kAudioUnitScope_Input : kAudioUnitScope_Output)
|
||||||
|
#define CORE_AUDIO_ELEMENT(core_audio) ((core_audio)->is_src ? 1 : 0)
|
||||||
|
|
||||||
typedef struct _GstCoreAudio GstCoreAudio;
|
typedef struct _GstCoreAudio GstCoreAudio;
|
||||||
typedef struct _GstCoreAudioClass GstCoreAudioClass;
|
typedef struct _GstCoreAudioClass GstCoreAudioClass;
|
||||||
|
|
||||||
|
@ -83,6 +88,7 @@ struct _GstCoreAudio
|
||||||
gboolean is_src;
|
gboolean is_src;
|
||||||
gboolean is_passthrough;
|
gboolean is_passthrough;
|
||||||
AudioDeviceID device_id;
|
AudioDeviceID device_id;
|
||||||
|
GstCaps *cached_caps;
|
||||||
gint stream_idx;
|
gint stream_idx;
|
||||||
gboolean io_proc_active;
|
gboolean io_proc_active;
|
||||||
gboolean io_proc_needs_deactivation;
|
gboolean io_proc_needs_deactivation;
|
||||||
|
@ -144,10 +150,14 @@ gboolean gst_core_audio_audio_device_is_spdif_avail (AudioDeviceID devi
|
||||||
|
|
||||||
gboolean gst_core_audio_select_device (GstCoreAudio * core_audio);
|
gboolean gst_core_audio_select_device (GstCoreAudio * core_audio);
|
||||||
|
|
||||||
AudioChannelLayout * gst_core_audio_audio_device_get_channel_layout (AudioDeviceID device_id, gboolean output);
|
GstCaps *
|
||||||
|
gst_core_audio_probe_caps (GstCoreAudio * core_audio, GstCaps * in_caps);
|
||||||
|
|
||||||
|
AudioChannelLayout *
|
||||||
|
gst_core_audio_get_channel_layout (GstCoreAudio * core_audio, gboolean outer);
|
||||||
|
|
||||||
gboolean gst_core_audio_parse_channel_layout (AudioChannelLayout * layout,
|
gboolean gst_core_audio_parse_channel_layout (AudioChannelLayout * layout,
|
||||||
gint channels, guint64 * channel_mask, GstAudioChannelPosition * pos);
|
guint * channels, guint64 * channel_mask, GstAudioChannelPosition * pos);
|
||||||
GstCaps * gst_core_audio_asbd_to_caps (AudioStreamBasicDescription * asbd,
|
GstCaps * gst_core_audio_asbd_to_caps (AudioStreamBasicDescription * asbd,
|
||||||
AudioChannelLayout * layout);
|
AudioChannelLayout * layout);
|
||||||
|
|
||||||
|
|
|
@ -157,6 +157,7 @@ buffer_list_alloc (UInt32 channels, UInt32 size, gboolean interleaved)
|
||||||
UInt32 num_buffers, n;
|
UInt32 num_buffers, n;
|
||||||
|
|
||||||
num_buffers = interleaved ? 1 : channels;
|
num_buffers = interleaved ? 1 : channels;
|
||||||
|
/* AudioBufferList member mBuffers is variable-length array */
|
||||||
list_size = G_STRUCT_OFFSET (AudioBufferList, mBuffers[num_buffers]);
|
list_size = G_STRUCT_OFFSET (AudioBufferList, mBuffers[num_buffers]);
|
||||||
list = (AudioBufferList *) g_malloc (list_size);
|
list = (AudioBufferList *) g_malloc (list_size);
|
||||||
|
|
||||||
|
@ -213,39 +214,66 @@ audiounit_error:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
_core_audio_set_property (GstCoreAudio * core_audio, AudioUnitPropertyID inID,
|
||||||
|
void *inData, UInt32 inDataSize)
|
||||||
|
{
|
||||||
|
OSStatus status;
|
||||||
|
AudioUnitScope scope;
|
||||||
|
AudioUnitElement element;
|
||||||
|
|
||||||
|
scope = CORE_AUDIO_INNER_SCOPE (core_audio);
|
||||||
|
element = CORE_AUDIO_ELEMENT (core_audio);
|
||||||
|
|
||||||
|
status =
|
||||||
|
AudioUnitSetProperty (core_audio->audiounit, inID, scope, element, inData,
|
||||||
|
inDataSize);
|
||||||
|
|
||||||
|
if (status != noErr) {
|
||||||
|
GST_WARNING_OBJECT (core_audio->osxbuf,
|
||||||
|
"Failed to set Audio Unit property: %d", (int) status);
|
||||||
|
return FALSE;;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
gst_core_audio_set_channel_layout (GstCoreAudio * core_audio,
|
gst_core_audio_set_channel_layout (GstCoreAudio * core_audio,
|
||||||
gint channels, GstCaps * caps)
|
gint channels, GstCaps * caps)
|
||||||
{
|
{
|
||||||
/* Configure the output stream and allocate ringbuffer memory */
|
|
||||||
AudioChannelLayout *layout = NULL;
|
AudioChannelLayout *layout = NULL;
|
||||||
OSStatus status;
|
gboolean ret;
|
||||||
int layoutSize, element, i;
|
gsize layoutSize;
|
||||||
AudioUnitScope scope;
|
gint i;
|
||||||
GstStructure *structure;
|
GstStructure *structure;
|
||||||
GstAudioChannelPosition *positions = NULL;
|
GstAudioChannelPosition positions[GST_OSX_AUDIO_MAX_CHANNEL];
|
||||||
guint64 channel_mask;
|
guint64 channel_mask;
|
||||||
|
|
||||||
/* Describe channels */
|
g_return_val_if_fail (channels <= GST_OSX_AUDIO_MAX_CHANNEL, FALSE);
|
||||||
layoutSize = sizeof (AudioChannelLayout) +
|
|
||||||
channels * sizeof (AudioChannelDescription);
|
/* Determine the channel positions */
|
||||||
|
structure = gst_caps_get_structure (caps, 0);
|
||||||
|
channel_mask = 0;
|
||||||
|
gst_structure_get (structure, "channel-mask", GST_TYPE_BITMASK, &channel_mask,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (channel_mask != 0)
|
||||||
|
gst_audio_channel_positions_from_mask (channels, channel_mask, positions);
|
||||||
|
|
||||||
|
/* AudioChannelLayout member mChannelDescriptions is variable-length array */
|
||||||
|
layoutSize =
|
||||||
|
G_STRUCT_OFFSET (AudioChannelLayout, mChannelDescriptions[channels]);
|
||||||
layout = g_malloc (layoutSize);
|
layout = g_malloc (layoutSize);
|
||||||
|
|
||||||
structure = gst_caps_get_structure (caps, 0);
|
/* Fill out the AudioChannelLayout */
|
||||||
if (gst_structure_get (structure, "channel-mask", GST_TYPE_BITMASK,
|
|
||||||
&channel_mask, NULL)) {
|
|
||||||
positions = g_new (GstAudioChannelPosition, channels);
|
|
||||||
gst_audio_channel_positions_from_mask (channels, channel_mask, positions);
|
|
||||||
}
|
|
||||||
|
|
||||||
layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
|
layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
|
||||||
layout->mChannelBitmap = 0; /* Not used */
|
layout->mChannelBitmap = 0; /* Not used */
|
||||||
layout->mNumberChannelDescriptions = channels;
|
layout->mNumberChannelDescriptions = channels;
|
||||||
for (i = 0; i < channels; i++) {
|
for (i = 0; i < channels; i++) {
|
||||||
if (positions) {
|
if (channel_mask != 0) {
|
||||||
layout->mChannelDescriptions[i].mChannelLabel =
|
layout->mChannelDescriptions[i].mChannelLabel =
|
||||||
gst_audio_channel_position_to_coreaudio_channel_label (positions[i],
|
gst_audio_channel_position_to_core_audio (positions[i], i);
|
||||||
i);
|
|
||||||
} else {
|
} else {
|
||||||
/* Discrete channel numbers are ORed into this */
|
/* Discrete channel numbers are ORed into this */
|
||||||
layout->mChannelDescriptions[i].mChannelLabel =
|
layout->mChannelDescriptions[i].mChannelLabel =
|
||||||
|
@ -253,61 +281,30 @@ gst_core_audio_set_channel_layout (GstCoreAudio * core_audio,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Others unused */
|
/* Others unused */
|
||||||
layout->mChannelDescriptions[i].mChannelFlags = 0;
|
layout->mChannelDescriptions[i].mChannelFlags = kAudioChannelFlags_AllOff;
|
||||||
layout->mChannelDescriptions[i].mCoordinates[0] = 0.f;
|
layout->mChannelDescriptions[i].mCoordinates[0] = 0.f;
|
||||||
layout->mChannelDescriptions[i].mCoordinates[1] = 0.f;
|
layout->mChannelDescriptions[i].mCoordinates[1] = 0.f;
|
||||||
layout->mChannelDescriptions[i].mCoordinates[2] = 0.f;
|
layout->mChannelDescriptions[i].mCoordinates[2] = 0.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (positions) {
|
/* Sets GStreamer-ordered channel layout on the inner scope.
|
||||||
g_free (positions);
|
* Reordering between the inner scope and outer scope is handled
|
||||||
positions = NULL;
|
* by the Audio Unit itself. */
|
||||||
}
|
ret = _core_audio_set_property (core_audio,
|
||||||
|
kAudioUnitProperty_AudioChannelLayout, layout, layoutSize);
|
||||||
scope = core_audio->is_src ? kAudioUnitScope_Output : kAudioUnitScope_Input;
|
|
||||||
element = core_audio->is_src ? 1 : 0;
|
|
||||||
|
|
||||||
if (layoutSize) {
|
|
||||||
status = AudioUnitSetProperty (core_audio->audiounit,
|
|
||||||
kAudioUnitProperty_AudioChannelLayout,
|
|
||||||
scope, element, layout, layoutSize);
|
|
||||||
if (status) {
|
|
||||||
GST_WARNING_OBJECT (core_audio->osxbuf,
|
|
||||||
"Failed to set output channel layout: %d", (int) status);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free (layout);
|
g_free (layout);
|
||||||
return TRUE;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
gst_core_audio_set_format (GstCoreAudio * core_audio,
|
gst_core_audio_set_format (GstCoreAudio * core_audio,
|
||||||
AudioStreamBasicDescription format)
|
AudioStreamBasicDescription format)
|
||||||
{
|
{
|
||||||
/* Configure the output stream and allocate ringbuffer memory */
|
|
||||||
OSStatus status;
|
|
||||||
UInt32 propertySize;
|
|
||||||
int element;
|
|
||||||
AudioUnitScope scope;
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (core_audio->osxbuf, "Setting format for AudioUnit");
|
GST_DEBUG_OBJECT (core_audio->osxbuf, "Setting format for AudioUnit");
|
||||||
|
|
||||||
scope = core_audio->is_src ? kAudioUnitScope_Output : kAudioUnitScope_Input;
|
return _core_audio_set_property (core_audio, kAudioUnitProperty_StreamFormat,
|
||||||
element = core_audio->is_src ? 1 : 0;
|
&format, sizeof (AudioStreamBasicDescription));
|
||||||
|
|
||||||
propertySize = sizeof (AudioStreamBasicDescription);
|
|
||||||
status = AudioUnitSetProperty (core_audio->audiounit,
|
|
||||||
kAudioUnitProperty_StreamFormat, scope, element, &format, propertySize);
|
|
||||||
|
|
||||||
if (status) {
|
|
||||||
GST_WARNING_OBJECT (core_audio->osxbuf,
|
|
||||||
"Failed to set audio description: %d", (int) status);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
|
@ -375,14 +372,10 @@ gst_core_audio_open_device (GstCoreAudio * core_audio, OSType sub_type,
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioChannelLabel
|
AudioChannelLabel
|
||||||
gst_audio_channel_position_to_coreaudio_channel_label (GstAudioChannelPosition
|
gst_audio_channel_position_to_core_audio (GstAudioChannelPosition
|
||||||
position, int channel)
|
position, int channel)
|
||||||
{
|
{
|
||||||
switch (position) {
|
switch (position) {
|
||||||
case GST_AUDIO_CHANNEL_POSITION_NONE:
|
|
||||||
return kAudioChannelLabel_Discrete_0 | channel;
|
|
||||||
case GST_AUDIO_CHANNEL_POSITION_MONO:
|
|
||||||
return kAudioChannelLabel_Mono;
|
|
||||||
case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT:
|
case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT:
|
||||||
return kAudioChannelLabel_Left;
|
return kAudioChannelLabel_Left;
|
||||||
case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT:
|
case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT:
|
||||||
|
@ -397,19 +390,141 @@ gst_audio_channel_position_to_coreaudio_channel_label (GstAudioChannelPosition
|
||||||
return kAudioChannelLabel_LFEScreen;
|
return kAudioChannelLabel_LFEScreen;
|
||||||
case GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER:
|
case GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER:
|
||||||
return kAudioChannelLabel_Center;
|
return kAudioChannelLabel_Center;
|
||||||
case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
|
|
||||||
return kAudioChannelLabel_Center; // ???
|
|
||||||
case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
|
|
||||||
return kAudioChannelLabel_Center; // ???
|
|
||||||
case GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT:
|
case GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT:
|
||||||
return kAudioChannelLabel_LeftSurroundDirect;
|
return kAudioChannelLabel_LeftSurroundDirect;
|
||||||
case GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT:
|
case GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT:
|
||||||
return kAudioChannelLabel_RightSurroundDirect;
|
return kAudioChannelLabel_RightSurroundDirect;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
|
||||||
|
return kAudioChannelLabel_LeftCenter;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
|
||||||
|
return kAudioChannelLabel_RightCenter;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT:
|
||||||
|
return kAudioChannelLabel_TopBackLeft;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER:
|
||||||
|
return kAudioChannelLabel_TopBackCenter;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT:
|
||||||
|
return kAudioChannelLabel_TopBackRight;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_WIDE_LEFT:
|
||||||
|
return kAudioChannelLabel_LeftWide;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_WIDE_RIGHT:
|
||||||
|
return kAudioChannelLabel_RightWide;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_LFE2:
|
||||||
|
return kAudioChannelLabel_LFE2;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT:
|
||||||
|
return kAudioChannelLabel_VerticalHeightLeft;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT:
|
||||||
|
return kAudioChannelLabel_VerticalHeightRight;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER:
|
||||||
|
return kAudioChannelLabel_VerticalHeightCenter;
|
||||||
|
|
||||||
|
/* Special position values */
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_NONE:
|
||||||
|
return kAudioChannelLabel_Discrete_0 | channel;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_MONO:
|
||||||
|
return kAudioChannelLabel_Mono;
|
||||||
|
|
||||||
|
/* Following positions are unmapped --
|
||||||
|
* i.e. mapped to kAudioChannelLabel_Unknown: */
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_TOP_CENTER:
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_LEFT:
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_RIGHT:
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_CENTER:
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_LEFT:
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_RIGHT:
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_SURROUND_LEFT:
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_SURROUND_RIGHT:
|
||||||
default:
|
default:
|
||||||
return kAudioChannelLabel_Unknown;
|
return kAudioChannelLabel_Unknown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Performs a best-effort conversion. 'channel' is used for warnings only. */
|
||||||
|
GstAudioChannelPosition
|
||||||
|
gst_core_audio_channel_label_to_gst (AudioChannelLabel label,
|
||||||
|
int channel, gboolean warn)
|
||||||
|
{
|
||||||
|
switch (label) {
|
||||||
|
case kAudioChannelLabel_Left:
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
|
||||||
|
case kAudioChannelLabel_Right:
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
|
||||||
|
case kAudioChannelLabel_Center:
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER;
|
||||||
|
case kAudioChannelLabel_LFEScreen:
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_LFE1;
|
||||||
|
case kAudioChannelLabel_LeftSurround:
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_REAR_LEFT;
|
||||||
|
case kAudioChannelLabel_RightSurround:
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT;
|
||||||
|
case kAudioChannelLabel_LeftSurroundDirect:
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT;
|
||||||
|
case kAudioChannelLabel_RightSurroundDirect:
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT;
|
||||||
|
case kAudioChannelLabel_CenterSurround:
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_REAR_CENTER;
|
||||||
|
case kAudioChannelLabel_LeftCenter:
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
|
||||||
|
case kAudioChannelLabel_RightCenter:
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
|
||||||
|
case kAudioChannelLabel_TopBackLeft:
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT;
|
||||||
|
case kAudioChannelLabel_TopBackCenter:
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER;
|
||||||
|
case kAudioChannelLabel_TopBackRight:
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT;
|
||||||
|
case kAudioChannelLabel_LeftWide:
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_WIDE_LEFT;
|
||||||
|
case kAudioChannelLabel_RightWide:
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_WIDE_RIGHT;
|
||||||
|
case kAudioChannelLabel_LFE2:
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_LFE2;
|
||||||
|
case kAudioChannelLabel_VerticalHeightLeft:
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT;
|
||||||
|
case kAudioChannelLabel_VerticalHeightRight:
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT;
|
||||||
|
case kAudioChannelLabel_VerticalHeightCenter:
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER;
|
||||||
|
|
||||||
|
/* Special position values */
|
||||||
|
|
||||||
|
case kAudioChannelLabel_Mono:
|
||||||
|
/* GST_AUDIO_CHANNEL_POSITION_MONO is only for 1-channel layouts */
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_INVALID;
|
||||||
|
case kAudioChannelLabel_Discrete:
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_NONE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Following labels are unmapped --
|
||||||
|
i.e. mapped to GST_AUDIO_CHANNEL_POSITION_INVALID:
|
||||||
|
*/
|
||||||
|
case kAudioChannelLabel_RearSurroundLeft:
|
||||||
|
case kAudioChannelLabel_RearSurroundRight:
|
||||||
|
case kAudioChannelLabel_TopCenterSurround:
|
||||||
|
case kAudioChannelLabel_LeftTotal:
|
||||||
|
case kAudioChannelLabel_RightTotal:
|
||||||
|
case kAudioChannelLabel_HearingImpaired:
|
||||||
|
case kAudioChannelLabel_Narration:
|
||||||
|
case kAudioChannelLabel_DialogCentricMix:
|
||||||
|
case kAudioChannelLabel_CenterSurroundDirect:
|
||||||
|
case kAudioChannelLabel_Haptic:
|
||||||
|
default:
|
||||||
|
if (label >> 16 != 0) { /* kAudioChannelLabel_Discrete_N */
|
||||||
|
/* no way to store discrete channel order */
|
||||||
|
if (warn)
|
||||||
|
GST_WARNING
|
||||||
|
("Core Audio channel %u labeled kAudioChannelLabel_Discrete_%u -- discrete order will be lost",
|
||||||
|
channel, ((unsigned int) label) & 0xFFFF);
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_NONE;
|
||||||
|
} else {
|
||||||
|
if (warn)
|
||||||
|
GST_WARNING
|
||||||
|
("Core Audio channel %u has unsupported label %d and will be skipped",
|
||||||
|
channel, (int) label);
|
||||||
|
return GST_AUDIO_CHANNEL_POSITION_INVALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
gst_core_audio_dump_channel_layout (AudioChannelLayout * channel_layout)
|
gst_core_audio_dump_channel_layout (AudioChannelLayout * channel_layout)
|
||||||
{
|
{
|
||||||
|
|
|
@ -61,5 +61,6 @@ OSStatus gst_core_audio_render_notify (GstCoreAudio * core_a
|
||||||
unsigned int inNumberFrames,
|
unsigned int inNumberFrames,
|
||||||
AudioBufferList * ioData);
|
AudioBufferList * ioData);
|
||||||
|
|
||||||
AudioChannelLabel gst_audio_channel_position_to_coreaudio_channel_label (GstAudioChannelPosition position, int channel);
|
AudioChannelLabel gst_audio_channel_position_to_core_audio (GstAudioChannelPosition position, int channel);
|
||||||
|
|
||||||
|
GstAudioChannelPosition gst_core_audio_channel_label_to_gst (AudioChannelLabel label, int channel, gboolean warn);
|
||||||
|
|
|
@ -319,7 +319,8 @@ _audio_device_has_output (AudioDeviceID device_id)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioChannelLayout *
|
#ifdef GST_CORE_AUDIO_DEBUG
|
||||||
|
static AudioChannelLayout *
|
||||||
gst_core_audio_audio_device_get_channel_layout (AudioDeviceID device_id,
|
gst_core_audio_audio_device_get_channel_layout (AudioDeviceID device_id,
|
||||||
gboolean output)
|
gboolean output)
|
||||||
{
|
{
|
||||||
|
@ -384,6 +385,7 @@ failed:
|
||||||
g_free (layout);
|
g_free (layout);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static inline AudioStreamID *
|
static inline AudioStreamID *
|
||||||
_audio_device_get_streams (AudioDeviceID device_id, gint * nstreams)
|
_audio_device_get_streams (AudioDeviceID device_id, gint * nstreams)
|
||||||
|
|
|
@ -99,13 +99,6 @@ gst_core_audio_initialize_impl (GstCoreAudio * core_audio,
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioChannelLayout *
|
|
||||||
gst_core_audio_audio_device_get_channel_layout (AudioDeviceID device_id,
|
|
||||||
gboolean output)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_core_audio_select_device_impl (GstCoreAudio * core_audio)
|
gst_core_audio_select_device_impl (GstCoreAudio * core_audio)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue