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