* It includes functions allowing to open a new thread, send pictures to a
* thread, and destroy a previously oppened video output thread.
*****************************************************************************
- * Copyright (C) 2000 VideoLAN
- * $Id: video_output.c,v 1.131 2001/05/31 03:57:54 sam Exp $
+ * Copyright (C) 2000-2001 VideoLAN
+ * $Id: video_output.c,v 1.144 2001/11/28 15:08:06 massiot Exp $
*
* Authors: Vincent Seguin <seguin@via.ecp.fr>
*
#include <stdio.h> /* sprintf() */
#include <string.h> /* strerror() */
-#ifdef STATS
+#ifdef HAVE_SYS_TIMES_H
# include <sys/times.h>
#endif
#include "config.h"
#include "common.h"
+#include "intf_msg.h"
#include "threads.h"
#include "mtime.h"
#include "modules.h"
#include "video_spu.h"
#include "video_yuv.h"
-#include "intf_msg.h"
-
#include "main.h"
/*****************************************************************************
* If pi_status is NULL, then the function will block until the thread is ready.
* If not, it will be updated using one of the THREAD_* constants.
*****************************************************************************/
-vout_thread_t * vout_CreateThread ( int *pi_status )
+vout_thread_t * vout_CreateThread ( int *pi_status, int i_width, int i_height )
{
vout_thread_t * p_vout; /* thread descriptor */
int i_status; /* thread status */
/* Initialize some fields used by the system-dependant method - these
* fields will probably be modified by the method, and are only
* preferences */
- p_vout->i_changes = 0;
- p_vout->i_width = main_GetIntVariable( VOUT_WIDTH_VAR,
- VOUT_WIDTH_DEFAULT );
- p_vout->i_height = main_GetIntVariable( VOUT_HEIGHT_VAR,
- VOUT_HEIGHT_DEFAULT );
- p_vout->i_bytes_per_line = p_vout->i_width * 2;
- p_vout->i_screen_depth = main_GetIntVariable( VOUT_DEPTH_VAR,
+ p_vout->i_changes = 0;
+ p_vout->i_width = main_GetIntVariable( VOUT_WIDTH_VAR, 0 );
+ if( !p_vout->i_width )
+ {
+ p_vout->i_width = i_width ? i_width : VOUT_WIDTH_DEFAULT;
+ }
+ p_vout->i_height = main_GetIntVariable( VOUT_HEIGHT_VAR, 0 );
+ if( !p_vout->i_height )
+ {
+ p_vout->i_height = i_height ? i_height : VOUT_HEIGHT_DEFAULT;
+ }
+ p_vout->i_bytes_per_line = p_vout->i_width * 2;
+ p_vout->i_screen_depth = main_GetIntVariable( VOUT_DEPTH_VAR,
VOUT_DEPTH_DEFAULT );
- p_vout->i_bytes_per_pixel = 2;
- p_vout->f_gamma = VOUT_GAMMA_DEFAULT; // FIXME: replace with
- // variable
- p_vout->b_need_render = 1;
- p_vout->b_YCbr = 0;
-
- p_vout->b_grayscale = main_GetIntVariable( VOUT_GRAYSCALE_VAR,
- VOUT_GRAYSCALE_DEFAULT );
- p_vout->b_info = 0;
- p_vout->b_interface = 0;
- p_vout->b_scale = 1;
- p_vout->b_fullscreen = main_GetIntVariable( VOUT_FULLSCREEN_VAR,
+ p_vout->i_bytes_per_pixel = 2;
+ p_vout->f_gamma = VOUT_GAMMA_DEFAULT; // FIXME: replace with
+ // variable
+ p_vout->b_need_render = 1;
+ p_vout->b_YCbr = 0;
+
+ p_vout->b_grayscale = main_GetIntVariable( VOUT_GRAYSCALE_VAR,
+ VOUT_GRAYSCALE_DEFAULT );
+ p_vout->b_info = 0;
+ p_vout->b_interface = 0;
+ p_vout->b_scale = 1;
+ p_vout->b_fullscreen = main_GetIntVariable( VOUT_FULLSCREEN_VAR,
VOUT_FULLSCREEN_DEFAULT );
- intf_WarnMsg( 3, "wished configuration: %dx%d, %d/%d bpp (%d Bpl)",
+ intf_WarnMsg( 3, "vout info: asking for %dx%d, %d/%d bpp (%d Bpl)",
p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
p_vout->i_bytes_per_pixel * 8, p_vout->i_bytes_per_line );
/* Initialize statistics fields */
p_vout->c_fps_samples = 0;
+ p_vout->c_pictures = 0;
+ p_vout->c_late_pictures = 0;
+ p_vout->c_jitter_samples = 0;
+ p_vout->display_jitter = 0;
+ p_vout->c_loops = 0;
/* Initialize buffer index */
p_vout->i_buffer_index = 0;
+ /* Initialize fonts */
+ p_vout->p_default_font = NULL;
+ p_vout->p_large_font = NULL;
+
/* Initialize pictures and subpictures - translation tables and functions
* will be initialized later in InitThread */
for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++)
(void *) RunThread, (void *) p_vout) )
{
intf_ErrMsg("vout error: %s", strerror(ENOMEM));
- vout_UnloadFont( p_vout->p_default_font );
- vout_UnloadFont( p_vout->p_large_font );
- p_vout->pf_destroy( p_vout );
+ module_Unneed( p_vout->p_module );
free( p_vout );
return( NULL );
}
- intf_WarnMsg( 1, "vout: video display initialized (%dx%d, %d/%d bpp)",
- p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
- p_vout->i_bytes_per_pixel * 8 );
-
/* If status is NULL, wait until the thread is created */
if( pi_status == NULL )
{
do
{
msleep( THREAD_SLEEP );
- }while( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR)
- && (i_status != THREAD_FATAL) );
+ } while( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR)
+ && (i_status != THREAD_FATAL) );
}
}
switch( i_type )
{
case TEXT_SUBPICTURE: /* text subpicture */
- p_free_subpic->p_data = malloc( i_size + 1 );
+ p_free_subpic->p_data = memalign( 16, i_size + 1 );
break;
case DVD_SUBPICTURE: /* DVD subpicture unit */
- p_free_subpic->p_data = malloc( i_size );
+ p_free_subpic->p_data = memalign( 16, i_size );
break;
#ifdef DEBUG
default:
{
case YUV_420_PICTURE: /* YUV 420: 1,1/4,1/4 samples per pixel */
i_chroma_width = i_width / 2;
- p_free_picture->p_data = malloc( i_height * i_chroma_width * 3 * sizeof( yuv_data_t ) );
+ p_free_picture->p_data = memalign( 16, i_height * i_chroma_width
+ * 3 * sizeof( yuv_data_t ) );
p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*4/2;
p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*5/2;
break;
case YUV_422_PICTURE: /* YUV 422: 1,1/2,1/2 samples per pixel */
i_chroma_width = i_width / 2;
- p_free_picture->p_data = malloc( i_height * i_chroma_width * 4 * sizeof( yuv_data_t ) );
+ p_free_picture->p_data = memalign( 16, i_height * i_chroma_width
+ * 4 * sizeof( yuv_data_t ) );
p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*2;
p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*3;
break;
case YUV_444_PICTURE: /* YUV 444: 1,1,1 samples per pixel */
i_chroma_width = i_width;
- p_free_picture->p_data = malloc( i_height * i_chroma_width * 3 * sizeof( yuv_data_t ) );
+ p_free_picture->p_data = memalign( 16, i_height * i_chroma_width
+ * 3 * sizeof( yuv_data_t ) );
p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width;
p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*2;
#endif
vlc_mutex_unlock( &p_vout->picture_lock );
- /* Initialize mutex */
- vlc_mutex_init( &(p_free_picture->lock_deccount) );
-
return( p_free_picture );
}
intf_DbgMsg("picture %p", p_pic);
#endif
- /* destroy the lock that had been initialized in CreatePicture */
- vlc_mutex_destroy( &(p_pic->lock_deccount) );
-
vlc_mutex_unlock( &p_vout->picture_lock );
}
vlc_mutex_lock( &p_vout->change_lock );
-#ifdef STATS
- p_vout->c_loops = 0;
-#endif
-
/* Create and initialize system-dependant method - this function issues its
* own error messages */
if( p_vout->pf_create( p_vout ) )
{
- module_Unneed( p_vout->p_module );
- free( p_vout );
+ /* If pf_create has failed then we have to make sure
+ * pf_destroy won't be called, because the plugin should have
+ * cleaned up all its mess */
+ p_vout->pf_destroy = NULL;
return( 1 );
}
- intf_WarnMsg( 3, "actual configuration: %dx%d, %d/%d bpp (%d Bpl), "
+ intf_WarnMsg( 1, "vout: video display initialized (%dx%d, %d/%d bpp)",
+ p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
+ p_vout->i_bytes_per_pixel * 8 );
+
+ intf_WarnMsg( 3, "vout info: got %dx%d, %d/%d bpp (%d Bpl), "
"masks: 0x%x/0x%x/0x%x",
p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
p_vout->i_bytes_per_pixel * 8, p_vout->i_bytes_per_line,
intf_ErrMsg( "vout error: could not load large font" );
}
- /* Initialize output method - this function issues its own error messages */
+ /* Initialize output method. This function issues its own error messages */
if( p_vout->pf_init( p_vout ) )
{
+ /* If pf_init has failed then we have to make sure
+ * pf_destroy won't be called, because the plugin should have
+ * cleaned up all its mess */
+ p_vout->pf_destroy = NULL;
return( 1 );
}
if( vout_InitYUV( p_vout ) )
{
intf_ErrMsg("vout error: can't allocate YUV translation tables");
+ p_vout->pf_destroy( p_vout );
+ /* Make sure pf_destroy won't be called again */
+ p_vout->pf_destroy = NULL;
return( 1 );
}
/*
* Initialize thread
*/
- p_vout->b_error = InitThread( p_vout );
- if( p_vout->b_error )
+ if( InitThread( p_vout ) )
{
+ /* Something bad happened */
DestroyThread( p_vout, THREAD_ERROR );
return;
}
-
/*
* Main loop - it is not executed if an error occured during
* initialization
while( (!p_vout->b_die) && (!p_vout->b_error) )
{
/* Initialize loop variables */
+ p_vout->p_rendered_pic = NULL;
p_pic = NULL;
p_subpic = NULL;
p_ephemer = NULL;
ephemer_date = 0;
display_date = 0;
current_date = mdate();
-#ifdef STATS
+
p_vout->c_loops++;
if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
{
- intf_Msg("vout stats: picture heap: %d/%d",
- p_vout->i_pictures, VOUT_MAX_PICTURES);
+ intf_StatMsg( "vout info: picture heap: %d/%d",
+ p_vout->i_pictures, VOUT_MAX_PICTURES );
}
-#endif
/*
* Find the picture to display - this operation does not need lock,
if( p_pic )
{
+ p_vout->c_pictures++;
+
/* Computes FPS rate */
p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ] = display_date;
- if( display_date < current_date - p_vout->render_time )
+ if( display_date < current_date + p_vout->render_time )
{
/* Picture is late: it will be destroyed and the thread
* will sleep and go to next picture */
p_pic->i_status = DESTROYED_PICTURE;
p_vout->i_pictures--;
}
- intf_WarnMsg( 1,
- "warning: late picture skipped (%p)", p_pic );
vlc_mutex_unlock( &p_vout->picture_lock );
+ intf_WarnMsg( 1,
+ "vout warning: late picture skipped (%p)", p_pic );
+ p_vout->c_late_pictures++;
+
continue;
}
else if( display_date > current_date + VOUT_DISPLAY_DELAY )
p_vout->last_display_date = display_date;
p_vout->p_rendered_pic = p_pic;
- /* Set picture dimensions and clear buffer */
- SetBufferPicture( p_vout, p_pic );
-
/* FIXME: if b_need_render == 0 we need to do something with
* the subpictures one day. */
if( p_vout->b_need_render && b_display )
{
+ /* Set picture dimensions and clear buffer */
+ SetBufferPicture( p_vout, p_pic );
+
/* Render picture and information */
RenderPicture( p_vout, p_pic );
if( p_vout->b_info )
#endif
if( b_display /* && !(p_vout->i_changes & VOUT_NODISPLAY_CHANGE) */ )
{
+ mtime_t jitter;
+
p_vout->pf_display( p_vout );
#ifndef SYS_BEOS
p_vout->i_buffer_index = ++p_vout->i_buffer_index & 1;
#endif
+
+ /* Update statistics */
+ jitter = display_date - mdate();
+ if( jitter < 0 ) jitter = -jitter;
+ p_vout->display_jitter = ((p_vout->display_jitter
+ * p_vout->c_jitter_samples) + jitter)
+ / (p_vout->c_jitter_samples + 1);
+ if( p_vout->c_jitter_samples < MAX_JITTER_SAMPLES )
+ {
+ p_vout->c_jitter_samples++;
+ }
}
if( p_pic )
*/
if( p_vout->pf_manage( p_vout ) | Manage( p_vout ) )
{
- /* A fatal error occured, and the thread must terminate immediately,
+ /* A fatal error occured, and the thread must terminate immediately
* without displaying anything - setting b_error to 1 cause the
* immediate end of the main while() loop. */
p_vout->b_error = 1;
/* Store status */
*p_vout->pi_status = THREAD_END;
-#ifdef STATS
+ if( p_main->b_stats )
{
+#ifdef HAVE_SYS_TIMES_H
struct tms cpu_usage;
times( &cpu_usage );
- intf_Msg( "vout stats: cpu usage (user: %d, system: %d)",
- cpu_usage.tms_utime, cpu_usage.tms_stime );
- }
+ intf_StatMsg( "vout info: %d loops consuming user: %d, system: %d",
+ p_vout->c_loops, cpu_usage.tms_utime, cpu_usage.tms_stime );
+#else
+ intf_StatMsg( "vout info: %d loops", p_vout->c_loops );
#endif
+ intf_StatMsg( "vout info: %d pictures received, discarded %d",
+ p_vout->c_pictures, p_vout->c_late_pictures );
+ intf_StatMsg( "vout info: average display jitter of %lld µs",
+ p_vout->display_jitter );
+ }
+
/* Destroy all remaining pictures and subpictures */
for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
{
/* Destroy thread structures allocated by Create and InitThread */
vout_UnloadFont( p_vout->p_default_font );
vout_UnloadFont( p_vout->p_large_font );
- p_vout->pf_destroy( p_vout );
+ if( p_vout->pf_destroy != NULL ) p_vout->pf_destroy( p_vout );
/* Destroy the locks */
vlc_mutex_destroy( &p_vout->picture_lock );
(long) p_vout->c_fps_samples, (long) p_vout->render_time );
Print( p_vout, 0, 0, LEFT_RALIGN, TOP_RALIGN, psz_buffer );
-#ifdef STATS
- /*
- * Print picture information in lower right corner
- */
- sprintf( psz_buffer, "%s picture %dx%d (%dx%d%+d%+d %s) -> %dx%d+%d+%d",
+ if( p_main->b_stats )
+ {
+ /*
+ * Print picture information in lower right corner
+ */
+ sprintf( psz_buffer, "%s picture %dx%d (%dx%d%+d%+d %s) -> %dx%d+%d+%d",
(p_pic->i_type == YUV_420_PICTURE) ? "4:2:0" :
((p_pic->i_type == YUV_422_PICTURE) ? "4:2:2" :
((p_pic->i_type == YUV_444_PICTURE) ? "4:4:4" : "ukn-type")),
p_vout->p_buffer[ p_vout->i_buffer_index ].i_pic_height,
p_vout->p_buffer[ p_vout->i_buffer_index ].i_pic_x,
p_vout->p_buffer[ p_vout->i_buffer_index ].i_pic_y );
- Print( p_vout, 0, 0, RIGHT_RALIGN, BOTTOM_RALIGN, psz_buffer );
-#endif
+ Print( p_vout, 0, 0, RIGHT_RALIGN, BOTTOM_RALIGN, psz_buffer );
+ }
}
/*****************************************************************************
switch( p_subpic->i_type )
{
case DVD_SUBPICTURE: /* DVD subpicture unit */
- vout_RenderRGBSPU( p_pic, p_subpic,
- &p_vout->p_buffer[ p_vout->i_buffer_index ],
- p_vout->i_bytes_per_pixel,
- p_vout->i_bytes_per_line );
- /* vout_RenderYUVSPU( p_pic, p_subpic ); */
+ if( p_vout->b_need_render )
+ {
+ vout_RenderRGBSPU( p_pic, p_subpic,
+ &p_vout->p_buffer[ p_vout->i_buffer_index ],
+ p_vout->i_bytes_per_pixel,
+ p_vout->i_bytes_per_line );
+ }
+ else
+ {
+ vout_RenderYUVSPU( p_pic, p_subpic );
+ }
break;
case TEXT_SUBPICTURE: /* single line text */