diff --git a/gst-libs/gst/codecparsers/gstmpegvideoparser.c b/gst-libs/gst/codecparsers/gstmpegvideoparser.c index e9cddd2f8c..eb6e61913a 100644 --- a/gst-libs/gst/codecparsers/gstmpegvideoparser.c +++ b/gst-libs/gst/codecparsers/gstmpegvideoparser.c @@ -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; } } diff --git a/gst-libs/gst/codecparsers/gstmpegvideoparser.h b/gst-libs/gst/codecparsers/gstmpegvideoparser.h index 7c23114eab..cf2ff02711 100644 --- a/gst-libs/gst/codecparsers/gstmpegvideoparser.h +++ b/gst-libs/gst/codecparsers/gstmpegvideoparser.h @@ -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); diff --git a/gst/videoparsers/gstmpegvideoparse.c b/gst/videoparsers/gstmpegvideoparse.c index f9e6db1e40..fc328b3f74 100644 --- a/gst/videoparsers/gstmpegvideoparse.c +++ b/gst/videoparsers/gstmpegvideoparse.c @@ -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; diff --git a/gst/videoparsers/gstmpegvideoparse.h b/gst/videoparsers/gstmpegvideoparse.h index 21b05d34e9..47ad4ceecc 100644 --- a/gst/videoparsers/gstmpegvideoparse.h +++ b/gst/videoparsers/gstmpegvideoparse.h @@ -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;