summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>2009-06-10 12:42:44 +0200
committerMark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>2009-06-19 18:12:58 +0200
commit1aeb7d9b54687d11dfe21b0637de4f53a32b1f1c (patch)
tree0387df68de8126cff074e501c11a21ecd3d6ec3a
parentb0c0651d7ceec26d3d62e46eabeaa4ad2bc73a18 (diff)
qtmux: add 3GP style tagging (and refactor appropriately)
-rw-r--r--gst/qtmux/atoms.c129
-rw-r--r--gst/qtmux/atoms.h19
-rw-r--r--gst/qtmux/fourcc.h9
-rw-r--r--gst/qtmux/gstqtmux.c479
-rw-r--r--gst/qtmux/gstqtmuxmap.c2
5 files changed, 487 insertions, 151 deletions
diff --git a/gst/qtmux/atoms.c b/gst/qtmux/atoms.c
index 2ce8631dc..be578f299 100644
--- a/gst/qtmux/atoms.c
+++ b/gst/qtmux/atoms.c
@@ -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;
diff --git a/gst/qtmux/atoms.h b/gst/qtmux/atoms.h
index 0bf3f207b..4c94141b3 100644
--- a/gst/qtmux/atoms.h
+++ b/gst/qtmux/atoms.h
@@ -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__ */
diff --git a/gst/qtmux/fourcc.h b/gst/qtmux/fourcc.h
index d61075bb0..9b1fe65a3 100644
--- a/gst/qtmux/fourcc.h
+++ b/gst/qtmux/fourcc.h
@@ -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__ */
diff --git a/gst/qtmux/gstqtmux.c b/gst/qtmux/gstqtmux.c
index 913bfc309..fde411163 100644
--- a/gst/qtmux/gstqtmux.c
+++ b/gst/qtmux/gstqtmux.c
@@ -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));
diff --git a/gst/qtmux/gstqtmuxmap.c b/gst/qtmux/gstqtmuxmap.c
index e0e4faed9..808856a22 100644
--- a/gst/qtmux/gstqtmuxmap.c
+++ b/gst/qtmux/gstqtmuxmap.c
@@ -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;
}