gst/typefind/gsttypefindfunctions.c: Rework mpeg video stream typefinding a bit more...
authorTim-Philipp Müller <tim@centricular.net>
Wed, 30 Apr 2008 20:54:56 +0000 (20:54 +0000)
committerTim-Philipp Müller <tim@centricular.net>
Wed, 30 Apr 2008 20:54:56 +0000 (20:54 +0000)
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.

ChangeLog
gst/typefind/gsttypefindfunctions.c

index fe1c7f5..336e0a9 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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>
 
        * ext/theora/theoradec.c:
@@ -10,9 +29,9 @@
        * gst/typefind/gsttypefindfunctions.c: (MpegVideoStreamCtx),
          (mpeg_video_stream_ctx_advance), (mpeg_video_stream_ctx_ensure_data),
          (mpeg_video_stream_type_find):
-         Refactor a bit: use context structure to track parsing offset and size of
-         available data and make the code a bit clearer. Fixes bad memory access
-         in #356937.
+         Refactor a bit: use context structure to track parsing offset and
+         size of available data and make the code a bit clearer. Fixes bad
+         memory access in #356937.
 
 2008-04-28  Michael Smith <msmith@songbirdnest.com>
 
index acad8f9..5f8c284 100644 (file)
@@ -1636,23 +1636,6 @@ done:
 static GstStaticCaps mpeg_video_caps = GST_STATIC_CAPS ("video/mpeg, "
     "systemstream = (boolean) false");
 #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
@@ -1696,76 +1679,102 @@ mpeg_video_stream_ctx_ensure_data (GstTypeFind * tf, MpegVideoStreamCtx * c,
     return TRUE;
 
   c->data = gst_type_find_peek (tf, c->offset, GST_MPEGVID_TYPEFIND_SYNC_SIZE);
-  if (c->data == NULL)
-    return FALSE;
+  if (c->data != NULL) {
+    c->size = GST_MPEGVID_TYPEFIND_SYNC_SIZE;
+    return TRUE;
+  }
 
-  c->size = GST_MPEGVID_TYPEFIND_SYNC_SIZE;
-  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
 mpeg_video_stream_type_find (GstTypeFind * tf, gpointer unused)
 {
   MpegVideoStreamCtx c = { 0, NULL, 0 };
+  gboolean seen_seq = FALSE;
+  gboolean seen_gop = FALSE;
+  guint num_pic_headers = 0;
   gint found = 0;
 
-  while (1) {
-    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)
+  while (c.offset < GST_MPEGVID_TYPEFIND_TRY_SYNC) {
+    if (found >= GST_MPEGVID_TYPEFIND_TRY_PICTURES)
       break;
 
     if (!mpeg_video_stream_ctx_ensure_data (tf, &c, 5))
       break;
 
-    if (IS_MPEG_HEADER (c.data)) {
-      /* An MPEG PACK header indicates that this isn't an elementary stream */
-      if (IS_MPEG_PACK_CODE (c.data[3])) {
-        if (mpeg_sys_is_valid_pack (tf, c.data, c.size, NULL))
-          break;
-      }
+    if (!IS_MPEG_HEADER (c.data))
+      goto next;
 
-      /* are we a sequence (0xB3) header? */
-      if (c.data[3] == 0xB3) {
-        mpeg_video_stream_ctx_advance (tf, &c, 4 + 8);
-        continue;
-      }
+    /* 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;
 
-      /* ... or a GOP (0xB8) header? */
-      if (c.data[3] == 0xB8) {
-        mpeg_video_stream_ctx_advance (tf, &c, 8);
-        continue;
-      }
+    /* do we have a sequence header? */
+    if (c.data[3] == 0xB3) {
+      seen_seq = TRUE;
+      mpeg_video_stream_ctx_advance (tf, &c, 4 + 8);
+      continue;
+    }
 
-      /* ... else, we should now see an image header ... */
-      /* is [4] really what we want here, not [3]? Won't [4] == 0 only work for
-       * the first image or the first few images? (tpm) */
-      if (c.data[4] == 0x00) {
-        mpeg_video_stream_ctx_advance (tf, &c, 8);
+    /* we really want to see a sequence header first */
+    if (!seen_seq)
+      goto next;
 
-        if (!mpeg_video_stream_ctx_ensure_data (tf, &c, 5))
-          break;
+    /* next, a GOP header would be nice */
+    if (c.data[3] == 0xB8) {
+      seen_gop = TRUE;
+      mpeg_video_stream_ctx_advance (tf, &c, 8);
+      continue;
+    }
 
-        /* .. 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);
-          found += 1;
-          continue;
-        }
-      }
+    /* we really want to see a sequence+GOP header before continuing */
+    if (!seen_gop)
+      goto next;
+
+    /* 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);
+      continue;
     }
 
+    /* ... each followed by a slice header with slice_vertical_pos=1 */
+    if (c.data[3] == 0x01 && num_pic_headers > found) {
+      mpeg_video_stream_ctx_advance (tf, &c, 4);
+      found += 1;
+      continue;
+    }
+
+  next:
+
     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 ***/
@@ -3054,9 +3063,7 @@ plugin_init (GstPlugin * plugin)
       mpeg_ts_type_find, mpeg_ts_exts, MPEGTS_CAPS, NULL, NULL);
   TYPE_FIND_REGISTER (plugin, "application/ogg", GST_RANK_PRIMARY,
       ogganx_type_find, ogg_exts, OGGANX_CAPS, NULL, NULL);
-  TYPE_FIND_REGISTER (plugin, "video/mpeg,elementary", GST_RANK_SECONDARY,
-      mpeg_video_type_find, mpeg_video_exts, MPEG_VIDEO_CAPS, NULL, NULL);
-  TYPE_FIND_REGISTER (plugin, "video/mpeg-stream", GST_RANK_MARGINAL,
+  TYPE_FIND_REGISTER (plugin, "video/mpeg-elementary", GST_RANK_MARGINAL,
       mpeg_video_stream_type_find, mpeg_video_exts, MPEG_VIDEO_CAPS, NULL,
       NULL);
   TYPE_FIND_REGISTER (plugin, "video/mpeg4", GST_RANK_PRIMARY,