From 96972eb462cc672d73c82cb2312f2116df1ced7c Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Sat, 9 Apr 2011 12:26:56 +0530 Subject: [PATCH] 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 --- gst/audioparsers/gstac3parse.c | 126 ++++++++++++++++++++++++++++++++++++---- gst/audioparsers/gstac3parse.h | 7 +++ tests/check/elements/ac3parse.c | 2 +- 3 files changed, 122 insertions(+), 13 deletions(-) diff --git a/gst/audioparsers/gstac3parse.c b/gst/audioparsers/gstac3parse.c index 3d20aa8..2ff8fa3 100644 --- a/gst/audioparsers/gstac3parse.c +++ b/gst/audioparsers/gstac3parse.c @@ -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); diff --git a/gst/audioparsers/gstac3parse.h b/gst/audioparsers/gstac3parse.h index 13270bb..7b651ee 100644 --- a/gst/audioparsers/gstac3parse.h +++ b/gst/audioparsers/gstac3parse.h @@ -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; }; /** diff --git a/tests/check/elements/ac3parse.c b/tests/check/elements/ac3parse.c index 03e8e1d..eb25004 100644 --- a/tests/check/elements/ac3parse.c +++ b/tests/check/elements/ac3parse.c @@ -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; -- 2.7.4