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
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;
while (i < (size - 4)) {
while (i <= (size - 4)) {
if (data[i + 2] > 1) {
i += 3;
} 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;
/* 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
* detect packets as a list of #GstMpegVideoTypeOffsetSize.
*
* Returns: a #GList of #GstMpegVideoTypeOffsetSize
* Returns: TRUE if a packet start code was found
*/
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)
{
gint off, rsize;
gint off;
GstByteReader br;
GList *ret = NULL;
if (!initialized) {
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) {
GST_DEBUG ("Can't parse from offset %d, buffer is to small", offset);
return NULL;
return FALSE;
}
size -= offset;
gst_byte_reader_init (&br, &data[offset], size);
off = scan_for_start_codes (&br, 0, size);
if (off < 0) {
GST_DEBUG ("No start code prefix in this buffer");
return NULL;
return FALSE;
}
while (off >= 0 && off + 3 < size) {
GstMpegVideoTypeOffsetSize *codoffsize;
if (gst_byte_reader_skip (&br, off + 3) == FALSE)
goto failed;
if (gst_byte_reader_get_uint8 (&br, &packet->type) == FALSE)
goto failed;
if (gst_byte_reader_skip (&br, off + 3) == FALSE)
goto failed;
packet->data = data;
packet->offset = offset + off + 4;
packet->size = -1;
codoffsize = g_malloc (sizeof (GstMpegVideoTypeOffsetSize));
if (gst_byte_reader_get_uint8 (&br, &codoffsize->type) == FALSE)
goto failed;
/* try to find end of packet */
size -= off + 4;
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);
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);
return TRUE;
failed:
{
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 _GstMpegVideoPictureExt GstMpegVideoPictureExt;
typedef struct _GstMpegVideoQuantMatrixExt GstMpegVideoQuantMatrixExt;
typedef struct _GstMpegVideoTypeOffsetSize GstMpegVideoTypeOffsetSize;
typedef struct _GstMpegVideoPacket GstMpegVideoPacket;
/**
* GstMpegVideoSequenceHdr:
@ -362,20 +362,24 @@ struct _GstMpegVideoGop
/**
* GstMpegVideoTypeOffsetSize:
*
* @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
* @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
*/
struct _GstMpegVideoTypeOffsetSize
struct _GstMpegVideoPacket
{
const guint8 *data;
guint8 type;
guint offset;
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,
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->pic_offset = -1;
mpvparse->frame_repeat_count = 0;
memset (mpvparse->ext_offsets, 0, sizeof (mpvparse->ext_offsets));
mpvparse->ext_count = 0;
}
static void
@ -227,18 +229,19 @@ static gboolean
gst_mpegv_parse_process_config (GstMpegvParse * mpvparse, GstBuffer * buf,
guint size)
{
GList *tmp;
guint8 *data;
guint8 *data_with_prefix;
GstMapInfo map;
gst_buffer_map (buf, &map, GST_MAP_READ);
data = map.data + mpvparse->seq_offset;
if (mpvparse->seq_offset < 4) {
/* This shouldn't happen, but just in case... */
GST_WARNING_OBJECT (mpvparse, "Sequence header start code missing.");
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 -
used for codec private data */
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,
map.size - mpvparse->seq_offset, 0)) {
size - mpvparse->seq_offset, 0)) {
if (mpvparse->fps_num == 0 || mpvparse->fps_den == 0) {
mpvparse->fps_num = mpvparse->sequencehdr.fps_n;
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 */
if (mpvparse->mpeg_version <= 0) {
GstMpegVideoTypeOffsetSize *tpoffsz;
gint i, offset;
mpvparse->mpeg_version = 1;
for (tmp = mpvparse->typeoffsize; tmp; tmp = tmp->next) {
tpoffsz = tmp->data;
if (tpoffsz->type == GST_MPEG_VIDEO_PACKET_EXTENSION) {
mpvparse->mpeg_version = 2;
if (gst_mpeg_video_parse_sequence_extension (&mpvparse->sequenceext,
map.data, map.size, tpoffsz->offset)) {
mpvparse->fps_num =
mpvparse->sequencehdr.fps_n * (mpvparse->sequenceext.fps_n_ext +
1);
mpvparse->fps_den =
mpvparse->sequencehdr.fps_d * (mpvparse->sequenceext.fps_d_ext +
1);
}
for (i = 0; i < mpvparse->ext_count; ++i) {
offset = mpvparse->ext_offsets[i];
mpvparse->mpeg_version = 2;
if (offset < size &&
gst_mpeg_video_parse_sequence_extension (&mpvparse->sequenceext,
map.data, size, offset)) {
mpvparse->fps_num =
mpvparse->sequencehdr.fps_n * (mpvparse->sequenceext.fps_n_ext + 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:
GST_LOG_OBJECT (mpvparse, "startcode is VIDEO PACKET EXTENSION");
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:
packet = FALSE;
break;
@ -444,6 +444,8 @@ gst_mpegv_parse_process_sc (GstMpegvParse * mpvparse,
/* set size to avoid processing config again */
if (mpvparse->seq_offset >= 0 && off != mpvparse->seq_offset &&
!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);
mpvparse->seq_size = off - mpvparse->seq_offset;
}
@ -485,7 +487,6 @@ update_frame_parsing_status (GstMpegvParse * mpvparse,
}
}
static GstFlowReturn
gst_mpegv_parse_handle_frame (GstBaseParse * parse,
GstBaseParseFrame * frame, gint * skipsize)
@ -493,121 +494,108 @@ gst_mpegv_parse_handle_frame (GstBaseParse * parse,
GstMpegvParse *mpvparse = GST_MPEGVIDEO_PARSE (parse);
GstBuffer *buf = frame->buffer;
gboolean ret = FALSE;
GList *tmp;
gint off = 0, fsize = -1;
gint off = 0;
GstMpegVideoPacket packet;
guint8 *data;
gint size;
GstMapInfo map;
gsize buf_size;
GstMpegVideoTypeOffsetSize *codoffsz;
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);
buf_size = map.size;
mpvparse->typeoffsize = gst_mpeg_video_parse (map.data, map.size, off);
gst_buffer_unmap (buf, &map);
data = map.data;
size = map.size;
/* No sc found */
if (mpvparse->typeoffsize == NULL) {
GST_LOG_OBJECT (mpvparse, "no start code");
*skipsize = gst_buffer_get_size (buf) - 3;
goto end;
retry:
/* at least start code and subsequent byte */
if (G_UNLIKELY (size < 5 + off))
goto exit;
/* 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 initial start code not at start, arrange to make it so */
if (mpvparse->last_sc < 0) {
/* check for sane start code */
for (tmp = mpvparse->typeoffsize; tmp; tmp = g_list_next (tmp)) {
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 (!gst_mpeg_video_parse (&packet, data, size, off)) {
/* didn't find anything that looks like a sync word, skip */
GST_LOG_OBJECT (mpvparse, "no start code found");
*skipsize = size - 3;
goto exit;
}
if (mpvparse->last_sc < 0) {
GST_LOG_OBJECT (mpvparse, "no valid start code");
*skipsize = gst_buffer_get_size (buf) - 3;
goto end;
off = packet.offset - 4;
GST_LOG_OBJECT (mpvparse, "possible sync at buffer offset %d", off);
/* 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 */
*skipsize = 0;
/* initial packet processed above */
for (tmp = mpvparse->typeoffsize; tmp; tmp = g_list_next (tmp)) {
codoffsz = tmp->data;
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);
/* position a bit further than last sc */
off++;
/* so now we have start code at start of data; locate next start code */
if (!gst_mpeg_video_parse (&packet, data, size, off)) {
off = -1;
} else {
/* need some more data */
/* request best next available */
ret = FALSE;
GST_LOG_OBJECT (mpvparse, "obtaining more data");
g_assert (packet.offset >= 4);
off = packet.offset - 4;
}
g_list_foreach (mpvparse->typeoffsize, (GFunc) g_free, NULL);
g_list_free (mpvparse->typeoffsize);
mpvparse->typeoffsize = NULL;
GST_LOG_OBJECT (mpvparse, "next start code at %d", off);
if (off < 0) {
/* 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) {
GstFlowReturn res;
*skipsize = 0;
g_assert (fsize <= buf_size);
g_assert (off <= map.size);
res = gst_mpegv_parse_parse_frame (parse, frame);
if (res == GST_BASE_PARSE_FLOW_DROPPED)
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;

View file

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