]> git.sesse.net Git - vlc/blob - src/video_output/video_output.c
. double buffering avec locks pour 3dfx
[vlc] / src / video_output / video_output.c
1 /******************************************************************************
2  * video_output.c : video output thread
3  * (c)2000 VideoLAN
4  ******************************************************************************
5  * This module describes the programming interface for video output threads.
6  * It includes functions allowing to open a new thread, send pictures to a
7  * thread, and destroy a previously oppenned video output thread.
8  ******************************************************************************/
9
10 /******************************************************************************
11  * Preamble
12  ******************************************************************************/
13 #include <errno.h>
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <string.h>
17
18 #include "common.h"
19 #include "config.h"
20 #include "mtime.h"
21 #include "vlc_thread.h"
22 #include "video.h"
23 #include "video_output.h"
24 #include "video_text.h"
25 #include "video_sys.h"
26 #include "video_yuv.h"
27 #include "intf_msg.h"
28 #include "main.h"
29
30 /******************************************************************************
31  * Local prototypes
32  ******************************************************************************/
33 static int      BinaryLog         ( u32 i );
34 static void     MaskToShift       ( int *pi_left, int *pi_right, u32 i_mask );
35 static int      InitThread        ( vout_thread_t *p_vout );
36 static void     RunThread         ( vout_thread_t *p_vout );
37 static void     ErrorThread       ( vout_thread_t *p_vout );
38 static void     EndThread         ( vout_thread_t *p_vout );
39 static void     DestroyThread     ( vout_thread_t *p_vout, int i_status );
40 static void     Print             ( vout_thread_t *p_vout, int i_x, int i_y, 
41                                     int i_h_align, int i_v_align,
42                                     unsigned char *psz_text );
43 static void     SetBufferArea     ( vout_thread_t *p_vout, int i_x, int i_y,
44                                     int i_w, int i_h );
45 static void     SetBufferPicture  ( vout_thread_t *p_vout, picture_t *p_pic );
46 static void     RenderPicture     ( vout_thread_t *p_vout, picture_t *p_pic );
47 static void     RenderPictureInfo ( vout_thread_t *p_vout, picture_t *p_pic );
48 static void     RenderSubPicture  ( vout_thread_t *p_vout,
49                                     subpicture_t *p_subpic );
50 static void     RenderInterface   ( vout_thread_t *p_vout );
51 static int      RenderIdle        ( vout_thread_t *p_vout );
52 static void     RenderInfo        ( vout_thread_t *p_vout );
53 static int      Manage            ( vout_thread_t *p_vout );
54 static int      Align             ( vout_thread_t *p_vout, int *pi_x,
55                                     int *pi_y, int i_width, int i_height,
56                                     int i_h_align, int i_v_align );
57 static void     SetPalette        ( p_vout_thread_t p_vout, u16 *red,
58                                     u16 *green, u16 *blue, u16 *transp );
59
60 /******************************************************************************
61  * vout_CreateThread: creates a new video output thread
62  ******************************************************************************
63  * This function creates a new video output thread, and returns a pointer
64  * to its description. On error, it returns NULL.
65  * If pi_status is NULL, then the function will block until the thread is ready.
66  * If not, it will be updated using one of the THREAD_* constants.
67  ******************************************************************************/
68 vout_thread_t * vout_CreateThread               ( char *psz_display, int i_root_window, 
69                                                   int i_width, int i_height, int *pi_status )
70 {
71     vout_thread_t * p_vout;                              /* thread descriptor */
72     int             i_status;                                /* thread status */
73     int             i_index;                /* index for array initialization */
74
75     /* Allocate descriptor */
76     intf_DbgMsg("\n");    
77     p_vout = (vout_thread_t *) malloc( sizeof(vout_thread_t) );
78     if( p_vout == NULL )
79     {
80         intf_ErrMsg("error: %s\n", strerror(ENOMEM));        
81         return( NULL );
82     }
83
84     /* Initialize thread properties - thread id and locks will be initialized 
85      * later */
86     p_vout->b_die               = 0;
87     p_vout->b_error             = 0;
88     p_vout->b_active            = 0;
89     p_vout->pi_status           = (pi_status != NULL) ? pi_status : &i_status;
90     *p_vout->pi_status          = THREAD_CREATE;
91
92     /* Initialize some fields used by the system-dependant method - these fields will
93      * probably be modified by the method, and are only preferences */
94     p_vout->i_changes           = 0;
95     p_vout->i_width             = i_width;
96     p_vout->i_height            = i_height;
97     p_vout->i_bytes_per_line    = i_width * 2;
98     p_vout->i_screen_depth      = 15;
99     p_vout->i_bytes_per_pixel   = 2;
100     p_vout->f_gamma             = VOUT_GAMMA;
101
102     p_vout->b_grayscale         = main_GetIntVariable( VOUT_GRAYSCALE_VAR, VOUT_GRAYSCALE_DEFAULT );
103     p_vout->b_info              = 0;
104     p_vout->b_interface         = 0;
105     p_vout->b_scale             = 0;
106
107     p_vout->p_set_palette       = SetPalette;
108     
109     intf_DbgMsg("wished configuration: %dx%d, %d/%d bpp (%d Bpl)\n",
110                 p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
111                 p_vout->i_bytes_per_pixel * 8, p_vout->i_bytes_per_line );
112
113     /* Initialize idle screen */
114     p_vout->last_display_date   = mdate();
115     p_vout->last_idle_date      = 0;
116
117 #ifdef STATS
118     /* Initialize statistics fields */
119     p_vout->render_time         = 0;    
120     p_vout->c_fps_samples       = 0;    
121 #endif      
122
123     /* Initialize buffer index */
124     p_vout->i_buffer_index      = 0;
125
126     /* Initialize pictures and subpictures - translation tables and functions
127      * will be initialized later in InitThread */    
128     for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++)
129     {
130         p_vout->p_picture[i_index].i_type   =   EMPTY_PICTURE;
131         p_vout->p_picture[i_index].i_status =   FREE_PICTURE;
132         p_vout->p_subpicture[i_index].i_type  = EMPTY_SUBPICTURE;
133         p_vout->p_subpicture[i_index].i_status= FREE_SUBPICTURE;
134     }
135    
136     /* Create and initialize system-dependant method - this function issues its
137      * own error messages */
138     if( vout_SysCreate( p_vout, psz_display, i_root_window ) )
139     {
140       free( p_vout );
141       return( NULL );
142     }
143     intf_DbgMsg("actual configuration: %dx%d, %d/%d bpp (%d Bpl), masks: 0x%x/0x%x/0x%x\n",
144                 p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
145                 p_vout->i_bytes_per_pixel * 8, p_vout->i_bytes_per_line,
146                 p_vout->i_red_mask, p_vout->i_green_mask, p_vout->i_blue_mask );
147
148     /* Calculate shifts from system-updated masks */
149     MaskToShift( &p_vout->i_red_lshift,   &p_vout->i_red_rshift,   p_vout->i_red_mask );
150     MaskToShift( &p_vout->i_green_lshift, &p_vout->i_green_rshift, p_vout->i_green_mask );
151     MaskToShift( &p_vout->i_blue_lshift,  &p_vout->i_blue_rshift,  p_vout->i_blue_mask );
152
153     /* Set some useful colors */
154     p_vout->i_white_pixel = RGB2PIXEL( p_vout, 255, 255, 255 );
155     p_vout->i_black_pixel = RGB2PIXEL( p_vout, 0, 0, 0 );
156     p_vout->i_gray_pixel  = RGB2PIXEL( p_vout, 128, 128, 128 );
157     p_vout->i_blue_pixel  = RGB2PIXEL( p_vout, 0, 0, 50 );    
158
159     /* Load fonts - fonts must be initialized after the systme method since
160      * they may be dependant of screen depth and other thread properties */
161     p_vout->p_default_font      = vout_LoadFont( VOUT_DEFAULT_FONT );    
162     if( p_vout->p_default_font == NULL )
163     {
164         vout_SysDestroy( p_vout );        
165         free( p_vout );        
166         return( NULL );        
167     }    
168     p_vout->p_large_font        = vout_LoadFont( VOUT_LARGE_FONT );        
169     if( p_vout->p_large_font == NULL )
170     {
171         vout_UnloadFont( p_vout->p_default_font );        
172         vout_SysDestroy( p_vout );        
173         free( p_vout );        
174         return( NULL );        
175     }     
176
177     /* Create thread and set locks */
178     vlc_mutex_init( &p_vout->picture_lock );
179     vlc_mutex_init( &p_vout->subpicture_lock );    
180     vlc_mutex_init( &p_vout->change_lock );    
181     vlc_mutex_lock( &p_vout->change_lock );    
182     if( vlc_thread_create( &p_vout->thread_id, "video output", (void *) RunThread, (void *) p_vout) )
183     {
184         intf_ErrMsg("error: %s\n", strerror(ENOMEM));
185         vout_UnloadFont( p_vout->p_default_font );
186         vout_UnloadFont( p_vout->p_large_font );        
187         vout_SysDestroy( p_vout );
188         free( p_vout );
189         return( NULL );
190     }   
191
192     intf_Msg("Video display initialized (%dx%d, %d/%d bpp)\n", p_vout->i_width, 
193              p_vout->i_height, p_vout->i_screen_depth, p_vout->i_bytes_per_pixel * 8 );
194
195     /* If status is NULL, wait until the thread is created */
196     if( pi_status == NULL )
197     {
198         do
199         {            
200             msleep( THREAD_SLEEP );
201         }while( (i_status != THREAD_READY) && (i_status != THREAD_ERROR) 
202                 && (i_status != THREAD_FATAL) );
203         if( i_status != THREAD_READY )
204         {
205             return( NULL );            
206         }        
207     }
208     return( p_vout );
209 }
210
211 /******************************************************************************
212  * vout_DestroyThread: destroys a previously created thread
213  ******************************************************************************
214  * Destroy a terminated thread. 
215  * The function will request a destruction of the specified thread. If pi_error
216  * is NULL, it will return once the thread is destroyed. Else, it will be 
217  * update using one of the THREAD_* constants.
218  ******************************************************************************/
219 void vout_DestroyThread( vout_thread_t *p_vout, int *pi_status )
220 {  
221     int     i_status;                                        /* thread status */
222
223     /* Set status */
224     intf_DbgMsg("\n");
225     p_vout->pi_status = (pi_status != NULL) ? pi_status : &i_status;
226     *p_vout->pi_status = THREAD_DESTROY;    
227      
228     /* Request thread destruction */
229     p_vout->b_die = 1;
230
231     /* If status is NULL, wait until thread has been destroyed */
232     if( pi_status == NULL )
233     {
234         do
235         {
236             msleep( THREAD_SLEEP );
237         }while( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR) 
238                 && (i_status != THREAD_FATAL) );   
239     }
240 }
241
242 /******************************************************************************
243  * vout_DisplaySubPicture: display a subpicture unit
244  ******************************************************************************
245  * Remove the reservation flag of an subpicture, which will cause it to be ready 
246  * for display. The picture does not need to be locked, since it is ignored by
247  * the output thread if is reserved.
248  ******************************************************************************/
249 void  vout_DisplaySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
250 {
251 #ifdef DEBUG_VIDEO
252     char        psz_begin_date[MSTRTIME_MAX_SIZE];  /* buffer for date string */
253     char        psz_end_date[MSTRTIME_MAX_SIZE];    /* buffer for date string */
254 #endif
255
256 #ifdef DEBUG
257     /* Check if status is valid */
258     if( p_subpic->i_status != RESERVED_SUBPICTURE )
259     {
260         intf_DbgMsg("error: subpicture %p has invalid status %d\n", p_subpic, 
261                     p_subpic->i_status );       
262     }   
263 #endif
264
265     /* Remove reservation flag */
266     p_subpic->i_status = READY_SUBPICTURE;
267
268 #ifdef DEBUG_VIDEO
269     /* Send subpicture informations */
270     intf_DbgMsg("subpicture %p: type=%d, begin date=%s, end date=%s\n", 
271                 p_subpic, p_subpic->i_type, 
272                 mstrtime( psz_begin_date, p_subpic->begin_date ), 
273                 mstrtime( psz_end_date, p_subpic->end_date ) );    
274 #endif
275 }
276
277 /******************************************************************************
278  * vout_CreateSubPicture: allocate an subpicture in the video output heap.
279  ******************************************************************************
280  * This function create a reserved subpicture in the video output heap. 
281  * A null pointer is returned if the function fails. This method provides an
282  * already allocated zone of memory in the spu data fields. It needs locking
283  * since several pictures can be created by several producers threads. 
284  ******************************************************************************/
285 subpicture_t *vout_CreateSubPicture( vout_thread_t *p_vout, int i_type, 
286                                      int i_size )
287 {
288     int                 i_subpic;                          /* subpicture index */
289     subpicture_t *      p_free_subpic = NULL;         /* first free subpicture */    
290     subpicture_t *      p_destroyed_subpic = NULL;   /* first destroyed subpic */
291
292     /* Get lock */
293     vlc_mutex_lock( &p_vout->subpicture_lock );
294
295     /* 
296      * Look for an empty place 
297      */
298     for( i_subpic = 0; i_subpic < VOUT_MAX_PICTURES; i_subpic++ )
299     {
300         if( p_vout->p_subpicture[i_subpic].i_status == DESTROYED_SUBPICTURE )
301         {
302             /* Subpicture is marked for destruction, but is still allocated */
303             if( (p_vout->p_subpicture[i_subpic].i_type  == i_type)   &&
304                 (p_vout->p_subpicture[i_subpic].i_size  >= i_size) )
305             {
306                 /* Memory size do match or is smaller : memory will not be reallocated, 
307                  * and function can end immediately - this is the best possible case, 
308                  * since no memory allocation needs to be done */
309                 p_vout->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE;
310 #ifdef DEBUG_VIDEO
311                 intf_DbgMsg("subpicture %p (in destroyed subpicture slot)\n", 
312                             &p_vout->p_subpicture[i_subpic] );                
313 #endif
314                 vlc_mutex_unlock( &p_vout->subpicture_lock );
315                 return( &p_vout->p_subpicture[i_subpic] );
316             }
317             else if( p_destroyed_subpic == NULL )
318             {
319                 /* Memory size do not match, but subpicture index will be kept in
320                  * case no other place are left */
321                 p_destroyed_subpic = &p_vout->p_subpicture[i_subpic];                
322             }       
323         }
324         else if( (p_free_subpic == NULL) && 
325                  (p_vout->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE ))
326         {
327             /* Subpicture is empty and ready for allocation */
328             p_free_subpic = &p_vout->p_subpicture[i_subpic];
329         }
330     }
331
332     /* If no free subpicture is available, use a destroyed subpicture */
333     if( (p_free_subpic == NULL) && (p_destroyed_subpic != NULL ) )
334     { 
335         /* No free subpicture or matching destroyed subpicture has been found, but
336          * a destroyed subpicture is still avalaible */
337         free( p_destroyed_subpic->p_data );        
338         p_free_subpic = p_destroyed_subpic;
339     }
340
341     /*
342      * Prepare subpicture
343      */
344     if( p_free_subpic != NULL )
345     {
346         /* Allocate memory */
347         switch( i_type )
348         {
349         case TEXT_SUBPICTURE:                               /* text subpicture */
350             p_free_subpic->p_data = malloc( i_size + 1 );             
351             break;
352 #ifdef DEBUG
353         default:
354             intf_DbgMsg("error: unknown subpicture type %d\n", i_type );
355             p_free_subpic->p_data   =  NULL;            
356             break;            
357 #endif    
358         }
359
360         if( p_free_subpic->p_data != NULL )
361         {                    /* Copy subpicture informations, set some default values */
362             p_free_subpic->i_type                      = i_type;
363             p_free_subpic->i_status                    = RESERVED_SUBPICTURE;
364             p_free_subpic->i_size                      = i_size;            
365             p_free_subpic->i_x                         = 0;
366             p_free_subpic->i_y                         = 0;             
367             p_free_subpic->i_width                     = 0;
368             p_free_subpic->i_height                    = 0;
369             p_free_subpic->i_horizontal_align          = CENTER_RALIGN;             
370             p_free_subpic->i_vertical_align            = CENTER_RALIGN;             
371         }
372         else
373         {
374             /* Memory allocation failed : set subpicture as empty */
375             p_free_subpic->i_type   =  EMPTY_SUBPICTURE;            
376             p_free_subpic->i_status =  FREE_SUBPICTURE;            
377             p_free_subpic =            NULL;            
378             intf_ErrMsg("warning: %s\n", strerror( ENOMEM ) );            
379         }
380         
381 #ifdef DEBUG_VIDEO
382         intf_DbgMsg("subpicture %p (in free subpicture slot)\n", p_free_subpic );        
383 #endif
384         vlc_mutex_unlock( &p_vout->subpicture_lock );
385         return( p_free_subpic );
386     }
387     
388     /* No free or destroyed subpicture could be found */
389     intf_DbgMsg( "warning: heap is full\n" );
390     vlc_mutex_unlock( &p_vout->subpicture_lock );
391     return( NULL );
392 }
393
394 /******************************************************************************
395  * vout_DestroySubPicture: remove a subpicture from the heap
396  ******************************************************************************
397  * This function frees a previously reserved subpicture.
398  * It is meant to be used when the construction of a picture aborted.
399  * This function does not need locking since reserved subpictures are ignored 
400  * by the output thread.
401  ******************************************************************************/
402 void vout_DestroySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
403 {
404 #ifdef DEBUG
405    /* Check if status is valid */
406    if( p_subpic->i_status != RESERVED_SUBPICTURE )
407    {
408        intf_DbgMsg("error: subpicture %p has invalid status %d\n", 
409                    p_subpic, p_subpic->i_status );       
410    }   
411 #endif
412
413     p_subpic->i_status = DESTROYED_SUBPICTURE;
414
415 #ifdef DEBUG_VIDEO
416     intf_DbgMsg("subpicture %p\n", p_subpic);    
417 #endif
418 }
419
420 /*******************************************************************************
421  * vout_DisplayPicture: display a picture
422  *******************************************************************************
423  * Remove the reservation flag of a picture, which will cause it to be ready for
424  * display. The picture won't be displayed until vout_DatePicture has been 
425  * called.
426  *******************************************************************************/
427 void  vout_DisplayPicture( vout_thread_t *p_vout, picture_t *p_pic )
428 {
429     vlc_mutex_lock( &p_vout->picture_lock );
430     switch( p_pic->i_status )
431     {
432     case RESERVED_PICTURE:        
433         p_pic->i_status = RESERVED_DISP_PICTURE;
434         break;        
435     case RESERVED_DATED_PICTURE:
436         p_pic->i_status = READY_PICTURE;
437         break;        
438 #ifdef DEBUG
439     default:        
440         intf_DbgMsg("error: picture %p has invalid status %d\n", p_pic, p_pic->i_status );    
441         break;        
442 #endif
443     }
444
445 #ifdef DEBUG_VIDEO
446     intf_DbgMsg("picture %p\n", p_pic);
447 #endif
448     vlc_mutex_unlock( &p_vout->picture_lock );
449 }
450
451 /*******************************************************************************
452  * vout_DatePicture: date a picture
453  *******************************************************************************
454  * Remove the reservation flag of a picture, which will cause it to be ready for
455  * display. The picture won't be displayed until vout_DisplayPicture has been 
456  * called.
457  *******************************************************************************/
458 void  vout_DatePicture( vout_thread_t *p_vout, picture_t *p_pic, mtime_t date )
459 {
460 #ifdef DEBUG_VIDEO
461     char        psz_date[MSTRTIME_MAX_SIZE];                           /* date */
462 #endif
463
464     vlc_mutex_lock( &p_vout->picture_lock );
465     p_pic->date = date;    
466     switch( p_pic->i_status )
467     {
468     case RESERVED_PICTURE:        
469         p_pic->i_status = RESERVED_DATED_PICTURE;
470         break;        
471     case RESERVED_DISP_PICTURE:
472         p_pic->i_status = READY_PICTURE;
473         break;        
474 #ifdef DEBUG
475     default:        
476         intf_DbgMsg("error: picture %p has invalid status %d\n", p_pic, p_pic->i_status );    
477         break;        
478 #endif
479     }
480
481 #ifdef DEBUG_VIDEO
482     intf_DbgMsg("picture %p, display date: %s\n", p_pic, mstrtime( psz_date, p_pic->date) );
483 #endif
484     vlc_mutex_unlock( &p_vout->picture_lock );
485 }
486
487 /******************************************************************************
488  * vout_CreatePicture: allocate a picture in the video output heap.
489  ******************************************************************************
490  * This function create a reserved image in the video output heap. 
491  * A null pointer is returned if the function fails. This method provides an
492  * already allocated zone of memory in the picture data fields. It needs locking
493  * since several pictures can be created by several producers threads. 
494  ******************************************************************************/
495 picture_t *vout_CreatePicture( vout_thread_t *p_vout, int i_type, 
496                                int i_width, int i_height )
497 {
498     int         i_picture;                                   /* picture index */
499     int         i_chroma_width = 0;                           /* chroma width */    
500     picture_t * p_free_picture = NULL;                  /* first free picture */    
501     picture_t * p_destroyed_picture = NULL;        /* first destroyed picture */    
502
503     /* Get lock */
504     vlc_mutex_lock( &p_vout->picture_lock );
505
506     /* 
507      * Look for an empty place 
508      */
509     for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
510     {
511         if( p_vout->p_picture[i_picture].i_status == DESTROYED_PICTURE )
512         {
513             /* Picture is marked for destruction, but is still allocated - note
514              * that if width and type are the same for two pictures, chroma_width 
515              * should also be the same */
516             if( (p_vout->p_picture[i_picture].i_type           == i_type)   &&
517                 (p_vout->p_picture[i_picture].i_height         == i_height) &&
518                 (p_vout->p_picture[i_picture].i_width          == i_width) )
519             {
520                 /* Memory size do match : memory will not be reallocated, and function
521                  * can end immediately - this is the best possible case, since no
522                  * memory allocation needs to be done */
523                 p_vout->p_picture[i_picture].i_status = RESERVED_PICTURE;
524 #ifdef DEBUG_VIDEO
525                 intf_DbgMsg("picture %p (in destroyed picture slot)\n", 
526                             &p_vout->p_picture[i_picture] );                
527 #endif
528                 vlc_mutex_unlock( &p_vout->picture_lock );
529                 return( &p_vout->p_picture[i_picture] );
530             }
531             else if( p_destroyed_picture == NULL )
532             {
533                 /* Memory size do not match, but picture index will be kept in
534                  * case no other place are left */
535                 p_destroyed_picture = &p_vout->p_picture[i_picture];                
536             }       
537         }
538         else if( (p_free_picture == NULL) && 
539                  (p_vout->p_picture[i_picture].i_status == FREE_PICTURE ))
540         {
541             /* Picture is empty and ready for allocation */
542             p_free_picture = &p_vout->p_picture[i_picture];            
543         }
544     }
545
546     /* If no free picture is available, use a destroyed picture */
547     if( (p_free_picture == NULL) && (p_destroyed_picture != NULL ) )
548     { 
549         /* No free picture or matching destroyed picture has been found, but
550          * a destroyed picture is still avalaible */
551         free( p_destroyed_picture->p_data );        
552         p_free_picture = p_destroyed_picture;        
553     }
554
555     /*
556      * Prepare picture
557      */
558     if( p_free_picture != NULL )
559     {
560         /* Allocate memory */
561         switch( i_type )
562         {
563         case YUV_420_PICTURE:          /* YUV 420: 1,1/4,1/4 samples per pixel */
564             i_chroma_width = i_width / 2;            
565             p_free_picture->p_data = malloc( i_height * i_chroma_width * 3 * sizeof( yuv_data_t ) );
566             p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
567             p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*4/2;
568             p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*5/2;
569             break;
570         case YUV_422_PICTURE:          /* YUV 422: 1,1/2,1/2 samples per pixel */
571             i_chroma_width = i_width / 2;            
572             p_free_picture->p_data = malloc( i_height * i_chroma_width * 4 * sizeof( yuv_data_t ) );
573             p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
574             p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*2;
575             p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*3;
576             break;
577         case YUV_444_PICTURE:              /* YUV 444: 1,1,1 samples per pixel */
578             i_chroma_width = i_width;            
579             p_free_picture->p_data = malloc( i_height * i_chroma_width * 3 * sizeof( yuv_data_t ) );
580             p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
581             p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width;
582             p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*2;
583             break;                
584 #ifdef DEBUG
585         default:
586             intf_DbgMsg("error: unknown picture type %d\n", i_type );
587             p_free_picture->p_data   =  NULL;            
588             break;            
589 #endif    
590         }
591
592         if( p_free_picture->p_data != NULL )
593         {        
594             /* Copy picture informations, set some default values */
595             p_free_picture->i_type                      = i_type;
596             p_free_picture->i_status                    = RESERVED_PICTURE;
597             p_free_picture->i_matrix_coefficients       = 1; 
598             p_free_picture->i_width                     = i_width;
599             p_free_picture->i_height                    = i_height;
600             p_free_picture->i_chroma_width              = i_chroma_width;            
601             p_free_picture->i_display_horizontal_offset = 0;
602             p_free_picture->i_display_vertical_offset   = 0;            
603             p_free_picture->i_display_width             = i_width;
604             p_free_picture->i_display_height            = i_height;
605             p_free_picture->i_aspect_ratio              = AR_SQUARE_PICTURE;            
606             p_free_picture->i_refcount                  = 0;            
607         }
608         else
609         {
610             /* Memory allocation failed : set picture as empty */
611             p_free_picture->i_type   =  EMPTY_PICTURE;            
612             p_free_picture->i_status =  FREE_PICTURE;            
613             p_free_picture =            NULL;            
614             intf_ErrMsg("warning: %s\n", strerror( ENOMEM ) );            
615         }
616         
617 #ifdef DEBUG_VIDEO
618         intf_DbgMsg("picture %p (in free picture slot)\n", p_free_picture );        
619 #endif
620         vlc_mutex_unlock( &p_vout->picture_lock );
621         return( p_free_picture );
622     }
623     
624     /* No free or destroyed picture could be found */
625     intf_DbgMsg( "warning: heap is full\n" );
626     vlc_mutex_unlock( &p_vout->picture_lock );
627     return( NULL );
628 }
629
630 /******************************************************************************
631  * vout_DestroyPicture: remove a permanent or reserved picture from the heap
632  ******************************************************************************
633  * This function frees a previously reserved picture or a permanent
634  * picture. It is meant to be used when the construction of a picture aborted.
635  * Note that the picture will be destroyed even if it is linked !
636  * This function does not need locking since reserved pictures are ignored by
637  * the output thread.
638  ******************************************************************************/
639 void vout_DestroyPicture( vout_thread_t *p_vout, picture_t *p_pic )
640 {
641 #ifdef DEBUG
642    /* Check if picture status is valid */
643    if( (p_pic->i_status != RESERVED_PICTURE) && 
644        (p_pic->i_status != RESERVED_DATED_PICTURE) &&
645        (p_pic->i_status != RESERVED_DISP_PICTURE) )
646    {
647        intf_DbgMsg("error: picture %p has invalid status %d\n", p_pic, p_pic->i_status );       
648    }   
649 #endif
650
651     p_pic->i_status = DESTROYED_PICTURE;
652
653 #ifdef DEBUG_VIDEO
654     intf_DbgMsg("picture %p\n", p_pic);    
655 #endif
656 }
657
658 /******************************************************************************
659  * vout_LinkPicture: increment reference counter of a picture
660  ******************************************************************************
661  * This function increment the reference counter of a picture in the video
662  * heap. It needs a lock since several producer threads can access the picture.
663  ******************************************************************************/
664 void vout_LinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
665 {
666     vlc_mutex_lock( &p_vout->picture_lock );
667     p_pic->i_refcount++;
668
669 #ifdef DEBUG_VIDEO
670     intf_DbgMsg("picture %p refcount=%d\n", p_pic, p_pic->i_refcount );    
671 #endif
672
673     vlc_mutex_unlock( &p_vout->picture_lock );
674 }
675
676 /******************************************************************************
677  * vout_UnlinkPicture: decrement reference counter of a picture
678  ******************************************************************************
679  * This function decrement the reference counter of a picture in the video heap.
680  ******************************************************************************/
681 void vout_UnlinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
682 {
683     vlc_mutex_lock( &p_vout->picture_lock );
684     p_pic->i_refcount--;
685
686 #ifdef DEBUG_VIDEO
687     if( p_pic->i_refcount < 0 )
688     {
689         intf_DbgMsg("error: refcount < 0\n");
690         p_pic->i_refcount = 0;        
691     }    
692 #endif
693
694     if( (p_pic->i_refcount == 0) && (p_pic->i_status == DISPLAYED_PICTURE) )
695     {
696         p_pic->i_status = DESTROYED_PICTURE;
697     }
698
699 #ifdef DEBUG_VIDEO
700     intf_DbgMsg("picture %p refcount=%d\n", p_pic, p_pic->i_refcount );    
701 #endif
702
703     vlc_mutex_unlock( &p_vout->picture_lock );
704 }
705
706 /******************************************************************************
707  * vout_SetBuffers: set buffers adresses
708  ******************************************************************************
709  * This function is called by system drivers to set buffers video memory 
710  * adresses.
711  ******************************************************************************/
712 void vout_SetBuffers( vout_thread_t *p_vout, void *p_buf1, void *p_buf2 )
713 {
714     /* No picture previously */
715     p_vout->p_buffer[0].i_pic_x =         0;
716     p_vout->p_buffer[0].i_pic_y =         0;
717     p_vout->p_buffer[0].i_pic_width =     0;
718     p_vout->p_buffer[0].i_pic_height =    0;
719     p_vout->p_buffer[1].i_pic_x =         0;
720     p_vout->p_buffer[1].i_pic_y =         0;
721     p_vout->p_buffer[1].i_pic_width =     0;
722     p_vout->p_buffer[1].i_pic_height =    0;
723
724     /* The first area covers all the screen */
725     p_vout->p_buffer[0].i_areas =                 1;
726     p_vout->p_buffer[0].pi_area_begin[0] =        0;
727     p_vout->p_buffer[0].pi_area_end[0] =          p_vout->i_height - 1;
728     p_vout->p_buffer[1].i_areas =                 1;
729     p_vout->p_buffer[1].pi_area_begin[0] =        0;
730     p_vout->p_buffer[1].pi_area_end[0] =          p_vout->i_height - 1;
731
732     /* Set adresses */
733     p_vout->p_buffer[0].p_data = p_buf1;    
734     p_vout->p_buffer[1].p_data = p_buf2;    
735 }
736
737 /*****************************************************************************
738  * vout_Pixel2RGB: return red, green and blue from pixel value
739  *****************************************************************************
740  * Return color values, in 0-255 range, of the decomposition of a pixel. This
741  * is a slow routine and should only be used for initialization phase.
742  *****************************************************************************/
743 void vout_Pixel2RGB( vout_thread_t *p_vout, u32 i_pixel, int *pi_red, int *pi_green, int *pi_blue )
744 {
745     *pi_red =   i_pixel & p_vout->i_red_mask;
746     *pi_green = i_pixel & p_vout->i_green_mask;
747     *pi_blue =  i_pixel & p_vout->i_blue_mask;
748 }
749
750 /* following functions are local */
751
752 /*****************************************************************************
753  * BinaryLog: computes the base 2 log of a binary value
754  *****************************************************************************
755  * This functions is used by MaskToShift, to get a bit index from a binary 
756  * value.
757  *****************************************************************************/
758 static int BinaryLog(u32 i)
759 {
760     int i_log;
761
762     i_log = 0;
763     if (i & 0xffff0000) 
764     {        
765         i_log = 16;
766     }    
767     if (i & 0xff00ff00) 
768     {        
769         i_log += 8;
770     }    
771     if (i & 0xf0f0f0f0) 
772     {        
773         i_log += 4;
774     }    
775     if (i & 0xcccccccc) 
776     {        
777         i_log += 2;
778     }    
779     if (i & 0xaaaaaaaa) 
780     {        
781         i_log++;
782     }    
783     if (i != ((u32)1 << i_log))
784     {        
785         intf_ErrMsg("internal error: binary log overflow\n");        
786     }    
787
788     return( i_log );
789 }
790
791 /*****************************************************************************
792  * MaskToShift: transform a color mask into right and left shifts
793  *****************************************************************************
794  * This function is used for obtaining color shifts from masks.
795  *****************************************************************************/
796 static void MaskToShift( int *pi_left, int *pi_right, u32 i_mask )
797 {
798     u32 i_low, i_high;                 /* lower hand higher bits of the mask */
799
800     /* Get bits */
801     i_low =  i_mask & (- i_mask);                   /* lower bit of the mask */
802     i_high = i_mask + i_low;                       /* higher bit of the mask */
803
804     /* Transform bits into an index */
805     i_low =  BinaryLog (i_low);
806     i_high = BinaryLog (i_high);
807
808     /* Update pointers and return */
809     *pi_left =   i_low;
810     *pi_right = (8 - i_high + i_low);
811 }
812
813 /******************************************************************************
814  * InitThread: initialize video output thread
815  ******************************************************************************
816  * This function is called from RunThread and performs the second step of the
817  * initialization. It returns 0 on success. Note that the thread's flag are not
818  * modified inside this function.
819  ******************************************************************************/
820 static int InitThread( vout_thread_t *p_vout )
821 {
822     /* Update status */
823     intf_DbgMsg("\n");
824     *p_vout->pi_status = THREAD_START;    
825
826     /* Initialize output method - this function issues its own error messages */
827     if( vout_SysInit( p_vout ) )
828     {
829         return( 1 );
830     } 
831
832     /* Initialize convertion tables and functions */
833     if( vout_InitYUV( p_vout ) )
834     {
835         intf_ErrMsg("error: can't allocate YUV translation tables\n");
836         return( 1 );                
837     }
838     
839     /* Mark thread as running and return */
840     p_vout->b_active =          1;    
841     *p_vout->pi_status =        THREAD_READY;    
842     intf_DbgMsg("thread ready\n");    
843     return( 0 );    
844 }
845
846 /******************************************************************************
847  * RunThread: video output thread
848  ******************************************************************************
849  * Video output thread. This function does only returns when the thread is
850  * terminated. It handles the pictures arriving in the video heap and the
851  * display device events.
852  ******************************************************************************/
853 static void RunThread( vout_thread_t *p_vout)
854 {
855     int             i_index;                                 /* index in heap */
856     mtime_t         current_date;                             /* current date */
857     mtime_t         display_date;                             /* display date */    
858     boolean_t       b_display;                                /* display flag */    
859     picture_t *     p_pic;                                 /* picture pointer */
860     subpicture_t *  p_subpic;                           /* subpicture pointer */    
861      
862     /* 
863      * Initialize thread
864      */
865     p_vout->b_error = InitThread( p_vout );
866     if( p_vout->b_error )
867     {
868         DestroyThread( p_vout, THREAD_ERROR );
869         return;        
870     }    
871     intf_DbgMsg("\n");
872
873     /*
874      * Main loop - it is not executed if an error occured during
875      * initialization
876      */
877     while( (!p_vout->b_die) && (!p_vout->b_error) )
878     {
879         /* Initialize loop variables */
880         p_pic =         NULL;
881         p_subpic =      NULL;
882         display_date =  0;        
883         current_date =  mdate();
884
885         /* 
886          * Find the picture to display - this operation does not need lock,
887          * since only READY_PICTUREs are handled 
888          */
889         for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
890         {
891             if( (p_vout->p_picture[i_index].i_status == READY_PICTURE) &&
892                 ( (p_pic == NULL) || 
893                   (p_vout->p_picture[i_index].date < display_date) ) )
894             {
895                 p_pic = &p_vout->p_picture[i_index];
896                 display_date = p_pic->date;                
897             }
898         }
899  
900         if( p_pic )
901         {
902 #ifdef STATS
903             /* Computes FPS rate */
904             p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ] = display_date;
905 #endif      
906             if( display_date < current_date )
907             {
908                 /* Picture is late: it will be destroyed and the thread will sleep and
909                  * go to next picture */
910                 vlc_mutex_lock( &p_vout->picture_lock );
911                 p_pic->i_status = p_pic->i_refcount ? DISPLAYED_PICTURE : DESTROYED_PICTURE;
912                 intf_DbgMsg( "warning: late picture %p skipped refcount=%d\n", p_pic, p_pic->i_refcount );
913                 vlc_mutex_unlock( &p_vout->picture_lock );
914                 p_pic =         NULL;                
915                 display_date =  0;                
916             }
917             else if( display_date > current_date + VOUT_DISPLAY_DELAY )
918             {
919                 /* A picture is ready to be rendered, but its rendering date is
920                  * far from the current one so the thread will perform an empty loop
921                  * as if no picture were found. The picture state is unchanged */
922                 p_pic =         NULL;                
923                 display_date =  0;                
924             }
925         }
926
927         /*
928          * Find the subpicture to display - this operation does not need lock, since
929          * only READY_SUBPICTURES are handled. If no picture has been selected,
930          * display_date will depend on the subpicture
931          */
932         //??
933
934         /*
935          * Perform rendering, sleep and display rendered picture
936          */
937         if( p_pic )                          /* picture and perhaps subpicture */
938         {
939             b_display = p_vout->b_active;            
940             p_vout->last_display_date = display_date;
941             
942             if( b_display )
943             {                
944                 /* Set picture dimensions and clear buffer */
945                 SetBufferPicture( p_vout, p_pic );
946
947                 /* Render picture and informations */
948                 RenderPicture( p_vout, p_pic );             
949                 if( p_vout->b_info )
950                 {
951                     RenderPictureInfo( p_vout, p_pic );
952                     RenderInfo( p_vout );                
953                 }
954             }
955             
956             /* Remove picture from heap */
957             vlc_mutex_lock( &p_vout->picture_lock );
958             p_pic->i_status = p_pic->i_refcount ? DISPLAYED_PICTURE : DESTROYED_PICTURE;
959             vlc_mutex_unlock( &p_vout->picture_lock );                          
960
961             /* Render interface and subpicture */
962             if( b_display && p_vout->b_interface )
963             {
964                 RenderInterface( p_vout );                
965             }
966             if( p_subpic )
967             {
968                 if( b_display )
969                 {                    
970                     RenderSubPicture( p_vout, p_subpic );
971                 }                
972
973                 /* Remove subpicture from heap */
974                 vlc_mutex_lock( &p_vout->subpicture_lock );
975                 p_subpic->i_status = DESTROYED_SUBPICTURE;
976                 vlc_mutex_unlock( &p_vout->subpicture_lock );                          
977             }
978
979         }
980         else if( p_subpic )                                /* subpicture alone */
981         {
982             b_display = p_vout->b_active;
983             p_vout->last_display_date = display_date;            
984
985             if( b_display )
986             {                
987                 /* Clear buffer */
988                 SetBufferPicture( p_vout, NULL );
989
990                 /* Render informations, interface and subpicture */
991                 if( p_vout->b_info )
992                 {
993                     RenderInfo( p_vout );
994                 }
995                 if( p_vout->b_interface )
996                 {
997                     RenderInterface( p_vout );
998                 }
999                 RenderSubPicture( p_vout, p_subpic );            
1000             }            
1001
1002             /* Remove subpicture from heap */
1003             vlc_mutex_lock( &p_vout->subpicture_lock );
1004             p_subpic->i_status = DESTROYED_SUBPICTURE;
1005             vlc_mutex_unlock( &p_vout->subpicture_lock );                          
1006         }
1007         else if( p_vout->b_active )          /* idle or interface screen alone */
1008         {
1009             if( p_vout->b_interface && 0 /* && ?? intf_change */ )
1010             {
1011                 /* Interface has changed, so a new rendering is required - force
1012                  * it by setting last idle date to 0 */
1013                 p_vout->last_idle_date = 0;                
1014             }
1015
1016             /* Render idle screen and update idle date, then render interface if
1017              * required */
1018             b_display = RenderIdle( p_vout );
1019             if( b_display )
1020             {                
1021                 p_vout->last_idle_date = current_date;
1022                 if( p_vout->b_interface )
1023                 {
1024                     RenderInterface( p_vout );                
1025                 }
1026             }
1027             
1028         }       
1029         else
1030         {
1031             b_display = 0;            
1032         }        
1033
1034         /*
1035          * Sleep, wake up and display rendered picture
1036          */
1037
1038 #ifdef STATS
1039         /* Store render time */
1040         p_vout->render_time = mdate() - current_date;
1041 #endif
1042
1043         /* Give back change lock */
1044         vlc_mutex_unlock( &p_vout->change_lock );        
1045
1046         /* Sleep a while or until a given date */
1047         if( display_date != 0 )
1048         {
1049             mwait( display_date );
1050         }
1051         else
1052         {
1053             msleep( VOUT_IDLE_SLEEP );                
1054         }            
1055
1056         /* On awakening, take back lock and send immediately picture to display, 
1057          * then swap buffers */
1058         vlc_mutex_lock( &p_vout->change_lock );        
1059 #ifdef DEBUG_VIDEO
1060         intf_DbgMsg( "picture %p, subpicture %p in buffer %d, display=%d\n", p_pic, p_subpic,
1061                      p_vout->i_buffer_index, b_display && !(p_vout->i_changes & VOUT_NODISPLAY_CHANGE) );        
1062 #endif            
1063         if( b_display && !(p_vout->i_changes & VOUT_NODISPLAY_CHANGE) )
1064         {
1065             vout_SysDisplay( p_vout );
1066             p_vout->i_buffer_index = ++p_vout->i_buffer_index & 1;
1067         }
1068
1069         /*
1070          * Check events and manage thread
1071          */
1072         if( vout_SysManage( p_vout ) | Manage( p_vout ) )
1073         {
1074             /* A fatal error occured, and the thread must terminate immediately,
1075              * without displaying anything - setting b_error to 1 cause the
1076              * immediate end of the main while() loop. */
1077             p_vout->b_error = 1;
1078         }  
1079     } 
1080
1081     /*
1082      * Error loop - wait until the thread destruction is requested
1083      */
1084     if( p_vout->b_error )
1085     {
1086         ErrorThread( p_vout );        
1087     }
1088
1089     /* End of thread */
1090     EndThread( p_vout );
1091     DestroyThread( p_vout, THREAD_OVER ); 
1092     intf_DbgMsg( "thread end\n" );
1093 }
1094
1095 /******************************************************************************
1096  * ErrorThread: RunThread() error loop
1097  ******************************************************************************
1098  * This function is called when an error occured during thread main's loop. The
1099  * thread can still receive feed, but must be ready to terminate as soon as
1100  * possible.
1101  ******************************************************************************/
1102 static void ErrorThread( vout_thread_t *p_vout )
1103 {
1104     /* Wait until a `die' order */
1105     intf_DbgMsg("\n");
1106     while( !p_vout->b_die )
1107     {
1108         /* Sleep a while */
1109         msleep( VOUT_IDLE_SLEEP );                
1110     }
1111 }
1112
1113 /*******************************************************************************
1114  * EndThread: thread destruction
1115  *******************************************************************************
1116  * This function is called when the thread ends after a sucessfull 
1117  * initialization. It frees all ressources allocated by InitThread.
1118  *******************************************************************************/
1119 static void EndThread( vout_thread_t *p_vout )
1120 {
1121     int     i_index;                                          /* index in heap */
1122             
1123     /* Store status */
1124     intf_DbgMsg("\n");
1125     *p_vout->pi_status = THREAD_END;    
1126
1127     /* Destroy all remaining pictures and subpictures */
1128     for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
1129     {
1130         if( p_vout->p_picture[i_index].i_status != FREE_PICTURE )
1131         {
1132             free( p_vout->p_picture[i_index].p_data );
1133         }
1134         if( p_vout->p_subpicture[i_index].i_status != FREE_SUBPICTURE )
1135         {
1136             free( p_vout->p_subpicture[i_index].p_data );            
1137         }        
1138     }
1139
1140     /* Destroy translation tables */
1141     vout_EndYUV( p_vout );  
1142     vout_SysEnd( p_vout );    
1143 }
1144
1145 /*******************************************************************************
1146  * DestroyThread: thread destruction
1147  *******************************************************************************
1148  * This function is called when the thread ends. It frees all ressources
1149  * allocated by CreateThread. Status is available at this stage.
1150  *******************************************************************************/
1151 static void DestroyThread( vout_thread_t *p_vout, int i_status )
1152 {  
1153     int *pi_status;                                           /* status adress */
1154     
1155     /* Store status adress */
1156     intf_DbgMsg("\n");
1157     pi_status = p_vout->pi_status;    
1158     
1159     /* Destroy thread structures allocated by Create and InitThread */
1160     vout_UnloadFont( p_vout->p_default_font );
1161     vout_UnloadFont( p_vout->p_large_font ); 
1162     vout_SysDestroy( p_vout );
1163     free( p_vout );
1164     *pi_status = i_status;    
1165 }
1166
1167 /*******************************************************************************
1168  * Print: print simple text on a picture
1169  *******************************************************************************
1170  * This function will print a simple text on the picture. It is designed to
1171  * print debugging or general informations.
1172  *******************************************************************************/
1173 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 )
1174 {
1175     int                 i_text_height;                    /* total text height */
1176     int                 i_text_width;                      /* total text width */
1177
1178     /* Update upper left coordinates according to alignment */
1179     vout_TextSize( p_vout->p_default_font, 0, psz_text, &i_text_width, &i_text_height );
1180     if( !Align( p_vout, &i_x, &i_y, i_text_width, i_text_height, i_h_align, i_v_align ) )
1181     {
1182         /* Set area and print text */
1183         SetBufferArea( p_vout, i_x, i_y, i_text_width, i_text_height );    
1184         vout_Print( p_vout->p_default_font, p_vout->p_buffer[ p_vout->i_buffer_index ].p_data + 
1185                     i_y * p_vout->i_bytes_per_line + i_x * p_vout->i_bytes_per_pixel,
1186                     p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line, 
1187                     p_vout->i_white_pixel, 0, 0, 
1188                     0, psz_text );
1189     }    
1190 }
1191
1192 /*******************************************************************************
1193  * SetBufferArea: activate an area in current buffer
1194  *******************************************************************************
1195  * This function is called when something is rendered on the current buffer.
1196  * It set the area as active and prepare it to be cleared on next rendering.
1197  * Pay attention to the fact that in this functions, i_h is in fact the end y
1198  * coordinate of the new area.
1199  *******************************************************************************/
1200 static void SetBufferArea( vout_thread_t *p_vout, int i_x, int i_y, int i_w, int i_h )
1201 {
1202     vout_buffer_t *     p_buffer;                            /* current buffer */
1203     int                 i_area_begin, i_area_end;   /* area vertical extension */
1204     int                 i_area, i_area_copy;                     /* area index */    
1205     int                 i_area_shift;              /* shift distance for areas */    
1206     
1207     /* Choose buffer and modify h to end of area position */
1208     p_buffer =  &p_vout->p_buffer[ p_vout->i_buffer_index ];    
1209     i_h +=      i_y - 1;
1210  
1211     /* 
1212      * Remove part of the area which is inside the picture - this is done
1213      * by calling again SetBufferArea with the correct areas dimensions.
1214      */
1215     if( (i_x >= p_buffer->i_pic_x) && (i_x + i_w <= p_buffer->i_pic_x + p_buffer->i_pic_width) )
1216     {
1217         i_area_begin =  p_buffer->i_pic_y;
1218         i_area_end =    i_area_begin + p_buffer->i_pic_height - 1;
1219
1220         if( ((i_y >= i_area_begin) && (i_y <= i_area_end)) ||
1221             ((i_h >= i_area_begin) && (i_h <= i_area_end)) ||
1222             ((i_y <  i_area_begin) && (i_h > i_area_end)) )
1223         {                    
1224             /* Keep the stripe above the picture, if any */
1225             if( i_y < i_area_begin )
1226             {
1227                 SetBufferArea( p_vout, i_x, i_y, i_w, i_area_begin - i_y );            
1228             }
1229             /* Keep the stripe below the picture, if any */
1230             if( i_h > i_area_end )
1231             {
1232                 SetBufferArea( p_vout, i_x, i_area_end, i_w, i_h - i_area_end );
1233             }        
1234             return;
1235         }        
1236     }   
1237
1238     /* Skip some extensions until interesting areas */
1239     for( i_area = 0; 
1240          (i_area < p_buffer->i_areas) &&
1241              (p_buffer->pi_area_end[i_area] + 1 <= i_y); 
1242          i_area++ )
1243     {
1244         ;        
1245     }
1246     
1247     if( i_area == p_buffer->i_areas )
1248     {
1249         /* New area is below all existing ones: just add it at the end of the 
1250          * array, if possible - else, append it to the last one */
1251         if( i_area < VOUT_MAX_AREAS )
1252         {
1253             p_buffer->pi_area_begin[i_area] = i_y;
1254             p_buffer->pi_area_end[i_area] = i_h;            
1255             p_buffer->i_areas++;            
1256         }
1257         else
1258         {
1259 #ifdef DEBUG_VIDEO
1260             intf_DbgMsg("areas overflow\n");            
1261 #endif
1262             p_buffer->pi_area_end[VOUT_MAX_AREAS - 1] = i_h;  
1263         }        
1264     }
1265     else 
1266     {
1267         i_area_begin =  p_buffer->pi_area_begin[i_area];
1268         i_area_end =    p_buffer->pi_area_end[i_area];
1269         
1270         if( i_y < i_area_begin ) 
1271         {
1272             if( i_h >= i_area_begin - 1 )
1273             {                
1274                 /* Extend area above */
1275                 p_buffer->pi_area_begin[i_area] = i_y;
1276             }
1277             else
1278             {
1279                 /* Create a new area above : merge last area if overflow, then 
1280                  * move all old areas down */
1281                 if( p_buffer->i_areas == VOUT_MAX_AREAS )
1282                 {                    
1283 #ifdef DEBUG_VIDEO
1284                     intf_DbgMsg("areas overflow\n");       
1285 #endif
1286                     p_buffer->pi_area_end[VOUT_MAX_AREAS - 2] = p_buffer->pi_area_end[VOUT_MAX_AREAS - 1];                    
1287                 }
1288                 else
1289                 {
1290                     p_buffer->i_areas++;                    
1291                 }
1292                 for( i_area_copy = p_buffer->i_areas - 1; i_area_copy > i_area; i_area_copy-- )
1293                 {
1294                     p_buffer->pi_area_begin[i_area_copy] = p_buffer->pi_area_begin[i_area_copy - 1];
1295                     p_buffer->pi_area_end[i_area_copy] =   p_buffer->pi_area_end[i_area_copy - 1];
1296                 }
1297                 p_buffer->pi_area_begin[i_area] = i_y;
1298                 p_buffer->pi_area_end[i_area] = i_h;
1299                 return;
1300             }              
1301         }
1302         if( i_h > i_area_end )
1303         {
1304             /* Find further areas which can be merged with the new one */
1305             for( i_area_copy = i_area + 1; 
1306                  (i_area_copy < p_buffer->i_areas) &&
1307                      (p_buffer->pi_area_begin[i_area] <= i_h);
1308                  i_area_copy++ )
1309             {
1310                 ;                
1311             }
1312             i_area_copy--;            
1313
1314             if( i_area_copy != i_area )
1315             {
1316                 /* Merge with last possible areas */
1317                 p_buffer->pi_area_end[i_area] = MAX( i_h, p_buffer->pi_area_end[i_area_copy] );
1318
1319                 /* Shift lower areas upward */
1320                 i_area_shift = i_area_copy - i_area;                
1321                 p_buffer->i_areas -= i_area_shift;
1322                 for( i_area_copy = i_area + 1; i_area_copy < p_buffer->i_areas; i_area_copy++ )
1323                 {
1324                     p_buffer->pi_area_begin[i_area_copy] = p_buffer->pi_area_begin[i_area_copy + i_area_shift];
1325                     p_buffer->pi_area_end[i_area_copy] =   p_buffer->pi_area_end[i_area_copy + i_area_shift];
1326                 }
1327             }
1328             else
1329             {
1330                 /* Extend area below */
1331                 p_buffer->pi_area_end[i_area] = i_h;
1332             }            
1333         }
1334     }
1335 }
1336
1337 /*******************************************************************************
1338  * SetBufferPicture: clear buffer and set picture area
1339  *******************************************************************************
1340  * This function is called before any rendering. It clears the current 
1341  * rendering buffer and set the new picture area. If the picture pointer is
1342  * NULL, then no picture area is defined. Floating operations are avoided since
1343  * some MMX calculations may follow.
1344  *******************************************************************************/
1345 static void SetBufferPicture( vout_thread_t *p_vout, picture_t *p_pic )
1346 {
1347     vout_buffer_t *     p_buffer;                            /* current buffer */
1348     int                 i_pic_x, i_pic_y;                  /* picture position */
1349     int                 i_pic_width, i_pic_height;       /* picture dimensions */    
1350     int                 i_old_pic_y, i_old_pic_height;     /* old picture area */    
1351     int                 i_vout_width, i_vout_height;     /* display dimensions */
1352     int                 i_area;                                  /* area index */    
1353     int                 i_data_index;                       /* area data index */    
1354     int                 i_data_size;     /* area data size, in 256 bytes blocs */
1355     u64 *               p_data;                     /* area data, for clearing */    
1356     byte_t *            p_data8;             /* area data, for clearing (slow) */
1357     
1358     /* Choose buffer and set display dimensions */
1359     p_buffer =          &p_vout->p_buffer[ p_vout->i_buffer_index ];    
1360     i_vout_width =      p_vout->i_width;
1361     i_vout_height =     p_vout->i_height;    
1362
1363     /*
1364      * Computes new picture size 
1365      */
1366     if( p_pic != NULL )
1367     {
1368         /* Try horizontal scaling first - width must be a mutiple of 16 */
1369         i_pic_width = (( p_vout->b_scale || (p_pic->i_width > i_vout_width)) ? 
1370                        i_vout_width : p_pic->i_width) & ~0xf;
1371         switch( p_pic->i_aspect_ratio )
1372         {
1373         case AR_3_4_PICTURE:
1374             i_pic_height = i_pic_width * 3 / 4;
1375             break;                
1376         case AR_16_9_PICTURE:
1377             i_pic_height = i_pic_width * 9 / 16;
1378             break;
1379         case AR_221_1_PICTURE:        
1380             i_pic_height = i_pic_width * 100 / 221;
1381             break;               
1382         case AR_SQUARE_PICTURE:
1383         default:
1384             i_pic_height = p_pic->i_height * i_pic_width / p_pic->i_width;            
1385             break;
1386         }
1387
1388         /* If picture dimensions using horizontal scaling are too large, use 
1389          * vertical scaling. Since width must be a multiple of 16, height is
1390          * adjusted again after. */
1391         if( i_pic_height > i_vout_height )
1392         {
1393             i_pic_height = ( p_vout->b_scale || (p_pic->i_height > i_vout_height)) ? 
1394                 i_vout_height : p_pic->i_height;
1395             switch( p_pic->i_aspect_ratio )
1396             {
1397             case AR_3_4_PICTURE:
1398                 i_pic_width = (i_pic_height * 4 / 3) & ~0xf;
1399                 i_pic_height = i_pic_width * 3 / 4;
1400                 break;                
1401             case AR_16_9_PICTURE:
1402                 i_pic_width = (i_pic_height * 16 / 9) & ~0xf;
1403                 i_pic_height = i_pic_width * 9 / 16;
1404                 break;
1405             case AR_221_1_PICTURE:        
1406                 i_pic_width = (i_pic_height * 221 / 100) & ~0xf;
1407                 i_pic_height = i_pic_width * 100 / 221;
1408                 break;               
1409             case AR_SQUARE_PICTURE:
1410             default:
1411                 i_pic_width = (p_pic->i_width * i_pic_height / p_pic->i_height) & ~0xf;
1412                 i_pic_height = p_pic->i_height * i_pic_width / p_pic->i_width;
1413                 break;
1414             }        
1415         }        
1416
1417         /* Set picture position */
1418         i_pic_x = (p_vout->i_width - i_pic_width) / 2;
1419         i_pic_y = (p_vout->i_height - i_pic_height) / 2;                
1420     }    
1421     else
1422     {
1423         /* No picture: size is 0 */
1424         i_pic_x =       0;
1425         i_pic_y =       0;
1426         i_pic_width =   0;
1427         i_pic_height =  0;
1428     }
1429
1430     /*
1431      * Set new picture size - if is is smaller than the previous one, clear 
1432      * around it. Since picture are centered, only their size is tested.
1433      */                                          
1434     if( (p_buffer->i_pic_width > i_pic_width) || (p_buffer->i_pic_height > i_pic_height) )
1435     {
1436         i_old_pic_y =            p_buffer->i_pic_y;
1437         i_old_pic_height =       p_buffer->i_pic_height;
1438         p_buffer->i_pic_x =      i_pic_x;
1439         p_buffer->i_pic_y =      i_pic_y;
1440         p_buffer->i_pic_width =  i_pic_width;
1441         p_buffer->i_pic_height = i_pic_height;                        
1442         SetBufferArea( p_vout, 0, i_old_pic_y, p_vout->i_width, i_old_pic_height );
1443     }
1444     else
1445     {
1446         p_buffer->i_pic_x =      i_pic_x;
1447         p_buffer->i_pic_y =      i_pic_y;
1448         p_buffer->i_pic_width =  i_pic_width;
1449         p_buffer->i_pic_height = i_pic_height;    
1450     }
1451
1452     /*
1453      * Clear areas
1454      */
1455     for( i_area = 0; i_area < p_buffer->i_areas; i_area++ )
1456     {
1457 #ifdef DEBUG_VIDEO    
1458         intf_DbgMsg("clearing picture %p area in buffer %d: %d-%d\n", p_pic, 
1459                     p_vout->i_buffer_index, p_buffer->pi_area_begin[i_area], p_buffer->pi_area_end[i_area] );
1460 #endif
1461         i_data_size = (p_buffer->pi_area_end[i_area] - p_buffer->pi_area_begin[i_area] + 1) * p_vout->i_bytes_per_line;
1462         p_data = (u64*) (p_buffer->p_data + p_vout->i_bytes_per_line * p_buffer->pi_area_begin[i_area]);
1463         for( i_data_index = i_data_size / 256; i_data_index-- ; )
1464         {
1465             /* Clear 256 bytes block */
1466             *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;
1467             *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;
1468             *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;
1469             *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;
1470             *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;
1471             *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;
1472             *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;
1473             *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;
1474         }
1475         for( i_data_index = (i_data_size % 256) / 16; i_data_index--; )
1476         {
1477             /* Clear remaining 16 bytes blocks */
1478             *p_data++ = 0;  *p_data++ = 0;            
1479         }
1480         p_data8 = (byte_t *)p_data;        
1481         for( i_data_index = i_data_size % 16; i_data_index--; )
1482         {
1483             /* Clear remaining bytes */
1484             *p_data8++ = 0;            
1485         }
1486     }    
1487
1488     /*
1489      * Clear areas array
1490      */
1491     p_buffer->i_areas = 0;
1492 }
1493
1494 /******************************************************************************
1495  * RenderPicture: render a picture
1496  ******************************************************************************
1497  * This function convert a picture from a video heap to a pixel-encoded image
1498  * and copy it to the current rendering buffer. No lock is required, since the
1499  * rendered picture has been determined as existant, and will only be destroyed
1500  * by the vout thread later.
1501  ******************************************************************************/
1502 static void RenderPicture( vout_thread_t *p_vout, picture_t *p_pic )
1503 {
1504 #ifdef DEBUG_VIDEO
1505     char                psz_date[MSTRTIME_MAX_SIZE];          /* picture date */    
1506     mtime_t             render_time;                /* picture rendering time */
1507 #endif
1508     vout_buffer_t *     p_buffer;                         /* rendering buffer */    
1509     byte_t *            p_pic_data;                 /* convertion destination */
1510     
1511     /* Get and set rendering informations */
1512     p_buffer =          &p_vout->p_buffer[ p_vout->i_buffer_index ];    
1513     p_pic_data =        p_buffer->p_data + 
1514         p_buffer->i_pic_x * p_vout->i_bytes_per_pixel +
1515         p_buffer->i_pic_y * p_vout->i_bytes_per_line;
1516 #ifdef DEBUG_VIDEO
1517     render_time = mdate();    
1518 #endif
1519
1520     /*
1521      * Choose appropriate rendering function and render picture 
1522      */
1523     switch( p_pic->i_type )
1524     {
1525     case YUV_420_PICTURE:
1526         p_vout->yuv.p_Convert420( p_vout, p_pic_data, 
1527                                   p_pic->p_y, p_pic->p_u, p_pic->p_v,
1528                                   p_pic->i_width, p_pic->i_height,
1529                                   p_buffer->i_pic_width, p_buffer->i_pic_height, 
1530                                   p_vout->i_bytes_per_line / p_vout->i_bytes_per_pixel,
1531                                   p_pic->i_matrix_coefficients );
1532         break;        
1533     case YUV_422_PICTURE:
1534         p_vout->yuv.p_Convert422( p_vout, p_pic_data, 
1535                                   p_pic->p_y, p_pic->p_u, p_pic->p_v,
1536                                   p_pic->i_width, p_pic->i_height,
1537                                   p_buffer->i_pic_width, p_buffer->i_pic_height, 
1538                                   p_vout->i_bytes_per_line / p_vout->i_bytes_per_pixel,
1539                                   p_pic->i_matrix_coefficients );
1540         break;        
1541     case YUV_444_PICTURE:
1542         p_vout->yuv.p_Convert444( p_vout, p_pic_data, 
1543                                   p_pic->p_y, p_pic->p_u, p_pic->p_v,
1544                                   p_pic->i_width, p_pic->i_height,
1545                                   p_buffer->i_pic_width, p_buffer->i_pic_height, 
1546                                   p_vout->i_bytes_per_line / p_vout->i_bytes_per_pixel,
1547                                   p_pic->i_matrix_coefficients );
1548         break;        
1549 #ifdef DEBUG
1550     default:        
1551         intf_DbgMsg("error: unknown picture type %d\n", p_pic->i_type );
1552         break;        
1553 #endif
1554     }
1555
1556 #ifdef DEBUG_VIDEO
1557     /* Print picture date and rendering time */
1558     intf_DbgMsg("picture %p rendered in buffer %d (%ld us), display date: %s\n", p_pic,
1559                 p_vout->i_buffer_index, (long) (mdate() - render_time), 
1560                 mstrtime( psz_date, p_pic->date ));
1561 #endif
1562 }
1563
1564 /******************************************************************************
1565  * RenderPictureInfo: print additionnal informations on a picture
1566  ******************************************************************************
1567  * This function will print informations such as fps and other picture
1568  * dependant informations.
1569  ******************************************************************************/
1570 static void RenderPictureInfo( vout_thread_t *p_vout, picture_t *p_pic )
1571 {
1572 #if defined(STATS) || defined(DEBUG)
1573     char        psz_buffer[256];                             /* string buffer */
1574 #endif
1575
1576 #ifdef STATS
1577     /* 
1578      * Print FPS rate in upper right corner 
1579      */
1580     if( p_vout->c_fps_samples > VOUT_FPS_SAMPLES )
1581     {        
1582         sprintf( psz_buffer, "%.2f fps", (double) VOUT_FPS_SAMPLES * 1000000 /
1583                  ( p_vout->p_fps_sample[ (p_vout->c_fps_samples - 1) % VOUT_FPS_SAMPLES ] -
1584                    p_vout->p_fps_sample[ p_vout->c_fps_samples % VOUT_FPS_SAMPLES ] ) );        
1585         Print( p_vout, 0, 0, RIGHT_RALIGN, TOP_RALIGN, psz_buffer );
1586     }
1587
1588     /* 
1589      * Print frames count and loop time in upper left corner 
1590      */
1591     sprintf( psz_buffer, "%ld frames   rendering: %ld us", 
1592              (long) p_vout->c_fps_samples, (long) p_vout->render_time );
1593     Print( p_vout, 0, 0, LEFT_RALIGN, TOP_RALIGN, psz_buffer );
1594 #endif
1595
1596 #ifdef DEBUG
1597     /*
1598      * Print picture information in lower right corner
1599      */
1600     sprintf( psz_buffer, "%s picture %dx%d (%dx%d%+d%+d %s) -> %dx%d+%d+%d",
1601              (p_pic->i_type == YUV_420_PICTURE) ? "4:2:0" :
1602              ((p_pic->i_type == YUV_422_PICTURE) ? "4:2:2" :
1603               ((p_pic->i_type == YUV_444_PICTURE) ? "4:4:4" : "ukn-type")),
1604              p_pic->i_width, p_pic->i_height,
1605              p_pic->i_display_width, p_pic->i_display_height,
1606              p_pic->i_display_horizontal_offset, p_pic->i_display_vertical_offset,
1607              (p_pic->i_aspect_ratio == AR_SQUARE_PICTURE) ? "sq" :
1608              ((p_pic->i_aspect_ratio == AR_3_4_PICTURE) ? "4:3" :
1609               ((p_pic->i_aspect_ratio == AR_16_9_PICTURE) ? "16:9" :
1610                ((p_pic->i_aspect_ratio == AR_221_1_PICTURE) ? "2.21:1" : "ukn-ar" ))),
1611              p_vout->p_buffer[ p_vout->i_buffer_index ].i_pic_width,
1612              p_vout->p_buffer[ p_vout->i_buffer_index ].i_pic_height,
1613              p_vout->p_buffer[ p_vout->i_buffer_index ].i_pic_x,
1614              p_vout->p_buffer[ p_vout->i_buffer_index ].i_pic_y );    
1615     Print( p_vout, 0, 0, RIGHT_RALIGN, BOTTOM_RALIGN, psz_buffer );
1616 #endif
1617 }
1618
1619 /******************************************************************************
1620  * RenderIdle: render idle picture
1621  ******************************************************************************
1622  * This function will print something on the screen. It will return 0 if 
1623  * nothing has been rendered, or 1 if something has been changed on the screen.
1624  * Note that if you absolutely want something to be printed, you will have
1625  * to force it by setting the last idle date to 0.
1626  * Unlike other rendering functions, this one calls the SetBufferPicture 
1627  * function when needed.
1628  ******************************************************************************/
1629 static int RenderIdle( vout_thread_t *p_vout )
1630 {
1631     int         i_x = 0, i_y = 0;                            /* text position */    
1632     int         i_width, i_height;                               /* text size */    
1633     mtime_t     current_date;                                 /* current date */
1634     const char *psz_text = "no stream";                    /* text to display */
1635     
1636     
1637     current_date = mdate();    
1638     if( (current_date - p_vout->last_display_date) > VOUT_IDLE_DELAY &&
1639         (current_date - p_vout->last_idle_date) > VOUT_IDLE_DELAY )
1640     {
1641         SetBufferPicture( p_vout, NULL );            
1642         vout_TextSize( p_vout->p_large_font, WIDE_TEXT | OUTLINED_TEXT, psz_text,
1643                        &i_width, &i_height );
1644         if( !Align( p_vout, &i_x, &i_y, i_width, i_height, CENTER_RALIGN, CENTER_RALIGN ) )
1645         {  
1646             vout_Print( p_vout->p_large_font, 
1647                         p_vout->p_buffer[ p_vout->i_buffer_index ].p_data +
1648                         i_x * p_vout->i_bytes_per_pixel + i_y * p_vout->i_bytes_per_line,
1649                         p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line,
1650                         p_vout->i_white_pixel, p_vout->i_gray_pixel, 0,
1651                         WIDE_TEXT | OUTLINED_TEXT, psz_text );        
1652             SetBufferArea( p_vout, i_x, i_y, i_width, i_height );
1653         }        
1654         return( 1 );        
1655     }
1656     return( 0 );    
1657 }
1658
1659 /******************************************************************************
1660  * RenderInfo: render additionnal informations
1661  ******************************************************************************
1662  * This function render informations which do not depend of the current picture
1663  * rendered.
1664  ******************************************************************************/
1665 static void RenderInfo( vout_thread_t *p_vout )
1666 {
1667 #ifdef DEBUG
1668     char        psz_buffer[256];                             /* string buffer */
1669     int         i_ready_pic = 0;                            /* ready pictures */
1670     int         i_reserved_pic = 0;                      /* reserved pictures */
1671     int         i_picture;                                   /* picture index */
1672 #endif
1673
1674 #ifdef DEBUG
1675     /* 
1676      * Print thread state in lower left corner  
1677      */
1678     for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
1679     {
1680         switch( p_vout->p_picture[i_picture].i_status )
1681         {
1682         case RESERVED_PICTURE:
1683         case RESERVED_DATED_PICTURE:
1684         case RESERVED_DISP_PICTURE:
1685             i_reserved_pic++;            
1686             break;            
1687         case READY_PICTURE:
1688             i_ready_pic++;            
1689             break;            
1690         }        
1691     }
1692     sprintf( psz_buffer, "pic: %d/%d/%d", 
1693              i_reserved_pic, i_ready_pic, VOUT_MAX_PICTURES );
1694     Print( p_vout, 0, 0, LEFT_RALIGN, BOTTOM_RALIGN, psz_buffer );    
1695 #endif
1696 }
1697
1698 /*******************************************************************************
1699  * RenderSubPicture: render a subpicture
1700  *******************************************************************************
1701  * This function render a sub picture unit.
1702  *******************************************************************************/
1703 static void RenderSubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
1704 {
1705     p_vout_font_t       p_font;                                   /* text font */    
1706     int                 i_width, i_height;            /* subpicture dimensions */    
1707     
1708     switch( p_subpic->i_type )
1709     {
1710     case TEXT_SUBPICTURE:                                  /* single line text */
1711         /* Select default font if not specified */
1712         p_font = p_subpic->type.text.p_font;
1713         if( p_font == NULL )
1714         {
1715             p_font = p_vout->p_default_font;            
1716         }
1717
1718         /* Computes text size (width and height fields are ignored) and print it */
1719         vout_TextSize( p_font, p_subpic->type.text.i_style, p_subpic->p_data, &i_width, &i_height );        
1720         if( !Align( p_vout, &p_subpic->i_x, &p_subpic->i_y, i_width, i_height,
1721                     p_subpic->i_horizontal_align, p_subpic->i_vertical_align ) )
1722         {
1723             vout_Print( p_font, p_vout->p_buffer[ p_vout->i_buffer_index ].p_data +
1724                         p_subpic->i_x * p_vout->i_bytes_per_pixel +
1725                         p_subpic->i_y * p_vout->i_bytes_per_line, 
1726                         p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line,
1727                         p_subpic->type.text.i_char_color, p_subpic->type.text.i_border_color,
1728                         p_subpic->type.text.i_bg_color, p_subpic->type.text.i_style,
1729                         p_subpic->p_data );            
1730             SetBufferArea( p_vout, p_subpic->i_x, p_subpic->i_y, i_width, i_height );            
1731         }        
1732         break;        
1733         
1734 #ifdef DEBUG
1735     default:
1736         intf_DbgMsg("error: unknown subpicture %p type %d\n", p_subpic, p_subpic->i_type );        
1737 #endif
1738     }
1739 }
1740
1741 /*******************************************************************************
1742  * RenderInterface: render the interface
1743  *******************************************************************************
1744  * This function render the interface, if any.
1745  *******************************************************************************/
1746 static void RenderInterface( vout_thread_t *p_vout )
1747 {
1748     int         i_height, i_text_height;              /* total and text height */
1749     int         i_width_1, i_width_2;                            /* text width */
1750     int         i_byte;                                          /* byte index */
1751     const char *psz_text_1 = "[1-9] Channel   [i]nfo   [c]olor     [g/G]amma";
1752     const char *psz_text_2 = "[+/-] Volume    [m]ute   [s]caling   [Q]uit";    
1753
1754     /* Get text size */
1755     vout_TextSize( p_vout->p_large_font, OUTLINED_TEXT, psz_text_1, &i_width_1, &i_height );
1756     vout_TextSize( p_vout->p_large_font, OUTLINED_TEXT, psz_text_2, &i_width_2, &i_text_height );
1757     i_height += i_text_height;
1758
1759     /* Render background */
1760     for( i_byte = (p_vout->i_height - i_height) * p_vout->i_bytes_per_line;
1761          i_byte < p_vout->i_height * p_vout->i_bytes_per_line;
1762          i_byte++ )
1763     {
1764         //?? noooo !
1765         p_vout->p_buffer[ p_vout->i_buffer_index ].p_data[ i_byte ] = p_vout->i_blue_pixel;
1766     }    
1767
1768     /* Render text, if not larger than screen */
1769     if( i_width_1 < p_vout->i_width )
1770     {        
1771         vout_Print( p_vout->p_large_font, p_vout->p_buffer[ p_vout->i_buffer_index ].p_data +
1772                     (p_vout->i_height - i_height) * p_vout->i_bytes_per_line,
1773                     p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line,
1774                     p_vout->i_white_pixel, p_vout->i_black_pixel, 0,
1775                     OUTLINED_TEXT, psz_text_1 );
1776     }
1777     if( i_width_2 < p_vout->i_width )
1778     {        
1779         vout_Print( p_vout->p_large_font, p_vout->p_buffer[ p_vout->i_buffer_index ].p_data +
1780                     (p_vout->i_height - i_height + i_text_height) * p_vout->i_bytes_per_line,
1781                     p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line,
1782                     p_vout->i_white_pixel, p_vout->i_black_pixel, 0,
1783                     OUTLINED_TEXT, psz_text_2 );
1784     }    
1785
1786     /* Activate modified area */
1787     SetBufferArea( p_vout, 0, p_vout->i_height - i_height, p_vout->i_width, i_height );
1788 }
1789
1790 /******************************************************************************
1791  * Manage: manage thread
1792  ******************************************************************************
1793  * This function will handle changes in thread configuration.
1794  ******************************************************************************/
1795 static int Manage( vout_thread_t *p_vout )
1796 {
1797 #ifdef DEBUG_VIDEO
1798     if( p_vout->i_changes )
1799     {
1800         intf_DbgMsg("changes: 0x%x (no display: 0x%x)\n", p_vout->i_changes, 
1801                     p_vout->i_changes & VOUT_NODISPLAY_CHANGE );        
1802     }    
1803 #endif
1804
1805     /* On gamma or grayscale change, rebuild tables */
1806     if( p_vout->i_changes & (VOUT_GAMMA_CHANGE | VOUT_GRAYSCALE_CHANGE | 
1807                              VOUT_YUV_CHANGE) )
1808     {
1809         if( vout_ResetYUV( p_vout ) )
1810         {
1811             intf_ErrMsg("error: can't rebuild convertion tables\n");            
1812             return( 1 );            
1813         }        
1814     }
1815
1816     /* Clear changes flags which does not need management or have been
1817      * handled */
1818     p_vout->i_changes &= ~(VOUT_GAMMA_CHANGE | VOUT_GRAYSCALE_CHANGE | 
1819                            VOUT_YUV_CHANGE   | VOUT_INFO_CHANGE | 
1820                            VOUT_INTF_CHANGE  | VOUT_SCALE_CHANGE );
1821
1822     /* Detect unauthorized changes */
1823     if( p_vout->i_changes )
1824     {
1825         /* Some changes were not acknowledged by vout_SysManage or this function,
1826          * it means they should not be authorized */
1827         intf_ErrMsg( "error: unauthorized changes in the video output thread\n" );        
1828         return( 1 );        
1829     }
1830     
1831     return( 0 );    
1832 }
1833
1834 /******************************************************************************
1835  * Align: align a subpicture in the screen
1836  ******************************************************************************
1837  * This function is used for rendering text or subpictures. It returns non 0
1838  * it the final aera is not fully included in display area. Return coordinates
1839  * are absolute.
1840  ******************************************************************************/
1841 static int Align( vout_thread_t *p_vout, int *pi_x, int *pi_y, 
1842                    int i_width, int i_height, int i_h_align, int i_v_align )
1843 {
1844     /* Align horizontally */
1845     switch( i_h_align )
1846     {
1847     case CENTER_ALIGN:
1848         *pi_x -= i_width / 2;        
1849         break;        
1850     case CENTER_RALIGN:
1851         *pi_x += (p_vout->i_width - i_width) / 2;        
1852         break;        
1853     case RIGHT_ALIGN:   
1854         *pi_x -= i_width;        
1855         break;        
1856     case RIGHT_RALIGN:
1857         *pi_x += p_vout->i_width - i_width;        
1858         break;        
1859     }
1860
1861     /* Align vertically */
1862     switch( i_v_align )
1863     {
1864     case CENTER_ALIGN:
1865         *pi_y -= i_height / 2;        
1866         break;
1867     case CENTER_RALIGN:
1868         *pi_y += (p_vout->i_height - i_height) / 2;        
1869         break;        
1870     case BOTTOM_ALIGN:
1871         *pi_y -= i_height;        
1872         break;        
1873     case BOTTOM_RALIGN:
1874         *pi_y += p_vout->i_height - i_height;        
1875         break;        
1876     case SUBTITLE_RALIGN:
1877         *pi_y += (p_vout->i_height + p_vout->p_buffer[ p_vout->i_buffer_index ].i_pic_y + 
1878                   p_vout->p_buffer[ p_vout->i_buffer_index ].i_pic_height - i_height) / 2;        
1879         break;        
1880     }
1881
1882     /* Return non 0 if clipping failed */
1883     return( (*pi_x < 0) || (*pi_y < 0) || 
1884             (*pi_x + i_width > p_vout->i_width) || (*pi_y + i_height > p_vout->i_height) );    
1885 }
1886
1887 /******************************************************************************
1888  * SetPalette: sets an 8 bpp palette
1889  ******************************************************************************
1890  * This function is just a prototype that does nothing. Architectures that
1891  * support palette allocation should override it.
1892  ******************************************************************************/
1893 static void     SetPalette        ( p_vout_thread_t p_vout, u16 *red,
1894                                     u16 *green, u16 *blue, u16 *transp )
1895 {
1896     intf_ErrMsg( "SetPalette: method does not support palette changing\n" );
1897 }
1898