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