/*****************************************************************************
* mpeg_system.c: TS, PS and PES management
*****************************************************************************
- * Copyright (C) 1998, 1999, 2000 VideoLAN
- * $Id: mpeg_system.c,v 1.43 2001/03/07 00:18:46 henri Exp $
+ * Copyright (C) 1998-2001 VideoLAN
+ * $Id: mpeg_system.c,v 1.91 2002/04/23 14:16:20 sam Exp $
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
* Michel Lespinasse <walken@via.ecp.fr>
/*****************************************************************************
* Preamble
*****************************************************************************/
-#include "defs.h"
-
#include <stdlib.h>
+#include <string.h> /* memcpy(), memset() */
+#include <sys/types.h> /* off_t */
-#include "config.h"
-#include "common.h"
-#include "threads.h"
-#include "mtime.h"
-
-#include "intf_msg.h"
+#include <videolan/vlc.h>
#include "stream_control.h"
#include "input_ext-intf.h"
#include "input_ext-dec.h"
-
-#include "input.h"
-#include "mpeg_system.h"
-
-#include "main.h" /* AC3/MPEG channel, SPU channel */
+#include "input_ext-plugins.h"
/*****************************************************************************
* Local prototypes
#define p_pes (p_es->p_pes)
- //intf_DbgMsg("End of PES packet %p", p_pes);
-
/* Parse the header. The header has a variable length, but in order
* to improve the algorithm, we will read the 14 bytes we may be
* interested in */
if( MoveChunk( p_header, &p_data, &p_byte, PES_HEADER_SIZE )
!= PES_HEADER_SIZE )
{
- intf_WarnMsg( 3, "PES packet too short to have a header" );
- p_input->pf_delete_pes( p_input->p_method_data, p_pes );
+ intf_WarnMsg( 1, "input: PES packet too short to have a header" );
+ input_DeletePES( p_input->p_method_data, p_pes );
p_pes = NULL;
return;
}
/* Get the PES size if defined */
- p_es->i_pes_real_size = U16_AT(p_header + 4) + 6;
+ p_es->i_pes_real_size = U16_AT(p_header + 4);
+ if( p_es->i_pes_real_size )
+ {
+ p_es->i_pes_real_size += 6;
+ }
/* First read the 6 header bytes common to all PES packets:
* use them to test the PES validity */
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" );
- p_input->pf_delete_pes( p_input->p_method_data, p_pes );
+ intf_ErrMsg( "input error: data loss, "
+ "PES packet doesn't start with 0x000001" );
+ input_DeletePES( p_input->p_method_data, p_pes );
p_pes = NULL;
}
else
{
/* PES_packet_length is set and != total received payload */
/* Warn the decoder that the data may be corrupt. */
- intf_WarnMsg( 3, "PES sizes do not match : packet corrupted" );
+ intf_WarnMsg( 1, "input: packet corrupted, "
+ "PES sizes do not match" );
}
switch( p_es->i_stream_id )
i_max_len = MoveChunk( p_full_header, &p_data, &p_byte, 12 );
if( i_max_len < 2 )
{
- intf_WarnMsg( 3,
+ intf_WarnMsg( 1,
"PES packet too short to have a MPEG-2 header" );
- p_input->pf_delete_pes( p_input->p_method_data,
+ input_DeletePES( p_input->p_method_data,
p_pes );
p_pes = NULL;
return;
{
if( i_max_len < 7 )
{
- intf_WarnMsg( 3,
+ intf_WarnMsg( 1,
"PES packet too short to have a MPEG-2 header" );
- p_input->pf_delete_pes( p_input->p_method_data,
+ input_DeletePES( p_input->p_method_data,
p_pes );
p_pes = NULL;
return;
{
if( i_max_len < 12 )
{
- intf_WarnMsg( 3,
+ intf_WarnMsg( 1,
"PES packet too short to have a MPEG-2 header" );
- p_input->pf_delete_pes( p_input->p_method_data,
+ input_DeletePES( p_input->p_method_data,
p_pes );
p_pes = NULL;
return;
i_pes_header_size++;
if( MoveChunk( NULL, &p_data, &p_byte, 1 ) != 1 )
{
- intf_WarnMsg( 3,
+ intf_WarnMsg( 1,
"PES packet too short to have a MPEG-1 header" );
- p_input->pf_delete_pes( p_input->p_method_data, p_pes );
+ input_DeletePES( p_input->p_method_data, p_pes );
p_pes = NULL;
return;
}
}
if( i_pes_header_size == 23 )
{
- intf_ErrMsg( "Too much MPEG-1 stuffing" );
- p_input->pf_delete_pes( p_input->p_method_data, p_pes );
+ intf_ErrMsg( "input error: too much MPEG-1 stuffing" );
+ input_DeletePES( 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( 3,
- "PES packet too short to have a MPEG-1 header" );
- p_input->pf_delete_pes( p_input->p_method_data, p_pes );
+ intf_WarnMsg( 1, "input: PES packet too short "
+ "to have a MPEG-1 header" );
+ input_DeletePES( 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( 3,
- "PES packet too short to have a MPEG-1 header" );
- p_input->pf_delete_pes( p_input->p_method_data, p_pes );
+ intf_WarnMsg( 1, "input: PES packet too short "
+ "to have a MPEG-1 header" );
+ input_DeletePES( 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( 3,
- "PES packet too short to have a MPEG-1 header" );
- p_input->pf_delete_pes( p_input->p_method_data,
+ intf_WarnMsg( 1, "input: PES packet too short "
+ "to have a MPEG-1 header" );
+ input_DeletePES( p_input->p_method_data,
p_pes );
p_pes = NULL;
return;
/* Go to the next data packet. */
if( (p_data = p_data->p_next) == NULL )
{
- intf_ErrMsg( "PES header bigger than payload" );
- p_input->pf_delete_pes( p_input->p_method_data, p_pes );
+ intf_ErrMsg( "input error: PES header bigger than payload" );
+ input_DeletePES( 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" );
- p_input->pf_delete_pes( p_input->p_method_data, p_pes );
+ intf_ErrMsg( "input error: PES header bigger than payload" );
+ input_DeletePES( 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);
- p_input->pf_delete_pes( p_input->p_method_data, p_pes );
+ intf_ErrMsg( "input error: no fifo to receive PES %p "
+ "(who wrote this damn code ?)", p_pes );
+ input_DeletePES( p_input->p_method_data, p_pes );
}
p_pes = NULL;
}
{
#define p_pes (p_es->p_pes)
- //intf_DbgMsg("PES-demultiplexing %p (%p)", p_ts_packet, p_pes);
-
/* If we lost data, insert a NULL data packet (philosophy : 0 is quite
* often an escape sequence in decoders, so that should make them wait
* for the next start code). */
if( !b_unit_start && p_pes == NULL )
{
/* Random access... */
- p_input->pf_delete_packet( p_input->p_method_data, p_data );
+ input_DeletePacket( p_input->p_method_data, p_data );
}
else
{
* packet. This is also here that we can synchronize with the
* stream if we lost packets or if the decoder has just
* started. */
- if( (p_pes = p_input->pf_new_pes( p_input->p_method_data ) ) == NULL )
+ if( (p_pes = input_NewPES( p_input->p_method_data ) ) == NULL )
{
- intf_ErrMsg("Out of memory");
+ intf_ErrMsg( "input error: out of memory" );
p_input->b_error = 1;
return;
}
if( p_data->p_payload_end - p_data->p_payload_start
>= PES_HEADER_SIZE )
{
- p_es->i_pes_real_size =
- U16_AT(p_data->p_payload_start + 4) + 6;
-
+ p_es->i_pes_real_size = ((u16)p_data->p_payload_start[4] << 8)
+ + p_data->p_payload_start[5] + 6;
}
else
{
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
{
u16 i_id;
- i_id = p_data->p_payload_start[3]; /* stream_id */
+ i_id = p_data->p_demux_start[3]; /* stream_id */
if( i_id == 0xBD )
{
/* FIXME : this is not valid if the header is split in multiple
* packets */
/* stream_private_id */
- i_id |= p_data->p_payload_start[ 9 + p_data->p_payload_start[8] ] << 8;
+ i_id |= p_data->p_demux_start[ 9 + p_data->p_demux_start[8] ] << 8;
}
return( i_id );
}
int i;
int i_new_es_number = 0;
- if( p_data->p_payload_start + 10 > p_data->p_payload_end )
+ if( p_data->p_demux_start + 10 > p_data->p_payload_end )
{
- intf_ErrMsg( "PSM too short : packet corrupt" );
+ intf_ErrMsg( "input error: PSM too short : packet corrupt" );
return;
}
if( p_demux->b_has_PSM
- && p_demux->i_PSM_version == (p_data->p_payload_start[6] & 0x1F) )
+ && p_demux->i_PSM_version == (p_data->p_demux_start[6] & 0x1F) )
{
/* Already got that one. */
return;
}
- intf_DbgMsg( "Building PSM" );
p_demux->b_has_PSM = 1;
- p_demux->i_PSM_version = p_data->p_payload_start[6] & 0x1F;
+ p_demux->i_PSM_version = p_data->p_demux_start[6] & 0x1F;
/* Go to elementary_stream_map_length, jumping over
* program_stream_info. */
- p_byte = p_data->p_payload_start + 10
- + U16_AT(&p_data->p_payload_start[8]);
+ p_byte = p_data->p_demux_start + 10
+ + U16_AT(&p_data->p_demux_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_input->stream.pp_programs[0]->pp_es[i_new_es_number] );
}
-#ifdef STATS
- intf_Msg( "input info: The stream map after the PSM is now :" );
- input_DumpStream( p_input );
-#endif
+ if( p_main->b_stats )
+ {
+ intf_StatMsg( "input info: The stream map after the PSM is now :" );
+ input_DumpStream( p_input );
+ }
vlc_mutex_unlock( &p_input->stream.stream_lock );
}
+/*****************************************************************************
+ * input_ReadPS: store a PS packet into a data_buffer_t
+ *****************************************************************************/
+#define PEEK( SIZE ) \
+ i_error = input_Peek( p_input, &p_peek, SIZE ); \
+ if( i_error == -1 ) \
+ { \
+ return( -1 ); \
+ } \
+ else if( i_error < SIZE ) \
+ { \
+ /* EOF */ \
+ return( 0 ); \
+ }
+
+ssize_t input_ReadPS( input_thread_t * p_input, data_packet_t ** pp_data )
+{
+ byte_t * p_peek;
+ size_t i_packet_size;
+ ssize_t i_error, i_read;
+
+ /* Read what we believe to be a packet header. */
+ PEEK( 4 );
+
+ if( p_peek[0] || p_peek[1] || p_peek[2] != 1 || p_peek[3] < 0xB9 )
+ {
+ if( p_peek[0] || p_peek[1] || p_peek[2] )
+ {
+ /* It is common for MPEG-1 streams to pad with zeros
+ * (although it is forbidden by the recommendation), so
+ * don't bother everybody in this case. */
+ intf_WarnMsg( 3, "input warning: garbage (0x%.2x%.2x%.2x%.2x)",
+ p_peek[0], p_peek[1], p_peek[2], p_peek[3] );
+ }
+
+ /* This is not the startcode of a packet. Read the stream
+ * until we find one. */
+ while( p_peek[0] || p_peek[1] || p_peek[2] != 1 || p_peek[3] < 0xB9 )
+ {
+ p_input->p_current_data++;
+ PEEK( 4 );
+ }
+ /* Packet found. */
+ }
+
+ /* 0x1B9 == SYSTEM_END_CODE, it is only 4 bytes long. */
+ if( p_peek[3] != 0xB9 )
+ {
+ /* The packet is at least 6 bytes long. */
+ PEEK( 6 );
+
+ if( p_peek[3] != 0xBA )
+ {
+ /* That's the case for all packets, except pack header. */
+ i_packet_size = (p_peek[4] << 8) | p_peek[5];
+ }
+ else
+ {
+ /* Pack header. */
+ if( (p_peek[4] & 0xC0) == 0x40 )
+ {
+ /* MPEG-2 */
+ i_packet_size = 8;
+ }
+ else if( (p_peek[4] & 0xF0) == 0x20 )
+ {
+ /* MPEG-1 */
+ i_packet_size = 6;
+ }
+ else
+ {
+ intf_ErrMsg( "Unable to determine stream type" );
+ return( -1 );
+ }
+ }
+ }
+ else
+ {
+ /* System End Code */
+ i_packet_size = -2;
+ }
+
+ /* Fetch a packet of the appropriate size. */
+ i_read = input_SplitBuffer( p_input, pp_data, i_packet_size + 6 );
+ if( i_read <= 0 )
+ {
+ return( i_read );
+ }
+
+ /* In MPEG-2 pack headers we still have to read stuffing bytes. */
+ if( ((*pp_data)->p_demux_start[3] == 0xBA) && (i_packet_size == 8) )
+ {
+ size_t i_stuffing = ((*pp_data)->p_demux_start[13] & 0x7);
+ /* Force refill of the input buffer - though we don't care
+ * about p_peek. Please note that this is unoptimized. */
+ PEEK( i_stuffing );
+ p_input->p_current_data += i_stuffing;
+ }
+
+ return( 1 );
+}
+
+#undef PEEK
+
/*****************************************************************************
* input_ParsePS: read the PS header
*****************************************************************************/
u32 i_code;
es_descriptor_t * p_es = NULL;
- i_code = p_data->p_payload_start[3];
+ i_code = p_data->p_demux_start[3];
+
if( i_code > 0xBC ) /* ES start code */
{
u16 i_id;
i_id, 0 );
if( p_es != NULL )
{
- p_es->i_stream_id = p_data->p_payload_start[3];
+ p_es->i_stream_id = p_data->p_demux_start[3];
/* Set stream type and auto-spawn. */
if( (i_id & 0xF0) == 0xE0 )
{
/* MPEG video */
p_es->i_type = MPEG2_VIDEO_ES;
+ p_es->i_cat = VIDEO_ES;
#ifdef AUTO_SPAWN
if( !p_input->stream.b_seekable )
input_SelectES( p_input, p_es );
/* MPEG audio */
p_es->i_type = MPEG2_AUDIO_ES;
p_es->b_audio = 1;
+ p_es->i_cat = AUDIO_ES;
#ifdef AUTO_SPAWN
if( !p_input->stream.b_seekable )
- if( main_GetIntVariable( INPUT_CHANNEL_VAR, 0 )
- == (p_es->i_id & 0x1F) )
- switch( main_GetIntVariable( INPUT_AUDIO_VAR, 0 ) )
+ if( config_GetIntVariable( "audio-channel" )
+ == (p_es->i_id & 0x1F) ||
+ ( config_GetIntVariable( "audio-channel" ) < 0
+ && !(p_es->i_id & 0x1F) ) )
+ switch( config_GetIntVariable( "audio-type" ) )
{
- case 0:
- main_PutIntVariable( INPUT_CHANNEL_VAR,
- REQUESTED_MPEG );
+ case -1:
case REQUESTED_MPEG:
input_SelectES( p_input, p_es );
}
/* AC3 audio (0x80->0x8F) */
p_es->i_type = AC3_AUDIO_ES;
p_es->b_audio = 1;
+ p_es->i_cat = AUDIO_ES;
#ifdef AUTO_SPAWN
if( !p_input->stream.b_seekable )
- if( main_GetIntVariable( INPUT_CHANNEL_VAR, 0 )
- == ((p_es->i_id & 0xF00) >> 8) )
- switch( main_GetIntVariable( INPUT_AUDIO_VAR, 0 ) )
+ if( config_GetIntVariable( "audio-channel" )
+ == ((p_es->i_id & 0xF00) >> 8) ||
+ ( config_GetIntVariable( "audio-channel" ) < 0
+ && !((p_es->i_id & 0xF00) >> 8)) )
+ switch( config_GetIntVariable( "audio-type" ) )
{
- case 0:
- main_PutIntVariable( INPUT_CHANNEL_VAR,
- REQUESTED_AC3 );
+ case -1:
case REQUESTED_AC3:
input_SelectES( p_input, p_es );
}
{
/* Subtitles video (0x20->0x3F) */
p_es->i_type = DVD_SPU_ES;
+ p_es->i_cat = SPU_ES;
#ifdef AUTO_SPAWN
- if( main_GetIntVariable( INPUT_SUBTITLE_VAR, -1 )
+ if( config_GetIntVariable( "spu-channel" )
== ((p_es->i_id & 0x1F00) >> 8) )
{
if( !p_input->stream.b_seekable )
/* LPCM audio (0xA0->0xAF) */
p_es->i_type = LPCM_AUDIO_ES;
p_es->b_audio = 1;
- /* FIXME : write the decoder */
+ p_es->i_cat = AUDIO_ES;
}
else
{
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_demux_start[0] << 24)
+ | ((u32)p_data->p_demux_start[1] << 16)
+ | ((u32)p_data->p_demux_start[2] << 8)
+ | p_data->p_demux_start[3];
if( i_code <= 0x1BC )
{
switch( i_code )
mtime_t scr_time;
u32 i_mux_rate;
- if( (p_data->p_payload_start[4] & 0xC0) == 0x40 )
+ if( (p_data->p_demux_start[4] & 0xC0) == 0x40 )
{
/* MPEG-2 */
byte_t p_header[14];
byte_t * p_byte;
- p_byte = p_data->p_payload_start;
+ p_byte = p_data->p_demux_start;
if( MoveChunk( p_header, &p_data, &p_byte, 14 ) != 14 )
{
- intf_WarnMsg( 3, "Packet too short to have a header" );
+ intf_WarnMsg( 1, "input: packet too short "
+ "to have a header" );
b_trash = 1;
break;
}
/* mux_rate */
i_mux_rate = ((u32)U16_AT(p_header + 10) << 6)
| (p_header[12] >> 2);
+ /* FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME
+ * This is the biggest kludge ever !
+ * I don't know what's wrong with mux_rate calculation
+ * but this heuristic work well : */
+ i_mux_rate <<= 1;
+ i_mux_rate /= 3;
}
else
{
/* MPEG-1 SCR is like PTS. */
byte_t p_header[12];
byte_t * p_byte;
- p_byte = p_data->p_payload_start;
+ p_byte = p_data->p_demux_start;
if( MoveChunk( p_header, &p_data, &p_byte, 12 ) != 12 )
{
- intf_WarnMsg( 3, "Packet too short to have a header" );
+ intf_WarnMsg( 1, "input: packet too short "
+ "to have a header" );
b_trash = 1;
break;
}
i_mux_rate = (U32_AT(p_header + 8) & 0x7FFFFE) >> 1;
}
/* Call the pace control. */
- input_ClockManageRef( p_input, p_input->stream.pp_programs[0],
+ input_ClockManageRef( p_input,
+ p_input->stream.p_selected_program,
scr_time );
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( 1, "Unwanted packet received with start code %x",
- i_code );
+ intf_WarnMsg( 3, "input: unwanted packet received "
+ "with start code 0x%.8x", i_code );
}
}
else
&& (!p_es->b_audio || !p_input->stream.control.b_mute) )
{
vlc_mutex_unlock( &p_input->stream.control.control_lock );
-#ifdef STATS
p_es->c_packets++;
-#endif
input_GatherPES( p_input, p_data, p_es, 1, 0 );
}
else
/* 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++;
-#endif
+ input_DeletePacket( p_input->p_method_data, p_data );
+ p_input->stream.c_packets_trashed++;
}
}
* TS Demultiplexing
*/
+/*****************************************************************************
+ * input_ReadTS: store a TS packet into a data_buffer_t
+ *****************************************************************************/
+#define PEEK( SIZE ) \
+ i_error = input_Peek( p_input, &p_peek, SIZE ); \
+ if( i_error == -1 ) \
+ { \
+ return( -1 ); \
+ } \
+ else if( i_error < SIZE ) \
+ { \
+ /* EOF */ \
+ return( 0 ); \
+ }
+
+ssize_t input_ReadTS( input_thread_t * p_input, data_packet_t ** pp_data )
+{
+ byte_t * p_peek;
+ ssize_t i_error, i_read;
+
+ PEEK( 1 );
+
+ if( *p_peek != TS_SYNC_CODE )
+ {
+ intf_WarnMsg( 3, "input warning: garbage at input (%x)", *p_peek );
+
+ if( p_input->i_mtu )
+ {
+ while( *p_peek != TS_SYNC_CODE )
+ {
+ /* Try to resync on next packet. */
+ PEEK( TS_PACKET_SIZE );
+ p_input->p_current_data += TS_PACKET_SIZE;
+ PEEK( 1 );
+ }
+ }
+ else
+ {
+ /* Move forward until we find 0x47 (and hope it's the good
+ * one... FIXME) */
+ while( *p_peek != TS_SYNC_CODE )
+ {
+ p_input->p_current_data++;
+ PEEK( 1 );
+ }
+ }
+ }
+
+ i_read = input_SplitBuffer( p_input, pp_data, TS_PACKET_SIZE );
+ if( i_read <= 0 )
+ {
+ return( i_read );
+ }
+
+ return( 1 );
+}
+
/*****************************************************************************
* input_DemuxTS: first step of demultiplexing: the TS header
*****************************************************************************/
es_ts_data_t * p_es_demux = NULL;
pgrm_ts_data_t * p_pgrm_demux = NULL;
- #define p (p_data->p_buffer)
+#define p (p_data->p_demux_start)
/* Extract flags values from TS common header. */
- i_pid = U16_AT(&p[1]) & 0x1fff;
+ i_pid = ((p[1] & 0x1F) << 8) | p[2];
b_unit_start = (p[1] & 0x40);
b_adaptation = (p[3] & 0x20);
b_payload = (p[3] & 0x10);
p_es_demux = (es_ts_data_t *)p_es->p_demux_data;
if( p_es_demux->b_psi )
+ {
b_psi = 1;
+ }
else
+ {
p_pgrm_demux = (pgrm_ts_data_t *)p_es->p_pgrm->p_demux_data;
+ }
}
vlc_mutex_lock( &p_input->stream.control.control_lock );
/* 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;
+ 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 );
/* Don't change the order of the tests : if b_psi then p_pgrm_demux
- * may still be null. Who said it was ugly ? */
+ * may still be null. Who said it was ugly ?
+ * I have written worse. --Meuuh */
if( ( p_es != NULL ) &&
((p_es->p_decoder_fifo != NULL) || b_psi
|| (p_pgrm_demux->i_pcr_pid == i_pid) ) )
{
-#ifdef STATS
p_es->c_packets++;
-#endif
/* Extract adaptation field information if any */
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;
-#ifdef STATS
p_es->c_invalid_packets++;
-#endif
}
/* Now we are sure that the byte containing flags is present:
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 );
p_es->p_pgrm->i_synchro_state = SYNCHRO_REINIT;
/* There also may be a continuity_counter
- * discontinuity: resynchronise our counter with
+ * discontinuity: resynchronize our counter with
* the one of the stream. */
p_es_demux->i_continuity_counter = (p[3] & 0x0f) - 1;
}
/* If this is a PCR_PID, and this TS packet contains a
* PCR, we pass it along to the PCR decoder. */
- if( (p_pgrm_demux->i_pcr_pid == i_pid) && (p[5] & 0x10) )
+ if( !b_psi && (p_pgrm_demux->i_pcr_pid == i_pid)
+ && (p[5] & 0x10) )
{
/* There should be a PCR field in the packet, check
* if the adaptation field is long enough to carry
{
/* Read the PCR. */
mtime_t pcr_time;
- pcr_time =
- ( (mtime_t)U32_AT((u32*)&p[6]) << 1 )
- | ( p[10] >> 7 );
+ pcr_time = ( (mtime_t)p[6] << 25 ) |
+ ( (mtime_t)p[7] << 17 ) |
+ ( (mtime_t)p[8] << 9 ) |
+ ( (mtime_t)p[9] << 1 ) |
+ ( (mtime_t)p[10] >> 7 );
/* Call the pace control. */
input_ClockManageRef( p_input, p_es->p_pgrm,
pcr_time );
* draft. As there is nothing interesting in this packet
* (except PCR that have already been handled), we can trash
* the packet. */
- intf_WarnMsg( 1,
- "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 )
{
- /* FIXME: this can never happen, can it ? --Meuuh */
/* Duplicate packet: mark it as being to be trashed. */
- intf_WarnMsg( 1, "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_DbgMsg( "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 */
/* 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++;
-#endif
+ input_DeletePacket( p_input->p_method_data, p_data );
+ p_input->stream.c_packets_trashed++;
}
else
{
* (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" );
+ intf_WarnMsg( 2, "input: 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" );
+ intf_WarnMsg( 2, "input: invalid PSI packet" );
p_psi->b_trash = 1;
}
else
{
- p_psi->i_section_length = U16_AT(p+1) & 0x0fff;
+ 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->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;
}
}
{
/* This was the last section of PSI */
p_psi->b_is_complete = 1;
+
+ 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_WarnMsg(2, "Received unknown PSI in DemuxPSI");
+ }
}
}
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;
}
}
- 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
+
+ input_DeletePacket( p_input->p_method_data, p_data );
return ;
}
*****************************************************************************/
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 krashed */
- 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 = U16_AT(p_current_data+1) & 0x0fff;
+ 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 = U16_AT(p_current_data + i_loop*4 + 8);
- i_pmt_pid = U16_AT( p_current_data + i_loop*4 + 10) & 0x1fff;
+ 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
+#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,
+ * careful to delete 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 )
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;
-
+
+ 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 */
- 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 = U16_AT(p_current_data+1) & 0x0fff;
+ 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 = U16_AT(p_current_data+10) & 0x0fff;
+ 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;
+ 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;
+ 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 decoders what kind of stream it is */
p_new_es->i_type = i_stream_type;
+
+ /* 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:
+ p_new_es->i_cat = VIDEO_ES;
+ break;
+ case MPEG1_AUDIO_ES:
+ case MPEG2_AUDIO_ES:
+ p_new_es->i_cat = AUDIO_ES;
+ break;
+ case LPCM_AUDIO_ES :
+ case AC3_AUDIO_ES :
+ p_new_es->i_stream_id = 0xBD;
+ p_new_es->i_cat = AUDIO_ES;
+ break;
+ /* Not sure this one is fully specification-compliant */
+ case DVD_SPU_ES :
+ p_new_es->i_stream_id = 0xBD;
+ p_new_es->i_cat = SPU_ES;
+ break;
+ default :
+ p_new_es->i_cat = UNKNOWN_ES;
+ break;
+ }
+ }
- /* 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_data += 3 + i_section_length;
- p_current_section+=1;
+ 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;
+ u16 i_id = (u16)config_GetIntVariable( "program" );
+
+ 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_es->p_pgrm )
+ 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
-
- /* Remove lock */
- vlc_mutex_unlock( &p_input->stream.stream_lock );
}
+