mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 19:51:11 +00:00
qtmux: add 3GP style tagging (and refactor appropriately)
This commit is contained in:
parent
65b69ddc99
commit
7c9a6093d8
5 changed files with 487 additions and 151 deletions
|
@ -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;
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue