Commit bad6f235 authored by Christophe Massiot's avatar Christophe Massiot

* Totally new frame dropping algorithm.

  * Fixed a bug in video_ouput.c which made the stream go backwards
    sometimes.
  * Fixed a bug in video_ouput.c which trashed more late pictures than
    necessary.
  * Fixed the DEBUG mode in the Makefile.
  * Fixed a bug in mwait() which made us wait too long.

Ca va tuer.
parent 7e4135b3
......@@ -8,6 +8,13 @@
mode.
* Added --broadcast option for network input.
* Screen is now emptied when framebuffer output exits.
* Totally new frame dropping algorithm.
* Fixed a bug in video_ouput.c which made the stream go backwards
sometimes.
* Fixed a bug in video_ouput.c which trashed more late pictures than
necessary.
* Fixed the DEBUG mode in the Makefile.
* Fixed a bug in mwait() which made us wait too long.
Mon, 28 Aug 2000 02:34:18 +0200
0.1.99i :
......
......@@ -106,10 +106,6 @@ endif
else
CFLAGS += -march=pentium
endif
# Eventual MMX optimizations for x86
ifneq (,$(findstring mmx,$(ARCH)))
CFLAGS += -DHAVE_MMX
endif
endif
# Optimizations for PowerPC
......@@ -127,9 +123,16 @@ ifneq (,$(findstring sparc,$(ARCH)))
CFLAGS += -mhard-float
endif
# End of optimizations
# /debug
endif
# Eventual MMX optimizations for x86
ifneq (,$(findstring mmx,$(ARCH)))
CFLAGS += -DHAVE_MMX
endif
# End of optimizations
#
# C compiler flags: dependancies
#
......
......@@ -343,6 +343,9 @@
/* Number of pictures required to computes the FPS rate */
#define VOUT_FPS_SAMPLES 20
/* Better be in advance when awakening than late... */
#define VOUT_MWAIT_TOLERANCE ((int)(0.020*CLOCK_FREQ))
/*
* Framebuffer settings
*/
......@@ -361,7 +364,7 @@
* It should be approximately the time needed to perform a complete picture
* loop. Since it only happens when the video heap is full, it does not need
* to be too low, even if it blocks the decoder. */
#define VPAR_OUTMEM_SLEEP ((int)(0.050*CLOCK_FREQ))
#define VPAR_OUTMEM_SLEEP ((int)(0.020*CLOCK_FREQ))
/* Optimization level, from 0 to 2 - 1 is generally a good compromise. Remember
* that raising this level dramatically lengthens the compilation time. */
......@@ -438,11 +441,3 @@
/* Maximal number of commands which can be saved in history list */
#define INTF_CONSOLE_MAX_HISTORY 20
/*****************************************************************************
* Synchro configuration
*****************************************************************************/
#define VOUT_SYNCHRO_LEVEL_START 5 << 10
#define VOUT_SYNCHRO_LEVEL_MAX 15 << 10
#define VOUT_SYNCHRO_HEAP_IDEAL_SIZE 5
......@@ -135,7 +135,7 @@ typedef struct vout_thread_s
p_vout_sys_t p_sys; /* system output method */
vdec_DecodeMacroblock_t *
vdec_DecodeMacroblock; /* decoder function to use */
/* Current display properties */
u16 i_changes; /* changes made to the thread */
int i_width; /* current output method width */
......@@ -181,6 +181,7 @@ typedef struct vout_thread_s
boolean_t b_info; /* print additional information */
boolean_t b_interface; /* render interface */
boolean_t b_scale; /* allow picture scaling */
mtime_t render_time; /* last picture render time */
/* Idle screens management */
mtime_t last_display_date; /* last non idle display date */
......@@ -190,7 +191,6 @@ typedef struct vout_thread_s
#ifdef STATS
/* Statistics - these numbers are not supposed to be accurate, but are a
* good indication of the thread status */
mtime_t render_time; /* last picture render time */
count_t c_fps_samples; /* picture counts */
mtime_t p_fps_sample[VOUT_FPS_SAMPLES]; /* FPS samples dates */
#endif
......@@ -208,10 +208,6 @@ typedef struct vout_thread_s
/* Bitmap fonts */
p_vout_font_t p_default_font; /* default font */
p_vout_font_t p_large_font; /* large font */
/* Synchronization informations - synchro level is updated by the vout
* thread and read by decoder threads */
int i_synchro_level; /* trashing level */
} vout_thread_t;
/* Flags for changes - these flags are set in the i_changes field when another
......@@ -224,7 +220,9 @@ typedef struct vout_thread_s
#define VOUT_DEPTH_CHANGE 0x0400 /* depth changed */
#define VOUT_GAMMA_CHANGE 0x0010 /* gamma changed */
#define VOUT_YUV_CHANGE 0x0800 /* change yuv tables */
#define VOUT_NODISPLAY_CHANGE 0xff00 /* changes which forbidden display */
/* Disabled for thread deadlocks issues --Meuuh */
//#define VOUT_NODISPLAY_CHANGE 0xff00 /* changes which forbidden display */
/*****************************************************************************
* Macros
......
......@@ -3,9 +3,7 @@
*****************************************************************************
* Copyright (C) 1999, 2000 VideoLAN
*
* Authors: Samuel Hocevar <sam@via.ecp.fr>
* Jean-Marc Dressler <polux@via.ecp.fr>
* Christophe Massiot <massiot@via.ecp.fr>
* Author: Christophe Massiot <massiot@via.ecp.fr>
*
* 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
......@@ -35,102 +33,58 @@
* "video_fifo.h"
*****************************************************************************/
#define SAM_SYNCHRO
//#define POLUX_SYNCHRO
//#define MEUUH_SYNCHRO
/*****************************************************************************
* video_synchro_t and video_synchro_tab_s : timers for the video synchro
*****************************************************************************/
#ifdef SAM_SYNCHRO
#define MAX_DECODING_PIC 16
#define MAX_PIC_AVERAGE 8
/* Read the discussion on top of vpar_synchro.c for more information. */
typedef struct video_synchro_s
{
/* synchro algorithm */
int i_type;
int i_type;
/* fifo containing decoding dates */
mtime_t i_date_fifo[16];
unsigned int i_start;
unsigned int i_stop;
/* mean decoding time */
mtime_t i_delay;
mtime_t i_theorical_delay;
/* dates */
mtime_t i_last_pts; /* pts of the last displayed image */
mtime_t i_last_seen_I_pts; /* date of the last I we decoded */
mtime_t i_last_kept_I_pts; /* pts of last non-dropped I image */
/* P images since the last I */
unsigned int i_P_seen;
unsigned int i_P_kept;
/* B images since the last I */
unsigned int i_B_seen;
unsigned int i_B_kept;
/* can we display pictures ? */
boolean_t b_all_I;
boolean_t b_all_P;
int displayable_p;
boolean_t b_all_B;
int displayable_b;
boolean_t b_dropped_last;
} video_synchro_t;
#define FIFO_INCREMENT( i_counter ) \
p_vpar->synchro.i_counter = (p_vpar->synchro.i_counter + 1) & 0xf;
#define VPAR_SYNCHRO_DEFAULT 0
#define VPAR_SYNCHRO_I 1
#define VPAR_SYNCHRO_Iplus 2
#define VPAR_SYNCHRO_IP 3
#define VPAR_SYNCHRO_IPplus 4
#define VPAR_SYNCHRO_IPB 5
mtime_t p_date_fifo[MAX_DECODING_PIC];
int pi_coding_types[MAX_DECODING_PIC];
unsigned int i_start, i_end;
vlc_mutex_t fifo_lock;
/* stream properties */
unsigned int i_n_p, i_n_b;
/* decoding values */
mtime_t p_tau[4]; /* average decoding durations */
unsigned int pi_meaningful[4]; /* number of durations read */
/* and p_vout->render_time (read with p_vout->change_lock) */
/* stream context */
unsigned int i_eta_p, i_eta_b;
boolean_t b_dropped_last; /* for special synchros below */
mtime_t backward_pts, current_pts;
#ifdef STATS
unsigned int i_B_self, i_B_next, i_B_last, i_B_I;
#endif
#ifdef MEUUH_SYNCHRO
typedef struct video_synchro_s
{
int kludge_level, kludge_p, kludge_b, kludge_nbp, kludge_nbb;
int kludge_nbframes;
mtime_t kludge_date, kludge_prevdate;
int i_coding_type;
} video_synchro_t;
#define SYNC_TOLERATE ((int)(0.010*CLOCK_FREQ)) /* 10 ms */
#define SYNC_DELAY ((int)(0.500*CLOCK_FREQ)) /* 500 ms */
#endif
#ifdef POLUX_SYNCHRO
#define SYNC_AVERAGE_COUNT 10
typedef struct video_synchro_s
{
/* Date Section */
/* Dates needed to compute the date of the current frame
* We also use the stream frame rate (sequence.i_frame_rate) */
mtime_t i_current_frame_date;
mtime_t i_backward_frame_date;
#define FIFO_INCREMENT( i_counter ) \
p_vpar->synchro.i_counter = \
(p_vpar->synchro.i_counter + 1) % MAX_DECODING_PIC;
/* Frame Trashing Section */
int i_b_nb, i_p_nb; /* number of decoded P and B between two I */
float r_b_average, r_p_average;
int i_b_count, i_p_count, i_i_count;
int i_b_trasher; /* used for brensenham algorithm */
} video_synchro_t;
#endif
/* Synchro algorithms */
#define VPAR_SYNCHRO_DEFAULT 0
#define VPAR_SYNCHRO_I 1
#define VPAR_SYNCHRO_Iplus 2
#define VPAR_SYNCHRO_IP 3
#define VPAR_SYNCHRO_IPplus 4
#define VPAR_SYNCHRO_IPB 5
/*****************************************************************************
* Prototypes
*****************************************************************************/
void vpar_SynchroInit ( struct vpar_thread_s * p_vpar );
boolean_t vpar_SynchroChoose ( struct vpar_thread_s * p_vpar,
int i_coding_type, int i_structure );
void vpar_SynchroTrash ( struct vpar_thread_s * p_vpar,
......@@ -139,7 +93,3 @@ void vpar_SynchroDecode ( struct vpar_thread_s * p_vpar,
int i_coding_type, int i_structure );
void vpar_SynchroEnd ( struct vpar_thread_s * p_vpar );
mtime_t vpar_SynchroDate ( struct vpar_thread_s * p_vpar );
#ifndef SAM_SYNCHRO
void vpar_SynchroKludge ( struct vpar_thread_s *, mtime_t );
#endif
......@@ -118,7 +118,9 @@ void mwait( mtime_t date )
gettimeofday( &tv_date, NULL );
/* calculate delay and check if current date is before wished date */
delay = date - (mtime_t) tv_date.tv_sec * 1000000 - (mtime_t) tv_date.tv_usec;
delay = date - (mtime_t) tv_date.tv_sec * 1000000 - (mtime_t) tv_date.tv_usec - 10000;
/* Linux/i386 has a granularity of 10 ms. It's better to be in advance
* than to be late. */
if( delay <= 0 ) /* wished date is now or already passed */
{
return;
......
......@@ -74,7 +74,6 @@ static void RenderInterface ( vout_thread_t *p_vout );
static int RenderIdle ( vout_thread_t *p_vout );
static int RenderSplash ( vout_thread_t *p_vout );
static void RenderInfo ( vout_thread_t *p_vout );
static void Synchronize ( vout_thread_t *p_vout, s64 i_delay );
static int Manage ( vout_thread_t *p_vout );
static int Align ( vout_thread_t *p_vout, int *pi_x,
int *pi_y, int i_width, int i_height,
......@@ -175,10 +174,10 @@ vout_thread_t * vout_CreateThread ( char *psz_display, int i_root_window,
p_vout->last_display_date = 0;
p_vout->last_idle_date = 0;
p_vout->init_display_date = mdate();
p_vout->render_time = 10000;
#ifdef STATS
/* Initialize statistics fields */
p_vout->render_time = 0;
p_vout->c_fps_samples = 0;
#endif
......@@ -199,9 +198,6 @@ vout_thread_t * vout_CreateThread ( char *psz_display, int i_root_window,
}
p_vout->i_pictures = 0;
/* Initialize synchronization information */
p_vout->i_synchro_level = VOUT_SYNCHRO_LEVEL_START;
/* Create and initialize system-dependant method - this function issues its
* own error messages */
if( p_vout->p_sys_create( p_vout, psz_display, i_root_window, p_data ) )
......@@ -950,10 +946,6 @@ static int InitThread( vout_thread_t *p_vout )
*****************************************************************************/
static void RunThread( vout_thread_t *p_vout)
{
/* XXX?? welcome to gore land */
static int i_trash_count = 0;
static mtime_t last_display_date = 0;
int i_index; /* index in heap */
mtime_t current_date; /* current date */
mtime_t display_date; /* display date */
......@@ -1005,12 +997,7 @@ static void RunThread( vout_thread_t *p_vout)
/* Computes FPS rate */
p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ] = display_date;
#endif
/* XXX?? */
i_trash_count++;
//fprintf( stderr, "gap : %Ld\n", display_date-last_display_date );
last_display_date = display_date;
#if 1
if( display_date < current_date && i_trash_count > 4 )
if( display_date < current_date )
{
/* Picture is late: it will be destroyed and the thread
* will sleep and go to next picture */
......@@ -1025,20 +1012,12 @@ last_display_date = display_date;
p_pic->i_status = DESTROYED_PICTURE;
p_vout->i_pictures--;
}
intf_DbgMsg( "warning: late picture %p skipped refcount=%d\n", p_pic, p_pic->i_refcount );
intf_ErrMsg( "warning: late picture skipped (%p)\n", p_pic );
vlc_mutex_unlock( &p_vout->picture_lock );
/* Update synchronization information as if display delay
* was 0 */
Synchronize( p_vout, display_date - current_date );
p_pic = NULL;
display_date = 0;
i_trash_count = 0;
continue;
}
else
#endif
if( display_date > current_date + VOUT_DISPLAY_DELAY )
else if( display_date > current_date + VOUT_DISPLAY_DELAY )
{
/* A picture is ready to be rendered, but its rendering date
* is far from the current one so the thread will perform an
......@@ -1047,12 +1026,6 @@ last_display_date = display_date;
p_pic = NULL;
display_date = 0;
}
else
{
/* Picture will be displayed, update synchronization
* information */
Synchronize( p_vout, display_date - current_date );
}
}
/*
* Find the subpictures to display - this operation does not need
......@@ -1070,11 +1043,9 @@ last_display_date = display_date;
}
}
/*
* Perform rendering, sleep and display rendered picture
*/
if( p_pic ) /* picture and perhaps subpicture */
{
b_display = p_vout->b_active;
......@@ -1173,18 +1144,28 @@ last_display_date = display_date;
* Sleep, wake up and display rendered picture
*/
#ifdef STATS
/* Store render time */
p_vout->render_time = mdate() - current_date;
#endif
if( display_date != 0 )
{
/* Store render time */
p_vout->render_time += mdate() - current_date;
p_vout->render_time >>= 1;
}
/* Give back change lock */
vlc_mutex_unlock( &p_vout->change_lock );
#ifdef DEBUG_VIDEO
{
char psz_date[MSTRTIME_MAX_SIZE];
intf_DbgMsg( "picture %p waiting until %s\n", p_pic,
mstrtime(psz_date, display_date),
}
#endif
/* Sleep a while or until a given date */
if( display_date != 0 )
{
mwait( display_date );
mwait( display_date - VOUT_MWAIT_TOLERANCE );
}
else
{
......@@ -1196,9 +1177,9 @@ last_display_date = display_date;
vlc_mutex_lock( &p_vout->change_lock );
#ifdef DEBUG_VIDEO
intf_DbgMsg( "picture %p, subpicture %p in buffer %d, display=%d\n", p_pic, p_subpic,
p_vout->i_buffer_index, b_display && !(p_vout->i_changes & VOUT_NODISPLAY_CHANGE) );
p_vout->i_buffer_index, b_display /* && !(p_vout->i_changes & VOUT_NODISPLAY_CHANGE) */ );
#endif
if( b_display && !(p_vout->i_changes & VOUT_NODISPLAY_CHANGE) )
if( b_display /* && !(p_vout->i_changes & VOUT_NODISPLAY_CHANGE) */ )
{
p_vout->p_sys_display( p_vout );
#ifndef SYS_BEOS
......@@ -2017,81 +1998,6 @@ static void RenderInterface( vout_thread_t *p_vout )
SetBufferArea( p_vout, 0, p_vout->i_height - i_height, p_vout->i_width, i_height );
}
/*****************************************************************************
* Synchronize: update synchro level depending of heap state
*****************************************************************************
* This function is called during the main vout loop.
*****************************************************************************/
static void Synchronize( vout_thread_t *p_vout, s64 i_delay )
{
int i_synchro_inc = 0;
/* XXX?? gore following */
static int i_panic_count = 0;
static int i_last_synchro_inc = 0;
static int i_synchro_level = VOUT_SYNCHRO_LEVEL_START;
static int i_truc = 10;
if( i_delay < 0 )
{
//fprintf( stderr, "PANIC %d\n", i_panic_count );
i_panic_count++;
}
i_truc *= 2;
if( p_vout->i_pictures > VOUT_SYNCHRO_HEAP_IDEAL_SIZE+1 )
{
i_truc = 40;
i_synchro_inc += p_vout->i_pictures - VOUT_SYNCHRO_HEAP_IDEAL_SIZE - 1;
}
else
{
if( p_vout->i_pictures < VOUT_SYNCHRO_HEAP_IDEAL_SIZE )
{
i_truc = 32;
i_synchro_inc += p_vout->i_pictures - VOUT_SYNCHRO_HEAP_IDEAL_SIZE;
}
}
if( i_truc > VOUT_SYNCHRO_LEVEL_MAX >> 5 ||
i_synchro_inc*i_last_synchro_inc < 0 )
{
i_truc = 32;
}
if( i_delay < 6000 )
{
i_truc = 16;
i_synchro_inc -= 2;
}
else if( i_delay < 70000 )
{
i_truc = 24+(24*i_delay)/70000;
if( i_truc < 16 )
i_truc = 16;
i_synchro_inc -= 1+(5*(70000-i_delay))/70000;
}
else if( i_delay > 100000 )
{
i_synchro_level += 1 << 10;
if( i_delay > 130000 )
i_synchro_level += 1 << 10;
}
i_synchro_level += ( i_synchro_inc << 10 ) / i_truc;
p_vout->i_synchro_level = ( i_synchro_level + (1 << 9) );
if( i_synchro_level > VOUT_SYNCHRO_LEVEL_MAX )
{
i_synchro_level = VOUT_SYNCHRO_LEVEL_MAX;
}
//fprintf( stderr, "synchro level : %d, heap : %d (%d, %d) (%d, %f) - %Ld\n", p_vout->i_synchro_level,
// p_vout->i_pictures, i_last_synchro_inc, i_synchro_inc, i_truc, r_synchro_level, i_delay );
i_last_synchro_inc = i_synchro_inc;
}
/*****************************************************************************
* Manage: manage thread
*****************************************************************************
......@@ -2103,7 +2009,7 @@ static int Manage( vout_thread_t *p_vout )
if( p_vout->i_changes )
{
intf_DbgMsg("changes: 0x%x (no display: 0x%x)\n", p_vout->i_changes,
p_vout->i_changes & VOUT_NODISPLAY_CHANGE );
0 /* p_vout->i_changes & VOUT_NODISPLAY_CHANGE */ );
}
#endif
......
......@@ -66,7 +66,6 @@ static int InitThread ( vpar_thread_t *p_vpar );
static void RunThread ( vpar_thread_t *p_vpar );
static void ErrorThread ( vpar_thread_t *p_vpar );
static void EndThread ( vpar_thread_t *p_vpar );
static int SynchroType ( );
/*****************************************************************************
* vpar_CreateThread: create a generic parser thread
......@@ -78,7 +77,8 @@ static int SynchroType ( );
*****************************************************************************/
#include "main.h"
#include "interface.h"
extern main_t* p_main;
extern main_t * p_main;
vpar_thread_t * vpar_CreateThread( /* video_cfg_t *p_cfg, */ input_thread_t *p_input /*,
vout_thread_t *p_vout, int *pi_status */ )
{
......@@ -277,54 +277,7 @@ static int InitThread( vpar_thread_t *p_vpar )
/*
* Initialize the synchro properties
*/
#ifdef SAM_SYNCHRO
/* Get an possible synchro algorithm */
p_vpar->synchro.i_type = SynchroType();
/* last seen PTS */
p_vpar->synchro.i_last_pts = 0;
/* for i frames */
p_vpar->synchro.i_last_seen_I_pts = 0;
p_vpar->synchro.i_last_kept_I_pts = 0;
/* the fifo */
p_vpar->synchro.i_start = 0;
p_vpar->synchro.i_stop = 0;
/* mean decoding time - at least 200 ms for a slow machine */
p_vpar->synchro.i_delay = 200000;
p_vpar->synchro.i_theorical_delay = 40000; /* 25 fps */
/* assume we can display all Is and 2 Ps */
p_vpar->synchro.b_all_I = 1 << 10;
p_vpar->synchro.b_all_P = 0;
p_vpar->synchro.displayable_p = 2 << 10;
p_vpar->synchro.b_all_B = 0;
p_vpar->synchro.displayable_b = 0;
p_vpar->synchro.b_dropped_last = 0;
/* assume there were about 3 P and 6 B images between I's */
p_vpar->synchro.i_P_seen = p_vpar->synchro.i_P_kept = 1 << 10;
p_vpar->synchro.i_B_seen = p_vpar->synchro.i_B_kept = 1 << 10;
#endif
#ifdef MEUUH_SYNCHRO
p_vpar->synchro.kludge_level = 5;
p_vpar->synchro.kludge_nbp = p_vpar->synchro.kludge_p = 5;
p_vpar->synchro.kludge_nbb = p_vpar->synchro.kludge_b = 6;
p_vpar->synchro.kludge_b = 0;
p_vpar->synchro.kludge_prevdate = 0;
#endif
#ifdef POLUX_SYNCHRO
p_vpar->synchro.i_current_frame_date = 0;
p_vpar->synchro.i_backward_frame_date = 0;
p_vpar->synchro.r_p_average = p_vpar->synchro.i_p_nb = 6;
p_vpar->synchro.r_b_average = p_vpar->synchro.i_b_nb = 6;
p_vpar->synchro.i_p_count = 0;
p_vpar->synchro.i_b_count = 0;
p_vpar->synchro.i_i_count = 0;
#endif
vpar_SynchroInit( p_vpar );
/* Mark thread as running and return */
intf_DbgMsg("vpar debug: InitThread(%p) succeeded\n", p_vpar);
......@@ -476,61 +429,3 @@ static void EndThread( vpar_thread_t *p_vpar )
intf_DbgMsg("vpar debug: EndThread(%p)\n", p_vpar);
}
/*****************************************************************************
* SynchroType: Get the user's synchro type
*****************************************************************************
* This function is called at initialization.
*****************************************************************************/
static int SynchroType( )
{
char * psz_synchro = main_GetPszVariable( VPAR_SYNCHRO_VAR, NULL );
if( psz_synchro == NULL )
{
return VPAR_SYNCHRO_DEFAULT;
}
switch( *psz_synchro++ )
{
case 'i':
case 'I':
switch( *psz_synchro++ )
{
case '\0':
return VPAR_SYNCHRO_I;
case '+':
if( *psz_synchro ) return 0;
return VPAR_SYNCHRO_Iplus;
case 'p':
case 'P':
switch( *psz_synchro++ )
{
case '\0':
return VPAR_SYNCHRO_IP;
case '+':
if( *psz_synchro ) return 0;
return VPAR_SYNCHRO_IPplus;
case 'b':
case 'B':
if( *psz_synchro ) return 0;
return VPAR_SYNCHRO_IPB;
default:
return VPAR_SYNCHRO_DEFAULT;
}
default:
return VPAR_SYNCHRO_DEFAULT;
}
}
return VPAR_SYNCHRO_DEFAULT;
}
......@@ -3,9 +3,9 @@
*****************************************************************************
* Copyright (C) 1999, 2000 VideoLAN
*
* Authors: Samuel Hocevar <sam@via.ecp.fr>
* Jean-Marc Dressler <polu@via.ecp.fr>
* Christophe Massiot <massiot@via.ecp.fr>
* Authors: Christophe Massiot <massiot@via.ecp.fr>
* Samuel Hocevar <sam@via.ecp.fr>
* Jean-Marc Dressler <polux@via.ecp.fr>
*
* 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
......@@ -22,6 +22,80 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*
* DISCUSSION : How to Write an efficient Frame-Dropping Algorithm
* ==========
*
* This implementation is based on mathematical and statistical
* developments. Older implementations used an enslavement, considering
* that if we're late when reading an I picture, we will decode one frame
* less. It had a tendancy to derive, and wasn't responsive enough, which
* would have caused trouble with the stream control stuff.
*
* 1. Structure of a picture stream
* =============================
* Between 2 I's, we have for instance :
* I B P B P B P B P B P B I
* t0 t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 t11 t12
* Please bear in mind that B's and IP's will be inverted when displaying
* (decoding order != presentation order). Thus, t1 < t0.
*
* 2. Definitions
* ===========
* t[0..12] : Presentation timestamps of pictures 0..12.
* t : Current timestamp, at the moment of the decoding.
* T : Picture period, T = 1/frame_rate.
* tau[I,P,B] : Mean time to decode an [I,P,B] picture.
* tauYUV : Mean time to render a picture (given by the video_output).
* tau[I,P,B] = 2 * tau[I,P,B] + tauYUV