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

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

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

30
/******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
31
 * Local prototypes
32
 ******************************************************************************/
33 34 35 36
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 );
37
static void     DestroyThread           ( vout_thread_t *p_vout, int i_status );
Vincent Seguin's avatar
Vincent Seguin committed
38
static void     Print                   ( vout_thread_t *p_vout, int i_x, int i_y, int i_halign, int i_valign, unsigned char *psz_text );
39 40 41 42 43 44 45 46 47

static void     SetBufferArea           ( vout_thread_t *p_vout, int i_x, int i_y, int i_w, int i_h );
static void     SetBufferPicture        ( vout_thread_t *p_vout, picture_t *p_pic );
static void     RenderPicture           ( vout_thread_t *p_vout, picture_t *p_pic );
static void     RenderPictureInfo       ( vout_thread_t *p_vout, picture_t *p_pic );
static void     RenderSubtitle          ( vout_thread_t *p_vout, subtitle_t *p_sub );
static void     RenderInterface         ( vout_thread_t *p_vout );
static void     RenderIdle              ( vout_thread_t *p_vout );
static void     RenderInfo              ( vout_thread_t *p_vout );
Vincent Seguin's avatar
Vincent Seguin committed
48
static int      Manage                  ( vout_thread_t *p_vout );
Michel Kaempf's avatar
Michel Kaempf committed
49

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

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

74 75
    /* Initialize thread properties - thread id and locks will be initialized 
     * later */
76 77 78 79 80 81
    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;    

82
    /* Initialize some fields used by the system-dependant method - these fields will
83
     * probably be modified by the method, and are only preferences */
84 85
    p_vout->i_width             = i_width;
    p_vout->i_height            = i_height;
86
    p_vout->i_bytes_per_line    = i_width * 2;    
87 88
    p_vout->i_screen_depth      = 15;
    p_vout->i_bytes_per_pixel   = 2;
89
    p_vout->f_gamma             = VOUT_GAMMA;    
90 91 92

    p_vout->b_grayscale         = main_GetIntVariable( VOUT_GRAYSCALE_VAR, 
                                                       VOUT_GRAYSCALE_DEFAULT );
Vincent Seguin's avatar
Vincent Seguin committed
93
    p_vout->b_info              = 0;    
94 95 96
    p_vout->b_interface         = 0;
    p_vout->b_scale             = 0;
    
Vincent Seguin's avatar
Vincent Seguin committed
97
    intf_DbgMsg("wished configuration: %dx%d,%d (%d bytes/pixel, %d bytes/line)\n",
98
                p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
Vincent Seguin's avatar
Vincent Seguin committed
99
                p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line );
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123

#ifdef STATS
    /* Initialize statistics fields */
    p_vout->render_time         = 0;    
    p_vout->c_fps_samples       = 0;    
#endif      

    /* Initialize running properties */
    p_vout->i_changes           = 0;
    p_vout->last_picture_date   = 0;
    p_vout->last_display_date   = 0;

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

    /* Initialize pictures and subtitles - translation tables and functions
     * will be initialized later in InitThread */    
    for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++)
    {
        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;
    }
124 125 126
   
    /* Create and initialize system-dependant method - this function issues its
     * own error messages */
Vincent Seguin's avatar
Vincent Seguin committed
127
    if( vout_SysCreate( p_vout, psz_display, i_root_window ) )
Michel Kaempf's avatar
Michel Kaempf committed
128
    {
Vincent Seguin's avatar
Vincent Seguin committed
129 130
      free( p_vout );
      return( NULL );
Michel Kaempf's avatar
Michel Kaempf committed
131
    }
Vincent Seguin's avatar
Vincent Seguin committed
132
    intf_DbgMsg("actual configuration: %dx%d,%d (%d bytes/pixel, %d bytes/line)\n",
133
                p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
Vincent Seguin's avatar
Vincent Seguin committed
134
                p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line );
Vincent Seguin's avatar
Vincent Seguin committed
135

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

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

169
    intf_Msg("Video display initialized (%dx%d, %d bpp)\n", 
170 171
             p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth );    

Michel Kaempf's avatar
Michel Kaempf committed
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
    /* 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 );
}

188
/******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
189
 * vout_DestroyThread: destroys a previously created thread
190
 ******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
191 192 193 194
 * 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.
195
 ******************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
196 197
void vout_DestroyThread( vout_thread_t *p_vout, int *pi_status )
{  
198
    int     i_status;                                        /* thread status */
Michel Kaempf's avatar
Michel Kaempf committed
199 200

    /* Set status */
201
    intf_DbgMsg("\n");
Michel Kaempf's avatar
Michel Kaempf committed
202
    p_vout->pi_status = (pi_status != NULL) ? pi_status : &i_status;
Vincent Seguin's avatar
Vincent Seguin committed
203
    *p_vout->pi_status = THREAD_DESTROY;    
Michel Kaempf's avatar
Michel Kaempf committed
204 205 206 207 208
     
    /* Request thread destruction */
    p_vout->b_die = 1;

    /* If status is NULL, wait until thread has been destroyed */
209
    if( pi_status == NULL )
Michel Kaempf's avatar
Michel Kaempf committed
210 211 212 213 214 215 216 217 218
    {
        do
        {
            msleep( THREAD_SLEEP );
        }while( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR) 
                && (i_status != THREAD_FATAL) );   
    }
}

219
/******************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
220
 * vout_DisplaySubtitle: display a subtitle
221
 ******************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
222 223 224
 * 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.
225
 ******************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
226 227 228
void  vout_DisplaySubtitle( vout_thread_t *p_vout, subtitle_t *p_sub )
{
#ifdef DEBUG_VIDEO
229 230
    char        psz_begin_date[MSTRTIME_MAX_SIZE];  /* buffer for date string */
    char        psz_end_date[MSTRTIME_MAX_SIZE];    /* buffer for date string */
Vincent Seguin's avatar
Vincent Seguin committed
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
#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
}

252
/******************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
253
 * vout_CreateSubtitle: allocate a subtitle in the video output heap.
254
 ******************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
255 256 257 258
 * 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. 
259
 ******************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
260 261 262
subtitle_t *vout_CreateSubtitle( vout_thread_t *p_vout, int i_type, 
                                 int i_size )
{
263
    //??
Vincent Seguin's avatar
Vincent Seguin committed
264 265
}

266
/******************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
267
 * vout_DestroySubtitle: remove a permanent or reserved subtitle from the heap
268
 ******************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
269 270 271 272
 * 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.
273
 ******************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
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
}

291
/******************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
292
 * vout_DisplayPicture: display a picture
293
 ******************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
294
 * Remove the reservation flag of a picture, which will cause it to be ready for
295 296
 * display. The picture won't be displayed until vout_DatePicture has been 
 * called.
297
 ******************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
298
void  vout_DisplayPicture( vout_thread_t *p_vout, picture_t *p_pic )
Michel Kaempf's avatar
Michel Kaempf committed
299
{
300 301
    vlc_mutex_lock( &p_vout->picture_lock );
    switch( p_pic->i_status )
302
    {
303 304 305 306 307 308 309 310 311 312
    case RESERVED_PICTURE:        
        p_pic->i_status = RESERVED_DISP_PICTURE;
        break;        
    case RESERVED_DATED_PICTURE:
        p_pic->i_status = READY_PICTURE;
        break;        
#ifdef DEBUG
    default:        
        intf_DbgMsg("error: picture %p has invalid status %d\n", p_pic, p_pic->i_status );    
        break;        
313
#endif
314
    }
Michel Kaempf's avatar
Michel Kaempf committed
315

316
#ifdef DEBUG_VIDEO
317
    intf_DbgMsg("picture %p\n", p_pic );
318
#endif
319 320

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

323
/******************************************************************************
324
 * vout_DatePicture: date a picture
325
 ******************************************************************************
326
 * Remove the reservation flag of a picture, which will cause it to be ready for
327 328
 * display. The picture won't be displayed until vout_DisplayPicture has been 
 * called.
329
 ******************************************************************************/
330 331
void  vout_DatePicture( vout_thread_t *p_vout, picture_t *p_pic, mtime_t date )
{
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
    vlc_mutex_lock( &p_vout->picture_lock );
    p_pic->date = date;    
    switch( p_pic->i_status )
    {
    case RESERVED_PICTURE:        
        p_pic->i_status = RESERVED_DATED_PICTURE;
        break;        
    case RESERVED_DISP_PICTURE:
        p_pic->i_status = READY_PICTURE;
        break;        
#ifdef DEBUG
    default:        
        intf_DbgMsg("error: picture %p has invalid status %d\n", p_pic, p_pic->i_status );    
        break;        
#endif
    }

#ifdef DEBUG_VIDEO
    intf_DbgMsg("picture %p\n", p_pic);
#endif
352 353

    vlc_mutex_unlock( &p_vout->picture_lock );
354 355
}

356
/******************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
357
 * vout_CreatePicture: allocate a picture in the video output heap.
358
 ******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
359
 * This function create a reserved image in the video output heap. 
Vincent Seguin's avatar
Vincent Seguin committed
360
 * A null pointer is returned if the function fails. This method provides an
361
 * already allocated zone of memory in the picture data fields. It needs locking
362
 * since several pictures can be created by several producers threads. 
363
 ******************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
364
picture_t *vout_CreatePicture( vout_thread_t *p_vout, int i_type, 
365
			       int i_width, int i_height )
Michel Kaempf's avatar
Michel Kaempf committed
366
{
367 368 369 370
    int         i_picture;                                   /* picture index */
    int         i_chroma_width = 0;                           /* chroma width */    
    picture_t * p_free_picture = NULL;                  /* first free picture */    
    picture_t * p_destroyed_picture = NULL;        /* first destroyed picture */    
Michel Kaempf's avatar
Michel Kaempf committed
371

Vincent Seguin's avatar
Vincent Seguin committed
372
    /* Get lock */
Vincent Seguin's avatar
Vincent Seguin committed
373
    vlc_mutex_lock( &p_vout->picture_lock );
Michel Kaempf's avatar
Michel Kaempf committed
374

Vincent Seguin's avatar
Vincent Seguin committed
375 376 377 378 379 380 381 382 383
    /* 
     * 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 )
	{
384 385 386
	    /* 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
387 388
	    if( (p_vout->p_picture[i_picture].i_type           == i_type)   &&
		(p_vout->p_picture[i_picture].i_height         == i_height) &&
389
		(p_vout->p_picture[i_picture].i_width          == i_width) )
Vincent Seguin's avatar
Vincent Seguin committed
390 391 392 393 394
	    {
		/* 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;
395 396 397 398
#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
399
		vlc_mutex_unlock( &p_vout->picture_lock );
Vincent Seguin's avatar
Vincent Seguin committed
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
		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
415 416
    }

Vincent Seguin's avatar
Vincent Seguin committed
417 418 419 420 421 422 423
    /* 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
424 425
    }

Vincent Seguin's avatar
Vincent Seguin committed
426 427 428 429 430 431 432
    /*
     * Prepare picture
     */
    if( p_free_picture != NULL )
    {
        /* Allocate memory */
        switch( i_type )
Michel Kaempf's avatar
Michel Kaempf committed
433
        {
434
        case YUV_420_PICTURE:          /* YUV 420: 1,1/4,1/4 samples per pixel */
Vincent Seguin's avatar
Vincent Seguin committed
435 436
            i_chroma_width = i_width / 2;            
            p_free_picture->p_data = malloc( i_height * i_chroma_width * 3 * sizeof( yuv_data_t ) );
437
            p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
Vincent Seguin's avatar
Vincent Seguin committed
438 439
            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;
440 441
            break;
        case YUV_422_PICTURE:          /* YUV 422: 1,1/2,1/2 samples per pixel */
442 443 444
            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
445 446
            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;
447 448
            break;
        case YUV_444_PICTURE:              /* YUV 444: 1,1,1 samples per pixel */
449 450 451
            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
452 453
            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
454 455 456
            break;                
#ifdef DEBUG
        default:
457
            intf_DbgMsg("error: unknown picture type %d\n", i_type );
458 459
            p_free_picture->p_data   =  NULL;            
            break;            
Vincent Seguin's avatar
Vincent Seguin committed
460
#endif    
Michel Kaempf's avatar
Michel Kaempf committed
461 462
        }

Vincent Seguin's avatar
Vincent Seguin committed
463 464
        if( p_free_picture->p_data != NULL )
        {        
465
            /* Copy picture informations, set some default values */
466 467
            p_free_picture->i_type                      = i_type;
            p_free_picture->i_status                    = RESERVED_PICTURE;
468
            p_free_picture->i_matrix_coefficients       = 1; 
469 470
            p_free_picture->i_width                     = i_width;
            p_free_picture->i_height                    = i_height;
471 472 473 474 475 476
            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;            
477
            p_free_picture->i_refcount                  = 0;            
Vincent Seguin's avatar
Vincent Seguin committed
478 479 480 481
        }
        else
        {
            /* Memory allocation failed : set picture as empty */
482 483 484 485
            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
486 487
        }
        
488 489 490
#ifdef DEBUG_VIDEO
        intf_DbgMsg("picture %p (in free picture slot)\n", p_free_picture );        
#endif
Vincent Seguin's avatar
Vincent Seguin committed
491
        vlc_mutex_unlock( &p_vout->picture_lock );
Vincent Seguin's avatar
Vincent Seguin committed
492
        return( p_free_picture );
Michel Kaempf's avatar
Michel Kaempf committed
493
    }
Vincent Seguin's avatar
Vincent Seguin committed
494 495
    
    // No free or destroyed picture could be found
496
    intf_DbgMsg( "warning: heap is full\n" );
Vincent Seguin's avatar
Vincent Seguin committed
497
    vlc_mutex_unlock( &p_vout->picture_lock );
Vincent Seguin's avatar
Vincent Seguin committed
498
    return( NULL );
Michel Kaempf's avatar
Michel Kaempf committed
499 500
}

501
/******************************************************************************
502
 * vout_DestroyPicture: remove a permanent or reserved picture from the heap
503
 ******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
504
 * This function frees a previously reserved picture or a permanent
Vincent Seguin's avatar
Vincent Seguin committed
505 506
 * 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 !
507 508
 * This function does not need locking since reserved pictures are ignored by
 * the output thread.
509
 ******************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
510
void vout_DestroyPicture( vout_thread_t *p_vout, picture_t *p_pic )
Michel Kaempf's avatar
Michel Kaempf committed
511
{
512
#ifdef DEBUG
513
   /* Check if picture status is valid */
514 515 516
   if( (p_pic->i_status != RESERVED_PICTURE) && 
       (p_pic->i_status != RESERVED_DATED_PICTURE) &&
       (p_pic->i_status != RESERVED_DISP_PICTURE) )
517
   {
Vincent Seguin's avatar
Vincent Seguin committed
518
       intf_DbgMsg("error: picture %p has invalid status %d\n", p_pic, p_pic->i_status );       
519 520 521
   }   
#endif

Vincent Seguin's avatar
Vincent Seguin committed
522
    p_pic->i_status = DESTROYED_PICTURE;
523 524 525 526

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

529
/******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
530
 * vout_LinkPicture: increment reference counter of a picture
531
 ******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
532
 * This function increment the reference counter of a picture in the video
533
 * heap. It needs a lock since several producer threads can access the picture.
534
 ******************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
535 536
void vout_LinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
{
Vincent Seguin's avatar
Vincent Seguin committed
537
    vlc_mutex_lock( &p_vout->picture_lock );
Michel Kaempf's avatar
Michel Kaempf committed
538
    p_pic->i_refcount++;
539 540

#ifdef DEBUG_VIDEO
541
    intf_DbgMsg("picture %p refcount=%d\n", p_pic, p_pic->i_refcount );    
542
#endif
543 544

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

547
/******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
548
 * vout_UnlinkPicture: decrement reference counter of a picture
549
 ******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
550
 * This function decrement the reference counter of a picture in the video heap.
551
 ******************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
552 553
void vout_UnlinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
{
Vincent Seguin's avatar
Vincent Seguin committed
554
    vlc_mutex_lock( &p_vout->picture_lock );
Michel Kaempf's avatar
Michel Kaempf committed
555
    p_pic->i_refcount--;
556 557 558 559 560 561 562 563 564

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

Vincent Seguin's avatar
Vincent Seguin committed
565
    if( (p_pic->i_refcount == 0) && (p_pic->i_status == DISPLAYED_PICTURE) )
Michel Kaempf's avatar
Michel Kaempf committed
566
    {
Vincent Seguin's avatar
Vincent Seguin committed
567
	p_pic->i_status = DESTROYED_PICTURE;
Michel Kaempf's avatar
Michel Kaempf committed
568
    }
569 570

#ifdef DEBUG_VIDEO
571
    intf_DbgMsg("picture %p refcount=%d\n", p_pic, p_pic->i_refcount );    
572
#endif
573 574

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

577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
/******************************************************************************
 * vout_ClearBuffer: clear a whole buffer
 ******************************************************************************
 * This function is called when a buffer is initialized. It clears the whole
 * buffer.
 ******************************************************************************/
void vout_ClearBuffer( vout_thread_t *p_vout, vout_buffer_t *p_buffer )
{
    /* No picture previously */
    p_buffer->i_pic_x =         0;
    p_buffer->i_pic_y =         0;
    p_buffer->i_pic_width =     0;
    p_buffer->i_pic_height =    0;

    /* The first area covers all the screen */
    p_buffer->i_areas =                 1;
    p_buffer->pi_area_begin[0] =        0;
    p_buffer->pi_area_end[0] =          p_vout->i_height - 1;
}

Michel Kaempf's avatar
Michel Kaempf committed
597 598
/* following functions are local */

599
/******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
600
 * InitThread: initialize video output thread
601
 ******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
602 603 604
 * 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.
605
 ******************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
606 607 608
static int InitThread( vout_thread_t *p_vout )
{
    /* Update status */
609
    intf_DbgMsg("\n");
Michel Kaempf's avatar
Michel Kaempf committed
610 611
    *p_vout->pi_status = THREAD_START;    

612 613
    /* Initialize output method - this function issues its own error messages */
    if( vout_SysInit( p_vout ) )
Michel Kaempf's avatar
Michel Kaempf committed
614 615 616 617
    {
        return( 1 );
    } 

618 619 620 621
    /* Initialize convertion tables and functions */
    if( vout_InitTables( p_vout ) )
    {
        intf_ErrMsg("error: can't allocate translation tables\n");
622
        return( 1 );                
Vincent Seguin's avatar
Vincent Seguin committed
623 624
    }
    
Michel Kaempf's avatar
Michel Kaempf committed
625
    /* Mark thread as running and return */
626 627
    p_vout->b_active =          1;    
    *p_vout->pi_status =        THREAD_READY;    
628
    intf_DbgMsg("thread ready\n");    
629
    return( 0 );    
Michel Kaempf's avatar
Michel Kaempf committed
630 631
}

632
/******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
633
 * RunThread: video output thread
634
 ******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
635 636 637
 * 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.
638
 ******************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
639 640
static void RunThread( vout_thread_t *p_vout)
{
641
    int             i_index;                                 /* index in heap */
642
    mtime_t         current_date;                             /* current date */
643
    mtime_t         display_date;                             /* display date */    
644 645
    boolean_t       b_display;                                /* display flag */    
    picture_t *     p_pic;                                 /* picture pointer */
646
    subtitle_t *    p_sub;                                /* subtitle pointer */    
647
     
Michel Kaempf's avatar
Michel Kaempf committed
648
    /* 
649
     * Initialize thread
Michel Kaempf's avatar
Michel Kaempf committed
650 651 652 653
     */
    p_vout->b_error = InitThread( p_vout );
    if( p_vout->b_error )
    {
654
        DestroyThread( p_vout, THREAD_ERROR );
Michel Kaempf's avatar
Michel Kaempf committed
655 656
        return;        
    }    
657
    intf_DbgMsg("\n");
Michel Kaempf's avatar
Michel Kaempf committed
658 659 660 661 662 663

    /*
     * Main loop - it is not executed if an error occured during
     * initialization
     */
    while( (!p_vout->b_die) && (!p_vout->b_error) )
664 665 666 667 668 669 670
    {
        /* Initialize loop variables */
        p_pic =         NULL;
        p_sub =         NULL;
        display_date =  0;        
        current_date =  mdate();

Vincent Seguin's avatar
Vincent Seguin committed
671
        /* 
672
	 * Find the picture to display - this operation does not need lock,
673
         * since only READY_PICTUREs are handled 
674
         */
675
        for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
Vincent Seguin's avatar
Vincent Seguin committed
676
	{
677
	    if( (p_vout->p_picture[i_index].i_status == READY_PICTURE) &&
Vincent Seguin's avatar
Vincent Seguin committed
678
		( (p_pic == NULL) || 
679
		  (p_vout->p_picture[i_index].date < display_date) ) )
Vincent Seguin's avatar
Vincent Seguin committed
680
	    {
681 682
                p_pic = &p_vout->p_picture[i_index];
                display_date = p_pic->date;                
Vincent Seguin's avatar
Vincent Seguin committed
683 684
	    }
	}
Vincent Seguin's avatar
Vincent Seguin committed
685
 
Vincent Seguin's avatar
Vincent Seguin committed
686
        if( p_pic )
Michel Kaempf's avatar
Michel Kaempf committed
687
        {
688 689
#ifdef STATS
            /* Computes FPS rate */
690
            p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ] = display_date;
691
#endif	    
692
	    if( display_date < current_date )
Vincent Seguin's avatar
Vincent Seguin committed
693
	    {
694 695
		/* Picture is late: it will be destroyed and the thread will sleep and
                 * go to next picture */
Vincent Seguin's avatar
Vincent Seguin committed
696
                vlc_mutex_lock( &p_vout->picture_lock );
697
                p_pic->i_status = p_pic->i_refcount ? DISPLAYED_PICTURE : DESTROYED_PICTURE;
698 699
		intf_DbgMsg( "warning: late picture %p skipped refcount=%d\n", p_pic, p_pic->i_refcount );
                vlc_mutex_unlock( &p_vout->picture_lock );
700
                p_pic =         NULL;                
701
                display_date =  0;                
Vincent Seguin's avatar
Vincent Seguin committed
702
	    }
703
	    else if( display_date > current_date + VOUT_DISPLAY_DELAY )
Vincent Seguin's avatar
Vincent Seguin committed
704 705 706 707
	    {
		/* 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 */
708
                p_pic =         NULL;                
709
                display_date =  0;                
Vincent Seguin's avatar
Vincent Seguin committed
710
	    }
Michel Kaempf's avatar
Michel Kaempf committed
711
        }
712 713 714 715 716 717 718 719

        /*
         * Find the subtitle to display - this operation does not need lock, since
         * only READY_SUBTITLEs are handled. If no picture has been selected,
         * display_date will depend on the subtitle
         */
        //??

Vincent Seguin's avatar
Vincent Seguin committed
720 721
        /*
         * Perform rendering, sleep and display rendered picture
722
         */
723
        if( p_pic )                            /* picture and perhaps subtitle */
724
        {
725 726 727 728 729 730 731 732 733
            b_display = p_vout->b_active;            

            if( b_display )
            {                
                /* Set picture dimensions and clear buffer */
                SetBufferPicture( p_vout, p_pic );

                /* Render picture and informations */
                RenderPicture( p_vout, p_pic );             
Vincent Seguin's avatar
Vincent Seguin committed
734 735
                if( p_vout->b_info )
                {
736 737 738
                    RenderPictureInfo( p_vout, p_pic );
                    RenderInfo( p_vout );                
                }
Vincent Seguin's avatar
Vincent Seguin committed
739
            }
740
            
Vincent Seguin's avatar
Vincent Seguin committed
741 742 743 744
            /* 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 );                          
745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765

            /* Render interface and subtitles */
            if( b_display && p_vout->b_interface )
            {
                RenderInterface( p_vout );                
            }
            if( p_sub )
            {
                if( b_display )
                {                    
                    RenderSubtitle( p_vout, p_sub );
                }                

                /* Remove subtitle from heap */
                vlc_mutex_lock( &p_vout->subtitle_lock );
                p_sub->i_status = DESTROYED_SUBTITLE;
                vlc_mutex_unlock( &p_vout->subtitle_lock );                          
            }

        }
        else if( p_sub )                                     /* subtitle alone */
Vincent Seguin's avatar
Vincent Seguin committed
766
        {
767 768 769
            b_display = p_vout->b_active;

            if( b_display )
Vincent Seguin's avatar
Vincent Seguin committed
770
            {                
771 772 773 774
                /* Clear buffer */
                SetBufferPicture( p_vout, NULL );

                /* Render informations, interface and subtitle */
Vincent Seguin's avatar
Vincent Seguin committed
775
                if( p_vout->b_info )
776 777 778 779 780 781 782 783
                {
                    RenderInfo( p_vout );
                }
                if( p_vout->b_interface )
                {
                    RenderInterface( p_vout );
                }
                RenderSubtitle( p_vout, p_sub );            
Vincent Seguin's avatar
Vincent Seguin committed
784
            }            
785 786 787 788 789 790 791 792 793 794

            /* Remove subtitle from heap */
            vlc_mutex_lock( &p_vout->subtitle_lock );
            p_sub->i_status = DESTROYED_SUBTITLE;
            vlc_mutex_unlock( &p_vout->subtitle_lock );                          
        }
        else                                              /* idle screen alone */
        {            
            //??? render on idle screen or interface change
            b_display = 0;             //???
Vincent Seguin's avatar
Vincent Seguin committed
795 796
        }

797 798 799 800 801 802 803 804 805
        /*
         * Sleep, wake up and display rendered picture
         */

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

Vincent Seguin's avatar
Vincent Seguin committed
806 807 808
        /* Give back change lock */
        vlc_mutex_unlock( &p_vout->change_lock );        

Vincent Seguin's avatar
Vincent Seguin committed
809
        /* Sleep a while or until a given date */
810
        if( display_date != 0 )
Vincent Seguin's avatar
Vincent Seguin committed
811
        {
812
            mwait( display_date );
Vincent Seguin's avatar
Vincent Seguin committed
813 814 815 816 817 818
        }
        else
        {
            msleep( VOUT_IDLE_SLEEP );                
        }            

819 820
        /* On awakening, take back lock and send immediately picture to display, 
         * then swap buffers */
Vincent Seguin's avatar
Vincent Seguin committed
821
        vlc_mutex_lock( &p_vout->change_lock );        
822 823 824 825
#ifdef DEBUG_VIDEO
        intf_DbgMsg( "picture %p, subtitle %p\n", p_pic, p_sub );        
#endif            
        if( b_display && !(p_vout->i_changes & VOUT_NODISPLAY_CHANGE) )
Vincent Seguin's avatar
Vincent Seguin committed
826 827
        {
            vout_SysDisplay( p_vout );
828
            p_vout->i_buffer_index = ++p_vout->i_buffer_index & 1;
Vincent Seguin's avatar
Vincent Seguin committed
829
        }
830

Vincent Seguin's avatar
Vincent Seguin committed
831
        /*
Vincent Seguin's avatar
Vincent Seguin committed
832
         * Check events and manage thread
Vincent Seguin's avatar
Vincent Seguin committed
833
	 */
Vincent Seguin's avatar
Vincent Seguin committed
834
        if( vout_SysManage( p_vout ) | Manage( p_vout ) )
Vincent Seguin's avatar
Vincent Seguin committed
835 836 837 838 839
	{
	    /* 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
840
	}  
Michel Kaempf's avatar
Michel Kaempf committed
841 842 843
    } 

    /*
844
     * Error loop - wait until the thread destruction is requested
Michel Kaempf's avatar
Michel Kaempf committed
845 846 847 848 849 850 851 852
     */
    if( p_vout->b_error )
    {
        ErrorThread( p_vout );        
    }

    /* End of thread */
    EndThread( p_vout );
853
    DestroyThread( p_vout, THREAD_OVER ); 
854
    intf_DbgMsg( "thread end\n" );
Michel Kaempf's avatar
Michel Kaempf committed
855 856
}

857
/******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
858
 * ErrorThread: RunThread() error loop
859
 ******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
860 861 862
 * 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.
863
 ******************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
864 865 866
static void ErrorThread( vout_thread_t *p_vout )
{
    /* Wait until a `die' order */
867
    intf_DbgMsg("\n");
Michel Kaempf's avatar
Michel Kaempf committed
868 869 870 871 872 873 874
    while( !p_vout->b_die )
    {
        /* Sleep a while */
        msleep( VOUT_IDLE_SLEEP );                
    }
}

875
/*******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
876
 * EndThread: thread destruction
877
 *******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
878
 * This function is called when the thread ends after a sucessfull 
879 880
 * initialization. It frees all ressources allocated by InitThread.
 *******************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
881 882
static void EndThread( vout_thread_t *p_vout )
{
883 884
    int     i_index;                                          /* index in heap */
            
Michel Kaempf's avatar
Michel Kaempf committed
885
    /* Store status */
886
    intf_DbgMsg("\n");
887
    *p_vout->pi_status = THREAD_END;    
Michel Kaempf's avatar
Michel Kaempf committed
888

889 890
    /* Destroy all remaining pictures and subtitles */
    for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
Vincent Seguin's avatar
Vincent Seguin committed
891
    {
892
	if( p_vout->p_picture[i_index].i_status != FREE_PICTURE )
Vincent Seguin's avatar
Vincent Seguin committed
893
	{
894
            free( p_vout->p_picture[i_index].p_data );
Vincent Seguin's avatar
Vincent Seguin committed
895
        }
896 897 898 899
        if( p_vout->p_subtitle[i_index].i_status != FREE_SUBTITLE )
        {
            free( p_vout->p_subtitle[i_index].p_data );            
        }        
Vincent Seguin's avatar
Vincent Seguin committed
900
    }
Vincent Seguin's avatar
Vincent Seguin committed
901

902
    /* Destroy translation tables */