vlc | branch: master | Francois Cartegnie <[email protected]> | Thu Oct 25 22:34:58 2018 +0200| [be35a870d7ae4592e1f47484b03f82da529f2213] | committer: Francois Cartegnie
mux: mp4: rework mux helper for better abstraction > http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=be35a870d7ae4592e1f47484b03f82da529f2213 --- .../demux/smooth/playlist/ForgedInitSegment.cpp | 92 ++--- modules/mux/mp4/libmp4mux.c | 269 ++++++++++----- modules/mux/mp4/libmp4mux.h | 42 ++- modules/mux/mp4/mp4.c | 382 ++++++++++----------- 4 files changed, 446 insertions(+), 339 deletions(-) diff --git a/modules/demux/smooth/playlist/ForgedInitSegment.cpp b/modules/demux/smooth/playlist/ForgedInitSegment.cpp index 631e580ac9..8375e0a243 100644 --- a/modules/demux/smooth/playlist/ForgedInitSegment.cpp +++ b/modules/demux/smooth/playlist/ForgedInitSegment.cpp @@ -210,16 +210,10 @@ void ForgedInitSegment::setLanguage(const std::string &lang) block_t * ForgedInitSegment::buildMoovBox() { const Timescale &trackTimescale = inheritTimescale(); - mp4mux_trackinfo_t trackinfo; - mp4mux_trackinfo_Init(&trackinfo, - 0x01, /* Will always be 1st and unique track; tfhd patched on block read */ - (uint32_t) trackTimescale); - trackinfo.i_read_duration = duration.Get(); - trackinfo.i_trex_default_length = 1; - trackinfo.i_trex_default_size = 1; - - es_format_Init(&trackinfo.fmt, es_type, vlc_fourcc_GetCodec(es_type, fourcc)); - trackinfo.fmt.i_original_fourcc = fourcc; + + es_format_t fmt; + es_format_Init(&fmt, es_type, vlc_fourcc_GetCodec(es_type, fourcc)); + fmt.i_original_fourcc = fourcc; switch(es_type) { case VIDEO_ES: @@ -227,44 +221,44 @@ block_t * ForgedInitSegment::buildMoovBox() fourcc == VLC_FOURCC( 'A', 'V', 'C', 'B' ) || fourcc == VLC_FOURCC( 'H', '2', '6', '4' ) ) { - trackinfo.fmt.i_codec = VLC_CODEC_H264; + fmt.i_codec = VLC_CODEC_H264; } else if( fourcc == VLC_FOURCC( 'W', 'V', 'C', '1' ) ) { - trackinfo.fmt.i_codec = VLC_CODEC_VC1; -// trackinfo.fmt.video.i_bits_per_pixel = 0x18; // No clue why this was set in smooth streamfilter + fmt.i_codec = VLC_CODEC_VC1; +// fmt.video.i_bits_per_pixel = 0x18; // No clue why this was set in smooth streamfilter } - trackinfo.fmt.video.i_width = width; - trackinfo.fmt.video.i_height = height; - trackinfo.fmt.video.i_visible_width = width; - trackinfo.fmt.video.i_visible_height = height; + fmt.video.i_width = width; + fmt.video.i_height = height; + fmt.video.i_visible_width = width; + fmt.video.i_visible_height = height; if(i_extradata && extradata) { - trackinfo.fmt.p_extra = malloc(i_extradata); - if(trackinfo.fmt.p_extra) + fmt.p_extra = malloc(i_extradata); + if(fmt.p_extra) { - memcpy(trackinfo.fmt.p_extra, extradata, i_extradata); - trackinfo.fmt.i_extra = i_extradata; + memcpy(fmt.p_extra, extradata, i_extradata); + fmt.i_extra = i_extradata; } } break; case AUDIO_ES: - trackinfo.fmt.audio.i_channels = formatex.nChannels; - trackinfo.fmt.audio.i_rate = formatex.nSamplesPerSec; - trackinfo.fmt.audio.i_bitspersample = formatex.wBitsPerSample; - trackinfo.fmt.audio.i_blockalign = formatex.nBlockAlign; - trackinfo.fmt.i_bitrate = formatex.nAvgBytesPerSec * 8; // FIXME (use bitrate) ? + fmt.audio.i_channels = formatex.nChannels; + fmt.audio.i_rate = formatex.nSamplesPerSec; + fmt.audio.i_bitspersample = formatex.wBitsPerSample; + fmt.audio.i_blockalign = formatex.nBlockAlign; + fmt.i_bitrate = formatex.nAvgBytesPerSec * 8; // FIXME (use bitrate) ? if(i_extradata && extradata) { - trackinfo.fmt.p_extra = malloc(i_extradata); - if(trackinfo.fmt.p_extra) + fmt.p_extra = malloc(i_extradata); + if(fmt.p_extra) { - memcpy(trackinfo.fmt.p_extra, extradata, i_extradata); - trackinfo.fmt.i_extra = i_extradata; + memcpy(fmt.p_extra, extradata, i_extradata); + fmt.i_extra = i_extradata; } } break; @@ -277,28 +271,36 @@ block_t * ForgedInitSegment::buildMoovBox() } if(!language.empty()) - trackinfo.fmt.psz_language = strdup(language.c_str()); + fmt.psz_language = strdup(language.c_str()); - mp4mux_trackinfo_t *p_tracks = &trackinfo; bo_t *box = NULL; - - if(mp4mux_CanMux( NULL, &trackinfo.fmt, VLC_FOURCC('s', 'm', 'o', 'o'), true )) - box = mp4mux_GetMoovBox(NULL, &p_tracks, 1, - trackTimescale.ToTime(duration.Get()), - true, false, false, false); - - mp4mux_trackinfo_Clear(&trackinfo); - - block_t *moov = NULL; - if(box) + mp4mux_handle_t *muxh = mp4mux_New(FRAGMENTED); + if(muxh) { - moov = box->b; - free(box); + if(mp4mux_CanMux(NULL, &fmt, VLC_FOURCC('s', 'm', 'o', 'o'), true )) + { + mp4mux_trackinfo_t *p_track = mp4mux_track_Add(muxh, + 0x01, /* Will always be 1st and unique track; tfhd patched on block read */ + &fmt, (uint32_t) trackTimescale); + if(p_track) + { + p_track->i_read_duration = duration.Get(); + p_track->i_trex_default_length = 1; + p_track->i_trex_default_size = 1; + } + } + + box = mp4mux_GetMoov(muxh, NULL, trackTimescale.ToTime(duration.Get())); + mp4mux_Delete(muxh); } + es_format_Clean(&fmt); - if(!moov) + if(!box) return NULL; + block_t *moov = box->b; + free(box); + vlc_fourcc_t extra[] = {MAJOR_isom, VLC_FOURCC('p','i','f','f'), VLC_FOURCC('i','s','o','2'), VLC_FOURCC('s','m','o','o')}; box = mp4mux_GetFtyp(VLC_FOURCC('i','s','m','l'), 1, extra, ARRAY_SIZE(extra)); diff --git a/modules/mux/mp4/libmp4mux.c b/modules/mux/mp4/libmp4mux.c index a78f8d8794..7cfea5dde3 100644 --- a/modules/mux/mp4/libmp4mux.c +++ b/modules/mux/mp4/libmp4mux.c @@ -34,36 +34,145 @@ #include <vlc_es.h> #include <vlc_iso_lang.h> #include <vlc_bits.h> +#include <vlc_arrays.h> #include <vlc_text_style.h> #include <assert.h> #include <time.h> -bool mp4mux_trackinfo_Init(mp4mux_trackinfo_t *p_stream, unsigned i_id, - uint32_t i_timescale) +struct mp4mux_handle_t +{ + unsigned options; + vlc_array_t tracks; +}; + +static bool mp4mux_trackinfo_Init(mp4mux_trackinfo_t *p_stream, unsigned i_id, + uint32_t i_timescale) { memset(p_stream, 0, sizeof(*p_stream)); p_stream->i_track_id = i_id; p_stream->i_timescale = i_timescale; - p_stream->i_entry_count = 0; - p_stream->i_entry_max = 1000; - - p_stream->entry = calloc(p_stream->i_entry_max, sizeof(mp4mux_entry_t)); - if(!p_stream->entry) - return false; + es_format_Init(&p_stream->fmt, 0, 0); return true; } -void mp4mux_trackinfo_Clear(mp4mux_trackinfo_t *p_stream) +static void mp4mux_trackinfo_Clear(mp4mux_trackinfo_t *p_stream) { es_format_Clean(&p_stream->fmt); if (p_stream->a52_frame) block_Release(p_stream->a52_frame); - free(p_stream->entry); + free(p_stream->samples); free(p_stream->p_edits); } +mp4mux_trackinfo_t * mp4mux_track_Add(mp4mux_handle_t *h, unsigned id, + const es_format_t *fmt, uint32_t timescale) +{ + mp4mux_trackinfo_t *t = malloc(sizeof(*t)); + if(!mp4mux_trackinfo_Init(t, 0, 0)) + { + free(t); + return NULL; + } + t->i_track_id = id; + t->i_timescale = timescale; + t->i_firstdts = VLC_TICK_INVALID; + es_format_Init(&t->fmt, fmt->i_cat, fmt->i_codec); + es_format_Copy(&t->fmt, fmt); + vlc_array_append(&h->tracks, t); + return t; +} + +bool mp4mux_track_AddEdit(mp4mux_trackinfo_t *t, const mp4mux_edit_t *p_newedit) +{ + mp4mux_edit_t *p_realloc = realloc( t->p_edits, sizeof(mp4mux_edit_t) * + (t->i_edits_count + 1) ); + if(unlikely(!p_realloc)) + return false; + + t->p_edits = p_realloc; + t->p_edits[t->i_edits_count++] = *p_newedit; + + return true; +} + +const mp4mux_edit_t *mp4mux_track_GetLastEdit(const mp4mux_trackinfo_t *t) +{ + if(t->i_edits_count) + return &t->p_edits[t->i_edits_count - 1]; + else return NULL; +} + +void mp4mux_track_DebugEdits(vlc_object_t *obj, const mp4mux_trackinfo_t *t) +{ + for( unsigned i=0; i<t->i_edits_count; i++ ) + { + msg_Dbg(obj, "tk %d elst media time %" PRId64 " duration %" PRIu64 " offset %" PRId64 , + t->i_track_id, + t->p_edits[i].i_start_time, + t->p_edits[i].i_duration, + t->p_edits[i].i_start_offset); + } +} + +bool mp4mux_track_AddSample(mp4mux_trackinfo_t *t, const mp4mux_sample_t *entry) +{ + /* XXX: -1 to always have 2 entry for easy adding of empty SPU */ + if (t->i_samples_count + 2 >= t->i_samples_max) + { + mp4mux_sample_t *p_realloc = + realloc(t->samples, sizeof(*p_realloc) * (t->i_samples_max + 1000)); + if(!p_realloc) + return false; + t->samples = p_realloc; + t->i_samples_max += 1000; + } + t->samples[t->i_samples_count++] = *entry; + return true; +} + +mp4mux_sample_t *mp4mux_track_GetLastSample(mp4mux_trackinfo_t *t) +{ + if(t->i_samples_count) + return &t->samples[t->i_samples_count - 1]; + else return NULL; +} + +mp4mux_handle_t * mp4mux_New(enum mp4mux_options options) +{ + mp4mux_handle_t *h = malloc(sizeof(*h)); + vlc_array_init(&h->tracks); + h->options = options; + return h; +} + +void mp4mux_Delete(mp4mux_handle_t *h) +{ + for(size_t i=0; i<vlc_array_count(&h->tracks); i++) + { + mp4mux_trackinfo_t *t = vlc_array_item_at_index(&h->tracks, i); + mp4mux_trackinfo_Clear(t); + free(t); + } + vlc_array_clear(&h->tracks); + free(h); +} + +void mp4mux_Set64BitExt(mp4mux_handle_t *h) +{ + /* FIXME FIXME + * Quicktime actually doesn't like the 64 bits extensions !!! */ + if(h->options & QUICKTIME) + return; + + h->options |= USE64BITEXT; +} + +bool mp4mux_Is(mp4mux_handle_t *h, enum mp4mux_options o) +{ + return h->options & o; +} bo_t *box_new(const char *fcc) { @@ -235,10 +344,10 @@ static bo_t *GetESDS(mp4mux_trackinfo_t *p_track) int64_t i_bitrate_avg = 0; int64_t i_bitrate_max = 0; /* Compute avg/max bitrate */ - for (unsigned i = 0; i < p_track->i_entry_count; i++) { - i_bitrate_avg += p_track->entry[i].i_size; - if (p_track->entry[i].i_length > 0) { - int64_t i_bitrate = CLOCK_FREQ * 8 * p_track->entry[i].i_size / p_track->entry[i].i_length; + for (unsigned i = 0; i < p_track->i_samples_count; i++) { + i_bitrate_avg += p_track->samples[i].i_size; + if (p_track->samples[i].i_length > 0) { + int64_t i_bitrate = CLOCK_FREQ * 8 * p_track->samples[i].i_size / p_track->samples[i].i_length; if (i_bitrate > i_bitrate_max) i_bitrate_max = i_bitrate; } @@ -828,15 +937,15 @@ static bo_t *GetSVQ3Tag(es_format_t *p_fmt) return smi; } -static bo_t *GetUdtaTag(mp4mux_trackinfo_t **pp_tracks, unsigned int i_tracks) +static bo_t *GetUdtaTag(mp4mux_handle_t *muxh) { bo_t *udta = box_new("udta"); if (!udta) return NULL; /* Requirements */ - for (unsigned int i = 0; i < i_tracks; i++) { - mp4mux_trackinfo_t *p_stream = pp_tracks[i]; + for (unsigned int i = 0; i < vlc_array_count(&muxh->tracks); i++) { + mp4mux_trackinfo_t *p_stream = vlc_array_item_at_index(&muxh->tracks, i); vlc_fourcc_t codec = p_stream->fmt.i_codec; if (codec == VLC_CODEC_MP4V || codec == VLC_CODEC_MP4A) { @@ -1219,7 +1328,7 @@ static bo_t *GetTextBox(vlc_object_t *p_obj, mp4mux_trackinfo_t *p_track, bool b return NULL; } -static int64_t GetScaledEntryDuration( const mp4mux_entry_t *p_entry, uint32_t i_timescale, +static int64_t GetScaledEntryDuration( const mp4mux_sample_t *p_entry, uint32_t i_timescale, vlc_tick_t *pi_total_mtime, int64_t *pi_total_scaled ) { const vlc_tick_t i_totalscaledtototalmtime = vlc_tick_from_samples(*pi_total_scaled, i_timescale); @@ -1276,8 +1385,8 @@ static bo_t *GetStblBox(vlc_object_t *p_obj, mp4mux_trackinfo_t *p_track, bool b unsigned i_chunk = 0; unsigned i_stsc_last_val = 0, i_stsc_entries = 0; - for (unsigned i = 0; i < p_track->i_entry_count; i_chunk++) { - mp4mux_entry_t *entry = p_track->entry; + for (unsigned i = 0; i < p_track->i_samples_count; i_chunk++) { + mp4mux_sample_t *entry = p_track->samples; int i_first = i; if (b_stco64) @@ -1285,8 +1394,8 @@ static bo_t *GetStblBox(vlc_object_t *p_obj, mp4mux_trackinfo_t *p_track, bool b else bo_add_32be(stco, entry[i].i_pos); - for (; i < p_track->i_entry_count; i++) - if (i >= p_track->i_entry_count - 1 || + for (; i < p_track->i_samples_count; i++) + if (i >= p_track->i_samples_count - 1 || entry[i].i_pos + entry[i].i_size != entry[i+1].i_pos) { i++; break; @@ -1324,16 +1433,16 @@ static bo_t *GetStblBox(vlc_object_t *p_obj, mp4mux_trackinfo_t *p_track, bool b vlc_tick_t i_total_mtime = 0; int64_t i_total_scaled = 0; unsigned i_index = 0; - for (unsigned i = 0; i < p_track->i_entry_count; i_index++) { + for (unsigned i = 0; i < p_track->i_samples_count; i_index++) { int i_first = i; - int64_t i_scaled = GetScaledEntryDuration(&p_track->entry[i], p_track->i_timescale, + int64_t i_scaled = GetScaledEntryDuration(&p_track->samples[i], p_track->i_timescale, &i_total_mtime, &i_total_scaled); - for (unsigned j=i+1; j < p_track->i_entry_count; j++) + for (unsigned j=i+1; j < p_track->i_samples_count; j++) { vlc_tick_t i_total_mtime_next = i_total_mtime; int64_t i_total_scaled_next = i_total_scaled; - int64_t i_scalednext = GetScaledEntryDuration(&p_track->entry[j], p_track->i_timescale, + int64_t i_scalednext = GetScaledEntryDuration(&p_track->samples[j], p_track->i_timescale, &i_total_mtime_next, &i_total_scaled_next); if( i_scalednext != i_scaled ) break; @@ -1357,13 +1466,13 @@ static bo_t *GetStblBox(vlc_object_t *p_obj, mp4mux_trackinfo_t *p_track, bool b { bo_add_32be(ctts, 0); i_index = 0; - for (unsigned i = 0; i < p_track->i_entry_count; i_index++) + for (unsigned i = 0; i < p_track->i_samples_count; i_index++) { int i_first = i; - vlc_tick_t i_offset = p_track->entry[i].i_pts_dts; + vlc_tick_t i_offset = p_track->samples[i].i_pts_dts; - for (; i < p_track->i_entry_count; ++i) - if (i == p_track->i_entry_count || p_track->entry[i].i_pts_dts != i_offset) + for (; i < p_track->i_samples_count; ++i) + if (i == p_track->i_samples_count || p_track->samples[i].i_pts_dts != i_offset) break; bo_add_32be(ctts, i - i_first); // sample-count @@ -1381,22 +1490,22 @@ static bo_t *GetStblBox(vlc_object_t *p_obj, mp4mux_trackinfo_t *p_track, bool b return NULL; } int i_size = 0; - for (unsigned i = 0; i < p_track->i_entry_count; i++) + for (unsigned i = 0; i < p_track->i_samples_count; i++) { if ( i == 0 ) - i_size = p_track->entry[i].i_size; - else if ( p_track->entry[i].i_size != i_size ) + i_size = p_track->samples[i].i_size; + else if ( p_track->samples[i].i_size != i_size ) { i_size = 0; break; } } bo_add_32be(stsz, i_size); // sample-size - bo_add_32be(stsz, p_track->i_entry_count); // sample-count + bo_add_32be(stsz, p_track->i_samples_count); // sample-count if ( i_size == 0 ) // all samples have different size { - for (unsigned i = 0; i < p_track->i_entry_count; i++) - bo_add_32be(stsz, p_track->entry[i].i_size); // sample-size + for (unsigned i = 0; i < p_track->i_samples_count; i++) + bo_add_32be(stsz, p_track->samples[i].i_size); // sample-size } /* create stss table */ @@ -1405,16 +1514,16 @@ static bo_t *GetStblBox(vlc_object_t *p_obj, mp4mux_trackinfo_t *p_track, bool b if ( p_track->fmt.i_cat == VIDEO_ES || p_track->fmt.i_cat == AUDIO_ES ) { vlc_tick_t i_interval = -1; - for (unsigned i = 0; i < p_track->i_entry_count; i++) + for (unsigned i = 0; i < p_track->i_samples_count; i++) { if ( i_interval != -1 ) { - i_interval += p_track->entry[i].i_length + p_track->entry[i].i_pts_dts; + i_interval += p_track->samples[i].i_length + p_track->samples[i].i_pts_dts; if ( i_interval < VLC_TICK_FROM_SEC(2) ) continue; } - if (p_track->entry[i].i_flags & BLOCK_FLAG_TYPE_I) { + if (p_track->samples[i].i_flags & BLOCK_FLAG_TYPE_I) { if (stss == NULL) { stss = box_full_new("stss", 0, 0); if(!stss) @@ -1457,9 +1566,7 @@ static bo_t *GetStblBox(vlc_object_t *p_obj, mp4mux_trackinfo_t *p_track, bool b return stbl; } -bo_t * mp4mux_GetMoovBox(vlc_object_t *p_obj, mp4mux_trackinfo_t **pp_tracks, unsigned int i_tracks, - vlc_tick_t i_duration, - bool b_fragmented, bool b_mov, bool b_64_ext, bool b_stco64 ) +bo_t * mp4mux_GetMoov(mp4mux_handle_t *h, vlc_object_t *p_obj, vlc_tick_t i_duration) { bo_t *moov, *mvhd; @@ -1468,17 +1575,17 @@ bo_t * mp4mux_GetMoovBox(vlc_object_t *p_obj, mp4mux_trackinfo_t **pp_tracks, un /* Important for smooth streaming where its (not muxed here) media time offsets * are in timescale == track timescale */ - if( i_tracks == 1 ) - i_movie_timescale = pp_tracks[0]->i_timescale; + if( vlc_array_count(&h->tracks) == 1 ) + i_movie_timescale = ((mp4mux_trackinfo_t *)vlc_array_item_at_index(&h->tracks, 0))->i_timescale; moov = box_new("moov"); if(!moov) return NULL; /* Create general info */ - if( i_duration == 0 && !b_fragmented ) + if( i_duration == 0 && (h->options & FRAGMENTED) == 0 ) { - for (unsigned int i = 0; i < i_tracks; i++) { - mp4mux_trackinfo_t *p_stream = pp_tracks[i]; + for (unsigned int i = 0; i < vlc_array_count(&h->tracks); i++) { + mp4mux_trackinfo_t *p_stream = vlc_array_item_at_index(&h->tracks, 0); i_duration = __MAX(i_duration, p_stream->i_read_duration); } if(p_obj) @@ -1487,7 +1594,7 @@ bo_t * mp4mux_GetMoovBox(vlc_object_t *p_obj, mp4mux_trackinfo_t **pp_tracks, un int64_t i_movie_duration = samples_from_vlc_tick(i_duration, i_movie_timescale); /* *** add /moov/mvhd *** */ - if (!b_64_ext) { + if ((h->options & USE64BITEXT) == 0) { mvhd = box_full_new("mvhd", 0, 0); if(!mvhd) { @@ -1524,15 +1631,18 @@ bo_t * mp4mux_GetMoovBox(vlc_object_t *p_obj, mp4mux_trackinfo_t **pp_tracks, un bo_add_32be(mvhd, 0); // pre-defined /* Next available track id */ - bo_add_32be(mvhd, (i_tracks) ? pp_tracks[i_tracks -1]->i_track_id + 1: 1); // next-track-id + const mp4mux_trackinfo_t *lasttrack = vlc_array_count(&h->tracks) + ? vlc_array_item_at_index(&h->tracks, vlc_array_count(&h->tracks) - 1) + : NULL; + bo_add_32be(mvhd, lasttrack ? lasttrack->i_track_id + 1: 1); // next-track-id box_gather(moov, mvhd); - for (unsigned int i_trak = 0; i_trak < i_tracks; i_trak++) { - mp4mux_trackinfo_t *p_stream = pp_tracks[i_trak]; + for (unsigned int i_trak = 0; i_trak < vlc_array_count(&h->tracks); i_trak++) { + mp4mux_trackinfo_t *p_stream = vlc_array_item_at_index(&h->tracks, i_trak); int64_t i_stream_duration; - if ( !b_fragmented ) + if ( (h->options & FRAGMENTED) == 0 ) i_stream_duration = samples_from_vlc_tick(p_stream->i_read_duration, i_movie_timescale); else i_stream_duration = 0; @@ -1544,8 +1654,8 @@ bo_t * mp4mux_GetMoovBox(vlc_object_t *p_obj, mp4mux_trackinfo_t **pp_tracks, un /* *** add /moov/trak/tkhd *** */ bo_t *tkhd; - if (!b_64_ext) { - if (b_mov) + if ((h->options & USE64BITEXT) == 0) { + if (h->options & QUICKTIME) tkhd = box_full_new("tkhd", 0, 0x0f); else tkhd = box_full_new("tkhd", 0, 1); @@ -1560,7 +1670,7 @@ bo_t * mp4mux_GetMoovBox(vlc_object_t *p_obj, mp4mux_trackinfo_t **pp_tracks, un bo_add_32be(tkhd, 0); // reserved 0 bo_add_32be(tkhd, i_stream_duration); // duration } else { - if (b_mov) + if (h->options & QUICKTIME) tkhd = box_full_new("tkhd", 1, 0x0f); else tkhd = box_full_new("tkhd", 1, 1); @@ -1603,8 +1713,8 @@ bo_t * mp4mux_GetMoovBox(vlc_object_t *p_obj, mp4mux_trackinfo_t **pp_tracks, un } else { int i_width = 320 << 16; int i_height = 200; - for (unsigned int i = 0; i < i_tracks; i++) { - mp4mux_trackinfo_t *tk = pp_tracks[i]; + for (unsigned int i = 0; i < vlc_array_count(&h->tracks); i++) { + const mp4mux_trackinfo_t *tk = vlc_array_item_at_index(&h->tracks, i); if (tk->fmt.i_cat == VIDEO_ES) { if (tk->fmt.video.i_sar_num > 0 && tk->fmt.video.i_sar_den > 0) @@ -1624,7 +1734,7 @@ bo_t * mp4mux_GetMoovBox(vlc_object_t *p_obj, mp4mux_trackinfo_t **pp_tracks, un box_gather(trak, tkhd); /* *** add /moov/trak/edts and elst */ - bo_t *edts = GetEDTS(p_stream, i_movie_timescale, b_64_ext); + bo_t *edts = GetEDTS(p_stream, i_movie_timescale, h->options & USE64BITEXT); if(edts) box_gather(trak, edts); @@ -1638,7 +1748,7 @@ bo_t * mp4mux_GetMoovBox(vlc_object_t *p_obj, mp4mux_trackinfo_t **pp_tracks, un /* media header */ bo_t *mdhd; - if (!b_64_ext) { + if ((h->options & USE64BITEXT) == 0) { mdhd = box_full_new("mdhd", 0, 0); if(!mdhd) { @@ -1696,7 +1806,7 @@ bo_t * mp4mux_GetMoovBox(vlc_object_t *p_obj, mp4mux_trackinfo_t **pp_tracks, un continue; } - if (b_mov) + if (h->options & QUICKTIME) bo_add_fourcc(hdlr, "mhlr"); // media handler else bo_add_32be(hdlr, 0); @@ -1711,7 +1821,7 @@ bo_t * mp4mux_GetMoovBox(vlc_object_t *p_obj, mp4mux_trackinfo_t **pp_tracks, un /* sbtl/tx3g Apple subs */ /* text/text Apple textmedia */ if(p_stream->fmt.i_codec == VLC_CODEC_TX3G) - bo_add_fourcc(hdlr, (b_mov) ? "sbtl" : "text"); + bo_add_fourcc(hdlr, (h->options & QUICKTIME) ? "sbtl" : "text"); else if(p_stream->fmt.i_codec == VLC_CODEC_TTML) bo_add_fourcc(hdlr, "sbtl"); else @@ -1722,7 +1832,7 @@ bo_t * mp4mux_GetMoovBox(vlc_object_t *p_obj, mp4mux_trackinfo_t **pp_tracks, un bo_add_32be(hdlr, 0); // reserved bo_add_32be(hdlr, 0); // reserved - if (b_mov) + if (h->options & QUICKTIME) bo_add_8(hdlr, 12); /* Pascal string for .mov */ if (p_stream->fmt.i_cat == AUDIO_ES) @@ -1732,7 +1842,7 @@ bo_t * mp4mux_GetMoovBox(vlc_object_t *p_obj, mp4mux_trackinfo_t **pp_tracks, un else bo_add_mem(hdlr, 12, (uint8_t*)"Text Handler"); - if (!b_mov) + if ((h->options & QUICKTIME) == 0) bo_add_8(hdlr, 0); /* asciiz string for .mp4, yes that's BRAIN DAMAGED F**K MP4 */ box_gather(mdia, hdlr); @@ -1766,7 +1876,7 @@ bo_t * mp4mux_GetMoovBox(vlc_object_t *p_obj, mp4mux_trackinfo_t **pp_tracks, un box_gather(minf, vmhd); } } else if (p_stream->fmt.i_cat == SPU_ES) { - if(b_mov && + if((h->options & QUICKTIME) && (p_stream->fmt.i_codec == VLC_CODEC_SUBT|| p_stream->fmt.i_codec == VLC_CODEC_TX3G|| p_stream->fmt.i_codec == VLC_CODEC_QTXT)) @@ -1814,15 +1924,15 @@ bo_t * mp4mux_GetMoovBox(vlc_object_t *p_obj, mp4mux_trackinfo_t **pp_tracks, un /* add stbl */ bo_t *stbl; - if ( b_fragmented ) + if (h->options & FRAGMENTED) { - uint32_t i_backup = p_stream->i_entry_count; - p_stream->i_entry_count = 0; - stbl = GetStblBox(p_obj, p_stream, b_mov, b_stco64); - p_stream->i_entry_count = i_backup; + uint32_t i_backup = p_stream->i_samples_count; + p_stream->i_samples_count = 0; + stbl = GetStblBox(p_obj, p_stream, h->options & QUICKTIME, h->options & USE64BITEXT); + p_stream->i_samples_count = i_backup; } else - stbl = GetStblBox(p_obj, p_stream, b_mov, b_stco64); + stbl = GetStblBox(p_obj, p_stream, h->options & QUICKTIME, h->options & USE64BITEXT); /* append stbl to minf */ p_stream->i_stco_pos += bo_size(minf); @@ -1842,35 +1952,36 @@ bo_t * mp4mux_GetMoovBox(vlc_object_t *p_obj, mp4mux_trackinfo_t **pp_tracks, un } /* Add user data tags */ - box_gather(moov, GetUdtaTag(pp_tracks, i_tracks)); + box_gather(moov, GetUdtaTag(h)); - if ( b_fragmented ) + if ( h->options & FRAGMENTED ) { bo_t *mvex = box_new("mvex"); if( mvex ) { if( i_movie_duration ) { - bo_t *mehd = box_full_new("mehd", b_64_ext ? 1 : 0, 0); + bo_t *mehd = box_full_new("mehd", (h->options & USE64BITEXT) ? 1 : 0, 0); if(mehd) { - if(b_64_ext) + if((h->options & USE64BITEXT)) bo_add_64be(mehd, i_movie_duration); else bo_add_32be(mehd, i_movie_duration); box_gather(mvex, mehd); } } - for (unsigned int i_trak = 0; mvex && i_trak < i_tracks; i_trak++) + + for (unsigned int i = 0; mvex && i < vlc_array_count(&h->tracks); i++) { - mp4mux_trackinfo_t *p_stream = pp_tracks[i_trak]; + mp4mux_trackinfo_t *p_stream = vlc_array_item_at_index(&h->tracks, i); /* Try to find some defaults */ - if ( p_stream->i_entry_count ) + if ( p_stream->i_samples_count ) { // FIXME: find highest occurence - p_stream->i_trex_default_length = p_stream->entry[0].i_length; - p_stream->i_trex_default_size = p_stream->entry[0].i_size; + p_stream->i_trex_default_length = p_stream->samples[0].i_length; + p_stream->i_trex_default_size = p_stream->samples[0].i_size; } /* *** add /mvex/trex *** */ diff --git a/modules/mux/mp4/libmp4mux.h b/modules/mux/mp4/libmp4mux.h index 0a5d0ab452..6a45bc3fa2 100644 --- a/modules/mux/mp4/libmp4mux.h +++ b/modules/mux/mp4/libmp4mux.h @@ -24,6 +24,8 @@ #include <vlc_es.h> #include <vlc_boxes.h> +typedef struct mp4mux_handle_t mp4mux_handle_t; + typedef struct { uint64_t i_pos; @@ -32,7 +34,7 @@ typedef struct vlc_tick_t i_pts_dts; vlc_tick_t i_length; unsigned int i_flags; -} mp4mux_entry_t; +} mp4mux_sample_t; typedef struct { @@ -47,9 +49,9 @@ typedef struct es_format_t fmt; /* index */ - unsigned int i_entry_count; - unsigned int i_entry_max; - mp4mux_entry_t *entry; + unsigned int i_samples_count; + unsigned int i_samples_max; + mp4mux_sample_t *samples; /* XXX: needed for other codecs too, see lavf */ block_t *a52_frame; @@ -74,8 +76,32 @@ typedef struct } mp4mux_trackinfo_t; -bool mp4mux_trackinfo_Init( mp4mux_trackinfo_t *, unsigned, uint32_t ); -void mp4mux_trackinfo_Clear( mp4mux_trackinfo_t * ); +enum mp4mux_options +{ + FRAGMENTED = 1 << 0, + QUICKTIME = 1 << 1, + USE64BITEXT = 1 << 2, +}; + +mp4mux_handle_t * mp4mux_New(enum mp4mux_options); +void mp4mux_Delete(mp4mux_handle_t *); +void mp4mux_Set64BitExt(mp4mux_handle_t *); +bool mp4mux_Is(mp4mux_handle_t *, enum mp4mux_options); + +mp4mux_trackinfo_t * mp4mux_track_Add(mp4mux_handle_t *, unsigned id, + const es_format_t *fmt, uint32_t timescale); +/* ELST */ +bool mp4mux_track_AddEdit(mp4mux_trackinfo_t *, const mp4mux_edit_t *); +const mp4mux_edit_t *mp4mux_track_GetLastEdit(const mp4mux_trackinfo_t *); +void mp4mux_track_DebugEdits(vlc_object_t *, const mp4mux_trackinfo_t *); +/* Samples */ +bool mp4mux_track_AddSample(mp4mux_trackinfo_t *, const mp4mux_sample_t *); +mp4mux_sample_t *mp4mux_track_GetLastSample(mp4mux_trackinfo_t *); + + +bo_t *mp4mux_GetMoov(mp4mux_handle_t *, vlc_object_t *, vlc_tick_t i_movie_duration); + +/* old */ bo_t *box_new (const char *fcc); bo_t *box_full_new(const char *fcc, uint8_t v, uint32_t f); @@ -84,6 +110,4 @@ void box_gather (bo_t *box, bo_t *box2); bool mp4mux_CanMux(vlc_object_t *, const es_format_t *, vlc_fourcc_t, bool); bo_t *mp4mux_GetFtyp(vlc_fourcc_t, uint32_t, vlc_fourcc_t[], size_t i_fourcc); -bo_t *mp4mux_GetMoovBox(vlc_object_t *, mp4mux_trackinfo_t **pp_tracks, unsigned int i_tracks, - vlc_tick_t i_movie_duration, - bool b_fragmented, bool b_mov, bool b_64ext, bool b_stco64); + diff --git a/modules/mux/mp4/mp4.c b/modules/mux/mp4/mp4.c index 7e31a6fb1e..5799245486 100644 --- a/modules/mux/mp4/mp4.c +++ b/modules/mux/mp4/mp4.c @@ -127,7 +127,7 @@ typedef struct mp4_fragqueue_t typedef struct { - mp4mux_trackinfo_t mux; + mp4mux_trackinfo_t *tinfo; /* index */ vlc_tick_t i_length_neg; @@ -153,9 +153,8 @@ typedef struct typedef struct { - bool b_mov; + mp4mux_handle_t *muxh; bool b_3gp; - bool b_64_ext; bool b_fast_start; /* global */ @@ -171,17 +170,14 @@ typedef struct /* mp4frag */ - bool b_fragmented; vlc_tick_t i_written_duration; uint32_t i_mfhd_sequence; } sout_mux_sys_t; static void box_send(sout_mux_t *p_mux, bo_t *box); -static bo_t *BuildMoov(sout_mux_t *p_mux); static block_t *ConvertSUBT(block_t *); static bool CreateCurrentEdit(mp4_stream_t *, vlc_tick_t, bool); -static void DebugEdits(sout_mux_t *, const mp4_stream_t *); static int MuxStream(sout_mux_t *p_mux, sout_input_t *p_input, mp4_stream_t *p_stream); static int WriteSlowStartHeader(sout_mux_t *p_mux) @@ -189,7 +185,7 @@ static int WriteSlowStartHeader(sout_mux_t *p_mux) sout_mux_sys_t *p_sys = p_mux->p_sys; bo_t *box; - if (!p_sys->b_mov) { + if (!mp4mux_Is(p_sys->muxh, QUICKTIME)) { /* Now add ftyp header */ if(p_sys->b_3gp) { @@ -238,13 +234,18 @@ static int Open(vlc_object_t *p_this) msg_Dbg(p_mux, "Mp4 muxer opened"); config_ChainParse(p_mux, SOUT_CFG_PREFIX, ppsz_sout_options, p_mux->p_cfg); - p_sys->b_mov = p_mux->psz_mux && !strcmp(p_mux->psz_mux, "mov"); - p_sys->b_3gp = p_mux->psz_mux && !strcmp(p_mux->psz_mux, "3gp"); - p_sys->b_fragmented = p_mux->psz_mux && (!strcmp(p_mux->psz_mux, "mp4frag") || - !strcmp(p_mux->psz_mux, "mp4stream")); - /* FIXME FIXME - * Quicktime actually doesn't like the 64 bits extensions !!! */ - p_sys->b_64_ext = false; + enum mp4mux_options options = 0; + if(p_mux->psz_mux) + { + if(!strcmp(p_mux->psz_mux, "mov")) + options |= QUICKTIME; + if(!strcmp(p_mux->psz_mux, "mp4frag") || !strcmp(p_mux->psz_mux, "mp4stream")) + options |= FRAGMENTED; + } + + p_sys->b_3gp = p_mux->psz_mux && !strcmp(p_mux->psz_mux, "3gp"); + + p_sys->muxh = mp4mux_New(options); p_sys->i_pos = 0; p_sys->i_nb_streams = 0; @@ -261,7 +262,7 @@ static int Open(vlc_object_t *p_this) p_mux->pf_control = Control; p_mux->pf_addstream = AddStream; p_mux->pf_delstream = DelStream; - p_mux->pf_mux = p_sys->b_fragmented ? MuxFrag : Mux; + p_mux->pf_mux = (options & FRAGMENTED) ? MuxFrag : Mux; return VLC_SUCCESS; } @@ -296,9 +297,11 @@ static void Close(vlc_object_t *p_this) sout_AccessOutWrite(p_mux->p_access, bo.b); /* Create MOOV header */ - const bool b_stco64 = (p_sys->i_pos >= (((uint64_t)0x1) << 32)); + const bool b_64bitext = (p_sys->i_pos >= (((uint64_t)0x1) << 32)); + if(b_64bitext) + mp4mux_Set64BitExt(p_sys->muxh); uint64_t i_moov_pos = p_sys->i_pos; - bo_t *moov = BuildMoov(p_mux); + bo_t *moov = mp4mux_GetMoov(p_sys->muxh, VLC_OBJECT(p_mux), 0); /* Check we need to create "fast start" files */ p_sys->b_fast_start = var_GetBool(p_this, SOUT_CFG_PREFIX "faststart"); @@ -337,15 +340,15 @@ static void Close(vlc_object_t *p_this) for (unsigned int i_trak = 0; i_trak < p_sys->i_nb_streams; i_trak++) { mp4_stream_t *p_stream = p_sys->pp_streams[i_trak]; unsigned i_written = 0; - for (unsigned i = 0; i < p_stream->mux.i_entry_count; ) { - mp4mux_entry_t *entry = p_stream->mux.entry; - if (b_stco64) - bo_set_64be(moov, p_stream->mux.i_stco_pos + i_written++ * 8, entry[i].i_pos + p_sys->i_mdat_pos - i_moov_pos); + for (unsigned i = 0; i < p_stream->tinfo->i_samples_count; ) { + mp4mux_sample_t *entry = p_stream->tinfo->samples; + if (b_64bitext) + bo_set_64be(moov, p_stream->tinfo->i_stco_pos + i_written++ * 8, entry[i].i_pos + p_sys->i_mdat_pos - i_moov_pos); else - bo_set_32be(moov, p_stream->mux.i_stco_pos + i_written++ * 4, entry[i].i_pos + p_sys->i_mdat_pos - i_moov_pos); + bo_set_32be(moov, p_stream->tinfo->i_stco_pos + i_written++ * 4, entry[i].i_pos + p_sys->i_mdat_pos - i_moov_pos); - for (; i < p_stream->mux.i_entry_count; i++) - if (i >= p_stream->mux.i_entry_count - 1 || + for (; i < p_stream->tinfo->i_samples_count; i++) + if (i >= p_stream->tinfo->i_samples_count - 1 || entry[i].i_pos + entry[i].i_size != entry[i+1].i_pos) { i++; break; @@ -363,12 +366,10 @@ static void Close(vlc_object_t *p_this) cleanup: /* Clean-up */ - for (unsigned int i_trak = 0; i_trak < p_sys->i_nb_streams; i_trak++) { - mp4_stream_t *p_stream = p_sys->pp_streams[i_trak]; - mp4mux_trackinfo_Clear(&p_stream->mux); - free(p_stream); - } + for (unsigned int i_trak = 0; i_trak < p_sys->i_nb_streams; i_trak++) + free(p_sys->pp_streams[i_trak]); TAB_CLEAN(p_sys->i_nb_streams, p_sys->pp_streams); + mp4mux_Delete(p_sys->muxh); free(p_sys); } @@ -407,8 +408,8 @@ static int AddStream(sout_mux_t *p_mux, sout_input_t *p_input) mp4_stream_t *p_stream; if(!mp4mux_CanMux(VLC_OBJECT(p_mux), p_input->p_fmt, - p_sys->b_mov ? MAJOR_qt__ : MAJOR_isom, - p_sys->b_fragmented)) + mp4mux_Is(p_sys->muxh, QUICKTIME) ? MAJOR_qt__ : MAJOR_isom, + mp4mux_Is(p_sys->muxh, FRAGMENTED))) { msg_Err(p_mux, "unsupported codec %4.4s in mp4", (char*)&p_input->p_fmt->i_codec); @@ -416,52 +417,57 @@ static int AddStream(sout_mux_t *p_mux, sout_input_t *p_input) } p_stream = malloc(sizeof(mp4_stream_t)); - if (!p_stream || - !mp4mux_trackinfo_Init(&p_stream->mux, p_sys->i_nb_streams + 1, CLOCK_FREQ)) - { - free(p_stream); + if(!p_stream) return VLC_ENOMEM; - } - es_format_Copy(&p_stream->mux.fmt, p_input->p_fmt); - p_stream->i_length_neg = 0; - p_stream->i_first_dts = VLC_TICK_INVALID; - switch( p_stream->mux.fmt.i_cat ) + uint32_t i_track_timescale = CLOCK_FREQ; + es_format_t trackfmt; + es_format_Init(&trackfmt, p_input->p_fmt->i_cat, p_input->p_fmt->i_codec); + es_format_Copy(&trackfmt, p_input->p_fmt); + + switch( p_input->p_fmt->i_cat ) { case AUDIO_ES: - if(!p_stream->mux.fmt.audio.i_rate) + if(!trackfmt.audio.i_rate) { msg_Warn( p_mux, "no audio rate given for stream %d, assuming 48KHz", p_sys->i_nb_streams ); - p_stream->mux.fmt.audio.i_rate = 48000; + trackfmt.audio.i_rate = 48000; } - p_stream->mux.i_timescale = p_stream->mux.fmt.audio.i_rate; + i_track_timescale = trackfmt.audio.i_rate; break; case VIDEO_ES: - if( !p_stream->mux.fmt.video.i_frame_rate || - !p_stream->mux.fmt.video.i_frame_rate_base ) + if( !trackfmt.video.i_frame_rate || + !trackfmt.video.i_frame_rate_base ) { msg_Warn( p_mux, "Missing frame rate for stream %d, assuming 25fps", p_sys->i_nb_streams ); - p_stream->mux.fmt.video.i_frame_rate = 25; - p_stream->mux.fmt.video.i_frame_rate_base = 1; + trackfmt.video.i_frame_rate = 25; + trackfmt.video.i_frame_rate_base = 1; } - p_stream->mux.i_timescale = p_stream->mux.fmt.video.i_frame_rate * - p_stream->mux.fmt.video.i_frame_rate_base; + i_track_timescale = trackfmt.video.i_frame_rate * + trackfmt.video.i_frame_rate_base; - if( p_stream->mux.i_timescale > CLOCK_FREQ ) - p_stream->mux.i_timescale = CLOCK_FREQ; - else if( p_stream->mux.i_timescale < 90000 ) - p_stream->mux.i_timescale = 90000; + if( i_track_timescale > CLOCK_FREQ ) + i_track_timescale = CLOCK_FREQ; + else if( i_track_timescale < 90000 ) + i_track_timescale = 90000; break; default: break; } - p_stream->mux.p_edits = NULL; - p_stream->mux.i_edits_count = 0; - p_stream->mux.i_firstdts = VLC_TICK_INVALID; + p_stream->tinfo = mp4mux_track_Add(p_sys->muxh, p_sys->i_nb_streams + 1, + &trackfmt, i_track_timescale); + es_format_Clean(&trackfmt); + if(!p_stream->tinfo) + { + free(p_stream); + return VLC_ENOMEM; + } + + p_stream->i_length_neg = 0; p_stream->i_last_dts = VLC_TICK_INVALID; p_stream->i_last_pts = VLC_TICK_INVALID; @@ -495,13 +501,13 @@ static void DelStream(sout_mux_t *p_mux, sout_input_t *p_input) sout_mux_sys_t *p_sys = p_mux->p_sys; mp4_stream_t *p_stream = (mp4_stream_t*)p_input->p_sys; - if(!p_sys->b_fragmented) + if(!mp4mux_Is(p_sys->muxh, FRAGMENTED)) { while(block_FifoCount(p_input->p_fifo) > 0 && MuxStream(p_mux, p_input, p_stream) == VLC_SUCCESS) {}; if(CreateCurrentEdit(p_stream, p_sys->i_start_dts, false)) - DebugEdits(p_mux, p_stream); + mp4mux_track_DebugEdits(VLC_OBJECT(p_mux), p_stream->tinfo); } msg_Dbg(p_mux, "removing input"); @@ -510,64 +516,46 @@ static void DelStream(sout_mux_t *p_mux, sout_input_t *p_input) /***************************************************************************** * Mux: *****************************************************************************/ -static void DebugEdits(sout_mux_t *p_mux, const mp4_stream_t *p_stream) -{ - for( unsigned i=0; i<p_stream->mux.i_edits_count; i++ ) - { - msg_Dbg(p_mux, "tk %d elst media time %" PRId64 " duration %" PRIu64 " offset %" PRId64 , - p_stream->mux.i_track_id, - p_stream->mux.p_edits[i].i_start_time, - p_stream->mux.p_edits[i].i_duration, - p_stream->mux.p_edits[i].i_start_offset); - } -} - static bool CreateCurrentEdit(mp4_stream_t *p_stream, vlc_tick_t i_mux_start_dts, bool b_fragmented) { + const mp4mux_edit_t *p_lastedit = mp4mux_track_GetLastEdit(p_stream->tinfo); + /* Never more than first empty edit for fragmented */ - if(p_stream->mux.i_edits_count && b_fragmented) + if(p_lastedit != NULL && b_fragmented) return true; - if(p_stream->mux.i_entry_count == 0) + if(p_stream->tinfo->i_samples_count == 0) return true; - mp4mux_edit_t *p_realloc = realloc( p_stream->mux.p_edits, sizeof(mp4mux_edit_t) * - (p_stream->mux.i_edits_count + 1) ); - if(unlikely(!p_realloc)) - return false; + mp4mux_edit_t newedit; - mp4mux_edit_t *p_newedit = &p_realloc[p_stream->mux.i_edits_count]; - if(p_stream->mux.i_edits_count == 0) + if(p_lastedit == NULL) { - p_newedit->i_start_time = 0; - p_newedit->i_start_offset = __MAX(0, p_stream->i_first_dts - i_mux_start_dts); + newedit.i_start_time = 0; + newedit.i_start_offset = __MAX(0, p_stream->i_first_dts - i_mux_start_dts); } else { - const mp4mux_edit_t *p_lastedit = &p_realloc[p_stream->mux.i_edits_count - 1]; - p_newedit->i_start_time = __MAX(0, p_lastedit->i_start_time + p_lastedit->i_duration); - p_newedit->i_start_offset = 0; + newedit.i_start_time = __MAX(0, p_lastedit->i_start_time + p_lastedit->i_duration); + newedit.i_start_offset = 0; } if(b_fragmented) { - p_newedit->i_duration = 0; + newedit.i_duration = 0; } else { if(p_stream->i_last_pts != VLC_TICK_INVALID) - p_newedit->i_duration = p_stream->i_last_pts - p_stream->i_first_dts; + newedit.i_duration = p_stream->i_last_pts - p_stream->i_first_dts; else - p_newedit->i_duration = p_stream->i_last_dts - p_stream->i_first_dts; - if(p_stream->mux.i_entry_count) - p_newedit->i_duration += p_stream->mux.entry[p_stream->mux.i_entry_count - 1].i_length; + newedit.i_duration = p_stream->i_last_dts - p_stream->i_first_dts; + if(p_stream->tinfo->i_samples_count) + newedit.i_duration += p_stream->tinfo->samples[p_stream->tinfo->i_samples_count - 1].i_length; } - p_stream->mux.p_edits = p_realloc; - p_stream->mux.i_edits_count++; - - return true; + return mp4mux_track_AddEdit(p_stream->tinfo, &newedit); } static block_t * BlockDequeue(sout_input_t *p_input, mp4_stream_t *p_stream) @@ -576,7 +564,7 @@ static block_t * BlockDequeue(sout_input_t *p_input, mp4_stream_t *p_stream) if(unlikely(!p_block)) return NULL; - switch(p_stream->mux.fmt.i_codec) + switch(p_stream->tinfo->fmt.i_codec) { case VLC_CODEC_AV1: p_block = AV1_Pack_Sample(p_block); @@ -590,8 +578,8 @@ static block_t * BlockDequeue(sout_input_t *p_input, mp4_stream_t *p_stream) break; case VLC_CODEC_A52: case VLC_CODEC_EAC3: - if (p_stream->mux.a52_frame == NULL && p_block->i_buffer >= 8) - p_stream->mux.a52_frame = block_Duplicate(p_block); + if (p_stream->tinfo->a52_frame == NULL && p_block->i_buffer >= 8) + p_stream->tinfo->a52_frame = block_Duplicate(p_block); break; default: break; @@ -614,11 +602,12 @@ static int MuxStream(sout_mux_t *p_mux, sout_input_t *p_input, mp4_stream_t *p_s return VLC_SUCCESS; /* Reset reference dts in case of discontinuity (ex: gather sout) */ - if (p_data->i_flags & BLOCK_FLAG_DISCONTINUITY && p_stream->mux.i_entry_count) + if (p_data->i_flags & BLOCK_FLAG_DISCONTINUITY && p_stream->tinfo->i_samples_count) { if(p_stream->i_first_dts != VLC_TICK_INVALID) { - if(!CreateCurrentEdit(p_stream, p_sys->i_start_dts, p_sys->b_fragmented)) + if(!CreateCurrentEdit(p_stream, p_sys->i_start_dts, + mp4mux_Is(p_sys->muxh, FRAGMENTED))) { block_Release( p_data ); return VLC_ENOMEM; @@ -631,13 +620,6 @@ static int MuxStream(sout_mux_t *p_mux, sout_input_t *p_input, mp4_stream_t *p_s p_stream->i_last_pts = VLC_TICK_INVALID; } - /* XXX: -1 to always have 2 entry for easy adding of empty SPU */ - if (p_stream->mux.i_entry_count >= p_stream->mux.i_entry_max - 2) { - p_stream->mux.i_entry_max += 1000; - p_stream->mux.entry = xrealloc(p_stream->mux.entry, - p_stream->mux.i_entry_max * sizeof(mp4mux_entry_t)); - } - /* Set current segment ranges */ if( p_stream->i_first_dts == VLC_TICK_INVALID ) { @@ -646,7 +628,7 @@ static int MuxStream(sout_mux_t *p_mux, sout_input_t *p_input, mp4_stream_t *p_s p_sys->i_start_dts = p_stream->i_first_dts; } - if (p_stream->mux.fmt.i_cat != SPU_ES) + if (p_stream->tinfo->fmt.i_cat != SPU_ES) { /* Fix length of the sample */ if (block_FifoCount(p_input->p_fifo) > 0) @@ -654,29 +636,29 @@ static int MuxStream(sout_mux_t *p_mux, sout_input_t *p_input, mp4_stream_t *p_s block_t *p_next = block_FifoShow(p_input->p_fifo); if ( p_next->i_flags & BLOCK_FLAG_DISCONTINUITY ) { /* we have no way to know real length except by decoding */ - if ( p_stream->mux.fmt.i_cat == VIDEO_ES ) + if ( p_stream->tinfo->fmt.i_cat == VIDEO_ES ) { p_data->i_length = vlc_tick_from_samples( - p_stream->mux.fmt.video.i_frame_rate_base, - p_stream->mux.fmt.video.i_frame_rate ); + p_stream->tinfo->fmt.video.i_frame_rate_base, + p_stream->tinfo->fmt.video.i_frame_rate ); if( p_data->i_flags & BLOCK_FLAG_SINGLE_FIELD ) p_data->i_length >>= 1; msg_Dbg( p_mux, "video track %u fixup to %"PRId64" for sample %u", - p_stream->mux.i_track_id, p_data->i_length, p_stream->mux.i_entry_count ); + p_stream->tinfo->i_track_id, p_data->i_length, p_stream->tinfo->i_samples_count ); } - else if ( p_stream->mux.fmt.i_cat == AUDIO_ES && - p_stream->mux.fmt.audio.i_rate && + else if ( p_stream->tinfo->fmt.i_cat == AUDIO_ES && + p_stream->tinfo->fmt.audio.i_rate && p_data->i_nb_samples ) { p_data->i_length = vlc_tick_from_samples(p_data->i_nb_samples, - p_stream->mux.fmt.audio.i_rate); + p_stream->tinfo->fmt.audio.i_rate); msg_Dbg( p_mux, "audio track %u fixup to %"PRId64" for sample %u", - p_stream->mux.i_track_id, p_data->i_length, p_stream->mux.i_entry_count ); + p_stream->tinfo->i_track_id, p_data->i_length, p_stream->tinfo->i_samples_count ); } else if ( p_data->i_length <= 0 ) { msg_Warn( p_mux, "unknown length for track %u sample %u", - p_stream->mux.i_track_id, p_stream->mux.i_entry_count ); + p_stream->tinfo->i_track_id, p_stream->tinfo->i_samples_count ); p_data->i_length = 1; } } @@ -700,16 +682,16 @@ static int MuxStream(sout_mux_t *p_mux, sout_input_t *p_input, mp4_stream_t *p_s } else /* SPU_ES */ { - if (p_stream->mux.i_entry_count > 0 && - p_stream->mux.entry[p_stream->mux.i_entry_count-1].i_length == 0) + mp4mux_sample_t *p_lastsample = mp4mux_track_GetLastSample(p_stream->tinfo); + if (p_lastsample != NULL && p_lastsample->i_length == 0) { /* length of previous spu, stored in spu clearer */ int64_t i_length = dts_fb_pts( p_data ) - p_stream->i_last_dts; if(i_length < 0) i_length = 0; /* Fix entry */ - p_stream->mux.entry[p_stream->mux.i_entry_count-1].i_length = i_length; - p_stream->mux.i_read_duration += i_length; + p_lastsample->i_length = i_length; + p_stream->tinfo->i_read_duration += i_length; } } @@ -719,36 +701,39 @@ static int MuxStream(sout_mux_t *p_mux, sout_input_t *p_input, mp4_stream_t *p_s p_stream->i_last_pts = p_data->i_pts; /* add index entry */ - mp4mux_entry_t *e = &p_stream->mux.entry[p_stream->mux.i_entry_count++]; - e->i_pos = p_sys->i_pos; - e->i_size = p_data->i_buffer; + mp4mux_sample_t sample; + sample.i_pos = p_sys->i_pos; + sample.i_size = p_data->i_buffer; if ( p_data->i_dts != VLC_TICK_INVALID && p_data->i_pts > p_data->i_dts ) { - e->i_pts_dts = p_data->i_pts - p_data->i_dts; - if ( !p_stream->mux.b_hasbframes ) - p_stream->mux.b_hasbframes = true; + sample.i_pts_dts = p_data->i_pts - p_data->i_dts; + if ( !p_stream->tinfo->b_hasbframes ) + p_stream->tinfo->b_hasbframes = true; } - else e->i_pts_dts = 0; + else sample.i_pts_dts = 0; - e->i_length = p_data->i_length; - e->i_flags = p_data->i_flags; + sample.i_length = p_data->i_length; + sample.i_flags = p_data->i_flags; /* update */ - p_stream->mux.i_read_duration += __MAX( 0, p_data->i_length ); + p_stream->tinfo->i_read_duration += __MAX( 0, p_data->i_length ); p_stream->i_last_dts = dts_fb_pts( p_data ); /* write data */ - p_sys->i_pos += p_data->i_buffer; - sout_AccessOutWrite(p_mux->p_access, p_data); + if(mp4mux_track_AddSample(p_stream->tinfo, &sample)) + { + p_sys->i_pos += p_data->i_buffer; + sout_AccessOutWrite(p_mux->p_access, p_data); + } /* Add SPU clearing tag (duration tb fixed on next SPU or stream end )*/ - if ( p_stream->mux.fmt.i_cat == SPU_ES && e->i_length > 0 ) + if ( p_stream->tinfo->fmt.i_cat == SPU_ES && sample.i_length > 0 ) { block_t *p_empty = NULL; - if(p_stream->mux.fmt.i_codec == VLC_CODEC_SUBT|| - p_stream->mux.fmt.i_codec == VLC_CODEC_QTXT|| - p_stream->mux.fmt.i_codec == VLC_CODEC_TX3G) + if(p_stream->tinfo->fmt.i_codec == VLC_CODEC_SUBT|| + p_stream->tinfo->fmt.i_codec == VLC_CODEC_QTXT|| + p_stream->tinfo->fmt.i_codec == VLC_CODEC_TX3G) { p_empty = block_Alloc(3); if(p_empty) @@ -759,13 +744,13 @@ static int MuxStream(sout_mux_t *p_mux, sout_input_t *p_input, mp4_stream_t *p_s p_empty->p_buffer[2] = ' '; } } - else if(p_stream->mux.fmt.i_codec == VLC_CODEC_TTML) + else if(p_stream->tinfo->fmt.i_codec == VLC_CODEC_TTML) { p_empty = block_Alloc(40); if(p_empty) memcpy(p_empty->p_buffer, "<tt><body><div><p></p></div></body></tt>", 40); } - else if(p_stream->mux.fmt.i_codec == VLC_CODEC_WEBVTT) + else if(p_stream->tinfo->fmt.i_codec == VLC_CODEC_WEBVTT) { p_empty = block_Alloc(8); if(p_empty) @@ -773,27 +758,30 @@ static int MuxStream(sout_mux_t *p_mux, sout_input_t *p_input, mp4_stream_t *p_s } /* point to start of our empty */ - p_stream->i_last_dts += e->i_length; + p_stream->i_last_dts += sample.i_length; if(p_empty) { /* Append a idx entry */ /* XXX: No need to grow the entry here */ - mp4mux_entry_t *e_empty = &p_stream->mux.entry[p_stream->mux.i_entry_count++]; - e_empty->i_pos = p_sys->i_pos; - e_empty->i_size = p_empty->i_buffer; - e_empty->i_pts_dts= 0; - e_empty->i_length = 0; /* will add dts diff later*/ - e_empty->i_flags = 0; - - p_sys->i_pos += p_empty->i_buffer; - sout_AccessOutWrite(p_mux->p_access, p_empty); + mp4mux_sample_t closersample; + closersample.i_pos = p_sys->i_pos; + closersample.i_size = p_empty->i_buffer; + closersample.i_pts_dts= 0; + closersample.i_length = 0; /* will add dts diff later*/ + closersample.i_flags = 0; + + if(mp4mux_track_AddSample(p_stream->tinfo, &closersample)) + { + p_sys->i_pos += p_empty->i_buffer; + sout_AccessOutWrite(p_mux->p_access, p_empty); + } } } /* Update the global segment/media duration */ - if( p_stream->mux.i_read_duration > p_sys->i_read_duration ) - p_sys->i_read_duration = p_stream->mux.i_read_duration; + if( p_stream->tinfo->i_read_duration > p_sys->i_read_duration ) + p_sys->i_read_duration = p_stream->tinfo->i_read_duration; return VLC_SUCCESS; } @@ -969,13 +957,13 @@ static bo_t *GetMoofBox(sout_mux_t *p_mux, size_t *pi_mdat_total_size, { /* Current segment have all same duration value, different than trex's default */ if (b_allsamelength && - p_stream->read.p_first->p_block->i_length != p_stream->mux.i_trex_default_length && + p_stream->read.p_first->p_block->i_length != p_stream->tinfo->i_trex_default_length && p_stream->read.p_first->p_block->i_length) i_tfhd_flags |= MP4_TFHD_DFLT_SAMPLE_DURATION; /* Current segment have all same size value, different than trex's default */ if (b_allsamesize && - p_stream->read.p_first->p_block->i_buffer != p_stream->mux.i_trex_default_size && + p_stream->read.p_first->p_block->i_buffer != p_stream->tinfo->i_trex_default_size && p_stream->read.p_first->p_block->i_buffer) i_tfhd_flags |= MP4_TFHD_DFLT_SAMPLE_SIZE; } @@ -992,11 +980,11 @@ static bo_t *GetMoofBox(sout_mux_t *p_mux, size_t *pi_mdat_total_size, bo_free(traf); continue; } - bo_add_32be(tfhd, p_stream->mux.i_track_id); + bo_add_32be(tfhd, p_stream->tinfo->i_track_id); /* set the local sample duration default */ if (i_tfhd_flags & MP4_TFHD_DFLT_SAMPLE_DURATION) - bo_add_32be(tfhd, samples_from_vlc_tick(p_stream->read.p_first->p_block->i_length, p_stream->mux.i_timescale)); + bo_add_32be(tfhd, samples_from_vlc_tick(p_stream->read.p_first->p_block->i_length, p_stream->tinfo->i_timescale)); /* set the local sample size default */ if (i_tfhd_flags & MP4_TFHD_DFLT_SAMPLE_SIZE) @@ -1011,7 +999,7 @@ static bo_t *GetMoofBox(sout_mux_t *p_mux, size_t *pi_mdat_total_size, bo_free(traf); continue; } - bo_add_64be(tfdt, samples_from_vlc_tick(p_stream->i_written_duration, p_stream->mux.i_timescale) ); + bo_add_64be(tfdt, samples_from_vlc_tick(p_stream->i_written_duration, p_stream->tinfo->i_timescale) ); box_gather(traf, tfdt); /* *** add /moof/traf/trun *** */ @@ -1023,14 +1011,14 @@ static bo_t *GetMoofBox(sout_mux_t *p_mux, size_t *pi_mdat_total_size, i_trun_flags |= MP4_TRUN_FIRST_FLAGS; if (!b_allsamelength || - ( !(i_tfhd_flags & MP4_TFHD_DFLT_SAMPLE_DURATION) && p_stream->mux.i_trex_default_length == 0 )) + ( !(i_tfhd_flags & MP4_TFHD_DFLT_SAMPLE_DURATION) && p_stream->tinfo->i_trex_default_length == 0 )) i_trun_flags |= MP4_TRUN_SAMPLE_DURATION; if (!b_allsamesize || - ( !(i_tfhd_flags & MP4_TFHD_DFLT_SAMPLE_SIZE) && p_stream->mux.i_trex_default_size == 0 )) + ( !(i_tfhd_flags & MP4_TFHD_DFLT_SAMPLE_SIZE) && p_stream->tinfo->i_trex_default_size == 0 )) i_trun_flags |= MP4_TRUN_SAMPLE_SIZE; - if (p_stream->mux.b_hasbframes) + if (p_stream->tinfo->b_hasbframes) i_trun_flags |= MP4_TRUN_SAMPLE_TIME_OFFSET; if (i_fixupoffset == 0) @@ -1071,7 +1059,7 @@ static bo_t *GetMoofBox(sout_mux_t *p_mux, size_t *pi_mdat_total_size, DEQUEUE_ENTRY(p_stream->read, p_entry); if (i_trun_flags & MP4_TRUN_SAMPLE_DURATION) - bo_add_32be(trun, samples_from_vlc_tick(p_entry->p_block->i_length, p_stream->mux.i_timescale)); // sample duration + bo_add_32be(trun, samples_from_vlc_tick(p_entry->p_block->i_length, p_stream->tinfo->i_timescale)); // sample duration if (i_trun_flags & MP4_TRUN_SAMPLE_SIZE) bo_add_32be(trun, p_entry->p_block->i_buffer); // sample size @@ -1084,7 +1072,7 @@ static bo_t *GetMoofBox(sout_mux_t *p_mux, size_t *pi_mdat_total_size, { i_diff = p_entry->p_block->i_pts - p_entry->p_block->i_dts; } - bo_add_32be(trun, samples_from_vlc_tick(i_diff, p_stream->mux.i_timescale)); // ctts + bo_add_32be(trun, samples_from_vlc_tick(i_diff, p_stream->tinfo->i_timescale)); // ctts } *pi_mdat_total_size += p_entry->p_block->i_buffer; @@ -1095,7 +1083,7 @@ static bo_t *GetMoofBox(sout_mux_t *p_mux, size_t *pi_mdat_total_size, /* Add keyframe entry if needed */ if (p_stream->b_hasiframes && (p_entry->p_block->i_flags & BLOCK_FLAG_TYPE_I) && - (p_stream->mux.fmt.i_cat == VIDEO_ES || p_stream->mux.fmt.i_cat == AUDIO_ES)) + (p_stream->tinfo->fmt.i_cat == VIDEO_ES || p_stream->tinfo->fmt.i_cat == AUDIO_ES)) { AddKeyframeEntry(p_stream, i_write_pos, i_trak, i_sample, i_time); } @@ -1178,7 +1166,7 @@ static bo_t *GetMfraBox(sout_mux_t *p_mux) { bo_t *tfra = box_full_new("tfra", 0, 0x0); if (!tfra) continue; - bo_add_32be(tfra, p_stream->mux.i_track_id); + bo_add_32be(tfra, p_stream->tinfo->i_track_id); bo_add_32be(tfra, 0x3); // reserved + lengths (1,1,4)=>(0,0,3) bo_add_32be(tfra, p_stream->i_indexentries); for(uint32_t i_index=0; i_index<p_stream->i_indexentries; i_index++) @@ -1206,36 +1194,19 @@ static bo_t *GetMfraBox(sout_mux_t *p_mux) return mfra; } -static bo_t *BuildMoov(sout_mux_t *p_mux) -{ - sout_mux_sys_t *p_sys = (sout_mux_sys_t*) p_mux->p_sys; - const bool b_stco64 = (p_sys->i_pos >= (((uint64_t)0x1) << 32)); - /* map our structs */ - mp4mux_trackinfo_t **pp_infos = NULL; - if(p_sys->i_nb_streams) /* Trackless moov ? */ - { - pp_infos = vlc_alloc(p_sys->i_nb_streams, sizeof(mp4mux_trackinfo_t *)); - if(!pp_infos) - return NULL; - for(unsigned int i=0; i<p_sys->i_nb_streams; i++) - pp_infos[i] = &p_sys->pp_streams[i]->mux; - } - bo_t *p_moov = mp4mux_GetMoovBox(VLC_OBJECT(p_mux), pp_infos, p_sys->i_nb_streams, 0, - p_sys->b_fragmented, p_sys->b_mov, p_sys->b_64_ext, b_stco64); - free(pp_infos); - return p_moov; -} - static void FlushHeader(sout_mux_t *p_mux) { sout_mux_sys_t *p_sys = (sout_mux_sys_t*) p_mux->p_sys; + if(p_sys->i_pos >= (((uint64_t)0x1) << 32)) + mp4mux_Set64BitExt(p_sys->muxh); + /* Now add ftyp header */ bo_t *ftyp = mp4mux_GetFtyp(MAJOR_isom, 0, NULL, 0); if(!ftyp) return; - bo_t *moov = BuildMoov(p_mux); + bo_t *moov = mp4mux_GetMoov(p_sys->muxh, VLC_OBJECT(p_mux), 0); /* merge into a single block */ box_gather(ftyp, moov); @@ -1261,7 +1232,7 @@ static void WriteFragments(sout_mux_t *p_mux, bool b_flush) { mp4_stream_t *p_stream = p_sys->pp_streams[j]; if(CreateCurrentEdit(p_stream, p_sys->i_start_dts, true)) - DebugEdits(p_mux, p_stream); + mp4mux_track_DebugEdits(VLC_OBJECT(p_mux), p_stream->tinfo); } } @@ -1275,8 +1246,8 @@ static void WriteFragments(sout_mux_t *p_mux, bool b_flush) /* set a barrier so we try to align to keyframe */ if (p_stream->b_hasiframes && p_stream->i_last_iframe_time > p_stream->i_written_duration && - (p_stream->mux.fmt.i_cat == VIDEO_ES || - p_stream->mux.fmt.i_cat == AUDIO_ES) ) + (p_stream->tinfo->fmt.i_cat == VIDEO_ES || + p_stream->tinfo->fmt.i_cat == AUDIO_ES) ) { i_barrier_time = __MIN(i_barrier_time, p_stream->i_last_iframe_time); } @@ -1317,27 +1288,27 @@ static void WriteFragments(sout_mux_t *p_mux, bool b_flush) * This is the end boundary case. */ static void LengthLocalFixup(sout_mux_t *p_mux, const mp4_stream_t *p_stream, block_t *p_entrydata) { - if ( p_stream->mux.fmt.i_cat == VIDEO_ES && p_stream->mux.fmt.video.i_frame_rate ) + if ( p_stream->tinfo->fmt.i_cat == VIDEO_ES && p_stream->tinfo->fmt.video.i_frame_rate ) { p_entrydata->i_length = vlc_tick_from_samples( - p_stream->mux.fmt.video.i_frame_rate_base, - p_stream->mux.fmt.video.i_frame_rate); + p_stream->tinfo->fmt.video.i_frame_rate_base, + p_stream->tinfo->fmt.video.i_frame_rate); msg_Dbg(p_mux, "video track %d fixup to %"PRId64" for sample %u", - p_stream->mux.i_track_id, p_entrydata->i_length, p_stream->mux.i_entry_count - 1); + p_stream->tinfo->i_track_id, p_entrydata->i_length, p_stream->tinfo->i_samples_count - 1); } - else if (p_stream->mux.fmt.i_cat == AUDIO_ES && - p_stream->mux.fmt.audio.i_rate && - p_entrydata->i_nb_samples && p_stream->mux.fmt.audio.i_rate) + else if (p_stream->tinfo->fmt.i_cat == AUDIO_ES && + p_stream->tinfo->fmt.audio.i_rate && + p_entrydata->i_nb_samples && p_stream->tinfo->fmt.audio.i_rate) { p_entrydata->i_length = vlc_tick_from_samples(p_entrydata->i_nb_samples, - p_stream->mux.fmt.audio.i_rate); + p_stream->tinfo->fmt.audio.i_rate); msg_Dbg(p_mux, "audio track %d fixup to %"PRId64" for sample %u", - p_stream->mux.i_track_id, p_entrydata->i_length, p_stream->mux.i_entry_count - 1); + p_stream->tinfo->i_track_id, p_entrydata->i_length, p_stream->tinfo->i_samples_count - 1); } else { msg_Warn(p_mux, "unknown length for track %d sample %u", - p_stream->mux.i_track_id, p_stream->mux.i_entry_count - 1); + p_stream->tinfo->i_track_id, p_stream->tinfo->i_samples_count - 1); p_entrydata->i_length = 1; } } @@ -1367,11 +1338,10 @@ static void CleanupFrag(sout_mux_sys_t *p_sys) p_stream->towrite.p_first = p_next; } free(p_stream->p_indexentries); - - mp4mux_trackinfo_Clear(&p_stream->mux); free(p_stream); } TAB_CLEAN(p_sys->i_nb_streams, p_sys->pp_streams); + mp4mux_Delete(p_sys->muxh); free(p_sys); } @@ -1464,15 +1434,15 @@ static int MuxFrag(sout_mux_t *p_mux) p_stream->p_held_entry = NULL; if (p_stream->b_hasiframes && (p_heldblock->i_flags & BLOCK_FLAG_TYPE_I) && - p_stream->mux.i_read_duration - p_sys->i_written_duration < FRAGMENT_LENGTH) + p_stream->tinfo->i_read_duration - p_sys->i_written_duration < FRAGMENT_LENGTH) { /* Flag the last iframe time, we'll use it as boundary so it will start next fragment */ - p_stream->i_last_iframe_time = p_stream->mux.i_read_duration; + p_stream->i_last_iframe_time = p_stream->tinfo->i_read_duration; } /* update buffered time */ - p_stream->mux.i_read_duration += __MAX(0, p_heldblock->i_length); + p_stream->tinfo->i_read_duration += __MAX(0, p_heldblock->i_length); } @@ -1485,26 +1455,26 @@ static int MuxFrag(sout_mux_t *p_mux) p_stream->p_held_entry->i_run = p_stream->i_current_run; p_stream->p_held_entry->p_next = NULL; - if (p_stream->mux.fmt.i_cat == VIDEO_ES ) + if (p_stream->tinfo->fmt.i_cat == VIDEO_ES ) { if (!p_stream->b_hasiframes && (p_currentblock->i_flags & BLOCK_FLAG_TYPE_I)) p_stream->b_hasiframes = true; - if (!p_stream->mux.b_hasbframes && p_currentblock->i_dts != VLC_TICK_INVALID && + if (!p_stream->tinfo->b_hasbframes && p_currentblock->i_dts != VLC_TICK_INVALID && p_currentblock->i_pts > p_currentblock->i_dts) - p_stream->mux.b_hasbframes = true; + p_stream->tinfo->b_hasbframes = true; } /* Update the global fragment/media duration */ - vlc_tick_t i_min_read_duration = p_stream->mux.i_read_duration; + vlc_tick_t i_min_read_duration = p_stream->tinfo->i_read_duration; vlc_tick_t i_min_written_duration = p_stream->i_written_duration; for (unsigned int i=0; i<p_sys->i_nb_streams; i++) { const mp4_stream_t *p_s = p_sys->pp_streams[i]; - if (p_s->mux.fmt.i_cat != VIDEO_ES && p_s->mux.fmt.i_cat != AUDIO_ES) + if (p_s->tinfo->fmt.i_cat != VIDEO_ES && p_s->tinfo->fmt.i_cat != AUDIO_ES) continue; - if (p_s->mux.i_read_duration < i_min_read_duration) - i_min_read_duration = p_s->mux.i_read_duration; + if (p_s->tinfo->i_read_duration < i_min_read_duration) + i_min_read_duration = p_s->tinfo->i_read_duration; if (p_s->i_written_duration < i_min_written_duration) i_min_written_duration = p_s->i_written_duration; _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
