qtmux: add 3GP style tagging (and refactor appropriately)

This commit is contained in:
Mark Nauwelaerts 2009-06-10 12:42:44 +02:00
parent b0c0651d7c
commit 1aeb7d9b54
5 changed files with 487 additions and 151 deletions

View 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;

View 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__ */

View 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__ */

View 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));

View 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;
}