ps.c 20.4 KB
Newer Older
1
/*****************************************************************************
2
 * ps.c: Program Stream demux module for VLC.
3
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2004-2009 VLC authors and VideoLAN
5
 * $Id$
6 7 8
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
9 10 11
 * 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
12 13 14 15
 * (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
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
16 17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
18
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
19 20 21
 * 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.
22 23 24 25 26 27
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

28 29 30 31
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

32
#include <vlc_common.h>
33
#include <vlc_plugin.h>
Clément Stenac's avatar
Clément Stenac committed
34
#include <vlc_demux.h>
35

36
#include "pes.h"
37 38 39 40 41 42 43
#include "ps.h"

/* TODO:
 *  - re-add pre-scanning.
 *  - ...
 */

44 45 46 47 48
#define TIME_TEXT N_("Trust MPEG timestamps")
#define TIME_LONGTEXT N_("Normally we use the timestamps of the MPEG files " \
    "to calculate position and duration. However sometimes this might not " \
    "be usable. Disable this option to calculate from the bitrate instead." )

49 50 51
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
52
static int  OpenForce( vlc_object_t * );
53 54
static int  Open   ( vlc_object_t * );
static void Close  ( vlc_object_t * );
55

56 57
vlc_module_begin ()
    set_description( N_("MPEG-PS demuxer") )
58
    set_shortname( N_("PS") )
59 60 61 62 63
    set_category( CAT_INPUT )
    set_subcategory( SUBCAT_INPUT_DEMUX )
    set_capability( "demux", 1 )
    set_callbacks( OpenForce, Close )
    add_shortcut( "ps" )
64

65
    add_bool( "ps-trust-timestamps", true, TIME_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
66
                 TIME_LONGTEXT, true )
67
        change_safe ()
68

69 70 71 72
    add_submodule ()
    set_description( N_("MPEG-PS demuxer") )
    set_capability( "demux", 8 )
    set_callbacks( Open, Close )
73
    add_shortcut( "ps" )
74
vlc_module_end ()
75 76 77 78 79 80 81

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/

struct demux_sys_t
{
82
    ps_psm_t    psm;
83
    ps_track_t  tk[PS_TK_COUNT];
84 85

    int64_t     i_scr;
86
    int64_t     i_last_scr;
87
    int         i_mux_rate;
88 89 90
    int64_t     i_length;
    int         i_time_track;
    int64_t     i_current_pts;
91

92 93
    int         i_aob_mlp_count;

94 95
    bool  b_lost_sync;
    bool  b_have_pack;
96
    bool  b_bad_scr;
97
    bool  b_seekable;
98 99 100 101 102 103 104 105 106 107 108
};

static int Demux  ( demux_t *p_demux );
static int Control( demux_t *p_demux, int i_query, va_list args );

static int      ps_pkt_resynch( stream_t *, uint32_t *pi_code );
static block_t *ps_pkt_read   ( stream_t *, uint32_t i_code );

/*****************************************************************************
 * Open
 *****************************************************************************/
109
static int OpenCommon( vlc_object_t *p_this, bool b_force )
110 111 112 113
{
    demux_t     *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys;

114
    const uint8_t *p_peek;
115

116
    if( vlc_stream_Peek( p_demux->s, &p_peek, 4 ) < 4 )
117 118 119 120 121
    {
        msg_Err( p_demux, "cannot peek" );
        return VLC_EGENERIC;
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
122
    if( memcmp( p_peek, "\x00\x00\x01", 3 ) || ( p_peek[3] < 0xb9 ) )
123
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
124 125 126
        if( !b_force )
            return VLC_EGENERIC;

127 128
        msg_Warn( p_demux, "this does not look like an MPEG PS stream, "
                  "continuing anyway" );
129 130 131 132
    }

    /* Fill p_demux field */
    p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
133
    if( !p_sys ) return VLC_ENOMEM;
134

135 136 137
    p_demux->pf_demux = Demux;
    p_demux->pf_control = Control;

138 139 140
    /* Init p_sys */
    p_sys->i_mux_rate = 0;
    p_sys->i_scr      = -1;
141
    p_sys->i_last_scr = -1;
142 143
    p_sys->i_length   = -1;
    p_sys->i_current_pts = (mtime_t) 0;
144
    p_sys->i_time_track = -1;
145
    p_sys->i_aob_mlp_count = 0;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
146

147 148
    p_sys->b_lost_sync = false;
    p_sys->b_have_pack = false;
149
    p_sys->b_bad_scr   = false;
150
    p_sys->b_seekable  = false;
151

152
    vlc_stream_Control( p_demux->s, STREAM_CAN_SEEK, &p_sys->b_seekable );
153

154
    ps_psm_init( &p_sys->psm );
155 156 157 158 159 160 161
    ps_track_init( p_sys->tk );

    /* TODO prescanning of ES */

    return VLC_SUCCESS;
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
162
static int OpenForce( vlc_object_t *p_this )
163
{
164
    return OpenCommon( p_this, true );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
165
}
166

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
167 168
static int Open( vlc_object_t *p_this )
{
169
    return OpenCommon( p_this, p_this->obj.force );
170 171
}

172 173 174 175 176 177 178
/*****************************************************************************
 * Close
 *****************************************************************************/
static void Close( vlc_object_t *p_this )
{
    demux_t     *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys = p_demux->p_sys;
179 180 181 182 183 184 185 186 187 188 189
    int i;

    for( i = 0; i < PS_TK_COUNT; i++ )
    {
        ps_track_t *tk = &p_sys->tk[i];
        if( tk->b_seen )
        {
            es_format_Clean( &tk->fmt );
            if( tk->es ) es_out_Del( p_demux->out, tk->es );
        }
    }
190

191 192
    ps_psm_destroy( &p_sys->psm );

193 194 195
    free( p_sys );
}

196
static int Demux2( demux_t *p_demux, bool b_end )
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
{
    demux_sys_t *p_sys = p_demux->p_sys;
    int i_ret, i_id;
    uint32_t i_code;
    block_t *p_pkt;

    i_ret = ps_pkt_resynch( p_demux->s, &i_code );
    if( i_ret < 0 )
    {
        return 0;
    }
    else if( i_ret == 0 )
    {
        if( !p_sys->b_lost_sync )
            msg_Warn( p_demux, "garbage at input, trying to resync..." );

213
        p_sys->b_lost_sync = true;
214 215 216 217
        return 1;
    }

    if( p_sys->b_lost_sync ) msg_Warn( p_demux, "found sync code" );
218
    p_sys->b_lost_sync = false;
219 220 221 222 223 224 225 226

    if( ( p_pkt = ps_pkt_read( p_demux->s, i_code ) ) == NULL )
    {
        return 0;
    }
    if( (i_id = ps_pkt_id( p_pkt )) >= 0xc0 )
    {
        ps_track_t *tk = &p_sys->tk[PS_ID_TO_TK(i_id)];
227 228
        if( !ps_pkt_parse_pes( VLC_OBJECT(p_demux), p_pkt, tk->i_skip ) &&
             p_pkt->i_pts > VLC_TS_INVALID )
229
        {
230 231 232 233 234 235 236 237
            if( b_end && p_pkt->i_pts > tk->i_last_pts )
            {
                tk->i_last_pts = p_pkt->i_pts;
            }
            else if ( tk->i_first_pts == -1 )
            {
                tk->i_first_pts = p_pkt->i_pts;
            }
238 239 240 241 242 243
        }
    }
    block_Release( p_pkt );
    return 1;
}

244
static bool FindLength( demux_t *p_demux )
245 246 247 248
{
    demux_sys_t *p_sys = p_demux->p_sys;
    int64_t i_current_pos = -1, i_size = 0, i_end = 0;

Rémi Duraffort's avatar
Rémi Duraffort committed
249
    if( !var_CreateGetBool( p_demux, "ps-trust-timestamps" ) )
250
        return true;
251 252 253

    if( p_sys->i_length == -1 ) /* First time */
    {
254 255
        p_sys->i_length = 0;
        /* Check beginning */
256
        int i = 0;
257
        i_current_pos = vlc_stream_Tell( p_demux->s );
258
        while( i < 40 && Demux2( p_demux, false ) > 0 ) i++;
259 260

        /* Check end */
261
        i_size = stream_Size( p_demux->s );
262
        i_end = VLC_CLIP( i_size, 0, 200000 );
263
        if( vlc_stream_Seek( p_demux->s, i_size - i_end ) == VLC_SUCCESS )
264 265 266 267
        {
            i = 0;
            while( i < 400 && Demux2( p_demux, true ) > 0 ) i++;
            if( i_current_pos >= 0 &&
268
                vlc_stream_Seek( p_demux->s, i_current_pos ) != VLC_SUCCESS )
269 270 271
                    return false;
        }
        else return false;
272 273
    }

Tobias Güntner's avatar
Tobias Güntner committed
274
    /* Find the longest track */
275
    for( int i = 0; i < PS_TK_COUNT; i++ )
276 277
    {
        ps_track_t *tk = &p_sys->tk[i];
278
        if( tk->i_last_pts > 0 &&
Tobias Güntner's avatar
Tobias Güntner committed
279 280 281 282
            tk->i_last_pts > tk->i_first_pts )
        {
            int64_t i_length = (int64_t)tk->i_last_pts - tk->i_first_pts;
            if( i_length > p_sys->i_length )
283
            {
Tobias Güntner's avatar
Tobias Güntner committed
284 285
                p_sys->i_length = i_length;
                p_sys->i_time_track = i;
286
                msg_Dbg( p_demux, "we found a length of: %"PRId64 "s", p_sys->i_length / CLOCK_FREQ );
287
            }
Tobias Güntner's avatar
Tobias Güntner committed
288
        }
289
    }
290
    return true;
291 292
}

293 294 295 296 297 298
/*****************************************************************************
 * Demux:
 *****************************************************************************/
static int Demux( demux_t *p_demux )
{
    demux_sys_t *p_sys = p_demux->p_sys;
299
    int i_ret, i_id, i_mux_rate;
300
    uint32_t i_code;
301
    block_t *p_pkt;
302 303 304 305

    i_ret = ps_pkt_resynch( p_demux->s, &i_code );
    if( i_ret < 0 )
    {
306
        return VLC_DEMUXER_EOF;
307 308 309
    }
    else if( i_ret == 0 )
    {
310 311 312
        if( !p_sys->b_lost_sync )
            msg_Warn( p_demux, "garbage at input, trying to resync..." );

313
        p_sys->b_lost_sync = true;
314
        return VLC_DEMUXER_SUCCESS;
315 316
    }

317
    if( p_sys->b_lost_sync ) msg_Warn( p_demux, "found sync code" );
318
    p_sys->b_lost_sync = false;
319

320
    if( p_sys->i_length < 0 && p_sys->b_seekable )
321 322 323 324
    {
        if( !FindLength( p_demux ) )
            return VLC_DEMUXER_EGENERIC;
    }
325

326 327
    if( ( p_pkt = ps_pkt_read( p_demux->s, i_code ) ) == NULL )
    {
328
        return VLC_DEMUXER_EOF;
329 330 331 332
    }

    switch( i_code )
    {
333 334 335 336 337 338
    case 0x1b9:
        block_Release( p_pkt );
        break;

    case 0x1ba:
        if( !ps_pkt_parse_pack( p_pkt, &p_sys->i_scr, &i_mux_rate ) )
339
        {
340
            p_sys->i_last_scr = p_sys->i_scr;
341
            if( !p_sys->b_have_pack ) p_sys->b_have_pack = true;
342 343
            /* done later on to work around bad vcd/svcd streams */
            /* es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_sys->i_scr ); */
344 345 346 347
            if( i_mux_rate > 0 ) p_sys->i_mux_rate = i_mux_rate;
        }
        block_Release( p_pkt );
        break;
348

349
    case 0x1bb:
350
        if( !ps_pkt_parse_system( p_pkt, &p_sys->psm, p_sys->tk ) )
351 352 353
        {
            int i;
            for( i = 0; i < PS_TK_COUNT; i++ )
354
            {
355 356 357
                ps_track_t *tk = &p_sys->tk[i];

                if( tk->b_seen && !tk->es && tk->fmt.i_cat != UNKNOWN_ES )
358
                {
359
                    tk->es = es_out_Add( p_demux->out, &tk->fmt );
360 361 362
                }
            }
        }
363 364
        block_Release( p_pkt );
        break;
365

366
    case 0x1bc:
367 368 369
        if( p_sys->psm.i_version == 0xFFFF )
            msg_Dbg( p_demux, "contains a PSM");

370
        ps_psm_fill( &p_sys->psm, p_pkt, p_sys->tk, p_demux->out );
371 372
        block_Release( p_pkt );
        break;
373

374 375
    default:
        if( (i_id = ps_pkt_id( p_pkt )) >= 0xc0 )
376
        {
377 378 379 380 381 382 383 384 385 386 387 388 389
            /* Small heuristic to improve MLP detection from AOB */
            if( i_id == 0xa001 &&
                p_sys->i_aob_mlp_count < 500 )
            {
                p_sys->i_aob_mlp_count++;
            }
            else if( i_id == 0xbda1 &&
                     p_sys->i_aob_mlp_count > 0 )
            {
                p_sys->i_aob_mlp_count--;
                i_id = 0xa001;
            }

390
            bool b_new = false;
391
            ps_track_t *tk = &p_sys->tk[PS_ID_TO_TK(i_id)];
392

393 394
            if( !tk->b_seen )
            {
395
                if( !ps_track_fill( tk, &p_sys->psm, i_id, p_pkt ) )
396
                {
397
                    tk->es = es_out_Add( p_demux->out, &tk->fmt );
398
                    b_new = true;
399
                }
400 401 402 403
                else
                {
                    msg_Dbg( p_demux, "es id=0x%x format unknown", i_id );
                }
404
                tk->b_seen = true;
405
            }
406 407 408 409

            /* The popular VCD/SVCD subtitling WinSubMux does not
             * renumber the SCRs when merging subtitles into the PES */
            if( tk->b_seen &&
410 411
                ( tk->fmt.i_codec == VLC_CODEC_OGT ||
                  tk->fmt.i_codec == VLC_CODEC_CVD ) )
412 413
            {
                p_sys->i_scr = -1;
414
                p_sys->i_last_scr = -1;
415 416
            }

417 418
            if( p_sys->i_scr >= 0 && !p_sys->b_bad_scr )
            {
419 420 421 422 423
                if( (tk->fmt.i_cat == AUDIO_ES || tk->fmt.i_cat == VIDEO_ES) &&
                    tk->i_first_pts > VLC_TS_INVALID && tk->i_first_pts - p_sys->i_scr > CLOCK_FREQ )
                {
                    msg_Warn( p_demux, "Incorrect SCR timing offset by of %ld ms, disabling",
                                       tk->i_first_pts - p_sys->i_scr / 1000 );
424
                    p_sys->b_bad_scr = true; /* Disable Offset SCR */
425
                }
426 427 428
                else
                    es_out_Control( p_demux->out, ES_OUT_SET_PCR, VLC_TS_0 + p_sys->i_scr );
            }
429

430
            if( tk->b_seen && tk->es &&
431
                !ps_pkt_parse_pes( VLC_OBJECT(p_demux), p_pkt, tk->i_skip ) )
432
            {
433 434 435 436 437 438 439 440 441
                if( (tk->fmt.i_cat == AUDIO_ES || tk->fmt.i_cat == VIDEO_ES) &&
                    !p_sys->b_bad_scr && p_sys->i_scr > 0 && p_pkt->i_pts > 0 &&
                    p_sys->i_scr > p_pkt->i_pts + CLOCK_FREQ / 4 )
                {
                    msg_Warn( p_demux, "Incorrect SCR timing in advance of %ld ms, disabling",
                                       p_sys->i_scr - p_pkt->i_pts / 1000 );
                    p_sys->b_bad_scr = true;
                }

442
                if( ((!b_new && !p_sys->b_have_pack) || p_sys->b_bad_scr) &&
443
                    (tk->fmt.i_cat == AUDIO_ES) &&
444
                    (p_pkt->i_pts > VLC_TS_INVALID) )
445 446
                {
                    /* A hack to sync the A/V on PES files. */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
447
                    msg_Dbg( p_demux, "force SCR: %"PRId64, p_pkt->i_pts );
448 449
                    es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_pkt->i_pts );
                }
450 451 452 453 454 455 456
                if( tk->fmt.i_codec == VLC_CODEC_TELETEXT &&
                    p_pkt->i_pts <= VLC_TS_INVALID && p_sys->i_last_scr >= 0 )
                {
                    /* Teletext may have missing PTS (ETSI EN 300 472 Annexe A)
                     * In this case use the last SCR + 40ms */
                    p_pkt->i_pts = VLC_TS_0 + p_sys->i_last_scr + 40000;
                }
457

458 459 460 461 462
                if( (int64_t)p_pkt->i_pts > p_sys->i_current_pts )
                {
                    p_sys->i_current_pts = (int64_t)p_pkt->i_pts;
                }

463
                es_out_Send( p_demux->out, tk->es, p_pkt );
464 465 466 467 468
            }
            else
            {
                block_Release( p_pkt );
            }
469 470

            p_sys->i_scr = -1;
471
        }
472 473 474 475 476
        else
        {
            block_Release( p_pkt );
        }
        break;
477
    }
478

479
    demux_UpdateTitleFromStream( p_demux );
480
    return VLC_DEMUXER_SUCCESS;
481 482 483 484 485 486 487 488 489 490 491 492 493
}

/*****************************************************************************
 * Control:
 *****************************************************************************/
static int Control( demux_t *p_demux, int i_query, va_list args )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    double f, *pf;
    int64_t i64, *pi64;

    switch( i_query )
    {
494 495 496 497
        case DEMUX_CAN_SEEK:
            *va_arg( args, bool * ) = p_sys->b_seekable;
            return VLC_SUCCESS;

498 499 500 501 502
        case DEMUX_GET_POSITION:
            pf = (double*) va_arg( args, double* );
            i64 = stream_Size( p_demux->s );
            if( i64 > 0 )
            {
503
                double current = vlc_stream_Tell( p_demux->s );
504
                *pf = current / (double)i64;
505 506 507 508 509 510
            }
            else
            {
                *pf = 0.0;
            }
            return VLC_SUCCESS;
511

512 513 514
        case DEMUX_SET_POSITION:
            f = (double) va_arg( args, double );
            i64 = stream_Size( p_demux->s );
515
            p_sys->i_current_pts = 0;
516
            p_sys->i_last_scr = -1;
517

518
            return vlc_stream_Seek( p_demux->s, (int64_t)(i64 * f) );
519 520 521

        case DEMUX_GET_TIME:
            pi64 = (int64_t*)va_arg( args, int64_t * );
522
            if( p_sys->i_time_track >= 0 && p_sys->i_current_pts > 0 )
523 524 525 526
            {
                *pi64 = p_sys->i_current_pts - p_sys->tk[p_sys->i_time_track].i_first_pts;
                return VLC_SUCCESS;
            }
527 528
            if( p_sys->i_mux_rate > 0 )
            {
529
                *pi64 = (int64_t)1000000 * ( vlc_stream_Tell( p_demux->s ) / 50 ) /
530
                    p_sys->i_mux_rate;
531 532 533 534 535 536 537
                return VLC_SUCCESS;
            }
            *pi64 = 0;
            return VLC_EGENERIC;

        case DEMUX_GET_LENGTH:
            pi64 = (int64_t*)va_arg( args, int64_t * );
538 539 540 541 542 543
            if( p_sys->i_length > 0 )
            {
                *pi64 = p_sys->i_length;
                return VLC_SUCCESS;
            }
            else if( p_sys->i_mux_rate > 0 )
544
            {
545 546
                *pi64 = (int64_t)1000000 * ( stream_Size( p_demux->s ) / 50 ) /
                    p_sys->i_mux_rate;
547 548 549 550 551 552
                return VLC_SUCCESS;
            }
            *pi64 = 0;
            return VLC_EGENERIC;

        case DEMUX_SET_TIME:
553 554 555 556
            i64 = (int64_t)va_arg( args, int64_t );
            if( p_sys->i_time_track >= 0 && p_sys->i_current_pts > 0 )
            {
                int64_t i_now = p_sys->i_current_pts - p_sys->tk[p_sys->i_time_track].i_first_pts;
557
                int64_t i_pos = vlc_stream_Tell( p_demux->s );
558

559 560 561
                if( !i_now )
                    return i64 ? VLC_EGENERIC : VLC_SUCCESS;

562
                p_sys->i_current_pts = 0;
563
                p_sys->i_last_scr = -1;
564
                i_pos *= (float)i64 / (float)i_now;
565

566
                return vlc_stream_Seek( p_demux->s, i_pos );
567 568 569
            }
            return VLC_EGENERIC;

570 571 572 573 574 575 576
        case DEMUX_GET_TITLE_INFO:
        {
            struct input_title_t ***v = va_arg( args, struct input_title_t*** );
            int *c = va_arg( args, int * );

            *va_arg( args, int* ) = 0; /* Title offset */
            *va_arg( args, int* ) = 0; /* Chapter offset */
577 578
            return vlc_stream_Control( p_demux->s, STREAM_GET_TITLE_INFO, v,
                                       c );
579 580 581
        }

        case DEMUX_SET_TITLE:
582
            return vlc_stream_vaControl( p_demux->s, STREAM_SET_TITLE, args );
583 584

        case DEMUX_SET_SEEKPOINT:
585 586
            return vlc_stream_vaControl( p_demux->s, STREAM_SET_SEEKPOINT,
                                         args );
587

588
        case DEMUX_GET_META:
589
            return vlc_stream_vaControl( p_demux->s, STREAM_GET_META, args );
590

591 592 593 594 595 596 597 598 599 600
        case DEMUX_GET_FPS:
        default:
            return VLC_EGENERIC;
    }
}

/*****************************************************************************
 * Divers:
 *****************************************************************************/

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
601
/* PSResynch: resynch on a system startcode
602 603 604 605 606
 *  It doesn't skip more than 512 bytes
 *  -1 -> error, 0 -> not synch, 1 -> ok
 */
static int ps_pkt_resynch( stream_t *s, uint32_t *pi_code )
{
607
    const uint8_t *p_peek;
608 609 610
    int     i_peek;
    int     i_skip;

611
    if( vlc_stream_Peek( s, &p_peek, 4 ) < 4 )
612 613 614
    {
        return -1;
    }
615 616
    if( p_peek[0] == 0 && p_peek[1] == 0 && p_peek[2] == 1 &&
        p_peek[3] >= 0xb9 )
617 618 619 620 621
    {
        *pi_code = 0x100 | p_peek[3];
        return 1;
    }

622
    if( ( i_peek = vlc_stream_Peek( s, &p_peek, 512 ) ) < 4 )
623 624 625 626 627 628 629 630 631 632 633
    {
        return -1;
    }
    i_skip = 0;

    for( ;; )
    {
        if( i_peek < 4 )
        {
            break;
        }
634 635
        if( p_peek[0] == 0 && p_peek[1] == 0 && p_peek[2] == 1 &&
            p_peek[3] >= 0xb9 )
636 637
        {
            *pi_code = 0x100 | p_peek[3];
638
            return vlc_stream_Read( s, NULL, i_skip ) == i_skip ? 1 : -1;
639 640 641 642 643 644
        }

        p_peek++;
        i_peek--;
        i_skip++;
    }
645
    return vlc_stream_Read( s, NULL, i_skip ) == i_skip ? 0 : -1;
646 647 648 649
}

static block_t *ps_pkt_read( stream_t *s, uint32_t i_code )
{
650
    const uint8_t *p_peek;
651
    int i_peek = vlc_stream_Peek( s, &p_peek, 14 );
652 653
    if( i_peek < 4 )
        return NULL;
654

655 656
    int i_size = ps_pkt_size( p_peek, i_peek );
    if( i_size <= 6 && p_peek[3] > 0xba )
657
    {
658 659 660 661
        /* Special case, search the next start code */
        i_size = 6;
        for( ;; )
        {
662
            i_peek = vlc_stream_Peek( s, &p_peek, i_size + 1024 );
663 664 665 666 667 668
            if( i_peek <= i_size + 4 )
            {
                return NULL;
            }
            while( i_size <= i_peek - 4 )
            {
669 670
                if( p_peek[i_size] == 0x00 && p_peek[i_size+1] == 0x00 &&
                    p_peek[i_size+2] == 0x01 && p_peek[i_size+3] >= 0xb9 )
671
                {
672
                    return vlc_stream_Block( s, i_size );
673 674 675 676 677 678 679 680
                }
                i_size++;
            }
        }
    }
    else
    {
        /* Normal case */
681
        return vlc_stream_Block( s, i_size );
682
    }
683

684
    VLC_UNUSED(i_code);
685 686
    return NULL;
}