* and TS system layers
*****************************************************************************
* Copyright (C) 1999, 2000 VideoLAN
- * $Id: mpeg_system.h,v 1.1 2001/02/08 04:43:27 sam Exp $
+ * $Id: mpeg_system.h,v 1.2 2001/02/21 04:38:59 henri Exp $
*
* Authors:
*
#define TS_PACKET_SIZE 188 /* Size of a TS packet */
#define PSI_SECTION_SIZE 4096 /* Maximum size of a PSI section */
+#define PAT_UNINITIALIZED (1 << 6)
+#define PMT_UNINITIALIZED (1 << 6)
+
+#define PSI_IS_PAT 0x00
+#define PSI_IS_PMT 0x01
+#define UNKNOWN_PSI 0xff
/*****************************************************************************
* psi_section_t
{
byte_t buffer[PSI_SECTION_SIZE];
- /* Is there a section being decoded ? */
- boolean_t b_running_section;
+ u8 i_section_number;
+ u8 i_last_section_number;
+ u8 i_version_number;
+ u16 i_section_length;
+ u16 i_read_in_section;
+
+ /* the PSI is complete */
+ boolean_t b_is_complete;
+
+ /* packet missed up ? */
+ boolean_t b_trash;
+
+ /*about sections */
+ boolean_t b_section_complete;
+
+ /* where are we currently ? */
+ byte_t * p_current;
- u16 i_length;
- u16 i_current_position;
} psi_section_t;
/*****************************************************************************
{
boolean_t b_psi; /* Does the stream have to be handled by
* the PSI decoder ? */
+
+ int i_psi_type; /* There are different types of PSI */
+
psi_section_t * p_psi_section; /* PSI packets */
/* Markers */
typedef struct pgrm_ts_data_s
{
u16 i_pcr_pid; /* PCR ES, for TS streams */
+ int i_pmt_version;
} pgrm_ts_data_t;
/*****************************************************************************
*****************************************************************************/
typedef struct stream_ts_data_s
{
- /* Program Association Table status */
- u8 i_PAT_version; /* version number */
- boolean_t b_is_PAT_complete; /* Is the PAT complete ? */
- u8 i_known_PAT_sections;
- /* Number of section we received so far */
- byte_t a_known_PAT_sections[32];
- /* Already received sections */
-
- /* Program Map Table status */
- boolean_t b_is_PMT_complete; /* Is the PMT complete ? */
- u8 i_known_PMT_sections;
- /* Number of section we received so far */
- byte_t a_known_PMT_sections[32];
- /* Already received sections */
-
- /* Service Description Table status */
- u8 i_SDT_version; /* version number */
- boolean_t b_is_SDT_complete; /* Is the SDT complete ? */
- u8 i_known_SDT_sections;
- /* Number of section we received so far */
- byte_t a_known_SDT_sections[32];
- /* Already received sections */
+ int i_pat_version; /* Current version of the PAT */
} stream_ts_data_t;
/*****************************************************************************
struct data_packet_s * );
void input_DemuxPS( struct input_thread_s *, struct data_packet_s * );
void input_DemuxTS( struct input_thread_s *, struct data_packet_s * );
+void input_DemuxPSI( input_thread_t *, data_packet_t *, es_descriptor_t *,
+ boolean_t, boolean_t );
* input_ts.c: TS demux and netlist management
*****************************************************************************
* Copyright (C) 1998, 1999, 2000 VideoLAN
- * $Id: input_ts.c,v 1.5 2001/02/20 07:49:13 sam Exp $
+ * $Id: input_ts.c,v 1.6 2001/02/21 04:38:59 henri Exp $
*
* Authors:
*
{
/* Initialize netlist and TS structures */
thread_ts_data_t * p_method;
- pgrm_ts_data_t * p_pgrm_demux;
- es_descriptor_t * kludge1;
+ es_descriptor_t * p_pat_es;
+ es_ts_data_t * p_demux_data;
+ stream_ts_data_t * p_stream_data;
/* Initialise structure */
p_method = malloc( sizeof( thread_ts_data_t ) );
/* Initialize the stream */
input_InitStream( p_input, sizeof( stream_ts_data_t ) );
- /* FIXME : PSIDemux and PSIDecode */
- /* Add audio and video programs */
- /* p_input->stream.pp_programs[0] = */
- input_AddProgram( p_input, 0, sizeof( pgrm_ts_data_t ) );
- p_pgrm_demux =
- (pgrm_ts_data_t *)p_input->stream.pp_programs[0]->p_demux_data;
- p_pgrm_demux->i_pcr_pid = 0x78;
-
- kludge1 = input_AddES( p_input, p_input->stream.pp_programs[0],
- 0x78, sizeof( es_ts_data_t ) );
-
- // kludge
- kludge1->i_type = MPEG2_VIDEO_ES;
-
- input_SelectES( p_input, kludge1 );
-
- vlc_mutex_lock( &(p_input->stream.stream_lock) );
- p_input->stream.pp_programs[0]->b_is_ok = 1;
- vlc_mutex_unlock( &(p_input->stream.stream_lock) );
-
-//debug
-intf_ErrMsg("End of TSINIT");
+ /* Init */
+ p_stream_data = (stream_ts_data_t *)p_input->stream.p_demux_data;
+ p_stream_data->i_pat_version = PAT_UNINITIALIZED ;
+
+ /* 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;
+
}
/*****************************************************************************
*****************************************************************************/
static void TSEnd( input_thread_t * p_input )
{
+ es_descriptor_t * p_pat_es;
+
+ p_pat_es = input_FindES( p_input, 0x00 );
+ if( p_pat_es != NULL )
+ input_DelES( p_input, p_pat_es );
+ free(p_input->p_plugin_data);
}
/*****************************************************************************
* mpeg_system.c: TS, PS and PES management
*****************************************************************************
* Copyright (C) 1998, 1999, 2000 VideoLAN
- * $Id: mpeg_system.c,v 1.37 2001/02/19 19:08:59 massiot Exp $
+ * $Id: mpeg_system.c,v 1.38 2001/02/21 04:38:59 henri Exp $
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
* Michel Lespinasse <walken@via.ecp.fr>
* BenoƮt Steiner <benny@via.ecp.fr>
* Samuel Hocevar <sam@via.ecp.fr>
+ * Henri Fallon <henri@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* Local prototypes
*****************************************************************************/
+static void input_DecodePAT( input_thread_t *, es_descriptor_t *);
+static void input_DecodePMT( input_thread_t *, es_descriptor_t *);
/*
* PES Packet management
*****************************************************************************/
void input_DemuxTS( input_thread_t * p_input, data_packet_t * p_data )
{
- int i_pid, i_dummy;
+ u16 i_pid;
+ int i_dummy;
boolean_t b_adaptation; /* Adaptation field is present */
boolean_t b_payload; /* Packet carries payload */
boolean_t b_unit_start; /* A PSI or a PES start in the packet */
boolean_t b_trash = 0; /* Is the packet unuseful ? */
boolean_t b_lost = 0; /* Was there a packet loss ? */
+ boolean_t b_psi = 0; /* Is this a PSI ? */
es_descriptor_t * p_es = NULL;
es_ts_data_t * p_es_demux = NULL;
pgrm_ts_data_t * p_pgrm_demux = NULL;
#define p (p_data->p_buffer)
-
- //intf_DbgMsg("input debug: TS-demultiplexing packet %p, pid %d",
- // p_ts_packet, U16_AT(&p[1]) & 0x1fff);
-
+
/* Extract flags values from TS common header. */
i_pid = U16_AT(&p[1]) & 0x1fff;
b_unit_start = (p[1] & 0x40);
/* Find out the elementary stream. */
vlc_mutex_lock( &p_input->stream.stream_lock );
-// kludge
-if ( i_pid == 0x78 )
-{
- p_es = input_FindES( p_input, 0x78 );
- p_es->i_type = MPEG2_VIDEO_ES;
- if( p_es->p_pes == NULL )
- intf_ErrMsg("Got p_es . p_es->p_pes == null ? %d",p_es->p_pes == NULL);
-}
-else
-{
- p_es = NULL;
- b_trash = 1;
-}
-
+
+ p_es= input_FindES( p_input, i_pid );
+ if( (p_es != NULL) && (p_es->p_demux_data != NULL) )
+ {
+ p_es_demux = (es_ts_data_t *)p_es->p_demux_data;
+
+ if( p_es_demux->b_psi )
+ b_psi = 1;
+ }
+
vlc_mutex_lock( &p_input->stream.control.control_lock );
- if( p_es == NULL || p_es->p_decoder_fifo == NULL
- || (p_es->b_audio && p_input->stream.control.b_mute) )
+
+ if( ( p_es == NULL ) || (p_es->b_audio && p_input->stream.control.b_mute) )
{
/* Not selected. Just read the adaptation field for a PCR. */
b_trash = 1;
}
+ else if( p_es->p_decoder_fifo == NULL && !b_psi )
+ b_trash =1;
+
vlc_mutex_unlock( &p_input->stream.control.control_lock );
vlc_mutex_unlock( &p_input->stream.stream_lock );
-// kludge
- if (p_es != NULL )
- {
-
- p_es_demux = (es_ts_data_t *)p_es->p_demux_data;
- p_pgrm_demux = (pgrm_ts_data_t *)p_es->p_pgrm->p_demux_data;
-
- p_pgrm_demux->i_pcr_pid = 0x78;
- if( (p_es->p_decoder_fifo != NULL) || (p_pgrm_demux->i_pcr_pid == i_pid) )
+ if( ( p_es != NULL ) &&
+ ((p_es->p_decoder_fifo != NULL) || b_psi
+ || (p_pgrm_demux->i_pcr_pid == i_pid) ) )
{
+ p_es_demux = (es_ts_data_t *)p_es->p_demux_data;
+
+ if( ! p_es_demux->b_psi )
+ {
+ p_pgrm_demux = (pgrm_ts_data_t *)p_es->p_pgrm->p_demux_data;
+ }
+
#ifdef STATS
p_es->c_packets++;
#endif
} /* not continuous */
} /* continuity */
} /* if selected or PCR */
-
- }
/* Trash the packet if it has no payload or if it isn't selected */
if( b_trash )
{
-
p_input->pf_delete_packet( p_input->p_method_data, p_data );
#ifdef STATS
p_input->c_packets_trashed++;
}
else
{
- if( p_es_demux->b_psi )
+ if( b_psi )
{
-//debug
-//printf("DemuxTS : Was a PSI\n");
/* The payload contains PSI tables */
-#if 0
- /* FIXME ! write the PSI decoder :p */
input_DemuxPSI( p_input, p_data, p_es,
b_unit_start, b_lost );
-#endif
+
}
else
{
/* The payload carries a PES stream */
input_GatherPES( p_input, p_data, p_es, b_unit_start, b_lost );
}
+
}
#undef p
}
+
+/*
+ * PSI demultiplexing and decoding
+ */
+
+/*****************************************************************************
+ * DemuxPSI : makes up complete PSI data
+ *****************************************************************************/
+void input_DemuxPSI( input_thread_t * p_input, data_packet_t * p_data,
+ es_descriptor_t * p_es, boolean_t b_unit_start, boolean_t b_lost )
+{
+ 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 )
+ {
+ /* intf_WarnMsg( 2, */
+ intf_ErrMsg( "Non zero pointer field found. Trying to continue" );
+ p+=(u8)p[0];
+ }
+ else
+ p++;
+
+ /* This is the begining of a new section */
+
+ if( ((u8)(p[1]) & 0xc0) != 0x80 )
+ {
+ intf_ErrMsg( "Invalid PSI packet" );
+ p_psi->b_trash = 1;
+ }
+ else
+ {
+ p_psi->i_section_length = U16_AT(p+1) & 0x0fff;
+ 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) )
+ {
+ intf_WarnMsg( 2,"PSI version differs inside same PAT" );
+ p_psi->b_trash = 1;
+ }
+ if( p_psi->i_section_number + 1 != (u8)p[6] )
+ {
+ intf_WarnMsg( 2,
+ "PSI Section discontinuity. Packet lost ?");
+ p_psi->b_trash = 1;
+ }
+ else
+ p_psi->i_section_number++;
+ }
+ else
+ {
+ intf_WarnMsg( 2, "Received unexpected new PSI section" );
+ 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;
+ }
+ }
+ 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;
+ }
+ }
+
+ if ( p_psi->b_is_complete )
+ {
+ switch( p_demux_data->i_psi_type)
+ {
+ case PSI_IS_PAT:
+ input_DecodePAT( p_input, p_es );
+ break;
+ case PSI_IS_PMT:
+ input_DecodePMT( p_input, p_es );
+ break;
+ default:
+ intf_ErrMsg("Received unknown PSI in demuxPSI");
+ }
+ }
+#undef p_psi
+#undef p
+
+ return ;
+}
+
+/*****************************************************************************
+ * DecodePAT : Decodes Programm association table and deal with it
+ *****************************************************************************/
+static void input_DecodePAT( input_thread_t * p_input, es_descriptor_t * p_es )
+{
+
+ stream_ts_data_t * p_stream_data;
+ es_ts_data_t * p_demux_data;
+
+
+ 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)
+
+ if( p_stream_data->i_pat_version != p_psi->i_version_number )
+ {
+ /* PAT has changed. We are going to delete all programms 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 krashed */
+ pgrm_descriptor_t * p_pgrm;
+ es_descriptor_t * p_current_es;
+ es_ts_data_t * p_es_demux;
+ pgrm_ts_data_t * p_pgrm_demux;
+ byte_t * p_current_data;
+
+ int i_section_length,i_program_id,i_pmt_pid;
+ int i_loop, i_current_section;
+
+ p_current_data = p_psi->buffer;
+
+
+ for( i_loop = 0; i_loop < p_input->stream.i_pgrm_number; i_loop++ )
+ {
+ input_DelProgram( p_input, p_input->stream.pp_programs[i_loop] );
+ }
+
+ do
+ {
+ i_section_length = U16_AT(p_current_data+1) & 0x0fff;
+ i_current_section = (u8)p_current_data[6];
+
+ for( i_loop = 0; i_loop < (i_section_length-9)/4 ; i_loop++ )
+ {
+ i_program_id = U16_AT(p_current_data + i_loop*4 + 8);
+ i_pmt_pid = U16_AT( p_current_data + i_loop*4 + 10) & 0x1fff;
+
+ /* 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 deletes the ES and add them again. If the PMT doesn't change,
+ * there no need to do anything.
+ *****************************************************************************/
+static void input_DecodePMT( 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 = U16_AT(p_current_section + 8) & 0x1fff;
+
+ /* Delete all ES in this program except the PSI */
+ for( i_loop=0; i_loop < p_es->p_pgrm->i_es_number; 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 = U16_AT(p_current_data+1) & 0x0fff;
+ i_current_section = (u8)p_current_data[6];
+ i_prog_info_length = U16_AT(p_current_data+10) & 0x0fff;
+
+ /* 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 = U16_AT( p_current_data + 1 ) & 0x1fff;
+ i_es_info_length = U16_AT( p_current_data + 3 ) & 0x0fff;
+
+ /* 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 ) );
+ p_new_es->i_type = i_stream_type;
+
+ /* We want to decode */
+ input_SelectES( p_input, p_new_es );
+
+ 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+=1;
+
+ } while( i_current_section < p_psi->i_last_section_number );
+
+ p_pgrm_data->i_pmt_version = p_psi->i_version_number;
+
+ }
+
+#undef p_psi
+}