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