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