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