video_output.c 35.5 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 );
Vincent Seguin's avatar
Vincent Seguin committed
42 43 44
static int      RenderIdle              ( vout_thread_t *p_vout );
static int      RenderInfo              ( vout_thread_t *p_vout );
static int      Manage                  ( vout_thread_t *p_vout );
Michel Kaempf's avatar
Michel Kaempf committed
45 46 47 48 49 50 51

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

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

72 73 74 75 76 77 78
    /* 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;    

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

117
#ifdef STATS
118
    /* Initialize statistics fields */
Vincent Seguin's avatar
Vincent Seguin committed
119 120
    p_vout->loop_time           = 0;    
    p_vout->c_fps_samples       = 0;    
121
#endif      
Michel Kaempf's avatar
Michel Kaempf committed
122 123

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

135 136 137
    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
138 139 140 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
    /* 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
168
    *p_vout->pi_status = THREAD_DESTROY;    
Michel Kaempf's avatar
Michel Kaempf committed
169 170 171 172 173
     
    /* Request thread destruction */
    p_vout->b_die = 1;

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

Vincent Seguin's avatar
Vincent Seguin committed
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
/*******************************************************************************
 * vout_DisplaySubtitle: display a subtitle
 *******************************************************************************
 * Remove the reservation flag of a subtitle, which will cause it to be ready for
 * display. The picture does not need to be locked, since it is ignored by
 * the output thread if is reserved.
 *******************************************************************************/
void  vout_DisplaySubtitle( vout_thread_t *p_vout, subtitle_t *p_sub )
{
#ifdef DEBUG_VIDEO
    char        psz_begin_date[MSTRTIME_MAX_SIZE];   /* buffer for date string */
    char        psz_end_date[MSTRTIME_MAX_SIZE];     /* buffer for date string */
#endif

#ifdef DEBUG
    /* Check if status is valid */
    if( p_sub->i_status != RESERVED_SUBTITLE )
    {
        intf_DbgMsg("error: subtitle %p has invalid status %d\n", p_sub, p_sub->i_status );       
    }   
#endif

    /* Remove reservation flag */
    p_sub->i_status = READY_SUBTITLE;

#ifdef DEBUG_VIDEO
    /* Send subtitle informations */
    intf_DbgMsg("subtitle %p: type=%d, begin date=%s, end date=%s\n", p_sub, p_sub->i_type, 
                mstrtime( psz_begin_date, p_sub->begin_date ), 
                mstrtime( psz_end_date, p_sub->end_date ) );    
#endif
}

/*******************************************************************************
 * vout_CreateSubtitle: allocate a subtitle in the video output heap.
 *******************************************************************************
 * This function create a reserved subtitle in the video output heap. 
 * A null pointer is returned if the function fails. This method provides an
 * already allocated zone of memory in the subtitle data fields. It needs locking
 * since several pictures can be created by several producers threads. 
 *******************************************************************************/
subtitle_t *vout_CreateSubtitle( vout_thread_t *p_vout, int i_type, 
                                 int i_size )
{
    //???
}

/*******************************************************************************
 * vout_DestroySubtitle: remove a permanent or reserved subtitle from the heap
 *******************************************************************************
 * This function frees a previously reserved subtitle.
 * It is meant to be used when the construction of a picture aborted.
 * This function does not need locking since reserved subtitles are ignored by
 * the output thread.
 *******************************************************************************/
void vout_DestroySubtitle( vout_thread_t *p_vout, subtitle_t *p_sub )
{
#ifdef DEBUG
   /* Check if subtitle status is valid */
   if( p_sub->i_status != RESERVED_SUBTITLE )
   {
       intf_DbgMsg("error: subtitle %p has invalid status %d\n", p_sub, p_sub->i_status );       
   }   
#endif

    p_sub->i_status = DESTROYED_SUBTITLE;

#ifdef DEBUG_VIDEO
    intf_DbgMsg("subtitle %p\n", p_sub);    
#endif
}

Michel Kaempf's avatar
Michel Kaempf committed
256
/*******************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
257
 * vout_DisplayPicture: display a picture
Michel Kaempf's avatar
Michel Kaempf committed
258
 *******************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
259
 * Remove the reservation flag of a picture, which will cause it to be ready for
260 261
 * 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
262
 *******************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
263
void  vout_DisplayPicture( vout_thread_t *p_vout, picture_t *p_pic )
Michel Kaempf's avatar
Michel Kaempf committed
264
{
265 266 267 268
#ifdef DEBUG_VIDEO
    char        psz_date[MSTRTIME_MAX_SIZE];         /* buffer for date string */
#endif

269
#ifdef DEBUG
270 271 272
    /* Check if picture status is valid */
    if( p_pic->i_status != RESERVED_PICTURE )
    {
Vincent Seguin's avatar
Vincent Seguin committed
273
        intf_DbgMsg("error: picture %p has invalid status %d\n", p_pic, p_pic->i_status );       
274
    }   
275
#endif
Michel Kaempf's avatar
Michel Kaempf committed
276 277

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

280 281 282 283 284
#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
285 286 287
}

/*******************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
288
 * vout_CreatePicture: allocate a picture in the video output heap.
Michel Kaempf's avatar
Michel Kaempf committed
289 290
 *******************************************************************************
 * This function create a reserved image in the video output heap. 
Vincent Seguin's avatar
Vincent Seguin committed
291
 * A null pointer is returned if the function fails. This method provides an
292
 * already allocated zone of memory in the picture data fields. It needs locking
293
 * since several pictures can be created by several producers threads. 
Michel Kaempf's avatar
Michel Kaempf committed
294
 *******************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
295
picture_t *vout_CreatePicture( vout_thread_t *p_vout, int i_type, 
296
			       int i_width, int i_height )
Michel Kaempf's avatar
Michel Kaempf committed
297
{
Vincent Seguin's avatar
Vincent Seguin committed
298
    int         i_picture;                                    /* picture index */
Vincent Seguin's avatar
Vincent Seguin committed
299
    int         i_chroma_width = 0;                            /* chroma width */    
Vincent Seguin's avatar
Vincent Seguin committed
300 301
    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
302

Vincent Seguin's avatar
Vincent Seguin committed
303
    /* Get lock */
Vincent Seguin's avatar
Vincent Seguin committed
304
    vlc_mutex_lock( &p_vout->picture_lock );
Michel Kaempf's avatar
Michel Kaempf committed
305

Vincent Seguin's avatar
Vincent Seguin committed
306 307 308 309 310 311 312 313 314
    /* 
     * 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 )
	{
315 316 317
	    /* 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
318 319
	    if( (p_vout->p_picture[i_picture].i_type           == i_type)   &&
		(p_vout->p_picture[i_picture].i_height         == i_height) &&
320
		(p_vout->p_picture[i_picture].i_width          == i_width) )
Vincent Seguin's avatar
Vincent Seguin committed
321 322 323 324 325
	    {
		/* 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;
326 327 328 329
#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
330
		vlc_mutex_unlock( &p_vout->picture_lock );
Vincent Seguin's avatar
Vincent Seguin committed
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
		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
346 347
    }

Vincent Seguin's avatar
Vincent Seguin committed
348 349 350 351 352 353 354
    /* 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
355 356
    }

Vincent Seguin's avatar
Vincent Seguin committed
357 358 359 360 361 362 363
    /*
     * Prepare picture
     */
    if( p_free_picture != NULL )
    {
        /* Allocate memory */
        switch( i_type )
Michel Kaempf's avatar
Michel Kaempf committed
364
        {
365
        case YUV_420_PICTURE:          /* YUV 420: 1,1/4,1/4 samples per pixel */
Vincent Seguin's avatar
Vincent Seguin committed
366 367
            i_chroma_width = i_width / 2;            
            p_free_picture->p_data = malloc( i_height * i_chroma_width * 3 * sizeof( yuv_data_t ) );
368
            p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
Vincent Seguin's avatar
Vincent Seguin committed
369 370
            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;
371 372
            break;
        case YUV_422_PICTURE:          /* YUV 422: 1,1/2,1/2 samples per pixel */
373 374 375
            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
376 377
            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;
378 379
            break;
        case YUV_444_PICTURE:              /* YUV 444: 1,1,1 samples per pixel */
380 381 382
            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
383 384
            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
385 386 387
            break;                
#ifdef DEBUG
        default:
388
            intf_DbgMsg("error: unknown picture type %d\n", i_type );
389 390
            p_free_picture->p_data   =  NULL;            
            break;            
Vincent Seguin's avatar
Vincent Seguin committed
391
#endif    
Michel Kaempf's avatar
Michel Kaempf committed
392 393
        }

Vincent Seguin's avatar
Vincent Seguin committed
394 395
        if( p_free_picture->p_data != NULL )
        {        
396
            /* Copy picture informations, set some default values */
397 398
            p_free_picture->i_type                      = i_type;
            p_free_picture->i_status                    = RESERVED_PICTURE;
399
            p_free_picture->i_matrix_coefficients       = 1; 
400 401
            p_free_picture->i_width                     = i_width;
            p_free_picture->i_height                    = i_height;
402 403 404 405 406 407
            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;            
408
            p_free_picture->i_refcount                  = 0;            
Vincent Seguin's avatar
Vincent Seguin committed
409 410 411 412
        }
        else
        {
            /* Memory allocation failed : set picture as empty */
413 414 415 416
            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
417 418
        }
        
419 420 421
#ifdef DEBUG_VIDEO
        intf_DbgMsg("picture %p (in free picture slot)\n", p_free_picture );        
#endif
Vincent Seguin's avatar
Vincent Seguin committed
422
        vlc_mutex_unlock( &p_vout->picture_lock );
Vincent Seguin's avatar
Vincent Seguin committed
423
        return( p_free_picture );
Michel Kaempf's avatar
Michel Kaempf committed
424
    }
Vincent Seguin's avatar
Vincent Seguin committed
425 426
    
    // No free or destroyed picture could be found
427
    intf_DbgMsg( "warning: heap is full\n" );
Vincent Seguin's avatar
Vincent Seguin committed
428
    vlc_mutex_unlock( &p_vout->picture_lock );
Vincent Seguin's avatar
Vincent Seguin committed
429
    return( NULL );
Michel Kaempf's avatar
Michel Kaempf committed
430 431 432
}

/*******************************************************************************
433
 * vout_DestroyPicture: remove a permanent or reserved picture from the heap
Michel Kaempf's avatar
Michel Kaempf committed
434 435
 *******************************************************************************
 * This function frees a previously reserved picture or a permanent
Vincent Seguin's avatar
Vincent Seguin committed
436 437
 * 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 !
438 439
 * This function does not need locking since reserved pictures are ignored by
 * the output thread.
Michel Kaempf's avatar
Michel Kaempf committed
440
 *******************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
441
void vout_DestroyPicture( vout_thread_t *p_vout, picture_t *p_pic )
Michel Kaempf's avatar
Michel Kaempf committed
442
{
443
#ifdef DEBUG
444 445 446
   /* Check if picture status is valid */
   if( p_pic->i_status != RESERVED_PICTURE )
   {
Vincent Seguin's avatar
Vincent Seguin committed
447
       intf_DbgMsg("error: picture %p has invalid status %d\n", p_pic, p_pic->i_status );       
448 449 450
   }   
#endif

Vincent Seguin's avatar
Vincent Seguin committed
451
    p_pic->i_status = DESTROYED_PICTURE;
452 453 454 455

#ifdef DEBUG_VIDEO
    intf_DbgMsg("picture %p\n", p_pic);    
#endif
Michel Kaempf's avatar
Michel Kaempf committed
456 457 458 459 460 461
}

/*******************************************************************************
 * vout_LinkPicture: increment reference counter of a picture
 *******************************************************************************
 * This function increment the reference counter of a picture in the video
462
 * heap. It needs a lock since several producer threads can access the picture.
Michel Kaempf's avatar
Michel Kaempf committed
463 464 465
 *******************************************************************************/
void vout_LinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
{
Vincent Seguin's avatar
Vincent Seguin committed
466
    vlc_mutex_lock( &p_vout->picture_lock );
Michel Kaempf's avatar
Michel Kaempf committed
467
    p_pic->i_refcount++;
Vincent Seguin's avatar
Vincent Seguin committed
468
    vlc_mutex_unlock( &p_vout->picture_lock );
469 470 471 472

#ifdef DEBUG_VIDEO
    intf_DbgMsg("picture %p\n", p_pic);    
#endif
Michel Kaempf's avatar
Michel Kaempf committed
473 474 475 476 477 478 479 480 481
}

/*******************************************************************************
 * 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
482
    vlc_mutex_lock( &p_vout->picture_lock );
Michel Kaempf's avatar
Michel Kaempf committed
483
    p_pic->i_refcount--;
Vincent Seguin's avatar
Vincent Seguin committed
484
    if( (p_pic->i_refcount == 0) && (p_pic->i_status == DISPLAYED_PICTURE) )
Michel Kaempf's avatar
Michel Kaempf committed
485
    {
Vincent Seguin's avatar
Vincent Seguin committed
486
	p_pic->i_status = DESTROYED_PICTURE;
Michel Kaempf's avatar
Michel Kaempf committed
487
    }
Vincent Seguin's avatar
Vincent Seguin committed
488
    vlc_mutex_unlock( &p_vout->picture_lock );
489 490 491 492

#ifdef DEBUG_VIDEO
    intf_DbgMsg("picture %p\n", p_pic);    
#endif
Michel Kaempf's avatar
Michel Kaempf committed
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
}

/* 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;    

511 512
    /* Initialize output method - this function issues its own error messages */
    if( vout_SysInit( p_vout ) )
Michel Kaempf's avatar
Michel Kaempf committed
513 514 515 516 517
    {
        *p_vout->pi_status = THREAD_ERROR;        
        return( 1 );
    } 

Vincent Seguin's avatar
Vincent Seguin committed
518
    /* Initialize pictures and subtitles */    
519
    for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++)
Vincent Seguin's avatar
Vincent Seguin committed
520
    {
Vincent Seguin's avatar
Vincent Seguin committed
521 522 523 524
        p_vout->p_picture[i_index].i_type   = EMPTY_PICTURE;
        p_vout->p_picture[i_index].i_status = FREE_PICTURE;
        p_vout->p_subtitle[i_index].i_type  = EMPTY_SUBTITLE;
        p_vout->p_subtitle[i_index].i_status= FREE_SUBTITLE;
525 526 527 528 529 530
    }

    /* Initialize convertion tables and functions */
    if( vout_InitTables( p_vout ) )
    {
        intf_ErrMsg("error: can't allocate translation tables\n");
531
        return( 1 );                
Vincent Seguin's avatar
Vincent Seguin committed
532 533
    }
    
Michel Kaempf's avatar
Michel Kaempf committed
534
    /* Mark thread as running and return */
535 536
    p_vout->b_active =          1;    
    *p_vout->pi_status =        THREAD_READY;    
537
    intf_DbgMsg("thread ready\n");    
538
    return( 0 );    
Michel Kaempf's avatar
Michel Kaempf committed
539 540 541 542 543 544 545 546 547 548 549 550 551
}

/*******************************************************************************
 * 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 */
    mtime_t         current_date;                              /* current date */
552
    mtime_t         pic_date = 0;                              /* picture date */    
553 554 555
    boolean_t       b_display;                                 /* display flag */    
    picture_t *     p_pic;                                  /* picture pointer */
     
Michel Kaempf's avatar
Michel Kaempf committed
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
    /* 
     * 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) )
571
    {            
Vincent Seguin's avatar
Vincent Seguin committed
572
        /* 
573 574 575
	 * Find the picture to display - this operation does not need lock,
         * since only READY_PICTURES are handled 
         */
576
        p_pic = NULL;         
Vincent Seguin's avatar
Vincent Seguin committed
577
        current_date = mdate();
578
        for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
Vincent Seguin's avatar
Vincent Seguin committed
579 580 581
	{
	    if( (p_vout->p_picture[i_picture].i_status == READY_PICTURE) &&
		( (p_pic == NULL) || 
582
		  (p_vout->p_picture[i_picture].date < pic_date) ) )
Vincent Seguin's avatar
Vincent Seguin committed
583
	    {
584
                p_pic = &p_vout->p_picture[i_picture];
585
                pic_date = p_pic->date;                
Vincent Seguin's avatar
Vincent Seguin committed
586 587
	    }
	}
Vincent Seguin's avatar
Vincent Seguin committed
588
 
Vincent Seguin's avatar
Vincent Seguin committed
589 590 591 592
        /* 
	 * Render picture if any
	 */
        if( p_pic )
Michel Kaempf's avatar
Michel Kaempf committed
593
        {
594 595
#ifdef STATS
            /* Computes FPS rate */
596 597
            p_vout->fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ] = pic_date;
#endif	    
598
	    if( pic_date < current_date )
Vincent Seguin's avatar
Vincent Seguin committed
599
	    {
600 601
		/* Picture is late: it will be destroyed and the thread will sleep and
                 * go to next picture */
Vincent Seguin's avatar
Vincent Seguin committed
602
                vlc_mutex_lock( &p_vout->picture_lock );
603
                p_pic->i_status = p_pic->i_refcount ? DISPLAYED_PICTURE : DESTROYED_PICTURE;
Vincent Seguin's avatar
Vincent Seguin committed
604
                vlc_mutex_unlock( &p_vout->picture_lock );
605 606 607
#ifdef DEBUG_VIDEO
		intf_DbgMsg( "warning: late picture %p skipped\n", p_pic );
#endif
608
                p_pic =         NULL;                
Vincent Seguin's avatar
Vincent Seguin committed
609
	    }
610
	    else if( pic_date > current_date + VOUT_DISPLAY_DELAY )
Vincent Seguin's avatar
Vincent Seguin committed
611 612 613 614
	    {
		/* 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 */
615
                p_pic =         NULL;                
Vincent Seguin's avatar
Vincent Seguin committed
616
	    }
Michel Kaempf's avatar
Michel Kaempf committed
617
        }
618

Vincent Seguin's avatar
Vincent Seguin committed
619 620 621
              
        /*
         * Perform rendering, sleep and display rendered picture
622
         */
Vincent Seguin's avatar
Vincent Seguin committed
623
        if( p_pic )
624
        {
Vincent Seguin's avatar
Vincent Seguin committed
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 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
            /* A picture is ready to be displayed : render it */
            if( p_vout->b_active )
            {                    
                RenderPicture( p_vout, p_pic );
                if( p_vout->b_info )
                {
                    RenderPictureInfo( p_vout, p_pic );
                    RenderInfo( p_vout );                    
                }                    
                b_display = 1;                    
            }
            else
            {
                b_display = 0;                
            } 

            /* 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 );                          
        }            
        else
        {
            /* No picture. However, an idle screen may be ready to display */
            b_display = p_vout->b_active && (                   RenderIdle( p_vout ) | 
                                            ( p_vout->b_info && RenderInfo( p_vout ) ));
        }

        /* Sleep a while or until a given date */
        if( p_pic )
        {
#ifdef STATS
            /* Computes loop time */
            p_vout->loop_time = mdate() - current_date;            
#endif
            mwait( pic_date );
        }
        else
        {
            msleep( VOUT_IDLE_SLEEP );                
        }            

        /* On awakening, send immediately picture to display */
        if( b_display && p_vout->b_active )
        {
            vout_SysDisplay( p_vout );
        }
672

Vincent Seguin's avatar
Vincent Seguin committed
673
        /*
Vincent Seguin's avatar
Vincent Seguin committed
674
         * Check events and manage thread
Vincent Seguin's avatar
Vincent Seguin committed
675
	 */
Vincent Seguin's avatar
Vincent Seguin committed
676
        if( vout_SysManage( p_vout ) | Manage( p_vout ) )
Vincent Seguin's avatar
Vincent Seguin committed
677 678 679 680 681
	{
	    /* 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;
Vincent Seguin's avatar
Vincent Seguin committed
682
	}  
Michel Kaempf's avatar
Michel Kaempf committed
683 684 685 686 687 688 689 690 691 692 693 694
    } 

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

    /* End of thread */
    EndThread( p_vout );
695
    intf_DbgMsg( "thread end\n" );
Michel Kaempf's avatar
Michel Kaempf committed
696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723
}

/*******************************************************************************
 * 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
724
    int     i_picture;
Michel Kaempf's avatar
Michel Kaempf committed
725 726 727 728 729
        
    /* Store status */
    pi_status = p_vout->pi_status;    
    *pi_status = THREAD_END;    

Vincent Seguin's avatar
Vincent Seguin committed
730 731 732 733 734 735 736 737
    /* 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
738

739
    /* Destroy translation tables */
740
    vout_EndTables( p_vout );
Vincent Seguin's avatar
Vincent Seguin committed
741
    
Michel Kaempf's avatar
Michel Kaempf committed
742
    /* Destroy thread structures allocated by InitThread */
Vincent Seguin's avatar
Vincent Seguin committed
743
    vout_SysEnd( p_vout );
744
    vout_SysDestroy( p_vout );
Michel Kaempf's avatar
Michel Kaempf committed
745 746 747 748 749 750 751 752 753 754 755 756
    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
757
 * by the vout thread later.
Michel Kaempf's avatar
Michel Kaempf committed
758 759 760
 *******************************************************************************/
static void RenderPicture( vout_thread_t *p_vout, picture_t *p_pic )
{
761 762 763
    /* 
     * Prepare scaling 
     */
764 765 766 767 768 769
    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 */
Vincent Seguin's avatar
Vincent Seguin committed
770 771
/*        p_vout->i_new_width =   p_pic->i_width;
        p_vout->i_new_height =  p_pic->i_height;*/
772 773 774 775 776 777 778 779
        return;        
#else
        /* Other drivers: the video output thread can't change its size, so
         * we need to change the aspect ratio */
        //????
#endif
    }    

780 781 782
    /*
     * Choose appropriate rendering function and render picture
     */
783 784
    switch( p_pic->i_type )
    {
785 786 787 788
    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
789
                                 4, p_pic->i_matrix_coefficients );
790
        break;        
791
    case YUV_422_PICTURE:
Vincent Seguin's avatar
Vincent Seguin committed
792
 /* ??? p_vout->p_convert_yuv_420( p_vout, 
793 794 795 796 797 798
                                   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;        
799
    case YUV_444_PICTURE:
800 801 802 803 804 805 806
/*  ???      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;                
807 808
#ifdef DEBUG
    default:        
809
        intf_DbgMsg("error: unknown picture type %d\n", p_pic->i_type );
810 811 812
        break;        
#endif
    }
813

814 815 816 817
    /* 
     * Terminate scaling 
     */
    //??
Michel Kaempf's avatar
Michel Kaempf committed
818 819
}

820

821 822

/*******************************************************************************
823
 * RenderPictureInfo: print additionnal informations on a picture
824 825 826
 *******************************************************************************
 * This function will add informations such as fps and buffer size on a picture
 *******************************************************************************/
827
static void RenderPictureInfo( vout_thread_t *p_vout, picture_t *p_pic )
828 829
{
    char        psz_buffer[256];                              /* string buffer */
830 831 832 833 834
#ifdef DEBUG
    int         i_ready_pic = 0;                             /* ready pictures */
    int         i_reserved_pic = 0;                       /* reserved pictures */
    int         i_picture;                                    /* picture index */
#endif
835 836

#ifdef STATS
837 838 839
    /* 
     * Print FPS rate in upper right corner 
     */
840
    if( p_vout->c_fps_samples > VOUT_FPS_SAMPLES )
841 842
    {        
        sprintf( psz_buffer, "%.2f fps", (double) VOUT_FPS_SAMPLES * 1000000 /
843 844
                 ( 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 ] ) );        
845 846 847
        vout_SysPrint( p_vout, p_vout->i_width, 0, 1, -1, psz_buffer );
    }

848 849 850
    /* 
     * Print statistics in upper left corner 
     */
Vincent Seguin's avatar
Vincent Seguin committed
851 852
    sprintf( psz_buffer, "gamma=%.2f   %ld frames", 
             p_vout->f_gamma, p_vout->c_fps_samples );
853 854 855
    vout_SysPrint( p_vout, 0, 0, -1, -1, psz_buffer );    
#endif
    
856
#ifdef DEBUG
857 858 859
    /* 
     * Print heap state in lower left corner  
     */
860 861 862 863 864 865 866 867 868 869 870 871 872 873
    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 );
874 875 876 877
    vout_SysPrint( p_vout, 0, p_vout->i_height, -1, 1, psz_buffer );    
#endif

#ifdef DEBUG_VIDEO
Vincent Seguin's avatar
Vincent Seguin committed
878
    //??
879
#endif
880 881
}

882 883 884
/*******************************************************************************
 * RenderIdle: render idle picture
 *******************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
885
 * This function will clear the display or print a logo.
886
 *******************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
887
static int RenderIdle( vout_thread_t *p_vout )
888
{
Vincent Seguin's avatar
Vincent Seguin committed
889 890 891
    //??
    return( 0 );    
}
892

Vincent Seguin's avatar
Vincent Seguin committed
893 894 895 896 897 898 899 900 901
/*******************************************************************************
 * RenderInfo: render additionnal informations
 *******************************************************************************
 * ??
 *******************************************************************************/
static int RenderInfo( vout_thread_t *p_vout )
{
    //??
    return( 0 );    
902
}
903

Vincent Seguin's avatar
Vincent Seguin committed
904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923
/*******************************************************************************
 * Manage: manage thread
 *******************************************************************************
 * ??
 *******************************************************************************/
static int Manage( vout_thread_t *p_vout )
{
    //??

    /* Detect unauthorized changes */
    if( p_vout->i_changes )
    {
        /* Some changes were not acknowledged by vout_SysManage or this function,
         * it means they should not be authorized */
        intf_ErrMsg( "error: unauthorized changes in the video output thread\n" );        
        return( 1 );        
    }
    
    return( 0 );    
}