/*****************************************************************************
* mpeg_system.c: TS, PS and PES management
*****************************************************************************
- * Copyright (C) 1998, 1999, 2000 VideoLAN
- * $Id: mpeg_system.c,v 1.63 2001/11/12 03:07:13 stef Exp $
+ * Copyright (C) 1998-2001 VideoLAN
+ * $Id: mpeg_system.c,v 1.75 2001/12/17 16:42:27 sam Exp $
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
* Michel Lespinasse <walken@via.ecp.fr>
#include <string.h> /* memcpy(), memset() */
#include <sys/types.h> /* off_t */
-#include "config.h"
#include "common.h"
+#include "intf_msg.h"
#include "threads.h"
#include "mtime.h"
-#include "intf_msg.h"
-
#include "stream_control.h"
#include "input_ext-intf.h"
#include "input_ext-dec.h"
#include "input_ext-plugins.h"
-#include "main.h" /* AC3/MPEG channel, SPU channel, b_stat */
-
/*****************************************************************************
* Local prototypes
*****************************************************************************/
if( MoveChunk( p_header, &p_data, &p_byte, PES_HEADER_SIZE )
!= PES_HEADER_SIZE )
{
- intf_WarnMsg( 1, "PES packet too short to have a header" );
+ intf_WarnMsg( 1, "input: PES packet too short to have a header" );
p_input->pf_delete_pes( p_input->p_method_data, p_pes );
p_pes = NULL;
return;
if( (p_header[0] || p_header[1] || (p_header[2] != 1)) )
{
/* packet_start_code_prefix != 0x000001 */
- intf_ErrMsg( "PES packet doesn't start with 0x000001 : data loss" );
+ intf_ErrMsg( "input error: data loss, "
+ "PES packet doesn't start with 0x000001" );
p_input->pf_delete_pes( p_input->p_method_data, p_pes );
p_pes = NULL;
}
{
/* PES_packet_length is set and != total received payload */
/* Warn the decoder that the data may be corrupt. */
- intf_WarnMsg( 1, "PES sizes do not match : packet corrupted" );
+ intf_WarnMsg( 1, "input: packet corrupted, "
+ "PES sizes do not match" );
}
switch( p_es->i_stream_id )
}
if( i_pes_header_size == 23 )
{
- intf_ErrMsg( "Too much MPEG-1 stuffing" );
+ intf_ErrMsg( "input error: too much MPEG-1 stuffing" );
p_input->pf_delete_pes( p_input->p_method_data, p_pes );
p_pes = NULL;
return;
i_pes_header_size += 2;
if( MoveChunk( NULL, &p_data, &p_byte, 2 ) != 2 )
{
- intf_WarnMsg( 1,
- "PES packet too short to have a MPEG-1 header" );
+ intf_WarnMsg( 1, "input: PES packet too short "
+ "to have a MPEG-1 header" );
p_input->pf_delete_pes( p_input->p_method_data, p_pes );
p_pes = NULL;
return;
i_pes_header_size += 4;
if( MoveChunk( p_ts, &p_data, &p_byte, 5 ) != 5 )
{
- intf_WarnMsg( 1,
- "PES packet too short to have a MPEG-1 header" );
+ intf_WarnMsg( 1, "input: PES packet too short "
+ "to have a MPEG-1 header" );
p_input->pf_delete_pes( p_input->p_method_data, p_pes );
p_pes = NULL;
return;
i_pes_header_size += 5;
if( MoveChunk( p_ts, &p_data, &p_byte, 5 ) != 5 )
{
- intf_WarnMsg( 1,
- "PES packet too short to have a MPEG-1 header" );
+ intf_WarnMsg( 1, "input: PES packet too short "
+ "to have a MPEG-1 header" );
p_input->pf_delete_pes( p_input->p_method_data,
p_pes );
p_pes = NULL;
/* Go to the next data packet. */
if( (p_data = p_data->p_next) == NULL )
{
- intf_ErrMsg( "PES header bigger than payload" );
+ intf_ErrMsg( "input error: PES header bigger than payload" );
p_input->pf_delete_pes( p_input->p_method_data, p_pes );
p_pes = NULL;
return;
/* This last packet is partly header, partly payload. */
if( i_payload_size < i_pes_header_size )
{
- intf_ErrMsg( "PES header bigger than payload" );
+ intf_ErrMsg( "input error: PES header bigger than payload" );
p_input->pf_delete_pes( p_input->p_method_data, p_pes );
p_pes = NULL;
return;
}
else
{
- intf_ErrMsg("No fifo to receive PES %p (who wrote this damn code ?)",
- p_pes);
+ intf_ErrMsg( "input error: no fifo to receive PES %p "
+ "(who wrote this damn code ?)", p_pes );
p_input->pf_delete_pes( p_input->p_method_data, p_pes );
}
p_pes = NULL;
* started. */
if( (p_pes = p_input->pf_new_pes( p_input->p_method_data ) ) == NULL )
{
- intf_ErrMsg("Out of memory");
+ intf_ErrMsg( "input error: out of memory" );
p_input->b_error = 1;
return;
}
else
{
/* Update the relations between the data packets */
- p_es->p_last->p_next = p_data;
+ p_pes->p_last->p_next = p_data;
}
- p_es->p_last = p_data;
+ p_pes->p_last = p_data;
+ p_pes->i_nb_data++;
/* Size of the payload carried in the data packet */
p_pes->i_pes_size += (p_data->p_payload_end
if( p_data->p_payload_start + 10 > p_data->p_payload_end )
{
- intf_ErrMsg( "PSM too short : packet corrupt" );
+ intf_ErrMsg( "input error: PSM too short : packet corrupt" );
return;
}
return;
}
- intf_DbgMsg( "Building PSM" );
+ intf_DbgMsg( "input: building PSM" );
p_demux->b_has_PSM = 1;
p_demux->i_PSM_version = p_data->p_payload_start[6] & 0x1F;
+ U16_AT(&p_data->p_payload_start[8]);
if( p_byte > p_data->p_payload_end )
{
- intf_ErrMsg( "PSM too short : packet corrupt" );
+ intf_ErrMsg( "input error: PSM too short, packet corrupt" );
return;
}
/* This is the full size of the elementary_stream_map.
p_byte += 2;
if( p_end > p_data->p_payload_end )
{
- intf_ErrMsg( "PSM too short : packet corrupt" );
+ intf_ErrMsg( "input error: PSM too short, packet corrupt" );
return;
}
}
/* Un-select the streams that are no longer parts of the program. */
- for( i = i_new_es_number;
- i < p_input->stream.pp_programs[0]->i_es_number;
- i++ )
+ while( i_new_es_number < p_input->stream.pp_programs[0]->i_es_number )
{
/* We remove pp_es[i_new_es_member] and not pp_es[i] because the
* list will be emptied starting from the end */
p_es->i_type = UNKNOWN_ES;
}
}
+
+ /* Tell the interface the stream has changed */
+ p_input->stream.b_changed = 1;
}
} /* stream.b_is_ok */
vlc_mutex_unlock( &p_input->stream.stream_lock );
boolean_t b_trash = 0;
es_descriptor_t * p_es = NULL;
- i_code = U32_AT( p_data->p_payload_start );
+ i_code = ((u32)p_data->p_payload_start[0] << 24)
+ | ((u32)p_data->p_payload_start[1] << 16)
+ | ((u32)p_data->p_payload_start[2] << 8)
+ | p_data->p_payload_start[3];
if( i_code <= 0x1BC )
{
switch( i_code )
if( MoveChunk( p_header, &p_data, &p_byte, 14 ) != 14 )
{
- intf_WarnMsg( 1, "Packet too short to have a header" );
+ intf_WarnMsg( 1, "input: packet too short "
+ "to have a header" );
b_trash = 1;
break;
}
if( MoveChunk( p_header, &p_data, &p_byte, 12 ) != 12 )
{
- intf_WarnMsg( 1, "Packet too short to have a header" );
+ intf_WarnMsg( 1, "input: packet too short "
+ "to have a header" );
b_trash = 1;
break;
}
if( i_mux_rate != p_input->stream.i_mux_rate
&& p_input->stream.i_mux_rate )
{
- intf_WarnMsg(2,
- "Mux_rate changed - expect cosmetic errors");
+ intf_WarnMsg( 2, "input: mux_rate changed, "
+ "expect cosmetic errors" );
}
p_input->stream.i_mux_rate = i_mux_rate;
default:
/* This should not happen */
b_trash = 1;
- intf_WarnMsg( 3, "Unwanted packet received with start code 0x%.8x",
- i_code );
+ intf_WarnMsg( 3, "input: unwanted packet received "
+ "with start code 0x%.8x", i_code );
}
}
else
if( b_payload ? (p[4] > 182) : (p[4] != 183) )
{
intf_WarnMsg( 2,
- "Invalid TS adaptation field (%p)",
+ "input: invalid TS adaptation field (%p)",
p_data );
p_data->b_discard_payload = 1;
p_es->c_invalid_packets++;
if( p[5] & 0x80 )
{
intf_WarnMsg( 2,
- "discontinuity_indicator"
+ "input: discontinuity_indicator"
" encountered by TS demux (position read: %d,"
" saved: %d)",
p[5] & 0x80, p_es_demux->i_continuity_counter );
* draft. As there is nothing interesting in this packet
* (except PCR that have already been handled), we can trash
* the packet. */
- intf_WarnMsg( 3,
- "Packet without payload received by TS demux" );
+ intf_WarnMsg( 3, "input: packet without payload received "
+ "by TS demux" );
b_trash = 1;
}
else if( i_dummy <= 0 )
{
/* Duplicate packet: mark it as being to be trashed. */
- intf_WarnMsg( 3, "Duplicate packet received by TS demux" );
+ intf_WarnMsg( 3, "input: duplicate packet received "
+ "by TS demux" );
b_trash = 1;
}
else if( p_es_demux->i_continuity_counter == 0xFF )
* this ES since the continuity counter ranges between 0 and
* 0x0F excepts when it has been initialized by the input:
* init the counter to the correct value. */
- intf_WarnMsg( 3, "First packet for PID %d received by TS demux",
- p_es->i_id );
+ intf_WarnMsg( 3, "input: first packet for PID %d received "
+ "by TS demux", p_es->i_id );
p_es_demux->i_continuity_counter = (p[3] & 0x0f);
}
else
* continuity_counter wrapped and we received a dup packet:
* as we don't know, do as if we missed a packet to be sure
* to recover from this situation */
- intf_WarnMsg( 2,
- "Packet lost by TS demux: current %d, packet %d",
- p_es_demux->i_continuity_counter & 0x0f,
- p[3] & 0x0f );
+ intf_WarnMsg( 2, "input: packet lost by TS demux: "
+ "current %d, packet %d",
+ p_es_demux->i_continuity_counter & 0x0f,
+ p[3] & 0x0f );
b_lost = 1;
p_es_demux->i_continuity_counter = p[3] & 0x0f;
} /* not continuous */
* (see ISO/IEC 13818 (2.4.4.2) which should be set to 0x00 */
if( (u8)p[0] != 0x00 )
{
- intf_WarnMsg( 2,
- "Non zero pointer field found. Trying to continue" );
+ intf_WarnMsg( 2, "input: non zero pointer field found, "
+ "trying to continue" );
p+=(u8)p[0];
}
else
if( ((u8)(p[1]) & 0xc0) != 0x80 )
{
- intf_WarnMsg( 2, "Invalid PSI packet" );
+ intf_WarnMsg( 2, "input: invalid PSI packet" );
p_psi->b_trash = 1;
}
else
if( p_psi->i_version_number != (( p[5] >> 1 ) & 0x1f) )
{
- intf_WarnMsg( 2,
- "PSI version differs inside same PAT" );
+ intf_WarnMsg( 2, "input: 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 ?");
+ intf_WarnMsg( 2, "input: PSI Section discontinuity, "
+ "packet lost ?" );
p_psi->b_trash = 1;
}
else
}
else
{
- intf_WarnMsg( 2, "Received unexpected new PSI section" );
+ intf_WarnMsg( 2, "input: got unexpected new PSI section" );
p_psi->b_trash = 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->i_read_in_section += p_data->p_payload_end - p;
p_psi->p_current += p_data->p_payload_end - p;
}
*****************************************************************************/
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;
+ 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;
+
+ boolean_t b_changed = 0;
+
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 )
{
- /* PAT has changed. We are going to delete all programms and
+ 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 krashes */
- pgrm_descriptor_t * p_pgrm;
- es_descriptor_t * p_current_es;
+ * this is a secure method to avoid crashes */
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++ )
+ /* Delete all programs */
+ while( p_input->stream.i_pgrm_number )
{
- input_DelProgram( p_input, p_input->stream.pp_programs[i_loop] );
+ input_DelProgram( p_input, p_input->stream.pp_programs[0] );
}
do
{
- i_section_length = ((p_current_data[1] & 0xF) << 8) |
+ 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++ )
+ for( i_loop = 0; i_loop < (i_section_length - 9) / 4 ; i_loop++ )
{
- i_program_id = ( *(p_current_data + i_loop * 4 + 8) << 8 ) |
- *(p_current_data + i_loop * 4 + 9);
- i_pmt_pid = ( (*( p_current_data + i_loop * 4 + 10) & 0x1F)
- << 8 ) |
- *( p_current_data + i_loop * 4 + 11);
+ 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 )
}
}
- p_current_data+=3+i_section_length;
-
+ 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*/
+
+ /* Go to the beginning of the next section */
p_stream_data->i_pat_version = p_psi->i_version_number;
}
#undef p_psi
+ /* FIXME This has nothing to do here */
+ p_input->stream.p_selected_program = p_input->stream.pp_programs[0] ;
}
/*****************************************************************************
p_current_section = p_psi->buffer;
p_current_data = p_psi->buffer;
- p_pgrm_data->i_pcr_pid = ( (*(p_current_section + 8) & 0x1F) << 8 ) |
+ p_pgrm_data->i_pcr_pid = ( ((u32)*(p_current_section + 8) & 0x1F) << 8 ) |
*(p_current_section + 9);
i_audio_es = 0;
i_required_spu_es = 0;
}
- /* Delete all ES in this program except the PSI */
- for( i_loop=0; i_loop < p_es->p_pgrm->i_es_number; i_loop++ )
+ /* 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] );
+ {
+ input_DelES( p_input, p_es->p_pgrm->pp_es[i_loop] );
+ }
}
/* Then add what we received in this PMT */
do
{
- i_section_length = ( (*(p_current_data + 1) & 0xF) << 8 ) |
+ 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 = ( (*(p_current_data + 10) & 0xF) << 8 ) |
+ i_prog_info_length = ( ((u32)*(p_current_data + 10) & 0xF) << 8 ) |
*(p_current_data + 11);
/* For the moment we ignore program descriptors */
while( p_current_data < p_current_section + i_section_length -1 )
{
i_stream_type = (int)p_current_data[0];
- i_pid = ( (*(p_current_data + 1) & 0x1F) << 8 ) |
+ i_pid = ( ((u32)*(p_current_data + 1) & 0x1F) << 8 ) |
*(p_current_data + 2);
- i_es_info_length = ( (*(p_current_data + 3) & 0xF) << 8 ) |
+ i_es_info_length = ( ((u32)*(p_current_data + 3) & 0xF) << 8 ) |
*(p_current_data + 4);
/* Add this ES to the program */
p_new_es->i_cat = VIDEO_ES;
input_SelectES( p_input, p_new_es );
break;
- p_new_es->i_stream_id = 0xBD;
case MPEG1_AUDIO_ES:
case MPEG2_AUDIO_ES:
p_new_es->i_cat = AUDIO_ES;
if( i_required_audio_es > i_audio_es )
{
- intf_WarnMsg( 2, "TS input: Non-existing audio ES required." );
+ intf_WarnMsg( 2, "input: non-existing audio ES required" );
}
if( i_required_spu_es > i_spu_es )
{
- intf_WarnMsg( 2, "TS input: Non-existing subtitles ES required." );
+ intf_WarnMsg( 2, "input: non-existing subtitles ES required" );
}
p_pgrm_data->i_pmt_version = p_psi->i_version_number;
#undef p_psi
}
+