mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-28 03:00:35 +00:00
8ff93a6a3d
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
319 lines
9.4 KiB
C
319 lines
9.4 KiB
C
/* 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;
|
|
}
|