libmp4.c 170 KB
Newer Older
1 2 3
/*****************************************************************************
 * libmp4.c : LibMP4 library for mp4 module for vlc
 *****************************************************************************
Jean-Baptiste Kempf's avatar
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
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
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
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"
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

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 */
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
}
71
#endif
72

73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
#define MP4_GET1BYTE( dst )  MP4_GETX_PRIVATE( dst, *p_peek, 1 )
#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_GETFOURCC( dst ) MP4_GETX_PRIVATE( dst, \
                VLC_FOURCC(p_peek[0],p_peek[1],p_peek[2],p_peek[3]), 4)

#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 )

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

88
static char *mp4_getstringz( uint8_t **restrict in, uint64_t *restrict size )
89 90 91 92
{
    assert( *size <= SSIZE_MAX );

    size_t len = strnlen( (const char *)*in, *size );
93
    if( len == 0 || len >= *size )
94 95
        return NULL;

96 97 98
    len++;

    char *ret = malloc( len );
99 100 101 102 103 104 105 106 107
    if( likely(ret != NULL) )
        memcpy( ret, *in, len );
    *in += len;
    *size -= len;
    return ret;
}

#define MP4_GETSTRINGZ( p_str ) \
    do \
108
        (p_str) = mp4_getstringz( &p_peek, &i_read ); \
109
    while(0)
110

111 112 113 114 115
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 )
{
116 117 118
    const size_t headersize = mp4_box_headersize( box );

    if( unlikely(readsize < headersize) || unlikely(readsize > SSIZE_MAX) )
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
        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 * ),
147
                                           uint64_t *restrict readsize )
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
{
    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 );
}


164
#define MP4_READBOX_ENTER_PARTIAL( MP4_Box_data_TYPE_t, maxread, release ) \
165
    uint64_t i_read = (maxread); \
166 167 168 169
    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; \
170
    const size_t header_size = mp4_box_headersize( p_box ); \
171 172
    uint8_t *p_peek = p_buff + header_size; \
    i_read -= header_size
173 174

#define MP4_READBOX_ENTER( MP4_Box_data_TYPE_t, release ) \
175 176 177 178
    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; \
179
    uint64_t i_read = p_box->i_size; \
180 181 182
    const size_t header_size = mp4_box_headersize( p_box ); \
    uint8_t *p_peek = p_buff + header_size; \
    i_read -= header_size
183 184 185 186 187 188 189 190

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

Laurent Aimar's avatar
Laurent Aimar committed
191 192 193
/*****************************************************************************
 * Some prototypes.
 *****************************************************************************/
194
static MP4_Box_t *MP4_ReadBox( stream_t *p_stream, MP4_Box_t *p_father );
195 196
static int MP4_Box_Read_Specific( stream_t *p_stream, MP4_Box_t *p_box, MP4_Box_t *p_father );
static void MP4_Box_Clean_Specific( MP4_Box_t *p_box );
197
static int MP4_PeekBoxHeader( stream_t *p_stream, MP4_Box_t *p_box );
198

199
int MP4_Seek( stream_t *p_stream, uint64_t i_pos )
200 201
{
    bool b_canseek = false;
202
    if ( vlc_stream_Control( p_stream, STREAM_CAN_SEEK, &b_canseek ) != VLC_SUCCESS ||
203 204 205
         b_canseek )
    {
        /* can seek or don't know */
206
        return vlc_stream_Seek( p_stream, i_pos );
207 208 209
    }
    /* obviously can't seek then */

210
    int64_t i_current_pos = vlc_stream_Tell( p_stream );
211 212 213 214 215 216 217 218
    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;
219 220 221 222

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

225 226 227 228 229 230 231
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;
232
    p_childbox->p_father = p_parent;
233 234
}

235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
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;
}

252 253 254
/* Don't use vlc_stream_Seek directly */
#undef vlc_stream_Seek
#define vlc_stream_Seek(a,b) __NO__
255 256

/*****************************************************************************
257
 * MP4_PeekBoxHeader : Load only common parameters for all boxes
258
 *****************************************************************************
259
 * p_box need to be an already allocated MP4_Box_t, and all data
260 261 262 263
 *  will only be peek not read
 *
 * RETURN : 0 if it fail, 1 otherwise
 *****************************************************************************/
264
static int MP4_PeekBoxHeader( stream_t *p_stream, MP4_Box_t *p_box )
265
{
266
    int      i_read;
267
    const uint8_t  *p_peek;
268

269
    if( ( ( i_read = vlc_stream_Peek( p_stream, &p_peek, 32 ) ) < 8 ) )
270
    {
271
        return 0;
272
    }
273
    p_box->i_pos = vlc_stream_Tell( p_stream );
274

275
    p_box->data.p_payload = NULL;
276 277 278 279
    p_box->p_father = NULL;
    p_box->p_first  = NULL;
    p_box->p_last  = NULL;
    p_box->p_next   = NULL;
280

281 282 283 284 285 286 287
    MP4_GET4BYTES( p_box->i_shortsize );
    MP4_GETFOURCC( p_box->i_type );

    /* Now special case */

    if( p_box->i_shortsize == 1 )
    {
288 289
        if( i_read < 8 )
            return 0;
290 291 292 293 294 295 296 297
        /* 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 */
    }
298

299 300 301 302
    if( UINT64_MAX - p_box->i_size < p_box->i_pos )
        return 0;

    if( p_box->i_type == ATOM_uuid )
303
    {
304 305
        if( i_read < 16 )
            return 0;
306 307 308
        /* get extented type on 16 bytes */
        GetUUID( &p_box->i_uuid, p_peek );
    }
309

310
#ifdef MP4_ULTRA_VERBOSE
311 312
    if( p_box->i_size )
    {
313
        if MP4_BOX_TYPE_ASCII()
314 315
            msg_Dbg( p_stream, "found Box: %4.4s size %"PRId64" %"PRId64,
                    (char*)&p_box->i_type, p_box->i_size, p_box->i_pos );
316
        else
317
            msg_Dbg( p_stream, "found Box: c%3.3s size %"PRId64,
318
                    (char*)&p_box->i_type+1, p_box->i_size );
319 320 321
    }
#endif

322
    return 1;
323 324 325
}

/*****************************************************************************
326
 * MP4_ReadBoxRestricted : Reads box from current position
327
 *****************************************************************************
328 329
 * if p_box == NULL, box is invalid or failed, position undefined
 * on success, position is past read box or EOF
330
 *****************************************************************************/
331
static MP4_Box_t *MP4_ReadBoxRestricted( stream_t *p_stream, MP4_Box_t *p_father,
332 333
                                         const uint32_t onlytypes[], const uint32_t nottypes[],
                                         bool *pb_restrictionhit )
334
{
335 336 337
    MP4_Box_t peekbox = { 0 };
    if ( !MP4_PeekBoxHeader( p_stream, &peekbox ) )
        return NULL;
338

339
    if( peekbox.i_size < 8 )
340
    {
341
        msg_Warn( p_stream, "found an invalid sized %"PRIu64" box %4.4s @%"PRIu64 ,
342
                  peekbox.i_size, (char *) &peekbox.i_type, vlc_stream_Tell(p_stream) );
343
        return NULL;
344 345
    }

346
    for( size_t i=0; nottypes && nottypes[i]; i++ )
347
    {
348
        if( nottypes[i] == peekbox.i_type )
349 350
        {
            *pb_restrictionhit = true;
351
            return NULL;
352
        }
353 354
    }

355
    for( size_t i=0; onlytypes && onlytypes[i]; i++ )
356
    {
357
        if( onlytypes[i] != peekbox.i_type )
358 359
        {
            *pb_restrictionhit = true;
360
            return NULL;
361
        }
362
    }
363

364 365 366 367 368 369 370 371 372 373 374
    /* 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 */
375 376
        }
    }
377 378 379 380 381 382 383 384

    /* 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;
385
    p_box->p_father = p_father;
386 387 388
    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 );
389
        MP4_BoxFree( p_box );
390
        p_box = NULL;
391 392 393
    }

    /* Check is we consumed all data */
394
    if( vlc_stream_Tell( p_stream ) < i_next )
395
    {
396 397
        MP4_Seek( p_stream, i_next - 1 ); /*  since past seek can fail when hitting EOF */
        MP4_Seek( p_stream, i_next );
398 399 400 401 402 403
        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;
        }
404 405
    }

406 407
    if ( p_box )
        MP4_BoxAddChild( p_father, p_box );
408 409 410 411

    return p_box;
}

412
/*****************************************************************************
413
 * For all known box a loader is given,
414 415
 * you have to be already read container header
 * without container size, file position on exit is unknown
416
 *****************************************************************************/
417
static int MP4_ReadBoxContainerChildrenIndexed( stream_t *p_stream,
418 419
               MP4_Box_t *p_container, const uint32_t stoplist[],
               const uint32_t excludelist[], bool b_indexed )
420
{
421 422
    /* Size of root container is set to 0 when unknown, for exemple
     * with a DASH stream. In that case, we skip the following check */
423
    if( (p_container->i_size || p_container->p_father)
424
            && ( vlc_stream_Tell( p_stream ) + ((b_indexed)?16:8) >
425
        (uint64_t)(p_container->i_pos + p_container->i_size) )
426
      )
427 428
    {
        /* there is no box to load */
429
        return 0;
430
    }
431

432
    uint64_t i_last_pos = 0; /* used to detect read failure loops */
433 434
    const uint64_t i_end = p_container->i_pos + p_container->i_size;
    MP4_Box_t *p_box = NULL;
435
    bool b_onexclude = false;
436
    bool b_continue;
437 438
    do
    {
439
        b_continue = false;
440 441
        if ( p_container->i_size )
        {
442
            const uint64_t i_tell = vlc_stream_Tell( p_stream );
443 444 445 446
            if( i_tell + ((b_indexed)?16:8) >= i_end )
                break;
        }

447 448 449 450
        uint32_t i_index = 0;
        if ( b_indexed )
        {
            uint8_t read[8];
451
            if ( vlc_stream_Read( p_stream, read, 8 ) < 8 )
452
                break;
453 454
            i_index = GetDWBE(&read[4]);
        }
455 456
        b_onexclude = false; /* If stopped due exclude list */
        if( (p_box = MP4_ReadBoxRestricted( p_stream, p_container, NULL, excludelist, &b_onexclude )) )
457
        {
458
            b_continue = true;
459 460 461 462 463 464 465
            p_box->i_index = i_index;
            for(size_t i=0; stoplist && stoplist[i]; i++)
            {
                if( p_box->i_type == stoplist[i] )
                    return 1;
            }
        }
466

467 468
        const uint64_t i_tell = vlc_stream_Tell( p_stream );
        if ( p_container->i_size && i_tell >= i_end )
Frédéric Yhuel's avatar
Frédéric Yhuel committed
469
        {
470 471 472 473 474 475 476 477
            assert( i_tell == i_end );
            break;
        }

        if ( !p_box )
        {
            /* Continue with next if box fails to load */
            if( i_last_pos == i_tell )
478
                break;
479 480
            i_last_pos = i_tell;
            b_continue = true;
Frédéric Yhuel's avatar
Frédéric Yhuel committed
481
        }
482

483
    } while( b_continue );
484

485
    /* Always move to end of container */
486
    if ( !b_onexclude &&  p_container->i_size )
487
    {
488
        const uint64_t i_tell = vlc_stream_Tell( p_stream );
489 490 491
        if ( i_tell != i_end )
            MP4_Seek( p_stream, i_end );
    }
492

493
    return 1;
494 495
}

496 497 498 499 500 501 502
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 );
}

503
int MP4_ReadBoxContainerChildren( stream_t *p_stream, MP4_Box_t *p_container,
504
                                  const uint32_t stoplist[] )
505 506
{
    return MP4_ReadBoxContainerChildrenIndexed( p_stream, p_container,
507
                                                stoplist, NULL, false );
508 509
}

510 511 512 513 514 515 516 517 518 519 520 521 522 523
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 )
{
524 525
    if(!p_container)
        return 0;
526 527
    stream_t *p_substream = vlc_stream_MemoryNew( p_stream, p_buffer, i_size,
                                                  true );
528 529
    if( !p_substream )
        return 0;
530
    MP4_Box_t *p_last = p_container->p_last;
531
    MP4_ReadBoxContainerChildren( p_substream, p_container, NULL );
532
    vlc_stream_Delete( p_substream );
533 534 535 536 537 538 539 540 541 542
    /* 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;
}

543
static int MP4_ReadBoxContainer( stream_t *p_stream, MP4_Box_t *p_container )
544
{
545 546
    if( p_container->i_size &&
        ( p_container->i_size <= (size_t)mp4_box_headersize(p_container ) + 8 ) )
547 548
    {
        /* container is empty, 8 stand for the first header in this box */
549
        return 1;
550
    }
551

552
    /* enter box */
553 554 555
    if ( MP4_Seek( p_stream, p_container->i_pos +
                      mp4_box_headersize( p_container ) ) )
        return 0;
556
    return MP4_ReadBoxContainerChildren( p_stream, p_container, NULL );
557 558
}

559
static int MP4_ReadBoxSkip( stream_t *p_stream, MP4_Box_t *p_box )
560
{
561
    /* XXX sometime moov is hidden in a free box */
562
    if( p_box->p_father &&
563
        p_box->p_father->i_type == ATOM_root &&
564
        p_box->i_type == ATOM_free )
565
    {
566
        const uint8_t *p_peek;
567
        size_t header_size = mp4_box_headersize( p_box ) + 4;
568 569
        vlc_fourcc_t i_fcc;

570 571 572
        ssize_t i_read = vlc_stream_Peek( p_stream, &p_peek, 44 );
        if( unlikely(i_read < (ssize_t)header_size) )
            return 0;
573

574 575
        p_peek += header_size;
        i_read -= header_size;
576 577 578 579 580

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

581
            if( i_fcc == ATOM_cmov || i_fcc == ATOM_mvhd )
582
            {
583
                msg_Warn( p_stream, "detected moov hidden in a free box ..." );
584

585
                p_box->i_type = ATOM_foov;
586 587 588 589
                return MP4_ReadBoxContainer( p_stream, p_box );
            }
        }
    }
590

591
    /* Nothing to do */
592
#ifdef MP4_ULTRA_VERBOSE
593 594 595 596
    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 );
597
#endif
598
    return 1;
599 600
}

601 602
static int MP4_ReadBox_ilst( stream_t *p_stream, MP4_Box_t *p_box )
{
603
    if( p_box->i_size < 8 || vlc_stream_Read( p_stream, NULL, 8 ) < 8 )
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
        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;
626
    case HANDLER_mdta:
627
        return MP4_ReadBoxContainerChildrenIndexed( p_stream, p_box, NULL, NULL, true );
628
    case HANDLER_mdir:
629
        return MP4_ReadBoxContainerChildren( p_stream, p_box, NULL );
630 631 632 633 634 635
    default:
        msg_Warn( p_stream, "Unknown ilst handler type '%4.4s'", (char*)&p_box->i_handler );
        return 0;
    }
}

636 637 638 639 640
static void MP4_FreeBox_ftyp( MP4_Box_t *p_box )
{
    FREENULL( p_box->data.p_ftyp->i_compatible_brands );
}

641
static int MP4_ReadBox_ftyp( stream_t *p_stream, MP4_Box_t *p_box )
642
{
643
    MP4_READBOX_ENTER( MP4_Box_data_ftyp_t, MP4_FreeBox_ftyp );
644

645 646
    MP4_GETFOURCC( p_box->data.p_ftyp->i_major_brand );
    MP4_GET4BYTES( p_box->data.p_ftyp->i_minor_version );
647

648 649
    p_box->data.p_ftyp->i_compatible_brands_count = i_read / 4;
    if( p_box->data.p_ftyp->i_compatible_brands_count > 0 )
650
    {
651
        uint32_t *tab = p_box->data.p_ftyp->i_compatible_brands =
652 653
            vlc_alloc( p_box->data.p_ftyp->i_compatible_brands_count,
                       sizeof(uint32_t) );
654

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
655
        if( unlikely( tab == NULL ) )
656
            MP4_READBOX_EXIT( 0 );
657

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
658
        for( unsigned i = 0; i < p_box->data.p_ftyp->i_compatible_brands_count; i++ )
659
        {
660
            MP4_GETFOURCC( tab[i] );
661 662 663 664 665 666 667
        }
    }
    else
    {
        p_box->data.p_ftyp->i_compatible_brands = NULL;
    }

668
    MP4_READBOX_EXIT( 1 );
669 670 671
}


672
static int MP4_ReadBox_mvhd(  stream_t *p_stream, MP4_Box_t *p_box )
673 674 675 676 677 678
{
#ifdef MP4_VERBOSE
    char s_creation_time[128];
    char s_modification_time[128];
    char s_duration[128];
#endif
679
    MP4_READBOX_ENTER( MP4_Box_data_mvhd_t, NULL );
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696

    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 );
    }
697 698
    MP4_GET4BYTES( p_box->data.p_mvhd->i_rate );
    MP4_GET2BYTES( p_box->data.p_mvhd->i_volume );
699 700
    MP4_GET2BYTES( p_box->data.p_mvhd->i_reserved1 );

701

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
702
    for( unsigned i = 0; i < 2; i++ )
703 704 705
    {
        MP4_GET4BYTES( p_box->data.p_mvhd->i_reserved2[i] );
    }
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
706
    for( unsigned i = 0; i < 9; i++ )
707 708 709
    {
        MP4_GET4BYTES( p_box->data.p_mvhd->i_matrix[i] );
    }
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
710
    for( unsigned i = 0; i < 6; i++ )
711 712 713
    {
        MP4_GET4BYTES( p_box->data.p_mvhd->i_predefined[i] );
    }
714

715
    MP4_GET4BYTES( p_box->data.p_mvhd->i_next_track_id );
716 717


718
#ifdef MP4_VERBOSE
719
    MP4_ConvertDate2Str( s_creation_time, p_box->data.p_mvhd->i_creation_time, false );
720
    MP4_ConvertDate2Str( s_modification_time,
721
                         p_box->data.p_mvhd->i_modification_time, false );
722
    if( p_box->data.p_mvhd->i_rate && p_box->data.p_mvhd->i_timescale )
723
    {
724
        MP4_ConvertDate2Str( s_duration, p_box->data.p_mvhd->i_duration / p_box->data.p_mvhd->i_timescale, true );
725 726 727 728
    }
    else
    {
        s_duration[0] = 0;
729
    }
730
    msg_Dbg( p_stream, "read box: \"mvhd\" creation %s modification %s time scale %d duration %s rate %f volume %f next track id %d",
731 732
                  s_creation_time,
                  s_modification_time,
733
                  (uint32_t)p_box->data.p_mvhd->i_timescale,
734 735 736
                  s_duration,
                  (float)p_box->data.p_mvhd->i_rate / (1<<16 ),
                  (float)p_box->data.p_mvhd->i_volume / 256 ,
737
                  (uint32_t)p_box->data.p_mvhd->i_next_track_id );
738 739 740 741
#endif
    MP4_READBOX_EXIT( 1 );
}

742 743
static int MP4_ReadBox_mfhd(  stream_t *p_stream, MP4_Box_t *p_box )
{
744
    MP4_READBOX_ENTER( MP4_Box_data_mfhd_t, NULL );
745

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

748 749 750 751 752 753 754 755 756
    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 );
}

757 758
static int MP4_ReadBox_tfxd(  stream_t *p_stream, MP4_Box_t *p_box )
{
759
    MP4_READBOX_ENTER( MP4_Box_data_tfxd_t, NULL );
760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787

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

788 789 790 791 792
static void MP4_FreeBox_tfrf( MP4_Box_t *p_box )
{
    FREENULL( p_box->data.p_tfrf->p_tfrf_data_fields );
}

793 794
static int MP4_ReadBox_tfrf(  stream_t *p_stream, MP4_Box_t *p_box )
{
795
    MP4_READBOX_ENTER( MP4_Box_data_tfxd_t, MP4_FreeBox_tfrf );
796 797 798 799 800 801 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 830 831 832 833 834 835 836 837 838 839 840

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

841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861
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" ) )
862
    {
863
        msg_Dbg( p_stream, "Left-right stereo mode" );
864 865
        p_360_data->e_stereo_mode = XML360_STEREOSCOPIC_LEFT_RIGHT;
    }
866 867

    if ( strcasestr( psz_rdf, "top-bottom" ) )
868
    {
869
        msg_Dbg( p_stream, "Top-bottom stereo mode" );
870 871
        p_360_data->e_stereo_mode = XML360_STEREOSCOPIC_TOP_BOTTOM;
    }
872 873 874 875 876 877

    free( psz_rdf );

    MP4_READBOX_EXIT( 1 );
}

878 879 880 881
static int MP4_ReadBox_st3d( stream_t *p_stream, MP4_Box_t *p_box )
{
    MP4_READBOX_ENTER( MP4_Box_data_st3d_t, NULL );

882 883 884 885 886 887 888 889 890
    uint8_t i_version;
    MP4_GET1BYTE( i_version );
    if ( i_version != 0 )
        MP4_READBOX_EXIT( 0 );

    uint32_t i_flags;
    VLC_UNUSED( i_flags );
    MP4_GET3BYTES( i_flags );

891 892 893 894 895 896 897 898 899 900
    MP4_Box_data_st3d_t *p_data = p_box->data.p_st3d;
    MP4_GET1BYTE( p_data->i_stereo_mode );

    MP4_READBOX_EXIT( 1 );
}

static int MP4_ReadBox_prhd( stream_t *p_stream, MP4_Box_t *p_box )
{
    MP4_READBOX_ENTER( MP4_Box_data_prhd_t, NULL );

901 902 903 904 905 906 907 908 909
    uint8_t i_version;
    MP4_GET1BYTE( i_version );
    if (i_version != 0)
        MP4_READBOX_EXIT( 0 );

    uint32_t i_flags;
    VLC_UNUSED( i_flags );
    MP4_GET3BYTES( i_flags );

910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927
    MP4_Box_data_prhd_t *p_data = p_box->data.p_prhd;
    int32_t fixed16_16;
    MP4_GET4BYTES( fixed16_16 );
    p_data->f_pose_yaw_degrees   = (float) fixed16_16 / 65536.0f;

    MP4_GET4BYTES( fixed16_16 );
    p_data->f_pose_pitch_degrees = (float) fixed16_16 / 65536.0f;

    MP4_GET4BYTES( fixed16_16 );
    p_data->f_pose_roll_degrees  = (float) fixed16_16 / 65536.0f;

    MP4_READBOX_EXIT( 1 );
}

static int MP4_ReadBox_equi( stream_t *p_stream, MP4_Box_t *p_box )
{
    MP4_READBOX_ENTER( MP4_Box_data_equi_t, NULL );

928 929 930 931 932 933 934 935 936
    uint8_t i_version;
    MP4_GET1BYTE( i_version );
    if (i_version != 0)
        MP4_READBOX_EXIT( 0 );

    uint32_t i_flags;
    VLC_UNUSED( i_flags );
    MP4_GET3BYTES( i_flags );

937 938 939 940 941 942 943 944 945 946 947 948 949
    MP4_Box_data_equi_t *p_data = p_box->data.p_equi;
    MP4_GET4BYTES( p_data->i_projection_bounds_top );
    MP4_GET4BYTES( p_data->i_projection_bounds_bottom );
    MP4_GET4BYTES( p_data->i_projection_bounds_left );
    MP4_GET4BYTES( p_data->i_projection_bounds_right );

    MP4_READBOX_EXIT( 1 );
}

static int MP4_ReadBox_cbmp( stream_t *p_stream, MP4_Box_t *p_box )
{
    MP4_READBOX_ENTER( MP4_Box_data_cbmp_t, NULL );

950 951 952 953 954 955 956 957 958
    uint8_t i_version;
    MP4_GET1BYTE( i_version );
    if (i_version != 0)
        MP4_READBOX_EXIT( 0 );

    uint32_t i_flags;
    VLC_UNUSED( i_flags );
    MP4_GET3BYTES( i_flags );

959 960 961 962 963 964 965
    MP4_Box_data_cbmp_t *p_data = p_box->data.p_cbmp;
    MP4_GET4BYTES( p_data->i_layout );
    MP4_GET4BYTES( p_data->i_padding );

    MP4_READBOX_EXIT( 1 );
}

966
static void MP4_FreeBox_sidx( MP4_Box_t *p_box )
967
{
968
    FREENULL( p_box->data.p_sidx->p_items );
969 970
}

971 972
static int MP4_ReadBox_sidx(  stream_t *p_stream, MP4_Box_t *p_box )
{
973
    MP4_READBOX_ENTER( MP4_Box_data_sidx_t, MP4_FreeBox_sidx );
974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991

    MP4_Box_data_sidx_t *p_sidx_data = p_box->data.p_sidx;
    MP4_GETVERSIONFLAGS( p_sidx_data );

    MP4_GET4BYTES( p_sidx_data->i_reference_ID );
    MP4_GET4BYTES( p_sidx_data->i_timescale );

    if( p_sidx_data->i_version == 0 )
    {
        MP4_GET4BYTES( p_sidx_data->i_earliest_presentation_time );
        MP4_GET4BYTES( p_sidx_data->i_first_offset );
    }
    else
    {
        MP4_GET8BYTES( p_sidx_data->i_earliest_presentation_time );
        MP4_GET8BYTES( p_sidx_data->i_first_offset );
    }

992 993
    uint16_t i_reserved, i_count;

994
    VLC_UNUSED(i_reserved);
995
    MP4_GET2BYTES( i_reserved );
996
    MP4_GET2BYTES( i_count );
997 998
    if( i_count == 0 )
        MP4_READBOX_EXIT( 1 );
999 1000 1001 1002 1003

    p_sidx_data->i_reference_count = i_count;
    p_sidx_data->p_items = vlc_alloc( i_count, sizeof( MP4_Box_sidx_item_t ) );
    if( unlikely(p_sidx_data->p_items == NULL) )
        MP4_READBOX_EXIT( 0 );
1004 1005 1006

    for( unsigned i = 0; i < i_count; i++ )
    {
1007 1008 1009
        MP4_Box_sidx_item_t *item = p_sidx_data->p_items + i;
        uint32_t tmp;

1010
        MP4_GET4BYTES( tmp );
1011 1012 1013
        item->b_reference_type = tmp >> 31;
        item->i_referenced_size = tmp & 0x7fffffff;
        MP4_GET4BYTES( item->i_subsegment_duration );
1014 1015

        MP4_GET4BYTES( tmp );
1016 1017 1018
        item->b_starts_with_SAP = tmp >> 31;
        item->i_SAP_type = (tmp >> 24) & 0x70;
        item->i_SAP_delta_time = tmp & 0xfffffff;
1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036
    }

#ifdef MP4_VERBOSE
    msg_Dbg( p_stream, "read box: \"sidx\" version %d, flags 0x%x, "\
            "ref_ID %"PRIu32", timescale %"PRIu32", ref_count %"PRIu16", "\
            "first subsegmt duration %"PRIu32,
                p_sidx_data->i_version,
                p_sidx_data->i_flags,
                p_sidx_data->i_reference_ID,
                p_sidx_data->i_timescale,
                p_sidx_data->i_reference_count,
                p_sidx_data->p_items[0].i_subsegment_duration
           );
#endif

    MP4_READBOX_EXIT( 1 );
}

1037 1038
static int MP4_ReadBox_tfhd(  stream_t *p_stream, MP4_Box_t *p_box )
{
1039
    MP4_READBOX_ENTER( MP4_Box_data_tfhd_t, NULL );
1040 1041 1042

    MP4_GETVERSIONFLAGS( p_box->data.p_tfhd );

1043 1044 1045 1046 1047 1048 1049
    if( p_box->data.p_tfhd->i_version != 0 )
    {
        msg_Warn( p_stream, "'tfhd' box with version != 0. "\
                " Don't know what to do with that, please patch" );
        MP4_READBOX_EXIT( 0 );
    }

1050 1051
    MP4_GET4BYTES( p_box->data.p_tfhd->i_track_ID );

1052
    if( p_box->data.p_tfhd->i_flags & MP4_TFHD_DURATION_IS_EMPTY )
1053
    {
1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070
        msg_Dbg( p_stream, "'duration-is-empty' flag is present "\
                "=> no samples for this time interval." );
        p_box->data.p_tfhd->b_empty = true;
    }
    else
        p_box->data.p_tfhd->b_empty = false;

    if( p_box->data.p_tfhd->i_flags & MP4_TFHD_BASE_DATA_OFFSET )
        MP4_GET8BYTES( p_box->data.p_tfhd->i_base_data_offset );
    if( p_box->data.p_tfhd->i_flags & MP4_TFHD_SAMPLE_DESC_INDEX )
        MP4_GET4BYTES( p_box->data.p_tfhd->i_sample_description_index );
    if( p_box->data.p_tfhd->i_flags & MP4_TFHD_DFLT_SAMPLE_DURATION )
        MP4_GET4BYTES( p_box->data.p_tfhd->i_default_sample_duration );
    if( p_box->data.p_tfhd->i_flags & MP4_TFHD_DFLT_SAMPLE_SIZE )
        MP4_GET4BYTES( p_box->data.p_tfhd->i_default_sample_size );
    if( p_box->data.p_tfhd->i_flags & MP4_TFHD_DFLT_SAMPLE_FLAGS )
        MP4_GET4BYTES( p_box->data.p_tfhd->i_default_sample_flags );
1071 1072

#ifdef MP4_VERBOSE
1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093
    char psz_base[128] = "\0";
    char psz_desc[128] = "\0";
    char psz_dura[128] = "\0";
    char psz_size[128] = "\0";
    char psz_flag[128] = "\0";
    if( p_box->data.p_tfhd->i_flags & MP4_TFHD_BASE_DATA_OFFSET )
        snprintf(psz_base, sizeof(psz_base), "base offset %"PRId64, p_box->data.p_tfhd->i_base_data_offset);
    if( p_box->data.p_tfhd->i_flags & MP4_TFHD_SAMPLE_DESC_INDEX )
        snprintf(psz_desc, sizeof(psz_desc), "sample description index %d", p_box->data.p_tfhd->i_sample_description_index);
    if( p_box->data.p_tfhd->i_flags & MP4_TFHD_DFLT_SAMPLE_DURATION )
        snprintf(psz_dura, sizeof(psz_dura), "sample duration %d", p_box->data.p_tfhd->i_default_sample_duration);
    if( p_box->data.p_tfhd->i_flags & MP4_TFHD_DFLT_SAMPLE_SIZE )
        snprintf(psz_size, sizeof(psz_size), "sample size %d", p_box->data.p_tfhd->i_default_sample_size);
    if( p_box->data.p_tfhd->i_flags & MP4_TFHD_DFLT_SAMPLE_FLAGS )
        snprintf(psz_flag, sizeof(psz_flag), "sample flags 0x%x", p_box->data.p_tfhd->i_default_sample_flags);

    msg_Dbg( p_stream, "read box: \"tfhd\" version %d flags 0x%x track ID %d %s %s %s %s %s",
                p_box->data.p_tfhd->i_version,
                p_box->data.p_tfhd->i_flags,
                p_box->data.p_tfhd->i_track_ID,
                psz_base, psz_desc, psz_dura, psz_size, psz_flag );
1094 1095 1096 1097 1098
#endif

    MP4_READBOX_EXIT( 1 );
}

1099 1100 1101 1102 1103
static void MP4_FreeBox_trun( MP4_Box_t *p_box )
{
    FREENULL( p_box->data.p_trun->p_samples );
}

1104 1105
static int MP4_ReadBox_trun(  stream_t *p_stream, MP4_Box_t *p_box )
{
1106 1107
    uint32_t count;

1108
    MP4_READBOX_ENTER( MP4_Box_data_trun_t, MP4_FreeBox_trun );
1109 1110
    MP4_Box_data_trun_t *p_trun = p_box->data.p_trun;
    MP4_GETVERSIONFLAGS( p_trun );
1111
    MP4_GET4BYTES( count );
1112

1113 1114 1115 1116
    if( p_trun->i_flags & MP4_TRUN_DATA_OFFSET )
        MP4_GET4BYTES( p_trun->i_data_offset );
    if( p_trun->i_flags & MP4_TRUN_FIRST_FLAGS )
        MP4_GET4BYTES( p_trun->i_first_sample_flags );
1117

1118 1119 1120 1121 1122 1123 1124
    uint64_t i_entry_size =
        !!(p_trun->i_flags & MP4_TRUN_SAMPLE_DURATION) +
        !!(p_trun->i_flags & MP4_TRUN_SAMPLE_SIZE) +
        !!(p_trun->i_flags & MP4_TRUN_SAMPLE_FLAGS) +
        !!(p_trun->i_flags & MP4_TRUN_SAMPLE_TIME_OFFSET);

    if( i_entry_size * 4 * count > i_read )
1125 1126
        MP4_READBOX_EXIT( 0 );

1127 1128
    p_trun->p_samples = vlc_alloc( count, sizeof(MP4_descriptor_trun_sample_t) );
    if ( p_trun->p_samples == NULL )
1129
        MP4_READBOX_EXIT( 0 );
1130
    p_trun->i_sample_count = count;
1131

1132
    for( unsigned int i = 0; i < count; i++ )
1133
    {
1134 1135
        MP4_descriptor_trun_sample_t *p_sample = &p_trun->p_samples[i];
        if( p_trun->i_flags & MP4_TRUN_SAMPLE_DURATION )
1136
            MP4_GET4BYTES( p_sample->i_duration );
1137
        if( p_trun->i_flags & MP4_TRUN_SAMPLE_SIZE )
1138
            MP4_GET4BYTES( p_sample->i_size );
1139
        if( p_trun->i_flags & MP4_TRUN_SAMPLE_FLAGS )
1140
            MP4_GET4BYTES( p_sample->i_flags );
1141
        if( p_trun->i_flags & MP4_TRUN_SAMPLE_TIME_OFFSET )
1142
            MP4_GET4BYTES( p_sample->i_composition_time_offset.v0 );
1143 1144
    }

1145
#ifdef MP4_ULTRA_VERBOSE
1146
    msg_Dbg( p_stream, "read box: \"trun\" version %u flags 0x%x sample count %"PRIu32,
1147 1148 1149
                  p_trun->i_version,
                  p_trun->i_flags,
                  p_trun->i_sample_count );
1150

1151
    for( unsigned int i = 0; i < count; i++ )
1152
    {
1153
        MP4_descriptor_trun_sample_t *p_sample = &p_trun->p_samples[i];
1154 1155
        msg_Dbg( p_stream, "read box: \"trun\" sample %4.4u flags 0x%x "\
            "duration %"PRIu32" size %"PRIu32" composition time offset %"PRIu32,
1156 1157 1158 1159 1160 1161 1162 1163
                        i, p_sample->i_flags, p_sample->i_duration,
                        p_sample->i_size, p_sample->i_composition_time_offset );
    }
#endif

    MP4_READBOX_EXIT( 1 );
}

1164 1165
static int MP4_ReadBox_tfdt( stream_t *p_stream, MP4_Box_t *p_box )
{
1166
    MP4_READBOX_ENTER( MP4_Box_data_tfdt_t, NULL );
1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180
    if( i_read < 8 )
        MP4_READBOX_EXIT( 0 );

    MP4_GETVERSIONFLAGS( p_box->data.p_tfdt );

    if( p_box->data.p_tfdt->i_version == 0 )
        MP4_GET4BYTES( p_box->data.p_tfdt->i_base_media_decode_time );
    else if( p_box->data.p_tfdt->i_version == 1 )
        MP4_GET8BYTES( p_box->data.p_tfdt->i_base_media_decode_time );
    else
        MP4_READBOX_EXIT( 0 );

    MP4_READBOX_EXIT( 1 );
}
1181

1182
static int MP4_ReadBox_tkhd(  stream_t *p_stream, MP4_Box_t *p_box )
1183 1184 1185 1186 1187 1188
{
#ifdef MP4_VERBOSE
    char s_creation_time[128];
    char s_modification_time[128];
    char s_duration[128];
#endif
1189
    MP4_READBOX_ENTER( MP4_Box_data_tkhd_t, NULL );
1190

1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208
    MP4_GETVERSIONFLAGS( p_box->data.p_tkhd );

    if( p_box->data.p_tkhd->i_version )
    {
        MP4_GET8BYTES( p_box->data.p_tkhd->i_creation_time );
        MP4_GET8BYTES( p_box->data.p_tkhd->i_modification_time );
        MP4_GET4BYTES( p_box->data.p_tkhd->i_track_ID );
        MP4_GET4BYTES( p_box->data.p_tkhd->i_reserved );
        MP4_GET8BYTES( p_box->data.p_tkhd->i_duration );
    }
    else
    {
        MP4_GET4BYTES( p_box->data.p_tkhd->i_creation_time );
        MP4_GET4BYTES( p_box->data.p_tkhd->i_modification_time );
        MP4_GET4BYTES( p_box->data.p_tkhd->i_track_ID );
        MP4_GET4BYTES( p_box->data.p_tkhd->i_reserved );
        MP4_GET4BYTES( p_box->data.p_tkhd->i_duration );
    }
1209

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
1210
    for( unsigned i = 0; i < 2; i++ )
1211 1212 1213 1214 1215 1216 1217 1218
    {
        MP4_GET4BYTES( p_box->data.p_tkhd->i_reserved2[i] );
    }
    MP4_GET2BYTES( p_box->data.p_tkhd->i_layer );
    MP4_GET2BYTES( p_box->data.p_tkhd->i_predefined );
    MP4_GET2BYTES( p_box->data.p_tkhd->i_volume );
    MP4_GET2BYTES( p_box->data.p_tkhd->i_reserved3 );

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
1219
    for( unsigned i = 0; i < 9; i++ )
1220 1221 1222 1223 1224
    {
        MP4_GET4BYTES( p_box->data.p_tkhd->i_matrix[i] );
    }
    MP4_GET4BYTES( p_box->data.p_tkhd->i_width );
    MP4_GET4BYTES( p_box->data.p_tkhd->i_height );
1225

1226
    double rotation = 0;//angle in degrees to be rotated clockwise
1227
    double scale[2];    // scale factor; sx = scale[0] , sy = scale[1]
1228
    int32_t *matrix = p_box->data.p_tkhd->i_matrix;
1229

1230 1231 1232 1233
    scale[0] = sqrt(conv_fx(matrix[0]) * conv_fx(matrix[0]) +
                    conv_fx(matrix[3]) * conv_fx(matrix[3]));
    scale[1] = sqrt(conv_fx(matrix[1]) * conv_fx(matrix[1]) +
                    conv_fx(matrix[4]) * conv_fx(matrix[4]));
1234

1235 1236 1237 1238 1239 1240 1241
    if( likely(scale[0] > 0 && scale[1] > 0) )
    {
        rotation = atan2(conv_fx(matrix[1]) / scale[1],
                         conv_fx(matrix[0]) / scale[0]) * 180 / M_PI;
        if (rotation < 0)
            rotation += 360.;
    }
1242

1243 1244
    p_box->data.p_tkhd->f_rotation = rotation;

1245
#ifdef MP4_VERBOSE
1246 1247 1248 1249 1250
    double translate[2];// amount to translate; tx = translate[0] , ty = translate[1]

    translate[0] = conv_fx(matrix[6]);
    translate[1] = conv_fx(matrix[7]);

1251 1252 1253
    MP4_ConvertDate2Str( s_creation_time, p_box->data.p_mvhd->i_creation_time, false );
    MP4_ConvertDate2Str( s_modification_time, p_box->data.p_mvhd->i_modification_time, false );
    MP4_ConvertDate2Str( s_duration, p_box->data.p_mvhd->i_duration, true );
1254

1255
    msg_Dbg( p_stream, "read box: \"tkhd\" creation %s modification %s duration %s track ID %d layer %d volume %f rotation %f scaleX %f scaleY %f translateX %f translateY %f width %f height %f. "
1256
            "Matrix: %i %i %i %i %i %i %i %i %i",
1257 1258 1259 1260 1261 1262
                  s_creation_time,
                  s_modification_time,
                  s_duration,
                  p_box->data.p_tkhd->i_track_ID,
                  p_box->data.p_tkhd->i_layer,
                  (float)p_box->data.p_tkhd->i_volume / 256 ,
1263 1264 1265 1266 1267
                  rotation,
                  scale[0],
                  scale[1],
                  translate[0],
                  translate[1],
1268 1269
                  (float)p_box->data.p_tkhd->i_width / BLOCK16x16,
                  (float)p_box->data.p_tkhd->i_height / BLOCK16x16,
1270 1271 1272 1273 1274 1275 1276 1277 1278
                  p_box->data.p_tkhd->i_matrix[0],
                  p_box->data.p_tkhd->i_matrix[1],
                  p_box->data.p_tkhd->i_matrix[2],
                  p_box->data.p_tkhd->i_matrix[3],
                  p_box->data.p_tkhd->i_matrix[4],
                  p_box->data.p_tkhd->i_matrix[5],
                  p_box->data.p_tkhd->i_matrix[6],
                  p_box->data.p_tkhd->i_matrix[7],
                  p_box->data.p_tkhd->i_matrix[8] );
1279 1280 1281 1282
#endif
    MP4_READBOX_EXIT( 1 );
}

1283 1284 1285 1286
static int MP4_ReadBox_load( stream_t *p_stream, MP4_Box_t *p_box )
{
    if ( p_box->i_size != 24 )
        return 0;
1287
    MP4_READBOX_ENTER( MP4_Box_data_load_t, NULL );
1288 1289 1290 1291 1292 1293
    MP4_GET4BYTES( p_box->data.p_load->i_start_time );
    MP4_GET4BYTES( p_box->data.p_load->i_duration );
    MP4_GET4BYTES( p_box->data.p_load->i_flags );
    MP4_GET4BYTES( p_box->data.p_load->i_hints );
    MP4_READBOX_EXIT( 1 );
}
1294

1295
static int MP4_ReadBox_mdhd( stream_t *p_stream, MP4_Box_t *p_box )
1296
{
1297
    uint16_t i_language;
1298 1299 1300 1301 1302
#ifdef MP4_VERBOSE
    char s_creation_time[128];
    char s_modification_time[128];
    char s_duration[128];
#endif
1303
    MP4_READBOX_ENTER( MP4_Box_data_mdhd_t, NULL );
1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320

    MP4_GETVERSIONFLAGS( p_box->data.p_mdhd );

    if( p_box->data.p_mdhd->i_version )
    {
        MP4_GET8BYTES( p_box->data.p_mdhd->i_creation_time );
        MP4_GET8BYTES( p_box->data.p_mdhd->i_modification_time );
        MP4_GET4BYTES( p_box->data.p_mdhd->i_timescale );
        MP4_GET8BYTES( p_box->data.p_mdhd->i_duration );
    }
    else
    {
        MP4_GET4BYTES( p_box->data.p_mdhd->i_creation_time );
        MP4_GET4BYTES( p_box->data.p_mdhd->i_modification_time );
        MP4_GET4BYTES( p_box->data.p_mdhd->i_timescale );
        MP4_GET4BYTES( p_box->data.p_mdhd->i_duration );
    }
1321 1322

    MP4_GET2BYTES( i_language );
1323 1324
    decodeQtLanguageCode( i_language, p_box->data.p_mdhd->rgs_language,
                          &p_box->data.p_mdhd->b_mac_encoding );
1325

1326
    MP4_GET2BYTES( p_box->data.p_mdhd->i_quality );
1327

1328
#ifdef MP4_VERBOSE
1329 1330 1331
    MP4_ConvertDate2Str( s_creation_time, p_box->data.p_mdhd->i_creation_time, false );
    MP4_ConvertDate2Str( s_modification_time, p_box->data.p_mdhd->i_modification_time, false );
    MP4_ConvertDate2Str( s_duration, p_box->data.p_mdhd->i_duration, true );
1332
    msg_Dbg( p_stream, "read box: \"mdhd\" creation %s modification %s time scale %d duration %s language %3.3s",
1333 1334
                  s_creation_time,
                  s_modification_time,
1335
                  (uint32_t)p_box->data.p_mdhd->i_timescale,
1336
                  s_duration,
1337
                  (char*) &p_box->data.p_mdhd->rgs_language );
1338 1339 1340 1341
#endif
    MP4_READBOX_EXIT( 1 );
}

1342 1343 1344 1345
static void MP4_FreeBox_hdlr( MP4_Box_t *p_box )
{
    FREENULL( p_box->data.p_hdlr->psz_name );
}
1346

1347
static int MP4_ReadBox_hdlr( stream_t *p_stream, MP4_Box_t *p_box )
Sam Hocevar's avatar