audioparsers: fix some more parsers

This commit is contained in:
Edward Hervey 2011-06-06 20:43:31 +02:00
parent 0b1bdcf7cb
commit 7389cb1513
5 changed files with 434 additions and 271 deletions

View file

@ -469,6 +469,7 @@ gst_aac_parse_detect_stream (GstAacParse * aacparse,
int skip_size = 0; int skip_size = 0;
int bitstream_type; int bitstream_type;
int sr_idx; int sr_idx;
GstCaps *sinkcaps;
aacparse->header_type = DSPAAC_HEADER_ADIF; aacparse->header_type = DSPAAC_HEADER_ADIF;
aacparse->mpegversion = 4; aacparse->mpegversion = 4;
@ -531,8 +532,9 @@ gst_aac_parse_detect_stream (GstAacParse * aacparse,
gst_base_parse_set_min_frame_size (GST_BASE_PARSE (aacparse), 512); gst_base_parse_set_min_frame_size (GST_BASE_PARSE (aacparse), 512);
/* arrange for metadata and get out of the way */ /* arrange for metadata and get out of the way */
gst_aac_parse_set_src_caps (aacparse, sinkcaps = gst_pad_get_current_caps (GST_BASE_PARSE_SINK_PAD (aacparse));
GST_PAD_CAPS (GST_BASE_PARSE_SINK_PAD (aacparse))); gst_aac_parse_set_src_caps (aacparse, sinkcaps);
gst_caps_unref (sinkcaps);
/* not syncable, not easily seekable (unless we push data from start */ /* not syncable, not easily seekable (unless we push data from start */
gst_base_parse_set_syncable (GST_BASE_PARSE_CAST (aacparse), FALSE); gst_base_parse_set_syncable (GST_BASE_PARSE_CAST (aacparse), FALSE);
@ -662,14 +664,18 @@ gst_aac_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
if (G_UNLIKELY (rate != aacparse->sample_rate if (G_UNLIKELY (rate != aacparse->sample_rate
|| channels != aacparse->channels)) { || channels != aacparse->channels)) {
GstCaps *sinkcaps;
aacparse->sample_rate = rate; aacparse->sample_rate = rate;
aacparse->channels = channels; aacparse->channels = channels;
if (!gst_aac_parse_set_src_caps (aacparse, sinkcaps = gst_pad_get_current_caps (GST_BASE_PARSE (aacparse)->sinkpad);
GST_PAD_CAPS (GST_BASE_PARSE (aacparse)->sinkpad))) { if (!gst_aac_parse_set_src_caps (aacparse, sinkcaps)) {
/* If linking fails, we need to return appropriate error */ /* If linking fails, we need to return appropriate error */
gst_caps_unref (sinkcaps);
ret = GST_FLOW_NOT_LINKED; ret = GST_FLOW_NOT_LINKED;
} }
gst_caps_unref (sinkcaps);
gst_base_parse_set_frame_rate (GST_BASE_PARSE (aacparse), gst_base_parse_set_frame_rate (GST_BASE_PARSE (aacparse),
aacparse->sample_rate, 1024, 2, 2); aacparse->sample_rate, 1024, 2, 2);

View file

@ -240,18 +240,24 @@ static gboolean
gst_ac3_parse_frame_header_ac3 (GstAc3Parse * ac3parse, GstBuffer * buf, gst_ac3_parse_frame_header_ac3 (GstAc3Parse * ac3parse, GstBuffer * buf,
guint * frame_size, guint * rate, guint * chans, guint * blks, guint * sid) guint * frame_size, guint * rate, guint * chans, guint * blks, guint * sid)
{ {
GstBitReader bits = GST_BIT_READER_INIT_FROM_BUFFER (buf); GstBitReader bits;
gpointer data;
gsize size;
guint8 fscod, frmsizcod, bsid, acmod, lfe_on; guint8 fscod, frmsizcod, bsid, acmod, lfe_on;
gboolean ret = FALSE;
GST_LOG_OBJECT (ac3parse, "parsing ac3"); GST_LOG_OBJECT (ac3parse, "parsing ac3");
data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
gst_bit_reader_init (&bits, data, size);
gst_bit_reader_skip_unchecked (&bits, 16 + 16); gst_bit_reader_skip_unchecked (&bits, 16 + 16);
fscod = gst_bit_reader_get_bits_uint8_unchecked (&bits, 2); fscod = gst_bit_reader_get_bits_uint8_unchecked (&bits, 2);
frmsizcod = gst_bit_reader_get_bits_uint8_unchecked (&bits, 6); frmsizcod = gst_bit_reader_get_bits_uint8_unchecked (&bits, 6);
if (G_UNLIKELY (fscod == 3 || frmsizcod >= G_N_ELEMENTS (frmsizcod_table))) { if (G_UNLIKELY (fscod == 3 || frmsizcod >= G_N_ELEMENTS (frmsizcod_table))) {
GST_DEBUG_OBJECT (ac3parse, "bad fscod=%d frmsizcod=%d", fscod, frmsizcod); GST_DEBUG_OBJECT (ac3parse, "bad fscod=%d frmsizcod=%d", fscod, frmsizcod);
return FALSE; goto cleanup;
} }
bsid = gst_bit_reader_get_bits_uint8_unchecked (&bits, 5); bsid = gst_bit_reader_get_bits_uint8_unchecked (&bits, 5);
@ -262,7 +268,7 @@ gst_ac3_parse_frame_header_ac3 (GstAc3Parse * ac3parse, GstBuffer * buf,
* but seemingly only defines 6 and 8 cases */ * but seemingly only defines 6 and 8 cases */
if (bsid > 8) { if (bsid > 8) {
GST_DEBUG_OBJECT (ac3parse, "unexpected bsid=%d", bsid); GST_DEBUG_OBJECT (ac3parse, "unexpected bsid=%d", bsid);
return FALSE; goto cleanup;
} else if (bsid != 8 && bsid != 6) { } else if (bsid != 8 && bsid != 6) {
GST_DEBUG_OBJECT (ac3parse, "undefined bsid=%d", bsid); GST_DEBUG_OBJECT (ac3parse, "undefined bsid=%d", bsid);
} }
@ -287,24 +293,35 @@ gst_ac3_parse_frame_header_ac3 (GstAc3Parse * ac3parse, GstBuffer * buf,
if (sid) if (sid)
*sid = 0; *sid = 0;
return TRUE; ret = TRUE;
cleanup:
gst_buffer_unmap (buf, data, size);
return ret;
} }
static gboolean static gboolean
gst_ac3_parse_frame_header_eac3 (GstAc3Parse * ac3parse, GstBuffer * buf, gst_ac3_parse_frame_header_eac3 (GstAc3Parse * ac3parse, GstBuffer * buf,
guint * frame_size, guint * rate, guint * chans, guint * blks, guint * sid) guint * frame_size, guint * rate, guint * chans, guint * blks, guint * sid)
{ {
GstBitReader bits = GST_BIT_READER_INIT_FROM_BUFFER (buf); GstBitReader bits;
gpointer data;
gsize size;
guint16 frmsiz, sample_rate, blocks; guint16 frmsiz, sample_rate, blocks;
guint8 strmtyp, fscod, fscod2, acmod, lfe_on, strmid, numblkscod; guint8 strmtyp, fscod, fscod2, acmod, lfe_on, strmid, numblkscod;
gboolean ret = FALSE;
GST_LOG_OBJECT (ac3parse, "parsing e-ac3"); GST_LOG_OBJECT (ac3parse, "parsing e-ac3");
data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
gst_bit_reader_init (&bits, data, size);
gst_bit_reader_skip_unchecked (&bits, 16); gst_bit_reader_skip_unchecked (&bits, 16);
strmtyp = gst_bit_reader_get_bits_uint8_unchecked (&bits, 2); /* strmtyp */ strmtyp = gst_bit_reader_get_bits_uint8_unchecked (&bits, 2); /* strmtyp */
if (G_UNLIKELY (strmtyp == 3)) { if (G_UNLIKELY (strmtyp == 3)) {
GST_DEBUG_OBJECT (ac3parse, "bad strmtyp %d", strmtyp); GST_DEBUG_OBJECT (ac3parse, "bad strmtyp %d", strmtyp);
return FALSE; goto cleanup;
} }
strmid = gst_bit_reader_get_bits_uint8_unchecked (&bits, 3); /* substreamid */ strmid = gst_bit_reader_get_bits_uint8_unchecked (&bits, 3); /* substreamid */
@ -314,7 +331,7 @@ gst_ac3_parse_frame_header_eac3 (GstAc3Parse * ac3parse, GstBuffer * buf,
fscod2 = gst_bit_reader_get_bits_uint8_unchecked (&bits, 2); /* fscod2 */ fscod2 = gst_bit_reader_get_bits_uint8_unchecked (&bits, 2); /* fscod2 */
if (G_UNLIKELY (fscod2 == 3)) { if (G_UNLIKELY (fscod2 == 3)) {
GST_DEBUG_OBJECT (ac3parse, "invalid fscod2"); GST_DEBUG_OBJECT (ac3parse, "invalid fscod2");
return FALSE; goto cleanup;
} }
sample_rate = fscod_rates[fscod2] / 2; sample_rate = fscod_rates[fscod2] / 2;
blocks = 6; blocks = 6;
@ -340,7 +357,12 @@ gst_ac3_parse_frame_header_eac3 (GstAc3Parse * ac3parse, GstBuffer * buf,
if (sid) if (sid)
*sid = (strmtyp & 0x1) << 3 | strmid; *sid = (strmtyp & 0x1) << 3 | strmid;
return TRUE; ret = TRUE;
cleanup:
gst_buffer_unmap (buf, data, size);
return ret;
} }
static gboolean static gboolean
@ -348,35 +370,49 @@ gst_ac3_parse_frame_header (GstAc3Parse * parse, GstBuffer * buf,
guint * framesize, guint * rate, guint * chans, guint * blocks, guint * framesize, guint * rate, guint * chans, guint * blocks,
guint * sid, gboolean * eac) guint * sid, gboolean * eac)
{ {
GstBitReader bits = GST_BIT_READER_INIT_FROM_BUFFER (buf); GstBitReader bits;
guint16 sync; guint16 sync;
guint8 bsid; guint8 bsid;
gpointer data;
gsize size;
gboolean ret = FALSE;
GST_MEMDUMP_OBJECT (parse, "AC3 frame sync", GST_BUFFER_DATA (buf), 16); data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
gst_bit_reader_init (&bits, data, size);
GST_MEMDUMP_OBJECT (parse, "AC3 frame sync", data, MIN (size, 16));
sync = gst_bit_reader_get_bits_uint16_unchecked (&bits, 16); sync = gst_bit_reader_get_bits_uint16_unchecked (&bits, 16);
gst_bit_reader_skip_unchecked (&bits, 16 + 8); gst_bit_reader_skip_unchecked (&bits, 16 + 8);
bsid = gst_bit_reader_peek_bits_uint8_unchecked (&bits, 5); bsid = gst_bit_reader_peek_bits_uint8_unchecked (&bits, 5);
if (G_UNLIKELY (sync != 0x0b77)) if (G_UNLIKELY (sync != 0x0b77))
return FALSE; goto cleanup;
GST_LOG_OBJECT (parse, "bsid = %d", bsid); GST_LOG_OBJECT (parse, "bsid = %d", bsid);
if (bsid <= 10) { if (bsid <= 10) {
if (eac) if (eac)
*eac = FALSE; *eac = FALSE;
return gst_ac3_parse_frame_header_ac3 (parse, buf, framesize, rate, chans, ret = gst_ac3_parse_frame_header_ac3 (parse, buf, framesize, rate, chans,
blocks, sid); blocks, sid);
} else if (bsid <= 16) { goto cleanup;
}
if (bsid <= 16) {
if (eac) if (eac)
*eac = TRUE; *eac = TRUE;
return gst_ac3_parse_frame_header_eac3 (parse, buf, framesize, rate, chans, ret = gst_ac3_parse_frame_header_eac3 (parse, buf, framesize, rate, chans,
blocks, sid); blocks, sid);
} else { goto cleanup;
GST_DEBUG_OBJECT (parse, "unexpected bsid %d", bsid);
return FALSE;
} }
GST_DEBUG_OBJECT (parse, "unexpected bsid %d", bsid);
cleanup:
gst_buffer_unmap (buf, data, size);
return ret;
} }
static gboolean static gboolean
@ -385,35 +421,40 @@ gst_ac3_parse_check_valid_frame (GstBaseParse * parse,
{ {
GstAc3Parse *ac3parse = GST_AC3_PARSE (parse); GstAc3Parse *ac3parse = GST_AC3_PARSE (parse);
GstBuffer *buf = frame->buffer; GstBuffer *buf = frame->buffer;
GstByteReader reader = GST_BYTE_READER_INIT_FROM_BUFFER (buf); GstByteReader reader;
gint off; gint off;
gboolean lost_sync, draining; gboolean lost_sync, draining;
gpointer data;
gsize size;
gboolean ret = FALSE;
if (G_UNLIKELY (GST_BUFFER_SIZE (buf) < 6)) data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
return FALSE;
if (G_UNLIKELY (size < 6))
goto cleanup;
off = gst_byte_reader_masked_scan_uint32 (&reader, 0xffff0000, 0x0b770000, off = gst_byte_reader_masked_scan_uint32 (&reader, 0xffff0000, 0x0b770000,
0, GST_BUFFER_SIZE (buf)); 0, size);
GST_LOG_OBJECT (parse, "possible sync at buffer offset %d", off); GST_LOG_OBJECT (parse, "possible sync at buffer offset %d", off);
/* didn't find anything that looks like a sync word, skip */ /* didn't find anything that looks like a sync word, skip */
if (off < 0) { if (off < 0) {
*skipsize = GST_BUFFER_SIZE (buf) - 3; *skipsize = size - 3;
return FALSE; goto cleanup;
} }
/* possible frame header, but not at offset 0? skip bytes before sync */ /* possible frame header, but not at offset 0? skip bytes before sync */
if (off > 0) { if (off > 0) {
*skipsize = off; *skipsize = off;
return FALSE; goto cleanup;
} }
/* make sure the values in the frame header look sane */ /* make sure the values in the frame header look sane */
if (!gst_ac3_parse_frame_header (ac3parse, buf, framesize, NULL, NULL, if (!gst_ac3_parse_frame_header (ac3parse, buf, framesize, NULL, NULL,
NULL, NULL, NULL)) { NULL, NULL, NULL)) {
*skipsize = off + 2; *skipsize = off + 2;
return FALSE; goto cleanup;
} }
GST_LOG_OBJECT (parse, "got frame"); GST_LOG_OBJECT (parse, "got frame");
@ -431,12 +472,12 @@ gst_ac3_parse_check_valid_frame (GstBaseParse * parse,
GST_DEBUG_OBJECT (ac3parse, "... but not sufficient data"); GST_DEBUG_OBJECT (ac3parse, "... but not sufficient data");
gst_base_parse_set_min_frame_size (parse, *framesize + 6); gst_base_parse_set_min_frame_size (parse, *framesize + 6);
*skipsize = 0; *skipsize = 0;
return FALSE; goto cleanup;
} else { } else {
if (word != 0x0b77) { if (word != 0x0b77) {
GST_DEBUG_OBJECT (ac3parse, "0x%x not OK", word); GST_DEBUG_OBJECT (ac3parse, "0x%x not OK", word);
*skipsize = off + 2; *skipsize = off + 2;
return FALSE; goto cleanup;
} else { } else {
/* ok, got sync now, let's assume constant frame size */ /* ok, got sync now, let's assume constant frame size */
gst_base_parse_set_min_frame_size (parse, *framesize); gst_base_parse_set_min_frame_size (parse, *framesize);
@ -444,7 +485,12 @@ gst_ac3_parse_check_valid_frame (GstBaseParse * parse,
} }
} }
return TRUE; ret = TRUE;
cleanup:
gst_buffer_unmap (buf, data, size);
return ret;
} }
static GstFlowReturn static GstFlowReturn
@ -480,7 +526,6 @@ gst_ac3_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
GstCaps *caps = gst_caps_new_simple (eac ? "audio/x-eac3" : "audio/x-ac3", GstCaps *caps = gst_caps_new_simple (eac ? "audio/x-eac3" : "audio/x-ac3",
"framed", G_TYPE_BOOLEAN, TRUE, "rate", G_TYPE_INT, rate, "framed", G_TYPE_BOOLEAN, TRUE, "rate", G_TYPE_INT, rate,
"channels", G_TYPE_INT, chans, NULL); "channels", G_TYPE_INT, chans, NULL);
gst_buffer_set_caps (buf, caps);
gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), caps); gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), caps);
gst_caps_unref (caps); gst_caps_unref (caps);

View file

@ -78,27 +78,14 @@ static gboolean gst_dca_parse_check_valid_frame (GstBaseParse * parse,
static GstFlowReturn gst_dca_parse_parse_frame (GstBaseParse * parse, static GstFlowReturn gst_dca_parse_parse_frame (GstBaseParse * parse,
GstBaseParseFrame * frame); GstBaseParseFrame * frame);
GST_BOILERPLATE (GstDcaParse, gst_dca_parse, GstBaseParse, GST_TYPE_BASE_PARSE); #define gst_dca_parse_parent_class parent_class
G_DEFINE_TYPE (GstDcaParse, gst_dca_parse, GST_TYPE_BASE_PARSE);
static void
gst_dca_parse_base_init (gpointer klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&sink_template));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&src_template));
gst_element_class_set_details_simple (element_class,
"DTS Coherent Acoustics audio stream parser", "Codec/Parser/Audio",
"DCA parser", "Tim-Philipp Müller <tim centricular net>");
}
static void static void
gst_dca_parse_class_init (GstDcaParseClass * klass) gst_dca_parse_class_init (GstDcaParseClass * klass)
{ {
GstBaseParseClass *parse_class = GST_BASE_PARSE_CLASS (klass); GstBaseParseClass *parse_class = GST_BASE_PARSE_CLASS (klass);
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass);
GST_DEBUG_CATEGORY_INIT (dca_parse_debug, "dcaparse", 0, GST_DEBUG_CATEGORY_INIT (dca_parse_debug, "dcaparse", 0,
@ -111,6 +98,15 @@ gst_dca_parse_class_init (GstDcaParseClass * klass)
parse_class->check_valid_frame = parse_class->check_valid_frame =
GST_DEBUG_FUNCPTR (gst_dca_parse_check_valid_frame); GST_DEBUG_FUNCPTR (gst_dca_parse_check_valid_frame);
parse_class->parse_frame = GST_DEBUG_FUNCPTR (gst_dca_parse_parse_frame); parse_class->parse_frame = GST_DEBUG_FUNCPTR (gst_dca_parse_parse_frame);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&sink_template));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&src_template));
gst_element_class_set_details_simple (element_class,
"DTS Coherent Acoustics audio stream parser", "Codec/Parser/Audio",
"DCA parser", "Tim-Philipp Müller <tim centricular net>");
} }
static void static void
@ -126,7 +122,7 @@ gst_dca_parse_reset (GstDcaParse * dcaparse)
} }
static void static void
gst_dca_parse_init (GstDcaParse * dcaparse, GstDcaParseClass * klass) gst_dca_parse_init (GstDcaParse * dcaparse)
{ {
gst_base_parse_set_min_frame_size (GST_BASE_PARSE (dcaparse), gst_base_parse_set_min_frame_size (GST_BASE_PARSE (dcaparse),
DCA_MIN_FRAMESIZE); DCA_MIN_FRAMESIZE);
@ -255,7 +251,7 @@ gst_dca_parse_parse_header (GstDcaParse * dcaparse,
static gint static gint
gst_dca_parse_find_sync (GstDcaParse * dcaparse, GstByteReader * reader, gst_dca_parse_find_sync (GstDcaParse * dcaparse, GstByteReader * reader,
const GstBuffer * buf, guint32 * sync) gsize bufsize, guint32 * sync)
{ {
guint32 best_sync = 0; guint32 best_sync = 0;
guint best_offset = G_MAXUINT; guint best_offset = G_MAXUINT;
@ -265,7 +261,7 @@ gst_dca_parse_find_sync (GstDcaParse * dcaparse, GstByteReader * reader,
/* Raw little endian */ /* Raw little endian */
off = gst_byte_reader_masked_scan_uint32 (reader, 0xffffffff, 0xfe7f0180, off = gst_byte_reader_masked_scan_uint32 (reader, 0xffffffff, 0xfe7f0180,
0, GST_BUFFER_SIZE (buf)); 0, bufsize);
if (off >= 0 && off < best_offset) { if (off >= 0 && off < best_offset) {
best_offset = off; best_offset = off;
best_sync = 0xfe7f0180; best_sync = 0xfe7f0180;
@ -273,7 +269,7 @@ gst_dca_parse_find_sync (GstDcaParse * dcaparse, GstByteReader * reader,
/* Raw big endian */ /* Raw big endian */
off = gst_byte_reader_masked_scan_uint32 (reader, 0xffffffff, 0x7ffe8001, off = gst_byte_reader_masked_scan_uint32 (reader, 0xffffffff, 0x7ffe8001,
0, GST_BUFFER_SIZE (buf)); 0, bufsize);
if (off >= 0 && off < best_offset) { if (off >= 0 && off < best_offset) {
best_offset = off; best_offset = off;
best_sync = 0x7ffe8001; best_sync = 0x7ffe8001;
@ -284,7 +280,7 @@ gst_dca_parse_find_sync (GstDcaParse * dcaparse, GstByteReader * reader,
/* 14-bit little endian */ /* 14-bit little endian */
off = gst_byte_reader_masked_scan_uint32 (reader, 0xffffffff, 0xff1f00e8, off = gst_byte_reader_masked_scan_uint32 (reader, 0xffffffff, 0xff1f00e8,
0, GST_BUFFER_SIZE (buf)); 0, bufsize);
if (off >= 0 && off < best_offset) { if (off >= 0 && off < best_offset) {
best_offset = off; best_offset = off;
best_sync = 0xff1f00e8; best_sync = 0xff1f00e8;
@ -292,7 +288,7 @@ gst_dca_parse_find_sync (GstDcaParse * dcaparse, GstByteReader * reader,
/* 14-bit big endian */ /* 14-bit big endian */
off = gst_byte_reader_masked_scan_uint32 (reader, 0xffffffff, 0x1fffe800, off = gst_byte_reader_masked_scan_uint32 (reader, 0xffffffff, 0x1fffe800,
0, GST_BUFFER_SIZE (buf)); 0, bufsize);
if (off >= 0 && off < best_offset) { if (off >= 0 && off < best_offset) {
best_offset = off; best_offset = off;
best_sync = 0x1fffe800; best_sync = 0x1fffe800;
@ -311,33 +307,40 @@ gst_dca_parse_check_valid_frame (GstBaseParse * parse,
{ {
GstDcaParse *dcaparse = GST_DCA_PARSE (parse); GstDcaParse *dcaparse = GST_DCA_PARSE (parse);
GstBuffer *buf = frame->buffer; GstBuffer *buf = frame->buffer;
GstByteReader r = GST_BYTE_READER_INIT_FROM_BUFFER (buf); GstByteReader r;
gboolean parser_draining; gboolean parser_draining;
gboolean parser_in_sync; gboolean parser_in_sync;
gboolean terminator; gboolean terminator;
guint32 sync = 0; guint32 sync = 0;
guint size, rate, chans, num_blocks, samples_per_block; guint size, rate, chans, num_blocks, samples_per_block;
gint off = -1; gint off = -1;
gpointer data;
gsize bufsize;
gboolean ret = FALSE;
if (G_UNLIKELY (GST_BUFFER_SIZE (buf) < 16)) data = gst_buffer_map (buf, &bufsize, NULL, GST_MAP_READ);
return FALSE;
if (G_UNLIKELY (bufsize < 16))
goto cleanup;
parser_in_sync = !GST_BASE_PARSE_LOST_SYNC (parse); parser_in_sync = !GST_BASE_PARSE_LOST_SYNC (parse);
gst_byte_reader_init (&r, data, bufsize);
if (G_LIKELY (parser_in_sync && dcaparse->last_sync != 0)) { if (G_LIKELY (parser_in_sync && dcaparse->last_sync != 0)) {
off = gst_byte_reader_masked_scan_uint32 (&r, 0xffffffff, off = gst_byte_reader_masked_scan_uint32 (&r, 0xffffffff,
dcaparse->last_sync, 0, GST_BUFFER_SIZE (buf)); dcaparse->last_sync, 0, size);
} }
if (G_UNLIKELY (off < 0)) { if (G_UNLIKELY (off < 0)) {
off = gst_dca_parse_find_sync (dcaparse, &r, buf, &sync); off = gst_dca_parse_find_sync (dcaparse, &r, bufsize, &sync);
} }
/* didn't find anything that looks like a sync word, skip */ /* didn't find anything that looks like a sync word, skip */
if (off < 0) { if (off < 0) {
*skipsize = GST_BUFFER_SIZE (buf) - 3; *skipsize = bufsize - 3;
GST_DEBUG_OBJECT (dcaparse, "no sync, skipping %d bytes", *skipsize); GST_DEBUG_OBJECT (dcaparse, "no sync, skipping %d bytes", *skipsize);
return FALSE; goto cleanup;
} }
GST_LOG_OBJECT (parse, "possible sync %08x at buffer offset %d", sync, off); GST_LOG_OBJECT (parse, "possible sync %08x at buffer offset %d", sync, off);
@ -345,14 +348,14 @@ gst_dca_parse_check_valid_frame (GstBaseParse * parse,
/* possible frame header, but not at offset 0? skip bytes before sync */ /* possible frame header, but not at offset 0? skip bytes before sync */
if (off > 0) { if (off > 0) {
*skipsize = off; *skipsize = off;
return FALSE; goto cleanup;
} }
/* make sure the values in the frame header look sane */ /* make sure the values in the frame header look sane */
if (!gst_dca_parse_parse_header (dcaparse, &r, &size, &rate, &chans, NULL, if (!gst_dca_parse_parse_header (dcaparse, &r, &size, &rate, &chans, NULL,
NULL, &num_blocks, &samples_per_block, &terminator)) { NULL, &num_blocks, &samples_per_block, &terminator)) {
*skipsize = 4; *skipsize = 4;
return FALSE; goto cleanup;
} }
GST_LOG_OBJECT (parse, "got frame, sync %08x, size %u, rate %d, channels %d", GST_LOG_OBJECT (parse, "got frame, sync %08x, size %u, rate %d, channels %d",
@ -367,19 +370,19 @@ gst_dca_parse_check_valid_frame (GstBaseParse * parse,
if (!parser_in_sync && !parser_draining) { if (!parser_in_sync && !parser_draining) {
/* check for second frame to be sure */ /* check for second frame to be sure */
GST_DEBUG_OBJECT (dcaparse, "resyncing; checking next frame syncword"); GST_DEBUG_OBJECT (dcaparse, "resyncing; checking next frame syncword");
if (GST_BUFFER_SIZE (buf) >= (size + 16)) { if (bufsize >= (size + 16)) {
guint s2, r2, c2, n2, s3; guint s2, r2, c2, n2, s3;
gboolean t; gboolean t;
GST_MEMDUMP ("buf", GST_BUFFER_DATA (buf), size + 16); GST_MEMDUMP ("buf", data, size + 16);
gst_byte_reader_init_from_buffer (&r, buf); gst_byte_reader_init (&r, data, bufsize);
gst_byte_reader_skip_unchecked (&r, size); gst_byte_reader_skip_unchecked (&r, size);
if (!gst_dca_parse_parse_header (dcaparse, &r, &s2, &r2, &c2, NULL, NULL, if (!gst_dca_parse_parse_header (dcaparse, &r, &s2, &r2, &c2, NULL, NULL,
&n2, &s3, &t)) { &n2, &s3, &t)) {
GST_DEBUG_OBJECT (dcaparse, "didn't find second syncword"); GST_DEBUG_OBJECT (dcaparse, "didn't find second syncword");
*skipsize = 4; *skipsize = 4;
return FALSE; goto cleanup;
} }
/* ok, got sync now, let's assume constant frame size */ /* ok, got sync now, let's assume constant frame size */
@ -388,13 +391,17 @@ gst_dca_parse_check_valid_frame (GstBaseParse * parse,
/* FIXME: baseparse always seems to hand us buffers of min_frame_size /* FIXME: baseparse always seems to hand us buffers of min_frame_size
* bytes, which is unhelpful here */ * bytes, which is unhelpful here */
GST_LOG_OBJECT (dcaparse, "next sync out of reach (%u < %u)", GST_LOG_OBJECT (dcaparse, "next sync out of reach (%u < %u)",
GST_BUFFER_SIZE (buf), size + 16); bufsize, size + 16);
/* *skipsize = 0; */ /* *skipsize = 0; */
/* return FALSE; */ /* return FALSE; */
} }
} }
return TRUE; ret = TRUE;
cleanup:
gst_buffer_unmap (buf, data, bufsize);
return ret;
} }
static GstFlowReturn static GstFlowReturn
@ -402,10 +409,15 @@ gst_dca_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
{ {
GstDcaParse *dcaparse = GST_DCA_PARSE (parse); GstDcaParse *dcaparse = GST_DCA_PARSE (parse);
GstBuffer *buf = frame->buffer; GstBuffer *buf = frame->buffer;
GstByteReader r = GST_BYTE_READER_INIT_FROM_BUFFER (buf); GstByteReader r;
guint size, rate, chans, depth, block_size, num_blocks, samples_per_block; guint size, rate, chans, depth, block_size, num_blocks, samples_per_block;
gint endianness; gint endianness;
gboolean terminator; gboolean terminator;
gpointer data;
gsize bufsize;
data = gst_buffer_map (buf, &bufsize, NULL, GST_MAP_READ);
gst_byte_reader_init (&r, data, bufsize);
if (!gst_dca_parse_parse_header (dcaparse, &r, &size, &rate, &chans, &depth, if (!gst_dca_parse_parse_header (dcaparse, &r, &size, &rate, &chans, &depth,
&endianness, &num_blocks, &samples_per_block, &terminator)) &endianness, &num_blocks, &samples_per_block, &terminator))
@ -425,7 +437,6 @@ gst_dca_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
"endianness", G_TYPE_INT, endianness, "depth", G_TYPE_INT, depth, "endianness", G_TYPE_INT, endianness, "depth", G_TYPE_INT, depth,
"block-size", G_TYPE_INT, block_size, "frame-size", G_TYPE_INT, size, "block-size", G_TYPE_INT, block_size, "frame-size", G_TYPE_INT, size,
NULL); NULL);
gst_buffer_set_caps (buf, caps);
gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), caps); gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), caps);
gst_caps_unref (caps); gst_caps_unref (caps);
@ -439,6 +450,7 @@ gst_dca_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
gst_base_parse_set_frame_rate (parse, rate, block_size, 0, 0); gst_base_parse_set_frame_rate (parse, rate, block_size, 0, 0);
} }
gst_buffer_unmap (buf, data, bufsize);
return GST_FLOW_OK; return GST_FLOW_OK;
/* ERRORS */ /* ERRORS */
@ -446,6 +458,7 @@ broken_header:
{ {
/* this really shouldn't ever happen */ /* this really shouldn't ever happen */
GST_ELEMENT_ERROR (parse, STREAM, DECODE, (NULL), (NULL)); GST_ELEMENT_ERROR (parse, STREAM, DECODE, (NULL), (NULL));
gst_buffer_unmap (buf, data, bufsize);
return GST_FLOW_ERROR; return GST_FLOW_ERROR;
} }
} }

View file

@ -202,34 +202,19 @@ static gboolean gst_flac_parse_convert (GstBaseParse * parse,
GstFormat src_format, gint64 src_value, GstFormat dest_format, GstFormat src_format, gint64 src_value, GstFormat dest_format,
gint64 * dest_value); gint64 * dest_value);
GST_BOILERPLATE (GstFlacParse, gst_flac_parse, GstBaseParse, #define gst_flac_parse_parent_class parent_class
GST_TYPE_BASE_PARSE); G_DEFINE_TYPE (GstFlacParse, gst_flac_parse, GST_TYPE_BASE_PARSE);
static void
gst_flac_parse_base_init (gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&src_factory));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&sink_factory));
gst_element_class_set_details_simple (element_class, "FLAC audio parser",
"Codec/Parser/Audio",
"Parses audio with the FLAC lossless audio codec",
"Sebastian Dröge <sebastian.droege@collabora.co.uk>");
GST_DEBUG_CATEGORY_INIT (flacparse_debug, "flacparse", 0,
"Flac parser element");
}
static void static void
gst_flac_parse_class_init (GstFlacParseClass * klass) gst_flac_parse_class_init (GstFlacParseClass * klass)
{ {
GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstBaseParseClass *baseparse_class = GST_BASE_PARSE_CLASS (klass); GstBaseParseClass *baseparse_class = GST_BASE_PARSE_CLASS (klass);
GST_DEBUG_CATEGORY_INIT (flacparse_debug, "flacparse", 0,
"Flac parser element");
gobject_class->finalize = gst_flac_parse_finalize; gobject_class->finalize = gst_flac_parse_finalize;
gobject_class->set_property = gst_flac_parse_set_property; gobject_class->set_property = gst_flac_parse_set_property;
gobject_class->get_property = gst_flac_parse_get_property; gobject_class->get_property = gst_flac_parse_get_property;
@ -248,10 +233,20 @@ gst_flac_parse_class_init (GstFlacParseClass * klass)
baseparse_class->pre_push_frame = baseparse_class->pre_push_frame =
GST_DEBUG_FUNCPTR (gst_flac_parse_pre_push_frame); GST_DEBUG_FUNCPTR (gst_flac_parse_pre_push_frame);
baseparse_class->convert = GST_DEBUG_FUNCPTR (gst_flac_parse_convert); baseparse_class->convert = GST_DEBUG_FUNCPTR (gst_flac_parse_convert);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&src_factory));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&sink_factory));
gst_element_class_set_details_simple (element_class, "FLAC audio parser",
"Codec/Parser/Audio",
"Parses audio with the FLAC lossless audio codec",
"Sebastian Dröge <sebastian.droege@collabora.co.uk>");
} }
static void static void
gst_flac_parse_init (GstFlacParse * flacparse, GstFlacParseClass * klass) gst_flac_parse_init (GstFlacParse * flacparse)
{ {
flacparse->check_frame_checksums = DEFAULT_CHECK_FRAME_CHECKSUMS; flacparse->check_frame_checksums = DEFAULT_CHECK_FRAME_CHECKSUMS;
} }
@ -571,15 +566,16 @@ gst_flac_parse_frame_is_valid (GstFlacParse * flacparse,
GstBaseParseFrame * frame, guint * ret) GstBaseParseFrame * frame, guint * ret)
{ {
GstBuffer *buffer; GstBuffer *buffer;
const guint8 *data; guint8 *data;
guint max, size, remaining; gsize size;
guint max, remaining;
guint i, search_start, search_end; guint i, search_start, search_end;
FrameHeaderCheckReturn header_ret; FrameHeaderCheckReturn header_ret;
guint16 block_size; guint16 block_size;
gboolean result = FALSE;
buffer = frame->buffer; buffer = frame->buffer;
data = GST_BUFFER_DATA (buffer); data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
size = GST_BUFFER_SIZE (buffer);
if (size <= flacparse->min_framesize) if (size <= flacparse->min_framesize)
goto need_more; goto need_more;
@ -589,10 +585,10 @@ gst_flac_parse_frame_is_valid (GstFlacParse * flacparse,
&block_size); &block_size);
if (header_ret == FRAME_HEADER_INVALID) { if (header_ret == FRAME_HEADER_INVALID) {
*ret = 0; *ret = 0;
return FALSE; goto cleanup;
} else if (header_ret == FRAME_HEADER_MORE_DATA) {
goto need_more;
} }
if (header_ret == FRAME_HEADER_MORE_DATA)
goto need_more;
/* mind unknown framesize */ /* mind unknown framesize */
search_start = MAX (2, flacparse->min_framesize); search_start = MAX (2, flacparse->min_framesize);
@ -619,7 +615,8 @@ gst_flac_parse_frame_is_valid (GstFlacParse * flacparse,
} }
*ret = i; *ret = i;
flacparse->block_size = block_size; flacparse->block_size = block_size;
return TRUE; result = TRUE;
goto cleanup;
} else if (header_ret == FRAME_HEADER_MORE_DATA) { } else if (header_ret == FRAME_HEADER_MORE_DATA) {
goto need_more; goto need_more;
} }
@ -635,12 +632,14 @@ gst_flac_parse_frame_is_valid (GstFlacParse * flacparse,
if (actual_crc == expected_crc) { if (actual_crc == expected_crc) {
*ret = size; *ret = size;
flacparse->block_size = block_size; flacparse->block_size = block_size;
return TRUE; result = TRUE;
goto cleanup;
} }
} else { } else {
*ret = size; *ret = size;
flacparse->block_size = block_size; flacparse->block_size = block_size;
return TRUE; result = TRUE;
goto cleanup;
} }
} }
@ -649,7 +648,11 @@ need_more:
if (max == 16) if (max == 16)
max = 1 << 24; max = 1 << 24;
*ret = MIN (size + 4096, max); *ret = MIN (size + 4096, max);
return FALSE; result = TRUE;
cleanup:
gst_buffer_unmap (buffer, data, size);
return result;
} }
static gboolean static gboolean
@ -658,100 +661,124 @@ gst_flac_parse_check_valid_frame (GstBaseParse * parse,
{ {
GstFlacParse *flacparse = GST_FLAC_PARSE (parse); GstFlacParse *flacparse = GST_FLAC_PARSE (parse);
GstBuffer *buffer = frame->buffer; GstBuffer *buffer = frame->buffer;
const guint8 *data = GST_BUFFER_DATA (buffer); guint8 *data;
gsize bufsize;
gboolean result = TRUE;
if (G_UNLIKELY (GST_BUFFER_SIZE (buffer) < 4)) data = gst_buffer_map (buffer, &bufsize, NULL, GST_MAP_READ);
return FALSE;
if (G_UNLIKELY (bufsize < 4)) {
result = FALSE;
goto cleanup;
}
if (flacparse->state == GST_FLAC_PARSE_STATE_INIT) { if (flacparse->state == GST_FLAC_PARSE_STATE_INIT) {
if (memcmp (GST_BUFFER_DATA (buffer), "fLaC", 4) == 0) { if (memcmp (data, "fLaC", 4) == 0) {
GST_DEBUG_OBJECT (flacparse, "fLaC marker found"); GST_DEBUG_OBJECT (flacparse, "fLaC marker found");
*framesize = 4; *framesize = 4;
return TRUE; goto cleanup;
} else if (data[0] == 0xff && (data[1] >> 2) == 0x3e) { }
if (data[0] == 0xff && (data[1] >> 2) == 0x3e) {
GST_DEBUG_OBJECT (flacparse, "Found headerless FLAC"); GST_DEBUG_OBJECT (flacparse, "Found headerless FLAC");
/* Minimal size of a frame header */ /* Minimal size of a frame header */
gst_base_parse_set_min_frame_size (GST_BASE_PARSE (flacparse), 9); gst_base_parse_set_min_frame_size (GST_BASE_PARSE (flacparse), 9);
flacparse->state = GST_FLAC_PARSE_STATE_GENERATE_HEADERS; flacparse->state = GST_FLAC_PARSE_STATE_GENERATE_HEADERS;
*skipsize = 0; *skipsize = 0;
return FALSE; result = FALSE;
} else { goto cleanup;
GST_DEBUG_OBJECT (flacparse, "fLaC marker not found");
return FALSE;
} }
} else if (flacparse->state == GST_FLAC_PARSE_STATE_HEADERS) { GST_DEBUG_OBJECT (flacparse, "fLaC marker not found");
result = FALSE;
goto cleanup;
}
if (flacparse->state == GST_FLAC_PARSE_STATE_HEADERS) {
guint size = 4 + ((data[1] << 16) | (data[2] << 8) | (data[3])); guint size = 4 + ((data[1] << 16) | (data[2] << 8) | (data[3]));
GST_DEBUG_OBJECT (flacparse, "Found metadata block of size %u", size); GST_DEBUG_OBJECT (flacparse, "Found metadata block of size %u", size);
*framesize = size; *framesize = size;
return TRUE; goto cleanup;
} else { }
if ((GST_READ_UINT16_BE (data) & 0xfffe) == 0xfff8) {
gboolean ret;
guint next;
flacparse->offset = GST_BUFFER_OFFSET (buffer); if ((GST_READ_UINT16_BE (data) & 0xfffe) == 0xfff8) {
flacparse->blocking_strategy = 0; gboolean ret;
flacparse->block_size = 0; guint next;
flacparse->sample_number = 0;
GST_DEBUG_OBJECT (flacparse, "Found sync code"); flacparse->offset = GST_BUFFER_OFFSET (buffer);
ret = gst_flac_parse_frame_is_valid (flacparse, frame, &next); flacparse->blocking_strategy = 0;
if (ret) { flacparse->block_size = 0;
*framesize = next; flacparse->sample_number = 0;
return TRUE;
} else {
/* If we're at EOS and the frame was not valid, drop it! */
if (G_UNLIKELY (GST_BASE_PARSE_DRAINING (flacparse))) {
GST_WARNING_OBJECT (flacparse, "EOS");
return FALSE;
}
if (next == 0) { GST_DEBUG_OBJECT (flacparse, "Found sync code");
} else if (next > GST_BUFFER_SIZE (buffer)) { ret = gst_flac_parse_frame_is_valid (flacparse, frame, &next);
GST_DEBUG_OBJECT (flacparse, "Requesting %u bytes", next); if (ret) {
*skipsize = 0; *framesize = next;
gst_base_parse_set_min_frame_size (parse, next); goto cleanup;
return FALSE;
} else {
GST_ERROR_OBJECT (flacparse,
"Giving up on invalid frame (%d bytes)",
GST_BUFFER_SIZE (buffer));
return FALSE;
}
}
} else { } else {
GstByteReader reader = GST_BYTE_READER_INIT_FROM_BUFFER (buffer); /* If we're at EOS and the frame was not valid, drop it! */
gint off; if (G_UNLIKELY (GST_BASE_PARSE_DRAINING (flacparse))) {
GST_WARNING_OBJECT (flacparse, "EOS");
off = result = FALSE;
gst_byte_reader_masked_scan_uint32 (&reader, 0xfffc0000, 0xfff80000, goto cleanup;
0, GST_BUFFER_SIZE (buffer));
if (off > 0) {
GST_DEBUG_OBJECT (parse, "Possible sync at buffer offset %d", off);
*skipsize = off;
return FALSE;
} else {
GST_DEBUG_OBJECT (flacparse, "Sync code not found");
*skipsize = GST_BUFFER_SIZE (buffer) - 3;
return FALSE;
} }
if (next == 0) {
} else if (next > bufsize) {
GST_DEBUG_OBJECT (flacparse, "Requesting %u bytes", next);
*skipsize = 0;
gst_base_parse_set_min_frame_size (parse, next);
result = FALSE;
goto cleanup;
} else {
GST_ERROR_OBJECT (flacparse,
"Giving up on invalid frame (%d bytes)", bufsize);
result = FALSE;
goto cleanup;
}
}
} else {
GstByteReader reader;
gint off;
gst_byte_reader_init (&reader, data, bufsize);
off =
gst_byte_reader_masked_scan_uint32 (&reader, 0xfffc0000, 0xfff80000,
0, bufsize);
if (off > 0) {
GST_DEBUG_OBJECT (parse, "Possible sync at buffer offset %d", off);
*skipsize = off;
result = FALSE;
goto cleanup;
} else {
GST_DEBUG_OBJECT (flacparse, "Sync code not found");
*skipsize = bufsize - 3;
result = FALSE;
goto cleanup;
} }
} }
return FALSE; result = FALSE;
cleanup:
gst_buffer_unmap (buffer, data, bufsize);
return result;
} }
static gboolean static gboolean
gst_flac_parse_handle_streaminfo (GstFlacParse * flacparse, GstBuffer * buffer) gst_flac_parse_handle_streaminfo (GstFlacParse * flacparse, GstBuffer * buffer)
{ {
GstBitReader reader = GST_BIT_READER_INIT_FROM_BUFFER (buffer); GstBitReader reader;
guint8 *data;
gsize size;
if (GST_BUFFER_SIZE (buffer) != 4 + 34) { data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
gst_bit_reader_init (&reader, data, size);
if (size != 4 + 34) {
GST_ERROR_OBJECT (flacparse, "Invalid metablock size for STREAMINFO: %u", GST_ERROR_OBJECT (flacparse, "Invalid metablock size for STREAMINFO: %u",
GST_BUFFER_SIZE (buffer)); size);
return FALSE; goto failure;
} }
/* Skip metadata block header */ /* Skip metadata block header */
@ -780,7 +807,7 @@ gst_flac_parse_handle_streaminfo (GstFlacParse * flacparse, GstBuffer * buffer)
goto error; goto error;
if (flacparse->samplerate == 0) { if (flacparse->samplerate == 0) {
GST_ERROR_OBJECT (flacparse, "Invalid sample rate 0"); GST_ERROR_OBJECT (flacparse, "Invalid sample rate 0");
return FALSE; goto failure;
} }
if (!gst_bit_reader_get_bits_uint8 (&reader, &flacparse->channels, 3)) if (!gst_bit_reader_get_bits_uint8 (&reader, &flacparse->channels, 3))
@ -789,7 +816,7 @@ gst_flac_parse_handle_streaminfo (GstFlacParse * flacparse, GstBuffer * buffer)
if (flacparse->channels > 8) { if (flacparse->channels > 8) {
GST_ERROR_OBJECT (flacparse, "Invalid number of channels %u", GST_ERROR_OBJECT (flacparse, "Invalid number of channels %u",
flacparse->channels); flacparse->channels);
return FALSE; goto failure;
} }
if (!gst_bit_reader_get_bits_uint8 (&reader, &flacparse->bps, 5)) if (!gst_bit_reader_get_bits_uint8 (&reader, &flacparse->bps, 5))
@ -803,6 +830,8 @@ gst_flac_parse_handle_streaminfo (GstFlacParse * flacparse, GstBuffer * buffer)
GST_FORMAT_DEFAULT, flacparse->total_samples, 0); GST_FORMAT_DEFAULT, flacparse->total_samples, 0);
} }
gst_buffer_unmap (buffer, data, size);
GST_DEBUG_OBJECT (flacparse, "STREAMINFO:\n" GST_DEBUG_OBJECT (flacparse, "STREAMINFO:\n"
"\tmin/max blocksize: %u/%u,\n" "\tmin/max blocksize: %u/%u,\n"
"\tmin/max framesize: %u/%u,\n" "\tmin/max framesize: %u/%u,\n"
@ -819,6 +848,8 @@ gst_flac_parse_handle_streaminfo (GstFlacParse * flacparse, GstBuffer * buffer)
error: error:
GST_ERROR_OBJECT (flacparse, "Failed to read data"); GST_ERROR_OBJECT (flacparse, "Failed to read data");
failure:
gst_buffer_unmap (buffer, data, size);
return FALSE; return FALSE;
} }
@ -826,8 +857,13 @@ static gboolean
gst_flac_parse_handle_vorbiscomment (GstFlacParse * flacparse, gst_flac_parse_handle_vorbiscomment (GstFlacParse * flacparse,
GstBuffer * buffer) GstBuffer * buffer)
{ {
flacparse->tags = gst_tag_list_from_vorbiscomment_buffer (buffer, guint8 *data;
GST_BUFFER_DATA (buffer), 4, NULL); gsize size;
data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
flacparse->tags = gst_tag_list_from_vorbiscomment (data, size, data, 4, NULL);
gst_buffer_unmap (buffer, data, size);
if (flacparse->tags == NULL) { if (flacparse->tags == NULL) {
GST_ERROR_OBJECT (flacparse, "Invalid vorbiscomment block"); GST_ERROR_OBJECT (flacparse, "Invalid vorbiscomment block");
@ -842,11 +878,15 @@ gst_flac_parse_handle_vorbiscomment (GstFlacParse * flacparse,
static gboolean static gboolean
gst_flac_parse_handle_picture (GstFlacParse * flacparse, GstBuffer * buffer) gst_flac_parse_handle_picture (GstFlacParse * flacparse, GstBuffer * buffer)
{ {
GstByteReader reader = GST_BYTE_READER_INIT_FROM_BUFFER (buffer); GstByteReader reader;
const guint8 *data = GST_BUFFER_DATA (buffer); guint8 *data;
gsize bufsize;
guint32 img_len = 0, img_type = 0; guint32 img_len = 0, img_type = 0;
guint32 img_mimetype_len = 0, img_description_len = 0; guint32 img_mimetype_len = 0, img_description_len = 0;
data = gst_buffer_map (buffer, &bufsize, NULL, GST_MAP_READ);
gst_byte_reader_init (&reader, data, bufsize);
if (!gst_byte_reader_skip (&reader, 4)) if (!gst_byte_reader_skip (&reader, 4))
goto error; goto error;
@ -880,10 +920,12 @@ gst_flac_parse_handle_picture (GstFlacParse * flacparse, GstBuffer * buffer)
flacparse->tags = NULL; flacparse->tags = NULL;
} }
gst_buffer_unmap (buffer, data, bufsize);
return TRUE; return TRUE;
error: error:
GST_ERROR_OBJECT (flacparse, "Error reading data"); GST_ERROR_OBJECT (flacparse, "Error reading data");
gst_buffer_unmap (buffer, data, bufsize);
return FALSE; return FALSE;
} }
@ -904,6 +946,8 @@ gst_flac_parse_process_seektable (GstFlacParse * flacparse, gint64 boffset)
{ {
GstByteReader br; GstByteReader br;
gint64 offset = 0, samples = 0; gint64 offset = 0, samples = 0;
gpointer data;
gsize bufsize;
GST_DEBUG_OBJECT (flacparse, GST_DEBUG_OBJECT (flacparse,
"parsing seektable; base offset %" G_GINT64_FORMAT, boffset); "parsing seektable; base offset %" G_GINT64_FORMAT, boffset);
@ -911,7 +955,9 @@ gst_flac_parse_process_seektable (GstFlacParse * flacparse, gint64 boffset)
if (boffset <= 0) if (boffset <= 0)
goto done; goto done;
gst_byte_reader_init_from_buffer (&br, flacparse->seektable); data = gst_buffer_map (flacparse->seektable, &bufsize, NULL, GST_MAP_READ);
gst_byte_reader_init (&br, data, bufsize);
/* skip header */ /* skip header */
if (!gst_byte_reader_skip (&br, 4)) if (!gst_byte_reader_skip (&br, 4))
goto done; goto done;
@ -937,6 +983,7 @@ gst_flac_parse_process_seektable (GstFlacParse * flacparse, gint64 boffset)
} }
done: done:
gst_buffer_unmap (flacparse->seektable, data, bufsize);
gst_buffer_unref (flacparse->seektable); gst_buffer_unref (flacparse->seektable);
flacparse->seektable = NULL; flacparse->seektable = NULL;
} }
@ -978,8 +1025,10 @@ gst_flac_parse_handle_headers (GstFlacParse * flacparse)
for (l = flacparse->headers; l; l = l->next) { for (l = flacparse->headers; l; l = l->next) {
GstBuffer *header = l->data; GstBuffer *header = l->data;
const guint8 *data = GST_BUFFER_DATA (header); guint8 *data;
guint size = GST_BUFFER_SIZE (header); gsize size;
data = gst_buffer_map (header, &size, NULL, GST_MAP_READ);
GST_BUFFER_FLAG_SET (header, GST_BUFFER_FLAG_IN_CAPS); GST_BUFFER_FLAG_SET (header, GST_BUFFER_FLAG_IN_CAPS);
@ -990,6 +1039,8 @@ gst_flac_parse_handle_headers (GstFlacParse * flacparse)
} else if (size > 1 && (data[0] & 0x7f) == 4) { } else if (size > 1 && (data[0] & 0x7f) == 4) {
vorbiscomment = header; vorbiscomment = header;
} }
gst_buffer_unmap (header, data, size);
} }
if (marker == NULL || streaminfo == NULL || vorbiscomment == NULL) { if (marker == NULL || streaminfo == NULL || vorbiscomment == NULL) {
@ -1005,21 +1056,29 @@ gst_flac_parse_handle_headers (GstFlacParse * flacparse)
{ {
GstBuffer *buf; GstBuffer *buf;
guint16 num; guint16 num;
guint8 *sinfodata, *writedata;
gsize sinfosize, writesize;
sinfodata = gst_buffer_map (streaminfo, &sinfosize, NULL, GST_MAP_READ);
/* minus one for the marker that is merged with streaminfo here */ /* minus one for the marker that is merged with streaminfo here */
num = g_list_length (flacparse->headers) - 1; num = g_list_length (flacparse->headers) - 1;
buf = gst_buffer_new_and_alloc (13 + GST_BUFFER_SIZE (streaminfo)); buf = gst_buffer_new_and_alloc (13 + sinfosize);
GST_BUFFER_DATA (buf)[0] = 0x7f; writedata = gst_buffer_map (buf, &writesize, NULL, GST_MAP_WRITE);
memcpy (GST_BUFFER_DATA (buf) + 1, "FLAC", 4);
GST_BUFFER_DATA (buf)[5] = 0x01; /* mapping version major */ writedata[0] = 0x7f;
GST_BUFFER_DATA (buf)[6] = 0x00; /* mapping version minor */ memcpy (writedata + 1, "FLAC", 4);
GST_BUFFER_DATA (buf)[7] = (num & 0xFF00) >> 8; writedata[5] = 0x01; /* mapping version major */
GST_BUFFER_DATA (buf)[8] = (num & 0x00FF) >> 0; writedata[6] = 0x00; /* mapping version minor */
memcpy (GST_BUFFER_DATA (buf) + 9, "fLaC", 4); writedata[7] = (num & 0xFF00) >> 8;
memcpy (GST_BUFFER_DATA (buf) + 13, GST_BUFFER_DATA (streaminfo), writedata[8] = (num & 0x00FF) >> 0;
GST_BUFFER_SIZE (streaminfo)); memcpy (writedata + 9, "fLaC", 4);
memcpy (writedata + 13, sinfodata, sinfosize);
_value_array_append_buffer (&array, buf); _value_array_append_buffer (&array, buf);
gst_buffer_unmap (streaminfo, sinfodata, sinfosize);
gst_buffer_unmap (buf, writedata, writesize);
gst_buffer_unref (buf); gst_buffer_unref (buf);
} }
@ -1053,9 +1112,7 @@ push_headers:
flacparse->headers = flacparse->headers =
g_list_delete_link (flacparse->headers, flacparse->headers); g_list_delete_link (flacparse->headers, flacparse->headers);
buf = gst_buffer_make_metadata_writable (buf); buf = gst_buffer_make_writable (buf);
gst_buffer_set_caps (buf,
GST_PAD_CAPS (GST_BASE_PARSE_SRC_PAD (GST_BASE_PARSE (flacparse))));
/* init, set and give away frame */ /* init, set and give away frame */
gst_base_parse_frame_init (&frame); gst_base_parse_frame_init (&frame);
@ -1079,9 +1136,12 @@ gst_flac_parse_generate_headers (GstFlacParse * flacparse)
{ {
GstBuffer *marker, *streaminfo, *vorbiscomment; GstBuffer *marker, *streaminfo, *vorbiscomment;
guint8 *data; guint8 *data;
gsize bufsize;
marker = gst_buffer_new_and_alloc (4); marker = gst_buffer_new_and_alloc (4);
memcpy (GST_BUFFER_DATA (marker), "fLaC", 4); data = gst_buffer_map (marker, &bufsize, NULL, GST_MAP_WRITE);
memcpy (data, "fLaC", 4);
gst_buffer_unmap (marker, data, bufsize);
GST_BUFFER_TIMESTAMP (marker) = GST_CLOCK_TIME_NONE; GST_BUFFER_TIMESTAMP (marker) = GST_CLOCK_TIME_NONE;
GST_BUFFER_DURATION (marker) = GST_CLOCK_TIME_NONE; GST_BUFFER_DURATION (marker) = GST_CLOCK_TIME_NONE;
GST_BUFFER_OFFSET (marker) = 0; GST_BUFFER_OFFSET (marker) = 0;
@ -1089,7 +1149,7 @@ gst_flac_parse_generate_headers (GstFlacParse * flacparse)
flacparse->headers = g_list_append (flacparse->headers, marker); flacparse->headers = g_list_append (flacparse->headers, marker);
streaminfo = gst_buffer_new_and_alloc (4 + 34); streaminfo = gst_buffer_new_and_alloc (4 + 34);
data = GST_BUFFER_DATA (streaminfo); data = gst_buffer_map (streaminfo, &bufsize, NULL, GST_MAP_WRITE);
memset (data, 0, 4 + 34); memset (data, 0, 4 + 34);
/* metadata block header */ /* metadata block header */
@ -1138,6 +1198,7 @@ gst_flac_parse_generate_headers (GstFlacParse * flacparse)
} }
/* MD5 = 0; */ /* MD5 = 0; */
gst_buffer_unmap (streaminfo, data, bufsize);
GST_BUFFER_TIMESTAMP (streaminfo) = GST_CLOCK_TIME_NONE; GST_BUFFER_TIMESTAMP (streaminfo) = GST_CLOCK_TIME_NONE;
GST_BUFFER_DURATION (streaminfo) = GST_CLOCK_TIME_NONE; GST_BUFFER_DURATION (streaminfo) = GST_CLOCK_TIME_NONE;
GST_BUFFER_OFFSET (streaminfo) = 0; GST_BUFFER_OFFSET (streaminfo) = 0;
@ -1157,23 +1218,27 @@ gst_flac_parse_generate_headers (GstFlacParse * flacparse)
sizeof (header), NULL); sizeof (header), NULL);
gst_tag_list_free (taglist); gst_tag_list_free (taglist);
data = gst_buffer_map (vorbiscomment, &bufsize, NULL, GST_MAP_WRITE);
/* Get rid of framing bit */ /* Get rid of framing bit */
if (GST_BUFFER_DATA (vorbiscomment)[GST_BUFFER_SIZE (vorbiscomment) - if (data[bufsize - 1] == 1) {
1] == 1) {
GstBuffer *sub; GstBuffer *sub;
sub = sub =
gst_buffer_create_sub (vorbiscomment, 0, gst_buffer_copy_region (vorbiscomment, GST_BUFFER_COPY_ALL, 0,
GST_BUFFER_SIZE (vorbiscomment) - 1); bufsize - 1);
gst_buffer_unmap (vorbiscomment, data, bufsize);
gst_buffer_unref (vorbiscomment); gst_buffer_unref (vorbiscomment);
vorbiscomment = sub; vorbiscomment = sub;
data = gst_buffer_map (vorbiscomment, &bufsize, NULL, GST_MAP_WRITE);
} }
size = GST_BUFFER_SIZE (vorbiscomment) - 4; size = bufsize - 4;
GST_BUFFER_DATA (vorbiscomment)[1] = ((size & 0xFF0000) >> 16); data[1] = ((size & 0xFF0000) >> 16);
GST_BUFFER_DATA (vorbiscomment)[2] = ((size & 0x00FF00) >> 8); data[2] = ((size & 0x00FF00) >> 8);
GST_BUFFER_DATA (vorbiscomment)[3] = (size & 0x0000FF); data[3] = (size & 0x0000FF);
gst_buffer_unmap (vorbiscomment, data, bufsize);
GST_BUFFER_TIMESTAMP (vorbiscomment) = GST_CLOCK_TIME_NONE; GST_BUFFER_TIMESTAMP (vorbiscomment) = GST_CLOCK_TIME_NONE;
GST_BUFFER_DURATION (vorbiscomment) = GST_CLOCK_TIME_NONE; GST_BUFFER_DURATION (vorbiscomment) = GST_CLOCK_TIME_NONE;
GST_BUFFER_OFFSET (vorbiscomment) = 0; GST_BUFFER_OFFSET (vorbiscomment) = 0;
@ -1189,7 +1254,11 @@ gst_flac_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
{ {
GstFlacParse *flacparse = GST_FLAC_PARSE (parse); GstFlacParse *flacparse = GST_FLAC_PARSE (parse);
GstBuffer *buffer = frame->buffer; GstBuffer *buffer = frame->buffer;
const guint8 *data = GST_BUFFER_DATA (buffer); guint8 *data = NULL;
gsize bufsize;
GstFlowReturn res = GST_FLOW_ERROR;
data = gst_buffer_map (buffer, &bufsize, NULL, GST_MAP_READ);
if (flacparse->state == GST_FLAC_PARSE_STATE_INIT) { if (flacparse->state == GST_FLAC_PARSE_STATE_INIT) {
GST_BUFFER_TIMESTAMP (buffer) = GST_CLOCK_TIME_NONE; GST_BUFFER_TIMESTAMP (buffer) = GST_CLOCK_TIME_NONE;
@ -1204,14 +1273,15 @@ gst_flac_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
flacparse->headers = flacparse->headers =
g_list_append (flacparse->headers, gst_buffer_ref (buffer)); g_list_append (flacparse->headers, gst_buffer_ref (buffer));
return GST_BASE_PARSE_FLOW_DROPPED; res = GST_BASE_PARSE_FLOW_DROPPED;
} else if (flacparse->state == GST_FLAC_PARSE_STATE_HEADERS) { } else if (flacparse->state == GST_FLAC_PARSE_STATE_HEADERS) {
gboolean is_last = ((data[0] & 0x80) == 0x80); gboolean is_last = ((data[0] & 0x80) == 0x80);
guint type = (data[0] & 0x7F); guint type = (data[0] & 0x7F);
if (type == 127) { if (type == 127) {
GST_WARNING_OBJECT (flacparse, "Invalid metadata block type"); GST_WARNING_OBJECT (flacparse, "Invalid metadata block type");
return GST_BASE_PARSE_FLOW_DROPPED; res = GST_BASE_PARSE_FLOW_DROPPED;
goto cleanup;
} }
GST_DEBUG_OBJECT (flacparse, "Handling metadata block of type %u", type); GST_DEBUG_OBJECT (flacparse, "Handling metadata block of type %u", type);
@ -1219,19 +1289,19 @@ gst_flac_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
switch (type) { switch (type) {
case 0: /* STREAMINFO */ case 0: /* STREAMINFO */
if (!gst_flac_parse_handle_streaminfo (flacparse, buffer)) if (!gst_flac_parse_handle_streaminfo (flacparse, buffer))
return GST_FLOW_ERROR; goto cleanup;
break; break;
case 3: /* SEEKTABLE */ case 3: /* SEEKTABLE */
if (!gst_flac_parse_handle_seektable (flacparse, buffer)) if (!gst_flac_parse_handle_seektable (flacparse, buffer))
return GST_FLOW_ERROR; goto cleanup;
break; break;
case 4: /* VORBIS_COMMENT */ case 4: /* VORBIS_COMMENT */
if (!gst_flac_parse_handle_vorbiscomment (flacparse, buffer)) if (!gst_flac_parse_handle_vorbiscomment (flacparse, buffer))
return GST_FLOW_ERROR; goto cleanup;
break; break;
case 6: /* PICTURE */ case 6: /* PICTURE */
if (!gst_flac_parse_handle_picture (flacparse, buffer)) if (!gst_flac_parse_handle_picture (flacparse, buffer))
return GST_FLOW_ERROR; goto cleanup;
break; break;
case 1: /* PADDING */ case 1: /* PADDING */
case 2: /* APPLICATION */ case 2: /* APPLICATION */
@ -1250,7 +1320,7 @@ gst_flac_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
if (is_last) { if (is_last) {
if (!gst_flac_parse_handle_headers (flacparse)) if (!gst_flac_parse_handle_headers (flacparse))
return GST_FLOW_ERROR; goto cleanup;
/* Minimal size of a frame header */ /* Minimal size of a frame header */
gst_base_parse_set_min_frame_size (GST_BASE_PARSE (flacparse), MAX (9, gst_base_parse_set_min_frame_size (GST_BASE_PARSE (flacparse), MAX (9,
@ -1259,7 +1329,7 @@ gst_flac_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
} }
/* DROPPED because we pushed already or will push all headers manually */ /* DROPPED because we pushed already or will push all headers manually */
return GST_BASE_PARSE_FLOW_DROPPED; res = GST_BASE_PARSE_FLOW_DROPPED;
} else { } else {
if (flacparse->offset != GST_BUFFER_OFFSET (buffer)) { if (flacparse->offset != GST_BUFFER_OFFSET (buffer)) {
FrameHeaderCheckReturn ret; FrameHeaderCheckReturn ret;
@ -1267,17 +1337,17 @@ gst_flac_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
flacparse->offset = GST_BUFFER_OFFSET (buffer); flacparse->offset = GST_BUFFER_OFFSET (buffer);
ret = ret =
gst_flac_parse_frame_header_is_valid (flacparse, gst_flac_parse_frame_header_is_valid (flacparse,
GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), TRUE, NULL); data, bufsize, TRUE, NULL);
if (ret != FRAME_HEADER_VALID) { if (ret != FRAME_HEADER_VALID) {
GST_ERROR_OBJECT (flacparse, GST_ERROR_OBJECT (flacparse,
"Baseclass didn't provide a complete frame"); "Baseclass didn't provide a complete frame");
return GST_FLOW_ERROR; goto cleanup;
} }
} }
if (flacparse->block_size == 0) { if (flacparse->block_size == 0) {
GST_ERROR_OBJECT (flacparse, "Unparsed frame"); GST_ERROR_OBJECT (flacparse, "Unparsed frame");
return GST_FLOW_ERROR; goto cleanup;
} }
if (flacparse->seektable) if (flacparse->seektable)
@ -1289,15 +1359,15 @@ gst_flac_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
"Generating headers for variable blocksize streams not supported"); "Generating headers for variable blocksize streams not supported");
if (!gst_flac_parse_handle_headers (flacparse)) if (!gst_flac_parse_handle_headers (flacparse))
return GST_FLOW_ERROR; goto cleanup;
} else { } else {
GST_DEBUG_OBJECT (flacparse, "Generating headers"); GST_DEBUG_OBJECT (flacparse, "Generating headers");
if (!gst_flac_parse_generate_headers (flacparse)) if (!gst_flac_parse_generate_headers (flacparse))
return GST_FLOW_ERROR; goto cleanup;
if (!gst_flac_parse_handle_headers (flacparse)) if (!gst_flac_parse_handle_headers (flacparse))
return GST_FLOW_ERROR; goto cleanup;
} }
flacparse->state = GST_FLAC_PARSE_STATE_DATA; flacparse->state = GST_FLAC_PARSE_STATE_DATA;
} }
@ -1336,8 +1406,14 @@ gst_flac_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
flacparse->blocking_strategy = 0; flacparse->blocking_strategy = 0;
flacparse->block_size = 0; flacparse->block_size = 0;
flacparse->sample_number = 0; flacparse->sample_number = 0;
return GST_FLOW_OK; res = GST_FLOW_OK;
} }
cleanup:
if (data)
gst_buffer_unmap (buffer, data, bufsize);
return res;
} }
static GstFlowReturn static GstFlowReturn

View file

@ -98,8 +98,8 @@ static gboolean gst_mpeg_audio_parse_convert (GstBaseParse * parse,
GstFormat src_format, gint64 src_value, GstFormat src_format, gint64 src_value,
GstFormat dest_format, gint64 * dest_value); GstFormat dest_format, gint64 * dest_value);
GST_BOILERPLATE (GstMpegAudioParse, gst_mpeg_audio_parse, GstBaseParse, #define gst_mpeg_audio_parse_parent_class parent_class
GST_TYPE_BASE_PARSE); G_DEFINE_TYPE (GstMpegAudioParse, gst_mpeg_audio_parse, GST_TYPE_BASE_PARSE);
#define GST_TYPE_MPEG_AUDIO_CHANNEL_MODE \ #define GST_TYPE_MPEG_AUDIO_CHANNEL_MODE \
(gst_mpeg_audio_channel_mode_get_type()) (gst_mpeg_audio_channel_mode_get_type())
@ -137,27 +137,11 @@ gst_mpeg_audio_channel_mode_get_nick (gint mode)
return NULL; return NULL;
} }
static void
gst_mpeg_audio_parse_base_init (gpointer klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&sink_template));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&src_template));
gst_element_class_set_details_simple (element_class, "MPEG1 Audio Parser",
"Codec/Parser/Audio",
"Parses and frames mpeg1 audio streams (levels 1-3), provides seek",
"Jan Schmidt <thaytan@mad.scientist.com>,"
"Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>");
}
static void static void
gst_mpeg_audio_parse_class_init (GstMpegAudioParseClass * klass) gst_mpeg_audio_parse_class_init (GstMpegAudioParseClass * klass)
{ {
GstBaseParseClass *parse_class = GST_BASE_PARSE_CLASS (klass); GstBaseParseClass *parse_class = GST_BASE_PARSE_CLASS (klass);
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass);
GST_DEBUG_CATEGORY_INIT (mpeg_audio_parse_debug, "mpegaudioparse", 0, GST_DEBUG_CATEGORY_INIT (mpeg_audio_parse_debug, "mpegaudioparse", 0,
@ -185,6 +169,17 @@ gst_mpeg_audio_parse_class_init (GstMpegAudioParseClass * klass)
"channel mode", "MPEG audio channel mode", NULL); "channel mode", "MPEG audio channel mode", NULL);
g_type_class_ref (GST_TYPE_MPEG_AUDIO_CHANNEL_MODE); g_type_class_ref (GST_TYPE_MPEG_AUDIO_CHANNEL_MODE);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&sink_template));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&src_template));
gst_element_class_set_details_simple (element_class, "MPEG1 Audio Parser",
"Codec/Parser/Audio",
"Parses and frames mpeg1 audio streams (levels 1-3), provides seek",
"Jan Schmidt <thaytan@mad.scientist.com>,"
"Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>");
} }
static void static void
@ -220,8 +215,7 @@ gst_mpeg_audio_parse_reset (GstMpegAudioParse * mp3parse)
} }
static void static void
gst_mpeg_audio_parse_init (GstMpegAudioParse * mp3parse, gst_mpeg_audio_parse_init (GstMpegAudioParse * mp3parse)
GstMpegAudioParseClass * klass)
{ {
gst_mpeg_audio_parse_reset (mp3parse); gst_mpeg_audio_parse_reset (mp3parse);
} }
@ -374,13 +368,13 @@ gst_mp3parse_validate_extended (GstMpegAudioParse * mp3parse, GstBuffer * buf,
guint32 header, int bpf, gboolean at_eos, gint * valid) guint32 header, int bpf, gboolean at_eos, gint * valid)
{ {
guint32 next_header; guint32 next_header;
const guint8 *data; guint8 *data;
guint available; gsize available;
gboolean res = TRUE;
int frames_found = 1; int frames_found = 1;
int offset = bpf; int offset = bpf;
available = GST_BUFFER_SIZE (buf); data = gst_buffer_map (buf, &available, NULL, GST_MAP_READ);
data = GST_BUFFER_DATA (buf);
while (frames_found < MIN_RESYNC_FRAMES) { while (frames_found < MIN_RESYNC_FRAMES) {
/* Check if we have enough data for all these frames, plus the next /* Check if we have enough data for all these frames, plus the next
@ -389,10 +383,11 @@ gst_mp3parse_validate_extended (GstMpegAudioParse * mp3parse, GstBuffer * buf,
if (at_eos) { if (at_eos) {
/* Running out of data at EOS is fine; just accept it */ /* Running out of data at EOS is fine; just accept it */
*valid = TRUE; *valid = TRUE;
return TRUE; goto cleanup;
} else { } else {
*valid = offset + 4; *valid = offset + 4;
return FALSE; res = FALSE;
goto cleanup;
} }
} }
@ -413,14 +408,14 @@ gst_mp3parse_validate_extended (GstMpegAudioParse * mp3parse, GstBuffer * buf,
(guint) header, (guint) header & HDRMASK, (guint) next_header, (guint) header, (guint) header & HDRMASK, (guint) next_header,
(guint) next_header & HDRMASK, bpf); (guint) next_header & HDRMASK, bpf);
*valid = FALSE; *valid = FALSE;
return TRUE; goto cleanup;
} else if ((((next_header >> 12) & 0xf) == 0) || } else if ((((next_header >> 12) & 0xf) == 0) ||
(((next_header >> 12) & 0xf) == 0xf)) { (((next_header >> 12) & 0xf) == 0xf)) {
/* The essential parts were the same, but the bitrate held an /* The essential parts were the same, but the bitrate held an
invalid value - also reject */ invalid value - also reject */
GST_DEBUG_OBJECT (mp3parse, "next header invalid (bitrate)"); GST_DEBUG_OBJECT (mp3parse, "next header invalid (bitrate)");
*valid = FALSE; *valid = FALSE;
return TRUE; goto cleanup;
} }
bpf = mp3_type_frame_length_from_header (mp3parse, next_header, bpf = mp3_type_frame_length_from_header (mp3parse, next_header,
@ -431,7 +426,10 @@ gst_mp3parse_validate_extended (GstMpegAudioParse * mp3parse, GstBuffer * buf,
} }
*valid = TRUE; *valid = TRUE;
return TRUE;
cleanup:
gst_buffer_unmap (buf, data, available);
return res;
} }
static gboolean static gboolean
@ -487,37 +485,43 @@ gst_mpeg_audio_parse_check_valid_frame (GstBaseParse * parse,
{ {
GstMpegAudioParse *mp3parse = GST_MPEG_AUDIO_PARSE (parse); GstMpegAudioParse *mp3parse = GST_MPEG_AUDIO_PARSE (parse);
GstBuffer *buf = frame->buffer; GstBuffer *buf = frame->buffer;
GstByteReader reader = GST_BYTE_READER_INIT_FROM_BUFFER (buf); GstByteReader reader;
gint off, bpf; gint off, bpf;
gboolean lost_sync, draining, valid, caps_change; gboolean lost_sync, draining, valid, caps_change;
guint32 header; guint32 header;
guint bitrate, layer, rate, channels, version, mode, crc; guint bitrate, layer, rate, channels, version, mode, crc;
guint8 *data;
gsize bufsize;
gboolean res = FALSE;
if (G_UNLIKELY (GST_BUFFER_SIZE (buf) < 6)) data = gst_buffer_map (buf, &bufsize, NULL, GST_MAP_READ);
return FALSE; if (G_UNLIKELY (bufsize < 6))
goto cleanup;
gst_byte_reader_init (&reader, data, bufsize);
off = gst_byte_reader_masked_scan_uint32 (&reader, 0xffe00000, 0xffe00000, off = gst_byte_reader_masked_scan_uint32 (&reader, 0xffe00000, 0xffe00000,
0, GST_BUFFER_SIZE (buf)); 0, bufsize);
GST_LOG_OBJECT (parse, "possible sync at buffer offset %d", off); GST_LOG_OBJECT (parse, "possible sync at buffer offset %d", off);
/* didn't find anything that looks like a sync word, skip */ /* didn't find anything that looks like a sync word, skip */
if (off < 0) { if (off < 0) {
*skipsize = GST_BUFFER_SIZE (buf) - 3; *skipsize = bufsize - 3;
return FALSE; goto cleanup;
} }
/* possible frame header, but not at offset 0? skip bytes before sync */ /* possible frame header, but not at offset 0? skip bytes before sync */
if (off > 0) { if (off > 0) {
*skipsize = off; *skipsize = off;
return FALSE; goto cleanup;
} }
/* make sure the values in the frame header look sane */ /* make sure the values in the frame header look sane */
header = GST_READ_UINT32_BE (GST_BUFFER_DATA (buf)); header = GST_READ_UINT32_BE (data);
if (!gst_mpeg_audio_parse_head_check (mp3parse, header)) { if (!gst_mpeg_audio_parse_head_check (mp3parse, header)) {
*skipsize = 1; *skipsize = 1;
return FALSE; goto cleanup;
} }
GST_LOG_OBJECT (parse, "got frame"); GST_LOG_OBJECT (parse, "got frame");
@ -541,21 +545,25 @@ gst_mpeg_audio_parse_check_valid_frame (GstBaseParse * parse,
/* not enough data */ /* not enough data */
gst_base_parse_set_min_frame_size (parse, valid); gst_base_parse_set_min_frame_size (parse, valid);
*skipsize = 0; *skipsize = 0;
return FALSE; goto cleanup;
} else { } else {
if (!valid) { if (!valid) {
*skipsize = off + 2; *skipsize = off + 2;
return FALSE; goto cleanup;
} }
} }
} else if (draining && lost_sync && caps_change && mp3parse->rate > 0) { } else if (draining && lost_sync && caps_change && mp3parse->rate > 0) {
/* avoid caps jitter that we can't be sure of */ /* avoid caps jitter that we can't be sure of */
*skipsize = off + 2; *skipsize = off + 2;
return FALSE; goto cleanup;
} }
*framesize = bpf; *framesize = bpf;
return TRUE; res = TRUE;
cleanup:
gst_buffer_unmap (buf, data, bufsize);
return res;
} }
static void static void
@ -571,7 +579,8 @@ gst_mpeg_audio_parse_handle_first_frame (GstMpegAudioParse * mp3parse,
gint64 upstream_total_bytes = 0; gint64 upstream_total_bytes = 0;
GstFormat fmt = GST_FORMAT_BYTES; GstFormat fmt = GST_FORMAT_BYTES;
guint32 read_id_xing = 0, read_id_vbri = 0; guint32 read_id_xing = 0, read_id_vbri = 0;
const guint8 *data; guint8 *data, *origdata;
gsize bufsize;
guint bitrate; guint bitrate;
if (mp3parse->sent_codec_tag) if (mp3parse->sent_codec_tag)
@ -598,8 +607,8 @@ gst_mpeg_audio_parse_handle_first_frame (GstMpegAudioParse * mp3parse,
offset_vbri += 4; offset_vbri += 4;
/* Check if we have enough data to read the Xing header */ /* Check if we have enough data to read the Xing header */
avail = GST_BUFFER_SIZE (buf); origdata = data = gst_buffer_map (buf, &bufsize, NULL, GST_MAP_READ);
data = GST_BUFFER_DATA (buf); avail = bufsize;
if (avail >= offset_xing + 4) { if (avail >= offset_xing + 4) {
read_id_xing = GST_READ_UINT32_BE (data + offset_xing); read_id_xing = GST_READ_UINT32_BE (data + offset_xing);
@ -639,7 +648,7 @@ gst_mpeg_audio_parse_handle_first_frame (GstMpegAudioParse * mp3parse,
if (avail < bytes_needed) { if (avail < bytes_needed) {
GST_DEBUG_OBJECT (mp3parse, GST_DEBUG_OBJECT (mp3parse,
"Not enough data to read Xing header (need %d)", bytes_needed); "Not enough data to read Xing header (need %d)", bytes_needed);
return; goto cleanup;
} }
GST_DEBUG_OBJECT (mp3parse, "Reading Xing header"); GST_DEBUG_OBJECT (mp3parse, "Reading Xing header");
@ -795,7 +804,7 @@ gst_mpeg_audio_parse_handle_first_frame (GstMpegAudioParse * mp3parse,
if (avail < offset_vbri + 26) { if (avail < offset_vbri + 26) {
GST_DEBUG_OBJECT (mp3parse, GST_DEBUG_OBJECT (mp3parse,
"Not enough data to read VBRI header (need %d)", offset_vbri + 26); "Not enough data to read VBRI header (need %d)", offset_vbri + 26);
return; goto cleanup;
} }
GST_DEBUG_OBJECT (mp3parse, "Reading VBRI header"); GST_DEBUG_OBJECT (mp3parse, "Reading VBRI header");
@ -806,7 +815,7 @@ gst_mpeg_audio_parse_handle_first_frame (GstMpegAudioParse * mp3parse,
if (GST_READ_UINT16_BE (data) != 0x0001) { if (GST_READ_UINT16_BE (data) != 0x0001) {
GST_WARNING_OBJECT (mp3parse, GST_WARNING_OBJECT (mp3parse,
"Unsupported VBRI version 0x%x", GST_READ_UINT16_BE (data)); "Unsupported VBRI version 0x%x", GST_READ_UINT16_BE (data));
return; goto cleanup;
} }
data += 2; data += 2;
@ -879,10 +888,10 @@ gst_mpeg_audio_parse_handle_first_frame (GstMpegAudioParse * mp3parse,
GST_DEBUG_OBJECT (mp3parse, GST_DEBUG_OBJECT (mp3parse,
"Not enough data to read VBRI header (need %d)", "Not enough data to read VBRI header (need %d)",
offset_vbri + 26 + nseek_points * seek_bytes); offset_vbri + 26 + nseek_points * seek_bytes);
return; goto cleanup;
} }
data = GST_BUFFER_DATA (buf); data = origdata;
data += offset_vbri + 26; data += offset_vbri + 26;
/* VBRI seek table: frame/seek_frames -> byte */ /* VBRI seek table: frame/seek_frames -> byte */
@ -956,6 +965,9 @@ gst_mpeg_audio_parse_handle_first_frame (GstMpegAudioParse * mp3parse,
bitrate = 0; bitrate = 0;
gst_base_parse_set_average_bitrate (GST_BASE_PARSE (mp3parse), bitrate); gst_base_parse_set_average_bitrate (GST_BASE_PARSE (mp3parse), bitrate);
cleanup:
gst_buffer_unmap (buf, origdata, bufsize);
} }
static GstFlowReturn static GstFlowReturn
@ -964,12 +976,16 @@ gst_mpeg_audio_parse_parse_frame (GstBaseParse * parse,
{ {
GstMpegAudioParse *mp3parse = GST_MPEG_AUDIO_PARSE (parse); GstMpegAudioParse *mp3parse = GST_MPEG_AUDIO_PARSE (parse);
GstBuffer *buf = frame->buffer; GstBuffer *buf = frame->buffer;
guint8 *data;
gsize bufsize;
guint bitrate, layer, rate, channels, version, mode, crc; guint bitrate, layer, rate, channels, version, mode, crc;
g_return_val_if_fail (GST_BUFFER_SIZE (buf) >= 4, GST_FLOW_ERROR); data = gst_buffer_map (buf, &bufsize, NULL, GST_MAP_READ);
if (G_UNLIKELY (bufsize < 4))
goto short_buffer;
if (!mp3_type_frame_length_from_header (mp3parse, if (!mp3_type_frame_length_from_header (mp3parse,
GST_READ_UINT32_BE (GST_BUFFER_DATA (buf)), GST_READ_UINT32_BE (data),
&version, &layer, &channels, &bitrate, &rate, &mode, &crc)) &version, &layer, &channels, &bitrate, &rate, &mode, &crc))
goto broken_header; goto broken_header;
@ -981,7 +997,6 @@ gst_mpeg_audio_parse_parse_frame (GstBaseParse * parse,
"layer", G_TYPE_INT, layer, "layer", G_TYPE_INT, layer,
"rate", G_TYPE_INT, rate, "rate", G_TYPE_INT, rate,
"channels", G_TYPE_INT, channels, "parsed", G_TYPE_BOOLEAN, TRUE, NULL); "channels", G_TYPE_INT, channels, "parsed", G_TYPE_BOOLEAN, TRUE, NULL);
gst_buffer_set_caps (buf, caps);
gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), caps); gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), caps);
gst_caps_unref (caps); gst_caps_unref (caps);
@ -1025,15 +1040,23 @@ gst_mpeg_audio_parse_parse_frame (GstBaseParse * parse,
mp3parse->last_crc = crc; mp3parse->last_crc = crc;
mp3parse->last_mode = mode; mp3parse->last_mode = mode;
gst_buffer_unmap (buf, data, bufsize);
return GST_FLOW_OK; return GST_FLOW_OK;
/* ERRORS */ /* ERRORS */
broken_header: broken_header:
{ {
/* this really shouldn't ever happen */ /* this really shouldn't ever happen */
gst_buffer_unmap (buf, data, bufsize);
GST_ELEMENT_ERROR (parse, STREAM, DECODE, (NULL), (NULL)); GST_ELEMENT_ERROR (parse, STREAM, DECODE, (NULL), (NULL));
return GST_FLOW_ERROR; return GST_FLOW_ERROR;
} }
short_buffer:
{
gst_buffer_unmap (buf, data, bufsize);
return GST_FLOW_ERROR;
}
} }
static gboolean static gboolean