mp4.c 175 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

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

49 50
#define CFG_PREFIX "mp4-"

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

54 55 56 57 58
#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.")

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

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

    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()
81
vlc_module_end ()
82

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

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

    mtime_t      i_pcr;

97
    uint64_t     i_moov_duration;
98 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 */
    uint64_t     i_nztime;             /* time position of the presentation (CLOCK_FREQ timescale) */
102 103
    unsigned int i_tracks;       /* number of tracks */
    mp4_track_t  *track;         /* array of track */
104
    float        f_fps;          /* number of frame per seconds */
105

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

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

114
    MP4_Box_t *p_moov;
115

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

124 125
    /* */
    MP4_Box_t    *p_tref_chap;
126 127

    /* */
128
    bool seekpoint_changed;
129
    int          i_seekpoint;
130
    input_title_t *p_title;
131
    vlc_meta_t    *p_meta;
132 133 134 135 136

    /* ASF in MP4 */
    asf_packet_sys_t asfpacketsys;
    uint64_t i_preroll;         /* foobar */
    int64_t  i_preroll_start;
137 138 139 140 141

    struct
    {
        int es_cat_filters;
    } hacks;
142 143

    mp4_fragments_index_t *p_fragsindex;
144 145
};

146
#define DEMUX_INCREMENT (CLOCK_FREQ / 4) /* How far the pcr will go, each round */
147 148
#define DEMUX_TRACK_MAX_PRELOAD (CLOCK_FREQ * 15) /* maximum preloading, to deal with interleaving */

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 165 166
static void MP4_TrackSetup( demux_t *, mp4_track_t *, MP4_Box_t  *, bool, bool );
static void MP4_TrackInit( mp4_track_t * );
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 );
Laurent Aimar's avatar
Laurent Aimar committed
171
static int  MP4_TrackSeek   ( demux_t *, mp4_track_t *, mtime_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 );
Laurent Aimar's avatar
Laurent Aimar committed
176
static void     MP4_TrackSetELST( demux_t *, mp4_track_t *, int64_t );
Laurent Aimar's avatar
Laurent Aimar committed
177

178
static void     MP4_UpdateSeekpoint( demux_t *, int64_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 190
static int  ProbeIndex( demux_t *p_demux );

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

static int FragGetMoofBySidxIndex( demux_t *p_demux, mtime_t i_target_time,
194
                                   uint64_t *pi_moof_pos, mtime_t *pi_sampletime );
195
static int FragGetMoofByTfraIndex( demux_t *p_demux, const mtime_t i_target_time, unsigned i_track_ID,
196
                                   uint64_t *pi_moof_pos, mtime_t *pi_sampletime );
197
static void FragResetContext( demux_sys_t * );
198

199 200 201 202 203
/* 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 );

204 205 206 207
/* 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 );

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

210 211
/* Helpers */

212 213 214 215 216 217 218 219 220 221 222 223 224 225
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;
}

226 227
static uint32_t stream_ReadU32( stream_t *s, void *p_read, uint32_t i_toread )
{
228
    ssize_t i_return = 0;
229 230
    if ( i_toread > INT32_MAX )
    {
231
        i_return = vlc_stream_Read( s, p_read, (size_t) INT32_MAX );
232 233 234 235 236
        if ( i_return < INT32_MAX )
            return i_return;
        else
            i_toread -= INT32_MAX;
    }
237
    i_return += vlc_stream_Read( s, (uint8_t *)p_read + i_return, (size_t) i_toread );
238 239 240
    return i_return;
}

241 242
static MP4_Box_t * MP4_GetTrexByTrackID( MP4_Box_t *p_moov, const uint32_t i_id )
{
243 244
    if(!p_moov)
        return NULL;
245 246 247
    MP4_Box_t *p_trex = MP4_BoxGet( p_moov, "mvex/trex" );
    while( p_trex )
    {
248 249
        if ( p_trex->i_type == ATOM_trex &&
             BOXDATA(p_trex) && BOXDATA(p_trex)->i_track_ID == i_id )
250 251 252 253 254 255 256
                break;
        else
            p_trex = p_trex->p_next;
    }
    return p_trex;
}

257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
/**
 * 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;
}

274 275 276 277 278 279 280
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 &&
281
            (p_tkhd = MP4_BoxGet( p_trak, "tkhd" )) && BOXDATA(p_tkhd) &&
282 283 284 285 286 287 288 289
            BOXDATA(p_tkhd)->i_track_ID == i_id )
                break;
        else
            p_trak = p_trak->p_next;
    }
    return p_trak;
}

290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
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;
}

306 307 308 309 310 311 312 313 314 315
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;
}

316
/* Return time in microsecond of a track */
Laurent Aimar's avatar
Laurent Aimar committed
317
static inline int64_t MP4_TrackGetDTS( demux_t *p_demux, mp4_track_t *p_track )
Laurent Aimar's avatar
Laurent Aimar committed
318
{
319
    demux_sys_t *p_sys = p_demux->p_sys;
320
    const mp4_chunk_t *p_chunk = &p_track->chunk[p_track->i_chunk];
321 322

    unsigned int i_index = 0;
323 324
    unsigned int i_sample = p_track->i_sample - p_chunk->i_sample_first;
    int64_t i_dts = p_chunk->i_first_dts;
Laurent Aimar's avatar
Laurent Aimar committed
325

326
    while( i_sample > 0 && i_index < p_chunk->i_entries_dts )
Laurent Aimar's avatar
Laurent Aimar committed
327
    {
328
        if( i_sample > p_chunk->p_sample_count_dts[i_index] )
Laurent Aimar's avatar
Laurent Aimar committed
329
        {
330 331 332
            i_dts += p_chunk->p_sample_count_dts[i_index] *
                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
333 334 335 336
            i_index++;
        }
        else
        {
337
            i_dts += i_sample * p_chunk->p_sample_delta_dts[i_index];
Laurent Aimar's avatar
Laurent Aimar committed
338 339 340 341
            break;
        }
    }

342 343
    i_dts = MP4_rescale( i_dts, p_track->i_timescale, CLOCK_FREQ );

344
    /* now handle elst */
345
    if( p_track->p_elst && p_track->BOXDATA(p_elst)->i_entry_count )
346
    {
347
        MP4_Box_data_elst_t *elst = p_track->BOXDATA(p_elst);
348 349 350 351 352 353

        /* 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 )
        {
354
            i_dts -= MP4_rescale( elst->i_media_time[p_track->i_elst], p_track->i_timescale, CLOCK_FREQ );
355 356 357
        }

        /* add i_elst_time */
358
        i_dts += MP4_rescale( p_track->i_elst_time, p_sys->i_timescale, CLOCK_FREQ );
359 360 361 362

        if( i_dts < 0 ) i_dts = 0;
    }

363
    return i_dts;
Laurent Aimar's avatar
Laurent Aimar committed
364
}
365

366 367
static inline bool MP4_TrackGetPTSDelta( demux_t *p_demux, mp4_track_t *p_track,
                                         int64_t *pi_delta )
Laurent Aimar's avatar
Laurent Aimar committed
368
{
369
    VLC_UNUSED( p_demux );
370
    mp4_chunk_t *ck = &p_track->chunk[p_track->i_chunk];
371

Laurent Aimar's avatar
Laurent Aimar committed
372 373 374 375
    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 )
376
        return false;
Laurent Aimar's avatar
Laurent Aimar committed
377

378
    for( i_index = 0; i_index < ck->i_entries_pts ; i_index++ )
Laurent Aimar's avatar
Laurent Aimar committed
379 380
    {
        if( i_sample < ck->p_sample_count_pts[i_index] )
381
        {
382 383
            *pi_delta = MP4_rescale( ck->p_sample_offset_pts[i_index],
                                     p_track->i_timescale, CLOCK_FREQ );
384 385
            return true;
        }
Laurent Aimar's avatar
Laurent Aimar committed
386 387 388

        i_sample -= ck->p_sample_count_pts[i_index];
    }
389
    return false;
Laurent Aimar's avatar
Laurent Aimar committed
390 391
}

392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 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
static inline mtime_t MP4_GetSamplesDuration( demux_t *p_demux, mp4_track_t *p_track,
                                              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;
        }
    }

    return MP4_rescale( i_duration, p_track->i_timescale, CLOCK_FREQ );
}

Laurent Aimar's avatar
Laurent Aimar committed
439 440
static inline int64_t MP4_GetMoviePTS(demux_sys_t *p_sys )
{
441
    return p_sys->i_nztime;
Laurent Aimar's avatar
Laurent Aimar committed
442
}
443

444 445
static void LoadChapter( demux_t  *p_demux );

446
static int LoadInitFrag( demux_t *p_demux )
447 448 449
{
    demux_sys_t *p_sys = p_demux->p_sys;

450 451
    /* Load all boxes ( except raw data ) */
    if( ( p_sys->p_root = MP4_BoxGetRoot( p_demux->s ) ) == NULL )
452
    {
453
        goto LoadInitFragError;
454
    }
455

456 457 458 459 460 461 462
    return VLC_SUCCESS;

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

463
static int CreateTracks( demux_t *p_demux, unsigned i_tracks )
464 465 466
{
    demux_sys_t *p_sys = p_demux->p_sys;

467 468 469
    if( SIZE_MAX / i_tracks < sizeof(mp4_track_t) )
        return VLC_EGENERIC;

Thomas Guillem's avatar
Thomas Guillem committed
470
    p_sys->track = vlc_alloc( i_tracks, sizeof(mp4_track_t)  );
471
    if( p_sys->track == NULL )
472 473
        return VLC_ENOMEM;
    p_sys->i_tracks = i_tracks;
474

475 476 477
    for( unsigned i=0; i<i_tracks; i++ )
        MP4_TrackInit( &p_sys->track[i] );

478 479 480
    return VLC_SUCCESS;
}

481 482 483 484
static block_t * MP4_EIA608_Convert( block_t * p_block )
{
    /* Rebuild codec data from encap */
    size_t i_copied = 0;
485
    size_t i_remaining = __MIN(p_block->i_buffer, INT64_MAX / 3);
486 487 488
    uint32_t i_bytes = 0;
    block_t *p_newblock;

489
    /* always need at least 10 bytes (atom size+header+1pair)*/
490 491
    if ( i_remaining < 10 ||
         !(i_bytes = GetDWBE(p_block->p_buffer)) ||
492
         (i_bytes > i_remaining) ||
493 494 495 496 497 498 499 500 501 502 503 504 505 506
         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
    {
507
        p_write[i_copied++] = CC_PKT_BYTE0(0); /* cc1 == field 0 */
508 509 510 511 512 513 514
        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 );

515
    /* cdt2 is optional */
516 517
    if ( i_remaining >= 10 &&
         (i_bytes = GetDWBE(p_read)) &&
518
         (i_bytes <= i_remaining) &&
519 520 521 522 523 524 525
         !memcmp("cdt2", &p_read[4], 4) )
    {
        p_read += 8;
        i_bytes -= 8;
        i_remaining -= 8;
        do
        {
526
            p_write[i_copied++] = CC_PKT_BYTE0(0); /* cc1 == field 0 */
527 528 529 530 531 532 533
            p_write[i_copied++] = p_read[0];
            p_write[i_copied++] = p_read[1];
            p_read += 2;
            i_bytes -= 2;
        } while( i_bytes >= 2 );
    }

534
    p_newblock->i_pts = p_block->i_dts;
535
    p_newblock->i_buffer = i_copied;
536 537 538
    p_newblock->i_flags = BLOCK_FLAG_TYPE_P;
    block_Release( p_block );

539 540 541
    return p_newblock;
}

542 543 544 545 546 547 548
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;
}

549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
/* Analyzes chunks to find max interleave length
 * sets flat flag if no interleaving is in use */
static void MP4_GetInterleaving( demux_t *p_demux, uint64_t *pi_max_contiguous, bool *pb_flat )
{
    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;
        }

588 589 590 591 592
        /* 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;

593 594
        if( tk != nexttk )
        {
595
            i_duration = MP4_rescale( i_duration, tk->i_timescale, CLOCK_FREQ );
596 597 598 599 600 601
            if( i_duration > *pi_max_contiguous )
                *pi_max_contiguous = i_duration;
            i_duration = 0;

            if( tk->i_chunk != tk->i_chunk_count )
                *pb_flat = false;
602 603 604

            if( nexttk && nexttk->i_chunk > 0 ) /* new run number */
                nexttk->chunk[nexttk->i_chunk].i_virtual_run_number++;
605 606 607 608 609 610 611 612 613 614
        }

        tk = nexttk;
    }

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

615
static block_t * MP4_Block_Convert( demux_t *p_demux, const mp4_track_t *p_track, block_t *p_block )
616 617 618 619 620 621
{
    /* might have some encap */
    if( p_track->fmt.i_cat == SPU_ES )
    {
        switch( p_track->fmt.i_codec )
        {
622
            case VLC_CODEC_WEBVTT:
François Cartegnie's avatar
François Cartegnie committed
623
            case VLC_CODEC_TTML:
624 625
            case VLC_CODEC_TX3G:
            case VLC_CODEC_SPU:
626
            case VLC_CODEC_SUBT:
627 628
            /* accept as-is */
            break;
629
            case VLC_CODEC_CEA608:
630 631
                p_block = MP4_EIA608_Convert( p_block );
            break;
632 633 634 635 636
        default:
            p_block->i_buffer = 0;
            break;
        }
    }
637 638 639 640
    else if( p_track->fmt.i_original_fourcc == ATOM_rrtp )
    {
        p_block = MP4_RTPHint_Convert( p_demux, p_block, p_track->fmt.i_codec );
    }
641 642 643 644

    return p_block;
}

645 646
static void MP4_Block_Send( demux_t *p_demux, mp4_track_t *p_track, block_t *p_block )
{
647 648
    demux_sys_t *p_sys = p_demux->p_sys;

649 650 651 652
    p_block = MP4_Block_Convert( p_demux, p_track, p_block );
    if( p_block == NULL )
        return;

653
    if ( p_track->b_chans_reorder )
654 655 656 657 658 659
    {
        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 );
    }
660

661
    p_block->i_flags |= p_track->i_block_flags;
662 663 664 665 666
    if( p_track->i_next_block_flags )
    {
        p_block->i_flags |= p_track->i_next_block_flags;
        p_track->i_next_block_flags = 0;
    }
667

668 669 670 671 672
    /* ASF packets in mov */
    if( p_track->p_asf )
    {
        /* Fake a new stream from MP4 block */
        stream_t *p_stream = p_demux->s;
673
        p_demux->s = vlc_stream_MemoryNew( p_demux, p_block->p_buffer, p_block->i_buffer, true );
674 675 676 677 678
        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 */
679
            DemuxASFPacket( &p_sys->asfpacketsys, p_block->i_buffer, p_block->i_buffer );
680
            vlc_stream_Delete(p_demux->s);
681 682 683 684 685 686
        }
        block_Release(p_block);
        p_demux->s = p_stream;
    }
    else
        es_out_Send( p_demux->out, p_track->p_es, p_block );
687 688
}

689 690 691
int  OpenHEIF ( vlc_object_t * );
void CloseHEIF( vlc_object_t * );

692
/*****************************************************************************
693
 * Open: check file and initializes MP4 structures
694
 *****************************************************************************/
695
static int Open( vlc_object_t * p_this )
696
{
Laurent Aimar's avatar
Laurent Aimar committed
697
    demux_t  *p_demux = (demux_t *)p_this;
698
    demux_sys_t     *p_sys;
699

Christophe Mutricy's avatar
Christophe Mutricy committed
700
    const uint8_t   *p_peek;
701

702
    MP4_Box_t       *p_ftyp;
703 704
    const MP4_Box_t *p_mvhd = NULL;
    const MP4_Box_t *p_mvex = NULL;
705

706
    bool      b_enabled_es;
707

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

711
    switch( VLC_FOURCC( p_peek[4], p_peek[5], p_peek[6], p_peek[7] ) )
712
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
713 714 715 716 717 718 719 720
        case ATOM_moov:
        case ATOM_foov:
        case ATOM_moof:
        case ATOM_mdat:
        case ATOM_udta:
        case ATOM_free:
        case ATOM_skip:
        case ATOM_wide:
721
        case ATOM_uuid:
722
        case VLC_FOURCC( 'p', 'n', 'o', 't' ):
723
            break;
724
        case ATOM_ftyp:
725 726 727 728
        {
            /* Early handle some brands */
            switch( VLC_FOURCC(p_peek[8], p_peek[9], p_peek[10], p_peek[11]) )
            {
729 730 731 732
                /* HEIF pictures goes to heif demux */
                case MAJOR_heic:
                case MAJOR_heix:
                case MAJOR_mif1:
733 734 735 736 737 738
                /* We don't yet support f4v, but avformat does. */
                case MAJOR_f4v:
                    return VLC_EGENERIC;
                default:
                    break;
            }
739
            break;
740
        }
741
         default:
742
            return VLC_EGENERIC;
743
    }
744

745 746 747 748 749
    /* create our structure that will contains all data */
    p_sys = calloc( 1, sizeof( demux_sys_t ) );
    if ( !p_sys )
        return VLC_EGENERIC;

750
    /* I need to seek */
751
    vlc_stream_Control( p_demux->s, STREAM_CAN_SEEK, &p_sys->b_seekable );
752 753
    if( p_sys->b_seekable )
        vlc_stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &p_sys->b_fastseekable );
754

755
    /*Set exported functions */
Laurent Aimar's avatar
Laurent Aimar committed
756 757
    p_demux->pf_demux = Demux;
    p_demux->pf_control = Control;
758

759
    p_sys->context.i_lastseqnumber = UINT32_MAX;
760

761
    p_demux->p_sys = p_sys;
762

763
    if( LoadInitFrag( p_demux ) != VLC_SUCCESS )
764 765
        goto error;

766
    MP4_BoxDumpStructure( p_demux->s, p_sys->p_root );
767

768
    if( ( p_ftyp = MP4_BoxGet( p_sys->p_root, "/ftyp" ) ) )
769
    {
770
        switch( BOXDATA(p_ftyp)->i_major_brand )
771
        {
772
            case MAJOR_isom:
Laurent Aimar's avatar
Laurent Aimar committed
773
                msg_Dbg( p_demux,
François Cartegnie's avatar
François Cartegnie committed
774
                         "ISO Media (isom) version %d.",
775
                         BOXDATA(p_ftyp)->i_minor_version );
776
                break;
777 778 779 780
            case MAJOR_3gp4:
            case MAJOR_3gp5:
            case MAJOR_3gp6:
            case MAJOR_3gp7:
781 782
                msg_Dbg( p_demux, "3GPP Media Release: %4.4s",
                         (char *)&BOXDATA(p_ftyp)->i_major_brand );
783
                break;
784
            case MAJOR_qt__:
François Cartegnie's avatar
François Cartegnie committed
785
                msg_Dbg( p_demux, "Apple QuickTime media" );
786
                break;
787
            case MAJOR_isml:
François Cartegnie's avatar
François Cartegnie committed
788
                msg_Dbg( p_demux, "PIFF (= isml = fMP4) media" );
789
                break;
790
            case MAJOR_dash:
François Cartegnie's avatar
François Cartegnie committed
791
                msg_Dbg( p_demux, "DASH Stream" );
792
                break;
793 794 795 796 797
            case MAJOR_M4A:
                msg_Dbg( p_demux, "iTunes audio" );
                if( var_InheritBool( p_demux, CFG_PREFIX"m4a-audioonly" ) )
                    p_sys->hacks.es_cat_filters = AUDIO_ES;
                break;
798
            default:
Laurent Aimar's avatar
Laurent Aimar committed
799
                msg_Dbg( p_demux,
François Cartegnie's avatar
François Cartegnie committed
800
                         "unrecognized major media specification (%4.4s).",
801
                          (char*)&BOXDATA(p_ftyp)->i_major_brand );
802 803
                break;
        }
804 805 806 807 808
        /* also lookup in compatibility list */
        for(uint32_t i=0; i<BOXDATA(p_ftyp)->i_compatible_brands_count; i++)
        {
            if (BOXDATA(p_ftyp)->i_compatible_brands[i] == MAJOR_dash)
            {
François Cartegnie's avatar
François Cartegnie committed
809
                msg_Dbg( p_demux, "DASH Stream" );
810
            }
811 812 813 814
            else if (BOXDATA(p_ftyp)->i_compatible_brands[i] == VLC_FOURCC('s', 'm', 'o', 'o') )
            {
                msg_Dbg( p_demux, "Handling VLC Smooth Stream" );
            }
815
        }
816 817 818
    }
    else
    {
François Cartegnie's avatar
François Cartegnie committed
819
        msg_Dbg( p_demux, "file type box missing (assuming ISO Media)" );
820 821
    }

822 823 824
    /* the file need to have one moov box */
    p_sys->p_moov = MP4_BoxGet( p_sys->p_root, "/moov" );
    if( unlikely(!p_sys->p_moov) )
825
    {
826 827 828 829 830 831 832 833
        p_sys->p_moov = MP4_BoxGet( p_sys->p_root, "/foov" );
        if( !p_sys->p_moov )
        {
            msg_Err( p_demux, "MP4 plugin discarded (no moov,foov,moof box)" );
            goto error;
        }
        /* we have a free box as a moov, rename it */
        p_sys->p_moov->i_type = ATOM_moov;
834 835
    }

836
    p_mvhd = MP4_BoxGet( p_sys->p_moov, "mvhd" );
837
    if( p_mvhd && BOXDATA(p_mvhd) && BOXDATA(p_mvhd)->i_timescale )
838 839 840 841 842 843 844 845
    {
        p_sys->i_timescale = BOXDATA(p_mvhd)->i_timescale;
        p_sys->i_moov_duration = p_sys->i_duration = BOXDATA(p_mvhd)->i_duration;
        p_sys->i_cumulated_duration = BOXDATA(p_mvhd)->i_duration;
    }
    else
    {
        msg_Warn( p_demux, "No valid mvhd found" );
846
        goto error;
847 848
    }

849 850
    MP4_Box_t *p_rmra = MP4_BoxGet( p_sys->p_root, "/moov/rmra" );
    if( p_rmra != NULL && p_demux->p_input != NULL )
851 852 853 854
    {
        int        i_count = MP4_BoxCount( p_rmra, "rmda" );
        int        i;

Laurent Aimar's avatar
Laurent Aimar committed
855
        msg_Dbg( p_demux, "detected playlist mov file (%d ref)", i_count );
856

857
        input_thread_t *p_input = p_demux->p_input;
858
        input_item_t *p_current = input_GetItem( p_input );
859

860 861
        input_item_node_t *p_subitems = input_item_node_Create( p_current );

862
        for( i = 0; i < i_count; i++ )
863
        {
864 865 866
            MP4_Box_t *p_rdrf = MP4_BoxGet( p_rmra, "rmda[%d]/rdrf", i );
            char      *psz_ref;
            uint32_t  i_ref_type;
zorglub's avatar
zorglub committed
867

868
            if( !p_rdrf || !BOXDATA(p_rdrf) || !( psz_ref = strdup( BOXDATA(p_rdrf)->psz_ref ) ) )
869
            {
870 871
                continue;
            }
872
            i_ref_type = BOXDATA(p_rdrf)->i_ref_type;
873

874 875 876 877 878 879
            msg_Dbg( p_demux, "new ref=`%s' type=%4.4s",
                     psz_ref, (char*)&i_ref_type );

            if( i_ref_type == VLC_FOURCC( 'u', 'r', 'l', ' ' ) )
            {
                if( strstr( psz_ref, "qt5gateQT" ) )
880
                {
881
                    msg_Dbg( p_demux, "ignoring pseudo ref =`%s'", psz_ref );
882
                    free( psz_ref );
883 884
                    continue;
                }
885 886
                if( !strncmp( psz_ref, "http://", 7 ) ||
                    !strncmp( psz_ref, "rtsp://", 7 ) )
887
                {
888
                    ;