mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-21 07:46:38 +00:00
9c29cd70ee
DTS type I-III specify the burst length in bits. Only type IV (which we do not currently support) needs it to be specified in bytes. Thanks to Julien Moutte for pointing this out.
322 lines
9.6 KiB
C
322 lines
9.6 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
|
|
* @since: 0.10.36
|
|
*
|
|
* This module contains some helper functions for encapsulating various
|
|
* audio formats in IEC 61937 headers and padding.
|
|
*
|
|
*/
|
|
|
|
#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:
|
|
* @spec: the ringbufer spec
|
|
*
|
|
* Calculated the size of the buffer expected by gst_audio_iec61937_payload() for
|
|
* payloading type from @spec.
|
|
*
|
|
* Returns: the size or 0 if the given @type is not supported or cannot be
|
|
* payloaded.
|
|
*
|
|
* Since: 0.10.36
|
|
*/
|
|
guint
|
|
gst_audio_iec61937_frame_size (const GstAudioRingBufferSpec * spec)
|
|
{
|
|
switch (spec->type) {
|
|
case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_AC3:
|
|
return IEC61937_PAYLOAD_SIZE_AC3;
|
|
|
|
case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_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_AUDIO_RING_BUFFER_FORMAT_TYPE_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_AUDIO_RING_BUFFER_FORMAT_TYPE_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->info.rate < 32000)
|
|
frames = 768;
|
|
else if (version == 2 && layer == 1 && spec->info.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
|
|
* @spec: the ringbufer spec for @src
|
|
*
|
|
* Payloads @src in the form specified by IEC 61937 for the type from @spec 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.36
|
|
*/
|
|
gboolean
|
|
gst_audio_iec61937_payload (const guint8 * src, guint src_n, guint8 * dst,
|
|
guint dst_n, const GstAudioRingBufferSpec * 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_AUDIO_RING_BUFFER_FORMAT_TYPE_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_AUDIO_RING_BUFFER_FORMAT_TYPE_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_AUDIO_RING_BUFFER_FORMAT_TYPE_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 bits (for type I-III) */
|
|
tmp = src_n * 8;
|
|
dst[six] = ((guint16) tmp) >> 8;
|
|
dst[seven] = ((guint16) tmp) & 0xff;
|
|
break;
|
|
}
|
|
|
|
case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_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->info.rate >= 32000))
|
|
dst[five] = 0x05;
|
|
else if (version == 2 && layer == 1 && spec->info.rate < 32000)
|
|
dst[five] = 0x08;
|
|
else if (version == 2 && layer == 2 && spec->info.rate < 32000)
|
|
dst[five] = 0x09;
|
|
else if (version == 2 && layer == 3 && spec->info.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;
|
|
}
|