mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-03 15:06:34 +00:00
qtmux: add mfra to fragmented file
Based on patch by Marc-André Lureau <mlureau@flumotion.com>
This commit is contained in:
parent
4b64dc2f3e
commit
26a281bdab
4 changed files with 320 additions and 9 deletions
|
@ -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 *
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue