audio: Add an IEC 61937 payloading library
authorArun Raghavan <arun.raghavan@collabora.co.uk>
Mon, 7 Mar 2011 15:19:16 +0000 (20:49 +0530)
committerArun Raghavan <arun.raghavan@collabora.co.uk>
Sat, 14 May 2011 13:23:12 +0000 (18:53 +0530)
This can be used by sinks to take compressed formats, correctly payload
these in IEC 61937 frames and feed these to sinks that support
passthrough output over IEC 60958 (S/PDIF) or, in the case of MP3, over
Bluetooth.

Initial implementation includes AC3, E-AC3, MPEG-1, MPEG-2 (non-AAC),
and DTS (type-I/II/II) payloading. More formats can be added as needed.

API: gst_audio_iec61937_frame_size()
API: gst_audio_iec61937_payload()

https://bugzilla.gnome.org/show_bug.cgi?id=642730

docs/libs/gst-plugins-base-libs-docs.sgml
docs/libs/gst-plugins-base-libs-sections.txt
gst-libs/gst/audio/Makefile.am
gst-libs/gst/audio/gstaudioiec61937.c [new file with mode: 0644]
gst-libs/gst/audio/gstaudioiec61937.h [new file with mode: 0644]

index b7b61a70a76d0a74b851556c1cd4955ee3fbeff6..54df8dba20d0f975f41d62d0bc159ef0a067fbb9 100644 (file)
@@ -52,6 +52,7 @@
       <xi:include href="xml/gstbaseaudiosrc.xml" />
       <xi:include href="xml/gstmultichannel.xml" />
       <xi:include href="xml/gstringbuffer.xml" />
+      <xi:include href="xml/gstaudioiec61937.xml" />
     </chapter>
 
     <chapter id="gstreamer-cdda">
index 41235a09efa5e4a68e64b2309de9ae6fe8016b9f..15bd466856316110d3d0286841b519d6fcdcd77f 100644 (file)
@@ -317,6 +317,13 @@ gst_ring_buffer_debug_spec_buff
 gst_ring_buffer_debug_spec_caps
 </SECTION>
 
+<SECTION>
+<FILE>gstaudioiec61937</FILE>
+<INCLUDE>gst/audio/gstaudioiec61937.h</INCLUDE>
+gst_audio_iec61937_frame_size
+gst_audio_iec61937_payload
+</SECTION>
+
 
 # cdda
 
index e531e73526fde97b5df3a61498ab1141d94ee214..7977f89806c306351d6de79744c36300e0211827 100644 (file)
@@ -26,7 +26,8 @@ libgstaudio_@GST_MAJORMINOR@_la_SOURCES = \
        gstbaseaudiosrc.c \
        gstaudiofilter.c \
        gstaudiosink.c \
-       gstaudiosrc.c
+       gstaudiosrc.c \
+       gstaudioiec61937.c
 nodist_libgstaudio_@GST_MAJORMINOR@_la_SOURCES = $(built_sources) $(built_headers)
 
 libgstaudio_@GST_MAJORMINOR@includedir = $(includedir)/gstreamer-@GST_MAJORMINOR@/gst/audio
@@ -40,7 +41,8 @@ libgstaudio_@GST_MAJORMINOR@include_HEADERS = \
        gstaudiosink.h \
        gstaudiosrc.h \
        mixerutils.h \
-       multichannel.h
+       multichannel.h \
+       gstaudioiec61937.h
 
 nodist_libgstaudio_@GST_MAJORMINOR@include_HEADERS = \
        audio-enumtypes.h
diff --git a/gst-libs/gst/audio/gstaudioiec61937.c b/gst-libs/gst/audio/gstaudioiec61937.c
new file mode 100644 (file)
index 0000000..caf150d
--- /dev/null
@@ -0,0 +1,319 @@
+/* GStreamer audio helper functions for IEC 61937 payloading
+ * (c) 2011 Intel Corporation
+ *     2011 Collabora Multimedia
+ *     2011 Arun Raghavan <arun.raghavan@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:gstaudioiec61937
+ * @short_description: Utility functions for IEC 61937 payloading
+ *
+ * This module contains some helper functions for encapsulating various
+ * audio formats in IEC 61937 headers and padding.
+ *
+ * Since: 0.10.35
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include "gstaudioiec61937.h"
+
+#define IEC61937_HEADER_SIZE      8
+#define IEC61937_PAYLOAD_SIZE_AC3 (1536 * 4)
+#define IEC61937_PAYLOAD_SIZE_EAC3 (6144 * 4)
+
+static gint
+caps_get_int_field (const GstCaps * caps, const gchar * field)
+{
+  const GstStructure *st;
+  gint ret = 0;
+
+  st = gst_caps_get_structure (caps, 0);
+  gst_structure_get_int (st, field, &ret);
+
+  return ret;
+}
+
+static const gchar *
+caps_get_string_field (const GstCaps * caps, const gchar * field)
+{
+  const GstStructure *st = gst_caps_get_structure (caps, 0);
+  return gst_structure_get_string (st, field);
+}
+
+/**
+ * gst_audio_iec61937_frame_size
+ * @type: the type of data to be payloaded as a #GstBufferFormatType
+ *
+ * Returns 0 if the given @type is not supported or cannot be payloaded, else
+ * returns the size of the buffer expected by gst_audio_iec61937_payload() for
+ * payloading @type.
+ *
+ * Since: 0.10.35
+ */
+guint
+gst_audio_iec61937_frame_size (const GstRingBufferSpec * spec)
+{
+  switch (spec->type) {
+    case GST_BUFTYPE_AC3:
+      return IEC61937_PAYLOAD_SIZE_AC3;
+
+    case GST_BUFTYPE_EAC3:
+      /* Check that the parser supports /some/ alignment. Need to be less
+       * strict about this at checking time since the alignment is dynamically
+       * set at the moment. */
+      if (caps_get_string_field (spec->caps, "alignment"))
+        return IEC61937_PAYLOAD_SIZE_EAC3;
+      else
+        return 0;
+
+    case GST_BUFTYPE_DTS:
+    {
+      gint dts_frame_size = caps_get_int_field (spec->caps, "frame-size");
+      gint iec_frame_size = caps_get_int_field (spec->caps, "block-size") * 4;
+
+      /* Note: this will also (correctly) fail if either field is missing */
+      if (iec_frame_size >= (dts_frame_size + IEC61937_HEADER_SIZE))
+        return iec_frame_size;
+      else
+        return 0;
+    }
+
+    case GST_BUFTYPE_MPEG:
+    {
+      int version, layer, channels, frames;
+
+      version = caps_get_int_field (spec->caps, "mpegaudioversion");
+      layer = caps_get_int_field (spec->caps, "layer");
+      channels = caps_get_int_field (spec->caps, "channels");
+
+      /* Bail out if we can't figure out either, if it's MPEG 2.5, or if it's
+       * MP3 with multichannel audio */
+      if (!version || !layer || version == 3 || channels > 2)
+        return 0;
+
+      if (version == 1 && layer == 1)
+        frames = 384;
+      else if (version == 2 && layer == 1 && spec->rate < 32000)
+        frames = 768;
+      else if (version == 2 && layer == 1 && spec->rate < 32000)
+        frames = 2304;
+      else
+        frames = 1152;
+
+      return frames * 4;
+    }
+
+    default:
+      return 0;
+  }
+}
+
+/**
+ * gst_audio_iec61937_payload
+ * @src: a buffer containing the data to payload
+ * @src_n: size of @src in bytes
+ * @dst: the destination buffer to store the payloaded contents in. Should not
+ *       overlap with @src
+ * @dst_n: size of @dst in bytes
+ * @type: the type of data in @src
+ *
+ * Payloads @src in the form specified by IEC 61937 for @type and stores
+ * the result in @dst. @src must contain exactly one frame of data and the
+ * frame is not checked for errors.
+ *
+ * Returns: transfer-full: #TRUE if the payloading was successful, #FALSE
+ * otherwise.
+ *
+ * Since: 0.10.35
+ */
+gboolean
+gst_audio_iec61937_payload (const guint8 * src, guint src_n, guint8 * dst,
+    guint dst_n, const GstRingBufferSpec * spec)
+{
+  guint i, tmp;
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+  guint8 zero = 0, one = 1, two = 2, three = 3, four = 4, five = 5, six = 6,
+      seven = 7;
+#else
+  /* We need to send the data byte-swapped */
+  guint8 zero = 1, one = 0, two = 3, three = 2, four = 5, five = 4, six = 7,
+      seven = 6;
+#endif
+
+  g_return_val_if_fail (src != NULL, FALSE);
+  g_return_val_if_fail (dst != NULL, FALSE);
+  g_return_val_if_fail (src != dst, FALSE);
+  g_return_val_if_fail (dst_n >= gst_audio_iec61937_frame_size (spec), FALSE);
+
+  if (dst_n < src_n + IEC61937_HEADER_SIZE)
+    return FALSE;
+
+  /* Pa, Pb */
+  dst[zero] = 0xF8;
+  dst[one] = 0x72;
+  dst[two] = 0x4E;
+  dst[three] = 0x1F;
+
+  switch (spec->type) {
+    case GST_BUFTYPE_AC3:
+    {
+      g_return_val_if_fail (src_n >= 6, FALSE);
+
+      /* Pc: bit 13-15 - stream number (0)
+       *     bit 11-12 - reserved (0)
+       *     bit  8-10 - bsmod from AC3 frame */
+      dst[four] = src[5] & 0x7;
+      /* Pc: bit    7  - error bit (0)
+       *     bit  5-6  - subdata type (0)
+       *     bit  0-4  - data type (1) */
+      dst[five] = 1;
+      /* Pd: bit 15-0  - frame size in bits */
+      tmp = src_n * 8;
+      dst[six] = (guint8) (tmp >> 8);
+      dst[seven] = (guint8) (tmp & 0xff);
+
+      break;
+    }
+
+    case GST_BUFTYPE_EAC3:
+    {
+      if (g_str_equal (caps_get_string_field (spec->caps, "alignment"),
+              "iec61937"))
+        return FALSE;
+
+      /* Pc: bit 13-15 - stream number (0)
+       *     bit 11-12 - reserved (0)
+       *     bit  8-10 - bsmod from E-AC3 frame if present */
+      /* FIXME: this works, but nicer if we can put in the actual bsmod */
+      dst[four] = 0;
+      /* Pc: bit    7  - error bit (0)
+       *     bit  5-6  - subdata type (0)
+       *     bit  0-4  - data type (21) */
+      dst[five] = 21;
+      /* Pd: bit 15-0  - frame size in bytes */
+      dst[six] = ((guint16) src_n) >> 8;
+      dst[seven] = ((guint16) src_n) & 0xff;
+
+      break;
+    }
+
+    case GST_BUFTYPE_DTS:
+    {
+      int blocksize = caps_get_int_field (spec->caps, "block-size");
+
+      g_return_val_if_fail (src_n != 0, FALSE);
+
+      if (blocksize == 0)
+        return FALSE;
+
+      /* Pc: bit 13-15 - stream number (0)
+       *     bit 11-12 - reserved (0)
+       *     bit  8-10 - for DTS type I-III (0) */
+      dst[four] = 0;
+      /* Pc: bit    7  - error bit (0)
+       *     bit  5-6  - reserved (0)
+       *     bit  0-4  - data type (11 = type I, 12 = type II,
+       *                            13 = type III) */
+      dst[five] = 11 + (blocksize / 1024);
+      /* Pd: bit 15-0  - frame size in bytes */
+      dst[six] = ((guint16) src_n) >> 8;
+      dst[seven] = ((guint16) src_n) & 0xff;
+      break;
+    }
+
+    case GST_BUFTYPE_MPEG:
+    {
+      int version, layer;
+
+      version = caps_get_int_field (spec->caps, "mpegaudioversion");
+      layer = caps_get_int_field (spec->caps, "layer");
+
+      g_return_val_if_fail (version > 0 && layer > 0, FALSE);
+
+      /* NOTE: multichannel audio (MPEG-2) is not supported */
+
+      /* Pc: bit 13-15 - stream number (0)
+       *     bit 11-12 - reserved (0)
+       *     bit  9-10 - 0 - no dynamic range control
+       *               - 2 - dynamic range control exists
+       *               - 1,3 - reserved
+       *     bit    8  - Normal (0) or Karaoke (1) mode */
+      dst[four] = 0;
+      /* Pc: bit    7  - error bit (0)
+       *     bit  5-6  - reserved (0)
+       *     bit  0-4  - data type (04 = MPEG 1, Layer 1
+       *                            05 = MPEG 1, Layer 2, 3 / MPEG 2, w/o ext.
+       *                            06 = MPEG 2, with extension
+       *                            08 - MPEG 2 LSF, Layer 1
+       *                            09 - MPEG 2 LSF, Layer 2
+       *                            10 - MPEG 2 LSF, Layer 3 */
+      if (version == 1 && layer == 1)
+        dst[five] = 0x04;
+      else if ((version == 1 && (layer == 2 || layer == 3)) ||
+          (version == 2 && spec->rate >= 32000))
+        dst[five] = 0x05;
+      else if (version == 2 && layer == 1 && spec->rate < 32000)
+        dst[five] = 0x08;
+      else if (version == 2 && layer == 2 && spec->rate < 32000)
+        dst[five] = 0x09;
+      else if (version == 2 && layer == 3 && spec->rate < 32000)
+        dst[five] = 0x0A;
+      else
+        g_return_val_if_reached (FALSE);
+      /* Pd: bit 15-0  - frame size in bits */
+      dst[six] = ((guint16) src_n * 8) >> 8;
+      dst[seven] = ((guint16) src_n * 8) & 0xff;
+
+      break;
+    }
+
+    default:
+      return FALSE;
+  }
+
+  /* Copy the payload */
+  i = 8;
+
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+  memcpy (dst + i, src, src_n);
+#else
+  /* Byte-swapped again */
+  /* FIXME: orc-ify this */
+  for (tmp = 1; tmp < src_n; tmp += 2) {
+    dst[i + tmp - 1] = src[tmp];
+    dst[i + tmp] = src[tmp - 1];
+  }
+  /* Do we have 1 byte remaining? */
+  if (src_n % 2) {
+    dst[i + src_n - 1] = 0;
+    dst[i + src_n] = src[src_n - 1];
+    i++;
+  }
+#endif
+
+  i += src_n;
+
+  /* Zero the rest */
+  memset (dst + i, 0, dst_n - i);
+
+  return TRUE;
+}
diff --git a/gst-libs/gst/audio/gstaudioiec61937.h b/gst-libs/gst/audio/gstaudioiec61937.h
new file mode 100644 (file)
index 0000000..52da245
--- /dev/null
@@ -0,0 +1,38 @@
+/* GStreamer audio helper functions for IEC 61937 payloading
+ * (c) 2011 Intel Corporation
+ *     2011 Collabora Multimedia
+ *     2011 Arun Raghavan <arun.raghavan@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/**
+ * SECTION:gstaudioiec61937
+ * @short_description: Utility functions for IEC 61937 payloading
+ *
+ * This module contains some helper functions for encapsulating various
+ * audio formats in IEC 61937 headers and padding.
+ */
+
+#ifndef __GST_AUDIO_IEC61937_H__
+#define __GST_AUDIO_IEC61937_H__
+
+#include <gst/audio/gstringbuffer.h>
+
+guint gst_audio_iec61937_frame_size (const GstRingBufferSpec * spec);
+gboolean gst_audio_iec61937_payload (const guint8 * src, guint src_n,
+    guint8 * dst, guint dst_n, const GstRingBufferSpec * spec);
+
+#endif /* __GST_AUDIO_IEC61937_H__ */