gstreamer/gst/isomp4/atoms.c
Alex Ashley a965185dee qtdemux: Add support for the avc3 sample entry format of the AVC file format
Amendment 2 of ISO/IEC 14496-15 (AVC file format) is defining a new
structure for fragmented MP4 called "avc3". The principal difference
between AVC1 and AVC3 is the location of the codec initialisation
data (e.g. SPS, PPS). In AVC1 this data is placed in the initial
MOOV box (moov.trak.mdia.minf.stbl.stsd.avc1) but in AVC3 this data
goes in the first sample of every fragment (i.e. the first sample in
each mdat box).  The principal reason for avc3 is to make it easier
for client implementations, because it removes the requirement to
insert the SPS+PPS in to the decoder pipeline every time there is a
representation change.

This commit adds support for the "avc3" atom, which is almost identical
to the "avc1" atom, except it does not contain any SPS or PPS data.

https://bugzilla.gnome.org/show_bug.cgi?id=702004
2013-09-04 13:33:22 +02:00

4507 lines
112 KiB
C

/* Quicktime muxer plugin for GStreamer
* Copyright (C) 2008-2010 Thiago Santos <thiagoss@embedded.ufcg.edu.br>
* Copyright (C) 2008 Mark Nauwelaerts <mnauw@users.sf.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/*
* Unless otherwise indicated, Source Code is licensed under MIT license.
* See further explanation attached in License Statement (distributed in the file
* LICENSE).
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "atoms.h"
#include <string.h>
#include <glib.h>
#include <gst/gst.h>
#include <gst/base/gstbytewriter.h>
#include <gst/tag/tag.h>
/**
* Creates a new AtomsContext for the given flavor.
*/
AtomsContext *
atoms_context_new (AtomsTreeFlavor flavor)
{
AtomsContext *context = g_new0 (AtomsContext, 1);
context->flavor = flavor;
return context;
}
/**
* Frees an AtomsContext and all memory associated with it
*/
void
atoms_context_free (AtomsContext * context)
{
g_free (context);
}
/* -- creation, initialization, clear and free functions -- */
#define SECS_PER_DAY (24 * 60 * 60)
#define LEAP_YEARS_FROM_1904_TO_1970 17
static guint64
get_current_qt_time (void)
{
GTimeVal timeval;
g_get_current_time (&timeval);
/* FIXME this should use UTC coordinated time */
return timeval.tv_sec + (((1970 - 1904) * (guint64) 365) +
LEAP_YEARS_FROM_1904_TO_1970) * SECS_PER_DAY;
}
static void
common_time_info_init (TimeInfo * ti)
{
ti->creation_time = ti->modification_time = get_current_qt_time ();
ti->timescale = 0;
ti->duration = 0;
}
static void
atom_header_set (Atom * header, guint32 fourcc, gint32 size, gint64 ext_size)
{
header->type = fourcc;
header->size = size;
header->extended_size = ext_size;
}
static void
atom_clear (Atom * atom)
{
}
static void
atom_full_init (AtomFull * full, guint32 fourcc, gint32 size, gint64 ext_size,
guint8 version, guint8 flags[3])
{
atom_header_set (&(full->header), fourcc, size, ext_size);
full->version = version;
full->flags[0] = flags[0];
full->flags[1] = flags[1];
full->flags[2] = flags[2];
}
static void
atom_full_clear (AtomFull * full)
{
atom_clear (&full->header);
}
static void
atom_full_free (AtomFull * full)
{
atom_full_clear (full);
g_free (full);
}
static guint32
atom_full_get_flags_as_uint (AtomFull * full)
{
return full->flags[0] << 16 | full->flags[1] << 8 | full->flags[2];
}
static void
atom_full_set_flags_as_uint (AtomFull * full, guint32 flags_as_uint)
{
full->flags[2] = flags_as_uint & 0xFF;
full->flags[1] = (flags_as_uint & 0xFF00) >> 8;
full->flags[0] = (flags_as_uint & 0xFF0000) >> 16;
}
static AtomInfo *
build_atom_info_wrapper (Atom * atom, gpointer copy_func, gpointer free_func)
{
AtomInfo *info = NULL;
if (atom) {
info = g_new0 (AtomInfo, 1);
info->atom = atom;
info->copy_data_func = copy_func;
info->free_func = free_func;
}
return info;
}
static GList *
atom_info_list_prepend_atom (GList * ai, Atom * atom,
AtomCopyDataFunc copy_func, AtomFreeFunc free_func)
{
if (atom)
return g_list_prepend (ai,
build_atom_info_wrapper (atom, copy_func, free_func));
else
return ai;
}
static void
atom_info_list_free (GList * ai)
{
while (ai) {
AtomInfo *info = (AtomInfo *) ai->data;
info->free_func (info->atom);
g_free (info);
ai = g_list_delete_link (ai, ai);
}
}
static AtomData *
atom_data_new (guint32 fourcc)
{
AtomData *data = g_new0 (AtomData, 1);
atom_header_set (&data->header, fourcc, 0, 0);
return data;
}
static void
atom_data_alloc_mem (AtomData * data, guint32 size)
{
if (data->data) {
g_free (data->data);
}
data->data = g_new0 (guint8, size);
data->datalen = size;
}
static AtomData *
atom_data_new_from_gst_buffer (guint32 fourcc, const GstBuffer * buf)
{
AtomData *data = atom_data_new (fourcc);
gsize size = gst_buffer_get_size ((GstBuffer *) buf);
atom_data_alloc_mem (data, size);
gst_buffer_extract ((GstBuffer *) buf, 0, data->data, size);
return data;
}
static void
atom_data_free (AtomData * data)
{
atom_clear (&data->header);
g_free (data->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
atom_ftyp_init (AtomFTYP * ftyp, guint32 major, guint32 version, GList * brands)
{
gint index;
GList *it = NULL;
atom_header_set (&ftyp->header, FOURCC_ftyp, 16, 0);
ftyp->major_brand = major;
ftyp->version = version;
/* always include major brand as compatible brand */
ftyp->compatible_brands_size = g_list_length (brands) + 1;
ftyp->compatible_brands = g_new (guint32, ftyp->compatible_brands_size);
ftyp->compatible_brands[0] = major;
index = 1;
for (it = brands; it != NULL; it = g_list_next (it)) {
ftyp->compatible_brands[index++] = GPOINTER_TO_UINT (it->data);
}
}
AtomFTYP *
atom_ftyp_new (AtomsContext * context, guint32 major, guint32 version,
GList * brands)
{
AtomFTYP *ftyp = g_new0 (AtomFTYP, 1);
atom_ftyp_init (ftyp, major, version, brands);
return ftyp;
}
void
atom_ftyp_free (AtomFTYP * ftyp)
{
atom_clear (&ftyp->header);
g_free (ftyp->compatible_brands);
ftyp->compatible_brands = NULL;
g_free (ftyp);
}
static void
atom_esds_init (AtomESDS * esds)
{
guint8 flags[3] = { 0, 0, 0 };
atom_full_init (&esds->header, FOURCC_esds, 0, 0, 0, flags);
desc_es_init (&esds->es);
}
static AtomESDS *
atom_esds_new (void)
{
AtomESDS *esds = g_new0 (AtomESDS, 1);
atom_esds_init (esds);
return esds;
}
static void
atom_esds_free (AtomESDS * esds)
{
atom_full_clear (&esds->header);
desc_es_descriptor_clear (&esds->es);
g_free (esds);
}
static AtomFRMA *
atom_frma_new (void)
{
AtomFRMA *frma = g_new0 (AtomFRMA, 1);
atom_header_set (&frma->header, FOURCC_frma, 0, 0);
return frma;
}
static void
atom_frma_free (AtomFRMA * frma)
{
atom_clear (&frma->header);
g_free (frma);
}
static AtomWAVE *
atom_wave_new (void)
{
AtomWAVE *wave = g_new0 (AtomWAVE, 1);
atom_header_set (&wave->header, FOURCC_wave, 0, 0);
return wave;
}
static void
atom_wave_free (AtomWAVE * wave)
{
atom_clear (&wave->header);
atom_info_list_free (wave->extension_atoms);
g_free (wave);
}
static void
atom_elst_init (AtomELST * elst)
{
guint8 flags[3] = { 0, 0, 0 };
atom_full_init (&elst->header, FOURCC_elst, 0, 0, 0, flags);
elst->entries = 0;
}
static void
atom_elst_clear (AtomELST * elst)
{
GSList *walker;
atom_full_clear (&elst->header);
walker = elst->entries;
while (walker) {
g_free ((EditListEntry *) walker->data);
walker = g_slist_next (walker);
}
g_slist_free (elst->entries);
}
static void
atom_edts_init (AtomEDTS * edts)
{
atom_header_set (&edts->header, FOURCC_edts, 0, 0);
atom_elst_init (&edts->elst);
}
static void
atom_edts_clear (AtomEDTS * edts)
{
atom_clear (&edts->header);
atom_elst_clear (&edts->elst);
}
static AtomEDTS *
atom_edts_new (void)
{
AtomEDTS *edts = g_new0 (AtomEDTS, 1);
atom_edts_init (edts);
return edts;
}
static void
atom_edts_free (AtomEDTS * edts)
{
atom_edts_clear (edts);
g_free (edts);
}
static void
atom_sample_entry_init (SampleTableEntry * se, guint32 type)
{
atom_header_set (&se->header, type, 0, 0);
memset (se->reserved, 0, sizeof (guint8) * 6);
se->data_reference_index = 0;
}
static void
atom_sample_entry_free (SampleTableEntry * se)
{
atom_clear (&se->header);
}
static void
sample_entry_mp4a_init (SampleTableEntryMP4A * mp4a)
{
atom_sample_entry_init (&mp4a->se, FOURCC_mp4a);
mp4a->version = 0;
mp4a->revision_level = 0;
mp4a->vendor = 0;
mp4a->channels = 2;
mp4a->sample_size = 16;
mp4a->compression_id = 0;
mp4a->packet_size = 0;
mp4a->sample_rate = 0;
/* following only used if version is 1 */
mp4a->samples_per_packet = 0;
mp4a->bytes_per_packet = 0;
mp4a->bytes_per_frame = 0;
mp4a->bytes_per_sample = 0;
mp4a->extension_atoms = NULL;
}
static SampleTableEntryMP4A *
sample_entry_mp4a_new (void)
{
SampleTableEntryMP4A *mp4a = g_new0 (SampleTableEntryMP4A, 1);
sample_entry_mp4a_init (mp4a);
return mp4a;
}
static void
sample_entry_mp4a_free (SampleTableEntryMP4A * mp4a)
{
atom_sample_entry_free (&mp4a->se);
atom_info_list_free (mp4a->extension_atoms);
g_free (mp4a);
}
static void
sample_entry_mp4v_init (SampleTableEntryMP4V * mp4v, AtomsContext * context)
{
atom_sample_entry_init (&mp4v->se, FOURCC_mp4v);
mp4v->version = 0;
mp4v->revision_level = 0;
mp4v->vendor = 0;
mp4v->temporal_quality = 0;
mp4v->spatial_quality = 0;
/* qt and ISO base media do not contradict, and examples agree */
mp4v->horizontal_resolution = 0x00480000;
mp4v->vertical_resolution = 0x00480000;
mp4v->datasize = 0;
mp4v->frame_count = 1;
memset (mp4v->compressor, 0, sizeof (guint8) * 32);
mp4v->depth = 0;
mp4v->color_table_id = 0;
mp4v->extension_atoms = NULL;
}
static void
sample_entry_mp4v_free (SampleTableEntryMP4V * mp4v)
{
atom_sample_entry_free (&mp4v->se);
atom_info_list_free (mp4v->extension_atoms);
g_free (mp4v);
}
static SampleTableEntryMP4V *
sample_entry_mp4v_new (AtomsContext * context)
{
SampleTableEntryMP4V *mp4v = g_new0 (SampleTableEntryMP4V, 1);
sample_entry_mp4v_init (mp4v, context);
return mp4v;
}
static void
atom_stsd_init (AtomSTSD * stsd)
{
guint8 flags[3] = { 0, 0, 0 };
atom_full_init (&stsd->header, FOURCC_stsd, 0, 0, 0, flags);
stsd->entries = NULL;
stsd->n_entries = 0;
}
static void
atom_stsd_remove_entries (AtomSTSD * stsd)
{
GList *walker;
walker = stsd->entries;
while (walker) {
GList *aux = walker;
SampleTableEntry *se = (SampleTableEntry *) aux->data;
walker = g_list_next (walker);
stsd->entries = g_list_remove_link (stsd->entries, aux);
switch (se->kind) {
case AUDIO:
sample_entry_mp4a_free ((SampleTableEntryMP4A *) se);
break;
case VIDEO:
sample_entry_mp4v_free ((SampleTableEntryMP4V *) se);
break;
default:
/* best possible cleanup */
atom_sample_entry_free (se);
}
g_list_free (aux);
}
stsd->n_entries = 0;
}
static void
atom_stsd_clear (AtomSTSD * stsd)
{
atom_stsd_remove_entries (stsd);
atom_full_clear (&stsd->header);
}
static void
atom_ctts_init (AtomCTTS * ctts)
{
guint8 flags[3] = { 0, 0, 0 };
atom_full_init (&ctts->header, FOURCC_ctts, 0, 0, 0, flags);
atom_array_init (&ctts->entries, 128);
ctts->do_pts = FALSE;
}
static AtomCTTS *
atom_ctts_new (void)
{
AtomCTTS *ctts = g_new0 (AtomCTTS, 1);
atom_ctts_init (ctts);
return ctts;
}
static void
atom_ctts_free (AtomCTTS * ctts)
{
atom_full_clear (&ctts->header);
atom_array_clear (&ctts->entries);
g_free (ctts);
}
static void
atom_stts_init (AtomSTTS * stts)
{
guint8 flags[3] = { 0, 0, 0 };
atom_full_init (&stts->header, FOURCC_stts, 0, 0, 0, flags);
atom_array_init (&stts->entries, 512);
}
static void
atom_stts_clear (AtomSTTS * stts)
{
atom_full_clear (&stts->header);
atom_array_clear (&stts->entries);
}
static void
atom_stsz_init (AtomSTSZ * stsz)
{
guint8 flags[3] = { 0, 0, 0 };
atom_full_init (&stsz->header, FOURCC_stsz, 0, 0, 0, flags);
atom_array_init (&stsz->entries, 1024);
stsz->sample_size = 0;
stsz->table_size = 0;
}
static void
atom_stsz_clear (AtomSTSZ * stsz)
{
atom_full_clear (&stsz->header);
atom_array_clear (&stsz->entries);
stsz->table_size = 0;
}
static void
atom_stsc_init (AtomSTSC * stsc)
{
guint8 flags[3] = { 0, 0, 0 };
atom_full_init (&stsc->header, FOURCC_stsc, 0, 0, 0, flags);
atom_array_init (&stsc->entries, 128);
}
static void
atom_stsc_clear (AtomSTSC * stsc)
{
atom_full_clear (&stsc->header);
atom_array_clear (&stsc->entries);
}
static void
atom_co64_init (AtomSTCO64 * co64)
{
guint8 flags[3] = { 0, 0, 0 };
atom_full_init (&co64->header, FOURCC_stco, 0, 0, 0, flags);
atom_array_init (&co64->entries, 256);
}
static void
atom_stco64_clear (AtomSTCO64 * stco64)
{
atom_full_clear (&stco64->header);
atom_array_clear (&stco64->entries);
}
static void
atom_stss_init (AtomSTSS * stss)
{
guint8 flags[3] = { 0, 0, 0 };
atom_full_init (&stss->header, FOURCC_stss, 0, 0, 0, flags);
atom_array_init (&stss->entries, 128);
}
static void
atom_stss_clear (AtomSTSS * stss)
{
atom_full_clear (&stss->header);
atom_array_clear (&stss->entries);
}
void
atom_stbl_init (AtomSTBL * stbl)
{
atom_header_set (&stbl->header, FOURCC_stbl, 0, 0);
atom_stts_init (&stbl->stts);
atom_stss_init (&stbl->stss);
atom_stsd_init (&stbl->stsd);
atom_stsz_init (&stbl->stsz);
atom_stsc_init (&stbl->stsc);
stbl->ctts = NULL;
atom_co64_init (&stbl->stco64);
}
void
atom_stbl_clear (AtomSTBL * stbl)
{
atom_clear (&stbl->header);
atom_stsd_clear (&stbl->stsd);
atom_stts_clear (&stbl->stts);
atom_stss_clear (&stbl->stss);
atom_stsc_clear (&stbl->stsc);
atom_stsz_clear (&stbl->stsz);
if (stbl->ctts) {
atom_ctts_free (stbl->ctts);
}
atom_stco64_clear (&stbl->stco64);
}
static void
atom_vmhd_init (AtomVMHD * vmhd, AtomsContext * context)
{
guint8 flags[3] = { 0, 0, 1 };
atom_full_init (&vmhd->header, FOURCC_vmhd, 0, 0, 0, flags);
vmhd->graphics_mode = 0x0;
memset (vmhd->opcolor, 0, sizeof (guint16) * 3);
if (context->flavor == ATOMS_TREE_FLAVOR_MOV) {
vmhd->graphics_mode = 0x40;
vmhd->opcolor[0] = 32768;
vmhd->opcolor[1] = 32768;
vmhd->opcolor[2] = 32768;
}
}
static AtomVMHD *
atom_vmhd_new (AtomsContext * context)
{
AtomVMHD *vmhd = g_new0 (AtomVMHD, 1);
atom_vmhd_init (vmhd, context);
return vmhd;
}
static void
atom_vmhd_free (AtomVMHD * vmhd)
{
atom_full_clear (&vmhd->header);
g_free (vmhd);
}
static void
atom_smhd_init (AtomSMHD * smhd)
{
guint8 flags[3] = { 0, 0, 0 };
atom_full_init (&smhd->header, FOURCC_smhd, 0, 0, 0, flags);
smhd->balance = 0;
smhd->reserved = 0;
}
static AtomSMHD *
atom_smhd_new (void)
{
AtomSMHD *smhd = g_new0 (AtomSMHD, 1);
atom_smhd_init (smhd);
return smhd;
}
static void
atom_smhd_free (AtomSMHD * smhd)
{
atom_full_clear (&smhd->header);
g_free (smhd);
}
static void
atom_hmhd_free (AtomHMHD * hmhd)
{
atom_full_clear (&hmhd->header);
g_free (hmhd);
}
static void
atom_hdlr_init (AtomHDLR * hdlr)
{
guint8 flags[3] = { 0, 0, 0 };
atom_full_init (&hdlr->header, FOURCC_hdlr, 0, 0, 0, flags);
hdlr->component_type = 0;
hdlr->handler_type = 0;
hdlr->manufacturer = 0;
hdlr->flags = 0;
hdlr->flags_mask = 0;
hdlr->name = g_strdup ("");
}
static AtomHDLR *
atom_hdlr_new (void)
{
AtomHDLR *hdlr = g_new0 (AtomHDLR, 1);
atom_hdlr_init (hdlr);
return hdlr;
}
static void
atom_hdlr_clear (AtomHDLR * hdlr)
{
atom_full_clear (&hdlr->header);
if (hdlr->name) {
g_free (hdlr->name);
hdlr->name = NULL;
}
}
static void
atom_hdlr_free (AtomHDLR * hdlr)
{
atom_hdlr_clear (hdlr);
g_free (hdlr);
}
static void
atom_url_init (AtomURL * url)
{
guint8 flags[3] = { 0, 0, 1 };
atom_full_init (&url->header, FOURCC_url_, 0, 0, 0, flags);
url->location = NULL;
}
static void
atom_url_free (AtomURL * url)
{
atom_full_clear (&url->header);
if (url->location) {
g_free (url->location);
url->location = NULL;
}
g_free (url);
}
static AtomURL *
atom_url_new (void)
{
AtomURL *url = g_new0 (AtomURL, 1);
atom_url_init (url);
return url;
}
static AtomFull *
atom_alis_new (void)
{
guint8 flags[3] = { 0, 0, 1 };
AtomFull *alis = g_new0 (AtomFull, 1);
atom_full_init (alis, FOURCC_alis, 0, 0, 0, flags);
return alis;
}
static void
atom_dref_init (AtomDREF * dref, AtomsContext * context)
{
guint8 flags[3] = { 0, 0, 0 };
atom_full_init (&dref->header, FOURCC_dref, 0, 0, 0, flags);
/* in either case, alis or url init arranges to set self-contained flag */
if (context->flavor == ATOMS_TREE_FLAVOR_MOV) {
/* alis dref for qt */
AtomFull *alis = atom_alis_new ();
dref->entries = g_list_append (dref->entries, alis);
} else {
/* url for iso spec, as 'alis' not specified there */
AtomURL *url = atom_url_new ();
dref->entries = g_list_append (dref->entries, url);
}
}
static void
atom_dref_clear (AtomDREF * dref)
{
GList *walker;
atom_full_clear (&dref->header);
walker = dref->entries;
while (walker) {
GList *aux = walker;
Atom *atom = (Atom *) aux->data;
walker = g_list_next (walker);
dref->entries = g_list_remove_link (dref->entries, aux);
switch (atom->type) {
case FOURCC_alis:
atom_full_free ((AtomFull *) atom);
break;
case FOURCC_url_:
atom_url_free ((AtomURL *) atom);
break;
default:
/* we do nothing, better leak than crash */
break;
}
g_list_free (aux);
}
}
static void
atom_dinf_init (AtomDINF * dinf, AtomsContext * context)
{
atom_header_set (&dinf->header, FOURCC_dinf, 0, 0);
atom_dref_init (&dinf->dref, context);
}
static void
atom_dinf_clear (AtomDINF * dinf)
{
atom_clear (&dinf->header);
atom_dref_clear (&dinf->dref);
}
static void
atom_minf_init (AtomMINF * minf, AtomsContext * context)
{
atom_header_set (&minf->header, FOURCC_minf, 0, 0);
minf->vmhd = NULL;
minf->smhd = NULL;
minf->hmhd = NULL;
if (context->flavor == ATOMS_TREE_FLAVOR_MOV) {
minf->hdlr = atom_hdlr_new ();
minf->hdlr->component_type = FOURCC_dhlr;
minf->hdlr->handler_type = FOURCC_alis;
} else {
minf->hdlr = NULL;
}
atom_dinf_init (&minf->dinf, context);
atom_stbl_init (&minf->stbl);
}
static void
atom_minf_clear_handlers (AtomMINF * minf)
{
if (minf->vmhd) {
atom_vmhd_free (minf->vmhd);
minf->vmhd = NULL;
}
if (minf->smhd) {
atom_smhd_free (minf->smhd);
minf->smhd = NULL;
}
if (minf->hmhd) {
atom_hmhd_free (minf->hmhd);
minf->hmhd = NULL;
}
}
static void
atom_minf_clear (AtomMINF * minf)
{
atom_clear (&minf->header);
atom_minf_clear_handlers (minf);
if (minf->hdlr) {
atom_hdlr_free (minf->hdlr);
}
atom_dinf_clear (&minf->dinf);
atom_stbl_clear (&minf->stbl);
}
static void
atom_mdhd_init (AtomMDHD * mdhd)
{
guint8 flags[3] = { 0, 0, 0 };
atom_full_init (&mdhd->header, FOURCC_mdhd, 0, 0, 0, flags);
common_time_info_init (&mdhd->time_info);
mdhd->language_code = 0;
mdhd->quality = 0;
}
static void
atom_mdhd_clear (AtomMDHD * mdhd)
{
atom_full_clear (&mdhd->header);
}
static void
atom_mdia_init (AtomMDIA * mdia, AtomsContext * context)
{
atom_header_set (&mdia->header, FOURCC_mdia, 0, 0);
atom_mdhd_init (&mdia->mdhd);
atom_hdlr_init (&mdia->hdlr);
atom_minf_init (&mdia->minf, context);
}
static void
atom_mdia_clear (AtomMDIA * mdia)
{
atom_clear (&mdia->header);
atom_mdhd_clear (&mdia->mdhd);
atom_hdlr_clear (&mdia->hdlr);
atom_minf_clear (&mdia->minf);
}
static void
atom_tkhd_init (AtomTKHD * tkhd, AtomsContext * context)
{
/*
* flags info
* 1 -> track enabled
* 2 -> track in movie
* 4 -> track in preview
*/
guint8 flags[3] = { 0, 0, 7 };
atom_full_init (&tkhd->header, FOURCC_tkhd, 0, 0, 0, flags);
tkhd->creation_time = tkhd->modification_time = get_current_qt_time ();
tkhd->duration = 0;
tkhd->track_ID = 0;
tkhd->reserved = 0;
tkhd->reserved2[0] = tkhd->reserved2[1] = 0;
tkhd->layer = 0;
tkhd->alternate_group = 0;
tkhd->volume = 0;
tkhd->reserved3 = 0;
memset (tkhd->matrix, 0, sizeof (guint32) * 9);
tkhd->matrix[0] = 1 << 16;
tkhd->matrix[4] = 1 << 16;
tkhd->matrix[8] = 16384 << 16;
tkhd->width = 0;
tkhd->height = 0;
}
static void
atom_tkhd_clear (AtomTKHD * tkhd)
{
atom_full_clear (&tkhd->header);
}
static void
atom_trak_init (AtomTRAK * trak, AtomsContext * context)
{
atom_header_set (&trak->header, FOURCC_trak, 0, 0);
atom_tkhd_init (&trak->tkhd, context);
trak->edts = NULL;
atom_mdia_init (&trak->mdia, context);
}
AtomTRAK *
atom_trak_new (AtomsContext * context)
{
AtomTRAK *trak = g_new0 (AtomTRAK, 1);
atom_trak_init (trak, context);
return trak;
}
static void
atom_trak_clear (AtomTRAK * trak)
{
atom_clear (&trak->header);
atom_tkhd_clear (&trak->tkhd);
if (trak->edts)
atom_edts_free (trak->edts);
atom_mdia_clear (&trak->mdia);
}
static void
atom_trak_free (AtomTRAK * trak)
{
atom_trak_clear (trak);
g_free (trak);
}
static void
atom_ilst_init (AtomILST * ilst)
{
atom_header_set (&ilst->header, FOURCC_ilst, 0, 0);
ilst->entries = NULL;
}
static AtomILST *
atom_ilst_new (void)
{
AtomILST *ilst = g_new0 (AtomILST, 1);
atom_ilst_init (ilst);
return ilst;
}
static void
atom_ilst_free (AtomILST * ilst)
{
if (ilst->entries)
atom_info_list_free (ilst->entries);
atom_clear (&ilst->header);
g_free (ilst);
}
static void
atom_meta_init (AtomMETA * meta)
{
guint8 flags[3] = { 0, 0, 0 };
atom_full_init (&meta->header, FOURCC_meta, 0, 0, 0, flags);
atom_hdlr_init (&meta->hdlr);
/* FIXME (ISOM says this is always 0) */
meta->hdlr.component_type = FOURCC_mhlr;
meta->hdlr.handler_type = FOURCC_mdir;
meta->ilst = NULL;
}
static AtomMETA *
atom_meta_new (void)
{
AtomMETA *meta = g_new0 (AtomMETA, 1);
atom_meta_init (meta);
return meta;
}
static void
atom_meta_free (AtomMETA * meta)
{
atom_full_clear (&meta->header);
atom_hdlr_clear (&meta->hdlr);
if (meta->ilst)
atom_ilst_free (meta->ilst);
meta->ilst = NULL;
g_free (meta);
}
static void
atom_udta_init (AtomUDTA * udta)
{
atom_header_set (&udta->header, FOURCC_udta, 0, 0);
udta->meta = NULL;
}
static AtomUDTA *
atom_udta_new (void)
{
AtomUDTA *udta = g_new0 (AtomUDTA, 1);
atom_udta_init (udta);
return udta;
}
static void
atom_udta_free (AtomUDTA * udta)
{
atom_clear (&udta->header);
if (udta->meta)
atom_meta_free (udta->meta);
udta->meta = NULL;
if (udta->entries)
atom_info_list_free (udta->entries);
g_free (udta);
}
static void
atom_tag_data_init (AtomTagData * data)
{
guint8 flags[] = { 0, 0, 0 };
atom_full_init (&data->header, FOURCC_data, 0, 0, 0, flags);
}
static void
atom_tag_data_clear (AtomTagData * data)
{
atom_full_clear (&data->header);
g_free (data->data);
data->datalen = 0;
}
/*
* Fourcc is the tag fourcc
* flags will be truncated to 24bits
*/
static AtomTag *
atom_tag_new (guint32 fourcc, guint32 flags_as_uint)
{
AtomTag *tag = g_new0 (AtomTag, 1);
tag->header.type = fourcc;
atom_tag_data_init (&tag->data);
atom_full_set_flags_as_uint (&tag->data.header, flags_as_uint);
return tag;
}
static void
atom_tag_free (AtomTag * tag)
{
atom_clear (&tag->header);
atom_tag_data_clear (&tag->data);
g_free (tag);
}
static void
atom_mvhd_init (AtomMVHD * mvhd)
{
guint8 flags[3] = { 0, 0, 0 };
atom_full_init (&(mvhd->header), FOURCC_mvhd, sizeof (AtomMVHD), 0, 0, flags);
common_time_info_init (&mvhd->time_info);
mvhd->prefered_rate = 1 << 16;
mvhd->volume = 1 << 8;
mvhd->reserved3 = 0;
memset (mvhd->reserved4, 0, sizeof (guint32[2]));
memset (mvhd->matrix, 0, sizeof (guint32[9]));
mvhd->matrix[0] = 1 << 16;
mvhd->matrix[4] = 1 << 16;
mvhd->matrix[8] = 16384 << 16;
mvhd->preview_time = 0;
mvhd->preview_duration = 0;
mvhd->poster_time = 0;
mvhd->selection_time = 0;
mvhd->selection_duration = 0;
mvhd->current_time = 0;
mvhd->next_track_id = 1;
}
static void
atom_mvhd_clear (AtomMVHD * mvhd)
{
atom_full_clear (&mvhd->header);
}
static void
atom_mehd_init (AtomMEHD * mehd)
{
guint8 flags[3] = { 0, 0, 0 };
atom_full_init (&mehd->header, FOURCC_mehd, 0, 0, 1, flags);
mehd->fragment_duration = 0;
}
static void
atom_mvex_init (AtomMVEX * mvex)
{
atom_header_set (&mvex->header, FOURCC_mvex, 0, 0);
atom_mehd_init (&mvex->mehd);
mvex->trexs = NULL;
}
static void
atom_moov_init (AtomMOOV * moov, AtomsContext * context)
{
atom_header_set (&(moov->header), FOURCC_moov, 0, 0);
atom_mvhd_init (&(moov->mvhd));
atom_mvex_init (&(moov->mvex));
moov->udta = NULL;
moov->traks = NULL;
moov->context = *context;
}
AtomMOOV *
atom_moov_new (AtomsContext * context)
{
AtomMOOV *moov = g_new0 (AtomMOOV, 1);
atom_moov_init (moov, context);
return moov;
}
static void
atom_trex_free (AtomTREX * trex)
{
atom_full_clear (&trex->header);
g_free (trex);
}
static void
atom_mvex_clear (AtomMVEX * mvex)
{
GList *walker;
atom_clear (&mvex->header);
walker = mvex->trexs;
while (walker) {
atom_trex_free ((AtomTREX *) walker->data);
walker = g_list_next (walker);
}
g_list_free (mvex->trexs);
mvex->trexs = NULL;
}
void
atom_moov_free (AtomMOOV * moov)
{
GList *walker;
atom_clear (&moov->header);
atom_mvhd_clear (&moov->mvhd);
walker = moov->traks;
while (walker) {
atom_trak_free ((AtomTRAK *) walker->data);
walker = g_list_next (walker);
}
g_list_free (moov->traks);
moov->traks = NULL;
if (moov->udta) {
atom_udta_free (moov->udta);
moov->udta = NULL;
}
atom_mvex_clear (&moov->mvex);
g_free (moov);
}
/* -- end of init / free -- */
/* -- copy data functions -- */
static guint8
atom_full_get_version (AtomFull * full)
{
return full->version;
}
static guint64
common_time_info_copy_data (TimeInfo * ti, gboolean trunc_to_32,
guint8 ** buffer, guint64 * size, guint64 * offset)
{
guint64 original_offset = *offset;
if (trunc_to_32) {
prop_copy_uint32 ((guint32) ti->creation_time, buffer, size, offset);
prop_copy_uint32 ((guint32) ti->modification_time, buffer, size, offset);
prop_copy_uint32 (ti->timescale, buffer, size, offset);
prop_copy_uint32 ((guint32) ti->duration, buffer, size, offset);
} else {
prop_copy_uint64 (ti->creation_time, buffer, size, offset);
prop_copy_uint64 (ti->modification_time, buffer, size, offset);
prop_copy_uint32 (ti->timescale, buffer, size, offset);
prop_copy_uint64 (ti->duration, buffer, size, offset);
}
return *offset - original_offset;
}
static void
atom_write_size (guint8 ** buffer, guint64 * size, guint64 * offset,
guint64 atom_pos)
{
/* this only works for non-extended atom size, which is OK
* (though it could be made to do mem_move, etc and write extended size) */
prop_copy_uint32 (*offset - atom_pos, buffer, size, &atom_pos);
}
guint64
atom_copy_data (Atom * atom, guint8 ** buffer, guint64 * size, guint64 * offset)
{
guint64 original_offset = *offset;
/* copies type and size */
prop_copy_uint32 (atom->size, buffer, size, offset);
prop_copy_fourcc (atom->type, buffer, size, offset);
/* extended size needed */
if (atom->size == 1) {
/* really should not happen other than with mdat atom;
* would be a problem for size (re)write code, not to mention memory */
g_return_val_if_fail (atom->type == FOURCC_mdat, 0);
prop_copy_uint64 (atom->extended_size, buffer, size, offset);
}
return *offset - original_offset;
}
static guint64
atom_full_copy_data (AtomFull * atom, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_copy_data (&atom->header, buffer, size, offset)) {
return 0;
}
prop_copy_uint8 (atom->version, buffer, size, offset);
prop_copy_uint8_array (atom->flags, 3, buffer, size, offset);
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_info_list_copy_data (GList * ai, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
while (ai) {
AtomInfo *info = (AtomInfo *) ai->data;
if (!info->copy_data_func (info->atom, buffer, size, offset)) {
return 0;
}
ai = g_list_next (ai);
}
return *offset - original_offset;
}
static guint64
atom_data_copy_data (AtomData * data, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_copy_data (&data->header, buffer, size, offset)) {
return 0;
}
if (data->datalen)
prop_copy_uint8_array (data->data, data->datalen, buffer, size, offset);
atom_write_size (buffer, size, 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
atom_ftyp_copy_data (AtomFTYP * ftyp, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_copy_data (&ftyp->header, buffer, size, offset)) {
return 0;
}
prop_copy_fourcc (ftyp->major_brand, buffer, size, offset);
prop_copy_uint32 (ftyp->version, buffer, size, offset);
prop_copy_fourcc_array (ftyp->compatible_brands, ftyp->compatible_brands_size,
buffer, size, offset);
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
guint64
atom_mvhd_copy_data (AtomMVHD * atom, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint8 version;
guint64 original_offset = *offset;
if (!atom_full_copy_data (&(atom->header), buffer, size, offset)) {
return 0;
}
version = atom_full_get_version (&(atom->header));
if (version == 0) {
common_time_info_copy_data (&atom->time_info, TRUE, buffer, size, offset);
} else if (version == 1) {
common_time_info_copy_data (&atom->time_info, FALSE, buffer, size, offset);
} else {
*offset = original_offset;
return 0;
}
prop_copy_uint32 (atom->prefered_rate, buffer, size, offset);
prop_copy_uint16 (atom->volume, buffer, size, offset);
prop_copy_uint16 (atom->reserved3, buffer, size, offset);
prop_copy_uint32_array (atom->reserved4, 2, buffer, size, offset);
prop_copy_uint32_array (atom->matrix, 9, buffer, size, offset);
prop_copy_uint32 (atom->preview_time, buffer, size, offset);
prop_copy_uint32 (atom->preview_duration, buffer, size, offset);
prop_copy_uint32 (atom->poster_time, buffer, size, offset);
prop_copy_uint32 (atom->selection_time, buffer, size, offset);
prop_copy_uint32 (atom->selection_duration, buffer, size, offset);
prop_copy_uint32 (atom->current_time, buffer, size, offset);
prop_copy_uint32 (atom->next_track_id, buffer, size, offset);
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_tkhd_copy_data (AtomTKHD * tkhd, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_full_copy_data (&tkhd->header, buffer, size, offset)) {
return 0;
}
if (atom_full_get_version (&tkhd->header) == 0) {
prop_copy_uint32 ((guint32) tkhd->creation_time, buffer, size, offset);
prop_copy_uint32 ((guint32) tkhd->modification_time, buffer, size, offset);
prop_copy_uint32 (tkhd->track_ID, buffer, size, offset);
prop_copy_uint32 (tkhd->reserved, buffer, size, offset);
prop_copy_uint32 ((guint32) tkhd->duration, buffer, size, offset);
} else {
prop_copy_uint64 (tkhd->creation_time, buffer, size, offset);
prop_copy_uint64 (tkhd->modification_time, buffer, size, offset);
prop_copy_uint32 (tkhd->track_ID, buffer, size, offset);
prop_copy_uint32 (tkhd->reserved, buffer, size, offset);
prop_copy_uint64 (tkhd->duration, buffer, size, offset);
}
prop_copy_uint32_array (tkhd->reserved2, 2, buffer, size, offset);
prop_copy_uint16 (tkhd->layer, buffer, size, offset);
prop_copy_uint16 (tkhd->alternate_group, buffer, size, offset);
prop_copy_uint16 (tkhd->volume, buffer, size, offset);
prop_copy_uint16 (tkhd->reserved3, buffer, size, offset);
prop_copy_uint32_array (tkhd->matrix, 9, buffer, size, offset);
prop_copy_uint32 (tkhd->width, buffer, size, offset);
prop_copy_uint32 (tkhd->height, buffer, size, offset);
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_hdlr_copy_data (AtomHDLR * hdlr, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_full_copy_data (&hdlr->header, buffer, size, offset)) {
return 0;
}
prop_copy_fourcc (hdlr->component_type, buffer, size, offset);
prop_copy_fourcc (hdlr->handler_type, buffer, size, offset);
prop_copy_fourcc (hdlr->manufacturer, buffer, size, offset);
prop_copy_uint32 (hdlr->flags, buffer, size, offset);
prop_copy_uint32 (hdlr->flags_mask, buffer, size, offset);
prop_copy_size_string ((guint8 *) hdlr->name, strlen (hdlr->name), buffer,
size, offset);
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_vmhd_copy_data (AtomVMHD * vmhd, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_full_copy_data (&vmhd->header, buffer, size, offset)) {
return 0;
}
prop_copy_uint16 (vmhd->graphics_mode, buffer, size, offset);
prop_copy_uint16_array (vmhd->opcolor, 3, buffer, size, offset);
atom_write_size (buffer, size, offset, original_offset);
return original_offset - *offset;
}
static guint64
atom_smhd_copy_data (AtomSMHD * smhd, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_full_copy_data (&smhd->header, buffer, size, offset)) {
return 0;
}
prop_copy_uint16 (smhd->balance, buffer, size, offset);
prop_copy_uint16 (smhd->reserved, buffer, size, offset);
atom_write_size (buffer, size, offset, original_offset);
return original_offset - *offset;
}
static guint64
atom_hmhd_copy_data (AtomHMHD * hmhd, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_full_copy_data (&hmhd->header, buffer, size, offset)) {
return 0;
}
prop_copy_uint16 (hmhd->max_pdu_size, buffer, size, offset);
prop_copy_uint16 (hmhd->avg_pdu_size, buffer, size, offset);
prop_copy_uint32 (hmhd->max_bitrate, buffer, size, offset);
prop_copy_uint32 (hmhd->avg_bitrate, buffer, size, offset);
prop_copy_uint32 (hmhd->sliding_avg_bitrate, buffer, size, offset);
atom_write_size (buffer, size, offset, original_offset);
return original_offset - *offset;
}
static gboolean
atom_url_same_file_flag (AtomURL * url)
{
return (url->header.flags[2] & 0x1) == 1;
}
static guint64
atom_url_copy_data (AtomURL * url, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_full_copy_data (&url->header, buffer, size, offset)) {
return 0;
}
if (!atom_url_same_file_flag (url)) {
prop_copy_null_terminated_string (url->location, buffer, size, offset);
}
atom_write_size (buffer, size, offset, original_offset);
return original_offset - *offset;
}
guint64
atom_stts_copy_data (AtomSTTS * stts, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
guint i;
if (!atom_full_copy_data (&stts->header, buffer, size, offset)) {
return 0;
}
prop_copy_uint32 (atom_array_get_len (&stts->entries), buffer, size, offset);
/* minimize realloc */
prop_copy_ensure_buffer (buffer, size, offset,
8 * atom_array_get_len (&stts->entries));
for (i = 0; i < atom_array_get_len (&stts->entries); i++) {
STTSEntry *entry = &atom_array_index (&stts->entries, i);
prop_copy_uint32 (entry->sample_count, buffer, size, offset);
prop_copy_int32 (entry->sample_delta, buffer, size, offset);
}
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_sample_entry_copy_data (SampleTableEntry * se, guint8 ** buffer,
guint64 * size, guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_copy_data (&se->header, buffer, size, offset)) {
return 0;
}
prop_copy_uint8_array (se->reserved, 6, buffer, size, offset);
prop_copy_uint16 (se->data_reference_index, buffer, size, offset);
return *offset - original_offset;
}
static guint64
atom_esds_copy_data (AtomESDS * esds, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_full_copy_data (&esds->header, buffer, size, offset)) {
return 0;
}
if (!desc_es_descriptor_copy_data (&esds->es, buffer, size, offset)) {
return 0;
}
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_frma_copy_data (AtomFRMA * frma, guint8 ** buffer,
guint64 * size, guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_copy_data (&(frma->header), buffer, size, offset))
return 0;
prop_copy_fourcc (frma->media_type, buffer, size, offset);
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_mp4s_copy_data (SampleTableEntryMP4S * mp4s, guint8 ** buffer,
guint64 * size, guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_sample_entry_copy_data (&mp4s->se, buffer, size, offset)) {
return 0;
}
if (!atom_esds_copy_data (&mp4s->es, buffer, size, offset)) {
return 0;
}
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_hint_sample_entry_copy_data (AtomHintSampleEntry * hse, guint8 ** buffer,
guint64 * size, guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_sample_entry_copy_data (&hse->se, buffer, size, offset)) {
return 0;
}
prop_copy_uint32 (hse->size, buffer, size, offset);
prop_copy_uint8_array (hse->data, hse->size, buffer, size, offset);
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
sample_entry_mp4a_copy_data (SampleTableEntryMP4A * mp4a, guint8 ** buffer,
guint64 * size, guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_sample_entry_copy_data (&mp4a->se, buffer, size, offset)) {
return 0;
}
prop_copy_uint16 (mp4a->version, buffer, size, offset);
prop_copy_uint16 (mp4a->revision_level, buffer, size, offset);
prop_copy_uint32 (mp4a->vendor, buffer, size, offset);
prop_copy_uint16 (mp4a->channels, buffer, size, offset);
prop_copy_uint16 (mp4a->sample_size, buffer, size, offset);
prop_copy_uint16 (mp4a->compression_id, buffer, size, offset);
prop_copy_uint16 (mp4a->packet_size, buffer, size, offset);
prop_copy_uint32 (mp4a->sample_rate, buffer, size, offset);
/* this should always be 0 for mp4 flavor */
if (mp4a->version == 1) {
prop_copy_uint32 (mp4a->samples_per_packet, buffer, size, offset);
prop_copy_uint32 (mp4a->bytes_per_packet, buffer, size, offset);
prop_copy_uint32 (mp4a->bytes_per_frame, buffer, size, offset);
prop_copy_uint32 (mp4a->bytes_per_sample, buffer, size, offset);
}
if (mp4a->extension_atoms) {
if (!atom_info_list_copy_data (mp4a->extension_atoms, buffer, size, offset))
return 0;
}
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
sample_entry_mp4v_copy_data (SampleTableEntryMP4V * mp4v, guint8 ** buffer,
guint64 * size, guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_sample_entry_copy_data (&mp4v->se, buffer, size, offset)) {
return 0;
}
prop_copy_uint16 (mp4v->version, buffer, size, offset);
prop_copy_uint16 (mp4v->revision_level, buffer, size, offset);
prop_copy_fourcc (mp4v->vendor, buffer, size, offset);
prop_copy_uint32 (mp4v->temporal_quality, buffer, size, offset);
prop_copy_uint32 (mp4v->spatial_quality, buffer, size, offset);
prop_copy_uint16 (mp4v->width, buffer, size, offset);
prop_copy_uint16 (mp4v->height, buffer, size, offset);
prop_copy_uint32 (mp4v->horizontal_resolution, buffer, size, offset);
prop_copy_uint32 (mp4v->vertical_resolution, buffer, size, offset);
prop_copy_uint32 (mp4v->datasize, buffer, size, offset);
prop_copy_uint16 (mp4v->frame_count, buffer, size, offset);
prop_copy_fixed_size_string ((guint8 *) mp4v->compressor, 32, buffer, size,
offset);
prop_copy_uint16 (mp4v->depth, buffer, size, offset);
prop_copy_uint16 (mp4v->color_table_id, buffer, size, offset);
/* extra atoms */
if (mp4v->extension_atoms &&
!atom_info_list_copy_data (mp4v->extension_atoms, buffer, size, offset))
return 0;
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
guint64
atom_stsz_copy_data (AtomSTSZ * stsz, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
guint i;
if (!atom_full_copy_data (&stsz->header, buffer, size, offset)) {
return 0;
}
prop_copy_uint32 (stsz->sample_size, buffer, size, offset);
prop_copy_uint32 (stsz->table_size, buffer, size, offset);
if (stsz->sample_size == 0) {
/* minimize realloc */
prop_copy_ensure_buffer (buffer, size, offset, 4 * stsz->table_size);
/* entry count must match sample count */
g_assert (atom_array_get_len (&stsz->entries) == stsz->table_size);
for (i = 0; i < atom_array_get_len (&stsz->entries); i++) {
prop_copy_uint32 (atom_array_index (&stsz->entries, i), buffer, size,
offset);
}
}
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
guint64
atom_stsc_copy_data (AtomSTSC * stsc, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
guint i;
if (!atom_full_copy_data (&stsc->header, buffer, size, offset)) {
return 0;
}
prop_copy_uint32 (atom_array_get_len (&stsc->entries), buffer, size, offset);
/* minimize realloc */
prop_copy_ensure_buffer (buffer, size, offset,
12 * atom_array_get_len (&stsc->entries));
for (i = 0; i < atom_array_get_len (&stsc->entries); i++) {
STSCEntry *entry = &atom_array_index (&stsc->entries, i);
prop_copy_uint32 (entry->first_chunk, buffer, size, offset);
prop_copy_uint32 (entry->samples_per_chunk, buffer, size, offset);
prop_copy_uint32 (entry->sample_description_index, buffer, size, offset);
}
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
guint64
atom_ctts_copy_data (AtomCTTS * ctts, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
guint i;
if (!atom_full_copy_data (&ctts->header, buffer, size, offset)) {
return 0;
}
prop_copy_uint32 (atom_array_get_len (&ctts->entries), buffer, size, offset);
/* minimize realloc */
prop_copy_ensure_buffer (buffer, size, offset,
8 * atom_array_get_len (&ctts->entries));
for (i = 0; i < atom_array_get_len (&ctts->entries); i++) {
CTTSEntry *entry = &atom_array_index (&ctts->entries, i);
prop_copy_uint32 (entry->samplecount, buffer, size, offset);
prop_copy_uint32 (entry->sampleoffset, buffer, size, offset);
}
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
guint64
atom_stco64_copy_data (AtomSTCO64 * stco64, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
guint i;
gboolean trunc_to_32 = stco64->header.header.type == FOURCC_stco;
if (!atom_full_copy_data (&stco64->header, buffer, size, offset)) {
return 0;
}
prop_copy_uint32 (atom_array_get_len (&stco64->entries), buffer, size,
offset);
/* minimize realloc */
prop_copy_ensure_buffer (buffer, size, offset,
8 * atom_array_get_len (&stco64->entries));
for (i = 0; i < atom_array_get_len (&stco64->entries); i++) {
guint64 *value = &atom_array_index (&stco64->entries, i);
if (trunc_to_32) {
prop_copy_uint32 ((guint32) * value, buffer, size, offset);
} else {
prop_copy_uint64 (*value, buffer, size, offset);
}
}
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
guint64
atom_stss_copy_data (AtomSTSS * stss, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
guint i;
if (atom_array_get_len (&stss->entries) == 0) {
/* FIXME not needing this atom might be confused with error while copying */
return 0;
}
if (!atom_full_copy_data (&stss->header, buffer, size, offset)) {
return 0;
}
prop_copy_uint32 (atom_array_get_len (&stss->entries), buffer, size, offset);
/* minimize realloc */
prop_copy_ensure_buffer (buffer, size, offset,
4 * atom_array_get_len (&stss->entries));
for (i = 0; i < atom_array_get_len (&stss->entries); i++) {
prop_copy_uint32 (atom_array_index (&stss->entries, i), buffer, size,
offset);
}
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_stsd_copy_data (AtomSTSD * stsd, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
GList *walker;
if (!atom_full_copy_data (&stsd->header, buffer, size, offset)) {
return 0;
}
prop_copy_uint32 (stsd->n_entries, buffer, size, offset);
for (walker = g_list_last (stsd->entries); walker != NULL;
walker = g_list_previous (walker)) {
SampleTableEntry *se = (SampleTableEntry *) walker->data;
switch (((Atom *) walker->data)->type) {
case FOURCC_mp4a:
if (!sample_entry_mp4a_copy_data ((SampleTableEntryMP4A *) walker->data,
buffer, size, offset)) {
return 0;
}
break;
case FOURCC_mp4s:
if (!atom_mp4s_copy_data ((SampleTableEntryMP4S *) walker->data,
buffer, size, offset)) {
return 0;
}
break;
case FOURCC_mp4v:
if (!sample_entry_mp4v_copy_data ((SampleTableEntryMP4V *) walker->data,
buffer, size, offset)) {
return 0;
}
break;
default:
if (se->kind == VIDEO) {
if (!sample_entry_mp4v_copy_data ((SampleTableEntryMP4V *)
walker->data, buffer, size, offset)) {
return 0;
}
} else if (se->kind == AUDIO) {
if (!sample_entry_mp4a_copy_data ((SampleTableEntryMP4A *)
walker->data, buffer, size, offset)) {
return 0;
}
} else {
if (!atom_hint_sample_entry_copy_data (
(AtomHintSampleEntry *) walker->data, buffer, size, offset)) {
return 0;
}
}
break;
}
}
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_stbl_copy_data (AtomSTBL * stbl, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_copy_data (&stbl->header, buffer, size, offset)) {
return 0;
}
if (!atom_stsd_copy_data (&stbl->stsd, buffer, size, offset)) {
return 0;
}
if (!atom_stts_copy_data (&stbl->stts, buffer, size, offset)) {
return 0;
}
/* this atom is optional, so let's check if we need it
* (to avoid false error) */
if (atom_array_get_len (&stbl->stss.entries)) {
if (!atom_stss_copy_data (&stbl->stss, buffer, size, offset)) {
return 0;
}
}
if (!atom_stsc_copy_data (&stbl->stsc, buffer, size, offset)) {
return 0;
}
if (!atom_stsz_copy_data (&stbl->stsz, buffer, size, offset)) {
return 0;
}
if (stbl->ctts && stbl->ctts->do_pts) {
if (!atom_ctts_copy_data (stbl->ctts, buffer, size, offset)) {
return 0;
}
}
if (!atom_stco64_copy_data (&stbl->stco64, buffer, size, offset)) {
return 0;
}
atom_write_size (buffer, size, offset, original_offset);
return original_offset - *offset;
}
static guint64
atom_dref_copy_data (AtomDREF * dref, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
GList *walker;
if (!atom_full_copy_data (&dref->header, buffer, size, offset)) {
return 0;
}
prop_copy_uint32 (g_list_length (dref->entries), buffer, size, offset);
walker = dref->entries;
while (walker != NULL) {
Atom *atom = (Atom *) walker->data;
if (atom->type == FOURCC_url_) {
atom_url_copy_data ((AtomURL *) atom, buffer, size, offset);
} else if (atom->type == FOURCC_alis) {
atom_full_copy_data ((AtomFull *) atom, buffer, size, offset);
} else {
g_error ("Unsupported atom used inside dref atom");
}
walker = g_list_next (walker);
}
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_dinf_copy_data (AtomDINF * dinf, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_copy_data (&dinf->header, buffer, size, offset)) {
return 0;
}
if (!atom_dref_copy_data (&dinf->dref, buffer, size, offset)) {
return 0;
}
atom_write_size (buffer, size, offset, original_offset);
return original_offset - *offset;
}
static guint64
atom_minf_copy_data (AtomMINF * minf, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_copy_data (&minf->header, buffer, size, offset)) {
return 0;
}
if (minf->vmhd) {
if (!atom_vmhd_copy_data (minf->vmhd, buffer, size, offset)) {
return 0;
}
} else if (minf->smhd) {
if (!atom_smhd_copy_data (minf->smhd, buffer, size, offset)) {
return 0;
}
} else if (minf->hmhd) {
if (!atom_hmhd_copy_data (minf->hmhd, buffer, size, offset)) {
return 0;
}
}
if (minf->hdlr) {
if (!atom_hdlr_copy_data (minf->hdlr, buffer, size, offset)) {
return 0;
}
}
if (!atom_dinf_copy_data (&minf->dinf, buffer, size, offset)) {
return 0;
}
if (!atom_stbl_copy_data (&minf->stbl, buffer, size, offset)) {
return 0;
}
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_mdhd_copy_data (AtomMDHD * mdhd, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_full_copy_data (&mdhd->header, buffer, size, offset)) {
return 0;
}
if (!common_time_info_copy_data (&mdhd->time_info,
atom_full_get_version (&mdhd->header) == 0, buffer, size, offset)) {
return 0;
}
prop_copy_uint16 (mdhd->language_code, buffer, size, offset);
prop_copy_uint16 (mdhd->quality, buffer, size, offset);
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_mdia_copy_data (AtomMDIA * mdia, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_copy_data (&mdia->header, buffer, size, offset)) {
return 0;
}
if (!atom_mdhd_copy_data (&mdia->mdhd, buffer, size, offset)) {
return 0;
}
if (!atom_hdlr_copy_data (&mdia->hdlr, buffer, size, offset)) {
return 0;
}
if (!atom_minf_copy_data (&mdia->minf, buffer, size, offset)) {
return 0;
}
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_elst_copy_data (AtomELST * elst, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
GSList *walker;
if (!atom_full_copy_data (&elst->header, buffer, size, offset)) {
return 0;
}
prop_copy_uint32 (g_slist_length (elst->entries), buffer, size, offset);
for (walker = elst->entries; walker != NULL; walker = g_slist_next (walker)) {
EditListEntry *entry = (EditListEntry *) walker->data;
prop_copy_uint32 (entry->duration, buffer, size, offset);
prop_copy_uint32 (entry->media_time, buffer, size, offset);
prop_copy_uint32 (entry->media_rate, buffer, size, offset);
}
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_edts_copy_data (AtomEDTS * edts, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_copy_data (&(edts->header), buffer, size, offset))
return 0;
if (!atom_elst_copy_data (&(edts->elst), buffer, size, offset))
return 0;
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
guint64
atom_trak_copy_data (AtomTRAK * trak, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_copy_data (&trak->header, buffer, size, offset)) {
return 0;
}
if (!atom_tkhd_copy_data (&trak->tkhd, buffer, size, offset)) {
return 0;
}
if (trak->edts) {
if (!atom_edts_copy_data (trak->edts, buffer, size, offset)) {
return 0;
}
}
if (!atom_mdia_copy_data (&trak->mdia, buffer, size, offset)) {
return 0;
}
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_tag_data_copy_data (AtomTagData * data, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_full_copy_data (&data->header, buffer, size, offset)) {
return 0;
}
prop_copy_uint32 (data->reserved, buffer, size, offset);
prop_copy_uint8_array (data->data, data->datalen, buffer, size, offset);
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_tag_copy_data (AtomTag * tag, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_copy_data (&tag->header, buffer, size, offset)) {
return 0;
}
if (!atom_tag_data_copy_data (&tag->data, buffer, size, offset)) {
return 0;
}
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_ilst_copy_data (AtomILST * ilst, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_copy_data (&ilst->header, buffer, size, offset)) {
return 0;
}
/* extra atoms */
if (ilst->entries &&
!atom_info_list_copy_data (ilst->entries, buffer, size, offset))
return 0;
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_meta_copy_data (AtomMETA * meta, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_full_copy_data (&meta->header, buffer, size, offset)) {
return 0;
}
if (!atom_hdlr_copy_data (&meta->hdlr, buffer, size, offset)) {
return 0;
}
if (meta->ilst) {
if (!atom_ilst_copy_data (meta->ilst, buffer, size, offset)) {
return 0;
}
}
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_udta_copy_data (AtomUDTA * udta, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_copy_data (&udta->header, buffer, size, offset)) {
return 0;
}
if (udta->meta) {
if (!atom_meta_copy_data (udta->meta, buffer, size, offset)) {
return 0;
}
}
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);
return *offset - original_offset;
}
static guint64
atom_mehd_copy_data (AtomMEHD * mehd, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_full_copy_data (&mehd->header, buffer, size, offset)) {
return 0;
}
prop_copy_uint64 (mehd->fragment_duration, buffer, size, offset);
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_trex_copy_data (AtomTREX * trex, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_full_copy_data (&trex->header, buffer, size, offset)) {
return 0;
}
prop_copy_uint32 (trex->track_ID, buffer, size, offset);
prop_copy_uint32 (trex->default_sample_description_index, buffer, size,
offset);
prop_copy_uint32 (trex->default_sample_duration, buffer, size, offset);
prop_copy_uint32 (trex->default_sample_size, buffer, size, offset);
prop_copy_uint32 (trex->default_sample_flags, buffer, size, offset);
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_mvex_copy_data (AtomMVEX * mvex, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
GList *walker;
if (!atom_copy_data (&mvex->header, buffer, size, offset)) {
return 0;
}
if (!atom_mehd_copy_data (&mvex->mehd, buffer, size, offset)) {
return 0;
}
walker = g_list_first (mvex->trexs);
while (walker != NULL) {
if (!atom_trex_copy_data ((AtomTREX *) walker->data, buffer, size, offset)) {
return 0;
}
walker = g_list_next (walker);
}
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
guint64
atom_moov_copy_data (AtomMOOV * atom, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
GList *walker;
if (!atom_copy_data (&(atom->header), buffer, size, offset))
return 0;
if (!atom_mvhd_copy_data (&(atom->mvhd), buffer, size, offset))
return 0;
walker = g_list_first (atom->traks);
while (walker != NULL) {
if (!atom_trak_copy_data ((AtomTRAK *) walker->data, buffer, size, offset)) {
return 0;
}
walker = g_list_next (walker);
}
if (atom->udta) {
if (!atom_udta_copy_data (atom->udta, buffer, size, offset)) {
return 0;
}
}
if (atom->fragmented) {
if (!atom_mvex_copy_data (&atom->mvex, buffer, size, offset)) {
return 0;
}
}
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_wave_copy_data (AtomWAVE * wave, guint8 ** buffer,
guint64 * size, guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_copy_data (&(wave->header), buffer, size, offset))
return 0;
if (wave->extension_atoms) {
if (!atom_info_list_copy_data (wave->extension_atoms, buffer, size, offset))
return 0;
}
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
/* -- end of copy data functions -- */
/* -- general functions, API and support functions */
/* add samples to tables */
static void
atom_stsc_add_new_entry (AtomSTSC * stsc, guint32 first_chunk, guint32 nsamples)
{
STSCEntry nentry;
gint len;
if ((len = atom_array_get_len (&stsc->entries)) &&
((atom_array_index (&stsc->entries, len - 1)).samples_per_chunk ==
nsamples))
return;
nentry.first_chunk = first_chunk;
nentry.samples_per_chunk = nsamples;
nentry.sample_description_index = 1;
atom_array_append (&stsc->entries, nentry, 128);
}
static void
atom_stts_add_entry (AtomSTTS * stts, guint32 sample_count, gint32 sample_delta)
{
STTSEntry *entry = NULL;
if (G_LIKELY (atom_array_get_len (&stts->entries) != 0))
entry = &atom_array_index (&stts->entries,
atom_array_get_len (&stts->entries) - 1);
if (entry && entry->sample_delta == sample_delta) {
entry->sample_count += sample_count;
} else {
STTSEntry nentry;
nentry.sample_count = sample_count;
nentry.sample_delta = sample_delta;
atom_array_append (&stts->entries, nentry, 256);
}
}
static void
atom_stsz_add_entry (AtomSTSZ * stsz, guint32 nsamples, guint32 size)
{
guint32 i;
stsz->table_size += nsamples;
if (stsz->sample_size != 0) {
/* it is constant size, we don't need entries */
return;
}
for (i = 0; i < nsamples; i++) {
atom_array_append (&stsz->entries, size, 1024);
}
}
static guint32
atom_stco64_get_entry_count (AtomSTCO64 * stco64)
{
return atom_array_get_len (&stco64->entries);
}
static void
atom_stco64_add_entry (AtomSTCO64 * stco64, guint64 entry)
{
atom_array_append (&stco64->entries, entry, 256);
if (entry > G_MAXUINT32)
stco64->header.header.type = FOURCC_co64;
}
static void
atom_stss_add_entry (AtomSTSS * stss, guint32 sample)
{
atom_array_append (&stss->entries, sample, 512);
}
static void
atom_stbl_add_stss_entry (AtomSTBL * stbl)
{
guint32 sample_index = stbl->stsz.table_size;
atom_stss_add_entry (&stbl->stss, sample_index);
}
static void
atom_ctts_add_entry (AtomCTTS * ctts, guint32 nsamples, guint32 offset)
{
CTTSEntry *entry = NULL;
if (G_LIKELY (atom_array_get_len (&ctts->entries) != 0))
entry = &atom_array_index (&ctts->entries,
atom_array_get_len (&ctts->entries) - 1);
if (entry == NULL || entry->sampleoffset != offset) {
CTTSEntry nentry;
nentry.samplecount = nsamples;
nentry.sampleoffset = offset;
atom_array_append (&ctts->entries, nentry, 256);
if (offset != 0)
ctts->do_pts = TRUE;
} else {
entry->samplecount += nsamples;
}
}
static void
atom_stbl_add_ctts_entry (AtomSTBL * stbl, guint32 nsamples, guint32 offset)
{
if (stbl->ctts == NULL) {
stbl->ctts = atom_ctts_new ();
}
atom_ctts_add_entry (stbl->ctts, nsamples, offset);
}
void
atom_stbl_add_samples (AtomSTBL * stbl, guint32 nsamples, guint32 delta,
guint32 size, guint64 chunk_offset, gboolean sync, gint64 pts_offset)
{
atom_stts_add_entry (&stbl->stts, nsamples, delta);
atom_stsz_add_entry (&stbl->stsz, nsamples, size);
atom_stco64_add_entry (&stbl->stco64, chunk_offset);
atom_stsc_add_new_entry (&stbl->stsc,
atom_stco64_get_entry_count (&stbl->stco64), nsamples);
if (sync)
atom_stbl_add_stss_entry (stbl);
/* always store to arrange for consistent content */
atom_stbl_add_ctts_entry (stbl, nsamples, pts_offset);
}
void
atom_trak_add_samples (AtomTRAK * trak, guint32 nsamples, guint32 delta,
guint32 size, guint64 chunk_offset, gboolean sync, gint64 pts_offset)
{
AtomSTBL *stbl = &trak->mdia.minf.stbl;
atom_stbl_add_samples (stbl, nsamples, delta, size, chunk_offset, sync,
pts_offset);
}
/* trak and moov molding */
guint32
atom_trak_get_timescale (AtomTRAK * trak)
{
return trak->mdia.mdhd.time_info.timescale;
}
guint32
atom_trak_get_id (AtomTRAK * trak)
{
return trak->tkhd.track_ID;
}
static void
atom_trak_set_id (AtomTRAK * trak, guint32 id)
{
trak->tkhd.track_ID = id;
}
static void
atom_moov_add_trex (AtomMOOV * moov, AtomTREX * trex)
{
moov->mvex.trexs = g_list_append (moov->mvex.trexs, trex);
}
static AtomTREX *
atom_trex_new (AtomTRAK * trak)
{
guint8 flags[3] = { 0, 0, 0 };
AtomTREX *trex = g_new0 (AtomTREX, 1);
atom_full_init (&trex->header, FOURCC_trex, 0, 0, 0, flags);
trex->track_ID = trak->tkhd.track_ID;
trex->default_sample_description_index = 1;
trex->default_sample_duration = 0;
trex->default_sample_size = 0;
trex->default_sample_flags = 0;
return trex;
}
void
atom_moov_add_trak (AtomMOOV * moov, AtomTRAK * trak)
{
atom_trak_set_id (trak, moov->mvhd.next_track_id++);
moov->traks = g_list_append (moov->traks, trak);
/* additional trak means also new trex */
atom_moov_add_trex (moov, atom_trex_new (trak));
}
static guint64
atom_trak_get_duration (AtomTRAK * trak)
{
return trak->tkhd.duration;
}
static guint64
atom_stts_get_total_duration (AtomSTTS * stts)
{
guint i;
guint64 sum = 0;
for (i = 0; i < atom_array_get_len (&stts->entries); i++) {
STTSEntry *entry = &atom_array_index (&stts->entries, i);
sum += (guint64) (entry->sample_count) * entry->sample_delta;
}
return sum;
}
static void
atom_trak_update_duration (AtomTRAK * trak, guint64 moov_timescale)
{
trak->mdia.mdhd.time_info.duration =
atom_stts_get_total_duration (&trak->mdia.minf.stbl.stts);
if (trak->mdia.mdhd.time_info.timescale != 0) {
trak->tkhd.duration =
gst_util_uint64_scale (trak->mdia.mdhd.time_info.duration,
moov_timescale, trak->mdia.mdhd.time_info.timescale);
} else {
trak->tkhd.duration = 0;
}
}
static guint32
atom_moov_get_timescale (AtomMOOV * moov)
{
return moov->mvhd.time_info.timescale;
}
void
atom_moov_update_timescale (AtomMOOV * moov, guint32 timescale)
{
moov->mvhd.time_info.timescale = timescale;
}
void
atom_moov_update_duration (AtomMOOV * moov)
{
GList *traks = moov->traks;
guint64 dur, duration = 0;
while (traks) {
AtomTRAK *trak = (AtomTRAK *) traks->data;
atom_trak_update_duration (trak, atom_moov_get_timescale (moov));
dur = atom_trak_get_duration (trak);
if (dur > duration)
duration = dur;
traks = g_list_next (traks);
}
moov->mvhd.time_info.duration = duration;
moov->mvex.mehd.fragment_duration = duration;
}
void
atom_moov_set_fragmented (AtomMOOV * moov, gboolean fragmented)
{
moov->fragmented = fragmented;
}
void
atom_stco64_chunks_add_offset (AtomSTCO64 * stco64, guint32 offset)
{
guint i;
for (i = 0; i < atom_array_get_len (&stco64->entries); i++) {
guint64 *value = &atom_array_index (&stco64->entries, i);
*value += offset;
}
}
void
atom_moov_chunks_add_offset (AtomMOOV * moov, guint32 offset)
{
GList *traks = moov->traks;
while (traks) {
AtomTRAK *trak = (AtomTRAK *) traks->data;
atom_stco64_chunks_add_offset (&trak->mdia.minf.stbl.stco64, offset);
traks = g_list_next (traks);
}
}
void
atom_trak_update_bitrates (AtomTRAK * trak, guint32 avg_bitrate,
guint32 max_bitrate)
{
AtomESDS *esds = NULL;
AtomData *btrt = NULL;
AtomWAVE *wave = NULL;
AtomSTSD *stsd;
GList *iter;
GList *extensioniter = NULL;
g_return_if_fail (trak != NULL);
if (avg_bitrate == 0 && max_bitrate == 0)
return;
stsd = &trak->mdia.minf.stbl.stsd;
for (iter = stsd->entries; iter; iter = g_list_next (iter)) {
SampleTableEntry *entry = iter->data;
switch (entry->kind) {
case AUDIO:{
SampleTableEntryMP4A *audioentry = (SampleTableEntryMP4A *) entry;
extensioniter = audioentry->extension_atoms;
break;
}
case VIDEO:{
SampleTableEntryMP4V *videoentry = (SampleTableEntryMP4V *) entry;
extensioniter = videoentry->extension_atoms;
break;
}
default:
break;
}
}
for (; extensioniter; extensioniter = g_list_next (extensioniter)) {
AtomInfo *atominfo = extensioniter->data;
if (atominfo->atom->type == FOURCC_esds) {
esds = (AtomESDS *) atominfo->atom;
} else if (atominfo->atom->type == FOURCC_btrt) {
btrt = (AtomData *) atominfo->atom;
} else if (atominfo->atom->type == FOURCC_wave) {
wave = (AtomWAVE *) atominfo->atom;
}
}
/* wave might have an esds internally */
if (wave) {
for (extensioniter = wave->extension_atoms; extensioniter;
extensioniter = g_list_next (extensioniter)) {
AtomInfo *atominfo = extensioniter->data;
if (atominfo->atom->type == FOURCC_esds) {
esds = (AtomESDS *) atominfo->atom;
break;
}
}
}
if (esds) {
if (avg_bitrate && esds->es.dec_conf_desc.avg_bitrate == 0)
esds->es.dec_conf_desc.avg_bitrate = avg_bitrate;
if (max_bitrate && esds->es.dec_conf_desc.max_bitrate == 0)
esds->es.dec_conf_desc.max_bitrate = max_bitrate;
}
if (btrt) {
/* type(4bytes) + size(4bytes) + buffersize(4bytes) +
* maxbitrate(bytes) + avgbitrate(bytes) */
if (max_bitrate && GST_READ_UINT32_BE (btrt->data + 4) == 0)
GST_WRITE_UINT32_BE (btrt->data + 4, max_bitrate);
if (avg_bitrate && GST_READ_UINT32_BE (btrt->data + 8) == 0)
GST_WRITE_UINT32_BE (btrt->data + 8, avg_bitrate);
}
}
/*
* Meta tags functions
*/
static void
atom_moov_init_metatags (AtomMOOV * moov, AtomsContext * context)
{
if (!moov->udta) {
moov->udta = atom_udta_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 ();
}
}
}
static void
atom_tag_data_alloc_data (AtomTagData * data, guint size)
{
if (data->data != NULL) {
g_free (data->data);
}
data->data = g_new0 (guint8, size);
data->datalen = size;
}
static void
atom_moov_append_tag (AtomMOOV * moov, AtomInfo * tag)
{
GList **entries;
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
atom_moov_add_tag (AtomMOOV * moov, guint32 fourcc, guint32 flags,
const guint8 * data, guint size)
{
AtomTag *tag;
AtomTagData *tdata;
tag = atom_tag_new (fourcc, flags);
tdata = &tag->data;
atom_tag_data_alloc_data (tdata, size);
g_memmove (tdata->data, data, size);
atom_moov_append_tag (moov,
build_atom_info_wrapper ((Atom *) tag, atom_tag_copy_data,
atom_tag_free));
}
void
atom_moov_add_str_tag (AtomMOOV * moov, guint32 fourcc, const gchar * value)
{
gint len = strlen (value);
if (len > 0)
atom_moov_add_tag (moov, fourcc, METADATA_TEXT_FLAG, (guint8 *) value, len);
}
void
atom_moov_add_uint_tag (AtomMOOV * moov, guint32 fourcc, guint32 flags,
guint32 value)
{
guint8 data[8] = { 0, };
if (flags) {
GST_WRITE_UINT16_BE (data, value);
atom_moov_add_tag (moov, fourcc, flags, data, 2);
} else {
GST_WRITE_UINT32_BE (data + 2, value);
atom_moov_add_tag (moov, fourcc, flags, data, 8);
}
}
static GstBuffer *
_gst_buffer_new_wrapped (gpointer mem, gsize size, GFreeFunc free_func)
{
GstBuffer *buf;
buf = gst_buffer_new ();
gst_buffer_append_memory (buf,
gst_memory_new_wrapped (free_func ? 0 : GST_MEMORY_FLAG_READONLY,
mem, size, 0, size, mem, free_func));
return buf;
}
void
atom_moov_add_blob_tag (AtomMOOV * moov, guint8 * data, guint size)
{
AtomData *data_atom;
GstBuffer *buf;
guint len;
guint32 fourcc;
if (size < 8)
return;
/* blob is unparsed atom;
* extract size and fourcc, and wrap remainder in data atom */
len = GST_READ_UINT32_BE (data);
fourcc = GST_READ_UINT32_LE (data + 4);
if (len > size)
return;
buf = _gst_buffer_new_wrapped (data + 8, len - 8, NULL);
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));
}
void
atom_moov_add_3gp_tag (AtomMOOV * moov, guint32 fourcc, guint8 * data,
guint size)
{
AtomData *data_atom;
GstBuffer *buf;
guint8 *bdata;
/* need full atom */
bdata = g_malloc (size + 4);
/* full atom: version and flags */
GST_WRITE_UINT32_BE (bdata, 0);
memcpy (bdata + 4, data, size);
buf = _gst_buffer_new_wrapped (bdata, size + 4, g_free);
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);
}
void
atom_moov_add_xmp_tags (AtomMOOV * moov, GstBuffer * xmpbuffer)
{
AtomData *data_atom = NULL;
if (moov->context.flavor == ATOMS_TREE_FLAVOR_MOV) {
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));
}
} else {
GST_DEBUG ("Not adding xmp to moov atom, it is only used in 'mov' format");
}
}
/*
* Functions for specifying media types
*/
static void
atom_minf_set_audio (AtomMINF * minf)
{
atom_minf_clear_handlers (minf);
minf->smhd = atom_smhd_new ();
}
static void
atom_minf_set_video (AtomMINF * minf, AtomsContext * context)
{
atom_minf_clear_handlers (minf);
minf->vmhd = atom_vmhd_new (context);
}
static void
atom_hdlr_set_type (AtomHDLR * hdlr, AtomsContext * context, guint32 comp_type,
guint32 hdlr_type)
{
if (context->flavor == ATOMS_TREE_FLAVOR_MOV) {
hdlr->component_type = comp_type;
}
hdlr->handler_type = hdlr_type;
}
static void
atom_hdlr_set_name (AtomHDLR * hdlr, const char *name)
{
if (hdlr->name)
g_free (hdlr->name);
hdlr->name = g_strdup (name);
}
static void
atom_mdia_set_hdlr_type_audio (AtomMDIA * mdia, AtomsContext * context)
{
atom_hdlr_set_type (&mdia->hdlr, context, FOURCC_mhlr, FOURCC_soun);
/* Some players (low-end hardware) check for this name, which is what
* QuickTime itself sets */
atom_hdlr_set_name (&mdia->hdlr, "SoundHandler");
}
static void
atom_mdia_set_hdlr_type_video (AtomMDIA * mdia, AtomsContext * context)
{
atom_hdlr_set_type (&mdia->hdlr, context, FOURCC_mhlr, FOURCC_vide);
/* Some players (low-end hardware) check for this name, which is what
* QuickTime itself sets */
atom_hdlr_set_name (&mdia->hdlr, "VideoHandler");
}
static void
atom_mdia_set_audio (AtomMDIA * mdia, AtomsContext * context)
{
atom_mdia_set_hdlr_type_audio (mdia, context);
atom_minf_set_audio (&mdia->minf);
}
static void
atom_mdia_set_video (AtomMDIA * mdia, AtomsContext * context)
{
atom_mdia_set_hdlr_type_video (mdia, context);
atom_minf_set_video (&mdia->minf, context);
}
static void
atom_tkhd_set_audio (AtomTKHD * tkhd)
{
tkhd->volume = 0x0100;
tkhd->width = tkhd->height = 0;
}
static void
atom_tkhd_set_video (AtomTKHD * tkhd, AtomsContext * context, guint32 width,
guint32 height)
{
tkhd->volume = 0;
/* qt and ISO base media do not contradict, and examples agree */
tkhd->width = width;
tkhd->height = height;
}
static void
atom_edts_add_entry (AtomEDTS * edts, EditListEntry * entry)
{
edts->elst.entries = g_slist_append (edts->elst.entries, entry);
}
/*
* Adds a new entry to this trak edits list
* duration is in the moov's timescale
* media_time is the offset in the media time to start from (media's timescale)
* rate is a 32 bits fixed-point
*/
void
atom_trak_add_elst_entry (AtomTRAK * trak, guint32 duration, guint32 media_time,
guint32 rate)
{
EditListEntry *entry = g_new (EditListEntry, 1);
entry->duration = duration;
entry->media_time = media_time;
entry->media_rate = rate;
if (trak->edts == NULL) {
trak->edts = atom_edts_new ();
}
atom_edts_add_entry (trak->edts, entry);
}
/* re-negotiation is prevented at top-level, so only 1 entry expected.
* Quite some more care here and elsewhere may be needed to
* support several entries */
static SampleTableEntryMP4A *
atom_trak_add_audio_entry (AtomTRAK * trak, AtomsContext * context,
guint32 type)
{
AtomSTSD *stsd = &trak->mdia.minf.stbl.stsd;
SampleTableEntryMP4A *mp4a = sample_entry_mp4a_new ();
mp4a->se.header.type = type;
mp4a->se.kind = AUDIO;
mp4a->compression_id = -1;
mp4a->se.data_reference_index = 1;
stsd->entries = g_list_prepend (stsd->entries, mp4a);
stsd->n_entries++;
return mp4a;
}
static SampleTableEntryMP4V *
atom_trak_add_video_entry (AtomTRAK * trak, AtomsContext * context,
guint32 type)
{
SampleTableEntryMP4V *mp4v = sample_entry_mp4v_new (context);
AtomSTSD *stsd = &trak->mdia.minf.stbl.stsd;
mp4v->se.header.type = type;
mp4v->se.kind = VIDEO;
mp4v->se.data_reference_index = 1;
mp4v->horizontal_resolution = 72 << 16;
mp4v->vertical_resolution = 72 << 16;
if (context->flavor == ATOMS_TREE_FLAVOR_MOV) {
mp4v->spatial_quality = 512;
mp4v->temporal_quality = 512;
}
stsd->entries = g_list_prepend (stsd->entries, mp4v);
stsd->n_entries++;
return mp4v;
}
static void
atom_trak_set_constant_size_samples (AtomTRAK * trak, guint32 sample_size)
{
trak->mdia.minf.stbl.stsz.sample_size = sample_size;
}
static void
atom_trak_set_audio (AtomTRAK * trak, AtomsContext * context)
{
atom_tkhd_set_audio (&trak->tkhd);
atom_mdia_set_audio (&trak->mdia, context);
}
static void
atom_trak_set_video (AtomTRAK * trak, AtomsContext * context, guint32 width,
guint32 height)
{
atom_tkhd_set_video (&trak->tkhd, context, width, height);
atom_mdia_set_video (&trak->mdia, context);
}
static void
atom_trak_set_audio_commons (AtomTRAK * trak, AtomsContext * context,
guint32 rate)
{
atom_trak_set_audio (trak, context);
trak->mdia.mdhd.time_info.timescale = rate;
}
static void
atom_trak_set_video_commons (AtomTRAK * trak, AtomsContext * context,
guint32 rate, guint32 width, guint32 height)
{
atom_trak_set_video (trak, context, width, height);
trak->mdia.mdhd.time_info.timescale = rate;
trak->tkhd.width = width << 16;
trak->tkhd.height = height << 16;
}
void
atom_trak_set_audio_type (AtomTRAK * trak, AtomsContext * context,
AudioSampleEntry * entry, guint32 scale, AtomInfo * ext, gint sample_size)
{
SampleTableEntryMP4A *ste;
atom_trak_set_audio_commons (trak, context, scale);
atom_stsd_remove_entries (&trak->mdia.minf.stbl.stsd);
ste = atom_trak_add_audio_entry (trak, context, entry->fourcc);
trak->is_video = FALSE;
trak->is_h264 = FALSE;
ste->version = entry->version;
ste->compression_id = entry->compression_id;
ste->sample_size = entry->sample_size;
ste->sample_rate = entry->sample_rate << 16;
ste->channels = entry->channels;
ste->samples_per_packet = entry->samples_per_packet;
ste->bytes_per_sample = entry->bytes_per_sample;
ste->bytes_per_packet = entry->bytes_per_packet;
ste->bytes_per_frame = entry->bytes_per_frame;
if (ext)
ste->extension_atoms = g_list_prepend (ste->extension_atoms, ext);
/* 0 size means variable size */
atom_trak_set_constant_size_samples (trak, sample_size);
}
static AtomInfo *
build_pasp_extension (AtomTRAK * trak, gint par_width, gint par_height)
{
AtomData *atom_data;
GstBuffer *buf;
guint8 *data;
data = g_malloc (8);
/* ihdr = image header box */
GST_WRITE_UINT32_BE (data, par_width);
GST_WRITE_UINT32_BE (data + 4, par_height);
buf = _gst_buffer_new_wrapped (data, 8, g_free);
atom_data = atom_data_new_from_gst_buffer (FOURCC_pasp, buf);
gst_buffer_unref (buf);
return build_atom_info_wrapper ((Atom *) atom_data, atom_data_copy_data,
atom_data_free);
}
void
atom_trak_set_video_type (AtomTRAK * trak, AtomsContext * context,
VisualSampleEntry * entry, guint32 scale, GList * ext_atoms_list)
{
SampleTableEntryMP4V *ste;
gint dwidth, dheight;
gint par_n = 0, par_d = 0;
if ((entry->par_n != 1 || entry->par_d != 1) &&
(entry->par_n != entry->par_d)) {
par_n = entry->par_n;
par_d = entry->par_d;
}
dwidth = entry->width;
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_MOV)) {
if (par_n > par_d) {
dwidth = entry->width * par_n / par_d;
dheight = entry->height;
} else {
dwidth = entry->width * par_n / par_d;
dheight = entry->height;
}
}
atom_trak_set_video_commons (trak, context, scale, dwidth, dheight);
atom_stsd_remove_entries (&trak->mdia.minf.stbl.stsd);
ste = atom_trak_add_video_entry (trak, context, entry->fourcc);
trak->is_video = TRUE;
trak->is_h264 = (entry->fourcc == FOURCC_avc1
|| entry->fourcc == FOURCC_avc3);
ste->version = entry->version;
ste->width = entry->width;
ste->height = entry->height;
ste->depth = entry->depth;
ste->color_table_id = entry->color_table_id;
ste->frame_count = entry->frame_count;
if (ext_atoms_list)
ste->extension_atoms = g_list_concat (ste->extension_atoms, ext_atoms_list);
/* QT spec has a pasp extension atom in stsd that can hold PAR */
if (par_n && (context->flavor == ATOMS_TREE_FLAVOR_MOV)) {
ste->extension_atoms = g_list_append (ste->extension_atoms,
build_pasp_extension (trak, par_n, par_d));
}
}
static void
atom_mfhd_init (AtomMFHD * mfhd, guint32 sequence_number)
{
guint8 flags[3] = { 0, 0, 0 };
atom_full_init (&(mfhd->header), FOURCC_mfhd, 0, 0, 0, flags);
mfhd->sequence_number = sequence_number;
}
static void
atom_moof_init (AtomMOOF * moof, AtomsContext * context,
guint32 sequence_number)
{
atom_header_set (&moof->header, FOURCC_moof, 0, 0);
atom_mfhd_init (&moof->mfhd, sequence_number);
moof->trafs = NULL;
}
AtomMOOF *
atom_moof_new (AtomsContext * context, guint32 sequence_number)
{
AtomMOOF *moof = g_new0 (AtomMOOF, 1);
atom_moof_init (moof, context, sequence_number);
return moof;
}
static void
atom_trun_free (AtomTRUN * trun)
{
atom_full_clear (&trun->header);
atom_array_clear (&trun->entries);
g_free (trun);
}
static void
atom_sdtp_free (AtomSDTP * sdtp)
{
atom_full_clear (&sdtp->header);
atom_array_clear (&sdtp->entries);
g_free (sdtp);
}
void
atom_traf_free (AtomTRAF * traf)
{
GList *walker;
walker = traf->truns;
while (walker) {
atom_trun_free ((AtomTRUN *) walker->data);
walker = g_list_next (walker);
}
g_list_free (traf->truns);
traf->truns = NULL;
walker = traf->sdtps;
while (walker) {
atom_sdtp_free ((AtomSDTP *) walker->data);
walker = g_list_next (walker);
}
g_list_free (traf->sdtps);
traf->sdtps = NULL;
g_free (traf);
}
void
atom_moof_free (AtomMOOF * moof)
{
GList *walker;
walker = moof->trafs;
while (walker) {
atom_traf_free ((AtomTRAF *) walker->data);
walker = g_list_next (walker);
}
g_list_free (moof->trafs);
moof->trafs = NULL;
g_free (moof);
}
static guint64
atom_mfhd_copy_data (AtomMFHD * mfhd, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_full_copy_data (&mfhd->header, buffer, size, offset)) {
return 0;
}
prop_copy_uint32 (mfhd->sequence_number, buffer, size, offset);
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_tfhd_copy_data (AtomTFHD * tfhd, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
guint32 flags;
if (!atom_full_copy_data (&tfhd->header, buffer, size, offset)) {
return 0;
}
prop_copy_uint32 (tfhd->track_ID, buffer, size, offset);
flags = atom_full_get_flags_as_uint (&tfhd->header);
if (flags & TF_BASE_DATA_OFFSET)
prop_copy_uint64 (tfhd->base_data_offset, buffer, size, offset);
if (flags & TF_SAMPLE_DESCRIPTION_INDEX)
prop_copy_uint32 (tfhd->sample_description_index, buffer, size, offset);
if (flags & TF_DEFAULT_SAMPLE_DURATION)
prop_copy_uint32 (tfhd->default_sample_duration, buffer, size, offset);
if (flags & TF_DEFAULT_SAMPLE_SIZE)
prop_copy_uint32 (tfhd->default_sample_size, buffer, size, offset);
if (flags & TF_DEFAULT_SAMPLE_FLAGS)
prop_copy_uint32 (tfhd->default_sample_flags, buffer, size, offset);
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_trun_copy_data (AtomTRUN * trun, guint8 ** buffer, guint64 * size,
guint64 * offset, guint32 * data_offset)
{
guint64 original_offset = *offset;
guint32 flags, i;
flags = atom_full_get_flags_as_uint (&trun->header);
/* if first trun in moof, forcibly add data_offset and record
* where it must be written later on */
if (data_offset && !*data_offset) {
flags |= TR_DATA_OFFSET;
} else {
flags &= ~TR_DATA_OFFSET;
}
atom_full_set_flags_as_uint (&trun->header, flags);
if (!atom_full_copy_data (&trun->header, buffer, size, offset)) {
return 0;
}
prop_copy_uint32 (trun->sample_count, buffer, size, offset);
if (flags & TR_DATA_OFFSET) {
*data_offset = *offset;
prop_copy_int32 (trun->data_offset, buffer, size, offset);
}
if (flags & TR_FIRST_SAMPLE_FLAGS)
prop_copy_uint32 (trun->first_sample_flags, buffer, size, offset);
for (i = 0; i < atom_array_get_len (&trun->entries); i++) {
TRUNSampleEntry *entry = &atom_array_index (&trun->entries, i);
if (flags & TR_SAMPLE_DURATION)
prop_copy_uint32 (entry->sample_duration, buffer, size, offset);
if (flags & TR_SAMPLE_SIZE)
prop_copy_uint32 (entry->sample_size, buffer, size, offset);
if (flags & TR_SAMPLE_FLAGS)
prop_copy_uint32 (entry->sample_flags, buffer, size, offset);
if (flags & TR_COMPOSITION_TIME_OFFSETS)
prop_copy_uint32 (entry->sample_composition_time_offset,
buffer, size, offset);
}
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_sdtp_copy_data (AtomSDTP * sdtp, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
if (!atom_full_copy_data (&sdtp->header, buffer, size, offset)) {
return 0;
}
/* all entries at once */
prop_copy_fixed_size_string (&atom_array_index (&sdtp->entries, 0),
atom_array_get_len (&sdtp->entries), buffer, size, offset);
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_traf_copy_data (AtomTRAF * traf, guint8 ** buffer, guint64 * size,
guint64 * offset, guint32 * data_offset)
{
guint64 original_offset = *offset;
GList *walker;
if (!atom_copy_data (&traf->header, buffer, size, offset)) {
return 0;
}
if (!atom_tfhd_copy_data (&traf->tfhd, buffer, size, offset)) {
return 0;
}
walker = g_list_first (traf->truns);
while (walker != NULL) {
if (!atom_trun_copy_data ((AtomTRUN *) walker->data, buffer, size, offset,
data_offset)) {
return 0;
}
walker = g_list_next (walker);
}
walker = g_list_first (traf->sdtps);
while (walker != NULL) {
if (!atom_sdtp_copy_data ((AtomSDTP *) walker->data, buffer, size, offset)) {
return 0;
}
walker = g_list_next (walker);
}
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
/* creates moof atom; metadata is written expecting actual buffer data
* is in mdata directly after moof, and is consecutively written per trak */
guint64
atom_moof_copy_data (AtomMOOF * moof, guint8 ** buffer,
guint64 * size, guint64 * offset)
{
guint64 original_offset = *offset;
GList *walker;
guint32 data_offset = 0;
if (!atom_copy_data (&moof->header, buffer, size, offset))
return 0;
if (!atom_mfhd_copy_data (&moof->mfhd, buffer, size, offset))
return 0;
walker = g_list_first (moof->trafs);
while (walker != NULL) {
if (!atom_traf_copy_data ((AtomTRAF *) walker->data, buffer, size, offset,
&data_offset)) {
return 0;
}
walker = g_list_next (walker);
}
atom_write_size (buffer, size, offset, original_offset);
if (*buffer && data_offset) {
/* first trun needs a data-offset relative to moof start
* = moof size + mdat prefix */
GST_WRITE_UINT32_BE (*buffer + data_offset, *offset - original_offset + 8);
}
return *offset - original_offset;
}
static void
atom_tfhd_init (AtomTFHD * tfhd, guint32 track_ID)
{
guint8 flags[3] = { 0, 0, 0 };
atom_full_init (&tfhd->header, FOURCC_tfhd, 0, 0, 0, flags);
tfhd->track_ID = track_ID;
tfhd->base_data_offset = 0;
tfhd->sample_description_index = 1;
tfhd->default_sample_duration = 0;
tfhd->default_sample_size = 0;
tfhd->default_sample_flags = 0;
}
static void
atom_trun_init (AtomTRUN * trun)
{
guint8 flags[3] = { 0, 0, 0 };
atom_full_init (&trun->header, FOURCC_trun, 0, 0, 0, flags);
trun->sample_count = 0;
trun->data_offset = 0;
trun->first_sample_flags = 0;
atom_array_init (&trun->entries, 512);
}
static AtomTRUN *
atom_trun_new (void)
{
AtomTRUN *trun = g_new0 (AtomTRUN, 1);
atom_trun_init (trun);
return trun;
}
static void
atom_sdtp_init (AtomSDTP * sdtp)
{
guint8 flags[3] = { 0, 0, 0 };
atom_full_init (&sdtp->header, FOURCC_sdtp, 0, 0, 0, flags);
atom_array_init (&sdtp->entries, 512);
}
static AtomSDTP *
atom_sdtp_new (AtomsContext * context)
{
AtomSDTP *sdtp = g_new0 (AtomSDTP, 1);
atom_sdtp_init (sdtp);
return sdtp;
}
static void
atom_traf_add_sdtp (AtomTRAF * traf, AtomSDTP * sdtp)
{
traf->sdtps = g_list_append (traf->sdtps, sdtp);
}
static void
atom_sdtp_add_samples (AtomSDTP * sdtp, guint8 val)
{
/* it does not make much/any sense according to specs,
* but that's how MS isml samples seem to do it */
atom_array_append (&sdtp->entries, val, 256);
}
static void
atom_trun_add_samples (AtomTRUN * trun, guint32 delta, guint32 size,
guint32 flags, gint64 pts_offset)
{
TRUNSampleEntry nentry;
if (pts_offset != 0)
trun->header.flags[1] |= TR_COMPOSITION_TIME_OFFSETS;
nentry.sample_duration = delta;
nentry.sample_size = size;
nentry.sample_flags = flags;
nentry.sample_composition_time_offset = pts_offset;
atom_array_append (&trun->entries, nentry, 256);
trun->sample_count++;
}
static void
atom_traf_init (AtomTRAF * traf, AtomsContext * context, guint32 track_ID)
{
atom_header_set (&traf->header, FOURCC_traf, 0, 0);
atom_tfhd_init (&traf->tfhd, track_ID);
traf->truns = NULL;
if (context->flavor == ATOMS_TREE_FLAVOR_ISML)
atom_traf_add_sdtp (traf, atom_sdtp_new (context));
}
AtomTRAF *
atom_traf_new (AtomsContext * context, guint32 track_ID)
{
AtomTRAF *traf = g_new0 (AtomTRAF, 1);
atom_traf_init (traf, context, track_ID);
return traf;
}
static void
atom_traf_add_trun (AtomTRAF * traf, AtomTRUN * trun)
{
traf->truns = g_list_append (traf->truns, trun);
}
void
atom_traf_add_samples (AtomTRAF * traf, guint32 delta, guint32 size,
gboolean sync, gint64 pts_offset, gboolean sdtp_sync)
{
AtomTRUN *trun;
guint32 flags;
/* 0x10000 is sample-is-difference-sample flag
* low byte stuff is what ismv uses */
flags = (sync ? 0x0 : 0x10000) | (sdtp_sync ? 0x40 : 0xc0);
if (G_UNLIKELY (!traf->truns)) {
trun = atom_trun_new ();
atom_traf_add_trun (traf, trun);
/* optimistic; indicate all defaults present in tfhd */
traf->tfhd.header.flags[2] = TF_DEFAULT_SAMPLE_DURATION |
TF_DEFAULT_SAMPLE_SIZE | TF_DEFAULT_SAMPLE_FLAGS;
traf->tfhd.default_sample_duration = delta;
traf->tfhd.default_sample_size = size;
traf->tfhd.default_sample_flags = flags;
trun->first_sample_flags = flags;
}
trun = traf->truns->data;
/* check if still matching defaults,
* if not, abandon default and need entry for each sample */
if (traf->tfhd.default_sample_duration != delta) {
traf->tfhd.header.flags[2] &= ~TF_DEFAULT_SAMPLE_DURATION;
trun->header.flags[1] |= (TR_SAMPLE_DURATION >> 8);
}
if (traf->tfhd.default_sample_size != size) {
traf->tfhd.header.flags[2] &= ~TF_DEFAULT_SAMPLE_SIZE;
trun->header.flags[1] |= (TR_SAMPLE_SIZE >> 8);
}
if (traf->tfhd.default_sample_flags != flags) {
if (trun->sample_count == 1) {
/* at least will need first sample flag */
traf->tfhd.default_sample_flags = flags;
trun->header.flags[2] |= TR_FIRST_SAMPLE_FLAGS;
} else {
/* now we need sample flags for each sample */
traf->tfhd.header.flags[2] &= ~TF_DEFAULT_SAMPLE_FLAGS;
trun->header.flags[1] |= (TR_SAMPLE_FLAGS >> 8);
trun->header.flags[2] &= ~TR_FIRST_SAMPLE_FLAGS;
}
}
atom_trun_add_samples (traf->truns->data, delta, size, flags, pts_offset);
if (traf->sdtps)
atom_sdtp_add_samples (traf->sdtps->data, 0x10 | ((flags & 0xff) >> 4));
}
guint32
atom_traf_get_sample_num (AtomTRAF * traf)
{
AtomTRUN *trun;
if (G_UNLIKELY (!traf->truns))
return 0;
trun = traf->truns->data;
return atom_array_get_len (&trun->entries);
}
void
atom_moof_add_traf (AtomMOOF * moof, AtomTRAF * traf)
{
moof->trafs = g_list_append (moof->trafs, traf);
}
static void
atom_tfra_free (AtomTFRA * tfra)
{
atom_full_clear (&tfra->header);
atom_array_clear (&tfra->entries);
g_free (tfra);
}
AtomMFRA *
atom_mfra_new (AtomsContext * context)
{
AtomMFRA *mfra = g_new0 (AtomMFRA, 1);
atom_header_set (&mfra->header, FOURCC_mfra, 0, 0);
return mfra;
}
void
atom_mfra_add_tfra (AtomMFRA * mfra, AtomTFRA * tfra)
{
mfra->tfras = g_list_append (mfra->tfras, tfra);
}
void
atom_mfra_free (AtomMFRA * mfra)
{
GList *walker;
walker = mfra->tfras;
while (walker) {
atom_tfra_free ((AtomTFRA *) walker->data);
walker = g_list_next (walker);
}
g_list_free (mfra->tfras);
mfra->tfras = NULL;
atom_clear (&mfra->header);
g_free (mfra);
}
static void
atom_tfra_init (AtomTFRA * tfra, guint32 track_ID)
{
guint8 flags[3] = { 0, 0, 0 };
atom_full_init (&tfra->header, FOURCC_tfra, 0, 0, 0, flags);
tfra->track_ID = track_ID;
atom_array_init (&tfra->entries, 512);
}
AtomTFRA *
atom_tfra_new (AtomsContext * context, guint32 track_ID)
{
AtomTFRA *tfra = g_new0 (AtomTFRA, 1);
atom_tfra_init (tfra, track_ID);
return tfra;
}
static inline gint
need_bytes (guint32 num)
{
gint n = 0;
while (num >>= 8)
n++;
return n;
}
void
atom_tfra_add_entry (AtomTFRA * tfra, guint64 dts, guint32 sample_num)
{
TFRAEntry entry;
entry.time = dts;
/* fill in later */
entry.moof_offset = 0;
/* always write a single trun in a single traf */
entry.traf_number = 1;
entry.trun_number = 1;
entry.sample_number = sample_num;
/* auto-use 64 bits if needed */
if (dts > G_MAXUINT32)
tfra->header.version = 1;
/* 1 byte will always do for traf and trun number,
* check how much sample_num needs */
tfra->lengths = (tfra->lengths & 0xfc) ||
MAX (tfra->lengths, need_bytes (sample_num));
atom_array_append (&tfra->entries, entry, 256);
}
void
atom_tfra_update_offset (AtomTFRA * tfra, guint64 offset)
{
gint i;
/* auto-use 64 bits if needed */
if (offset > G_MAXUINT32)
tfra->header.version = 1;
for (i = atom_array_get_len (&tfra->entries) - 1; i >= 0; i--) {
TFRAEntry *entry = &atom_array_index (&tfra->entries, i);
if (entry->moof_offset)
break;
entry->moof_offset = offset;
}
}
static guint64
atom_tfra_copy_data (AtomTFRA * tfra, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
guint32 i;
TFRAEntry *entry;
guint32 data;
guint bytes;
guint version;
if (!atom_full_copy_data (&tfra->header, buffer, size, offset)) {
return 0;
}
prop_copy_uint32 (tfra->track_ID, buffer, size, offset);
prop_copy_uint32 (tfra->lengths, buffer, size, offset);
prop_copy_uint32 (atom_array_get_len (&tfra->entries), buffer, size, offset);
version = tfra->header.version;
for (i = 0; i < atom_array_get_len (&tfra->entries); ++i) {
entry = &atom_array_index (&tfra->entries, i);
if (version) {
prop_copy_uint64 (entry->time, buffer, size, offset);
prop_copy_uint64 (entry->moof_offset, buffer, size, offset);
} else {
prop_copy_uint32 (entry->time, buffer, size, offset);
prop_copy_uint32 (entry->moof_offset, buffer, size, offset);
}
bytes = (tfra->lengths & (0x3 << 4)) + 1;
data = GUINT32_TO_BE (entry->traf_number);
prop_copy_fixed_size_string (((guint8 *) & data) + 4 - bytes, bytes,
buffer, size, offset);
bytes = (tfra->lengths & (0x3 << 2)) + 1;
data = GUINT32_TO_BE (entry->trun_number);
prop_copy_fixed_size_string (((guint8 *) & data) + 4 - bytes, bytes,
buffer, size, offset);
bytes = (tfra->lengths & (0x3)) + 1;
data = GUINT32_TO_BE (entry->sample_number);
prop_copy_fixed_size_string (((guint8 *) & data) + 4 - bytes, bytes,
buffer, size, offset);
}
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
static guint64
atom_mfro_copy_data (guint32 s, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
guint8 flags[3] = { 0, 0, 0 };
AtomFull mfro;
atom_full_init (&mfro, FOURCC_mfro, 0, 0, 0, flags);
if (!atom_full_copy_data (&mfro, buffer, size, offset)) {
return 0;
}
prop_copy_uint32 (s, buffer, size, offset);
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
guint64
atom_mfra_copy_data (AtomMFRA * mfra, guint8 ** buffer, guint64 * size,
guint64 * offset)
{
guint64 original_offset = *offset;
GList *walker;
if (!atom_copy_data (&mfra->header, buffer, size, offset))
return 0;
walker = g_list_first (mfra->tfras);
while (walker != NULL) {
if (!atom_tfra_copy_data ((AtomTFRA *) walker->data, buffer, size, offset)) {
return 0;
}
walker = g_list_next (walker);
}
/* 16 is the size of the mfro atom */
if (!atom_mfro_copy_data (*offset - original_offset + 16, buffer,
size, offset))
return 0;
atom_write_size (buffer, size, offset, original_offset);
return *offset - original_offset;
}
/* some sample description construction helpers */
AtomInfo *
build_esds_extension (AtomTRAK * trak, guint8 object_type, guint8 stream_type,
const GstBuffer * codec_data, guint32 avg_bitrate, guint32 max_bitrate)
{
guint32 track_id;
AtomESDS *esds;
track_id = trak->tkhd.track_ID;
esds = atom_esds_new ();
esds->es.id = track_id & 0xFFFF;
esds->es.dec_conf_desc.object_type = object_type;
esds->es.dec_conf_desc.stream_type = stream_type << 2 | 0x01;
if (avg_bitrate > 0)
esds->es.dec_conf_desc.avg_bitrate = avg_bitrate;
if (max_bitrate > 0)
esds->es.dec_conf_desc.max_bitrate = max_bitrate;
/* optional DecoderSpecificInfo */
if (codec_data) {
DecoderSpecificInfoDescriptor *desc;
gsize size;
esds->es.dec_conf_desc.dec_specific_info = desc =
desc_dec_specific_info_new ();
size = gst_buffer_get_size ((GstBuffer *) codec_data);
desc_dec_specific_info_alloc_data (desc, size);
gst_buffer_extract ((GstBuffer *) codec_data, 0, desc->data, size);
}
return build_atom_info_wrapper ((Atom *) esds, atom_esds_copy_data,
atom_esds_free);
}
AtomInfo *
build_btrt_extension (guint32 buffer_size_db, guint32 avg_bitrate,
guint32 max_bitrate)
{
AtomData *atom_data;
GstBuffer *buf;
guint8 *data;
data = g_malloc (12);
GST_WRITE_UINT32_BE (data, buffer_size_db);
GST_WRITE_UINT32_BE (data + 4, max_bitrate);
GST_WRITE_UINT32_BE (data + 8, avg_bitrate);
buf = _gst_buffer_new_wrapped (data, 12, g_free);
atom_data = atom_data_new_from_gst_buffer (FOURCC_btrt, buf);
gst_buffer_unref (buf);
return build_atom_info_wrapper ((Atom *) atom_data, atom_data_copy_data,
atom_data_free);
}
static AtomInfo *
build_mov_wave_extension (AtomTRAK * trak, guint32 fourcc, AtomInfo * atom1,
AtomInfo * atom2, gboolean terminator)
{
AtomWAVE *wave;
AtomFRMA *frma;
Atom *ext_atom;
/* Build WAVE atom for sample table entry */
wave = atom_wave_new ();
/* Prepend Terminator atom to the WAVE list first, so it ends up last */
if (terminator) {
ext_atom = (Atom *) atom_data_new (FOURCC_null);
wave->extension_atoms =
atom_info_list_prepend_atom (wave->extension_atoms, (Atom *) ext_atom,
(AtomCopyDataFunc) atom_data_copy_data, (AtomFreeFunc) atom_data_free);
}
/* Add supplied atoms to WAVE */
if (atom2)
wave->extension_atoms = g_list_prepend (wave->extension_atoms, atom2);
if (atom1)
wave->extension_atoms = g_list_prepend (wave->extension_atoms, atom1);
/* Add FRMA to the WAVE */
frma = atom_frma_new ();
frma->media_type = fourcc;
wave->extension_atoms =
atom_info_list_prepend_atom (wave->extension_atoms, (Atom *) frma,
(AtomCopyDataFunc) atom_frma_copy_data, (AtomFreeFunc) atom_frma_free);
return build_atom_info_wrapper ((Atom *) wave, atom_wave_copy_data,
atom_wave_free);
}
AtomInfo *
build_mov_aac_extension (AtomTRAK * trak, const GstBuffer * codec_data,
guint32 avg_bitrate, guint32 max_bitrate)
{
AtomInfo *esds, *mp4a;
GstBuffer *buf;
guint32 tmp = 0;
/* Add ESDS atom to WAVE */
esds = build_esds_extension (trak, ESDS_OBJECT_TYPE_MPEG4_P3,
ESDS_STREAM_TYPE_AUDIO, codec_data, avg_bitrate, max_bitrate);
/* Add MP4A atom to the WAVE:
* not really in spec, but makes offset based players happy */
buf = _gst_buffer_new_wrapped (&tmp, 4, NULL);
mp4a = build_codec_data_extension (FOURCC_mp4a, buf);
gst_buffer_unref (buf);
return build_mov_wave_extension (trak, FOURCC_mp4a, mp4a, esds, TRUE);
}
AtomInfo *
build_mov_alac_extension (AtomTRAK * trak, const GstBuffer * codec_data)
{
AtomInfo *alac;
alac = build_codec_data_extension (FOURCC_alac, codec_data);
return build_mov_wave_extension (trak, FOURCC_alac, NULL, alac, TRUE);
}
AtomInfo *
build_fiel_extension (gint fields)
{
AtomData *atom_data;
GstBuffer *buf;
guint8 f = fields;
if (fields == 1) {
return NULL;
}
buf = _gst_buffer_new_wrapped (&f, 1, NULL);
atom_data =
atom_data_new_from_gst_buffer (GST_MAKE_FOURCC ('f', 'i', 'e', 'l'), buf);
gst_buffer_unref (buf);
return build_atom_info_wrapper ((Atom *) atom_data, atom_data_copy_data,
atom_data_free);
}
AtomInfo *
build_jp2x_extension (const GstBuffer * prefix)
{
AtomData *atom_data;
if (!prefix) {
return NULL;
}
atom_data =
atom_data_new_from_gst_buffer (GST_MAKE_FOURCC ('j', 'p', '2', 'x'),
prefix);
return build_atom_info_wrapper ((Atom *) atom_data, atom_data_copy_data,
atom_data_free);
}
AtomInfo *
build_jp2h_extension (AtomTRAK * trak, gint width, gint height,
const gchar * colorspace, gint ncomp, const GValue * cmap_array,
const GValue * cdef_array)
{
AtomData *atom_data;
GstBuffer *buf;
guint8 cenum;
gint i;
gint idhr_size = 22;
gint colr_size = 15;
gint cmap_size = 0, cdef_size = 0;
gint cmap_array_size = 0;
gint cdef_array_size = 0;
GstByteWriter writer;
g_return_val_if_fail (cmap_array == NULL ||
GST_VALUE_HOLDS_ARRAY (cmap_array), NULL);
g_return_val_if_fail (cdef_array == NULL ||
GST_VALUE_HOLDS_ARRAY (cdef_array), NULL);
if (g_str_equal (colorspace, "sRGB")) {
cenum = 0x10;
if (ncomp == 0)
ncomp = 3;
} else if (g_str_equal (colorspace, "GRAY")) {
cenum = 0x11;
if (ncomp == 0)
ncomp = 1;
} else if (g_str_equal (colorspace, "sYUV")) {
cenum = 0x12;
if (ncomp == 0)
ncomp = 3;
} else
return NULL;
if (cmap_array) {
cmap_array_size = gst_value_array_get_size (cmap_array);
cmap_size = 8 + cmap_array_size * 4;
}
if (cdef_array) {
cdef_array_size = gst_value_array_get_size (cdef_array);
cdef_size = 8 + 2 + cdef_array_size * 6;
}
gst_byte_writer_init_with_size (&writer,
idhr_size + colr_size + cmap_size + cdef_size, TRUE);
/* ihdr = image header box */
gst_byte_writer_put_uint32_be_unchecked (&writer, 22);
gst_byte_writer_put_uint32_le_unchecked (&writer, GST_MAKE_FOURCC ('i', 'h',
'd', 'r'));
gst_byte_writer_put_uint32_be_unchecked (&writer, height);
gst_byte_writer_put_uint32_be_unchecked (&writer, width);
gst_byte_writer_put_uint16_be_unchecked (&writer, ncomp);
/* 8 bits per component, unsigned */
gst_byte_writer_put_uint8_unchecked (&writer, 0x7);
/* compression type; reserved */
gst_byte_writer_put_uint8_unchecked (&writer, 0x7);
/* colour space (un)known */
gst_byte_writer_put_uint8_unchecked (&writer, 0x0);
/* intellectual property right (box present) */
gst_byte_writer_put_uint8_unchecked (&writer, 0x0);
/* colour specification box */
gst_byte_writer_put_uint32_be_unchecked (&writer, 15);
gst_byte_writer_put_uint32_le_unchecked (&writer, GST_MAKE_FOURCC ('c', 'o',
'l', 'r'));
/* specification method: enumerated */
gst_byte_writer_put_uint8_unchecked (&writer, 0x1);
/* precedence; reserved */
gst_byte_writer_put_uint8_unchecked (&writer, 0x0);
/* approximation; reserved */
gst_byte_writer_put_uint8_unchecked (&writer, 0x0);
/* enumerated colourspace */
gst_byte_writer_put_uint32_be_unchecked (&writer, cenum);
if (cmap_array) {
gst_byte_writer_put_uint32_be_unchecked (&writer, cmap_size);
gst_byte_writer_put_uint32_le_unchecked (&writer,
GST_MAKE_FOURCC ('c', 'm', 'a', 'p'));
for (i = 0; i < cmap_array_size; i++) {
const GValue *item;
gint value;
guint16 cmp;
guint8 mtyp;
guint8 pcol;
item = gst_value_array_get_value (cmap_array, i);
value = g_value_get_int (item);
/* value is '(mtyp << 24) | (pcol << 16) | cmp' */
cmp = value & 0xFFFF;
mtyp = value >> 24;
pcol = (value >> 16) & 0xFF;
if (mtyp == 1)
GST_WARNING ("MTYP of cmap atom signals Pallete Mapping, but we don't "
"handle Pallete mapping atoms yet");
gst_byte_writer_put_uint16_be_unchecked (&writer, cmp);
gst_byte_writer_put_uint8_unchecked (&writer, mtyp);
gst_byte_writer_put_uint8_unchecked (&writer, pcol);
}
}
if (cdef_array) {
gst_byte_writer_put_uint32_be_unchecked (&writer, cdef_size);
gst_byte_writer_put_uint32_le_unchecked (&writer,
GST_MAKE_FOURCC ('c', 'd', 'e', 'f'));
gst_byte_writer_put_uint16_be_unchecked (&writer, cdef_array_size);
for (i = 0; i < cdef_array_size; i++) {
const GValue *item;
gint value;
item = gst_value_array_get_value (cdef_array, i);
value = g_value_get_int (item);
gst_byte_writer_put_uint16_be_unchecked (&writer, i);
if (value > 0) {
gst_byte_writer_put_uint16_be_unchecked (&writer, 0);
gst_byte_writer_put_uint16_be_unchecked (&writer, value);
} else if (value < 0) {
gst_byte_writer_put_uint16_be_unchecked (&writer, -value);
gst_byte_writer_put_uint16_be_unchecked (&writer, 0); /* TODO what here? */
} else {
gst_byte_writer_put_uint16_be_unchecked (&writer, 1);
gst_byte_writer_put_uint16_be_unchecked (&writer, 0);
}
}
}
g_assert (gst_byte_writer_get_remaining (&writer) == 0);
buf = gst_byte_writer_reset_and_get_buffer (&writer);
atom_data = atom_data_new_from_gst_buffer (FOURCC_jp2h, buf);
gst_buffer_unref (buf);
return build_atom_info_wrapper ((Atom *) atom_data, atom_data_copy_data,
atom_data_free);
}
AtomInfo *
build_codec_data_extension (guint32 fourcc, const GstBuffer * codec_data)
{
AtomData *data;
AtomInfo *result = NULL;
if (codec_data) {
data = atom_data_new_from_gst_buffer (fourcc, codec_data);
result = build_atom_info_wrapper ((Atom *) data, atom_data_copy_data,
atom_data_free);
}
return result;
}
AtomInfo *
build_amr_extension (void)
{
guint8 ext[9];
GstBuffer *buf;
AtomInfo *res;
/* vendor */
GST_WRITE_UINT32_LE (ext, 0);
/* decoder version */
GST_WRITE_UINT8 (ext + 4, 0);
/* mode set (all modes) */
GST_WRITE_UINT16_BE (ext + 5, 0x81FF);
/* mode change period (no restriction) */
GST_WRITE_UINT8 (ext + 7, 0);
/* frames per sample */
GST_WRITE_UINT8 (ext + 8, 1);
buf = _gst_buffer_new_wrapped (ext, sizeof (ext), NULL);
res = build_codec_data_extension (GST_MAKE_FOURCC ('d', 'a', 'm', 'r'), buf);
gst_buffer_unref (buf);
return res;
}
AtomInfo *
build_h263_extension (void)
{
guint8 ext[7];
GstBuffer *buf;
AtomInfo *res;
/* vendor */
GST_WRITE_UINT32_LE (ext, 0);
/* decoder version */
GST_WRITE_UINT8 (ext + 4, 0);
/* level / profile */
/* FIXME ? maybe ? obtain somewhere; baseline for now */
GST_WRITE_UINT8 (ext + 5, 10);
GST_WRITE_UINT8 (ext + 6, 0);
buf = _gst_buffer_new_wrapped (ext, sizeof (ext), NULL);
res = build_codec_data_extension (GST_MAKE_FOURCC ('d', '2', '6', '3'), buf);
gst_buffer_unref (buf);
return res;
}
AtomInfo *
build_gama_atom (gdouble gamma)
{
AtomInfo *res;
guint32 gamma_fp;
GstBuffer *buf;
/* convert to uint32 from fixed point */
gamma_fp = (guint32) 65536 *gamma;
gamma_fp = GUINT32_TO_BE (gamma_fp);
buf = _gst_buffer_new_wrapped (&gamma_fp, 4, NULL);
res = build_codec_data_extension (FOURCC_gama, buf);
gst_buffer_unref (buf);
return res;
}
AtomInfo *
build_SMI_atom (const GstBuffer * seqh)
{
AtomInfo *res;
GstBuffer *buf;
gsize size;
guint8 *data;
/* the seqh plus its size and fourcc */
size = gst_buffer_get_size ((GstBuffer *) seqh);
data = g_malloc (size + 8);
GST_WRITE_UINT32_LE (data, FOURCC_SEQH);
GST_WRITE_UINT32_BE (data + 4, size + 8);
gst_buffer_extract ((GstBuffer *) seqh, 0, data + 8, size);
buf = _gst_buffer_new_wrapped (data, size + 8, g_free);
res = build_codec_data_extension (FOURCC_SMI_, buf);
gst_buffer_unref (buf);
return res;
}
static AtomInfo *
build_ima_adpcm_atom (gint channels, gint rate, gint blocksize)
{
AtomData *atom_data;
GstBuffer *buf;
guint8 *data;
const gint ima_adpcm_atom_size = 20;
guint32 fourcc;
gint samplesperblock;
gint bytespersec;
/* The FOURCC for WAV codecs in QT is 'ms' followed by the 16 bit wave codec
identifier. Note that the identifier here is big-endian, but when used
within the WAVE header (below), it's little endian. */
fourcc = MS_WAVE_FOURCC (0x11);
data = g_malloc (ima_adpcm_atom_size);
/* This atom's content is a WAVE header, including 2 bytes of extra data.
Note that all of this is little-endian, unlike most stuff in qt. */
/* 4 bytes header per channel (including 1 sample). Then 2 samples per byte
for the rest. Simplifies to this. */
samplesperblock = 2 * blocksize / channels - 7;
bytespersec = rate * blocksize / samplesperblock;
GST_WRITE_UINT16_LE (data, 0x11);
GST_WRITE_UINT16_LE (data + 2, channels);
GST_WRITE_UINT32_LE (data + 4, rate);
GST_WRITE_UINT32_LE (data + 8, bytespersec);
GST_WRITE_UINT16_LE (data + 12, blocksize);
GST_WRITE_UINT16_LE (data + 14, 4);
GST_WRITE_UINT16_LE (data + 16, 2); /* Two extra bytes */
GST_WRITE_UINT16_LE (data + 18, samplesperblock);
buf = _gst_buffer_new_wrapped (data, ima_adpcm_atom_size, g_free);
atom_data = atom_data_new_from_gst_buffer (fourcc, buf);
gst_buffer_unref (buf);
return build_atom_info_wrapper ((Atom *) atom_data, atom_data_copy_data,
atom_data_free);
}
AtomInfo *
build_ima_adpcm_extension (gint channels, gint rate, gint blocksize)
{
AtomWAVE *wave;
AtomFRMA *frma;
Atom *ext_atom;
/* Add WAVE atom */
wave = atom_wave_new ();
/* Prepend Terminator atom to the WAVE list first, so it ends up last */
ext_atom = (Atom *) atom_data_new (FOURCC_null);
wave->extension_atoms =
atom_info_list_prepend_atom (wave->extension_atoms, (Atom *) ext_atom,
(AtomCopyDataFunc) atom_data_copy_data, (AtomFreeFunc) atom_data_free);
/* Add wave ima adpcm atom to WAVE */
wave->extension_atoms = g_list_prepend (wave->extension_atoms,
build_ima_adpcm_atom (channels, rate, blocksize));
/* Add FRMA to the WAVE */
frma = atom_frma_new ();
frma->media_type = MS_WAVE_FOURCC (0x11);
wave->extension_atoms =
atom_info_list_prepend_atom (wave->extension_atoms, (Atom *) frma,
(AtomCopyDataFunc) atom_frma_copy_data, (AtomFreeFunc) atom_frma_free);
return build_atom_info_wrapper ((Atom *) wave, atom_wave_copy_data,
atom_wave_free);
}
AtomInfo *
build_uuid_xmp_atom (GstBuffer * xmp_data)
{
AtomUUID *uuid;
gsize size;
static guint8 xmp_uuid[] = { 0xBE, 0x7A, 0xCF, 0xCB,
0x97, 0xA9, 0x42, 0xE8,
0x9C, 0x71, 0x99, 0x94,
0x91, 0xE3, 0xAF, 0xAC
};
if (xmp_data == NULL)
return NULL;
uuid = atom_uuid_new ();
memcpy (uuid->uuid, xmp_uuid, 16);
size = gst_buffer_get_size (xmp_data);
uuid->data = g_malloc (size);
uuid->datalen = size;
gst_buffer_extract (xmp_data, 0, uuid->data, size);
return build_atom_info_wrapper ((Atom *) uuid, atom_uuid_copy_data,
atom_uuid_free);
}