summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gst/qtmux/atoms.c226
-rw-r--r--gst/qtmux/atoms.h37
-rw-r--r--gst/qtmux/gstqtmux.c61
-rw-r--r--gst/qtmux/gstqtmux.h5
4 files changed, 320 insertions, 9 deletions
diff --git a/gst/qtmux/atoms.c b/gst/qtmux/atoms.c
index 8ea269f57..755607857 100644
--- a/gst/qtmux/atoms.c
+++ b/gst/qtmux/atoms.c
@@ -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 *
diff --git a/gst/qtmux/atoms.h b/gst/qtmux/atoms.h
index 980c8973d..8f483033e 100644
--- a/gst/qtmux/atoms.h
+++ b/gst/qtmux/atoms.h
@@ -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
diff --git a/gst/qtmux/gstqtmux.c b/gst/qtmux/gstqtmux.c
index e00d25b5f..39fce4e85 100644
--- a/gst/qtmux/gstqtmux.c
+++ b/gst/qtmux/gstqtmux.c
@@ -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);
diff --git a/gst/qtmux/gstqtmux.h b/gst/qtmux/gstqtmux.h
index 9856d7781..76f9d2315 100644
--- a/gst/qtmux/gstqtmux.h
+++ b/gst/qtmux/gstqtmux.h
@@ -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;