video_output.c 66 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 );
38 39
static void     Print                   ( vout_thread_t *p_vout, int i_x, int i_y, 
                                          int i_h_align, int i_v_align, unsigned char *psz_text );
40 41 42 43
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 );
44
static void     RenderSubPicture        ( vout_thread_t *p_vout, subpicture_t *p_subpic );
45
static void     RenderInterface         ( vout_thread_t *p_vout );
46
static int      RenderIdle              ( vout_thread_t *p_vout );
47
static void     RenderInfo              ( vout_thread_t *p_vout );
Vincent Seguin's avatar
Vincent Seguin committed
48
static int      Manage                  ( vout_thread_t *p_vout );
49 50
static int      Align                   ( vout_thread_t *p_vout, int *pi_x, int *pi_y, 
                                          int i_width, int i_height, int i_h_align, int i_v_align );
Michel Kaempf's avatar
Michel Kaempf committed
51

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

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

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

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

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

104 105 106 107
    /* Initialize idle screen */
    p_vout->last_display_date   = mdate();
    p_vout->last_idle_date      = 0;

108 109 110 111 112 113 114 115 116
#ifdef STATS
    /* Initialize statistics fields */
    p_vout->render_time         = 0;    
    p_vout->c_fps_samples       = 0;    
#endif      

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

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

138 139
    /* 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
140 141 142 143 144 145 146 147 148 149 150 151 152 153
    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 );        
154
    }     
Vincent Seguin's avatar
Vincent Seguin committed
155

Michel Kaempf's avatar
Michel Kaempf committed
156
    /* Create thread and set locks */
Vincent Seguin's avatar
Vincent Seguin committed
157
    vlc_mutex_init( &p_vout->picture_lock );
158
    vlc_mutex_init( &p_vout->subpicture_lock );    
Vincent Seguin's avatar
Vincent Seguin committed
159 160 161
    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
162
    {
163
        intf_ErrMsg("error: %s\n", strerror(ENOMEM));
Vincent Seguin's avatar
Vincent Seguin committed
164 165
        vout_UnloadFont( p_vout->p_default_font );
        vout_UnloadFont( p_vout->p_large_font );        
Vincent Seguin's avatar
Vincent Seguin committed
166
	vout_SysDestroy( p_vout );
Michel Kaempf's avatar
Michel Kaempf committed
167 168 169 170
        free( p_vout );
        return( NULL );
    }   

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

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

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

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

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

221
/******************************************************************************
222
 * vout_DisplaySubPicture: display a subpicture unit
223
 ******************************************************************************
224 225
 * Remove the reservation flag of an subpicture, which will cause it to be ready 
 * for display. The picture does not need to be locked, since it is ignored by
Vincent Seguin's avatar
Vincent Seguin committed
226
 * the output thread if is reserved.
227
 ******************************************************************************/
228
void  vout_DisplaySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
Vincent Seguin's avatar
Vincent Seguin committed
229 230
{
#ifdef DEBUG_VIDEO
231 232
    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
233 234 235 236
#endif

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

    /* Remove reservation flag */
245
    p_subpic->i_status = READY_SUBPICTURE;
Vincent Seguin's avatar
Vincent Seguin committed
246 247

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

256
/******************************************************************************
257
 * vout_CreateSubPicture: allocate an subpicture in the video output heap.
258
 ******************************************************************************
259
 * This function create a reserved subpicture in the video output heap. 
Vincent Seguin's avatar
Vincent Seguin committed
260
 * A null pointer is returned if the function fails. This method provides an
261
 * already allocated zone of memory in the spu data fields. It needs locking
Vincent Seguin's avatar
Vincent Seguin committed
262
 * since several pictures can be created by several producers threads. 
263
 ******************************************************************************/
264 265
subpicture_t *vout_CreateSubPicture( vout_thread_t *p_vout, int i_type, 
                                     int i_size )
Vincent Seguin's avatar
Vincent Seguin committed
266
{
267
    //??
Vincent Seguin's avatar
Vincent Seguin committed
268 269
}

270
/******************************************************************************
271
 * vout_DestroySubPicture: remove a subpicture from the heap
272
 ******************************************************************************
273
 * This function frees a previously reserved subpicture.
Vincent Seguin's avatar
Vincent Seguin committed
274
 * It is meant to be used when the construction of a picture aborted.
275 276
 * This function does not need locking since reserved subpictures are ignored 
 * by the output thread.
277
 ******************************************************************************/
278
void vout_DestroySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
Vincent Seguin's avatar
Vincent Seguin committed
279 280
{
#ifdef DEBUG
281 282
   /* Check if status is valid */
   if( p_subpic->i_status != RESERVED_SUBPICTURE )
Vincent Seguin's avatar
Vincent Seguin committed
283
   {
284 285
       intf_DbgMsg("error: subpicture %p has invalid status %d\n", 
                   p_subpic, p_subpic->i_status );       
Vincent Seguin's avatar
Vincent Seguin committed
286 287 288
   }   
#endif

289
    p_subpic->i_status = DESTROYED_SUBPICTURE;
Vincent Seguin's avatar
Vincent Seguin committed
290 291

#ifdef DEBUG_VIDEO
292
    intf_DbgMsg("subpicture %p\n", p_subpic);    
Vincent Seguin's avatar
Vincent Seguin committed
293 294 295
#endif
}

296
/******************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
297
 * vout_DisplayPicture: display a picture
298
 ******************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
299
 * Remove the reservation flag of a picture, which will cause it to be ready for
300 301
 * display. The picture won't be displayed until vout_DatePicture has been 
 * called.
302
 ******************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
303
void  vout_DisplayPicture( vout_thread_t *p_vout, picture_t *p_pic )
Michel Kaempf's avatar
Michel Kaempf committed
304
{
305 306
    vlc_mutex_lock( &p_vout->picture_lock );
    switch( p_pic->i_status )
307
    {
308 309 310 311 312 313 314 315 316 317
    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;        
318
#endif
319
    }
Michel Kaempf's avatar
Michel Kaempf committed
320

321
#ifdef DEBUG_VIDEO
322
    intf_DbgMsg("picture %p\n", p_pic );
323
#endif
324 325

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

328
/******************************************************************************
329
 * vout_DatePicture: date a picture
330
 ******************************************************************************
331
 * Remove the reservation flag of a picture, which will cause it to be ready for
332 333
 * display. The picture won't be displayed until vout_DisplayPicture has been 
 * called.
334
 ******************************************************************************/
335 336
void  vout_DatePicture( vout_thread_t *p_vout, picture_t *p_pic, mtime_t date )
{
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
    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
357 358

    vlc_mutex_unlock( &p_vout->picture_lock );
359 360
}

361
/******************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
362
 * vout_CreatePicture: allocate a picture in the video output heap.
363
 ******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
364
 * This function create a reserved image in the video output heap. 
Vincent Seguin's avatar
Vincent Seguin committed
365
 * A null pointer is returned if the function fails. This method provides an
366
 * already allocated zone of memory in the picture data fields. It needs locking
367
 * since several pictures can be created by several producers threads. 
368
 ******************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
369
picture_t *vout_CreatePicture( vout_thread_t *p_vout, int i_type, 
370
			       int i_width, int i_height )
Michel Kaempf's avatar
Michel Kaempf committed
371
{
372 373 374 375
    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
376

Vincent Seguin's avatar
Vincent Seguin committed
377
    /* Get lock */
Vincent Seguin's avatar
Vincent Seguin committed
378
    vlc_mutex_lock( &p_vout->picture_lock );
Michel Kaempf's avatar
Michel Kaempf committed
379

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

Vincent Seguin's avatar
Vincent Seguin committed
422 423 424 425 426 427 428
    /* 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
429 430
    }

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

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

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

Vincent Seguin's avatar
Vincent Seguin committed
527
    p_pic->i_status = DESTROYED_PICTURE;
528 529 530 531

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

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

#ifdef DEBUG_VIDEO
546
    intf_DbgMsg("picture %p refcount=%d\n", p_pic, p_pic->i_refcount );    
547
#endif
548 549

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

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

#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
570
    if( (p_pic->i_refcount == 0) && (p_pic->i_status == DISPLAYED_PICTURE) )
Michel Kaempf's avatar
Michel Kaempf committed
571
    {
Vincent Seguin's avatar
Vincent Seguin committed
572
	p_pic->i_status = DESTROYED_PICTURE;
Michel Kaempf's avatar
Michel Kaempf committed
573
    }
574 575

#ifdef DEBUG_VIDEO
576
    intf_DbgMsg("picture %p refcount=%d\n", p_pic, p_pic->i_refcount );    
577
#endif
578 579

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

582
/******************************************************************************
583
 * vout_SetBuffers: set buffers adresses
584
 ******************************************************************************
585 586
 * This function is called by system drivers to set buffers video memory 
 * adresses.
587
 ******************************************************************************/
588
void vout_SetBuffers( vout_thread_t *p_vout, void *p_buf1, void *p_buf2 )
589 590
{
    /* No picture previously */
591 592 593 594 595 596 597 598
    p_vout->p_buffer[0].i_pic_x =         0;
    p_vout->p_buffer[0].i_pic_y =         0;
    p_vout->p_buffer[0].i_pic_width =     0;
    p_vout->p_buffer[0].i_pic_height =    0;
    p_vout->p_buffer[1].i_pic_x =         0;
    p_vout->p_buffer[1].i_pic_y =         0;
    p_vout->p_buffer[1].i_pic_width =     0;
    p_vout->p_buffer[1].i_pic_height =    0;
599 600

    /* The first area covers all the screen */
601 602 603 604 605 606 607 608 609 610
    p_vout->p_buffer[0].i_areas =                 1;
    p_vout->p_buffer[0].pi_area_begin[0] =        0;
    p_vout->p_buffer[0].pi_area_end[0] =          p_vout->i_height - 1;
    p_vout->p_buffer[1].i_areas =                 1;
    p_vout->p_buffer[1].pi_area_begin[0] =        0;
    p_vout->p_buffer[1].pi_area_end[0] =          p_vout->i_height - 1;

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

Michel Kaempf's avatar
Michel Kaempf committed
613 614
/* following functions are local */

615
/******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
616
 * InitThread: initialize video output thread
617
 ******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
618 619 620
 * 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.
621
 ******************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
622 623 624
static int InitThread( vout_thread_t *p_vout )
{
    /* Update status */
625
    intf_DbgMsg("\n");
Michel Kaempf's avatar
Michel Kaempf committed
626 627
    *p_vout->pi_status = THREAD_START;    

628 629
    /* Initialize output method - this function issues its own error messages */
    if( vout_SysInit( p_vout ) )
Michel Kaempf's avatar
Michel Kaempf committed
630 631 632 633
    {
        return( 1 );
    } 

634
    /* Initialize convertion tables and functions */
635
    if( vout_InitYUV( p_vout ) )
636
    {
637
        intf_ErrMsg("error: can't allocate YUV translation tables\n");
638
        return( 1 );                
Vincent Seguin's avatar
Vincent Seguin committed
639 640
    }
    
Michel Kaempf's avatar
Michel Kaempf committed
641
    /* Mark thread as running and return */
642 643
    p_vout->b_active =          1;    
    *p_vout->pi_status =        THREAD_READY;    
644
    intf_DbgMsg("thread ready\n");    
645
    return( 0 );    
Michel Kaempf's avatar
Michel Kaempf committed
646 647
}

648
/******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
649
 * RunThread: video output thread
650
 ******************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
651 652 653
 * 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.
654
 ******************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
655 656
static void RunThread( vout_thread_t *p_vout)
{
657
    int             i_index;                                 /* index in heap */
658
    mtime_t         current_date;                             /* current date */
659
    mtime_t         display_date;                             /* display date */    
660 661
    boolean_t       b_display;                                /* display flag */    
    picture_t *     p_pic;                                 /* picture pointer */
662
    subpicture_t *  p_subpic;                           /* subpicture pointer */    
663
     
Michel Kaempf's avatar
Michel Kaempf committed
664
    /* 
665
     * Initialize thread
Michel Kaempf's avatar
Michel Kaempf committed
666 667 668 669
     */
    p_vout->b_error = InitThread( p_vout );
    if( p_vout->b_error )
    {
670
        DestroyThread( p_vout, THREAD_ERROR );
Michel Kaempf's avatar
Michel Kaempf committed
671 672
        return;        
    }    
673
    intf_DbgMsg("\n");
Michel Kaempf's avatar
Michel Kaempf committed
674 675 676 677 678 679

    /*
     * Main loop - it is not executed if an error occured during
     * initialization
     */
    while( (!p_vout->b_die) && (!p_vout->b_error) )
680 681 682
    {
        /* Initialize loop variables */
        p_pic =         NULL;
683
        p_subpic =      NULL;
684 685 686
        display_date =  0;        
        current_date =  mdate();

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

        /*
730
         * Find the subpicture to display - this operation does not need lock, since
731 732
         * only READY_SUBPICTURES are handled. If no picture has been selected,
         * display_date will depend on the subpicture
733 734 735
         */
        //??

Vincent Seguin's avatar
Vincent Seguin committed
736 737
        /*
         * Perform rendering, sleep and display rendered picture
738
         */
739
        if( p_pic )                          /* picture and perhaps subpicture */
740
        {
741
            b_display = p_vout->b_active;            
742 743
            p_vout->last_display_date = display_date;
            
744 745 746 747 748 749 750
            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
751 752
                if( p_vout->b_info )
                {
753 754 755
                    RenderPictureInfo( p_vout, p_pic );
                    RenderInfo( p_vout );                
                }
Vincent Seguin's avatar
Vincent Seguin committed
756
            }
757
            
Vincent Seguin's avatar
Vincent Seguin committed
758 759 760 761
            /* 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 );                          
762

763
            /* Render interface and subpicture */
764 765 766 767
            if( b_display && p_vout->b_interface )
            {
                RenderInterface( p_vout );                
            }
768
            if( p_subpic )
769 770 771
            {
                if( b_display )
                {                    
772
                    RenderSubPicture( p_vout, p_subpic );
773 774
                }                

775 776 777 778
                /* Remove subpicture from heap */
                vlc_mutex_lock( &p_vout->subpicture_lock );
                p_subpic->i_status = DESTROYED_SUBPICTURE;
                vlc_mutex_unlock( &p_vout->subpicture_lock );                          
779 780 781
            }

        }
782
        else if( p_subpic )                                /* subpicture alone */
Vincent Seguin's avatar
Vincent Seguin committed
783
        {
784
            b_display = p_vout->b_active;
785
            p_vout->last_display_date = display_date;            
786 787

            if( b_display )
Vincent Seguin's avatar
Vincent Seguin committed
788
            {                
789 790 791
                /* Clear buffer */
                SetBufferPicture( p_vout, NULL );

792
                /* Render informations, interface and subpicture */
Vincent Seguin's avatar
Vincent Seguin committed
793
                if( p_vout->b_info )
794 795 796 797 798 799 800
                {
                    RenderInfo( p_vout );
                }
                if( p_vout->b_interface )
                {
                    RenderInterface( p_vout );
                }
801
                RenderSubPicture( p_vout, p_subpic );            
Vincent Seguin's avatar
Vincent Seguin committed
802
            }            
803

804 805 806 807
            /* Remove subpicture from heap */
            vlc_mutex_lock( &p_vout->subpicture_lock );
            p_subpic->i_status = DESTROYED_SUBPICTURE;
            vlc_mutex_unlock( &p_vout->subpicture_lock );                          
Vincent Seguin's avatar
Vincent Seguin committed
808
        }
809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835
        else if( p_vout->b_active )          /* idle or interface screen alone */
        {
            //?? clear: SetBufferPicture( p_vout, NULL );
            if( p_vout->b_interface /* && ?? intf_change -> cause use of 100% CPU ! */ )
            {
                /* Interface has changed, so a new rendering is required - force
                 * it by setting last idle date to 0 */
                p_vout->last_idle_date = 0;                
            }

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

837 838 839 840 841 842 843 844 845
        /*
         * 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
846 847 848
        /* Give back change lock */
        vlc_mutex_unlock( &p_vout->change_lock );        

Vincent Seguin's avatar
Vincent Seguin committed
849
        /* Sleep a while or until a given date */
850
        if( display_date != 0 )
Vincent Seguin's avatar
Vincent Seguin committed
851
        {
852
            mwait( display_date );
Vincent Seguin's avatar
Vincent Seguin committed
853 854 855 856 857 858
        }
        else
        {
            msleep( VOUT_IDLE_SLEEP );                
        }            

859 860
        /* On awakening, take back lock and send immediately picture to display, 
         * then swap buffers */
Vincent Seguin's avatar
Vincent Seguin committed
861
        vlc_mutex_lock( &p_vout->change_lock );        
862
#ifdef DEBUG_VIDEO
863
        intf_DbgMsg( "picture %p, subpicture %p\n", p_pic, p_subpic );        
864 865
#endif            
        if( b_display && !(p_vout->i_changes & VOUT_NODISPLAY_CHANGE) )
Vincent Seguin's avatar
Vincent Seguin committed
866 867
        {
            vout_SysDisplay( p_vout );
868
            p_vout->i_buffer_index = ++p_vout->i_buffer_index & 1;
Vincent Seguin's avatar
Vincent Seguin committed
869
        }
870

Vincent Seguin's avatar
Vincent Seguin committed
871
        /*
Vincent Seguin's avatar
Vincent Seguin committed
872
         * Check events and manage thread
Vincent Seguin's avatar
Vincent Seguin committed
873
	 */
Vincent Seguin's avatar
Vincent Seguin committed
874
        if( vout_SysManage( p_vout ) | Manage( p_vout ) )
Vincent Seguin's avatar
Vincent Seguin committed
875 876 877 878 879
	{
	    /* 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
880
	}  
Michel Kaempf's avatar
Michel Kaempf committed
881 882 883
    } 

    /*
884
     * Error loop - wait until the thread destruction is requested
Michel Kaempf's avatar
Michel Kaempf committed
885 886 887 888 889 890 891 892
     */
    if( p_vout->b_error )
    {
        ErrorThread( p_vout );        
    }

    /* End of thread */
    EndThread( p_vout );
Vincent Seguin's avatar