mp4.c 179 KB
Newer Older
1 2 3
/*****************************************************************************
 * mp4.c : MP4 file input module for vlc
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2001-2004, 2010 VLC authors and VideoLAN
5
 *
6
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
7
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
8 9 10
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
11
 * (at your option) any later version.
12
 *
13 14
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
15 16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
17
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
18 19 20
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21
 *****************************************************************************/
22 23 24
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
25 26 27 28

/*****************************************************************************
 * Preamble
 *****************************************************************************/
29 30
#include "mp4.h"

zorglub's avatar
zorglub committed
31
#include <vlc_demux.h>
32
#include <vlc_charset.h>                           /* EnsureUTF8 */
33
#include <vlc_input.h>
34
#include <vlc_aout.h>
35
#include <vlc_plugin.h>
36
#include <vlc_dialog.h>
37
#include <vlc_url.h>
38
#include <assert.h>
39
#include <limits.h>
40
#include "../codec/cc.h"
41
#include "heif.h"
42
#include "../av1_unpack.h"
43

44 45 46
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
47 48
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );
49

50 51
#define CFG_PREFIX "mp4-"

Alexander Law's avatar
Alexander Law committed
52 53
#define MP4_M4A_TEXT     N_("M4A audio only")
#define MP4_M4A_LONGTEXT N_("Ignore non audio tracks from iTunes audio files")
54

55 56 57 58 59
#define HEIF_DURATION_TEXT N_("Duration in seconds")
#define HEIF_DURATION_LONGTEXT N_( \
    "Duration in seconds before simulating an end of file. " \
    "A negative value means an unlimited play time.")

60 61 62 63
vlc_module_begin ()
    set_category( CAT_INPUT )
    set_subcategory( SUBCAT_INPUT_DEMUX )
    set_description( N_("MP4 stream demuxer") )
64
    set_shortname( N_("MP4") )
65
    set_capability( "demux", 240 )
66
    set_callbacks( Open, Close )
67

68
    add_category_hint("Hacks", NULL)
69
    add_bool( CFG_PREFIX"m4a-audioonly", false, MP4_M4A_TEXT, MP4_M4A_LONGTEXT, true )
70 71 72 73 74 75 76 77 78 79 80 81

    add_submodule()
        set_category( CAT_INPUT )
        set_subcategory( SUBCAT_INPUT_DEMUX )
        set_description( N_("HEIF demuxer") )
        set_shortname( "heif" )
        set_capability( "demux", 239 )
        set_callbacks( OpenHEIF, CloseHEIF )
        set_section( N_("HEIF demuxer"), NULL )
        add_float( "heif-image-duration", HEIF_DEFAULT_DURATION,
                   HEIF_DURATION_TEXT, HEIF_DURATION_LONGTEXT, false )
            change_safe()
82
vlc_module_end ()
83

84 85 86
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
87
static int   Demux   ( demux_t * );
88
static int   DemuxRef( demux_t *p_demux ){ (void)p_demux; return 0;}
89
static int   DemuxFrag( demux_t * );
90
static int   Control ( demux_t *, int, va_list );
91

92
typedef struct
93 94 95
{
    MP4_Box_t    *p_root;      /* container for the whole file */

Steve Lhomme's avatar
Steve Lhomme committed
96
    vlc_tick_t   i_pcr;
97

98
    uint64_t     i_moov_duration;
99 100 101
    uint64_t     i_duration;           /* Declared fragmented duration (movie time scale) */
    uint64_t     i_cumulated_duration; /* Same as above, but not from probing, (movie time scale) */
    uint32_t     i_timescale;          /* movie time scale */
102
    vlc_tick_t   i_nztime;             /* time position of the presentation (CLOCK_FREQ timescale) */
103 104
    unsigned int i_tracks;       /* number of tracks */
    mp4_track_t  *track;         /* array of track */
105
    float        f_fps;          /* number of frame per seconds */
106

107
    bool         b_fragmented;   /* fMP4 */
108 109
    bool         b_seekable;
    bool         b_fastseekable;
110
    bool         b_error;        /* unrecoverable */
111

112 113
    bool            b_index_probed;     /* mFra sync points index */
    bool            b_fragments_probed; /* moof segments index created */
114

115
    MP4_Box_t *p_moov;
116

117 118 119
    struct
    {
        uint32_t        i_current_box_type;
120
        MP4_Box_t      *p_fragment_atom;
121
        uint64_t        i_post_mdat_offset;
122
        uint32_t        i_lastseqnumber;
123 124
    } context;

125
    /* */
126
    bool seekpoint_changed;
127
    int          i_seekpoint;
128
    input_title_t *p_title;
129
    vlc_meta_t    *p_meta;
130 131 132

    /* ASF in MP4 */
    asf_packet_sys_t asfpacketsys;
133
    vlc_tick_t i_preroll;       /* foobar */
Steve Lhomme's avatar
Steve Lhomme committed
134
    vlc_tick_t i_preroll_start;
135 136 137 138 139

    struct
    {
        int es_cat_filters;
    } hacks;
140 141

    mp4_fragments_index_t *p_fragsindex;
142
} demux_sys_t;
143

144
#define DEMUX_INCREMENT VLC_TICK_FROM_MS(250) /* How far the pcr will go, each round */
145
#define DEMUX_TRACK_MAX_PRELOAD VLC_TICK_FROM_SEC(15) /* maximum preloading, to deal with interleaving */
146

147 148
#define INVALID_PRELOAD  UINT_MAX

149
#define VLC_DEMUXER_EOS (VLC_DEMUXER_EGENERIC - 1)
150
#define VLC_DEMUXER_FATAL (VLC_DEMUXER_EGENERIC - 2)
151

152 153 154 155 156 157 158 159 160
const uint32_t rgi_pict_atoms[2] = { ATOM_PICT, ATOM_pict };
const char *psz_meta_roots[] = { "/moov/udta/meta/ilst",
                                 "/moov/meta/ilst",
                                 "/moov/udta/meta",
                                 "/moov/udta",
                                 "/meta/ilst",
                                 "/udta",
                                 NULL };

161
/*****************************************************************************
162
 * Declaration of local function
163
 *****************************************************************************/
164
static void MP4_TrackSetup( demux_t *, mp4_track_t *, MP4_Box_t  *, bool, bool );
165
static void MP4_TrackInit( mp4_track_t *, const MP4_Box_t * );
166
static void MP4_TrackClean( es_out_t *, mp4_track_t * );
167

168 169
static void MP4_Block_Send( demux_t *, mp4_track_t *, block_t * );

170
static void MP4_TrackSelect  ( demux_t *, mp4_track_t *, bool );
Steve Lhomme's avatar
Steve Lhomme committed
171
static int  MP4_TrackSeek   ( demux_t *, mp4_track_t *, vlc_tick_t );
172

Laurent Aimar's avatar
Laurent Aimar committed
173
static uint64_t MP4_TrackGetPos    ( mp4_track_t * );
174
static uint32_t MP4_TrackGetReadSize( mp4_track_t *, uint32_t * );
175
static int      MP4_TrackNextSample( demux_t *, mp4_track_t *, uint32_t );
176
static void     MP4_TrackSetELST( demux_t *, mp4_track_t *, vlc_tick_t );
Laurent Aimar's avatar
Laurent Aimar committed
177

178
static void     MP4_UpdateSeekpoint( demux_t *, vlc_tick_t );
179

180
static MP4_Box_t * MP4_GetTrexByTrackID( MP4_Box_t *p_moov, const uint32_t i_id );
181
static void MP4_GetDefaultSizeAndDuration( MP4_Box_t *p_moov,
182 183 184
                                           const MP4_Box_data_tfhd_t *p_tfhd_data,
                                           uint32_t *pi_default_size,
                                           uint32_t *pi_default_duration );
185

186 187
static stime_t GetMoovTrackDuration( demux_sys_t *p_sys, unsigned i_track_ID );

188
static int  ProbeFragments( demux_t *p_demux, bool b_force, bool *pb_fragmented );
189
static int  ProbeFragmentsChecked( demux_t *p_demux );
190 191
static int  ProbeIndex( demux_t *p_demux );

192
static int FragCreateTrunIndex( demux_t *, MP4_Box_t *, MP4_Box_t *, stime_t );
193

Steve Lhomme's avatar
Steve Lhomme committed
194 195 196 197
static int FragGetMoofBySidxIndex( demux_t *p_demux, vlc_tick_t i_target_time,
                                   uint64_t *pi_moof_pos, vlc_tick_t *pi_sampletime );
static int FragGetMoofByTfraIndex( demux_t *p_demux, const vlc_tick_t i_target_time, unsigned i_track_ID,
                                   uint64_t *pi_moof_pos, vlc_tick_t *pi_sampletime );
198
static void FragResetContext( demux_sys_t * );
199

200 201 202 203 204
/* ASF Handlers */
static asf_track_info_t * MP4ASF_GetTrackInfo( asf_packet_sys_t *p_packetsys, uint8_t i_stream_number );
static void MP4ASF_Send(asf_packet_sys_t *p_packetsys, uint8_t i_stream_number, block_t **pp_frame);
static void MP4ASF_ResetFrames( demux_sys_t *p_sys );

205 206 207 208
/* RTP Hint track */
static block_t * MP4_RTPHint_Convert( demux_t *p_demux, block_t *p_block, vlc_fourcc_t i_codec );
static block_t * MP4_RTPHintToFrame( demux_t *p_demux, block_t *p_block, uint32_t packetcount );

209 210
static int MP4_LoadMeta( demux_sys_t *p_sys, vlc_meta_t *p_meta );

211 212
/* Helpers */

213 214 215 216 217 218 219 220 221 222 223 224 225 226
static int64_t MP4_rescale( int64_t i_value, uint32_t i_timescale, uint32_t i_newscale )
{
    if( i_timescale == i_newscale )
        return i_value;

    if( i_value <= INT64_MAX / i_newscale )
        return i_value * i_newscale / i_timescale;

    /* overflow */
    int64_t q = i_value / i_timescale;
    int64_t r = i_value % i_timescale;
    return q * i_newscale + r * i_newscale / i_timescale;
}

227 228 229 230 231
static vlc_tick_t MP4_rescale_mtime( int64_t i_value, uint32_t i_timescale )
{
    return MP4_rescale(i_value, i_timescale, CLOCK_FREQ);
}

232 233 234 235 236
static int64_t MP4_rescale_qtime( vlc_tick_t i_value, uint32_t i_timescale )
{
    return MP4_rescale(i_value, CLOCK_FREQ, i_timescale);
}

237 238
static uint32_t stream_ReadU32( stream_t *s, void *p_read, uint32_t i_toread )
{
239
    ssize_t i_return = 0;
240 241
    if ( i_toread > INT32_MAX )
    {
242
        i_return = vlc_stream_Read( s, p_read, (size_t) INT32_MAX );
243 244 245 246 247
        if ( i_return < INT32_MAX )
            return i_return;
        else
            i_toread -= INT32_MAX;
    }
248
    i_return += vlc_stream_Read( s, (uint8_t *)p_read + i_return, (size_t) i_toread );
249 250 251
    return i_return;
}

252
static inline bool MP4_isMetadata(const mp4_track_t *tk)
253
{
254
    return tk->as_reftype != 0;
255 256
}

257 258
static MP4_Box_t * MP4_GetTrexByTrackID( MP4_Box_t *p_moov, const uint32_t i_id )
{
259 260
    if(!p_moov)
        return NULL;
261 262 263
    MP4_Box_t *p_trex = MP4_BoxGet( p_moov, "mvex/trex" );
    while( p_trex )
    {
264 265
        if ( p_trex->i_type == ATOM_trex &&
             BOXDATA(p_trex) && BOXDATA(p_trex)->i_track_ID == i_id )
266 267 268 269 270 271 272
                break;
        else
            p_trex = p_trex->p_next;
    }
    return p_trex;
}

273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
/**
 * Return the track identified by tid
 */
static mp4_track_t *MP4_GetTrackByTrackID( demux_t *p_demux, const uint32_t tid )
{
    demux_sys_t *p_sys = p_demux->p_sys;

    mp4_track_t *ret = NULL;
    for( unsigned i = 0; i < p_sys->i_tracks; i++ )
    {
        ret = &p_sys->track[i];
        if( ret->i_track_ID == tid )
            return ret;
    }
    return NULL;
}

290 291 292 293 294 295 296
static MP4_Box_t * MP4_GetTrakByTrackID( MP4_Box_t *p_moov, const uint32_t i_id )
{
    MP4_Box_t *p_trak = MP4_BoxGet( p_moov, "trak" );
    MP4_Box_t *p_tkhd;
    while( p_trak )
    {
        if( p_trak->i_type == ATOM_trak &&
297
            (p_tkhd = MP4_BoxGet( p_trak, "tkhd" )) && BOXDATA(p_tkhd) &&
298 299 300 301 302 303 304 305
            BOXDATA(p_tkhd)->i_track_ID == i_id )
                break;
        else
            p_trak = p_trak->p_next;
    }
    return p_trak;
}

306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
static MP4_Box_t * MP4_GetTrafByTrackID( MP4_Box_t *p_moof, const uint32_t i_id )
{
    MP4_Box_t *p_traf = MP4_BoxGet( p_moof, "traf" );
    MP4_Box_t *p_tfhd;
    while( p_traf )
    {
        if( p_traf->i_type == ATOM_traf &&
            (p_tfhd = MP4_BoxGet( p_traf, "tfhd" )) && BOXDATA(p_tfhd) &&
            BOXDATA(p_tfhd)->i_track_ID == i_id )
                break;
        else
            p_traf = p_traf->p_next;
    }
    return p_traf;
}

322 323 324 325 326 327 328 329 330 331
static es_out_id_t * MP4_AddTrackES( es_out_t *out, mp4_track_t *p_track )
{
    es_out_id_t *p_es = es_out_Add( out, &p_track->fmt );
    /* Force SPU which isn't selected/defaulted */
    if( p_track->fmt.i_cat == SPU_ES && p_es && p_track->b_forced_spu )
        es_out_Control( out, ES_OUT_SET_ES_DEFAULT, p_es );

    return p_es;
}

332
/* Return time in microsecond of a track */
333
static inline vlc_tick_t MP4_TrackGetDTS( demux_t *p_demux, mp4_track_t *p_track )
Laurent Aimar's avatar
Laurent Aimar committed
334
{
335
    demux_sys_t *p_sys = p_demux->p_sys;
336
    const mp4_chunk_t *p_chunk = &p_track->chunk[p_track->i_chunk];
337 338

    unsigned int i_index = 0;
339
    unsigned int i_sample = p_track->i_sample - p_chunk->i_sample_first;
340
    int64_t sdts = p_chunk->i_first_dts;
Laurent Aimar's avatar
Laurent Aimar committed
341

342
    while( i_sample > 0 && i_index < p_chunk->i_entries_dts )
Laurent Aimar's avatar
Laurent Aimar committed
343
    {
344
        if( i_sample > p_chunk->p_sample_count_dts[i_index] )
Laurent Aimar's avatar
Laurent Aimar committed
345
        {
346
            sdts += p_chunk->p_sample_count_dts[i_index] *
347 348
                p_chunk->p_sample_delta_dts[i_index];
            i_sample -= p_chunk->p_sample_count_dts[i_index];
Laurent Aimar's avatar
Laurent Aimar committed
349 350 351 352
            i_index++;
        }
        else
        {
353
            sdts += i_sample * p_chunk->p_sample_delta_dts[i_index];
Laurent Aimar's avatar
Laurent Aimar committed
354 355 356 357
            break;
        }
    }

358
    vlc_tick_t i_dts = MP4_rescale_mtime( sdts, p_track->i_timescale );
359

360
    /* now handle elst */
361
    if( p_track->p_elst && p_track->BOXDATA(p_elst)->i_entry_count )
362
    {
363
        MP4_Box_data_elst_t *elst = p_track->BOXDATA(p_elst);
364 365 366 367 368 369

        /* convert to offset */
        if( ( elst->i_media_rate_integer[p_track->i_elst] > 0 ||
              elst->i_media_rate_fraction[p_track->i_elst] > 0 ) &&
            elst->i_media_time[p_track->i_elst] > 0 )
        {
370
            i_dts -= MP4_rescale_mtime( elst->i_media_time[p_track->i_elst], p_track->i_timescale );
371 372 373
        }

        /* add i_elst_time */
374
        i_dts += MP4_rescale_mtime( p_track->i_elst_time, p_sys->i_timescale );
375 376 377 378

        if( i_dts < 0 ) i_dts = 0;
    }

379
    return i_dts;
Laurent Aimar's avatar
Laurent Aimar committed
380
}
381

382
static inline bool MP4_TrackGetPTSDelta( demux_t *p_demux, mp4_track_t *p_track,
383
                                         vlc_tick_t *pi_delta )
Laurent Aimar's avatar
Laurent Aimar committed
384
{
385
    VLC_UNUSED( p_demux );
386
    mp4_chunk_t *ck = &p_track->chunk[p_track->i_chunk];
387

Laurent Aimar's avatar
Laurent Aimar committed
388 389 390 391
    unsigned int i_index = 0;
    unsigned int i_sample = p_track->i_sample - ck->i_sample_first;

    if( ck->p_sample_count_pts == NULL || ck->p_sample_offset_pts == NULL )
392
        return false;
Laurent Aimar's avatar
Laurent Aimar committed
393

394
    for( i_index = 0; i_index < ck->i_entries_pts ; i_index++ )
Laurent Aimar's avatar
Laurent Aimar committed
395 396
    {
        if( i_sample < ck->p_sample_count_pts[i_index] )
397
        {
398 399
            *pi_delta = MP4_rescale_mtime( ck->p_sample_offset_pts[i_index],
                                           p_track->i_timescale );
400 401
            return true;
        }
Laurent Aimar's avatar
Laurent Aimar committed
402 403 404

        i_sample -= ck->p_sample_count_pts[i_index];
    }
405
    return false;
Laurent Aimar's avatar
Laurent Aimar committed
406 407
}

Steve Lhomme's avatar
Steve Lhomme committed
408
static inline vlc_tick_t MP4_GetSamplesDuration( demux_t *p_demux, mp4_track_t *p_track,
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
                                              unsigned i_nb_samples )
{
    VLC_UNUSED( p_demux );

    const mp4_chunk_t *p_chunk = &p_track->chunk[p_track->i_chunk];
    stime_t i_duration = 0;

    /* Forward to right index, and set remaining count in that index */
    unsigned i_index = 0;
    unsigned i_remain = 0;
    for( unsigned i = p_chunk->i_sample_first;
         i<p_track->i_sample && i_index < p_chunk->i_entries_dts; )
    {
        if( p_track->i_sample - i >= p_chunk->p_sample_count_dts[i_index] )
        {
            i += p_chunk->p_sample_count_dts[i_index];
            i_index++;
        }
        else
        {
            i_remain = p_track->i_sample - i;
            break;
        }
    }

    /* Compute total duration from all samples from index */
    while( i_nb_samples > 0 && i_index < p_chunk->i_entries_dts )
    {
        if( i_nb_samples >= p_chunk->p_sample_count_dts[i_index] - i_remain )
        {
            i_duration += (p_chunk->p_sample_count_dts[i_index] - i_remain) *
                          (int64_t) p_chunk->p_sample_delta_dts[i_index];
            i_nb_samples -= (p_chunk->p_sample_count_dts[i_index] - i_remain);
            i_index++;
            i_remain = 0;
        }
        else
        {
            i_duration += i_nb_samples * p_chunk->p_sample_delta_dts[i_index];
            break;
        }
    }

452
    return MP4_rescale_mtime( i_duration, p_track->i_timescale );
453 454
}

455
static inline vlc_tick_t MP4_GetMoviePTS(demux_sys_t *p_sys )
Laurent Aimar's avatar
Laurent Aimar committed
456
{
457
    return p_sys->i_nztime;
Laurent Aimar's avatar
Laurent Aimar committed
458
}
459

460 461
static void LoadChapter( demux_t  *p_demux );

462
static int LoadInitFrag( demux_t *p_demux )
463 464 465
{
    demux_sys_t *p_sys = p_demux->p_sys;

466
    /* Load all boxes ( except raw data ) */
467 468
    MP4_Box_t *p_root = MP4_BoxGetRoot( p_demux->s );
    if( p_root == NULL || !MP4_BoxGet( p_root, "/moov" ) )
469
    {
470
        MP4_BoxFree( p_root );
471
        goto LoadInitFragError;
472
    }
473

474 475
    p_sys->p_root = p_root;

476 477 478 479 480 481 482
    return VLC_SUCCESS;

LoadInitFragError:
    msg_Warn( p_demux, "MP4 plugin discarded (not a valid initialization chunk)" );
    return VLC_EGENERIC;
}

483
static int CreateTracks( demux_t *p_demux, unsigned i_tracks )
484 485 486
{
    demux_sys_t *p_sys = p_demux->p_sys;

487 488 489
    if( SIZE_MAX / i_tracks < sizeof(mp4_track_t) )
        return VLC_EGENERIC;

Thomas Guillem's avatar
Thomas Guillem committed
490
    p_sys->track = vlc_alloc( i_tracks, sizeof(mp4_track_t)  );
491
    if( p_sys->track == NULL )
492 493
        return VLC_ENOMEM;
    p_sys->i_tracks = i_tracks;
494

495
    for( unsigned i=0; i<i_tracks; i++ )
496 497 498 499
    {
        MP4_TrackInit( &p_sys->track[i],
                       MP4_BoxGet( p_sys->p_root, "/moov/trak[%d]", i ) );
    }
500

501 502 503
    return VLC_SUCCESS;
}

504 505 506 507
static block_t * MP4_EIA608_Convert( block_t * p_block )
{
    /* Rebuild codec data from encap */
    size_t i_copied = 0;
508
    size_t i_remaining = __MIN(p_block->i_buffer, INT64_MAX / 3);
509 510 511
    uint32_t i_bytes = 0;
    block_t *p_newblock;

512
    /* always need at least 10 bytes (atom size+header+1pair)*/
513 514
    if ( i_remaining < 10 ||
         !(i_bytes = GetDWBE(p_block->p_buffer)) ||
515
         (i_bytes > i_remaining) ||
516 517 518 519 520 521 522 523 524 525 526 527 528 529
         memcmp("cdat", &p_block->p_buffer[4], 4) ||
         !(p_newblock = block_Alloc( i_remaining * 3 - 8 )) )
    {
        p_block->i_buffer = 0;
        return p_block;
    }

    uint8_t *p_write = p_newblock->p_buffer;
    uint8_t *p_read = &p_block->p_buffer[8];
    i_bytes -= 8;
    i_remaining -= 8;

    do
    {
530
        p_write[i_copied++] = CC_PKT_BYTE0(0); /* cc1 == field 0 */
531 532 533 534 535 536 537
        p_write[i_copied++] = p_read[0];
        p_write[i_copied++] = p_read[1];
        p_read += 2;
        i_bytes -= 2;
        i_remaining -= 2;
    } while( i_bytes >= 2 );

538
    /* cdt2 is optional */
539 540
    if ( i_remaining >= 10 &&
         (i_bytes = GetDWBE(p_read)) &&
541
         (i_bytes <= i_remaining) &&
542 543 544 545 546 547 548
         !memcmp("cdt2", &p_read[4], 4) )
    {
        p_read += 8;
        i_bytes -= 8;
        i_remaining -= 8;
        do
        {
549
            p_write[i_copied++] = CC_PKT_BYTE0(0); /* cc1 == field 0 */
550 551 552 553 554 555 556
            p_write[i_copied++] = p_read[0];
            p_write[i_copied++] = p_read[1];
            p_read += 2;
            i_bytes -= 2;
        } while( i_bytes >= 2 );
    }

557
    p_newblock->i_pts = p_block->i_dts;
558
    p_newblock->i_buffer = i_copied;
559 560 561
    p_newblock->i_flags = BLOCK_FLAG_TYPE_P;
    block_Release( p_block );

562 563 564
    return p_newblock;
}

565 566 567 568 569 570 571
static uint32_t MP4_TrackGetRunSeq( mp4_track_t *p_track )
{
    if( p_track->i_chunk_count > 0 )
        return p_track->chunk[p_track->i_chunk].i_virtual_run_number;
    return 0;
}

572 573
/* Analyzes chunks to find max interleave length
 * sets flat flag if no interleaving is in use */
574
static void MP4_GetInterleaving( demux_t *p_demux, vlc_tick_t *pi_max_contiguous, bool *pb_flat )
575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
{
    demux_sys_t *p_sys = p_demux->p_sys;
    *pi_max_contiguous = 0;
    *pb_flat = true;

    /* Find first recorded chunk */
    mp4_track_t *tk = NULL;
    uint64_t i_duration = 0;
    for( unsigned i=0; i < p_sys->i_tracks; i++ )
    {
        mp4_track_t *cur = &p_sys->track[i];
        if( !cur->i_chunk_count )
            continue;

        if( tk == NULL || cur->chunk[0].i_offset < tk->chunk[0].i_offset )
            tk = cur;
    }

    for( ; tk != NULL; )
    {
        i_duration += tk->chunk[tk->i_chunk].i_duration;
        tk->i_chunk++;

        /* Find next chunk in data order */
        mp4_track_t *nexttk = NULL;
        for( unsigned i=0; i < p_sys->i_tracks; i++ )
        {
            mp4_track_t *cur = &p_sys->track[i];
            if( cur->i_chunk == cur->i_chunk_count )
                continue;

            if( nexttk == NULL ||
                cur->chunk[cur->i_chunk].i_offset < nexttk->chunk[nexttk->i_chunk].i_offset )
                nexttk = cur;
        }

611 612 613 614 615
        /* copy previous run */
        if( nexttk && nexttk->i_chunk > 0 )
            nexttk->chunk[nexttk->i_chunk].i_virtual_run_number =
                    nexttk->chunk[nexttk->i_chunk - 1].i_virtual_run_number;

616 617
        if( tk != nexttk )
        {
618
            vlc_tick_t i_dur = MP4_rescale_mtime( i_duration, tk->i_timescale );
619 620
            if( i_dur > *pi_max_contiguous )
                *pi_max_contiguous = i_dur;
621 622 623 624
            i_duration = 0;

            if( tk->i_chunk != tk->i_chunk_count )
                *pb_flat = false;
625 626 627

            if( nexttk && nexttk->i_chunk > 0 ) /* new run number */
                nexttk->chunk[nexttk->i_chunk].i_virtual_run_number++;
628 629 630 631 632 633 634 635 636 637
        }

        tk = nexttk;
    }

    /* reset */
    for( unsigned i=0; i < p_sys->i_tracks; i++ )
        p_sys->track[i].i_chunk = 0;
}

638
static block_t * MP4_Block_Convert( demux_t *p_demux, const mp4_track_t *p_track, block_t *p_block )
639 640 641 642 643 644
{
    /* might have some encap */
    if( p_track->fmt.i_cat == SPU_ES )
    {
        switch( p_track->fmt.i_codec )
        {
645
            case VLC_CODEC_WEBVTT:
François Cartegnie's avatar
François Cartegnie committed
646
            case VLC_CODEC_TTML:
647
            case VLC_CODEC_QTXT:
648 649
            case VLC_CODEC_TX3G:
            case VLC_CODEC_SPU:
650
            case VLC_CODEC_SUBT:
651 652
            /* accept as-is */
            break;
653
            case VLC_CODEC_CEA608:
654 655
                p_block = MP4_EIA608_Convert( p_block );
            break;
656 657 658 659 660
        default:
            p_block->i_buffer = 0;
            break;
        }
    }
661 662
    else if( p_track->fmt.i_codec == VLC_CODEC_AV1 )
    {
663
        p_block = AV1_Unpack_Sample( p_block );
664
    }
665 666 667 668
    else if( p_track->fmt.i_original_fourcc == ATOM_rrtp )
    {
        p_block = MP4_RTPHint_Convert( p_demux, p_block, p_track->fmt.i_codec );
    }
669 670 671 672

    return p_block;
}

673 674
static void MP4_Block_Send( demux_t *p_demux, mp4_track_t *p_track, block_t *p_block )
{
675 676
    demux_sys_t *p_sys = p_demux->p_sys;

677 678 679 680
    p_block = MP4_Block_Convert( p_demux, p_track, p_block );
    if( p_block == NULL )
        return;

681
    if ( p_track->b_chans_reorder )
682 683 684 685 686 687
    {
        aout_ChannelReorder( p_block->p_buffer, p_block->i_buffer,
                             p_track->fmt.audio.i_channels,
                             p_track->rgi_chans_reordering,
                             p_track->fmt.i_codec );
    }
688

689
    p_block->i_flags |= p_track->i_block_flags;
690 691 692 693 694
    if( p_track->i_next_block_flags )
    {
        p_block->i_flags |= p_track->i_next_block_flags;
        p_track->i_next_block_flags = 0;
    }
695

696 697 698 699 700
    /* ASF packets in mov */
    if( p_track->p_asf )
    {
        /* Fake a new stream from MP4 block */
        stream_t *p_stream = p_demux->s;
701
        p_demux->s = vlc_stream_MemoryNew( p_demux, p_block->p_buffer, p_block->i_buffer, true );
702 703 704 705 706
        if ( p_demux->s )
        {
            p_track->i_dts_backup = p_block->i_dts;
            p_track->i_pts_backup = p_block->i_pts;
            /* And demux it as ASF packet */
707
            DemuxASFPacket( &p_sys->asfpacketsys, p_block->i_buffer, p_block->i_buffer );
708
            vlc_stream_Delete(p_demux->s);
709 710 711 712 713 714
        }
        block_Release(p_block);
        p_demux->s = p_stream;
    }
    else
        es_out_Send( p_demux->out, p_track->p_es, p_block );
715 716
}

717 718 719
int  OpenHEIF ( vlc_object_t * );
void CloseHEIF( vlc_object_t * );

720
/*****************************************************************************
721
 * Open: check file and initializes MP4 structures
722
 *****************************************************************************/
723
static int Open( vlc_object_t * p_this )
724
{
Laurent Aimar's avatar
Laurent Aimar committed
725
    demux_t  *p_demux = (demux_t *)p_this;
726
    demux_sys_t     *p_sys;
727

Christophe Mutricy's avatar
Christophe Mutricy committed
728
    const uint8_t   *p_peek;
729

730
    MP4_Box_t       *p_ftyp;
731 732
    const MP4_Box_t *p_mvhd = NULL;
    const MP4_Box_t *p_mvex = NULL;
733

734
    bool      b_enabled_es;
735

736
    /* A little test to see if it could be a mp4 */
737
    if( vlc_stream_Peek( p_demux->s, &p_peek, 12 ) < 12 ) return VLC_EGENERIC;
738

739
    switch( VLC_FOURCC( p_peek[4], p_peek[5], p_peek[6], p_peek[7] ) )
740
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
741 742 743 744 745 746 747 748
        case ATOM_moov:
        case ATOM_foov:
        case ATOM_moof:
        case ATOM_mdat:
        case ATOM_udta:
        case ATOM_free:
        case ATOM_skip:
        case ATOM_wide:
749
        case ATOM_uuid:
750
        case VLC_FOURCC( 'p', 'n', 'o', 't' ):
751
            break;
752
        case ATOM_ftyp:
753 754 755 756
        {
            /* Early handle some brands */
            switch( VLC_FOURCC(p_peek[8], p_peek[9], p_peek[10], p_peek[11]) )
            {
757
                /* HEIF pictures goes to heif demux */
758 759 760 761 762
                case BRAND_heic:
                case BRAND_heix:
                case BRAND_mif1:
                case BRAND_jpeg:
                case BRAND_avci:
763
                case BRAND_avif:
764
                /* We don't yet support f4v, but avformat does. */
765
                case BRAND_f4v:
766 767 768 769
                    return VLC_EGENERIC;
                default:
                    break;
            }
770
            break;
771
        }
772
         default:
773
            return VLC_EGENERIC;
774
    }
775

776 777 778 779 780
    /* create our structure that will contains all data */
    p_sys = calloc( 1, sizeof( demux_sys_t ) );
    if ( !p_sys )
        return VLC_EGENERIC;

781
    /* I need to seek */
782
    vlc_stream_Control( p_demux->s, STREAM_CAN_SEEK, &p_sys->b_seekable );
783 784
    if( p_sys->b_seekable )
        vlc_stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &p_sys->b_fastseekable );
785

786
    /*Set exported functions */
Laurent Aimar's avatar
Laurent Aimar committed
787 788
    p_demux->pf_demux = Demux;
    p_demux->pf_control = Control;
789

790
    p_sys->context.i_lastseqnumber = UINT32_MAX;
791

792
    p_demux->p_sys = p_sys;
793

794
    if( LoadInitFrag( p_demux ) != VLC_SUCCESS )
795 796
        goto error;

797
    MP4_BoxDumpStructure( p_demux->s, p_sys->p_root );
798

799
    if( ( p_ftyp = MP4_BoxGet( p_sys->p_root, "/ftyp" ) ) )
800
    {
801
        switch( BOXDATA(p_ftyp)->i_major_brand )
802
        {
803
            case BRAND_isom:
Laurent Aimar's avatar
Laurent Aimar committed
804
                msg_Dbg( p_demux,
François Cartegnie's avatar
François Cartegnie committed
805
                         "ISO Media (isom) version %d.",
806
                         BOXDATA(p_ftyp)->i_minor_version );
807
                break;
808 809 810 811
            case BRAND_3gp4:
            case BRAND_3gp5:
            case BRAND_3gp6:
            case BRAND_3gp7:
812 813
                msg_Dbg( p_demux, "3GPP Media Release: %4.4s",
                         (char *)&BOXDATA(p_ftyp)->i_major_brand );
814
                break;
815
            case BRAND_qt__:
François Cartegnie's avatar
François Cartegnie committed
816
                msg_Dbg( p_demux, "Apple QuickTime media" );
817
                break;
818
            case BRAND_isml:
François Cartegnie's avatar
François Cartegnie committed
819
                msg_Dbg( p_demux, "PIFF (= isml = fMP4) media" );
820
                break;
821
            case BRAND_dash:
François Cartegnie's avatar
François Cartegnie committed
822
                msg_Dbg( p_demux, "DASH Stream" );
823
                break;
824
            case BRAND_M4A:
825 826 827 828
                msg_Dbg( p_demux, "iTunes audio" );
                if( var_InheritBool( p_demux, CFG_PREFIX"m4a-audioonly" ) )
                    p_sys->hacks.es_cat_filters = AUDIO_ES;
                break;
829
            default:
Laurent Aimar's avatar
Laurent Aimar committed
830
                msg_Dbg( p_demux,
François Cartegnie's avatar
François Cartegnie committed
831
                         "unrecognized major media specification (%4.4s).",
832
                          (char*)&BOXDATA(p_ftyp)->i_major_brand );
833 834
                break;
        }
835 836 837
        /* also lookup in compatibility list */
        for(uint32_t i=0; i<BOXDATA(p_ftyp)->i_compatible_brands_count; i++)
        {
838
            if (BOXDATA(p_ftyp)->i_compatible_brands[i] == BRAND_dash)
839
            {
François Cartegnie's avatar
François Cartegnie committed
840
                msg_Dbg( p_demux, "DASH Stream" );
841
            }