]> git.sesse.net Git - vlc/blob - src/video_output/video_output.c
API vout_DateImage (mais �a ne marche pas)
[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_sys.h"
25 #include "video_yuv.h"
26 #include "intf_msg.h"
27 #include "main.h"
28
29 /*******************************************************************************
30  * Local prototypes
31  *******************************************************************************/
32 static int      InitThread              ( vout_thread_t *p_vout );
33 static void     RunThread               ( vout_thread_t *p_vout );
34 static void     ErrorThread             ( vout_thread_t *p_vout );
35 static void     EndThread               ( vout_thread_t *p_vout );
36 static void     RenderBlank             ( vout_thread_t *p_vout );
37 static int      RenderPicture           ( vout_thread_t *p_vout, picture_t *p_pic, boolean_t b_blank );
38 static int      RenderPictureInfo       ( vout_thread_t *p_vout, picture_t *p_pic, boolean_t b_blank );
39 static int      RenderIdle              ( vout_thread_t *p_vout, boolean_t b_blank );
40 static int      RenderInfo              ( vout_thread_t *p_vout, boolean_t b_balnk );
41 static int      Manage                  ( vout_thread_t *p_vout );
42
43 /*******************************************************************************
44  * vout_CreateThread: creates a new video output thread
45  *******************************************************************************
46  * This function creates a new video output thread, and returns a pointer
47  * to its description. On error, it returns NULL.
48  * If pi_status is NULL, then the function will block until the thread is ready.
49  * If not, it will be updated using one of the THREAD_* constants.
50  *******************************************************************************/
51 vout_thread_t * vout_CreateThread               ( char *psz_display, int i_root_window, 
52                                                   int i_width, int i_height, int *pi_status )
53 {
54     vout_thread_t * p_vout;                               /* thread descriptor */
55     int             i_status;                                 /* thread status */
56
57     /* Allocate descriptor */
58     p_vout = (vout_thread_t *) malloc( sizeof(vout_thread_t) );
59     if( p_vout == NULL )
60     {
61         intf_ErrMsg("error: %s\n", strerror(ENOMEM));        
62         return( NULL );
63     }
64
65     /* Initialize thread properties */
66     p_vout->b_die               = 0;
67     p_vout->b_error             = 0;    
68     p_vout->b_active            = 0;
69     p_vout->pi_status           = (pi_status != NULL) ? pi_status : &i_status;
70     *p_vout->pi_status          = THREAD_CREATE;    
71
72     /* Initialize some fields used by the system-dependant method - these fields will
73      * probably be modified by the method, and are only preferences */
74 #ifdef DEBUG
75     p_vout->b_info              = 1;    
76 #else
77     p_vout->b_info              = 0;    
78 #endif
79     p_vout->b_grayscale         = main_GetIntVariable( VOUT_GRAYSCALE_VAR, 
80                                                        VOUT_GRAYSCALE_DEFAULT );
81     p_vout->i_width             = i_width;
82     p_vout->i_height            = i_height;
83     p_vout->i_bytes_per_line    = i_width * 2;    
84     p_vout->i_screen_depth      = 15;
85     p_vout->i_bytes_per_pixel   = 2;
86     p_vout->i_horizontal_scale  = 0;
87     p_vout->i_vertical_scale    = 0;
88     p_vout->f_gamma             = VOUT_GAMMA;    
89     intf_DbgMsg("wished configuration: %dx%d,%d (%d bytes/pixel, %d bytes/line), scaling %+d:%+d, gray=%d\n",
90                 p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
91                 p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line,
92                 p_vout->i_horizontal_scale, p_vout->i_vertical_scale, p_vout->b_grayscale );
93    
94     /* Create and initialize system-dependant method - this function issues its
95      * own error messages */
96     if( vout_SysCreate( p_vout, psz_display, i_root_window ) )
97     {
98       free( p_vout );
99       return( NULL );
100     }
101     intf_DbgMsg("actual configuration: %dx%d,%d (%d bytes/pixel, %d bytes/line), scaling %+d:%+d, gray=%d\n",
102                 p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
103                 p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line,
104                 p_vout->i_horizontal_scale, p_vout->i_vertical_scale, p_vout->b_grayscale );
105  
106 #ifdef STATS
107     /* Initialize statistics fields */
108     p_vout->render_time           = 0;    
109     p_vout->c_fps_samples       = 0;    
110 #endif      
111
112     /* Initialize running properties */
113     p_vout->i_changes           = 0;
114     p_vout->last_picture_date   = 0;
115     p_vout->last_display_date   = 0;
116
117     /* Create thread and set locks */
118     vlc_mutex_init( &p_vout->picture_lock );
119     vlc_mutex_init( &p_vout->subtitle_lock );    
120     vlc_mutex_init( &p_vout->change_lock );    
121     vlc_mutex_lock( &p_vout->change_lock );    
122     if( vlc_thread_create( &p_vout->thread_id, "video output", (void *) RunThread, (void *) p_vout) )
123     {
124         intf_ErrMsg("error: %s\n", strerror(ENOMEM));
125         vout_SysDestroy( p_vout );
126         free( p_vout );
127         return( NULL );
128     }   
129
130     intf_Msg("Video: display initialized (%dx%d, %d bpp)\n", 
131              p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth );    
132
133     /* If status is NULL, wait until the thread is created */
134     if( pi_status == NULL )
135     {
136         do
137         {            
138             msleep( THREAD_SLEEP );
139         }while( (i_status != THREAD_READY) && (i_status != THREAD_ERROR) 
140                 && (i_status != THREAD_FATAL) );
141         if( i_status != THREAD_READY )
142         {
143             return( NULL );            
144         }        
145     }
146     return( p_vout );
147 }
148
149 /*******************************************************************************
150  * vout_DestroyThread: destroys a previously created thread
151  *******************************************************************************
152  * Destroy a terminated thread. 
153  * The function will request a destruction of the specified thread. If pi_error
154  * is NULL, it will return once the thread is destroyed. Else, it will be 
155  * update using one of the THREAD_* constants.
156  *******************************************************************************/
157 void vout_DestroyThread( vout_thread_t *p_vout, int *pi_status )
158 {  
159     int     i_status;                                         /* thread status */
160
161     /* Set status */
162     p_vout->pi_status = (pi_status != NULL) ? pi_status : &i_status;
163     *p_vout->pi_status = THREAD_DESTROY;    
164      
165     /* Request thread destruction */
166     p_vout->b_die = 1;
167
168     /* If status is NULL, wait until thread has been destroyed */
169     if( pi_status == NULL )
170     {
171         do
172         {
173             msleep( THREAD_SLEEP );
174         }while( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR) 
175                 && (i_status != THREAD_FATAL) );   
176     }
177 }
178
179 /*******************************************************************************
180  * vout_DisplaySubtitle: display a subtitle
181  *******************************************************************************
182  * Remove the reservation flag of a subtitle, which will cause it to be ready for
183  * display. The picture does not need to be locked, since it is ignored by
184  * the output thread if is reserved.
185  *******************************************************************************/
186 void  vout_DisplaySubtitle( vout_thread_t *p_vout, subtitle_t *p_sub )
187 {
188 #ifdef DEBUG_VIDEO
189     char        psz_begin_date[MSTRTIME_MAX_SIZE];   /* buffer for date string */
190     char        psz_end_date[MSTRTIME_MAX_SIZE];     /* buffer for date string */
191 #endif
192
193 #ifdef DEBUG
194     /* Check if status is valid */
195     if( p_sub->i_status != RESERVED_SUBTITLE )
196     {
197         intf_DbgMsg("error: subtitle %p has invalid status %d\n", p_sub, p_sub->i_status );       
198     }   
199 #endif
200
201     /* Remove reservation flag */
202     p_sub->i_status = READY_SUBTITLE;
203
204 #ifdef DEBUG_VIDEO
205     /* Send subtitle informations */
206     intf_DbgMsg("subtitle %p: type=%d, begin date=%s, end date=%s\n", p_sub, p_sub->i_type, 
207                 mstrtime( psz_begin_date, p_sub->begin_date ), 
208                 mstrtime( psz_end_date, p_sub->end_date ) );    
209 #endif
210 }
211
212 /*******************************************************************************
213  * vout_CreateSubtitle: allocate a subtitle in the video output heap.
214  *******************************************************************************
215  * This function create a reserved subtitle in the video output heap. 
216  * A null pointer is returned if the function fails. This method provides an
217  * already allocated zone of memory in the subtitle data fields. It needs locking
218  * since several pictures can be created by several producers threads. 
219  *******************************************************************************/
220 subtitle_t *vout_CreateSubtitle( vout_thread_t *p_vout, int i_type, 
221                                  int i_size )
222 {
223     //???
224 }
225
226 /*******************************************************************************
227  * vout_DestroySubtitle: remove a permanent or reserved subtitle from the heap
228  *******************************************************************************
229  * This function frees a previously reserved subtitle.
230  * It is meant to be used when the construction of a picture aborted.
231  * This function does not need locking since reserved subtitles are ignored by
232  * the output thread.
233  *******************************************************************************/
234 void vout_DestroySubtitle( vout_thread_t *p_vout, subtitle_t *p_sub )
235 {
236 #ifdef DEBUG
237    /* Check if subtitle status is valid */
238    if( p_sub->i_status != RESERVED_SUBTITLE )
239    {
240        intf_DbgMsg("error: subtitle %p has invalid status %d\n", p_sub, p_sub->i_status );       
241    }   
242 #endif
243
244     p_sub->i_status = DESTROYED_SUBTITLE;
245
246 #ifdef DEBUG_VIDEO
247     intf_DbgMsg("subtitle %p\n", p_sub);    
248 #endif
249 }
250
251 /*******************************************************************************
252  * vout_DisplayPicture: display a picture
253  *******************************************************************************
254  * Remove the reservation flag of a picture, which will cause it to be ready for
255  * display. The picture does not need to be locked, since it is ignored by
256  * the output thread if is reserved. The picture won't be displayed until 
257  * vout_DatePicture has been called.
258  *******************************************************************************/
259 void  vout_DisplayPicture( vout_thread_t *p_vout, picture_t *p_pic )
260 {
261 #ifdef DEBUG_VIDEO
262     char        psz_date[MSTRTIME_MAX_SIZE];         /* buffer for date string */
263 #endif
264
265 #ifdef DEBUG
266     /* Check if picture status is valid */
267     if( p_pic->i_status != RESERVED_PICTURE )
268     {
269         intf_DbgMsg("error: picture %p has invalid status %d\n", p_pic, p_pic->i_status );       
270     }   
271 #endif
272
273     /* Remove reservation flag */
274     p_pic->i_status = READY_PICTURE;
275
276 #ifdef DEBUG_VIDEO
277     /* Send picture informations */
278     intf_DbgMsg("picture %p: type=%d, %dx%d, date=%s\n", p_pic, p_pic->i_type, 
279                 p_pic->i_width,p_pic->i_height, mstrtime( psz_date, p_pic->date ) );    
280 #endif
281 }
282
283 /*******************************************************************************
284  * vout_DatePicture: date a picture
285  *******************************************************************************
286  * Remove the reservation flag of a picture, which will cause it to be ready for
287  * display. The picture does not need to be locked, since it is ignored by
288  * the output thread if is reserved. The picture won't be displayed until
289  * vout_DisplayPicture has been called.
290  *******************************************************************************/
291 void  vout_DatePicture( vout_thread_t *p_vout, picture_t *p_pic, mtime_t date )
292 {
293     //??
294 }
295
296 /*******************************************************************************
297  * vout_CreatePicture: allocate a picture in the video output heap.
298  *******************************************************************************
299  * This function create a reserved image in the video output heap. 
300  * A null pointer is returned if the function fails. This method provides an
301  * already allocated zone of memory in the picture data fields. It needs locking
302  * since several pictures can be created by several producers threads. 
303  *******************************************************************************/
304 picture_t *vout_CreatePicture( vout_thread_t *p_vout, int i_type, 
305                                int i_width, int i_height )
306 {
307     int         i_picture;                                    /* picture index */
308     int         i_chroma_width = 0;                            /* chroma width */    
309     picture_t * p_free_picture = NULL;                   /* first free picture */    
310     picture_t * p_destroyed_picture = NULL;         /* first destroyed picture */    
311
312     /* Get lock */
313     vlc_mutex_lock( &p_vout->picture_lock );
314
315     /* 
316      * Look for an empty place 
317      */
318     for( i_picture = 0; 
319          i_picture < VOUT_MAX_PICTURES; 
320          i_picture++ )
321     {
322         if( p_vout->p_picture[i_picture].i_status == DESTROYED_PICTURE )
323         {
324             /* Picture is marked for destruction, but is still allocated - note
325              * that if width and type are the same for two pictures, chroma_width 
326              * should also be the same */
327             if( (p_vout->p_picture[i_picture].i_type           == i_type)   &&
328                 (p_vout->p_picture[i_picture].i_height         == i_height) &&
329                 (p_vout->p_picture[i_picture].i_width          == i_width) )
330             {
331                 /* Memory size do match : memory will not be reallocated, and function
332                  * can end immediately - this is the best possible case, since no
333                  * memory allocation needs to be done */
334                 p_vout->p_picture[i_picture].i_status = RESERVED_PICTURE;
335 #ifdef DEBUG_VIDEO
336                 intf_DbgMsg("picture %p (in destroyed picture slot)\n", 
337                             &p_vout->p_picture[i_picture] );                
338 #endif
339                 vlc_mutex_unlock( &p_vout->picture_lock );
340                 return( &p_vout->p_picture[i_picture] );
341             }
342             else if( p_destroyed_picture == NULL )
343             {
344                 /* Memory size do not match, but picture index will be kept in
345                  * case no other place are left */
346                 p_destroyed_picture = &p_vout->p_picture[i_picture];                
347             }       
348         }
349         else if( (p_free_picture == NULL) && 
350                  (p_vout->p_picture[i_picture].i_status == FREE_PICTURE ))
351         {
352             /* Picture is empty and ready for allocation */
353             p_free_picture = &p_vout->p_picture[i_picture];            
354         }
355     }
356
357     /* If no free picture is available, use a destroyed picture */
358     if( (p_free_picture == NULL) && (p_destroyed_picture != NULL ) )
359     { 
360         /* No free picture or matching destroyed picture has been found, but
361          * a destroyed picture is still avalaible */
362         free( p_destroyed_picture->p_data );        
363         p_free_picture = p_destroyed_picture;        
364     }
365
366     /*
367      * Prepare picture
368      */
369     if( p_free_picture != NULL )
370     {
371         /* Allocate memory */
372         switch( i_type )
373         {
374         case YUV_420_PICTURE:          /* YUV 420: 1,1/4,1/4 samples per pixel */
375             i_chroma_width = i_width / 2;            
376             p_free_picture->p_data = malloc( i_height * i_chroma_width * 3 * sizeof( yuv_data_t ) );
377             p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
378             p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*4/2;
379             p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*5/2;
380             break;
381         case YUV_422_PICTURE:          /* YUV 422: 1,1/2,1/2 samples per pixel */
382             i_chroma_width = i_width / 2;            
383             p_free_picture->p_data = malloc( i_height * i_chroma_width * 4 * sizeof( yuv_data_t ) );
384             p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
385             p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*2;
386             p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*3;
387             break;
388         case YUV_444_PICTURE:              /* YUV 444: 1,1,1 samples per pixel */
389             i_chroma_width = i_width;            
390             p_free_picture->p_data = malloc( i_height * i_chroma_width * 3 * sizeof( yuv_data_t ) );
391             p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
392             p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width;
393             p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*2;
394             break;                
395 #ifdef DEBUG
396         default:
397             intf_DbgMsg("error: unknown picture type %d\n", i_type );
398             p_free_picture->p_data   =  NULL;            
399             break;            
400 #endif    
401         }
402
403         if( p_free_picture->p_data != NULL )
404         {        
405             /* Copy picture informations, set some default values */
406             p_free_picture->i_type                      = i_type;
407             p_free_picture->i_status                    = RESERVED_PICTURE;
408             p_free_picture->i_matrix_coefficients       = 1; 
409             p_free_picture->i_width                     = i_width;
410             p_free_picture->i_height                    = i_height;
411             p_free_picture->i_chroma_width              = i_chroma_width;            
412             p_free_picture->i_display_horizontal_offset = 0;
413             p_free_picture->i_display_vertical_offset   = 0;            
414             p_free_picture->i_display_width             = i_width;
415             p_free_picture->i_display_height            = i_height;
416             p_free_picture->i_aspect_ratio              = AR_SQUARE_PICTURE;            
417             p_free_picture->i_refcount                  = 0;            
418         }
419         else
420         {
421             /* Memory allocation failed : set picture as empty */
422             p_free_picture->i_type   =  EMPTY_PICTURE;            
423             p_free_picture->i_status =  FREE_PICTURE;            
424             p_free_picture =            NULL;            
425             intf_ErrMsg("warning: %s\n", strerror( ENOMEM ) );            
426         }
427         
428 #ifdef DEBUG_VIDEO
429         intf_DbgMsg("picture %p (in free picture slot)\n", p_free_picture );        
430 #endif
431         vlc_mutex_unlock( &p_vout->picture_lock );
432         return( p_free_picture );
433     }
434     
435     // No free or destroyed picture could be found
436     intf_DbgMsg( "warning: heap is full\n" );
437     vlc_mutex_unlock( &p_vout->picture_lock );
438     return( NULL );
439 }
440
441 /*******************************************************************************
442  * vout_DestroyPicture: remove a permanent or reserved picture from the heap
443  *******************************************************************************
444  * This function frees a previously reserved picture or a permanent
445  * picture. It is meant to be used when the construction of a picture aborted.
446  * Note that the picture will be destroyed even if it is linked !
447  * This function does not need locking since reserved pictures are ignored by
448  * the output thread.
449  *******************************************************************************/
450 void vout_DestroyPicture( vout_thread_t *p_vout, picture_t *p_pic )
451 {
452 #ifdef DEBUG
453    /* Check if picture status is valid */
454    if( p_pic->i_status != RESERVED_PICTURE )
455    {
456        intf_DbgMsg("error: picture %p has invalid status %d\n", p_pic, p_pic->i_status );       
457    }   
458 #endif
459
460     p_pic->i_status = DESTROYED_PICTURE;
461
462 #ifdef DEBUG_VIDEO
463     intf_DbgMsg("picture %p\n", p_pic);    
464 #endif
465 }
466
467 /*******************************************************************************
468  * vout_LinkPicture: increment reference counter of a picture
469  *******************************************************************************
470  * This function increment the reference counter of a picture in the video
471  * heap. It needs a lock since several producer threads can access the picture.
472  *******************************************************************************/
473 void vout_LinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
474 {
475     vlc_mutex_lock( &p_vout->picture_lock );
476     p_pic->i_refcount++;
477     vlc_mutex_unlock( &p_vout->picture_lock );
478
479 #ifdef DEBUG_VIDEO
480     intf_DbgMsg("picture %p\n", p_pic);    
481 #endif
482 }
483
484 /*******************************************************************************
485  * vout_UnlinkPicture: decrement reference counter of a picture
486  *******************************************************************************
487  * This function decrement the reference counter of a picture in the video heap.
488  *******************************************************************************/
489 void vout_UnlinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
490 {
491     vlc_mutex_lock( &p_vout->picture_lock );
492     p_pic->i_refcount--;
493
494 #ifdef DEBUG_VIDEO
495     if( p_pic->i_refcount < 0 )
496     {
497         intf_DbgMsg("error: refcount < 0\n");
498         p_pic->i_refcount = 0;        
499     }    
500 #endif
501
502     if( (p_pic->i_refcount == 0) && (p_pic->i_status == DISPLAYED_PICTURE) )
503     {
504         p_pic->i_status = DESTROYED_PICTURE;
505     }
506     vlc_mutex_unlock( &p_vout->picture_lock );
507
508 #ifdef DEBUG_VIDEO
509     intf_DbgMsg("picture %p\n", p_pic);    
510 #endif
511 }
512
513 /* following functions are local */
514
515 /*******************************************************************************
516  * InitThread: initialize video output thread
517  *******************************************************************************
518  * This function is called from RunThread and performs the second step of the
519  * initialization. It returns 0 on success. Note that the thread's flag are not
520  * modified inside this function.
521  *******************************************************************************/
522 static int InitThread( vout_thread_t *p_vout )
523 {
524     int     i_index;                                          /* generic index */    
525
526     /* Update status */
527     *p_vout->pi_status = THREAD_START;    
528
529     /* Initialize output method - this function issues its own error messages */
530     if( vout_SysInit( p_vout ) )
531     {
532         *p_vout->pi_status = THREAD_ERROR;        
533         return( 1 );
534     } 
535
536     /* Initialize pictures and subtitles */    
537     for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++)
538     {
539         p_vout->p_picture[i_index].i_type   = EMPTY_PICTURE;
540         p_vout->p_picture[i_index].i_status = FREE_PICTURE;
541         p_vout->p_subtitle[i_index].i_type  = EMPTY_SUBTITLE;
542         p_vout->p_subtitle[i_index].i_status= FREE_SUBTITLE;
543     }
544
545     /* Initialize convertion tables and functions */
546     if( vout_InitTables( p_vout ) )
547     {
548         intf_ErrMsg("error: can't allocate translation tables\n");
549         return( 1 );                
550     }
551     
552     /* Mark thread as running and return */
553     p_vout->b_active =          1;    
554     *p_vout->pi_status =        THREAD_READY;    
555     intf_DbgMsg("thread ready\n");    
556     return( 0 );    
557 }
558
559 /*******************************************************************************
560  * RunThread: video output thread
561  *******************************************************************************
562  * Video output thread. This function does only returns when the thread is
563  * terminated. It handles the pictures arriving in the video heap and the
564  * display device events.
565  *******************************************************************************/
566 static void RunThread( vout_thread_t *p_vout)
567 {
568     int             i_picture;                                /* picture index */
569     mtime_t         current_date;                              /* current date */
570     mtime_t         pic_date = 0;                              /* picture date */    
571     boolean_t       b_display;                                 /* display flag */    
572     picture_t *     p_pic;                                  /* picture pointer */
573      
574     /* 
575      * Initialize thread and free configuration 
576      */
577     p_vout->b_error = InitThread( p_vout );
578     if( p_vout->b_error )
579     {
580         free( p_vout );                                  /* destroy descriptor */
581         return;        
582     }    
583
584     /*
585      * Main loop - it is not executed if an error occured during
586      * initialization
587      */
588     while( (!p_vout->b_die) && (!p_vout->b_error) )
589     {            
590         /* 
591          * Find the picture to display - this operation does not need lock,
592          * since only READY_PICTURES are handled 
593          */
594         p_pic = NULL;         
595         current_date = mdate();
596         for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
597         {
598             if( (p_vout->p_picture[i_picture].i_status == READY_PICTURE) &&
599                 ( (p_pic == NULL) || 
600                   (p_vout->p_picture[i_picture].date < pic_date) ) )
601             {
602                 p_pic = &p_vout->p_picture[i_picture];
603                 pic_date = p_pic->date;                
604             }
605         }
606  
607         /* 
608          * Render picture if any
609          */
610         if( p_pic )
611         {
612 #ifdef STATS
613             /* Computes FPS rate */
614             p_vout->fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ] = pic_date;
615 #endif      
616             if( pic_date < current_date )
617             {
618                 /* Picture is late: it will be destroyed and the thread will sleep and
619                  * go to next picture */
620                 vlc_mutex_lock( &p_vout->picture_lock );
621                 p_pic->i_status = p_pic->i_refcount ? DISPLAYED_PICTURE : DESTROYED_PICTURE;
622                 vlc_mutex_unlock( &p_vout->picture_lock );
623 #ifdef DEBUG_VIDEO
624                 intf_DbgMsg( "warning: late picture %p skipped\n", p_pic );
625 #endif
626                 p_pic =         NULL;                
627             }
628             else if( pic_date > current_date + VOUT_DISPLAY_DELAY )
629             {
630                 /* A picture is ready to be rendered, but its rendering date is
631                  * far from the current one so the thread will perform an empty loop
632                  * as if no picture were found. The picture state is unchanged */
633                 p_pic =         NULL;                
634             }
635         }
636               
637         /*
638          * Perform rendering, sleep and display rendered picture
639          */
640         if( p_pic )
641         {
642             /* A picture is ready to be displayed : render it */
643             if( p_vout->b_active )
644             {                    
645                 b_display = RenderPicture( p_vout, p_pic, 1 );
646                 if( p_vout->b_info )
647                 {
648                     b_display |= RenderPictureInfo( p_vout, p_pic, b_display );
649                     b_display |= RenderInfo( p_vout, b_display );                    
650                 }                    
651             }
652             else
653             {
654                 b_display = 0;                
655             } 
656
657             /* Remove picture from heap */
658             vlc_mutex_lock( &p_vout->picture_lock );
659             p_pic->i_status = p_pic->i_refcount ? DISPLAYED_PICTURE : DESTROYED_PICTURE;
660             vlc_mutex_unlock( &p_vout->picture_lock );                          
661         }            
662         else
663         {
664             /* No picture. However, an idle screen may be ready to display */
665             if( p_vout->b_active )
666             {                
667                 b_display = RenderIdle( p_vout, 1 );
668                 if( p_vout->b_info )
669                 {                    
670                     b_display |= RenderInfo( p_vout, b_display );
671                 }        
672             }
673             else
674             {
675                 b_display = 0;                
676             }            
677         }
678
679         /* Give back change lock */
680         vlc_mutex_unlock( &p_vout->change_lock );        
681
682         /* Sleep a while or until a given date */
683         if( p_pic )
684         {
685             mwait( pic_date );
686         }
687         else
688         {
689             msleep( VOUT_IDLE_SLEEP );                
690         }            
691
692         /* On awakening, take back lock and send immediately picture to display */
693         vlc_mutex_lock( &p_vout->change_lock );        
694         if( b_display && p_vout->b_active && 
695             !(p_vout->i_changes & VOUT_NODISPLAY_CHANGE) )
696         {
697             vout_SysDisplay( p_vout );
698         }
699
700         /*
701          * Check events and manage thread
702          */
703         if( vout_SysManage( p_vout ) | Manage( p_vout ) )
704         {
705             /* A fatal error occured, and the thread must terminate immediately,
706              * without displaying anything - setting b_error to 1 cause the
707              * immediate end of the main while() loop. */
708             p_vout->b_error = 1;
709         }  
710     } 
711
712     /*
713      * Error loop
714      */
715     if( p_vout->b_error )
716     {
717         ErrorThread( p_vout );        
718     }
719
720     /* End of thread */
721     EndThread( p_vout );
722     intf_DbgMsg( "thread end\n" );
723 }
724
725 /*******************************************************************************
726  * ErrorThread: RunThread() error loop
727  *******************************************************************************
728  * This function is called when an error occured during thread main's loop. The
729  * thread can still receive feed, but must be ready to terminate as soon as
730  * possible.
731  *******************************************************************************/
732 static void ErrorThread( vout_thread_t *p_vout )
733 {
734     /* Wait until a `die' order */
735     while( !p_vout->b_die )
736     {
737         /* Sleep a while */
738         msleep( VOUT_IDLE_SLEEP );                
739     }
740 }
741
742 /*******************************************************************************
743  * EndThread: thread destruction
744  *******************************************************************************
745  * This function is called when the thread ends after a sucessfull 
746  * initialization.
747  *******************************************************************************/
748 static void EndThread( vout_thread_t *p_vout )
749 {
750     int *   pi_status;                                        /* thread status */
751     int     i_picture;
752         
753     /* Store status */
754     pi_status = p_vout->pi_status;    
755     *pi_status = THREAD_END;    
756
757     /* Destroy all remaining pictures */
758     for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
759     {
760         if( p_vout->p_picture[i_picture].i_status != FREE_PICTURE )
761         {
762             free( p_vout->p_picture[i_picture].p_data );
763         }
764     }
765
766     /* Destroy translation tables */
767     vout_EndTables( p_vout );
768     
769     /* Destroy thread structures allocated by InitThread */
770     vout_SysEnd( p_vout );
771     vout_SysDestroy( p_vout );
772     free( p_vout );
773
774     /* Update status */
775     *pi_status = THREAD_OVER;    
776 }
777
778 /*******************************************************************************
779  * RenderBlank: render a blank screen
780  *******************************************************************************
781  * This function is called by all other rendering functions when they arrive on
782  * a non blanked screen.
783  *******************************************************************************/
784 static void RenderBlank( vout_thread_t *p_vout )
785 {
786     //?? toooooo slow
787     int  i_index;                                    /* current 32 bits sample */    
788     int  i_width;                                 /* number of 32 bits samples */    
789     u32 *p_pic;                                  /* pointer to 32 bits samples */
790     
791     /* Initialize variables */
792     p_pic =     vout_SysGetPicture( p_vout );
793     i_width =   p_vout->i_bytes_per_line * p_vout->i_height / 128;
794
795     /* Clear beginning of screen by 128 bytes blocks */
796     for( i_index = 0; i_index < i_width; i_index++ )
797     {
798         *p_pic++ = 0;   *p_pic++ = 0;
799         *p_pic++ = 0;   *p_pic++ = 0;
800         *p_pic++ = 0;   *p_pic++ = 0;
801         *p_pic++ = 0;   *p_pic++ = 0;
802         *p_pic++ = 0;   *p_pic++ = 0;
803         *p_pic++ = 0;   *p_pic++ = 0;
804         *p_pic++ = 0;   *p_pic++ = 0;
805         *p_pic++ = 0;   *p_pic++ = 0;
806         *p_pic++ = 0;   *p_pic++ = 0;
807         *p_pic++ = 0;   *p_pic++ = 0;
808         *p_pic++ = 0;   *p_pic++ = 0;
809         *p_pic++ = 0;   *p_pic++ = 0;
810         *p_pic++ = 0;   *p_pic++ = 0;
811         *p_pic++ = 0;   *p_pic++ = 0;
812         *p_pic++ = 0;   *p_pic++ = 0;
813         *p_pic++ = 0;   *p_pic++ = 0;
814     }
815
816     /* Clear last pixels */
817     //??
818 }
819
820
821 /*******************************************************************************
822  * RenderPicture: render a picture
823  *******************************************************************************
824  * This function convert a picture from a video heap to a pixel-encoded image
825  * and copy it to the current rendering buffer. No lock is required, since the
826  * rendered picture has been determined as existant, and will only be destroyed
827  * by the vout thread later.
828  *******************************************************************************/
829 static int RenderPicture( vout_thread_t *p_vout, picture_t *p_pic, boolean_t b_blank )
830 {
831 #ifdef STATS
832     /* Start recording render time */
833     p_vout->render_time = mdate();
834 #endif
835
836     /* Mark last picture date */
837     p_vout->last_picture_date = p_pic->date;    
838
839     /* Blank screen if required */
840     if( b_blank )
841     {
842 // ?????       RenderBlank( p_vout );
843     }
844
845     /* 
846      * Prepare scaling 
847      */
848     if( (p_pic->i_width > p_vout->i_width) || (p_pic->i_height > p_vout->i_height) )
849     {
850 #ifdef VIDEO_X11
851         /* X11: window can be resized, so resize it - the picture won't be 
852          * rendered since any alteration of the window size means recreating the
853          * XImages */
854 /*        p_vout->i_new_width =   p_pic->i_width;
855         p_vout->i_new_height =  p_pic->i_height;*/
856 #else
857         /* Other drivers: the video output thread can't change its size, so
858          * we need to change the aspect ratio */
859         //????
860 #endif
861     }    
862
863     /*
864      * Choose appropriate rendering function and render picture
865      */
866     switch( p_pic->i_type )
867     {
868     case YUV_420_PICTURE:
869         p_vout->p_ConvertYUV420( p_vout, vout_SysGetPicture( p_vout ),
870                                  p_pic->p_y, p_pic->p_u, p_pic->p_v,
871                                  p_pic->i_width, p_pic->i_height, 0, 0,
872                                  4, p_pic->i_matrix_coefficients );
873         break;        
874     case YUV_422_PICTURE:
875  /* ??? p_vout->p_convert_yuv_420( p_vout, 
876                                    p_pic->p_y, p_pic->p_u, p_pic->p_v,
877                                    i_chroma_width, i_chroma_height,
878                                     p_vout->i_width / 2, p_vout->i_height,
879                                    p_vout->i_bytes_per_line,
880                                    0, 0, 0 );
881   */      break;        
882     case YUV_444_PICTURE:
883 /*  ???      p_vout->p_convert_yuv_420( p_vout, 
884                                    p_pic->p_y, p_pic->p_u, p_pic->p_v,
885                                    i_chroma_width, i_chroma_height,
886                                    p_vout->i_width, p_vout->i_height,
887                                    p_vout->i_bytes_per_line,
888                                    0, 0, 0 );
889   */      break;                
890 #ifdef DEBUG
891     default:        
892         intf_DbgMsg("error: unknown picture type %d\n", p_pic->i_type );
893         break;        
894 #endif
895     }
896
897     /* 
898      * Terminate scaling 
899      */
900     //??
901
902 #ifdef STATS
903     /* End recording render time */
904     p_vout->render_time = mdate() - p_vout->render_time;
905 #endif
906     return( 1 );    
907 }
908
909 /*******************************************************************************
910  * RenderPictureInfo: print additionnal informations on a picture
911  *******************************************************************************
912  * This function will print informations such as fps and other picture
913  * dependant informations.
914  *******************************************************************************/
915 static int RenderPictureInfo( vout_thread_t *p_vout, picture_t *p_pic, boolean_t b_blank )
916 {
917     char        psz_buffer[256];                              /* string buffer */
918
919 #ifdef STATS
920     /* 
921      * Print FPS rate in upper right corner 
922      */
923     if( p_vout->c_fps_samples > VOUT_FPS_SAMPLES )
924     {        
925         sprintf( psz_buffer, "%.2f fps", (double) VOUT_FPS_SAMPLES * 1000000 /
926                  ( p_vout->fps_sample[ (p_vout->c_fps_samples - 1) % VOUT_FPS_SAMPLES ] -
927                    p_vout->fps_sample[ p_vout->c_fps_samples % VOUT_FPS_SAMPLES ] ) );        
928         vout_SysPrint( p_vout, p_vout->i_width, 0, 1, -1, psz_buffer );
929     }
930
931     /* 
932      * Print frames count and loop time in upper left corner 
933      */
934     sprintf( psz_buffer, "%ld frames   render time: %lu us", 
935              p_vout->c_fps_samples, (long unsigned) p_vout->render_time );
936     vout_SysPrint( p_vout, 0, 0, -1, -1, psz_buffer );    
937 #endif
938
939 #ifdef DEBUG
940     /*
941      * Print picture information in lower right corner
942      */
943     sprintf( psz_buffer, "%s picture %dx%d (%dx%d%+d%+d %s)",
944              (p_pic->i_type == YUV_420_PICTURE) ? "4:2:0" :
945              ((p_pic->i_type == YUV_422_PICTURE) ? "4:2:2" :
946               ((p_pic->i_type == YUV_444_PICTURE) ? "4:4:4" : "ukn-type")),
947              p_pic->i_width, p_pic->i_height,
948              p_pic->i_display_width, p_pic->i_display_height,
949              p_pic->i_display_horizontal_offset, p_pic->i_display_vertical_offset,
950              (p_pic->i_aspect_ratio == AR_SQUARE_PICTURE) ? "sq" :
951              ((p_pic->i_aspect_ratio == AR_3_4_PICTURE) ? "4:3" :
952               ((p_pic->i_aspect_ratio == AR_16_9_PICTURE) ? "16:9" :
953                ((p_pic->i_aspect_ratio == AR_221_1_PICTURE) ? "2.21:1" : "ukn-ar" ))));    
954     vout_SysPrint( p_vout, p_vout->i_width, p_vout->i_height, 1, 1, psz_buffer );
955 #endif
956     
957     return( 0 );    
958 }
959
960 /*******************************************************************************
961  * RenderIdle: render idle picture
962  *******************************************************************************
963  * This function will clear the display or print a logo.
964  *******************************************************************************/
965 static int RenderIdle( vout_thread_t *p_vout, boolean_t b_blank )
966 {
967     /* Blank screen if required */
968     if( (mdate() - p_vout->last_picture_date > VOUT_IDLE_DELAY) &&
969         (p_vout->last_picture_date > p_vout->last_display_date) &&
970         b_blank )
971     {        
972         RenderBlank( p_vout );
973         p_vout->last_display_date = mdate();        
974         vout_SysPrint( p_vout, p_vout->i_width / 2, p_vout->i_height / 2, 0, 0,
975                        "no stream" );        
976         return( 1 );        
977     }
978
979     return( 0 );    
980 }
981
982 /*******************************************************************************
983  * RenderInfo: render additionnal informations
984  *******************************************************************************
985  * This function render informations which do not depend of the current picture
986  * rendered.
987  *******************************************************************************/
988 static int RenderInfo( vout_thread_t *p_vout, boolean_t b_blank )
989 {
990     char        psz_buffer[256];                              /* string buffer */
991 #ifdef DEBUG
992     int         i_ready_pic = 0;                             /* ready pictures */
993     int         i_reserved_pic = 0;                       /* reserved pictures */
994     int         i_picture;                                    /* picture index */
995 #endif
996
997 #ifdef DEBUG
998     /* 
999      * Print thread state in lower left corner  
1000      */
1001     for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
1002     {
1003         switch( p_vout->p_picture[i_picture].i_status )
1004         {
1005         case RESERVED_PICTURE:
1006             i_reserved_pic++;            
1007             break;            
1008         case READY_PICTURE:
1009             i_ready_pic++;            
1010             break;            
1011         }        
1012     }
1013     sprintf( psz_buffer, "%s %dx%d:%d scaling %+d:%+d g%+.2f   pic: %d/%d/%d", 
1014              p_vout->b_grayscale ? "gray" : "rgb", 
1015              p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth, 
1016              p_vout->i_horizontal_scale, p_vout->i_vertical_scale, 
1017              p_vout->f_gamma, i_reserved_pic, i_ready_pic,
1018              VOUT_MAX_PICTURES );
1019     vout_SysPrint( p_vout, 0, p_vout->i_height, -1, 1, psz_buffer );    
1020 #endif
1021     return( 0 );    
1022 }
1023
1024 /*******************************************************************************
1025  * Manage: manage thread
1026  *******************************************************************************
1027  * This function will handle changes in thread configuration.
1028  *******************************************************************************/
1029 static int Manage( vout_thread_t *p_vout )
1030 {
1031     /* On gamma or grayscale change, rebuild tables */
1032     if( p_vout->i_changes & (VOUT_GAMMA_CHANGE | VOUT_GRAYSCALE_CHANGE) )
1033     {
1034         vout_ResetTables( p_vout );        
1035     }    
1036
1037     /* Clear changes flags which does not need management or have been handled */
1038     p_vout->i_changes &= ~(VOUT_INFO_CHANGE | VOUT_GAMMA_CHANGE | VOUT_GRAYSCALE_CHANGE);
1039
1040     /* Detect unauthorized changes */
1041     if( p_vout->i_changes )
1042     {
1043         /* Some changes were not acknowledged by vout_SysManage or this function,
1044          * it means they should not be authorized */
1045         intf_ErrMsg( "error: unauthorized changes in the video output thread\n" );        
1046         return( 1 );        
1047     }
1048     
1049     return( 0 );    
1050 }