qtdemux: Parse and use creation time tag from mvhd
authorThiago Santos <thiago.sousa.santos@collabora.co.uk>
Tue, 16 Nov 2010 20:48:16 +0000 (17:48 -0300)
committerThiago Santos <thiago.sousa.santos@collabora.co.uk>
Thu, 2 Dec 2010 20:12:29 +0000 (17:12 -0300)
Expose creation time from mvhd as a datetime tag

Fixes #634928

gst/qtdemux/qtdemux.c

index c3ceaa9..0e5a0fc 100644 (file)
 /* if the sample index is larger than this, something is likely wrong */
 #define QTDEMUX_MAX_SAMPLE_INDEX_SIZE (50*1024*1024)
 
+/* For converting qt creation times to unix epoch times */
+#define QTDEMUX_SECONDS_PER_DAY (60 * 60 * 24)
+#define QTDEMUX_LEAP_YEARS_FROM_1904_TO_1970 17
+#define QTDEMUX_SECONDS_FROM_1904_TO_1970 (((1970 - 1904) * (guint64) 365 + \
+    QTDEMUX_LEAP_YEARS_FROM_1904_TO_1970) * QTDEMUX_SECONDS_PER_DAY)
+
 GST_DEBUG_CATEGORY (qtdemux_debug);
 
 /*typedef struct _QtNode QtNode; */
@@ -4239,7 +4245,7 @@ qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
   /* sync sample atom */
   stream->stps_present = FALSE;
   if ((stream->stss_present =
-          ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stss,
+          !!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stss,
               &stream->stss) ? TRUE : FALSE) == TRUE) {
     /* copy atom data into a new buffer for later use */
     stream->stss.data = g_memdup (stream->stss.data, stream->stss.size);
@@ -4257,7 +4263,7 @@ qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
 
     /* partial sync sample atom */
     if ((stream->stps_present =
-            ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stps,
+            !!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stps,
                 &stream->stps) ? TRUE : FALSE) == TRUE) {
       /* copy atom data into a new buffer for later use */
       stream->stps.data = g_memdup (stream->stps.data, stream->stps.size);
@@ -4381,7 +4387,7 @@ qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
 
   /* composition time-to-sample */
   if ((stream->ctts_present =
-          ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_ctts,
+          !!qtdemux_tree_get_child_by_type_full (stbl, FOURCC_ctts,
               &stream->ctts) ? TRUE : FALSE) == TRUE) {
     /* copy atom data into a new buffer for later use */
     stream->ctts.data = g_memdup (stream->ctts.data, stream->ctts.size);
@@ -7004,7 +7010,8 @@ qtdemux_parse_udta (GstQTDemux * qtdemux, GNode * udta)
   }
 
   GST_DEBUG_OBJECT (qtdemux, "new tag list");
-  qtdemux->tag_list = gst_tag_list_new ();
+  if (!qtdemux->tag_list)
+    qtdemux->tag_list = gst_tag_list_new ();
 
   i = 0;
   while (i < G_N_ELEMENTS (add_funcs)) {
@@ -7255,6 +7262,8 @@ qtdemux_parse_tree (GstQTDemux * qtdemux)
   GNode *trak;
   GNode *udta;
   gint64 duration;
+  guint64 creation_time;
+  GstDateTime *datetime = NULL;
   gint version;
 
   mvhd = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvhd);
@@ -7265,9 +7274,11 @@ qtdemux_parse_tree (GstQTDemux * qtdemux)
 
   version = QT_UINT8 ((guint8 *) mvhd->data + 8);
   if (version == 1) {
+    creation_time = QT_UINT64 ((guint8 *) mvhd->data + 12);
     qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 28);
     qtdemux->duration = QT_UINT64 ((guint8 *) mvhd->data + 32);
   } else if (version == 0) {
+    creation_time = QT_UINT32 ((guint8 *) mvhd->data + 12);
     qtdemux->timescale = QT_UINT32 ((guint8 *) mvhd->data + 20);
     qtdemux->duration = QT_UINT32 ((guint8 *) mvhd->data + 24);
   } else {
@@ -7275,6 +7286,26 @@ qtdemux_parse_tree (GstQTDemux * qtdemux)
     return FALSE;
   }
 
+  /* Moving qt creation time (secs since 1904) to unix time */
+  if (creation_time != 0) {
+    if (creation_time > QTDEMUX_SECONDS_FROM_1904_TO_1970) {
+      creation_time -= QTDEMUX_SECONDS_FROM_1904_TO_1970;
+      datetime = gst_date_time_new_from_unix_epoch_local_time (creation_time);
+    } else {
+      GST_WARNING_OBJECT (qtdemux, "Can't handle datetimes before 1970 yet, "
+          "please file a bug at http://bugzilla.gnome.org");
+    }
+  }
+  if (datetime) {
+    if (!qtdemux->tag_list)
+      qtdemux->tag_list = gst_tag_list_new ();
+
+    /* Use KEEP as explicit tags should have a higher priority than mvhd tag */
+    gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_KEEP, GST_TAG_DATE_TIME,
+        datetime, NULL);
+    gst_date_time_unref (datetime);
+  }
+
   GST_INFO_OBJECT (qtdemux, "timescale: %u", qtdemux->timescale);
   GST_INFO_OBJECT (qtdemux, "duration: %" G_GUINT64_FORMAT, qtdemux->duration);