mp4.c 59.3 KB
Newer Older
1 2 3
/*****************************************************************************
 * mp4.c : MP4 file input module for vlc
 *****************************************************************************
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
4
 * Copyright (C) 2001-2004 VideoLAN
5
 * $Id: mp4.c,v 1.59 2004/02/07 13:26:24 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();
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
44
    set_description( _("MP4 stream demuxer") );
45
    set_capability( "demux", 242 );
46
    set_callbacks( Open, Close );
47 48
vlc_module_end();

49 50 51 52
/* TODO:
 *  - DEMUX_GET_META and meta parsing
 */

53 54 55 56 57 58 59 60 61 62 63
/*****************************************************************************
 * 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 );

64
/*****************************************************************************
65
 * Declaration of local function
66
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
67 68
static void MP4_TrackCreate ( input_thread_t *, mp4_track_t *, MP4_Box_t  *);
static void MP4_TrackDestroy( input_thread_t *, mp4_track_t * );
69

Laurent Aimar's avatar
Laurent Aimar committed
70 71
static int  MP4_TrackSelect ( input_thread_t *, mp4_track_t *, mtime_t );
static void MP4_TrackUnselect(input_thread_t *, mp4_track_t * );
72

Laurent Aimar's avatar
Laurent Aimar committed
73
static int  MP4_TrackSeek   ( input_thread_t *, mp4_track_t *, mtime_t );
74

Laurent Aimar's avatar
Laurent Aimar committed
75 76 77
static uint64_t MP4_TrackGetPos    ( mp4_track_t * );
static int      MP4_TrackSampleSize( mp4_track_t * );
static int      MP4_TrackNextSample( input_thread_t *, mp4_track_t * );
78
static void     MP4_TrackSetELST( input_thread_t *, mp4_track_t *, int64_t );
Laurent Aimar's avatar
Laurent Aimar committed
79 80

/* Return time in s of a track */
81
static inline int64_t MP4_TrackGetPTS( input_thread_t *p_input, mp4_track_t *p_track )
Laurent Aimar's avatar
Laurent Aimar committed
82 83 84
{
    unsigned int i_sample;
    unsigned int i_index;
85
    int64_t i_dts;
Laurent Aimar's avatar
Laurent Aimar committed
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107

    i_sample = p_track->i_sample - p_track->chunk[p_track->i_chunk].i_sample_first;
    i_dts = p_track->chunk[p_track->i_chunk].i_first_dts;
    i_index = 0;
    while( i_sample > 0 )
    {
        if( i_sample > p_track->chunk[p_track->i_chunk].p_sample_count_dts[i_index] )
        {
            i_dts += p_track->chunk[p_track->i_chunk].p_sample_count_dts[i_index] *
                        p_track->chunk[p_track->i_chunk].p_sample_delta_dts[i_index];
            i_sample -= p_track->chunk[p_track->i_chunk].p_sample_count_dts[i_index];
            i_index++;
        }
        else
        {
            i_dts += i_sample *
                        p_track->chunk[p_track->i_chunk].p_sample_delta_dts[i_index];
            i_sample = 0;
            break;
        }
    }

108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
    /* now handle elst */
    if( p_track->p_elst )
    {
        demux_sys_t         *p_sys = p_input->p_demux_data;
        MP4_Box_data_elst_t *elst = p_track->p_elst->data.p_elst;

        /* 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 */
        i_dts += p_track->i_elst_time * p_track->i_timescale / p_sys->i_timescale;

        if( i_dts < 0 ) i_dts = 0;
    }

Laurent Aimar's avatar
Laurent Aimar committed
128 129 130 131 132 133
    return (int64_t)1000000 * i_dts / p_track->i_timescale;
}
static inline int64_t MP4_GetMoviePTS(demux_sys_t *p_sys )
{
    return (int64_t)1000000 * p_sys->i_time / p_sys->i_timescale;
}
134

Laurent Aimar's avatar
Laurent Aimar committed
135
#define FREE( p ) if( p ) { free( p ); (p) = NULL;}
136

137
/*****************************************************************************
138
 * Open: check file and initializes MP4 structures
139
 *****************************************************************************/
140
static int Open( vlc_object_t * p_this )
141
{
142
    input_thread_t  *p_input = (input_thread_t *)p_this;
143
    demux_sys_t     *p_sys;
144

145
    uint8_t         *p_peek;
146

147
    MP4_Box_t       *p_ftyp;
148
    MP4_Box_t       *p_rmra;
149 150
    MP4_Box_t       *p_mvhd;
    MP4_Box_t       *p_trak;
151

152
    unsigned int    i;
153
    vlc_bool_t      b_seekable;
154

155
    /* a little test to see if it could be a mp4 */
156
    if( stream_Peek( p_input->s, &p_peek, 8 ) < 8 )
157 158
    {
        msg_Warn( p_input, "MP4 plugin discarded (cannot peek)" );
159
        return VLC_EGENERIC;
160
    }
161
    switch( VLC_FOURCC( p_peek[4], p_peek[5], p_peek[6], p_peek[7] ) )
162
    {
163 164 165 166 167 168 169 170 171
        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:
172
        case VLC_FOURCC( 'p', 'n', 'o', 't' ):
173 174 175
            break;
         default:
            msg_Warn( p_input, "MP4 plugin discarded (not a valid file)" );
176
            return VLC_EGENERIC;
177
    }
178

179
    /* I need to seek */
180 181
    stream_Control( p_input->s, STREAM_CAN_SEEK, &b_seekable );
    if( !b_seekable )
182
    {
183 184
        msg_Warn( p_input, "MP4 plugin discarded (unseekable)" );
        return VLC_EGENERIC;
185
    }
186

187 188 189 190 191
    /*Set exported functions */
    p_input->pf_demux = Demux;
    p_input->pf_demux_control = Control;

    /* create our structure that will contains all data */
192 193
    p_input->p_demux_data = p_sys = malloc( sizeof( demux_sys_t ) );
    memset( p_sys, 0, sizeof( demux_sys_t ) );
194 195

    /* Now load all boxes ( except raw data ) */
196
    if( ( p_sys->p_root = MP4_BoxGetRoot( p_input ) ) == NULL )
197 198
    {
        msg_Warn( p_input, "MP4 plugin discarded (not a valid file)" );
Laurent Aimar's avatar
Laurent Aimar committed
199
        goto error;
200 201
    }

202
    MP4_BoxDumpStructure( p_input, p_sys->p_root );
203

204
    if( ( p_ftyp = MP4_BoxGet( p_sys->p_root, "/ftyp" ) ) )
205 206 207 208
    {
        switch( p_ftyp->data.p_ftyp->i_major_brand )
        {
            case( FOURCC_isom ):
209
                msg_Dbg( p_input,
210 211
                         "ISO Media file (isom) version %d.",
                         p_ftyp->data.p_ftyp->i_minor_version );
212 213
                break;
            default:
214
                msg_Dbg( p_input,
215 216
                         "unrecognized major file specification (%4.4s).",
                          (char*)&p_ftyp->data.p_ftyp->i_major_brand );
217 218 219 220 221
                break;
        }
    }
    else
    {
222
        msg_Dbg( p_input, "file type box missing (assuming ISO Media file)" );
223 224 225
    }

    /* the file need to have one moov box */
226
    if( MP4_BoxCount( p_sys->p_root, "/moov" ) <= 0 )
227
    {
228
        MP4_Box_t *p_foov = MP4_BoxGet( p_sys->p_root, "/foov" );
229 230 231 232

        if( !p_foov )
        {
            msg_Err( p_input, "MP4 plugin discarded (no moov box)" );
233
            goto error;
234 235 236
        }
        /* we have a free box as a moov, rename it */
        p_foov->i_type = FOURCC_moov;
237 238
    }

239
    if( ( p_rmra = MP4_BoxGet( p_sys->p_root,  "/moov/rmra" ) ) )
240 241 242 243 244 245 246 247 248 249 250 251 252
    {
        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 )
        {
253
            //p_playlist->pp_items[p_playlist->i_index]->b_autodeletion = VLC_TRUE;
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
            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;
                    }
276 277
                    if( !strncmp( psz_ref, "http://", 7 ) ||
                        !strncmp( psz_ref, "rtsp://", 7 ) )
278 279
                    {
                        msg_Dbg( p_input, "adding ref = `%s'", psz_ref );
Clément Stenac's avatar
Clément Stenac committed
280
                        playlist_Add( p_playlist, psz_ref, psz_ref,
Gildas Bazin's avatar
 
Gildas Bazin committed
281
                                      PLAYLIST_APPEND, PLAYLIST_END );
282 283 284 285
                    }
                    else
                    {
                        /* msg dbg relative ? */
286 287
                        char *psz_absolute = alloca( strlen( p_input->psz_source ) + strlen( psz_ref ) + 1);
                        char *end = strrchr( p_input->psz_source, '/' );
288 289 290

                        if( end )
                        {
291
                            int i_len = end + 1 - p_input->psz_source;
292

293
                            strncpy( psz_absolute, p_input->psz_source, i_len);
294
                            psz_absolute[i_len] = '\0';
295 296 297 298 299 300 301
                        }
                        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
302
                        playlist_Add( p_playlist, psz_absolute, psz_absolute,
Gildas Bazin's avatar
 
Gildas Bazin committed
303
                                      PLAYLIST_APPEND, PLAYLIST_END );
304 305 306 307
                    }
                }
                else
                {
Laurent Aimar's avatar
Laurent Aimar committed
308 309
                    msg_Err( p_input, "unknown ref type=%4.4s FIXME (send a bug report)",
                             (char*)&p_rdrf->data.p_rdrf->i_ref_type );
310 311 312 313 314 315 316 317 318 319
                }
            }
            vlc_object_release( p_playlist );
        }
        else
        {
            msg_Err( p_input, "can't find playlist" );
        }
    }

320
    if( !(p_mvhd = MP4_BoxGet( p_sys->p_root, "/moov/mvhd" ) ) )
321
    {
322 323 324
        if( !p_rmra )
        {
            msg_Err( p_input, "cannot find /moov/mvhd" );
325
            goto error;
326 327 328 329
        }
        else
        {
            msg_Warn( p_input, "cannot find /moov/mvhd (pure ref file)" );
330
            p_input->pf_demux = DemuxRef;
331
            return VLC_SUCCESS;
332
        }
333 334 335
    }
    else
    {
336 337
        p_sys->i_timescale = p_mvhd->data.p_mvhd->i_timescale;
        p_sys->i_duration = p_mvhd->data.p_mvhd->i_duration;
338
    }
339

340
    if( !( p_sys->i_tracks = MP4_BoxCount( p_sys->p_root, "/moov/trak" ) ) )
341
    {
342
        msg_Err( p_input, "cannot find any /moov/trak" );
343
        goto error;
344
    }
345
    msg_Dbg( p_input, "find %d track%c",
346 347
                        p_sys->i_tracks,
                        p_sys->i_tracks ? 's':' ' );
348

349 350 351 352 353 354
    /*  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" );
355
        goto error;
356
    }
357
    if( p_sys->i_duration/p_sys->i_timescale > 0 )
358 359 360
    {
        p_input->stream.i_mux_rate =
            p_input->stream.p_selected_area->i_size / 50 /
361
            ( p_sys->i_duration / p_sys->i_timescale );
362 363 364 365 366 367 368 369
    }
    else
    {
        p_input->stream.i_mux_rate = 0;
    }
    vlc_mutex_unlock( &p_input->stream.stream_lock );


370
    /* allocate memory */
Laurent Aimar's avatar
Laurent Aimar committed
371 372
    p_sys->track = calloc( p_sys->i_tracks, sizeof( mp4_track_t ) );
    memset( p_sys->track, 0, p_sys->i_tracks * sizeof( mp4_track_t ) );
373 374

    /* now process each track and extract all usefull informations */
375
    for( i = 0; i < p_sys->i_tracks; i++ )
376
    {
377 378
        p_trak = MP4_BoxGet( p_sys->p_root, "/moov/trak[%d]", i );
        MP4_TrackCreate( p_input, &p_sys->track[i], p_trak );
379

380
        if( p_sys->track[i].b_ok )
381 382
        {
            char *psz_cat;
383
            switch( p_sys->track[i].fmt.i_cat )
384 385 386 387 388 389 390 391
            {
                case( VIDEO_ES ):
                    psz_cat = "video";
                    break;
                case( AUDIO_ES ):
                    psz_cat = "audio";
                    break;
                default:
392
                    psz_cat = "unknown";
393 394
                    break;
            }
395

396 397
            msg_Dbg( p_input, "adding track[Id 0x%x] %s (%s) language %s",
                            p_sys->track[i].i_track_ID,
398
                            psz_cat,
399 400
                            p_sys->track[i].b_enable ? "enable":"disable",
                            p_sys->track[i].fmt.psz_language ? p_sys->track[i].fmt.psz_language : "undef" );
401 402 403
        }
        else
        {
404
            msg_Dbg( p_input, "ignoring track[Id 0x%x]", p_sys->track[i].i_track_ID );
405 406 407
        }

    }
408 409 410
    return VLC_SUCCESS;

error:
411
    if( p_sys->p_root )
Laurent Aimar's avatar
Laurent Aimar committed
412
    {
413
        MP4_BoxFree( p_input, p_sys->p_root );
Laurent Aimar's avatar
Laurent Aimar committed
414
    }
415
    free( p_sys );
416
    return VLC_EGENERIC;
417 418 419
}

/*****************************************************************************
420
 * Demux: read packet and send them to decoders
421 422
 *****************************************************************************
 * TODO check for newly selected track (ie audio upt to now )
423
 *****************************************************************************/
424
static int Demux( input_thread_t *p_input )
425
{
426
    demux_sys_t *p_sys = p_input->p_demux_data;
427
    unsigned int i_track;
428

429 430 431 432 433

    unsigned int i_track_selected;
    vlc_bool_t   b_play_audio;

    /* check for newly selected/unselected track */
434
    for( i_track = 0, i_track_selected = 0; i_track <  p_sys->i_tracks; i_track++ )
435
    {
Laurent Aimar's avatar
Laurent Aimar committed
436 437 438
        mp4_track_t *tk = &p_sys->track[i_track];

        if( tk->b_selected && tk->i_sample >= tk->i_sample_count )
439
        {
Laurent Aimar's avatar
Laurent Aimar committed
440 441
            msg_Warn( p_input, "track[0x%x] will be disabled", tk->i_track_ID );
            MP4_TrackUnselect( p_input, tk);
442
        }
Laurent Aimar's avatar
Laurent Aimar committed
443
        else if( tk->b_ok )
444
        {
445
            vlc_bool_t b;
Laurent Aimar's avatar
Laurent Aimar committed
446
            es_out_Control( p_input->p_es_out, ES_OUT_GET_ES_STATE, tk->p_es, &b );
447

Laurent Aimar's avatar
Laurent Aimar committed
448
            if( tk->b_selected && !b )
449
            {
Laurent Aimar's avatar
Laurent Aimar committed
450
                MP4_TrackUnselect( p_input, tk );
451
            }
Laurent Aimar's avatar
Laurent Aimar committed
452
            else if( !tk->b_selected && b)
453
            {
Laurent Aimar's avatar
Laurent Aimar committed
454
                MP4_TrackSelect( p_input, tk, MP4_GetMoviePTS( p_sys ) );
455 456
            }

Laurent Aimar's avatar
Laurent Aimar committed
457
            if( tk->b_selected )
458 459 460 461 462 463 464 465 466
            {
                i_track_selected++;
            }
        }
    }

    if( i_track_selected <= 0 )
    {
        msg_Warn( p_input, "no track selected, exiting..." );
Laurent Aimar's avatar
Laurent Aimar committed
467
        return 0;
468 469
    }

470 471 472
    /* first wait for the good time to read a packet */
    input_ClockManageRef( p_input,
                          p_input->stream.p_selected_program,
473
                          p_sys->i_pcr );
474 475

    /* update pcr XXX in mpeg scale so in 90000 unit/s */
476
    p_sys->i_pcr = MP4_GetMoviePTS( p_sys ) * 9 / 100;
477 478

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

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

484
    for( i_track = 0; i_track < p_sys->i_tracks; i_track++ )
485
    {
Laurent Aimar's avatar
Laurent Aimar committed
486 487 488
        mp4_track_t *tk = &p_sys->track[i_track];

        if( !tk->b_ok || !tk->b_selected )
489
        {
490
            continue;
491
        }
492

493
        while( MP4_TrackGetPTS( p_input, tk ) < MP4_GetMoviePTS( p_sys ) )
494
        {
495 496 497 498 499 500
#if 0
            msg_Dbg( p_input, "tk=%lld mv=%lld",
                     MP4_TrackGetPTS( p_input, tk ),
                     MP4_GetMoviePTS( p_sys ) );
#endif

Laurent Aimar's avatar
Laurent Aimar committed
501 502
            if( MP4_TrackSampleSize( tk ) > 0 &&
                ( b_play_audio || tk->fmt.i_cat != AUDIO_ES ) )
503
            {
504
                block_t *p_block;
505

506
                /* go,go go ! */
Laurent Aimar's avatar
Laurent Aimar committed
507
                if( stream_Seek( p_input->s, MP4_TrackGetPos( tk ) ) )
508
                {
Laurent Aimar's avatar
Laurent Aimar committed
509 510
                    msg_Warn( p_input, "track[0x%x] will be disabled (eof?)", tk->i_track_ID );
                    MP4_TrackUnselect( p_input, tk );
511 512 513
                    break;
                }

Laurent Aimar's avatar
Laurent Aimar committed
514
                /* now read pes */
515
                if( ( p_block = stream_Block( p_input->s,
Laurent Aimar's avatar
Laurent Aimar committed
516
                                              MP4_TrackSampleSize( tk ) ) ) == NULL )
517
                {
Laurent Aimar's avatar
Laurent Aimar committed
518 519
                    msg_Warn( p_input, "track[0x%x] will be disabled (eof?)", tk->i_track_ID );
                    MP4_TrackUnselect( p_input, tk );
520 521
                    break;
                }
Gildas Bazin's avatar
 
Gildas Bazin committed
522

Laurent Aimar's avatar
Laurent Aimar committed
523
                if( tk->b_drms && tk->p_drms )
524
                {
Laurent Aimar's avatar
Laurent Aimar committed
525 526
                    drms_decrypt( tk->p_drms,
                                  (uint32_t*)p_block->p_buffer,
527 528
                                  p_block->i_buffer );
                }
Laurent Aimar's avatar
Laurent Aimar committed
529 530 531
                p_block->i_dts =
                    input_ClockGetTS( p_input,
                                      p_input->stream.p_selected_program,
532
                                      MP4_TrackGetPTS( p_input, tk ) * 9/100 );
533

Laurent Aimar's avatar
Laurent Aimar committed
534
                p_block->i_pts = tk->fmt.i_cat == VIDEO_ES ? 0 : p_block->i_dts;
535

Laurent Aimar's avatar
Laurent Aimar committed
536
                if( !tk->b_drms || ( tk->b_drms && tk->p_drms ) )
537
                {
Laurent Aimar's avatar
Laurent Aimar committed
538
                    es_out_Send( p_input->p_es_out, tk->p_es, p_block );
539
                }
Laurent Aimar's avatar
Laurent Aimar committed
540
            }
541

Laurent Aimar's avatar
Laurent Aimar committed
542 543 544 545
            /* Next sample */
            if( MP4_TrackNextSample( p_input, tk ) )
            {
                break;
546
            }
547 548 549
        }
    }

Laurent Aimar's avatar
Laurent Aimar committed
550
    return 1;
551
}
552
/*****************************************************************************
553
 * Seek: Got to i_date
554
 ******************************************************************************/
555
static int   Seek     ( input_thread_t *p_input, mtime_t i_date )
556
{
557
    demux_sys_t *p_sys = p_input->p_demux_data;
558
    unsigned int i_track;
Laurent Aimar's avatar
Laurent Aimar committed
559

560
    /* First update update global time */
561 562
    p_sys->i_time = i_date * p_sys->i_timescale / 1000000;
    p_sys->i_pcr  = i_date* 9 / 100;
563

564
    /* Now for each stream try to go to this time */
565
    for( i_track = 0; i_track < p_sys->i_tracks; i_track++ )
566
    {
Laurent Aimar's avatar
Laurent Aimar committed
567 568 569
        mp4_track_t *tk = &p_sys->track[i_track];

        if( tk->b_ok && tk->b_selected )
570
        {
Laurent Aimar's avatar
Laurent Aimar committed
571
            MP4_TrackSeek( p_input, tk, i_date );
572
        }
573 574 575
    }
    return( 1 );
}
576 577

/*****************************************************************************
578
 * Control:
579
 *****************************************************************************/
Gildas Bazin's avatar
 
Gildas Bazin committed
580
static int Control( input_thread_t *p_input, int i_query, va_list args )
581 582 583
{
    demux_sys_t *p_sys = p_input->p_demux_data;

Gildas Bazin's avatar
 
Gildas Bazin committed
584
    double f, *pf;
585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602
    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 );
Gildas Bazin's avatar
 
Gildas Bazin committed
603 604 605 606 607 608 609 610
            if( p_sys->i_timescale > 0 )
            {
                i64 = (int64_t)( f * (double)1000000 *
                                 (double)p_sys->i_duration /
                                 (double)p_sys->i_timescale );
                return Seek( p_input, i64 );
            }
            else return VLC_SUCCESS;
611 612 613

        case DEMUX_GET_TIME:
            pi64 = (int64_t*)va_arg( args, int64_t * );
Gildas Bazin's avatar
 
Gildas Bazin committed
614 615 616 617 618 619 620
            if( p_sys->i_timescale > 0 )
            {
                *pi64 = (mtime_t)1000000 *
                        (mtime_t)p_sys->i_time /
                        (mtime_t)p_sys->i_timescale;
            }
            else *pi64 = 0;
621 622 623 624 625 626 627 628
            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 * );
Gildas Bazin's avatar
 
Gildas Bazin committed
629 630 631 632 633 634 635
            if( p_sys->i_timescale > 0 )
            {
                *pi64 = (mtime_t)1000000 *
                        (mtime_t)p_sys->i_duration /
                        (mtime_t)p_sys->i_timescale;
            }
            else *pi64 = 0;
636
            return VLC_SUCCESS;
Gildas Bazin's avatar
 
Gildas Bazin committed
637

638 639 640
        case DEMUX_GET_FPS:
            msg_Warn( p_input, "DEMUX_GET_FPS unimplemented !!" );
            return VLC_EGENERIC;
Gildas Bazin's avatar
 
Gildas Bazin committed
641

642 643 644
        case DEMUX_GET_META:
            return VLC_EGENERIC;

645 646 647 648 649 650 651 652 653 654
        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 )
655 656
{
    unsigned int i_track;
657
    input_thread_t *  p_input = (input_thread_t *)p_this;
658
    demux_sys_t *p_sys = p_input->p_demux_data;
659

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

662 663
    MP4_BoxFree( p_input, p_sys->p_root );
    for( i_track = 0; i_track < p_sys->i_tracks; i_track++ )
664
    {
665
        MP4_TrackDestroy( p_input, &p_sys->track[i_track] );
666
    }
667
    FREE( p_sys->track );
668

669
    free( p_sys );
670 671 672
}


673

674 675 676 677
/****************************************************************************
 * Local functions, specific to vlc
 ****************************************************************************/

678 679
/* now create basic chunk data, the rest will be filled by MP4_CreateSamplesIndex */
static int TrackCreateChunksIndex( input_thread_t *p_input,
Laurent Aimar's avatar
Laurent Aimar committed
680
                                   mp4_track_t *p_demux_track )
681
{
682 683
    MP4_Box_t *p_co64; /* give offset for each chunk, same for stco and co64 */
    MP4_Box_t *p_stsc;
684

685 686 687 688 689 690
    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" ) ) ))
691
    {
692
        return( VLC_EGENERIC );
693
    }
694 695 696

    p_demux_track->i_chunk_count = p_co64->data.p_co64->i_entry_count;
    if( !p_demux_track->i_chunk_count )
697
    {
698 699
        msg_Warn( p_input, "no chunk defined" );
        return( VLC_EGENERIC );
700
    }
701
    p_demux_track->chunk = calloc( p_demux_track->i_chunk_count,
Laurent Aimar's avatar
Laurent Aimar committed
702
                                   sizeof( mp4_chunk_t ) );
703 704 705

    /* first we read chunk offset */
    for( i_chunk = 0; i_chunk < p_demux_track->i_chunk_count; i_chunk++ )
706
    {
707 708
        p_demux_track->chunk[i_chunk].i_offset =
                p_co64->data.p_co64->i_chunk_offset[i_chunk];
709 710
    }

711 712 713 714 715 716
    /* 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 )
717
    {
718 719
        msg_Warn( p_input, "cannot read chunk table or table empty" );
        return( VLC_EGENERIC );
720
    }
721 722

    while( i_index )
723
    {
724 725 726 727 728 729 730 731 732 733
        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;
734 735
    }

736 737
    p_demux_track->chunk[0].i_sample_first = 0;
    for( i_chunk = 1; i_chunk < p_demux_track->i_chunk_count; i_chunk++ )
738
    {
739 740 741
        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;
742
    }
743

744 745 746
    msg_Dbg( p_input,
             "track[Id 0x%x] read %d chunk",
             p_demux_track->i_track_ID,
747
             p_demux_track->i_chunk_count );
748

749
    return( VLC_SUCCESS );
750
}
751
static int TrackCreateSamplesIndex( input_thread_t *p_input,
Laurent Aimar's avatar
Laurent Aimar committed
752
                                    mp4_track_t *p_demux_track )
753 754
{
    MP4_Box_t *p_stts; /* makes mapping between sample and decoding time,
755
                          ctts make same mapping but for composition time,
756
                          not yet used and probably not usefull */
757 758
    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
759 760 761
                          as a unique type */
    /* TODO use also stss and stsh table for seeking */
    /* FIXME use edit table */
762 763
    int64_t i_sample;
    int64_t i_chunk;
764

765 766
    int64_t i_index;
    int64_t i_index_sample_used;
767

768 769
    int64_t i_last_dts;

770 771
    p_stts = MP4_BoxGet( p_demux_track->p_stbl, "stts" );
    p_stsz = MP4_BoxGet( p_demux_track->p_stbl, "stsz" ); /* FIXME and stz2 */
772 773 774 775

    if( ( !p_stts )||( !p_stsz ) )
    {
        msg_Warn( p_input, "cannot read sample table" );
776
        return( VLC_EGENERIC );
777
    }
778

779 780 781 782 783 784 785 786 787 788 789 790 791 792
    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;
793
        p_demux_track->p_sample_size =
794
            calloc( p_demux_track->i_sample_count, sizeof( uint32_t ) );
795

796 797
        for( i_sample = 0; i_sample < p_demux_track->i_sample_count; i_sample++ )
        {
798
            p_demux_track->p_sample_size[i_sample] =
799 800 801 802 803 804 805
                    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
806
       the box !, so each chunk will contain an "extract" of this table
807
       for fast research */
808

809 810
    i_last_dts = 0;
    i_index = 0; i_index_sample_used =0;
811

812 813 814 815
    /* create and init last data for each chunk */
    for(i_chunk = 0 ; i_chunk < p_demux_track->i_chunk_count; i_chunk++ )
    {

816
        int64_t i_entry, i_sample_count, i;
817 818
        /* save last dts */
        p_demux_track->chunk[i_chunk].i_first_dts = i_last_dts;
819
    /* count how many entries needed for this chunk
820 821 822
       for p_sample_delta_dts and p_sample_count_dts */

        i_sample_count = p_demux_track->chunk[i_chunk].i_sample_count;
823 824

        i_entry = 0;
825 826 827 828 829
        while( i_sample_count > 0 )
        {
            i_sample_count -= p_stts->data.p_stts->i_sample_count[i_index+i_entry];
            if( i_entry == 0 )
            {
830
                i_sample_count += i_index_sample_used; /* don't count already used sample
831 832 833 834
                                                   int this entry */
            }
            i_entry++;
        }
835

836
        /* allocate them */
837
        p_demux_track->chunk[i_chunk].p_sample_count_dts =
838
            calloc( i_entry, sizeof( uint32_t ) );
839
        p_demux_track->chunk[i_chunk].p_sample_delta_dts =
840
            calloc( i_entry, sizeof( uint32_t ) );
841 842 843 844 845

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

849 850 851 852 853
            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;
854
            i_sample_count -= i_used;
855 856 857 858 859

            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];