qtmux: Follow xmp serialization guidelines closer
authorThiago Santos <thiago.sousa.santos@collabora.co.uk>
Wed, 15 Sep 2010 20:54:49 +0000 (17:54 -0300)
committerTim-Philipp Müller <tim.muller@collabora.co.uk>
Tue, 12 Apr 2011 19:32:18 +0000 (20:32 +0100)
qt and isom variants have different ways of serializing
xmp, follow these guidelines.

Those can be found in Adobe's xmp docs.

gst/quicktime/atoms.c
gst/quicktime/atoms.h
gst/quicktime/fourcc.h
gst/quicktime/gstqtmux.c
gst/quicktime/gstqtmux.h

index 94a1a45..29d1017 100644 (file)
@@ -206,6 +206,23 @@ atom_data_free (AtomData * data)
   g_free (data);
 }
 
+static AtomUUID *
+atom_uuid_new (void)
+{
+  AtomUUID *uuid = g_new0 (AtomUUID, 1);
+
+  atom_header_set (&uuid->header, FOURCC_uuid, 0, 0);
+  return uuid;
+}
+
+static void
+atom_uuid_free (AtomUUID * data)
+{
+  atom_clear (&data->header);
+  g_free (data->data);
+  g_free (data);
+}
+
 static void
 atom_ftyp_init (AtomFTYP * ftyp, guint32 major, guint32 version, GList * brands)
 {
@@ -1319,6 +1336,23 @@ atom_data_copy_data (AtomData * data, guint8 ** buffer, guint64 * size,
   return *offset - original_offset;
 }
 
+static guint64
+atom_uuid_copy_data (AtomUUID * uuid, guint8 ** buffer, guint64 * size,
+    guint64 * offset)
+{
+  guint64 original_offset = *offset;
+
+  if (!atom_copy_data (&uuid->header, buffer, size, offset)) {
+    return 0;
+  }
+  prop_copy_uint8_array (uuid->uuid, 16, buffer, size, offset);
+  if (uuid->datalen)
+    prop_copy_uint8_array (uuid->data, uuid->datalen, buffer, size, offset);
+
+  atom_write_size (buffer, size, offset, original_offset);
+  return *offset - original_offset;
+}
+
 guint64
 atom_ftyp_copy_data (AtomFTYP * ftyp, guint8 ** buffer, guint64 * size,
     guint64 * offset)
@@ -2775,17 +2809,23 @@ atom_moov_add_3gp_uint_tag (AtomMOOV * moov, guint32 fourcc, guint16 value)
 void
 atom_moov_add_xmp_tags (AtomMOOV * moov, const GstTagList * tags)
 {
-  GstBuffer *xmpbuffer = gst_tag_list_to_xmp_buffer (tags, TRUE);
+  GstBuffer *xmpbuffer;
   AtomData *data_atom = NULL;
 
-  data_atom = atom_data_new_from_gst_buffer (FOURCC_XMP_, xmpbuffer);
-  gst_buffer_unref (xmpbuffer);
-
-  atom_moov_init_metatags (moov, &moov->context);
+  if (moov->context.flavor == ATOMS_TREE_FLAVOR_MOV) {
+    xmpbuffer = gst_tag_list_to_xmp_buffer (tags, TRUE);
+    if (xmpbuffer) {
+      data_atom = atom_data_new_from_gst_buffer (FOURCC_XMP_, xmpbuffer);
+      atom_moov_init_metatags (moov, &moov->context);
+      moov->udta->entries = g_list_append (moov->udta->entries,
+          build_atom_info_wrapper ((Atom *) data_atom, atom_data_copy_data,
+              atom_data_free));
+      gst_buffer_unref (xmpbuffer);
+    }
+  } else {
+    GST_DEBUG ("Not adding xmp to moov atom, it is only used in 'mov' format");
+  }
 
-  moov->udta->entries = g_list_append (moov->udta->entries,
-      build_atom_info_wrapper ((Atom *) data_atom, atom_data_copy_data,
-          atom_data_free));
 }
 
 /*
@@ -3566,3 +3606,30 @@ build_ima_adpcm_extension (gint channels, gint rate, gint blocksize)
   return build_atom_info_wrapper ((Atom *) wave, atom_wave_copy_data,
       atom_wave_free);
 }
+
+AtomInfo *
+build_uuid_xmp_atom (const GstTagList * taglist)
+{
+  GstBuffer *xmp_data;
+  AtomUUID *uuid;
+  static guint8 xmp_uuid[] = { 0xBE, 0x7A, 0xCF, 0xCB,
+    0x97, 0xA9, 0x42, 0xE8,
+    0x9C, 0x71, 0x99, 0x94,
+    0x91, 0xE3, 0xAF, 0xAC
+  };
+
+  xmp_data = gst_tag_list_to_xmp_buffer (taglist, TRUE);
+  if (xmp_data == NULL)
+    return NULL;
+
+  uuid = atom_uuid_new ();
+  memcpy (uuid->uuid, xmp_uuid, 16);
+
+  uuid->data = g_malloc (GST_BUFFER_SIZE (xmp_data));
+  uuid->datalen = GST_BUFFER_SIZE (xmp_data);
+  memcpy (uuid->data, GST_BUFFER_DATA (xmp_data), GST_BUFFER_SIZE (xmp_data));
+
+  gst_buffer_unref (xmp_data);
+  return build_atom_info_wrapper ((Atom *) uuid, atom_uuid_copy_data,
+      atom_uuid_free);
+}
index bdf83be..387904b 100644 (file)
@@ -147,9 +147,22 @@ typedef struct _AtomData
 
   /* not written */
   guint32 datalen;
+
   guint8 *data;
 } AtomData;
 
+typedef struct _AtomUUID
+{
+  Atom header;
+
+  guint8 uuid[16];
+
+  /* not written */
+  guint32 datalen;
+
+  guint8 *data;
+} AtomUUID;
+
 typedef struct _AtomFTYP
 {
   Atom header;
@@ -741,6 +754,7 @@ AtomInfo *   build_gama_atom             (gdouble gamma);
 AtomInfo *   build_SMI_atom              (const GstBuffer *seqh);
 AtomInfo *   build_ima_adpcm_extension   (gint channels, gint rate,
                                           gint blocksize);
+AtomInfo *   build_uuid_xmp_atom         (const GstTagList * taglist);
 
 
 /*
index 51a2a5f..cbef8bb 100644 (file)
@@ -187,6 +187,7 @@ G_BEGIN_DECLS
 #define FOURCC_soco     GST_MAKE_FOURCC('s','o','c','o')
 #define FOURCC_sosn     GST_MAKE_FOURCC('s','o','s','n')
 #define FOURCC_XMP_     GST_MAKE_FOURCC('X','M','P','_')
+#define FOURCC_uuid     GST_MAKE_FOURCC('u','u','i','d')
 
 
 /* SVQ3 fourcc */
index a599f58..7f5ab30 100644 (file)
@@ -300,6 +300,12 @@ gst_qt_mux_reset (GstQTMux * qtmux, gboolean alloc)
     fclose (qtmux->moov_recov_file);
     qtmux->moov_recov_file = NULL;
   }
+  for (walk = qtmux->extra_atoms; walk; walk = g_slist_next (walk)) {
+    AtomInfo *ainfo = (AtomInfo *) walk->data;
+    ainfo->free_func (ainfo->atom);
+  }
+  g_slist_free (qtmux->extra_atoms);
+  qtmux->extra_atoms = NULL;
 
   GST_OBJECT_LOCK (qtmux);
   gst_tag_setter_reset_tags (GST_TAG_SETTER (qtmux));
@@ -880,15 +886,24 @@ gst_qt_mux_add_xmp_tags (GstQTMux * qtmux, const GstTagList * list)
 {
   GstQTMuxClass *qtmux_klass = (GstQTMuxClass *) (G_OBJECT_GET_CLASS (qtmux));
 
-  /* adobe specs only say 'quicktime', but I guess we can extrapolate to
-   * mp4 and gpp. Keep mj2 out for now as we don't add any tags for it yet.
+  /* adobe specs only have 'quicktime' and 'mp4',
+   * but I guess we can extrapolate to gpp.
+   * Keep mj2 out for now as we don't add any tags for it yet.
    * If you have further info about xmp on these formats, please share */
   if (qtmux_klass->format == GST_QT_MUX_FORMAT_MJ2)
     return;
 
   GST_DEBUG_OBJECT (qtmux, "Adding xmp tags");
 
-  atom_moov_add_xmp_tags (qtmux->moov, list);
+  if (qtmux_klass->format == GST_QT_MUX_FORMAT_QT) {
+    atom_moov_add_xmp_tags (qtmux->moov, list);
+  } else {
+    /* for isom/mp4, it is a top level uuid atom */
+    AtomInfo *ainfo = build_uuid_xmp_atom (list);
+    if (ainfo) {
+      qtmux->extra_atoms = g_slist_prepend (qtmux->extra_atoms, ainfo);
+    }
+  }
 }
 
 static void
@@ -1489,6 +1504,17 @@ gst_qt_mux_stop_file (GstQTMux * qtmux)
     GST_DEBUG_OBJECT (qtmux, "calculated moov atom size %" G_GUINT64_FORMAT,
         offset);
     offset += qtmux->header_size + (large_file ? 16 : 8);
+
+    /* sum up with the extra atoms size */
+    for (walk = qtmux->extra_atoms; walk; walk = g_slist_next (walk)) {
+      guint64 extra_size = 0, extra_offset = 0;
+      AtomInfo *ainfo = (AtomInfo *) walk->data;
+
+      if (!ainfo->copy_data_func (ainfo->atom, NULL, &extra_size,
+              &extra_offset))
+        goto serialize_error;
+      offset += extra_offset;
+    }
   } else
     offset = qtmux->header_size;
   atom_moov_chunks_add_offset (qtmux->moov, offset);
@@ -1497,8 +1523,7 @@ gst_qt_mux_stop_file (GstQTMux * qtmux)
   offset = size = 0;
   data = NULL;
   GST_LOG_OBJECT (qtmux, "Copying movie header into buffer");
-  ret = atom_moov_copy_data (qtmux->moov, &data, &size, &offset);
-  if (!ret)
+  if (!atom_moov_copy_data (qtmux->moov, &data, &size, &offset))
     goto serialize_error;
 
   buffer = gst_buffer_new ();
@@ -1509,6 +1534,23 @@ gst_qt_mux_stop_file (GstQTMux * qtmux)
   GST_DEBUG_OBJECT (qtmux, "Pushing movie atoms");
   gst_qt_mux_send_buffer (qtmux, buffer, NULL, FALSE);
 
+  /* push extra top-level atoms */
+  for (walk = qtmux->extra_atoms; walk; walk = g_slist_next (walk)) {
+    AtomInfo *ainfo = (AtomInfo *) walk->data;
+
+    offset = size = 0;
+    data = NULL;
+    if (!ainfo->copy_data_func (ainfo->atom, &data, &size, &offset))
+      goto serialize_error;
+
+    buffer = gst_buffer_new ();
+    GST_BUFFER_MALLOCDATA (buffer) = GST_BUFFER_DATA (buffer) = data;
+    GST_BUFFER_SIZE (buffer) = offset;
+    GST_DEBUG_OBJECT (qtmux, "Pushing extra top-level atom %" GST_FOURCC_FORMAT,
+        GST_FOURCC_ARGS (ainfo->atom->type));
+    gst_qt_mux_send_buffer (qtmux, buffer, NULL, FALSE);
+  }
+
   /* if needed, send mdat atom and move buffered data into it */
   if (qtmux->fast_start_file) {
     /* mdat size = accumulated (buffered data) + mdat atom header */
@@ -1520,12 +1562,12 @@ gst_qt_mux_stop_file (GstQTMux * qtmux)
     if (ret != GST_FLOW_OK)
       return ret;
   } else {
-    /* mdata needs update iff not using faststart */
-    GST_DEBUG_OBJECT (qtmux, "updating mdata size");
+    /* mdat needs update iff not using faststart */
+    GST_DEBUG_OBJECT (qtmux, "updating mdat size");
     ret = gst_qt_mux_update_mdat_size (qtmux, qtmux->mdat_pos,
         qtmux->mdat_size, NULL);
     /* note; no seeking back to the end of file is done,
-     * since we longer write anything anyway */
+     * since we no longer write anything anyway */
   }
 
   return ret;
@@ -1874,8 +1916,12 @@ gst_qt_mux_collected (GstCollectPads * pads, gpointer user_data)
   } else {
     ret = gst_qt_mux_stop_file (qtmux);
     if (ret == GST_FLOW_OK) {
+      GST_DEBUG_OBJECT (qtmux, "Pushing eos");
       gst_pad_push_event (qtmux->srcpad, gst_event_new_eos ());
       ret = GST_FLOW_UNEXPECTED;
+    } else {
+      GST_WARNING_OBJECT (qtmux, "Failed to stop file: %s",
+          gst_flow_get_name (ret));
     }
     qtmux->state = GST_QT_MUX_STATE_EOS;
   }
index 16e7f42..013211a 100644 (file)
@@ -144,6 +144,8 @@ struct _GstQTMux
   AtomsContext *context;
   AtomFTYP *ftyp;
   AtomMOOV *moov;
+  GSList *extra_atoms; /* list of extra top-level atoms (e.g. UUID for xmp)
+                        * Stored as AtomInfo structs */
 
   /* fast start */
   FILE *fast_start_file;