*****************************************************************************
* 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
* "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,
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
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,
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
}
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 ) )
*****************************************************************************/
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 */
/* 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 */
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
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
}
}
-
/*
* Perform rendering, sleep and display rendered picture
*/
-
if( p_pic ) /* picture and perhaps subpicture */
{
b_display = p_vout->b_active;
* 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
{
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
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
*****************************************************************************
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
*****************************************************************************
* 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
* 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
+ * : Mean time + typical difference (estimated to tau, that
+ * needs to be confirmed) + render time.
+ * DELTA : A given error margin.
+ *
+ * 3. Decoding of an I picture
+ * ========================
+ * On fast machines (ie. those who can decode all Is), we decode all I.
+ * Otherwise :
+ * We can decode an I picture if we simply have enough time to decode it
+ * before displaying :
+ * t0 - t > tau´I + DELTA
+ *
+ * 4. Decoding of a P picture
+ * =======================
+ * On fast machines (ie. those who can decode all Ps), we decode all P.
+ * Otherwise :
+ * First criterion : have time to decode it.
+ * t2 - t > tau´P + DELTA
+ *
+ * Second criterion : it shouldn't prevent us from decoding the forthcoming I
+ * picture, which is more important.
+ * t12 - t > tau´P + tau´I + DELTA
+ *
+ * 5. Decoding of a B picture
+ * =======================
+ * First criterion : have time to decode it.
+ * t1 - t > tau´B + DELTA
+ *
+ * Second criterion : it shouldn't prevent us from decoding all P pictures
+ * until the next I, which are more important.
+ * t4 - t > tau´B + tau´P + DELTA
+ * [...]
+ * t10 - t > tau´B + 4 * tau´P + DELTA
+ * It is possible to demonstrate that if the first and the last inequations
+ * are verified, the inequations in between will be verified too.
+ *
+ * Third criterion : it shouldn't prevent us from decoding the forthcoming I
+ * picture, which is more important.
+ * t12 - t > tau´B + 4 * tau´P + tau´I + DELTA
+ *
+ * If STATS is defined, the counters in p_vpar->synchro will refer to the
+ * number of failures of these inequations.
+ *
+ * I hope you will have a pleasant flight and do not forget your life
+ * jacket.
+ * --Meuuh (2000-11-09)
+ */
+
/*****************************************************************************
* Preamble
*****************************************************************************/
#include "vpar_synchro.h"
#include "video_parser.h"
-#define MAX_COUNT 3
+#include "main.h"
/*
* Local prototypes
*/
+static int SynchroType( void );
+static void SynchroNewPicture( vpar_thread_t * p_vpar, int i_coding_type );
-#ifdef SAM_SYNCHRO
+/* Error margins */
+#define DELTA_I (int)(0.010*CLOCK_FREQ)
+#define DELTA_P (int)(0.010*CLOCK_FREQ)
+#define DELTA_B (int)(0.060*CLOCK_FREQ)
+
+#define DEFAULT_NB_P 5
+#define DEFAULT_NB_B 1
/*****************************************************************************
- * vpar_SynchroUpdateStructures : Update the synchro structures
+ * vpar_SynchroInit : You know what ?
*****************************************************************************/
-void vpar_SynchroUpdateStructures( vpar_thread_t * p_vpar,
- int i_coding_type, boolean_t b_kept )
+void vpar_SynchroInit( vpar_thread_t * p_vpar )
{
- int i_can_display;
- mtime_t i_pts;
- pes_packet_t * p_pes = p_vpar->bit_stream.p_decoder_fifo->buffer[
- p_vpar->bit_stream.p_decoder_fifo->i_start ];
-
- /* try to guess the current DTS and PTS */
- if( p_pes->b_has_pts )
- {
- i_pts = p_pes->i_pts;
-
- /* if the image is I type, then the presentation timestamp is
- * the PTS of the PES. Otherwise, we calculate it with the
- * theorical framerate value */
- if( i_coding_type == I_CODING_TYPE )
- {
- p_vpar->synchro.i_last_pts = p_pes->i_pts;
- }
- else
- {
- p_vpar->synchro.i_last_pts += p_vpar->synchro.i_theorical_delay;
- }
-
- p_pes->b_has_pts = 0;
- }
- else
- {
- p_vpar->synchro.i_last_pts += p_vpar->synchro.i_theorical_delay;
- i_pts = p_vpar->synchro.i_last_pts;
- }
-
- /* update structures */
- switch(i_coding_type)
- {
- case P_CODING_TYPE:
-
- p_vpar->synchro.i_P_seen += 1024;
- if( b_kept ) p_vpar->synchro.i_P_kept += 1024;
- break;
-
- case B_CODING_TYPE:
- p_vpar->synchro.i_B_seen += 1024;
- if( b_kept ) p_vpar->synchro.i_B_kept += 1024;
- break;
-
- case I_CODING_TYPE:
-
- /* update the last I PTS we have, we need it to
- * calculate the theorical framerate */
- if (i_pts != p_vpar->synchro.i_last_seen_I_pts)
- {
- if ( p_vpar->synchro.i_last_seen_I_pts )
- {
- p_vpar->synchro.i_theorical_delay =
- 1024 * ( i_pts - p_vpar->synchro.i_last_seen_I_pts )
- / ( 1024 + p_vpar->synchro.i_B_seen
- + p_vpar->synchro.i_P_seen);
- }
- p_vpar->synchro.i_last_seen_I_pts = i_pts;
- }
-
- /* now we calculated all statistics, it's time to
- * decide what we have the time to display */
- i_can_display =
- ( (i_pts - p_vpar->synchro.i_last_kept_I_pts) << 10 )
- / p_vpar->synchro.i_delay;
-
- p_vpar->synchro.b_all_I = 0;
- p_vpar->synchro.b_all_B = 0;
- p_vpar->synchro.b_all_P = 0;
- p_vpar->synchro.displayable_p = 0;
- p_vpar->synchro.displayable_b = 0;
-
- if( ( p_vpar->synchro.b_all_I = ( i_can_display >= 1024 ) ) )
- {
- i_can_display -= 1024;
-
- if( !( p_vpar->synchro.b_all_P
- = ( i_can_display > p_vpar->synchro.i_P_seen ) ) )
- {
- p_vpar->synchro.displayable_p = i_can_display;
- }
- else
- {
- i_can_display -= p_vpar->synchro.i_P_seen;
-
- if( !( p_vpar->synchro.b_all_B
- = ( i_can_display > p_vpar->synchro.i_B_seen ) ) )
- {
- p_vpar->synchro.displayable_b = i_can_display;
- }
- }
- }
-
-#if 0
- if( p_vpar->synchro.b_all_I )
- intf_ErrMsg( " I: 1024/1024 " );
-
- if( p_vpar->synchro.b_all_P )
- intf_ErrMsg( "P: %i/%i ", p_vpar->synchro.i_P_seen,
- p_vpar->synchro.i_P_seen );
- else if( p_vpar->synchro.displayable_p > 0 )
- intf_ErrMsg( "P: %i/%i ", p_vpar->synchro.displayable_p,
- p_vpar->synchro.i_P_seen );
- else
- intf_ErrMsg( " " );
-
- if( p_vpar->synchro.b_all_B )
- intf_ErrMsg( "B: %i/%i", p_vpar->synchro.i_B_seen,
- p_vpar->synchro.i_B_seen );
- else if( p_vpar->synchro.displayable_b > 0 )
- intf_ErrMsg( "B: %i/%i", p_vpar->synchro.displayable_b,
- p_vpar->synchro.i_B_seen );
- else
- intf_ErrMsg( " " );
-
- intf_ErrMsg( "\rDecoding: " );
- /*intf_ErrMsg( "\n" );*/
-#endif
- p_vpar->synchro.i_P_seen = 0;
- p_vpar->synchro.i_B_seen = 0;
-
- /* update some values */
- if( b_kept )
- {
- p_vpar->synchro.i_last_kept_I_pts = i_pts;
- p_vpar->synchro.i_P_kept = 0;
- p_vpar->synchro.i_B_kept = 0;
- }
-
- break;
- }
+ p_vpar->synchro.i_type = SynchroType();
+ p_vpar->synchro.i_start = p_vpar->synchro.i_end = 0;
+ vlc_mutex_init( &p_vpar->synchro.fifo_lock );
+
+ /* We use a fake stream pattern, which is often right. */
+ p_vpar->synchro.i_n_p = p_vpar->synchro.i_eta_p = DEFAULT_NB_P;
+ p_vpar->synchro.i_n_b = p_vpar->synchro.i_eta_b = DEFAULT_NB_B;
+ memset( p_vpar->synchro.p_tau, 0, 4 * sizeof(mtime_t) );
+ memset( p_vpar->synchro.pi_meaningful, 0, 4 * sizeof(unsigned int) );
+ p_vpar->synchro.b_dropped_last = 0;
+ p_vpar->synchro.current_pts = mdate() + INPUT_PTS_DELAY;
+ p_vpar->synchro.backward_pts = 0;
}
/*****************************************************************************
boolean_t vpar_SynchroChoose( vpar_thread_t * p_vpar, int i_coding_type,
int i_structure )
{
- mtime_t i_delay = p_vpar->synchro.i_last_pts - mdate();
+ /* For clarity reasons, we separated the special synchros code from the
+ * mathematical synchro */
- switch( i_coding_type )
+ if( p_vpar->synchro.i_type != VPAR_SYNCHRO_DEFAULT )
{
+ switch( i_coding_type )
+ {
case I_CODING_TYPE:
-
- if( p_vpar->synchro.i_type != VPAR_SYNCHRO_DEFAULT )
+ /* I, IP, IP+, IPB */
+ if( p_vpar->synchro.i_type == VPAR_SYNCHRO_Iplus )
{
- /* I, IP, IP+, IPB */
- if( p_vpar->synchro.i_type == VPAR_SYNCHRO_Iplus )
- {
- p_vpar->synchro.b_dropped_last = 1;
- }
- return( 1 );
+ p_vpar->synchro.b_dropped_last = 1;
}
-
- return( p_vpar->synchro.b_all_I );
+ return( 1 );
case P_CODING_TYPE:
-
if( p_vpar->synchro.i_type == VPAR_SYNCHRO_I ) /* I */
{
return( 0 );
}
}
- if( p_vpar->synchro.i_type >= VPAR_SYNCHRO_IP ) /* IP, IP+, IPB */
+ return( 1 ); /* IP, IP+, IPB */
+
+ case B_CODING_TYPE:
+ if( p_vpar->synchro.i_type <= VPAR_SYNCHRO_IP ) /* I, IP */
{
- return( 1 );
+ return( 0 );
}
-
- if( p_vpar->synchro.b_all_P )
+ else if( p_vpar->synchro.i_type == VPAR_SYNCHRO_IPB ) /* IPB */
{
return( 1 );
}
- if( p_vpar->synchro.displayable_p * i_delay
- < p_vpar->synchro.i_delay )
- {
- return( 0 );
- }
+ p_vpar->synchro.b_dropped_last ^= 1; /* IP+ */
+ return( !p_vpar->synchro.b_dropped_last );
+ }
+ return( 0 ); /* never reached but gcc yells at me */
+ }
+ else
+ {
+#define TAU_PRIME( coding_type ) (p_vpar->synchro.p_tau[(coding_type)] \
+ + (p_vpar->synchro.p_tau[(coding_type)] >> 1) \
+ + tau_yuv)
+#define S p_vpar->synchro
+ /* VPAR_SYNCHRO_DEFAULT */
+ mtime_t now, pts, period, tau_yuv;
+ boolean_t b_decode = 0, b_decode2;
- p_vpar->synchro.displayable_p -= 1024;
+ now = mdate();
+ period = 1000000 / (p_vpar->sequence.i_frame_rate) * 1001;
- return( 1 );
+ //vlc_mutex_lock( &p_vpar->p_vout->change_lock );
+ tau_yuv = p_vpar->p_vout->render_time;
+ //vlc_mutex_unlock( &p_vpar->p_vout->change_lock );
- case B_CODING_TYPE:
+ vlc_mutex_lock( &p_vpar->synchro.fifo_lock );
- if( p_vpar->synchro.i_type != VPAR_SYNCHRO_DEFAULT )
+ switch( i_coding_type )
+ {
+ case I_CODING_TYPE:
+ /* Stream structure changes */
+ S.i_n_p = S.i_eta_p || DEFAULT_NB_P;
+
+ if( S.backward_pts )
{
- if( p_vpar->synchro.i_type <= VPAR_SYNCHRO_IP ) /* I, IP */
- {
- return( 0 );
- }
- else if( p_vpar->synchro.i_type == VPAR_SYNCHRO_IPB ) /* IPB */
- {
- return( 1 );
- }
+ pts = S.backward_pts;
+ }
+ else
+ {
+ pts = S.current_pts + period * S.i_n_b;
+ }
- if( p_vpar->synchro.b_dropped_last ) /* IP+ */
- {
- p_vpar->synchro.b_dropped_last = 0;
- return( 1 );
- }
+ b_decode = ( (1 + S.i_n_p * (S.i_n_b + 1)) * period >
+ S.p_tau[I_CODING_TYPE] ) ||
+ ( (pts - now) > (TAU_PRIME(I_CODING_TYPE) + DELTA_I) );
+ if( !b_decode )
+ intf_Msg("vpar synchro: trashing I\n");
+ break;
- p_vpar->synchro.b_dropped_last = 1;
- return( 0 );
- }
+ case P_CODING_TYPE:
+ /* Stream structure changes */
+ S.i_n_b = S.i_eta_b || DEFAULT_NB_B;
+ if( S.i_eta_p + 1 > S.i_n_p )
+ S.i_n_p++;
- if( p_vpar->synchro.b_all_B )
+ if( S.backward_pts )
{
- return( 1 );
+ pts = S.backward_pts;
}
-
- if( p_vpar->synchro.displayable_b <= 0 )
+ else
{
- return( 0 );
+ pts = S.current_pts + period * S.i_n_b;
}
- if( i_delay < 0 )
+ if( (S.i_n_b + 1) * period > S.p_tau[P_CODING_TYPE] )
{
- p_vpar->synchro.displayable_b -= 512;
- return( 0 );
+ b_decode = (pts - now > 0);
+ }
+ else
+ {
+ b_decode = (pts - now) > (TAU_PRIME(P_CODING_TYPE) + DELTA_P);
+ /* next I */
+ b_decode &= (pts - now
+ + period
+ * ( (S.i_n_p - S.i_eta_p - 1) * (1 + S.i_n_b) - 1 ))
+ > (TAU_PRIME(P_CODING_TYPE)
+ + TAU_PRIME(I_CODING_TYPE) + DELTA_P);
}
+ break;
- p_vpar->synchro.displayable_b -= 1024;
- return( 1 );
- }
+ case B_CODING_TYPE:
+ /* Stream structure changes */
+ if( S.i_eta_b + 1 > S.i_n_b )
+ S.i_n_b++;
- return( 0 );
+ pts = S.current_pts;
+ if( (S.i_n_b + 1) * period > S.p_tau[P_CODING_TYPE] )
+ {
+ b_decode = (pts - now) > (TAU_PRIME(B_CODING_TYPE) + DELTA_B);
+#ifdef STATS
+ S.i_B_self += !b_decode;
+#endif
+ /* Remember that S.i_eta_b is for the moment only eta_b - 1. */
+ if( S.i_eta_p != S.i_n_p ) /* next P */
+ {
+ b_decode2 = (pts - now
+ + period
+ * ( 2 * S.i_n_b - S.i_eta_b - 1))
+ > (TAU_PRIME(B_CODING_TYPE)
+ + TAU_PRIME(P_CODING_TYPE) + DELTA_B);
+ b_decode &= b_decode2;
+#ifdef STATS
+ S.i_B_next += !b_decode2;
+#endif
+ }
+ if( S.i_eta_p < S.i_n_p - 1 ) /* last P */
+ {
+ b_decode2 = (pts - now
+ + period
+ * ( (S.i_n_p - S.i_eta_p) * (1 + S.i_n_b)
+ + S.i_n_b - (S.i_eta_b + 1) + 1))
+ > (TAU_PRIME(B_CODING_TYPE)
+ + (S.i_n_p - S.i_eta_p)
+ * TAU_PRIME(P_CODING_TYPE)
+ + DELTA_B);
+ b_decode &= b_decode2;
+#ifdef STATS
+ S.i_B_last += !b_decode2;
+#endif
+ }
+ b_decode2 = (pts - now
+ + period
+ * ( (S.i_n_p - S.i_eta_p + 1) * (1 + S.i_n_b)
+ + S.i_n_b - (S.i_eta_b + 1) + 1 ))
+ > (TAU_PRIME(B_CODING_TYPE)
+ + (S.i_n_p - S.i_eta_p)
+ * TAU_PRIME(P_CODING_TYPE)
+ + TAU_PRIME(I_CODING_TYPE)
+ + DELTA_B);
+ b_decode &= b_decode2;
+#ifdef STATS
+ S.i_B_I += !b_decode2;
+#endif
+ }
+ else
+ {
+ b_decode = 0;
+ }
+ }
+
+ vlc_mutex_unlock( &p_vpar->synchro.fifo_lock );
+ return( b_decode );
+#undef S
+#undef TAU_PRIME
+ }
}
/*****************************************************************************
void vpar_SynchroTrash( vpar_thread_t * p_vpar, int i_coding_type,
int i_structure )
{
- vpar_SynchroUpdateStructures (p_vpar, i_coding_type, 0);
-
+ SynchroNewPicture( p_vpar, i_coding_type );
}
/*****************************************************************************
* vpar_SynchroDecode : Update timers when we decide to decode a picture
*****************************************************************************/
void vpar_SynchroDecode( vpar_thread_t * p_vpar, int i_coding_type,
- int i_structure )
+ int i_structure )
{
- vpar_SynchroUpdateStructures (p_vpar, i_coding_type, 1);
+ vlc_mutex_lock( &p_vpar->synchro.fifo_lock );
- p_vpar->synchro.i_date_fifo[p_vpar->synchro.i_stop] = mdate();
+ if( ((p_vpar->synchro.i_end + 1 - p_vpar->synchro.i_start)
+ % MAX_DECODING_PIC) )
+ {
+ p_vpar->synchro.p_date_fifo[p_vpar->synchro.i_end] = mdate();
+ p_vpar->synchro.pi_coding_types[p_vpar->synchro.i_end] = i_coding_type;
- FIFO_INCREMENT( i_stop );
+ FIFO_INCREMENT( i_end );
+ }
+ else
+ {
+ /* FIFO full, panic() */
+ intf_ErrMsg("vpar error: synchro fifo full, estimations will be biased\n");
+ }
+ vlc_mutex_unlock( &p_vpar->synchro.fifo_lock );
+ SynchroNewPicture( p_vpar, i_coding_type );
}
/*****************************************************************************
*****************************************************************************/
void vpar_SynchroEnd( vpar_thread_t * p_vpar )
{
- if( p_vpar->synchro.i_stop != p_vpar->synchro.i_start )
- {
- mtime_t i_delay;
+ mtime_t tau;
+ int i_coding_type;
- i_delay = ( mdate() -
- p_vpar->synchro.i_date_fifo[p_vpar->synchro.i_start] )
- / ( (p_vpar->synchro.i_stop - p_vpar->synchro.i_start) & 0x0f );
+ vlc_mutex_lock( &p_vpar->synchro.fifo_lock );
- p_vpar->synchro.i_delay =
- ( 7 * p_vpar->synchro.i_delay + i_delay ) >> 3;
+ tau = mdate() - p_vpar->synchro.p_date_fifo[p_vpar->synchro.i_start];
+ i_coding_type = p_vpar->synchro.pi_coding_types[p_vpar->synchro.i_start];
-#if 0
- intf_ErrMsg( "decode %lli (mean %lli, theorical %lli)\n",
- i_delay, p_vpar->synchro.i_delay,
- p_vpar->synchro.i_theorical_delay );
-#endif
- }
- else
+ /* Mean with average tau, to ensure stability. */
+ p_vpar->synchro.p_tau[i_coding_type] =
+ (p_vpar->synchro.pi_meaningful[i_coding_type]
+ * p_vpar->synchro.p_tau[i_coding_type] + tau)
+ / (p_vpar->synchro.pi_meaningful[i_coding_type] + 1);
+ if( p_vpar->synchro.pi_meaningful[i_coding_type] < MAX_PIC_AVERAGE )
{
- intf_ErrMsg( "vpar error: critical ! fifo full\n" );
+ p_vpar->synchro.pi_meaningful[i_coding_type]++;
}
FIFO_INCREMENT( i_start );
+
+ vlc_mutex_unlock( &p_vpar->synchro.fifo_lock );
}
/*****************************************************************************
*****************************************************************************/
mtime_t vpar_SynchroDate( vpar_thread_t * p_vpar )
{
-#if 0
+ /* No need to lock, since PTS are only used by the video parser. */
+ return( p_vpar->synchro.current_pts );
+}
- mtime_t i_displaydate = p_vpar->synchro.i_last_pts;
+/*****************************************************************************
+ * SynchroType: Get the user's synchro type
+ *****************************************************************************
+ * This function is called at initialization.
+ *****************************************************************************/
+static int SynchroType( void )
+{
+ char * psz_synchro = main_GetPszVariable( VPAR_SYNCHRO_VAR, NULL );
- static mtime_t i_delta = 0;
+ if( psz_synchro == NULL )
+ {
+ return VPAR_SYNCHRO_DEFAULT;
+ }
- intf_ErrMsg( "displaying image with delay %lli and delta %lli\n",
- i_displaydate - mdate(),
- i_displaydate - i_delta );
+ switch( *psz_synchro++ )
+ {
+ case 'i':
+ case 'I':
+ switch( *psz_synchro++ )
+ {
+ case '\0':
+ return VPAR_SYNCHRO_I;
- intf_ErrMsg ( "theorical fps: %f - actual fps: %f \n",
- 1000000.0 / p_vpar->synchro.i_theorical_delay, 1000000.0 / p_vpar->synchro.i_delay );
+ case '+':
+ if( *psz_synchro ) return 0;
+ return VPAR_SYNCHRO_Iplus;
- i_delta = i_displaydate;
+ case 'p':
+ case 'P':
+ switch( *psz_synchro++ )
+ {
+ case '\0':
+ return VPAR_SYNCHRO_IP;
- return i_displaydate;
-#else
- static s64 i_last_date = 0;
-//printf("%d: %lld\n", p_vpar->picture.i_coding_type, p_vpar->synchro.i_last_pts - i_last_date);
-//i_last_date = p_vpar->synchro.i_last_pts;
- return p_vpar->synchro.i_last_pts;
+ case '+':
+ if( *psz_synchro ) return 0;
+ return VPAR_SYNCHRO_IPplus;
-#endif
-}
+ case 'b':
+ case 'B':
+ if( *psz_synchro ) return 0;
+ return VPAR_SYNCHRO_IPB;
-#endif
+ default:
+ return VPAR_SYNCHRO_DEFAULT;
+
+ }
+ default:
+ return VPAR_SYNCHRO_DEFAULT;
+ }
+ }
-#ifdef POLUX_SYNCHRO
+ return VPAR_SYNCHRO_DEFAULT;
+}
-void vpar_SynchroSetCurrentDate( vpar_thread_t * p_vpar, int i_coding_type )
+/*****************************************************************************
+ * SynchroNewPicture: Update stream structure and PTS
+ *****************************************************************************/
+static void SynchroNewPicture( vpar_thread_t * p_vpar, int i_coding_type )
{
- pes_packet_t * p_pes =
- p_vpar->bit_stream.p_decoder_fifo->buffer[p_vpar->bit_stream.p_decoder_fifo->i_start];
-
+ pes_packet_t * p_pes;
switch( i_coding_type )
{
+ case I_CODING_TYPE:
+ p_vpar->synchro.i_eta_p = p_vpar->synchro.i_eta_b = 0;
+#ifdef STATS
+ intf_Msg( "vpar synchro stats: I(%lld) P(%lld) B(%lld)[%d:%d:%d:%d] YUV(%lld)\n",
+ p_vpar->synchro.p_tau[I_CODING_TYPE],
+ p_vpar->synchro.p_tau[P_CODING_TYPE],
+ p_vpar->synchro.p_tau[B_CODING_TYPE],
+ p_vpar->synchro.i_B_self, p_vpar->synchro.i_B_next,
+ p_vpar->synchro.i_B_last, p_vpar->synchro.i_B_I,
+ p_vpar->p_vout->render_time );
+ p_vpar->synchro.i_B_self = p_vpar->synchro.i_B_next =
+ p_vpar->synchro.i_B_last = p_vpar->synchro.i_B_I = 0;
+#endif
+ break;
+ case P_CODING_TYPE:
+ p_vpar->synchro.i_eta_b = 0;
+ p_vpar->synchro.i_eta_p++;
+ break;
case B_CODING_TYPE:
+ p_vpar->synchro.i_eta_b++;
+ break;
+ }
+
+ p_pes = DECODER_FIFO_START( *p_vpar->bit_stream.p_decoder_fifo );
+
+ if( i_coding_type == B_CODING_TYPE )
+ {
if( p_pes->b_has_pts )
{
- if( p_pes->i_pts < p_vpar->synchro.i_current_frame_date )
+ if( p_pes->i_pts < p_vpar->synchro.current_pts )
{
- intf_ErrMsg( "vpar warning: pts_date < current_date\n" );
+ intf_ErrMsg("vpar warning: pts_date < current_date\n");
}
- p_vpar->synchro.i_current_frame_date = p_pes->i_pts;
+ p_vpar->synchro.current_pts = p_pes->i_pts;
p_pes->b_has_pts = 0;
}
else
{
- p_vpar->synchro.i_current_frame_date += 1000000 / (p_vpar->sequence.i_frame_rate) * 1001;
+ p_vpar->synchro.current_pts += 1000000 / (p_vpar->sequence.i_frame_rate) * 1001;
}
- break;
-
- default:
-
- if( p_vpar->synchro.i_backward_frame_date == 0 )
+ }
+ else
+ {
+ if( p_vpar->synchro.backward_pts == 0 )
{
- p_vpar->synchro.i_current_frame_date += 1000000 / (p_vpar->sequence.i_frame_rate) * 1001;
+ p_vpar->synchro.current_pts += 1000000 / (p_vpar->sequence.i_frame_rate) * 1001;
}
else
{
- if( p_vpar->synchro.i_backward_frame_date < p_vpar->synchro.i_current_frame_date )
+ if( p_vpar->synchro.backward_pts < p_vpar->synchro.current_pts )
{
- intf_ErrMsg( "vpar warning: backward_date < current_date (%Ld)\n",
- p_vpar->synchro.i_backward_frame_date - p_vpar->synchro.i_current_frame_date );
+ intf_ErrMsg("vpar warning: backward_date < current_date\n");
}
- p_vpar->synchro.i_current_frame_date = p_vpar->synchro.i_backward_frame_date;
- p_vpar->synchro.i_backward_frame_date = 0;
+ p_vpar->synchro.current_pts = p_vpar->synchro.backward_pts;
+ p_vpar->synchro.backward_pts = 0;
}
if( p_pes->b_has_pts )
{
- p_vpar->synchro.i_backward_frame_date = p_pes->i_pts;
+ /* Store the PTS for the next time we have to date an I picture. */
+ p_vpar->synchro.backward_pts = p_pes->i_pts;
p_pes->b_has_pts = 0;
}
- break;
- }
-}
-
-boolean_t vpar_SynchroChoose( vpar_thread_t * p_vpar, int i_coding_type,
- int i_structure )
-{
- boolean_t b_result = 1;
- int i_synchro_level = p_vpar->p_vout->i_synchro_level;
-
- vpar_SynchroSetCurrentDate( p_vpar, i_coding_type );
-
- /*
- * The synchro level is updated by the video input (see SynchroLevelUpdate)
- * so we just use the synchro_level to decide which frame to trash
- */
-
- switch( i_coding_type )
- {
- case I_CODING_TYPE:
-
- p_vpar->synchro.r_p_average =
- (p_vpar->synchro.r_p_average*(SYNC_AVERAGE_COUNT-1)+p_vpar->synchro.i_p_count)/SYNC_AVERAGE_COUNT;
- p_vpar->synchro.r_b_average =
- (p_vpar->synchro.r_b_average*(SYNC_AVERAGE_COUNT-1)+p_vpar->synchro.i_b_count)/SYNC_AVERAGE_COUNT;
-
- p_vpar->synchro.i_p_nb = (int)(p_vpar->synchro.r_p_average+0.5);
- p_vpar->synchro.i_b_nb = (int)(p_vpar->synchro.r_b_average+0.5);
-
- p_vpar->synchro.i_p_count = p_vpar->synchro.i_b_count = 0;
- p_vpar->synchro.i_b_trasher = p_vpar->synchro.i_b_nb / 2;
- p_vpar->synchro.i_i_count++;
- break;
-
- case P_CODING_TYPE:
- p_vpar->synchro.i_p_count++;
- if( p_vpar->synchro.i_p_count > i_synchro_level )
- {
- b_result = 0;
- }
- break;
-
- case B_CODING_TYPE:
- p_vpar->synchro.i_b_count++;
- if( p_vpar->synchro.i_p_nb >= i_synchro_level )
- {
- /* We must trash all the B */
- b_result = 0;
- }
- else
- {
- /* We use the brensenham algorithm to decide which B to trash */
- p_vpar->synchro.i_b_trasher +=
- p_vpar->synchro.i_b_nb - (i_synchro_level-p_vpar->synchro.i_p_nb);
- if( p_vpar->synchro.i_b_trasher >= p_vpar->synchro.i_b_nb )
- {
- b_result = 0;
- p_vpar->synchro.i_b_trasher -= p_vpar->synchro.i_b_nb;
- }
- }
- break;
}
-
- return( b_result );
-}
-
-void vpar_SynchroTrash( vpar_thread_t * p_vpar, int i_coding_type,
- int i_structure )
-{
- vpar_SynchroChoose( p_vpar, i_coding_type, i_structure );
-}
-
-void vpar_SynchroUpdateLevel()
-{
- //vlc_mutex_lock( &level_lock );
- //vlc_mutex_unlock( &level_lock );
}
-
-mtime_t vpar_SynchroDate( vpar_thread_t * p_vpar )
-{
- return( p_vpar->synchro.i_current_frame_date );
-}
-
-/* functions with no use */
-
-void vpar_SynchroEnd( vpar_thread_t * p_vpar )
-{
-}
-
-void vpar_SynchroDecode( vpar_thread_t * p_vpar, int i_coding_type,
- int i_structure )
-{
-}
-
-#endif