From beb50032496d9cb15a8fec15d42df3ec54cff95e Mon Sep 17 00:00:00 2001
From: David Schleef <ds@schleef.org>
Date: Tue, 20 Apr 2004 08:27:15 +0000
Subject: [PATCH] gst/qtdemux/qtdemux.c: A number of new features and hacks to
 extract the esds atom and put it into the caps.  (bug #1...

Original commit message from CVS:
* gst/qtdemux/qtdemux.c: (gst_qtdemux_handle_sink_event),
(qtdemux_parse), (qtdemux_type_get), (qtdemux_dump_mvhd),
(qtdemux_dump_tkhd), (qtdemux_dump_stsd), (qtdemux_dump_unknown),
(qtdemux_parse_trak), (qtdemux_video_caps), (qtdemux_audio_caps):
A number of new features and hacks to extract the esds atom and
put it into the caps.  (bug #137724)
---
 ChangeLog             |   9 +++
 gst/qtdemux/qtdemux.c | 149 +++++++++++++++++++++++++++++++++++++-----
 2 files changed, 142 insertions(+), 16 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index c0a8627d46..041f965d4f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2004-04-20  David Schleef  <ds@schleef.org>
+
+	* gst/qtdemux/qtdemux.c: (gst_qtdemux_handle_sink_event),
+	(qtdemux_parse), (qtdemux_type_get), (qtdemux_dump_mvhd),
+	(qtdemux_dump_tkhd), (qtdemux_dump_stsd), (qtdemux_dump_unknown),
+	(qtdemux_parse_trak), (qtdemux_video_caps), (qtdemux_audio_caps):
+	A number of new features and hacks to extract the esds atom and
+	put it into the caps.  (bug #137724)
+
 2004-04-19  David Schleef  <ds@schleef.org>
 
 	* gconf/Makefile.am: Fix for non-GNU make
diff --git a/gst/qtdemux/qtdemux.c b/gst/qtdemux/qtdemux.c
index 21fe628450..a25fdb1805 100644
--- a/gst/qtdemux/qtdemux.c
+++ b/gst/qtdemux/qtdemux.c
@@ -125,7 +125,7 @@ enum
 static GstStaticPadTemplate gst_qtdemux_sink_template =
     GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
-    GST_PAD_SOMETIMES,
+    GST_PAD_ALWAYS,
     GST_STATIC_CAPS ("video/quicktime; audio/x-m4a")
     );
 
@@ -159,7 +159,7 @@ static void qtdemux_parse_tree (GstQTDemux * qtdemux);
 static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux, guint32 fourcc,
     const guint8 * stsd_data);
 static GstCaps *qtdemux_audio_caps (GstQTDemux * qtdemux, guint32 fourcc,
-    const guint8 * data);
+    const guint8 * data, int len);
 
 static GType
 gst_qtdemux_get_type (void)
@@ -797,6 +797,10 @@ gst_qtdemux_add_stream (GstQTDemux * qtdemux, QtDemuxStream * stream)
 #define FOURCC_cmov	GST_MAKE_FOURCC('c','m','o','v')
 #define FOURCC_dcom	GST_MAKE_FOURCC('d','c','o','m')
 #define FOURCC_cmvd	GST_MAKE_FOURCC('c','m','v','d')
+#define FOURCC_hint	GST_MAKE_FOURCC('h','i','n','t')
+#define FOURCC_mp4a	GST_MAKE_FOURCC('m','p','4','a')
+#define FOURCC_wave	GST_MAKE_FOURCC('w','a','v','e')
+#define FOURCC_esds	GST_MAKE_FOURCC('e','s','d','s')
 
 
 static void qtdemux_dump_mvhd (GstQTDemux * qtdemux, void *buffer, int depth);
@@ -815,6 +819,8 @@ static void qtdemux_dump_stco (GstQTDemux * qtdemux, void *buffer, int depth);
 static void qtdemux_dump_co64 (GstQTDemux * qtdemux, void *buffer, int depth);
 static void qtdemux_dump_dcom (GstQTDemux * qtdemux, void *buffer, int depth);
 static void qtdemux_dump_cmvd (GstQTDemux * qtdemux, void *buffer, int depth);
+static void qtdemux_dump_unknown (GstQTDemux * qtdemux, void *buffer,
+    int depth);
 
 QtNodeType qt_node_types[] = {
   {FOURCC_moov, "movie", QT_CONTAINER,},
@@ -872,6 +878,9 @@ QtNodeType qt_node_types[] = {
       qtdemux_dump_dcom},
   {FOURCC_cmvd, "compressed movie data", 0,
       qtdemux_dump_cmvd},
+  {FOURCC_hint, "hint", 0, qtdemux_dump_unknown},
+  {FOURCC_mp4a, "mp4a", 0, qtdemux_dump_unknown},
+  {FOURCC_wave, "wave", QT_CONTAINER},
   {0, "unknown", 0},
 };
 static int n_qt_node_types = sizeof (qt_node_types) / sizeof (qt_node_types[0]);
@@ -980,6 +989,9 @@ qtdemux_parse (GstQTDemux * qtdemux, GNode * node, void *buffer, int length)
 
   type = qtdemux_type_get (fourcc);
 
+  if (fourcc == 0 || node_length == 8)
+    return;
+
   GST_LOG ("parsing '" GST_FOURCC_FORMAT "', length=%d",
       GST_FOURCC_ARGS (fourcc), node_length);
 
@@ -997,6 +1009,10 @@ qtdemux_parse (GstQTDemux * qtdemux, GNode * node, void *buffer, int length)
         GST_LOG ("buffer overrun");
       }
       len = QTDEMUX_GUINT32_GET (buf);
+      if (len < 8) {
+        GST_LOG ("bad length");
+        break;
+      }
 
       child = g_node_new (buf);
       g_node_append (node, child);
@@ -1005,6 +1021,53 @@ qtdemux_parse (GstQTDemux * qtdemux, GNode * node, void *buffer, int length)
       buf += len;
     }
   } else {
+    if (fourcc == FOURCC_stsd) {
+      void *buf;
+      guint32 len;
+
+      buf = buffer + 16;
+      end = buffer + length;
+      while (buf < end) {
+        GNode *child;
+
+        if (buf + 8 >= end) {
+          /* FIXME: get annoyed */
+          GST_LOG ("buffer overrun");
+        }
+        len = QTDEMUX_GUINT32_GET (buf);
+
+        child = g_node_new (buf);
+        g_node_append (node, child);
+        qtdemux_parse (qtdemux, child, buf, len);
+
+        buf += len;
+      }
+    } else if (fourcc == FOURCC_mp4a) {
+      void *buf;
+      guint32 len;
+      guint32 version;
+
+      version = QTDEMUX_GUINT32_GET (buffer + 16);
+      if (version == 0x00010000) {
+        buf = buffer + 0x34;
+        end = buffer + length;
+        while (buf < end) {
+          GNode *child;
+
+          if (buf + 8 >= end) {
+            /* FIXME: get annoyed */
+            GST_LOG ("buffer overrun");
+          }
+          len = QTDEMUX_GUINT32_GET (buf);
+
+          child = g_node_new (buf);
+          g_node_append (node, child);
+          qtdemux_parse (qtdemux, child, buf, len);
+
+          buf += len;
+        }
+      }
+    }
 #if 0
     if (fourcc == FOURCC_cmvd) {
       int uncompressed_length;
@@ -1047,6 +1110,9 @@ qtdemux_type_get (guint32 fourcc)
     if (qt_node_types[i].fourcc == fourcc)
       return qt_node_types + i;
   }
+
+  GST_LOG ("unknown QuickTime node type " GST_FOURCC_FORMAT,
+      GST_FOURCC_ARGS (fourcc));
   return qt_node_types + n_qt_node_types - 1;
 }
 
@@ -1433,6 +1499,13 @@ qtdemux_dump_cmvd (GstQTDemux * qtdemux, void *buffer, int depth)
   GST_LOG ("%*s  length: %d", depth, "", QTDEMUX_GUINT32_GET (buffer + 8));
 }
 
+static void
+qtdemux_dump_unknown (GstQTDemux * qtdemux, void *buffer, int depth)
+{
+  GST_LOG ("%*s  length: %d", depth, "", QTDEMUX_GUINT32_GET (buffer + 8));
+
+}
+
 
 static GNode *
 qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc)
@@ -1520,6 +1593,9 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
   GNode *stco;
   GNode *co64;
   GNode *stts;
+  GNode *mp4a;
+  GNode *wave;
+  GNode *esds;
   int n_samples;
   QtDemuxSample *samples;
   int n_samples_per_chunk;
@@ -1588,7 +1664,9 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
   } else if (stream->subtype == FOURCC_soun) {
     int version, samplesize;
     guint32 fourcc;
+    int len;
 
+    len = QTDEMUX_GUINT32_GET (stsd->data + 16);
     GST_LOG ("st type:          " GST_FOURCC_FORMAT,
         GST_FOURCC_ARGS (QTDEMUX_FOURCC_GET (stsd->data + 16 + 4)));
 
@@ -1628,7 +1706,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
           QTDEMUX_GUINT32_GET (stsd->data + offset + 12));
       stream->compression = 1;
       offset = 68;
-    } else {
+    } else if (version == 0x00000000) {
       stream->bytes_per_frame = stream->n_channels * samplesize / 8;
       stream->samples_per_packet = 1;
       stream->compression = 1;
@@ -1640,11 +1718,28 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
         stream->compression = 3;
       if (fourcc == GST_MAKE_FOURCC ('i', 'm', 'a', '4'))
         stream->compression = 4;
+    } else {
+      GST_ERROR ("unknown version %08x", version);
     }
 
-    stream->caps = qtdemux_audio_caps (qtdemux, fourcc,
-        (QTDEMUX_GUINT32_GET (stsd->data) >
-            offset) ? stsd->data + offset : NULL);
+    stream->caps = qtdemux_audio_caps (qtdemux, fourcc, NULL, 0);
+
+    mp4a = qtdemux_tree_get_child_by_type (stsd, FOURCC_mp4a);
+    wave = NULL;
+    if (mp4a)
+      wave = qtdemux_tree_get_child_by_type (mp4a, FOURCC_wave);
+    esds = NULL;
+    if (wave)
+      esds = qtdemux_tree_get_child_by_type (wave, FOURCC_esds);
+    if (esds) {
+      GstBuffer *buffer;
+      int len = QTDEMUX_GUINT32_GET (esds->data);
+
+      buffer = gst_buffer_new_and_alloc (len - 8);
+      memcpy (GST_BUFFER_DATA (buffer), esds->data + 8, len - 8);
+
+      gst_caps_set_simple (stream->caps, "esds", GST_TYPE_BUFFER, buffer, NULL);
+    }
     GST_INFO ("type " GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
         GST_FOURCC_ARGS (QTDEMUX_FOURCC_GET (stsd->data + 16 + 4)),
         stream->caps);
@@ -1935,23 +2030,37 @@ qtdemux_video_caps (GstQTDemux * qtdemux, guint32 fourcc,
       /* Cinepak */
       return gst_caps_from_string ("video/x-cinepak");
     case GST_MAKE_FOURCC ('r', 'p', 'z', 'a'):
+      /* Apple Video */
+      return gst_caps_from_string ("video/x-apple-video");
     case GST_MAKE_FOURCC ('r', 'l', 'e', ' '):
       /* Run-length encoding */
     case GST_MAKE_FOURCC ('s', 'm', 'c', ' '):
     case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'):
     default:
+#if 0
       g_critical ("Don't know how to convert fourcc '" GST_FOURCC_FORMAT
           "' to caps\n", GST_FOURCC_ARGS (fourcc));
       return NULL;
+#endif
+      {
+        char *s;
+
+        s = g_strdup_printf ("video/x-gst-fourcc-" GST_FOURCC_FORMAT,
+            GST_FOURCC_ARGS (fourcc));
+        return gst_caps_new_simple (s, NULL);
+      }
   }
 }
 
 static GstCaps *
-qtdemux_audio_caps (GstQTDemux * qtdemux, guint32 fourcc, const guint8 * data)
+qtdemux_audio_caps (GstQTDemux * qtdemux, guint32 fourcc, const guint8 * data,
+    int len)
 {
   switch (fourcc) {
+#if 0
     case GST_MAKE_FOURCC ('N', 'O', 'N', 'E'):
       return NULL;              /*gst_caps_from_string ("audio/raw"); */
+#endif
     case GST_MAKE_FOURCC ('r', 'a', 'w', ' '):
       /* FIXME */
       return gst_caps_from_string ("audio/x-raw-int, "
@@ -1961,7 +2070,7 @@ qtdemux_audio_caps (GstQTDemux * qtdemux, guint32 fourcc, const guint8 * data)
       return gst_caps_from_string ("audio/x-raw-int, "
           "width = (int) 16, "
           "depth = (int) 16, "
-          "endianness = (int) G_BIG_ENDIAN, " "signed = (boolean) true");
+          "endianness = (int) BIG_ENDIAN, " "signed = (boolean) true");
     case GST_MAKE_FOURCC ('s', 'o', 'w', 't'):
       /* FIXME */
       return gst_caps_from_string ("audio/x-raw-int, "
@@ -2021,17 +2130,16 @@ qtdemux_audio_caps (GstQTDemux * qtdemux, guint32 fourcc, const guint8 * data)
       return gst_caps_from_string ("audio/x-dv");
     case GST_MAKE_FOURCC ('m', 'p', '4', 'a'):
       /* MPEG-4 AAC */
-      return gst_caps_from_string ("audio/mpeg, " "mpegversion = (int) 4");
+      return gst_caps_new_simple ("audio/mpeg",
+          "mpegversion", G_TYPE_INT, 4, NULL);
     case GST_MAKE_FOURCC ('Q', 'D', 'M', '2'):
       /* FIXME: QDesign music version 2 (no constant) */
-      if (QTDEMUX_GUINT32_GET (data) <= 100) {
-        gst_util_dump_mem ((guint8 *) data, 100);
-        return gst_caps_new_simple ("audio/x-qdm2",
-            "framesize", G_TYPE_INT, QTDEMUX_GUINT32_GET (data + 52),
-            "bitrate", G_TYPE_INT, QTDEMUX_GUINT32_GET (data + 40),
-            "blocksize", G_TYPE_INT, QTDEMUX_GUINT32_GET (data + 44), NULL);
-      }
+      return gst_caps_new_simple ("audio/x-qdm2",
+          "framesize", G_TYPE_INT, QTDEMUX_GUINT32_GET (data + 52),
+          "bitrate", G_TYPE_INT, QTDEMUX_GUINT32_GET (data + 40),
+          "blocksize", G_TYPE_INT, QTDEMUX_GUINT32_GET (data + 44), NULL);
     case GST_MAKE_FOURCC ('a', 'g', 's', 'm'):
+      /* GSM */
       return gst_caps_new_simple ("audio/x-gsm", NULL);
     case GST_MAKE_FOURCC ('q', 't', 'v', 'r'):
       /* ? */
@@ -2042,8 +2150,17 @@ qtdemux_audio_caps (GstQTDemux * qtdemux, guint32 fourcc, const guint8 * data)
     case GST_MAKE_FOURCC ('Q', 'c', 'l', 'p'):
       /* QUALCOMM PureVoice */
     default:
+#if 0
       g_critical ("Don't know how to convert fourcc '" GST_FOURCC_FORMAT
           "' to caps\n", GST_FOURCC_ARGS (fourcc));
       return NULL;
+#endif
+      {
+        char *s;
+
+        s = g_strdup_printf ("audio/x-gst-fourcc-" GST_FOURCC_FORMAT,
+            GST_FOURCC_ARGS (fourcc));
+        return gst_caps_new_simple (s, NULL);
+      }
   }
 }