libmp4.c 176 KB
Newer Older
1 2 3
/*****************************************************************************
 * libmp4.c : LibMP4 library for mp4 module for vlc
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2001-2004, 2010 VLC authors and VideoLAN
5 6
 *
 * Author: Laurent Aimar <fenrir@via.ecp.fr>
7
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
8 9 10
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
11
 * (at your option) any later version.
12
 *
13 14
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
15 16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
17
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
18 19 20
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21
 *****************************************************************************/
22

23 24 25 26
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

27
#include <vlc_common.h>
28
#include <vlc_stream.h>                               /* vlc_stream_Peek*/
29
#include <vlc_strings.h>                              /* vlc_ascii_tolower */
30

31
#ifdef HAVE_ZLIB_H
32
#   include <zlib.h>                                  /* for compressed moov */
33 34
#endif

35
#include "libmp4.h"
36
#include "languages.h"
gnarula's avatar
gnarula committed
37
#include <math.h>
38
#include <assert.h>
39
#include <limits.h>
40 41

/* Some assumptions:
42
 * The input method HAS to be seekable
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
43
 */
44

gnarula's avatar
gnarula committed
45 46 47 48 49 50 51
/* convert 16.16 fixed point to floating point */
static double conv_fx( int32_t fx ) {
    double fp = fx;
    fp /= 65536.;
    return fp;
}

52
/* some functions for mp4 encoding of variables */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
53
#ifdef MP4_VERBOSE
54
static void MP4_ConvertDate2Str( char *psz, uint64_t i_date, bool b_relative )
55 56 57 58 59 60
{
    int i_day;
    int i_hour;
    int i_min;
    int i_sec;

Laurent Aimar's avatar
Laurent Aimar committed
61
    /* date begin at 1 jan 1904 */
62 63
    if ( !b_relative )
        i_date += ((INT64_C(1904) * 365) + 17) * 24 * 60 * 60;
Laurent Aimar's avatar
Laurent Aimar committed
64

65 66 67 68
    i_day = i_date / ( 60*60*24);
    i_hour = ( i_date /( 60*60 ) ) % 60;
    i_min  = ( i_date / 60 ) % 60;
    i_sec =  i_date % 60;
69
    sprintf( psz, "%dd-%2.2dh:%2.2dm:%2.2ds", i_day, i_hour, i_min, i_sec );
70
}
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
71
#endif
72

73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
#define MP4_GETX_PRIVATE(dst, code, size) \
    do \
    { \
        if( (i_read) >= (size) ) \
        { \
            dst = (code); \
            p_peek += (size); \
            i_read -= (size); \
        } \
        else \
        { \
            dst = 0; \
            i_read = 0; \
        } \
    } while(0)

89
#define MP4_GET1BYTE( dst )  MP4_GETX_PRIVATE( dst, *p_peek, 1 )
90
#define MP4_GET2BYTES( dst ) MP4_GETX_PRIVATE( dst, GetWBE(p_peek), 2 )
91 92 93 94 95 96 97
#define MP4_GET3BYTES( dst ) MP4_GETX_PRIVATE( dst, Get24bBE(p_peek), 3 )
#define MP4_GET4BYTES( dst ) MP4_GETX_PRIVATE( dst, GetDWBE(p_peek), 4 )
#define MP4_GET8BYTES( dst ) MP4_GETX_PRIVATE( dst, GetQWBE(p_peek), 8 )

#define MP4_GET2BYTESLE( dst ) MP4_GETX_PRIVATE( dst, GetWLE(p_peek), 2 )
#define MP4_GET4BYTESLE( dst ) MP4_GETX_PRIVATE( dst, GetDWLE(p_peek), 4 )
#define MP4_GET8BYTESLE( dst ) MP4_GETX_PRIVATE( dst, GetQWLE(p_peek), 8 )
98
#define MP4_GETFOURCC( dst )   MP4_GET4BYTESLE( dst )
99 100 101 102 103

#define MP4_GETVERSIONFLAGS( p_void ) \
    MP4_GET1BYTE( p_void->i_version ); \
    MP4_GET3BYTES( p_void->i_flags )

104
static char *mp4_getstringz( uint8_t **restrict in, uint64_t *restrict size )
105 106 107
{
    assert( *size <= SSIZE_MAX );

108 109 110 111 112 113 114 115 116 117
    if( *size == 0 )
        return NULL;

    if( *in == 0 ) /* Null string stored */
    {
        *in += 1;
        *size -= 1;
        return NULL;
    }

118
    size_t len = strnlen( (const char *)*in, *size );
119
    if( len == 0 || len >= *size )
120 121
        return NULL;

122 123 124
    len++;

    char *ret = malloc( len );
125 126 127 128 129 130 131 132 133
    if( likely(ret != NULL) )
        memcpy( ret, *in, len );
    *in += len;
    *size -= len;
    return ret;
}

#define MP4_GETSTRINGZ( p_str ) \
    do \
134
        (p_str) = mp4_getstringz( &p_peek, &i_read ); \
135
    while(0)
136

137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
/* This macro is used when we want to printf the box type
 * APPLE annotation box is :
 *  either 0xA9 + 24-bit ASCII text string (and 0xA9 isn't printable)
 *  either 32-bit ASCII text string
 */
#define MP4_BOX_TYPE_ASCII() ( ((char*)&p_box->i_type)[0] != (char)0xA9 )

static inline uint32_t Get24bBE( const uint8_t *p )
{
    return( ( p[0] <<16 ) + ( p[1] <<8 ) + p[2] );
}

static inline void GetUUID( UUID_t *p_uuid, const uint8_t *p_buff )
{
    memcpy( p_uuid, p_buff, 16 );
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
154 155 156 157 158
static uint8_t *mp4_readbox_enter_common( stream_t *s, MP4_Box_t *box,
                                          size_t typesize,
                                          void (*release)( MP4_Box_t * ),
                                          uint64_t readsize )
{
159 160 161
    const size_t headersize = mp4_box_headersize( box );

    if( unlikely(readsize < headersize) || unlikely(readsize > SSIZE_MAX) )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
        return NULL;

    uint8_t *buf = malloc( readsize );
    if( unlikely(buf == NULL) )
        return NULL;

    ssize_t val = vlc_stream_Read( s, buf, readsize );
    if( (size_t)val != readsize )
    {
        msg_Warn( s, "mp4: wanted %"PRIu64" bytes, got %zd", readsize, val );
        goto error;
    }

    box->data.p_payload = malloc( typesize );
    if( unlikely(box->data.p_payload == NULL) )
        goto error;

    memset( box->data.p_payload, 0, typesize );
    box->pf_free = release;
    return buf;
error:
    free( buf );
    return NULL;
}

static uint8_t *mp4_readbox_enter_partial( stream_t *s, MP4_Box_t *box,
                                           size_t typesize,
                                           void (*release)( MP4_Box_t * ),
190
                                           uint64_t *restrict readsize )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
{
    if( (uint64_t)*readsize > box->i_size )
        *readsize = box->i_size;

    return mp4_readbox_enter_common( s, box, typesize, release, *readsize );
}

static uint8_t *mp4_readbox_enter( stream_t *s, MP4_Box_t *box,
                                   size_t typesize,
                                   void (*release)( MP4_Box_t * ) )
{
    uint64_t readsize = box->i_size;
    return mp4_readbox_enter_common( s, box, typesize, release, readsize );
}


207
#define MP4_READBOX_ENTER_PARTIAL( MP4_Box_data_TYPE_t, maxread, release ) \
208
    uint64_t i_read = (maxread); \
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
209 210 211 212
    uint8_t *p_buff = mp4_readbox_enter_partial( p_stream, p_box, \
        sizeof( MP4_Box_data_TYPE_t ), release, &i_read ); \
    if( unlikely(p_buff == NULL) ) \
        return 0; \
213
    const size_t header_size = mp4_box_headersize( p_box ); \
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
214 215
    uint8_t *p_peek = p_buff + header_size; \
    i_read -= header_size
216 217

#define MP4_READBOX_ENTER( MP4_Box_data_TYPE_t, release ) \
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
218 219 220 221
    uint8_t *p_buff = mp4_readbox_enter( p_stream, p_box, \
        sizeof(MP4_Box_data_TYPE_t), release ); \
    if( unlikely(p_buff == NULL) ) \
        return 0; \
222
    uint64_t i_read = p_box->i_size; \
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
223 224 225
    const size_t header_size = mp4_box_headersize( p_box ); \
    uint8_t *p_peek = p_buff + header_size; \
    i_read -= header_size
226 227 228 229 230 231 232 233

#define MP4_READBOX_EXIT( i_code ) \
    do \
    { \
        free( p_buff ); \
        return( i_code ); \
    } while (0)

Laurent Aimar's avatar
Laurent Aimar committed
234 235 236
/*****************************************************************************
 * Some prototypes.
 *****************************************************************************/
sigmunau's avatar
sigmunau committed
237
static MP4_Box_t *MP4_ReadBox( stream_t *p_stream, MP4_Box_t *p_father );
238
static int MP4_Box_Read_Specific( stream_t *p_stream, MP4_Box_t *p_box, MP4_Box_t *p_father );
239
static int MP4_PeekBoxHeader( stream_t *p_stream, MP4_Box_t *p_box );
240

241
int MP4_Seek( stream_t *p_stream, uint64_t i_pos )
242 243
{
    bool b_canseek = false;
244
    if ( vlc_stream_Control( p_stream, STREAM_CAN_SEEK, &b_canseek ) != VLC_SUCCESS ||
245 246 247
         b_canseek )
    {
        /* can seek or don't know */
248
        return vlc_stream_Seek( p_stream, i_pos );
249 250 251
    }
    /* obviously can't seek then */

252
    int64_t i_current_pos = vlc_stream_Tell( p_stream );
253 254 255 256 257 258 259 260
    if ( i_current_pos < 0 || i_pos < (uint64_t)i_current_pos )
        return VLC_EGENERIC;

    size_t i_toread = i_pos - i_current_pos;
    if( i_toread == 0 )
        return VLC_SUCCESS;
    else if( i_toread > (1<<17) )
        return VLC_EGENERIC;
261 262 263 264

    if( vlc_stream_Read( p_stream, NULL, i_toread ) != (ssize_t)i_toread )
        return VLC_EGENERIC;
    return VLC_SUCCESS;
265 266
}

267 268 269 270 271 272 273
static void MP4_BoxAddChild( MP4_Box_t *p_parent, MP4_Box_t *p_childbox )
{
    if( !p_parent->p_first )
            p_parent->p_first = p_childbox;
    else
            p_parent->p_last->p_next = p_childbox;
    p_parent->p_last = p_childbox;
274
    p_childbox->p_father = p_parent;
275 276
}

277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
MP4_Box_t * MP4_BoxExtract( MP4_Box_t **pp_chain, uint32_t i_type )
{
    MP4_Box_t *p_box = *pp_chain;
    while( p_box )
    {
        if( p_box->i_type == i_type )
        {
            *pp_chain = p_box->p_next;
            p_box->p_next = NULL;
            return p_box;
        }
        pp_chain = &p_box->p_next;
        p_box = p_box->p_next;
    }
    return NULL;
}

294 295 296
/* Don't use vlc_stream_Seek directly */
#undef vlc_stream_Seek
#define vlc_stream_Seek(a,b) __NO__
297 298

/*****************************************************************************
299
 * MP4_PeekBoxHeader : Load only common parameters for all boxes
300
 *****************************************************************************
301
 * p_box need to be an already allocated MP4_Box_t, and all data
302 303 304 305
 *  will only be peek not read
 *
 * RETURN : 0 if it fail, 1 otherwise
 *****************************************************************************/
306
static int MP4_PeekBoxHeader( stream_t *p_stream, MP4_Box_t *p_box )
307
{
308
    int      i_read;
309
    const uint8_t  *p_peek;
310

311
    if( ( ( i_read = vlc_stream_Peek( p_stream, &p_peek, 32 ) ) < 8 ) )
312
    {
313
        return 0;
314
    }
315
    p_box->i_pos = vlc_stream_Tell( p_stream );
316

317
    p_box->data.p_payload = NULL;
318 319 320 321
    p_box->p_father = NULL;
    p_box->p_first  = NULL;
    p_box->p_last  = NULL;
    p_box->p_next   = NULL;
322

323 324 325 326 327 328 329
    MP4_GET4BYTES( p_box->i_shortsize );
    MP4_GETFOURCC( p_box->i_type );

    /* Now special case */

    if( p_box->i_shortsize == 1 )
    {
330 331
        if( i_read < 8 )
            return 0;
332 333 334 335 336 337 338 339
        /* get the true size on 64 bits */
        MP4_GET8BYTES( p_box->i_size );
    }
    else
    {
        p_box->i_size = p_box->i_shortsize;
        /* XXX size of 0 means that the box extends to end of file */
    }
340

341 342 343 344
    if( UINT64_MAX - p_box->i_size < p_box->i_pos )
        return 0;

    if( p_box->i_type == ATOM_uuid )
345
    {
346 347
        if( i_read < 16 )
            return 0;
348 349 350
        /* get extented type on 16 bytes */
        GetUUID( &p_box->i_uuid, p_peek );
    }
351

352
#ifdef MP4_ULTRA_VERBOSE
353 354
    if( p_box->i_size )
    {
355
        if MP4_BOX_TYPE_ASCII()
356 357
            msg_Dbg( p_stream, "found Box: %4.4s size %"PRId64" %"PRId64,
                    (char*)&p_box->i_type, p_box->i_size, p_box->i_pos );
358
        else
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
359
            msg_Dbg( p_stream, "found Box: c%3.3s size %"PRId64,
360
                    (char*)&p_box->i_type+1, p_box->i_size );
361 362 363
    }
#endif

364
    return 1;
365 366 367
}

/*****************************************************************************
368
 * MP4_ReadBoxRestricted : Reads box from current position
369
 *****************************************************************************
370 371
 * if p_box == NULL, box is invalid or failed, position undefined
 * on success, position is past read box or EOF
372
 *****************************************************************************/
373
static MP4_Box_t *MP4_ReadBoxRestricted( stream_t *p_stream, MP4_Box_t *p_father,
374 375
                                         const uint32_t onlytypes[], const uint32_t nottypes[],
                                         bool *pb_restrictionhit )
376
{
377 378 379
    MP4_Box_t peekbox = { 0 };
    if ( !MP4_PeekBoxHeader( p_stream, &peekbox ) )
        return NULL;
380

381
    if( peekbox.i_size < 8 )
382
    {
383
        msg_Warn( p_stream, "found an invalid sized %"PRIu64" box %4.4s @%"PRIu64 ,
384
                  peekbox.i_size, (char *) &peekbox.i_type, vlc_stream_Tell(p_stream) );
385
        return NULL;
386 387
    }

388
    for( size_t i=0; nottypes && nottypes[i]; i++ )
389
    {
390
        if( nottypes[i] == peekbox.i_type )
391 392
        {
            *pb_restrictionhit = true;
393
            return NULL;
394
        }
395 396
    }

397
    for( size_t i=0; onlytypes && onlytypes[i]; i++ )
398
    {
399
        if( onlytypes[i] != peekbox.i_type )
400 401
        {
            *pb_restrictionhit = true;
402
            return NULL;
403
        }
404
    }
405

406 407 408 409 410 411 412 413 414 415 416
    /* if father's size == 0, it means unknown or infinite size,
     * and we skip the followong check */
    if( p_father && p_father->i_size > 0 )
    {
        const uint64_t i_box_next = peekbox.i_size + peekbox.i_pos;
        const uint64_t i_father_next = p_father->i_size + p_father->i_pos;
        /* check if it's within p-father */
        if( i_box_next > i_father_next )
        {
            msg_Warn( p_stream, "out of bound child %4.4s", (char*) &peekbox.i_type );
            return NULL; /* out of bound */
417 418
        }
    }
419 420 421 422 423 424 425 426

    /* Everything seems OK */
    MP4_Box_t *p_box = (MP4_Box_t *) malloc( sizeof(MP4_Box_t) );
    if( !p_box )
        return NULL;
    *p_box = peekbox;

    const uint64_t i_next = p_box->i_pos + p_box->i_size;
427
    p_box->p_father = p_father;
428 429 430
    if( MP4_Box_Read_Specific( p_stream, p_box, p_father ) != VLC_SUCCESS )
    {
        msg_Warn( p_stream, "Failed reading box %4.4s", (char*) &peekbox.i_type );
431
        MP4_BoxFree( p_box );
432
        p_box = NULL;
433 434 435
    }

    /* Check is we consumed all data */
436
    if( vlc_stream_Tell( p_stream ) < i_next )
437
    {
438 439
        MP4_Seek( p_stream, i_next - 1 ); /*  since past seek can fail when hitting EOF */
        MP4_Seek( p_stream, i_next );
440 441 442 443 444 445
        if( vlc_stream_Tell( p_stream ) < i_next - 1 ) /* Truncated box */
        {
            msg_Warn( p_stream, "truncated box %4.4s discarded", (char*) &peekbox.i_type );
            MP4_BoxFree( p_box );
            p_box = NULL;
        }
446 447
    }

448 449
    if ( p_box )
        MP4_BoxAddChild( p_father, p_box );
450 451 452 453

    return p_box;
}

454
/*****************************************************************************
455
 * For all known box a loader is given,
456 457
 * you have to be already read container header
 * without container size, file position on exit is unknown
458
 *****************************************************************************/
459
static int MP4_ReadBoxContainerChildrenIndexed( stream_t *p_stream,
460 461
               MP4_Box_t *p_container, const uint32_t stoplist[],
               const uint32_t excludelist[], bool b_indexed )
462
{
463 464
    /* Size of root container is set to 0 when unknown, for exemple
     * with a DASH stream. In that case, we skip the following check */
465
    if( (p_container->i_size || p_container->p_father)
466
            && ( vlc_stream_Tell( p_stream ) + ((b_indexed)?16:8) >
467
        (uint64_t)(p_container->i_pos + p_container->i_size) )
468
      )
469 470
    {
        /* there is no box to load */
471
        return 0;
472
    }
473

474
    uint64_t i_last_pos = 0; /* used to detect read failure loops */
475 476
    const uint64_t i_end = p_container->i_pos + p_container->i_size;
    MP4_Box_t *p_box = NULL;
477
    bool b_onexclude = false;
478
    bool b_continue;
479 480
    do
    {
481
        b_continue = false;
482 483
        if ( p_container->i_size )
        {
484
            const uint64_t i_tell = vlc_stream_Tell( p_stream );
485 486 487 488
            if( i_tell + ((b_indexed)?16:8) >= i_end )
                break;
        }

489 490 491 492
        uint32_t i_index = 0;
        if ( b_indexed )
        {
            uint8_t read[8];
493
            if ( vlc_stream_Read( p_stream, read, 8 ) < 8 )
494
                break;
495 496
            i_index = GetDWBE(&read[4]);
        }
497 498
        b_onexclude = false; /* If stopped due exclude list */
        if( (p_box = MP4_ReadBoxRestricted( p_stream, p_container, NULL, excludelist, &b_onexclude )) )
499
        {
500
            b_continue = true;
501 502 503 504 505 506 507
            p_box->i_index = i_index;
            for(size_t i=0; stoplist && stoplist[i]; i++)
            {
                if( p_box->i_type == stoplist[i] )
                    return 1;
            }
        }
508

509 510
        const uint64_t i_tell = vlc_stream_Tell( p_stream );
        if ( p_container->i_size && i_tell >= i_end )
yhuelf's avatar
yhuelf committed
511
        {
512 513 514 515 516 517 518 519
            assert( i_tell == i_end );
            break;
        }

        if ( !p_box )
        {
            /* Continue with next if box fails to load */
            if( i_last_pos == i_tell )
520
                break;
521 522
            i_last_pos = i_tell;
            b_continue = true;
yhuelf's avatar
yhuelf committed
523
        }
524

525
    } while( b_continue );
526

527
    /* Always move to end of container */
528
    if ( !b_onexclude &&  p_container->i_size )
529
    {
530
        const uint64_t i_tell = vlc_stream_Tell( p_stream );
531 532 533
        if ( i_tell != i_end )
            MP4_Seek( p_stream, i_end );
    }
534

535
    return 1;
536 537
}

538 539 540 541 542 543 544
int MP4_ReadBoxContainerRestricted( stream_t *p_stream, MP4_Box_t *p_container,
                                    const uint32_t stoplist[], const uint32_t excludelist[] )
{
    return MP4_ReadBoxContainerChildrenIndexed( p_stream, p_container,
                                                stoplist, excludelist, false );
}

545
int MP4_ReadBoxContainerChildren( stream_t *p_stream, MP4_Box_t *p_container,
546
                                  const uint32_t stoplist[] )
547 548
{
    return MP4_ReadBoxContainerChildrenIndexed( p_stream, p_container,
549
                                                stoplist, NULL, false );
550 551
}

552 553 554 555 556 557 558 559 560 561 562 563 564 565
static void MP4_BoxOffsetUp( MP4_Box_t *p_box, uint64_t i_offset )
{
    while(p_box)
    {
        p_box->i_pos += i_offset;
        MP4_BoxOffsetUp( p_box->p_first, i_offset );
        p_box = p_box->p_next;
    }
}

/* Reads within an already read/in memory box (containers without having to seek) */
static int MP4_ReadBoxContainerRawInBox( stream_t *p_stream, MP4_Box_t *p_container,
                                         uint8_t *p_buffer, uint64_t i_size, uint64_t i_offset )
{
566 567
    if(!p_container)
        return 0;
568 569
    stream_t *p_substream = vlc_stream_MemoryNew( p_stream, p_buffer, i_size,
                                                  true );
570 571
    if( !p_substream )
        return 0;
572
    MP4_Box_t *p_last = p_container->p_last;
573
    MP4_ReadBoxContainerChildren( p_substream, p_container, NULL );
574
    vlc_stream_Delete( p_substream );
575 576 577 578 579 580 581 582 583 584
    /* do pos fixup */
    if( p_container )
    {
        MP4_Box_t *p_box = p_last ? p_last : p_container->p_first;
        MP4_BoxOffsetUp(p_box, i_offset);
    }

    return 1;
}

sigmunau's avatar
sigmunau committed
585
static int MP4_ReadBoxContainer( stream_t *p_stream, MP4_Box_t *p_container )
586
{
587 588
    if( p_container->i_size &&
        ( p_container->i_size <= (size_t)mp4_box_headersize(p_container ) + 8 ) )
589 590
    {
        /* container is empty, 8 stand for the first header in this box */
591
        return 1;
592
    }
593

594
    /* enter box */
595 596 597
    if ( MP4_Seek( p_stream, p_container->i_pos +
                      mp4_box_headersize( p_container ) ) )
        return 0;
598
    return MP4_ReadBoxContainerChildren( p_stream, p_container, NULL );
599 600
}

sigmunau's avatar
sigmunau committed
601
static int MP4_ReadBoxSkip( stream_t *p_stream, MP4_Box_t *p_box )
602
{
603
    /* XXX sometime moov is hidden in a free box */
604
    if( p_box->p_father &&
605
        p_box->p_father->i_type == ATOM_root &&
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
606
        p_box->i_type == ATOM_free )
607
    {
608
        const uint8_t *p_peek;
609
        size_t header_size = mp4_box_headersize( p_box ) + 4;
610 611
        vlc_fourcc_t i_fcc;

612 613 614
        ssize_t i_read = vlc_stream_Peek( p_stream, &p_peek, 44 );
        if( unlikely(i_read < (ssize_t)header_size) )
            return 0;
615

616 617
        p_peek += header_size;
        i_read -= header_size;
618 619 620 621 622

        if( i_read >= 8 )
        {
            i_fcc = VLC_FOURCC( p_peek[0], p_peek[1], p_peek[2], p_peek[3] );

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
623
            if( i_fcc == ATOM_cmov || i_fcc == ATOM_mvhd )
624
            {
sigmunau's avatar
sigmunau committed
625
                msg_Warn( p_stream, "detected moov hidden in a free box ..." );
626

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
627
                p_box->i_type = ATOM_foov;
628 629 630 631
                return MP4_ReadBoxContainer( p_stream, p_box );
            }
        }
    }
632

633
    /* Nothing to do */
634
#ifdef MP4_ULTRA_VERBOSE
635 636 637 638
    if MP4_BOX_TYPE_ASCII()
        msg_Dbg( p_stream, "skip box: \"%4.4s\"", (char*)&p_box->i_type );
    else
        msg_Dbg( p_stream, "skip box: \"c%3.3s\"", (char*)&p_box->i_type+1 );
639
#endif
640
    return 1;
641 642
}

643 644
static int MP4_ReadBox_ilst( stream_t *p_stream, MP4_Box_t *p_box )
{
645
    if( p_box->i_size < 8 || vlc_stream_Read( p_stream, NULL, 8 ) < 8 )
646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667
        return 0;

    /* Find our handler */
    if ( !p_box->i_handler && p_box->p_father )
    {
        const MP4_Box_t *p_sibling = p_box->p_father->p_first;
        while( p_sibling )
        {
            if ( p_sibling->i_type == ATOM_hdlr && p_sibling->data.p_hdlr )
            {
                p_box->i_handler = p_sibling->data.p_hdlr->i_handler_type;
                break;
            }
            p_sibling = p_sibling->p_next;
        }
    }

    switch( p_box->i_handler )
    {
    case 0:
        msg_Warn( p_stream, "no handler for ilst atom" );
        return 0;
668
    case HANDLER_mdta:
669
        return MP4_ReadBoxContainerChildrenIndexed( p_stream, p_box, NULL, NULL, true );
670
    case HANDLER_mdir:
671
        return MP4_ReadBoxContainerChildren( p_stream, p_box, NULL );
672 673 674 675 676 677
    default:
        msg_Warn( p_stream, "Unknown ilst handler type '%4.4s'", (char*)&p_box->i_handler );
        return 0;
    }
}

678 679
static void MP4_FreeBox_ftyp( MP4_Box_t *p_box )
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
680
    free( p_box->data.p_ftyp->i_compatible_brands );
681 682
}

sigmunau's avatar
sigmunau committed
683
static int MP4_ReadBox_ftyp( stream_t *p_stream, MP4_Box_t *p_box )
684
{
685
    MP4_READBOX_ENTER( MP4_Box_data_ftyp_t, MP4_FreeBox_ftyp );
686

687 688
    MP4_GETFOURCC( p_box->data.p_ftyp->i_major_brand );
    MP4_GET4BYTES( p_box->data.p_ftyp->i_minor_version );
689

690 691
    p_box->data.p_ftyp->i_compatible_brands_count = i_read / 4;
    if( p_box->data.p_ftyp->i_compatible_brands_count > 0 )
692
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
693
        uint32_t *tab = p_box->data.p_ftyp->i_compatible_brands =
694 695
            vlc_alloc( p_box->data.p_ftyp->i_compatible_brands_count,
                       sizeof(uint32_t) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
696

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
697
        if( unlikely( tab == NULL ) )
698
            MP4_READBOX_EXIT( 0 );
699

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
700
        for( unsigned i = 0; i < p_box->data.p_ftyp->i_compatible_brands_count; i++ )
701
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
702
            MP4_GETFOURCC( tab[i] );
703 704 705 706 707 708 709
        }
    }
    else
    {
        p_box->data.p_ftyp->i_compatible_brands = NULL;
    }

710
    MP4_READBOX_EXIT( 1 );
711 712 713
}


sigmunau's avatar
sigmunau committed
714
static int MP4_ReadBox_mvhd(  stream_t *p_stream, MP4_Box_t *p_box )
715 716 717 718 719 720
{
#ifdef MP4_VERBOSE
    char s_creation_time[128];
    char s_modification_time[128];
    char s_duration[128];
#endif
721
    MP4_READBOX_ENTER( MP4_Box_data_mvhd_t, NULL );
722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738

    MP4_GETVERSIONFLAGS( p_box->data.p_mvhd );

    if( p_box->data.p_mvhd->i_version )
    {
        MP4_GET8BYTES( p_box->data.p_mvhd->i_creation_time );
        MP4_GET8BYTES( p_box->data.p_mvhd->i_modification_time );
        MP4_GET4BYTES( p_box->data.p_mvhd->i_timescale );
        MP4_GET8BYTES( p_box->data.p_mvhd->i_duration );
    }
    else
    {
        MP4_GET4BYTES( p_box->data.p_mvhd->i_creation_time );
        MP4_GET4BYTES( p_box->data.p_mvhd->i_modification_time );
        MP4_GET4BYTES( p_box->data.p_mvhd->i_timescale );
        MP4_GET4BYTES( p_box->data.p_mvhd->i_duration );
    }
739 740
    MP4_GET4BYTES( p_box->data.p_mvhd->i_rate );
    MP4_GET2BYTES( p_box->data.p_mvhd->i_volume );
741 742
    MP4_GET2BYTES( p_box->data.p_mvhd->i_reserved1 );

743

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
744
    for( unsigned i = 0; i < 2; i++ )
745 746 747
    {
        MP4_GET4BYTES( p_box->data.p_mvhd->i_reserved2[i] );
    }
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
748
    for( unsigned i = 0; i < 9; i++ )
749 750 751
    {
        MP4_GET4BYTES( p_box->data.p_mvhd->i_matrix[i] );
    }
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
752
    for( unsigned i = 0; i < 6; i++ )
753 754 755
    {
        MP4_GET4BYTES( p_box->data.p_mvhd->i_predefined[i] );
    }
756

757
    MP4_GET4BYTES( p_box->data.p_mvhd->i_next_track_id );
758 759


760
#ifdef MP4_VERBOSE
761
    MP4_ConvertDate2Str( s_creation_time, p_box->data.p_mvhd->i_creation_time, false );
762
    MP4_ConvertDate2Str( s_modification_time,
763
                         p_box->data.p_mvhd->i_modification_time, false );
764
    if( p_box->data.p_mvhd->i_rate && p_box->data.p_mvhd->i_timescale )
765
    {
766
        MP4_ConvertDate2Str( s_duration, p_box->data.p_mvhd->i_duration / p_box->data.p_mvhd->i_timescale, true );
767 768 769 770
    }
    else
    {
        s_duration[0] = 0;
771
    }
sigmunau's avatar
sigmunau committed
772
    msg_Dbg( p_stream, "read box: \"mvhd\" creation %s modification %s time scale %d duration %s rate %f volume %f next track id %d",
773 774
                  s_creation_time,
                  s_modification_time,
775
                  (uint32_t)p_box->data.p_mvhd->i_timescale,
776 777 778
                  s_duration,
                  (float)p_box->data.p_mvhd->i_rate / (1<<16 ),
                  (float)p_box->data.p_mvhd->i_volume / 256 ,
779
                  (uint32_t)p_box->data.p_mvhd->i_next_track_id );
780 781 782 783
#endif
    MP4_READBOX_EXIT( 1 );
}

784 785
static int MP4_ReadBox_mfhd(  stream_t *p_stream, MP4_Box_t *p_box )
{
786
    MP4_READBOX_ENTER( MP4_Box_data_mfhd_t, NULL );
787

F. Yhuel's avatar
F. Yhuel committed
788 789
    MP4_GETVERSIONFLAGS( p_box->data.p_mvhd );

790 791 792 793 794 795 796 797 798
    MP4_GET4BYTES( p_box->data.p_mfhd->i_sequence_number );

#ifdef MP4_VERBOSE
    msg_Dbg( p_stream, "read box: \"mfhd\" sequence number %d",
                  p_box->data.p_mfhd->i_sequence_number );
#endif
    MP4_READBOX_EXIT( 1 );
}

799 800
static int MP4_ReadBox_tfxd(  stream_t *p_stream, MP4_Box_t *p_box )
{
801
    MP4_READBOX_ENTER( MP4_Box_data_tfxd_t, NULL );
802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829

    MP4_Box_data_tfxd_t *p_tfxd_data = p_box->data.p_tfxd;
    MP4_GETVERSIONFLAGS( p_tfxd_data );

    if( p_tfxd_data->i_version == 0 )
    {
        MP4_GET4BYTES( p_tfxd_data->i_fragment_abs_time );
        MP4_GET4BYTES( p_tfxd_data->i_fragment_duration );
    }
    else
    {
        MP4_GET8BYTES( p_tfxd_data->i_fragment_abs_time );
        MP4_GET8BYTES( p_tfxd_data->i_fragment_duration );
    }

#ifdef MP4_VERBOSE
    msg_Dbg( p_stream, "read box: \"tfxd\" version %d, flags 0x%x, "\
            "fragment duration %"PRIu64", fragment abs time %"PRIu64,
                p_tfxd_data->i_version,
                p_tfxd_data->i_flags,
                p_tfxd_data->i_fragment_duration,
                p_tfxd_data->i_fragment_abs_time
           );
#endif

    MP4_READBOX_EXIT( 1 );
}

830 831
static void MP4_FreeBox_tfrf( MP4_Box_t *p_box )
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
832
    free( p_box->data.p_tfrf->p_tfrf_data_fields );
833 834
}

835 836
static int MP4_ReadBox_tfrf(  stream_t *p_stream, MP4_Box_t *p_box )
{
837
    MP4_READBOX_ENTER( MP4_Box_data_tfxd_t, MP4_FreeBox_tfrf );
838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882

    MP4_Box_data_tfrf_t *p_tfrf_data = p_box->data.p_tfrf;
    MP4_GETVERSIONFLAGS( p_tfrf_data );

    MP4_GET1BYTE( p_tfrf_data->i_fragment_count );

    p_tfrf_data->p_tfrf_data_fields = calloc( p_tfrf_data->i_fragment_count,
                                              sizeof( TfrfBoxDataFields_t ) );
    if( !p_tfrf_data->p_tfrf_data_fields )
        MP4_READBOX_EXIT( 0 );

    for( uint8_t i = 0; i < p_tfrf_data->i_fragment_count; i++ )
    {
        TfrfBoxDataFields_t *TfrfBoxDataField = &p_tfrf_data->p_tfrf_data_fields[i];
        if( p_tfrf_data->i_version == 0 )
        {
            MP4_GET4BYTES( TfrfBoxDataField->i_fragment_abs_time );
            MP4_GET4BYTES( TfrfBoxDataField->i_fragment_duration );
        }
        else
        {
            MP4_GET8BYTES( TfrfBoxDataField->i_fragment_abs_time );
            MP4_GET8BYTES( TfrfBoxDataField->i_fragment_duration );
        }
    }

#ifdef MP4_VERBOSE
    msg_Dbg( p_stream, "read box: \"tfrf\" version %d, flags 0x%x, "\
            "fragment count %"PRIu8, p_tfrf_data->i_version,
                p_tfrf_data->i_flags, p_tfrf_data->i_fragment_count );

    for( uint8_t i = 0; i < p_tfrf_data->i_fragment_count; i++ )
    {
        TfrfBoxDataFields_t *TfrfBoxDataField = &p_tfrf_data->p_tfrf_data_fields[i];
        msg_Dbg( p_stream, "\"tfrf\" fragment duration %"PRIu64", "\
                                    "fragment abs time %"PRIu64,
                    TfrfBoxDataField->i_fragment_duration,
                    TfrfBoxDataField->i_fragment_abs_time );
    }

#endif

    MP4_READBOX_EXIT( 1 );
}

883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903
static int MP4_ReadBox_XML360( stream_t *p_stream, MP4_Box_t *p_box )
{
    MP4_READBOX_ENTER( MP4_Box_data_360_t, NULL );

    MP4_Box_data_360_t *p_360_data = p_box->data.p_360;

    /* Copy the string for pattern matching as it does not end
    with a '\0' in the stream. */
    char *psz_rdf = strndup((char *)p_peek, i_read);

    if ( unlikely( !psz_rdf ) )
        MP4_READBOX_EXIT( 0 );

    /* Try to find the string "GSpherical:Spherical" because the v1
    spherical video spec says the tag must be there. */

    if ( strcasestr( psz_rdf, "Gspherical:Spherical" ) )
        p_360_data->i_projection_mode = PROJECTION_MODE_EQUIRECTANGULAR;

    /* Try to find the stero mode. */
    if ( strcasestr( psz_rdf, "left-right" ) )
904
    {
905
        msg_Dbg( p_stream, "Left-right stereo mode" );
906 907
        p_360_data->e_stereo_mode = XML360_STEREOSCOPIC_LEFT_RIGHT;
    }
908 909

    if ( strcasestr( psz_rdf, "top-bottom" ) )
910
    {
911
        msg_Dbg( p_stream, "Top-bottom stereo mode" );
912 913
        p_360_data->e_stereo_mode = XML360_STEREOSCOPIC_TOP_BOTTOM;
    }
914