Commit 50118171 authored by Christophe Massiot's avatar Christophe Massiot

* Big cleanup of the PS input plugin ;

* Fixed a bug in AC3 initialization ;
* PS streams are now pre-parsed (this can take a while) if possible ;

./configure is required after this update.
parent d719269b
......@@ -183,6 +183,9 @@
/* Maximum number of selected ES in an input thread */
#define INPUT_MAX_SELECTED_ES 10
/* Maximum size of a data packet (128 kB) */
#define INPUT_MAX_PACKET_SIZE 131072
/* Maximum number of TS packets in the client at any time
* INPUT_MAX_TS + 1 must be a power of 2, to optimize the %(INPUT_MAX_TS+1)
* operation with a &INPUT_MAX_TS in the case of a fifo netlist.
......
/* Structures exported to the interface */
/*****************************************************************************
* input_ext-intf.h: structures of the input exported to the interface
* This header provides structures to read the stream descriptors and
* control the pace of reading.
*****************************************************************************
* Copyright (C) 1999, 2000 VideoLAN
* $Id: input_ext-intf.h,v 1.4 2000/12/20 16:04:31 massiot Exp $
*
* Authors:
*
* 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.
*****************************************************************************/
/*
* Communication input -> interface
......@@ -54,8 +77,10 @@ typedef struct es_descriptor_s
#define MPEG1_AUDIO_ES 0x03
#define MPEG2_AUDIO_ES 0x04
#define AC3_AUDIO_ES 0x81
#define DVD_SPU_ES 0x82 /* 0x82 might violate the norm */
/* These ones might violate the norm : */
#define DVD_SPU_ES 0x82
#define LPCM_AUDIO_ES 0x83
#define UNKNOWN_ES 0xFF
/*****************************************************************************
* pgrm_descriptor_t
......
......@@ -126,6 +126,17 @@ static int InitThread (ac3dec_thread_t * p_ac3dec)
intf_DbgMsg ("ac3dec debug: initializing ac3 decoder thread %p\n", p_ac3dec);
/* Get the first data packet. */
vlc_mutex_lock( &p_ac3dec->p_fifo->data_lock );
while ( DECODER_FIFO_ISEMPTY( *p_ac3dec->p_fifo ) )
{
if ( p_ac3dec->p_fifo->b_die )
{
vlc_mutex_unlock( &p_ac3dec->p_fifo->data_lock );
return -1;
}
vlc_cond_wait( &p_ac3dec->p_fifo->data_wait, &p_ac3dec->p_fifo->data_lock );
}
p_ac3dec->p_data = DECODER_FIFO_START(*p_ac3dec->p_fifo)->p_first;
byte_stream = ac3_byte_stream (&p_ac3dec->ac3_decoder);
byte_stream->p_byte = p_ac3dec->p_data->p_payload_start;
......
......@@ -4,7 +4,7 @@
* decoders.
*****************************************************************************
* Copyright (C) 1998, 1999, 2000 VideoLAN
* $Id: input.c,v 1.59 2000/12/19 19:08:51 massiot Exp $
* $Id: input.c,v 1.60 2000/12/20 16:04:31 massiot Exp $
*
* Authors:
*
......@@ -176,7 +176,8 @@ void input_DestroyThread( input_thread_t *p_input, int *pi_status )
*****************************************************************************/
static void RunThread( input_thread_t *p_input )
{
data_packet_t * pp_packets[INPUT_READ_ONCE];
data_packet_t * pp_packets[INPUT_READ_ONCE];
int i_error, i;
InitThread( p_input );
......@@ -195,15 +196,23 @@ static void RunThread( input_thread_t *p_input )
}
vlc_mutex_unlock( &p_input->stream.control.control_lock );
p_input->p_plugin->pf_read( p_input, pp_packets );
if( !p_input->b_error )
i_error = p_input->p_plugin->pf_read( p_input, pp_packets );
/* Demultiplex read packets. */
for( i = 0; i < INPUT_READ_ONCE && pp_packets[i] != NULL; i++ )
{
int i;
p_input->p_plugin->pf_demux( p_input, pp_packets[i] );
}
for( i = 0; i < INPUT_READ_ONCE && pp_packets[i] != NULL; i++ )
if( i_error )
{
if( i_error == 1 )
{
p_input->p_plugin->pf_demux( p_input, pp_packets[i] );
/* End of file */
intf_WarnMsg( 1, "End of file reached" );
/* FIXME: don't treat that as an error */
}
p_input->b_error = 1;
}
}
......
/* Communication plugin -> input */
/*****************************************************************************
* input.h: structures of the input not exported to other modules
*****************************************************************************
* Copyright (C) 1999, 2000 VideoLAN
* $Id: input.h,v 1.4 2000/12/20 16:04:31 massiot Exp $
*
* Authors:
*
* 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.
*****************************************************************************/
/*
* Communication plugin -> input
*/
/* FIXME: you've gotta move this move this, you've gotta move this move this */
#define INPUT_READ_ONCE 7 /* We live in a world dominated by Ethernet. *
* Ethernet MTU is 1500 bytes, so in a UDP *
* packet we can put : 1500/188 = 7 TS *
......@@ -22,7 +48,7 @@ typedef struct input_capabilities_s
void (* pf_end)( struct input_thread_s * );
/* Read & Demultiplex */
void (* pf_read)( struct input_thread_s *,
int (* pf_read)( struct input_thread_s *,
struct data_packet_s * pp_packets[INPUT_READ_ONCE] );
void (* pf_demux)( struct input_thread_s *,
struct data_packet_s * );
......@@ -54,6 +80,7 @@ void input_InitStream( struct input_thread_s *, size_t );
struct pgrm_descriptor_s * input_AddProgram( struct input_thread_s *,
u16, size_t );
void input_DelProgram( struct input_thread_s *, u16 );
void input_DumpStream( struct input_thread_s * );
struct es_descriptor_s * input_AddES( struct input_thread_s *,
struct pgrm_descriptor_s *, u16,
size_t );
......
......@@ -2,7 +2,7 @@
* input_programs.c: es_descriptor_t, pgrm_descriptor_t management
*****************************************************************************
* Copyright (C) 1999, 2000 VideoLAN
* $Id: input_programs.c,v 1.4 2000/12/19 19:08:51 massiot Exp $
* $Id: input_programs.c,v 1.5 2000/12/20 16:04:31 massiot Exp $
*
* Authors:
*
......@@ -51,6 +51,7 @@
*****************************************************************************/
void input_InitStream( input_thread_t * p_input, size_t i_data_len )
{
p_input->stream.i_stream_id = 0;
p_input->stream.i_pgrm_number = 0;
p_input->stream.pp_programs = NULL;
......@@ -283,6 +284,44 @@ void input_DelES( input_thread_t * p_input, u16 i_id )
}
}
#ifdef STATS
/*****************************************************************************
* input_DumpStream: dumps the contents of a stream descriptor
*****************************************************************************/
void input_DumpStream( input_thread_t * p_input )
{
int i, j;
#define S p_input->stream
intf_Msg( "input info: Dumping stream ID 0x%x\n", S.i_stream_id );
if( S.b_seekable )
intf_Msg( "input info: seekable stream, position: %d/%d\n",
S.i_tell, S.i_size );
else
intf_Msg( "input info: %s\n", S.b_pace_control ? "pace controlled" :
"pace un-controlled" );
#undef S
for( i = 0; i < p_input->stream.i_pgrm_number; i++ )
{
#define P p_input->stream.pp_programs[i]
intf_Msg( "input info: Dumping program 0x%x, version %d (%s)\n",
P->i_number, P->i_version,
P->b_is_ok ? "complete" : "partial" );
if( P->i_synchro_state == SYNCHRO_OK )
intf_Msg( "input info: synchro absolute delta : %lld (jitter : %lld)\n",
P->delta_absolute, P->delta_cr );
#undef P
for( j = 0; j < p_input->stream.pp_programs[i]->i_es_number; j++ )
{
#define ES p_input->stream.pp_programs[i]->pp_es[j]
intf_Msg( "input info: ES 0x%x, stream 0x%x, type 0x%x, %s\n",
ES->i_id, ES->i_stream_id, ES->i_type,
ES->p_decoder_fifo != NULL ? "selected" : "not selected");
#undef ES
}
}
}
#endif
/*****************************************************************************
* InitDecConfig: initializes a decoder_config_t
*****************************************************************************/
......
......@@ -2,6 +2,7 @@
* input_ps.c: PS demux and packet management
*****************************************************************************
* Copyright (C) 1998, 1999, 2000 VideoLAN
* $Id: input_ps.c,v 1.7 2000/12/20 16:04:31 massiot Exp $
*
* Authors:
*
......@@ -52,7 +53,7 @@
* Local prototypes
*****************************************************************************/
static int PSProbe ( struct input_thread_s * );
static void PSRead ( struct input_thread_s *,
static int PSRead ( struct input_thread_s *,
data_packet_t * p_packets[INPUT_READ_ONCE] );
static void PSInit ( struct input_thread_s * );
static void PSEnd ( struct input_thread_s * );
......@@ -100,10 +101,49 @@ static void PSInit( input_thread_t * p_input )
}
fseek( p_method->stream, 0, SEEK_SET );
/* Pre-parse the stream to gather stream_descriptor_t. */
input_InitStream( p_input, 0 );
input_AddProgram( p_input, 0, sizeof( stream_ps_data_t ) );
if( p_input->stream.b_seekable )
{
/* Pre-parse the stream to gather stream_descriptor_t. */
p_input->stream.pp_programs[0]->b_is_ok = 0;
/* FIXME: don't read all stream (it can be long !) */
while( !p_input->b_die && !p_input->b_error )
{
int i_result, i;
data_packet_t * pp_packets[INPUT_READ_ONCE];
i_result = PSRead( p_input, pp_packets );
if( i_result == 1 ) break;
if( i_result == -1 )
{
p_input->b_error = 1;
break;
}
for( i = 0; i < INPUT_READ_ONCE && pp_packets[i] != NULL; i++ )
{
/* FIXME: use i_p_config_t */
input_ParsePS( p_input, pp_packets[i] );
}
}
fseek( p_method->stream, 0, SEEK_SET );
vlc_mutex_lock( &p_input->stream.stream_lock );
p_input->stream.pp_programs[0]->b_is_ok = 1;
p_input->stream.i_tell = 0;
#ifdef STATS
input_DumpStream( p_input );
#endif
vlc_mutex_unlock( &p_input->stream.stream_lock );
}
else
{
/* The programs will be added when we read them. */
vlc_mutex_lock( &p_input->stream.stream_lock );
p_input->stream.pp_programs[0]->b_is_ok = 0;
vlc_mutex_unlock( &p_input->stream.stream_lock );
}
}
/*****************************************************************************
......@@ -116,125 +156,157 @@ static void PSEnd( input_thread_t * p_input )
}
/*****************************************************************************
* PSRead: reads a data packet
* SafeRead: reads a chunk of stream and correctly detects errors
*****************************************************************************/
/* FIXME: read INPUT_READ_ONCE packet at once */
static void PSRead( input_thread_t * p_input,
data_packet_t * p_packets[INPUT_READ_ONCE] )
static __inline__ int SafeRead( input_thread_t * p_input, byte_t * p_buffer,
size_t i_len )
{
byte_t p_header[6];
data_packet_t * p_data;
int i_packet_size;
thread_ps_data_t * p_method;
int i_error;
p_method = (thread_ps_data_t *)p_input->p_method_data;
while( fread( p_header, 6, 1, p_method->stream ) != 1 )
while( fread( p_buffer, i_len, 1, p_method->stream ) != 1 )
{
int i_error;
if( (i_error = ferror( p_method->stream )) )
if( feof( p_method->stream ) )
{
intf_ErrMsg( "Read 1 failed (%s)", strerror(i_error) );
p_input->b_error = 1;
return;
return( 1 );
}
if( feof( p_method->stream ) )
if( (i_error = ferror( p_method->stream )) )
{
intf_ErrMsg( "EOF reached" );
p_input->b_error = 1;
return;
intf_ErrMsg( "Read failed (%s)", strerror(i_error) );
return( -1 );
}
}
vlc_mutex_lock( &p_input->stream.stream_lock );
p_input->stream.i_tell += i_len;
vlc_mutex_unlock( &p_input->stream.stream_lock );
return( 0 );
}
if( (U32_AT(p_header) & 0xFFFFFF00) != 0x100L )
/*****************************************************************************
* PSRead: reads a data packet
*****************************************************************************
* Returns -1 in case of error, 0 if everything went well, and 1 in case of
* EOF.
*****************************************************************************/
static int PSRead( input_thread_t * p_input,
data_packet_t * pp_packets[INPUT_READ_ONCE] )
{
byte_t p_header[6];
data_packet_t * p_data;
size_t i_packet_size;
int i_packet, i_error;
thread_ps_data_t * p_method;
p_method = (thread_ps_data_t *)p_input->p_method_data;
memset( pp_packets, 0, INPUT_READ_ONCE * sizeof(data_packet_t *) );
for( i_packet = 0; i_packet < INPUT_READ_ONCE; i_packet++ )
{
u32 i_buffer = U32_AT(p_header);
intf_WarnMsg( 1, "Garbage at input (%x)\n", i_buffer );
while( (i_buffer & 0xFFFFFF00) != 0x100L )
/* Read what we believe to be a packet header. */
if( (i_error = SafeRead( p_input, p_header, 6 )) )
{
i_buffer <<= 8;
i_buffer |= getc( p_method->stream );
if( feof(p_method->stream) || ferror(p_method->stream) )
{
p_input->b_error = 1;
return;
}
return( i_error );
}
*(u32 *)p_header = U32_AT(&i_buffer);
fread( p_header + 4, 2, 1, p_method->stream );
}
if( U32_AT(p_header) != 0x1BA )
{
i_packet_size = U16_AT(&p_header[4]);
}
else
{
if( (p_header[4] & 0xC0) == 0x40 )
if( (U32_AT(p_header) & 0xFFFFFF00) != 0x100L )
{
/* MPEG-2 */
i_packet_size = 8;
/* This is not the startcode of a packet. Read the stream
* until we find one. */
u32 i_startcode = U32_AT(p_header);
int i_dummy;
if( i_startcode )
{
/* 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( 1, "Garbage at input (%x)\n", i_startcode );
}
while( (i_startcode & 0xFFFFFF00) != 0x100L )
{
i_startcode <<= 8;
if( (i_dummy = getc( p_method->stream )) != EOF )
{
i_startcode |= i_dummy;
}
else
{
return( 1 );
}
}
/* Packet found. */
*(u32 *)p_header = U32_AT(&i_startcode);
if( (i_error = SafeRead( p_input, p_header + 4, 2 )) )
{
return( i_error );
}
}
else if( (p_header[4] & 0xF0) == 0x20 )
if( U32_AT(p_header) != 0x1BA )
{
/* MPEG-1 */
i_packet_size = 6;
/* That's the case for all packets, except pack header. */
i_packet_size = U16_AT(&p_header[4]);
}
else
{
intf_ErrMsg( "Unable to determine stream type" );
p_input->b_error = 1;
return;
/* Pack header. */
if( (p_header[4] & 0xC0) == 0x40 )
{
/* MPEG-2 */
i_packet_size = 8;
}
else if( (p_header[4] & 0xF0) == 0x20 )
{
/* MPEG-1 */
i_packet_size = 6;
}
else
{
intf_ErrMsg( "Unable to determine stream type" );
return( -1 );
}
}
}
if( (p_data = NewPacket( p_input, i_packet_size + 6 )) == NULL )
{
p_input->b_error = 1;
intf_ErrMsg( "Out of memory" );
return;
}
memcpy( p_data->p_buffer, p_header, 6 );
/* FIXME: catch EINTR ! */
while( fread( p_data->p_buffer + 6, i_packet_size,
1, p_method->stream ) != 1 )
{
int i_error;
if( (i_error = ferror( p_method->stream)) )
/* Fetch a packet of the appropriate size. */
if( (p_data = NewPacket( p_input, i_packet_size + 6 )) == NULL )
{
intf_ErrMsg( "Read 1 failed (%s)", strerror(i_error) );
p_input->b_error = 1;
return;
intf_ErrMsg( "Out of memory" );
return( -1 );
}
if( feof( p_method->stream ) )
/* Copy the header we already read. */
memcpy( p_data->p_buffer, p_header, 6 );
/* Read the remaining of the packet. */
if( (i_error =
SafeRead( p_input, p_data->p_buffer + 6, i_packet_size )) )
{
intf_ErrMsg( "EOF reached" );
p_input->b_error = 1;
return;
return( i_error );
}
}
if( U32_AT(p_header) == 0x1BA )
{
if( i_packet_size == 8 )
/* In MPEG-2 pack headers we still have to read stuffing bytes. */
if( U32_AT(p_header) == 0x1BA )
{
/* MPEG-2 stuffing bytes */
byte_t p_garbage[8];
if( (p_data->p_buffer[13] & 0x7) != 0 )
if( i_packet_size == 8 && (p_data->p_buffer[13] & 0x7) != 0 )
{
/* FIXME: catch EINTR ! */
fread( p_garbage, p_garbage[0] & 0x7, 1,
p_method->stream );
/* MPEG-2 stuffing bytes */
byte_t p_garbage[8];
if( (i_error = SafeRead( p_input, p_garbage,
p_data->p_buffer[13] & 0x7)) )
{
return( i_error );
}
}
}
/* Give the packet to the other input stages. */
pp_packets[i_packet] = p_data;
}
memset( p_packets, 0, sizeof(p_packets) );
p_packets[0] = p_data;
return( 0 );
}
......@@ -250,6 +322,13 @@ static struct data_packet_s * NewPacket( void * p_garbage,
{
data_packet_t * p_data;
/* Safety check */
if( i_size > INPUT_MAX_PACKET_SIZE )
{
intf_ErrMsg( "Packet too big (%d)", i_size );
return NULL;
}
if( (p_data = (data_packet_t *)malloc( sizeof(data_packet_t) )) == NULL )
{
intf_DbgMsg( "Out of memory" );
......
......@@ -2,7 +2,7 @@
* mpeg_system.c: TS, PS and PES management
*****************************************************************************
* Copyright (C) 1998, 1999, 2000 VideoLAN
* $Id: mpeg_system.c,v 1.9 2000/12/19 19:08:51 massiot Exp $
* $Id: mpeg_system.c,v 1.10 2000/12/20 16:04:31 massiot Exp $
*
* Authors:
*
......@@ -748,6 +748,105 @@ static void DecodePSM( input_thread_t * p_input, data_packet_t * p_data )
}
}
/*****************************************************************************
* input_ParsePS: read the PS header
*****************************************************************************/
es_descriptor_t * input_ParsePS( input_thread_t * p_input,
data_packet_t * p_data )
{
u32 i_code;
es_descriptor_t * p_es = NULL;
i_code = U32_AT( p_data->p_buffer );
if( i_code > 0x1BC ) /* ES start code */
{
u16 i_id;
int i_dummy;
/* This is a PES packet. Find out if we want it or not. */
i_id = GetID( p_data );
vlc_mutex_lock( &p_input->stream.stream_lock );
if( p_input->stream.pp_programs[0]->b_is_ok )
{
/* Look only at the selected ES. */
for( i_dummy = 0; i_dummy < INPUT_MAX_SELECTED_ES; i_dummy++ )
{
if( p_input->pp_selected_es[i_dummy] != NULL
&& p_input->pp_selected_es[i_dummy]->i_id == i_id )
{
p_es = p_input->pp_selected_es[i_dummy];
break;
}
}
}
else
{
/* Search all ES ; if not found -> AddES */
for( i_dummy = 0; i_dummy < INPUT_MAX_ES; i_dummy++ )
{
if( p_input->p_es[i_dummy].i_id != EMPTY_ID
&& p_input->p_es[i_dummy].i_id == i_id )
{
p_es = &p_input->p_es[i_dummy];
break;
}
}
if( p_es == NULL )
{
p_es = input_AddES( p_input, p_input->stream.pp_programs[0],
i_id, 0 );
if( p_es != NULL )
{
p_es->i_stream_id = p_data->p_buffer[3];
/* Set stream type and auto-spawn. */
if( (i_id & 0xF0) == 0xE0 )
{
/* MPEG video */
p_es->i_type = MPEG2_VIDEO_ES;
#ifdef AUTO_SPAWN
input_SelectES( p_input, p_es );
#endif
}
else if( (i_id & 0xE0) == 0xC0 )
{
/* MPEG audio */
p_es->i_type = MPEG2_AUDIO_ES;
#ifdef AUTO_SPAWN
input_SelectES( p_input, p_es );
#endif
}
else if( (i_id & 0xF0FF) == 0x80BD )
{
/* AC3 audio */
p_es->i_type = AC3_AUDIO_ES;
#ifdef AUTO_SPAWN
input_SelectES( p_input, p_es );
#endif
}
else if( (i_id & 0xF0FF) == 0x20BD )
{
/* Subtitles video */
p_es->i_type = DVD_SPU_ES;
#ifdef AUTO_SPAWN
input_SelectES( p_input, p_es );
#endif
}
else
{
p_es->i_type = UNKNOWN_ES;
}
}
}
} /* stream.b_is_ok */
vlc_mutex_unlock( &p_input->stream.stream_lock );
} /* i_code > 0xBC */
return( p_es );
}
/*****************************************************************************
* input_DemuxPS: first step of demultiplexing: the PS header
*****************************************************************************/
......@@ -758,7 +857,7 @@ void input_DemuxPS( input_thread_t * p_input, data_packet_t * p_data )
es_descriptor_t * p_es = NULL;
i_code = U32_AT( p_data->p_buffer );
if( i_code >= 0x1B9 && i_code <= 0x1BC )
if( i_code <= 0x1BC )
{
switch( i_code )
{
......@@ -819,99 +918,9 @@ void input_DemuxPS( input_thread_t * p_input, data_packet_t * p_data )
}
else
{
u16 i_id;
int i_dummy;
/* This is a PES packet. Find out if we want it or not. */
i_id = GetID( p_data );
vlc_mutex_lock( &p_input->stream.stream_lock );
#if 1
for( i_dummy = 0; i_dummy < INPUT_MAX_ES; i_dummy++ )
{
if( p_input->p_es[i_dummy].i_id != EMPTY_ID
&& p_input->p_es[i_dummy].i_id == i_id )
{
p_es = &p_input->p_es[i_dummy];
break;
}
}
#else
for( i_dummy = 0; i_dummy < INPUT_MAX_SELECTED_ES; i_dummy++ )
{
if( p_input->pp_selected_es[i_dummy] != NULL
&& p_input->pp_selected_es[i_dummy]->i_id == i_id )
{
p_es = p_input->pp_selected_es[i_dummy];
break;
}
}
#endif
vlc_mutex_unlock( &p_input->stream.stream_lock );
if( p_es == NULL )
{
#if 1
vlc_mutex_lock( &p_input->stream.stream_lock );
p_es = input_AddES( p_input, p_input->stream.pp_programs[0],
i_id, 0 );
if( p_es != NULL )
{
if( (i_id & 0xF0) == 0xE0 )
{
/* MPEG video */
p_es->i_stream_id = i_id;
p_es->i_type = MPEG2_VIDEO_ES;
#ifdef AUTO_SPAWN
input_SelectES( p_input, p_es );
#endif
}
else if( (i_id & 0xE0) == 0xC0 )