mp4.c 54.8 KB
Newer Older
1 2 3 4
/*****************************************************************************
 * mp4.c : MP4 file input module for vlc
 *****************************************************************************
 * Copyright (C) 2001 VideoLAN
5
 * $Id: mp4.c,v 1.53 2004/01/18 06:33:21 fenrir Exp $
6
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
7
 *
8 9 10 11
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
12
 *
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>                                      /* malloc(), free() */

#include <vlc/vlc.h>
#include <vlc/input.h>
30
#include <vlc_playlist.h>
31
#include "iso_lang.h"
32

33 34
#include "libmp4.h"
#include "mp4.h"
35
#include "drms.h"
36 37 38 39

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
40 41
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );
42

43
vlc_module_begin();
Gildas Bazin's avatar
 
Gildas Bazin committed
44
    set_description( _("MP4 demuxer") );
45
    set_capability( "demux", 242 );
46
    set_callbacks( Open, Close );
47 48
vlc_module_end();

49 50 51 52 53 54 55 56 57 58 59
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static int    Demux   ( input_thread_t * );
static int    DemuxRef( input_thread_t *p_input )
{
    return 0;
}
static int   Seek     ( input_thread_t *, mtime_t );
static int   Control  ( input_thread_t *, int, va_list );

60
/*****************************************************************************
61
 * Declaration of local function
62
 *****************************************************************************/
63 64
static void MP4_TrackCreate ( input_thread_t *, track_data_mp4_t *, MP4_Box_t  *);
static void MP4_TrackDestroy( input_thread_t *, track_data_mp4_t * );
65

66 67
static int  MP4_TrackSelect ( input_thread_t *, track_data_mp4_t *, mtime_t );
static void MP4_TrackUnselect(input_thread_t *, track_data_mp4_t * );
68

69
static int  MP4_TrackSeek   ( input_thread_t *, track_data_mp4_t *, mtime_t );
70

71 72 73
static uint64_t MP4_GetTrackPos    ( track_data_mp4_t * );
static int      MP4_TrackSampleSize( track_data_mp4_t * );
static int      MP4_TrackNextSample( input_thread_t *, track_data_mp4_t * );
74

75 76
static char *LanguageGetName( const char *psz_code );

77 78
#define FREE( p ) \
    if( p ) { free( p ); (p) = NULL;}
79

80
/*****************************************************************************
81
 * Open: check file and initializes MP4 structures
82
 *****************************************************************************/
83
static int Open( vlc_object_t * p_this )
84
{
85
    input_thread_t  *p_input = (input_thread_t *)p_this;
86
    demux_sys_t     *p_sys;
87

88
    uint8_t         *p_peek;
89

90
    MP4_Box_t       *p_ftyp;
91
    MP4_Box_t       *p_rmra;
92 93
    MP4_Box_t       *p_mvhd;
    MP4_Box_t       *p_trak;
94

95
    unsigned int    i;
96
    vlc_bool_t      b_seekable;
97

98
    /* a little test to see if it could be a mp4 */
99
    if( stream_Peek( p_input->s, &p_peek, 8 ) < 8 )
100 101
    {
        msg_Warn( p_input, "MP4 plugin discarded (cannot peek)" );
102
        return VLC_EGENERIC;
103
    }
104
    switch( VLC_FOURCC( p_peek[4], p_peek[5], p_peek[6], p_peek[7] ) )
105
    {
106 107 108 109 110 111 112 113 114
        case FOURCC_ftyp:
        case FOURCC_moov:
        case FOURCC_foov:
        case FOURCC_moof:
        case FOURCC_mdat:
        case FOURCC_udta:
        case FOURCC_free:
        case FOURCC_skip:
        case FOURCC_wide:
115
        case VLC_FOURCC( 'p', 'n', 'o', 't' ):
116 117 118
            break;
         default:
            msg_Warn( p_input, "MP4 plugin discarded (not a valid file)" );
119
            return VLC_EGENERIC;
120
    }
121

122
    /* I need to seek */
123 124
    stream_Control( p_input->s, STREAM_CAN_SEEK, &b_seekable );
    if( !b_seekable )
125
    {
126 127
        msg_Warn( p_input, "MP4 plugin discarded (unseekable)" );
        return VLC_EGENERIC;
128
    }
129

130 131 132 133 134
    /*Set exported functions */
    p_input->pf_demux = Demux;
    p_input->pf_demux_control = Control;

    /* create our structure that will contains all data */
135 136
    p_input->p_demux_data = p_sys = malloc( sizeof( demux_sys_t ) );
    memset( p_sys, 0, sizeof( demux_sys_t ) );
137 138

    /* Now load all boxes ( except raw data ) */
139
    if( ( p_sys->p_root = MP4_BoxGetRoot( p_input ) ) == NULL )
140 141
    {
        msg_Warn( p_input, "MP4 plugin discarded (not a valid file)" );
Laurent Aimar's avatar
Laurent Aimar committed
142
        goto error;
143 144
    }

145
    MP4_BoxDumpStructure( p_input, p_sys->p_root );
146

147
    if( ( p_ftyp = MP4_BoxGet( p_sys->p_root, "/ftyp" ) ) )
148 149 150 151
    {
        switch( p_ftyp->data.p_ftyp->i_major_brand )
        {
            case( FOURCC_isom ):
152
                msg_Dbg( p_input,
153 154
                         "ISO Media file (isom) version %d.",
                         p_ftyp->data.p_ftyp->i_minor_version );
155 156
                break;
            default:
157
                msg_Dbg( p_input,
158 159
                         "unrecognized major file specification (%4.4s).",
                          (char*)&p_ftyp->data.p_ftyp->i_major_brand );
160 161 162 163 164
                break;
        }
    }
    else
    {
165
        msg_Dbg( p_input, "file type box missing (assuming ISO Media file)" );
166 167 168
    }

    /* the file need to have one moov box */
169
    if( MP4_BoxCount( p_sys->p_root, "/moov" ) <= 0 )
170
    {
171
        MP4_Box_t *p_foov = MP4_BoxGet( p_sys->p_root, "/foov" );
172 173 174 175

        if( !p_foov )
        {
            msg_Err( p_input, "MP4 plugin discarded (no moov box)" );
176
            goto error;
177 178 179
        }
        /* we have a free box as a moov, rename it */
        p_foov->i_type = FOURCC_moov;
180 181
    }

182
    if( ( p_rmra = MP4_BoxGet( p_sys->p_root,  "/moov/rmra" ) ) )
183 184 185 186 187 188 189 190 191 192 193 194 195
    {
        playlist_t *p_playlist;
        int        i_count = MP4_BoxCount( p_rmra, "rmda" );
        int        i;

        msg_Dbg( p_input, "detected playlist mov file (%d ref)", i_count );

        p_playlist =
            (playlist_t *)vlc_object_find( p_input,
                                           VLC_OBJECT_PLAYLIST,
                                           FIND_ANYWHERE );
        if( p_playlist )
        {
196
            //p_playlist->pp_items[p_playlist->i_index]->b_autodeletion = VLC_TRUE;
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
            for( i = 0; i < i_count; i++ )
            {
                MP4_Box_t *p_rdrf = MP4_BoxGet( p_rmra, "rmda[%d]/rdrf", i );
                char      *psz_ref;
                uint32_t  i_ref_type;

                if( !p_rdrf || !( psz_ref = p_rdrf->data.p_rdrf->psz_ref ) )
                {
                    continue;
                }
                i_ref_type = p_rdrf->data.p_rdrf->i_ref_type;

                msg_Dbg( p_input, "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" ) )
                    {
                        msg_Dbg( p_input, "ignoring pseudo ref =`%s'", psz_ref );
                        continue;
                    }
219 220
                    if( !strncmp( psz_ref, "http://", 7 ) ||
                        !strncmp( psz_ref, "rtsp://", 7 ) )
221 222
                    {
                        msg_Dbg( p_input, "adding ref = `%s'", psz_ref );
Clément Stenac's avatar
Clément Stenac committed
223
                        playlist_Add( p_playlist, psz_ref, psz_ref,
Gildas Bazin's avatar
 
Gildas Bazin committed
224
                                      PLAYLIST_APPEND, PLAYLIST_END );
225 226 227 228
                    }
                    else
                    {
                        /* msg dbg relative ? */
229 230
                        char *psz_absolute = alloca( strlen( p_input->psz_source ) + strlen( psz_ref ) + 1);
                        char *end = strrchr( p_input->psz_source, '/' );
231 232 233

                        if( end )
                        {
234
                            int i_len = end + 1 - p_input->psz_source;
235

236
                            strncpy( psz_absolute, p_input->psz_source, i_len);
237
                            psz_absolute[i_len] = '\0';
238 239 240 241 242 243 244
                        }
                        else
                        {
                            strcpy( psz_absolute, "" );
                        }
                        strcat( psz_absolute, psz_ref );
                        msg_Dbg( p_input, "adding ref = `%s'", psz_absolute );
Clément Stenac's avatar
Clément Stenac committed
245
                        playlist_Add( p_playlist, psz_absolute, psz_absolute,
Gildas Bazin's avatar
 
Gildas Bazin committed
246
                                      PLAYLIST_APPEND, PLAYLIST_END );
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
                    }
                }
                else
                {
                    msg_Err( p_input, "unknown ref type=%4.4s FIXME (send a bug report)", (char*)&p_rdrf->data.p_rdrf->i_ref_type );
                }
            }
            vlc_object_release( p_playlist );
        }
        else
        {
            msg_Err( p_input, "can't find playlist" );
        }
    }

262
    if( !(p_mvhd = MP4_BoxGet( p_sys->p_root, "/moov/mvhd" ) ) )
263
    {
264 265 266
        if( !p_rmra )
        {
            msg_Err( p_input, "cannot find /moov/mvhd" );
267
            goto error;
268 269 270 271
        }
        else
        {
            msg_Warn( p_input, "cannot find /moov/mvhd (pure ref file)" );
272
            p_input->pf_demux = DemuxRef;
273
            return VLC_SUCCESS;
274
        }
275 276 277
    }
    else
    {
278 279
        p_sys->i_timescale = p_mvhd->data.p_mvhd->i_timescale;
        p_sys->i_duration = p_mvhd->data.p_mvhd->i_duration;
280
    }
281

282
    if( !( p_sys->i_tracks = MP4_BoxCount( p_sys->p_root, "/moov/trak" ) ) )
283
    {
284
        msg_Err( p_input, "cannot find any /moov/trak" );
285
        goto error;
286
    }
287
    msg_Dbg( p_input, "find %d track%c",
288 289
                        p_sys->i_tracks,
                        p_sys->i_tracks ? 's':' ' );
290

291 292 293 294 295 296
    /*  create one program */
    vlc_mutex_lock( &p_input->stream.stream_lock );
    if( input_InitStream( p_input, 0 ) == -1)
    {
        vlc_mutex_unlock( &p_input->stream.stream_lock );
        msg_Err( p_input, "cannot init stream" );
297
        goto error;
298
    }
299
    if( p_sys->i_duration/p_sys->i_timescale > 0 )
300 301 302
    {
        p_input->stream.i_mux_rate =
            p_input->stream.p_selected_area->i_size / 50 /
303
            ( p_sys->i_duration / p_sys->i_timescale );
304 305 306 307 308 309 310 311
    }
    else
    {
        p_input->stream.i_mux_rate = 0;
    }
    vlc_mutex_unlock( &p_input->stream.stream_lock );


312
    /* allocate memory */
313 314
    p_sys->track = calloc( p_sys->i_tracks, sizeof( track_data_mp4_t ) );
    memset( p_sys->track, 0, p_sys->i_tracks * sizeof( track_data_mp4_t ) );
315 316

    /* now process each track and extract all usefull informations */
317
    for( i = 0; i < p_sys->i_tracks; i++ )
318
    {
319 320
        p_trak = MP4_BoxGet( p_sys->p_root, "/moov/trak[%d]", i );
        MP4_TrackCreate( p_input, &p_sys->track[i], p_trak );
321

322
        if( p_sys->track[i].b_ok )
323 324
        {
            char *psz_cat;
325
            switch( p_sys->track[i].fmt.i_cat )
326 327 328 329 330 331 332 333
            {
                case( VIDEO_ES ):
                    psz_cat = "video";
                    break;
                case( AUDIO_ES ):
                    psz_cat = "audio";
                    break;
                default:
334
                    psz_cat = "unknown";
335 336
                    break;
            }
337

338 339
            msg_Dbg( p_input, "adding track[Id 0x%x] %s (%s) language %s",
                            p_sys->track[i].i_track_ID,
340
                            psz_cat,
341 342
                            p_sys->track[i].b_enable ? "enable":"disable",
                            p_sys->track[i].fmt.psz_language ? p_sys->track[i].fmt.psz_language : "undef" );
343 344 345
        }
        else
        {
346
            msg_Dbg( p_input, "ignoring track[Id 0x%x]", p_sys->track[i].i_track_ID );
347 348 349
        }

    }
350 351 352
    return VLC_SUCCESS;

error:
353
    if( p_sys->p_root )
Laurent Aimar's avatar
Laurent Aimar committed
354
    {
355
        MP4_BoxFree( p_input, p_sys->p_root );
Laurent Aimar's avatar
Laurent Aimar committed
356
    }
357
    free( p_sys );
358
    return VLC_EGENERIC;
359 360 361
}

/*****************************************************************************
362
 * Demux: read packet and send them to decoders
363 364
 *****************************************************************************
 * TODO check for newly selected track (ie audio upt to now )
365
 *****************************************************************************/
366
static int Demux( input_thread_t *p_input )
367
{
368
    demux_sys_t *p_sys = p_input->p_demux_data;
369
    unsigned int i_track;
370

371 372 373 374 375

    unsigned int i_track_selected;
    vlc_bool_t   b_play_audio;

    /* check for newly selected/unselected track */
376
    for( i_track = 0, i_track_selected = 0; i_track <  p_sys->i_tracks; i_track++ )
377
    {
378
#define track   p_sys->track[i_track]
379 380 381 382 383 384 385
        if( track.b_selected && track.i_sample >= track.i_sample_count )
        {
            msg_Warn( p_input, "track[0x%x] will be disabled", track.i_track_ID );
            MP4_TrackUnselect( p_input, &track );
        }
        else if( track.b_ok )
        {
386 387 388 389
            vlc_bool_t b;
            es_out_Control( p_input->p_es_out, ES_OUT_GET_ES_STATE, track.p_es, &b );

            if( track.b_selected && !b )
390 391 392
            {
                MP4_TrackUnselect( p_input, &track );
            }
393
            else if( !track.b_selected && b)
394
            {
395
                MP4_TrackSelect( p_input, &track, MP4_GetMoviePTS( p_sys ) );
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411
            }

            if( track.b_selected )
            {
                i_track_selected++;
            }
        }
#undef  track
    }

    if( i_track_selected <= 0 )
    {
        msg_Warn( p_input, "no track selected, exiting..." );
        return( 0 );
    }

412 413 414
    /* first wait for the good time to read a packet */
    input_ClockManageRef( p_input,
                          p_input->stream.p_selected_program,
415
                          p_sys->i_pcr );
416 417

    /* update pcr XXX in mpeg scale so in 90000 unit/s */
418
    p_sys->i_pcr = MP4_GetMoviePTS( p_sys ) * 9 / 100;
419 420

    /* we will read 100ms for each stream so ...*/
421
    p_sys->i_time += __MAX( p_sys->i_timescale / 10 , 1 );
422

Gildas Bazin's avatar
 
Gildas Bazin committed
423 424
    /* Check if we need to send the audio data to decoder */
    b_play_audio = !p_input->stream.control.b_mute;
425

426
    for( i_track = 0; i_track < p_sys->i_tracks; i_track++ )
427
    {
428
#define track p_sys->track[i_track]
429 430
        if( !track.b_ok ||
            !track.b_selected ||
431
            MP4_GetTrackPTS( &track ) >= MP4_GetMoviePTS( p_sys ) )
432
        {
433
            continue;
434
        }
435 436

        while( MP4_GetTrackPTS( &track ) < MP4_GetMoviePTS( p_sys ) )
437 438
        {

439 440
            if( ( !b_play_audio && track.fmt.i_cat == AUDIO_ES ) ||
                MP4_TrackSampleSize( &track ) <= 0 )
441
            {
442 443 444 445
                if( MP4_TrackNextSample( p_input, &track ) )
                {
                    break;
                }
446
            }
447 448
            else
            {
449
                block_t *p_block;
450

451
                /* go,go go ! */
452
                if( stream_Seek( p_input->s, MP4_GetTrackPos( &track ) ) )
453 454 455 456 457 458
                {
                    msg_Warn( p_input, "track[0x%x] will be disabled (eof?)", track.i_track_ID );
                    MP4_TrackUnselect( p_input, &track );
                    break;
                }

Laurent Aimar's avatar
Laurent Aimar committed
459
                /* now read pes */
460 461
                if( ( p_block = stream_Block( p_input->s,
                                              MP4_TrackSampleSize( &track ) ) ) == NULL )
462
                {
Laurent Aimar's avatar
Laurent Aimar committed
463 464
                    msg_Warn( p_input, "track[0x%x] will be disabled (eof?)", track.i_track_ID );
                    MP4_TrackUnselect( p_input, &track );
465 466
                    break;
                }
Gildas Bazin's avatar
 
Gildas Bazin committed
467

468
                if( track.b_drms && track.p_drms )
469 470 471 472 473 474
                {
                    drms_decrypt( track.p_drms,
                                  (uint32_t *)p_block->p_buffer,
                                  p_block->i_buffer );
                }

475
                if( track.fmt.i_cat == VIDEO_ES )
476
                {
477 478 479 480 481 482
                    /* FIXME sometime we can calculate PTS */
                    p_block->i_pts = 0;
                    p_block->i_dts =
                        input_ClockGetTS( p_input,
                                          p_input->stream.p_selected_program,
                                          MP4_GetTrackPTS( &track ) * 9/100 );
483 484 485
                }
                else
                {
486 487 488 489 490
                    p_block->i_pts =
                    p_block->i_dts =
                        input_ClockGetTS( p_input,
                                          p_input->stream.p_selected_program,
                                          MP4_GetTrackPTS( &track ) * 9/100 );
491 492
                }

493 494 495 496
                if( !track.b_drms || ( track.b_drms && track.p_drms ) )
                {
                    es_out_Send( p_input->p_es_out, track.p_es, p_block );
                }
497

498 499 500 501 502
                if( MP4_TrackNextSample( p_input, &track ) )
                {
                    break;
                }
            }
503
        }
504
#undef track
505 506
    }

507
    return( 1 );
508
}
509
/*****************************************************************************
510
 * Seek: Got to i_date
511
 ******************************************************************************/
512
static int   Seek     ( input_thread_t *p_input, mtime_t i_date )
513
{
514
    demux_sys_t *p_sys = p_input->p_demux_data;
515
    unsigned int i_track;
516
    /* First update update global time */
517 518
    p_sys->i_time = i_date * p_sys->i_timescale / 1000000;
    p_sys->i_pcr  = i_date* 9 / 100;
519

520
    /* Now for each stream try to go to this time */
521
    for( i_track = 0; i_track < p_sys->i_tracks; i_track++ )
522
    {
523
#define track p_sys->track[i_track]
524 525 526 527 528
        if( track.b_ok && track.b_selected )
        {
            MP4_TrackSeek( p_input, &track, i_date );
        }
#undef  track
529 530 531
    }
    return( 1 );
}
532 533

/*****************************************************************************
534
 * Control:
535
 *****************************************************************************/
536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
static int   Control  ( input_thread_t *p_input, int i_query, va_list args )
{
    demux_sys_t *p_sys = p_input->p_demux_data;

    double   f, *pf;
    int64_t i64, *pi64;

    switch( i_query )
    {
        case DEMUX_GET_POSITION:
            pf = (double*)va_arg( args, double * );
            if( p_sys->i_duration > 0 )
            {
                *pf = (double)p_sys->i_time / (double)p_sys->i_duration;
            }
            else
            {
                *pf = 0.0;
            }
            return VLC_SUCCESS;

        case DEMUX_SET_POSITION:
            f = (double)va_arg( args, double );
            i64 = (int64_t)( f *
                             (double)1000000 *
                             (double)p_sys->i_duration /
                             (double)p_sys->i_timescale );
            return Seek( p_input, i64 );

        case DEMUX_GET_TIME:
            pi64 = (int64_t*)va_arg( args, int64_t * );
            *pi64 = (mtime_t)1000000 *
                    (mtime_t)p_sys->i_time /
                    (mtime_t)p_sys->i_timescale;
            return VLC_SUCCESS;

        case DEMUX_SET_TIME:
            i64 = (int64_t)va_arg( args, int64_t );
            return Seek( p_input, i64 );

        case DEMUX_GET_LENGTH:
            pi64 = (int64_t*)va_arg( args, int64_t * );
            *pi64 = (mtime_t)1000000 *
                    (mtime_t)p_sys->i_duration /
                    (mtime_t)p_sys->i_timescale;
            return VLC_SUCCESS;
582 583 584
        case DEMUX_GET_FPS:
            msg_Warn( p_input, "DEMUX_GET_FPS unimplemented !!" );
            return VLC_EGENERIC;
585 586 587 588 589 590 591 592 593 594
        default:
            msg_Err( p_input, "control query unimplemented !!!" );
            return demux_vaControlDefault( p_input, i_query, args );
    }
}

/*****************************************************************************
 * Close: frees unused data
 *****************************************************************************/
static void Close ( vlc_object_t * p_this )
595 596
{
    unsigned int i_track;
597
    input_thread_t *  p_input = (input_thread_t *)p_this;
598
    demux_sys_t *p_sys = p_input->p_demux_data;
599

600
    msg_Dbg( p_input, "freeing all memory" );
Laurent Aimar's avatar
Laurent Aimar committed
601

602 603
    MP4_BoxFree( p_input, p_sys->p_root );
    for( i_track = 0; i_track < p_sys->i_tracks; i_track++ )
604
    {
605
        MP4_TrackDestroy( p_input, &p_sys->track[i_track] );
606
    }
607
    FREE( p_sys->track );
608

609
    free( p_sys );
610 611 612
}


613

614 615 616 617
/****************************************************************************
 * Local functions, specific to vlc
 ****************************************************************************/

618 619 620
/* now create basic chunk data, the rest will be filled by MP4_CreateSamplesIndex */
static int TrackCreateChunksIndex( input_thread_t *p_input,
                                   track_data_mp4_t *p_demux_track )
621
{
622 623
    MP4_Box_t *p_co64; /* give offset for each chunk, same for stco and co64 */
    MP4_Box_t *p_stsc;
624

625 626 627 628 629 630
    unsigned int i_chunk;
    unsigned int i_index, i_last;

    if( ( !(p_co64 = MP4_BoxGet( p_demux_track->p_stbl, "stco" ) )&&
          !(p_co64 = MP4_BoxGet( p_demux_track->p_stbl, "co64" ) ) )||
        ( !(p_stsc = MP4_BoxGet( p_demux_track->p_stbl, "stsc" ) ) ))
631
    {
632
        return( VLC_EGENERIC );
633
    }
634 635 636

    p_demux_track->i_chunk_count = p_co64->data.p_co64->i_entry_count;
    if( !p_demux_track->i_chunk_count )
637
    {
638 639
        msg_Warn( p_input, "no chunk defined" );
        return( VLC_EGENERIC );
640
    }
641 642 643 644 645
    p_demux_track->chunk = calloc( p_demux_track->i_chunk_count,
                                   sizeof( chunk_data_mp4_t ) );

    /* first we read chunk offset */
    for( i_chunk = 0; i_chunk < p_demux_track->i_chunk_count; i_chunk++ )
646
    {
647 648
        p_demux_track->chunk[i_chunk].i_offset =
                p_co64->data.p_co64->i_chunk_offset[i_chunk];
649 650
    }

651 652 653 654 655 656
    /* now we read index for SampleEntry( soun vide mp4a mp4v ...)
        to be used for the sample XXX begin to 1
        We construct it begining at the end */
    i_last = p_demux_track->i_chunk_count; /* last chunk proceded */
    i_index = p_stsc->data.p_stsc->i_entry_count;
    if( !i_index )
657
    {
658 659
        msg_Warn( p_input, "cannot read chunk table or table empty" );
        return( VLC_EGENERIC );
660
    }
661 662

    while( i_index )
663
    {
664 665 666 667 668 669 670 671 672 673
        i_index--;
        for( i_chunk = p_stsc->data.p_stsc->i_first_chunk[i_index] - 1;
                i_chunk < i_last; i_chunk++ )
        {
            p_demux_track->chunk[i_chunk].i_sample_description_index =
                    p_stsc->data.p_stsc->i_sample_description_index[i_index];
            p_demux_track->chunk[i_chunk].i_sample_count =
                    p_stsc->data.p_stsc->i_samples_per_chunk[i_index];
        }
        i_last = p_stsc->data.p_stsc->i_first_chunk[i_index] - 1;
674 675
    }

676 677
    p_demux_track->chunk[0].i_sample_first = 0;
    for( i_chunk = 1; i_chunk < p_demux_track->i_chunk_count; i_chunk++ )
678
    {
679 680 681
        p_demux_track->chunk[i_chunk].i_sample_first =
            p_demux_track->chunk[i_chunk-1].i_sample_first +
                p_demux_track->chunk[i_chunk-1].i_sample_count;
682
    }
683

684 685 686 687
    msg_Dbg( p_input,
             "track[Id 0x%x] read %d chunk",
             p_demux_track->i_track_ID,
            p_demux_track->i_chunk_count );
688

689
    return( VLC_SUCCESS );
690
}
691 692
static int TrackCreateSamplesIndex( input_thread_t *p_input,
                                    track_data_mp4_t *p_demux_track )
693 694
{
    MP4_Box_t *p_stts; /* makes mapping between sample and decoding time,
695
                          ctts make same mapping but for composition time,
696
                          not yet used and probably not usefull */
697 698
    MP4_Box_t *p_stsz; /* gives sample size of each samples, there is also stz2
                          that uses a compressed form FIXME make them in libmp4
699 700 701
                          as a unique type */
    /* TODO use also stss and stsh table for seeking */
    /* FIXME use edit table */
702 703
    int64_t i_sample;
    int64_t i_chunk;
704

705 706
    int64_t i_index;
    int64_t i_index_sample_used;
707

708 709
    int64_t i_last_dts;

710 711
    p_stts = MP4_BoxGet( p_demux_track->p_stbl, "stts" );
    p_stsz = MP4_BoxGet( p_demux_track->p_stbl, "stsz" ); /* FIXME and stz2 */
712 713 714 715

    if( ( !p_stts )||( !p_stsz ) )
    {
        msg_Warn( p_input, "cannot read sample table" );
716
        return( VLC_EGENERIC );
717
    }
718

719 720 721 722 723 724 725 726 727 728 729 730 731 732
    p_demux_track->i_sample_count = p_stsz->data.p_stsz->i_sample_count;


    /* for sample size, there are 2 case */
    if( p_stsz->data.p_stsz->i_sample_size )
    {
        /* 1: all sample have the same size, so no need to construct a table */
        p_demux_track->i_sample_size = p_stsz->data.p_stsz->i_sample_size;
        p_demux_track->p_sample_size = NULL;
    }
    else
    {
        /* 2: each sample can have a different size */
        p_demux_track->i_sample_size = 0;
733
        p_demux_track->p_sample_size =
734
            calloc( p_demux_track->i_sample_count, sizeof( uint32_t ) );
735

736 737
        for( i_sample = 0; i_sample < p_demux_track->i_sample_count; i_sample++ )
        {
738
            p_demux_track->p_sample_size[i_sample] =
739 740 741 742 743 744 745
                    p_stsz->data.p_stsz->i_entry_size[i_sample];
        }
    }
    /* we have extract all information from stsz,
        now use stts */

    /* if we don't want to waste too much memory, we can't expand
746
       the box !, so each chunk will contain an "extract" of this table
747
       for fast research */
748

749 750
    i_last_dts = 0;
    i_index = 0; i_index_sample_used =0;
751

752 753 754 755
    /* create and init last data for each chunk */
    for(i_chunk = 0 ; i_chunk < p_demux_track->i_chunk_count; i_chunk++ )
    {

756
        int64_t i_entry, i_sample_count, i;
757 758
        /* save last dts */
        p_demux_track->chunk[i_chunk].i_first_dts = i_last_dts;
759
    /* count how many entries needed for this chunk
760 761 762
       for p_sample_delta_dts and p_sample_count_dts */

        i_sample_count = p_demux_track->chunk[i_chunk].i_sample_count;
763 764

        i_entry = 0;
765 766 767 768 769
        while( i_sample_count > 0 )
        {
            i_sample_count -= p_stts->data.p_stts->i_sample_count[i_index+i_entry];
            if( i_entry == 0 )
            {
770
                i_sample_count += i_index_sample_used; /* don't count already used sample
771 772 773 774
                                                   int this entry */
            }
            i_entry++;
        }
775

776
        /* allocate them */
777
        p_demux_track->chunk[i_chunk].p_sample_count_dts =
778
            calloc( i_entry, sizeof( uint32_t ) );
779
        p_demux_track->chunk[i_chunk].p_sample_delta_dts =
780
            calloc( i_entry, sizeof( uint32_t ) );
781 782 783 784 785

        /* now copy */
        i_sample_count = p_demux_track->chunk[i_chunk].i_sample_count;
        for( i = 0; i < i_entry; i++ )
        {
786 787
            int64_t i_used;
            int64_t i_rest;
788

789 790 791 792 793
            i_rest = p_stts->data.p_stts->i_sample_count[i_index] - i_index_sample_used;

            i_used = __MIN( i_rest, i_sample_count );

            i_index_sample_used += i_used;
794
            i_sample_count -= i_used;
795 796 797 798 799

            p_demux_track->chunk[i_chunk].p_sample_count_dts[i] = i_used;

            p_demux_track->chunk[i_chunk].p_sample_delta_dts[i] =
                        p_stts->data.p_stts->i_sample_delta[i_index];
800 801

            i_last_dts += i_used *
802 803 804 805 806
                    p_demux_track->chunk[i_chunk].p_sample_delta_dts[i];

            if( i_index_sample_used >=
                             p_stts->data.p_stts->i_sample_count[i_index] )
            {
807

808 809 810 811
                i_index++;
                i_index_sample_used = 0;
            }
        }
812

813 814
    }

815 816
    msg_Dbg( p_input,
             "track[Id 0x%x] read %d samples length:"I64Fd"s",
817
             p_demux_track->i_track_ID,
818 819
             p_demux_track->i_sample_count,
             i_last_dts / p_demux_track->i_timescale );
820

821
    return( VLC_SUCCESS );
822 823
}

824 825 826 827 828 829 830
/*
 * TrackCreateES:
 *  Create ES and PES to init decoder if needed, for a track starting at i_chunk
 */
static int  TrackCreateES   ( input_thread_t   *p_input,
                              track_data_mp4_t *p_track,
                              unsigned int     i_chunk,
831
                              es_out_id_t      **pp_es )
832
{
833
    MP4_Box_t   *p_sample;
834
    MP4_Box_t   *p_esds;
835

836 837
    *pp_es = NULL;

838
    if( !p_track->chunk[i_chunk].i_sample_description_index )
839
    {
840
        msg_Warn( p_input,
841
                  "invalid SampleEntry index (track[Id 0x%x])",
842
                  p_track->i_track_ID );
843
        return VLC_EGENERIC;
844
    }
845

846
    p_sample = MP4_BoxGet(  p_track->p_stsd, "[%d]",
847
                p_track->chunk[i_chunk].i_sample_description_index - 1 );
848

849
    if( !p_sample || !p_sample->data.p_data )
850
    {
851
        msg_Warn( p_input,
852
                  "cannot find SampleEntry (track[Id 0x%x])",
853 854
                  p_track->i_track_ID );
        return( VLC_EGENERIC );
855 856
    }

857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875
    p_track->p_sample = p_sample;

    if( p_track->i_sample_size == 1 )
    {
        MP4_Box_data_sample_soun_t *p_soun;

        p_soun = p_sample->data.p_sample_soun;

        if( p_soun->i_qt_version == 0 )
        {
            switch( p_sample->i_type )
            {
                case VLC_FOURCC( 'i', 'm', 'a', '4' ):
                    p_soun->i_qt_version = 1;
                    p_soun->i_sample_per_packet = 64;
                    p_soun->i_bytes_per_packet  = 34;
                    p_soun->i_bytes_per_frame   = 34 * p_soun->i_channelcount;
                    p_soun->i_bytes_per_sample  = 2;
                    break;
876 877 878 879 880 881 882 883 884 885 886 887 888 889
                case VLC_FOURCC( 'M', 'A', 'C', '3' ):
                    p_soun->i_qt_version = 1;
                    p_soun->i_sample_per_packet = 6;
                    p_soun->i_bytes_per_packet  = 2;
                    p_soun->i_bytes_per_frame   = 2 * p_soun->i_channelcount;
                    p_soun->i_bytes_per_sample  = 2;
                    break;
                case VLC_FOURCC( 'M', 'A', 'C', '6' ):
                    p_soun->i_qt_version = 1;
                    p_soun->i_sample_per_packet = 12;
                    p_soun->i_bytes_per_packet  = 2;
                    p_soun->i_bytes_per_frame   = 2 * p_soun->i_channelcount;
                    p_soun->i_bytes_per_sample  = 2;
                    break;
890 891 892
                default:
                    break;
            }
893 894 895 896 897

        }
        else if( p_soun->i_qt_version == 1 && p_soun->i_sample_per_packet <= 0 )
        {
            p_soun->i_qt_version = 0;
898 899 900
        }
    }

901

902 903 904 905 906
    /* It's a little ugly but .. there are special cases */
    switch( p_sample->i_type )
    {
        case( VLC_FOURCC( '.', 'm', 'p', '3' ) ):
        case( VLC_FOURCC( 'm', 's', 0x00, 0x55 ) ):
907
            p_track->fmt.i_codec = VLC_FOURCC( 'm', 'p', 'g', 'a' );
908 909
            break;
        case( VLC_FOURCC( 'r', 'a', 'w', ' ' ) ):
910
            p_track->fmt.i_codec = VLC_FOURCC( 'a', 'r', 'a', 'w' );
911
            break;
912
        case( VLC_FOURCC( 's', '2', '6', '3' ) ):
913
            p_track->fmt.i_codec = VLC_FOURCC( 'h', '2', '6', '3' );
914
            break;
915
        default:
916
            p_track->fmt.i_codec = p_sample->i_type;
917 918
            break;
    }