ps.c 23.7 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
#define PS_PACKET_PROBE 3

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

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

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

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

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

struct demux_sys_t
{
84
    ps_psm_t    psm;
85
    ps_track_t  tk[PS_TK_COUNT];
86 87

    int64_t     i_scr;
88
    int64_t     i_last_scr;
89
    int         i_mux_rate;
90 91 92
    int64_t     i_length;
    int         i_time_track;
    int64_t     i_current_pts;
93

94 95
    int         i_aob_mlp_count;

96 97
    bool  b_lost_sync;
    bool  b_have_pack;
98
    bool  b_bad_scr;
99
    bool  b_seekable;
100
    bool  b_cdxa;
101 102 103 104 105
};

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

106 107
static int      ps_pkt_resynch( stream_t *, bool, bool );
static block_t *ps_pkt_read   ( stream_t * );
108 109 110 111

/*****************************************************************************
 * Open
 *****************************************************************************/
112
static int OpenCommon( vlc_object_t *p_this, bool b_force )
113 114 115 116
{
    demux_t     *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys;

117
    const uint8_t *p_peek;
118 119
    ssize_t i_peek = 0;
    ssize_t i_offset = 0;
120
    bool b_cdxa = false;
121

122 123
    i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 16 );
    if( i_peek < 16 )
124 125 126 127 128
    {
        msg_Err( p_demux, "cannot peek" );
        return VLC_EGENERIC;
    }

129 130 131 132 133 134 135 136 137 138 139
    if( !memcmp( p_peek, "RIFF", 4 ) && !memcmp( &p_peek[8], "CDXA", 4 ) )
    {
        b_cdxa = true;
        msg_Info( p_demux, "Detected CDXA-PS" );
    }
    else if( b_force )
    {
        msg_Warn( p_demux, "this does not look like an MPEG PS stream, "
                  "continuing anyway" );
    }
    else for( unsigned i=0; i<PS_PACKET_PROBE; i++ )
140
    {
141
        if( i_peek < i_offset + 16 )
142
        {
143 144
            i_peek = vlc_stream_Peek( p_demux->s, &p_peek, i_offset + 16 );
            if( i_peek < i_offset + 16 )
145 146
                return VLC_EGENERIC;
        }
147 148 149 150 151 152 153 154 155 156 157 158 159

        const uint8_t startcode[3] = { 0x00, 0x00, 0x01 };
        const uint8_t *p_header = &p_peek[i_offset];
        if( memcmp( p_header, startcode, 3 ) ||
           ( (p_header[3] & 0xB0) != 0xB0 &&
            !(p_header[3] >= 0xC0 && p_header[3] <= 0xEF) &&
              p_header[3] != 0xFF ) )
            return VLC_EGENERIC;

        ssize_t i_pessize = ps_pkt_size( p_header, 16 );
        if( i_pessize < 5 )
            return VLC_EGENERIC;
        i_offset += i_pessize;
160 161 162 163
    }

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

166 167 168
    p_demux->pf_demux = Demux;
    p_demux->pf_control = Control;

169 170 171
    /* Init p_sys */
    p_sys->i_mux_rate = 0;
    p_sys->i_scr      = -1;
172
    p_sys->i_last_scr = -1;
173 174
    p_sys->i_length   = -1;
    p_sys->i_current_pts = (mtime_t) 0;
175
    p_sys->i_time_track = -1;
176
    p_sys->i_aob_mlp_count = 0;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
177

178 179
    p_sys->b_lost_sync = false;
    p_sys->b_have_pack = false;
180
    p_sys->b_bad_scr   = false;
181
    p_sys->b_seekable  = false;
182
    p_sys->b_cdxa      = b_cdxa;
183

184
    vlc_stream_Control( p_demux->s, STREAM_CAN_SEEK, &p_sys->b_seekable );
185

186
    ps_psm_init( &p_sys->psm );
187 188 189 190 191 192 193
    ps_track_init( p_sys->tk );

    /* TODO prescanning of ES */

    return VLC_SUCCESS;
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
194
static int OpenForce( vlc_object_t *p_this )
195
{
196
    return OpenCommon( p_this, true );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
197
}
198

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
199 200
static int Open( vlc_object_t *p_this )
{
201
    return OpenCommon( p_this, p_this->obj.force );
202 203
}

204 205 206 207 208 209 210
/*****************************************************************************
 * 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;
211 212 213 214 215 216 217 218 219 220 221
    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 );
        }
    }
222

223 224
    ps_psm_destroy( &p_sys->psm );

225 226 227
    free( p_sys );
}

228
static int Probe( demux_t *p_demux, bool b_end )
229 230 231 232 233
{
    demux_sys_t *p_sys = p_demux->p_sys;
    int i_ret, i_id;
    block_t *p_pkt;

234
    i_ret = ps_pkt_resynch( p_demux->s, p_sys->b_cdxa, p_sys->b_have_pack );
235 236
    if( i_ret < 0 )
    {
237
        return VLC_DEMUXER_EOF;
238 239 240 241 242 243
    }
    else if( i_ret == 0 )
    {
        if( !p_sys->b_lost_sync )
            msg_Warn( p_demux, "garbage at input, trying to resync..." );

244
        p_sys->b_lost_sync = true;
245
        return VLC_DEMUXER_SUCCESS;
246 247 248
    }

    if( p_sys->b_lost_sync ) msg_Warn( p_demux, "found sync code" );
249
    p_sys->b_lost_sync = false;
250

251
    if( ( p_pkt = ps_pkt_read( p_demux->s ) ) == NULL )
252
    {
253
        return VLC_DEMUXER_EOF;
254
    }
255 256 257

    i_id = ps_pkt_id( p_pkt );
    if( i_id >= 0xc0 )
258 259
    {
        ps_track_t *tk = &p_sys->tk[PS_ID_TO_TK(i_id)];
260 261
        if( !ps_pkt_parse_pes( VLC_OBJECT(p_demux), p_pkt, tk->i_skip ) &&
             p_pkt->i_pts > VLC_TS_INVALID )
262
        {
263 264 265 266 267 268 269 270
            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;
            }
271 272
        }
    }
273 274 275 276

    if( i_id == PS_STREAM_ID_PACK_HEADER )
        p_sys->b_have_pack = true;

277
    block_Release( p_pkt );
278
    return VLC_DEMUXER_SUCCESS;
279 280
}

281
static bool FindLength( demux_t *p_demux )
282 283 284 285
{
    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
286
    if( !var_CreateGetBool( p_demux, "ps-trust-timestamps" ) )
287
        return true;
288 289 290

    if( p_sys->i_length == -1 ) /* First time */
    {
291 292
        p_sys->i_length = 0;
        /* Check beginning */
293
        int i = 0;
294
        i_current_pos = vlc_stream_Tell( p_demux->s );
295
        while( i < 40 && Probe( p_demux, false ) > 0 ) i++;
296 297

        /* Check end */
298
        i_size = stream_Size( p_demux->s );
299
        i_end = VLC_CLIP( i_size, 0, 200000 );
300
        if( vlc_stream_Seek( p_demux->s, i_size - i_end ) == VLC_SUCCESS )
301 302
        {
            i = 0;
303
            while( i < 400 && Probe( p_demux, true ) > 0 ) i++;
304
            if( i_current_pos >= 0 &&
305
                vlc_stream_Seek( p_demux->s, i_current_pos ) != VLC_SUCCESS )
306 307 308
                    return false;
        }
        else return false;
309 310
    }

Tobias Güntner's avatar
Tobias Güntner committed
311
    /* Find the longest track */
312
    for( int i = 0; i < PS_TK_COUNT; i++ )
313 314
    {
        ps_track_t *tk = &p_sys->tk[i];
315
        if( tk->i_last_pts > 0 &&
Tobias Güntner's avatar
Tobias Güntner committed
316 317 318 319
            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 )
320
            {
Tobias Güntner's avatar
Tobias Güntner committed
321 322
                p_sys->i_length = i_length;
                p_sys->i_time_track = i;
323
                msg_Dbg( p_demux, "we found a length of: %"PRId64 "s", p_sys->i_length / CLOCK_FREQ );
324
            }
Tobias Güntner's avatar
Tobias Güntner committed
325
        }
326
    }
327
    return true;
328 329
}

330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
static void NotifyDiscontinuity( ps_track_t *p_tk, es_out_t *out )
{
    bool b_selected;
    for( size_t i = 0; i < PS_TK_COUNT; i++ )
    {
        ps_track_t *tk = &p_tk[i];
        if( tk->b_seen && tk->es &&
                es_out_Control( out, ES_OUT_GET_ES_STATE, tk->es, &b_selected ) == VLC_SUCCESS
                && b_selected )
        {
            tk->i_next_block_flags |= BLOCK_FLAG_DISCONTINUITY;
        }
    }
}

345 346 347 348 349 350
/*****************************************************************************
 * Demux:
 *****************************************************************************/
static int Demux( demux_t *p_demux )
{
    demux_sys_t *p_sys = p_demux->p_sys;
351
    int i_ret, i_mux_rate;
352
    block_t *p_pkt;
353

354
    i_ret = ps_pkt_resynch( p_demux->s, p_sys->b_cdxa, p_sys->b_have_pack );
355 356
    if( i_ret < 0 )
    {
357
        return VLC_DEMUXER_EOF;
358 359 360
    }
    else if( i_ret == 0 )
    {
361
        if( !p_sys->b_lost_sync )
362 363 364 365 366
        {
            msg_Warn( p_demux, "garbage at input from %"PRIu64", trying to resync...",
                                vlc_stream_Tell(p_demux->s) );
            NotifyDiscontinuity( p_sys->tk, p_demux->out );
        }
367

368
        p_sys->b_lost_sync = true;
369
        return VLC_DEMUXER_SUCCESS;
370 371
    }

372
    if( p_sys->b_lost_sync ) msg_Warn( p_demux, "found sync code" );
373
    p_sys->b_lost_sync = false;
374

375
    if( p_sys->i_length < 0 && p_sys->b_seekable )
376 377 378 379
    {
        if( !FindLength( p_demux ) )
            return VLC_DEMUXER_EGENERIC;
    }
380

381
    if( ( p_pkt = ps_pkt_read( p_demux->s ) ) == NULL )
382
    {
383
        return VLC_DEMUXER_EOF;
384 385
    }

386
    if( p_pkt->i_buffer < 4 )
387
    {
388 389 390 391 392 393 394 395
        block_Release( p_pkt );
        return VLC_DEMUXER_EGENERIC;
    }

    const uint8_t i_stream_id = p_pkt->p_buffer[3];
    switch( i_stream_id )
    {
    case PS_STREAM_ID_END_STREAM:
396 397 398
        block_Release( p_pkt );
        break;

399
    case PS_STREAM_ID_PACK_HEADER:
400
        if( !ps_pkt_parse_pack( p_pkt, &p_sys->i_scr, &i_mux_rate ) )
401
        {
402
            p_sys->i_last_scr = p_sys->i_scr;
403
            if( !p_sys->b_have_pack ) p_sys->b_have_pack = true;
404 405
            /* done later on to work around bad vcd/svcd streams */
            /* es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_sys->i_scr ); */
406 407 408 409
            if( i_mux_rate > 0 ) p_sys->i_mux_rate = i_mux_rate;
        }
        block_Release( p_pkt );
        break;
410

411
    case PS_STREAM_ID_SYSTEM_HEADER:
412
        if( !ps_pkt_parse_system( p_pkt, &p_sys->psm, p_sys->tk ) )
413 414 415
        {
            int i;
            for( i = 0; i < PS_TK_COUNT; i++ )
416
            {
417 418 419
                ps_track_t *tk = &p_sys->tk[i];

                if( tk->b_seen && !tk->es && tk->fmt.i_cat != UNKNOWN_ES )
420
                {
421
                    tk->es = es_out_Add( p_demux->out, &tk->fmt );
422 423 424
                }
            }
        }
425 426
        block_Release( p_pkt );
        break;
427

428
    case PS_STREAM_ID_MAP:
429 430 431
        if( p_sys->psm.i_version == 0xFFFF )
            msg_Dbg( p_demux, "contains a PSM");

432
        ps_psm_fill( &p_sys->psm, p_pkt, p_sys->tk, p_demux->out );
433 434
        block_Release( p_pkt );
        break;
435

436
    default:
437 438 439 440 441 442 443 444
        /* Reject non video/audio nor PES */
        if( i_stream_id < 0xC0 || i_stream_id > 0xEF )
        {
            block_Release( p_pkt );
            break;
        }
        //ft
    case PS_STREAM_ID_PRIVATE_STREAM1:
445
        {
446
            int i_id = ps_pkt_id( p_pkt );
447 448 449 450 451 452 453 454 455 456 457 458 459
            /* 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;
            }

460
            bool b_new = false;
461
            ps_track_t *tk = &p_sys->tk[PS_ID_TO_TK(i_id)];
462

463 464
            if( !tk->b_seen )
            {
465
                if( !ps_track_fill( tk, &p_sys->psm, i_id, p_pkt ) )
466
                {
467
                    tk->es = es_out_Add( p_demux->out, &tk->fmt );
468
                    b_new = true;
469
                }
470 471 472 473
                else
                {
                    msg_Dbg( p_demux, "es id=0x%x format unknown", i_id );
                }
474
                tk->b_seen = true;
475
            }
476 477 478 479

            /* The popular VCD/SVCD subtitling WinSubMux does not
             * renumber the SCRs when merging subtitles into the PES */
            if( tk->b_seen &&
480 481
                ( tk->fmt.i_codec == VLC_CODEC_OGT ||
                  tk->fmt.i_codec == VLC_CODEC_CVD ) )
482 483
            {
                p_sys->i_scr = -1;
484
                p_sys->i_last_scr = -1;
485 486
            }

487 488
            if( p_sys->i_scr >= 0 && !p_sys->b_bad_scr )
            {
489 490 491 492 493
                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 );
494
                    p_sys->b_bad_scr = true; /* Disable Offset SCR */
495
                }
496 497 498
                else
                    es_out_Control( p_demux->out, ES_OUT_SET_PCR, VLC_TS_0 + p_sys->i_scr );
            }
499

500
            if( tk->b_seen && tk->es &&
501
                !ps_pkt_parse_pes( VLC_OBJECT(p_demux), p_pkt, tk->i_skip ) )
502
            {
503 504 505 506 507 508 509 510 511
                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;
                }

512
                if( ((!b_new && !p_sys->b_have_pack) || p_sys->b_bad_scr) &&
513
                    (tk->fmt.i_cat == AUDIO_ES) &&
514
                    (p_pkt->i_pts > VLC_TS_INVALID) )
515 516
                {
                    /* A hack to sync the A/V on PES files. */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
517
                    msg_Dbg( p_demux, "force SCR: %"PRId64, p_pkt->i_pts );
518 519
                    es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_pkt->i_pts );
                }
520 521 522 523 524 525 526
                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;
                }
527

528 529 530 531 532
                if( (int64_t)p_pkt->i_pts > p_sys->i_current_pts )
                {
                    p_sys->i_current_pts = (int64_t)p_pkt->i_pts;
                }

533 534 535 536 537 538
                if( tk->i_next_block_flags )
                {
                    p_pkt->i_flags = tk->i_next_block_flags;
                    tk->i_next_block_flags = 0;
                }

539
                es_out_Send( p_demux->out, tk->es, p_pkt );
540 541 542 543 544
            }
            else
            {
                block_Release( p_pkt );
            }
545 546

            p_sys->i_scr = -1;
547
        }
548
        break;
549
    }
550

551
    demux_UpdateTitleFromStream( p_demux );
552
    return VLC_DEMUXER_SUCCESS;
553 554 555 556 557 558 559 560 561 562
}

/*****************************************************************************
 * 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;
563
    int i_ret;
564 565 566

    switch( i_query )
    {
567 568 569 570
        case DEMUX_CAN_SEEK:
            *va_arg( args, bool * ) = p_sys->b_seekable;
            return VLC_SUCCESS;

571 572 573 574 575
        case DEMUX_GET_POSITION:
            pf = (double*) va_arg( args, double* );
            i64 = stream_Size( p_demux->s );
            if( i64 > 0 )
            {
576
                double current = vlc_stream_Tell( p_demux->s );
577
                *pf = current / (double)i64;
578 579 580 581 582 583
            }
            else
            {
                *pf = 0.0;
            }
            return VLC_SUCCESS;
584

585 586 587
        case DEMUX_SET_POSITION:
            f = (double) va_arg( args, double );
            i64 = stream_Size( p_demux->s );
588
            p_sys->i_current_pts = 0;
589
            p_sys->i_last_scr = -1;
590

591 592 593 594 595 596 597
            i_ret = vlc_stream_Seek( p_demux->s, (int64_t)(i64 * f) );
            if( i_ret == VLC_SUCCESS )
            {
                NotifyDiscontinuity( p_sys->tk, p_demux->out );
                return i_ret;
            }
            break;
598 599 600

        case DEMUX_GET_TIME:
            pi64 = (int64_t*)va_arg( args, int64_t * );
601
            if( p_sys->i_time_track >= 0 && p_sys->i_current_pts > 0 )
602 603 604 605
            {
                *pi64 = p_sys->i_current_pts - p_sys->tk[p_sys->i_time_track].i_first_pts;
                return VLC_SUCCESS;
            }
606 607
            if( p_sys->i_mux_rate > 0 )
            {
608
                *pi64 = (int64_t)1000000 * ( vlc_stream_Tell( p_demux->s ) / 50 ) /
609
                    p_sys->i_mux_rate;
610 611 612
                return VLC_SUCCESS;
            }
            *pi64 = 0;
613
            break;
614 615 616

        case DEMUX_GET_LENGTH:
            pi64 = (int64_t*)va_arg( args, int64_t * );
617 618 619 620 621 622
            if( p_sys->i_length > 0 )
            {
                *pi64 = p_sys->i_length;
                return VLC_SUCCESS;
            }
            else if( p_sys->i_mux_rate > 0 )
623
            {
624 625
                *pi64 = (int64_t)1000000 * ( stream_Size( p_demux->s ) / 50 ) /
                    p_sys->i_mux_rate;
626 627 628
                return VLC_SUCCESS;
            }
            *pi64 = 0;
629
            break;
630 631

        case DEMUX_SET_TIME:
632 633 634 635
            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;
636
                int64_t i_pos = vlc_stream_Tell( p_demux->s );
637

638 639 640
                if( !i_now )
                    return i64 ? VLC_EGENERIC : VLC_SUCCESS;

641
                p_sys->i_current_pts = 0;
642
                p_sys->i_last_scr = -1;
643
                i_pos *= (float)i64 / (float)i_now;
644

645 646 647 648 649 650
                i_ret = vlc_stream_Seek( p_demux->s, i_pos );
                if( i_ret == VLC_SUCCESS )
                {
                    NotifyDiscontinuity( p_sys->tk, p_demux->out );
                    return i_ret;
                }
651
            }
652
            break;
653

654 655 656 657 658 659 660
        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 */
661 662
            return vlc_stream_Control( p_demux->s, STREAM_GET_TITLE_INFO, v,
                                       c );
663 664 665
        }

        case DEMUX_SET_TITLE:
666
            return vlc_stream_vaControl( p_demux->s, STREAM_SET_TITLE, args );
667 668

        case DEMUX_SET_SEEKPOINT:
669 670
            return vlc_stream_vaControl( p_demux->s, STREAM_SET_SEEKPOINT,
                                         args );
671

672
        case DEMUX_GET_META:
673
            return vlc_stream_vaControl( p_demux->s, STREAM_GET_META, args );
674

675 676
        case DEMUX_GET_FPS:
        default:
677
            break;
678
    }
679
    return VLC_EGENERIC;
680 681 682 683 684 685
}

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

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
686
/* PSResynch: resynch on a system startcode
687 688 689
 *  It doesn't skip more than 512 bytes
 *  -1 -> error, 0 -> not synch, 1 -> ok
 */
690
static int ps_pkt_resynch( stream_t *s, bool b_cdxa, bool b_pack )
691
{
692
    const uint8_t *p_peek;
693 694 695
    int     i_peek;
    int     i_skip;

696
    if( vlc_stream_Peek( s, &p_peek, 4 ) < 4 )
697 698 699
    {
        return -1;
    }
700
    if( p_peek[0] == 0 && p_peek[1] == 0 && p_peek[2] == 1 &&
701
        p_peek[3] >= PS_STREAM_ID_END_STREAM )
702 703 704 705
    {
        return 1;
    }

706
    if( ( i_peek = vlc_stream_Peek( s, &p_peek, 512 ) ) < 4 )
707 708 709 710 711 712 713 714 715 716 717
    {
        return -1;
    }
    i_skip = 0;

    for( ;; )
    {
        if( i_peek < 4 )
        {
            break;
        }
718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737
        /* Handle mid stream 24 bytes padding+CRC creating emulated sync codes with incorrect
           PES sizes and frelling up to UINT16_MAX bytes followed by 24 bytes CDXA Header */
        if( b_cdxa && i_skip == 0 && i_peek >= 48 )
        {
            size_t i = 0;
            while( i<24 && p_peek[i] == 0 )
                i++;
            if( i == 20 )
            {
                const uint8_t cdxasynccode[12] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                   0xff, 0xff, 0xff, 0xff, 0xff, 0x00 };
                if( !memcmp( &p_peek[24], cdxasynccode, 12 ) )
                {
                    i_peek -= 48;
                    p_peek += 48;
                    i_skip += 48;
                    continue;
                }
            }
        }
738

739
        if( p_peek[0] == 0 && p_peek[1] == 0 && p_peek[2] == 1 &&
740 741
            p_peek[3] >= PS_STREAM_ID_END_STREAM &&
            ( !b_pack || p_peek[3] == PS_STREAM_ID_PACK_HEADER ) )
742
        {
743
            return vlc_stream_Read( s, NULL, i_skip ) == i_skip ? 1 : -1;
744 745 746 747 748 749
        }

        p_peek++;
        i_peek--;
        i_skip++;
    }
750
    return vlc_stream_Read( s, NULL, i_skip ) == i_skip ? 0 : -1;
751 752
}

753
static block_t *ps_pkt_read( stream_t *s )
754
{
755
    const uint8_t *p_peek;
756
    int i_peek = vlc_stream_Peek( s, &p_peek, 14 );
757 758
    if( i_peek < 4 )
        return NULL;
759

760
    int i_size = ps_pkt_size( p_peek, i_peek );
761
    if( i_size <= 6 && p_peek[3] > PS_STREAM_ID_PACK_HEADER )
762
    {
763 764 765 766
        /* Special case, search the next start code */
        i_size = 6;
        for( ;; )
        {
767
            i_peek = vlc_stream_Peek( s, &p_peek, i_size + 1024 );
768 769 770 771 772 773
            if( i_peek <= i_size + 4 )
            {
                return NULL;
            }
            while( i_size <= i_peek - 4 )
            {
774
                if( p_peek[i_size] == 0x00 && p_peek[i_size+1] == 0x00 &&
775
                    p_peek[i_size+2] == 0x01 && p_peek[i_size+3] >= PS_STREAM_ID_END_STREAM )
776
                {
777
                    return vlc_stream_Block( s, i_size );
778 779 780 781 782 783 784 785
                }
                i_size++;
            }
        }
    }
    else
    {
        /* Normal case */
786
        return vlc_stream_Block( s, i_size );
787
    }
788

789 790
    return NULL;
}