mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 08:46:40 +00:00
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:
parent
365b419216
commit
7065a65ec8
5 changed files with 146 additions and 16 deletions
|
@ -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;
|
||||||
|
|
||||||
|
if (moov->context.flavor == ATOMS_TREE_FLAVOR_MOV) {
|
||||||
|
xmpbuffer = gst_tag_list_to_xmp_buffer (tags, TRUE);
|
||||||
|
if (xmpbuffer) {
|
||||||
data_atom = atom_data_new_from_gst_buffer (FOURCC_XMP_, xmpbuffer);
|
data_atom = atom_data_new_from_gst_buffer (FOURCC_XMP_, xmpbuffer);
|
||||||
gst_buffer_unref (xmpbuffer);
|
|
||||||
|
|
||||||
atom_moov_init_metatags (moov, &moov->context);
|
atom_moov_init_metatags (moov, &moov->context);
|
||||||
|
|
||||||
moov->udta->entries = g_list_append (moov->udta->entries,
|
moov->udta->entries = g_list_append (moov->udta->entries,
|
||||||
build_atom_info_wrapper ((Atom *) data_atom, atom_data_copy_data,
|
build_atom_info_wrapper ((Atom *) data_atom, atom_data_copy_data,
|
||||||
atom_data_free));
|
atom_data_free));
|
||||||
|
gst_buffer_unref (xmpbuffer);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
GST_DEBUG ("Not adding xmp to moov atom, it is only used in 'mov' format");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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");
|
||||||
|
|
||||||
|
if (qtmux_klass->format == GST_QT_MUX_FORMAT_QT) {
|
||||||
atom_moov_add_xmp_tags (qtmux->moov, list);
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue