From 7d64e16b30f0b46a2178a25af809ec6dc790069d Mon Sep 17 00:00:00 2001 From: Andoni Morales Alastruey Date: Wed, 4 Jul 2012 17:42:49 +0400 Subject: [PATCH] directsoundsink: add support for ac-3 over spdif --- sys/directsound/gstdirectsoundsink.c | 161 +++++++++++++++++++++++++-- 1 file changed, 150 insertions(+), 11 deletions(-) diff --git a/sys/directsound/gstdirectsoundsink.c b/sys/directsound/gstdirectsoundsink.c index fec8f9283d..fae5fa0d33 100644 --- a/sys/directsound/gstdirectsoundsink.c +++ b/sys/directsound/gstdirectsoundsink.c @@ -55,6 +55,7 @@ #include #include #include "gstdirectsoundsink.h" +#include #include @@ -79,6 +80,8 @@ static void gst_directsound_sink_get_property (GObject * object, guint prop_id, static GstCaps *gst_directsound_sink_getcaps (GstBaseSink * bsink, GstCaps * filter); +static GstBuffer *gst_directsound_sink_payload (GstAudioBaseSink * sink, + GstBuffer * buf); static gboolean gst_directsound_sink_prepare (GstAudioSink * asink, GstAudioRingBufferSpec * spec); static gboolean gst_directsound_sink_unprepare (GstAudioSink * asink); @@ -90,6 +93,7 @@ static guint gst_directsound_sink_delay (GstAudioSink * asink); static void gst_directsound_sink_reset (GstAudioSink * asink); static GstCaps *gst_directsound_probe_supported_formats (GstDirectSoundSink * dsoundsink, const GstCaps * template_caps); +static gboolean gst_directsound_sink_query (GstBaseSink * pad, GstQuery * query); static void gst_directsound_sink_set_volume (GstDirectSoundSink * sink, gdouble volume, gboolean store); @@ -110,7 +114,7 @@ static GstStaticPadTemplate directsoundsink_sink_factory = "format = (string) S8, " "layout = (string) interleaved, " "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ];" - "audio/x-iec958")); + "audio/x-ac3, framed = (boolean) true;")); enum { @@ -136,6 +140,7 @@ gst_directsound_sink_class_init (GstDirectSoundSinkClass * klass) GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass); GstAudioSinkClass *gstaudiosink_class = GST_AUDIO_SINK_CLASS (klass); + GstAudioBaseSinkClass *gstaudiobasesink_class = GST_AUDIO_BASE_SINK_CLASS (klass); GstElementClass *element_class = GST_ELEMENT_CLASS (klass); GST_DEBUG_CATEGORY_INIT (directsoundsink_debug, "directsoundsink", 0, @@ -150,6 +155,12 @@ gst_directsound_sink_class_init (GstDirectSoundSinkClass * klass) gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_directsound_sink_getcaps); + gstbasesink_class->query = + GST_DEBUG_FUNCPTR (gst_directsound_sink_query); + + gstaudiobasesink_class->payload = + GST_DEBUG_FUNCPTR (gst_directsound_sink_payload); + gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_directsound_sink_prepare); gstaudiosink_class->unprepare = @@ -274,6 +285,82 @@ gst_directsound_sink_getcaps (GstBaseSink * bsink, GstCaps * filter) return caps; } +static gboolean +gst_directsound_sink_acceptcaps (GstBaseSink * sink, GstQuery * query) +{ + GstDirectSoundSink *dsink = GST_DIRECTSOUND_SINK (sink); + GstAudioRingBuffer *rbuf = GST_AUDIO_BASE_SINK (dsink)->ringbuffer; + GstPad *pad; + GstCaps *caps; + GstCaps *pad_caps; + GstStructure *st; + gboolean ret = FALSE; + + GstAudioRingBufferSpec spec = { 0 }; + + if (G_UNLIKELY (dsink == NULL)) + return FALSE; + + pad = sink->sinkpad; + + gst_query_parse_accept_caps (query, &caps); + GST_DEBUG_OBJECT (pad, "caps %" GST_PTR_FORMAT, caps); + + pad_caps = gst_pad_query_caps (pad, NULL); + if (pad_caps) { + ret = gst_caps_can_intersect (pad_caps, caps); + gst_caps_unref (pad_caps); + if (!ret) + goto done; + } + + /* If we've not got fixed caps, creating a stream might fail, so let's just + * return from here with default acceptcaps behaviour */ + if (!gst_caps_is_fixed (caps)) + goto done; + + if (!gst_audio_ring_buffer_parse_caps (&rbuf->spec, caps)) + goto done; + + /* Make sure input is framed (one frame per buffer) and can be payloaded */ + switch (rbuf->spec.type) { + case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_AC3: + { + gboolean framed = FALSE, parsed = FALSE; + st = gst_caps_get_structure (caps, 0); + + gst_structure_get_boolean (st, "framed", &framed); + gst_structure_get_boolean (st, "parsed", &parsed); + if ((!framed && !parsed) || gst_audio_iec61937_frame_size (&spec) <= 0) + goto done; + } + default:{ + } + } + ret = TRUE; + +done: + gst_object_unref (dsink); + gst_query_set_accept_caps_result (query, ret); + return TRUE; +} + +static gboolean +gst_directsound_sink_query (GstBaseSink * sink, GstQuery * query) +{ + gboolean res = TRUE; + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_ACCEPT_CAPS: + res = gst_directsound_sink_acceptcaps (sink, query); + break; + default: + res = FALSE; + } + + return res; +} + static gboolean gst_directsound_sink_open (GstAudioSink * asink) { @@ -301,6 +388,14 @@ gst_directsound_sink_open (GstAudioSink * asink) return TRUE; } +static boolean +gst_directsound_sink_is_spdif_format (GstDirectSoundSink * dsoundsink) +{ + GstAudioRingBufferFormatType type; + type = GST_AUDIO_BASE_SINK (dsoundsink)->ringbuffer->spec.type; + return type == GST_AUDIO_RING_BUFFER_FORMAT_TYPE_AC3; +} + static gboolean gst_directsound_sink_prepare (GstAudioSink * asink, GstAudioRingBufferSpec * spec) @@ -318,7 +413,7 @@ gst_directsound_sink_prepare (GstAudioSink * asink, /* fill the WAVEFORMATEX structure with spec params */ memset (&wfx, 0, sizeof (wfx)); - if (spec->type != GST_AUDIO_RING_BUFFER_FORMAT_TYPE_IEC958) { + if (gst_directsound_sink_is_spdif_format (dsoundsink)) { wfx.cbSize = sizeof (wfx); wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.nChannels = spec->info.channels; @@ -361,7 +456,7 @@ gst_directsound_sink_prepare (GstAudioSink * asink, dsoundsink->buffer_size = spec->segsize * spec->segtotal; GST_INFO_OBJECT (dsoundsink, - "GstRingBufferSpec->channels: %d, GstRingBufferSpec->rate: %d, GstRingBufferSpec->bytes_per_sample: %d\n" + "GstAudioRingBufferSpec->channels: %d, GstAudioRingBufferSpec->rate: %d, GstAudioRingBufferSpec->bytes_per_sample: %d\n" "WAVEFORMATEX.nSamplesPerSec: %ld, WAVEFORMATEX.wBitsPerSample: %d, WAVEFORMATEX.nBlockAlign: %d, WAVEFORMATEX.nAvgBytesPerSec: %ld\n" "Size of dsound circular buffer=>%d\n", spec->info.channels, spec->info.rate, spec->info.bpf, wfx.nSamplesPerSec, wfx.wBitsPerSample, @@ -371,7 +466,7 @@ gst_directsound_sink_prepare (GstAudioSink * asink, memset (&descSecondary, 0, sizeof (DSBUFFERDESC)); descSecondary.dwSize = sizeof (DSBUFFERDESC); descSecondary.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS; - if (spec->type != GST_AUDIO_RING_BUFFER_FORMAT_TYPE_IEC958) + if (!gst_directsound_sink_is_spdif_format (dsoundsink)) descSecondary.dwFlags |= DSBCAPS_CTRLVOLUME; descSecondary.dwBufferBytes = dsoundsink->buffer_size; @@ -436,10 +531,6 @@ gst_directsound_sink_write (GstAudioSink * asink, gpointer data, guint length) dsoundsink = GST_DIRECTSOUND_SINK (asink); - /* Fix endianness */ - if (dsoundsink->type == GST_AUDIO_RING_BUFFER_FORMAT_TYPE_IEC958) - _swab (data, data, length); - GST_DSOUND_LOCK (dsoundsink); /* get current buffer status */ @@ -639,8 +730,7 @@ gst_directsound_probe_supported_formats (GstDirectSoundSink * dsoundsink, GST_INFO_OBJECT (dsoundsink, "AC3 passthrough not supported " "(IDirectSound_CreateSoundBuffer returned: %s)\n", DXGetErrorString9 (hRes)); - caps = - gst_caps_subtract (caps, gst_caps_new_empty_simple ("audio/x-iec958")); + caps = gst_caps_subtract (caps, gst_caps_new_empty_simple ("audio/x-ac3")); } else { GST_INFO_OBJECT (dsoundsink, "AC3 passthrough supported"); hRes = IDirectSoundBuffer_Release (dsoundsink->pDSBSecondary); @@ -651,12 +741,61 @@ gst_directsound_probe_supported_formats (GstDirectSoundSink * dsoundsink, } } #else - caps = gst_caps_subtract (caps, gst_caps_new_simple ("audio/x-iec958", NULL)); + caps = gst_caps_subtract (caps, gst_caps_new_simple ("audio/x-ac3", NULL)); #endif return caps; } +static GstBuffer * +gst_directsound_sink_payload (GstAudioBaseSink * sink, GstBuffer * buf) +{ + switch (sink->ringbuffer->spec.type) { + case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_AC3: + { + gint framesize = gst_audio_iec61937_frame_size (&sink->ringbuffer->spec); + GstBuffer *out; + GstMapInfo infobuf, infoout; + gboolean success; + + if (framesize <= 0) + return NULL; + + out = gst_buffer_new_and_alloc (framesize); + + if (!gst_buffer_map (buf, &infobuf, GST_MAP_READWRITE)) + { + gst_buffer_unref (out); + return NULL; + } + if (!gst_buffer_map (out, &infoout, GST_MAP_READWRITE)) + { + gst_buffer_unmap (buf, &infobuf); + gst_buffer_unref (out); + return NULL; + } + success = gst_audio_iec61937_payload (infobuf.data, infobuf.size, + infoout.data, infoout.size, &sink->ringbuffer->spec); + if (!success) { + gst_buffer_unmap (out, &infoout); + gst_buffer_unmap (buf, &infobuf); + gst_buffer_unref (out); + return NULL; + } + + gst_buffer_copy_into (out, buf, GST_BUFFER_COPY_ALL, 0, -1); + /* Fix endianness */ + _swab ((gchar *) infoout.data, (gchar *) infoout.data, infobuf.size); + gst_buffer_unmap (out, &infoout); + gst_buffer_unmap (buf, &infobuf); + return out; + } + + default: + return gst_buffer_ref (buf); + } +} + static void gst_directsound_sink_set_volume (GstDirectSoundSink * dsoundsink, gdouble dvolume, gboolean store)