ac3parse: Add support for IEC 61937 alignment

When pushing out buffers over S/PDIF or HDMI, IEC 61937 payloading
requires each buffer to contain 6 blocks from each substream. This adds
code to collect all the frames needed to meet this requirement before
pushing out a buffer.

https://bugzilla.gnome.org/show_bug.cgi?id=650313
This commit is contained in:
Arun Raghavan 2011-04-09 12:26:56 +05:30
parent 6095d2a3f0
commit 96972eb462
3 changed files with 122 additions and 13 deletions

View file

@ -206,6 +206,7 @@ gst_ac3_parse_reset (GstAc3Parse * ac3parse)
ac3parse->sample_rate = -1;
ac3parse->blocks = -1;
ac3parse->eac = FALSE;
ac3parse->align = GST_AC3_PARSE_ALIGN_NONE;
}
static void
@ -241,15 +242,61 @@ gst_ac3_parse_stop (GstBaseParse * parse)
return TRUE;
}
static void
gst_ac3_parse_set_alignment (GstAc3Parse * ac3parse, gboolean eac)
{
GstCaps *caps;
GstStructure *st;
const gchar *str = NULL;
int i;
if (G_LIKELY (!eac))
goto done;
caps = gst_pad_get_allowed_caps (GST_BASE_PARSE_SRC_PAD (ac3parse));
if (!caps)
goto done;
for (i = 0; i < gst_caps_get_size (caps); i++) {
st = gst_caps_get_structure (caps, i);
if (!g_str_equal (gst_structure_get_name (st), "audio/x-eac3"))
continue;
if ((str = gst_structure_get_string (st, "alignment"))) {
if (strcmp (str, "iec61937") == 0) {
ac3parse->align = GST_AC3_PARSE_ALIGN_IEC61937;
GST_DEBUG_OBJECT (ac3parse, "picked iec61937 alignment");
} else
GST_INFO_OBJECT (ac3parse, "unknown alignment: %s", str);
break;
}
}
if (caps)
gst_caps_unref (caps);
done:
/* default */
if (ac3parse->align == GST_AC3_PARSE_ALIGN_NONE) {
ac3parse->align = GST_AC3_PARSE_ALIGN_FRAME;
GST_DEBUG_OBJECT (ac3parse, "picked syncframe alignment");
}
}
static gboolean
gst_ac3_parse_frame_header_ac3 (GstAc3Parse * ac3parse, GstBuffer * buf,
guint * frame_size, guint * rate, guint * chans, guint * blks, guint * sid)
gint skip, guint * frame_size, guint * rate, guint * chans, guint * blks,
guint * sid)
{
GstBitReader bits = GST_BIT_READER_INIT_FROM_BUFFER (buf);
guint8 fscod, frmsizcod, bsid, acmod, lfe_on;
GST_LOG_OBJECT (ac3parse, "parsing ac3");
gst_bit_reader_skip_unchecked (&bits, skip * 8);
gst_bit_reader_skip_unchecked (&bits, 16 + 16);
fscod = gst_bit_reader_get_bits_uint8_unchecked (&bits, 2);
frmsizcod = gst_bit_reader_get_bits_uint8_unchecked (&bits, 6);
@ -297,7 +344,8 @@ gst_ac3_parse_frame_header_ac3 (GstAc3Parse * ac3parse, GstBuffer * buf,
static gboolean
gst_ac3_parse_frame_header_eac3 (GstAc3Parse * ac3parse, GstBuffer * buf,
guint * frame_size, guint * rate, guint * chans, guint * blks, guint * sid)
gint skip, guint * frame_size, guint * rate, guint * chans, guint * blks,
guint * sid)
{
GstBitReader bits = GST_BIT_READER_INIT_FROM_BUFFER (buf);
guint16 frmsiz, sample_rate, blocks;
@ -305,6 +353,8 @@ gst_ac3_parse_frame_header_eac3 (GstAc3Parse * ac3parse, GstBuffer * buf,
GST_LOG_OBJECT (ac3parse, "parsing e-ac3");
gst_bit_reader_skip_unchecked (&bits, skip * 8);
gst_bit_reader_skip_unchecked (&bits, 16);
strmtyp = gst_bit_reader_get_bits_uint8_unchecked (&bits, 2); /* strmtyp */
if (G_UNLIKELY (strmtyp == 3)) {
@ -349,7 +399,7 @@ gst_ac3_parse_frame_header_eac3 (GstAc3Parse * ac3parse, GstBuffer * buf,
}
static gboolean
gst_ac3_parse_frame_header (GstAc3Parse * parse, GstBuffer * buf,
gst_ac3_parse_frame_header (GstAc3Parse * parse, GstBuffer * buf, gint skip,
guint * framesize, guint * rate, guint * chans, guint * blocks,
guint * sid, gboolean * eac)
{
@ -359,6 +409,8 @@ gst_ac3_parse_frame_header (GstAc3Parse * parse, GstBuffer * buf,
GST_MEMDUMP_OBJECT (parse, "AC3 frame sync", GST_BUFFER_DATA (buf), 16);
gst_bit_reader_skip_unchecked (&bits, skip * 8);
sync = gst_bit_reader_get_bits_uint16_unchecked (&bits, 16);
gst_bit_reader_skip_unchecked (&bits, 16 + 8);
bsid = gst_bit_reader_peek_bits_uint8_unchecked (&bits, 5);
@ -371,13 +423,13 @@ gst_ac3_parse_frame_header (GstAc3Parse * parse, GstBuffer * buf,
if (bsid <= 10) {
if (eac)
*eac = FALSE;
return gst_ac3_parse_frame_header_ac3 (parse, buf, framesize, rate, chans,
blocks, sid);
return gst_ac3_parse_frame_header_ac3 (parse, buf, skip, framesize, rate,
chans, blocks, sid);
} else if (bsid <= 16) {
if (eac)
*eac = TRUE;
return gst_ac3_parse_frame_header_eac3 (parse, buf, framesize, rate, chans,
blocks, sid);
return gst_ac3_parse_frame_header_eac3 (parse, buf, skip, framesize, rate,
chans, blocks, sid);
} else {
GST_DEBUG_OBJECT (parse, "unexpected bsid %d", bsid);
return FALSE;
@ -392,7 +444,9 @@ gst_ac3_parse_check_valid_frame (GstBaseParse * parse,
GstBuffer *buf = frame->buffer;
GstByteReader reader = GST_BYTE_READER_INIT_FROM_BUFFER (buf);
gint off;
gboolean lost_sync, draining;
gboolean lost_sync, draining, eac, more = FALSE;
guint frmsiz, blocks, sid;
gint have_blocks = 0;
if (G_UNLIKELY (GST_BUFFER_SIZE (buf) < 6))
return FALSE;
@ -415,23 +469,68 @@ gst_ac3_parse_check_valid_frame (GstBaseParse * parse,
}
/* make sure the values in the frame header look sane */
if (!gst_ac3_parse_frame_header (ac3parse, buf, framesize, NULL, NULL,
NULL, NULL, NULL)) {
if (!gst_ac3_parse_frame_header (ac3parse, buf, 0, &frmsiz, NULL, NULL,
&blocks, &sid, &eac)) {
*skipsize = off + 2;
return FALSE;
}
*framesize = frmsiz;
if (G_UNLIKELY (ac3parse->align == GST_AC3_PARSE_ALIGN_NONE))
gst_ac3_parse_set_alignment (ac3parse, eac);
GST_LOG_OBJECT (parse, "got frame");
lost_sync = GST_BASE_PARSE_LOST_SYNC (parse);
draining = GST_BASE_PARSE_DRAINING (parse);
if (ac3parse->align == GST_AC3_PARSE_ALIGN_IEC61937) {
/* We need 6 audio blocks from each substream, so we keep going forwards
* till we have it */
g_assert (blocks > 0);
GST_LOG_OBJECT (ac3parse, "Need %d frames before pushing", 6 / blocks);
if (sid != 0) {
/* We need the first substream to be the one with id 0 */
GST_LOG_OBJECT (ac3parse, "Skipping till we find sid 0");
*skipsize = off + 2;
return FALSE;
}
*framesize = 0;
/* Loop till we have 6 blocks per substream */
for (have_blocks = 0; !more && have_blocks < 6; have_blocks += blocks) {
/* Loop till we get one frame from each substream */
do {
*framesize += frmsiz;
if (!gst_byte_reader_skip (&reader, frmsiz) ||
GST_BUFFER_SIZE (buf) < (*framesize + 6)) {
more = TRUE;
break;
}
if (!gst_ac3_parse_frame_header (ac3parse, buf, *framesize, &frmsiz,
NULL, NULL, NULL, &sid, &eac)) {
*skipsize = off + 2;
return FALSE;
}
} while (sid);
}
/* We're now at the next frame, so no need to skip if resyncing */
frmsiz = 0;
}
if (lost_sync && !draining) {
guint16 word = 0;
GST_DEBUG_OBJECT (ac3parse, "resyncing; checking next frame syncword");
if (!gst_byte_reader_skip (&reader, *framesize) ||
if (more || !gst_byte_reader_skip (&reader, frmsiz) ||
!gst_byte_reader_get_uint16_be (&reader, &word)) {
GST_DEBUG_OBJECT (ac3parse, "... but not sufficient data");
gst_base_parse_set_min_frame_size (parse, *framesize + 6);
@ -460,7 +559,7 @@ gst_ac3_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
guint fsize, rate, chans, blocks, sid;
gboolean eac, update_rate = FALSE;
if (!gst_ac3_parse_frame_header (ac3parse, buf, &fsize, &rate, &chans,
if (!gst_ac3_parse_frame_header (ac3parse, buf, 0, &fsize, &rate, &chans,
&blocks, &sid, &eac))
goto broken_header;
@ -486,6 +585,9 @@ gst_ac3_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
GstCaps *caps = gst_caps_new_simple (eac ? "audio/x-eac3" : "audio/x-ac3",
"framed", G_TYPE_BOOLEAN, TRUE, "rate", G_TYPE_INT, rate,
"channels", G_TYPE_INT, chans, NULL);
gst_caps_set_simple (caps, "alignment", G_TYPE_STRING,
ac3parse->align == GST_AC3_PARSE_ALIGN_IEC61937 ? "iec61937" : "frame",
NULL);
gst_buffer_set_caps (buf, caps);
gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), caps);
gst_caps_unref (caps);

View file

@ -42,6 +42,12 @@ G_BEGIN_DECLS
typedef struct _GstAc3Parse GstAc3Parse;
typedef struct _GstAc3ParseClass GstAc3ParseClass;
enum {
GST_AC3_PARSE_ALIGN_NONE,
GST_AC3_PARSE_ALIGN_FRAME,
GST_AC3_PARSE_ALIGN_IEC61937,
};
/**
* GstAc3Parse:
*
@ -55,6 +61,7 @@ struct _GstAc3Parse {
gint channels;
gint blocks;
gboolean eac;
gint align;
};
/**

View file

@ -110,7 +110,7 @@ GST_END_TEST;
GST_START_TEST (test_parse_detect_stream)
{
gst_parser_test_output_caps (ac3_frame, sizeof (ac3_frame),
NULL, SINK_CAPS_TMPL ",channels=1,rate=48000");
NULL, SINK_CAPS_TMPL ",channels=1,rate=48000,alignment=frame");
}
GST_END_TEST;