qtmux: add mfra to fragmented file

Based on patch by Marc-André Lureau <mlureau@flumotion.com>
This commit is contained in:
Mark Nauwelaerts 2010-11-18 16:48:06 +01:00 committed by Tim-Philipp Müller
parent 4b64dc2f3e
commit 26a281bdab
4 changed files with 320 additions and 9 deletions

View file

@ -3643,12 +3643,238 @@ atom_traf_add_samples (AtomTRAF * traf, guint32 delta, guint32 size,
pts_offset);
}
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 *

View file

@ -729,6 +729,33 @@ typedef struct _AtomWAVE
GList *extension_atoms;
} AtomWAVE;
typedef struct _TFRAEntry
{
guint64 time;
guint64 moof_offset;
guint32 traf_number;
guint32 trun_number;
guint32 sample_number;
} TFRAEntry;
typedef struct _AtomTFRA
{
AtomFull header;
guint32 track_ID;
guint32 lengths;
/* array of entries */
ATOM_ARRAY (TFRAEntry) entries;
} AtomTFRA;
typedef struct _AtomMFRA
{
Atom header;
/* list of tfra */
GList *tfras;
} AtomMFRA;
/*
* Function to serialize an atom
*/
@ -812,8 +839,18 @@ void atom_traf_free (AtomTRAF * traf);
void atom_traf_add_samples (AtomTRAF * traf, guint32 delta,
guint32 size, gboolean sync,
gboolean do_pts, gint64 pts_offset);
guint32 atom_traf_get_sample_num (AtomTRAF * traf);
void atom_moof_add_traf (AtomMOOF *moof, AtomTRAF *traf);
AtomMFRA* atom_mfra_new (AtomsContext *context);
void atom_mfra_free (AtomMFRA *mfra);
AtomTFRA* atom_tfra_new (AtomsContext *context, guint32 track_ID);
void atom_tfra_add_entry (AtomTFRA *tfra, guint64 dts, guint32 sample_num);
void atom_tfra_update_offset (AtomTFRA * tfra, guint64 offset);
void atom_mfra_add_tfra (AtomMFRA *mfra, AtomTFRA *tfra);
guint64 atom_mfra_copy_data (AtomMFRA *mfra, guint8 **buffer, guint64 *size, guint64* offset);
/* media sample description related helpers */
typedef struct

View file

@ -283,6 +283,9 @@ gst_qt_mux_pad_reset (GstQTPad * qtpad)
qtpad->traf = NULL;
}
atom_array_clear (&qtpad->fragment_buffers);
/* reference owned elsewhere */
qtpad->tfra = NULL;
}
/*
@ -310,6 +313,10 @@ gst_qt_mux_reset (GstQTMux * qtmux, gboolean alloc)
atom_moov_free (qtmux->moov);
qtmux->moov = NULL;
}
if (qtmux->mfra) {
atom_mfra_free (qtmux->mfra);
qtmux->mfra = NULL;
}
if (qtmux->fast_start_file) {
fclose (qtmux->fast_start_file);
g_remove (qtmux->fast_start_file_path);
@ -1532,13 +1539,16 @@ gst_qt_mux_start_file (GstQTMux * qtmux)
/* prepare moov and/or tags */
gst_qt_mux_configure_moov (qtmux, NULL);
gst_qt_mux_setup_metadata (qtmux);
ret = gst_qt_mux_send_moov (qtmux, NULL, FALSE);
ret = gst_qt_mux_send_moov (qtmux, &qtmux->header_size, FALSE);
if (ret != GST_FLOW_OK)
return ret;
/* extra atoms */
ret = gst_qt_mux_send_extra_atoms (qtmux, TRUE, NULL, FALSE);
ret =
gst_qt_mux_send_extra_atoms (qtmux, TRUE, &qtmux->header_size, FALSE);
if (ret != GST_FLOW_OK)
return ret;
/* prepare index */
qtmux->mfra = atom_mfra_new (qtmux->context);
} else {
/* extended to ensure some spare space */
ret = gst_qt_mux_send_mdat_header (qtmux, &qtmux->header_size, 0, TRUE);
@ -1595,6 +1605,20 @@ gst_qt_mux_stop_file (GstQTMux * qtmux)
if (qtmux->fragment_sequence) {
GstEvent *event;
if (qtmux->mfra) {
guint8 *data = NULL;
GstBuffer *buf;
size = offset = 0;
GST_DEBUG_OBJECT (qtmux, "adding mfra");
if (!atom_mfra_copy_data (qtmux->mfra, &data, &size, &offset))
goto serialize_error;
buf = _gst_buffer_new_take_data (data, offset);
ret = gst_qt_mux_send_buffer (qtmux, buf, NULL, FALSE);
if (ret != GST_FLOW_OK)
return ret;
}
timescale = qtmux->timescale;
/* only mvex duration is updated,
* mvhd should be consistent with empty moov
@ -1732,8 +1756,9 @@ ftyp_error:
static GstFlowReturn
gst_qt_mux_pad_fragment_add_buffer (GstQTMux * qtmux, GstQTPad * pad,
GstBuffer * buf, gboolean force, guint32 nsamples, guint32 delta,
guint32 size, gboolean sync, gboolean do_pts, gint64 pts_offset)
GstBuffer * buf, gboolean force, guint32 nsamples, gint64 dts,
guint32 delta, guint32 size, gboolean sync, gboolean do_pts,
gint64 pts_offset)
{
GstFlowReturn ret = GST_FLOW_OK;
@ -1752,6 +1777,10 @@ flush:
GstBuffer *buffer;
guint i, total_size;
/* now we know where moof ends up, update offset in tfra */
if (pad->tfra)
atom_tfra_update_offset (pad->tfra, qtmux->header_size);
moof = atom_moof_new (qtmux->context, qtmux->fragment_sequence);
/* takes ownership */
atom_moof_add_traf (moof, pad->traf);
@ -1759,7 +1788,7 @@ flush:
atom_moof_copy_data (moof, &data, &size, &offset);
buffer = _gst_buffer_new_take_data (data, offset);
GST_LOG_OBJECT (qtmux, "writing moof size %d", GST_BUFFER_SIZE (buffer));
ret = gst_qt_mux_send_buffer (qtmux, buffer, NULL, FALSE);
ret = gst_qt_mux_send_buffer (qtmux, buffer, &qtmux->header_size, FALSE);
/* and actual data */
total_size = 0;
@ -1771,11 +1800,13 @@ flush:
GST_LOG_OBJECT (qtmux, "writing %d buffers, total_size %d",
atom_array_get_len (&pad->fragment_buffers), total_size);
if (ret == GST_FLOW_OK)
ret = gst_qt_mux_send_mdat_header (qtmux, NULL, total_size, FALSE);
ret = gst_qt_mux_send_mdat_header (qtmux, &qtmux->header_size, total_size,
FALSE);
for (i = 0; i < atom_array_get_len (&pad->fragment_buffers); i++) {
if (G_LIKELY (ret == GST_FLOW_OK))
ret = gst_qt_mux_send_buffer (qtmux,
atom_array_index (&pad->fragment_buffers, i), NULL, FALSE);
atom_array_index (&pad->fragment_buffers, i), &qtmux->header_size,
FALSE);
else
gst_buffer_unref (atom_array_index (&pad->fragment_buffers, i));
}
@ -1793,6 +1824,11 @@ init:
atom_array_init (&pad->fragment_buffers, 512);
pad->fragment_duration = gst_util_uint64_scale (qtmux->fragment_duration,
atom_trak_get_timescale (pad->trak), 1000);
if (G_UNLIKELY (qtmux->mfra && !pad->tfra)) {
pad->tfra = atom_tfra_new (qtmux->context, atom_trak_get_id (pad->trak));
atom_mfra_add_tfra (qtmux->mfra, pad->tfra);
}
}
/* add buffer and metadata */
@ -1800,6 +1836,13 @@ init:
atom_array_append (&pad->fragment_buffers, buf, 256);
pad->fragment_duration -= delta;
if (pad->tfra) {
guint32 sn = atom_traf_get_sample_num (pad->traf);
if ((sync && pad->sync) || (sn == 1 && !pad->sync))
atom_tfra_add_entry (pad->tfra, dts, sn);
}
if (G_UNLIKELY (force))
goto flush;
@ -2049,8 +2092,8 @@ gst_qt_mux_add_buffer (GstQTMux * qtmux, GstQTPad * pad, GstBuffer * buf)
if (qtmux->fragment_sequence) {
/* ensure that always sync samples are marked as such */
return gst_qt_mux_pad_fragment_add_buffer (qtmux, pad, last_buf,
buf == NULL, nsamples, scaled_duration, sample_size, !pad->sync || sync,
do_pts, pts_offset);
buf == NULL, nsamples, last_dts, scaled_duration, sample_size,
!pad->sync || sync, do_pts, pts_offset);
} else {
atom_trak_add_samples (pad->trak, nsamples, scaled_duration, sample_size,
chunk_offset, sync, do_pts, pts_offset);

View file

@ -113,6 +113,8 @@ struct _GstQTPad
ATOM_ARRAY (GstBuffer *) fragment_buffers;
/* running fragment duration */
gint64 fragment_duration;
/* optional fragment index book-keeping */
AtomTFRA *tfra;
/* if nothing is set, it won't be called */
GstQTPadPrepareBufferFunc prepare_buf_func;
@ -154,6 +156,9 @@ struct _GstQTMux
GSList *extra_atoms; /* list of extra top-level atoms (e.g. UUID for xmp)
* Stored as AtomInfo structs */
/* fragmented file index */
AtomMFRA *mfra;
/* fast start */
FILE *fast_start_file;