video_output.c 74 KB
Newer Older
1
/******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
2
 * video_output.c : video output thread
3
 * (c)2000 VideoLAN
4
 ******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
5 6 7
 * This module describes the programming interface for video output threads.
 * It includes functions allowing to open a new thread, send pictures to a
 * thread, and destroy a previously oppenned video output thread.
8
 ******************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
9

10
/******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
11
 * Preamble
12
 ******************************************************************************/
13
#include <errno.h>
Michel Kaempf's avatar
Michel Kaempf committed
14 15 16
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
Vincent Seguin's avatar
Vincent Seguin committed
17

Michel Kaempf's avatar
Michel Kaempf committed
18 19 20
#include "common.h"
#include "config.h"
#include "mtime.h"
21
#include "vlc_thread.h"
Michel Kaempf's avatar
Michel Kaempf committed
22 23
#include "video.h"
#include "video_output.h"
Vincent Seguin's avatar
Vincent Seguin committed
24
#include "video_text.h"
Vincent Seguin's avatar
Vincent Seguin committed
25
#include "video_sys.h"
26
#include "video_yuv.h"
Vincent Seguin's avatar
Vincent Seguin committed
27
#include "intf_msg.h"
28
#include "main.h"
Vincent Seguin's avatar
Vincent Seguin committed
29

30
/******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
31
 * Local prototypes
32
 ******************************************************************************/
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
static int      InitThread        ( vout_thread_t *p_vout );
static void     RunThread         ( vout_thread_t *p_vout );
static void     ErrorThread       ( vout_thread_t *p_vout );
static void     EndThread         ( vout_thread_t *p_vout );
static void     DestroyThread     ( vout_thread_t *p_vout, int i_status );
static void     Print             ( vout_thread_t *p_vout, int i_x, int i_y, 
                                    int i_h_align, int i_v_align,
                                    unsigned char *psz_text );
static void     SetBufferArea     ( vout_thread_t *p_vout, int i_x, int i_y,
                                    int i_w, int i_h );
static void     SetBufferPicture  ( vout_thread_t *p_vout, picture_t *p_pic );
static void     RenderPicture     ( vout_thread_t *p_vout, picture_t *p_pic );
static void     RenderPictureInfo ( vout_thread_t *p_vout, picture_t *p_pic );
static void     RenderSubPicture  ( vout_thread_t *p_vout,
                                    subpicture_t *p_subpic );
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 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,
                                    int i_h_align, int i_v_align );
Michel Kaempf's avatar
Michel Kaempf committed
55

56
/******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
57
 * vout_CreateThread: creates a new video output thread
58
 ******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
59 60 61
 * This function creates a new video output thread, and returns a pointer
 * to its description. On error, it returns NULL.
 * If pi_status is NULL, then the function will block until the thread is ready.
62
 * If not, it will be updated using one of the THREAD_* constants.
63
 ******************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
64 65
vout_thread_t * vout_CreateThread               ( char *psz_display, int i_root_window, 
                                                  int i_width, int i_height, int *pi_status )
Michel Kaempf's avatar
Michel Kaempf committed
66
{
67 68
    vout_thread_t * p_vout;                              /* thread descriptor */
    int             i_status;                                /* thread status */
69
    int             i_index;                /* index for array initialization */
Michel Kaempf's avatar
Michel Kaempf committed
70

71
    /* Allocate descriptor */
72
    intf_DbgMsg("\n");    
Michel Kaempf's avatar
Michel Kaempf committed
73
    p_vout = (vout_thread_t *) malloc( sizeof(vout_thread_t) );
74
    if( p_vout == NULL )
Michel Kaempf's avatar
Michel Kaempf committed
75
    {
76
        intf_ErrMsg("error: %s\n", strerror(ENOMEM));        
Michel Kaempf's avatar
Michel Kaempf committed
77 78
        return( NULL );
    }
79

80 81
    /* Initialize thread properties - thread id and locks will be initialized 
     * later */
82 83 84 85 86 87
    p_vout->b_die               = 0;
    p_vout->b_error             = 0;    
    p_vout->b_active            = 0;
    p_vout->pi_status           = (pi_status != NULL) ? pi_status : &i_status;
    *p_vout->pi_status          = THREAD_CREATE;    

88
    /* Initialize some fields used by the system-dependant method - these fields will
89
     * probably be modified by the method, and are only preferences */
90
    p_vout->i_changes           = 0;
91 92
    p_vout->i_width             = i_width;
    p_vout->i_height            = i_height;
93
    p_vout->i_bytes_per_line    = i_width * 2;    
94 95
    p_vout->i_screen_depth      = 15;
    p_vout->i_bytes_per_pixel   = 2;
96
    p_vout->f_gamma             = VOUT_GAMMA;    
97 98 99

    p_vout->b_grayscale         = main_GetIntVariable( VOUT_GRAYSCALE_VAR, 
                                                       VOUT_GRAYSCALE_DEFAULT );
Vincent Seguin's avatar
Vincent Seguin committed
100
    p_vout->b_info              = 0;    
101 102 103
    p_vout->b_interface         = 0;
    p_vout->b_scale             = 0;
    
Vincent Seguin's avatar
Vincent Seguin committed
104
    intf_DbgMsg("wished configuration: %dx%d,%d (%d bytes/pixel, %d bytes/line)\n",
105
                p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
Vincent Seguin's avatar
Vincent Seguin committed
106
                p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line );
107

108 109 110 111
    /* Initialize idle screen */
    p_vout->last_display_date   = mdate();
    p_vout->last_idle_date      = 0;

112 113 114 115 116 117 118 119 120
#ifdef STATS
    /* Initialize statistics fields */
    p_vout->render_time         = 0;    
    p_vout->c_fps_samples       = 0;    
#endif      

    /* Initialize buffer index */
    p_vout->i_buffer_index      = 0;

121
    /* Initialize pictures and subpictures - translation tables and functions
122 123 124
     * will be initialized later in InitThread */    
    for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++)
    {
125 126 127 128
        p_vout->p_picture[i_index].i_type   =   EMPTY_PICTURE;
        p_vout->p_picture[i_index].i_status =   FREE_PICTURE;
        p_vout->p_subpicture[i_index].i_type  = EMPTY_SUBPICTURE;
        p_vout->p_subpicture[i_index].i_status= FREE_SUBPICTURE;
129
    }
130 131 132
   
    /* Create and initialize system-dependant method - this function issues its
     * own error messages */
Vincent Seguin's avatar
Vincent Seguin committed
133
    if( vout_SysCreate( p_vout, psz_display, i_root_window ) )
Michel Kaempf's avatar
Michel Kaempf committed
134
    {
Vincent Seguin's avatar
Vincent Seguin committed
135 136
      free( p_vout );
      return( NULL );
Michel Kaempf's avatar
Michel Kaempf committed
137
    }
Vincent Seguin's avatar
Vincent Seguin committed
138
    intf_DbgMsg("actual configuration: %dx%d,%d (%d bytes/pixel, %d bytes/line)\n",
139
                p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
Vincent Seguin's avatar
Vincent Seguin committed
140
                p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line );
Vincent Seguin's avatar
Vincent Seguin committed
141

142 143
    /* Load fonts - fonts must be initialized after the systme method since
     * they may be dependant of screen depth and other thread properties */
Vincent Seguin's avatar
Vincent Seguin committed
144 145 146 147 148 149 150 151 152 153 154 155 156 157
    p_vout->p_default_font      = vout_LoadFont( VOUT_DEFAULT_FONT );    
    if( p_vout->p_default_font == NULL )
    {
        vout_SysDestroy( p_vout );        
        free( p_vout );        
        return( NULL );        
    }    
    p_vout->p_large_font        = vout_LoadFont( VOUT_LARGE_FONT );        
    if( p_vout->p_large_font == NULL )
    {
        vout_UnloadFont( p_vout->p_default_font );        
        vout_SysDestroy( p_vout );        
        free( p_vout );        
        return( NULL );        
158
    }     
Vincent Seguin's avatar
Vincent Seguin committed
159

Michel Kaempf's avatar
Michel Kaempf committed
160
    /* Create thread and set locks */
Vincent Seguin's avatar
Vincent Seguin committed
161
    vlc_mutex_init( &p_vout->picture_lock );
162
    vlc_mutex_init( &p_vout->subpicture_lock );    
Vincent Seguin's avatar
Vincent Seguin committed
163 164 165
    vlc_mutex_init( &p_vout->change_lock );    
    vlc_mutex_lock( &p_vout->change_lock );    
    if( vlc_thread_create( &p_vout->thread_id, "video output", (void *) RunThread, (void *) p_vout) )
Michel Kaempf's avatar
Michel Kaempf committed
166
    {
167
        intf_ErrMsg("error: %s\n", strerror(ENOMEM));
Vincent Seguin's avatar
Vincent Seguin committed
168 169
        vout_UnloadFont( p_vout->p_default_font );
        vout_UnloadFont( p_vout->p_large_font );        
Vincent Seguin's avatar
Vincent Seguin committed
170
	vout_SysDestroy( p_vout );
Michel Kaempf's avatar
Michel Kaempf committed
171 172 173 174
        free( p_vout );
        return( NULL );
    }   

175
    intf_Msg("Video display initialized (%dx%d, %d bpp)\n", 
176 177
             p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth );    

Michel Kaempf's avatar
Michel Kaempf committed
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
    /* If status is NULL, wait until the thread is created */
    if( pi_status == NULL )
    {
        do
        {            
            msleep( THREAD_SLEEP );
        }while( (i_status != THREAD_READY) && (i_status != THREAD_ERROR) 
                && (i_status != THREAD_FATAL) );
        if( i_status != THREAD_READY )
        {
            return( NULL );            
        }        
    }
    return( p_vout );
}

194
/******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
195
 * vout_DestroyThread: destroys a previously created thread
196
 ******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
197 198 199 200
 * Destroy a terminated thread. 
 * The function will request a destruction of the specified thread. If pi_error
 * is NULL, it will return once the thread is destroyed. Else, it will be 
 * update using one of the THREAD_* constants.
201
 ******************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
202 203
void vout_DestroyThread( vout_thread_t *p_vout, int *pi_status )
{  
204
    int     i_status;                                        /* thread status */
Michel Kaempf's avatar
Michel Kaempf committed
205 206

    /* Set status */
207
    intf_DbgMsg("\n");
Michel Kaempf's avatar
Michel Kaempf committed
208
    p_vout->pi_status = (pi_status != NULL) ? pi_status : &i_status;
Vincent Seguin's avatar
Vincent Seguin committed
209
    *p_vout->pi_status = THREAD_DESTROY;    
Michel Kaempf's avatar
Michel Kaempf committed
210 211 212 213 214
     
    /* Request thread destruction */
    p_vout->b_die = 1;

    /* If status is NULL, wait until thread has been destroyed */
215
    if( pi_status == NULL )
Michel Kaempf's avatar
Michel Kaempf committed
216 217 218 219 220 221 222 223 224
    {
        do
        {
            msleep( THREAD_SLEEP );
        }while( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR) 
                && (i_status != THREAD_FATAL) );   
    }
}

225
/******************************************************************************
226
 * vout_DisplaySubPicture: display a subpicture unit
227
 ******************************************************************************
228 229
 * Remove the reservation flag of an subpicture, which will cause it to be ready 
 * for display. The picture does not need to be locked, since it is ignored by
Vincent Seguin's avatar
Vincent Seguin committed
230
 * the output thread if is reserved.
231
 ******************************************************************************/
232
void  vout_DisplaySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
Vincent Seguin's avatar
Vincent Seguin committed
233 234
{
#ifdef DEBUG_VIDEO
235 236
    char        psz_begin_date[MSTRTIME_MAX_SIZE];  /* buffer for date string */
    char        psz_end_date[MSTRTIME_MAX_SIZE];    /* buffer for date string */
Vincent Seguin's avatar
Vincent Seguin committed
237 238 239 240
#endif

#ifdef DEBUG
    /* Check if status is valid */
241
    if( p_subpic->i_status != RESERVED_SUBPICTURE )
Vincent Seguin's avatar
Vincent Seguin committed
242
    {
243 244
        intf_DbgMsg("error: subpicture %p has invalid status %d\n", p_subpic, 
                    p_subpic->i_status );       
Vincent Seguin's avatar
Vincent Seguin committed
245 246 247 248
    }   
#endif

    /* Remove reservation flag */
249
    p_subpic->i_status = READY_SUBPICTURE;
Vincent Seguin's avatar
Vincent Seguin committed
250 251

#ifdef DEBUG_VIDEO
252
    /* Send subpicture informations */
253 254 255 256
    intf_DbgMsg("subpicture %p: type=%d, begin date=%s, end date=%s\n", 
                p_subpic, p_subpic->i_type, 
                mstrtime( psz_begin_date, p_subpic->begin_date ), 
                mstrtime( psz_end_date, p_subpic->end_date ) );    
Vincent Seguin's avatar
Vincent Seguin committed
257 258 259
#endif
}

260
/******************************************************************************
261
 * vout_CreateSubPicture: allocate an subpicture in the video output heap.
262
 ******************************************************************************
263
 * This function create a reserved subpicture in the video output heap. 
Vincent Seguin's avatar
Vincent Seguin committed
264
 * A null pointer is returned if the function fails. This method provides an
265
 * already allocated zone of memory in the spu data fields. It needs locking
Vincent Seguin's avatar
Vincent Seguin committed
266
 * since several pictures can be created by several producers threads. 
267
 ******************************************************************************/
268 269
subpicture_t *vout_CreateSubPicture( vout_thread_t *p_vout, int i_type, 
                                     int i_size )
Vincent Seguin's avatar
Vincent Seguin committed
270
{
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
    int                 i_subpic;                          /* subpicture index */
    subpicture_t *      p_free_subpic = NULL;         /* first free subpicture */    
    subpicture_t *      p_destroyed_subpic = NULL;   /* first destroyed subpic */

    /* Get lock */
    vlc_mutex_lock( &p_vout->subpicture_lock );

    /* 
     * Look for an empty place 
     */
    for( i_subpic = 0; i_subpic < VOUT_MAX_PICTURES; i_subpic++ )
    {
	if( p_vout->p_subpicture[i_subpic].i_status == DESTROYED_SUBPICTURE )
	{
	    /* Subpicture is marked for destruction, but is still allocated */
	    if( (p_vout->p_subpicture[i_subpic].i_type  == i_type)   &&
		(p_vout->p_subpicture[i_subpic].i_size  >= i_size) )
	    {
		/* Memory size do match or is smaller : memory will not be reallocated, 
                 * and function can end immediately - this is the best possible case, 
                 * since no memory allocation needs to be done */
		p_vout->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE;
#ifdef DEBUG_VIDEO
                intf_DbgMsg("subpicture %p (in destroyed subpicture slot)\n", 
                            &p_vout->p_subpicture[i_subpic] );                
#endif
		vlc_mutex_unlock( &p_vout->subpicture_lock );
		return( &p_vout->p_subpicture[i_subpic] );
	    }
	    else if( p_destroyed_subpic == NULL )
	    {
		/* Memory size do not match, but subpicture index will be kept in
		 * case no other place are left */
		p_destroyed_subpic = &p_vout->p_subpicture[i_subpic];                
	    }	    
	}
        else if( (p_free_subpic == NULL) && 
                 (p_vout->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE ))
        {
	    /* Subpicture is empty and ready for allocation */
            p_free_subpic = &p_vout->p_subpicture[i_subpic];
        }
    }

    /* If no free subpicture is available, use a destroyed subpicture */
    if( (p_free_subpic == NULL) && (p_destroyed_subpic != NULL ) )
    { 
	/* No free subpicture or matching destroyed subpicture has been found, but
	 * a destroyed subpicture is still avalaible */
        free( p_destroyed_subpic->p_data );        
        p_free_subpic = p_destroyed_subpic;
    }

    /*
     * Prepare subpicture
     */
    if( p_free_subpic != NULL )
    {
        /* Allocate memory */
        switch( i_type )
        {
        case TEXT_SUBPICTURE:                               /* text subpicture */
            p_free_subpic->p_data = malloc( i_size + 1 );             
            break;
#ifdef DEBUG
        default:
            intf_DbgMsg("error: unknown subpicture type %d\n", i_type );
            p_free_subpic->p_data   =  NULL;            
            break;            
#endif    
        }

        if( p_free_subpic->p_data != NULL )
        {                    /* Copy subpicture informations, set some default values */
            p_free_subpic->i_type                      = i_type;
            p_free_subpic->i_status                    = RESERVED_SUBPICTURE;
            p_free_subpic->i_size                      = i_size;            
            p_free_subpic->i_x                         = 0;
            p_free_subpic->i_y                         = 0;             
            p_free_subpic->i_width                     = 0;
            p_free_subpic->i_height                    = 0;
            p_free_subpic->i_horizontal_align          = CENTER_RALIGN;             
            p_free_subpic->i_vertical_align            = CENTER_RALIGN;             
        }
        else
        {
            /* Memory allocation failed : set subpicture as empty */
            p_free_subpic->i_type   =  EMPTY_SUBPICTURE;            
            p_free_subpic->i_status =  FREE_SUBPICTURE;            
            p_free_subpic =            NULL;            
            intf_ErrMsg("warning: %s\n", strerror( ENOMEM ) );            
        }
        
#ifdef DEBUG_VIDEO
        intf_DbgMsg("subpicture %p (in free subpicture slot)\n", p_free_subpic );        
#endif
        vlc_mutex_unlock( &p_vout->subpicture_lock );
        return( p_free_subpic );
    }
    
    /* No free or destroyed subpicture could be found */
    intf_DbgMsg( "warning: heap is full\n" );
    vlc_mutex_unlock( &p_vout->subpicture_lock );
    return( NULL );
Vincent Seguin's avatar
Vincent Seguin committed
375 376
}

377
/******************************************************************************
378
 * vout_DestroySubPicture: remove a subpicture from the heap
379
 ******************************************************************************
380
 * This function frees a previously reserved subpicture.
Vincent Seguin's avatar
Vincent Seguin committed
381
 * It is meant to be used when the construction of a picture aborted.
382 383
 * This function does not need locking since reserved subpictures are ignored 
 * by the output thread.
384
 ******************************************************************************/
385
void vout_DestroySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
Vincent Seguin's avatar
Vincent Seguin committed
386 387
{
#ifdef DEBUG
388 389
   /* Check if status is valid */
   if( p_subpic->i_status != RESERVED_SUBPICTURE )
Vincent Seguin's avatar
Vincent Seguin committed
390
   {
391 392
       intf_DbgMsg("error: subpicture %p has invalid status %d\n", 
                   p_subpic, p_subpic->i_status );       
Vincent Seguin's avatar
Vincent Seguin committed
393 394 395
   }   
#endif

396
    p_subpic->i_status = DESTROYED_SUBPICTURE;
Vincent Seguin's avatar
Vincent Seguin committed
397 398

#ifdef DEBUG_VIDEO
399
    intf_DbgMsg("subpicture %p\n", p_subpic);    
Vincent Seguin's avatar
Vincent Seguin committed
400 401 402
#endif
}

Vincent Seguin's avatar
Vincent Seguin committed
403
/*******************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
404
 * vout_DisplayPicture: display a picture
Vincent Seguin's avatar
Vincent Seguin committed
405
 *******************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
406
 * Remove the reservation flag of a picture, which will cause it to be ready for
407 408
 * display. The picture won't be displayed until vout_DatePicture has been 
 * called.
Vincent Seguin's avatar
Vincent Seguin committed
409
 *******************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
410
void  vout_DisplayPicture( vout_thread_t *p_vout, picture_t *p_pic )
Michel Kaempf's avatar
Michel Kaempf committed
411
{
412 413
    vlc_mutex_lock( &p_vout->picture_lock );
    switch( p_pic->i_status )
414
    {
415 416 417 418 419 420 421 422 423 424
    case RESERVED_PICTURE:        
        p_pic->i_status = RESERVED_DISP_PICTURE;
        break;        
    case RESERVED_DATED_PICTURE:
        p_pic->i_status = READY_PICTURE;
        break;        
#ifdef DEBUG
    default:        
        intf_DbgMsg("error: picture %p has invalid status %d\n", p_pic, p_pic->i_status );    
        break;        
425
#endif
426
    }
Michel Kaempf's avatar
Michel Kaempf committed
427

428
#ifdef DEBUG_VIDEO
Vincent Seguin's avatar
Vincent Seguin committed
429
    intf_DbgMsg("picture %p\n", p_pic);
430
#endif
431
    vlc_mutex_unlock( &p_vout->picture_lock );
Michel Kaempf's avatar
Michel Kaempf committed
432 433
}

Vincent Seguin's avatar
Vincent Seguin committed
434
/*******************************************************************************
435
 * vout_DatePicture: date a picture
Vincent Seguin's avatar
Vincent Seguin committed
436
 *******************************************************************************
437
 * Remove the reservation flag of a picture, which will cause it to be ready for
438 439
 * display. The picture won't be displayed until vout_DisplayPicture has been 
 * called.
Vincent Seguin's avatar
Vincent Seguin committed
440
 *******************************************************************************/
441 442
void  vout_DatePicture( vout_thread_t *p_vout, picture_t *p_pic, mtime_t date )
{
Vincent Seguin's avatar
Vincent Seguin committed
443 444 445 446
#ifdef DEBUG_VIDEO
    char        psz_date[MSTRTIME_MAX_SIZE];                           /* date */
#endif

447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
    vlc_mutex_lock( &p_vout->picture_lock );
    p_pic->date = date;    
    switch( p_pic->i_status )
    {
    case RESERVED_PICTURE:        
        p_pic->i_status = RESERVED_DATED_PICTURE;
        break;        
    case RESERVED_DISP_PICTURE:
        p_pic->i_status = READY_PICTURE;
        break;        
#ifdef DEBUG
    default:        
        intf_DbgMsg("error: picture %p has invalid status %d\n", p_pic, p_pic->i_status );    
        break;        
#endif
    }

#ifdef DEBUG_VIDEO
Vincent Seguin's avatar
Vincent Seguin committed
465
    intf_DbgMsg("picture %p, display date: %s\n", p_pic, mstrtime( psz_date, p_pic->date) );
466
#endif
467
    vlc_mutex_unlock( &p_vout->picture_lock );
468 469
}

470
/******************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
471
 * vout_CreatePicture: allocate a picture in the video output heap.
472
 ******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
473
 * This function create a reserved image in the video output heap. 
Vincent Seguin's avatar
Vincent Seguin committed
474
 * A null pointer is returned if the function fails. This method provides an
475
 * already allocated zone of memory in the picture data fields. It needs locking
476
 * since several pictures can be created by several producers threads. 
477
 ******************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
478
picture_t *vout_CreatePicture( vout_thread_t *p_vout, int i_type, 
479
			       int i_width, int i_height )
Michel Kaempf's avatar
Michel Kaempf committed
480
{
481 482 483 484
    int         i_picture;                                   /* picture index */
    int         i_chroma_width = 0;                           /* chroma width */    
    picture_t * p_free_picture = NULL;                  /* first free picture */    
    picture_t * p_destroyed_picture = NULL;        /* first destroyed picture */    
Michel Kaempf's avatar
Michel Kaempf committed
485

Vincent Seguin's avatar
Vincent Seguin committed
486
    /* Get lock */
Vincent Seguin's avatar
Vincent Seguin committed
487
    vlc_mutex_lock( &p_vout->picture_lock );
Michel Kaempf's avatar
Michel Kaempf committed
488

Vincent Seguin's avatar
Vincent Seguin committed
489 490 491
    /* 
     * Look for an empty place 
     */
492
    for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
Vincent Seguin's avatar
Vincent Seguin committed
493 494 495
    {
	if( p_vout->p_picture[i_picture].i_status == DESTROYED_PICTURE )
	{
496 497 498
	    /* Picture is marked for destruction, but is still allocated - note
             * that if width and type are the same for two pictures, chroma_width 
             * should also be the same */
Vincent Seguin's avatar
Vincent Seguin committed
499 500
	    if( (p_vout->p_picture[i_picture].i_type           == i_type)   &&
		(p_vout->p_picture[i_picture].i_height         == i_height) &&
501
		(p_vout->p_picture[i_picture].i_width          == i_width) )
Vincent Seguin's avatar
Vincent Seguin committed
502 503 504 505 506
	    {
		/* Memory size do match : memory will not be reallocated, and function
                 * 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;
507 508 509 510
#ifdef DEBUG_VIDEO
                intf_DbgMsg("picture %p (in destroyed picture slot)\n", 
                            &p_vout->p_picture[i_picture] );                
#endif
Vincent Seguin's avatar
Vincent Seguin committed
511
		vlc_mutex_unlock( &p_vout->picture_lock );
Vincent Seguin's avatar
Vincent Seguin committed
512 513 514 515 516 517 518 519 520 521 522 523 524 525 526
		return( &p_vout->p_picture[i_picture] );
	    }
	    else if( p_destroyed_picture == NULL )
	    {
		/* Memory size do not match, but picture index will be kept in
		 * case no other place are left */
		p_destroyed_picture = &p_vout->p_picture[i_picture];                
	    }	    
	}
        else if( (p_free_picture == NULL) && 
                 (p_vout->p_picture[i_picture].i_status == FREE_PICTURE ))
        {
	    /* Picture is empty and ready for allocation */
            p_free_picture = &p_vout->p_picture[i_picture];            
        }
Michel Kaempf's avatar
Michel Kaempf committed
527 528
    }

Vincent Seguin's avatar
Vincent Seguin committed
529 530 531 532 533 534 535
    /* If no free picture is available, use a destroyed picture */
    if( (p_free_picture == NULL) && (p_destroyed_picture != NULL ) )
    { 
	/* No free picture or matching destroyed picture has been found, but
	 * a destroyed picture is still avalaible */
        free( p_destroyed_picture->p_data );        
        p_free_picture = p_destroyed_picture;        
Michel Kaempf's avatar
Michel Kaempf committed
536 537
    }

Vincent Seguin's avatar
Vincent Seguin committed
538 539 540 541 542 543 544
    /*
     * Prepare picture
     */
    if( p_free_picture != NULL )
    {
        /* Allocate memory */
        switch( i_type )
Michel Kaempf's avatar
Michel Kaempf committed
545
        {
546
        case YUV_420_PICTURE:          /* YUV 420: 1,1/4,1/4 samples per pixel */
Vincent Seguin's avatar
Vincent Seguin committed
547 548
            i_chroma_width = i_width / 2;            
            p_free_picture->p_data = malloc( i_height * i_chroma_width * 3 * sizeof( yuv_data_t ) );
549
            p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
Vincent Seguin's avatar
Vincent Seguin committed
550 551
            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;
552 553
            break;
        case YUV_422_PICTURE:          /* YUV 422: 1,1/2,1/2 samples per pixel */
554 555 556
            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_y = (yuv_data_t *)p_free_picture->p_data;
Vincent Seguin's avatar
Vincent Seguin committed
557 558
            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;
559 560
            break;
        case YUV_444_PICTURE:              /* YUV 444: 1,1,1 samples per pixel */
561 562 563
            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_y = (yuv_data_t *)p_free_picture->p_data;
Vincent Seguin's avatar
Vincent Seguin committed
564 565
            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;
Vincent Seguin's avatar
Vincent Seguin committed
566 567 568
            break;                
#ifdef DEBUG
        default:
569
            intf_DbgMsg("error: unknown picture type %d\n", i_type );
570 571
            p_free_picture->p_data   =  NULL;            
            break;            
Vincent Seguin's avatar
Vincent Seguin committed
572
#endif    
Michel Kaempf's avatar
Michel Kaempf committed
573 574
        }

Vincent Seguin's avatar
Vincent Seguin committed
575 576
        if( p_free_picture->p_data != NULL )
        {        
577
            /* Copy picture informations, set some default values */
578 579
            p_free_picture->i_type                      = i_type;
            p_free_picture->i_status                    = RESERVED_PICTURE;
580
            p_free_picture->i_matrix_coefficients       = 1; 
581 582
            p_free_picture->i_width                     = i_width;
            p_free_picture->i_height                    = i_height;
583 584 585 586 587 588
            p_free_picture->i_chroma_width              = i_chroma_width;            
            p_free_picture->i_display_horizontal_offset = 0;
            p_free_picture->i_display_vertical_offset   = 0;            
            p_free_picture->i_display_width             = i_width;
            p_free_picture->i_display_height            = i_height;
            p_free_picture->i_aspect_ratio              = AR_SQUARE_PICTURE;            
589
            p_free_picture->i_refcount                  = 0;            
Vincent Seguin's avatar
Vincent Seguin committed
590 591 592 593
        }
        else
        {
            /* Memory allocation failed : set picture as empty */
594 595 596 597
            p_free_picture->i_type   =  EMPTY_PICTURE;            
            p_free_picture->i_status =  FREE_PICTURE;            
            p_free_picture =            NULL;            
            intf_ErrMsg("warning: %s\n", strerror( ENOMEM ) );            
Vincent Seguin's avatar
Vincent Seguin committed
598 599
        }
        
600 601 602
#ifdef DEBUG_VIDEO
        intf_DbgMsg("picture %p (in free picture slot)\n", p_free_picture );        
#endif
Vincent Seguin's avatar
Vincent Seguin committed
603
        vlc_mutex_unlock( &p_vout->picture_lock );
Vincent Seguin's avatar
Vincent Seguin committed
604
        return( p_free_picture );
Michel Kaempf's avatar
Michel Kaempf committed
605
    }
Vincent Seguin's avatar
Vincent Seguin committed
606
    
607
    /* No free or destroyed picture could be found */
608
    intf_DbgMsg( "warning: heap is full\n" );
Vincent Seguin's avatar
Vincent Seguin committed
609
    vlc_mutex_unlock( &p_vout->picture_lock );
Vincent Seguin's avatar
Vincent Seguin committed
610
    return( NULL );
Michel Kaempf's avatar
Michel Kaempf committed
611 612
}

613
/******************************************************************************
614
 * vout_DestroyPicture: remove a permanent or reserved picture from the heap
615
 ******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
616
 * This function frees a previously reserved picture or a permanent
Vincent Seguin's avatar
Vincent Seguin committed
617 618
 * 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 !
619 620
 * This function does not need locking since reserved pictures are ignored by
 * the output thread.
621
 ******************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
622
void vout_DestroyPicture( vout_thread_t *p_vout, picture_t *p_pic )
Michel Kaempf's avatar
Michel Kaempf committed
623
{
624
#ifdef DEBUG
625
   /* Check if picture status is valid */
626 627 628
   if( (p_pic->i_status != RESERVED_PICTURE) && 
       (p_pic->i_status != RESERVED_DATED_PICTURE) &&
       (p_pic->i_status != RESERVED_DISP_PICTURE) )
629
   {
Vincent Seguin's avatar
Vincent Seguin committed
630
       intf_DbgMsg("error: picture %p has invalid status %d\n", p_pic, p_pic->i_status );       
631 632 633
   }   
#endif

Vincent Seguin's avatar
Vincent Seguin committed
634
    p_pic->i_status = DESTROYED_PICTURE;
635 636 637 638

#ifdef DEBUG_VIDEO
    intf_DbgMsg("picture %p\n", p_pic);    
#endif
Michel Kaempf's avatar
Michel Kaempf committed
639 640
}

641
/******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
642
 * vout_LinkPicture: increment reference counter of a picture
643
 ******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
644
 * This function increment the reference counter of a picture in the video
645
 * heap. It needs a lock since several producer threads can access the picture.
646
 ******************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
647 648
void vout_LinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
{
Vincent Seguin's avatar
Vincent Seguin committed
649
    vlc_mutex_lock( &p_vout->picture_lock );
Michel Kaempf's avatar
Michel Kaempf committed
650
    p_pic->i_refcount++;
651 652

#ifdef DEBUG_VIDEO
653
    intf_DbgMsg("picture %p refcount=%d\n", p_pic, p_pic->i_refcount );    
654
#endif
655 656

    vlc_mutex_unlock( &p_vout->picture_lock );
Michel Kaempf's avatar
Michel Kaempf committed
657 658
}

659
/******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
660
 * vout_UnlinkPicture: decrement reference counter of a picture
661
 ******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
662
 * This function decrement the reference counter of a picture in the video heap.
663
 ******************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
664 665
void vout_UnlinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
{
Vincent Seguin's avatar
Vincent Seguin committed
666
    vlc_mutex_lock( &p_vout->picture_lock );
Michel Kaempf's avatar
Michel Kaempf committed
667
    p_pic->i_refcount--;
668 669 670 671 672 673 674 675 676

#ifdef DEBUG_VIDEO
    if( p_pic->i_refcount < 0 )
    {
        intf_DbgMsg("error: refcount < 0\n");
        p_pic->i_refcount = 0;        
    }    
#endif

Vincent Seguin's avatar
Vincent Seguin committed
677
    if( (p_pic->i_refcount == 0) && (p_pic->i_status == DISPLAYED_PICTURE) )
Michel Kaempf's avatar
Michel Kaempf committed
678
    {
Vincent Seguin's avatar
Vincent Seguin committed
679
	p_pic->i_status = DESTROYED_PICTURE;
Michel Kaempf's avatar
Michel Kaempf committed
680
    }
681 682

#ifdef DEBUG_VIDEO
683
    intf_DbgMsg("picture %p refcount=%d\n", p_pic, p_pic->i_refcount );    
684
#endif
685 686

    vlc_mutex_unlock( &p_vout->picture_lock );
Michel Kaempf's avatar
Michel Kaempf committed
687 688
}

689
/******************************************************************************
690
 * vout_SetBuffers: set buffers adresses
691
 ******************************************************************************
692 693
 * This function is called by system drivers to set buffers video memory 
 * adresses.
694
 ******************************************************************************/
695
void vout_SetBuffers( vout_thread_t *p_vout, void *p_buf1, void *p_buf2 )
696 697
{
    /* No picture previously */
698 699 700 701 702 703 704 705
    p_vout->p_buffer[0].i_pic_x =         0;
    p_vout->p_buffer[0].i_pic_y =         0;
    p_vout->p_buffer[0].i_pic_width =     0;
    p_vout->p_buffer[0].i_pic_height =    0;
    p_vout->p_buffer[1].i_pic_x =         0;
    p_vout->p_buffer[1].i_pic_y =         0;
    p_vout->p_buffer[1].i_pic_width =     0;
    p_vout->p_buffer[1].i_pic_height =    0;
706 707

    /* The first area covers all the screen */
708 709 710 711 712 713 714 715 716 717
    p_vout->p_buffer[0].i_areas =                 1;
    p_vout->p_buffer[0].pi_area_begin[0] =        0;
    p_vout->p_buffer[0].pi_area_end[0] =          p_vout->i_height - 1;
    p_vout->p_buffer[1].i_areas =                 1;
    p_vout->p_buffer[1].pi_area_begin[0] =        0;
    p_vout->p_buffer[1].pi_area_end[0] =          p_vout->i_height - 1;

    /* Set adresses */
    p_vout->p_buffer[0].p_data = p_buf1;    
    p_vout->p_buffer[1].p_data = p_buf2;    
718 719
}

Michel Kaempf's avatar
Michel Kaempf committed
720 721
/* following functions are local */

722
/******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
723
 * InitThread: initialize video output thread
724
 ******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
725 726 727
 * This function is called from RunThread and performs the second step of the
 * initialization. It returns 0 on success. Note that the thread's flag are not
 * modified inside this function.
728
 ******************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
729 730 731
static int InitThread( vout_thread_t *p_vout )
{
    /* Update status */
732
    intf_DbgMsg("\n");
Michel Kaempf's avatar
Michel Kaempf committed
733 734
    *p_vout->pi_status = THREAD_START;    

735 736
    /* Initialize output method - this function issues its own error messages */
    if( vout_SysInit( p_vout ) )
Michel Kaempf's avatar
Michel Kaempf committed
737 738 739 740
    {
        return( 1 );
    } 

741
    /* Initialize convertion tables and functions */
742
    if( vout_InitYUV( p_vout ) )
743
    {
744
        intf_ErrMsg("error: can't allocate YUV translation tables\n");
745
        return( 1 );                
Vincent Seguin's avatar
Vincent Seguin committed
746 747
    }
    
Michel Kaempf's avatar
Michel Kaempf committed
748
    /* Mark thread as running and return */
749 750
    p_vout->b_active =          1;    
    *p_vout->pi_status =        THREAD_READY;    
751
    intf_DbgMsg("thread ready\n");    
752
    return( 0 );    
Michel Kaempf's avatar
Michel Kaempf committed
753 754
}

755
/******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
756
 * RunThread: video output thread
757
 ******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
758 759 760
 * Video output thread. This function does only returns when the thread is
 * terminated. It handles the pictures arriving in the video heap and the
 * display device events.
761
 ******************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
762 763
static void RunThread( vout_thread_t *p_vout)
{
764
    int             i_index;                                 /* index in heap */
765
    mtime_t         current_date;                             /* current date */
766
    mtime_t         display_date;                             /* display date */    
767 768
    boolean_t       b_display;                                /* display flag */    
    picture_t *     p_pic;                                 /* picture pointer */
769
    subpicture_t *  p_subpic;                           /* subpicture pointer */    
770
     
Michel Kaempf's avatar
Michel Kaempf committed
771
    /* 
772
     * Initialize thread
Michel Kaempf's avatar
Michel Kaempf committed
773 774 775 776
     */
    p_vout->b_error = InitThread( p_vout );
    if( p_vout->b_error )
    {
777
        DestroyThread( p_vout, THREAD_ERROR );
Michel Kaempf's avatar
Michel Kaempf committed
778 779
        return;        
    }    
780
    intf_DbgMsg("\n");
Michel Kaempf's avatar
Michel Kaempf committed
781 782 783 784 785 786

    /*
     * Main loop - it is not executed if an error occured during
     * initialization
     */
    while( (!p_vout->b_die) && (!p_vout->b_error) )
787 788 789
    {
        /* Initialize loop variables */
        p_pic =         NULL;
790
        p_subpic =      NULL;
791 792 793
        display_date =  0;        
        current_date =  mdate();

Vincent Seguin's avatar
Vincent Seguin committed
794
        /* 
795
	 * Find the picture to display - this operation does not need lock,
796
         * since only READY_PICTUREs are handled 
797
         */
798
        for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
Vincent Seguin's avatar
Vincent Seguin committed
799
	{
800
	    if( (p_vout->p_picture[i_index].i_status == READY_PICTURE) &&
Vincent Seguin's avatar
Vincent Seguin committed
801
		( (p_pic == NULL) || 
802
		  (p_vout->p_picture[i_index].date < display_date) ) )
Vincent Seguin's avatar
Vincent Seguin committed
803
	    {
804 805
                p_pic = &p_vout->p_picture[i_index];
                display_date = p_pic->date;                
Vincent Seguin's avatar
Vincent Seguin committed
806 807
	    }
	}
Vincent Seguin's avatar
Vincent Seguin committed
808
 
Vincent Seguin's avatar
Vincent Seguin committed
809
        if( p_pic )
Michel Kaempf's avatar
Michel Kaempf committed
810
        {
811 812
#ifdef STATS
            /* Computes FPS rate */
813
            p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ] = display_date;
814
#endif	    
815
	    if( display_date < current_date )
Vincent Seguin's avatar
Vincent Seguin committed
816
	    {
817 818
		/* Picture is late: it will be destroyed and the thread will sleep and
                 * go to next picture */
Vincent Seguin's avatar
Vincent Seguin committed
819
                vlc_mutex_lock( &p_vout->picture_lock );
820
                p_pic->i_status = p_pic->i_refcount ? DISPLAYED_PICTURE : DESTROYED_PICTURE;
821 822
		intf_DbgMsg( "warning: late picture %p skipped refcount=%d\n", p_pic, p_pic->i_refcount );
                vlc_mutex_unlock( &p_vout->picture_lock );
823
                p_pic =         NULL;                
824
                display_date =  0;                
Vincent Seguin's avatar
Vincent Seguin committed
825
	    }
826
	    else if( display_date > current_date + VOUT_DISPLAY_DELAY )
Vincent Seguin's avatar
Vincent Seguin committed
827 828 829 830
	    {
		/* 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
		 * as if no picture were found. The picture state is unchanged */
831
                p_pic =         NULL;                
832
                display_date =  0;                
Vincent Seguin's avatar
Vincent Seguin committed
833
	    }
Michel Kaempf's avatar
Michel Kaempf committed
834
        }
835 836

        /*
837
         * Find the subpicture to display - this operation does not need lock, since
838 839
         * only READY_SUBPICTURES are handled. If no picture has been selected,
         * display_date will depend on the subpicture
840 841 842
         */
        //??

Vincent Seguin's avatar
Vincent Seguin committed
843 844
        /*
         * Perform rendering, sleep and display rendered picture
845
         */
846
        if( p_pic )                          /* picture and perhaps subpicture */
847
        {
848
            b_display = p_vout->b_active;            
849 850
            p_vout->last_display_date = display_date;
            
851 852 853 854 855 856 857
            if( b_display )
            {                
                /* Set picture dimensions and clear buffer */
                SetBufferPicture( p_vout, p_pic );

                /* Render picture and informations */
                RenderPicture( p_vout, p_pic );             
Vincent Seguin's avatar
Vincent Seguin committed
858 859
                if( p_vout->b_info )
                {
860 861 862
                    RenderPictureInfo( p_vout, p_pic );
                    RenderInfo( p_vout );                
                }
Vincent Seguin's avatar
Vincent Seguin committed
863
            }
864
            
Vincent Seguin's avatar
Vincent Seguin committed
865 866 867 868
            /* Remove picture from heap */
            vlc_mutex_lock( &p_vout->picture_lock );
            p_pic->i_status = p_pic->i_refcount ? DISPLAYED_PICTURE : DESTROYED_PICTURE;
            vlc_mutex_unlock( &p_vout->picture_lock );                          
869

870
            /* Render interface and subpicture */
871 872 873 874
            if( b_display && p_vout->b_interface )
            {
                RenderInterface( p_vout );                
            }
875
            if( p_subpic )
876 877 878
            {
                if( b_display )
                {                    
879
                    RenderSubPicture( p_vout, p_subpic );
880 881
                }                

882 883 884 885
                /* Remove subpicture from heap */
                vlc_mutex_lock( &p_vout->subpicture_lock );
                p_subpic->i_status = DESTROYED_SUBPICTURE;
                vlc_mutex_unlock( &p_vout->subpicture_lock );                          
886 887 888
            }

        }
889
        else if( p_subpic )                                /* subpicture alone */
Vincent Seguin's avatar
Vincent Seguin committed
890
        {
891
            b_display = p_vout->b_active;
892
            p_vout->last_display_date = display_date;            
893 894

            if( b_display )
Vincent Seguin's avatar
Vincent Seguin committed
895
            {                
896 897 898
                /* Clear buffer */
                SetBufferPicture( p_vout, NULL );

899
                /* Render informations, interface and subpicture */
Vincent Seguin's avatar
Vincent Seguin committed
900
                if( p_vout->b_info )
901 902 903 904 905 906 907
                {
                    RenderInfo( p_vout );
                }
                if( p_vout->b_interface )
                {
                    RenderInterface( p_vout );
                }
908
                RenderSubPicture( p_vout, p_subpic );            
Vincent Seguin's avatar
Vincent Seguin committed
909
            }            
910

911 912 913 914
            /* Remove subpicture from heap */
            vlc_mutex_lock( &p_vout->subpicture_lock );
            p_subpic->i_status = DESTROYED_SUBPICTURE;
            vlc_mutex_unlock( &p_vout->subpicture_lock );                          
Vincent Seguin's avatar
Vincent Seguin committed
915
        }
916 917
        else if( p_vout->b_active )          /* idle or interface screen alone */
        {
918
            if( p_vout->b_interface && 0 /* && ?? intf_change */ )
919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941
            {
                /* Interface has changed, so a new rendering is required - force
                 * it by setting last idle date to 0 */
                p_vout->last_idle_date = 0;                
            }

            /* Render idle screen and update idle date, then render interface if
             * required */
            b_display = RenderIdle( p_vout );
            if( b_display )
            {                
                p_vout->last_idle_date = current_date;
                if( p_vout->b_interface )
                {
                    RenderInterface( p_vout );                
                }
            }
            
        }       
        else
        {
            b_display = 0;            
        }        
Vincent Seguin's avatar
Vincent Seguin committed
942

943 944 945 946 947 948 949 950 951
        /*
         * Sleep, wake up and display rendered picture
         */

#ifdef STATS
        /* Store render time */
        p_vout->render_time = mdate() - current_date;
#endif

Vincent Seguin's avatar
Vincent Seguin committed
952 953 954
        /* Give back change lock */
        vlc_mutex_unlock( &p_vout->change_lock );        

Vincent Seguin's avatar
Vincent Seguin committed
955
        /* Sleep a while or until a given date */
956
        if( display_date != 0 )
Vincent Seguin's avatar
Vincent Seguin committed
957
        {
958
            mwait( display_date );