qtdemux: add fragmented mp4 fourccs
authorMarc-André Lureau <mlureau@flumotion.com>
Thu, 11 Mar 2010 14:34:49 +0000 (15:34 +0100)
committerMark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
Fri, 3 Dec 2010 14:50:31 +0000 (15:50 +0100)
Adds fourcc's for tfra, tfhd, trun, sdtp, trex, mehd and
their dumps

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

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

index 2822d44..06bb344 100644 (file)
@@ -92,6 +92,28 @@ qt_atom_parser_get_offset_unchecked (GstByteReader * parser, guint off_size)
   }
 }
 
+/* size must be from 1 to 4 */
+static inline guint32
+qt_atom_parser_get_uint_with_size_unchecked (GstByteReader * parser,
+    guint size)
+{
+  switch (size) {
+  case 1:
+    return gst_byte_reader_get_uint8_unchecked (parser);
+  case 2:
+    return gst_byte_reader_get_uint16_be_unchecked (parser);
+  case 3:
+    return gst_byte_reader_get_uint24_be_unchecked (parser);
+  case 4:
+    return gst_byte_reader_get_uint32_be_unchecked (parser);
+  default:
+    g_assert_not_reached ();
+    gst_byte_reader_skip_unchecked (parser, size);
+    break;
+  }
+  return 0;
+}
+
 static inline gboolean
 qt_atom_parser_get_fourcc (GstByteReader * parser, guint32 * fourcc)
 {
index af45fe7..b384fcd 100644 (file)
@@ -510,6 +510,243 @@ qtdemux_dump_cmvd (GstQTDemux * qtdemux, GstByteReader * data, int depth)
 }
 
 gboolean
+qtdemux_dump_mfro (GstQTDemux * qtdemux, GstByteReader * data, int depth)
+{
+  if (!qt_atom_parser_has_remaining (data, 4))
+    return FALSE;
+
+  GST_LOG ("%*s  size: %d", depth, "", GET_UINT32 (data));
+  return TRUE;
+}
+
+gboolean
+qtdemux_dump_tfra (GstQTDemux * qtdemux, GstByteReader * data, int depth)
+{
+  guint64 time = 0, moof_offset = 0;
+  guint32 len = 0, num_entries = 0, ver_flags, track_id, i;
+  guint value_size, traf_size, trun_size, sample_size;
+
+  if (!gst_byte_reader_get_uint32_be (data, &ver_flags))
+    return FALSE;
+
+  GST_LOG ("%*s  version/flags: %08x", depth, "", ver_flags);
+
+  if (!gst_byte_reader_get_uint32_be (data, &track_id) ||
+      gst_byte_reader_get_uint32_be (data, &len) ||
+      gst_byte_reader_get_uint32_be (data, &num_entries))
+    return FALSE;
+
+  GST_LOG ("%*s  track ID:      %u", depth, "", track_id);
+  GST_LOG ("%*s  length:        0x%x", depth, "", len);
+  GST_LOG ("%*s  n entries:     %u", depth, "", num_entries);
+
+  value_size = ((ver_flags >> 24) == 1) ? sizeof (guint64) : sizeof (guint32);
+  sample_size = (len & 3) + 1;
+  trun_size = ((len & 12) >> 2) + 1;
+  traf_size = ((len & 48) >> 4) + 1;
+
+  if (!qt_atom_parser_has_chunks (data, num_entries,
+          value_size + value_size + traf_size + trun_size + sample_size))
+    return FALSE;
+
+  for (i = 0; i < num_entries; i++) {
+    qt_atom_parser_get_offset (data, value_size, &time);
+    qt_atom_parser_get_offset (data, value_size, &moof_offset);
+    GST_LOG ("%*s    time:          %" G_GUINT64_FORMAT, depth, "", time);
+    GST_LOG ("%*s    moof_offset:   %" G_GUINT64_FORMAT,
+        depth, "", moof_offset);
+    GST_LOG ("%*s    traf_number:   %u", depth, "",
+        qt_atom_parser_get_uint_with_size_unchecked (data, traf_size));
+    GST_LOG ("%*s    trun_number:   %u", depth, "",
+        qt_atom_parser_get_uint_with_size_unchecked (data, trun_size));
+    GST_LOG ("%*s    sample_number: %u", depth, "",
+        qt_atom_parser_get_uint_with_size_unchecked (data, sample_size));
+  }
+
+  return TRUE;
+}
+
+gboolean
+qtdemux_dump_tfhd (GstQTDemux * qtdemux, GstByteReader * data, int depth)
+{
+  guint32 flags, n, track_id;
+  guint64 base_data_offset;
+
+  if (!gst_byte_reader_skip (data, 1) ||
+      !gst_byte_reader_get_uint24_be (data, &flags))
+    return FALSE;
+  GST_LOG ("%*s  flags: %08x", depth, "", flags);
+
+  if (!gst_byte_reader_get_uint32_be (data, &track_id))
+    return FALSE;
+  GST_LOG ("%*s  track_id: %u", depth, "", track_id);
+
+  if (flags & TF_BASE_DATA_OFFSET) {
+    if (!gst_byte_reader_get_uint64_be (data, &base_data_offset))
+      return FALSE;
+    GST_LOG ("%*s    base-data-offset: %" G_GUINT64_FORMAT,
+        depth, "", base_data_offset);
+  }
+
+  if (flags & TF_SAMPLE_DESCRIPTION_INDEX) {
+    if (!gst_byte_reader_get_uint32_be (data, &n))
+      return FALSE;
+    GST_LOG ("%*s    sample-description-index: %u", depth, "", n);
+  }
+
+  if (flags & TF_DEFAULT_SAMPLE_DURATION) {
+    if (!gst_byte_reader_get_uint32_be (data, &n))
+      return FALSE;
+    GST_LOG ("%*s    default-sample-duration:  %u", depth, "", n);
+  }
+
+  if (flags & TF_DEFAULT_SAMPLE_SIZE) {
+    if (!gst_byte_reader_get_uint32_be (data, &n))
+      return FALSE;
+    GST_LOG ("%*s    default-sample-size:  %u", depth, "", n);
+  }
+
+  if (flags & TF_DEFAULT_SAMPLE_FLAGS) {
+    if (!gst_byte_reader_get_uint32_be (data, &n))
+      return FALSE;
+    GST_LOG ("%*s    default-sample-flags:  %u", depth, "", n);
+  }
+
+  GST_LOG ("%*s    duration-is-empty:     %s", depth, "",
+      flags & TF_DURATION_IS_EMPTY ? "yes" : "no");
+
+  return TRUE;
+}
+
+gboolean
+qtdemux_dump_trun (GstQTDemux * qtdemux, GstByteReader * data, int depth)
+{
+  guint32 flags, samples_count, data_offset, first_sample_flags;
+  guint32 sample_duration, sample_size, sample_flags, composition_time_offsets;
+  int i = 0;
+
+  if (!gst_byte_reader_skip (data, 1) ||
+      !gst_byte_reader_get_uint24_be (data, &flags))
+    return FALSE;
+
+  GST_LOG ("%*s  flags: %08x", depth, "", flags);
+
+  if (!gst_byte_reader_get_uint32_be (data, &samples_count))
+    return FALSE;
+  GST_LOG ("%*s  samples_count: %u", depth, "", samples_count);
+
+  if (flags & TR_DATA_OFFSET) {
+    if (!gst_byte_reader_get_uint32_be (data, &data_offset))
+      return FALSE;
+    GST_LOG ("%*s    data-offset: %u", depth, "", data_offset);
+  }
+
+  if (flags & TR_FIRST_SAMPLE_FLAGS) {
+    if (!gst_byte_reader_get_uint32_be (data, &first_sample_flags))
+      return FALSE;
+    GST_LOG ("%*s    first-sample-flags: %u", depth, "", first_sample_flags);
+  }
+
+  for (i = 0; i < samples_count; i++) {
+    if (flags & TR_SAMPLE_DURATION) {
+      if (!gst_byte_reader_get_uint32_be (data, &sample_duration))
+        return FALSE;
+      GST_LOG ("%*s    sample-duration:  %u", depth, "", sample_duration);
+    }
+
+    if (flags & TR_SAMPLE_SIZE) {
+      if (!gst_byte_reader_get_uint32_be (data, &sample_size))
+        return FALSE;
+      GST_LOG ("%*s    sample-size:  %u", depth, "", sample_size);
+    }
+
+    if (flags & TR_SAMPLE_FLAGS) {
+      if (!gst_byte_reader_get_uint32_be (data, &sample_flags))
+        return FALSE;
+      GST_LOG ("%*s    sample-flags:  %u", depth, "", sample_flags);
+    }
+
+    if (flags & TR_COMPOSITION_TIME_OFFSETS) {
+      if (!gst_byte_reader_get_uint32_be (data, &composition_time_offsets))
+        return FALSE;
+      GST_LOG ("%*s    composition_time_offsets:  %u", depth, "",
+          composition_time_offsets);
+    }
+  }
+
+  return TRUE;
+}
+
+gboolean
+qtdemux_dump_trex (GstQTDemux * qtdemux, GstByteReader * data, int depth)
+{
+  if (!qt_atom_parser_has_remaining (data, 4 + 4 + 4 + 4 + 4 + 4))
+    return FALSE;
+
+  GST_LOG ("%*s  version/flags: %08x", depth, "", GET_UINT32 (data));
+  GST_LOG ("%*s  track ID:      %08x", depth, "", GET_UINT32 (data));
+  GST_LOG ("%*s  default sample desc. index: %08x", depth, "",
+      GET_UINT32 (data));
+  GST_LOG ("%*s  default sample duration:    %08x", depth, "",
+      GET_UINT32 (data));
+  GST_LOG ("%*s  default sample size:        %08x", depth, "",
+      GET_UINT32 (data));
+  GST_LOG ("%*s  default sample flags:       %08x", depth, "",
+      GET_UINT32 (data));
+
+  return TRUE;
+}
+
+gboolean
+qtdemux_dump_mehd (GstQTDemux * qtdemux, GstByteReader * data, int depth)
+{
+  guint32 version;
+  guint64 fragment_duration;
+  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, &fragment_duration)) {
+    GST_LOG ("%*s  fragment duration: %" G_GUINT64_FORMAT,
+        depth, "", fragment_duration);
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+gboolean
+qtdemux_dump_sdtp (GstQTDemux * qtdemux, GstByteReader * data, int depth)
+{
+  guint32 version;
+  guint8 val;
+  guint i = 1;
+
+  version = GET_UINT32 (data);
+  GST_LOG ("%*s  version/flags: %08x", depth, "", version);
+
+  /* the sample_count is specified in the stsz or stz2 box.
+   * the information for a sample is stored in a single byte,
+   * so we read until there are no remaining bytes */
+  while (qt_atom_parser_has_remaining (data, 1)) {
+    val = GET_UINT8 (data);
+    GST_LOG ("%*s     sample number: %d", depth, "", i);
+    GST_LOG ("%*s     sample_depends_on: %d", depth, "",
+        ((guint16) (val)) & 0x3);
+    GST_LOG ("%*s     sample_is_depended_on: %d", depth, "",
+        ((guint16) (val >> 2)) & 0x3);
+    GST_LOG ("%*s     sample_has_redundancy: %d", depth, "",
+        ((guint16) (val >> 4)) & 0x3);
+    ++i;
+  }
+  return TRUE;
+}
+
+gboolean
 qtdemux_dump_unknown (GstQTDemux * qtdemux, GstByteReader * data, int depth)
 {
   int len;
index df2e9ca..9bb1f95 100644 (file)
@@ -61,6 +61,20 @@ gboolean qtdemux_dump_cmvd (GstQTDemux * qtdemux, GstByteReader * data,
     int depth);
 gboolean qtdemux_dump_ctts (GstQTDemux * qtdemux, GstByteReader * data,
     int depth);
+gboolean qtdemux_dump_mfro (GstQTDemux * qtdemux, GstByteReader * data,
+    int depth);
+gboolean qtdemux_dump_tfra (GstQTDemux * qtdemux, GstByteReader * data,
+    int depth);
+gboolean qtdemux_dump_tfhd (GstQTDemux * qtdemux, GstByteReader * data,
+    int depth);
+gboolean qtdemux_dump_trun (GstQTDemux * qtdemux, GstByteReader * data,
+    int depth);
+gboolean qtdemux_dump_trex (GstQTDemux * qtdemux, GstByteReader * data,
+    int depth);
+gboolean qtdemux_dump_mehd (GstQTDemux * qtdemux, GstByteReader * data,
+    int depth);
+gboolean qtdemux_dump_sdtp (GstQTDemux * qtdemux, GstByteReader * data,
+    int depth);
 gboolean qtdemux_dump_unknown (GstQTDemux * qtdemux, GstByteReader * data,
     int depth);
 
index 5e18c10..3f4483c 100644 (file)
@@ -209,6 +209,20 @@ G_BEGIN_DECLS
 #define FOURCC_XMP_     GST_MAKE_FOURCC('X','M','P','_')
 #define FOURCC_uuid     GST_MAKE_FOURCC('u','u','i','d')
 
+/* Fragmented MP4 */
+#define FOURCC_mehd     GST_MAKE_FOURCC('m','e','h','d')
+#define FOURCC_mfhd     GST_MAKE_FOURCC('m','f','h','d')
+#define FOURCC_mfra     GST_MAKE_FOURCC('m','f','r','a')
+#define FOURCC_mfro     GST_MAKE_FOURCC('m','f','r','o')
+#define FOURCC_moof     GST_MAKE_FOURCC('m','o','o','f')
+#define FOURCC_mvex     GST_MAKE_FOURCC('m','v','e','x')
+#define FOURCC_sdtp     GST_MAKE_FOURCC('s','d','t','p')
+#define FOURCC_tfhd     GST_MAKE_FOURCC('t','f','h','d')
+#define FOURCC_tfra     GST_MAKE_FOURCC('t','f','r','a')
+#define FOURCC_traf     GST_MAKE_FOURCC('t','r','a','f')
+#define FOURCC_trex     GST_MAKE_FOURCC('t','r','e','x')
+#define FOURCC_trun     GST_MAKE_FOURCC('t','r','u','n')
+
 G_END_DECLS
 
 #endif /* __GST_QTDEMUX_FOURCC_H__ */
index 6afc2d1..cf7fd05 100644 (file)
@@ -150,6 +150,25 @@ static const QtNodeType qt_node_types[] = {
   {FOURCC_XdxT, "XdxT", 0},
   {FOURCC_loci, "loci", 0},
   {FOURCC_clsf, "clsf", 0},
+  {FOURCC_mfra, "movie fragment random access",
+      QT_FLAG_CONTAINER,},
+  {FOURCC_tfra, "track fragment random access", 0,
+      qtdemux_dump_tfra},
+  {FOURCC_mfro, "movie fragment random access offset", 0,
+      qtdemux_dump_mfro},
+  {FOURCC_moof, "movie fragment", QT_FLAG_CONTAINER,},
+  {FOURCC_mfhd, "movie fragment header", 0,},
+  {FOURCC_traf, "track fragment", QT_FLAG_CONTAINER,},
+  {FOURCC_tfhd, "track fragment header", 0,
+      qtdemux_dump_tfhd},
+  {FOURCC_sdtp, "independent and disposable samples", 0,
+      qtdemux_dump_sdtp},
+  {FOURCC_trun, "track fragment run", 0, qtdemux_dump_trun},
+  {FOURCC_mdat, "moovie data", 0, qtdemux_dump_unknown},
+  {FOURCC_trex, "moovie data", 0, qtdemux_dump_trex},
+  {FOURCC_mvex, "mvex", QT_FLAG_CONTAINER,},
+  {FOURCC_mehd, "movie extends header", 0,
+      qtdemux_dump_mehd},
   {0, "unknown", 0,},
 };
 
index e5c0bd6..5d13efd 100644 (file)
@@ -45,7 +45,7 @@ typedef struct _QtNodeType QtNodeType;
 
 typedef enum {
   QT_FLAG_NONE      = (0),
-  QT_FLAG_CONTAINER = (1 << 0) 
+  QT_FLAG_CONTAINER = (1 << 0)
 } QtFlags;
 
 struct _QtNodeType {
@@ -55,6 +55,26 @@ struct _QtNodeType {
   QtDumpFunc   dump;
 };
 
+enum TfFlags
+{
+  TF_BASE_DATA_OFFSET         = 0x000001,   /* base-data-offset-present */
+  TF_SAMPLE_DESCRIPTION_INDEX = 0x000002,   /* sample-description-index-present */
+  TF_DEFAULT_SAMPLE_DURATION  = 0x000008,   /* default-sample-duration-present */
+  TF_DEFAULT_SAMPLE_SIZE      = 0x000010,   /* default-sample-size-present */
+  TF_DEFAULT_SAMPLE_FLAGS     = 0x000020,   /* default-sample-flags-present */
+  TF_DURATION_IS_EMPTY        = 0x100000    /* sample-composition-time-offsets-presents */
+};
+
+enum TrFlags
+{
+  TR_DATA_OFFSET              = 0x000001,   /* data-offset-present */
+  TR_FIRST_SAMPLE_FLAGS       = 0x000004,   /* first-sample-flags-present */
+  TR_SAMPLE_DURATION          = 0x000100,   /* sample-duration-present */
+  TR_SAMPLE_SIZE              = 0x000200,   /* sample-size-present */
+  TR_SAMPLE_FLAGS             = 0x000400,   /* sample-flags-present */
+  TR_COMPOSITION_TIME_OFFSETS = 0x000800    /* sample-composition-time-offsets-presents */
+};
+
 const QtNodeType *qtdemux_type_get (guint32 fourcc);
 
 G_END_DECLS