mp4.c 177 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 25 26
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

27 28 29 30
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

31 32
#include "mp4.h"

33
#include <vlc_plugin.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
34

Clément Stenac's avatar
Clément Stenac committed
35
#include <vlc_demux.h>
36
#include <vlc_charset.h>                           /* EnsureUTF8 */
37
#include <vlc_input.h>
38
#include <vlc_aout.h>
39
#include <assert.h>
40
#include <limits.h>
41

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

48 49 50 51
vlc_module_begin ()
    set_category( CAT_INPUT )
    set_subcategory( SUBCAT_INPUT_DEMUX )
    set_description( N_("MP4 stream demuxer") )
52
    set_shortname( N_("MP4") )
53
    set_capability( "demux", 240 )
54 55
    set_callbacks( Open, Close )
vlc_module_end ()
56

57 58 59
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
60
static int   Demux   ( demux_t * );
61
static int   DemuxRef( demux_t *p_demux ){ (void)p_demux; return 0;}
62
static int   DemuxFrg( demux_t * );
63
static int   DemuxAsLeaf( demux_t * );
64 65
static int   Seek    ( demux_t *, mtime_t );
static int   Control ( demux_t *, int, va_list );
66

67 68 69 70 71 72
struct demux_sys_t
{
    MP4_Box_t    *p_root;      /* container for the whole file */

    mtime_t      i_pcr;

73
    uint64_t     i_overall_duration; /* Full duration, including all fragments */
74 75
    uint64_t     i_time;         /* time position of the presentation
                                  * in movie timescale */
76
    uint32_t     i_timescale;    /* movie time scale */
77 78
    unsigned int i_tracks;       /* number of tracks */
    mp4_track_t  *track;         /* array of track */
79
    float        f_fps;          /* number of frame per seconds */
80

81
    bool         b_fragmented;   /* fMP4 */
82 83
    bool         b_seekable;
    bool         b_fastseekable;
84
    bool         b_seekmode;
85
    bool         b_smooth;       /* Is it Smooth Streaming? */
86
    bool         b_dash;
87

88
    bool            b_index_probed;
89 90 91 92
    bool            b_fragments_probed;
    mp4_fragment_t  moovfragment; /* moov */
    mp4_fragment_t *p_fragments;  /* known fragments (moof following moov) */

93 94 95 96 97
    struct
    {
        mp4_fragment_t *p_fragment;
        uint32_t        i_current_box_type;
        uint32_t        i_mdatbytesleft;
98
        uint32_t        i_lastseqnumber;
99 100
    } context;

101 102
    /* */
    MP4_Box_t    *p_tref_chap;
103 104 105

    /* */
    input_title_t *p_title;
106 107 108 109 110

    /* ASF in MP4 */
    asf_packet_sys_t asfpacketsys;
    uint64_t i_preroll;         /* foobar */
    int64_t  i_preroll_start;
111 112
};

113
/*****************************************************************************
114
 * Declaration of local function
115
 *****************************************************************************/
116
static void MP4_TrackCreate ( demux_t *, mp4_track_t *, MP4_Box_t  *, bool b_force_enable );
117
static int  MP4_SmoothTrackCreate( demux_t *, mp4_track_t *, const MP4_Box_t *);
118
static void MP4_TrackDestroy( demux_t *, mp4_track_t * );
119

120
static block_t * MP4_Block_Read( demux_t *, const mp4_track_t *, int );
121 122
static void MP4_Block_Send( demux_t *, mp4_track_t *, block_t * );

Laurent Aimar's avatar
Laurent Aimar committed
123 124
static int  MP4_TrackSelect ( demux_t *, mp4_track_t *, mtime_t );
static void MP4_TrackUnselect(demux_t *, mp4_track_t * );
125

Laurent Aimar's avatar
Laurent Aimar committed
126
static int  MP4_TrackSeek   ( demux_t *, mp4_track_t *, mtime_t );
127

Laurent Aimar's avatar
Laurent Aimar committed
128
static uint64_t MP4_TrackGetPos    ( mp4_track_t * );
129
static uint32_t MP4_TrackGetReadSize( mp4_track_t *, uint32_t * );
130
static int      MP4_TrackNextSample( demux_t *, mp4_track_t *, uint32_t );
Laurent Aimar's avatar
Laurent Aimar committed
131
static void     MP4_TrackSetELST( demux_t *, mp4_track_t *, int64_t );
Laurent Aimar's avatar
Laurent Aimar committed
132

133 134
static void     MP4_UpdateSeekpoint( demux_t * );

135
static MP4_Box_t * MP4_GetTrexByTrackID( MP4_Box_t *p_moov, const uint32_t i_id );
136 137 138 139
static void MP4_GetDefaultSizeAndDuration( demux_t *p_demux,
                                           const MP4_Box_data_tfhd_t *p_tfhd_data,
                                           uint32_t *pi_default_size,
                                           uint32_t *pi_default_duration );
140

141
static bool AddFragment( demux_t *p_demux, MP4_Box_t *p_moox );
142
static int  ProbeFragments( demux_t *p_demux, bool b_force );
143 144 145 146 147 148
static int  ProbeIndex( demux_t *p_demux );
static mp4_fragment_t *GetFragmentByPos( demux_t *p_demux, uint64_t i_pos, bool b_exact );
static mp4_fragment_t *GetFragmentByTime( demux_t *p_demux, const mtime_t i_time );

static int LeafIndexGetMoofPosByTime( demux_t *p_demux, const mtime_t i_target_time,
                                      uint64_t *pi_pos, mtime_t *pi_mooftime );
149
static mtime_t LeafGetTrackFragmentTimeOffset( demux_t *p_demux, mp4_fragment_t *, unsigned int );
150 151 152
static int LeafGetTrackAndChunkByMOOVPos( demux_t *p_demux, uint64_t *pi_pos,
                                      mp4_track_t **pp_tk, unsigned int *pi_chunk );
static int LeafMapTrafTrunContextes( demux_t *p_demux, MP4_Box_t *p_moof );
153

154 155 156 157 158
/* 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 );

159 160
/* Helpers */

161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
static uint32_t stream_ReadU32( stream_t *s, void *p_read, uint32_t i_toread )
{
    uint32_t i_return = 0;
    if ( i_toread > INT32_MAX )
    {
        i_return = stream_Read( s, p_read, INT32_MAX );
        if ( i_return < INT32_MAX )
            return i_return;
        else
            i_toread -= INT32_MAX;
    }
    i_return += stream_Read( s, (uint8_t *)p_read + i_return, (int32_t) i_toread );
    return i_return;
}

176 177 178 179 180 181 182 183 184 185 186 187
static bool MP4_stream_Tell( stream_t *s, uint64_t *pi_pos )
{
    int64_t i_pos = stream_Tell( s );
    if ( i_pos < 0 )
        return false;
    else
    {
        *pi_pos = (uint64_t) i_pos;
        return true;
    }
}

188 189 190 191 192 193 194 195 196 197 198
static mtime_t GetTrackDurationInFragment( const mp4_fragment_t *p_fragment,
                                           unsigned int i_track_ID )
{
    for( unsigned int i=0; i<p_fragment->i_durations; i++ )
    {
        if( i_track_ID == p_fragment->p_durations[i].i_track_ID )
            return p_fragment->p_durations[i].i_duration;
    }
    return 0;
}

199 200
static MP4_Box_t * MP4_GetTrexByTrackID( MP4_Box_t *p_moov, const uint32_t i_id )
{
201 202
    if(!p_moov)
        return NULL;
203 204 205
    MP4_Box_t *p_trex = MP4_BoxGet( p_moov, "mvex/trex" );
    while( p_trex )
    {
206 207
        if ( p_trex->i_type == ATOM_trex &&
             BOXDATA(p_trex) && BOXDATA(p_trex)->i_track_ID == i_id )
208 209 210 211 212 213 214
                break;
        else
            p_trex = p_trex->p_next;
    }
    return p_trex;
}

215 216 217 218 219 220 221
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 &&
222
            (p_tkhd = MP4_BoxGet( p_trak, "tkhd" )) && BOXDATA(p_tkhd) &&
223 224 225 226 227 228 229 230
            BOXDATA(p_tkhd)->i_track_ID == i_id )
                break;
        else
            p_trak = p_trak->p_next;
    }
    return p_trak;
}

231
/* Return time in microsecond of a track */
Laurent Aimar's avatar
Laurent Aimar committed
232
static inline int64_t MP4_TrackGetDTS( demux_t *p_demux, mp4_track_t *p_track )
Laurent Aimar's avatar
Laurent Aimar committed
233
{
234
    demux_sys_t *p_sys = p_demux->p_sys;
235
    const mp4_chunk_t *p_chunk;
236
    if( p_sys->b_fragmented )
237
        p_chunk = p_track->cchunk;
238
    else
239
        p_chunk = &p_track->chunk[p_track->i_chunk];
240 241

    unsigned int i_index = 0;
242 243
    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
244

245
    while( i_sample > 0 && i_index < p_chunk->i_entries_dts )
Laurent Aimar's avatar
Laurent Aimar committed
246
    {
247
        if( i_sample > p_chunk->p_sample_count_dts[i_index] )
Laurent Aimar's avatar
Laurent Aimar committed
248
        {
249 250 251
            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
252 253 254 255
            i_index++;
        }
        else
        {
256
            i_dts += i_sample * p_chunk->p_sample_delta_dts[i_index];
Laurent Aimar's avatar
Laurent Aimar committed
257 258 259 260
            break;
        }
    }

261 262 263
    /* now handle elst */
    if( p_track->p_elst )
    {
Laurent Aimar's avatar
Laurent Aimar committed
264
        demux_sys_t         *p_sys = p_demux->p_sys;
265
        MP4_Box_data_elst_t *elst = p_track->BOXDATA(p_elst);
266 267 268 269 270 271 272 273 274 275

        /* 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 )
        {
            i_dts -= elst->i_media_time[p_track->i_elst];
        }

        /* add i_elst_time */
276 277
        i_dts += p_track->i_elst_time * p_track->i_timescale /
            p_sys->i_timescale;
278 279 280 281

        if( i_dts < 0 ) i_dts = 0;
    }

282
    return CLOCK_FREQ * i_dts / p_track->i_timescale;
Laurent Aimar's avatar
Laurent Aimar committed
283
}
284

285 286
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
287
{
288 289 290 291 292 293 294
    demux_sys_t *p_sys = p_demux->p_sys;
    mp4_chunk_t *ck;
    if( p_sys->b_fragmented )
        ck = p_track->cchunk;
    else
        ck = &p_track->chunk[p_track->i_chunk];

Laurent Aimar's avatar
Laurent Aimar committed
295 296 297 298
    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 )
299
        return false;
Laurent Aimar's avatar
Laurent Aimar committed
300

301
    for( i_index = 0; i_index < ck->i_entries_pts ; i_index++ )
Laurent Aimar's avatar
Laurent Aimar committed
302 303
    {
        if( i_sample < ck->p_sample_count_pts[i_index] )
304 305 306 307 308
        {
            *pi_delta = ck->p_sample_offset_pts[i_index] * CLOCK_FREQ /
                        (int64_t)p_track->i_timescale;
            return true;
        }
Laurent Aimar's avatar
Laurent Aimar committed
309 310 311

        i_sample -= ck->p_sample_count_pts[i_index];
    }
312
    return false;
Laurent Aimar's avatar
Laurent Aimar committed
313 314
}

Laurent Aimar's avatar
Laurent Aimar committed
315 316
static inline int64_t MP4_GetMoviePTS(demux_sys_t *p_sys )
{
317
    return CLOCK_FREQ * p_sys->i_time / p_sys->i_timescale;
Laurent Aimar's avatar
Laurent Aimar committed
318
}
319

320 321
static void LoadChapter( demux_t  *p_demux );

322
static int LoadInitFrag( demux_t *p_demux )
323 324 325
{
    demux_sys_t *p_sys = p_demux->p_sys;

326
    if( p_sys->b_smooth ) /* Smooth Streaming */
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
    {
        if( ( p_sys->p_root = MP4_BoxGetSmooBox( p_demux->s ) ) == NULL )
        {
            goto LoadInitFragError;
        }
        else
        {
            MP4_Box_t *p_smoo = MP4_BoxGet( p_sys->p_root, "uuid" );
            if( !p_smoo || CmpUUID( &p_smoo->i_uuid, &SmooBoxUUID ) )
                goto LoadInitFragError;
            /* Get number of tracks */
            p_sys->i_tracks = 0;
            for( int i = 0; i < 3; i++ )
            {
                MP4_Box_t *p_stra = MP4_BoxGet( p_smoo, "uuid[%d]", i );
342
                if( p_stra && BOXDATA(p_stra) && BOXDATA(p_stra)->i_track_ID )
343 344
                    p_sys->i_tracks++;
                /* Get timescale and duration of the video track; */
345
                if( p_sys->i_timescale == 0 )
346
                {
347 348 349 350 351
                    if ( p_stra && BOXDATA(p_stra) )
                    {
                        p_sys->i_timescale = BOXDATA(p_stra)->i_timescale;
                        p_sys->i_overall_duration = BOXDATA(p_stra)->i_duration;
                    }
352 353 354 355 356
                    if( p_sys->i_timescale == 0 )
                    {
                        msg_Err( p_demux, "bad timescale" );
                        goto LoadInitFragError;
                    }
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
                }
            }
        }
    }
    else
    {
        /* Load all boxes ( except raw data ) */
        if( ( p_sys->p_root = MP4_BoxGetRoot( p_demux->s ) ) == NULL )
        {
            goto LoadInitFragError;
        }
    }
    return VLC_SUCCESS;

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

376
static int AllocateTracks( demux_t *p_demux, unsigned i_tracks )
377 378 379
{
    demux_sys_t *p_sys = p_demux->p_sys;

380
    p_sys->track = calloc( i_tracks, sizeof( mp4_track_t ) );
381
    if( p_sys->track == NULL )
382 383
        return VLC_ENOMEM;
    p_sys->i_tracks = i_tracks;
384 385 386

    if( p_sys->b_fragmented )
    {
387
        for( unsigned i = 0; i < i_tracks; i++ )
388
        {
389
            mp4_track_t *p_track = &p_sys->track[i];
390 391
            p_track->cchunk = calloc( 1, sizeof( mp4_chunk_t ) );
            if( unlikely( !p_track->cchunk ) )
392
                return VLC_ENOMEM;
393 394 395 396 397
        }
    }
    return VLC_SUCCESS;
}

398
static int CreateTracksFromSmooBox( demux_t *p_demux )
399 400 401 402
{
    demux_sys_t *p_sys = p_demux->p_sys;

    MP4_Box_t *p_smoo = MP4_BoxGet( p_sys->p_root, "uuid" );
403 404 405 406 407 408 409 410 411 412 413 414
    if( CmpUUID( &p_smoo->i_uuid, &SmooBoxUUID ) )
        return VLC_EGENERIC;

    /* Smooth tracks are stra UUID box below smooth UUID box */
    const unsigned i_tracks = MP4_BoxCount( p_smoo, "uuid" );

    if( AllocateTracks( p_demux, i_tracks ) != VLC_SUCCESS )
        return VLC_EGENERIC;

    unsigned j = 0;
    MP4_Box_t *p_stra = MP4_BoxGet( p_smoo, "uuid" );
    while( p_stra && j < p_sys->i_tracks )
415
    {
416 417
        if( !CmpUUID( &p_stra->i_uuid, &StraBoxUUID ) &&
            BOXDATA(p_stra) && BOXDATA(p_stra)->i_track_ID > 0 )
418
        {
419
            mp4_track_t *p_track = &p_sys->track[j++];
420
            MP4_SmoothTrackCreate( p_demux, p_track, p_stra );
421
        }
422
        p_stra = p_stra->p_next;
423
    }
424 425

    return VLC_SUCCESS;
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 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
static block_t * MP4_EIA608_Convert( block_t * p_block )
{
    /* Rebuild codec data from encap */
    size_t i_copied = 0;
    size_t i_remaining = p_block->i_buffer;
    uint32_t i_bytes = 0;
    block_t *p_newblock;

    if ( i_remaining < 10 ||
         !(i_bytes = GetDWBE(p_block->p_buffer)) ||
         (i_bytes + 8 > i_remaining) ||
         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
    {
        p_write[i_copied++] = 0; /* cc1 == field 0 */
        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 );

    if ( i_remaining >= 10 &&
         (i_bytes = GetDWBE(p_read)) &&
         (i_bytes + 8 <= i_remaining) &&
         !memcmp("cdt2", &p_read[4], 4) )
    {
        p_read += 8;
        i_bytes -= 8;
        i_remaining -= 8;
        do
        {
            p_write[i_copied++] = 0; /* cc1 == field 0 */
            p_write[i_copied++] = p_read[0];
            p_write[i_copied++] = p_read[1];
            p_read += 2;
            i_bytes -= 2;
        } while( i_bytes >= 2 );
    }

    block_Release( p_block );
    p_newblock->i_buffer = i_copied;
    return p_newblock;
}

484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
static block_t * MP4_Block_Read( demux_t *p_demux, const mp4_track_t *p_track, int i_size )
{
    block_t *p_block = stream_Block( p_demux->s, i_size );
    if ( !p_block )
        return NULL;

    /* might have some encap */
    if( p_track->fmt.i_cat == SPU_ES )
    {
        switch( p_track->fmt.i_codec )
        {
            case VLC_CODEC_TX3G:
            case VLC_CODEC_SPU:
            /* accept as-is */
            break;
499 500 501
            case VLC_CODEC_EIA608_1:
                p_block = MP4_EIA608_Convert( p_block );
            break;
502 503 504 505 506 507 508 509 510
        default:
            p_block->i_buffer = 0;
            break;
        }
    }

    return p_block;
}

511 512 513 514 515 516 517 518 519
static void MP4_Block_Send( demux_t *p_demux, mp4_track_t *p_track, block_t *p_block )
{
    if ( p_track->b_chans_reorder && aout_BitsPerSample( p_track->fmt.i_codec ) )
    {
        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 );
    }
520

521 522
    p_block->i_flags |= p_track->i_block_flags;

523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541
    /* ASF packets in mov */
    if( p_track->p_asf )
    {
        /* Fake a new stream from MP4 block */
        stream_t *p_stream = p_demux->s;
        p_demux->s = stream_MemoryNew( p_demux, p_block->p_buffer, p_block->i_buffer, true );
        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 */
            DemuxASFPacket( &p_demux->p_sys->asfpacketsys, p_block->i_buffer, p_block->i_buffer );
            stream_Delete(p_demux->s);
        }
        block_Release(p_block);
        p_demux->s = p_stream;
    }
    else
        es_out_Send( p_demux->out, p_track->p_es, p_block );
542 543
}

544
/*****************************************************************************
545
 * Open: check file and initializes MP4 structures
546
 *****************************************************************************/
547
static int Open( vlc_object_t * p_this )
548
{
Laurent Aimar's avatar
Laurent Aimar committed
549
    demux_t  *p_demux = (demux_t *)p_this;
550
    demux_sys_t     *p_sys;
551

Christophe Mutricy's avatar
Christophe Mutricy committed
552
    const uint8_t   *p_peek;
553

554
    MP4_Box_t       *p_ftyp;
555
    MP4_Box_t       *p_rmra;
556 557
    MP4_Box_t       *p_mvhd;
    MP4_Box_t       *p_trak;
558

559
    unsigned int    i;
560
    bool      b_enabled_es;
561

562
    /* A little test to see if it could be a mp4 */
563
    if( stream_Peek( p_demux->s, &p_peek, 11 ) < 11 ) return VLC_EGENERIC;
564

565
    switch( VLC_FOURCC( p_peek[4], p_peek[5], p_peek[6], p_peek[7] ) )
566
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
567 568 569 570 571 572 573 574
        case ATOM_moov:
        case ATOM_foov:
        case ATOM_moof:
        case ATOM_mdat:
        case ATOM_udta:
        case ATOM_free:
        case ATOM_skip:
        case ATOM_wide:
575
        case ATOM_uuid:
576
        case VLC_FOURCC( 'p', 'n', 'o', 't' ):
577
            break;
578 579 580 581 582
        case ATOM_ftyp:
            /* We don't yet support f4v, but avformat does. */
            if( p_peek[8] == 'f' && p_peek[9] == '4' && p_peek[10] == 'v' )
                return VLC_EGENERIC;
            break;
583
         default:
584
            return VLC_EGENERIC;
585
    }
586

587 588 589 590 591
    /* create our structure that will contains all data */
    p_sys = calloc( 1, sizeof( demux_sys_t ) );
    if ( !p_sys )
        return VLC_EGENERIC;

592
    /* I need to seek */
593 594
    stream_Control( p_demux->s, STREAM_CAN_SEEK, &p_sys->b_seekable );
    stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &p_sys->b_fastseekable );
595
    p_sys->b_seekmode = p_sys->b_fastseekable;
596

597
    /*Set exported functions */
Laurent Aimar's avatar
Laurent Aimar committed
598 599
    p_demux->pf_demux = Demux;
    p_demux->pf_control = Control;
600

601 602
    p_sys->context.i_lastseqnumber = 1;

603
    p_demux->p_sys = p_sys;
604

605 606 607
    if( stream_Peek( p_demux->s, &p_peek, 24 ) < 24 ) return VLC_EGENERIC;
    if( !CmpUUID( (UUID_t *)(p_peek + 8), &SmooBoxUUID ) )
    {
608
        p_sys->b_smooth = true;
609 610 611
        p_sys->b_fragmented = true;
    }

612
    if( LoadInitFrag( p_demux ) != VLC_SUCCESS )
613 614
        goto error;

615

616
    if( ( p_ftyp = MP4_BoxGet( p_sys->p_root, "/ftyp" ) ) )
617
    {
618
        switch( BOXDATA(p_ftyp)->i_major_brand )
619
        {
620
            case MAJOR_isom:
Laurent Aimar's avatar
Laurent Aimar committed
621
                msg_Dbg( p_demux,
622
                         "ISO Media file (isom) version %d.",
623
                         BOXDATA(p_ftyp)->i_minor_version );
624
                break;
625 626 627 628
            case MAJOR_3gp4:
            case MAJOR_3gp5:
            case MAJOR_3gp6:
            case MAJOR_3gp7:
629 630
                msg_Dbg( p_demux, "3GPP Media file Release: %c",
#ifdef WORDS_BIGENDIAN
631
                        BOXDATA(p_ftyp)->i_major_brand
632
#else
633
                        BOXDATA(p_ftyp)->i_major_brand >> 24
634 635 636
#endif
                        );
                break;
637
            case MAJOR_qt__:
638
                msg_Dbg( p_demux, "Apple QuickTime file" );
639
                break;
640
            case MAJOR_isml:
641 642
                msg_Dbg( p_demux, "PIFF (= isml = fMP4) file" );
                break;
643 644 645 646
            case MAJOR_dash:
                msg_Dbg( p_demux, "DASH Stream file" );
                p_sys->b_dash = true;
                break;
647
            default:
Laurent Aimar's avatar
Laurent Aimar committed
648
                msg_Dbg( p_demux,
649
                         "unrecognized major file specification (%4.4s).",
650
                          (char*)&BOXDATA(p_ftyp)->i_major_brand );
651 652
                break;
        }
653 654 655 656 657 658 659 660 661
        /* 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)
            {
                msg_Dbg( p_demux, "DASH Stream file" );
                p_sys->b_dash = true;
            }
        }
662 663 664
    }
    else
    {
Laurent Aimar's avatar
Laurent Aimar committed
665
        msg_Dbg( p_demux, "file type box missing (assuming ISO Media file)" );
666 667
    }

668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690
    if( MP4_BoxCount( p_sys->p_root, "/moov/mvex" ) > 0 )
    {
        if ( p_sys->b_seekable && !p_sys->b_smooth && !p_sys->b_dash )
        {
            /* Probe remaining to check if there's really fragments
               or if that file is just ready to append fragments */
            ProbeFragments( p_demux, false );
            p_sys->b_fragmented = !!MP4_BoxCount( p_sys->p_root, "/moof" );

            if ( p_sys->b_fragmented && !p_sys->i_overall_duration )
                ProbeFragments( p_demux, true );
        }
        else
            p_sys->b_fragmented = true;
    }

    if ( !p_sys->moovfragment.p_moox )
        AddFragment( p_demux, MP4_BoxGet( p_sys->p_root, "/moov" ) );

    /* we always need a moov entry, but smooth has a workaround */
    if ( !p_sys->moovfragment.p_moox && !p_sys->b_smooth )
        goto error;

691 692
    MP4_BoxDumpStructure( p_demux->s, p_sys->p_root );

693 694
    if( p_sys->b_smooth )
    {
695
        if( CreateTracksFromSmooBox( p_demux ) != VLC_SUCCESS )
696
            goto error;
697

698
        p_demux->pf_demux = DemuxFrg;
699
        msg_Dbg( p_demux, "Set DemuxFrg mode" );
700
        return VLC_SUCCESS;
701 702 703 704
    }
    else if( p_sys->b_fragmented )
    {
        p_demux->pf_demux = DemuxAsLeaf;
705 706 707 708 709 710 711
        msg_Dbg( p_demux, "Set experimental DemuxLeaf mode" );
    }

    if( !p_sys->b_seekable && p_demux->pf_demux == Demux )
    {
        msg_Warn( p_demux, "MP4 plugin discarded (not seekable)" );
        goto error;
712 713
    }

714
    /* the file need to have one moov box */
715 716
    p_sys->moovfragment.p_moox = MP4_BoxGet( p_sys->p_root, "/moov", 0 );
    if( !p_sys->moovfragment.p_moox )
717
    {
718
        MP4_Box_t *p_foov = MP4_BoxGet( p_sys->p_root, "/foov" );
719 720 721

        if( !p_foov )
        {
722 723
            msg_Err( p_demux, "MP4 plugin discarded (no moov,foov,moof box)" );
            goto error;
724 725
        }
        /* we have a free box as a moov, rename it */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
726
        p_foov->i_type = ATOM_moov;
727
        p_sys->moovfragment.p_moox = p_foov;
728 729
    }

730
    if( ( p_rmra = MP4_BoxGet( p_sys->p_root,  "/moov/rmra" ) ) )
731 732 733 734
    {
        int        i_count = MP4_BoxCount( p_rmra, "rmda" );
        int        i;

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

737
        input_thread_t *p_input = demux_GetParentInput( p_demux );
738
        input_item_t *p_current = input_GetItem( p_input );
739

740 741
        input_item_node_t *p_subitems = input_item_node_Create( p_current );

742
        for( i = 0; i < i_count; i++ )
743
        {
744 745 746
            MP4_Box_t *p_rdrf = MP4_BoxGet( p_rmra, "rmda[%d]/rdrf", i );
            char      *psz_ref;
            uint32_t  i_ref_type;
747

748
            if( !p_rdrf || !BOXDATA(p_rdrf) || !( psz_ref = strdup( BOXDATA(p_rdrf)->psz_ref ) ) )
749
            {
750 751
                continue;
            }
752
            i_ref_type = BOXDATA(p_rdrf)->i_ref_type;
753

754 755 756 757 758 759
            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" ) )
760
                {
761
                    msg_Dbg( p_demux, "ignoring pseudo ref =`%s'", psz_ref );
762 763
                    continue;
                }
764 765
                if( !strncmp( psz_ref, "http://", 7 ) ||
                    !strncmp( psz_ref, "rtsp://", 7 ) )
766
                {
767
                    ;
768 769 770
                }
                else
                {
771
                    char *psz_absolute;
772
                    char *psz_path = strdup( p_demux->psz_location );
773 774 775 776
                    char *end = strrchr( psz_path, '/' );
                    if( end ) end[1] = '\0';
                    else *psz_path = '\0';

Christophe Mutricy's avatar
Christophe Mutricy committed
777 778
                    if( asprintf( &psz_absolute, "%s://%s%s",
                                  p_demux->psz_access, psz_path, psz_ref ) < 0 )
Rémi Duraffort's avatar
Rémi Duraffort committed
779 780 781
                    {
                        free( psz_ref );
                        free( psz_path );
François Cartegnie's avatar
François Cartegnie committed
782
                        input_item_node_Delete( p_subitems );
Rémi Duraffort's avatar
Rémi Duraffort committed
783
                        vlc_object_release( p_input) ;
Christophe Mutricy's avatar
Christophe Mutricy committed
784
                        return VLC_ENOMEM;
Rémi Duraffort's avatar
Rémi Duraffort committed
785
                    }
786 787 788 789

                    free( psz_ref );
                    psz_ref = psz_absolute;
                    free( psz_path );
790
                }
791
                msg_Dbg( p_demux, "adding ref = `%s'", psz_ref );
792 793 794 795
                input_item_t *p_item = input_item_New( psz_ref, NULL );
                input_item_CopyOptions( p_current, p_item );
                input_item_node_AppendItem( p_subitems, p_item );
                vlc_gc_decref( p_item );
796
            }
797
            else
798
            {
799
                msg_Err( p_demux, "unknown ref type=%4.4s FIXME (send a bug report)",
800
                         (char*)&BOXDATA(p_rdrf)->i_ref_type );
801
            }
802
            free( psz_ref );
803
        }
804
        input_item_node_PostAndDelete( p_subitems );
805
        vlc_object_release( p_input );
806 807
    }

808
    if( !(p_mvhd = MP4_BoxGet( p_sys->p_root, "/moov/mvhd" ) ) )
809
    {
810 811
        if( !p_rmra )
        {
Laurent Aimar's avatar
Laurent Aimar committed
812
            msg_Err( p_demux, "cannot find /moov/mvhd" );
813
            goto error;
814 815 816
        }
        else
        {
Laurent Aimar's avatar
Laurent Aimar committed
817 818
            msg_Warn( p_demux, "cannot find /moov/mvhd (pure ref file)" );
            p_demux->pf_demux = DemuxRef;
819
            return VLC_SUCCESS;
820
        }
821 822 823
    }
    else
    {
824
        p_sys->i_timescale = BOXDATA(p_mvhd)->i_timescale;
825 826 827 828 829
        if( p_sys->i_timescale == 0 )
        {
            msg_Err( p_this, "bad timescale" );
            goto error;
        }
830
    }
831

832 833
    const unsigned i_tracks = MP4_BoxCount( p_sys->p_root, "/moov/trak" );
    if( i_tracks < 1 )
834
    {
Laurent Aimar's avatar
Laurent Aimar committed
835
        msg_Err( p_demux, "cannot find any /moov/trak" );
836
        goto error;
837
    }
838
    msg_Dbg( p_demux, "found %u track%c", i_tracks, i_tracks ? 's':' ' );
839

840
    if( AllocateTracks( p_demux, i_tracks ) != VLC_SUCCESS )
841
        goto error;
842

843 844
    /* Search the first chap reference (like quicktime) and
     * check that at least 1 stream is enabled */
845
    p_sys->p_tref_chap = NULL;
846
    b_enabled_es = false;
847 848 849 850
    for( i = 0; i < p_sys->i_tracks; i++ )
    {
        MP4_Box_t *p_trak = MP4_BoxGet( p_sys->p_root, "/moov/trak[%d]", i );

851 852

        MP4_Box_t *p_tkhd = MP4_BoxGet( p_trak, "tkhd" );
853
        if( p_tkhd && BOXDATA(p_tkhd) && (BOXDATA(p_tkhd)->i_flags&MP4_TRACK_ENABLED) )
854 855 856
            b_enabled_es = true;

        MP4_Box_t *p_chap = MP4_BoxGet( p_trak, "tref/chap", i );
857 858
        if( p_chap && p_chap->data.p_tref_generic &&
            p_chap->data.p_tref_generic->i_entry_count > 0 && !p_sys->p_tref_chap )
859
            p_sys->p_tref_chap = p_chap;
860 861
    }

Rémi Denis-Courmont's avatar
Typos  
Rémi Denis-Courmont committed
862
    /* now process each track and extract all useful information */
863
    for( i = 0; i < p_sys->i_tracks; i++ )
864
    {
865
        p_trak = MP4_BoxGet( p_sys->p_root, "/moov/trak[%d]", i );
866
        MP4_TrackCreate( p_demux, &p_sys->track[i], p_trak, !b_enabled_es );
867

868
        if( p_sys->track[i].b_ok && !p_sys->track[i].b_chapter )
869
        {
Laurent Aimar's avatar
Laurent Aimar committed
870
            const char *psz_cat;
871
            switch( p_sys->track[i].fmt.i_cat )
872 873 874 875 876 877 878
            {
                case( VIDEO_ES ):
                    psz_cat = "video";
                    break;
                case( AUDIO_ES ):
                    psz_cat = "audio";
                    break;
879 880 881 882
                case( SPU_ES ):
                    psz_cat = "subtitle";
                    break;

883
                default:
884
                    psz_cat = "unknown";
885 886
                    break;
            }
887

Laurent Aimar's avatar
Laurent Aimar committed
888
            msg_Dbg( p_demux, "adding track[Id 0x%x] %s (%s) language %s",
889 890 891 892
                     p_sys->track[i].i_track_ID, psz_cat,
                     p_sys->track[i].b_enable ? "enable":"disable",
                     p_sys->track[i].fmt.psz_language ?
                     p_sys->track[i].fmt.psz_language : "undef" );
893
        }
894 895 896 897 898 899 900
        else if( p_sys->track[i].b_ok && p_sys->track[i].b_chapter )
        {
            msg_Dbg( p_demux, "using track[Id 0x%x] for chapter language %s",
                     p_sys->track[i].i_track_ID,
                     p_sys->track[i].fmt.psz_language ?
                     p_sys->track[i].fmt.psz_language : "undef" );
        }