gst/typefind/gsttypefindfunctions.c: Rework mpeg video stream typefinding a bit more: make sure sequence,

Original commit message from CVS:
* gst/typefind/gsttypefindfunctions.c:
(mpeg_video_stream_ctx_ensure_data), (mpeg_video_stream_type_find),
(plugin_init):
Rework mpeg video stream typefinding a bit more: make sure sequence,
GOP, picture and slice headers appear in the order they should and
that we've in fact at least had one of each; fix picture header
detection; decouple picture and slice header check - don't assume
they're at a fixed offset, there may be extra data in between. Also,
announce varying degrees of probability depending on what we found
exactly (multiple pictures, at least one picture, just sequence and
GOP headers). Finally, in _ensure_data(), take into account that we
might be typefinding smaller amounts of data, such as the first
buffer of a stream, so fall back to the minimum size needed as long
as that's available, instead of erroring out if there's less than
2kB of data. Fixes #526173. Conveniently also doesn't recognise the
fuzzed file from #399342 as valid.
This commit is contained in:
Tim-Philipp Müller 2008-04-30 20:54:56 +00:00
parent 0947ecf74c
commit f8977b9e9e
2 changed files with 99 additions and 73 deletions

View file

@ -1,3 +1,22 @@
2008-04-30 Tim-Philipp Müller <tim.muller at collabora co uk>
* gst/typefind/gsttypefindfunctions.c:
(mpeg_video_stream_ctx_ensure_data), (mpeg_video_stream_type_find),
(plugin_init):
Rework mpeg video stream typefinding a bit more: make sure sequence,
GOP, picture and slice headers appear in the order they should and
that we've in fact at least had one of each; fix picture header
detection; decouple picture and slice header check - don't assume
they're at a fixed offset, there may be extra data in between. Also,
announce varying degrees of probability depending on what we found
exactly (multiple pictures, at least one picture, just sequence and
GOP headers). Finally, in _ensure_data(), take into account that we
might be typefinding smaller amounts of data, such as the first
buffer of a stream, so fall back to the minimum size needed as long
as that's available, instead of erroring out if there's less than
2kB of data. Fixes #526173. Conveniently also doesn't recognise the
fuzzed file from #399342 as valid.
2008-04-30 Michael Smith <msmith@songbirdnest.com> 2008-04-30 Michael Smith <msmith@songbirdnest.com>
* ext/theora/theoradec.c: * ext/theora/theoradec.c:
@ -10,9 +29,9 @@
* gst/typefind/gsttypefindfunctions.c: (MpegVideoStreamCtx), * gst/typefind/gsttypefindfunctions.c: (MpegVideoStreamCtx),
(mpeg_video_stream_ctx_advance), (mpeg_video_stream_ctx_ensure_data), (mpeg_video_stream_ctx_advance), (mpeg_video_stream_ctx_ensure_data),
(mpeg_video_stream_type_find): (mpeg_video_stream_type_find):
Refactor a bit: use context structure to track parsing offset and size of Refactor a bit: use context structure to track parsing offset and
available data and make the code a bit clearer. Fixes bad memory access size of available data and make the code a bit clearer. Fixes bad
in #356937. memory access in #356937.
2008-04-28 Michael Smith <msmith@songbirdnest.com> 2008-04-28 Michael Smith <msmith@songbirdnest.com>

View file

@ -1636,23 +1636,6 @@ done:
static GstStaticCaps mpeg_video_caps = GST_STATIC_CAPS ("video/mpeg, " static GstStaticCaps mpeg_video_caps = GST_STATIC_CAPS ("video/mpeg, "
"systemstream = (boolean) false"); "systemstream = (boolean) false");
#define MPEG_VIDEO_CAPS gst_static_caps_get(&mpeg_video_caps) #define MPEG_VIDEO_CAPS gst_static_caps_get(&mpeg_video_caps)
static void
mpeg_video_type_find (GstTypeFind * tf, gpointer unused)
{
static const guint8 sequence_header[] = { 0x00, 0x00, 0x01, 0xb3 };
guint8 *data = NULL;
data = gst_type_find_peek (tf, 0, 8);
if (data && memcmp (data, sequence_header, 4) == 0) {
GstCaps *caps = gst_caps_copy (MPEG_VIDEO_CAPS);
gst_structure_set (gst_caps_get_structure (caps, 0), "mpegversion",
G_TYPE_INT, 1, NULL);
gst_type_find_suggest (tf, GST_TYPE_FIND_POSSIBLE, caps);
gst_caps_unref (caps);
}
}
/* /*
* Idea is the same as MPEG system stream typefinding: We check each * Idea is the same as MPEG system stream typefinding: We check each
@ -1696,76 +1679,102 @@ mpeg_video_stream_ctx_ensure_data (GstTypeFind * tf, MpegVideoStreamCtx * c,
return TRUE; return TRUE;
c->data = gst_type_find_peek (tf, c->offset, GST_MPEGVID_TYPEFIND_SYNC_SIZE); c->data = gst_type_find_peek (tf, c->offset, GST_MPEGVID_TYPEFIND_SYNC_SIZE);
if (c->data == NULL) if (c->data != NULL) {
return FALSE;
c->size = GST_MPEGVID_TYPEFIND_SYNC_SIZE; c->size = GST_MPEGVID_TYPEFIND_SYNC_SIZE;
return TRUE; return TRUE;
} }
/* try min_size as fallback: we might be typefinding the first buffer of the
* stream and not have as much data available as we'd like */
c->data = gst_type_find_peek (tf, c->offset, min_len);
if (c->data != NULL) {
c->size = min_len;
return TRUE;
}
return FALSE;
}
static void static void
mpeg_video_stream_type_find (GstTypeFind * tf, gpointer unused) mpeg_video_stream_type_find (GstTypeFind * tf, gpointer unused)
{ {
MpegVideoStreamCtx c = { 0, NULL, 0 }; MpegVideoStreamCtx c = { 0, NULL, 0 };
gboolean seen_seq = FALSE;
gboolean seen_gop = FALSE;
guint num_pic_headers = 0;
gint found = 0; gint found = 0;
while (1) { while (c.offset < GST_MPEGVID_TYPEFIND_TRY_SYNC) {
if (found >= GST_MPEGVID_TYPEFIND_TRY_PICTURES) { if (found >= GST_MPEGVID_TYPEFIND_TRY_PICTURES)
GstCaps *caps = gst_caps_copy (MPEG_VIDEO_CAPS);
gst_structure_set (gst_caps_get_structure (caps, 0), "mpegversion",
G_TYPE_INT, 1, NULL);
gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM - 2, caps);
gst_caps_unref (caps);
return;
}
if (c.offset >= GST_MPEGVID_TYPEFIND_TRY_SYNC)
break; break;
if (!mpeg_video_stream_ctx_ensure_data (tf, &c, 5)) if (!mpeg_video_stream_ctx_ensure_data (tf, &c, 5))
break; break;
if (IS_MPEG_HEADER (c.data)) { if (!IS_MPEG_HEADER (c.data))
/* An MPEG PACK header indicates that this isn't an elementary stream */ goto next;
if (IS_MPEG_PACK_CODE (c.data[3])) {
if (mpeg_sys_is_valid_pack (tf, c.data, c.size, NULL))
break;
}
/* are we a sequence (0xB3) header? */ /* a pack header indicates that this isn't an elementary stream */
if (c.data[3] == 0xBA && mpeg_sys_is_valid_pack (tf, c.data, c.size, NULL))
return;
/* do we have a sequence header? */
if (c.data[3] == 0xB3) { if (c.data[3] == 0xB3) {
seen_seq = TRUE;
mpeg_video_stream_ctx_advance (tf, &c, 4 + 8); mpeg_video_stream_ctx_advance (tf, &c, 4 + 8);
continue; continue;
} }
/* ... or a GOP (0xB8) header? */ /* we really want to see a sequence header first */
if (!seen_seq)
goto next;
/* next, a GOP header would be nice */
if (c.data[3] == 0xB8) { if (c.data[3] == 0xB8) {
seen_gop = TRUE;
mpeg_video_stream_ctx_advance (tf, &c, 8); mpeg_video_stream_ctx_advance (tf, &c, 8);
continue; continue;
} }
/* ... else, we should now see an image header ... */ /* we really want to see a sequence+GOP header before continuing */
/* is [4] really what we want here, not [3]? Won't [4] == 0 only work for if (!seen_gop)
* the first image or the first few images? (tpm) */ goto next;
if (c.data[4] == 0x00) {
/* now that we've had a sequence+GOP, we'd like to see a picture header */
if (c.data[3] == 0x00) {
++num_pic_headers;
mpeg_video_stream_ctx_advance (tf, &c, 8); mpeg_video_stream_ctx_advance (tf, &c, 8);
continue;
}
if (!mpeg_video_stream_ctx_ensure_data (tf, &c, 5)) /* ... each followed by a slice header with slice_vertical_pos=1 */
break; if (c.data[3] == 0x01 && num_pic_headers > found) {
/* .. followed by a slice header */
if ((IS_MPEG_HEADER (c.data + 0) && c.data[3] == 0x01) ||
(IS_MPEG_HEADER (c.data + 1) && c.data[4] == 0x01)) {
mpeg_video_stream_ctx_advance (tf, &c, 4); mpeg_video_stream_ctx_advance (tf, &c, 4);
found += 1; found += 1;
continue; continue;
} }
}
} next:
mpeg_video_stream_ctx_advance (tf, &c, 1); mpeg_video_stream_ctx_advance (tf, &c, 1);
} }
if (found > 0 || num_pic_headers > 0) {
GstTypeFindProbability probability;
GstCaps *caps;
if (found >= GST_MPEGVID_TYPEFIND_TRY_PICTURES)
probability = GST_TYPE_FIND_MAXIMUM - 2;
else if (found > 0)
probability = GST_TYPE_FIND_POSSIBLE + 1;
else
probability = GST_TYPE_FIND_POSSIBLE - 10;
caps = gst_caps_copy (MPEG_VIDEO_CAPS);
gst_caps_set_simple (caps, "mpegversion", G_TYPE_INT, 1, NULL);
gst_type_find_suggest (tf, probability, caps);
gst_caps_unref (caps);
}
} }
/*** audio/x-aiff ***/ /*** audio/x-aiff ***/
@ -3054,9 +3063,7 @@ plugin_init (GstPlugin * plugin)
mpeg_ts_type_find, mpeg_ts_exts, MPEGTS_CAPS, NULL, NULL); mpeg_ts_type_find, mpeg_ts_exts, MPEGTS_CAPS, NULL, NULL);
TYPE_FIND_REGISTER (plugin, "application/ogg", GST_RANK_PRIMARY, TYPE_FIND_REGISTER (plugin, "application/ogg", GST_RANK_PRIMARY,
ogganx_type_find, ogg_exts, OGGANX_CAPS, NULL, NULL); ogganx_type_find, ogg_exts, OGGANX_CAPS, NULL, NULL);
TYPE_FIND_REGISTER (plugin, "video/mpeg,elementary", GST_RANK_SECONDARY, TYPE_FIND_REGISTER (plugin, "video/mpeg-elementary", GST_RANK_MARGINAL,
mpeg_video_type_find, mpeg_video_exts, MPEG_VIDEO_CAPS, NULL, NULL);
TYPE_FIND_REGISTER (plugin, "video/mpeg-stream", GST_RANK_MARGINAL,
mpeg_video_stream_type_find, mpeg_video_exts, MPEG_VIDEO_CAPS, NULL, mpeg_video_stream_type_find, mpeg_video_exts, MPEG_VIDEO_CAPS, NULL,
NULL); NULL);
TYPE_FIND_REGISTER (plugin, "video/mpeg4", GST_RANK_PRIMARY, TYPE_FIND_REGISTER (plugin, "video/mpeg4", GST_RANK_PRIMARY,