* Fixed a bug in the management of the first video PTS.
* control the pace of reading.
*****************************************************************************
* Copyright (C) 1999, 2000 VideoLAN
- * $Id: input_ext-intf.h,v 1.11 2001/01/30 05:48:23 sam Exp $
+ * $Id: input_ext-intf.h,v 1.12 2001/02/07 15:32:25 massiot Exp $
*
* Authors:
*
/* Synchronization information */
mtime_t delta_cr;
mtime_t cr_ref, sysdate_ref;
- mtime_t last_cr;
+ mtime_t last_cr; /* reference to detect unexpected stream
+ * discontinuities */
count_t c_average_count;
/* counter used to compute dynamic average values */
int i_synchro_state;
* input.h: structures of the input not exported to other modules
*****************************************************************************
* Copyright (C) 1999, 2000 VideoLAN
- * $Id: input.h,v 1.12 2001/01/24 19:05:55 massiot Exp $
+ * $Id: input.h,v 1.13 2001/02/07 15:32:25 massiot Exp $
*
* Authors:
*
* Ethernet MTU is 1500 bytes, so in a UDP *
* packet we can put : 1500/188 = 7 TS *
* packets. Have a nice day and merry Xmas. */
-#define PADDING_PACKET_SIZE 100 /* Size of the NULL packet inserted in case
+#define PADDING_PACKET_SIZE 188 /* Size of the NULL packet inserted in case
* of data loss (this should be < 188). */
/*****************************************************************************
/*****************************************************************************
* Prototypes from input_programs.c
*****************************************************************************/
-void input_InitStream( struct input_thread_s *, size_t );
+int input_InitStream( struct input_thread_s *, size_t );
void input_EndStream( struct input_thread_s * );
struct pgrm_descriptor_s * input_FindProgram( struct input_thread_s *, u16 );
struct pgrm_descriptor_s * input_AddProgram( struct input_thread_s *,
/*****************************************************************************
* Prototypes from input_clock.c
*****************************************************************************/
-mtime_t input_ClockToSysdate( struct input_thread_s *,
- struct pgrm_descriptor_s *, mtime_t );
void input_ClockNewRef( struct input_thread_s *,
struct pgrm_descriptor_s *, mtime_t );
+void input_EscapeDiscontinuity( struct input_thread_s *,
+ struct pgrm_descriptor_s * );
+void input_ClockInit( struct pgrm_descriptor_s * );
+void input_ClockManageRef( struct input_thread_s *,
+ struct pgrm_descriptor_s *, mtime_t );
+mtime_t input_ClockGetTS( struct input_thread_s *,
+ struct pgrm_descriptor_s *, mtime_t );
/*****************************************************************************
* Create a NULL packet for padding in case of a data loss
* input_clock.c: Clock/System date conversions, stream management
*****************************************************************************
* Copyright (C) 1999, 2000 VideoLAN
- * $Id: input_clock.c,v 1.1 2001/01/24 19:05:55 massiot Exp $
+ * $Id: input_clock.c,v 1.2 2001/02/07 15:32:26 massiot Exp $
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
*
#include "input.h"
+/*
+ * DISCUSSION : SYNCHRONIZATION METHOD
+ *
+ * In some cases we can impose the pace of reading (when reading from a
+ * file or a pipe), and for the synchronization we simply sleep() until
+ * it is time to deliver the packet to the decoders. When reading from
+ * the network, we must be read at the same pace as the server writes,
+ * otherwise the kernel's buffer will trash packets. The risk is now to
+ * overflow the input buffers in case the server goes too fast, that is
+ * why we do these calculations :
+ *
+ * We compute a mean for the pcr because we want to eliminate the
+ * network jitter and keep the low frequency variations. The mean is
+ * in fact a low pass filter and the jitter is a high frequency signal
+ * that is why it is eliminated by the filter/average.
+ *
+ * The low frequency variations enable us to synchronize the client clock
+ * with the server clock because they represent the time variation between
+ * the 2 clocks. Those variations (ie the filtered pcr) are used to compute
+ * the presentation dates for the audio and video frames. With those dates
+ * we can decode (or trash) the MPEG2 stream at "exactly" the same rate
+ * as it is sent by the server and so we keep the synchronization between
+ * the server and the client.
+ *
+ * It is a very important matter if you want to avoid underflow or overflow
+ * in all the FIFOs, but it may be not enough.
+ */
+
/*****************************************************************************
- * input_ClockToSysdate: converts a movie clock to system date
+ * Constants
*****************************************************************************/
-mtime_t input_ClockToSysdate( input_thread_t * p_input,
- pgrm_descriptor_t * p_pgrm, mtime_t i_clock )
+
+/* Maximum number of samples used to compute the dynamic average value.
+ * We use the following formula :
+ * new_average = (old_average * c_average + new_sample_value) / (c_average +1) */
+#define CR_MAX_AVERAGE_COUNTER 40
+
+/* Maximum gap allowed between two CRs. */
+#define CR_MAX_GAP 1000000
+
+/*****************************************************************************
+ * ClockToSysdate: converts a movie clock to system date
+ *****************************************************************************/
+static mtime_t ClockToSysdate( input_thread_t * p_input,
+ pgrm_descriptor_t * p_pgrm, mtime_t i_clock )
{
mtime_t i_sysdate = 0;
return( i_sysdate );
}
+/*****************************************************************************
+ * ClockCurrent: converts current system date to clock units
+ *****************************************************************************
+ * Caution : the synchro state must be SYNCHRO_OK for this to operate.
+ *****************************************************************************/
+static mtime_t ClockCurrent( input_thread_t * p_input,
+ pgrm_descriptor_t * p_pgrm )
+{
+ return( (mdate() - p_pgrm->sysdate_ref) * 27 * DEFAULT_RATE
+ / p_input->stream.control.i_rate / 300
+ + p_pgrm->cr_ref );
+}
+
/*****************************************************************************
* input_ClockNewRef: writes a new clock reference
*****************************************************************************/
p_pgrm->sysdate_ref = mdate();
}
+/*****************************************************************************
+ * input_EscapeDiscontinuity: send a NULL packet to the decoders
+ *****************************************************************************/
+void input_EscapeDiscontinuity( input_thread_t * p_input,
+ pgrm_descriptor_t * p_pgrm )
+{
+ int i_es;
+
+ for( i_es = 0; i_es < p_pgrm->i_es_number; i_es++ )
+ {
+ es_descriptor_t * p_es = p_pgrm->pp_es[i_es];
+
+ if( p_es->p_decoder_fifo != NULL )
+ {
+ input_NullPacket( p_input, p_es );
+ }
+ }
+}
+
+/*****************************************************************************
+ * input_ClockInit: reinitializes the clock reference after a stream
+ * discontinuity
+ *****************************************************************************/
+void input_ClockInit( pgrm_descriptor_t * p_pgrm )
+{
+ p_pgrm->last_cr = 0;
+ p_pgrm->cr_ref = 0;
+ p_pgrm->sysdate_ref = 0;
+ p_pgrm->delta_cr = 0;
+ p_pgrm->c_average_count = 0;
+}
+
+/*****************************************************************************
+ * input_ClockManageRef: manages a clock reference
+ *****************************************************************************/
+void input_ClockManageRef( input_thread_t * p_input,
+ pgrm_descriptor_t * p_pgrm, mtime_t i_clock )
+{
+ if( p_pgrm->i_synchro_state != SYNCHRO_OK )
+ {
+ /* Feed synchro with a new reference point. */
+ input_ClockNewRef( p_input, p_pgrm, i_clock );
+ p_pgrm->i_synchro_state = SYNCHRO_OK;
+ }
+ else
+ {
+ if ( p_pgrm->last_cr != 0 &&
+ ( (p_pgrm->last_cr - i_clock) > CR_MAX_GAP
+ || (p_pgrm->last_cr - i_clock) < - CR_MAX_GAP ) )
+ {
+ /* Stream discontinuity, for which we haven't received a
+ * warning from the stream control facilities (dd-edited
+ * stream ?). */
+ intf_WarnMsg( 3, "Clock gap, unexpected stream discontinuity" );
+ input_ClockInit( p_pgrm );
+ p_pgrm->i_synchro_state = SYNCHRO_START;
+ input_EscapeDiscontinuity( p_input, p_pgrm );
+ }
+
+ p_pgrm->last_cr = i_clock;
+
+ if( p_input->stream.b_pace_control
+ && p_input->stream.pp_programs[0] == p_pgrm )
+ {
+ /* Wait a while before delivering the packets to the decoder.
+ * In case of multiple programs, we arbitrarily follow the
+ * clock of the first program. */
+ mwait( ClockToSysdate( p_input, p_pgrm, i_clock ) );
+ }
+ else
+ {
+ /* Smooth clock reference variations. */
+ mtime_t i_extrapoled_clock = ClockCurrent( p_input, p_pgrm );
+
+ /* Bresenham algorithm to smooth variations. */
+ if( p_pgrm->c_average_count == CR_MAX_AVERAGE_COUNTER )
+ {
+ p_pgrm->delta_cr = ( p_pgrm->delta_cr
+ * (CR_MAX_AVERAGE_COUNTER - 1)
+ + i_extrapoled_clock )
+ / CR_MAX_AVERAGE_COUNTER;
+ }
+ else
+ {
+ p_pgrm->delta_cr = ( p_pgrm->delta_cr
+ * p_pgrm->c_average_count
+ + i_extrapoled_clock )
+ / (p_pgrm->c_average_count + 1);
+ p_pgrm->c_average_count++;
+ }
+ }
+ }
+}
+
+/*****************************************************************************
+ * input_ClockGetTS: manages a PTS or DTS
+ *****************************************************************************/
+mtime_t input_ClockGetTS( input_thread_t * p_input,
+ pgrm_descriptor_t * p_pgrm, mtime_t i_ts )
+{
+ if( p_pgrm->i_synchro_state == SYNCHRO_OK )
+ {
+ return( ClockToSysdate( p_input, p_pgrm, i_ts + p_pgrm->delta_cr )
+ + DEFAULT_PTS_DELAY );
+ }
+ else
+ {
+ return 0;
+ }
+}
+
* input_programs.c: es_descriptor_t, pgrm_descriptor_t management
*****************************************************************************
* Copyright (C) 1999, 2000 VideoLAN
- * $Id: input_programs.c,v 1.27 2001/01/24 19:05:55 massiot Exp $
+ * $Id: input_programs.c,v 1.28 2001/02/07 15:32:26 massiot Exp $
*
* Authors:
*
/*****************************************************************************
* input_InitStream: init the stream descriptor of the given input
*****************************************************************************/
-void input_InitStream( input_thread_t * p_input, size_t i_data_len )
+int input_InitStream( input_thread_t * p_input, size_t i_data_len )
{
p_input->stream.i_stream_id = 0;
p_input->stream.pp_es = NULL;
if ( (p_input->stream.p_demux_data = malloc( i_data_len )) == NULL )
{
intf_ErrMsg( "Unable to allocate memory in input_InitStream");
- /* FIXME : find a way to tell if failed */
- return;
+ return 1;
}
memset( p_input->stream.p_demux_data, 0, i_data_len );
}
+
+ return 0;
}
/*****************************************************************************
p_input->stream.pp_programs[i_pgrm_index]->i_es_number = 0;
p_input->stream.pp_programs[i_pgrm_index]->pp_es = NULL;
- p_input->stream.pp_programs[i_pgrm_index]->delta_cr = 0;
- p_input->stream.pp_programs[i_pgrm_index]->cr_ref = 0;
- p_input->stream.pp_programs[i_pgrm_index]->sysdate_ref = 0;
- p_input->stream.pp_programs[i_pgrm_index]->last_cr = 0;
- p_input->stream.pp_programs[i_pgrm_index]->c_average_count = 0;
+ input_ClockInit( p_input->stream.pp_programs[i_pgrm_index] );
+
p_input->stream.pp_programs[i_pgrm_index]->i_synchro_state
= SYNCHRO_START;
p_input->stream.pp_programs[i_pgrm_index]->b_discontinuity = 0;
* input_ps.c: PS demux and packet management
*****************************************************************************
* Copyright (C) 1998, 1999, 2000 VideoLAN
- * $Id: input_ps.c,v 1.21 2001/01/30 05:48:23 sam Exp $
+ * $Id: input_ps.c,v 1.22 2001/02/07 15:32:26 massiot Exp $
*
* Authors:
*
}
fseek( p_method->stream, 0, SEEK_SET );
+ /* FIXME : detect if InitStream failed */
input_InitStream( p_input, sizeof( stream_ps_data_t ) );
input_AddProgram( p_input, 0, sizeof( stream_ps_data_t ) );
* mpeg_system.c: TS, PS and PES management
*****************************************************************************
* Copyright (C) 1998, 1999, 2000 VideoLAN
- * $Id: mpeg_system.c,v 1.28 2001/01/30 05:48:23 sam Exp $
+ * $Id: mpeg_system.c,v 1.29 2001/02/07 15:32:26 massiot Exp $
*
* Authors:
*
p_pes = NULL;
return;
}
- p_pes->i_pts = input_ClockToSysdate( p_input, p_es->p_pgrm,
+ p_pes->i_pts = input_ClockGetTS( p_input, p_es->p_pgrm,
( ((mtime_t)(p_full_header[2] & 0x0E) << 29) |
(((mtime_t)U16_AT(p_full_header + 3) << 14) - (1 << 14)) |
- ((mtime_t)U16_AT(p_full_header + 5) >> 1) ) )
- + DEFAULT_PTS_DELAY;
+ ((mtime_t)U16_AT(p_full_header + 5) >> 1) ) );
if( b_has_dts )
{
p_pes = NULL;
return;
}
- p_pes->i_dts = input_ClockToSysdate( p_input,
- p_es->p_pgrm,
+ p_pes->i_dts = input_ClockGetTS( p_input, p_es->p_pgrm,
( ((mtime_t)(p_full_header[7] & 0x0E) << 29) |
(((mtime_t)U16_AT(p_full_header + 8) << 14)
- (1 << 14)) |
- ((mtime_t)U16_AT(p_full_header + 10) >> 1) ) )
- + DEFAULT_PTS_DELAY;
+ ((mtime_t)U16_AT(p_full_header + 10) >> 1) ) );
}
}
}
return;
}
- p_pes->i_pts = input_ClockToSysdate( p_input, p_es->p_pgrm,
+ p_pes->i_pts = input_ClockGetTS( p_input, p_es->p_pgrm,
( ((mtime_t)(p_ts[0] & 0x0E) << 29) |
(((mtime_t)U16_AT(p_ts + 1) << 14) - (1 << 14)) |
- ((mtime_t)U16_AT(p_ts + 3) >> 1) ) )
- + DEFAULT_PTS_DELAY;
+ ((mtime_t)U16_AT(p_ts + 3) >> 1) ) );
if( b_has_dts )
{
return;
}
- p_pes->i_dts = input_ClockToSysdate( p_input,
- p_es->p_pgrm,
+ p_pes->i_dts = input_ClockGetTS( p_input,
+ p_es->p_pgrm,
( ((mtime_t)(p_ts[0] & 0x0E) << 29) |
(((mtime_t)U16_AT(p_ts + 1) << 14) - (1 << 14)) |
- ((mtime_t)U16_AT(p_ts + 3) >> 1) ) )
- + DEFAULT_PTS_DELAY;
+ ((mtime_t)U16_AT(p_ts + 3) >> 1) ) );
}
}
}
}
-/*
- * Pace control
- */
-
-/*
- * DISCUSSION : SYNCHRONIZATION METHOD
- *
- * In some cases we can impose the pace of reading (when reading from a
- * file or a pipe), and for the synchronization we simply sleep() until
- * it is time to deliver the packet to the decoders. When reading from
- * the network, we must be read at the same pace as the server writes,
- * otherwise the kernel's buffer will trash packets. The risk is now to
- * overflow the input buffers in case the server goes too fast, that is
- * why we do these calculations :
- *
- * We compute an average for the pcr because we want to eliminate the
- * network jitter and keep the low frequency variations. The average is
- * in fact a low pass filter and the jitter is a high frequency signal
- * that is why it is eliminated by the filter/average.
- *
- * The low frequency variations enable us to synchronize the client clock
- * with the server clock because they represent the time variation between
- * the 2 clocks. Those variations (ie the filtered pcr) are used to compute
- * the presentation dates for the audio and video frames. With those dates
- * we can decode (or trash) the MPEG2 stream at "exactly" the same rate
- * as it is sent by the server and so we keep the synchronization between
- * the server and the client.
- *
- * It is a very important matter if you want to avoid underflow or overflow
- * in all the FIFOs, but it may be not enough.
- */
-
-/*****************************************************************************
- * Constants
- *****************************************************************************/
-
-/* Maximum number of samples used to compute the dynamic average value,
- * it is also the maximum of c_average_count in pgrm_ts_data_t.
- * We use the following formula :
- * new_average = (old_average * c_average + new_sample_value) / (c_average +1) */
-#define CR_MAX_AVERAGE_COUNTER 40
-
-/* Maximum gap allowed between two CRs. */
-#define CR_MAX_GAP 1000000
-
-/*****************************************************************************
- * CRReInit : Reinitialize the clock reference
- *****************************************************************************/
-static void CRReInit( pgrm_descriptor_t * p_pgrm )
-{
- p_pgrm->delta_cr = 0;
- p_pgrm->last_cr = 0;
- p_pgrm->c_average_count = 0;
-}
-
-/* FIXME: find a better name */
-/*****************************************************************************
- * CRDecode : Decode a clock reference
- *****************************************************************************/
-static void CRDecode( input_thread_t * p_input, pgrm_descriptor_t * p_pgrm,
- mtime_t cr_time )
-{
- if( p_pgrm->i_synchro_state != SYNCHRO_OK )
- {
- input_ClockNewRef( p_input, p_pgrm, cr_time );
- p_pgrm->i_synchro_state = SYNCHRO_OK;
- }
- else
- {
- if( p_pgrm->b_discontinuity ||
- ( p_pgrm->last_cr != 0 &&
- ( (p_pgrm->last_cr - cr_time) > CR_MAX_GAP
- || (p_pgrm->last_cr - cr_time) < - CR_MAX_GAP ) ) )
- {
-#if 0
- /* This code is deprecated */
- int i_es;
-
- /* Stream discontinuity. */
- intf_WarnMsg( 3, "CR re-initialiazed" );
- CRReInit( p_pgrm );
- p_pgrm->i_synchro_state = SYNCHRO_REINIT;
- p_pgrm->b_discontinuity = 0;
-
- /* Warn all the elementary streams */
- for( i_es = 0; i_es < p_pgrm->i_es_number; i_es++ )
- {
- p_pgrm->pp_es[i_es]->b_discontinuity = 1;
- }
-#endif
- }
- p_pgrm->last_cr = cr_time;
-
- if( p_input->stream.b_pace_control )
- {
- /* Wait a while before delivering the packets to the decoder. */
- mwait( input_ClockToSysdate( p_input, p_pgrm, cr_time ) );
- }
- else
- {
-#if 0
- /* This code is deprecated, too */
- mtime_t sys_time, delta_cr;
-
- sys_time = mdate();
- delta_cr = sys_time - cr_time;
-
- if( p_pgrm->c_average_count == CR_MAX_AVERAGE_COUNTER )
- {
- p_pgrm->delta_cr = ( delta_cr + (p_pgrm->delta_cr
- * (CR_MAX_AVERAGE_COUNTER - 1)) )
- / CR_MAX_AVERAGE_COUNTER;
- }
- else
- {
- p_pgrm->delta_cr = ( delta_cr + (p_pgrm->delta_cr
- * p_pgrm->c_average_count) )
- / ( p_pgrm->c_average_count + 1 );
- p_pgrm->c_average_count++;
- }
-#endif
- }
- }
-}
-
-
/*
* PS Demultiplexing
*/
}
/* Call the pace control. */
//intf_Msg("+%lld", scr_time);
- CRDecode( p_input, p_input->stream.pp_programs[0],
- scr_time );
+ input_ClockManageRef( p_input, p_input->stream.pp_programs[0],
+ scr_time );
b_trash = 1;
}
break;
( (mtime_t)U32_AT((u32*)&p[6]) << 1 )
| ( p[10] >> 7 );
/* Call the pace control. */
- CRDecode( p_input, p_es->p_pgrm, pcr_time );
+ input_ClockManageRef( p_input, p_es->p_pgrm,
+ pcr_time );
}
} /* PCR ? */
} /* valid TS adaptation field ? */
{
/* 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
{
- intf_DbgMsg( "module: hiding unused module `%s'",
- p_module->psz_name );
+ intf_WarnMsg( 1, "module: hiding unused module `%s'",
+ p_module->psz_name );
HideModule( p_module );
}
}
* video_parser.c : video parser thread
*****************************************************************************
* Copyright (C) 1999, 2000 VideoLAN
- * $Id: video_parser.c,v 1.70 2001/01/24 19:05:55 massiot Exp $
+ * $Id: video_parser.c,v 1.71 2001/02/07 15:32:26 massiot Exp $
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
* Samuel Hocevar <sam@via.ecp.fr>
p_vpar->bit_stream.pf_bitstream_callback = BitstreamCallback;
p_vpar->bit_stream.p_callback_arg = (void *)p_vpar;
+ /* InitBitstream has normally begun to read a PES packet, get its
+ * PTS/DTS */
+ if( !p_vpar->p_fifo->b_die )
+ {
+ BitstreamCallback( &p_vpar->bit_stream, 1 );
+ }
+
/* Initialize parsing data */
p_vpar->sequence.p_forward = NULL;
p_vpar->sequence.p_backward = NULL;