libmp4.c 92.7 KB
Newer Older
1
2
3
/*****************************************************************************
 * libmp4.c : LibMP4 library for mp4 module for vlc
 *****************************************************************************
4
 * Copyright (C) 2001-2004 the VideoLAN team
5
 * $Id$
6
7
 *
 * Author: Laurent Aimar <fenrir@via.ecp.fr>
8
 *
9
10
11
12
 * 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.
13
 *
14
15
16
17
18
19
20
 * 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
dionoea's avatar
dionoea committed
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22
 *****************************************************************************/
23
24
25
26
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
27
28
#include <vlc/vlc.h>

29

zorglub's avatar
zorglub committed
30
#include <vlc_demux.h>
31

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

36
#include "libmp4.h"
37
#include "drms.h"
38
39

/*****************************************************************************
40
41
 * Here are defined some macro to make life simpler but before using it
 *  *look* at the code.
42
43
 *
 *****************************************************************************/
44
45
#define MP4_BOX_HEADERSIZE( p_box )             \
  ( 8 + ( p_box->i_shortsize == 1 ? 8 : 0 )     \
46
      + ( p_box->i_type == FOURCC_uuid ? 16 : 0 ) )
47

48
49
50
51
52
#define MP4_GETX_PRIVATE(dst, code, size) do { \
    if( (i_read) >= (size) ) { dst = (code); p_peek += (size); } \
    else { dst = 0; }   \
    i_read -= (size);   \
  } while(0)
53

54
55
56
57
58
59
60
#define MP4_GET1BYTE( dst )  MP4_GETX_PRIVATE( dst, *p_peek, 1 )
#define MP4_GET2BYTES( dst ) MP4_GETX_PRIVATE( dst, GetWBE(p_peek), 2 )
#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)
61

62
63
64
#define MP4_GETVERSIONFLAGS( p_void ) \
    MP4_GET1BYTE( p_void->i_version ); \
    MP4_GET3BYTES( p_void->i_flags )
65

66
67
68
69
70
71
72
73
74
75
76
77
#define MP4_GETSTRINGZ( p_str )         \
    if( (i_read > 0) && (p_peek[0]) )   \
    {       \
        const int __i_copy__ = strnlen( (char*)p_peek, i_read-1 );  \
        p_str = calloc( sizeof(char), __i_copy__+1 );               \
        if( __i_copy__ > 0 ) memcpy( p_str, p_peek, __i_copy__ );   \
        p_str[__i_copy__] = 0;      \
        p_peek += __i_copy__ + 1;   \
        i_read -= __i_copy__ + 1;   \
    }       \
    else    \
    {       \
78
79
        p_str = NULL; \
    }
80

81
#define MP4_READBOX_ENTER( MP4_Box_data_TYPE_t ) \
zorglub's avatar
zorglub committed
82
    int64_t  i_read = p_box->i_size; \
83
    uint8_t *p_peek, *p_buff; \
zorglub's avatar
zorglub committed
84
    int i_actually_read; \
85
86
87
88
    if( !( p_peek = p_buff = malloc( i_read ) ) ) \
    { \
        return( 0 ); \
    } \
zorglub's avatar
zorglub committed
89
    i_actually_read = stream_Read( p_stream, p_peek, i_read ); \
zorglub's avatar
zorglub committed
90
    if( i_actually_read < 0 || (int64_t)i_actually_read < i_read )\
91
92
93
94
95
96
97
98
99
100
    { \
        free( p_buff ); \
        return( 0 ); \
    } \
    p_peek += MP4_BOX_HEADERSIZE( p_box ); \
    i_read -= MP4_BOX_HEADERSIZE( p_box ); \
    if( !( p_box->data.p_data = malloc( sizeof( MP4_Box_data_TYPE_t ) ) ) ) \
    { \
      free( p_buff ); \
      return( 0 ); \
101
102
    }

103
104
105
106
#define MP4_READBOX_EXIT( i_code ) \
    free( p_buff ); \
    if( i_read < 0 ) \
    { \
sigmunau's avatar
sigmunau committed
107
        msg_Warn( p_stream, "Not enough data" ); \
108
109
110
    } \
    return( i_code )

111

112
/* Some assumptions:
113
114
        * The input method HAVE to be seekable

115
116
*/

117
118
119
120
121
122
123
/* 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 )

124
static uint32_t Get24bBE( const uint8_t *p )
125
{
Laurent Aimar's avatar
Laurent Aimar committed
126
    return( ( p[0] <<16 ) + ( p[1] <<8 ) + p[2] );
127
128
}

129
static void GetUUID( UUID_t *p_uuid, const uint8_t *p_buff )
130
{
Laurent Aimar's avatar
Laurent Aimar committed
131
    memcpy( p_uuid, p_buff, 16 );
132
133
}

134
static void CreateUUID( UUID_t *p_uuid, uint32_t i_fourcc )
135
{
136
    /* made by 0xXXXXXXXX-0011-0010-8000-00aa00389b71
137
138
139
140
141
142
            where XXXXXXXX is the fourcc */
    /* FIXME implement this */
}

/* some functions for mp4 encoding of variables */

Laurent Aimar's avatar
Laurent Aimar committed
143
static void MP4_ConvertDate2Str( char *psz, uint64_t i_date )
144
145
146
147
148
149
{
    int i_day;
    int i_hour;
    int i_min;
    int i_sec;

Laurent Aimar's avatar
Laurent Aimar committed
150
    /* date begin at 1 jan 1904 */
151
    i_date += ((I64C(1904) * 365) + 17) * 24 * 60 * 60;
Laurent Aimar's avatar
Laurent Aimar committed
152

153
154
155
156
    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;
157
    sprintf( psz, "%dd-%2.2dh:%2.2dm:%2.2ds", i_day, i_hour, i_min, i_sec );
158
159
}

Laurent Aimar's avatar
Laurent Aimar committed
160
161
162
/*****************************************************************************
 * Some prototypes.
 *****************************************************************************/
sigmunau's avatar
sigmunau committed
163
static MP4_Box_t *MP4_ReadBox( stream_t *p_stream, MP4_Box_t *p_father );
164
165
166


/*****************************************************************************
167
 * MP4_ReadBoxCommon : Load only common parameters for all boxes
168
 *****************************************************************************
169
 * p_box need to be an already allocated MP4_Box_t, and all data
170
171
172
173
 *  will only be peek not read
 *
 * RETURN : 0 if it fail, 1 otherwise
 *****************************************************************************/
sigmunau's avatar
sigmunau committed
174
int MP4_ReadBoxCommon( stream_t *p_stream, MP4_Box_t *p_box )
175
{
176
    int      i_read;
177
    const uint8_t  *p_peek;
178

sigmunau's avatar
sigmunau committed
179
    if( ( ( i_read = stream_Peek( p_stream, &p_peek, 32 ) ) < 8 ) )
180
    {
181
        return 0;
182
    }
sigmunau's avatar
sigmunau committed
183
    p_box->i_pos = stream_Tell( p_stream );
184
185
186
187
188
189

    p_box->data.p_data = NULL;
    p_box->p_father = NULL;
    p_box->p_first  = NULL;
    p_box->p_last  = NULL;
    p_box->p_next   = NULL;
190

191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
    MP4_GET4BYTES( p_box->i_shortsize );
    MP4_GETFOURCC( p_box->i_type );

    /* Now special case */

    if( p_box->i_shortsize == 1 )
    {
        /* 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 */
    }
206

207
208
209
210
211
212
213
214
215
216
217
218
219
    if( p_box->i_type == FOURCC_uuid )
    {
        /* get extented type on 16 bytes */
        GetUUID( &p_box->i_uuid, p_peek );
        p_peek += 16; i_read -= 16;
    }
    else
    {
        CreateUUID( &p_box->i_uuid, p_box->i_type );
    }
#ifdef MP4_VERBOSE
    if( p_box->i_size )
    {
220
221
222
223
224
225
        if MP4_BOX_TYPE_ASCII()
            msg_Dbg( p_stream, "found Box: %4.4s size "I64Fd,
                    (char*)&p_box->i_type, p_box->i_size );
        else
            msg_Dbg( p_stream, "found Box: c%3.3s size "I64Fd,
                    (char*)&p_box->i_type+1, p_box->i_size );
226
227
228
    }
#endif

229
    return 1;
230
231
232
}

/*****************************************************************************
233
 * MP4_NextBox : Go to the next box
234
 *****************************************************************************
235
 * if p_box == NULL, go to the next box in which we are( at the begining ).
236
 *****************************************************************************/
sigmunau's avatar
sigmunau committed
237
static int MP4_NextBox( stream_t *p_stream, MP4_Box_t *p_box )
238
239
240
241
242
243
244
245
246
247
248
{
    MP4_Box_t box;

    if( !p_box )
    {
        MP4_ReadBoxCommon( p_stream, &box );
        p_box = &box;
    }

    if( !p_box->i_size )
    {
249
        return 2; /* Box with infinite size */
250
251
252
253
    }

    if( p_box->p_father )
    {
254
255
256
        const int i_box_end = p_box->i_size + p_box->i_pos;
        const int i_father_end = p_box->p_father->i_size + p_box->p_father->i_pos;

257
        /* check if it's within p-father */
258
        if( i_box_end >= i_father_end )
259
        {
260
261
            if( i_box_end > i_father_end )
                msg_Dbg( p_stream, "out of bound child" );
262
            return 0; /* out of bound */
263
264
        }
    }
sigmunau's avatar
sigmunau committed
265
    if( stream_Seek( p_stream, p_box->i_size + p_box->i_pos ) )
266
267
268
269
270
    {
        return 0;
    }

    return 1;
271
272
273
}

/*****************************************************************************
274
 * For all known box a loader is given,
275
276
 *  XXX: all common struct have to be already read by MP4_ReadBoxCommon
 *       after called one of theses functions, file position is unknown
277
 *       you need to call MP4_GotoBox to go where you want
278
 *****************************************************************************/
sigmunau's avatar
sigmunau committed
279
static int MP4_ReadBoxContainerRaw( stream_t *p_stream, MP4_Box_t *p_container )
280
281
{
    MP4_Box_t *p_box;
282

sigmunau's avatar
sigmunau committed
283
    if( stream_Tell( p_stream ) + 8 >
284
        (off_t)(p_container->i_pos + p_container->i_size) )
285
286
    {
        /* there is no box to load */
287
        return 0;
288
    }
289

290
291
    do
    {
292
293
        if( ( p_box = MP4_ReadBox( p_stream, p_container ) ) == NULL ) break;

Laurent Aimar's avatar
Laurent Aimar committed
294
        /* chain this box with the father and the other at same level */
295
296
        if( !p_container->p_first ) p_container->p_first = p_box;
        else p_container->p_last->p_next = p_box;
Laurent Aimar's avatar
Laurent Aimar committed
297
        p_container->p_last = p_box;
298

Laurent Aimar's avatar
Laurent Aimar committed
299
    } while( MP4_NextBox( p_stream, p_box ) == 1 );
300

301
    return 1;
302
303
}

sigmunau's avatar
sigmunau committed
304
static int MP4_ReadBoxContainer( stream_t *p_stream, MP4_Box_t *p_container )
305
{
306
    if( p_container->i_size <= (size_t)MP4_BOX_HEADERSIZE(p_container ) + 8 )
307
308
    {
        /* container is empty, 8 stand for the first header in this box */
309
        return 1;
310
    }
311

312
    /* enter box */
313
314
    stream_Seek( p_stream, p_container->i_pos +
                 MP4_BOX_HEADERSIZE( p_container ) );
315

316
    return MP4_ReadBoxContainerRaw( p_stream, p_container );
317
318
}

319
static void MP4_FreeBox_Common( MP4_Box_t *p_box )
320
321
322
323
{
    /* Up to now do nothing */
}

sigmunau's avatar
sigmunau committed
324
static int MP4_ReadBoxSkip( stream_t *p_stream, MP4_Box_t *p_box )
325
{
326
    /* XXX sometime moov is hiden in a free box */
327
328
    if( p_box->p_father &&
        p_box->p_father->i_type == VLC_FOURCC( 'r', 'o', 'o', 't' ) &&
329
330
        p_box->i_type == FOURCC_free )
    {
331
        const uint8_t *p_peek;
332
333
334
        int     i_read;
        vlc_fourcc_t i_fcc;

sigmunau's avatar
sigmunau committed
335
        i_read  = stream_Peek( p_stream, &p_peek, 44 );
336
337
338
339
340
341
342
343
344
345

        p_peek += MP4_BOX_HEADERSIZE( p_box ) + 4;
        i_read -= MP4_BOX_HEADERSIZE( p_box ) + 4;

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

            if( i_fcc == FOURCC_cmov || i_fcc == FOURCC_mvhd )
            {
sigmunau's avatar
sigmunau committed
346
                msg_Warn( p_stream, "detected moov hidden in a free box ..." );
347
348
349
350
351
352

                p_box->i_type = FOURCC_foov;
                return MP4_ReadBoxContainer( p_stream, p_box );
            }
        }
    }
353

354
355
    /* Nothing to do */
#ifdef MP4_VERBOSE
356
357
358
359
    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 );
360
#endif
361
    return 1;
362
363
}

sigmunau's avatar
sigmunau committed
364
static int MP4_ReadBox_ftyp( stream_t *p_stream, MP4_Box_t *p_box )
365
366
{
    MP4_READBOX_ENTER( MP4_Box_data_ftyp_t );
367

368
369
    MP4_GETFOURCC( p_box->data.p_ftyp->i_major_brand );
    MP4_GET4BYTES( p_box->data.p_ftyp->i_minor_version );
370

371
372
    if( ( p_box->data.p_ftyp->i_compatible_brands_count = i_read / 4 ) )
    {
373
        unsigned int i;
374
        p_box->data.p_ftyp->i_compatible_brands =
375
            calloc( p_box->data.p_ftyp->i_compatible_brands_count, sizeof(uint32_t));
376
377
378
379
380
381
382
383
384
385
386
387
388
389

        for( i =0; i < p_box->data.p_ftyp->i_compatible_brands_count; i++ )
        {
            MP4_GETFOURCC( p_box->data.p_ftyp->i_compatible_brands[i] );
        }
    }
    else
    {
        p_box->data.p_ftyp->i_compatible_brands = NULL;
    }

    MP4_READBOX_EXIT( 1 );
}

390
static void MP4_FreeBox_ftyp( MP4_Box_t *p_box )
391
{
zorglub's avatar
zorglub committed
392
    FREENULL( p_box->data.p_ftyp->i_compatible_brands );
393
394
395
}


sigmunau's avatar
sigmunau committed
396
static int MP4_ReadBox_mvhd(  stream_t *p_stream, MP4_Box_t *p_box )
397
{
398
    unsigned int i;
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
#ifdef MP4_VERBOSE
    char s_creation_time[128];
    char s_modification_time[128];
    char s_duration[128];
#endif
    MP4_READBOX_ENTER( MP4_Box_data_mvhd_t );

    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 );
    }
422
423
    MP4_GET4BYTES( p_box->data.p_mvhd->i_rate );
    MP4_GET2BYTES( p_box->data.p_mvhd->i_volume );
424
425
    MP4_GET2BYTES( p_box->data.p_mvhd->i_reserved1 );

426

427
428
429
430
431
432
433
434
435
436
437
438
    for( i = 0; i < 2; i++ )
    {
        MP4_GET4BYTES( p_box->data.p_mvhd->i_reserved2[i] );
    }
    for( i = 0; i < 9; i++ )
    {
        MP4_GET4BYTES( p_box->data.p_mvhd->i_matrix[i] );
    }
    for( i = 0; i < 6; i++ )
    {
        MP4_GET4BYTES( p_box->data.p_mvhd->i_predefined[i] );
    }
439

440
    MP4_GET4BYTES( p_box->data.p_mvhd->i_next_track_id );
441
442


443
444
#ifdef MP4_VERBOSE
    MP4_ConvertDate2Str( s_creation_time, p_box->data.p_mvhd->i_creation_time );
445
    MP4_ConvertDate2Str( s_modification_time,
446
447
448
                         p_box->data.p_mvhd->i_modification_time );
    if( p_box->data.p_mvhd->i_rate )
    {
449
        MP4_ConvertDate2Str( s_duration,
450
451
452
453
454
                 p_box->data.p_mvhd->i_duration / p_box->data.p_mvhd->i_rate );
    }
    else
    {
        s_duration[0] = 0;
455
    }
sigmunau's avatar
sigmunau committed
456
    msg_Dbg( p_stream, "read box: \"mvhd\" creation %s modification %s time scale %d duration %s rate %f volume %f next track id %d",
457
458
                  s_creation_time,
                  s_modification_time,
459
                  (uint32_t)p_box->data.p_mvhd->i_timescale,
460
461
462
                  s_duration,
                  (float)p_box->data.p_mvhd->i_rate / (1<<16 ),
                  (float)p_box->data.p_mvhd->i_volume / 256 ,
463
                  (uint32_t)p_box->data.p_mvhd->i_next_track_id );
464
465
466
467
#endif
    MP4_READBOX_EXIT( 1 );
}

sigmunau's avatar
sigmunau committed
468
static int MP4_ReadBox_tkhd(  stream_t *p_stream, MP4_Box_t *p_box )
469
{
470
    unsigned int i;
471
472
473
474
475
476
#ifdef MP4_VERBOSE
    char s_creation_time[128];
    char s_modification_time[128];
    char s_duration[128];
#endif
    MP4_READBOX_ENTER( MP4_Box_data_tkhd_t );
477

478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
    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 );
    }
496

497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
    for( i = 0; i < 2; i++ )
    {
        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 );

    for( i = 0; i < 9; i++ )
    {
        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 );
512

513
514
515
516
#ifdef MP4_VERBOSE
    MP4_ConvertDate2Str( s_creation_time, p_box->data.p_mvhd->i_creation_time );
    MP4_ConvertDate2Str( s_modification_time, p_box->data.p_mvhd->i_modification_time );
    MP4_ConvertDate2Str( s_duration, p_box->data.p_mvhd->i_duration );
517

sigmunau's avatar
sigmunau committed
518
    msg_Dbg( p_stream, "read box: \"tkhd\" creation %s modification %s duration %s track ID %d layer %d volume %f width %f height %f",
519
520
521
522
523
524
525
526
527
528
529
530
531
                  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 ,
                  (float)p_box->data.p_tkhd->i_width / 65536,
                  (float)p_box->data.p_tkhd->i_height / 65536 );
#endif
    MP4_READBOX_EXIT( 1 );
}


sigmunau's avatar
sigmunau committed
532
static int MP4_ReadBox_mdhd( stream_t *p_stream, MP4_Box_t *p_box )
533
{
534
    unsigned int i;
535
    uint16_t i_language;
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
#ifdef MP4_VERBOSE
    char s_creation_time[128];
    char s_modification_time[128];
    char s_duration[128];
#endif
    MP4_READBOX_ENTER( MP4_Box_data_mdhd_t );

    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 );
    }
559
    i_language = GetWBE( p_peek );
560
561
    for( i = 0; i < 3; i++ )
    {
562
        p_box->data.p_mdhd->i_language[i] =
563
564
565
566
                    ( ( i_language >> ( (2-i)*5 ) )&0x1f ) + 0x60;
    }

    MP4_GET2BYTES( p_box->data.p_mdhd->i_predefined );
567

568
569
570
571
#ifdef MP4_VERBOSE
    MP4_ConvertDate2Str( s_creation_time, p_box->data.p_mdhd->i_creation_time );
    MP4_ConvertDate2Str( s_modification_time, p_box->data.p_mdhd->i_modification_time );
    MP4_ConvertDate2Str( s_duration, p_box->data.p_mdhd->i_duration );
sigmunau's avatar
sigmunau committed
572
    msg_Dbg( p_stream, "read box: \"mdhd\" creation %s modification %s time scale %d duration %s language %c%c%c",
573
574
                  s_creation_time,
                  s_modification_time,
575
                  (uint32_t)p_box->data.p_mdhd->i_timescale,
576
577
578
579
580
581
582
583
584
                  s_duration,
                  p_box->data.p_mdhd->i_language[0],
                  p_box->data.p_mdhd->i_language[1],
                  p_box->data.p_mdhd->i_language[2] );
#endif
    MP4_READBOX_EXIT( 1 );
}


sigmunau's avatar
sigmunau committed
585
static int MP4_ReadBox_hdlr( stream_t *p_stream, MP4_Box_t *p_box )
586
{
587
588
    int32_t i_reserved;

589
    MP4_READBOX_ENTER( MP4_Box_data_hdlr_t );
590
591

    MP4_GETVERSIONFLAGS( p_box->data.p_hdlr );
592

593
    MP4_GETFOURCC( p_box->data.p_hdlr->i_predefined );
594
595
    MP4_GETFOURCC( p_box->data.p_hdlr->i_handler_type );

596
597
598
    MP4_GET4BYTES( i_reserved );
    MP4_GET4BYTES( i_reserved );
    MP4_GET4BYTES( i_reserved );
599
    p_box->data.p_hdlr->psz_name = NULL;
600

601
    if( i_read > 0 )
602
    {
603
        p_box->data.p_hdlr->psz_name = calloc( sizeof( char ), i_read + 1 );
604

605
606
607
608
609
        /* Yes, I love .mp4 :( */
        if( p_box->data.p_hdlr->i_predefined == VLC_FOURCC( 'm', 'h', 'l', 'r' ) )
        {
            uint8_t i_len;
            int i_copy;
610

611
612
            MP4_GET1BYTE( i_len );
            i_copy = __MIN( i_read, i_len );
613

614
615
616
617
618
619
620
621
622
            memcpy( p_box->data.p_hdlr->psz_name, p_peek, i_copy );
            p_box->data.p_hdlr->psz_name[i_copy] = '\0';
        }
        else
        {
            memcpy( p_box->data.p_hdlr->psz_name, p_peek, i_read );
            p_box->data.p_hdlr->psz_name[i_read] = '\0';
        }
    }
623
624

#ifdef MP4_VERBOSE
625
626
627
        msg_Dbg( p_stream, "read box: \"hdlr\" handler type %4.4s name %s",
                   (char*)&p_box->data.p_hdlr->i_handler_type,
                   p_box->data.p_hdlr->psz_name );
628
629
630
631
632

#endif
    MP4_READBOX_EXIT( 1 );
}

633
static void MP4_FreeBox_hdlr( MP4_Box_t *p_box )
634
{
zorglub's avatar
zorglub committed
635
    FREENULL( p_box->data.p_hdlr->psz_name );
636
637
}

sigmunau's avatar
sigmunau committed
638
static int MP4_ReadBox_vmhd( stream_t *p_stream, MP4_Box_t *p_box )
639
{
640
    unsigned int i;
641
642

    MP4_READBOX_ENTER( MP4_Box_data_vmhd_t );
643
644

    MP4_GETVERSIONFLAGS( p_box->data.p_vmhd );
645

646
647
648
649
650
    MP4_GET2BYTES( p_box->data.p_vmhd->i_graphics_mode );
    for( i = 0; i < 3; i++ )
    {
        MP4_GET2BYTES( p_box->data.p_vmhd->i_opcolor[i] );
    }
651

652
#ifdef MP4_VERBOSE
sigmunau's avatar
sigmunau committed
653
    msg_Dbg( p_stream, "read box: \"vmhd\" graphics-mode %d opcolor (%d, %d, %d)",
654
655
656
657
658
659
660
661
                      p_box->data.p_vmhd->i_graphics_mode,
                      p_box->data.p_vmhd->i_opcolor[0],
                      p_box->data.p_vmhd->i_opcolor[1],
                      p_box->data.p_vmhd->i_opcolor[2] );
#endif
    MP4_READBOX_EXIT( 1 );
}

sigmunau's avatar
sigmunau committed
662
static int MP4_ReadBox_smhd( stream_t *p_stream, MP4_Box_t *p_box )
663
{
664
    MP4_READBOX_ENTER( MP4_Box_data_smhd_t );
665
666
667
668
669
670
671
672

    MP4_GETVERSIONFLAGS( p_box->data.p_smhd );



    MP4_GET2BYTES( p_box->data.p_smhd->i_balance );

    MP4_GET2BYTES( p_box->data.p_smhd->i_reserved );
673

674
#ifdef MP4_VERBOSE
sigmunau's avatar
sigmunau committed
675
    msg_Dbg( p_stream, "read box: \"smhd\" balance %f",
676
677
678
679
680
681
                      (float)p_box->data.p_smhd->i_balance / 256 );
#endif
    MP4_READBOX_EXIT( 1 );
}


sigmunau's avatar
sigmunau committed
682
static int MP4_ReadBox_hmhd( stream_t *p_stream, MP4_Box_t *p_box )
683
{
684
    MP4_READBOX_ENTER( MP4_Box_data_hmhd_t );
685
686
687
688
689
690
691
692
693
694
695
696

    MP4_GETVERSIONFLAGS( p_box->data.p_hmhd );

    MP4_GET2BYTES( p_box->data.p_hmhd->i_max_PDU_size );
    MP4_GET2BYTES( p_box->data.p_hmhd->i_avg_PDU_size );

    MP4_GET4BYTES( p_box->data.p_hmhd->i_max_bitrate );
    MP4_GET4BYTES( p_box->data.p_hmhd->i_avg_bitrate );

    MP4_GET4BYTES( p_box->data.p_hmhd->i_reserved );

#ifdef MP4_VERBOSE
sigmunau's avatar
sigmunau committed
697
    msg_Dbg( p_stream, "read box: \"hmhd\" maxPDU-size %d avgPDU-size %d max-bitrate %d avg-bitrate %d",
698
699
700
701
702
703
704
705
                      p_box->data.p_hmhd->i_max_PDU_size,
                      p_box->data.p_hmhd->i_avg_PDU_size,
                      p_box->data.p_hmhd->i_max_bitrate,
                      p_box->data.p_hmhd->i_avg_bitrate );
#endif
    MP4_READBOX_EXIT( 1 );
}

sigmunau's avatar
sigmunau committed
706
static int MP4_ReadBox_url( stream_t *p_stream, MP4_Box_t *p_box )
707
{
708
    MP4_READBOX_ENTER( MP4_Box_data_url_t );
709
710
711
712
713

    MP4_GETVERSIONFLAGS( p_box->data.p_url );
    MP4_GETSTRINGZ( p_box->data.p_url->psz_location );

#ifdef MP4_VERBOSE
sigmunau's avatar
sigmunau committed
714
    msg_Dbg( p_stream, "read box: \"url\" url: %s",
715
716
717
718
719
720
721
                       p_box->data.p_url->psz_location );

#endif
    MP4_READBOX_EXIT( 1 );
}


722
static void MP4_FreeBox_url( MP4_Box_t *p_box )
723
{
724
    FREENULL( p_box->data.p_url->psz_location );
725
726
}

sigmunau's avatar
sigmunau committed
727
static int MP4_ReadBox_urn( stream_t *p_stream, MP4_Box_t *p_box )
728
729
730
731
732
733
{
    MP4_READBOX_ENTER( MP4_Box_data_urn_t );

    MP4_GETVERSIONFLAGS( p_box->data.p_urn );

    MP4_GETSTRINGZ( p_box->data.p_urn->psz_name );
734
735
    MP4_GETSTRINGZ( p_box->data.p_urn->psz_location );

736
#ifdef MP4_VERBOSE
sigmunau's avatar
sigmunau committed
737
    msg_Dbg( p_stream, "read box: \"urn\" name %s location %s",
738
739
740
741
742
                      p_box->data.p_urn->psz_name,
                      p_box->data.p_urn->psz_location );
#endif
    MP4_READBOX_EXIT( 1 );
}
743
static void MP4_FreeBox_urn( MP4_Box_t *p_box )
744
{
zorglub's avatar
zorglub committed
745
746
    FREENULL( p_box->data.p_urn->psz_name );
    FREENULL( p_box->data.p_urn->psz_location );
747
748
749
}


sigmunau's avatar
sigmunau committed
750
static int MP4_ReadBox_dref( stream_t *p_stream, MP4_Box_t *p_box )
751
752
{
    MP4_READBOX_ENTER( MP4_Box_data_dref_t );
753

754
755
756
    MP4_GETVERSIONFLAGS( p_box->data.p_dref );

    MP4_GET4BYTES( p_box->data.p_dref->i_entry_count );
757

sigmunau's avatar
sigmunau committed
758
    stream_Seek( p_stream, p_box->i_pos + MP4_BOX_HEADERSIZE( p_box ) + 8 );
759
760
761
    MP4_ReadBoxContainerRaw( p_stream, p_box );

#ifdef MP4_VERBOSE
sigmunau's avatar
sigmunau committed
762
    msg_Dbg( p_stream, "read box: \"dref\" entry-count %d",
763
764
765
766
767
768
769
                      p_box->data.p_dref->i_entry_count );

#endif
    MP4_READBOX_EXIT( 1 );
}


sigmunau's avatar
sigmunau committed
770
static int MP4_ReadBox_stts( stream_t *p_stream, MP4_Box_t *p_box )
771
{
772
    unsigned int i;
773
    MP4_READBOX_ENTER( MP4_Box_data_stts_t );
774
775
776
777

    MP4_GETVERSIONFLAGS( p_box->data.p_stts );
    MP4_GET4BYTES( p_box->data.p_stts->i_entry_count );

778
    p_box->data.p_stts->i_sample_count =
779
        calloc( sizeof( uint32_t ), p_box->data.p_stts->i_entry_count );
780
    p_box->data.p_stts->i_sample_delta =
781
        calloc( sizeof( uint32_t ), p_box->data.p_stts->i_entry_count );
782

783
784
785
786
787
    for( i = 0; (i < p_box->data.p_stts->i_entry_count )&&( i_read >=8 ); i++ )
    {
        MP4_GET4BYTES( p_box->data.p_stts->i_sample_count[i] );
        MP4_GET4BYTES( p_box->data.p_stts->i_sample_delta[i] );
    }
788

789
#ifdef MP4_VERBOSE
sigmunau's avatar
sigmunau committed
790
    msg_Dbg( p_stream, "read box: \"stts\" entry-count %d",
791
792
793
794
795
796
                      p_box->data.p_stts->i_entry_count );

#endif
    MP4_READBOX_EXIT( 1 );
}

797
static void MP4_FreeBox_stts( MP4_Box_t *p_box )
798
{
zorglub's avatar
zorglub committed
799
800
    FREENULL( p_box->data.p_stts->i_sample_count );
    FREENULL( p_box->data.p_stts->i_sample_delta );
801
802
}

sigmunau's avatar
sigmunau committed
803
static int MP4_ReadBox_ctts( stream_t *p_stream, MP4_Box_t *p_box )
804
{
805
    unsigned int i;
806
    MP4_READBOX_ENTER( MP4_Box_data_ctts_t );
807

808
    MP4_GETVERSIONFLAGS( p_box->data.p_ctts );
809

810
811
    MP4_GET4BYTES( p_box->data.p_ctts->i_entry_count );

812
    p_box->data.p_ctts->i_sample_count =
813
        calloc( sizeof( uint32_t ), p_box->data.p_ctts->i_entry_count );
814
    p_box->data.p_ctts->i_sample_offset =
815
        calloc( sizeof( uint32_t ), p_box->data.p_ctts->i_entry_count );
816

817
818
819
820
821
    for( i = 0; (i < p_box->data.p_ctts->i_entry_count )&&( i_read >=8 ); i++ )
    {
        MP4_GET4BYTES( p_box->data.p_ctts->i_sample_count[i] );
        MP4_GET4BYTES( p_box->data.p_ctts->i_sample_offset[i] );
    }
822

823
#ifdef MP4_VERBOSE
sigmunau's avatar
sigmunau committed
824
    msg_Dbg( p_stream, "read box: \"ctts\" entry-count %d",
825
826
827
828
829
830
                      p_box->data.p_ctts->i_entry_count );

#endif
    MP4_READBOX_EXIT( 1 );
}

831
static void MP4_FreeBox_ctts( MP4_Box_t *p_box )
832
{
zorglub's avatar
zorglub committed
833
834
    FREENULL( p_box->data.p_ctts->i_sample_count );
    FREENULL( p_box->data.p_ctts->i_sample_offset );
835
836
}

837
static int MP4_ReadLengthDescriptor( uint8_t **pp_peek, int64_t  *i_read )
838
{
839
840
    unsigned int i_b;
    unsigned int i_len = 0;
841
842
843
844
845
846
847
848
    do
    {
        i_b = **pp_peek;

        (*pp_peek)++;
        (*i_read)--;
        i_len = ( i_len << 7 ) + ( i_b&0x7f );
    } while( i_b&0x80 );
849
    return( i_len );
850
851
}

sigmunau's avatar
sigmunau committed
852
static int MP4_ReadBox_esds( stream_t *p_stream, MP4_Box_t *p_box )
853
854
{
#define es_descriptor p_box->data.p_esds->es_descriptor
855
856
857
    unsigned int i_len;
    unsigned int i_flags;
    unsigned int i_type;
858
859
860
861
862
863
864
865
866
867
868

    MP4_READBOX_ENTER( MP4_Box_data_esds_t );

    MP4_GETVERSIONFLAGS( p_box->data.p_esds );


    MP4_GET1BYTE( i_type );
    if( i_type == 0x03 ) /* MP4ESDescrTag */
    {
        i_len = MP4_ReadLengthDescriptor( &p_peek, &i_read );

869
#ifdef MP4_VERBOSE
sigmunau's avatar
sigmunau committed
870
        msg_Dbg( p_stream, "found esds MPEG4ESDescr (%dBytes)",
871
                 i_len );
872
873
#endif

874
875
876
877
878
879
880
881
882
883
884
885
886
        MP4_GET2BYTES( es_descriptor.i_ES_ID );
        MP4_GET1BYTE( i_flags );
        es_descriptor.b_stream_dependence = ( (i_flags&0x80) != 0);
        es_descriptor.b_url = ( (i_flags&0x40) != 0);
        es_descriptor.b_OCRstream = ( (i_flags&0x20) != 0);

        es_descriptor.i_stream_priority = i_flags&0x1f;
        if( es_descriptor.b_stream_dependence )
        {
            MP4_GET2BYTES( es_descriptor.i_depend_on_ES_ID );
        }
        if( es_descriptor.b_url )
        {
887
            unsigned int i_len;
888

889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
            MP4_GET1BYTE( i_len );
            es_descriptor.psz_URL = calloc( sizeof(char), i_len + 1 );
            memcpy( es_descriptor.psz_URL, p_peek, i_len );
            es_descriptor.psz_URL[i_len] = 0;
            p_peek += i_len;
            i_read -= i_len;
        }
        else
        {
            es_descriptor.psz_URL = NULL;
        }
        if( es_descriptor.b_OCRstream )
        {
            MP4_GET2BYTES( es_descriptor.i_OCR_ES_ID );
        }
        MP4_GET1BYTE( i_type ); /* get next type */
    }

    if( i_type != 0x04)/* MP4DecConfigDescrTag */
    {
909
910
         es_descriptor.p_decConfigDescr = NULL;
         MP4_READBOX_EXIT( 1 ); /* rest isn't interesting up to now */
911
912
913
    }

    i_len = MP4_ReadLengthDescriptor( &p_peek, &i_read );
914
915

#ifdef MP4_VERBOSE
sigmunau's avatar
sigmunau committed
916
        msg_Dbg( p_stream, "found esds MP4DecConfigDescr (%dBytes)",
917
                 i_len );
918
919
#endif

920
    es_descriptor.p_decConfigDescr =
921
922
923
924
925
926
927
928
929
930
931
932
            malloc( sizeof( MP4_descriptor_decoder_config_t ));

    MP4_GET1BYTE( es_descriptor.p_decConfigDescr->i_objectTypeIndication );
    MP4_GET1BYTE( i_flags );
    es_descriptor.p_decConfigDescr->i_streamType = i_flags >> 2;
    es_descriptor.p_decConfigDescr->b_upStream = ( i_flags >> 1 )&0x01;
    MP4_GET3BYTES( es_descriptor.p_decConfigDescr->i_buffer_sizeDB );
    MP4_GET4BYTES( es_descriptor.p_decConfigDescr->i_max_bitrate );
    MP4_GET4BYTES( es_descriptor.p_decConfigDescr->i_avg_bitrate );
    MP4_GET1BYTE( i_type );
    if( i_type !=  0x05 )/* MP4DecSpecificDescrTag */
    {
933
934
        es_descriptor.p_decConfigDescr->i_decoder_specific_info_len = 0;
        es_descriptor.p_decConfigDescr->p_decoder_specific_info  = NULL;
935
936
        MP4_READBOX_EXIT( 1 );
    }
937

938
    i_len = MP4_ReadLengthDescriptor( &p_peek, &i_read );
939
940

#ifdef MP4_VERBOSE
sigmunau's avatar
sigmunau committed
941
        msg_Dbg( p_stream, "found esds MP4DecSpecificDescr (%dBytes)",
942
                 i_len );
943
944
#endif

945
946
    es_descriptor.p_decConfigDescr->i_decoder_specific_info_len = i_len;
    es_descriptor.p_decConfigDescr->p_decoder_specific_info = malloc( i_len );
947
    memcpy( es_descriptor.p_decConfigDescr->p_decoder_specific_info,
948
949
950
951
952
953
954
            p_peek, i_len );

    MP4_READBOX_EXIT( 1 );

#undef es_descriptor
}

955
static void MP4_FreeBox_esds( MP4_Box_t *p_box )
956
{
zorglub's avatar
zorglub committed
957
    FREENULL( p_box->data.p_esds->es_descriptor.psz_URL );
958
959
    if( p_box->data.p_esds->es_descriptor.p_decConfigDescr )
    {
zorglub's avatar
zorglub committed
960
        FREENULL( p_box->data.p_esds->es_descriptor.p_decConfigDescr->p_decoder_specific_info );
961
    }
zorglub's avatar
zorglub committed
962
    FREENULL( p_box->data.p_esds->es_descriptor.p_decConfigDescr );
963
964
}

sigmunau's avatar
sigmunau committed
965
static int MP4_ReadBox_avcC( stream_t *p_stream, MP4_Box_t *p_box )
966
967
968
969
970
971
972
973
{
    MP4_Box_data_avcC_t *p_avcC;
    int i;

    MP4_READBOX_ENTER( MP4_Box_data_avcC_t );
    p_avcC = p_box->data.p_avcC;

    p_avcC->i_avcC = i_read;
gbazin's avatar
gbazin committed
974
975
976
977
978
    if( p_avcC->i_avcC > 0 )
    {
        p_avcC->p_avcC = malloc( p_avcC->i_avcC );
        memcpy( p_avcC->p_avcC, p_peek, i_read );
    }
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024

    MP4_GET1BYTE( p_avcC->i_version );
    MP4_GET1BYTE( p_avcC->i_profile );
    MP4_GET1BYTE( p_avcC->i_profile_compatibility );
    MP4_GET1BYTE( p_avcC->i_level );
    MP4_GET1BYTE( p_avcC->i_reserved1 );
    p_avcC->i_length_size = (p_avcC->i_reserved1&0x03) + 1;
    p_avcC->i_reserved1 >>= 2;

    MP4_GET1BYTE( p_avcC->i_reserved2 );
    p_avcC->i_sps = p_avcC->i_reserved2&0x1f;
    p_avcC->i_reserved2 >>= 5;

    if( p_avcC->i_sps > 0 )
    {
        p_avcC->i_sps_length = malloc( p_avcC->i_sps * sizeof( uint16_t ) );
        p_avcC->sps = malloc( p_avcC->i_sps * sizeof( uint8_t* ) );

        for( i = 0; i < p_avcC->i_sps; i++ )
        {
            MP4_GET2BYTES( p_avcC->i_sps_length[i] );
            p_avcC->sps[i] = malloc( p_avcC->i_sps_length[i] );
            memcpy( p_avcC->sps[i], p_peek, p_avcC->i_sps_length[i] );

            p_peek += p_avcC->i_sps_length[i];
            i_read -= p_avcC->i_sps_length[i];
        }
    }

    MP4_GET1BYTE( p_avcC->i_pps );
    if( p_avcC->i_pps > 0 )
    {
        p_avcC->i_pps_length = malloc( p_avcC->i_pps * sizeof( uint16_t ) );
        p_avcC->pps = malloc( p_avcC->i_pps * sizeof( uint8_t* ) );

        for( i = 0; i < p_avcC->i_pps; i++ )
        {
            MP4_GET2BYTES( p_avcC->i_pps_length[i] );
            p_avcC->pps[i] = malloc( p_avcC->i_pps_length[i] );
            memcpy( p_avcC->pps[i], p_peek, p_avcC->i_pps_length[i] );

            p_peek += p_avcC->i_pps_length[i];
            i_read -= p_avcC->i_pps_length[i];
        }
    }
#ifdef MP4_VERBOSE
sigmunau's avatar
sigmunau committed
1025
    msg_Dbg( p_stream,
hartman's avatar
hartman committed
1026
             "read box: \"avcC\" version=%d profile=0x%x level=0x%x length size=%d sps=%d pps=%d",
1027
1028
1029
1030
1031
             p_avcC->i_version, p_avcC->i_profile, p_avcC->i_level,
             p_avcC->i_length_size,
             p_avcC->i_sps, p_avcC->i_pps );
    for( i = 0; i < p_avcC->i_sps; i++ )
    {
sigmunau's avatar
sigmunau committed
1032
        msg_Dbg( p_stream, "         - sps[%d] length=%d",
1033
1034
1035
1036
                 i, p_avcC->i_sps_length[i] );
    }
    for( i = 0; i < p_avcC->i_pps; i++ )
    {
sigmunau's avatar
sigmunau committed
1037
        msg_Dbg( p_stream, "         - pps[%d] length=%d",
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
                 i, p_avcC->i_pps_length[i] );
    }

#endif
    MP4_READBOX_EXIT( 1 );
}

static void MP4_FreeBox_avcC( MP4_Box_t *p_box )
{
    MP4_Box_data_avcC_t *p_avcC = p_box->data.p_avcC;
    int i;

gbazin's avatar
gbazin committed
1050
1051
    if( p_avcC->i_avcC > 0 ) FREENULL( p_avcC->p_avcC );

1052
1053
    for( i = 0; i < p_avcC->i_sps; i++ )
    {
zorglub's avatar
zorglub committed
1054
        FREENULL( p_avcC->sps[i] );
1055
1056
1057
    }
    for( i = 0; i < p_avcC->i_pps; i++ )
    {
zorglub's avatar
zorglub committed
1058
        FREENULL( p_avcC->pps[i] );
1059
    }
zorglub's avatar
zorglub committed
1060
    if( p_avcC->i_sps > 0 ) FREENULL( p_avcC->sps );
gbazin's avatar
gbazin committed
1061
    if( p_avcC->i_sps > 0 ) FREENULL( p_avcC->i_sps_length );
zorglub's avatar
zorglub committed
1062
    if( p_avcC->i_pps > 0 ) FREENULL( p_avcC->pps );
gbazin's avatar
gbazin committed
1063
    if( p_avcC->i_pps > 0 ) FREENULL( p_avcC->i_pps_length );
1064
1065
}

sigmunau's avatar
sigmunau committed
1066
static int MP4_ReadBox_sample_soun( stream_t *p_stream, MP4_Box_t *p_box )
1067
{
1068
1069
    unsigned int i;

1070
    MP4_READBOX_ENTER( MP4_Box_data_sample_soun_t );
1071
    p_box->data.p_sample_soun->p_qt_description = NULL;
1072

gbazin's avatar
   
gbazin committed
1073
1074
1075
1076
1077
    /* Sanity check needed because the "wave" box does also contain an
     * "mp4a" box that we don't understand. */
    if( i_read < 28 )
    {