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 */
903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919
    vout_EndTables( p_vout );  
    vout_SysEnd( p_vout );    
}

/*******************************************************************************
 * DestroyThread: thread destruction
 *******************************************************************************
 * This function is called when the thread ends. It frees all ressources
 * allocated by CreateThread. Status is available at this stage.
 *******************************************************************************/
static void DestroyThread( vout_thread_t *p_vout, int i_status )
{  
    int *pi_status;                                           /* status adress */
    
    /* Store status adress */
    intf_DbgMsg("\n");
    pi_status = p_vout->pi_status;    
Vincent Seguin's avatar
Vincent Seguin committed
920
    
Vincent Seguin's avatar
Vincent Seguin committed
921 922 923
    /* Destroy thread structures allocated by Create and InitThread */
    vout_UnloadFont( p_vout->p_default_font );
    vout_UnloadFont( p_vout->p_large_font ); 
924
    vout_SysDestroy( p_vout );
Michel Kaempf's avatar
Michel Kaempf committed
925
    free( p_vout );
926
    *pi_status = i_status;    
Michel Kaempf's avatar
Michel Kaempf committed
927 928
}

Vincent Seguin's avatar
Vincent Seguin committed
929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968
/*******************************************************************************
 * Print: print simple text on a picture
 *******************************************************************************
 * This function will print a simple text on the picture. It is designed to
 * print debugging or general informations.
 *******************************************************************************/
void Print( vout_thread_t *p_vout, int i_x, int i_y, int i_halign, int i_valign, unsigned char *psz_text )
{
    int                 i_text_height;                    /* total text height */
    int                 i_text_width;                      /* total text width */

    /* Update upper left coordinates according to alignment */
    vout_TextSize( p_vout->p_default_font, 0, psz_text, &i_text_width, &i_text_height );
    switch( i_halign )
    {
    case 0:                                                        /* centered */
        i_x -= i_text_width / 2;
        break;        
    case 1:                                                   /* right aligned */
        i_x -= i_text_width;
        break;                
    }
    switch( i_valign )
    {
    case 0:                                                        /* centered */
        i_y -= i_text_height / 2;
        break;        
    case 1:                                                   /* bottom aligned */
        i_y -= i_text_height;
        break;                
    }

    /* Check clipping */
    if( (i_y < 0) || (i_y + i_text_height > p_vout->i_height) || 
        (i_x < 0) || (i_x + i_text_width > p_vout->i_width) )
    {
        intf_DbgMsg("'%s' would print outside the screen\n", psz_text);        
        return;        
    }    

969 970 971
    /* Set area and print text */
    SetBufferArea( p_vout, i_x, i_y, i_text_width, i_text_height );    
    vout_Print( p_vout->p_default_font, p_vout->p_buffer[ p_vout->i_buffer_index ].p_data + 
Vincent Seguin's avatar
Vincent Seguin committed
972 973
                i_y * p_vout->i_bytes_per_line + i_x * p_vout->i_bytes_per_pixel,
                p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line, 
974
                0xffffffff, 0x00000000, 0x00000000, TRANSPARENT_TEXT, psz_text );
Vincent Seguin's avatar
Vincent Seguin committed
975 976
}

977 978 979 980 981 982 983 984 985
/*******************************************************************************
 * SetBufferArea: activate an area in current buffer
 *******************************************************************************
 * This function is called when something is rendered on the current buffer.
 * It set the area as active and prepare it to be cleared on next rendering.
 * Pay attention to the fact that in this functions, i_h is in fact the end y
 * coordinate of the new area.
 *******************************************************************************/
static void SetBufferArea( vout_thread_t *p_vout, int i_x, int i_y, int i_w, int i_h )
Vincent Seguin's avatar
Vincent Seguin committed
986
{
987 988 989 990
    vout_buffer_t *     p_buffer;                            /* current buffer */
    int                 i_area_begin, i_area_end;   /* area vertical extension */
    int                 i_area, i_area_copy;                     /* area index */    
    int                 i_area_shift;              /* shift distance for areas */    
Vincent Seguin's avatar
Vincent Seguin committed
991
    
992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021
    /* Choose buffer and modify h to end of area position */
    p_buffer =  &p_vout->p_buffer[ p_vout->i_buffer_index ];    
    i_h +=      i_y - 1;
 
    /* 
     * Remove part of the area which is inside the picture - this is done
     * by calling again SetBufferArea with the correct areas dimensions.
     */
    if( (i_x >= p_buffer->i_pic_x) && (i_x + i_w <= p_buffer->i_pic_x + p_buffer->i_pic_width