mp4.c 185 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 <assert.h>
40

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
41
#include "id3genres.h"                             /* for ATOM_gnre */
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
80
    uint64_t     i_duration;     /* movie duration */
    unsigned int i_tracks;       /* number of tracks */
    mp4_track_t  *track;         /* array of track */
81
    float        f_fps;          /* number of frame per seconds */
82

83
    bool         b_fragmented;   /* fMP4 */
84
85
    bool         b_seekable;
    bool         b_fastseekable;
86
    bool         b_smooth;       /* Is it Smooth Streaming? */
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
98
99
    struct
    {
        mp4_fragment_t *p_fragment;
        uint32_t        i_current_box_type;
        uint32_t        i_mdatbytesleft;
    } context;

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

    /* */
    input_title_t *p_title;
105
106
};

107
108
#define BOXDATA(type) type->data.type

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

Laurent Aimar's avatar
Laurent Aimar committed
116
117
static int  MP4_TrackSelect ( demux_t *, mp4_track_t *, mtime_t );
static void MP4_TrackUnselect(demux_t *, mp4_track_t * );
118

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

Laurent Aimar's avatar
Laurent Aimar committed
121
static uint64_t MP4_TrackGetPos    ( mp4_track_t * );
122
static uint32_t MP4_TrackGetReadSize( mp4_track_t *, uint32_t * );
123
static int      MP4_TrackNextSample( demux_t *, mp4_track_t *, uint32_t );
Laurent Aimar's avatar
Laurent Aimar committed
124
static void     MP4_TrackSetELST( demux_t *, mp4_track_t *, int64_t );
Laurent Aimar's avatar
Laurent Aimar committed
125

126
static void     MP4_UpdateSeekpoint( demux_t * );
127
static const char *MP4_ConvertMacCode( uint16_t );
128

129
130
static MP4_Box_t * MP4_GetTrexByTrackID( MP4_Box_t *p_moov, const uint32_t i_id );

131
static bool AddFragment( demux_t *p_demux, MP4_Box_t *p_moox );
132
static int  ProbeFragments( demux_t *p_demux, bool b_force );
133
134
135
136
137
138
139
140
141
142
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 );
143
144
145

/* Helpers */

146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
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;
}

161
162
163
164
165
166
167
168
169
170
171
172
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;
    }
}

173
174
175
176
177
178
179
180
181
182
183
184
185
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 )
    {
        if ( p_trex->i_type == ATOM_trex && BOXDATA(p_trex)->i_track_ID == i_id )
                break;
        else
            p_trex = p_trex->p_next;
    }
    return p_trex;
}

186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
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 &&
            (p_tkhd = MP4_BoxGet( p_trak, "tkhd" )) &&
            BOXDATA(p_tkhd)->i_track_ID == i_id )
                break;
        else
            p_trak = p_trak->p_next;
    }
    return p_trak;
}

202
/* Return time in microsecond of a track */
Laurent Aimar's avatar
Laurent Aimar committed
203
static inline int64_t MP4_TrackGetDTS( demux_t *p_demux, mp4_track_t *p_track )
Laurent Aimar's avatar
Laurent Aimar committed
204
{
205
206
207
208
209
210
    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];
211
212
213
214

    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
215
216
217

    while( i_sample > 0 )
    {
218
        if( i_sample > chunk.p_sample_count_dts[i_index] )
Laurent Aimar's avatar
Laurent Aimar committed
219
        {
220
221
222
            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
223
224
225
226
            i_index++;
        }
        else
        {
227
            i_dts += i_sample * chunk.p_sample_delta_dts[i_index];
Laurent Aimar's avatar
Laurent Aimar committed
228
229
230
231
            break;
        }
    }

232
233
234
    /* now handle elst */
    if( p_track->p_elst )
    {
Laurent Aimar's avatar
Laurent Aimar committed
235
        demux_sys_t         *p_sys = p_demux->p_sys;
236
        MP4_Box_data_elst_t *elst = p_track->BOXDATA(p_elst);
237
238
239
240
241
242
243
244
245
246

        /* 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 */
247
248
        i_dts += p_track->i_elst_time * p_track->i_timescale /
            p_sys->i_timescale;
249
250
251
252

        if( i_dts < 0 ) i_dts = 0;
    }

253
    return CLOCK_FREQ * i_dts / p_track->i_timescale;
Laurent Aimar's avatar
Laurent Aimar committed
254
}
255

256
static inline int64_t MP4_TrackGetPTSDelta( demux_t *p_demux, mp4_track_t *p_track )
Laurent Aimar's avatar
Laurent Aimar committed
257
{
258
259
260
261
262
263
264
    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
265
266
267
268
269
270
271
272
273
    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] )
274
            return ck->p_sample_offset_pts[i_index] * CLOCK_FREQ /
275
                   (int64_t)p_track->i_timescale;
Laurent Aimar's avatar
Laurent Aimar committed
276
277
278
279
280

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

Laurent Aimar's avatar
Laurent Aimar committed
281
282
static inline int64_t MP4_GetMoviePTS(demux_sys_t *p_sys )
{
283
    return CLOCK_FREQ * p_sys->i_time / p_sys->i_timescale;
Laurent Aimar's avatar
Laurent Aimar committed
284
}
285

286
287
static void LoadChapter( demux_t  *p_demux );

288
static int LoadInitFrag( demux_t *p_demux )
289
290
291
{
    demux_sys_t *p_sys = p_demux->p_sys;

292
    if( p_sys->b_smooth ) /* Smooth Streaming */
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
    {
        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 );
308
                if( p_stra && BOXDATA(p_stra)->i_track_ID )
309
310
                    p_sys->i_tracks++;
                /* Get timescale and duration of the video track; */
311
                if( p_sys->i_timescale == 0 )
312
                {
313
314
                    p_sys->i_timescale = BOXDATA(p_stra)->i_timescale;
                    p_sys->i_duration = BOXDATA(p_stra)->i_duration;
315
                    p_sys->i_overall_duration = BOXDATA(p_stra)->i_duration;
316
317
318
319
320
                    if( p_sys->i_timescale == 0 )
                    {
                        msg_Err( p_demux, "bad timescale" );
                        goto LoadInitFragError;
                    }
321
322
323
324
325
326
327
328
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
                }
            }
        }
    }
    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 );
375
        if( !p_stra || BOXDATA(p_stra)->i_track_ID == 0 )
376
377
378
379
380
381
382
383
384
385
            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 );
        }
    }
}

386
/*****************************************************************************
387
 * Open: check file and initializes MP4 structures
388
 *****************************************************************************/
389
static int Open( vlc_object_t * p_this )
390
{
Laurent Aimar's avatar
Laurent Aimar committed
391
    demux_t  *p_demux = (demux_t *)p_this;
392
    demux_sys_t     *p_sys;
393

Christophe Mutricy's avatar
Christophe Mutricy committed
394
    const uint8_t   *p_peek;
395

396
    MP4_Box_t       *p_ftyp;
397
    MP4_Box_t       *p_rmra;
398
399
    MP4_Box_t       *p_mvhd;
    MP4_Box_t       *p_trak;
400

401
    unsigned int    i;
402
    bool      b_enabled_es;
403

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

407
    switch( VLC_FOURCC( p_peek[4], p_peek[5], p_peek[6], p_peek[7] ) )
408
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
409
410
411
412
413
414
415
416
        case ATOM_moov:
        case ATOM_foov:
        case ATOM_moof:
        case ATOM_mdat:
        case ATOM_udta:
        case ATOM_free:
        case ATOM_skip:
        case ATOM_wide:
417
        case ATOM_uuid:
418
        case VLC_FOURCC( 'p', 'n', 'o', 't' ):
419
            break;
420
421
422
423
424
        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;
425
         default:
426
            return VLC_EGENERIC;
427
    }
428

429
430
431
432
433
    /* create our structure that will contains all data */
    p_sys = calloc( 1, sizeof( demux_sys_t ) );
    if ( !p_sys )
        return VLC_EGENERIC;

434
    /* I need to seek */
435
436
    stream_Control( p_demux->s, STREAM_CAN_SEEK, &p_sys->b_seekable );
    if( !p_sys->b_seekable )
437
    {
438
        msg_Warn( p_demux, "MP4 plugin discarded (not seekable)" );
439
        free( p_sys );
440
        return VLC_EGENERIC;
441
    }
442
    stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &p_sys->b_fastseekable );
443

444
    /*Set exported functions */
Laurent Aimar's avatar
Laurent Aimar committed
445
446
    p_demux->pf_demux = Demux;
    p_demux->pf_control = Control;
447

448
    p_demux->p_sys = p_sys;
449

450
451
452
    if( stream_Peek( p_demux->s, &p_peek, 24 ) < 24 ) return VLC_EGENERIC;
    if( !CmpUUID( (UUID_t *)(p_peek + 8), &SmooBoxUUID ) )
    {
453
        p_sys->b_smooth = true;
454
455
456
        p_sys->b_fragmented = true;
    }

457
    if( LoadInitFrag( p_demux ) != VLC_SUCCESS )
458
459
        goto error;

460
    if( MP4_BoxCount( p_sys->p_root, "/moov/mvex" ) > 0 )
461
    {
462
463
464
465
        if ( p_sys->b_seekable )
        {
            /* Probe remaining to check if there's really fragments
               or if that file is just ready to append fragments */
466
            ProbeFragments( p_demux, false );
467
468
469
470
            p_sys->b_fragmented = !!MP4_BoxCount( p_sys->p_root, "/moof" );
        }
        else
            p_sys->b_fragmented = true;
471
472
473

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

476
477
478
479
480
481
482
    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;

483
    if ( p_sys->b_smooth )
484
485
486
    {
        p_demux->pf_demux = DemuxFrg;
    }
487
488
489
490
    else if( p_sys->b_fragmented )
    {
        p_demux->pf_demux = DemuxAsLeaf;
    }
491

492
    if( p_sys->b_smooth )
493
494
495
496
497
498
    {
        if( InitTracks( p_demux ) != VLC_SUCCESS )
            goto error;
        CreateTracksFromSmooBox( p_demux );
        return VLC_SUCCESS;
    }
499

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

502
    if( ( p_ftyp = MP4_BoxGet( p_sys->p_root, "/ftyp" ) ) )
503
    {
504
        switch( BOXDATA(p_ftyp)->i_major_brand )
505
        {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
506
            case( ATOM_isom ):
Laurent Aimar's avatar
Laurent Aimar committed
507
                msg_Dbg( p_demux,
508
                         "ISO Media file (isom) version %d.",
509
                         BOXDATA(p_ftyp)->i_minor_version );
510
                break;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
511
            case( ATOM_3gp4 ):
512
513
514
515
516
            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
517
                        BOXDATA(p_ftyp)->i_major_brand
518
#else
519
                        BOXDATA(p_ftyp)->i_major_brand >> 24
520
521
522
523
524
#endif
                        );
                break;
            case( VLC_FOURCC( 'q', 't', ' ', ' ') ):
                msg_Dbg( p_demux, "Apple QuickTime file" );
525
                break;
526
527
528
            case( VLC_FOURCC( 'i', 's', 'm', 'l') ):
                msg_Dbg( p_demux, "PIFF (= isml = fMP4) file" );
                break;
529
            default:
Laurent Aimar's avatar
Laurent Aimar committed
530
                msg_Dbg( p_demux,
531
                         "unrecognized major file specification (%4.4s).",
532
                          (char*)&BOXDATA(p_ftyp)->i_major_brand );
533
534
535
536
537
                break;
        }
    }
    else
    {
Laurent Aimar's avatar
Laurent Aimar committed
538
        msg_Dbg( p_demux, "file type box missing (assuming ISO Media file)" );
539
540
541
    }

    /* the file need to have one moov box */
542
543
    p_sys->moovfragment.p_moox = MP4_BoxGet( p_sys->p_root, "/moov", 0 );
    if( !p_sys->moovfragment.p_moox )
544
    {
545
        MP4_Box_t *p_foov = MP4_BoxGet( p_sys->p_root, "/foov" );
546
547
548

        if( !p_foov )
        {
549
550
            msg_Err( p_demux, "MP4 plugin discarded (no moov,foov,moof box)" );
            goto error;
551
552
        }
        /* we have a free box as a moov, rename it */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
553
        p_foov->i_type = ATOM_moov;
554
        p_sys->moovfragment.p_moox = p_foov;
555
556
    }

557
    if( ( p_rmra = MP4_BoxGet( p_sys->p_root,  "/moov/rmra" ) ) )
558
559
560
561
    {
        int        i_count = MP4_BoxCount( p_rmra, "rmda" );
        int        i;

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

564
        input_thread_t *p_input = demux_GetParentInput( p_demux );
565
        input_item_t *p_current = input_GetItem( p_input );
566

567
568
        input_item_node_t *p_subitems = input_item_node_Create( p_current );

569
        for( i = 0; i < i_count; i++ )
570
        {
571
572
573
            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
574

575
            if( !p_rdrf || !( psz_ref = strdup( BOXDATA(p_rdrf)->psz_ref ) ) )
576
            {
577
578
                continue;
            }
579
            i_ref_type = BOXDATA(p_rdrf)->i_ref_type;
580

581
582
583
584
585
586
            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" ) )
587
                {
588
                    msg_Dbg( p_demux, "ignoring pseudo ref =`%s'", psz_ref );
589
590
                    continue;
                }
591
592
                if( !strncmp( psz_ref, "http://", 7 ) ||
                    !strncmp( psz_ref, "rtsp://", 7 ) )
593
                {
594
                    ;
595
596
597
                }
                else
                {
598
                    char *psz_absolute;
599
                    char *psz_path = strdup( p_demux->psz_location );
600
601
602
603
                    char *end = strrchr( psz_path, '/' );
                    if( end ) end[1] = '\0';
                    else *psz_path = '\0';

Christophe Mutricy's avatar
Christophe Mutricy committed
604
605
                    if( asprintf( &psz_absolute, "%s://%s%s",
                                  p_demux->psz_access, psz_path, psz_ref ) < 0 )
ivoire's avatar
ivoire committed
606
607
608
                    {
                        free( psz_ref );
                        free( psz_path );
François Cartegnie's avatar
François Cartegnie committed
609
                        input_item_node_Delete( p_subitems );
ivoire's avatar
ivoire committed
610
                        vlc_object_release( p_input) ;
Christophe Mutricy's avatar
Christophe Mutricy committed
611
                        return VLC_ENOMEM;
ivoire's avatar
ivoire committed
612
                    }
613
614
615
616

                    free( psz_ref );
                    psz_ref = psz_absolute;
                    free( psz_path );
617
                }
618
                msg_Dbg( p_demux, "adding ref = `%s'", psz_ref );
619
620
621
622
                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 );
623
            }
624
            else
zorglub's avatar
zorglub committed
625
            {
626
                msg_Err( p_demux, "unknown ref type=%4.4s FIXME (send a bug report)",
627
                         (char*)&BOXDATA(p_rdrf)->i_ref_type );
zorglub's avatar
zorglub committed
628
            }
629
            free( psz_ref );
630
        }
631
        input_item_node_PostAndDelete( p_subitems );
632
        vlc_object_release( p_input );
633
634
    }

635
    if( !(p_mvhd = MP4_BoxGet( p_sys->p_root, "/moov/mvhd" ) ) )
636
    {
637
638
        if( !p_rmra )
        {
Laurent Aimar's avatar
Laurent Aimar committed
639
            msg_Err( p_demux, "cannot find /moov/mvhd" );
640
            goto error;
641
642
643
        }
        else
        {
Laurent Aimar's avatar
Laurent Aimar committed
644
645
            msg_Warn( p_demux, "cannot find /moov/mvhd (pure ref file)" );
            p_demux->pf_demux = DemuxRef;
646
            return VLC_SUCCESS;
647
        }
648
649
650
    }
    else
    {
651
        p_sys->i_timescale = BOXDATA(p_mvhd)->i_timescale;
652
653
654
655
656
        if( p_sys->i_timescale == 0 )
        {
            msg_Err( p_this, "bad timescale" );
            goto error;
        }
657
    }
658

659
    if ( p_sys->i_overall_duration == 0 )
660
    {
661
        /* Try in mehd if fragmented */
662
663
        MP4_Box_t *p_mehd = MP4_BoxGet( p_demux->p_sys->p_root, "moov/mvex/mehd");
        if ( p_mehd )
664
665
666
            p_sys->i_overall_duration = p_mehd->data.p_mehd->i_fragment_duration;
        else
            p_sys->i_overall_duration = p_sys->moovfragment.i_duration;
667
668
    }

669
    if( !( p_sys->i_tracks = MP4_BoxCount( p_sys->p_root, "/moov/trak" ) ) )
670
    {
Laurent Aimar's avatar
Laurent Aimar committed
671
        msg_Err( p_demux, "cannot find any /moov/trak" );
672
        goto error;
673
    }
674
    msg_Dbg( p_demux, "found %d track%c",
675
676
                        p_sys->i_tracks,
                        p_sys->i_tracks ? 's':' ' );
677

678
    if( InitTracks( p_demux ) != VLC_SUCCESS )
679
        goto error;
680

681
682
    /* Search the first chap reference (like quicktime) and
     * check that at least 1 stream is enabled */
683
    p_sys->p_tref_chap = NULL;
684
    b_enabled_es = false;
685
686
687
688
    for( i = 0; i < p_sys->i_tracks; i++ )
    {
        MP4_Box_t *p_trak = MP4_BoxGet( p_sys->p_root, "/moov/trak[%d]", i );

689
690

        MP4_Box_t *p_tkhd = MP4_BoxGet( p_trak, "tkhd" );
691
        if( p_tkhd && (BOXDATA(p_tkhd)->i_flags&MP4_TRACK_ENABLED) )
692
693
694
695
696
            b_enabled_es = true;

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

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

705
        if( p_sys->track[i].b_ok && !p_sys->track[i].b_chapter )
706
        {
Laurent Aimar's avatar
Laurent Aimar committed
707
            const char *psz_cat;
708
            switch( p_sys->track[i].fmt.i_cat )
709
710
711
712
713
714
715
            {
                case( VIDEO_ES ):
                    psz_cat = "video";
                    break;
                case( AUDIO_ES ):
                    psz_cat = "audio";
                    break;
716
717
718
719
                case( SPU_ES ):
                    psz_cat = "subtitle";
                    break;

720
                default:
721
                    psz_cat = "unknown";
722
723
                    break;
            }
724

Laurent Aimar's avatar
Laurent Aimar committed
725
            msg_Dbg( p_demux, "adding track[Id 0x%x] %s (%s) language %s",
726
727
728
729
                     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" );
730
        }
731
732
733
734
735
736
737
        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" );
        }
738
739
        else
        {
740
741
            msg_Dbg( p_demux, "ignoring track[Id 0x%x]",
                     p_sys->track[i].i_track_ID );
742
743
        }
    }
744

745
746
747
    mp4_fragment_t *p_fragment = &p_sys->moovfragment;
    while ( p_fragment )
    {
748
        msg_Dbg( p_demux, "fragment offset %"PRId64", data %"PRIu64"<->%"PRIu64", duration %"PRId64,
749
                 p_fragment->p_moox->i_pos, p_fragment->i_chunk_range_min_offset,
750
                 p_fragment->i_chunk_range_max_offset, CLOCK_FREQ * p_fragment->i_duration / p_sys->i_timescale );
751
752
753
        p_fragment = p_fragment->p_next;
    }

754
755
    /* */
    LoadChapter( p_demux );
756

757
758
759
    return VLC_SUCCESS;

error:
760
    if( p_sys->p_root )
Laurent Aimar's avatar
Laurent Aimar committed
761
    {
Laurent Aimar's avatar
Laurent Aimar committed
762
        MP4_BoxFree( p_demux->s, p_sys->p_root );
Laurent Aimar's avatar
Laurent Aimar committed
763
    }
764
    free( p_sys );
765
    return VLC_EGENERIC;
766
767
768
}

/*****************************************************************************
769
 * Demux: read packet and send them to decoders
770
771
 *****************************************************************************
 * TODO check for newly selected track (ie audio upt to now )
772
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
773
static int Demux( demux_t *p_demux )
774
{
Laurent Aimar's avatar
Laurent Aimar committed
775
    demux_sys_t *p_sys = p_demux->p_sys;
776
    unsigned int i_track;
777

778
779
780
781

    unsigned int i_track_selected;

    /* check for newly selected/unselected track */
782
783
    for( i_track = 0, i_track_selected = 0; i_track < p_sys->i_tracks;
         i_track++ )
784
    {
Laurent Aimar's avatar
Laurent Aimar committed
785
        mp4_track_t *tk = &p_sys->track[i_track];
786
        bool b;
Laurent Aimar's avatar
Laurent Aimar committed
787

788
        if( !tk->b_ok || tk->b_chapter ||
789
            ( tk->b_selected && tk->i_sample >= tk->i_sample_count ) )
790
        {
791
            continue;
792
        }
793

794
        es_out_Control( p_demux->out, ES_OUT_GET_ES_STATE, tk->p_es, &b );
795

796
797
798
799
800
801
802
803
804
805
806
807
        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++;
808
809
810
811
812
        }
    }

    if( i_track_selected <= 0 )
    {
Laurent Aimar's avatar
   
Laurent Aimar committed
813
814
815
        p_sys->i_time += __MAX( p_sys->i_timescale / 10 , 1 );
        if( p_sys->i_timescale > 0 )
        {
816
            int64_t i_length = CLOCK_FREQ *
817
                               (mtime_t)p_sys->moovfragment.i_duration /
Laurent Aimar's avatar
   
Laurent Aimar committed
818
819
820
821
822
823
                               (mtime_t)p_sys->i_timescale;
            if( MP4_GetMoviePTS( p_sys ) >= i_length )
                return 0;
            return 1;
        }

Laurent Aimar's avatar
Laurent Aimar committed
824
        msg_Warn( p_demux, "no track selected, exiting..." );
Laurent Aimar's avatar
Laurent Aimar committed
825
        return 0;
826
827
    }

828
829
830
    /* */
    MP4_UpdateSeekpoint( p_demux );

831
    /* first wait for the good time to read a packet */
832
    p_sys->i_pcr = MP4_GetMoviePTS( p_sys );
833

834
    bool b_data_sent = false;
835

836
837
838
    /* Find next track matching contiguous data */
    mp4_track_t *tk = NULL;
    uint64_t i_candidate_pos = UINT64_MAX;
839
    for( i_track = 0; i_track < p_sys->i_tracks; i_track++ )
840
    {
841
842
        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 )
843
            continue;
844

845
846
        uint64_t i_pos = MP4_TrackGetPos( tk_tmp );
        if ( i_pos <= i_candidate_pos )
847
        {
848
849
850
851
852
853
854
855
856
857
858
            i_candidate_pos = i_pos;
            tk = tk_tmp;
        }
    }

    if ( !tk )
    {
        msg_Dbg( p_demux, "Could not select track by data position" );
        goto end;
    }

859
#if 0
860
861
862
    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 );
863
#endif
864

865
    uint32_t i_nb_samples = 0;
866
    uint32_t i_samplessize = MP4_TrackGetReadSize( tk, &i_nb_samples );
867
868
869
870
    if( i_samplessize > 0 )
    {
        block_t *p_block;
        int64_t i_delta;
871
        uint64_t i_current_pos;
872

873
        /* go,go go ! */
874
875
876
877
        if ( !MP4_stream_Tell( p_demux->s, &i_current_pos ) )
            goto end;

        if( i_current_pos != i_candidate_pos )
878
879
880
881
882
883
884
885
886
        {
            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;
            }
        }
887

888
889
890
891
892
893
894
895
896
897
898
899
900
901
        /* 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;
        }
902

903
904
905
906
907
908
909
910
911
912
        /* dts */
        p_block->i_dts = VLC_TS_0 + MP4_TrackGetDTS( p_demux, tk );
        /* pts */
        i_delta = MP4_TrackGetPTSDelta( p_demux, tk );
        if( i_delta != -1 )
            p_block->i_pts = p_block->i_dts + i_delta;
        else if( tk->fmt.i_cat != VIDEO_ES )
            p_block->i_pts = p_block->i_dts;
        else
            p_block->i_pts = VLC_TS_INVALID;
913

914
915
916
917
        if ( !b_data_sent )
        {
            es_out_Control( p_demux->out, ES_OUT_SET_PCR, VLC_TS_0 + p_sys->i_pcr );
            b_data_sent = true;
918
        }
919
        es_out_Send( p_demux->out, tk->p_es, p_block );
920
    }
921

922
923
    /* Next sample */
    if ( i_nb_samples ) /* sample size could be 0, need to go fwd. see return */
924
        MP4_TrackNextSample( p_demux, tk, i_nb_samples );
925

926
end:
927
928
929
930
931
932
    if ( b_data_sent )
    {
        p_sys->i_pcr = INT64_MAX;
        for( i_track = 0; i_track < p_sys->i_tracks; i_track++ )
        {
            mp4_track_t *tk = &p_sys->track[i_track];
933
934
935
            if ( !tk->b_ok || !tk->b_selected  ||
                 (tk->fmt.i_cat != AUDIO_ES && tk->fmt.i_cat != VIDEO_ES) )
                continue;
936
937
938
939
940
            p_sys->i_pcr = __MIN( MP4_TrackGetDTS( p_demux, tk ), p_sys->i_pcr );
            p_sys->i_time = p_sys->i_pcr * p_sys->i_timescale / CLOCK_FREQ;
        }
    }

941
    return b_data_sent || ( i_samplessize == 0 && i_nb_samples );
942
}
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959