audio: Add an IEC 61937 payloading library

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
This commit is contained in:
Arun Raghavan 2011-03-07 20:49:16 +05:30
parent 643e5f586c
commit 8ff93a6a3d
5 changed files with 369 additions and 2 deletions

View file

@ -52,6 +52,7 @@
<xi:include href="xml/gstbaseaudiosrc.xml" /> <xi:include href="xml/gstbaseaudiosrc.xml" />
<xi:include href="xml/gstmultichannel.xml" /> <xi:include href="xml/gstmultichannel.xml" />
<xi:include href="xml/gstringbuffer.xml" /> <xi:include href="xml/gstringbuffer.xml" />
<xi:include href="xml/gstaudioiec61937.xml" />
</chapter> </chapter>
<chapter id="gstreamer-cdda"> <chapter id="gstreamer-cdda">

View file

@ -317,6 +317,13 @@ gst_ring_buffer_debug_spec_buff
gst_ring_buffer_debug_spec_caps gst_ring_buffer_debug_spec_caps
</SECTION> </SECTION>
<SECTION>
<FILE>gstaudioiec61937</FILE>
<INCLUDE>gst/audio/gstaudioiec61937.h</INCLUDE>
gst_audio_iec61937_frame_size
gst_audio_iec61937_payload
</SECTION>
# cdda # cdda

View file

@ -26,7 +26,8 @@ libgstaudio_@GST_MAJORMINOR@_la_SOURCES = \
gstbaseaudiosrc.c \ gstbaseaudiosrc.c \
gstaudiofilter.c \ gstaudiofilter.c \
gstaudiosink.c \ gstaudiosink.c \
gstaudiosrc.c gstaudiosrc.c \
gstaudioiec61937.c
nodist_libgstaudio_@GST_MAJORMINOR@_la_SOURCES = $(built_sources) $(built_headers) nodist_libgstaudio_@GST_MAJORMINOR@_la_SOURCES = $(built_sources) $(built_headers)
libgstaudio_@GST_MAJORMINOR@includedir = $(includedir)/gstreamer-@GST_MAJORMINOR@/gst/audio libgstaudio_@GST_MAJORMINOR@includedir = $(includedir)/gstreamer-@GST_MAJORMINOR@/gst/audio
@ -40,7 +41,8 @@ libgstaudio_@GST_MAJORMINOR@include_HEADERS = \
gstaudiosink.h \ gstaudiosink.h \
gstaudiosrc.h \ gstaudiosrc.h \
mixerutils.h \ mixerutils.h \
multichannel.h multichannel.h \
gstaudioiec61937.h
nodist_libgstaudio_@GST_MAJORMINOR@include_HEADERS = \ nodist_libgstaudio_@GST_MAJORMINOR@include_HEADERS = \
audio-enumtypes.h audio-enumtypes.h

View file

@ -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;
}

View file

@ -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__ */