From 1aeb7d9b54687d11dfe21b0637de4f53a32b1f1c Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Wed, 10 Jun 2009 12:42:44 +0200 Subject: qtmux: add 3GP style tagging (and refactor appropriately) --- gst/qtmux/atoms.c | 129 +++++++++++-- gst/qtmux/atoms.h | 19 +- gst/qtmux/fourcc.h | 9 + gst/qtmux/gstqtmux.c | 479 +++++++++++++++++++++++++++++++++++------------- gst/qtmux/gstqtmuxmap.c | 2 + 5 files changed, 487 insertions(+), 151 deletions(-) (limited to 'gst') diff --git a/gst/qtmux/atoms.c b/gst/qtmux/atoms.c index 2ce8631d..be578f29 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 0bf3f207..4c94141b 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 d61075bb..9b1fe65a 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 913bfc30..fde41116 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 e0e4faed..808856a2 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; } -- cgit v1.2.1