diff --git a/Makefile.am b/Makefile.am index 61b6def08c..494f197f73 100644 --- a/Makefile.am +++ b/Makefile.am @@ -203,6 +203,7 @@ HEADERS_include = \ include/vlc_playlist.h \ include/vlc_threads.h \ include/vlc_threads_funcs.h \ + include/vout_synchro.h \ include/win32_specific.h \ include/osd.h \ $(NULL) @@ -387,6 +388,7 @@ SOURCES_libvlc_common = \ src/video_output/video_text.c \ src/video_output/video_text.h \ src/video_output/vout_subpictures.c \ + src/video_output/vout_synchro.c \ src/audio_output/common.c \ src/audio_output/dec.c \ src/audio_output/filters.c \ diff --git a/include/vlc_common.h b/include/vlc_common.h index 7ae22b179f..b304746657 100644 --- a/include/vlc_common.h +++ b/include/vlc_common.h @@ -3,7 +3,7 @@ * Collection of useful common types and macros definitions ***************************************************************************** * Copyright (C) 1998, 1999, 2000 VideoLAN - * $Id: vlc_common.h,v 1.60 2003/04/13 20:00:20 fenrir Exp $ + * $Id: vlc_common.h,v 1.61 2003/04/14 22:22:32 massiot Exp $ * * Authors: Samuel Hocevar * Vincent Seguin @@ -242,6 +242,7 @@ typedef struct picture_sys_t picture_sys_t; typedef struct picture_heap_t picture_heap_t; typedef struct subpicture_t subpicture_t; typedef struct subpicture_sys_t subpicture_sys_t; +typedef struct vout_synchro_t vout_synchro_t; /* Stream output */ typedef struct sout_instance_t sout_instance_t; diff --git a/include/vout_synchro.h b/include/vout_synchro.h new file mode 100644 index 0000000000..d63cb1d1e8 --- /dev/null +++ b/include/vout_synchro.h @@ -0,0 +1,89 @@ +/***************************************************************************** + * vout_synchro.h: frame-dropping structures + ***************************************************************************** + * Copyright (C) 1999-2003 VideoLAN + * $Id: vout_synchro.h,v 1.1 2003/04/14 22:22:32 massiot Exp $ + * + * Authors: Christophe Massiot + * Jean-Marc Dressler + * Stéphane Borel + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +/***************************************************************************** + * vout_synchro_t : timers for the video synchro + *****************************************************************************/ +#define MAX_PIC_AVERAGE 8 + +/* Read the discussion on top of vout_synchro.c for more information. */ +struct vout_synchro_t +{ + VLC_COMMON_MEMBERS + + vout_thread_t * p_vout; + int i_frame_rate; + int i_current_rate; + + /* date of the beginning of the decoding of the current picture */ + mtime_t decoding_start; + + /* 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 */ + vlc_bool_t i_nb_ref; /* Number of reference pictures */ + unsigned int i_eta_p, i_eta_b; + mtime_t backward_pts, current_pts; + int i_current_period; /* period to add to the next picture */ + int i_backward_period; /* period to add after the next + * reference picture + * (backward_period * period / 2) */ + + /* statistics */ + unsigned int i_trashed_pic, i_not_chosen_pic, i_pic; +}; + +/* Pictures types */ +#define I_CODING_TYPE 1 +#define P_CODING_TYPE 2 +#define B_CODING_TYPE 3 +#define D_CODING_TYPE 4 /* MPEG-1 ONLY */ +/* other values are reserved */ + +/* Structures */ +#define TOP_FIELD 1 +#define BOTTOM_FIELD 2 +#define FRAME_STRUCTURE 3 + +/***************************************************************************** + * Prototypes + *****************************************************************************/ +#define vout_SynchroInit(a,b,c) __vout_SynchroInit(VLC_OBJECT(a),b,c) +VLC_EXPORT( vout_synchro_t *, __vout_SynchroInit, ( vlc_object_t *, vout_thread_t *, int ) ); +VLC_EXPORT( void, vout_SynchroRelease, ( vout_synchro_t * ) ); +VLC_EXPORT( void, vout_SynchroReset, ( vout_synchro_t * ) ); +VLC_EXPORT( vlc_bool_t, vout_SynchroChoose, ( vout_synchro_t *, int ) ); +VLC_EXPORT( void, vout_SynchroTrash, ( vout_synchro_t * ) ); +VLC_EXPORT( void, vout_SynchroDecode, ( vout_synchro_t * ) ); +VLC_EXPORT( void, vout_SynchroEnd, ( vout_synchro_t *, int, vlc_bool_t ) ); +VLC_EXPORT( mtime_t, vout_SynchroDate, ( vout_synchro_t * ) ); +VLC_EXPORT( void, vout_SynchroNewPicture, ( vout_synchro_t *, int, int, mtime_t, mtime_t, int ) ); + diff --git a/modules/codec/libmpeg2.c b/modules/codec/libmpeg2.c index 2fb63b4ec7..ad08e34640 100755 --- a/modules/codec/libmpeg2.c +++ b/modules/codec/libmpeg2.c @@ -2,7 +2,7 @@ * libmpeg2.c: mpeg2 video decoder module making use of libmpeg2. ***************************************************************************** * Copyright (C) 1999-2001 VideoLAN - * $Id: libmpeg2.c,v 1.8 2003/04/07 17:35:01 gbazin Exp $ + * $Id: libmpeg2.c,v 1.9 2003/04/14 22:22:32 massiot Exp $ * * Authors: Gildas Bazin * @@ -34,6 +34,8 @@ #include +#include "vout_synchro.h" + /* Aspect ratio (ISO/IEC 13818-2 section 6.3.3, table 6-3) */ #define AR_SQUARE_PICTURE 1 /* square pixels */ #define AR_3_4_PICTURE 2 /* 3:4 picture (TV) */ @@ -60,11 +62,14 @@ typedef struct dec_thread_t mtime_t i_previous_pts; mtime_t i_current_pts; mtime_t i_period_remainder; + int i_current_rate; + picture_t * p_picture_to_destroy; /* * Output properties */ vout_thread_t *p_vout; + vout_synchro_t *p_synchro; } dec_thread_t; @@ -102,6 +107,7 @@ static int OpenDecoder( vlc_object_t *p_this ) p_fifo->pf_run = RunDecoder; return VLC_SUCCESS; } + /***************************************************************************** * RunDecoder: the libmpeg2 decoder *****************************************************************************/ @@ -132,6 +138,7 @@ static int RunDecoder( decoder_fifo_t *p_fifo ) p_dec->i_current_pts = 0; p_dec->i_previous_pts = 0; p_dec->i_period_remainder = 0; + p_dec->p_picture_to_destroy = NULL; /* Initialize decoder */ p_dec->p_mpeg2dec = mpeg2_init(); @@ -163,6 +170,14 @@ static int RunDecoder( decoder_fifo_t *p_fifo ) break; } + if( p_dec->p_pes->b_discontinuity ) + { + vout_SynchroReset( p_dec->p_synchro ); + if ( p_dec->p_info->current_fbuf != NULL ) + p_dec->p_picture_to_destroy + = p_dec->p_info->current_fbuf->id; + } + if( p_dec->p_pes->i_pts ) { mpeg2_pts( p_dec->p_mpeg2dec, @@ -170,6 +185,8 @@ static int RunDecoder( decoder_fifo_t *p_fifo ) p_dec->i_previous_pts = p_dec->i_current_pts; p_dec->i_current_pts = p_dec->p_pes->i_pts; } + + p_dec->i_current_rate = p_dec->p_pes->i_rate; p_data = p_dec->p_pes->p_first; } @@ -187,6 +204,7 @@ static int RunDecoder( decoder_fifo_t *p_fifo ) { /* Initialize video output */ uint8_t *buf[3]; + buf[0] = buf[1] = buf[2] = NULL; /* Check whether the input gives a particular aspect ratio */ if( p_dec->p_fifo->p_demux_data @@ -231,76 +249,86 @@ static int RunDecoder( decoder_fifo_t *p_fifo ) mpeg2_custom_fbuf( p_dec->p_mpeg2dec, 1 ); /* Set the first 2 reference frames */ - if( (p_pic = GetNewPicture( p_dec, buf )) == NULL ) break; - mpeg2_set_buf( p_dec->p_mpeg2dec, buf, p_pic ); - if( (p_pic = GetNewPicture( p_dec, buf )) == NULL ) break; - mpeg2_set_buf( p_dec->p_mpeg2dec, buf, p_pic ); + mpeg2_set_buf( p_dec->p_mpeg2dec, buf, NULL ); + mpeg2_set_buf( p_dec->p_mpeg2dec, buf, NULL ); + + p_dec->p_synchro = vout_SynchroInit( p_dec->p_fifo, p_dec->p_vout, + 1000000 * 27 / p_dec->p_info->sequence->frame_period * 1001 ); } break; case STATE_PICTURE: { uint8_t *buf[3]; + buf[0] = buf[1] = buf[2] = NULL; - if( (p_pic = GetNewPicture( p_dec, buf )) == NULL ) break; - mpeg2_set_buf( p_dec->p_mpeg2dec, buf, p_pic ); + vout_SynchroNewPicture( p_dec->p_synchro, + p_dec->p_info->current_picture->flags & PIC_MASK_CODING_TYPE, + p_dec->p_info->current_picture->nb_fields, + (p_dec->p_info->current_picture->flags & PIC_FLAG_PTS) ? + ( (p_dec->p_info->current_picture->pts == + (uint32_t)p_dec->i_current_pts) ? + p_dec->i_current_pts : p_dec->i_previous_pts ) : 0, + 0, + p_dec->i_current_rate ); - /* Store the date for the picture */ - if( p_dec->p_info->current_picture->flags & PIC_FLAG_PTS ) + if ( !vout_SynchroChoose( p_dec->p_synchro, + p_dec->p_info->current_picture->flags & PIC_MASK_CODING_TYPE ) ) { - p_pic->date = ( p_dec->p_info->current_picture->pts == - (uint32_t)p_dec->i_current_pts ) ? - p_dec->i_current_pts : p_dec->i_previous_pts; + mpeg2_skip( p_dec->p_mpeg2dec, 1 ); + vout_SynchroTrash( p_dec->p_synchro ); + mpeg2_set_buf( p_dec->p_mpeg2dec, buf, NULL ); + } + else + { + mpeg2_skip( p_dec->p_mpeg2dec, 0 ); + vout_SynchroDecode( p_dec->p_synchro ); + if( (p_pic = GetNewPicture( p_dec, buf )) == NULL ) break; + mpeg2_set_buf( p_dec->p_mpeg2dec, buf, p_pic ); } } - break; + /* pass-through */ case STATE_END: - case STATE_SLICE: if( p_dec->p_info->display_fbuf && p_dec->p_info->display_fbuf->id ) { p_pic = (picture_t *)p_dec->p_info->display_fbuf->id; - /* Date the new picture */ - if( p_dec->p_info->display_picture->flags & PIC_FLAG_PTS ) + if ( p_pic != NULL ) { - p_dec->i_pts = p_pic->date; - p_dec->i_period_remainder = 0; + if ( p_dec->p_picture_to_destroy != p_pic ) + { + vout_SynchroEnd( p_dec->p_synchro, + p_dec->p_info->display_picture->flags + & PIC_MASK_CODING_TYPE, + 0 ); + vout_DatePicture( p_dec->p_vout, p_pic, + vout_SynchroDate( p_dec->p_synchro ) ); + vout_DisplayPicture( p_dec->p_vout, p_pic ); + } + else + { + p_dec->p_picture_to_destroy = NULL; + vout_SynchroEnd( p_dec->p_synchro, + p_dec->p_info->display_picture->flags + & PIC_MASK_CODING_TYPE, + 1 ); + vout_DestroyPicture( p_dec->p_vout, p_pic ); + } } - else + if( p_dec->p_info->discard_fbuf && + p_dec->p_info->discard_fbuf->id ) { - p_dec->i_pts += ( (p_dec->p_info->sequence->frame_period + - p_dec->i_period_remainder) / 27 ); - p_dec->i_period_remainder = - p_dec->p_info->sequence->frame_period + - p_dec->i_period_remainder - - ( p_dec->p_info->sequence->frame_period + - p_dec->i_period_remainder ) / 27 * 27; - } - vout_DatePicture( p_dec->p_vout, p_pic, p_dec->i_pts ); - - vout_DisplayPicture( p_dec->p_vout, p_pic ); - - /* Handle pulldown by adding some delay to the pts of the next - * picture. */ - if( p_dec->p_info->display_picture->nb_fields > 2 ) - { - int i_repeat_fields = - p_dec->p_info->display_picture->nb_fields - 2; - - p_dec->i_pts += ( (p_dec->p_info->sequence->frame_period + - p_dec->i_period_remainder) - / 27 / 2 * i_repeat_fields ); - p_dec->i_period_remainder = - p_dec->p_info->sequence->frame_period + - p_dec->i_period_remainder - - ( p_dec->p_info->sequence->frame_period + - p_dec->i_period_remainder ) / 27 / 2 * 27 * 2; + vout_UnlinkPicture( p_dec->p_vout, p_pic ); } } break; + case STATE_INVALID: + msg_Warn( p_dec->p_fifo, "Received STATE_INVALID" ); + break; + default: break; } @@ -357,6 +385,9 @@ static void CloseDecoder( dec_thread_t * p_dec ) vout_Request( p_dec->p_fifo, p_dec->p_vout, 0, 0, 0, 0 ); } + if( p_dec->p_synchro ) + vout_SynchroRelease( p_dec->p_synchro ); + if( p_dec->p_mpeg2dec ) mpeg2_close( p_dec->p_mpeg2dec ); free( p_dec ); @@ -380,6 +411,7 @@ static picture_t *GetNewPicture( dec_thread_t *p_dec, uint8_t **pp_buf ) } if( p_pic == NULL ) return NULL; + vout_LinkPicture( p_dec->p_vout, p_pic ); pp_buf[0] = p_pic->p[0].p_pixels; pp_buf[1] = p_pic->p[1].p_pixels; diff --git a/src/misc/modules.c b/src/misc/modules.c index b2e6f0abb3..3ad94a2b56 100644 --- a/src/misc/modules.c +++ b/src/misc/modules.c @@ -2,7 +2,7 @@ * modules.c : Builtin and plugin modules management functions ***************************************************************************** * Copyright (C) 2001 VideoLAN - * $Id: modules.c,v 1.117 2003/03/25 15:38:14 gbazin Exp $ + * $Id: modules.c,v 1.118 2003/04/14 22:22:32 massiot Exp $ * * Authors: Samuel Hocevar * Ethan C. Baldridge @@ -81,6 +81,7 @@ #include "video.h" #include "video_output.h" +#include "vout_synchro.h" #include "audio_output.h" #include "aout_internal.h" diff --git a/src/video_output/video_output.c b/src/video_output/video_output.c index cf4e1d74db..d5474ce639 100644 --- a/src/video_output/video_output.c +++ b/src/video_output/video_output.c @@ -5,7 +5,7 @@ * thread, and destroy a previously oppened video output thread. ***************************************************************************** * Copyright (C) 2000-2001 VideoLAN - * $Id: video_output.c,v 1.217 2003/03/28 17:02:25 gbazin Exp $ + * $Id: video_output.c,v 1.218 2003/04/14 22:22:32 massiot Exp $ * * Authors: Vincent Seguin * @@ -638,7 +638,7 @@ static void RunThread( vout_thread_t *p_vout) display_date = 0; current_date = mdate(); -#ifdef STATS +#if 0 p_vout->c_loops++; if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) ) { diff --git a/src/video_output/vout_synchro.c b/src/video_output/vout_synchro.c new file mode 100644 index 0000000000..1bab5ce8f1 --- /dev/null +++ b/src/video_output/vout_synchro.c @@ -0,0 +1,522 @@ +/***************************************************************************** + * vout_synchro.c : frame dropping routines + ***************************************************************************** + * Copyright (C) 1999-2001 VideoLAN + * $Id: vout_synchro.c,v 1.1 2003/04/14 22:22:32 massiot Exp $ + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * 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/2, that + * needs to be confirmed) + render time. + * DELTA : A given error margin. + * + * 3. General considerations + * ====================== + * We define three types of machines : + * 14T > tauI : machines capable of decoding all I pictures + * 2T > tauP : machines capable of decoding all P pictures + * T > tauB : machines capable of decoding all B pictures + * + * 4. Decoding of an I picture + * ======================== + * On fast machines, we decode all I's. + * Otherwise : + * We can decode an I picture if we simply have enough time to decode it + * before displaying : + * t0 - t > tauŽI + DELTA + * + * 5. Decoding of a P picture + * ======================= + * On fast machines, we decode all P's. + * Otherwise : + * First criterion : have time to decode it. + * t2 - t > tauŽP + DELTA + * + * Second criterion : it shouldn't prevent us from displaying the forthcoming + * I picture, which is more important. + * t12 - t > tauŽP + tauŽI + DELTA + * + * 6. Decoding of a B picture + * ======================= + * On fast machines, we decode all B's. Otherwise : + * t1 - t > tauŽB + DELTA + * Since the next displayed I or P is already decoded, we don't have to + * worry about it. + * + * I hope you will have a pleasant flight and do not forget your life + * jacket. + * --Meuuh (2000-12-29) + */ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#include /* free() */ +#include /* memcpy(), memset() */ + +#include +#include + +#include "vout_synchro.h" +#include "stream_control.h" + +/* + * Local prototypes + */ + +/* Error margins */ +#define DELTA (int)(0.075*CLOCK_FREQ) + +#define DEFAULT_NB_P 5 +#define DEFAULT_NB_B 1 + +/***************************************************************************** + * vout_SynchroInit : You know what ? + *****************************************************************************/ +vout_synchro_t * __vout_SynchroInit( vlc_object_t * p_object, + vout_thread_t * p_vout, int i_frame_rate ) +{ + vout_synchro_t * p_synchro = vlc_object_create( p_object, + sizeof(vout_synchro_t) ); + if ( p_synchro == NULL ) + { + msg_Err( p_object, "out of memory" ); + return NULL; + } + vlc_object_attach( p_synchro, p_vout ); + + /* We use a fake stream pattern, which is often right. */ + p_synchro->i_n_p = p_synchro->i_eta_p = DEFAULT_NB_P; + p_synchro->i_n_b = p_synchro->i_eta_b = DEFAULT_NB_B; + memset( p_synchro->p_tau, 0, 4 * sizeof(mtime_t) ); + memset( p_synchro->pi_meaningful, 0, 4 * sizeof(unsigned int) ); + p_synchro->i_nb_ref = 0; + p_synchro->current_pts = mdate() + DEFAULT_PTS_DELAY; + p_synchro->backward_pts = 0; + p_synchro->i_current_period = p_synchro->i_backward_period = 0; + p_synchro->i_trashed_pic = p_synchro->i_not_chosen_pic = + p_synchro->i_pic = 0; + + p_synchro->i_frame_rate = i_frame_rate; + p_synchro->p_vout = p_vout; + + return p_synchro; +} + +/***************************************************************************** + * vout_SynchroRelease : You know what ? + *****************************************************************************/ +void vout_SynchroRelease( vout_synchro_t * p_synchro ) +{ + vlc_object_detach( p_synchro ); + vlc_object_destroy( p_synchro ); +} + +/***************************************************************************** + * vout_SynchroReset : Reset the reference picture counter + *****************************************************************************/ +void vout_SynchroReset( vout_synchro_t * p_synchro ) +{ + p_synchro->i_nb_ref = 0; +} + +/***************************************************************************** + * vout_SynchroChoose : Decide whether we will decode a picture or not + *****************************************************************************/ +vlc_bool_t vout_SynchroChoose( vout_synchro_t * p_synchro, int i_coding_type ) +{ +#define TAU_PRIME( coding_type ) (p_synchro->p_tau[(coding_type)] \ + + (p_synchro->p_tau[(coding_type)] >> 1) \ + + tau_yuv) +#define S (*p_synchro) + /* VPAR_SYNCHRO_DEFAULT */ + mtime_t now, period, tau_yuv; + mtime_t pts = 0; + vlc_bool_t b_decode = 0; + + now = mdate(); + period = 1000000 * 1001 / p_synchro->i_frame_rate + * p_synchro->i_current_rate / DEFAULT_RATE; + + vlc_mutex_lock( &p_synchro->p_vout->change_lock ); + tau_yuv = p_synchro->p_vout->render_time; + vlc_mutex_unlock( &p_synchro->p_vout->change_lock ); + + switch( i_coding_type ) + { + case I_CODING_TYPE: + if( S.backward_pts ) + { + pts = S.backward_pts; + } + else + { + /* displaying order : B B P B B I + * ^ ^ + * | +- current picture + * +- current PTS + */ + pts = S.current_pts + period * (S.i_n_b + 2); + } + + if( (1 + S.i_n_p * (S.i_n_b + 1)) * period > + S.p_tau[I_CODING_TYPE] ) + { + b_decode = 1; + } + else + { + b_decode = (pts - now) > (TAU_PRIME(I_CODING_TYPE) + DELTA); + } + if( !b_decode ) + { + msg_Warn( p_synchro, + "synchro trashing I ("I64Fd")", pts - now ); + p_synchro->i_nb_ref = 0; + } + else if( p_synchro->i_nb_ref < 2 ) + p_synchro->i_nb_ref++; + break; + + case P_CODING_TYPE: + if( S.backward_pts ) + { + pts = S.backward_pts; + } + else + { + pts = S.current_pts + period * (S.i_n_b + 1); + } + + if( p_synchro->i_nb_ref < 1 ) + { + b_decode = 0; + } + else if( (1 + S.i_n_p * (S.i_n_b + 1)) * period > + S.p_tau[I_CODING_TYPE] ) + { + if( (S.i_n_b + 1) * period > S.p_tau[P_CODING_TYPE] ) + { + /* Security in case we're _really_ late */ + b_decode = (pts - now > 0); + } + else + { + b_decode = (pts - now) > (TAU_PRIME(P_CODING_TYPE) + DELTA); + /* next I */ + b_decode &= (pts - now + + period + * ( (S.i_n_p - S.i_eta_p) * (1 + S.i_n_b) - 1 )) + > (TAU_PRIME(P_CODING_TYPE) + + TAU_PRIME(I_CODING_TYPE) + DELTA); + } + } + else + { + b_decode = 0; + } + if( b_decode ) + p_synchro->i_nb_ref = 2; + else + p_synchro->i_nb_ref = 0; + break; + + case B_CODING_TYPE: + pts = S.current_pts; + + if( p_synchro->i_nb_ref < 2 ) + { + b_decode = 0; + } + else if( (S.i_n_b + 1) * period > S.p_tau[P_CODING_TYPE] ) + { + b_decode = (pts - now) > (TAU_PRIME(B_CODING_TYPE) + DELTA); + } + else + { + b_decode = 0; + } + } + + if( !b_decode ) + { + S.i_not_chosen_pic++; + } + return( b_decode ); +#undef S +#undef TAU_PRIME +} + +/***************************************************************************** + * vout_SynchroTrash : Update counters when we trash a picture + *****************************************************************************/ +void vout_SynchroTrash( vout_synchro_t * p_synchro ) +{ + p_synchro->i_trashed_pic++; +} + +/***************************************************************************** + * vout_SynchroDecode : Update timers when we decide to decode a picture + *****************************************************************************/ +void vout_SynchroDecode( vout_synchro_t * p_synchro ) +{ + p_synchro->decoding_start = mdate(); +} + +/***************************************************************************** + * vout_SynchroEnd : Called when the image is totally decoded + *****************************************************************************/ +void vout_SynchroEnd( vout_synchro_t * p_synchro, int i_coding_type, + vlc_bool_t b_garbage ) +{ + mtime_t tau; + + if( !b_garbage ) + { + tau = mdate() - p_synchro->decoding_start; + + /* If duration too high, something happened (pause ?), so don't + * take it into account. */ + if( tau < 3 * p_synchro->p_tau[i_coding_type] + || !p_synchro->pi_meaningful[i_coding_type] ) + { + /* Mean with average tau, to ensure stability. */ + p_synchro->p_tau[i_coding_type] = + (p_synchro->pi_meaningful[i_coding_type] + * p_synchro->p_tau[i_coding_type] + tau) + / (p_synchro->pi_meaningful[i_coding_type] + 1); + if( p_synchro->pi_meaningful[i_coding_type] < MAX_PIC_AVERAGE ) + { + p_synchro->pi_meaningful[i_coding_type]++; + } + } + } +} + +/***************************************************************************** + * vout_SynchroDate : When an image has been decoded, ask for its date + *****************************************************************************/ +mtime_t vout_SynchroDate( vout_synchro_t * p_synchro ) +{ + /* No need to lock, since PTS are only used by the video parser. */ + return p_synchro->current_pts; +} + +/***************************************************************************** + * vout_SynchroNewPicture: Update stream structure and PTS + *****************************************************************************/ +void vout_SynchroNewPicture( vout_synchro_t * p_synchro, int i_coding_type, + int i_repeat_field, mtime_t next_pts, + mtime_t next_dts, int i_current_rate ) +{ + mtime_t period = 1000000 * 1001 / p_synchro->i_frame_rate + * i_current_rate / DEFAULT_RATE; +#if 0 + mtime_t now = mdate(); +#endif + p_synchro->i_current_rate = i_current_rate; + + switch( i_coding_type ) + { + case I_CODING_TYPE: + if( p_synchro->i_eta_p + && p_synchro->i_eta_p != p_synchro->i_n_p ) + { +#if 0 + msg_Dbg( p_synchro, + "stream periodicity changed from P[%d] to P[%d]", + p_synchro->i_n_p, p_synchro->i_eta_p ); +#endif + p_synchro->i_n_p = p_synchro->i_eta_p; + } + p_synchro->i_eta_p = p_synchro->i_eta_b = 0; + +#if 0 + msg_Dbg( p_synchro, "I("I64Fd") P("I64Fd")[%d] B("I64Fd")" + "[%d] YUV("I64Fd") : trashed %d:%d/%d", + p_synchro->p_tau[I_CODING_TYPE], + p_synchro->p_tau[P_CODING_TYPE], + p_synchro->i_n_p, + p_synchro->p_tau[B_CODING_TYPE], + p_synchro->i_n_b, + p_synchro->p_vout->render_time, + p_synchro->i_not_chosen_pic, + p_synchro->i_trashed_pic - + p_synchro->i_not_chosen_pic, + p_synchro->i_pic ); + p_synchro->i_trashed_pic = p_synchro->i_not_chosen_pic + = p_synchro->i_pic = 0; +#else + if ( p_synchro->i_pic >= 100 ) + { + msg_Dbg( p_synchro, "decoded %d/%d pictures", + p_synchro->i_pic + - p_synchro->i_trashed_pic, + p_synchro->i_pic ); + p_synchro->i_trashed_pic = p_synchro->i_not_chosen_pic + = p_synchro->i_pic = 0; + } +#endif + break; + + case P_CODING_TYPE: + p_synchro->i_eta_p++; + if( p_synchro->i_eta_b + && p_synchro->i_eta_b != p_synchro->i_n_b ) + { + msg_Warn( p_synchro, + "stream periodicity changed from B[%d] to B[%d]", + p_synchro->i_n_b, p_synchro->i_eta_b ); + p_synchro->i_n_b = p_synchro->i_eta_b; + } + p_synchro->i_eta_b = 0; + break; + case B_CODING_TYPE: + p_synchro->i_eta_b++; + break; + } + + p_synchro->current_pts += p_synchro->i_current_period + * (period >> 1); + +#define PTS_THRESHOLD (period >> 2) + if( i_coding_type == B_CODING_TYPE ) + { + /* A video frame can be displayed 1, 2 or 3 times, according to + * repeat_first_field, top_field_first, progressive_sequence and + * progressive_frame. */ + p_synchro->i_current_period = i_repeat_field; + + if( next_pts ) + { + if( next_pts - p_synchro->current_pts + > PTS_THRESHOLD + || p_synchro->current_pts - next_pts + > PTS_THRESHOLD ) + { + msg_Warn( p_synchro, "vout synchro warning: pts != " + "current_date ("I64Fd")", + p_synchro->current_pts + - next_pts ); + } + p_synchro->current_pts = next_pts; + } + } + else + { + p_synchro->i_current_period = p_synchro->i_backward_period; + p_synchro->i_backward_period = i_repeat_field; + + if( p_synchro->backward_pts ) + { + if( next_dts && + (next_dts - p_synchro->backward_pts + > PTS_THRESHOLD + || p_synchro->backward_pts - next_dts + > PTS_THRESHOLD) ) + { + msg_Warn( p_synchro, "backward_pts != dts ("I64Fd")", + next_dts + - p_synchro->backward_pts ); + } + if( p_synchro->backward_pts - p_synchro->current_pts + > PTS_THRESHOLD + || p_synchro->current_pts - p_synchro->backward_pts + > PTS_THRESHOLD ) + { + msg_Warn( p_synchro, + "backward_pts != current_pts ("I64Fd")", + p_synchro->current_pts + - p_synchro->backward_pts ); + } + p_synchro->current_pts = p_synchro->backward_pts; + p_synchro->backward_pts = 0; + } + else if( next_dts ) + { + if( next_dts - p_synchro->current_pts + > PTS_THRESHOLD + || p_synchro->current_pts - next_dts + > PTS_THRESHOLD ) + { + msg_Warn( p_synchro, "dts != current_pts ("I64Fd")", + p_synchro->current_pts + - next_dts ); + } + /* By definition of a DTS. */ + p_synchro->current_pts = next_dts; + next_dts = 0; + } + + if( next_pts ) + { + /* Store the PTS for the next time we have to date an I picture. */ + p_synchro->backward_pts = next_pts; + next_pts = 0; + } + } +#undef PTS_THRESHOLD + +#if 0 + /* Removed for incompatibility with slow motion */ + if( p_synchro->current_pts + DEFAULT_PTS_DELAY < now ) + { + /* We cannot be _that_ late, something must have happened, reinit + * the dates. */ + msg_Warn( p_synchro, "PTS << now ("I64Fd"), resetting", + now - p_synchro->current_pts - DEFAULT_PTS_DELAY ); + p_synchro->current_pts = now + DEFAULT_PTS_DELAY; + } + if( p_synchro->backward_pts + && p_synchro->backward_pts + DEFAULT_PTS_DELAY < now ) + { + /* The same. */ + p_synchro->backward_pts = 0; + } +#endif + + p_synchro->i_pic++; +}