From bad6f235822f3b32f5d9cc52ed272fab6cde0e69 Mon Sep 17 00:00:00 2001 From: Christophe Massiot Date: Mon, 13 Nov 2000 13:58:18 +0000 Subject: [PATCH] * 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. --- ChangeLog | 7 + Makefile.in | 13 +- include/config.h.in | 13 +- include/video_output.h | 12 +- include/vpar_synchro.h | 124 ++---- src/misc/mtime.c | 4 +- src/video_output/video_output.c | 140 +------ src/video_parser/video_parser.c | 111 +---- src/video_parser/vpar_synchro.c | 714 ++++++++++++++++---------------- 9 files changed, 457 insertions(+), 681 deletions(-) diff --git a/ChangeLog b/ChangeLog index fa257a3353..6855676ff0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -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 : diff --git a/Makefile.in b/Makefile.in index 403ebf4fb8..d867a08708 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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 # diff --git a/include/config.h.in b/include/config.h.in index eab84b46bd..3400baa542 100644 --- a/include/config.h.in +++ b/include/config.h.in @@ -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 - diff --git a/include/video_output.h b/include/video_output.h index 5e44b4e246..2949df222f 100644 --- a/include/video_output.h +++ b/include/video_output.h @@ -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 diff --git a/include/vpar_synchro.h b/include/vpar_synchro.h index b2e6e7b21a..d7bec72afd 100644 --- a/include/vpar_synchro.h +++ b/include/vpar_synchro.h @@ -3,9 +3,7 @@ ***************************************************************************** * Copyright (C) 1999, 2000 VideoLAN * - * Authors: Samuel Hocevar - * Jean-Marc Dressler - * Christophe Massiot + * Author: Christophe Massiot * * 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 diff --git a/src/misc/mtime.c b/src/misc/mtime.c index 9272a11ec1..f019eb9f90 100644 --- a/src/misc/mtime.c +++ b/src/misc/mtime.c @@ -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; diff --git a/src/video_output/video_output.c b/src/video_output/video_output.c index 292c3a1f79..f34bc685ae 100644 --- a/src/video_output/video_output.c +++ b/src/video_output/video_output.c @@ -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 diff --git a/src/video_parser/video_parser.c b/src/video_parser/video_parser.c index 0af739cd50..9b58880730 100644 --- a/src/video_parser/video_parser.c +++ b/src/video_parser/video_parser.c @@ -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; - -} - diff --git a/src/video_parser/vpar_synchro.c b/src/video_parser/vpar_synchro.c index a9b06b281f..daf0cd5256 100644 --- a/src/video_parser/vpar_synchro.c +++ b/src/video_parser/vpar_synchro.c @@ -3,9 +3,9 @@ ***************************************************************************** * Copyright (C) 1999, 2000 VideoLAN * - * Authors: Samuel Hocevar - * Jean-Marc Dressler - * Christophe Massiot + * Authors: Christophe Massiot + * Samuel Hocevar + * Jean-Marc Dressler * * 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 + * : 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 *****************************************************************************/ @@ -53,151 +127,39 @@ #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( " Decoding: " ); - /*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; } /***************************************************************************** @@ -206,26 +168,22 @@ void vpar_SynchroUpdateStructures( vpar_thread_t * p_vpar, 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 ); @@ -244,71 +202,161 @@ boolean_t vpar_SynchroChoose( vpar_thread_t * p_vpar, int i_coding_type, } } - 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 + } } /***************************************************************************** @@ -317,22 +365,33 @@ boolean_t vpar_SynchroChoose( vpar_thread_t * p_vpar, int i_coding_type, 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 ); } /***************************************************************************** @@ -340,29 +399,27 @@ void vpar_SynchroDecode( vpar_thread_t * p_vpar, int 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 ); } /***************************************************************************** @@ -370,174 +427,137 @@ void vpar_SynchroEnd( vpar_thread_t * p_vpar ) *****************************************************************************/ 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