video_output.c 34.6 KB
Newer Older
Michel Kaempf's avatar
Michel Kaempf committed
1 2
/*******************************************************************************
 * video_output.c : video output thread
3
 * (c)2000 VideoLAN
Michel Kaempf's avatar
Michel Kaempf committed
4 5 6 7 8 9 10 11 12
 *******************************************************************************
 * 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.
 *******************************************************************************/

/*******************************************************************************
 * Preamble
 *******************************************************************************/
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 18

#ifdef VIDEO_X11
19
#include <X11/Xlib.h>                           /* for video_sys.h in X11 mode */
Vincent Seguin's avatar
Vincent Seguin committed
20
#endif
Michel Kaempf's avatar
Michel Kaempf committed
21 22 23 24

#include "common.h"
#include "config.h"
#include "mtime.h"
25
#include "vlc_thread.h"
Michel Kaempf's avatar
Michel Kaempf committed
26 27
#include "video.h"
#include "video_output.h"
Vincent Seguin's avatar
Vincent Seguin committed
28
#include "video_sys.h"
29
#include "video_yuv.h"
Vincent Seguin's avatar
Vincent Seguin committed
30
#include "intf_msg.h"
31
#include "main.h"
Vincent Seguin's avatar
Vincent Seguin committed
32 33

/*******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
34
 * Local prototypes
Vincent Seguin's avatar
Vincent Seguin committed
35
 *******************************************************************************/
36 37 38 39 40
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     RenderPicture           ( vout_thread_t *p_vout, picture_t *p_pic );
41
static void     RenderPictureInfo       ( vout_thread_t *p_vout, picture_t *p_pic );
42
static int      RenderIdle              ( vout_thread_t *p_vout, int i_level );
Michel Kaempf's avatar
Michel Kaempf committed
43 44 45 46 47 48 49

/*******************************************************************************
 * vout_CreateThread: creates a new video output thread
 *******************************************************************************
 * 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.
50
 * If not, it will be updated using one of the THREAD_* constants.
Michel Kaempf's avatar
Michel Kaempf committed
51
 *******************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
52
vout_thread_t * vout_CreateThread               ( 
53
#ifdef VIDEO_X11
54
                                                  char *psz_display, Window root_window, 
Vincent Seguin's avatar
Vincent Seguin committed
55 56 57
#endif
                                                  int i_width, int i_height, int *pi_status 
                                                )
Michel Kaempf's avatar
Michel Kaempf committed
58 59 60 61
{
    vout_thread_t * p_vout;                               /* thread descriptor */
    int             i_status;                                 /* thread status */

62
    /* Allocate descriptor */
Michel Kaempf's avatar
Michel Kaempf committed
63
    p_vout = (vout_thread_t *) malloc( sizeof(vout_thread_t) );
64
    if( p_vout == NULL )
Michel Kaempf's avatar
Michel Kaempf committed
65
    {
66
        intf_ErrMsg("error: %s\n", strerror(ENOMEM));        
Michel Kaempf's avatar
Michel Kaempf committed
67 68
        return( NULL );
    }
69

70 71 72 73 74 75 76
    /* Initialize thread properties */
    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;    

77
    /* Initialize some fields used by the system-dependant method - these fields will
78
     * probably be modified by the method, and are only preferences */
79 80 81
#ifdef DEBUG
    p_vout->b_info              = 1;    
#else
82
    p_vout->b_info              = 0;    
83
#endif
84 85
    p_vout->b_grayscale         = main_GetIntVariable( VOUT_GRAYSCALE_VAR, 
                                                       VOUT_GRAYSCALE_DEFAULT );
86 87
    p_vout->i_width             = i_width;
    p_vout->i_height            = i_height;
88
    p_vout->i_bytes_per_line    = i_width * 2;    
89 90 91 92
    p_vout->i_screen_depth      = 15;
    p_vout->i_bytes_per_pixel   = 2;
    p_vout->f_x_ratio           = 1;
    p_vout->f_y_ratio           = 1;
93
    p_vout->f_gamma             = VOUT_GAMMA;    
94
    intf_DbgMsg("wished configuration: %dx%d,%d (%d bytes/pixel, %d bytes/line), ratio %.2f:%.2f, gray=%d\n",
95
                p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
96 97
                p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line,
                p_vout->f_x_ratio, p_vout->f_y_ratio, p_vout->b_grayscale );
98 99 100
   
    /* Create and initialize system-dependant method - this function issues its
     * own error messages */
Vincent Seguin's avatar
Vincent Seguin committed
101 102
    if( vout_SysCreate( p_vout
#if defined(VIDEO_X11)
103
                        , psz_display, root_window 
Vincent Seguin's avatar
Vincent Seguin committed
104 105
#endif
        ) )
Michel Kaempf's avatar
Michel Kaempf committed
106
    {
Vincent Seguin's avatar
Vincent Seguin committed
107 108
      free( p_vout );
      return( NULL );
Michel Kaempf's avatar
Michel Kaempf committed
109
    }
110
    intf_DbgMsg("actual configuration: %dx%d,%d (%d bytes/pixel, %d bytes/line), ratio %.2f:%.2f, gray=%d\n",
111
                p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
112 113
                p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line,
                p_vout->f_x_ratio, p_vout->f_y_ratio, p_vout->b_grayscale );
114 115 116 117 118 119

    /* Initialize changement properties */
    p_vout->b_gamma_change      = 0;
    p_vout->i_new_width         = p_vout->i_width;
    p_vout->i_new_height        = p_vout->i_height; 

120
#ifdef STATS
121
    /* Initialize statistics fields */
122 123
    p_vout->c_loops             = 0;
    p_vout->c_idle_loops        = 0;
124
    p_vout->c_fps_samples       = 0;
125
#endif      
Michel Kaempf's avatar
Michel Kaempf committed
126 127

    /* Create thread and set locks */
Vincent Seguin's avatar
Vincent Seguin committed
128 129 130
    vlc_mutex_init( &p_vout->lock );
    if( vlc_thread_create( &p_vout->thread_id, "video output", 
			   (void *) RunThread, (void *) p_vout) )
Michel Kaempf's avatar
Michel Kaempf committed
131
    {
132
        intf_ErrMsg("error: %s\n", strerror(ENOMEM));
Vincent Seguin's avatar
Vincent Seguin committed
133
	vout_SysDestroy( p_vout );
Michel Kaempf's avatar
Michel Kaempf committed
134 135 136 137
        free( p_vout );
        return( NULL );
    }   

138 139 140
    intf_Msg("Video: display initialized (%dx%d, %d bpp)\n", 
             p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth );    

Michel Kaempf's avatar
Michel Kaempf committed
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
    /* 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 );
}

/*******************************************************************************
 * vout_DestroyThread: destroys a previously created thread
 *******************************************************************************
 * 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.
 *******************************************************************************/
void vout_DestroyThread( vout_thread_t *p_vout, int *pi_status )
{  
    int     i_status;                                         /* thread status */

    /* Set status */
    p_vout->pi_status = (pi_status != NULL) ? pi_status : &i_status;
Vincent Seguin's avatar
Vincent Seguin committed
171
    *p_vout->pi_status = THREAD_DESTROY;    
Michel Kaempf's avatar
Michel Kaempf committed
172 173 174 175 176
     
    /* Request thread destruction */
    p_vout->b_die = 1;

    /* If status is NULL, wait until thread has been destroyed */
177
    if( pi_status == NULL )
Michel Kaempf's avatar
Michel Kaempf committed
178 179 180 181 182 183 184 185 186 187
    {
        do
        {
            msleep( THREAD_SLEEP );
        }while( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR) 
                && (i_status != THREAD_FATAL) );   
    }
}

/*******************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
188
 * vout_DisplayPicture: display a picture
Michel Kaempf's avatar
Michel Kaempf committed
189
 *******************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
190
 * Remove the reservation flag of a picture, which will cause it to be ready for
191 192
 * display. The picture does not need to be locked, since it is ignored by
 * the output thread if is reserved.
Michel Kaempf's avatar
Michel Kaempf committed
193
 *******************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
194
void  vout_DisplayPicture( vout_thread_t *p_vout, picture_t *p_pic )
Michel Kaempf's avatar
Michel Kaempf committed
195
{
196 197 198 199
#ifdef DEBUG_VIDEO
    char        psz_date[MSTRTIME_MAX_SIZE];         /* buffer for date string */
#endif

200
#ifdef DEBUG
201 202 203
    /* Check if picture status is valid */
    if( p_pic->i_status != RESERVED_PICTURE )
    {
204
        intf_DbgMsg("error: picture %d has invalid status %d\n", p_pic, p_pic->i_status );       
205
    }   
206
#endif
Michel Kaempf's avatar
Michel Kaempf committed
207 208

    /* Remove reservation flag */
Vincent Seguin's avatar
Vincent Seguin committed
209
    p_pic->i_status = READY_PICTURE;
Michel Kaempf's avatar
Michel Kaempf committed
210

211 212 213 214 215
#ifdef DEBUG_VIDEO
    /* Send picture informations */
    intf_DbgMsg("picture %p: type=%d, %dx%d, date=%s\n", p_pic, p_pic->i_type, 
                p_pic->i_width,p_pic->i_height, mstrtime( psz_date, p_pic->date ) );    
#endif
Michel Kaempf's avatar
Michel Kaempf committed
216 217 218
}

/*******************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
219
 * vout_CreatePicture: allocate a picture in the video output heap.
Michel Kaempf's avatar
Michel Kaempf committed
220 221
 *******************************************************************************
 * This function create a reserved image in the video output heap. 
Vincent Seguin's avatar
Vincent Seguin committed
222
 * A null pointer is returned if the function fails. This method provides an
223
 * already allocated zone of memory in the picture data fields. It needs locking
224
 * since several pictures can be created by several producers threads. 
Michel Kaempf's avatar
Michel Kaempf committed
225
 *******************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
226
picture_t *vout_CreatePicture( vout_thread_t *p_vout, int i_type, 
227
			       int i_width, int i_height )
Michel Kaempf's avatar
Michel Kaempf committed
228
{
Vincent Seguin's avatar
Vincent Seguin committed
229
    int         i_picture;                                    /* picture index */
Vincent Seguin's avatar
Vincent Seguin committed
230
    int         i_chroma_width = 0;                            /* chroma width */    
Vincent Seguin's avatar
Vincent Seguin committed
231 232
    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
233

Vincent Seguin's avatar
Vincent Seguin committed
234 235
    /* Get lock */
    vlc_mutex_lock( &p_vout->lock );
Michel Kaempf's avatar
Michel Kaempf committed
236

Vincent Seguin's avatar
Vincent Seguin committed
237 238 239 240 241 242 243 244 245
    /* 
     * Look for an empty place 
     */
    for( i_picture = 0; 
         i_picture < VOUT_MAX_PICTURES; 
         i_picture++ )
    {
	if( p_vout->p_picture[i_picture].i_status == DESTROYED_PICTURE )
	{
246 247 248
	    /* 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
249 250
	    if( (p_vout->p_picture[i_picture].i_type           == i_type)   &&
		(p_vout->p_picture[i_picture].i_height         == i_height) &&
251
		(p_vout->p_picture[i_picture].i_width          == i_width) )
Vincent Seguin's avatar
Vincent Seguin committed
252 253 254 255 256
	    {
		/* 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;
257 258 259 260
#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
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
		vlc_mutex_unlock( &p_vout->lock );
		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
277 278
    }

Vincent Seguin's avatar
Vincent Seguin committed
279 280 281 282 283 284 285
    /* 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
286 287
    }

Vincent Seguin's avatar
Vincent Seguin committed
288 289 290 291 292 293 294
    /*
     * Prepare picture
     */
    if( p_free_picture != NULL )
    {
        /* Allocate memory */
        switch( i_type )
Michel Kaempf's avatar
Michel Kaempf committed
295
        {
296
        case YUV_420_PICTURE:          /* YUV 420: 1,1/4,1/4 samples per pixel */
Vincent Seguin's avatar
Vincent Seguin committed
297 298
            i_chroma_width = i_width / 2;            
            p_free_picture->p_data = malloc( i_height * i_chroma_width * 3 * sizeof( yuv_data_t ) );
299
            p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
Vincent Seguin's avatar
Vincent Seguin committed
300 301
            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;
302 303
            break;
        case YUV_422_PICTURE:          /* YUV 422: 1,1/2,1/2 samples per pixel */
304 305 306
            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
307 308
            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;
309 310
            break;
        case YUV_444_PICTURE:              /* YUV 444: 1,1,1 samples per pixel */
311 312 313
            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
314 315
            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
316 317 318
            break;                
#ifdef DEBUG
        default:
319
            intf_DbgMsg("error: unknown picture type %d\n", i_type );
320 321
            p_free_picture->p_data   =  NULL;            
            break;            
Vincent Seguin's avatar
Vincent Seguin committed
322
#endif    
Michel Kaempf's avatar
Michel Kaempf committed
323 324
        }

Vincent Seguin's avatar
Vincent Seguin committed
325 326
        if( p_free_picture->p_data != NULL )
        {        
327
            /* Copy picture informations, set some default values */
328 329
            p_free_picture->i_type                      = i_type;
            p_free_picture->i_status                    = RESERVED_PICTURE;
330
            p_free_picture->i_matrix_coefficients       = 1; 
331 332
            p_free_picture->i_width                     = i_width;
            p_free_picture->i_height                    = i_height;
333 334 335 336 337 338
            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;            
339
            p_free_picture->i_refcount                  = 0;            
Vincent Seguin's avatar
Vincent Seguin committed
340 341 342 343
        }
        else
        {
            /* Memory allocation failed : set picture as empty */
344 345 346 347
            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
348 349
        }
        
350 351 352
#ifdef DEBUG_VIDEO
        intf_DbgMsg("picture %p (in free picture slot)\n", p_free_picture );        
#endif
Vincent Seguin's avatar
Vincent Seguin committed
353 354
        vlc_mutex_unlock( &p_vout->lock );
        return( p_free_picture );
Michel Kaempf's avatar
Michel Kaempf committed
355
    }
Vincent Seguin's avatar
Vincent Seguin committed
356 357
    
    // No free or destroyed picture could be found
358
    intf_DbgMsg( "warning: heap is full\n" );
Vincent Seguin's avatar
Vincent Seguin committed
359 360
    vlc_mutex_unlock( &p_vout->lock );
    return( NULL );
Michel Kaempf's avatar
Michel Kaempf committed
361 362 363
}

/*******************************************************************************
364
 * vout_DestroyPicture: remove a permanent or reserved picture from the heap
Michel Kaempf's avatar
Michel Kaempf committed
365 366
 *******************************************************************************
 * This function frees a previously reserved picture or a permanent
Vincent Seguin's avatar
Vincent Seguin committed
367 368
 * 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 !
369 370
 * This function does not need locking since reserved pictures are ignored by
 * the output thread.
Michel Kaempf's avatar
Michel Kaempf committed
371
 *******************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
372
void vout_DestroyPicture( vout_thread_t *p_vout, picture_t *p_pic )
Michel Kaempf's avatar
Michel Kaempf committed
373
{
374
#ifdef DEBUG
375 376 377
   /* Check if picture status is valid */
   if( p_pic->i_status != RESERVED_PICTURE )
   {
378
       intf_DbgMsg("error: picture %d has invalid status %d\n", p_pic, p_pic->i_status );       
379 380 381
   }   
#endif

Vincent Seguin's avatar
Vincent Seguin committed
382
    p_pic->i_status = DESTROYED_PICTURE;
383 384 385 386

#ifdef DEBUG_VIDEO
    intf_DbgMsg("picture %p\n", p_pic);    
#endif
Michel Kaempf's avatar
Michel Kaempf committed
387 388 389 390 391 392
}

/*******************************************************************************
 * vout_LinkPicture: increment reference counter of a picture
 *******************************************************************************
 * This function increment the reference counter of a picture in the video
393
 * heap. It needs a lock since several producer threads can access the picture.
Michel Kaempf's avatar
Michel Kaempf committed
394 395 396
 *******************************************************************************/
void vout_LinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
{
Vincent Seguin's avatar
Vincent Seguin committed
397
    vlc_mutex_lock( &p_vout->lock );
Michel Kaempf's avatar
Michel Kaempf committed
398
    p_pic->i_refcount++;
399
    vlc_mutex_unlock( &p_vout->lock );
400 401 402 403

#ifdef DEBUG_VIDEO
    intf_DbgMsg("picture %p\n", p_pic);    
#endif
Michel Kaempf's avatar
Michel Kaempf committed
404 405 406 407 408 409 410 411 412
}

/*******************************************************************************
 * vout_UnlinkPicture: decrement reference counter of a picture
 *******************************************************************************
 * This function decrement the reference counter of a picture in the video heap.
 *******************************************************************************/
void vout_UnlinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
{
Vincent Seguin's avatar
Vincent Seguin committed
413
    vlc_mutex_lock( &p_vout->lock );
Michel Kaempf's avatar
Michel Kaempf committed
414
    p_pic->i_refcount--;
Vincent Seguin's avatar
Vincent Seguin committed
415
    if( (p_pic->i_refcount == 0) && (p_pic->i_status == DISPLAYED_PICTURE) )
Michel Kaempf's avatar
Michel Kaempf committed
416
    {
Vincent Seguin's avatar
Vincent Seguin committed
417
	p_pic->i_status = DESTROYED_PICTURE;
Michel Kaempf's avatar
Michel Kaempf committed
418
    }
419
    vlc_mutex_unlock( &p_vout->lock );
420 421 422 423

#ifdef DEBUG_VIDEO
    intf_DbgMsg("picture %p\n", p_pic);    
#endif
Michel Kaempf's avatar
Michel Kaempf committed
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
}

/* following functions are local */

/*******************************************************************************
 * InitThread: initialize video output thread
 *******************************************************************************
 * 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.
 *******************************************************************************/
static int InitThread( vout_thread_t *p_vout )
{
    int     i_index;                                          /* generic index */    

    /* Update status */
    *p_vout->pi_status = THREAD_START;    

442 443
    /* Initialize output method - this function issues its own error messages */
    if( vout_SysInit( p_vout ) )
Michel Kaempf's avatar
Michel Kaempf committed
444 445 446 447 448
    {
        *p_vout->pi_status = THREAD_ERROR;        
        return( 1 );
    } 

449 450
    /* Initialize pictures */    
    for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++)
Vincent Seguin's avatar
Vincent Seguin committed
451
    {
452 453 454 455 456 457 458 459
        p_vout->p_picture[i_index].i_type  = EMPTY_PICTURE;
        p_vout->p_picture[i_index].i_status= FREE_PICTURE;
    }

    /* Initialize convertion tables and functions */
    if( vout_InitTables( p_vout ) )
    {
        intf_ErrMsg("error: can't allocate translation tables\n");
460
        return( 1 );                
Vincent Seguin's avatar
Vincent Seguin committed
461 462
    }
    
Michel Kaempf's avatar
Michel Kaempf committed
463
    /* Mark thread as running and return */
464 465
    p_vout->b_active =          1;    
    *p_vout->pi_status =        THREAD_READY;    
466
    intf_DbgMsg("thread ready\n");    
467
    return( 0 );    
Michel Kaempf's avatar
Michel Kaempf committed
468 469 470 471 472 473 474 475 476 477 478 479 480
}

/*******************************************************************************
 * RunThread: video output thread
 *******************************************************************************
 * 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.
 *******************************************************************************/
static void RunThread( vout_thread_t *p_vout)
{
    int             i_picture;                                /* picture index */
    int             i_err;                                       /* error code */
481
    int             i_idle_level = 0;                            /* idle level */
Michel Kaempf's avatar
Michel Kaempf committed
482
    mtime_t         current_date;                              /* current date */
483
    mtime_t         pic_date = 0;                              /* picture date */    
484 485 486 487
    mtime_t         last_date = 0;                        /* last picture date */    
    boolean_t       b_display;                                 /* display flag */    
    picture_t *     p_pic;                                  /* picture pointer */
     
Michel Kaempf's avatar
Michel Kaempf committed
488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
    /* 
     * Initialize thread and free configuration 
     */
    p_vout->b_error = InitThread( p_vout );
    if( p_vout->b_error )
    {
        free( p_vout );                                  /* destroy descriptor */
        return;        
    }    

    /*
     * Main loop - it is not executed if an error occured during
     * initialization
     */
    while( (!p_vout->b_die) && (!p_vout->b_error) )
503
    {            
Vincent Seguin's avatar
Vincent Seguin committed
504
        /* 
505 506 507
	 * Find the picture to display - this operation does not need lock,
         * since only READY_PICTURES are handled 
         */
508
        p_pic = NULL;         
509
        for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
Vincent Seguin's avatar
Vincent Seguin committed
510 511 512
	{
	    if( (p_vout->p_picture[i_picture].i_status == READY_PICTURE) &&
		( (p_pic == NULL) || 
513
		  (p_vout->p_picture[i_picture].date < pic_date) ) )
Vincent Seguin's avatar
Vincent Seguin committed
514
	    {
515
                p_pic = &p_vout->p_picture[i_picture];
516
                pic_date = p_pic->date;                
Vincent Seguin's avatar
Vincent Seguin committed
517 518
	    }
	}
519
        current_date = mdate();
Vincent Seguin's avatar
Vincent Seguin committed
520 521 522 523 524

        /* 
	 * Render picture if any
	 */
        if( p_pic )
Michel Kaempf's avatar
Michel Kaempf committed
525
        {
526 527
#ifdef STATS
            /* Computes FPS rate */
528 529
            p_vout->fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ] = pic_date;
#endif	    
530
	    if( pic_date < current_date )
Vincent Seguin's avatar
Vincent Seguin committed
531
	    {
532 533 534 535 536
		/* Picture is late: it will be destroyed and the thread will sleep and
                 * go to next picture */
                vlc_mutex_lock( &p_vout->lock );
                p_pic->i_status = p_pic->i_refcount ? DISPLAYED_PICTURE : DESTROYED_PICTURE;
                vlc_mutex_unlock( &p_vout->lock );
537 538 539
#ifdef DEBUG_VIDEO
		intf_DbgMsg( "warning: late picture %p skipped\n", p_pic );
#endif
540
                p_pic =         NULL;                
Vincent Seguin's avatar
Vincent Seguin committed
541
	    }
542
	    else if( pic_date > current_date + VOUT_DISPLAY_DELAY )
Vincent Seguin's avatar
Vincent Seguin committed
543 544 545 546
	    {
		/* 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 */
547
                p_pic =         NULL;                
Vincent Seguin's avatar
Vincent Seguin committed
548 549 550 551
	    }
	    else
	    {
		/* Picture has not yet been displayed, and has a valid display
552 553
		 * date : render it, then mark it as displayed */
                if( p_vout->b_active )
554
                {                    
555
                    RenderPicture( p_vout, p_pic );
556 557 558 559
                    if( p_vout->b_info )
                    {
                        RenderPictureInfo( p_vout, p_pic );                        
                    }                    
560
                }                
561 562 563
                vlc_mutex_lock( &p_vout->lock );
                p_pic->i_status = p_pic->i_refcount ? DISPLAYED_PICTURE : DESTROYED_PICTURE;
                vlc_mutex_unlock( &p_vout->lock );
Vincent Seguin's avatar
Vincent Seguin committed
564
	    }
Michel Kaempf's avatar
Michel Kaempf committed
565
        }
566

567 568 569 570 571
        /* 
         * Rebuild tables if gamma has changed
         */
        if( p_vout->b_gamma_change )
        {
572
            //??
573
            p_vout->b_gamma_change = 0;            
574
            vout_ResetTables( p_vout );            // ?? test return value
575 576
        }        

Vincent Seguin's avatar
Vincent Seguin committed
577 578 579 580 581 582 583 584 585 586 587 588
        /*
         * Check events, sleep and display picture
	 */
        i_err = vout_SysManage( p_vout );
	if( i_err < 0 )
	{
	    /* 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;
	}
	else 
589
	{            
Vincent Seguin's avatar
Vincent Seguin committed
590 591
	    if( p_pic )
	    {
592 593 594 595 596 597
		/* A picture is ready to be displayed : remove blank screen flag */
                last_date =     pic_date;
                i_idle_level =  0;
                b_display =     1;                
                
                /* Sleep until its display date */
598
		mwait( pic_date );
Vincent Seguin's avatar
Vincent Seguin committed
599 600 601
	    }
	    else
	    {
602 603 604 605 606 607 608 609 610 611 612 613
                /* If last picture was a long time ago, increase idle level, reset
                 * date and render idle screen */
                if( !i_err && (current_date - last_date > VOUT_IDLE_DELAY) )
                {       
                    last_date = current_date;                    
                    b_display = p_vout->b_active && RenderIdle( p_vout, i_idle_level++ );
                }
                else
                {
                    b_display = 0;                    
                }
                
Michel Kaempf's avatar
Michel Kaempf committed
614
#ifdef STATS
Vincent Seguin's avatar
Vincent Seguin committed
615 616
		/* Update counters */
		p_vout->c_idle_loops++;
Michel Kaempf's avatar
Michel Kaempf committed
617
#endif
618 619 620

		/* Sleep to wait for new pictures */
		msleep( VOUT_IDLE_SLEEP );
Vincent Seguin's avatar
Vincent Seguin committed
621
	    }
622 623 624 625 626 627

            /* On awakening, send immediately picture to display */
            if( b_display && p_vout->b_active )
            {
                vout_SysDisplay( p_vout );
            }
Vincent Seguin's avatar
Vincent Seguin committed
628
	}
Michel Kaempf's avatar
Michel Kaempf committed
629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645

#ifdef STATS
        /* Update counters */
        p_vout->c_loops++;
#endif
    } 

    /*
     * Error loop
     */
    if( p_vout->b_error )
    {
        ErrorThread( p_vout );        
    }

    /* End of thread */
    EndThread( p_vout );
646
    intf_DbgMsg( "thread end\n" );
Michel Kaempf's avatar
Michel Kaempf committed
647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674
}

/*******************************************************************************
 * ErrorThread: RunThread() error loop
 *******************************************************************************
 * This function is called when an error occured during thread main's loop. The
 * thread can still receive feed, but must be ready to terminate as soon as
 * possible.
 *******************************************************************************/
static void ErrorThread( vout_thread_t *p_vout )
{
    /* Wait until a `die' order */
    while( !p_vout->b_die )
    {
        /* Sleep a while */
        msleep( VOUT_IDLE_SLEEP );                
    }
}

/*******************************************************************************
 * EndThread: thread destruction
 *******************************************************************************
 * This function is called when the thread ends after a sucessfull 
 * initialization.
 *******************************************************************************/
static void EndThread( vout_thread_t *p_vout )
{
    int *   pi_status;                                        /* thread status */
Vincent Seguin's avatar
Vincent Seguin committed
675
    int     i_picture;
Michel Kaempf's avatar
Michel Kaempf committed
676 677 678 679 680
        
    /* Store status */
    pi_status = p_vout->pi_status;    
    *pi_status = THREAD_END;    

Vincent Seguin's avatar
Vincent Seguin committed
681 682 683 684 685 686 687 688
    /* Destroy all remaining pictures */
    for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
    {
	if( p_vout->p_picture[i_picture].i_status != FREE_PICTURE )
	{
            free( p_vout->p_picture[i_picture].p_data );
        }
    }
Vincent Seguin's avatar
Vincent Seguin committed
689

690
    /* Destroy translation tables */
691
    vout_EndTables( p_vout );
Vincent Seguin's avatar
Vincent Seguin committed
692
    
Michel Kaempf's avatar
Michel Kaempf committed
693
    /* Destroy thread structures allocated by InitThread */
Vincent Seguin's avatar
Vincent Seguin committed
694
    vout_SysEnd( p_vout );
695
    vout_SysDestroy( p_vout );
Michel Kaempf's avatar
Michel Kaempf committed
696 697 698 699 700 701 702 703 704 705 706 707
    free( p_vout );

    /* Update status */
    *pi_status = THREAD_OVER;    
}

/*******************************************************************************
 * RenderPicture: render a picture
 *******************************************************************************
 * This function convert a picture from a video heap to a pixel-encoded image
 * and copy it to the current rendering buffer. No lock is required, since the
 * rendered picture has been determined as existant, and will only be destroyed
Vincent Seguin's avatar
Vincent Seguin committed
708
 * by the vout thread later.
Michel Kaempf's avatar
Michel Kaempf committed
709 710 711
 *******************************************************************************/
static void RenderPicture( vout_thread_t *p_vout, picture_t *p_pic )
{
712
#ifdef DEBUG_VIDEO
713
    /* Send picture informations and store rendering start date */
714 715 716 717
    intf_DbgMsg("picture %p\n", p_pic );
    p_vout->picture_render_time = mdate();    
#endif

718 719 720
    /* 
     * Prepare scaling 
     */
721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736
    if( (p_pic->i_width > p_vout->i_width) || (p_pic->i_height > p_vout->i_height) )
    {
#ifdef VIDEO_X11
        /* X11: window can be resized, so resize it - the picture won't be 
         * rendered since any alteration of the window size means recreating the
         * XImages */
        p_vout->i_new_width =   p_pic->i_width;
        p_vout->i_new_height =  p_pic->i_height;
        return;        
#else
        /* Other drivers: the video output thread can't change its size, so
         * we need to change the aspect ratio */
        //????
#endif
    }    

737 738 739
    /*
     * Choose appropriate rendering function and render picture
     */
740 741
    switch( p_pic->i_type )
    {
742 743 744 745
    case YUV_420_PICTURE:
        p_vout->p_ConvertYUV420( p_vout, vout_SysGetPicture( p_vout ),
                                 p_pic->p_y, p_pic->p_u, p_pic->p_v,
                                 p_pic->i_width, p_pic->i_height, 0, 0,
Vincent Seguin's avatar
Vincent Seguin committed
746
                                 4, p_pic->i_matrix_coefficients );
747
        break;        
748
    case YUV_422_PICTURE:
749 750 751 752 753 754 755
/*     ???   p_vout->p_convert_yuv_420( p_vout, 
                                   p_pic->p_y, p_pic->p_u, p_pic->p_v,
                                   i_chroma_width, i_chroma_height,
                                   p_vout->i_width / 2, p_vout->i_height,
                                   p_vout->i_bytes_per_line,
                                   0, 0, 0 );
  */      break;        
756
    case YUV_444_PICTURE:
757 758 759 760 761 762 763
/*  ???      p_vout->p_convert_yuv_420( p_vout, 
                                   p_pic->p_y, p_pic->p_u, p_pic->p_v,
                                   i_chroma_width, i_chroma_height,
                                   p_vout->i_width, p_vout->i_height,
                                   p_vout->i_bytes_per_line,
                                   0, 0, 0 );
  */      break;                
764 765
#ifdef DEBUG
    default:        
766
        intf_DbgMsg("error: unknown picture type %d\n", p_pic->i_type );
767 768 769
        break;        
#endif
    }
770

771 772 773 774 775
    /* 
     * Terminate scaling 
     */
    //??

776 777 778 779
#ifdef DEBUG_VIDEO
    /* Computes rendering time */
    p_vout->picture_render_time = mdate() - p_vout->picture_render_time;    
#endif
Michel Kaempf's avatar
Michel Kaempf committed
780 781
}

782

783 784

/*******************************************************************************
785
 * RenderPictureInfo: print additionnal informations on a picture
786 787 788
 *******************************************************************************
 * This function will add informations such as fps and buffer size on a picture
 *******************************************************************************/
789
static void RenderPictureInfo( vout_thread_t *p_vout, picture_t *p_pic )
790 791
{
    char        psz_buffer[256];                              /* string buffer */
792 793 794 795 796
#ifdef DEBUG
    int         i_ready_pic = 0;                             /* ready pictures */
    int         i_reserved_pic = 0;                       /* reserved pictures */
    int         i_picture;                                    /* picture index */
#endif
797 798

#ifdef STATS
799 800 801
    /* 
     * Print FPS rate in upper right corner 
     */
802
    if( p_vout->c_fps_samples > VOUT_FPS_SAMPLES )
803 804
    {        
        sprintf( psz_buffer, "%.2f fps", (double) VOUT_FPS_SAMPLES * 1000000 /
805 806
                 ( p_vout->fps_sample[ (p_vout->c_fps_samples - 1) % VOUT_FPS_SAMPLES ] -
                   p_vout->fps_sample[ p_vout->c_fps_samples % VOUT_FPS_SAMPLES ] ) );        
807 808 809
        vout_SysPrint( p_vout, p_vout->i_width, 0, 1, -1, psz_buffer );
    }

810 811 812
    /* 
     * Print statistics in upper left corner 
     */
813 814
    sprintf( psz_buffer, "gamma=%.2f   %ld frames (%.1f %% idle)", 
             p_vout->f_gamma, p_vout->c_fps_samples, p_vout->c_loops ? 
815
             (double ) p_vout->c_idle_loops * 100 / p_vout->c_loops : 100. );    
816 817 818
    vout_SysPrint( p_vout, 0, 0, -1, -1, psz_buffer );    
#endif
    
819
#ifdef DEBUG
820 821 822
    /* 
     * Print heap state in lower left corner  
     */
823 824 825 826 827 828 829 830 831 832 833 834 835 836
    for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
    {
        switch( p_vout->p_picture[i_picture].i_status )
        {
        case RESERVED_PICTURE:
            i_reserved_pic++;            
            break;            
        case READY_PICTURE:
            i_ready_pic++;            
            break;            
        }        
    }
    sprintf( psz_buffer, "video heap: %d/%d/%d", i_reserved_pic, i_ready_pic, 
             VOUT_MAX_PICTURES );
837 838 839 840
    vout_SysPrint( p_vout, 0, p_vout->i_height, -1, 1, psz_buffer );    
#endif

#ifdef DEBUG_VIDEO
841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862
    /* 
     * Print picture info in lower right corner 
     */
    switch( p_pic->i_type )
    {
    case YUV_420_PICTURE:
        sprintf( psz_buffer, "YUV 4:2:0 picture, rendering time: %lu us", 
                 (unsigned long) p_vout->picture_render_time );
        break;        
    case YUV_422_PICTURE:
        sprintf( psz_buffer, "YUV 4:2:2 picture, rendering time: %lu us", 
                 (unsigned long) p_vout->picture_render_time );
        break;        
    case YUV_444_PICTURE:
        sprintf( psz_buffer, "YUV 4:4:4 picture, rendering time: %lu us", 
                 (unsigned long) p_vout->picture_render_time );
        break;
    default:
        sprintf( psz_buffer, "unknown picture, rendering time: %lu us", 
                 (unsigned long) p_vout->picture_render_time );    
        break;        
    }    
863
    vout_SysPrint( p_vout, p_vout->i_width, p_vout->i_height, 1, 1, psz_buffer );    
864
#endif
865 866
}

867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903
/*******************************************************************************
 * RenderIdle: render idle picture
 *******************************************************************************
 * This function will clear the display or print a logo. Level will vary from 0
 * to a very high value that noone should never reach. It returns non 0 if 
 * something needs to be displayed and 0 if the previous picture can be kept.
 *******************************************************************************/
static int RenderIdle( vout_thread_t *p_vout, int i_level )
{
    byte_t      *pi_pic;                            /* pointer to picture data */
    
    /* Get frame pointer and clear display */
    pi_pic = vout_SysGetPicture( p_vout );    
     
    
    switch( i_level )
    {
    case 0:                                           /* level 0: clear screen */
        memset( pi_pic, 0, p_vout->i_bytes_per_line * p_vout->i_height );
        break;                
    case 1:                                            /* level 1: "no stream" */        
        memset( pi_pic, 0, p_vout->i_bytes_per_line * p_vout->i_height );
        vout_SysPrint( p_vout, p_vout->i_width / 2, p_vout->i_height / 2,
                       0, 0, "no stream" );     
        break;
    case 50:                                    /* level 50: copyright message */
        memset( pi_pic, 0, p_vout->i_bytes_per_line * p_vout->i_height );
        vout_SysPrint( p_vout, p_vout->i_width / 2, p_vout->i_height / 2,
                       0, 0, COPYRIGHT_MESSAGE );        
        break; 
    default:                            /* other levels: keep previous picture */
        return( 0 );
        break;        
    }

    return( 1 );    
}
904