qtmux: Follow xmp serialization guidelines closer

qt and isom variants have different ways of serializing
xmp, follow these guidelines.

Those can be found in Adobe's xmp docs.
This commit is contained in:
Thiago Santos 2010-09-15 17:54:49 -03:00 committed by Tim-Philipp Müller
parent 365b419216
commit 7065a65ec8
5 changed files with 146 additions and 16 deletions

View file

@ -206,6 +206,23 @@ atom_data_free (AtomData * data)
g_free (data); g_free (data);
} }
static AtomUUID *
atom_uuid_new (void)
{
AtomUUID *uuid = g_new0 (AtomUUID, 1);
atom_header_set (&uuid->header, FOURCC_uuid, 0, 0);
return uuid;
}
static void
atom_uuid_free (AtomUUID * data)
{
atom_clear (&data->header);
g_free (data->data);
g_free (data);
}
static void static void
atom_ftyp_init (AtomFTYP * ftyp, guint32 major, guint32 version, GList * brands) atom_ftyp_init (AtomFTYP * ftyp, guint32 major, guint32 version, GList * brands)
{ {
@ -1319,6 +1336,23 @@ atom_data_copy_data (AtomData * data, guint8 ** buffer, guint64 * size,
return *offset - original_offset; return *offset - original_offset;
} }
static guint64
atom_uuid_copy_data (AtomUUID * uuid, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_copy_data (&uuid->header, buffer, size, offset)) {
return 0;
}
prop_copy_uint8_array (uuid->uuid, 16, buffer, size, offset);
if (uuid->datalen)
prop_copy_uint8_array (uuid->data, uuid->datalen, buffer, size, offset);
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
guint64 guint64
atom_ftyp_copy_data (AtomFTYP * ftyp, guint8 ** buffer, guint64 * size, atom_ftyp_copy_data (AtomFTYP * ftyp, guint8 ** buffer, guint64 * size,
guint64 * offset) guint64 * offset)
@ -2775,17 +2809,23 @@ atom_moov_add_3gp_uint_tag (AtomMOOV * moov, guint32 fourcc, guint16 value)
void void
atom_moov_add_xmp_tags (AtomMOOV * moov, const GstTagList * tags) atom_moov_add_xmp_tags (AtomMOOV * moov, const GstTagList * tags)
{ {
GstBuffer *xmpbuffer = gst_tag_list_to_xmp_buffer (tags, TRUE); GstBuffer *xmpbuffer;
AtomData *data_atom = NULL; AtomData *data_atom = NULL;
data_atom = atom_data_new_from_gst_buffer (FOURCC_XMP_, xmpbuffer); if (moov->context.flavor == ATOMS_TREE_FLAVOR_MOV) {
gst_buffer_unref (xmpbuffer); xmpbuffer = gst_tag_list_to_xmp_buffer (tags, TRUE);
if (xmpbuffer) {
data_atom = atom_data_new_from_gst_buffer (FOURCC_XMP_, xmpbuffer);
atom_moov_init_metatags (moov, &moov->context);
moov->udta->entries = g_list_append (moov->udta->entries,
build_atom_info_wrapper ((Atom *) data_atom, atom_data_copy_data,
atom_data_free));
gst_buffer_unref (xmpbuffer);
}
} else {
GST_DEBUG ("Not adding xmp to moov atom, it is only used in 'mov' format");
}
atom_moov_init_metatags (moov, &moov->context);
moov->udta->entries = g_list_append (moov->udta->entries,
build_atom_info_wrapper ((Atom *) data_atom, atom_data_copy_data,
atom_data_free));
} }
/* /*
@ -3566,3 +3606,30 @@ build_ima_adpcm_extension (gint channels, gint rate, gint blocksize)
return build_atom_info_wrapper ((Atom *) wave, atom_wave_copy_data, return build_atom_info_wrapper ((Atom *) wave, atom_wave_copy_data,
atom_wave_free); atom_wave_free);
} }
AtomInfo *
build_uuid_xmp_atom (const GstTagList * taglist)
{
GstBuffer *xmp_data;
AtomUUID *uuid;
static guint8 xmp_uuid[] = { 0xBE, 0x7A, 0xCF, 0xCB,
0x97, 0xA9, 0x42, 0xE8,
0x9C, 0x71, 0x99, 0x94,
0x91, 0xE3, 0xAF, 0xAC
};
xmp_data = gst_tag_list_to_xmp_buffer (taglist, TRUE);
if (xmp_data == NULL)
return NULL;
uuid = atom_uuid_new ();
memcpy (uuid->uuid, xmp_uuid, 16);
uuid->data = g_malloc (GST_BUFFER_SIZE (xmp_data));
uuid->datalen = GST_BUFFER_SIZE (xmp_data);
memcpy (uuid->data, GST_BUFFER_DATA (xmp_data), GST_BUFFER_SIZE (xmp_data));
gst_buffer_unref (xmp_data);
return build_atom_info_wrapper ((Atom *) uuid, atom_uuid_copy_data,
atom_uuid_free);
}

View file

@ -147,9 +147,22 @@ typedef struct _AtomData
/* not written */ /* not written */
guint32 datalen; guint32 datalen;
guint8 *data; guint8 *data;
} AtomData; } AtomData;
typedef struct _AtomUUID
{
Atom header;
guint8 uuid[16];
/* not written */
guint32 datalen;
guint8 *data;
} AtomUUID;
typedef struct _AtomFTYP typedef struct _AtomFTYP
{ {
Atom header; Atom header;
@ -741,6 +754,7 @@ AtomInfo * build_gama_atom (gdouble gamma);
AtomInfo * build_SMI_atom (const GstBuffer *seqh); AtomInfo * build_SMI_atom (const GstBuffer *seqh);
AtomInfo * build_ima_adpcm_extension (gint channels, gint rate, AtomInfo * build_ima_adpcm_extension (gint channels, gint rate,
gint blocksize); gint blocksize);
AtomInfo * build_uuid_xmp_atom (const GstTagList * taglist);
/* /*

View file

@ -187,6 +187,7 @@ G_BEGIN_DECLS
#define FOURCC_soco GST_MAKE_FOURCC('s','o','c','o') #define FOURCC_soco GST_MAKE_FOURCC('s','o','c','o')
#define FOURCC_sosn GST_MAKE_FOURCC('s','o','s','n') #define FOURCC_sosn GST_MAKE_FOURCC('s','o','s','n')
#define FOURCC_XMP_ GST_MAKE_FOURCC('X','M','P','_') #define FOURCC_XMP_ GST_MAKE_FOURCC('X','M','P','_')
#define FOURCC_uuid GST_MAKE_FOURCC('u','u','i','d')
/* SVQ3 fourcc */ /* SVQ3 fourcc */

View file

@ -300,6 +300,12 @@ gst_qt_mux_reset (GstQTMux * qtmux, gboolean alloc)
fclose (qtmux->moov_recov_file); fclose (qtmux->moov_recov_file);
qtmux->moov_recov_file = NULL; qtmux->moov_recov_file = NULL;
} }
for (walk = qtmux->extra_atoms; walk; walk = g_slist_next (walk)) {
AtomInfo *ainfo = (AtomInfo *) walk->data;
ainfo->free_func (ainfo->atom);
}
g_slist_free (qtmux->extra_atoms);
qtmux->extra_atoms = NULL;
GST_OBJECT_LOCK (qtmux); GST_OBJECT_LOCK (qtmux);
gst_tag_setter_reset_tags (GST_TAG_SETTER (qtmux)); gst_tag_setter_reset_tags (GST_TAG_SETTER (qtmux));
@ -880,15 +886,24 @@ gst_qt_mux_add_xmp_tags (GstQTMux * qtmux, const GstTagList * list)
{ {
GstQTMuxClass *qtmux_klass = (GstQTMuxClass *) (G_OBJECT_GET_CLASS (qtmux)); GstQTMuxClass *qtmux_klass = (GstQTMuxClass *) (G_OBJECT_GET_CLASS (qtmux));
/* adobe specs only say 'quicktime', but I guess we can extrapolate to /* adobe specs only have 'quicktime' and 'mp4',
* mp4 and gpp. Keep mj2 out for now as we don't add any tags for it yet. * but I guess we can extrapolate to gpp.
* Keep mj2 out for now as we don't add any tags for it yet.
* If you have further info about xmp on these formats, please share */ * If you have further info about xmp on these formats, please share */
if (qtmux_klass->format == GST_QT_MUX_FORMAT_MJ2) if (qtmux_klass->format == GST_QT_MUX_FORMAT_MJ2)
return; return;
GST_DEBUG_OBJECT (qtmux, "Adding xmp tags"); GST_DEBUG_OBJECT (qtmux, "Adding xmp tags");
atom_moov_add_xmp_tags (qtmux->moov, list); if (qtmux_klass->format == GST_QT_MUX_FORMAT_QT) {
atom_moov_add_xmp_tags (qtmux->moov, list);
} else {
/* for isom/mp4, it is a top level uuid atom */
AtomInfo *ainfo = build_uuid_xmp_atom (list);
if (ainfo) {
qtmux->extra_atoms = g_slist_prepend (qtmux->extra_atoms, ainfo);
}
}
} }
static void static void
@ -1489,6 +1504,17 @@ gst_qt_mux_stop_file (GstQTMux * qtmux)
GST_DEBUG_OBJECT (qtmux, "calculated moov atom size %" G_GUINT64_FORMAT, GST_DEBUG_OBJECT (qtmux, "calculated moov atom size %" G_GUINT64_FORMAT,
offset); offset);
offset += qtmux->header_size + (large_file ? 16 : 8); offset += qtmux->header_size + (large_file ? 16 : 8);
/* sum up with the extra atoms size */
for (walk = qtmux->extra_atoms; walk; walk = g_slist_next (walk)) {
guint64 extra_size = 0, extra_offset = 0;
AtomInfo *ainfo = (AtomInfo *) walk->data;
if (!ainfo->copy_data_func (ainfo->atom, NULL, &extra_size,
&extra_offset))
goto serialize_error;
offset += extra_offset;
}
} else } else
offset = qtmux->header_size; offset = qtmux->header_size;
atom_moov_chunks_add_offset (qtmux->moov, offset); atom_moov_chunks_add_offset (qtmux->moov, offset);
@ -1497,8 +1523,7 @@ gst_qt_mux_stop_file (GstQTMux * qtmux)
offset = size = 0; offset = size = 0;
data = NULL; data = NULL;
GST_LOG_OBJECT (qtmux, "Copying movie header into buffer"); GST_LOG_OBJECT (qtmux, "Copying movie header into buffer");
ret = atom_moov_copy_data (qtmux->moov, &data, &size, &offset); if (!atom_moov_copy_data (qtmux->moov, &data, &size, &offset))
if (!ret)
goto serialize_error; goto serialize_error;
buffer = gst_buffer_new (); buffer = gst_buffer_new ();
@ -1509,6 +1534,23 @@ gst_qt_mux_stop_file (GstQTMux * qtmux)
GST_DEBUG_OBJECT (qtmux, "Pushing movie atoms"); GST_DEBUG_OBJECT (qtmux, "Pushing movie atoms");
gst_qt_mux_send_buffer (qtmux, buffer, NULL, FALSE); gst_qt_mux_send_buffer (qtmux, buffer, NULL, FALSE);
/* push extra top-level atoms */
for (walk = qtmux->extra_atoms; walk; walk = g_slist_next (walk)) {
AtomInfo *ainfo = (AtomInfo *) walk->data;
offset = size = 0;
data = NULL;
if (!ainfo->copy_data_func (ainfo->atom, &data, &size, &offset))
goto serialize_error;
buffer = gst_buffer_new ();
GST_BUFFER_MALLOCDATA (buffer) = GST_BUFFER_DATA (buffer) = data;
GST_BUFFER_SIZE (buffer) = offset;
GST_DEBUG_OBJECT (qtmux, "Pushing extra top-level atom %" GST_FOURCC_FORMAT,
GST_FOURCC_ARGS (ainfo->atom->type));
gst_qt_mux_send_buffer (qtmux, buffer, NULL, FALSE);
}
/* if needed, send mdat atom and move buffered data into it */ /* if needed, send mdat atom and move buffered data into it */
if (qtmux->fast_start_file) { if (qtmux->fast_start_file) {
/* mdat size = accumulated (buffered data) + mdat atom header */ /* mdat size = accumulated (buffered data) + mdat atom header */
@ -1520,12 +1562,12 @@ gst_qt_mux_stop_file (GstQTMux * qtmux)
if (ret != GST_FLOW_OK) if (ret != GST_FLOW_OK)
return ret; return ret;
} else { } else {
/* mdata needs update iff not using faststart */ /* mdat needs update iff not using faststart */
GST_DEBUG_OBJECT (qtmux, "updating mdata size"); GST_DEBUG_OBJECT (qtmux, "updating mdat size");
ret = gst_qt_mux_update_mdat_size (qtmux, qtmux->mdat_pos, ret = gst_qt_mux_update_mdat_size (qtmux, qtmux->mdat_pos,
qtmux->mdat_size, NULL); qtmux->mdat_size, NULL);
/* note; no seeking back to the end of file is done, /* note; no seeking back to the end of file is done,
* since we longer write anything anyway */ * since we no longer write anything anyway */
} }
return ret; return ret;
@ -1874,8 +1916,12 @@ gst_qt_mux_collected (GstCollectPads * pads, gpointer user_data)
} else { } else {
ret = gst_qt_mux_stop_file (qtmux); ret = gst_qt_mux_stop_file (qtmux);
if (ret == GST_FLOW_OK) { if (ret == GST_FLOW_OK) {
GST_DEBUG_OBJECT (qtmux, "Pushing eos");
gst_pad_push_event (qtmux->srcpad, gst_event_new_eos ()); gst_pad_push_event (qtmux->srcpad, gst_event_new_eos ());
ret = GST_FLOW_UNEXPECTED; ret = GST_FLOW_UNEXPECTED;
} else {
GST_WARNING_OBJECT (qtmux, "Failed to stop file: %s",
gst_flow_get_name (ret));
} }
qtmux->state = GST_QT_MUX_STATE_EOS; qtmux->state = GST_QT_MUX_STATE_EOS;
} }

View file

@ -144,6 +144,8 @@ struct _GstQTMux
AtomsContext *context; AtomsContext *context;
AtomFTYP *ftyp; AtomFTYP *ftyp;
AtomMOOV *moov; AtomMOOV *moov;
GSList *extra_atoms; /* list of extra top-level atoms (e.g. UUID for xmp)
* Stored as AtomInfo structs */
/* fast start */ /* fast start */
FILE *fast_start_file; FILE *fast_start_file;