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