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