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