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

zorglub's avatar
zorglub 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
#include "../codec/cc.h"
42

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

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

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

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

    mtime_t      i_pcr;

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

82
    bool         b_fragmented;   /* fMP4 */
83
84
    bool         b_seekable;
    bool         b_fastseekable;
85
    bool         b_seekmode;
86
87
    bool         b_smooth;       /* Is it Smooth Streaming? (streamfilter) */
    bool         b_ismv;
88

89
    bool            b_index_probed;
90
    bool            b_fragments_probed;
91
92

    mp4_fragments_t fragments;
93

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

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

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

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

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

122
static block_t * MP4_Block_Read( demux_t *, const mp4_track_t *, int );
123
124
static void MP4_Block_Send( demux_t *, mp4_track_t *, block_t * );

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

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

Laurent Aimar's avatar
Laurent Aimar committed
130
static uint64_t MP4_TrackGetPos    ( mp4_track_t * );
131
static uint32_t MP4_TrackGetReadSize( mp4_track_t *, uint32_t * );
132
static int      MP4_TrackNextSample( demux_t *, mp4_track_t *, uint32_t );
Laurent Aimar's avatar
Laurent Aimar committed
133
static void     MP4_TrackSetELST( demux_t *, mp4_track_t *, int64_t );
134
static bool     MP4_TrackIsInterleaved( const mp4_track_t * );
Laurent Aimar's avatar
Laurent Aimar committed
135

136
137
static void     MP4_UpdateSeekpoint( demux_t * );

138
static MP4_Box_t * MP4_GetTrexByTrackID( MP4_Box_t *p_moov, const uint32_t i_id );
139
140
141
142
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 );
143

144
static bool AddFragment( demux_t *p_demux, MP4_Box_t *p_moox );
145
static int  ProbeFragments( demux_t *p_demux, bool b_force );
146
147
148
149
150
151
152
static int  ProbeIndex( demux_t *p_demux );

static int LeafIndexGetMoofPosByTime( demux_t *p_demux, const mtime_t i_target_time,
                                      uint64_t *pi_pos, mtime_t *pi_mooftime );
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
static MP4_Box_t * MP4_GetTrexByTrackID( MP4_Box_t *p_moov, const uint32_t i_id )
{
190
191
    if(!p_moov)
        return NULL;
192
193
194
    MP4_Box_t *p_trex = MP4_BoxGet( p_moov, "mvex/trex" );
    while( p_trex )
    {
195
196
        if ( p_trex->i_type == ATOM_trex &&
             BOXDATA(p_trex) && BOXDATA(p_trex)->i_track_ID == i_id )
197
198
199
200
201
202
203
                break;
        else
            p_trex = p_trex->p_next;
    }
    return p_trex;
}

204
205
206
207
208
209
210
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 &&
211
            (p_tkhd = MP4_BoxGet( p_trak, "tkhd" )) && BOXDATA(p_tkhd) &&
212
213
214
215
216
217
218
219
            BOXDATA(p_tkhd)->i_track_ID == i_id )
                break;
        else
            p_trak = p_trak->p_next;
    }
    return p_trak;
}

220
/* Return time in microsecond of a track */
Laurent Aimar's avatar
Laurent Aimar committed
221
static inline int64_t MP4_TrackGetDTS( demux_t *p_demux, mp4_track_t *p_track )
Laurent Aimar's avatar
Laurent Aimar committed
222
{
223
    demux_sys_t *p_sys = p_demux->p_sys;
224
    const mp4_chunk_t *p_chunk;
225
    if( p_sys->b_fragmented )
226
        p_chunk = p_track->cchunk;
227
    else
228
        p_chunk = &p_track->chunk[p_track->i_chunk];
229
230

    unsigned int i_index = 0;
231
232
    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
233

234
    while( i_sample > 0 && i_index < p_chunk->i_entries_dts )
Laurent Aimar's avatar
Laurent Aimar committed
235
    {
236
        if( i_sample > p_chunk->p_sample_count_dts[i_index] )
Laurent Aimar's avatar
Laurent Aimar committed
237
        {
238
239
240
            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
241
242
243
244
            i_index++;
        }
        else
        {
245
            i_dts += i_sample * p_chunk->p_sample_delta_dts[i_index];
Laurent Aimar's avatar
Laurent Aimar committed
246
247
248
249
            break;
        }
    }

250
251
252
    /* now handle elst */
    if( p_track->p_elst )
    {
Laurent Aimar's avatar
Laurent Aimar committed
253
        demux_sys_t         *p_sys = p_demux->p_sys;
254
        MP4_Box_data_elst_t *elst = p_track->BOXDATA(p_elst);
255
256
257
258
259
260
261
262
263
264

        /* 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 */
265
266
        i_dts += p_track->i_elst_time * p_track->i_timescale /
            p_sys->i_timescale;
267
268
269
270

        if( i_dts < 0 ) i_dts = 0;
    }

271
    return CLOCK_FREQ * i_dts / p_track->i_timescale;
Laurent Aimar's avatar
Laurent Aimar committed
272
}
273

274
275
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
276
{
277
278
279
280
281
282
283
    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
284
285
286
287
    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 )
288
        return false;
Laurent Aimar's avatar
Laurent Aimar committed
289

290
    for( i_index = 0; i_index < ck->i_entries_pts ; i_index++ )
Laurent Aimar's avatar
Laurent Aimar committed
291
292
    {
        if( i_sample < ck->p_sample_count_pts[i_index] )
293
294
295
296
297
        {
            *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
298
299
300

        i_sample -= ck->p_sample_count_pts[i_index];
    }
301
    return false;
Laurent Aimar's avatar
Laurent Aimar committed
302
303
}

Laurent Aimar's avatar
Laurent Aimar committed
304
305
static inline int64_t MP4_GetMoviePTS(demux_sys_t *p_sys )
{
306
    return CLOCK_FREQ * p_sys->i_time / p_sys->i_timescale;
Laurent Aimar's avatar
Laurent Aimar committed
307
}
308

309
310
static void LoadChapter( demux_t  *p_demux );

311
static int LoadInitFrag( demux_t *p_demux )
312
313
314
{
    demux_sys_t *p_sys = p_demux->p_sys;

315
    if( p_sys->b_smooth ) /* Smooth Streaming */
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
    {
        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 );
331
                if( p_stra && BOXDATA(p_stra) && BOXDATA(p_stra)->i_track_ID )
332
333
                    p_sys->i_tracks++;
                /* Get timescale and duration of the video track; */
334
                if( p_sys->i_timescale == 0 )
335
                {
336
337
338
339
340
                    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;
                    }
341
342
343
344
345
                    if( p_sys->i_timescale == 0 )
                    {
                        msg_Err( p_demux, "bad timescale" );
                        goto LoadInitFragError;
                    }
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
                }
            }
        }
    }
    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;
}

365
static int AllocateTracks( demux_t *p_demux, unsigned i_tracks )
366
367
368
{
    demux_sys_t *p_sys = p_demux->p_sys;

369
    p_sys->track = calloc( i_tracks, sizeof( mp4_track_t ) );
370
    if( p_sys->track == NULL )
371
372
        return VLC_ENOMEM;
    p_sys->i_tracks = i_tracks;
373
374
375

    if( p_sys->b_fragmented )
    {
376
        for( unsigned i = 0; i < i_tracks; i++ )
377
        {
378
            mp4_track_t *p_track = &p_sys->track[i];
379
380
            p_track->cchunk = calloc( 1, sizeof( mp4_chunk_t ) );
            if( unlikely( !p_track->cchunk ) )
381
                return VLC_ENOMEM;
382
383
384
385
386
        }
    }
    return VLC_SUCCESS;
}

387
static int CreateTracksFromSmooBox( demux_t *p_demux )
388
389
390
391
{
    demux_sys_t *p_sys = p_demux->p_sys;

    MP4_Box_t *p_smoo = MP4_BoxGet( p_sys->p_root, "uuid" );
392
393
394
395
396
397
398
399
400
401
402
403
    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 )
404
    {
405
406
        if( !CmpUUID( &p_stra->i_uuid, &StraBoxUUID ) &&
            BOXDATA(p_stra) && BOXDATA(p_stra)->i_track_ID > 0 )
407
        {
408
            mp4_track_t *p_track = &p_sys->track[j++];
409
            MP4_SmoothTrackCreate( p_demux, p_track, p_track->cchunk, p_stra, true );
410
        }
411
        p_stra = p_stra->p_next;
412
    }
413
414

    return VLC_SUCCESS;
415
416
}

417
418
419
420
static block_t * MP4_EIA608_Convert( block_t * p_block )
{
    /* Rebuild codec data from encap */
    size_t i_copied = 0;
421
    size_t i_remaining = __MIN(p_block->i_buffer, INT64_MAX / 3);
422
423
424
    uint32_t i_bytes = 0;
    block_t *p_newblock;

425
    /* always need at least 10 bytes (atom size+header+1pair)*/
426
427
    if ( i_remaining < 10 ||
         !(i_bytes = GetDWBE(p_block->p_buffer)) ||
428
         (i_bytes > i_remaining) ||
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
         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 );

451
    /* cdt2 is optional */
452
453
    if ( i_remaining >= 10 &&
         (i_bytes = GetDWBE(p_read)) &&
454
         (i_bytes <= i_remaining) &&
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
         !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;
472
    p_newblock->i_flags = BLOCK_FLAG_ORDERED_CAPTIONS;
473
474
475
    return p_newblock;
}

476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
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;
491
492
493
            case VLC_CODEC_EIA608_1:
                p_block = MP4_EIA608_Convert( p_block );
            break;
494
495
496
497
498
499
500
501
502
        default:
            p_block->i_buffer = 0;
            break;
        }
    }

    return p_block;
}

503
504
505
506
507
508
509
510
511
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 );
    }
512

513
514
    p_block->i_flags |= p_track->i_block_flags;

515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
    /* 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 );
534
535
}

536
/*****************************************************************************
537
 * Open: check file and initializes MP4 structures
538
 *****************************************************************************/
539
static int Open( vlc_object_t * p_this )
540
{
Laurent Aimar's avatar
Laurent Aimar committed
541
    demux_t  *p_demux = (demux_t *)p_this;
542
    demux_sys_t     *p_sys;
543

Christophe Mutricy's avatar
Christophe Mutricy committed
544
    const uint8_t   *p_peek;
545

546
    MP4_Box_t       *p_ftyp;
547
    MP4_Box_t       *p_rmra;
548
549
    MP4_Box_t       *p_mvhd;
    MP4_Box_t       *p_trak;
550

551
    unsigned int    i;
552
    bool      b_enabled_es;
553

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

557
    switch( VLC_FOURCC( p_peek[4], p_peek[5], p_peek[6], p_peek[7] ) )
558
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
559
560
561
562
563
564
565
566
        case ATOM_moov:
        case ATOM_foov:
        case ATOM_moof:
        case ATOM_mdat:
        case ATOM_udta:
        case ATOM_free:
        case ATOM_skip:
        case ATOM_wide:
567
        case ATOM_uuid:
568
        case VLC_FOURCC( 'p', 'n', 'o', 't' ):
569
            break;
570
571
572
573
574
        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;
575
         default:
576
            return VLC_EGENERIC;
577
    }
578

579
580
581
582
583
    /* create our structure that will contains all data */
    p_sys = calloc( 1, sizeof( demux_sys_t ) );
    if ( !p_sys )
        return VLC_EGENERIC;

584
    /* I need to seek */
585
586
    stream_Control( p_demux->s, STREAM_CAN_SEEK, &p_sys->b_seekable );
    stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &p_sys->b_fastseekable );
587
    p_sys->b_seekmode = p_sys->b_fastseekable;
588

589
    /*Set exported functions */
Laurent Aimar's avatar
Laurent Aimar committed
590
591
    p_demux->pf_demux = Demux;
    p_demux->pf_control = Control;
592

593
594
    p_sys->context.i_lastseqnumber = 1;

595
596
    MP4_Fragments_Init( &p_sys->fragments );

597
    p_demux->p_sys = p_sys;
598

599
600
601
    if( stream_Peek( p_demux->s, &p_peek, 24 ) < 24 ) return VLC_EGENERIC;
    if( !CmpUUID( (UUID_t *)(p_peek + 8), &SmooBoxUUID ) )
    {
602
        p_sys->b_smooth = true;
603
604
605
        p_sys->b_fragmented = true;
    }

606
    if( LoadInitFrag( p_demux ) != VLC_SUCCESS )
607
608
        goto error;

609

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

665
666
    if( MP4_BoxCount( p_sys->p_root, "/moov/mvex" ) > 0 )
    {
667
        if ( p_sys->b_seekable && !p_sys->b_smooth )
668
669
670
671
672
673
674
675
        {
            /* 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 );
676
677
678
679
680
681
682

            MP4_Box_t *p_mdat = MP4_BoxGet( p_sys->p_root, "mdat" );
            if ( p_mdat )
            {
                stream_Seek( p_demux->s, p_mdat->i_pos );
                msg_Dbg( p_demux, "rewinding to mdat %"PRId64, p_mdat->i_pos );
            }
683
684
685
686
687
        }
        else
            p_sys->b_fragmented = true;
    }

688
    if ( !MP4_Fragment_Moov(&p_sys->fragments)->p_moox )
689
690
691
        AddFragment( p_demux, MP4_BoxGet( p_sys->p_root, "/moov" ) );

    /* we always need a moov entry, but smooth has a workaround */
692
    if ( !MP4_Fragment_Moov(&p_sys->fragments)->p_moox && !p_sys->b_smooth )
693
694
        goto error;

695
696
    MP4_BoxDumpStructure( p_demux->s, p_sys->p_root );

697
698
    if( p_sys->b_smooth )
    {
699
        if( CreateTracksFromSmooBox( p_demux ) != VLC_SUCCESS )
700
            goto error;
701

702
        p_demux->pf_demux = DemuxFrg;
703
        msg_Dbg( p_demux, "Set DemuxFrg mode" );
704
        return VLC_SUCCESS;
705
706
707
708
    }
    else if( p_sys->b_fragmented )
    {
        p_demux->pf_demux = DemuxAsLeaf;
709
710
711
712
713
714
715
        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;
716
717
    }

718
    /* the file need to have one moov box */
719
720
    MP4_Fragment_Moov(&p_sys->fragments)->p_moox = MP4_BoxGet( p_sys->p_root, "/moov", 0 );
    if( !MP4_Fragment_Moov(&p_sys->fragments)->p_moox )
721
    {
722
        MP4_Box_t *p_foov = MP4_BoxGet( p_sys->p_root, "/foov" );
723
724
725

        if( !p_foov )
        {
726
727
            msg_Err( p_demux, "MP4 plugin discarded (no moov,foov,moof box)" );
            goto error;
728
729
        }
        /* we have a free box as a moov, rename it */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
730
        p_foov->i_type = ATOM_moov;
731
        MP4_Fragment_Moov(&p_sys->fragments)->p_moox = p_foov;
732
733
    }

734
    if( ( p_rmra = MP4_BoxGet( p_sys->p_root,  "/moov/rmra" ) ) )
735
736
737
738
    {
        int        i_count = MP4_BoxCount( p_rmra, "rmda" );
        int        i;

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

741
        input_thread_t *p_input = p_demux->p_input;
742
        input_item_t *p_current = input_GetItem( p_input );
743

744
745
        input_item_node_t *p_subitems = input_item_node_Create( p_current );

746
        for( i = 0; i < i_count; i++ )
747
        {
748
749
750
            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
751

752
            if( !p_rdrf || !BOXDATA(p_rdrf) || !( psz_ref = strdup( BOXDATA(p_rdrf)->psz_ref ) ) )
753
            {
754
755
                continue;
            }
756
            i_ref_type = BOXDATA(p_rdrf)->i_ref_type;
757

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

Christophe Mutricy's avatar
Christophe Mutricy committed
781
782
                    if( asprintf( &psz_absolute, "%s://%s%s",
                                  p_demux->psz_access, psz_path, psz_ref ) < 0 )
ivoire's avatar
ivoire committed
783
784
785
                    {
                        free( psz_ref );
                        free( psz_path );
François Cartegnie's avatar
François Cartegnie committed
786
                        input_item_node_Delete( p_subitems );
Christophe Mutricy's avatar
Christophe Mutricy committed
787
                        return VLC_ENOMEM;
ivoire's avatar
ivoire committed
788
                    }
789
790
791
792

                    free( psz_ref );
                    psz_ref = psz_absolute;
                    free( psz_path );
793
                }
794
                msg_Dbg( p_demux, "adding ref = `%s'", psz_ref );
795
796
797
798
                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 );
799
            }
800
            else
zorglub's avatar
zorglub committed
801
            {
802
                msg_Err( p_demux, "unknown ref type=%4.4s FIXME (send a bug report)",
803
                         (char*)&BOXDATA(p_rdrf)->i_ref_type );
zorglub's avatar
zorglub committed
804
            }
805
            free( psz_ref );
806
        }
807
        input_item_node_PostAndDelete( p_subitems );
808
809
    }

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

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

842
    if( AllocateTracks( p_demux, i_tracks ) != VLC_SUCCESS )
843
        goto error;
844

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

853
854

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

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

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

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

885
                default:
886
                    psz_cat = "unknown";
887
888
                    break;
            }
889

Laurent Aimar's avatar
Laurent Aimar committed
890
            msg_Dbg( p_demux, "adding track[Id 0x%x] %s (%s) language %s",
891
892
893
894
                     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" );
895
        }
896
897
898
899
900
901
902
        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" );
        }
903
904
        else
        {
905
906
            msg_Dbg( p_demux, "ignoring track[Id 0x%x]",
                     p_sys->track[i].i_track_ID );
907
908
        }
    }
909

910
911
912
913
914
915
916
917
918
919
    if ( p_sys->i_overall_duration == 0 )
    {
        /* Try in mehd if fragmented */
        MP4_Box_t *p_mehd = MP4_BoxGet( p_demux->p_sys->p_root, "moov/mvex/mehd");
        if ( p_mehd && p_mehd->data.p_mehd )
            p_sys->i_overall_duration = p_mehd->data.p_mehd->i_fragment_duration;
        else
        {
            for( i = 0; i < p_sys->i_tracks; i++ )
            {
920
                mtime_t i_duration = GetTrackTotalDuration( &p_sys->fragments, p_sys->track[i].i_track_ID );
921
922
923
924
925
                p_sys->i_overall_duration = __MAX( p_sys->i_overall_duration, (uint64_t)i_duration );
            }
        }
    }

926
#ifdef MP4_VERBOSE
927
    DumpFragments( VLC_OBJECT(p_demux), &p_sys->fragments, p_sys->i_timescale );
928
#endif
929

930
931
932
933
934
935
936
937
938
939
940
941
942
943
    if( !p_sys->b_fragmented && p_sys->i_tracks > 1 && p_sys->b_seekable && !p_sys->b_seekmode )
    {
        for( unsigned i = 0; i < p_sys->i_tracks; i++ )
        {
            mp4_track_t *tk = &p_sys->track[i];
            if( !MP4_TrackIsInterleaved( tk ) )
            {
                msg_Warn( p_demux, "that media doesn't look interleaved, will need to seek");
                p_sys->b_seekmode = true;
                break;
            }
        }
    }

944
945
    /* */
    LoadChapter( p_demux );
946

947
948
949
950
951
952
953
954
955
    p_sys->asfpacketsys.p_demux = p_demux;
    p_sys->asfpacketsys.pi_preroll = &p_sys->i_preroll;
    p_sys->asfpacketsys.pi_preroll_start = &p_sys->i_preroll_start;
    p_sys->asfpacketsys.pf_doskip = NULL;
    p_sys->asfpacketsys.pf_send = MP4ASF_Send;
    p_sys->asfpacketsys.pf_gettrackinfo = MP4ASF_GetTrackInfo;
    p_sys->asfpacketsys.pf_updatetime = NULL;
    p_sys->asfpacketsys.pf_setaspectratio = NULL;

956
957
958
    return VLC_SUCCESS;

error:
959
960
961
    if( stream_Tell( p_demux->s ) > 0 )
        stream_Seek( p_demux->s, 0 );

962
    if( p_sys->p_root )
Laurent Aimar's avatar
Laurent Aimar committed
963
    {