typefinding: more fine-grained ogg typefinding
authorTim-Philipp Müller <tim.muller@collabora.co.uk>
Sun, 15 Apr 2012 21:32:06 +0000 (22:32 +0100)
committerTim-Philipp Müller <tim.muller@collabora.co.uk>
Sun, 15 Apr 2012 21:38:10 +0000 (22:38 +0100)
Typefind to audio/ogg, video/ogg, etc. Also change
application/x-annodex to application/annodex.

See http://wiki.xiph.org/MIME_Types_and_File_Extensions

gst/typefind/gsttypefindfunctions.c

index 50beb0a..d6d787a 100644 (file)
@@ -3589,30 +3589,124 @@ dv_type_find (GstTypeFind * tf, gpointer private)
 }
 
 
-/*** application/ogg and application/x-annodex ***/
-static GstStaticCaps ogg_caps = GST_STATIC_CAPS ("application/ogg");
-static GstStaticCaps annodex_caps = GST_STATIC_CAPS ("application/x-annodex");
+/*** Ogg and Annodex variants ***/
 static GstStaticCaps ogg_annodex_caps =
-    GST_STATIC_CAPS ("application/ogg;application/x-annodex");
+    GST_STATIC_CAPS ("application/ogg;video/ogg;audio/ogg;"
+    "application/annodex;audio/annodex;video/annodex;application/kate");
 
 #define OGGANX_CAPS (gst_static_caps_get(&ogg_annodex_caps))
 
+typedef enum
+{
+  OGG_AUDIO = 0,
+  OGG_VIDEO,
+  OGG_KATE,
+  OGG_OTHER,
+  OGG_SKELETON,
+  OGG_ANNODEX,
+  OGG_NUM
+} GstOggStreamType;
+
 static void
 ogganx_type_find (GstTypeFind * tf, gpointer private)
 {
-  const guint8 *data = gst_type_find_peek (tf, 0, 4);
+  const gchar *media_type;
+  DataScanCtx c = { 0, NULL, 0 };
+  guint ogg_syncs = 0;
+  guint hdr_count[OGG_NUM] = { 0, };
+  static const struct
+  {
+    const gchar marker[10];
+    guint8 marker_size;
+    GstOggStreamType stream_type;
+  } markers[] = {
+    {
+    "\001vorbis", 7, OGG_AUDIO}, {
+    "\200theora", 7, OGG_VIDEO}, {
+    "fLaC", 4, OGG_AUDIO}, {
+    "\177FLAC", 5, OGG_AUDIO}, {
+    "Speex", 5, OGG_AUDIO}, {
+    "CMML\0\0\0\0", 8, OGG_OTHER}, {
+    "PCM     ", 8, OGG_AUDIO}, {
+    "Annodex", 7, OGG_ANNODEX}, {
+    "fishead", 7, OGG_SKELETON}, {
+    "AnxData", 7, OGG_ANNODEX}, {
+    "CELT    ", 8, OGG_AUDIO}, {
+    "\200kate\0\0\0", 8, OGG_KATE}, {
+    "BBCD\0", 5, OGG_VIDEO}, {
+    "OVP80\1\1", 7, OGG_VIDEO}, {
+    "OpusHead", 8, OGG_AUDIO}, {
+    "\001audio\0\0\0", 9, OGG_AUDIO}, {
+    "\001video\0\0\0", 9, OGG_VIDEO}, {
+    "\001text\0\0\0", 9, OGG_OTHER}
+  };
+
+  while (c.offset < 4096 && data_scan_ctx_ensure_data (tf, &c, 64)) {
+    guint size, i;
+
+    if (memcmp (c.data, "OggS", 5) != 0)
+      break;
+
+    ++ogg_syncs;
+
+    /* check if BOS */
+    if (c.data[5] != 0x02)
+      break;
+
+    /* headers should only have one segment */
+    if (c.data[26] != 1)
+      break;
+
+    size = c.data[27];
+    if (size < 8)
+      break;
+
+    data_scan_ctx_advance (tf, &c, 28);
+
+    if (!data_scan_ctx_ensure_data (tf, &c, MAX (size, 8)))
+      break;
+
+    for (i = 0; i < G_N_ELEMENTS (markers); ++i) {
+      if (memcmp (c.data, markers[i].marker, markers[i].marker_size) == 0) {
+        ++hdr_count[markers[i].stream_type];
+        break;
+      }
+    }
 
-  if ((data != NULL) && (memcmp (data, "OggS", 4) == 0)) {
+    if (i == G_N_ELEMENTS (markers)) {
+      GST_MEMDUMP ("unknown Ogg stream marker", c.data, size);
+      ++hdr_count[OGG_OTHER];
+    }
+
+    data_scan_ctx_advance (tf, &c, size);
+  }
 
-    /* Check for an annodex fishbone header */
-    data = gst_type_find_peek (tf, 28, 8);
-    if (data && memcmp (data, "fishead\0", 8) == 0)
-      gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM,
-          gst_static_caps_get (&annodex_caps));
+  if (ogg_syncs == 0)
+    return;
 
-    gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM,
-        gst_static_caps_get (&ogg_caps));
+  /* FIXME: what about XSPF? */
+  if (hdr_count[OGG_ANNODEX] > 0) {
+    if (hdr_count[OGG_VIDEO] > 0)
+      media_type = "video/annodex";
+    else if (hdr_count[OGG_AUDIO] > 0)
+      media_type = "audio/annodex";
+    else
+      media_type = "application/annodex";
+  } else if (hdr_count[OGG_VIDEO] > 0) {
+    media_type = "video/ogg";
+  } else if (hdr_count[OGG_AUDIO] > 0) {
+    media_type = "audio/ogg";
+  } else if (hdr_count[OGG_KATE] > 0 && hdr_count[OGG_OTHER] == 0) {
+    media_type = "application/kate";
+  } else {
+    media_type = "application/ogg";
   }
+
+  GST_INFO ("found %s (audio:%u, video:%u, annodex:%u, skeleton:%u, other:%u)",
+      media_type, hdr_count[OGG_AUDIO], hdr_count[OGG_VIDEO],
+      hdr_count[OGG_ANNODEX], hdr_count[OGG_SKELETON], hdr_count[OGG_OTHER]);
+
+  gst_type_find_suggest_simple (tf, GST_TYPE_FIND_MAXIMUM, media_type, NULL);
 }
 
 /*** audio/x-vorbis ***/
@@ -4455,7 +4549,8 @@ plugin_init (GstPlugin * plugin)
   TYPE_FIND_REGISTER (plugin, "video/mpegts", GST_RANK_PRIMARY,
       mpeg_ts_type_find, "ts,mts", MPEGTS_CAPS, NULL, NULL);
   TYPE_FIND_REGISTER (plugin, "application/ogg", GST_RANK_PRIMARY,
-      ogganx_type_find, "anx,ogg,ogm", OGGANX_CAPS, NULL, NULL);
+      ogganx_type_find, "ogg,oga,ogv,ogm,ogx,spx,anx,axa,axv", OGGANX_CAPS,
+      NULL, NULL);
   TYPE_FIND_REGISTER (plugin, "video/mpeg-elementary", GST_RANK_MARGINAL,
       mpeg_video_stream_type_find, "mpv,mpeg,mpg", MPEG_VIDEO_CAPS, NULL, NULL);
   TYPE_FIND_REGISTER (plugin, "video/mpeg4", GST_RANK_PRIMARY,