mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-23 10:11:08 +00:00
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:
parent
29a947642e
commit
28f3858b94
4 changed files with 133 additions and 150 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue