ps.h 20.4 KB
Newer Older
1
2
3
/*****************************************************************************
 * ps.h: Program Stream demuxer helper
 *****************************************************************************
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2004-2009 the VideoLAN team
5
 * $Id$
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
 *
 * 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.
 *
 * 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
 *****************************************************************************/

zorglub's avatar
zorglub committed
24
#include <vlc_demux.h>
25
#include <assert.h>
zorglub's avatar
zorglub committed
26

27
28
29
/* 256-0xC0 for normal stream, 256 for 0xbd stream, 256 for 0xfd stream, 8 for 0xa0 AOB stream */
#define PS_TK_COUNT (256+256+256+8 - 0xc0)
#if 0
30
#define PS_ID_TO_TK( id ) ((id) <= 0xff ? (id) - 0xc0 : \
31
            ((id)&0xff) + (((id)&0xff00) == 0xbd00 ? 256-0xC0 : 512-0xc0) )
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#else
static inline int ps_id_to_tk( unsigned i_id )
{
    if( i_id <= 0xff )
        return i_id - 0xc0;
    else if( (i_id & 0xff00) == 0xbd00 )
        return 256-0xC0 + (i_id & 0xff);
    else if( (i_id & 0xff00) == 0xfd00 )
        return 512-0xc0 + (i_id & 0xff);
    else
        return 768-0xc0 + (i_id & 0x07);
}
#define PS_ID_TO_TK( id ) ps_id_to_tk( id )
#endif
46
47

typedef struct ps_psm_t ps_psm_t;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
48
49
static inline int ps_id_to_type( const ps_psm_t *, int );
static inline const uint8_t *ps_id_to_lang( const ps_psm_t *, int );
50
51
52

typedef struct
{
53
    bool  b_seen;
54
    int         i_skip;
55
    int         i_id;
56
57
    es_out_id_t *es;
    es_format_t fmt;
58
59
    mtime_t     i_first_pts;
    mtime_t     i_last_pts;
60

61
62
63
64
65
66
67
68
} ps_track_t;

/* Init a set of track */
static inline void ps_track_init( ps_track_t tk[PS_TK_COUNT] )
{
    int i;
    for( i = 0; i < PS_TK_COUNT; i++ )
    {
69
        tk[i].b_seen = false;
70
        tk[i].i_skip = 0;
71
        tk[i].i_id   = 0;
72
        tk[i].es     = NULL;
hartman's avatar
hartman committed
73
74
        tk[i].i_first_pts = -1;
        tk[i].i_last_pts = -1;
75
76
77
78
79
        es_format_Init( &tk[i].fmt, UNKNOWN_ES, 0 );
    }
}

/* From id fill i_skip and es_format_t */
80
static inline int ps_track_fill( ps_track_t *tk, ps_psm_t *p_psm, int i_id )
81
82
{
    tk->i_skip = 0;
83
    tk->i_id = i_id;
84
85
    if( ( i_id&0xff00 ) == 0xbd00 )
    {
86
        if( ( i_id&0xf8 ) == 0x88 || (i_id&0xf8) == 0x98 )
87
        {
88
            es_format_Init( &tk->fmt, AUDIO_ES, VLC_CODEC_DTS );
Laurent Aimar's avatar
Laurent Aimar committed
89
            tk->i_skip = 4;
90
        }
91
92
        else if( ( i_id&0xf0 ) == 0x80
               ||  (i_id&0xf0) == 0xc0 ) /* AC-3, Can also be used for DD+/E-AC-3 */
93
        {
94
            es_format_Init( &tk->fmt, AUDIO_ES, VLC_CODEC_A52 );
95
96
            tk->i_skip = 4;
        }
97
98
        else if( (i_id&0xf0) == 0xb0 )
        {
99
            es_format_Init( &tk->fmt, AUDIO_ES, VLC_CODEC_MLP );
100
101
            /* FIXME / untested ... no known decoder (at least not in VLC/ffmpeg) */
        }
102
103
        else if( ( i_id&0xe0 ) == 0x20 )
        {
104
            es_format_Init( &tk->fmt, SPU_ES, VLC_CODEC_SPU );
105
106
107
108
            tk->i_skip = 1;
        }
        else if( ( i_id&0xf0 ) == 0xa0 )
        {
109
            es_format_Init( &tk->fmt, AUDIO_ES, VLC_CODEC_DVD_LPCM );
110
111
            tk->i_skip = 1;
        }
Laurent Aimar's avatar
Laurent Aimar committed
112
113
        else if( ( i_id&0xff ) == 0x70 )
        {
gbazin's avatar
   
gbazin committed
114
            es_format_Init( &tk->fmt, SPU_ES, VLC_FOURCC('o','g','t',' ') );
Laurent Aimar's avatar
Laurent Aimar committed
115
116
117
        }
        else if( ( i_id&0xfc ) == 0x00 )
        {
gbazin's avatar
   
gbazin committed
118
            es_format_Init( &tk->fmt, SPU_ES, VLC_FOURCC('c','v','d',' ') );
Laurent Aimar's avatar
Laurent Aimar committed
119
        }
120
121
        else if( ( i_id&0xff ) == 0x10 )
        {
122
            es_format_Init( &tk->fmt, SPU_ES, VLC_CODEC_TELETEXT );
123
        }
124
125
126
127
128
129
        else
        {
            es_format_Init( &tk->fmt, UNKNOWN_ES, 0 );
            return VLC_EGENERIC;
        }
    }
130
131
132
133
134
    else if( (i_id&0xff00) == 0xfd00 )
    {
        uint8_t i_sub_id = i_id & 0xff;
        if( i_sub_id >= 0x55 && i_sub_id <= 0x5f )
        {
135
            es_format_Init( &tk->fmt, VIDEO_ES, VLC_CODEC_VC1 );
136
137
138
139
140
141
142
        }
        else
        {
            es_format_Init( &tk->fmt, UNKNOWN_ES, 0 );
            return VLC_EGENERIC;
        }
    }
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
    else if( (i_id&0xff00) == 0xa000 )
    {
        uint8_t i_sub_id = i_id & 0x07;
        if( i_sub_id == 0 )
        {
            es_format_Init( &tk->fmt, AUDIO_ES, VLC_CODEC_DVDA_LPCM );
            tk->i_skip = 1;
        }
        else if( i_sub_id == 1 )
        {
            es_format_Init( &tk->fmt, AUDIO_ES, VLC_CODEC_MLP );
            tk->i_skip = -1; /* It's a hack for variable skip value */
        }
        else
        {
            es_format_Init( &tk->fmt, UNKNOWN_ES, 0 );
            return VLC_EGENERIC;
        }
    }
162
163
    else
    {
164
165
166
167
        int i_type = ps_id_to_type( p_psm , i_id );

        es_format_Init( &tk->fmt, UNKNOWN_ES, 0 );

168
169
        if( (i_id&0xf0) == 0xe0 && i_type == 0x1b )
        {
170
            es_format_Init( &tk->fmt, VIDEO_ES, VLC_CODEC_H264 );
171
172
        }
        else if( (i_id&0xf0) == 0xe0 && i_type == 0x10 )
173
        {
174
            es_format_Init( &tk->fmt, VIDEO_ES, VLC_CODEC_MP4V );
175
176
        }
        else if( (i_id&0xf0) == 0xe0 && i_type == 0x02 )
177
        {
178
            es_format_Init( &tk->fmt, VIDEO_ES, VLC_CODEC_MPGV );
179
        }
180
181
        else if( ( i_id&0xe0 ) == 0xc0 && i_type == 0x0f )
        {
182
            es_format_Init( &tk->fmt, AUDIO_ES, VLC_CODEC_MP4A );
183
184
185
        }
        else if( ( i_id&0xe0 ) == 0xc0 && i_type == 0x11 )
        {
186
            es_format_Init( &tk->fmt, AUDIO_ES, VLC_CODEC_MP4A );
187
        }
188
        else if( ( i_id&0xe0 ) == 0xc0 && i_type == 0x03 )
189
        {
190
            es_format_Init( &tk->fmt, AUDIO_ES, VLC_CODEC_MPGA );
191
        }
192
193

        if( tk->fmt.i_cat == UNKNOWN_ES && ( i_id&0xf0 ) == 0xe0 )
194
        {
195
            es_format_Init( &tk->fmt, VIDEO_ES, VLC_CODEC_MPGV );
196
        }
197
198
        else if( tk->fmt.i_cat == UNKNOWN_ES && ( i_id&0xe0 ) == 0xc0 )
        {
199
            es_format_Init( &tk->fmt, AUDIO_ES, VLC_CODEC_MPGA );
200
        }
201
        else if( tk->fmt.i_cat == UNKNOWN_ES ) return VLC_EGENERIC;
202
    }
203
204

    /* PES packets usually contain truncated frames */
205
    tk->fmt.b_packetized = false;
206

Jean-Paul Saman's avatar
Jean-Paul Saman committed
207
    if( ps_id_to_lang( p_psm, i_id ) )
208
209
    {
        tk->fmt.psz_language = malloc( 4 );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
210
211
212
213
214
        if( tk->fmt.psz_language )
        {
            memcpy( tk->fmt.psz_language, ps_id_to_lang( p_psm , i_id ), 3 );
            tk->fmt.psz_language[3] = 0;
        }
215
216
    }

217
218
219
220
221
222
223
224
    return VLC_SUCCESS;
}

/* return the id of a PES (should be valid) */
static inline int ps_pkt_id( block_t *p_pkt )
{
    if( p_pkt->p_buffer[3] == 0xbd &&
        p_pkt->i_buffer >= 9 &&
225
        p_pkt->i_buffer >= 9 + (size_t)p_pkt->p_buffer[8] )
226
    {
227
228
229
230
231
        const unsigned i_start = 9 + p_pkt->p_buffer[8];
        const uint8_t i_sub_id = p_pkt->p_buffer[i_start];

        if( (i_sub_id & 0xfe) == 0xa0 &&
            p_pkt->i_buffer >= i_start + 7 &&
232
233
            ( p_pkt->p_buffer[i_start + 5] >=  0xc0 ||
              p_pkt->p_buffer[i_start + 6] != 0x80 ) )
234
235
236
237
238
239
240
        {
            /* AOB LPCM/MLP extension
             * XXX for MLP I think that the !=0x80 test is not good and
             * will fail for some valid files */
            return 0xa000 | (i_sub_id & 0x01);
        }

241
        /* VOB extension */
242
        return 0xbd00 | i_sub_id;
243
    }
244
245
246
247
248
249
250
    else if( p_pkt->p_buffer[3] == 0xfd &&
             p_pkt->i_buffer >= 9 &&
             (p_pkt->p_buffer[6]&0xC0) == 0x80 &&   /* mpeg2 */
             (p_pkt->p_buffer[7]&0x01) == 0x01 )    /* extension_flag */
    {
        /* ISO 13818 amendment 2 and SMPTE RP 227 */
        const uint8_t i_flags = p_pkt->p_buffer[7];
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
251
        unsigned int i_skip = 9;
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297

        /* Find PES extension */
        if( (i_flags & 0x80 ) )
        {
            i_skip += 5;        /* pts */
            if( (i_flags & 0x40) )
                i_skip += 5;    /* dts */
        }
        if( (i_flags & 0x20 ) )
            i_skip += 6;
        if( (i_flags & 0x10 ) )
            i_skip += 3;
        if( (i_flags & 0x08 ) )
            i_skip += 1;
        if( (i_flags & 0x04 ) )
            i_skip += 1;
        if( (i_flags & 0x02 ) )
            i_skip += 2;

        if( i_skip < p_pkt->i_buffer && (p_pkt->p_buffer[i_skip]&0x01) )
        {
            const uint8_t i_flags2 = p_pkt->p_buffer[i_skip];

            /* Find PES extension 2 */
            i_skip += 1;
            if( i_flags2 & 0x80 )
                i_skip += 16;
            if( (i_flags2 & 0x40) && i_skip < p_pkt->i_buffer )
                i_skip += 1 + p_pkt->p_buffer[i_skip];
            if( i_flags2 & 0x20 )
                i_skip += 2;
            if( i_flags2 & 0x10 )
                i_skip += 2;

            if( i_skip + 1 < p_pkt->i_buffer )
            {
                const int i_extension_field_length = p_pkt->p_buffer[i_skip]&0x7f;
                if( i_extension_field_length >=1 )
                {
                    int i_stream_id_extension_flag = (p_pkt->p_buffer[i_skip+1] >> 7)&0x1;
                    if( i_stream_id_extension_flag == 0 )
                        return 0xfd00 | (p_pkt->p_buffer[i_skip+1]&0x7f);
                }
            }
        }
    }
298
299
300
301
    return p_pkt->p_buffer[3];
}

/* return the size of the next packet
302
303
 * You need to give him at least 14 bytes (and it need to start as a
 * valid packet) It does not handle less than 6 bytes */
304
static inline int ps_pkt_size( const uint8_t *p, int i_peek )
305
{
306
    assert( i_peek >= 6 );
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
    if( p[3] == 0xb9 && i_peek >= 4 )
    {
        return 4;
    }
    else if( p[3] == 0xba )
    {
        if( (p[4] >> 6) == 0x01 && i_peek >= 14 )
        {
            return 14 + (p[13]&0x07);
        }
        else if( (p[4] >> 4) == 0x02 && i_peek >= 12 )
        {
            return 12;
        }
        return -1;
    }
    else if( i_peek >= 6 )
    {
        return 6 + ((p[4]<<8) | p[5] );
    }
    return -1;
}

/* parse a PACK PES */
gbazin's avatar
   
gbazin committed
331
332
static inline int ps_pkt_parse_pack( block_t *p_pkt, int64_t *pi_scr,
                                     int *pi_mux_rate )
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
{
    uint8_t *p = p_pkt->p_buffer;
    if( p_pkt->i_buffer >= 14 && (p[4] >> 6) == 0x01 )
    {
        *pi_scr =((((int64_t)p[4]&0x38) << 27 )|
                  (((int64_t)p[4]&0x03) << 28 )|
                   ((int64_t)p[5] << 20 )|
                  (((int64_t)p[6]&0xf8) << 12 )|
                  (((int64_t)p[6]&0x03) << 13 )|
                   ((int64_t)p[7] << 5 )|
                   ((int64_t)p[8] >> 3 )) * 100 / 9;

        *pi_mux_rate = ( p[10] << 14 )|( p[11] << 6 )|( p[12] >> 2);
    }
    else if( p_pkt->i_buffer >= 12 && (p[4] >> 4) == 0x02 )
    {
        *pi_scr =((((int64_t)p[4]&0x0e) << 29 )|
                   ((int64_t)p[5] << 22 )|
                  (((int64_t)p[6]&0xfe) << 14 )|
                   ((int64_t)p[7] <<  7 )|
                   ((int64_t)p[8] >> 1 )) * 100 / 9;

        *pi_mux_rate = ( ( p[9]&0x7f )<< 15 )|( p[10] << 7 )|( p[11] >> 1);
    }
    else
    {
        return VLC_EGENERIC;
    }
    return VLC_SUCCESS;
}

/* Parse a SYSTEM PES */
365
static inline int ps_pkt_parse_system( block_t *p_pkt, ps_psm_t *p_psm,
gbazin's avatar
   
gbazin committed
366
                                       ps_track_t tk[PS_TK_COUNT] )
367
368
369
{
    uint8_t *p = &p_pkt->p_buffer[6 + 3 + 1 + 1 + 1];

370
371
    /* System header is not useable if it references private streams (0xBD)
     * or 'all audio streams' (0xB8) or 'all video streams' (0xB9) */
372
373
    while( p < &p_pkt->p_buffer[p_pkt->i_buffer] )
    {
374
375
376
377
378
379
        int i_id = p[0];

        /* fprintf( stderr, "   SYSTEM_START_CODEEE: id=0x%x\n", p[0] ); */
        if( p[0] >= 0xBC || p[0] == 0xB8 || p[0] == 0xB9 ) p += 2;
        p++;

380
        if( i_id >= 0xc0 )
381
382
383
384
385
        {
            int i_tk = PS_ID_TO_TK( i_id );

            if( !tk[i_tk].b_seen )
            {
386
                if( !ps_track_fill( &tk[i_tk], p_psm, i_id ) )
387
                {
388
                    tk[i_tk].b_seen = true;
389
390
391
392
393
394
395
396
397
398
                }
            }
        }
    }
    return VLC_SUCCESS;
}

/* Parse a PES (and skip i_skip_extra in the payload) */
static inline int ps_pkt_parse_pes( block_t *p_pes, int i_skip_extra )
{
399
    uint8_t header[34];
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
400
    unsigned int i_skip  = 0;
401

402
    memcpy( header, p_pes->p_buffer, __MIN( p_pes->i_buffer, 34 ) );
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428

    switch( header[3] )
    {
        case 0xBC:  /* Program stream map */
        case 0xBE:  /* Padding */
        case 0xBF:  /* Private stream 2 */
        case 0xB0:  /* ECM */
        case 0xB1:  /* EMM */
        case 0xFF:  /* Program stream directory */
        case 0xF2:  /* DSMCC stream */
        case 0xF8:  /* ITU-T H.222.1 type E stream */
            i_skip = 6;
            break;

        default:
            if( ( header[6]&0xC0 ) == 0x80 )
            {
                /* mpeg2 PES */
                i_skip = header[8] + 9;

                if( header[7]&0x80 )    /* has pts */
                {
                    p_pes->i_pts = ((mtime_t)(header[ 9]&0x0e ) << 29)|
                                    (mtime_t)(header[10] << 22)|
                                   ((mtime_t)(header[11]&0xfe) << 14)|
                                    (mtime_t)(header[12] << 7)|
429
                                    (mtime_t)(header[13] >> 1);
430
431
432

                    if( header[7]&0x40 )    /* has dts */
                    {
433
434
435
436
437
                         p_pes->i_dts = ((mtime_t)(header[14]&0x0e ) << 29)|
                                         (mtime_t)(header[15] << 22)|
                                        ((mtime_t)(header[16]&0xfe) << 14)|
                                         (mtime_t)(header[17] << 7)|
                                         (mtime_t)(header[18] >> 1);
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
                    }
                }
            }
            else
            {
                i_skip = 6;
                while( i_skip < 23 && header[i_skip] == 0xff )
                {
                    i_skip++;
                }
                if( i_skip == 23 )
                {
                    /* msg_Err( p_demux, "too much MPEG-1 stuffing" ); */
                    return VLC_EGENERIC;
                }
                if( ( header[i_skip] & 0xC0 ) == 0x40 )
                {
                    i_skip += 2;
                }

                if(  header[i_skip]&0x20 )
                {
                     p_pes->i_pts = ((mtime_t)(header[i_skip]&0x0e ) << 29)|
                                     (mtime_t)(header[i_skip+1] << 22)|
                                    ((mtime_t)(header[i_skip+2]&0xfe) << 14)|
                                     (mtime_t)(header[i_skip+3] << 7)|
                                     (mtime_t)(header[i_skip+4] >> 1);

                    if( header[i_skip]&0x10 )    /* has dts */
                    {
                         p_pes->i_dts = ((mtime_t)(header[i_skip+5]&0x0e ) << 29)|
                                         (mtime_t)(header[i_skip+6] << 22)|
                                        ((mtime_t)(header[i_skip+7]&0xfe) << 14)|
                                         (mtime_t)(header[i_skip+8] << 7)|
                                         (mtime_t)(header[i_skip+9] >> 1);
                         i_skip += 10;
                    }
                    else
                    {
                        i_skip += 5;
                    }
                }
                else
                {
                    i_skip += 1;
                }
            }
    }

487
488
    if( i_skip_extra >= 0 )
        i_skip += i_skip_extra;
489
490
    else if( p_pes->i_buffer > i_skip + 3 &&
             ( ps_pkt_id( p_pes ) == 0xa001 || ps_pkt_id( p_pes ) == 0xbda1 ) )
491
        i_skip += 4 + p_pes->p_buffer[i_skip+3];
492
493
494
495
496
497
498
499
500
501
502
503
504
505

    if( p_pes->i_buffer <= i_skip )
    {
        return VLC_EGENERIC;
    }

    p_pes->p_buffer += i_skip;
    p_pes->i_buffer -= i_skip;

    p_pes->i_dts = 100 * p_pes->i_dts / 9;
    p_pes->i_pts = 100 * p_pes->i_pts / 9;

    return VLC_SUCCESS;
}
506
507

/* Program stream map handling */
508
typedef struct ps_es_t
509
510
511
512
513
514
515
{
    int i_type;
    int i_id;

    int i_descriptor;
    uint8_t *p_descriptor;

516
517
518
    /* Language is iso639-2T */
    uint8_t lang[3];

519
520
521
522
523
524
525
526
527
528
} ps_es_t;

struct ps_psm_t
{
    int i_version;

    int     i_es;
    ps_es_t **es;
};

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
529
static inline int ps_id_to_type( const ps_psm_t *p_psm, int i_id )
530
531
532
533
{
    int i;
    for( i = 0; p_psm && i < p_psm->i_es; i++ )
    {
534
        if( p_psm->es[i]->i_id == i_id ) return p_psm->es[i]->i_type;
535
536
537
538
    }
    return 0;
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
539
static inline const uint8_t *ps_id_to_lang( const ps_psm_t *p_psm, int i_id )
540
541
542
543
{
    int i;
    for( i = 0; p_psm && i < p_psm->i_es; i++ )
    {
544
        if( p_psm->es[i]->i_id == i_id ) return p_psm->es[i]->lang;
545
546
547
548
    }
    return 0;
}

549
550
551
552
553
554
555
556
557
558
559
static inline void ps_psm_init( ps_psm_t *p_psm )
{
    p_psm->i_version = 0xFFFF;
    p_psm->i_es = 0;
    p_psm->es = 0;
}

static inline void ps_psm_destroy( ps_psm_t *p_psm )
{
    while( p_psm->i_es-- )
    {
560
        free( p_psm->es[p_psm->i_es]->p_descriptor );
561
562
        free( p_psm->es[p_psm->i_es] );
    }
563
    free( p_psm->es );
564
565
566
567
568

    p_psm->es = 0;
    p_psm->i_es = 0;
}

569
570
static inline int ps_psm_fill( ps_psm_t *p_psm, block_t *p_pkt,
                               ps_track_t tk[PS_TK_COUNT], es_out_t *out )
571
572
573
{
    int i_buffer = p_pkt->i_buffer;
    uint8_t *p_buffer = p_pkt->p_buffer;
Laurent Aimar's avatar
Laurent Aimar committed
574
    int i_length, i_version, i_info_length, i_esm_length, i_es_base;
575
576
577

    if( !p_psm || p_buffer[3] != 0xbc ) return VLC_EGENERIC;

578
    i_length = (uint16_t)(p_buffer[4] << 8) + p_buffer[5] + 6;
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
    if( i_length > i_buffer ) return VLC_EGENERIC;

    //i_current_next_indicator = (p_buffer[6] && 0x01);
    i_version = (p_buffer[6] && 0xf8);

    if( p_psm->i_version == i_version ) return VLC_EGENERIC;

    ps_psm_destroy( p_psm );

    i_info_length = (uint16_t)(p_buffer[8] << 8) + p_buffer[9];
    if( i_info_length + 10 > i_length ) return VLC_EGENERIC;

    /* Elementary stream map */
    i_esm_length = (uint16_t)(p_buffer[ 10 + i_info_length ] << 8) +
        p_buffer[ 11 + i_info_length];
    i_es_base = 12 + i_info_length;

    while( i_es_base + 4 < i_length )
    {
Jean-Paul Saman's avatar
Jean-Paul Saman committed
598
        ps_es_t **tmp_es;
599
        ps_es_t es;
600
        es.lang[0] = es.lang[1] = es.lang[2] = 0;
601
602
603
604
605
606
607
608

        es.i_type = p_buffer[ i_es_base  ];
        es.i_id = p_buffer[ i_es_base + 1 ];
        i_info_length = (uint16_t)(p_buffer[ i_es_base + 2 ] << 8) +
            p_buffer[ i_es_base + 3 ];

        if( i_es_base + 4 + i_info_length > i_length ) break;

609
610
611
612
613
        /* TODO Add support for VC-1 stream:
         *      stream_type=0xea, stream_id=0xfd AND registration
         *      descriptor 0x5 with format_identifier == 0x56432D31 (VC-1)
         *      (I need a sample that use PSM with VC-1) */

614
615
616
617
        es.p_descriptor = 0;
        es.i_descriptor = i_info_length;
        if( i_info_length > 0 )
        {
618
619
            int i = 0;

620
            es.p_descriptor = malloc( i_info_length );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
621
            if( es.p_descriptor )
622
            {
Jean-Paul Saman's avatar
Jean-Paul Saman committed
623
                memcpy( es.p_descriptor, p_buffer + i_es_base + 4, i_info_length);
624

Jean-Paul Saman's avatar
Jean-Paul Saman committed
625
                while( i <= es.i_descriptor - 2 )
626
                {
Jean-Paul Saman's avatar
Jean-Paul Saman committed
627
628
629
630
631
632
633
634
635
636
637
638
639
640
                    /* Look for the ISO639 language descriptor */
                    if( es.p_descriptor[i] != 0x0a )
                    {
                        i += es.p_descriptor[i+1] + 2;
                        continue;
                    }

                    if( i <= es.i_descriptor - 6 )
                    {
                        es.lang[0] = es.p_descriptor[i+2];
                        es.lang[1] = es.p_descriptor[i+3];
                        es.lang[2] = es.p_descriptor[i+4];
                    }
                    break;
641
642
                }
            }
643
644
        }

Jean-Paul Saman's avatar
Jean-Paul Saman committed
645
646
647
648
649
650
651
652
653
654
655
        tmp_es = realloc( p_psm->es, sizeof(ps_es_t *) * (p_psm->i_es+1) );
        if( tmp_es )
        {
            p_psm->es = tmp_es;
            p_psm->es[p_psm->i_es] = malloc( sizeof(ps_es_t) );
            if( p_psm->es[p_psm->i_es] )
            {
                *p_psm->es[p_psm->i_es++] = es;
                i_es_base += 4 + i_info_length;
            }
        }
656
657
658
659
660
661
    }

    /* TODO: CRC */

    p_psm->i_version = i_version;

662
    /* Check/Modify our existing tracks */
Laurent Aimar's avatar
Laurent Aimar committed
663
    for( int i = 0; i < PS_TK_COUNT; i++ )
664
665
666
667
668
669
670
671
672
673
674
675
    {
        ps_track_t tk_tmp;

        if( !tk[i].b_seen || !tk[i].es ) continue;

        if( ps_track_fill( &tk_tmp, p_psm, tk[i].i_id ) != VLC_SUCCESS )
            continue;

        if( tk_tmp.fmt.i_codec == tk[i].fmt.i_codec ) continue;

        es_out_Del( out, tk[i].es );
        tk[i] = tk_tmp;
676
        tk[i].b_seen = true;
677
678
679
        tk[i].es = es_out_Add( out, &tk[i].fmt );
    }

680
681
    return VLC_SUCCESS;
}