mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-11 09:55:36 +00:00
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:
parent
6095d2a3f0
commit
96972eb462
3 changed files with 122 additions and 13 deletions
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue