isomp4: add DASH tfdt box support
authorDavid Corvoysier <david.corvoysier@orange.com>
Tue, 3 Jul 2012 15:50:24 +0000 (17:50 +0200)
committerMark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
Tue, 28 Aug 2012 14:28:27 +0000 (16:28 +0200)
MPEG DASH has defined a set of new boxes to specify duration, indexes and
offsets of ISOBMFF fragments.

The Track Fragment Base Media Decode Time (tfdt) Box can in particular be
included inside a traf box to specify the absolute decode time, measured on the
media timeline, of the first sample in decode order in the track fragment.

This information can be used by the isomp4 demux to find out the current position of
an MP4 fragment in the timeline.

This patch adds code to isomp4 to:
- parse the tfdt box
- adjust the time/position member of the new segment sent when playback starts

Fixes https://bugzilla.gnome.org/show_bug.cgi?id=677535

gst/isomp4/qtdemux.c
gst/isomp4/qtdemux_dump.c
gst/isomp4/qtdemux_dump.h
gst/isomp4/qtdemux_fourcc.h
gst/isomp4/qtdemux_types.c

index 411911dbe98efcb8d454918ef8ba7f137b3cf08e..8409123d1442cee26fee775205729233ea0c08a3 100644 (file)
@@ -2372,12 +2372,44 @@ unknown_stream:
   }
 }
 
+static gboolean
+qtdemux_parse_tfdt (GstQTDemux * qtdemux, GstByteReader * br,
+    guint64 * decode_time)
+{
+  guint32 version = 0;
+
+  if (!gst_byte_reader_get_uint32_be (br, &version))
+    return FALSE;
+
+  version >>= 24;
+  if (version == 1) {
+    if (!gst_byte_reader_get_uint64_be (br, decode_time))
+      goto failed;
+  } else {
+    guint32 dec_time = 0;
+    if (!gst_byte_reader_get_uint32_be (br, &dec_time))
+      goto failed;
+    *decode_time = dec_time;
+  }
+
+  GST_INFO_OBJECT (qtdemux, "Track fragment decode time: %" G_GUINT64_FORMAT,
+      *decode_time);
+
+  return TRUE;
+
+failed:
+  {
+    GST_DEBUG_OBJECT (qtdemux, "parsing tfdt failed");
+    return FALSE;
+  }
+}
+
 static gboolean
 qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
     guint64 moof_offset, QtDemuxStream * stream)
 {
-  GNode *moof_node, *traf_node, *tfhd_node, *trun_node;
-  GstByteReader trun_data, tfhd_data;
+  GNode *moof_node, *traf_node, *tfhd_node, *trun_node, *tfdt_node;
+  GstByteReader trun_data, tfhd_data, tfdt_data;
   guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;
   gint64 base_offset, running_offset;
 
@@ -2400,6 +2432,22 @@ qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
     if (!qtdemux_parse_tfhd (qtdemux, &tfhd_data, &stream, &ds_duration,
             &ds_size, &ds_flags, &base_offset))
       goto missing_tfhd;
+    tfdt_node =
+        qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfdt,
+        &tfdt_data);
+    if (tfdt_node) {
+      guint64 decode_time = 0;
+      qtdemux_parse_tfdt (qtdemux, &tfdt_data, &decode_time);
+      /* If there is a new segment pending, update the time/position */
+      if (qtdemux->pending_newsegment) {
+        gst_event_replace (&qtdemux->pending_newsegment,
+            gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
+                0, GST_CLOCK_TIME_NONE,
+                gst_util_uint64_scale (decode_time,
+                    GST_SECOND, stream->timescale)));
+      }
+    }
+
     if (G_UNLIKELY (!stream)) {
       /* we lost track of offset, we'll need to regain it,
        * but can delay complaining until later or avoid doing so altogether */
index 71350a6fa096d186e86dcd0ba3a76133ce473eda..952b0c78daf7c9e26672977969d34a415d957336 100644 (file)
@@ -720,6 +720,28 @@ qtdemux_dump_mehd (GstQTDemux * qtdemux, GstByteReader * data, int depth)
   return FALSE;
 }
 
+gboolean
+qtdemux_dump_tfdt (GstQTDemux * qtdemux, GstByteReader * data, int depth)
+{
+  guint32 version = 0;
+  guint64 decode_time;
+  guint value_size;
+
+  if (!gst_byte_reader_get_uint32_be (data, &version))
+    return FALSE;
+
+  GST_LOG ("%*s  version/flags: %08x", depth, "", version);
+
+  value_size = ((version >> 24) == 1) ? sizeof (guint64) : sizeof (guint32);
+  if (qt_atom_parser_get_offset (data, value_size, &decode_time)) {
+    GST_LOG ("%*s  Track fragment decode time: %" G_GUINT64_FORMAT,
+        depth, "", decode_time);
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
 gboolean
 qtdemux_dump_sdtp (GstQTDemux * qtdemux, GstByteReader * data, int depth)
 {
index 9bb1f95d4be7e3695a3822660f3b387db2227910..d5486eebf0f6c7c2dddcb8b567362cf485c6eab3 100644 (file)
@@ -75,6 +75,8 @@ gboolean qtdemux_dump_mehd (GstQTDemux * qtdemux, GstByteReader * data,
     int depth);
 gboolean qtdemux_dump_sdtp (GstQTDemux * qtdemux, GstByteReader * data,
     int depth);
+gboolean qtdemux_dump_tfdt (GstQTDemux * qtdemux, GstByteReader * data,
+    int depth);
 gboolean qtdemux_dump_unknown (GstQTDemux * qtdemux, GstByteReader * data,
     int depth);
 
index 6666a94e55074e6d9ae8956545a9fa4155dee567..f2a349291606b10b6de76e999e43d376cf0f531b 100644 (file)
@@ -230,6 +230,9 @@ G_BEGIN_DECLS
 #define FOURCC_ovc1     GST_MAKE_FOURCC('o','v','c','1')
 #define FOURCC_owma     GST_MAKE_FOURCC('o','w','m','a')
 
+/* MPEG DASH */
+#define FOURCC_tfdt     GST_MAKE_FOURCC('t','f','d','t')
+
 G_END_DECLS
 
 #endif /* __GST_QTDEMUX_FOURCC_H__ */
index 38da35b3d5c93cef9c5dcfe834f362a508c19ffe..65afc8e2a8e612a9d4a674b731def8385f13b1e8 100644 (file)
@@ -171,6 +171,7 @@ static const QtNodeType qt_node_types[] = {
       qtdemux_dump_mehd},
   {FOURCC_ovc1, "ovc1", 0},
   {FOURCC_owma, "owma", 0},
+  {FOURCC_tfdt, "Track fragment decode time", 0, qtdemux_dump_tfdt},
   {0, "unknown", 0,},
 };