mp4.c 189 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
37
#include <vlc_charset.h>                           /* EnsureUTF8 */
#include <vlc_meta.h>                              /* vlc_meta_t, vlc_meta_ */
38
#include <vlc_input.h>
39
#include <vlc_aout.h>
40
#include <assert.h>
41

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
42
#include "id3genres.h"                             /* for ATOM_gnre */
43
44
45
46

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

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

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

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

    mtime_t      i_pcr;

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

84
    bool         b_fragmented;   /* fMP4 */
85
86
    bool         b_seekable;
    bool         b_fastseekable;
87
    bool         b_seekmode;
88
    bool         b_smooth;       /* Is it Smooth Streaming? */
89

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

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

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

    /* */
    input_title_t *p_title;
107
108
};

109
110
#define BOXDATA(type) type->data.type

111
/*****************************************************************************
112
 * Declaration of local function
113
 *****************************************************************************/
114
static void MP4_TrackCreate ( demux_t *, mp4_track_t *, MP4_Box_t  *, bool b_force_enable );
115
static int MP4_frg_TrackCreate( demux_t *, mp4_track_t *, MP4_Box_t *);
Christophe Mutricy's avatar
Christophe Mutricy committed
116
static void MP4_TrackDestroy(  mp4_track_t * );
117

118
119
static void MP4_Block_Send( demux_t *, mp4_track_t *, block_t * );

Laurent Aimar's avatar
Laurent Aimar committed
120
121
static int  MP4_TrackSelect ( demux_t *, mp4_track_t *, mtime_t );
static void MP4_TrackUnselect(demux_t *, mp4_track_t * );
122

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

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

130
static void     MP4_UpdateSeekpoint( demux_t * );
131
static const char *MP4_ConvertMacCode( uint16_t );
132

133
134
static MP4_Box_t * MP4_GetTrexByTrackID( MP4_Box_t *p_moov, const uint32_t i_id );

135
static bool AddFragment( demux_t *p_demux, MP4_Box_t *p_moox );
136
static int  ProbeFragments( demux_t *p_demux, bool b_force );
137
138
139
140
141
142
143
144
145
146
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 );
static mtime_t LeafGetFragmentTimeOffset( demux_t *p_demux, mp4_fragment_t * );
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 );
147
148
149

/* Helpers */

150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
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;
}

165
166
167
168
169
170
171
172
173
174
175
176
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;
    }
}

177
178
179
180
181
static MP4_Box_t * MP4_GetTrexByTrackID( MP4_Box_t *p_moov, const uint32_t i_id )
{
    MP4_Box_t *p_trex = MP4_BoxGet( p_moov, "mvex/trex" );
    while( p_trex )
    {
182
183
        if ( p_trex->i_type == ATOM_trex &&
             BOXDATA(p_trex) && BOXDATA(p_trex)->i_track_ID == i_id )
184
185
186
187
188
189
190
                break;
        else
            p_trex = p_trex->p_next;
    }
    return p_trex;
}

191
192
193
194
195
196
197
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 &&
198
            (p_tkhd = MP4_BoxGet( p_trak, "tkhd" )) && BOXDATA(p_tkhd) &&
199
200
201
202
203
204
205
206
            BOXDATA(p_tkhd)->i_track_ID == i_id )
                break;
        else
            p_trak = p_trak->p_next;
    }
    return p_trak;
}

207
/* Return time in microsecond of a track */
Laurent Aimar's avatar
Laurent Aimar committed
208
static inline int64_t MP4_TrackGetDTS( demux_t *p_demux, mp4_track_t *p_track )
Laurent Aimar's avatar
Laurent Aimar committed
209
{
210
211
212
213
214
215
    demux_sys_t *p_sys = p_demux->p_sys;
    mp4_chunk_t chunk;
    if( p_sys->b_fragmented )
        chunk = *p_track->cchunk;
    else
        chunk = p_track->chunk[p_track->i_chunk];
216
217
218
219

    unsigned int i_index = 0;
    unsigned int i_sample = p_track->i_sample - chunk.i_sample_first;
    int64_t i_dts = chunk.i_first_dts;
Laurent Aimar's avatar
Laurent Aimar committed
220
221
222

    while( i_sample > 0 )
    {
223
        if( i_sample > chunk.p_sample_count_dts[i_index] )
Laurent Aimar's avatar
Laurent Aimar committed
224
        {
225
226
227
            i_dts += chunk.p_sample_count_dts[i_index] *
                chunk.p_sample_delta_dts[i_index];
            i_sample -= chunk.p_sample_count_dts[i_index];
Laurent Aimar's avatar
Laurent Aimar committed
228
229
230
231
            i_index++;
        }
        else
        {
232
            i_dts += i_sample * chunk.p_sample_delta_dts[i_index];
Laurent Aimar's avatar
Laurent Aimar committed
233
234
235
236
            break;
        }
    }

237
238
239
    /* now handle elst */
    if( p_track->p_elst )
    {
Laurent Aimar's avatar
Laurent Aimar committed
240
        demux_sys_t         *p_sys = p_demux->p_sys;
241
        MP4_Box_data_elst_t *elst = p_track->BOXDATA(p_elst);
242
243
244
245
246
247
248
249
250
251

        /* 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 */
252
253
        i_dts += p_track->i_elst_time * p_track->i_timescale /
            p_sys->i_timescale;
254
255
256
257

        if( i_dts < 0 ) i_dts = 0;
    }

258
    return CLOCK_FREQ * i_dts / p_track->i_timescale;
Laurent Aimar's avatar
Laurent Aimar committed
259
}
260

261
static inline int64_t MP4_TrackGetPTSDelta( demux_t *p_demux, mp4_track_t *p_track )
Laurent Aimar's avatar
Laurent Aimar committed
262
{
263
264
265
266
267
268
269
    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
270
271
272
273
274
275
276
277
278
    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 )
        return -1;

    for( i_index = 0;; i_index++ )
    {
        if( i_sample < ck->p_sample_count_pts[i_index] )
279
            return ck->p_sample_offset_pts[i_index] * CLOCK_FREQ /
280
                   (int64_t)p_track->i_timescale;
Laurent Aimar's avatar
Laurent Aimar committed
281
282
283
284
285

        i_sample -= ck->p_sample_count_pts[i_index];
    }
}

Laurent Aimar's avatar
Laurent Aimar committed
286
287
static inline int64_t MP4_GetMoviePTS(demux_sys_t *p_sys )
{
288
    return CLOCK_FREQ * p_sys->i_time / p_sys->i_timescale;
Laurent Aimar's avatar
Laurent Aimar committed
289
}
290

291
292
static void LoadChapter( demux_t  *p_demux );

293
static int LoadInitFrag( demux_t *p_demux )
294
295
296
{
    demux_sys_t *p_sys = p_demux->p_sys;

297
    if( p_sys->b_smooth ) /* Smooth Streaming */
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
    {
        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 );
313
                if( p_stra && BOXDATA(p_stra) && BOXDATA(p_stra)->i_track_ID )
314
315
                    p_sys->i_tracks++;
                /* Get timescale and duration of the video track; */
316
                if( p_sys->i_timescale == 0 )
317
                {
318
319
320
321
322
323
                    if ( p_stra && BOXDATA(p_stra) )
                    {
                        p_sys->i_timescale = BOXDATA(p_stra)->i_timescale;
                        p_sys->i_duration = BOXDATA(p_stra)->i_duration;
                        p_sys->i_overall_duration = BOXDATA(p_stra)->i_duration;
                    }
324
325
326
327
328
                    if( p_sys->i_timescale == 0 )
                    {
                        msg_Err( p_demux, "bad timescale" );
                        goto LoadInitFragError;
                    }
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
                }
            }
        }
    }
    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;
}

static int InitTracks( demux_t *p_demux )
{
    demux_sys_t *p_sys = p_demux->p_sys;

    p_sys->track = calloc( p_sys->i_tracks, sizeof( mp4_track_t ) );
    if( p_sys->track == NULL )
        return VLC_EGENERIC;

    if( p_sys->b_fragmented )
    {
        mp4_track_t *p_track;
        for( uint16_t i = 0; i < p_sys->i_tracks; i++ )
        {
            p_track = &p_sys->track[i];
            p_track->cchunk = calloc( 1, sizeof( mp4_chunk_t ) );
            if( unlikely( !p_track->cchunk ) )
            {
                free( p_sys->track );
                return VLC_EGENERIC;
            }
        }
    }
    return VLC_SUCCESS;
}

static void CreateTracksFromSmooBox( demux_t *p_demux )
{
    demux_sys_t *p_sys = p_demux->p_sys;

    MP4_Box_t *p_smoo = MP4_BoxGet( p_sys->p_root, "uuid" );
    mp4_track_t *p_track;
    int j = 0;
    for( int i = 0; i < 3; i++ )
    {
        MP4_Box_t *p_stra = MP4_BoxGet( p_smoo, "uuid[%d]", i );
383
        if( !p_stra || !BOXDATA(p_stra) || BOXDATA(p_stra)->i_track_ID == 0 )
384
385
386
387
388
389
390
391
392
393
            continue;
        else
        {
            p_track = &p_sys->track[j]; j++;
            MP4_frg_TrackCreate( p_demux, p_track, p_stra );
            p_track->p_es = es_out_Add( p_demux->out, &p_track->fmt );
        }
    }
}

394
395
396
397
398
399
400
401
402
403
404
405
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 );
    }
    es_out_Send( p_demux->out, p_track->p_es, p_block );
}

406
/*****************************************************************************
407
 * Open: check file and initializes MP4 structures
408
 *****************************************************************************/
409
static int Open( vlc_object_t * p_this )
410
{
Laurent Aimar's avatar
Laurent Aimar committed
411
    demux_t  *p_demux = (demux_t *)p_this;
412
    demux_sys_t     *p_sys;
413

Christophe Mutricy's avatar
Christophe Mutricy committed
414
    const uint8_t   *p_peek;
415

416
    MP4_Box_t       *p_ftyp;
417
    MP4_Box_t       *p_rmra;
418
419
    MP4_Box_t       *p_mvhd;
    MP4_Box_t       *p_trak;
420

421
    unsigned int    i;
422
    bool      b_enabled_es;
423

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

427
    switch( VLC_FOURCC( p_peek[4], p_peek[5], p_peek[6], p_peek[7] ) )
428
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
429
430
431
432
433
434
435
436
        case ATOM_moov:
        case ATOM_foov:
        case ATOM_moof:
        case ATOM_mdat:
        case ATOM_udta:
        case ATOM_free:
        case ATOM_skip:
        case ATOM_wide:
437
        case ATOM_uuid:
438
        case VLC_FOURCC( 'p', 'n', 'o', 't' ):
439
            break;
440
441
442
443
444
        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;
445
         default:
446
            return VLC_EGENERIC;
447
    }
448

449
450
451
452
453
    /* create our structure that will contains all data */
    p_sys = calloc( 1, sizeof( demux_sys_t ) );
    if ( !p_sys )
        return VLC_EGENERIC;

454
    /* I need to seek */
455
456
    stream_Control( p_demux->s, STREAM_CAN_SEEK, &p_sys->b_seekable );
    if( !p_sys->b_seekable )
457
    {
458
        msg_Warn( p_demux, "MP4 plugin discarded (not seekable)" );
459
        free( p_sys );
460
        return VLC_EGENERIC;
461
    }
462
    stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &p_sys->b_fastseekable );
463
    p_sys->b_seekmode = p_sys->b_fastseekable;
464

465
    /*Set exported functions */
Laurent Aimar's avatar
Laurent Aimar committed
466
467
    p_demux->pf_demux = Demux;
    p_demux->pf_control = Control;
468

469
    p_demux->p_sys = p_sys;
470

471
472
473
    if( stream_Peek( p_demux->s, &p_peek, 24 ) < 24 ) return VLC_EGENERIC;
    if( !CmpUUID( (UUID_t *)(p_peek + 8), &SmooBoxUUID ) )
    {
474
        p_sys->b_smooth = true;
475
476
477
        p_sys->b_fragmented = true;
    }

478
    if( LoadInitFrag( p_demux ) != VLC_SUCCESS )
479
480
        goto error;

481
    if( MP4_BoxCount( p_sys->p_root, "/moov/mvex" ) > 0 )
482
    {
483
484
485
486
        if ( p_sys->b_seekable )
        {
            /* Probe remaining to check if there's really fragments
               or if that file is just ready to append fragments */
487
            ProbeFragments( p_demux, false );
488
489
490
491
            p_sys->b_fragmented = !!MP4_BoxCount( p_sys->p_root, "/moof" );
        }
        else
            p_sys->b_fragmented = true;
492
493
494

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

497
498
499
500
501
502
503
    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;

504
    if ( p_sys->b_smooth )
505
506
507
    {
        p_demux->pf_demux = DemuxFrg;
    }
508
509
510
511
    else if( p_sys->b_fragmented )
    {
        p_demux->pf_demux = DemuxAsLeaf;
    }
512

513
    if( p_sys->b_smooth )
514
515
516
517
518
519
    {
        if( InitTracks( p_demux ) != VLC_SUCCESS )
            goto error;
        CreateTracksFromSmooBox( p_demux );
        return VLC_SUCCESS;
    }
520

Laurent Aimar's avatar
Laurent Aimar committed
521
    MP4_BoxDumpStructure( p_demux->s, p_sys->p_root );
522

523
    if( ( p_ftyp = MP4_BoxGet( p_sys->p_root, "/ftyp" ) ) )
524
    {
525
        switch( BOXDATA(p_ftyp)->i_major_brand )
526
        {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
527
            case( ATOM_isom ):
Laurent Aimar's avatar
Laurent Aimar committed
528
                msg_Dbg( p_demux,
529
                         "ISO Media file (isom) version %d.",
530
                         BOXDATA(p_ftyp)->i_minor_version );
531
                break;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
532
            case( ATOM_3gp4 ):
533
534
535
536
537
            case( VLC_FOURCC( '3', 'g', 'p', '5' ) ):
            case( VLC_FOURCC( '3', 'g', 'p', '6' ) ):
            case( VLC_FOURCC( '3', 'g', 'p', '7' ) ):
                msg_Dbg( p_demux, "3GPP Media file Release: %c",
#ifdef WORDS_BIGENDIAN
538
                        BOXDATA(p_ftyp)->i_major_brand
539
#else
540
                        BOXDATA(p_ftyp)->i_major_brand >> 24
541
542
543
544
545
#endif
                        );
                break;
            case( VLC_FOURCC( 'q', 't', ' ', ' ') ):
                msg_Dbg( p_demux, "Apple QuickTime file" );
546
                break;
547
548
549
            case( VLC_FOURCC( 'i', 's', 'm', 'l') ):
                msg_Dbg( p_demux, "PIFF (= isml = fMP4) file" );
                break;
550
            default:
Laurent Aimar's avatar
Laurent Aimar committed
551
                msg_Dbg( p_demux,
552
                         "unrecognized major file specification (%4.4s).",
553
                          (char*)&BOXDATA(p_ftyp)->i_major_brand );
554
555
556
557
558
                break;
        }
    }
    else
    {
Laurent Aimar's avatar
Laurent Aimar committed
559
        msg_Dbg( p_demux, "file type box missing (assuming ISO Media file)" );
560
561
562
    }

    /* the file need to have one moov box */
563
564
    p_sys->moovfragment.p_moox = MP4_BoxGet( p_sys->p_root, "/moov", 0 );
    if( !p_sys->moovfragment.p_moox )
565
    {
566
        MP4_Box_t *p_foov = MP4_BoxGet( p_sys->p_root, "/foov" );
567
568
569

        if( !p_foov )
        {
570
571
            msg_Err( p_demux, "MP4 plugin discarded (no moov,foov,moof box)" );
            goto error;
572
573
        }
        /* we have a free box as a moov, rename it */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
574
        p_foov->i_type = ATOM_moov;
575
        p_sys->moovfragment.p_moox = p_foov;
576
577
    }

578
    if( ( p_rmra = MP4_BoxGet( p_sys->p_root,  "/moov/rmra" ) ) )
579
580
581
582
    {
        int        i_count = MP4_BoxCount( p_rmra, "rmda" );
        int        i;

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

585
        input_thread_t *p_input = demux_GetParentInput( p_demux );
586
        input_item_t *p_current = input_GetItem( p_input );
587

588
589
        input_item_node_t *p_subitems = input_item_node_Create( p_current );

590
        for( i = 0; i < i_count; i++ )
591
        {
592
593
594
            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
595

596
            if( !p_rdrf || !BOXDATA(p_rdrf) || !( psz_ref = strdup( BOXDATA(p_rdrf)->psz_ref ) ) )
597
            {
598
599
                continue;
            }
600
            i_ref_type = BOXDATA(p_rdrf)->i_ref_type;
601

602
603
604
605
606
607
            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" ) )
608
                {
609
                    msg_Dbg( p_demux, "ignoring pseudo ref =`%s'", psz_ref );
610
611
                    continue;
                }
612
613
                if( !strncmp( psz_ref, "http://", 7 ) ||
                    !strncmp( psz_ref, "rtsp://", 7 ) )
614
                {
615
                    ;
616
617
618
                }
                else
                {
619
                    char *psz_absolute;
620
                    char *psz_path = strdup( p_demux->psz_location );
621
622
623
624
                    char *end = strrchr( psz_path, '/' );
                    if( end ) end[1] = '\0';
                    else *psz_path = '\0';

Christophe Mutricy's avatar
Christophe Mutricy committed
625
626
                    if( asprintf( &psz_absolute, "%s://%s%s",
                                  p_demux->psz_access, psz_path, psz_ref ) < 0 )
ivoire's avatar
ivoire committed
627
628
629
                    {
                        free( psz_ref );
                        free( psz_path );
François Cartegnie's avatar
François Cartegnie committed
630
                        input_item_node_Delete( p_subitems );
ivoire's avatar
ivoire committed
631
                        vlc_object_release( p_input) ;
Christophe Mutricy's avatar
Christophe Mutricy committed
632
                        return VLC_ENOMEM;
ivoire's avatar
ivoire committed
633
                    }
634
635
636
637

                    free( psz_ref );
                    psz_ref = psz_absolute;
                    free( psz_path );
638
                }
639
                msg_Dbg( p_demux, "adding ref = `%s'", psz_ref );
640
641
642
643
                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 );
644
            }
645
            else
zorglub's avatar
zorglub committed
646
            {
647
                msg_Err( p_demux, "unknown ref type=%4.4s FIXME (send a bug report)",
648
                         (char*)&BOXDATA(p_rdrf)->i_ref_type );
zorglub's avatar
zorglub committed
649
            }
650
            free( psz_ref );
651
        }
652
        input_item_node_PostAndDelete( p_subitems );
653
        vlc_object_release( p_input );
654
655
    }

656
    if( !(p_mvhd = MP4_BoxGet( p_sys->p_root, "/moov/mvhd" ) ) )
657
    {
658
659
        if( !p_rmra )
        {
Laurent Aimar's avatar
Laurent Aimar committed
660
            msg_Err( p_demux, "cannot find /moov/mvhd" );
661
            goto error;
662
663
664
        }
        else
        {
Laurent Aimar's avatar
Laurent Aimar committed
665
666
            msg_Warn( p_demux, "cannot find /moov/mvhd (pure ref file)" );
            p_demux->pf_demux = DemuxRef;
667
            return VLC_SUCCESS;
668
        }
669
670
671
    }
    else
    {
672
        p_sys->i_timescale = BOXDATA(p_mvhd)->i_timescale;
673
674
675
676
677
        if( p_sys->i_timescale == 0 )
        {
            msg_Err( p_this, "bad timescale" );
            goto error;
        }
678
    }
679

680
    if ( p_sys->i_overall_duration == 0 )
681
    {
682
        /* Try in mehd if fragmented */
683
        MP4_Box_t *p_mehd = MP4_BoxGet( p_demux->p_sys->p_root, "moov/mvex/mehd");
684
        if ( p_mehd && p_mehd->data.p_mehd )
685
686
687
            p_sys->i_overall_duration = p_mehd->data.p_mehd->i_fragment_duration;
        else
            p_sys->i_overall_duration = p_sys->moovfragment.i_duration;
688
689
    }

690
    if( !( p_sys->i_tracks = MP4_BoxCount( p_sys->p_root, "/moov/trak" ) ) )
691
    {
Laurent Aimar's avatar
Laurent Aimar committed
692
        msg_Err( p_demux, "cannot find any /moov/trak" );
693
        goto error;
694
    }
695
    msg_Dbg( p_demux, "found %d track%c",
696
697
                        p_sys->i_tracks,
                        p_sys->i_tracks ? 's':' ' );
698

699
    if( InitTracks( p_demux ) != VLC_SUCCESS )
700
        goto error;
701

702
703
    /* Search the first chap reference (like quicktime) and
     * check that at least 1 stream is enabled */
704
    p_sys->p_tref_chap = NULL;
705
    b_enabled_es = false;
706
707
708
709
    for( i = 0; i < p_sys->i_tracks; i++ )
    {
        MP4_Box_t *p_trak = MP4_BoxGet( p_sys->p_root, "/moov/trak[%d]", i );

710
711

        MP4_Box_t *p_tkhd = MP4_BoxGet( p_trak, "tkhd" );
712
        if( p_tkhd && BOXDATA(p_tkhd) && (BOXDATA(p_tkhd)->i_flags&MP4_TRACK_ENABLED) )
713
714
715
            b_enabled_es = true;

        MP4_Box_t *p_chap = MP4_BoxGet( p_trak, "tref/chap", i );
716
717
        if( p_chap && p_chap->data.p_tref_generic &&
            p_chap->data.p_tref_generic->i_entry_count > 0 && !p_sys->p_tref_chap )
718
            p_sys->p_tref_chap = p_chap;
719
720
    }

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

727
        if( p_sys->track[i].b_ok && !p_sys->track[i].b_chapter )
728
        {
Laurent Aimar's avatar
Laurent Aimar committed
729
            const char *psz_cat;
730
            switch( p_sys->track[i].fmt.i_cat )
731
732
733
734
735
736
737
            {
                case( VIDEO_ES ):
                    psz_cat = "video";
                    break;
                case( AUDIO_ES ):
                    psz_cat = "audio";
                    break;
738
739
740
741
                case( SPU_ES ):
                    psz_cat = "subtitle";
                    break;

742
                default:
743
                    psz_cat = "unknown";
744
745
                    break;
            }
746

Laurent Aimar's avatar
Laurent Aimar committed
747
            msg_Dbg( p_demux, "adding track[Id 0x%x] %s (%s) language %s",
748
749
750
751
                     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" );
752
        }
753
754
755
756
757
758
759
        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" );
        }
760
761
        else
        {
762
763
            msg_Dbg( p_demux, "ignoring track[Id 0x%x]",
                     p_sys->track[i].i_track_ID );
764
765
        }
    }
766

767
768
769
    mp4_fragment_t *p_fragment = &p_sys->moovfragment;
    while ( p_fragment )
    {
770
        msg_Dbg( p_demux, "fragment offset %"PRId64", data %"PRIu64"<->%"PRIu64", duration %"PRId64,
771
                 p_fragment->p_moox->i_pos, p_fragment->i_chunk_range_min_offset,
772
                 p_fragment->i_chunk_range_max_offset, CLOCK_FREQ * p_fragment->i_duration / p_sys->i_timescale );
773
774
775
        p_fragment = p_fragment->p_next;
    }

776
777
    /* */
    LoadChapter( p_demux );
778

779
780
781
    return VLC_SUCCESS;

error:
782
    if( p_sys->p_root )
Laurent Aimar's avatar
Laurent Aimar committed
783
    {
Laurent Aimar's avatar
Laurent Aimar committed
784
        MP4_BoxFree( p_demux->s, p_sys->p_root );
Laurent Aimar's avatar
Laurent Aimar committed
785
    }
786
    free( p_sys );
787
    return VLC_EGENERIC;
788
789
790
}

/*****************************************************************************
791
 * Demux: read packet and send them to decoders
792
793
 *****************************************************************************
 * TODO check for newly selected track (ie audio upt to now )
794
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
795
static int Demux( demux_t *p_demux )
796
{
Laurent Aimar's avatar
Laurent Aimar committed
797
    demux_sys_t *p_sys = p_demux->p_sys;
798
    unsigned int i_track;
799

800
801
802
803

    unsigned int i_track_selected;

    /* check for newly selected/unselected track */
804
805
    for( i_track = 0, i_track_selected = 0; i_track < p_sys->i_tracks;
         i_track++ )
806
    {
Laurent Aimar's avatar
Laurent Aimar committed
807
        mp4_track_t *tk = &p_sys->track[i_track];
808
        bool b;
Laurent Aimar's avatar
Laurent Aimar committed
809

810
        if( !tk->b_ok || tk->b_chapter ||
811
            ( tk->b_selected && tk->i_sample >= tk->i_sample_count ) )
812
        {
813
            continue;
814
        }
815

816
        es_out_Control( p_demux->out, ES_OUT_GET_ES_STATE, tk->p_es, &b );
817

818
819
820
821
822
823
824
825
826
827
828
829
        if( tk->b_selected && !b )
        {
            MP4_TrackUnselect( p_demux, tk );
        }
        else if( !tk->b_selected && b)
        {
            MP4_TrackSelect( p_demux, tk, MP4_GetMoviePTS( p_sys ) );
        }

        if( tk->b_selected )
        {
            i_track_selected++;
830
831
832
833
834
        }
    }

    if( i_track_selected <= 0 )
    {
Laurent Aimar's avatar
   
Laurent Aimar committed
835
836
837
        p_sys->i_time += __MAX( p_sys->i_timescale / 10 , 1 );
        if( p_sys->i_timescale > 0 )
        {
838
            int64_t i_length = CLOCK_FREQ *
839
                               (mtime_t)p_sys->moovfragment.i_duration /
Laurent Aimar's avatar
   
Laurent Aimar committed
840
841
842
843
844
845
                               (mtime_t)p_sys->i_timescale;
            if( MP4_GetMoviePTS( p_sys ) >= i_length )
                return 0;
            return 1;
        }

Laurent Aimar's avatar
Laurent Aimar committed
846
        msg_Warn( p_demux, "no track selected, exiting..." );
Laurent Aimar's avatar
Laurent Aimar committed
847
        return 0;
848
849
    }

850
851
852
    /* */
    MP4_UpdateSeekpoint( p_demux );

853
    /* first wait for the good time to read a packet */
854
    p_sys->i_pcr = MP4_GetMoviePTS( p_sys );
855

856
    bool b_data_sent = false;
857

858
859
860
    /* Find next track matching contiguous data */
    mp4_track_t *tk = NULL;
    uint64_t i_candidate_pos = UINT64_MAX;
861
    mtime_t i_candidate_dts = INT64_MAX;
862
    for( i_track = 0; i_track < p_sys->i_tracks; i_track++ )
863
    {
864
865
        mp4_track_t *tk_tmp = &p_sys->track[i_track];
        if( !tk_tmp->b_ok || tk_tmp->b_chapter || !tk_tmp->b_selected || tk_tmp->i_sample >= tk_tmp->i_sample_count )
866
            continue;
867

868
        if ( p_sys->b_seekmode )
869
        {
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
            mtime_t i_dts = MP4_TrackGetDTS( p_demux, tk_tmp );
            if ( i_dts <= i_candidate_dts )
            {
                tk = tk_tmp;
                i_candidate_dts = i_dts;
                i_candidate_pos = MP4_TrackGetPos( tk_tmp );
            }
        }
        else
        {
            /* Try to avoid seeking on non fastseekable. Will fail with non interleaved content */
            uint64_t i_pos = MP4_TrackGetPos( tk_tmp );
            if ( i_pos <= i_candidate_pos )
            {
                i_candidate_pos = i_pos;
                tk = tk_tmp;
            }
887
888
889
890
891
892
893
894
        }
    }

    if ( !tk )
    {
        msg_Dbg( p_demux, "Could not select track by data position" );
        goto end;
    }
895
896
897
898
899
900
901
902
903
904
    else if ( p_sys->b_seekmode )
    {
        if( stream_Seek( p_demux->s, i_candidate_pos ) )
        {
            msg_Warn( p_demux, "track[0x%x] will be disabled (eof?)",
                      tk->i_track_ID );
            MP4_TrackUnselect( p_demux, tk );
            goto end;
        }
    }
905

906
#if 0
907
908
909
    msg_Dbg( p_demux, "tk(%i)=%"PRId64" mv=%"PRId64" pos=%"PRIu64, i_track,
             MP4_TrackGetDTS( p_demux, tk ),
             MP4_GetMoviePTS( p_sys ), i_candidate_pos );
910
#endif
911

912
    uint32_t i_nb_samples = 0;
913
    uint32_t i_samplessize = MP4_TrackGetReadSize( tk, &i_nb_samples );
914
915
916
917
    if( i_samplessize > 0 )
    {
        block_t *p_block;
        int64_t i_delta;
918
        uint64_t i_current_pos;
919

920
        /* go,go go ! */
921
922
923
924
        if ( !MP4_stream_Tell( p_demux->s, &i_current_pos ) )
            goto end;

        if( i_current_pos != i_candidate_pos )
925
926
927
928
929
930
931
932
933
        {
            if( stream_Seek( p_demux->s, i_candidate_pos ) )
            {
                msg_Warn( p_demux, "track[0x%x] will be disabled (eof?)",
                          tk->i_track_ID );
                MP4_TrackUnselect( p_demux, tk );
                goto end;
            }
        }
934

935
936
937
938
939
940
941
942
943
944
945
946
947
948
        /* now read pes */
        if( !(p_block = stream_Block( p_demux->s, i_samplessize )) )
        {
            msg_Warn( p_demux, "track[0x%x] will be disabled (eof?)",
                      tk->i_track_ID );
            MP4_TrackUnselect( p_demux, tk );
            goto end;
        }
        else if( tk->fmt.i_cat == SPU_ES )
        {
            if ( tk->fmt.i_codec != VLC_CODEC_TX3G &&
                 tk->fmt.i_codec != VLC_CODEC_SPU )
                p_block->i_buffer = 0;
        }
949

950
951
952
953
954
955
956
957
958
959
        /* dts */
        p_block->i_dts = VLC_TS_0 + MP4_TrackGetDTS( p_demux