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