qtmux: add 3GP style tagging (and refactor appropriately)
authorMark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
Wed, 10 Jun 2009 10:42:44 +0000 (12:42 +0200)
committerTim-Philipp Müller <tim.muller@collabora.co.uk>
Tue, 12 Apr 2011 19:32:12 +0000 (20:32 +0100)
gst/quicktime/atoms.c
gst/quicktime/atoms.h
gst/quicktime/fourcc.h
gst/quicktime/gstqtmux.c
gst/quicktime/gstqtmuxmap.c

index 2ce8631..be578f2 100644 (file)
@@ -1036,7 +1036,8 @@ atom_meta_free (AtomMETA * meta)
 {
   atom_full_clear (&meta->header);
   atom_hdlr_clear (&meta->hdlr);
-  atom_ilst_free (meta->ilst);
+  if (meta->ilst)
+    atom_ilst_free (meta->ilst);
   meta->ilst = NULL;
   g_free (meta);
 }
@@ -1061,8 +1062,11 @@ static void
 atom_udta_free (AtomUDTA * udta)
 {
   atom_clear (&udta->header);
-  atom_meta_free (udta->meta);
+  if (udta->meta)
+    atom_meta_free (udta->meta);
   udta->meta = NULL;
+  if (udta->entries)
+    atom_info_list_free (udta->entries);
   g_free (udta);
 }
 
@@ -1149,6 +1153,7 @@ atom_moov_init (AtomMOOV * moov, AtomsContext * context)
   atom_mvhd_init (&(moov->mvhd));
   moov->udta = NULL;
   moov->traks = NULL;
+  moov->context = *context;
 }
 
 AtomMOOV *
@@ -1170,13 +1175,11 @@ atom_moov_free (AtomMOOV * moov)
 
   walker = moov->traks;
   while (walker) {
-    GList *aux = walker;
-
+    atom_trak_free ((AtomTRAK *) walker->data);
     walker = g_list_next (walker);
-    moov->traks = g_list_remove_link (moov->traks, aux);
-    atom_trak_free ((AtomTRAK *) aux->data);
-    g_list_free (aux);
   }
+  g_list_free (moov->traks);
+  moov->traks = NULL;
 
   if (moov->udta) {
     atom_udta_free (moov->udta);
@@ -2163,6 +2166,10 @@ atom_udta_copy_data (AtomUDTA * udta, guint8 ** buffer, guint64 * size,
     if (!atom_meta_copy_data (udta->meta, buffer, size, offset)) {
       return 0;
     }
+  } else if (udta->entries) {
+    /* extra atoms */
+    if (!atom_info_list_copy_data (udta->entries, buffer, size, offset))
+      return 0;
   }
 
   atom_write_size (buffer, size, offset, original_offset);
@@ -2517,16 +2524,18 @@ atom_moov_chunks_add_offset (AtomMOOV * moov, guint32 offset)
  * Meta tags functions
  */
 static void
-atom_moov_init_metatags (AtomMOOV * moov)
+atom_moov_init_metatags (AtomMOOV * moov, AtomsContext * context)
 {
   if (!moov->udta) {
     moov->udta = atom_udta_new ();
   }
-  if (!moov->udta->meta) {
-    moov->udta->meta = atom_meta_new ();
-  }
-  if (!moov->udta->meta->ilst) {
-    moov->udta->meta->ilst = atom_ilst_new ();
+  if (context->flavor != ATOMS_TREE_FLAVOR_3GP) {
+    if (!moov->udta->meta) {
+      moov->udta->meta = atom_meta_new ();
+    }
+    if (!moov->udta->meta->ilst) {
+      moov->udta->meta->ilst = atom_ilst_new ();
+    }
   }
 }
 
@@ -2543,11 +2552,14 @@ atom_tag_data_alloc_data (AtomTagData * data, guint size)
 static void
 atom_moov_append_tag (AtomMOOV * moov, AtomInfo * tag)
 {
-  AtomILST *ilst;
+  GList **entries;
 
-  atom_moov_init_metatags (moov);
-  ilst = moov->udta->meta->ilst;
-  ilst->entries = g_list_append (ilst->entries, tag);
+  atom_moov_init_metatags (moov, &moov->context);
+  if (moov->udta->meta)
+    entries = &moov->udta->meta->ilst->entries;
+  else
+    entries = &moov->udta->entries;
+  *entries = g_list_append (*entries, tag);
 }
 
 void
@@ -2621,6 +2633,87 @@ atom_moov_add_blob_tag (AtomMOOV * moov, guint8 * data, guint size)
           atom_data_free));
 }
 
+void
+atom_moov_add_3gp_tag (AtomMOOV * moov, guint32 fourcc, guint8 * data,
+    guint size)
+{
+  AtomData *data_atom;
+  GstBuffer *buf;
+  guint8 *bdata;
+
+  /* need full atom */
+  buf = gst_buffer_new_and_alloc (size + 4);
+  bdata = GST_BUFFER_DATA (buf);
+  /* full atom: version and flags */
+  GST_WRITE_UINT32_BE (bdata, 0);
+  memcpy (bdata + 4, data, size);
+
+  data_atom = atom_data_new_from_gst_buffer (fourcc, buf);
+  gst_buffer_unref (buf);
+
+  atom_moov_append_tag (moov,
+      build_atom_info_wrapper ((Atom *) data_atom, atom_data_copy_data,
+          atom_data_free));
+}
+
+guint16
+language_code (const char *lang)
+{
+  g_return_val_if_fail (lang != NULL, 0);
+  g_return_val_if_fail (strlen (lang) == 3, 0);
+
+  return (((lang[0] - 0x60) & 0x1F) << 10) + (((lang[1] - 0x60) & 0x1F) << 5) +
+      ((lang[2] - 0x60) & 0x1F);
+}
+
+void
+atom_moov_add_3gp_str_int_tag (AtomMOOV * moov, guint32 fourcc,
+    const gchar * value, gint16 ivalue)
+{
+  gint len = 0, size = 0;
+  guint8 *data;
+
+  if (value) {
+    len = strlen (value);
+    size = len + 3;
+  }
+
+  if (ivalue >= 0)
+    size += 2;
+
+  data = g_malloc (size + 3);
+  /* language tag and null-terminated UTF-8 string */
+  if (value) {
+    GST_WRITE_UINT16_BE (data, language_code (GST_QT_MUX_DEFAULT_TAG_LANGUAGE));
+    /* include 0 terminator */
+    memcpy (data + 2, value, len + 1);
+  }
+  /* 16-bit unsigned int if standalone, otherwise 8-bit */
+  if (ivalue >= 0) {
+    if (size == 2)
+      GST_WRITE_UINT16_BE (data + size - 2, ivalue);
+    else {
+      GST_WRITE_UINT8 (data + size - 2, ivalue & 0xFF);
+      size--;
+    }
+  }
+
+  atom_moov_add_3gp_tag (moov, fourcc, data, size);
+  g_free (data);
+}
+
+void
+atom_moov_add_3gp_str_tag (AtomMOOV * moov, guint32 fourcc, const gchar * value)
+{
+  atom_moov_add_3gp_str_int_tag (moov, fourcc, value, -1);
+}
+
+void
+atom_moov_add_3gp_uint_tag (AtomMOOV * moov, guint32 fourcc, guint16 value)
+{
+  atom_moov_add_3gp_str_int_tag (moov, fourcc, NULL, value);
+}
+
 /*
  * Functions for specifying media types
  */
@@ -2843,7 +2936,7 @@ atom_trak_set_video_type (AtomTRAK * trak, AtomsContext * context,
   dheight = entry->height;
   /* ISO file spec says track header w/h indicates track's visual presentation
    * (so this together with pixels w/h implicitly defines PAR) */
-  if (par_n && (context->flavor == ATOMS_TREE_FLAVOR_ISOM)) {
+  if (par_n && (context->flavor != ATOMS_TREE_FLAVOR_MOV)) {
     if (par_n > par_d) {
       dwidth = entry->width * par_n / par_d;
       dheight = entry->height;
index 0bf3f20..4c94141 100644 (file)
@@ -55,7 +55,8 @@
 typedef enum _AtomsTreeFlavor
 {
   ATOMS_TREE_FLAVOR_MOV,
-  ATOMS_TREE_FLAVOR_ISOM
+  ATOMS_TREE_FLAVOR_ISOM,
+  ATOMS_TREE_FLAVOR_3GP
 } AtomsTreeFlavor;
 
 typedef struct _AtomsContext
@@ -509,6 +510,9 @@ typedef struct _AtomUDTA
 {
   Atom header;
 
+  /* list of AtomInfo */
+  GList* entries;
+  /* or list is further down */
   AtomMETA *meta;
 } AtomUDTA;
 
@@ -526,6 +530,9 @@ typedef struct _AtomTRAK
 
 typedef struct _AtomMOOV
 {
+  /* style */
+  AtomsContext context;
+
   Atom header;
 
   AtomMVHD mvhd;
@@ -651,4 +658,14 @@ void atom_moov_add_tag        (AtomMOOV *moov, guint32 fourcc, guint32 flags,
                                const guint8 * data, guint size);
 void atom_moov_add_blob_tag   (AtomMOOV *moov, guint8 *data, guint size);
 
+void atom_moov_add_3gp_str_tag       (AtomMOOV * moov, guint32 fourcc, const gchar * value);
+void atom_moov_add_3gp_uint_tag      (AtomMOOV * moov, guint32 fourcc, guint16 value);
+void atom_moov_add_3gp_str_int_tag   (AtomMOOV * moov, guint32 fourcc, const gchar * value,
+                                      gint16 ivalue);
+void atom_moov_add_3gp_tag           (AtomMOOV * moov, guint32 fourcc, guint8 * data,
+                                      guint size);
+
+#define GST_QT_MUX_DEFAULT_TAG_LANGUAGE   "eng"
+guint16  language_code               (const char * lang);
+
 #endif /* __ATOMS_H__ */
index d61075b..9b1fe65 100644 (file)
@@ -182,6 +182,15 @@ G_BEGIN_DECLS
 #define FOURCC_titl     GST_MAKE_FOURCC('t','i','t','l')
 #define FOURCC__cmt     GST_MAKE_FOURCC(0xa9, 'c','m','t')
 
+/* 3gp tags */
+#define FOURCC_dscp     GST_MAKE_FOURCC('d','s','c','p')
+#define FOURCC_perf     GST_MAKE_FOURCC('p','e','r','f')
+#define FOURCC_auth     GST_MAKE_FOURCC('a','u','t','h')
+#define FOURCC_yrrc     GST_MAKE_FOURCC('y','r','r','c')
+#define FOURCC_albm     GST_MAKE_FOURCC('a','l','b','m')
+#define FOURCC_loci     GST_MAKE_FOURCC('l','o','c','i')
+#define FOURCC_kywd     GST_MAKE_FOURCC('k','y','w','d')
+
 G_END_DECLS
 
 #endif /* __FOURCC_H__ */
index 913bfc3..fde4111 100644 (file)
@@ -349,8 +349,294 @@ gst_qt_mux_finalize (GObject * object)
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
-/* FIXME approach below is pretty Apple/MOV/MP4/iTunes specific,
- * and as such does not comply with e.g. 3GPP specs */
+static void
+gst_qt_mux_add_mp4_tag (GstQTMux * qtmux, const GstTagList * list,
+    const char *tag, const char *tag2, guint32 fourcc)
+{
+  switch (gst_tag_get_type (tag)) {
+      /* strings */
+    case G_TYPE_STRING:
+    {
+      gchar *str = NULL;
+
+      if (!gst_tag_list_get_string (list, tag, &str) || !str)
+        break;
+      GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %s",
+          GST_FOURCC_ARGS (fourcc), str);
+      atom_moov_add_str_tag (qtmux->moov, fourcc, str);
+      g_free (str);
+      break;
+    }
+      /* double */
+    case G_TYPE_DOUBLE:
+    {
+      gdouble value;
+
+      if (!gst_tag_list_get_double (list, tag, &value))
+        break;
+      GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %u",
+          GST_FOURCC_ARGS (fourcc), (gint) value);
+      atom_moov_add_uint_tag (qtmux->moov, fourcc, 21, (gint) value);
+      break;
+    }
+      /* paired unsigned integers */
+    case G_TYPE_UINT:
+    {
+      guint value;
+      guint count;
+
+      if (!gst_tag_list_get_uint (list, tag, &value) ||
+          !gst_tag_list_get_uint (list, tag2, &count))
+        break;
+      GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %u/%u",
+          GST_FOURCC_ARGS (fourcc), value, count);
+      atom_moov_add_uint_tag (qtmux->moov, fourcc, 0,
+          value << 16 | (count & 0xFFFF));
+      break;
+    }
+    default:
+      g_assert_not_reached ();
+      break;
+  }
+}
+
+static void
+gst_qt_mux_add_mp4_date (GstQTMux * qtmux, const GstTagList * list,
+    const char *tag, const char *tag2, guint32 fourcc)
+{
+  GDate *date = NULL;
+  GDateYear year;
+  GDateMonth month;
+  GDateDay day;
+  gchar *str;
+
+  g_return_if_fail (gst_tag_get_type (tag) == GST_TYPE_DATE);
+
+  if (!gst_tag_list_get_date (list, tag, &date) || !date)
+    return;
+
+  year = g_date_get_year (date);
+  month = g_date_get_month (date);
+  day = g_date_get_day (date);
+
+  if (year == G_DATE_BAD_YEAR && month == G_DATE_BAD_MONTH &&
+      day == G_DATE_BAD_DAY) {
+    GST_WARNING_OBJECT (qtmux, "invalid date in tag");
+    return;
+  }
+
+  str = g_strdup_printf ("%u-%u-%u", year, month, day);
+  GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %s",
+      GST_FOURCC_ARGS (fourcc), str);
+  atom_moov_add_str_tag (qtmux->moov, fourcc, str);
+}
+
+static void
+gst_qt_mux_add_mp4_cover (GstQTMux * qtmux, const GstTagList * list,
+    const char *tag, const char *tag2, guint32 fourcc)
+{
+  GValue value = { 0, };
+  GstBuffer *buf;
+  GstCaps *caps;
+  GstStructure *structure;
+  gint flags = 0;
+
+  g_return_if_fail (gst_tag_get_type (tag) == GST_TYPE_BUFFER);
+
+  if (!gst_tag_list_copy_value (&value, list, tag))
+    return;
+
+  buf = gst_value_get_buffer (&value);
+  if (!buf)
+    goto done;
+
+  caps = gst_buffer_get_caps (buf);
+  if (!caps) {
+    GST_WARNING_OBJECT (qtmux, "preview image without caps");
+    goto done;
+  }
+
+  GST_DEBUG_OBJECT (qtmux, "preview image caps %" GST_PTR_FORMAT, caps);
+
+  structure = gst_caps_get_structure (caps, 0);
+  if (gst_structure_has_name (structure, "image/jpeg"))
+    flags = 13;
+  else if (gst_structure_has_name (structure, "image/png"))
+    flags = 14;
+  gst_caps_unref (caps);
+
+  if (!flags) {
+    GST_WARNING_OBJECT (qtmux, "preview image format not supported");
+    goto done;
+  }
+
+  GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT
+      " -> image size %d", GST_FOURCC_ARGS (fourcc), GST_BUFFER_SIZE (buf));
+  atom_moov_add_tag (qtmux->moov, fourcc, flags, GST_BUFFER_DATA (buf),
+      GST_BUFFER_SIZE (buf));
+done:
+  g_value_unset (&value);
+}
+
+static void
+gst_qt_mux_add_3gp_str (GstQTMux * qtmux, const GstTagList * list,
+    const char *tag, const char *tag2, guint32 fourcc)
+{
+  gchar *str = NULL;
+  guint number;
+
+  g_return_if_fail (gst_tag_get_type (tag) == G_TYPE_STRING);
+  g_return_if_fail (!tag2 || gst_tag_get_type (tag2) == G_TYPE_UINT);
+
+  if (!gst_tag_list_get_string (list, tag, &str) || !str)
+    return;
+
+  if (tag2)
+    if (!gst_tag_list_get_uint (list, tag2, &number))
+      tag2 = NULL;
+
+  if (!tag2) {
+    GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %s",
+        GST_FOURCC_ARGS (fourcc), str);
+    atom_moov_add_3gp_str_tag (qtmux->moov, fourcc, str);
+  } else {
+    GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %s/%d",
+        GST_FOURCC_ARGS (fourcc), str, number);
+    atom_moov_add_3gp_str_int_tag (qtmux->moov, fourcc, str, number);
+  }
+
+  g_free (str);
+}
+
+static void
+gst_qt_mux_add_3gp_date (GstQTMux * qtmux, const GstTagList * list,
+    const char *tag, const char *tag2, guint32 fourcc)
+{
+  GDate *date = NULL;
+  GDateYear year;
+
+  g_return_if_fail (gst_tag_get_type (tag) == GST_TYPE_DATE);
+
+  if (!gst_tag_list_get_date (list, tag, &date) || !date)
+    return;
+
+  year = g_date_get_year (date);
+
+  if (year == G_DATE_BAD_YEAR) {
+    GST_WARNING_OBJECT (qtmux, "invalid date in tag");
+    return;
+  }
+
+  GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %d", year);
+  atom_moov_add_3gp_uint_tag (qtmux->moov, fourcc, year);
+}
+
+static void
+gst_qt_mux_add_3gp_location (GstQTMux * qtmux, const GstTagList * list,
+    const char *tag, const char *tag2, guint32 fourcc)
+{
+  gdouble latitude = -360, longitude = -360, altitude = 0;
+  gchar *location = NULL;
+  guint8 *data, *ddata;
+  gint size = 0, len = 0;
+  gboolean ret = FALSE;
+
+  g_return_if_fail (strcmp (tag, GST_TAG_GEO_LOCATION_NAME) == 0);
+
+  ret = gst_tag_list_get_string (list, tag, &location);
+  ret |= gst_tag_list_get_double (list, GST_TAG_GEO_LOCATION_LONGITUDE,
+      &longitude);
+  ret |= gst_tag_list_get_double (list, GST_TAG_GEO_LOCATION_LATITUDE,
+      &latitude);
+  ret |= gst_tag_list_get_double (list, GST_TAG_GEO_LOCATION_ELEVATION,
+      &altitude);
+
+  if (!ret)
+    return;
+
+  if (location)
+    len = strlen (location);
+  size += len + 1 + 2;
+
+  /* role + (long, lat, alt) + body + notes */
+  size += 1 + 3 * 4 + 1 + 1;
+
+  data = ddata = g_malloc (size);
+
+  /* language tag */
+  GST_WRITE_UINT16_BE (data, language_code (GST_QT_MUX_DEFAULT_TAG_LANGUAGE));
+  /* location */
+  if (location)
+    memcpy (data + 2, location, len);
+  GST_WRITE_UINT8 (data + 2 + len, 0);
+  data += len + 1 + 2;
+  /* role */
+  GST_WRITE_UINT8 (data, 0);
+  /* long, lat, alt */
+  GST_WRITE_UINT32_BE (data + 1, (guint32) (longitude * 65536.0));
+  GST_WRITE_UINT32_BE (data + 5, (guint32) (latitude * 65536.0));
+  GST_WRITE_UINT32_BE (data + 9, (guint32) (altitude * 65536.0));
+  /* neither astronomical body nor notes */
+  GST_WRITE_UINT16_BE (data + 13, 0);
+
+  GST_DEBUG_OBJECT (qtmux, "Adding tag 'loci'");
+  atom_moov_add_3gp_tag (qtmux->moov, fourcc, ddata, size);
+  g_free (ddata);
+}
+
+static void
+gst_qt_mux_add_3gp_keywords (GstQTMux * qtmux, const GstTagList * list,
+    const char *tag, const char *tag2, guint32 fourcc)
+{
+  gchar *keywords = NULL;
+  guint8 *data, *ddata;
+  gint size = 0, i;
+  gchar **kwds;
+
+  g_return_if_fail (strcmp (tag, GST_TAG_KEYWORDS) == 0);
+
+  if (!gst_tag_list_get_string (list, tag, &keywords) || !keywords)
+    return;
+
+  kwds = g_strsplit (keywords, ",", 0);
+
+  size = 0;
+  for (i = 0; kwds[i]; i++) {
+    /* size byte + null-terminator */
+    size += strlen (kwds[i]) + 1 + 1;
+  }
+
+  /* language tag + count + keywords */
+  size += 2 + 1;
+
+  data = ddata = g_malloc (size);
+
+  /* language tag */
+  GST_WRITE_UINT16_BE (data, language_code (GST_QT_MUX_DEFAULT_TAG_LANGUAGE));
+  /* count */
+  GST_WRITE_UINT8 (data + 2, i);
+  data += 3;
+  /* keywords */
+  for (i = 0; kwds[i]; ++i) {
+    gint len = strlen (kwds[i]);
+
+    GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %s",
+        GST_FOURCC_ARGS (fourcc), kwds[i]);
+    /* size */
+    GST_WRITE_UINT8 (data, len + 1);
+    memcpy (data + 1, kwds[i], len + 1);
+    data += len + 2;
+  }
+
+  g_strfreev (kwds);
+
+  atom_moov_add_3gp_tag (qtmux->moov, fourcc, ddata, size);
+  g_free (ddata);
+}
+
+
+typedef void (*GstQTMuxAddTagFunc) (GstQTMux * mux, const GstTagList * list,
+    const char *tag, const char *tag2, guint32 fourcc);
 
 /*
  * Struct to record mappings from gstreamer tags to fourcc codes
@@ -360,25 +646,42 @@ typedef struct _GstTagToFourcc
   guint32 fourcc;
   const gchar *gsttag;
   const gchar *gsttag2;
+  const GstQTMuxAddTagFunc func;
 } GstTagToFourcc;
 
 /* tag list tags to fourcc matching */
-static const GstTagToFourcc tag_matches[] = {
-  {FOURCC__alb, GST_TAG_ALBUM,},
-  {FOURCC__ART, GST_TAG_ARTIST,},
-  {FOURCC__cmt, GST_TAG_COMMENT,},
-  {FOURCC__wrt, GST_TAG_COMPOSER,},
-  {FOURCC__gen, GST_TAG_GENRE,},
-  {FOURCC__nam, GST_TAG_TITLE,},
-  {FOURCC__des, GST_TAG_DESCRIPTION,},
-  {FOURCC__too, GST_TAG_ENCODER,},
-  {FOURCC_cprt, GST_TAG_COPYRIGHT,},
-  {FOURCC_keyw, GST_TAG_KEYWORDS,},
-  {FOURCC__day, GST_TAG_DATE,},
-  {FOURCC_tmpo, GST_TAG_BEATS_PER_MINUTE,},
-  {FOURCC_trkn, GST_TAG_TRACK_NUMBER, GST_TAG_TRACK_COUNT},
-  {FOURCC_disk, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT},
-  {FOURCC_covr, GST_TAG_PREVIEW_IMAGE,},
+static const GstTagToFourcc tag_matches_mp4[] = {
+  {FOURCC__alb, GST_TAG_ALBUM, NULL, gst_qt_mux_add_mp4_tag},
+  {FOURCC__ART, GST_TAG_ARTIST, NULL, gst_qt_mux_add_mp4_tag},
+  {FOURCC__cmt, GST_TAG_COMMENT, NULL, gst_qt_mux_add_mp4_tag},
+  {FOURCC__wrt, GST_TAG_COMPOSER, NULL, gst_qt_mux_add_mp4_tag},
+  {FOURCC__gen, GST_TAG_GENRE, NULL, gst_qt_mux_add_mp4_tag},
+  {FOURCC__nam, GST_TAG_TITLE, NULL, gst_qt_mux_add_mp4_tag},
+  {FOURCC__des, GST_TAG_DESCRIPTION, NULL, gst_qt_mux_add_mp4_tag},
+  {FOURCC__too, GST_TAG_ENCODER, NULL, gst_qt_mux_add_mp4_tag},
+  {FOURCC_cprt, GST_TAG_COPYRIGHT, NULL, gst_qt_mux_add_mp4_tag},
+  {FOURCC_keyw, GST_TAG_KEYWORDS, NULL, gst_qt_mux_add_mp4_tag},
+  {FOURCC__day, GST_TAG_DATE, NULL, gst_qt_mux_add_mp4_date},
+  {FOURCC_tmpo, GST_TAG_BEATS_PER_MINUTE, NULL, gst_qt_mux_add_mp4_tag},
+  {FOURCC_trkn, GST_TAG_TRACK_NUMBER, GST_TAG_TRACK_COUNT,
+      gst_qt_mux_add_mp4_tag},
+  {FOURCC_disk, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
+      gst_qt_mux_add_mp4_tag},
+  {FOURCC_covr, GST_TAG_PREVIEW_IMAGE, NULL, gst_qt_mux_add_mp4_cover},
+  {0, NULL,}
+};
+
+static const GstTagToFourcc tag_matches_3gp[] = {
+  {FOURCC_titl, GST_TAG_TITLE, NULL, gst_qt_mux_add_3gp_str},
+  {FOURCC_dscp, GST_TAG_DESCRIPTION, NULL, gst_qt_mux_add_3gp_str},
+  {FOURCC_cprt, GST_TAG_COPYRIGHT, NULL, gst_qt_mux_add_3gp_str},
+  {FOURCC_perf, GST_TAG_ARTIST, NULL, gst_qt_mux_add_3gp_str},
+  {FOURCC_auth, GST_TAG_COMPOSER, NULL, gst_qt_mux_add_3gp_str},
+  {FOURCC_gnre, GST_TAG_GENRE, NULL, gst_qt_mux_add_3gp_str},
+  {FOURCC_kywd, GST_TAG_KEYWORDS, NULL, gst_qt_mux_add_3gp_keywords},
+  {FOURCC_yrrc, GST_TAG_DATE, NULL, gst_qt_mux_add_3gp_date},
+  {FOURCC_albm, GST_TAG_ALBUM, GST_TAG_TRACK_NUMBER, gst_qt_mux_add_3gp_str},
+  {FOURCC_loci, GST_TAG_GEO_LOCATION_NAME, NULL, gst_qt_mux_add_3gp_location},
   {0, NULL,}
 };
 
@@ -388,127 +691,35 @@ static const GstTagToFourcc tag_matches[] = {
 static void
 gst_qt_mux_add_metadata_tags (GstQTMux * qtmux, const GstTagList * list)
 {
+  GstQTMuxClass *qtmux_klass = (GstQTMuxClass *) (G_OBJECT_GET_CLASS (qtmux));
   guint32 fourcc;
   gint i;
   const gchar *tag, *tag2;
+  const GstTagToFourcc *tag_matches;
+
+  switch (qtmux_klass->format) {
+    case GST_QT_MUX_FORMAT_3GP:
+      tag_matches = tag_matches_3gp;
+      break;
+    case GST_QT_MUX_FORMAT_MJ2:
+      tag_matches = NULL;
+      break;
+    default:
+      /* sort of iTunes style for mp4 and QT (?) */
+      tag_matches = tag_matches_mp4;
+      break;
+  }
+
+  if (!tag_matches)
+    return;
 
   for (i = 0; tag_matches[i].fourcc; i++) {
     fourcc = tag_matches[i].fourcc;
     tag = tag_matches[i].gsttag;
     tag2 = tag_matches[i].gsttag2;
 
-    switch (gst_tag_get_type (tag)) {
-        /* strings */
-      case G_TYPE_STRING:
-      {
-        gchar *str = NULL;
-
-        if (!gst_tag_list_get_string (list, tag, &str) || !str)
-          break;
-        GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %s",
-            GST_FOURCC_ARGS (fourcc), str);
-        atom_moov_add_str_tag (qtmux->moov, fourcc, str);
-        g_free (str);
-        break;
-      }
-        /* double */
-      case G_TYPE_DOUBLE:
-      {
-        gdouble value;
-
-        if (!gst_tag_list_get_double (list, tag, &value))
-          break;
-        GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %u",
-            GST_FOURCC_ARGS (fourcc), (gint) value);
-        atom_moov_add_uint_tag (qtmux->moov, fourcc, 21, (gint) value);
-        break;
-      }
-        /* paired unsigned integers */
-      case G_TYPE_UINT:
-      {
-        guint value;
-        guint count;
-
-        if (!gst_tag_list_get_uint (list, tag, &value) ||
-            !gst_tag_list_get_uint (list, tag2, &count))
-          break;
-        GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %u/%u",
-            GST_FOURCC_ARGS (fourcc), value, count);
-        atom_moov_add_uint_tag (qtmux->moov, fourcc, 0,
-            value << 16 | (count & 0xFFFF));
-        break;
-      }
-      default:
-      {
-        if (gst_tag_get_type (tag) == GST_TYPE_DATE) {
-          GDate *date = NULL;
-          GDateYear year;
-          GDateMonth month;
-          GDateDay day;
-          gchar *str;
-
-          if (!gst_tag_list_get_date (list, tag, &date) || !date)
-            break;
-          year = g_date_get_year (date);
-          month = g_date_get_month (date);
-          day = g_date_get_day (date);
-
-          if (year == G_DATE_BAD_YEAR && month == G_DATE_BAD_MONTH &&
-              day == G_DATE_BAD_DAY) {
-            GST_WARNING_OBJECT (qtmux, "invalid date in tag");
-            break;
-          }
-
-          str = g_strdup_printf ("%u-%u-%u", year, month, day);
-          GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %s",
-              GST_FOURCC_ARGS (fourcc), str);
-          atom_moov_add_str_tag (qtmux->moov, fourcc, str);
-        } else if (gst_tag_get_type (tag) == GST_TYPE_BUFFER) {
-          GValue value = { 0, };
-          GstBuffer *buf;
-          GstCaps *caps;
-          GstStructure *structure;
-          gint flags = 0;
-
-          if (!gst_tag_list_copy_value (&value, list, tag))
-            break;
-
-          buf = gst_value_get_buffer (&value);
-          if (!buf)
-            goto done;
-
-          caps = gst_buffer_get_caps (buf);
-          if (!caps) {
-            GST_WARNING_OBJECT (qtmux, "preview image without caps");
-            goto done;
-          }
-
-          GST_DEBUG_OBJECT (qtmux, "preview image caps %" GST_PTR_FORMAT, caps);
-
-          structure = gst_caps_get_structure (caps, 0);
-          if (gst_structure_has_name (structure, "image/jpeg"))
-            flags = 13;
-          else if (gst_structure_has_name (structure, "image/png"))
-            flags = 14;
-          gst_caps_unref (caps);
-
-          if (!flags) {
-            GST_WARNING_OBJECT (qtmux, "preview image format not supported");
-            goto done;
-          }
-
-          GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT
-              " -> image size %d", GST_FOURCC_ARGS (fourcc),
-              GST_BUFFER_SIZE (buf));
-          atom_moov_add_tag (qtmux->moov, fourcc, flags, GST_BUFFER_DATA (buf),
-              GST_BUFFER_SIZE (buf));
-        done:
-          g_value_unset (&value);
-        } else
-          g_assert_not_reached ();
-        break;
-      }
-    }
+    g_assert (tag_matches[i].func);
+    tag_matches[i].func (qtmux, list, tag, tag2, fourcc);
   }
 
   /* add unparsed blobs if present */
@@ -532,8 +743,12 @@ gst_qt_mux_add_metadata_tags (GstQTMux * qtmux, const GstTagList * list)
             GST_PTR_FORMAT, i, num_tags, GST_BUFFER_SIZE (buf), caps);
         s = gst_caps_get_structure (caps, 0);
         if (s && (style = gst_structure_get_string (s, "style"))) {
-          /* FIXME make into a parameter */
-          if (strcmp (style, "itunes") == 0) {
+          /* try to prevent some style tag ending up into another variant
+           * (todo: make into a list if more cases) */
+          if ((strcmp (style, "itunes") == 0 &&
+                  qtmux_klass->format == GST_QT_MUX_FORMAT_MP4) ||
+              (strcmp (style, "iso") == 0 &&
+                  qtmux_klass->format == GST_QT_MUX_FORMAT_3GP)) {
             GST_DEBUG_OBJECT (qtmux, "Adding private tag");
             atom_moov_add_blob_tag (qtmux->moov, GST_BUFFER_DATA (buf),
                 GST_BUFFER_SIZE (buf));
index e0e4fae..808856a 100644 (file)
@@ -201,6 +201,8 @@ gst_qt_mux_map_format_to_flavor (GstQTMuxFormat format)
 {
   if (format == GST_QT_MUX_FORMAT_QT)
     return ATOMS_TREE_FLAVOR_MOV;
+  else if (format == GST_QT_MUX_FORMAT_3GP)
+    return ATOMS_TREE_FLAVOR_3GP;
   else
     return ATOMS_TREE_FLAVOR_ISOM;
 }