/* 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
+#define VOUT_SYNCHRO_LEVEL_MAX 15
+#define VOUT_SYNCHRO_HEAP_IDEAL_SIZE 5
* 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 */
+ mtime_t p_fps_sample[VOUT_FPS_SAMPLES]; /* FPS samples dates */
#endif
/* Rendering buffers */
int i_buffer_index; /* buffer index */
vout_buffer_t p_buffer[2]; /* buffers properties */
- /* Videos heap and translation tables */
+ /* Videos heap and translation tables */
picture_t p_picture[VOUT_MAX_PICTURES]; /* pictures */
subpicture_t p_subpicture[VOUT_MAX_PICTURES]; /* subpictures */
+ int i_pictures; /* current heap size */
vout_yuv_t yuv; /* translation tables */
/* Bitmap fonts */
p_vout_font_t p_default_font; /* default font */
p_vout_font_t p_large_font; /* large font */
+ /* Synchronisation informations - synchro level is updated by the vout
+ * thread and read by decoder threads */
+ int i_synchro_level; /* trashing level */
} vout_thread_t;
/* Output methods */
* "video_fifo.h"
*****************************************************************************/
-#define SAM_SYNCHRO
+#define POLUX_SYNCHRO
/*****************************************************************************
* video_synchro_t and video_synchro_tab_s : timers for the video synchro
double actual_fps;
} video_synchro_t;
-#else
+#endif
+
+#ifdef MEUUH_SYNCHRO
typedef struct video_synchro_s
{
int kludge_level, kludge_p, kludge_b, kludge_nbp, kludge_nbb;
#define SYNC_DELAY 500000
#endif
+#ifdef POLUX_SYNCHRO
+
+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.r_frame_rate) */
+ mtime_t i_current_frame_date;
+ mtime_t i_backward_frame_date;
+
+ /* Frame Trashing Section */
+
+ int i_b_nb, i_p_nb; /* number of decoded P and B between two I */
+ int i_b_count, i_p_count, i_i_count;
+ int i_b_trasher; /* used for brensenham algorithm */
+
+} video_synchro_t;
+
+#endif
+
/*****************************************************************************
* Prototypes
*****************************************************************************/
/* This part of the header does not fit in the current TS packet:
copy the part of the header we are interested in to the
p_pes_header_save buffer. The buffer is dynamicly allocated if
- needed so it's time expensive but this situation almost never
- occur. */
+ needed so it's time expensive but this situation almost never occur. */
intf_DbgMsg("Code never tested encountered, WARNING ! (benny)\n");
- if( !p_pes->p_pes_header_save )
- p_pes->p_pes_header_save = malloc(PES_HEADER_SIZE);
+ if( !p_pes->p_pes_header_save )
+ {
+ p_pes->p_pes_header_save = malloc(PES_HEADER_SIZE);
+ }
do
{
static void RenderInterface ( vout_thread_t *p_vout );
static int RenderIdle ( 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->p_subpicture[i_index].i_type = EMPTY_SUBPICTURE;
p_vout->p_subpicture[i_index].i_status= FREE_SUBPICTURE;
}
+ p_vout->i_pictures = 0;
+
+ /* Initialize synchronization informations */
+ p_vout->i_synchro_level = VOUT_SYNCHRO_LEVEL_START;
/* Create and initialize system-dependant method - this function issues its
* own error messages */
* can end immediately - this is the best possible case, since no
* memory allocation needs to be done */
p_vout->p_picture[i_picture].i_status = RESERVED_PICTURE;
+ p_vout->i_pictures++;
#ifdef DEBUG_VIDEO
intf_DbgMsg("picture %p (in destroyed picture slot)\n",
&p_vout->p_picture[i_picture] );
p_free_picture->i_display_height = i_height;
p_free_picture->i_aspect_ratio = AR_SQUARE_PICTURE;
p_free_picture->i_refcount = 0;
+ p_vout->i_pictures++;
}
else
{
* This function frees a previously reserved picture or a permanent
* picture. It is meant to be used when the construction of a picture aborted.
* Note that the picture will be destroyed even if it is linked !
- * This function does not need locking since reserved pictures are ignored by
- * the output thread.
*****************************************************************************/
void vout_DestroyPicture( vout_thread_t *p_vout, picture_t *p_pic )
{
+ vlc_mutex_lock( &p_vout->picture_lock );
+
#ifdef DEBUG
/* Check if picture status is valid */
if( (p_pic->i_status != RESERVED_PICTURE) &&
}
#endif
- p_pic->i_status = DESTROYED_PICTURE;
+ p_pic->i_status = DESTROYED_PICTURE;
+ p_vout->i_pictures--;
#ifdef DEBUG_VIDEO
- intf_DbgMsg("picture %p\n", p_pic);
+ intf_DbgMsg("picture %p\n", p_pic);
#endif
+ vlc_mutex_unlock( &p_vout->picture_lock );
}
/*****************************************************************************
if( (p_pic->i_refcount == 0) && (p_pic->i_status == DISPLAYED_PICTURE) )
{
p_pic->i_status = DESTROYED_PICTURE;
+ p_vout->i_pictures--;
}
#ifdef DEBUG_VIDEO
/* Computes FPS rate */
p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ] = display_date;
#endif
+#if 0
if( display_date < current_date )
{
/* Picture is late: it will be destroyed and the thread will sleep and
* go to next picture */
+
vlc_mutex_lock( &p_vout->picture_lock );
- p_pic->i_status = p_pic->i_refcount ? DISPLAYED_PICTURE : DESTROYED_PICTURE;
- intf_DbgMsg( "warning: late picture %p skipped refcount=%d\n", p_pic, p_pic->i_refcount );
+ if( p_pic->i_refcount )
+ {
+ p_pic->i_status = DISPLAYED_PICTURE;
+ }
+ else
+ {
+ 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 );
vlc_mutex_unlock( &p_vout->picture_lock );
+
p_pic = NULL;
display_date = 0;
+
+ /* Update synchronization information as if display delay
+ * was 0 */
+ Synchronize( p_vout, 0 );
}
- else if( display_date > current_date + VOUT_DISPLAY_DELAY )
+ else
+#endif
+ 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 empty loop
p_pic = NULL;
display_date = 0;
}
+ else
+ {
+ /* Picture will be displayed, update synchronization
+ * information */
+ Synchronize( p_vout, display_date - current_date );
+ }
}
/*
/* Remove picture from heap */
vlc_mutex_lock( &p_vout->picture_lock );
- p_pic->i_status = p_pic->i_refcount ? DISPLAYED_PICTURE : DESTROYED_PICTURE;
+ if( p_pic->i_refcount )
+ {
+ p_pic->i_status = DISPLAYED_PICTURE;
+ }
+ else
+ {
+ p_pic->i_status = DESTROYED_PICTURE;
+ p_vout->i_pictures--;
+ }
vlc_mutex_unlock( &p_vout->picture_lock );
/* Render interface and subpicture */
break;
}
}
- sprintf( psz_buffer, "pic: %d/%d/%d",
- i_reserved_pic, i_ready_pic, VOUT_MAX_PICTURES );
+ sprintf( psz_buffer, "pic: %d (%d/%d)/%d",
+ p_vout->i_pictures, i_reserved_pic, i_ready_pic, VOUT_MAX_PICTURES );
Print( p_vout, 0, 0, LEFT_RALIGN, BOTTOM_RALIGN, psz_buffer );
#endif
}
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;
+ //???? gore following
+ static int i_panic_count = 0;
+ static int i_last_synchro_inc = 0;
+ static float r_synchro_level = VOUT_SYNCHRO_LEVEL_START;
+ static int i_truc = 1;
+
+ //?? heap size is p_vout->i_pictures
+ //??
+ if( i_delay < 0 )
+ {
+// intf_Msg("PANIC %d\n", i_panic_count++);
+ }
+/*
+ if( p_vout->i_pictures > VOUT_SYNCHRO_HEAP_IDEAL_SIZE+1 )
+ {
+ i_synchro_inc++;
+ }
+ else if( p_vout->i_pictures < VOUT_SYNCHRO_HEAP_IDEAL_SIZE )
+ {
+ i_synchro_inc--;
+ }
+*/
+ if( i_delay < 10000 )
+ {
+ i_truc = 4;
+ }
+
+ if( i_delay < 20000 )
+ {
+ i_synchro_inc--;
+ }
+ else if( i_delay > 50000 )
+ {
+ i_synchro_inc++;
+ }
+
+ if( i_synchro_inc*i_last_synchro_inc < 0 )
+ {
+ i_truc = 2;
+ }
+ else
+ {
+ i_truc *= 2;
+ }
+ if( i_truc > VOUT_SYNCHRO_LEVEL_MAX || i_delay == 0 )
+ {
+ i_truc = 2;
+ }
+
+ r_synchro_level += (float)i_synchro_inc / i_truc;
+ p_vout->i_synchro_level = (int) r_synchro_level;
+
+ if( r_synchro_level > VOUT_SYNCHRO_LEVEL_MAX )
+ {
+ r_synchro_level = VOUT_SYNCHRO_LEVEL_MAX;
+ }
+
+// printf( "synchro level : %d, (%d, %d) (%d, %f) - %Ld\n", p_vout->i_synchro_level,
+// i_last_synchro_inc, i_synchro_inc, i_truc, r_synchro_level, i_delay );
+ i_last_synchro_inc = i_synchro_inc;
+}
+
/*****************************************************************************
* Manage: manage thread
*****************************************************************************
p_vpar->synchro.tab_b[i_dummy].mean = 6;
p_vpar->synchro.tab_b[i_dummy].deviation = .5;
}
-#else
+#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_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.i_p_nb = 5;
+ 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
+
/* Mark thread as running and return */
intf_DbgMsg("vpar debug: InitThread(%p) succeeded\n", p_vpar);
return( 0 );
#include "video_parser.h"
#include "video_fifo.h"
+
+
+
+
+
+
+static int i_count = 0;
+
+
+
+
+
+
/*
* Welcome to vpar_blocks.c ! Here's where the heavy processor-critical parsing
* task is done. This file is divided in several parts :
p_vpar->pppl_dct_dc_size[1][0] = pl_dct_dc_chrom_init_table_1;
p_vpar->pppl_dct_dc_size[1][1] = pl_dct_dc_chrom_init_table_2;
- memset( p_vpar->ppl_dct_coef[0], MB_ERROR, 16 );
- memset( p_vpar->ppl_dct_coef[1], MB_ERROR, 16 );
-
+ /* ??? MB_ERROR is replaced by 0 because if we use -1 we
+ * can block in DecodeMPEG2Intra and others */
+ memset( p_vpar->ppl_dct_coef[0], 0, 16 );
+ memset( p_vpar->ppl_dct_coef[1], 0, 16 );
+
/* For table B14 & B15, we have a pointer to tables */
/* We fill the table thanks to the fonction defined above */
FillDCTTable( p_vpar->ppl_dct_coef[0], pl_DCT_tab0, 256, 60, 4 );
static int pi_x[12] = {0,8,0,8,0,0,0,0,8,8,8,8};
static int pi_y[2][12] = { {0,0,8,8,0,0,8,8,0,0,8,8},
{0,0,1,1,0,0,1,1,0,0,1,1} };
-
int i_mb, i_b, i_mask;
+ int i_inc;
macroblock_t * p_mb;
yuv_data_t * p_data1;
yuv_data_t * p_data2;
- *pi_mb_address += MacroblockAddressIncrement( p_vpar );
+i_count++;
+ i_inc = MacroblockAddressIncrement( p_vpar );
+ *pi_mb_address += i_inc;
+
+ if( i_inc < 0 )
+ {
+ fprintf( stderr, "vpar error: bad address increment (%d)\n", i_inc );
+ p_vpar->picture.b_error = 1;
+ return;
+ }
+
if( *pi_mb_address - i_mb_previous - 1 )
{
/* Skipped macroblock (ISO/IEC 13818-2 7.6.6). */
{
RemoveBits( &p_vpar->bit_stream, 8 );
}
- }
+ }
*pi_mb_address = (i_vert_code - 1)*p_vpar->sequence.i_mb_width;
+ if( *pi_mb_address < i_mb_address_save )
+ {
+ fprintf( stderr, "vpar error: slices do not follow, maybe a PES has been trashed\n" );
+ p_vpar->picture.b_error = 1;
+ return;
+ }
+
/* Reset DC coefficients predictors (ISO/IEC 13818-2 7.2.1). */
p_vpar->mb.pi_dc_dct_pred[0] = p_vpar->mb.pi_dc_dct_pred[1]
= p_vpar->mb.pi_dc_dct_pred[2]
i_chroma_format, i_structure,
b_second_field );
i_mb_address_save = *pi_mb_address;
+ if( p_vpar->picture.b_error )
+ {
+ return;
+ }
}
- while( ShowBits( &p_vpar->bit_stream, 23 ) && !p_vpar->picture.b_error
- && !p_vpar->b_die );
+ while( ShowBits( &p_vpar->bit_stream, 23 ) && !p_vpar->b_die );
NextStartCode( p_vpar );
}
vout_UnlinkPicture( p_vpar->p_vout, p_vpar->sequence.p_forward );
if( p_vpar->sequence.p_backward != NULL )
{
+#ifdef POLUX_SYNCHRO
+ vout_DatePicture( p_vpar->p_vout, p_vpar->sequence.p_backward,
+ vpar_SynchroDate( p_vpar ) );
+#endif
#ifdef SAM_SYNCHRO
vout_DatePicture( p_vpar->p_vout, p_vpar->sequence.p_backward,
vpar_SynchroDate( p_vpar ) );
-#else
+#endif
+#ifdef MEUUH_SYNCHRO
mtime_t date;
date = vpar_SynchroDate( p_vpar );
vout_DatePicture( p_vpar->p_vout, p_vpar->sequence.p_backward,
p_vpar->sequence.p_backward = p_newref;
if( p_newref != NULL )
vout_LinkPicture( p_vpar->p_vout, p_newref );
-#ifndef SAM_SYNCHRO
+#ifdef MEUUH_SYNCHRO
p_vpar->synchro.i_coding_type = i_coding_type;
#endif
}
else if( p_newref != NULL )
{
/* Put date immediately. */
- vout_DatePicture( p_vpar->p_vout, p_newref,
- vpar_SynchroDate( p_vpar ) );
+ vout_DatePicture( p_vpar->p_vout, p_newref, vpar_SynchroDate(p_vpar) );
}
}
RemoveBits( &p_vpar->bit_stream, 10 ); /* temporal_reference */
p_vpar->picture.i_coding_type = GetBits( &p_vpar->bit_stream, 3 );
RemoveBits( &p_vpar->bit_stream, 16 ); /* vbv_delay */
-
+
if( p_vpar->picture.i_coding_type == P_CODING_TYPE
|| p_vpar->picture.i_coding_type == B_CODING_TYPE )
{
p_vpar->picture.i_coding_type, i_structure );
}
}
-
+#ifdef POLUX_SYNCHRO
+ else if( !p_vpar->picture.i_current_structure )
+ {
+ vpar_SynchroTrash( p_vpar, p_vpar->picture.i_coding_type, i_structure );
+ }
+#endif
+
if( !b_parsable )
{
/* Update the reference pointers. */
ReferenceUpdate( p_vpar, p_vpar->picture.i_coding_type, NULL );
-
+#ifndef POLUX_SYNCHRO
/* Warn Synchro we have trashed a picture. */
vpar_SynchroTrash( p_vpar, p_vpar->picture.i_coding_type, i_structure );
-
+#endif
/* Update context. */
if( i_structure != FRAME_STRUCTURE )
p_vpar->picture.i_current_structure = i_structure;
return i_displaydate;
}
-#else
+#endif
+
+#ifdef MEUUH_SYNCHRO
/* synchro a deux balles backportee du decodeur de reference. NE MARCHE PAS
AVEC LES IMAGES MONOTRAMES */
}
#endif
+
+
+#ifdef POLUX_SYNCHRO
+
+void vpar_SynchroSetCurrentDate( 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];
+
+
+ switch( i_coding_type )
+ {
+ case B_CODING_TYPE:
+ if( p_pes->b_has_pts )
+ {
+ if( p_pes->i_pts < p_vpar->synchro.i_current_frame_date )
+ {
+ fprintf( stderr, "vpar warning: pts_date < current_date\n" );
+ }
+ p_vpar->synchro.i_current_frame_date = p_pes->i_pts;
+ p_pes->b_has_pts = 0;
+ }
+ else
+ {
+ p_vpar->synchro.i_current_frame_date += 1000000/(1+p_vpar->sequence.r_frame_rate);
+ }
+ break;
+
+ default:
+
+ if( p_vpar->synchro.i_backward_frame_date == 0 )
+ {
+ p_vpar->synchro.i_current_frame_date += 1000000/(1+p_vpar->sequence.r_frame_rate);
+ }
+ else
+ {
+ if( p_vpar->synchro.i_backward_frame_date < p_vpar->synchro.i_current_frame_date )
+ {
+ fprintf( stderr, "vpar warning: backward_date < current_date (%Ld)\n",
+ p_vpar->synchro.i_backward_frame_date - p_vpar->synchro.i_current_frame_date );
+ }
+ p_vpar->synchro.i_current_frame_date = p_vpar->synchro.i_backward_frame_date;
+ p_vpar->synchro.i_backward_frame_date = 0;
+ }
+
+ if( p_pes->b_has_pts )
+ {
+ p_vpar->synchro.i_backward_frame_date = 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:
+ if( p_vpar->synchro.i_i_count != 0 )
+ {
+ p_vpar->synchro.i_p_nb = p_vpar->synchro.i_p_count;
+ p_vpar->synchro.i_b_nb = p_vpar->synchro.i_b_count;
+ }
+ 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 )
+{
+//fprintf( stderr, "delay : %Ld\n" , mdate() - p_vpar->synchro.i_current_frame_date );
+ 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