mp4.c 59.2 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.58 2004/01/30 14:27:48 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 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
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
63 64
static void MP4_TrackCreate ( input_thread_t *, mp4_track_t *, MP4_Box_t  *);
static void MP4_TrackDestroy( input_thread_t *, mp4_track_t * );
65

Laurent Aimar's avatar
Laurent Aimar committed
66 67
static int  MP4_TrackSelect ( input_thread_t *, mp4_track_t *, mtime_t );
static void MP4_TrackUnselect(input_thread_t *, mp4_track_t * );
68

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

Laurent Aimar's avatar
Laurent Aimar committed
71 72 73
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 * );
74
static void     MP4_TrackSetELST( input_thread_t *, mp4_track_t *, int64_t );
Laurent Aimar's avatar
Laurent Aimar committed
75 76

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

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

104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
    /* 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
124 125 126 127 128 129
    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;
}
130

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

133
/*****************************************************************************
134
 * Open: check file and initializes MP4 structures
135
 *****************************************************************************/
136
static int Open( vlc_object_t * p_this )
137
{
138
    input_thread_t  *p_input = (input_thread_t *)p_this;
139
    demux_sys_t     *p_sys;
140

141
    uint8_t         *p_peek;
142

143
    MP4_Box_t       *p_ftyp;
144
    MP4_Box_t       *p_rmra;
145 146
    MP4_Box_t       *p_mvhd;
    MP4_Box_t       *p_trak;
147

148
    unsigned int    i;
149
    vlc_bool_t      b_seekable;
150

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

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

183 184 185 186 187
    /*Set exported functions */
    p_input->pf_demux = Demux;
    p_input->pf_demux_control = Control;

    /* create our structure that will contains all data */
188 189
    p_input->p_demux_data = p_sys = malloc( sizeof( demux_sys_t ) );
    memset( p_sys, 0, sizeof( demux_sys_t ) );
190 191

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

198
    MP4_BoxDumpStructure( p_input, p_sys->p_root );
199

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

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

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

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

                        if( end )
                        {
287
                            int i_len = end + 1 - p_input->psz_source;
288

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

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

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

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


366
    /* allocate memory */
Laurent Aimar's avatar
Laurent Aimar committed
367 368
    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 ) );
369 370

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

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

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

    }
404 405 406
    return VLC_SUCCESS;

error:
407
    if( p_sys->p_root )
Laurent Aimar's avatar
Laurent Aimar committed
408
    {
409
        MP4_BoxFree( p_input, p_sys->p_root );
Laurent Aimar's avatar
Laurent Aimar committed
410
    }
411
    free( p_sys );
412
    return VLC_EGENERIC;
413 414 415
}

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

425 426 427 428 429

    unsigned int i_track_selected;
    vlc_bool_t   b_play_audio;

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

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

Laurent Aimar's avatar
Laurent Aimar committed
444
            if( tk->b_selected && !b )
445
            {
Laurent Aimar's avatar
Laurent Aimar committed
446
                MP4_TrackUnselect( p_input, tk );
447
            }
Laurent Aimar's avatar
Laurent Aimar committed
448
            else if( !tk->b_selected && b)
449
            {
Laurent Aimar's avatar
Laurent Aimar committed
450
                MP4_TrackSelect( p_input, tk, MP4_GetMoviePTS( p_sys ) );
451 452
            }

Laurent Aimar's avatar
Laurent Aimar committed
453
            if( tk->b_selected )
454 455 456 457 458 459 460 461 462
            {
                i_track_selected++;
            }
        }
    }

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

466 467 468
    /* first wait for the good time to read a packet */
    input_ClockManageRef( p_input,
                          p_input->stream.p_selected_program,
469
                          p_sys->i_pcr );
470 471

    /* update pcr XXX in mpeg scale so in 90000 unit/s */
472
    p_sys->i_pcr = MP4_GetMoviePTS( p_sys ) * 9 / 100;
473 474

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

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

480
    for( i_track = 0; i_track < p_sys->i_tracks; i_track++ )
481
    {
Laurent Aimar's avatar
Laurent Aimar committed
482 483 484
        mp4_track_t *tk = &p_sys->track[i_track];

        if( !tk->b_ok || !tk->b_selected )
485
        {
486
            continue;
487
        }
488

489
        while( MP4_TrackGetPTS( p_input, tk ) < MP4_GetMoviePTS( p_sys ) )
490
        {
491 492 493 494 495 496
#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
497 498
            if( MP4_TrackSampleSize( tk ) > 0 &&
                ( b_play_audio || tk->fmt.i_cat != AUDIO_ES ) )
499
            {
500
                block_t *p_block;
501

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

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

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

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

Laurent Aimar's avatar
Laurent Aimar committed
532
                if( !tk->b_drms || ( tk->b_drms && tk->p_drms ) )
533
                {
Laurent Aimar's avatar
Laurent Aimar committed
534
                    es_out_Send( p_input->p_es_out, tk->p_es, p_block );
535
                }
Laurent Aimar's avatar
Laurent Aimar committed
536
            }
537

Laurent Aimar's avatar
Laurent Aimar committed
538 539 540 541
            /* Next sample */
            if( MP4_TrackNextSample( p_input, tk ) )
            {
                break;
542
            }
543 544 545
        }
    }

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

556
    /* First update update global time */
557 558
    p_sys->i_time = i_date * p_sys->i_timescale / 1000000;
    p_sys->i_pcr  = i_date* 9 / 100;
559

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

        if( tk->b_ok && tk->b_selected )
566
        {
Laurent Aimar's avatar
Laurent Aimar committed
567
            MP4_TrackSeek( p_input, tk, i_date );
568
        }
569 570 571
    }
    return( 1 );
}
572 573

/*****************************************************************************
574
 * Control:
575
 *****************************************************************************/
Gildas Bazin's avatar
 
Gildas Bazin committed
576
static int Control( input_thread_t *p_input, int i_query, va_list args )
577 578 579
{
    demux_sys_t *p_sys = p_input->p_demux_data;

Gildas Bazin's avatar
 
Gildas Bazin committed
580
    double f, *pf;
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598
    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
599 600 601 602 603 604 605 606
            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;
607 608 609

        case DEMUX_GET_TIME:
            pi64 = (int64_t*)va_arg( args, int64_t * );
Gildas Bazin's avatar
 
Gildas Bazin committed
610 611 612 613 614 615 616
            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;
617 618 619 620 621 622 623 624
            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
625 626 627 628 629 630 631
            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;
632
            return VLC_SUCCESS;
Gildas Bazin's avatar
 
Gildas Bazin committed
633

634 635 636
        case DEMUX_GET_FPS:
            msg_Warn( p_input, "DEMUX_GET_FPS unimplemented !!" );
            return VLC_EGENERIC;
Gildas Bazin's avatar
 
Gildas Bazin committed
637

638 639 640 641 642 643 644 645 646 647
        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 )
648 649
{
    unsigned int i_track;
650
    input_thread_t *  p_input = (input_thread_t *)p_this;
651
    demux_sys_t *p_sys = p_input->p_demux_data;
652

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

655 656
    MP4_BoxFree( p_input, p_sys->p_root );
    for( i_track = 0; i_track < p_sys->i_tracks; i_track++ )
657
    {
658
        MP4_TrackDestroy( p_input, &p_sys->track[i_track] );
659
    }
660
    FREE( p_sys->track );
661

662
    free( p_sys );
663 664 665
}


666

667 668 669 670
/****************************************************************************
 * Local functions, specific to vlc
 ****************************************************************************/

671 672
/* 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
673
                                   mp4_track_t *p_demux_track )
674
{
675 676
    MP4_Box_t *p_co64; /* give offset for each chunk, same for stco and co64 */
    MP4_Box_t *p_stsc;
677

678 679 680 681 682 683
    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" ) ) ))
684
    {
685
        return( VLC_EGENERIC );
686
    }
687 688 689

    p_demux_track->i_chunk_count = p_co64->data.p_co64->i_entry_count;
    if( !p_demux_track->i_chunk_count )
690
    {
691 692
        msg_Warn( p_input, "no chunk defined" );
        return( VLC_EGENERIC );
693
    }
694
    p_demux_track->chunk = calloc( p_demux_track->i_chunk_count,
Laurent Aimar's avatar
Laurent Aimar committed
695
                                   sizeof( mp4_chunk_t ) );
696 697 698

    /* first we read chunk offset */
    for( i_chunk = 0; i_chunk < p_demux_track->i_chunk_count; i_chunk++ )
699
    {
700 701
        p_demux_track->chunk[i_chunk].i_offset =
                p_co64->data.p_co64->i_chunk_offset[i_chunk];
702 703
    }

704 705 706 707 708 709
    /* 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 )
710
    {
711 712
        msg_Warn( p_input, "cannot read chunk table or table empty" );
        return( VLC_EGENERIC );
713
    }
714 715

    while( i_index )
716
    {
717 718 719 720 721 722 723 724 725 726
        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;
727 728
    }

729 730
    p_demux_track->chunk[0].i_sample_first = 0;
    for( i_chunk = 1; i_chunk < p_demux_track->i_chunk_count; i_chunk++ )
731
    {
732 733 734
        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;
735
    }
736

737 738 739
    msg_Dbg( p_input,
             "track[Id 0x%x] read %d chunk",
             p_demux_track->i_track_ID,
740
             p_demux_track->i_chunk_count );
741

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

758 759
    int64_t i_index;
    int64_t i_index_sample_used;
760

761 762
    int64_t i_last_dts;

763 764
    p_stts = MP4_BoxGet( p_demux_track->p_stbl, "stts" );
    p_stsz = MP4_BoxGet( p_demux_track->p_stbl, "stsz" ); /* FIXME and stz2 */
765 766 767 768

    if( ( !p_stts )||( !p_stsz ) )
    {
        msg_Warn( p_input, "cannot read sample table" );
769
        return( VLC_EGENERIC );
770
    }
771

772 773 774 775 776 777 778 779 780 781 782 783 784 785
    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;
786
        p_demux_track->p_sample_size =
787
            calloc( p_demux_track->i_sample_count, sizeof( uint32_t ) );
788

789 790
        for( i_sample = 0; i_sample < p_demux_track->i_sample_count; i_sample++ )
        {
791
            p_demux_track->p_sample_size[i_sample] =
792 793 794 795 796 797 798
                    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
799
       the box !, so each chunk will contain an "extract" of this table
800
       for fast research */
801

802 803
    i_last_dts = 0;
    i_index = 0; i_index_sample_used =0;
804

805 806 807 808
    /* create and init last data for each chunk */
    for(i_chunk = 0 ; i_chunk < p_demux_track->i_chunk_count; i_chunk++ )
    {

809
        int64_t i_entry, i_sample_count, i;
810 811
        /* save last dts */
        p_demux_track->chunk[i_chunk].i_first_dts = i_last_dts;
812
    /* count how many entries needed for this chunk
813 814 815
       for p_sample_delta_dts and p_sample_count_dts */

        i_sample_count = p_demux_track->chunk[i_chunk].i_sample_count;
816 817

        i_entry = 0;
818 819 820 821 822
        while( i_sample_count > 0 )
        {
            i_sample_count -= p_stts->data.p_stts->i_sample_count[i_index+i_entry];
            if( i_entry == 0 )
            {
823
                i_sample_count += i_index_sample_used; /* don't count already used sample
824 825 826 827
                                                   int this entry */
            }
            i_entry++;
        }
828

829
        /* allocate them */
830
        p_demux_track->chunk[i_chunk].p_sample_count_dts =
831
            calloc( i_entry, sizeof( uint32_t ) );
832
        p_demux_track->chunk[i_chunk].p_sample_delta_dts =
833
            calloc( i_entry, sizeof( uint32_t ) );
834 835 836 837 838

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

842 843 844 845 846
            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;
847
            i_sample_count -= i_used;
848 849 850 851 852

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

            i_last_dts += i_used *
855 856 857 858 859
                    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] )
            {