mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 00:36:51 +00:00
isomp4: Implement robust muxing using ping-pong strategy
Implement a robust recording mode, where the output file is always in a playable state, seeking and rewriting the moov header at a configurable interval. Rewriting moov is done using reserved space at the start of the file, and a ping-pong strategy where the moov is replaced atomically so it's never invalid. Track when tags have actually changed, and don't write them into the moov unless they've changed. Clear any existing tags when re-writing them, so we can do progressive moov updating in robust recording mode. Write placeholder mdat as a free atom plus a 32-bit mdat with '0' size, which means "rest of the file" in the spec. Re-write it later to a full 64-bit extended size atom if needed.
This commit is contained in:
parent
3d7b343525
commit
1d058c7d8a
5 changed files with 782 additions and 202 deletions
|
@ -1132,6 +1132,20 @@ atom_udta_clear (AtomUDTA * udta)
|
|||
atom_info_list_free (udta->entries);
|
||||
}
|
||||
|
||||
/* Clear added tags, but keep the context/flavor the same */
|
||||
void
|
||||
atom_udta_clear_tags (AtomUDTA * udta)
|
||||
{
|
||||
if (udta->entries) {
|
||||
atom_info_list_free (udta->entries);
|
||||
udta->entries = NULL;
|
||||
}
|
||||
if (udta->meta && udta->meta->ilst->entries) {
|
||||
atom_info_list_free (udta->meta->ilst->entries);
|
||||
udta->meta->ilst->entries = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
atom_tag_data_init (AtomTagData * data)
|
||||
{
|
||||
|
@ -1980,12 +1994,13 @@ atom_stco64_copy_data (AtomSTCO64 * stco64, guint8 ** buffer, guint64 * size,
|
|||
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);
|
||||
guint64 value =
|
||||
atom_array_index (&stco64->entries, i) + stco64->chunk_offset;
|
||||
|
||||
if (trunc_to_32) {
|
||||
prop_copy_uint32 ((guint32) * value, buffer, size, offset);
|
||||
prop_copy_uint32 ((guint32) value, buffer, size, offset);
|
||||
} else {
|
||||
prop_copy_uint64 (*value, buffer, size, offset);
|
||||
prop_copy_uint64 (value, buffer, size, offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2749,6 +2764,12 @@ atom_moov_add_trak (AtomMOOV * moov, AtomTRAK * trak)
|
|||
atom_moov_add_trex (moov, atom_trex_new (trak));
|
||||
}
|
||||
|
||||
guint
|
||||
atom_moov_get_trak_count (AtomMOOV * moov)
|
||||
{
|
||||
return g_list_length (moov->traks);
|
||||
}
|
||||
|
||||
static guint64
|
||||
atom_trak_get_duration (AtomTRAK * trak)
|
||||
{
|
||||
|
@ -2821,28 +2842,27 @@ atom_moov_set_fragmented (AtomMOOV * moov, gboolean fragmented)
|
|||
}
|
||||
|
||||
void
|
||||
atom_stco64_chunks_add_offset (AtomSTCO64 * stco64, guint32 offset)
|
||||
atom_stco64_chunks_set_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;
|
||||
}
|
||||
stco64->chunk_offset = offset;
|
||||
}
|
||||
|
||||
void
|
||||
atom_moov_chunks_add_offset (AtomMOOV * moov, guint32 offset)
|
||||
atom_moov_chunks_set_offset (AtomMOOV * moov, guint32 offset)
|
||||
{
|
||||
GList *traks = moov->traks;
|
||||
|
||||
if (offset == moov->chunks_offset)
|
||||
return; /* Nothing to do */
|
||||
|
||||
while (traks) {
|
||||
AtomTRAK *trak = (AtomTRAK *) traks->data;
|
||||
|
||||
atom_stco64_chunks_add_offset (&trak->mdia.minf.stbl.stco64, offset);
|
||||
atom_stco64_chunks_set_offset (&trak->mdia.minf.stbl.stco64, offset);
|
||||
traks = g_list_next (traks);
|
||||
}
|
||||
|
||||
moov->chunks_offset = offset;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -478,7 +478,8 @@ typedef struct _AtomSTSC
|
|||
typedef struct _AtomSTCO64
|
||||
{
|
||||
AtomFull header;
|
||||
|
||||
/* Global offset to add to entries when serialising */
|
||||
guint32 chunk_offset;
|
||||
ATOM_ARRAY (guint64) entries;
|
||||
} AtomSTCO64;
|
||||
|
||||
|
@ -754,6 +755,7 @@ typedef struct _AtomMOOV
|
|||
AtomUDTA udta;
|
||||
|
||||
gboolean fragmented;
|
||||
guint32 chunks_offset;
|
||||
} AtomMOOV;
|
||||
|
||||
typedef struct _AtomWAVE
|
||||
|
@ -843,12 +845,13 @@ guint64 atom_moov_copy_data (AtomMOOV *atom, guint8 **buffer, guint64
|
|||
void atom_moov_update_timescale (AtomMOOV *moov, guint32 timescale);
|
||||
void atom_moov_update_duration (AtomMOOV *moov);
|
||||
void atom_moov_set_fragmented (AtomMOOV *moov, gboolean fragmented);
|
||||
void atom_moov_chunks_add_offset (AtomMOOV *moov, guint32 offset);
|
||||
void atom_moov_chunks_set_offset (AtomMOOV *moov, guint32 offset);
|
||||
void atom_moov_add_trak (AtomMOOV *moov, AtomTRAK *trak);
|
||||
guint atom_moov_get_trak_count (AtomMOOV *moov);
|
||||
|
||||
guint64 atom_mvhd_copy_data (AtomMVHD * atom, guint8 ** buffer,
|
||||
guint64 * size, guint64 * offset);
|
||||
void atom_stco64_chunks_add_offset (AtomSTCO64 * stco64, guint32 offset);
|
||||
void atom_stco64_chunks_set_offset (AtomSTCO64 * stco64, guint32 offset);
|
||||
guint64 atom_trak_copy_data (AtomTRAK * atom, guint8 ** buffer,
|
||||
guint64 * size, guint64 * offset);
|
||||
void atom_stbl_clear (AtomSTBL * stbl);
|
||||
|
@ -974,6 +977,7 @@ AtomInfo * build_uuid_xmp_atom (GstBuffer * xmp);
|
|||
/*
|
||||
* Meta tags functions
|
||||
*/
|
||||
void atom_udta_clear_tags (AtomUDTA *udta);
|
||||
void atom_udta_add_str_tag (AtomUDTA *udta, guint32 fourcc, const gchar *value);
|
||||
void atom_udta_add_uint_tag (AtomUDTA *udta, guint32 fourcc, guint32 flags,
|
||||
guint32 value);
|
||||
|
|
|
@ -972,7 +972,7 @@ moov_recov_write_file (MoovRecovFile * moovrf, MdatRecovFile * mdatrf,
|
|||
TrakRecovData *trak = &(moovrf->traks_rd[i]);
|
||||
/* 16 for the mdat header */
|
||||
gint64 offset = moov_size + ftell (outf) + 16;
|
||||
atom_stco64_chunks_add_offset (&trak->stbl.stco64, offset);
|
||||
atom_stco64_chunks_set_offset (&trak->stbl.stco64, offset);
|
||||
}
|
||||
|
||||
/* write the moov */
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -128,6 +128,9 @@ struct _GstQTPad
|
|||
/* optional fragment index book-keeping */
|
||||
AtomTFRA *tfra;
|
||||
|
||||
/* Set when tags are received, cleared when written to moov */
|
||||
gboolean tags_changed;
|
||||
|
||||
GstTagList *tags;
|
||||
|
||||
/* if nothing is set, it won't be called */
|
||||
|
@ -148,7 +151,8 @@ typedef enum _GstQtMuxMode {
|
|||
GST_QT_MUX_MODE_MOOV_AT_END,
|
||||
GST_QT_MUX_MODE_FRAGMENTED,
|
||||
GST_QT_MUX_MODE_FRAGMENTED_STREAMABLE,
|
||||
GST_QT_MUX_MODE_FAST_START
|
||||
GST_QT_MUX_MODE_FAST_START,
|
||||
GST_QT_MUX_MODE_ROBUST_RECORDING
|
||||
} GstQtMuxMode;
|
||||
|
||||
struct _GstQTMux
|
||||
|
@ -168,14 +172,23 @@ struct _GstQTMux
|
|||
|
||||
/* size of header (prefix, atoms (ftyp, possibly moov, mdat header)) */
|
||||
guint64 header_size;
|
||||
/* accumulated size of raw media data (a priori not including mdat header) */
|
||||
/* accumulated size of raw media data (not including mdat header) */
|
||||
guint64 mdat_size;
|
||||
/* position of mdat atom (for later updating) */
|
||||
/* position of the moov (for fragmented mode) or reserved moov atom
|
||||
* area (for robust-muxing mode) */
|
||||
guint64 moov_pos;
|
||||
/* position of mdat atom header (for later updating of size) in
|
||||
* moov-at-end, fragmented and robust-muxing modes */
|
||||
guint64 mdat_pos;
|
||||
|
||||
/* keep track of the largest chunk to fine-tune brands */
|
||||
GstClockTime longest_chunk;
|
||||
|
||||
/* Earliest timestamp across all pads/traks */
|
||||
GstClockTime first_ts;
|
||||
/* Last DTS across all pads (= duration) */
|
||||
GstClockTime last_dts;
|
||||
|
||||
/* atom helper objects */
|
||||
AtomsContext *context;
|
||||
AtomFTYP *ftyp;
|
||||
|
@ -183,6 +196,10 @@ struct _GstQTMux
|
|||
GSList *extra_atoms; /* list of extra top-level atoms (e.g. UUID for xmp)
|
||||
* Stored as AtomInfo structs */
|
||||
|
||||
/* Set when tags are received, cleared when written to moov */
|
||||
gboolean tags_changed;
|
||||
|
||||
|
||||
/* fragmented file index */
|
||||
AtomMFRA *mfra;
|
||||
|
||||
|
@ -212,6 +229,30 @@ struct _GstQTMux
|
|||
* mode. */
|
||||
gboolean streamable;
|
||||
|
||||
/* Requested target maximum duration */
|
||||
GstClockTime reserved_max_duration;
|
||||
/* Estimate of remaining reserved header space (in ns of recording) */
|
||||
GstClockTime reserved_duration_remaining;
|
||||
/* Multiplier for conversion from reserved_max_duration to bytes */
|
||||
guint reserved_bytes_per_sec_per_trak;
|
||||
|
||||
/* Reserved minimum MOOV size in bytes
|
||||
* This is converted from reserved_max_duration
|
||||
* using the bytes/trak/sec estimate */
|
||||
guint32 reserved_moov_size;
|
||||
/* Basic size of the moov (static headers + tags) */
|
||||
guint32 base_moov_size;
|
||||
/* Size of the most recently generated moov header */
|
||||
guint32 last_moov_size;
|
||||
/* True if the first moov in the ping-pong buffers
|
||||
* is the active one. See gst_qt_mux_robust_recording_rewrite_moov() */
|
||||
gboolean reserved_moov_first_active;
|
||||
|
||||
/* Tracking of periodic MOOV updates */
|
||||
GstClockTime last_moov_update;
|
||||
GstClockTime reserved_moov_update_period;
|
||||
GstClockTime muxed_since_last_update;
|
||||
|
||||
/* for request pad naming */
|
||||
guint video_pads, audio_pads, subtitle_pads;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue