mpeg_ts.c 33.3 KB
Newer Older
Sam Hocevar's avatar
 
Sam Hocevar committed
1
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
2
 * mpeg_ts.c : Transport Stream input module for vlc
Sam Hocevar's avatar
 
Sam Hocevar committed
3
 *****************************************************************************
4
 * Copyright (C) 2000-2001 VideoLAN
5
 * $Id: mpeg_ts.c,v 1.17 2002/07/31 20:56:52 sam Exp $
Sam Hocevar's avatar
 
Sam Hocevar committed
6
 *
Sam Hocevar's avatar
 
Sam Hocevar committed
7
 * Authors: Henri Fallon <henri@via.ecp.fr>
8
 *          Johan Bilien <jobi@via.ecp.fr>
Sam Hocevar's avatar
 
Sam Hocevar committed
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
 *
 * 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
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
Christophe Massiot's avatar
Christophe Massiot committed
28 29 30
#include <stdlib.h>
#include <string.h>
#include <errno.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
31

32 33
#include <vlc/vlc.h>
#include <vlc/input.h>
Christophe Massiot's avatar
Christophe Massiot committed
34

35 36
#include "iso_lang.h"

37
#if defined MODULE_NAME_IS_mpeg_ts_dvbpsi
Sam Hocevar's avatar
Sam Hocevar committed
38 39 40 41 42 43 44 45 46 47 48 49 50
#   ifdef HAVE_DVBPSI_DR_H
#       include <dvbpsi/dvbpsi.h>
#       include <dvbpsi/descriptor.h>
#       include <dvbpsi/pat.h>
#       include <dvbpsi/pmt.h>
#       include <dvbpsi/dr.h>
#   else
#       include "dvbpsi.h"
#       include "descriptor.h"
#       include "tables/pat.h"
#       include "tables/pmt.h"
#       include "descriptors/dr.h"
#   endif
51
#endif
Sam Hocevar's avatar
Sam Hocevar committed
52

Sam Hocevar's avatar
 
Sam Hocevar committed
53
/*****************************************************************************
Christophe Massiot's avatar
Christophe Massiot committed
54
 * Constants
Sam Hocevar's avatar
 
Sam Hocevar committed
55
 *****************************************************************************/
Christophe Massiot's avatar
Christophe Massiot committed
56 57 58 59 60
#define TS_READ_ONCE 200

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
61 62
static int  Activate ( vlc_object_t * );
static int  Demux ( input_thread_t * );
Sam Hocevar's avatar
 
Sam Hocevar committed
63

64
#if defined MODULE_NAME_IS_mpeg_ts
65 66
static void TSDemuxPSI ( input_thread_t *, data_packet_t *,
                          es_descriptor_t *, vlc_bool_t );
67 68 69 70
static void TSDecodePAT( input_thread_t *, es_descriptor_t *);
static void TSDecodePMT( input_thread_t *, es_descriptor_t *);
#define PSI_CALLBACK TSDemuxPSI
#elif defined MODULE_NAME_IS_mpeg_ts_dvbpsi
71 72 73 74
static void TS_DVBPSI_DemuxPSI  ( input_thread_t *, data_packet_t *,
                                  es_descriptor_t *, vlc_bool_t );
static void TS_DVBPSI_HandlePAT ( input_thread_t *, dvbpsi_pat_t * );
static void TS_DVBPSI_HandlePMT ( input_thread_t *, dvbpsi_pmt_t * );
75 76 77
#define PSI_CALLBACK TS_DVBPSI_DemuxPSI
#endif

Sam Hocevar's avatar
 
Sam Hocevar committed
78
/*****************************************************************************
79
 * Module descriptor
Sam Hocevar's avatar
 
Sam Hocevar committed
80
 *****************************************************************************/
81
vlc_module_begin();
82
#if defined MODULE_NAME_IS_mpeg_ts
83 84 85
    set_description( _("ISO 13818-1 MPEG Transport Stream input") );
    set_capability( "demux", 160 );
    add_shortcut( "ts" );
86
#elif defined MODULE_NAME_IS_mpeg_ts_dvbpsi
87 88 89
    set_description( _("ISO 13818-1 MPEG Transport Stream input (libdvbpsi)") );
    set_capability( "demux", 170 );
    add_shortcut( "ts_dvbpsi" );
90
#endif
91 92
    set_callbacks( Activate, NULL );
vlc_module_end();
Christophe Massiot's avatar
Christophe Massiot committed
93 94

/*****************************************************************************
95
 * Activate: initializes TS structures
Christophe Massiot's avatar
Christophe Massiot committed
96
 *****************************************************************************/
97
static int Activate( vlc_object_t * p_this )
Christophe Massiot's avatar
Christophe Massiot committed
98
{
99
    input_thread_t *    p_input = (input_thread_t *)p_this;
Christophe Massiot's avatar
Christophe Massiot committed
100 101 102 103 104
    es_descriptor_t     * p_pat_es;
    es_ts_data_t        * p_demux_data;
    stream_ts_data_t    * p_stream_data;
    byte_t              * p_peek;

105 106 107
    /* Set the demux function */
    p_input->pf_demux = Demux;

Christophe Massiot's avatar
Christophe Massiot committed
108 109 110 111 112 113 114 115 116 117
    /* Initialize access plug-in structures. */
    if( p_input->i_mtu == 0 )
    {
        /* Improve speed. */
        p_input->i_bufsize = INPUT_DEFAULT_BUFSIZE;
    }

    /* Have a peep at the show. */
    if( input_Peek( p_input, &p_peek, 1 ) < 1 )
    {
118
        msg_Err( p_input, "cannot peek()" );
Christophe Massiot's avatar
Christophe Massiot committed
119 120 121 122 123
        return( -1 );
    }

    if( *p_peek != TS_SYNC_CODE )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
124
        if( *p_input->psz_demux && !strncmp( p_input->psz_demux, "ts", 3 ) )
Christophe Massiot's avatar
Christophe Massiot committed
125 126
        {
            /* User forced */
127
            msg_Err( p_input, "this does not look like a TS stream, continuing" );
Christophe Massiot's avatar
Christophe Massiot committed
128 129 130
        }
        else
        {
131
            msg_Warn( p_input, "TS module discarded (no sync)" );
Christophe Massiot's avatar
Christophe Massiot committed
132 133 134 135 136 137 138 139 140 141 142
            return( -1 );
        }
    }

    /* Adapt the bufsize for our only use. */
    if( p_input->i_mtu != 0 )
    {
        /* Have minimum granularity to avoid bottlenecks at the input level. */
        p_input->i_bufsize = (p_input->i_mtu / TS_PACKET_SIZE) * TS_PACKET_SIZE;
    }

143 144
    vlc_mutex_lock( &p_input->stream.stream_lock );

Christophe Massiot's avatar
Christophe Massiot committed
145 146 147 148
    if( input_InitStream( p_input, sizeof( stream_ts_data_t ) ) == -1 )
    {
        return( -1 );
    }
149
    
Christophe Massiot's avatar
Christophe Massiot committed
150 151 152
    p_stream_data = (stream_ts_data_t *)p_input->stream.p_demux_data;
    p_stream_data->i_pat_version = PAT_UNINITIALIZED ;

153 154 155 156 157 158
#ifdef MODULE_NAME_IS_mpeg_ts_dvbpsi
    p_stream_data->p_pat_handle = (dvbpsi_handle *)
      dvbpsi_AttachPAT( (dvbpsi_pat_callback) &TS_DVBPSI_HandlePAT, p_input ); 

    if( p_stream_data->p_pat_handle == NULL )
    {
159
        msg_Err( p_input, "could not create PAT decoder" );
160 161 162 163
        return( -1 );
    }
#endif
    
Christophe Massiot's avatar
Christophe Massiot committed
164 165 166 167 168 169 170 171 172 173 174
    /* We'll have to catch the PAT in order to continue
     * Then the input will catch the PMT and then the others ES
     * The PAT es is indepedent of any program. */
    p_pat_es = input_AddES( p_input, NULL,
                            0x00, sizeof( es_ts_data_t ) );
    p_demux_data = (es_ts_data_t *)p_pat_es->p_demux_data;
    p_demux_data->b_psi = 1;
    p_demux_data->i_psi_type = PSI_IS_PAT;
    p_demux_data->p_psi_section = malloc(sizeof(psi_section_t));
    p_demux_data->p_psi_section->b_is_complete = 1;

175 176
    vlc_mutex_unlock( &p_input->stream.stream_lock );
    
Christophe Massiot's avatar
Christophe Massiot committed
177 178 179 180
    return( 0 );
}

/*****************************************************************************
181
 * Demux: reads and demuxes data packets
Christophe Massiot's avatar
Christophe Massiot committed
182 183 184 185
 *****************************************************************************
 * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
 * packets.
 *****************************************************************************/
186
static int Demux( input_thread_t * p_input )
Christophe Massiot's avatar
Christophe Massiot committed
187 188 189 190 191 192 193 194 195
{
    int             i_read_once = (p_input->i_mtu ?
                                   p_input->i_bufsize / TS_PACKET_SIZE :
                                   TS_READ_ONCE);
    int             i;

    for( i = 0; i < i_read_once; i++ )
    {
        data_packet_t *     p_data;
196
        ssize_t             i_result;
Christophe Massiot's avatar
Christophe Massiot committed
197

198
        i_result = input_ReadTS( p_input, &p_data );
Christophe Massiot's avatar
Christophe Massiot committed
199

200
        if( i_result <= 0 )
Christophe Massiot's avatar
Christophe Massiot committed
201
        {
202
            return( i_result );
Christophe Massiot's avatar
Christophe Massiot committed
203 204
        }

205
        input_DemuxTS( p_input, p_data, (psi_callback_t) &PSI_CALLBACK );
Christophe Massiot's avatar
Christophe Massiot committed
206 207 208 209 210
    }

    return( i_read_once );
}

211 212 213 214 215 216 217 218 219 220

#if defined MODULE_NAME_IS_mpeg_ts
/*
 * PSI demultiplexing and decoding without libdvbpsi
 */

/*****************************************************************************
 * DemuxPSI : makes up complete PSI data
 *****************************************************************************/
static void TSDemuxPSI( input_thread_t * p_input, data_packet_t * p_data, 
221
        es_descriptor_t * p_es, vlc_bool_t b_unit_start )
222 223 224 225 226 227 228 229 230 231 232 233 234 235
{
    es_ts_data_t  * p_demux_data;
    
    p_demux_data = (es_ts_data_t *)p_es->p_demux_data;

#define p_psi (p_demux_data->p_psi_section)
#define p (p_data->p_payload_start)

    if( b_unit_start )
    {
        /* unit_start set to 1 -> presence of a pointer field
         * (see ISO/IEC 13818 (2.4.4.2) which should be set to 0x00 */
        if( (u8)p[0] != 0x00 )
        {
236 237
            msg_Warn( p_input,
                      "non-zero pointer field found, trying to continue" );
238 239 240 241 242 243 244 245 246 247 248
            p+=(u8)p[0];
        }
        else
        {
            p++;
        }

        /* This is the begining of a new section */

        if( ((u8)(p[1]) & 0xc0) != 0x80 ) 
        {
249
            msg_Warn( p_input, "invalid PSI packet" );
250 251 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
            p_psi->b_trash = 1;
        }
        else 
        {
            p_psi->i_section_length = ((p[1] & 0xF) << 8) | p[2];
            p_psi->b_section_complete = 0;
            p_psi->i_read_in_section = 0;
            p_psi->i_section_number = (u8)p[6];

            if( p_psi->b_is_complete || p_psi->i_section_number == 0 )
            {
                /* This is a new PSI packet */
                p_psi->b_is_complete = 0;
                p_psi->b_trash = 0;
                p_psi->i_version_number = ( p[5] >> 1 ) & 0x1f;
                p_psi->i_last_section_number = (u8)p[7];

                /* We'll write at the begining of the buffer */
                p_psi->p_current = p_psi->buffer;
            }
            else
            {
                if( p_psi->b_section_complete )
                {
                    /* New Section of an already started PSI */
                    p_psi->b_section_complete = 0;
                    
                    if( p_psi->i_version_number != (( p[5] >> 1 ) & 0x1f) )
                    {
279 280
                        msg_Warn( p_input,
                                  "PSI version differs inside same PAT" );
281 282 283 284
                        p_psi->b_trash = 1;
                    }
                    if( p_psi->i_section_number + 1 != (u8)p[6] )
                    {
285 286
                        msg_Warn( p_input,
                                  "PSI Section discontinuity, packet lost?" );
287 288 289 290 291 292 293
                        p_psi->b_trash = 1;
                    }
                    else
                        p_psi->i_section_number++;
                }
                else
                {
294
                    msg_Warn( p_input, "got unexpected new PSI section" );
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
                    p_psi->b_trash = 1;
                }
            }
        }
    } /* b_unit_start */
    
    if( !p_psi->b_trash )
    {
        /* read */
        if( (p_data->p_payload_end - p) >=
            ( p_psi->i_section_length - p_psi->i_read_in_section ) )
        {
            /* The end of the section is in this TS packet */
            memcpy( p_psi->p_current, p, 
            (p_psi->i_section_length - p_psi->i_read_in_section) );
    
            p_psi->b_section_complete = 1;
            p_psi->p_current += 
                (p_psi->i_section_length - p_psi->i_read_in_section);
                        
            if( p_psi->i_section_number == p_psi->i_last_section_number )
            {
                /* This was the last section of PSI */
                p_psi->b_is_complete = 1;

                switch( p_demux_data->i_psi_type)
                {
                case PSI_IS_PAT:
                    TSDecodePAT( p_input, p_es );
                    break;
                case PSI_IS_PMT:
                    TSDecodePMT( p_input, p_es );
                    break;
                default:
329
                    msg_Warn( p_input, "received unknown PSI in DemuxPSI" );
330 331 332 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
                }
            }
        }
        else
        {
            memcpy( p_psi->buffer, p, p_data->p_payload_end - p );
            p_psi->i_read_in_section += p_data->p_payload_end - p;

            p_psi->p_current += p_data->p_payload_end - p;
        }
    }

#undef p_psi    
#undef p
   
    input_DeletePacket( p_input->p_method_data, p_data );
    
    return ;
}

/*****************************************************************************
 * DecodePAT : Decodes Programm association table and deal with it
 *****************************************************************************/
static void TSDecodePAT( input_thread_t * p_input, es_descriptor_t * p_es )
{
    stream_ts_data_t  * p_stream_data;
    es_ts_data_t      * p_demux_data;

    pgrm_descriptor_t * p_pgrm;
    es_descriptor_t   * p_current_es;
    byte_t            * p_current_data;           

    int                 i_section_length, i_program_id, i_pmt_pid;
    int                 i_loop, i_current_section;

365
    vlc_bool_t          b_changed = 0;
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 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 429 430 431 432 433 434 435 436 437 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 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574

    p_demux_data = (es_ts_data_t *)p_es->p_demux_data;
    p_stream_data = (stream_ts_data_t *)p_input->stream.p_demux_data;
    
#define p_psi (p_demux_data->p_psi_section)

    /* Not so fast, Mike ! If the PAT version has changed, we first check
     * that its content has really changed before doing anything */
    if( p_stream_data->i_pat_version != p_psi->i_version_number )
    {
        int i_programs = p_input->stream.i_pgrm_number;

        p_current_data = p_psi->buffer;

        do
        {
            i_section_length = ((u32)(p_current_data[1] & 0xF) << 8) |
                                 p_current_data[2];
            i_current_section = (u8)p_current_data[6];
    
            for( i_loop = 0;
                 ( i_loop < (i_section_length - 9) / 4 ) && !b_changed;
                 i_loop++ )
            {
                i_program_id = ( (u32)*(p_current_data + i_loop * 4 + 8) << 8 )
                                 | *(p_current_data + i_loop * 4 + 9);
                i_pmt_pid = ( ((u32)*(p_current_data + i_loop * 4 + 10) & 0x1F)
                                    << 8 )
                               | *(p_current_data + i_loop * 4 + 11);

                if( i_program_id )
                {
                    if( (p_pgrm = input_FindProgram( p_input, i_program_id ))
                        && (p_current_es = input_FindES( p_input, i_pmt_pid ))
                        && p_current_es->p_pgrm == p_pgrm
                        && p_current_es->i_id == i_pmt_pid
                        && ((es_ts_data_t *)p_current_es->p_demux_data)->b_psi
                        && ((es_ts_data_t *)p_current_es->p_demux_data)
                            ->i_psi_type == PSI_IS_PMT )
                    {
                        i_programs--;
                    }
                    else
                    {
                        b_changed = 1;
                    }
                }
            }
            
            p_current_data += 3 + i_section_length;

        } while( ( i_current_section < p_psi->i_last_section_number )
                  && !b_changed );

        /* If we didn't find the expected amount of programs, the PAT has
         * changed. Otherwise, it only changed if b_changed is already != 0 */
        b_changed = b_changed || i_programs;
    }

    if( b_changed )
    {
        /* PAT has changed. We are going to delete all programs and 
         * create new ones. We chose not to only change what was needed
         * as a PAT change may mean the stream is radically changing and
         * this is a secure method to avoid crashes */
        es_ts_data_t      * p_es_demux;
        pgrm_ts_data_t    * p_pgrm_demux;
        
        p_current_data = p_psi->buffer;

        /* Delete all programs */
        while( p_input->stream.i_pgrm_number )
        {
            input_DelProgram( p_input, p_input->stream.pp_programs[0] );
        }
        
        do
        {
            i_section_length = ((u32)(p_current_data[1] & 0xF) << 8) |
                                 p_current_data[2];
            i_current_section = (u8)p_current_data[6];
    
            for( i_loop = 0; i_loop < (i_section_length - 9) / 4 ; i_loop++ )
            {
                i_program_id = ( (u32)*(p_current_data + i_loop * 4 + 8) << 8 )
                                 | *(p_current_data + i_loop * 4 + 9);
                i_pmt_pid = ( ((u32)*(p_current_data + i_loop * 4 + 10) & 0x1F)
                                    << 8 )
                               | *(p_current_data + i_loop * 4 + 11);
    
                /* If program = 0, we're having info about NIT not PMT */
                if( i_program_id )
                {
                    /* Add this program */
                    p_pgrm = input_AddProgram( p_input, i_program_id, 
                                               sizeof( pgrm_ts_data_t ) );
                   
                    /* whatis the PID of the PMT of this program */
                    p_pgrm_demux = (pgrm_ts_data_t *)p_pgrm->p_demux_data;
                    p_pgrm_demux->i_pmt_version = PMT_UNINITIALIZED;
    
                    /* Add the PMT ES to this program */
                    p_current_es = input_AddES( p_input, p_pgrm,(u16)i_pmt_pid,
                                        sizeof( es_ts_data_t) );
                    p_es_demux = (es_ts_data_t *)p_current_es->p_demux_data;
                    p_es_demux->b_psi = 1;
                    p_es_demux->i_psi_type = PSI_IS_PMT;
                    
                    p_es_demux->p_psi_section = 
                                            malloc( sizeof( psi_section_t ) );
                    p_es_demux->p_psi_section->b_is_complete = 0;
                }
            }
            
            p_current_data += 3 + i_section_length;

        } while( i_current_section < p_psi->i_last_section_number );

        /* Go to the beginning of the next section */
        p_stream_data->i_pat_version = p_psi->i_version_number;

    }
#undef p_psi

}

/*****************************************************************************
 * DecodePMT : decode a given Program Stream Map
 * ***************************************************************************
 * When the PMT changes, it may mean a deep change in the stream, and it is
 * careful to delete the ES and add them again. If the PMT doesn't change,
 * there no need to do anything.
 *****************************************************************************/
static void TSDecodePMT( input_thread_t * p_input, es_descriptor_t * p_es )
{

    pgrm_ts_data_t            * p_pgrm_data;
    es_ts_data_t              * p_demux_data;

    p_demux_data = (es_ts_data_t *)p_es->p_demux_data;
    p_pgrm_data = (pgrm_ts_data_t *)p_es->p_pgrm->p_demux_data;
    
#define p_psi (p_demux_data->p_psi_section)

    if( p_psi->i_version_number != p_pgrm_data->i_pmt_version ) 
    {
        es_descriptor_t   * p_new_es;  
        es_ts_data_t      * p_es_demux;
        byte_t            * p_current_data, * p_current_section;
        int                 i_section_length,i_current_section;
        int                 i_prog_info_length, i_loop;
        int                 i_es_info_length, i_pid, i_stream_type;
        
        p_current_section = p_psi->buffer;
        p_current_data = p_psi->buffer;

        p_pgrm_data->i_pcr_pid = ( ((u32)*(p_current_section + 8) & 0x1F) << 8 ) |
                                    *(p_current_section + 9);


        /* Lock stream information */
        vlc_mutex_lock( &p_input->stream.stream_lock );

        /* Delete all ES in this program  except the PSI. We start from the
         * end because i_es_number gets decremented after each deletion. */
        for( i_loop = p_es->p_pgrm->i_es_number ; i_loop ; )
        {
            i_loop--;
            p_es_demux = (es_ts_data_t *)
                         p_es->p_pgrm->pp_es[i_loop]->p_demux_data;
            if ( ! p_es_demux->b_psi )
            {
                input_DelES( p_input, p_es->p_pgrm->pp_es[i_loop] );
            }
        }

        /* Then add what we received in this PMT */
        do
        {
            i_section_length = ( ((u32)*(p_current_data + 1) & 0xF) << 8 ) |
                                  *(p_current_data + 2);
            i_current_section = (u8)p_current_data[6];
            i_prog_info_length = ( ((u32)*(p_current_data + 10) & 0xF) << 8 ) |
                                    *(p_current_data + 11);

            /* For the moment we ignore program descriptors */
            p_current_data += 12 + i_prog_info_length;
    
            /* The end of the section, before the CRC is at 
             * p_current_section + i_section_length -1 */
            while( p_current_data < p_current_section + i_section_length -1 )
            {
                i_stream_type = (int)p_current_data[0];
                i_pid = ( ((u32)*(p_current_data + 1) & 0x1F) << 8 ) |
                           *(p_current_data + 2);
                i_es_info_length = ( ((u32)*(p_current_data + 3) & 0xF) << 8 ) |
                                      *(p_current_data + 4);
                
                /* Add this ES to the program */
                p_new_es = input_AddES( p_input, p_es->p_pgrm, 
                                        (u16)i_pid, sizeof( es_ts_data_t ) );

                /* Tell the interface what kind of stream it is and select 
                 * the required ones */
                {
                    switch( i_stream_type )
                    {
                        case MPEG1_VIDEO_ES:
                        case MPEG2_VIDEO_ES:
575
                            p_new_es->i_fourcc = VLC_FOURCC('m','p','g','v');
576 577 578 579
                            p_new_es->i_cat = VIDEO_ES;
                            break;
                        case MPEG1_AUDIO_ES:
                        case MPEG2_AUDIO_ES:
580 581 582 583 584 585
                            p_new_es->i_fourcc = VLC_FOURCC('m','p','g','a');
                            p_new_es->i_cat = AUDIO_ES;
                            break;
                        case LPCM_AUDIO_ES:
                            p_new_es->i_fourcc = VLC_FOURCC('l','p','c','m');
                            p_new_es->i_stream_id = 0xBD;
586 587
                            p_new_es->i_cat = AUDIO_ES;
                            break;
588 589
                        case AC3_AUDIO_ES:
                            p_new_es->i_fourcc = VLC_FOURCC('a','5','2',' ');
590 591 592 593
                            p_new_es->i_stream_id = 0xBD;
                            p_new_es->i_cat = AUDIO_ES;
                            break;
                        /* Not sure this one is fully specification-compliant */
594 595
                        case DVD_SPU_ES:
                            p_new_es->i_fourcc = VLC_FOURCC('s','p','u',' ');
596 597 598 599
                            p_new_es->i_stream_id = 0xBD;
                            p_new_es->i_cat = SPU_ES;
                            break;
                        default :
600
                            p_new_es->i_fourcc = 0;
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621
                            p_new_es->i_cat = UNKNOWN_ES;
                            break;
                    }
                }
                
                p_current_data += 5 + i_es_info_length;
            }

            /* Go to the beginning of the next section*/
            p_current_data += 3 + i_section_length;
           
            p_current_section++;
            
        } while( i_current_section < p_psi->i_last_section_number );

        p_pgrm_data->i_pmt_version = p_psi->i_version_number;

        /* if no program is selected :*/
        if( !p_input->stream.p_selected_program )
        {
            pgrm_descriptor_t *     p_pgrm_to_select;
622
            u16 i_id = (u16)config_GetInt( p_input, "program" );
623 624 625 626 627

            if( i_id != 0 ) /* if user specified a program */
            {
                p_pgrm_to_select = input_FindProgram( p_input, i_id );

628
                if( p_pgrm_to_select && p_pgrm_to_select == p_es->p_pgrm )
629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654
                    p_input->pf_set_program( p_input, p_pgrm_to_select );
            }
            else
                    p_input->pf_set_program( p_input, p_es->p_pgrm );
        }
        
        /* inform interface that stream has changed */
        p_input->stream.b_changed = 1;
        /*  Remove lock */
        vlc_mutex_unlock( &p_input->stream.stream_lock );
    }
    
#undef p_psi
}

#elif defined MODULE_NAME_IS_mpeg_ts_dvbpsi
/*
 * PSI Decoding using libdvbcss 
 */

/*****************************************************************************
 * DemuxPSI : send the PSI to the right libdvbpsi decoder
 *****************************************************************************/
static void TS_DVBPSI_DemuxPSI( input_thread_t  * p_input, 
                                data_packet_t   * p_data, 
                                es_descriptor_t * p_es, 
655
                                vlc_bool_t        b_unit_start )
656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677
{
    es_ts_data_t        * p_es_demux_data;
    pgrm_ts_data_t      * p_pgrm_demux_data;
    stream_ts_data_t    * p_stream_demux_data;

    p_es_demux_data = ( es_ts_data_t * ) p_es->p_demux_data;
    p_stream_demux_data = ( stream_ts_data_t * ) p_input->stream.p_demux_data;

    switch( p_es_demux_data->i_psi_type)
    {
        case PSI_IS_PAT:
            dvbpsi_PushPacket( 
                    ( dvbpsi_handle ) p_stream_demux_data->p_pat_handle,
                    p_data->p_demux_start );
            break;
        case PSI_IS_PMT:
            p_pgrm_demux_data = ( pgrm_ts_data_t * )p_es->p_pgrm->p_demux_data;
            dvbpsi_PushPacket( 
                    ( dvbpsi_handle ) p_pgrm_demux_data->p_pmt_handle,
                    p_data->p_demux_start );
            break;
        default:
678
            msg_Warn( p_input, "received unknown PSI in DemuxPSI" );
679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735
    }
    
    input_DeletePacket( p_input->p_method_data, p_data );
}

/*****************************************************************************
 * HandlePAT: will treat a PAT returned by dvbpsi
 *****************************************************************************/

void TS_DVBPSI_HandlePAT( input_thread_t * p_input, dvbpsi_pat_t * p_new_pat )
{
    dvbpsi_pat_program_t *      p_pgrm;
    pgrm_descriptor_t *         p_new_pgrm;
    pgrm_ts_data_t *            p_pgrm_demux;
    es_descriptor_t *           p_current_es;
    es_ts_data_t *              p_es_demux;
    stream_ts_data_t *          p_stream_data;

    vlc_mutex_lock( &p_input->stream.stream_lock );
    
    p_stream_data = (stream_ts_data_t *)p_input->stream.p_demux_data;
    
    if ( !p_new_pat->b_current_next || 
            p_stream_data->i_pat_version == PAT_UNINITIALIZED  )
    {
        /* Delete all programs */
        while( p_input->stream.i_pgrm_number )
        {
            input_DelProgram( p_input, p_input->stream.pp_programs[0] );
        }
    
        /* treat the new programs list */
        p_pgrm = p_new_pat->p_first_program;
        
        while( p_pgrm )
        {
            /* If program = 0, we're having info about NIT not PMT */
            if( p_pgrm->i_number )
            {
                /* Add this program */
                p_new_pgrm = input_AddProgram( p_input, p_pgrm->i_number, 
                                            sizeof( pgrm_ts_data_t ) );

                p_pgrm_demux = (pgrm_ts_data_t *)p_new_pgrm->p_demux_data;
                p_pgrm_demux->i_pmt_version = PMT_UNINITIALIZED;
        
                /* Add the PMT ES to this program */
                p_current_es = input_AddES( p_input, p_new_pgrm,
                                            (u16) p_pgrm->i_pid,
                                            sizeof( es_ts_data_t) );
                p_es_demux = (es_ts_data_t *)p_current_es->p_demux_data;
                p_es_demux->b_psi = 1;
                p_es_demux->i_psi_type = PSI_IS_PMT;
                        
                p_es_demux->p_psi_section = malloc( sizeof( psi_section_t ) );
                if ( p_es_demux->p_psi_section == NULL )
                {
736
                    msg_Err( p_input, "out of memory" );
737 738 739 740 741 742 743 744 745 746 747 748 749 750
                    p_input->b_error = 1;
                    return;
                }
            
                p_es_demux->p_psi_section->b_is_complete = 0;
                
                /* Create a PMT decoder */
                p_pgrm_demux->p_pmt_handle = (dvbpsi_handle *)
                    dvbpsi_AttachPMT( p_pgrm->i_number,
                            (dvbpsi_pmt_callback) &TS_DVBPSI_HandlePMT, 
                            p_input );

                if( p_pgrm_demux->p_pmt_handle == NULL )
                {
751
                    msg_Err( p_input, "could not create PMT decoder" );
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
                    p_input->b_error = 1;
                    return;
                }

            }
            p_pgrm = p_pgrm->p_next; 
        }
        
        p_stream_data->i_pat_version = p_new_pat->i_version;
    }
    vlc_mutex_unlock( &p_input->stream.stream_lock );
}

/*****************************************************************************
 * HandlePMT: will treat a PMT returned by dvbpsi
 *****************************************************************************/
void TS_DVBPSI_HandlePMT( input_thread_t * p_input, dvbpsi_pmt_t * p_new_pmt )
{
    dvbpsi_pmt_es_t *       p_es;
    pgrm_descriptor_t *     p_pgrm;
    es_descriptor_t *       p_new_es;
    pgrm_ts_data_t *        p_pgrm_demux;
   
    vlc_mutex_lock( &p_input->stream.stream_lock );
    
    p_pgrm = input_FindProgram( p_input, p_new_pmt->i_program_number );

    if( p_pgrm == NULL )
    {
781
        msg_Warn( p_input, "PMT of unreferenced program found" );
782 783 784 785 786
        return;
    }

    p_pgrm_demux = (pgrm_ts_data_t *)p_pgrm->p_demux_data;
    p_pgrm_demux->i_pcr_pid = p_new_pmt->i_pcr_pid;
787

788 789 790 791 792 793 794 795 796 797 798
    if( !p_new_pmt->b_current_next || 
            p_pgrm_demux->i_pmt_version == PMT_UNINITIALIZED )
    {
        p_es = p_new_pmt->p_first_es;
        while( p_es )
        {
            /* Add this ES */
            p_new_es = input_AddES( p_input, p_pgrm, 
                            (u16)p_es->i_pid, sizeof( es_ts_data_t ) );
            if( p_new_es == NULL )
            {
799
                msg_Err( p_input, "could not add ES %d", p_es->i_pid );
800 801 802 803 804 805 806 807
                p_input->b_error = 1;
                return;
            }

            switch( p_es->i_type )
            {
                case MPEG1_VIDEO_ES:
                case MPEG2_VIDEO_ES:
808
                    p_new_es->i_fourcc = VLC_FOURCC('m','p','g','v');
809 810 811 812
                    p_new_es->i_cat = VIDEO_ES;
                    break;
                case MPEG1_AUDIO_ES:
                case MPEG2_AUDIO_ES:
813
                    p_new_es->i_fourcc = VLC_FOURCC('m','p','g','a');
814 815 816
                    p_new_es->i_cat = AUDIO_ES;
                    break;
                case LPCM_AUDIO_ES:
817 818 819 820
                    p_new_es->i_fourcc = VLC_FOURCC('l','p','c','m');
                    p_new_es->i_cat = AUDIO_ES;
                    p_new_es->i_stream_id = 0xBD;
                    break;
821
                case AC3_AUDIO_ES:
822
                    p_new_es->i_fourcc = VLC_FOURCC('a','5','2',' ');
823 824 825 826
                    p_new_es->i_cat = AUDIO_ES;
                    p_new_es->i_stream_id = 0xBD;
                    break;
                case DVD_SPU_ES:
827
                    p_new_es->i_fourcc = VLC_FOURCC('s','p','u',' ');
828 829 830 831
                    p_new_es->i_cat = SPU_ES;
                    p_new_es->i_stream_id = 0xBD;
                    break;
                default:
832
                    p_new_es->i_fourcc = 0;
833 834
                    p_new_es->i_cat = UNKNOWN_ES;
            }
835

836 837
            if(    ( p_new_es->i_cat == AUDIO_ES )
                || (p_new_es->i_cat == SPU_ES ) )
838 839 840 841 842 843 844 845 846 847
            {
                dvbpsi_descriptor_t *p_dr = p_es->p_first_descriptor;
                while( p_dr && ( p_dr->i_tag != 0x0a ) )
                    p_dr = p_dr->p_next;
                if( p_dr )
                {
                    dvbpsi_iso639_dr_t *p_decoded =
                                                dvbpsi_DecodeISO639Dr( p_dr );
                    if( p_decoded->i_code_count > 0 )
                    {
848
                        const iso639_lang_t * p_iso;
849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880
                        p_iso = GetLang_2T(p_decoded->i_iso_639_code);
                        if(p_iso)
                        {
                            if(p_iso->psz_native_name[0])
                                strcpy( p_new_es->psz_desc,
                                        p_iso->psz_native_name );
                            else
                                strcpy( p_new_es->psz_desc,
                                        p_iso->psz_eng_name );
                        }
                        else
                        {
                            strncpy( p_new_es->psz_desc,
                                     p_decoded->i_iso_639_code, 3 );
                        }
                    }
                }
                switch( p_es->i_type )
                {
                    case MPEG1_AUDIO_ES:
                    case MPEG2_AUDIO_ES:
                        strcat( p_new_es->psz_desc, " (mpeg)" );
                        break;
                    case LPCM_AUDIO_ES:
                        strcat( p_new_es->psz_desc, " (lpcm)" );
                        break;
                    case AC3_AUDIO_ES:
                        strcat( p_new_es->psz_desc, " (ac3)" );
                        break;
                }
            }

881 882 883 884 885 886 887
            p_es = p_es->p_next;
        }
        
        /* if no program is selected :*/
        if( !p_input->stream.p_selected_program )
        {
            pgrm_descriptor_t *     p_pgrm_to_select;
888
            u16 i_id = (u16)config_GetInt( p_input, "program" );
889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912

            if( i_id != 0 ) /* if user specified a program */
            {
                p_pgrm_to_select = input_FindProgram( p_input, i_id );

                if( p_pgrm_to_select && p_pgrm_to_select == p_pgrm )
                    p_input->pf_set_program( p_input, p_pgrm_to_select );
            }
            else
                    p_input->pf_set_program( p_input, p_pgrm );
        }
        /* if the pmt belongs to the currently selected program, we
         * reselect it to update its ES */
        else if( p_pgrm == p_input->stream.p_selected_program )
        {
            p_input->pf_set_program( p_input, p_pgrm );
        }
        
        p_pgrm_demux->i_pmt_version = p_new_pmt->i_version;
        p_input->stream.b_changed = 1;
    }
    vlc_mutex_unlock( &p_input->stream.stream_lock );
}
#endif