Commit ff85edc6 authored by Christophe Massiot's avatar Christophe Massiot
Browse files

* Centralized clock management, preparing for stream navigation ;

* Fixed a bug in the management of the first video PTS.
parent 422bd79f
......@@ -4,7 +4,7 @@
* 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:
*
......@@ -110,7 +110,8 @@ typedef struct pgrm_descriptor_s
/* 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;
......
......@@ -2,7 +2,7 @@
* 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:
*
......@@ -30,7 +30,7 @@
* 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). */
/*****************************************************************************
......@@ -78,7 +78,7 @@ void NextDataPacket ( struct bit_stream_s * );
/*****************************************************************************
* 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 *,
......@@ -103,10 +103,15 @@ void input_DecodePES( struct decoder_fifo_s *, struct pes_packet_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
......
......@@ -2,7 +2,7 @@
* 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>
*
......@@ -38,11 +38,51 @@
#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;
......@@ -59,6 +99,19 @@ mtime_t input_ClockToSysdate( input_thread_t * p_input,
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
*****************************************************************************/
......@@ -69,3 +122,114 @@ void input_ClockNewRef( input_thread_t * p_input, pgrm_descriptor_t * p_pgrm,
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;
}
}
......@@ -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.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:
*
......@@ -51,7 +51,7 @@
/*****************************************************************************
* 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;
......@@ -63,11 +63,12 @@ void input_InitStream( input_thread_t * p_input, size_t i_data_len )
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;
}
/*****************************************************************************
......@@ -147,11 +148,8 @@ pgrm_descriptor_t * input_AddProgram( input_thread_t * p_input,
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;
......
......@@ -2,7 +2,7 @@
* 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:
*
......@@ -103,6 +103,7 @@ static void PSInit( input_thread_t * p_input )
}
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 ) );
......
......@@ -2,7 +2,7 @@
* 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:
*
......@@ -217,11 +217,10 @@ void input_ParsePES( input_thread_t * p_input, es_descriptor_t * p_es )
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 )
{
......@@ -235,13 +234,11 @@ void input_ParsePES( input_thread_t * p_input, es_descriptor_t * p_es )
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) ) );
}
}
}
......@@ -313,11 +310,10 @@ void input_ParsePES( input_thread_t * p_input, es_descriptor_t * p_es )
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 )
{
......@@ -332,12 +328,11 @@ void input_ParsePES( input_thread_t * p_input, es_descriptor_t * p_es )
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) ) );
}
}
}
......@@ -491,132 +486,6 @@ void input_GatherPES( input_thread_t * p_input, data_packet_t * p_data,
}
/*
* 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
*/
......@@ -939,8 +808,8 @@ void input_DemuxPS( input_thread_t * p_input, data_packet_t * p_data )
}
/* 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;
......@@ -1111,7 +980,8 @@ void input_DemuxTS( input_thread_t * p_input, data_packet_t * p_data )
( (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 ? */
......@@ -1184,6 +1054,7 @@ void input_DemuxTS( input_thread_t * p_input, data_packet_t * p_data )
{
/* 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
......
......@@ -224,8 +224,8 @@ void module_ManageBank( module_bank_t * p_bank )
}
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 );
}
}
......
......@@ -2,7 +2,7 @@
* 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>
......@@ -225,6 +225,13 @@ static int InitThread( vpar_thread_t *p_vpar )
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;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment