]> git.sesse.net Git - vlc/blob - src/video_output/video_output.c
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 #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     if( (p_pic->i_refcount == 0) && (p_pic->i_status == DISPLAYED_PICTURE) )
480     {
481         p_pic->i_status = DESTROYED_PICTURE;
482     }
483     vlc_mutex_unlock( &p_vout->picture_lock );
484
485 #ifdef DEBUG_VIDEO
486     intf_DbgMsg("picture %p\n", p_pic);    
487 #endif
488 }
489
490 /* following functions are local */
491
492 /*******************************************************************************
493  * InitThread: initialize video output thread
494  *******************************************************************************
495  * This function is called from RunThread and performs the second step of the
496  * initialization. It returns 0 on success. Note that the thread's flag are not
497  * modified inside this function.
498  *******************************************************************************/
499 static int InitThread( vout_thread_t *p_vout )
500 {
501     int     i_index;                                          /* generic index */    
502
503     /* Update status */
504     *p_vout->pi_status = THREAD_START;    
505
506     /* Initialize output method - this function issues its own error messages */
507     if( vout_SysInit( p_vout ) )
508     {
509         *p_vout->pi_status = THREAD_ERROR;        
510         return( 1 );
511     } 
512
513     /* Initialize pictures and subtitles */    
514     for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++)
515     {
516         p_vout->p_picture[i_index].i_type   = EMPTY_PICTURE;
517         p_vout->p_picture[i_index].i_status = FREE_PICTURE;
518         p_vout->p_subtitle[i_index].i_type  = EMPTY_SUBTITLE;
519         p_vout->p_subtitle[i_index].i_status= FREE_SUBTITLE;
520     }
521
522     /* Initialize convertion tables and functions */
523     if( vout_InitTables( p_vout ) )
524     {
525         intf_ErrMsg("error: can't allocate translation tables\n");
526         return( 1 );                
527     }
528     
529     /* Mark thread as running and return */
530     p_vout->b_active =          1;    
531     *p_vout->pi_status =        THREAD_READY;    
532     intf_DbgMsg("thread ready\n");    
533     return( 0 );    
534 }
535
536 /*******************************************************************************
537  * RunThread: video output thread
538  *******************************************************************************
539  * Video output thread. This function does only returns when the thread is
540  * terminated. It handles the pictures arriving in the video heap and the
541  * display device events.
542  *******************************************************************************/
543 static void RunThread( vout_thread_t *p_vout)
544 {
545     int             i_picture;                                /* picture index */
546     mtime_t         current_date;                              /* current date */
547     mtime_t         pic_date = 0;                              /* picture date */    
548     boolean_t       b_display;                                 /* display flag */    
549     picture_t *     p_pic;                                  /* picture pointer */
550      
551     /* 
552      * Initialize thread and free configuration 
553      */
554     p_vout->b_error = InitThread( p_vout );
555     if( p_vout->b_error )
556     {
557         free( p_vout );                                  /* destroy descriptor */
558         return;        
559     }    
560
561     /*
562      * Main loop - it is not executed if an error occured during
563      * initialization
564      */
565     while( (!p_vout->b_die) && (!p_vout->b_error) )
566     {            
567         /* 
568          * Find the picture to display - this operation does not need lock,
569          * since only READY_PICTURES are handled 
570          */
571         p_pic = NULL;         
572         current_date = mdate();
573         for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
574         {
575             if( (p_vout->p_picture[i_picture].i_status == READY_PICTURE) &&
576                 ( (p_pic == NULL) || 
577                   (p_vout->p_picture[i_picture].date < pic_date) ) )
578             {
579                 p_pic = &p_vout->p_picture[i_picture];
580                 pic_date = p_pic->date;                
581             }
582         }
583  
584         /* 
585          * Render picture if any
586          */
587         if( p_pic )
588         {
589 #ifdef STATS
590             /* Computes FPS rate */
591             p_vout->fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ] = pic_date;
592 #endif      
593             if( pic_date < current_date )
594             {
595                 /* Picture is late: it will be destroyed and the thread will sleep and
596                  * go to next picture */
597                 vlc_mutex_lock( &p_vout->picture_lock );
598                 p_pic->i_status = p_pic->i_refcount ? DISPLAYED_PICTURE : DESTROYED_PICTURE;
599                 vlc_mutex_unlock( &p_vout->picture_lock );
600 #ifdef DEBUG_VIDEO
601                 intf_DbgMsg( "warning: late picture %p skipped\n", p_pic );
602 #endif
603                 p_pic =         NULL;                
604             }
605             else if( pic_date > current_date + VOUT_DISPLAY_DELAY )
606             {
607                 /* A picture is ready to be rendered, but its rendering date is
608                  * far from the current one so the thread will perform an empty loop
609                  * as if no picture were found. The picture state is unchanged */
610                 p_pic =         NULL;                
611             }
612         }
613               
614         /*
615          * Perform rendering, sleep and display rendered picture
616          */
617         if( p_pic )
618         {
619             /* A picture is ready to be displayed : render it */
620             if( p_vout->b_active )
621             {                    
622                 b_display = RenderPicture( p_vout, p_pic, 1 );
623                 if( p_vout->b_info )
624                 {
625                     b_display |= RenderPictureInfo( p_vout, p_pic, b_display );
626                     b_display |= RenderInfo( p_vout, b_display );                    
627                 }                    
628             }
629             else
630             {
631                 b_display = 0;                
632             } 
633
634             /* Remove picture from heap */
635             vlc_mutex_lock( &p_vout->picture_lock );
636             p_pic->i_status = p_pic->i_refcount ? DISPLAYED_PICTURE : DESTROYED_PICTURE;
637             vlc_mutex_unlock( &p_vout->picture_lock );                          
638         }            
639         else
640         {
641             /* No picture. However, an idle screen may be ready to display */
642             if( p_vout->b_active )
643             {                
644                 b_display = RenderIdle( p_vout, 1 );
645                 if( p_vout->b_info )
646                 {                    
647                     b_display |= RenderInfo( p_vout, b_display );
648                 }        
649             }
650             else
651             {
652                 b_display = 0;                
653             }            
654         }
655
656         /* Give back change lock */
657         vlc_mutex_unlock( &p_vout->change_lock );        
658
659         /* Sleep a while or until a given date */
660         if( p_pic )
661         {
662             mwait( pic_date );
663         }
664         else
665         {
666             msleep( VOUT_IDLE_SLEEP );                
667         }            
668
669         /* On awakening, take back lock and send immediately picture to display */
670         vlc_mutex_lock( &p_vout->change_lock );        
671         if( b_display && p_vout->b_active && 
672             !(p_vout->i_changes & VOUT_NODISPLAY_CHANGE) )
673         {
674             vout_SysDisplay( p_vout );
675         }
676
677         /*
678          * Check events and manage thread
679          */
680         if( vout_SysManage( p_vout ) | Manage( p_vout ) )
681         {
682             /* A fatal error occured, and the thread must terminate immediately,
683              * without displaying anything - setting b_error to 1 cause the
684              * immediate end of the main while() loop. */
685             p_vout->b_error = 1;
686         }  
687     } 
688
689     /*
690      * Error loop
691      */
692     if( p_vout->b_error )
693     {
694         ErrorThread( p_vout );        
695     }
696
697     /* End of thread */
698     EndThread( p_vout );
699     intf_DbgMsg( "thread end\n" );
700 }
701
702 /*******************************************************************************
703  * ErrorThread: RunThread() error loop
704  *******************************************************************************
705  * This function is called when an error occured during thread main's loop. The
706  * thread can still receive feed, but must be ready to terminate as soon as
707  * possible.
708  *******************************************************************************/
709 static void ErrorThread( vout_thread_t *p_vout )
710 {
711     /* Wait until a `die' order */
712     while( !p_vout->b_die )
713     {
714         /* Sleep a while */
715         msleep( VOUT_IDLE_SLEEP );                
716     }
717 }
718
719 /*******************************************************************************
720  * EndThread: thread destruction
721  *******************************************************************************
722  * This function is called when the thread ends after a sucessfull 
723  * initialization.
724  *******************************************************************************/
725 static void EndThread( vout_thread_t *p_vout )
726 {
727     int *   pi_status;                                        /* thread status */
728     int     i_picture;
729         
730     /* Store status */
731     pi_status = p_vout->pi_status;    
732     *pi_status = THREAD_END;    
733
734     /* Destroy all remaining pictures */
735     for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
736     {
737         if( p_vout->p_picture[i_picture].i_status != FREE_PICTURE )
738         {
739             free( p_vout->p_picture[i_picture].p_data );
740         }
741     }
742
743     /* Destroy translation tables */
744     vout_EndTables( p_vout );
745     
746     /* Destroy thread structures allocated by InitThread */
747     vout_SysEnd( p_vout );
748     vout_SysDestroy( p_vout );
749     free( p_vout );
750
751     /* Update status */
752     *pi_status = THREAD_OVER;    
753 }
754
755 /*******************************************************************************
756  * RenderBlank: render a blank screen
757  *******************************************************************************
758  * This function is called by all other rendering functions when they arrive on
759  * a non blanked screen.
760  *******************************************************************************/
761 static void RenderBlank( vout_thread_t *p_vout )
762 {
763     int  i_index;                                    /* current 32 bits sample */    
764     int  i_width;                                 /* number of 32 bits samples */    
765     u32 *p_pic;                                  /* pointer to 32 bits samples */
766     
767     /* Initialize variables */
768     p_pic =     vout_SysGetPicture( p_vout );
769     i_width =   p_vout->i_bytes_per_line * p_vout->i_height / 128;
770
771     /* Clear beginning of screen by 128 bytes blocks */
772     for( i_index = 0; i_index < i_width; i_index++ )
773     {
774         *p_pic++ = 0;   *p_pic++ = 0;
775         *p_pic++ = 0;   *p_pic++ = 0;
776         *p_pic++ = 0;   *p_pic++ = 0;
777         *p_pic++ = 0;   *p_pic++ = 0;
778         *p_pic++ = 0;   *p_pic++ = 0;
779         *p_pic++ = 0;   *p_pic++ = 0;
780         *p_pic++ = 0;   *p_pic++ = 0;
781         *p_pic++ = 0;   *p_pic++ = 0;
782         *p_pic++ = 0;   *p_pic++ = 0;
783         *p_pic++ = 0;   *p_pic++ = 0;
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     }
791
792     /* Clear last pixels */
793     //??
794 }
795
796
797 /*******************************************************************************
798  * RenderPicture: render a picture
799  *******************************************************************************
800  * This function convert a picture from a video heap to a pixel-encoded image
801  * and copy it to the current rendering buffer. No lock is required, since the
802  * rendered picture has been determined as existant, and will only be destroyed
803  * by the vout thread later.
804  *******************************************************************************/
805 static int RenderPicture( vout_thread_t *p_vout, picture_t *p_pic, boolean_t b_blank )
806 {
807     /* Mark last picture date */
808     p_vout->last_picture_date = p_pic->date;    
809
810     /* Blank screen if required */
811     if( b_blank )
812     {
813         RenderBlank( p_vout );
814     }
815
816     /* 
817      * Prepare scaling 
818      */
819     if( (p_pic->i_width > p_vout->i_width) || (p_pic->i_height > p_vout->i_height) )
820     {
821 #ifdef VIDEO_X11
822         /* X11: window can be resized, so resize it - the picture won't be 
823          * rendered since any alteration of the window size means recreating the
824          * XImages */
825 /*        p_vout->i_new_width =   p_pic->i_width;
826         p_vout->i_new_height =  p_pic->i_height;*/
827 #else
828         /* Other drivers: the video output thread can't change its size, so
829          * we need to change the aspect ratio */
830         //????
831 #endif
832     }    
833
834     /*
835      * Choose appropriate rendering function and render picture
836      */
837     switch( p_pic->i_type )
838     {
839     case YUV_420_PICTURE:
840         p_vout->p_ConvertYUV420( p_vout, vout_SysGetPicture( p_vout ),
841                                  p_pic->p_y, p_pic->p_u, p_pic->p_v,
842                                  p_pic->i_width, p_pic->i_height, 0, 0,
843                                  4, p_pic->i_matrix_coefficients );
844         break;        
845     case YUV_422_PICTURE:
846  /* ??? p_vout->p_convert_yuv_420( p_vout, 
847                                    p_pic->p_y, p_pic->p_u, p_pic->p_v,
848                                    i_chroma_width, i_chroma_height,
849                                    p_vout->i_width / 2, p_vout->i_height,
850                                    p_vout->i_bytes_per_line,
851                                    0, 0, 0 );
852   */      break;        
853     case YUV_444_PICTURE:
854 /*  ???      p_vout->p_convert_yuv_420( p_vout, 
855                                    p_pic->p_y, p_pic->p_u, p_pic->p_v,
856                                    i_chroma_width, i_chroma_height,
857                                    p_vout->i_width, p_vout->i_height,
858                                    p_vout->i_bytes_per_line,
859                                    0, 0, 0 );
860   */      break;                
861 #ifdef DEBUG
862     default:        
863         intf_DbgMsg("error: unknown picture type %d\n", p_pic->i_type );
864         break;        
865 #endif
866     }
867
868     /* 
869      * Terminate scaling 
870      */
871     //??
872
873     return( 1 );    
874 }
875
876
877
878 /*******************************************************************************
879  * RenderPictureInfo: print additionnal informations on a picture
880  *******************************************************************************
881  * This function will print informations such as fps and other picture
882  * dependant informations.
883  *******************************************************************************/
884 static int RenderPictureInfo( vout_thread_t *p_vout, picture_t *p_pic, boolean_t b_blank )
885 {
886     char        psz_buffer[256];                              /* string buffer */
887
888 #ifdef STATS
889     /* 
890      * Print FPS rate in upper right corner 
891      */
892     if( p_vout->c_fps_samples > VOUT_FPS_SAMPLES )
893     {        
894         sprintf( psz_buffer, "%.2f fps", (double) VOUT_FPS_SAMPLES * 1000000 /
895                  ( p_vout->fps_sample[ (p_vout->c_fps_samples - 1) % VOUT_FPS_SAMPLES ] -
896                    p_vout->fps_sample[ p_vout->c_fps_samples % VOUT_FPS_SAMPLES ] ) );        
897         vout_SysPrint( p_vout, p_vout->i_width, 0, 1, -1, psz_buffer );
898     }
899
900     /* 
901      * Print frames count and loop time in upper left corner 
902      */
903     sprintf( psz_buffer, "%ld frames   render time: %lu us", 
904              p_vout->c_fps_samples, (long unsigned) p_vout->render_time );
905     vout_SysPrint( p_vout, 0, 0, -1, -1, psz_buffer );    
906 #endif
907
908 #ifdef DEBUG
909     /*
910      * Print picture information in lower right corner
911      */
912     sprintf( psz_buffer, "%s picture (mc=%d) %dx%d (%dx%d%+d%+d ar=%s)",
913              (p_pic->i_type == YUV_420_PICTURE) ? "4:2:0" :
914              ((p_pic->i_type == YUV_422_PICTURE) ? "4:2:2" :
915               ((p_pic->i_type == YUV_444_PICTURE) ? "4:4:4" : "?")),
916              p_pic->i_matrix_coefficients, p_pic->i_width, p_pic->i_height,
917              p_pic->i_display_width, p_pic->i_display_height,
918              p_pic->i_display_horizontal_offset, p_pic->i_display_vertical_offset,
919              (p_pic->i_aspect_ratio == AR_SQUARE_PICTURE) ? "square" :
920              ((p_pic->i_aspect_ratio == AR_3_4_PICTURE) ? "4:3" :
921               ((p_pic->i_aspect_ratio == AR_16_9_PICTURE) ? "16:9" :
922                ((p_pic->i_aspect_ratio == AR_221_1_PICTURE) ? "2.21:1" : "?" ))));    
923     vout_SysPrint( p_vout, p_vout->i_width, p_vout->i_height, 1, 1, psz_buffer );
924 #endif
925     
926     return( 0 );    
927 }
928
929 /*******************************************************************************
930  * RenderIdle: render idle picture
931  *******************************************************************************
932  * This function will clear the display or print a logo.
933  *******************************************************************************/
934 static int RenderIdle( vout_thread_t *p_vout, boolean_t b_blank )
935 {
936     /* Blank screen if required */
937     if( (mdate() - p_vout->last_picture_date > VOUT_IDLE_DELAY) &&
938         (p_vout->last_picture_date > p_vout->last_display_date) &&
939         b_blank )
940     {        
941         RenderBlank( p_vout );
942         p_vout->last_display_date = mdate();        
943         vout_SysPrint( p_vout, p_vout->i_width / 2, p_vout->i_height / 2, 0, 0,
944                        "no stream" );        
945         return( 1 );        
946     }
947
948     return( 0 );    
949 }
950
951 /*******************************************************************************
952  * RenderInfo: render additionnal informations
953  *******************************************************************************
954  * This function render informations which do not depend of the current picture
955  * rendered.
956  *******************************************************************************/
957 static int RenderInfo( vout_thread_t *p_vout, boolean_t b_blank )
958 {
959     char        psz_buffer[256];                              /* string buffer */
960 #ifdef DEBUG
961     int         i_ready_pic = 0;                             /* ready pictures */
962     int         i_reserved_pic = 0;                       /* reserved pictures */
963     int         i_picture;                                    /* picture index */
964 #endif
965
966 #ifdef DEBUG
967     /* 
968      * Print thread state in lower left corner  
969      */
970     for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
971     {
972         switch( p_vout->p_picture[i_picture].i_status )
973         {
974         case RESERVED_PICTURE:
975             i_reserved_pic++;            
976             break;            
977         case READY_PICTURE:
978             i_ready_pic++;            
979             break;            
980         }        
981     }
982     sprintf( psz_buffer, "%s %dx%d:%d %.2f:%.2f g%+.2f   pic: %d/%d/%d", 
983              p_vout->b_grayscale ? "gray" : "rgb", 
984              p_vout->i_width, p_vout->i_height,
985              p_vout->i_screen_depth, p_vout->f_x_ratio, p_vout->f_y_ratio, p_vout->f_gamma,
986              i_reserved_pic, i_ready_pic,
987              VOUT_MAX_PICTURES );
988     vout_SysPrint( p_vout, 0, p_vout->i_height, -1, 1, psz_buffer );    
989 #endif
990     return( 0 );    
991 }
992
993 /*******************************************************************************
994  * Manage: manage thread
995  *******************************************************************************
996  * This function will handle changes in thread configuration.
997  *******************************************************************************/
998 static int Manage( vout_thread_t *p_vout )
999 {
1000     /* On gamma or grayscale change, rebuild tables */
1001     if( p_vout->i_changes & (VOUT_GAMMA_CHANGE | VOUT_GRAYSCALE_CHANGE) )
1002     {
1003         vout_ResetTables( p_vout );        
1004     }    
1005
1006     /* Clear changes flags which does not need management or have been handled */
1007     p_vout->i_changes &= ~(VOUT_INFO_CHANGE | VOUT_GAMMA_CHANGE | VOUT_GRAYSCALE_CHANGE);
1008
1009     /* Detect unauthorized changes */
1010     if( p_vout->i_changes )
1011     {
1012         /* Some changes were not acknowledged by vout_SysManage or this function,
1013          * it means they should not be authorized */
1014         intf_ErrMsg( "error: unauthorized changes in the video output thread\n" );        
1015         return( 1 );        
1016     }
1017     
1018     return( 0 );    
1019 }