Various changes to AC3->IEC958 framer. Mostly to make our IEC958 headers and dump the frame (as a probable sync failu...

Original commit message from CVS:
Various changes to AC3->IEC958 framer. Mostly to make our IEC958 headers
more accurate, and to check AC3 checksums (both of them in each frame),
and dump the frame (as a probable sync failure) if they don't match.
General code cleanup, improved comments. Changed to not construct the
header backwards, and not byteswap everything else. If we end up needing
to do little-endian output, we should swap in the element doing the
output (AC3 is big-endian).
This commit is contained in:
Michael Smith 2005-09-21 16:21:45 +00:00
parent d865cbd0f2
commit 5e1a50c7f0
4 changed files with 234 additions and 88 deletions

View file

@ -1,3 +1,19 @@
2005-09-21 Michael Smith <msmith@fluendo.com>
* gst/ac3parse/gstac3parse.c: (gst_ac3parse_class_init),
(gst_ac3parse_init), (gst_ac3parse_chain):
* gst/iec958/ac3_padder.c: (ac3_crc_init), (ac3_crc_update),
(ac3_crc_validate), (ac3p_init), (ac3p_parse):
* gst/iec958/ac3_padder.h:
* gst/iec958/ac3iec.c:
Various changes to AC3->IEC958 framer. Mostly to make our IEC958
headers more accurate, and to check AC3 checksums (both of them in
each frame), and dump the frame (as a probable sync failure) if they
don't match. General code cleanup, improved comments. Changed to not
construct the header backwards, and not byteswap everything else.
If we end up needing to do little-endian output, we should swap in
the element doing the output (AC3 is big-endian).
2005-09-20 Thomas Vander Stichele <thomas at apestaart dot org>
* docs/plugins/gst-plugins-ugly-plugins.args:

View file

@ -1,5 +1,6 @@
/* GStreamer
* Copyright (C) 2003, 2004 Martin Soto <martinsoto@users.sourceforge.net>
* 2005 Michael Smith <msmith@fluendo.com>
*
* ac3_padder.c: Pad AC3 frames for use with an SPDIF interface.
*
@ -24,16 +25,13 @@
#include "ac3_padder.h"
#define IEC61937_DATA_TYPE_AC3 1
struct frmsize_s
{
unsigned short bit_rate;
unsigned short frm_size[3];
};
static const struct frmsize_s frmsizecod_tbl[64] = {
static const struct frmsize_s frmsizecod_tbl[] = {
{32, {64, 69, 96}},
{32, {64, 70, 96}},
{40, {80, 87, 120}},
@ -74,14 +72,63 @@ static const struct frmsize_s frmsizecod_tbl[64] = {
{640, {1280, 1394, 1920}}
};
static const guint16 ac3_crc_lut[256] = {
0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011,
0x8033, 0x0036, 0x003c, 0x8039, 0x0028, 0x802d, 0x8027, 0x0022,
0x8063, 0x0066, 0x006c, 0x8069, 0x0078, 0x807d, 0x8077, 0x0072,
0x0050, 0x8055, 0x805f, 0x005a, 0x804b, 0x004e, 0x0044, 0x8041,
0x80c3, 0x00c6, 0x00cc, 0x80c9, 0x00d8, 0x80dd, 0x80d7, 0x00d2,
0x00f0, 0x80f5, 0x80ff, 0x00fa, 0x80eb, 0x00ee, 0x00e4, 0x80e1,
0x00a0, 0x80a5, 0x80af, 0x00aa, 0x80bb, 0x00be, 0x00b4, 0x80b1,
0x8093, 0x0096, 0x009c, 0x8099, 0x0088, 0x808d, 0x8087, 0x0082,
0x8183, 0x0186, 0x018c, 0x8189, 0x0198, 0x819d, 0x8197, 0x0192,
0x01b0, 0x81b5, 0x81bf, 0x01ba, 0x81ab, 0x01ae, 0x01a4, 0x81a1,
0x01e0, 0x81e5, 0x81ef, 0x01ea, 0x81fb, 0x01fe, 0x01f4, 0x81f1,
0x81d3, 0x01d6, 0x01dc, 0x81d9, 0x01c8, 0x81cd, 0x81c7, 0x01c2,
0x0140, 0x8145, 0x814f, 0x014a, 0x815b, 0x015e, 0x0154, 0x8151,
0x8173, 0x0176, 0x017c, 0x8179, 0x0168, 0x816d, 0x8167, 0x0162,
0x8123, 0x0126, 0x012c, 0x8129, 0x0138, 0x813d, 0x8137, 0x0132,
0x0110, 0x8115, 0x811f, 0x011a, 0x810b, 0x010e, 0x0104, 0x8101,
0x8303, 0x0306, 0x030c, 0x8309, 0x0318, 0x831d, 0x8317, 0x0312,
0x0330, 0x8335, 0x833f, 0x033a, 0x832b, 0x032e, 0x0324, 0x8321,
0x0360, 0x8365, 0x836f, 0x036a, 0x837b, 0x037e, 0x0374, 0x8371,
0x8353, 0x0356, 0x035c, 0x8359, 0x0348, 0x834d, 0x8347, 0x0342,
0x03c0, 0x83c5, 0x83cf, 0x03ca, 0x83db, 0x03de, 0x03d4, 0x83d1,
0x83f3, 0x03f6, 0x03fc, 0x83f9, 0x03e8, 0x83ed, 0x83e7, 0x03e2,
0x83a3, 0x03a6, 0x03ac, 0x83a9, 0x03b8, 0x83bd, 0x83b7, 0x03b2,
0x0390, 0x8395, 0x839f, 0x039a, 0x838b, 0x038e, 0x0384, 0x8381,
0x0280, 0x8285, 0x828f, 0x028a, 0x829b, 0x029e, 0x0294, 0x8291,
0x82b3, 0x02b6, 0x02bc, 0x82b9, 0x02a8, 0x82ad, 0x82a7, 0x02a2,
0x82e3, 0x02e6, 0x02ec, 0x82e9, 0x02f8, 0x82fd, 0x82f7, 0x02f2,
0x02d0, 0x82d5, 0x82df, 0x02da, 0x82cb, 0x02ce, 0x02c4, 0x82c1,
0x8243, 0x0246, 0x024c, 0x8249, 0x0258, 0x825d, 0x8257, 0x0252,
0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, 0x0264, 0x8261,
0x0220, 0x8225, 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231,
0x8213, 0x0216, 0x021c, 0x8219, 0x0208, 0x820d, 0x8207, 0x0202
};
typedef guint16 ac3_crc_state;
/* Go one byte forward in the input buffer. */
#define ac3p_in_fw(padder) ((padder)->in_ptr++, (padder)->remaining--)
static void
ac3_crc_init (ac3_crc_state * state)
{
*state = 0;
}
/* Go one byte forward in the output buffer. */
#define ac3p_out_fw(padder) ((padder)->out_ptr++, (padder)->bytes_to_copy--)
static void
ac3_crc_update (ac3_crc_state * state, guint8 * data, guint32 num_bytes)
{
int i;
for (i = 0; i < num_bytes; i++)
*state = ac3_crc_lut[data[i] ^ (*state >> 8)] ^ (*state << 8);
}
static int
ac3_crc_validate (ac3_crc_state * state)
{
return (*state == 0);
}
/**
* ac3p_init
@ -91,20 +138,47 @@ static const struct frmsize_s frmsizecod_tbl[64] = {
* subsequently used to parse an AC3 stream and convert it to IEC958
* (S/PDIF) padded packets.
*/
extern void
void
ac3p_init (ac3_padder * padder)
{
const char sync[4] = { 0x72, 0xF8, 0x1F, 0x4E };
const char sync[4] = { 0xF8, 0x72, 0x4E, 0x1F };
padder->state = AC3P_STATE_SYNC1;
padder->skipped = 0;
/* No material to read yet. */
padder->remaining = 0;
padder->buffer = NULL;
padder->buffer_end = 0;
padder->buffer_cur = 0;
padder->buffer_size = 0;
/* Initialize the sync bytes in the frame. */
memcpy (padder->frame.header, sync, 4);
}
void
ac3p_clear (ac3_padder * padder)
{
g_free (padder->buffer);
}
static void
resync (ac3_padder * padder, int offset, int skipped)
{
padder->buffer_cur -= offset;
padder->state = AC3P_STATE_SYNC1;
padder->skipped += skipped;
/* We don't want our buffer to grow unboundedly if we fail to find sync, but
* nor do we want to do this every time we call resync() */
if (padder->buffer_cur > 4096) {
memmove (padder->buffer, padder->buffer + padder->buffer_cur,
padder->buffer_end - padder->buffer_cur);
padder->buffer_end -= padder->buffer_cur;
padder->buffer_cur = 0;
}
}
/**
* ac3_push_data
@ -122,10 +196,14 @@ ac3p_init (ac3_padder * padder)
extern void
ac3p_push_data (ac3_padder * padder, guchar * data, guint size)
{
padder->in_ptr = data;
padder->remaining = size;
}
if (padder->buffer_end + size > padder->buffer_size) {
padder->buffer_size = padder->buffer_end + size;
padder->buffer = g_realloc (padder->buffer, padder->buffer_size);
}
memcpy (padder->buffer + padder->buffer_end, data, size);
padder->buffer_end += size;
}
/**
* ac3p_parse
@ -141,31 +219,31 @@ ac3p_push_data (ac3_padder * padder, guchar * data, guint size)
* the input stream must be pushed into the padder using
* ac3p_push_data(). This function should be called again after
* pushing the data.
*
* Note that the returned data (which naturally comes in 16 bit sub-frames) is
* big-endian, and may need to be byte-swapped for little-endian output.
*/
extern int
ac3p_parse (ac3_padder * padder)
{
while (padder->remaining > 0) {
while (padder->buffer_cur < padder->buffer_end) {
switch (padder->state) {
case AC3P_STATE_SYNC1:
if (*(padder->in_ptr) == 0x0b) {
if (padder->buffer[padder->buffer_cur++] == 0x0b) {
/* The first sync byte was found. Go to the next state. */
padder->frame.sync_byte1 = 0x0b;
padder->state = AC3P_STATE_SYNC2;
}
ac3p_in_fw (padder);
} else
resync (padder, 0, 1);
break;
case AC3P_STATE_SYNC2:
if (*(padder->in_ptr) == 0x77) {
if (padder->buffer[padder->buffer_cur++] == 0x77) {
/* The second sync byte was seen right after the first. Go to
the next state. */
padder->frame.sync_byte2 = 0x77;
padder->state = AC3P_STATE_HEADER;
/* Skip one byte. */
ac3p_in_fw (padder);
/* Prepare for reading the header. */
padder->out_ptr = (guchar *) & (padder->frame.crc1);
/* Discount the 2 sync bytes from the header size. */
@ -173,16 +251,15 @@ ac3p_parse (ac3_padder * padder)
} else {
/* The second sync byte was not seen. Go back to the
first state. */
padder->state = AC3P_STATE_SYNC1;
resync (padder, 0, 2);
}
break;
case AC3P_STATE_HEADER:
if (padder->bytes_to_copy > 0) {
/* Copy one byte. */
*(padder->out_ptr) = *(padder->in_ptr);
ac3p_in_fw (padder);
ac3p_out_fw (padder);
*(padder->out_ptr++) = padder->buffer[padder->buffer_cur++];
padder->bytes_to_copy--;
} else {
int fscod;
@ -190,24 +267,25 @@ ac3p_parse (ac3_padder * padder)
fscod = (padder->frame.code >> 6) & 0x03;
/* Calculate the frame size. */
padder->ac3_frame_size =
2 * frmsizecod_tbl[padder->frame.code & 0x3f].frm_size[fscod];
/* Set up the IEC header. */
if (padder->ac3_frame_size > 0) {
padder->frame.header[4] = IEC61937_DATA_TYPE_AC3;
} else {
/* Don't know what it is, better be careful. */
padder->state = AC3P_STATE_SYNC1;
break;
/* fscod == 3 is a reserved code, we're not meant to do playback in
* this case. frmsizecod being out-of-range (there are 38 entries)
* doesn't appear to be well-defined, but treat the same.
* The likely cause of both of these is false sync, so skip back to
* just in front of the previous sync word and start looking again.
*/
if (fscod == 3 || (padder->frame.code & 0x3f) >= 38) {
resync (padder, AC3P_AC3_HEADER_SIZE - 2, 2);
continue;
}
padder->frame.header[5] = 0x00;
padder->frame.header[6] = (padder->ac3_frame_size * 8) & 0xFF;
padder->frame.header[7] = ((padder->ac3_frame_size * 8) >> 8) & 0xFF;
/* Calculate the frame size (in 16 bit units). */
padder->ac3_frame_size =
frmsizecod_tbl[padder->frame.code & 0x3f].frm_size[fscod];
/* Prepare for reading the body. */
padder->bytes_to_copy = padder->ac3_frame_size - AC3P_AC3_HEADER_SIZE;
padder->bytes_to_copy = padder->ac3_frame_size * 2
- AC3P_AC3_HEADER_SIZE;
padder->state = AC3P_STATE_CONTENT;
}
break;
@ -215,36 +293,98 @@ ac3p_parse (ac3_padder * padder)
case AC3P_STATE_CONTENT:
if (padder->bytes_to_copy > 0) {
/* Copy one byte. */
*(padder->out_ptr) = *(padder->in_ptr);
ac3p_in_fw (padder);
ac3p_out_fw (padder);
*(padder->out_ptr++) = padder->buffer[padder->buffer_cur++];
padder->bytes_to_copy--;
} else {
guint16 *ptr, i;
int framesize;
int crclen1, crclen2;
guint8 *tmp;
ac3_crc_state state;
/* Frame ready. Prepare for output: */
/* Zero the non AC3 portion of the padded frame. */
memset (&(padder->frame.sync_byte1) + padder->ac3_frame_size, 0,
memset (&(padder->frame.sync_byte1) + padder->ac3_frame_size * 2, 0,
AC3P_IEC_FRAME_SIZE - AC3P_IEC_HEADER_SIZE -
padder->ac3_frame_size);
padder->ac3_frame_size * 2);
/* Fix the byte order in the AC3 portion: */
ptr = (guint16 *) & (padder->frame.sync_byte1);
i = padder->ac3_frame_size / 2;
while (i > 0) {
*ptr = GUINT16_TO_BE (*ptr);
ptr++;
i--;
/* Now checking the two CRCs. If either fails, then we re-feed all
* the data starting immediately after the 16-bit syncword (which we
* can now assume was a false sync) */
/* Length of CRC1 is defined as
truncate(framesize/2) + truncate(framesize/8)
units (each of which is 16 bit, as is 'framesize'), but this
includes the syncword, which is NOT calculated as part of
the CRC.
*/
framesize = padder->ac3_frame_size;
crclen1 = (framesize / 2 + framesize / 8) * 2 - 2;
tmp = (guint8 *) (&(padder->frame.crc1));
ac3_crc_init (&state);
ac3_crc_update (&state, tmp, crclen1);
if (!ac3_crc_validate (&state)) {
/* Rewind current stream pointer to immediately following the last
* attempted sync point, then continue parsing in initial state */
resync (padder, padder->ac3_frame_size - 2, 2);
continue;
}
/* Start over again. */
/* Now check CRC2, which covers the entire frame other than the
* 16-bit syncword */
crclen2 = padder->ac3_frame_size * 2 - 2;
ac3_crc_init (&state);
ac3_crc_update (&state, tmp, crclen2);
if (!ac3_crc_validate (&state)) {
/* Rewind current stream pointer to immediately following the last
* attempted sync point, then continue parsing in initial state */
resync (padder, padder->ac3_frame_size - 2, 2);
continue;
}
/* Now set up the rest of the IEC header (we already filled in the
32 bit sync word. */
/* Byte 5 has:
bits 0-4: data type dependent. For AC3, the bottom 3 of these bits
are copied from the AC3 frame (bsmod value), the top 2
bits are reserved, and set to zero.
bits 5-7: data stream number. We only do one data stream, so it's
zero.
*/
padder->frame.header[4] = padder->frame.bsidmod & 0x07;
/* Byte 6:
bits 0-4: data type. 1 for AC3.
bits 5-6: reserved, zero.
bit 7: error_flag. Zero if frame contains no errors
*/
padder->frame.header[5] = 1; /* AC3 is defined as datatype 1 */
/* Now, 16 bit frame size, in bits. */
padder->frame.header[6] = ((padder->ac3_frame_size * 16) >> 8) & 0xFF;
padder->frame.header[7] = (padder->ac3_frame_size * 16) & 0xFF;
/* We're done, reset state and signal that we have a frame */
padder->skipped = 0;
padder->state = AC3P_STATE_SYNC1;
memmove (padder->buffer, padder->buffer + padder->buffer_cur,
padder->buffer_end - padder->buffer_cur);
padder->buffer_end -= padder->buffer_cur;
padder->buffer_cur = 0;
return AC3P_EVENT_FRAME;
}
break;
}
}
return AC3P_EVENT_PUSH;
}

View file

@ -77,22 +77,28 @@ typedef struct {
typedef struct {
guint state; /* State of the reading automaton. */
guchar *in_ptr; /* Input pointer, marking the current
postion in the input buffer. */
guint remaining; /* The number of bytes remaining in the current
reading buffer. */
guchar *buffer; /* Input buffer */
gint buffer_end; /* End offset, in bytes, of currently valid data in
buffer */
gint buffer_size; /* Allocated size of buffer */
gint buffer_cur; /* Current position in buffer */
guchar *out_ptr; /* Output pointer, marking the current
position in the output frame. */
guint bytes_to_copy;
gint bytes_to_copy;
/* Number of bytes that still must be copied
to the output frame *during this reading
stage*. */
guint ac3_frame_size;
/* The size in bytes of the pure AC3 portion
gint ac3_frame_size;
/* The size in 16-bit units of the pure AC3 portion
of the current frame. */
gint skipped; /* Number of bytes skipped while trying to find sync */
ac3p_iec958_burst_frame frame;
/* The current output frame. */
} ac3_padder;
@ -102,6 +108,9 @@ typedef struct {
extern void
ac3p_init(ac3_padder *padder);
extern void
ac3p_clear(ac3_padder *padder);
extern void
ac3p_push_data(ac3_padder *padder, guchar *data, guint size);
@ -123,16 +132,6 @@ ac3p_parse(ac3_padder *padder);
*
* Returns the length in bytes of the last read raw AC3 frame.
*/
#define ac3p_frame_size(padder) ((padder)->ac3_frame_size)
/**
* ac3p_pending
* @padder: The padder structure.
*
* Returns a boolean value telling if there are pending material in
* the padder.
*/
#define ac3p_pending(padder) ((padder)->remaining > 0)
#define ac3p_frame_size(padder) ((padder)->ac3_frame_size * 2)
#endif

View file

@ -1,7 +1,8 @@
/* GStreamer
* Copyright (C) 2004 Martin Soto <martinsoto@users.sourceforge.net>
2005 Michael Smith <msmith@fluendo.com>
*
* ac3iec.c: Pad AC3 frames into IEC958 frames for the SP/DIF interface.
* ac3iec.c: Pad AC3 frames into IEC958 frames for the S/PDIF interface.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@ -42,7 +43,7 @@ GST_DEBUG_CATEGORY_STATIC (ac3iec_debug);
static GstElementDetails ac3iec_details = {
"AC3 to IEC958 filter",
"audio/x-private1-ac3",
"Pads AC3 frames into IEC958 frames suitable for a raw SP/DIF interface",
"Pads AC3 frames into IEC958 frames suitable for a raw S/PDIF interface",
"Martin Soto <martinsoto@users.sourceforge.net>"
};
@ -70,20 +71,9 @@ static GstStaticPadTemplate ac3iec_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
#if 0
GST_STATIC_CAPS ("audio/x-raw-int, "
"law = (int) 0, "
"endianness = (int) " G_STRINGIFY (G_LITTLE_ENDIAN) ", "
"signed = (boolean) true, "
"width = (int) 16, "
"depth = (int) 16, " "rate = (int) 48000, " "channels = (int) 2")
#endif
#if 1
GST_STATIC_CAPS ("audio/x-iec958")
#endif
);
static void ac3iec_base_init (gpointer g_class);
static void ac3iec_class_init (AC3IECClass * klass);
static void ac3iec_init (AC3IEC * ac3iec);
@ -384,6 +374,7 @@ ac3iec_change_state (GstElement * element, GstStateChange transition)
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
ac3p_clear (ac3iec->padder);
break;
case GST_STATE_CHANGE_READY_TO_NULL:
break;
@ -412,5 +403,5 @@ plugin_init (GstPlugin * plugin)
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
"iec958",
"Conversion elements to the iec958 SP/DIF format",
plugin_init, VERSION, "LGPL", PACKAGE, "http://seamless.sourceforge.net");
"Convert raw AC3 into IEC958 (S/PDIF) frames",
plugin_init, VERSION, "LGPL", GST_PACKAGE, GST_ORIGIN);