mpegvideoparse: tweak codec parser API and adjust parser element

... to allow for more efficient parsing and (more) consistent parsing API
among various codec parsers.

Fixes #672701.

Conflicts:

	gst/videoparsers/gstmpegvideoparse.c
This commit is contained in:
Mark Nauwelaerts 2012-05-22 14:00:36 +02:00
parent 29a947642e
commit 28f3858b94
4 changed files with 133 additions and 150 deletions

View file

@ -229,6 +229,7 @@ failed:
} }
} }
/* @size and @offset are wrt current reader position */
static inline guint static inline guint
scan_for_start_codes (const GstByteReader * reader, guint offset, guint size) scan_for_start_codes (const GstByteReader * reader, guint offset, guint size)
{ {
@ -245,7 +246,7 @@ scan_for_start_codes (const GstByteReader * reader, guint offset, guint size)
data = reader->data + reader->byte + offset; data = reader->data + reader->byte + offset;
while (i < (size - 4)) { while (i <= (size - 4)) {
if (data[i + 2] > 1) { if (data[i + 2] > 1) {
i += 3; i += 3;
} else if (data[i + 1]) { } else if (data[i + 1]) {
@ -257,7 +258,7 @@ scan_for_start_codes (const GstByteReader * reader, guint offset, guint size)
} }
} }
if (i < (size - 4)) if (i <= (size - 4))
return offset + i; return offset + i;
/* nothing found */ /* nothing found */
@ -275,14 +276,14 @@ scan_for_start_codes (const GstByteReader * reader, guint offset, guint size)
* Parses the MPEG 1/2 video bitstream contained in @data , and returns the * Parses the MPEG 1/2 video bitstream contained in @data , and returns the
* detect packets as a list of #GstMpegVideoTypeOffsetSize. * detect packets as a list of #GstMpegVideoTypeOffsetSize.
* *
* Returns: a #GList of #GstMpegVideoTypeOffsetSize * Returns: TRUE if a packet start code was found
*/ */
GList * gboolean
gst_mpeg_video_parse (const guint8 * data, gsize size, guint offset) gst_mpeg_video_parse (GstMpegVideoPacket * packet,
const guint8 * data, gsize size, guint offset)
{ {
gint off, rsize; gint off;
GstByteReader br; GstByteReader br;
GList *ret = NULL;
if (!initialized) { if (!initialized) {
GST_DEBUG_CATEGORY_INIT (mpegvideo_parser_debug, "codecparsers_mpegvideo", GST_DEBUG_CATEGORY_INIT (mpegvideo_parser_debug, "codecparsers_mpegvideo",
@ -292,53 +293,42 @@ gst_mpeg_video_parse (const guint8 * data, gsize size, guint offset)
if (size <= offset) { if (size <= offset) {
GST_DEBUG ("Can't parse from offset %d, buffer is to small", offset); GST_DEBUG ("Can't parse from offset %d, buffer is to small", offset);
return NULL; return FALSE;
} }
size -= offset; size -= offset;
gst_byte_reader_init (&br, &data[offset], size); gst_byte_reader_init (&br, &data[offset], size);
off = scan_for_start_codes (&br, 0, size); off = scan_for_start_codes (&br, 0, size);
if (off < 0) { if (off < 0) {
GST_DEBUG ("No start code prefix in this buffer"); GST_DEBUG ("No start code prefix in this buffer");
return NULL; return FALSE;
} }
while (off >= 0 && off + 3 < size) { if (gst_byte_reader_skip (&br, off + 3) == FALSE)
GstMpegVideoTypeOffsetSize *codoffsize; goto failed;
if (gst_byte_reader_get_uint8 (&br, &packet->type) == FALSE)
goto failed;
if (gst_byte_reader_skip (&br, off + 3) == FALSE) packet->data = data;
goto failed; packet->offset = offset + off + 4;
packet->size = -1;
codoffsize = g_malloc (sizeof (GstMpegVideoTypeOffsetSize)); /* try to find end of packet */
if (gst_byte_reader_get_uint8 (&br, &codoffsize->type) == FALSE) size -= off + 4;
goto failed; off = scan_for_start_codes (&br, 0, size);
codoffsize->offset = gst_byte_reader_get_pos (&br) + offset; if (off > 0)
packet->size = off;
rsize = gst_byte_reader_get_remaining (&br); return TRUE;
if (rsize == 0) {
/* if there are no more bytes after the start code set the size to -1 */
off = -1;
} else {
off = scan_for_start_codes (&br, 0, rsize);
}
codoffsize->size = off;
ret = g_list_prepend (ret, codoffsize);
codoffsize = ret->data;
}
return g_list_reverse (ret);
failed: failed:
{ {
GST_WARNING ("Failed to parse"); GST_WARNING ("Failed to parse");
return g_list_reverse (ret); return FALSE;
} }
} }

View file

@ -179,7 +179,7 @@ typedef struct _GstMpegVideoPictureHdr GstMpegVideoPictureHdr;
typedef struct _GstMpegVideoGop GstMpegVideoGop; typedef struct _GstMpegVideoGop GstMpegVideoGop;
typedef struct _GstMpegVideoPictureExt GstMpegVideoPictureExt; typedef struct _GstMpegVideoPictureExt GstMpegVideoPictureExt;
typedef struct _GstMpegVideoQuantMatrixExt GstMpegVideoQuantMatrixExt; typedef struct _GstMpegVideoQuantMatrixExt GstMpegVideoQuantMatrixExt;
typedef struct _GstMpegVideoTypeOffsetSize GstMpegVideoTypeOffsetSize; typedef struct _GstMpegVideoPacket GstMpegVideoPacket;
/** /**
* GstMpegVideoSequenceHdr: * GstMpegVideoSequenceHdr:
@ -362,20 +362,24 @@ struct _GstMpegVideoGop
/** /**
* GstMpegVideoTypeOffsetSize: * GstMpegVideoTypeOffsetSize:
*
* @type: the type of the packet that start at @offset * @type: the type of the packet that start at @offset
* @data: the data containing the packet starting at @offset
* @offset: the offset of the packet start in bytes, it is the exact, start of the packet, no sync code included * @offset: the offset of the packet start in bytes, it is the exact, start of the packet, no sync code included
* @size: The size in bytes of the packet or -1 if the end wasn't found. It is the exact size of the packet, no sync code included * @size: The size in bytes of the packet or -1 if the end wasn't found. It is the exact size of the packet, no sync code included
* *
* A structure that contains the type of a packet, its offset and its size * A structure that contains the type of a packet, its offset and its size
*/ */
struct _GstMpegVideoTypeOffsetSize struct _GstMpegVideoPacket
{ {
const guint8 *data;
guint8 type; guint8 type;
guint offset; guint offset;
gint size; gint size;
}; };
GList *gst_mpeg_video_parse (const guint8 * data, gsize size, guint offset); gboolean gst_mpeg_video_parse (GstMpegVideoPacket * packet,
const guint8 * data, gsize size, guint offset);
gboolean gst_mpeg_video_parse_sequence_header (GstMpegVideoSequenceHdr * params, gboolean gst_mpeg_video_parse_sequence_header (GstMpegVideoSequenceHdr * params,
const guint8 * data, gsize size, guint offset); const guint8 * data, gsize size, guint offset);

View file

@ -181,6 +181,8 @@ gst_mpegv_parse_reset_frame (GstMpegvParse * mpvparse)
mpvparse->seq_offset = -1; mpvparse->seq_offset = -1;
mpvparse->pic_offset = -1; mpvparse->pic_offset = -1;
mpvparse->frame_repeat_count = 0; mpvparse->frame_repeat_count = 0;
memset (mpvparse->ext_offsets, 0, sizeof (mpvparse->ext_offsets));
mpvparse->ext_count = 0;
} }
static void static void
@ -227,18 +229,19 @@ static gboolean
gst_mpegv_parse_process_config (GstMpegvParse * mpvparse, GstBuffer * buf, gst_mpegv_parse_process_config (GstMpegvParse * mpvparse, GstBuffer * buf,
guint size) guint size)
{ {
GList *tmp;
guint8 *data; guint8 *data;
guint8 *data_with_prefix; guint8 *data_with_prefix;
GstMapInfo map; GstMapInfo map;
gst_buffer_map (buf, &map, GST_MAP_READ);
data = map.data + mpvparse->seq_offset;
if (mpvparse->seq_offset < 4) { if (mpvparse->seq_offset < 4) {
/* This shouldn't happen, but just in case... */ /* This shouldn't happen, but just in case... */
GST_WARNING_OBJECT (mpvparse, "Sequence header start code missing."); GST_WARNING_OBJECT (mpvparse, "Sequence header start code missing.");
return FALSE; return FALSE;
} }
gst_buffer_map (buf, &map, GST_MAP_READ);
data = map.data + mpvparse->seq_offset;
g_assert (size <= map.size);
/* pointer to sequence header data including the start code prefix - /* pointer to sequence header data including the start code prefix -
used for codec private data */ used for codec private data */
data_with_prefix = data - 4; data_with_prefix = data - 4;
@ -254,7 +257,7 @@ gst_mpegv_parse_process_config (GstMpegvParse * mpvparse, GstBuffer * buf,
} }
if (gst_mpeg_video_parse_sequence_header (&mpvparse->sequencehdr, data, if (gst_mpeg_video_parse_sequence_header (&mpvparse->sequencehdr, data,
map.size - mpvparse->seq_offset, 0)) { size - mpvparse->seq_offset, 0)) {
if (mpvparse->fps_num == 0 || mpvparse->fps_den == 0) { if (mpvparse->fps_num == 0 || mpvparse->fps_den == 0) {
mpvparse->fps_num = mpvparse->sequencehdr.fps_n; mpvparse->fps_num = mpvparse->sequencehdr.fps_n;
mpvparse->fps_den = mpvparse->sequencehdr.fps_d; mpvparse->fps_den = mpvparse->sequencehdr.fps_d;
@ -271,24 +274,19 @@ gst_mpegv_parse_process_config (GstMpegvParse * mpvparse, GstBuffer * buf,
/* Set mpeg version, and parse sequence extension */ /* Set mpeg version, and parse sequence extension */
if (mpvparse->mpeg_version <= 0) { if (mpvparse->mpeg_version <= 0) {
GstMpegVideoTypeOffsetSize *tpoffsz; gint i, offset;
mpvparse->mpeg_version = 1; mpvparse->mpeg_version = 1;
for (tmp = mpvparse->typeoffsize; tmp; tmp = tmp->next) { for (i = 0; i < mpvparse->ext_count; ++i) {
tpoffsz = tmp->data; offset = mpvparse->ext_offsets[i];
mpvparse->mpeg_version = 2;
if (tpoffsz->type == GST_MPEG_VIDEO_PACKET_EXTENSION) { if (offset < size &&
mpvparse->mpeg_version = 2; gst_mpeg_video_parse_sequence_extension (&mpvparse->sequenceext,
map.data, size, offset)) {
if (gst_mpeg_video_parse_sequence_extension (&mpvparse->sequenceext, mpvparse->fps_num =
map.data, map.size, tpoffsz->offset)) { mpvparse->sequencehdr.fps_n * (mpvparse->sequenceext.fps_n_ext + 1);
mpvparse->fps_num = mpvparse->fps_den =
mpvparse->sequencehdr.fps_n * (mpvparse->sequenceext.fps_n_ext + mpvparse->sequencehdr.fps_d * (mpvparse->sequenceext.fps_d_ext + 1);
1);
mpvparse->fps_den =
mpvparse->sequencehdr.fps_d * (mpvparse->sequenceext.fps_d_ext +
1);
}
} }
} }
} }
@ -435,7 +433,9 @@ gst_mpegv_parse_process_sc (GstMpegvParse * mpvparse,
case GST_MPEG_VIDEO_PACKET_EXTENSION: case GST_MPEG_VIDEO_PACKET_EXTENSION:
GST_LOG_OBJECT (mpvparse, "startcode is VIDEO PACKET EXTENSION"); GST_LOG_OBJECT (mpvparse, "startcode is VIDEO PACKET EXTENSION");
parse_picture_extension (mpvparse, buf, off); parse_picture_extension (mpvparse, buf, off);
packet = FALSE; if (mpvparse->ext_count < G_N_ELEMENTS (mpvparse->ext_offsets))
mpvparse->ext_offsets[mpvparse->ext_count++] = off;
/* fall-through */
default: default:
packet = FALSE; packet = FALSE;
break; break;
@ -444,6 +444,8 @@ gst_mpegv_parse_process_sc (GstMpegvParse * mpvparse,
/* set size to avoid processing config again */ /* set size to avoid processing config again */
if (mpvparse->seq_offset >= 0 && off != mpvparse->seq_offset && if (mpvparse->seq_offset >= 0 && off != mpvparse->seq_offset &&
!mpvparse->seq_size && packet) { !mpvparse->seq_size && packet) {
/* should always be at start */
g_assert (mpvparse->seq_offset <= 4);
gst_mpegv_parse_process_config (mpvparse, buf, off - mpvparse->seq_offset); gst_mpegv_parse_process_config (mpvparse, buf, off - mpvparse->seq_offset);
mpvparse->seq_size = off - mpvparse->seq_offset; mpvparse->seq_size = off - mpvparse->seq_offset;
} }
@ -485,7 +487,6 @@ update_frame_parsing_status (GstMpegvParse * mpvparse,
} }
} }
static GstFlowReturn static GstFlowReturn
gst_mpegv_parse_handle_frame (GstBaseParse * parse, gst_mpegv_parse_handle_frame (GstBaseParse * parse,
GstBaseParseFrame * frame, gint * skipsize) GstBaseParseFrame * frame, gint * skipsize)
@ -493,121 +494,108 @@ gst_mpegv_parse_handle_frame (GstBaseParse * parse,
GstMpegvParse *mpvparse = GST_MPEGVIDEO_PARSE (parse); GstMpegvParse *mpvparse = GST_MPEGVIDEO_PARSE (parse);
GstBuffer *buf = frame->buffer; GstBuffer *buf = frame->buffer;
gboolean ret = FALSE; gboolean ret = FALSE;
GList *tmp; gint off = 0;
gint off = 0, fsize = -1; GstMpegVideoPacket packet;
guint8 *data;
gint size;
GstMapInfo map; GstMapInfo map;
gsize buf_size;
GstMpegVideoTypeOffsetSize *codoffsz;
update_frame_parsing_status (mpvparse, frame); update_frame_parsing_status (mpvparse, frame);
if (mpvparse->last_sc >= 0)
off = mpvparse->last_sc;
GST_LOG_OBJECT (mpvparse, "parsing at offset %d", off);
gst_buffer_map (buf, &map, GST_MAP_READ); gst_buffer_map (buf, &map, GST_MAP_READ);
buf_size = map.size; data = map.data;
mpvparse->typeoffsize = gst_mpeg_video_parse (map.data, map.size, off); size = map.size;
gst_buffer_unmap (buf, &map);
/* No sc found */ retry:
if (mpvparse->typeoffsize == NULL) { /* at least start code and subsequent byte */
GST_LOG_OBJECT (mpvparse, "no start code"); if (G_UNLIKELY (size < 5 + off))
*skipsize = gst_buffer_get_size (buf) - 3; goto exit;
goto end;
/* if already found a previous start code, e.g. start of frame, go for next */
if (mpvparse->last_sc >= 0) {
off = mpvparse->last_sc;
goto next;
} }
codoffsz = mpvparse->typeoffsize->data; if (!gst_mpeg_video_parse (&packet, data, size, off)) {
/* if initial start code not at start, arrange to make it so */ /* didn't find anything that looks like a sync word, skip */
if (mpvparse->last_sc < 0) { GST_LOG_OBJECT (mpvparse, "no start code found");
/* check for sane start code */ *skipsize = size - 3;
for (tmp = mpvparse->typeoffsize; tmp; tmp = g_list_next (tmp)) { goto exit;
codoffsz = tmp->data;
g_assert (codoffsz->offset >= 4);
off = codoffsz->offset - 4;
GST_LOG_OBJECT (mpvparse, "checking for valid start code at %d", off);
/* hard-code to 4 to force initial check */
if (gst_mpegv_parse_process_sc (mpvparse, buf, 4, codoffsz->type)) {
if (off > 0) {
GST_LOG_OBJECT (mpvparse, "skipping to start code at offset %d", off);
gst_mpegv_parse_reset_frame (mpvparse);
*skipsize = off;
goto end;
}
/* got starting start code where we want it now */
GST_LOG_OBJECT (mpvparse, "got valid start code");
mpvparse->last_sc = 0;
break;
}
/* avoid hard-code hack bite */
gst_mpegv_parse_reset_frame (mpvparse);
}
} }
if (mpvparse->last_sc < 0) { off = packet.offset - 4;
GST_LOG_OBJECT (mpvparse, "no valid start code"); GST_LOG_OBJECT (mpvparse, "possible sync at buffer offset %d", off);
*skipsize = gst_buffer_get_size (buf) - 3;
goto end; /* possible frame header, but not at offset 0? skip bytes before sync */
if (G_UNLIKELY (off > 0)) {
*skipsize = off;
goto exit;
} }
/* note: initial start code is assumed at offset 0 by subsequent code */
/* examine start code, see if it looks like an initial start code */
if (gst_mpegv_parse_process_sc (mpvparse, buf, 4, packet.type)) {
/* found sc */
GST_LOG_OBJECT (mpvparse, "valid start code found");
mpvparse->last_sc = 0;
} else {
off++;
gst_mpegv_parse_reset_frame (mpvparse);
GST_LOG_OBJECT (mpvparse, "invalid start code");
goto retry;
}
next:
/* start is fine as of now */ /* start is fine as of now */
*skipsize = 0; *skipsize = 0;
/* position a bit further than last sc */
/* initial packet processed above */ off++;
for (tmp = mpvparse->typeoffsize; tmp; tmp = g_list_next (tmp)) { /* so now we have start code at start of data; locate next start code */
codoffsz = tmp->data; if (!gst_mpeg_video_parse (&packet, data, size, off)) {
off = -1;
GST_LOG_OBJECT (mpvparse, "next start code at %d", codoffsz->offset);
if (codoffsz->offset > 4)
ret = gst_mpegv_parse_process_sc (mpvparse, buf, codoffsz->offset,
codoffsz->type);
else
ret = FALSE;
if (ret) {
*skipsize = 0;
fsize = codoffsz->offset - 4;
break;
}
/* start here next time around */
mpvparse->last_sc = codoffsz->offset - 4;
}
end:
if (fsize > 0) {
ret = TRUE;
GST_LOG_OBJECT (mpvparse, "found frame of size %d", fsize);
} else if (mpvparse->last_sc < 0) {
ret = FALSE;
GST_LOG_OBJECT (mpvparse, "skipping %d", *skipsize);
} else if (GST_BASE_PARSE_DRAINING (parse)) {
fsize = buf_size;
ret = TRUE;
GST_LOG_OBJECT (mpvparse, "no more data, assuming frame size %d", fsize);
} else { } else {
/* need some more data */ g_assert (packet.offset >= 4);
/* request best next available */ off = packet.offset - 4;
ret = FALSE;
GST_LOG_OBJECT (mpvparse, "obtaining more data");
} }
g_list_foreach (mpvparse->typeoffsize, (GFunc) g_free, NULL); GST_LOG_OBJECT (mpvparse, "next start code at %d", off);
g_list_free (mpvparse->typeoffsize); if (off < 0) {
mpvparse->typeoffsize = NULL; /* if draining, take all */
if (GST_BASE_PARSE_DRAINING (parse)) {
GST_LOG_OBJECT (mpvparse, "draining, accepting all data");
off = size;
ret = TRUE;
} else {
GST_LOG_OBJECT (mpvparse, "need more data");
/* resume scan where we left it */
/* need - 4 since off is incremented later on */
mpvparse->last_sc = size - 4;
/* request best next available */
off = G_MAXUINT;
goto exit;
}
} else {
/* decide whether this startcode ends a frame */
ret = gst_mpegv_parse_process_sc (mpvparse, buf, off + 4, packet.type);
}
if (!ret)
goto next;
exit:
gst_buffer_unmap (buf, &map);
if (ret) { if (ret) {
GstFlowReturn res; GstFlowReturn res;
*skipsize = 0; *skipsize = 0;
g_assert (fsize <= buf_size); g_assert (off <= map.size);
res = gst_mpegv_parse_parse_frame (parse, frame); res = gst_mpegv_parse_parse_frame (parse, frame);
if (res == GST_BASE_PARSE_FLOW_DROPPED) if (res == GST_BASE_PARSE_FLOW_DROPPED)
frame->flags |= GST_BASE_PARSE_FRAME_FLAG_DROP; frame->flags |= GST_BASE_PARSE_FRAME_FLAG_DROP;
return gst_base_parse_finish_frame (parse, frame, fsize); return gst_base_parse_finish_frame (parse, frame, off);
} }
return GST_FLOW_OK; return GST_FLOW_OK;

View file

@ -51,7 +51,8 @@ struct _GstMpegvParse {
GstBaseParse element; GstBaseParse element;
/* parse state */ /* parse state */
GList *typeoffsize; gint ext_offsets[10];
gint ext_count;
gint last_sc; gint last_sc;
gint seq_offset; gint seq_offset;
gint seq_size; gint seq_size;