directsoundsink: add support for ac-3 over spdif
authorAndoni Morales Alastruey <ylatuya@gmail.com>
Wed, 4 Jul 2012 13:42:49 +0000 (17:42 +0400)
committerSebastian Dröge <sebastian.droege@collabora.co.uk>
Wed, 4 Jul 2012 17:54:43 +0000 (19:54 +0200)
sys/directsound/gstdirectsoundsink.c

index fec8f92..fae5fa0 100644 (file)
@@ -55,6 +55,7 @@
 #include <gst/base/gstbasesink.h>
 #include <gst/audio/streamvolume.h>
 #include "gstdirectsoundsink.h"
+#include <gst/audio/gstaudioiec61937.h>
 
 #include <math.h>
 
@@ -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 =
@@ -275,6 +286,82 @@ gst_directsound_sink_getcaps (GstBaseSink * bsink, GstCaps * filter)
 }
 
 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)
 {
   GstDirectSoundSink *dsoundsink;
@@ -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)