]> git.sesse.net Git - vlc/blob - src/video_output/video_output.c
Nettoyage, ajout du gamma, pr�paration de la yuv walken.
[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 <math.h> 
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
18
19 #ifdef VIDEO_X11
20 #include <X11/Xlib.h>                           /* for video_sys.h in X11 mode */
21 #endif
22
23 #include "common.h"
24 #include "config.h"
25 #include "mtime.h"
26 #include "vlc_thread.h"
27 #include "video.h"
28 #include "video_output.h"
29 #include "video_sys.h"
30 #include "intf_msg.h"
31 #include "main.h"
32
33 /*******************************************************************************
34  * Macros
35  *******************************************************************************/
36
37 /* CLIP_BYTE: return value if between 0 and 255, else return nearest boundary 
38  * (0 or 255), used to build translations tables */
39 #define CLIP_BYTE( i_val ) ( (i_val < 0) ? 0 : ((i_val > 255) ? 255 : i_val) )
40
41 /* YUV_GRAYSCALE: parametric macro for YUV grayscale transformation.
42  * Due to the high performance need of this loop, all possible conditions 
43  * evaluations are made outside the transformation loop. However, the code does 
44  * not change much for two different loops. This macro allows to change slightly
45  * the content of the loop without having to copy and paste code. It is used in 
46  * RenderYUVPicture function. */
47 #define YUV_GRAYSCALE( TRANS_GRAY, P_PIC )                              \
48 /* Main loop */                                                         \
49 for (i_pic_y=0; i_pic_y < p_pic->i_height ; i_pic_y++)                  \
50 {                                                                       \
51     for (i_pic_x=0; i_pic_x< p_pic->i_width; i_pic_x+=16)               \
52     {                                                                   \
53         /* Convert 16 pixels (width is always multiple of 16 */         \
54         *P_PIC++ = TRANS_GRAY[ *p_y++ ];                                \
55         *P_PIC++ = TRANS_GRAY[ *p_y++ ];                                \
56         *P_PIC++ = TRANS_GRAY[ *p_y++ ];                                \
57         *P_PIC++ = TRANS_GRAY[ *p_y++ ];                                \
58         *P_PIC++ = TRANS_GRAY[ *p_y++ ];                                \
59         *P_PIC++ = TRANS_GRAY[ *p_y++ ];                                \
60         *P_PIC++ = TRANS_GRAY[ *p_y++ ];                                \
61         *P_PIC++ = TRANS_GRAY[ *p_y++ ];                                \
62         *P_PIC++ = TRANS_GRAY[ *p_y++ ];                                \
63         *P_PIC++ = TRANS_GRAY[ *p_y++ ];                                \
64         *P_PIC++ = TRANS_GRAY[ *p_y++ ];                                \
65         *P_PIC++ = TRANS_GRAY[ *p_y++ ];                                \
66         *P_PIC++ = TRANS_GRAY[ *p_y++ ];                                \
67         *P_PIC++ = TRANS_GRAY[ *p_y++ ];                                \
68         *P_PIC++ = TRANS_GRAY[ *p_y++ ];                                \
69         *P_PIC++ = TRANS_GRAY[ *p_y++ ];                                \
70     }                                                                   \
71     /* Skip until beginning of next line */                             \
72     P_PIC += i_eol_offset;                                              \
73 }                                                                       
74
75 /* YUV_TRANSFORM: parametric macro for YUV transformation.
76  * Due to the high performance need of this loop, all possible conditions 
77  * evaluations are made outside the transformation loop. However, the code does 
78  * not change much for two different loops. This macro allows to change slightly
79  * the content of the loop without having to copy and paste code. It is used in 
80  * RenderYUVPicture function. */
81 #define YUV_TRANSFORM( CHROMA, TRANS_RED, TRANS_GREEN, TRANS_BLUE, P_PIC ) \
82 /* Main loop */                                                         \
83 for (i_pic_y=0; i_pic_y < p_pic->i_height ; i_pic_y++)                  \
84 {                                                                       \
85     for (i_pic_x=0; i_pic_x< p_pic->i_width; i_pic_x+=2 )               \
86     {                                                                   \
87         /* First sample (complete) */                                   \
88         i_y = 76309 * *p_y++ - 1188177;                                 \
89         i_u = *p_u++ - 128;                                             \
90         i_v = *p_v++ - 128;                                             \
91         *P_PIC++ =                                                      \
92             TRANS_RED   [(i_y+i_crv*i_v)                >>16] |         \
93             TRANS_GREEN [(i_y-i_cgu*i_u-i_cgv*i_v)      >>16] |         \
94             TRANS_BLUE  [(i_y+i_cbu*i_u)                >>16];          \
95         i_y = 76309 * *p_y++ - 1188177;                                 \
96         /* Second sample (partial) */                                   \
97         if( CHROMA == 444 )                                             \
98         {                                                               \
99             i_u = *p_u++ - 128;                                         \
100             i_v = *p_v++ - 128;                                         \
101         }                                                               \
102         *P_PIC++ =                                                      \
103             TRANS_RED   [(i_y+i_crv*i_v)                >>16] |         \
104             TRANS_GREEN [(i_y-i_cgu*i_u-i_cgv*i_v)      >>16] |         \
105             TRANS_BLUE  [(i_y+i_cbu*i_u)                >>16];          \
106     }                                                                   \
107     if( (CHROMA == 420) && !(i_pic_y & 0x1) )                           \
108     {                                                                   \
109         p_u -= i_chroma_width;                                          \
110         p_v -= i_chroma_width;                                          \
111     }                                                                   \
112     /* Skip until beginning of next line */                             \
113     P_PIC += i_eol_offset;                                              \
114 }
115
116 /*******************************************************************************
117  * Constants
118  *******************************************************************************/
119
120 /* RGB/YUV inversion matrix (ISO/IEC 13818-2 section 6.3.6, table 6.9) */
121 const int MATRIX_COEFFICIENTS_TABLE[8][4] =
122 {
123   {117504, 138453, 13954, 34903},       /* no sequence_display_extension */
124   {117504, 138453, 13954, 34903},       /* ITU-R Rec. 709 (1990) */
125   {104597, 132201, 25675, 53279},       /* unspecified */
126   {104597, 132201, 25675, 53279},       /* reserved */
127   {104448, 132798, 24759, 53109},       /* FCC */
128   {104597, 132201, 25675, 53279},       /* ITU-R Rec. 624-4 System B, G */
129   {104597, 132201, 25675, 53279},       /* SMPTE 170M */
130   {117579, 136230, 16907, 35559}        /* SMPTE 240M (1987) */
131 };
132
133 /*******************************************************************************
134  * External prototypes
135  *******************************************************************************/
136 #ifdef HAVE_MMX
137 /* YUV transformations for MMX - in yuv-mmx.S 
138  *      p_y, p_u, p_v:          Y U and V planes
139  *      i_width, i_height:      frames dimensions (pixels)
140  *      i_ypitch, i_vpitch:     Y and V lines sizes (bytes)
141  *      i_aspect:               vertical aspect factor
142  *      p_pic:                  RGB frame
143  *      i_dci_offset:           ?? x offset for left image border
144  *      i_offset_to_line_0:     ?? x offset for left image border
145  *      i_pitch:                RGB line size (bytes)
146  *      i_colortype:            0 for 565, 1 for 555 */
147 void vout_YUV420_16_MMX( u8* p_y, u8* p_u, u8 *p_v, 
148                          unsigned int i_width, unsigned int i_height,
149                          unsigned int i_ypitch, unsigned int i_vpitch,
150                          unsigned int i_aspect, u8 *p_pic, 
151                          u32 i_dci_offset, u32 i_offset_to_line_0,
152                          int CCOPitch, int i_colortype );
153 #endif
154
155 /*******************************************************************************
156  * Local prototypes
157  *******************************************************************************/
158 static int      InitThread              ( vout_thread_t *p_vout );
159 static void     RunThread               ( vout_thread_t *p_vout );
160 static void     ErrorThread             ( vout_thread_t *p_vout );
161 static void     EndThread               ( vout_thread_t *p_vout );
162 static void     BuildTables             ( vout_thread_t *p_vout );
163 static void     RenderPicture           ( vout_thread_t *p_vout, picture_t *p_pic );
164 static void     RenderYUVGrayPicture    ( vout_thread_t *p_vout, picture_t *p_pic );
165 static void     RenderYUV16Picture      ( vout_thread_t *p_vout, picture_t *p_pic );
166 static void     RenderYUV32Picture      ( vout_thread_t *p_vout, picture_t *p_pic );
167 static void     RenderInfo              ( vout_thread_t *p_vout );
168 static int      RenderIdle              ( vout_thread_t *p_vout, int i_level );
169
170 /*******************************************************************************
171  * vout_CreateThread: creates a new video output thread
172  *******************************************************************************
173  * This function creates a new video output thread, and returns a pointer
174  * to its description. On error, it returns NULL.
175  * If pi_status is NULL, then the function will block until the thread is ready.
176  * If not, it will be updated using one of the THREAD_* constants.
177  *******************************************************************************/
178 vout_thread_t * vout_CreateThread               ( 
179 #ifdef VIDEO_X11
180                                                   char *psz_display, Window root_window, 
181 #endif
182                                                   int i_width, int i_height, int *pi_status 
183                                                 )
184 {
185     vout_thread_t * p_vout;                               /* thread descriptor */
186     int             i_status;                                 /* thread status */
187
188     /* Allocate descriptor */
189     p_vout = (vout_thread_t *) malloc( sizeof(vout_thread_t) );
190     if( p_vout == NULL )
191     {
192         intf_ErrMsg("error: %s\n", strerror(ENOMEM));        
193         return( NULL );
194     }
195
196     /* Initialize some fields used by the system-dependant method - these fields will
197      * probably be modified by the method */
198 #ifdef DEBUG
199     p_vout->b_info              = 1;    
200 #else
201     p_vout->b_info              = 0;    
202 #endif
203     p_vout->b_grayscale         = main_GetIntVariable( VOUT_GRAYSCALE_VAR, 
204                                                        VOUT_GRAYSCALE_DEFAULT );
205     p_vout->i_width             = i_width;
206     p_vout->i_height            = i_height;
207     p_vout->i_bytes_per_line    = i_width * 2;    
208     p_vout->i_screen_depth      = 15;
209     p_vout->i_bytes_per_pixel   = 2;
210     p_vout->f_x_ratio           = 1;
211     p_vout->f_y_ratio           = 1;
212     p_vout->f_gamma             = VOUT_GAMMA;    
213     intf_DbgMsg("wished configuration: %dx%d,%d (%d bytes/pixel, %d bytes/line), ratio %.2f:%.2f, gray=%d\n",
214                 p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
215                 p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line,
216                 p_vout->f_x_ratio, p_vout->f_y_ratio, p_vout->b_grayscale );
217    
218     /* Create and initialize system-dependant method - this function issues its
219      * own error messages */
220     if( vout_SysCreate( p_vout
221 #if defined(VIDEO_X11)
222                         , psz_display, root_window 
223 #endif
224         ) )
225     {
226       free( p_vout );
227       return( NULL );
228     }
229     intf_DbgMsg("actual configuration: %dx%d,%d (%d bytes/pixel, %d bytes/line), ratio %.2f:%.2f, gray=%d\n",
230                 p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
231                 p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line,
232                 p_vout->f_x_ratio, p_vout->f_y_ratio, p_vout->b_grayscale );
233   
234     /* Terminate the initialization */
235     p_vout->b_die               = 0;
236     p_vout->b_error             = 0;    
237     p_vout->b_active            = 0;
238     p_vout->pi_status           = (pi_status != NULL) ? pi_status : &i_status;
239     *p_vout->pi_status          = THREAD_CREATE;    
240 #ifdef STATS
241     p_vout->c_loops             = 0;
242     p_vout->c_idle_loops        = 0;
243     p_vout->c_fps_samples       = 0;
244 #endif      
245     p_vout->b_gamma_change      = 0;
246     p_vout->i_new_width         = p_vout->i_width;
247     p_vout->i_new_height        = p_vout->i_height;    
248
249     /* Create thread and set locks */
250     vlc_mutex_init( &p_vout->lock );
251     if( vlc_thread_create( &p_vout->thread_id, "video output", 
252                            (void *) RunThread, (void *) p_vout) )
253     {
254         intf_ErrMsg("error: %s\n", strerror(ENOMEM));
255         vout_SysDestroy( p_vout );
256         free( p_vout );
257         return( NULL );
258     }   
259
260     intf_Msg("Video: display initialized (%dx%d, %d bpp)\n", 
261              p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth );    
262
263     /* If status is NULL, wait until the thread is created */
264     if( pi_status == NULL )
265     {
266         do
267         {            
268             msleep( THREAD_SLEEP );
269         }while( (i_status != THREAD_READY) && (i_status != THREAD_ERROR) 
270                 && (i_status != THREAD_FATAL) );
271         if( i_status != THREAD_READY )
272         {
273             return( NULL );            
274         }        
275     }
276     return( p_vout );
277 }
278
279 /*******************************************************************************
280  * vout_DestroyThread: destroys a previously created thread
281  *******************************************************************************
282  * Destroy a terminated thread. 
283  * The function will request a destruction of the specified thread. If pi_error
284  * is NULL, it will return once the thread is destroyed. Else, it will be 
285  * update using one of the THREAD_* constants.
286  *******************************************************************************/
287 void vout_DestroyThread( vout_thread_t *p_vout, int *pi_status )
288 {  
289     int     i_status;                                         /* thread status */
290
291     /* Set status */
292     p_vout->pi_status = (pi_status != NULL) ? pi_status : &i_status;
293     *p_vout->pi_status = THREAD_DESTROY;    
294      
295     /* Request thread destruction */
296     p_vout->b_die = 1;
297
298     /* If status is NULL, wait until thread has been destroyed */
299     if( pi_status == NULL )
300     {
301         do
302         {
303             msleep( THREAD_SLEEP );
304         }while( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR) 
305                 && (i_status != THREAD_FATAL) );   
306     }
307 }
308
309 /*******************************************************************************
310  * vout_DisplayPicture: display a picture
311  *******************************************************************************
312  * Remove the reservation flag of a picture, which will cause it to be ready for
313  * display. The picture does not need to be locked, since it is ignored by
314  * the output thread if is reserved.
315  *******************************************************************************/
316 void  vout_DisplayPicture( vout_thread_t *p_vout, picture_t *p_pic )
317 {
318 #ifdef DEBUG_VIDEO
319     char        psz_date[MSTRTIME_MAX_SIZE];         /* buffer for date string */
320 #endif
321
322 #ifdef DEBUG
323     /* Check if picture status is valid */
324     if( p_pic->i_status != RESERVED_PICTURE )
325     {
326         intf_DbgMsg("error: picture %d has invalid status %d\n", p_pic, p_pic->i_status );       
327     }   
328 #endif
329
330     /* Remove reservation flag */
331     p_pic->i_status = READY_PICTURE;
332
333 #ifdef DEBUG_VIDEO
334     /* Send picture informations */
335     intf_DbgMsg("picture %p: type=%d, %dx%d, date=%s\n", p_pic, p_pic->i_type, 
336                 p_pic->i_width,p_pic->i_height, mstrtime( psz_date, p_pic->date ) );    
337 #endif
338 }
339
340 /*******************************************************************************
341  * vout_CreatePicture: allocate a picture in the video output heap.
342  *******************************************************************************
343  * This function create a reserved image in the video output heap. 
344  * A null pointer is returned if the function fails. This method provides an
345  * already allocated zone of memory in the picture data fields. It needs locking
346  * since several pictures can be created by several producers threads.
347  *******************************************************************************/
348 picture_t *vout_CreatePicture( vout_thread_t *p_vout, int i_type, 
349                                int i_width, int i_height, int i_bytes_per_line )
350 {
351     int         i_picture;                                    /* picture index */
352     picture_t * p_free_picture = NULL;                   /* first free picture */    
353     picture_t * p_destroyed_picture = NULL;         /* first destroyed picture */    
354
355     /* Get lock */
356     vlc_mutex_lock( &p_vout->lock );
357
358     /* 
359      * Look for an empty place 
360      */
361     for( i_picture = 0; 
362          i_picture < VOUT_MAX_PICTURES; 
363          i_picture++ )
364     {
365         if( p_vout->p_picture[i_picture].i_status == DESTROYED_PICTURE )
366         {
367             /* Picture is marked for destruction, but is still allocated */
368             if( (p_vout->p_picture[i_picture].i_type           == i_type)   &&
369                 (p_vout->p_picture[i_picture].i_height         == i_height) &&
370                 (p_vout->p_picture[i_picture].i_bytes_per_line == i_bytes_per_line) )
371             {
372                 /* Memory size do match : memory will not be reallocated, and function
373                  * can end immediately - this is the best possible case, since no
374                  * memory allocation needs to be done */
375                 p_vout->p_picture[i_picture].i_width  = i_width;
376                 p_vout->p_picture[i_picture].i_status = RESERVED_PICTURE;
377 #ifdef DEBUG_VIDEO
378                 intf_DbgMsg("picture %p (in destroyed picture slot)\n", 
379                             &p_vout->p_picture[i_picture] );                
380 #endif
381                 vlc_mutex_unlock( &p_vout->lock );
382                 return( &p_vout->p_picture[i_picture] );
383             }
384             else if( p_destroyed_picture == NULL )
385             {
386                 /* Memory size do not match, but picture index will be kept in
387                  * case no other place are left */
388                 p_destroyed_picture = &p_vout->p_picture[i_picture];                
389             }       
390         }
391         else if( (p_free_picture == NULL) && 
392                  (p_vout->p_picture[i_picture].i_status == FREE_PICTURE ))
393         {
394             /* Picture is empty and ready for allocation */
395             p_free_picture = &p_vout->p_picture[i_picture];            
396         }
397     }
398
399     /* If no free picture is available, use a destroyed picture */
400     if( (p_free_picture == NULL) && (p_destroyed_picture != NULL ) )
401     { 
402         /* No free picture or matching destroyed picture has been found, but
403          * a destroyed picture is still avalaible */
404         free( p_destroyed_picture->p_data );        
405         p_free_picture = p_destroyed_picture;        
406     }
407
408     /*
409      * Prepare picture
410      */
411     if( p_free_picture != NULL )
412     {
413         /* Allocate memory */
414         switch( i_type )
415         {
416         case YUV_420_PICTURE:                   /* YUV picture: bits per pixel */
417         case YUV_422_PICTURE:
418         case YUV_444_PICTURE:
419             p_free_picture->p_data = malloc( 3 * i_height * i_bytes_per_line );                
420             p_free_picture->p_y = (yuv_data_t *) p_free_picture->p_data;
421             p_free_picture->p_u = (yuv_data_t *)(p_free_picture->p_data + i_height * i_bytes_per_line);
422             p_free_picture->p_v = (yuv_data_t *)(p_free_picture->p_data + i_height * i_bytes_per_line * 2);
423             break;                
424 #ifdef DEBUG
425         default:
426             intf_DbgMsg("error: unknown picture type %d\n", i_type );
427             p_free_picture->p_data   =  NULL;            
428             break;            
429 #endif    
430         }
431
432         if( p_free_picture->p_data != NULL )
433         {        
434             /* Copy picture informations */
435             p_free_picture->i_type                      = i_type;
436             p_free_picture->i_status                    = RESERVED_PICTURE;
437             p_free_picture->i_width                     = i_width;
438             p_free_picture->i_height                    = i_height;
439             p_free_picture->i_bytes_per_line            = i_bytes_per_line;
440             p_free_picture->i_refcount                  = 0;            
441             p_free_picture->i_matrix_coefficients       = 1; 
442         }
443         else
444         {
445             /* Memory allocation failed : set picture as empty */
446             p_free_picture->i_type   =  EMPTY_PICTURE;            
447             p_free_picture->i_status =  FREE_PICTURE;            
448             p_free_picture =            NULL;            
449             intf_ErrMsg("warning: %s\n", strerror( ENOMEM ) );            
450         }
451         
452 #ifdef DEBUG_VIDEO
453         intf_DbgMsg("picture %p (in free picture slot)\n", p_free_picture );        
454 #endif
455         vlc_mutex_unlock( &p_vout->lock );
456         return( p_free_picture );
457     }
458     
459     // No free or destroyed picture could be found
460     intf_DbgMsg( "warning: heap is full\n" );
461     vlc_mutex_unlock( &p_vout->lock );
462     return( NULL );
463 }
464
465 /*******************************************************************************
466  * vout_DestroyPicture: remove a permanent or reserved picture from the heap
467  *******************************************************************************
468  * This function frees a previously reserved picture or a permanent
469  * picture. It is meant to be used when the construction of a picture aborted.
470  * Note that the picture will be destroyed even if it is linked !
471  * This function does not need locking since reserved pictures are ignored by
472  * the output thread.
473  *******************************************************************************/
474 void vout_DestroyPicture( vout_thread_t *p_vout, picture_t *p_pic )
475 {
476 #ifdef DEBUG
477    /* Check if picture status is valid */
478    if( p_pic->i_status != RESERVED_PICTURE )
479    {
480        intf_DbgMsg("error: picture %d has invalid status %d\n", p_pic, p_pic->i_status );       
481    }   
482 #endif
483
484     p_pic->i_status = DESTROYED_PICTURE;
485
486 #ifdef DEBUG_VIDEO
487     intf_DbgMsg("picture %p\n", p_pic);    
488 #endif
489 }
490
491 /*******************************************************************************
492  * vout_LinkPicture: increment reference counter of a picture
493  *******************************************************************************
494  * This function increment the reference counter of a picture in the video
495  * heap. It needs a lock since several producer threads can access the picture.
496  *******************************************************************************/
497 void vout_LinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
498 {
499     vlc_mutex_lock( &p_vout->lock );
500     p_pic->i_refcount++;
501     vlc_mutex_unlock( &p_vout->lock );
502
503 #ifdef DEBUG_VIDEO
504     intf_DbgMsg("picture %p\n", p_pic);    
505 #endif
506 }
507
508 /*******************************************************************************
509  * vout_UnlinkPicture: decrement reference counter of a picture
510  *******************************************************************************
511  * This function decrement the reference counter of a picture in the video heap.
512  *******************************************************************************/
513 void vout_UnlinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
514 {
515     vlc_mutex_lock( &p_vout->lock );
516     p_pic->i_refcount--;
517     if( (p_pic->i_refcount == 0) && (p_pic->i_status == DISPLAYED_PICTURE) )
518     {
519         p_pic->i_status = DESTROYED_PICTURE;
520     }
521     vlc_mutex_unlock( &p_vout->lock );
522
523 #ifdef DEBUG_VIDEO
524     intf_DbgMsg("picture %p\n", p_pic);    
525 #endif
526 }
527
528 /* following functions are local */
529
530 /*******************************************************************************
531  * InitThread: initialize video output thread
532  *******************************************************************************
533  * This function is called from RunThread and performs the second step of the
534  * initialization. It returns 0 on success. Note that the thread's flag are not
535  * modified inside this function.
536  *******************************************************************************/
537 static int InitThread( vout_thread_t *p_vout )
538 {
539     int     i_index;                                          /* generic index */    
540
541     /* Update status */
542     *p_vout->pi_status = THREAD_START;    
543     
544     /* Initialize pictures */    
545     for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++)
546     {
547         p_vout->p_picture[i_index].i_type  = EMPTY_PICTURE;
548         p_vout->p_picture[i_index].i_status= FREE_PICTURE;
549     }
550
551     /* Initialize output method - this function issues its own error messages */
552     if( vout_SysInit( p_vout ) )
553     {
554         *p_vout->pi_status = THREAD_ERROR;        
555         return( 1 );
556     } 
557
558     /* Allocate translation tables */
559     p_vout->p_trans_base = malloc( 4 * 1024 * p_vout->i_bytes_per_pixel );
560     if( p_vout->p_trans_base == NULL )
561     {
562         intf_ErrMsg("error: %s\n", strerror(ENOMEM));
563         return( 1 );                
564     }
565     p_vout->p_trans_red =   p_vout->p_trans_base +           384 *p_vout->i_bytes_per_pixel;
566     p_vout->p_trans_green = p_vout->p_trans_base + (  1024 + 384)*p_vout->i_bytes_per_pixel;
567     p_vout->p_trans_blue =  p_vout->p_trans_base + (2*1024 + 384)*p_vout->i_bytes_per_pixel;
568     p_vout->p_trans_gray =  p_vout->p_trans_base + (3*1024 + 384)*p_vout->i_bytes_per_pixel;
569     
570     /* Build translation tables */
571     BuildTables( p_vout );
572     
573     /* Mark thread as running and return */
574     p_vout->b_active =          1;    
575     *p_vout->pi_status =        THREAD_READY;    
576     intf_DbgMsg("thread ready\n");    
577     return( 0 );    
578 }
579
580 /*******************************************************************************
581  * RunThread: video output thread
582  *******************************************************************************
583  * Video output thread. This function does only returns when the thread is
584  * terminated. It handles the pictures arriving in the video heap and the
585  * display device events.
586  *******************************************************************************/
587 static void RunThread( vout_thread_t *p_vout)
588 {
589     int             i_picture;                                /* picture index */
590     int             i_err;                                       /* error code */
591     int             i_idle_level = 0;                            /* idle level */
592     mtime_t         current_date;                              /* current date */
593     mtime_t         pic_date = 0;                              /* picture date */    
594     mtime_t         last_date = 0;                        /* last picture date */    
595     boolean_t       b_display;                                 /* display flag */    
596     picture_t *     p_pic;                                  /* picture pointer */
597      
598     /* 
599      * Initialize thread and free configuration 
600      */
601     p_vout->b_error = InitThread( p_vout );
602     if( p_vout->b_error )
603     {
604         free( p_vout );                                  /* destroy descriptor */
605         return;        
606     }    
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         for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
620         {
621             if( (p_vout->p_picture[i_picture].i_status == READY_PICTURE) &&
622                 ( (p_pic == NULL) || 
623                   (p_vout->p_picture[i_picture].date < pic_date) ) )
624             {
625                 p_pic = &p_vout->p_picture[i_picture];
626                 pic_date = p_pic->date;                
627             }
628         }
629         current_date = mdate();
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->lock );
645                 p_pic->i_status = p_pic->i_refcount ? DISPLAYED_PICTURE : DESTROYED_PICTURE;
646                 vlc_mutex_unlock( &p_vout->lock );
647 #ifdef DEBUG_VIDEO
648                 intf_DbgMsg( "warning: late picture %p skipped\n", p_pic );
649 #endif
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             else
660             {
661                 /* Picture has not yet been displayed, and has a valid display
662                  * date : render it, then mark it as displayed */
663                 if( p_vout->b_active )
664                 {                    
665                     RenderPicture( p_vout, p_pic );
666                 }                
667                 vlc_mutex_lock( &p_vout->lock );
668                 p_pic->i_status = p_pic->i_refcount ? DISPLAYED_PICTURE : DESTROYED_PICTURE;
669                 vlc_mutex_unlock( &p_vout->lock );
670             }
671         }
672
673         /* 
674          * Rebuild tables if gamma has changed
675          */
676         if( p_vout->b_gamma_change )
677         {
678             p_vout->b_gamma_change = 0;            
679             BuildTables( p_vout );            
680         }        
681
682         /*
683          * Check events, sleep and display picture
684          */
685         i_err = vout_SysManage( p_vout );
686         if( i_err < 0 )
687         {
688             /* A fatal error occured, and the thread must terminate immediately,
689              * without displaying anything - setting b_error to 1 cause the
690              * immediate end of the main while() loop. */
691             p_vout->b_error = 1;
692         }
693         else 
694         {            
695             if( p_pic )
696             {
697                 /* A picture is ready to be displayed : remove blank screen flag */
698                 last_date =     pic_date;
699                 i_idle_level =  0;
700                 b_display =     1;                
701                 
702                 /* Render additionnal informations */
703                 if( p_vout->b_active && p_vout->b_info )
704                 {
705                     RenderInfo( p_vout );    
706                 }                
707                 
708                 /* Sleep until its display date */
709                 mwait( pic_date );
710             }
711             else
712             {
713                 /* If last picture was a long time ago, increase idle level, reset
714                  * date and render idle screen */
715                 if( !i_err && (current_date - last_date > VOUT_IDLE_DELAY) )
716                 {       
717                     last_date = current_date;                    
718                     b_display = p_vout->b_active && RenderIdle( p_vout, i_idle_level++ );
719                 }
720                 else
721                 {
722                     b_display = 0;                    
723                 }
724                 
725 #ifdef STATS
726                 /* Update counters */
727                 p_vout->c_idle_loops++;
728 #endif
729
730                 /* Sleep to wait for new pictures */
731                 msleep( VOUT_IDLE_SLEEP );
732             }
733
734             /* On awakening, send immediately picture to display */
735             if( b_display && p_vout->b_active )
736             {
737                 vout_SysDisplay( p_vout );
738             }
739         }
740
741 #ifdef STATS
742         /* Update counters */
743         p_vout->c_loops++;
744 #endif
745     } 
746
747     /*
748      * Error loop
749      */
750     if( p_vout->b_error )
751     {
752         ErrorThread( p_vout );        
753     }
754
755     /* End of thread */
756     EndThread( p_vout );
757     intf_DbgMsg( "thread end\n" );
758 }
759
760 /*******************************************************************************
761  * ErrorThread: RunThread() error loop
762  *******************************************************************************
763  * This function is called when an error occured during thread main's loop. The
764  * thread can still receive feed, but must be ready to terminate as soon as
765  * possible.
766  *******************************************************************************/
767 static void ErrorThread( vout_thread_t *p_vout )
768 {
769     /* Wait until a `die' order */
770     while( !p_vout->b_die )
771     {
772         /* Sleep a while */
773         msleep( VOUT_IDLE_SLEEP );                
774     }
775 }
776
777 /*******************************************************************************
778  * EndThread: thread destruction
779  *******************************************************************************
780  * This function is called when the thread ends after a sucessfull 
781  * initialization.
782  *******************************************************************************/
783 static void EndThread( vout_thread_t *p_vout )
784 {
785     int *   pi_status;                                        /* thread status */
786     int     i_picture;
787         
788     /* Store status */
789     pi_status = p_vout->pi_status;    
790     *pi_status = THREAD_END;    
791
792     /* Destroy all remaining pictures */
793     for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
794     {
795         if( p_vout->p_picture[i_picture].i_status != FREE_PICTURE )
796         {
797             free( p_vout->p_picture[i_picture].p_data );
798         }
799     }
800
801     /* Destroy translation tables */
802     free( p_vout->p_trans_base );
803     
804     /* Destroy thread structures allocated by InitThread */
805     vout_SysEnd( p_vout );
806     vout_SysDestroy( p_vout );
807     free( p_vout );
808
809     /* Update status */
810     *pi_status = THREAD_OVER;    
811 }
812
813 /*******************************************************************************
814  * BuildTables: build YUV translation tables
815  *******************************************************************************
816  * This function will build translations tables according to pixel width and
817  * gamma.
818  *******************************************************************************/  
819 static void BuildTables( vout_thread_t *p_vout )
820 {
821     u16 *       p_trans16_red =         (u16 *) p_vout->p_trans_red;
822     u16 *       p_trans16_green =       (u16 *) p_vout->p_trans_green;
823     u16 *       p_trans16_blue =        (u16 *) p_vout->p_trans_blue;
824     u16 *       p_trans16_gray =        (u16 *) p_vout->p_trans_gray;
825     u32 *       p_trans32_red =         (u32 *) p_vout->p_trans_red;
826     u32 *       p_trans32_green =       (u32 *) p_vout->p_trans_green;
827     u32 *       p_trans32_blue =        (u32 *) p_vout->p_trans_blue;
828     u32 *       p_trans32_gray =        (u32 *) p_vout->p_trans_gray;          
829     u8          i_gamma[256];                                   /* gamma table */    
830     int         i_index;                                    /* index in tables */
831     
832     /* Build gamma table */     
833     for( i_index = 0; i_index < 256; i_index++ )
834     {
835         i_gamma[i_index] = 255. * pow( (double)i_index / 255., p_vout->f_gamma );        
836     }
837         
838     /* Build red, green, blue and gray tables */
839     switch( p_vout->i_screen_depth )
840     {
841     case 15:
842         for( i_index = -384; i_index < 640; i_index++) 
843         {
844             p_trans16_red[i_index]     = (i_gamma[CLIP_BYTE( i_index )] & 0xf8)<<7;
845             p_trans16_green[i_index]   = (i_gamma[CLIP_BYTE( i_index )] & 0xf8)<<2;
846             p_trans16_blue[i_index]    =  i_gamma[CLIP_BYTE( i_index )] >> 3;
847             p_trans16_gray[i_index]    = p_trans16_red[i_index] | 
848                 p_trans16_green[i_index] | p_trans16_blue[i_index];            
849         }
850         break;        
851     case 16:
852         for( i_index = -384; i_index < 640; i_index++) 
853         {
854             p_trans16_red[i_index]     = (i_gamma[CLIP_BYTE( i_index )] & 0xf8)<<8;
855             p_trans16_green[i_index]   = (i_gamma[CLIP_BYTE( i_index )] & 0xfc)<<3;
856             p_trans16_blue[i_index]    =  i_gamma[CLIP_BYTE( i_index )] >> 3;
857             p_trans16_gray[i_index]    = p_trans16_red[i_index] |
858                 p_trans16_green[i_index] | p_trans16_blue[i_index];
859         }        
860         break;        
861     case 32:        
862         for( i_index = -384; i_index < 640; i_index++) 
863         {
864             p_trans32_red[i_index]     = i_gamma[CLIP_BYTE( i_index )] <<16;
865             p_trans32_green[i_index]   = i_gamma[CLIP_BYTE( i_index )] <<8;
866             p_trans32_blue[i_index]    = i_gamma[CLIP_BYTE( i_index )] ;
867             p_trans32_gray[i_index]    = p_trans32_red[i_index] |
868                 p_trans32_green[i_index] | p_trans32_blue[i_index];
869         }
870         break;        
871 #ifdef DEBUG
872     default:
873         intf_DbgMsg("error: invalid screen depth %d\n", p_vout->i_screen_depth );
874         break;      
875 #endif
876     } 
877 }
878
879 /*******************************************************************************
880  * RenderPicture: render a picture
881  *******************************************************************************
882  * This function convert a picture from a video heap to a pixel-encoded image
883  * and copy it to the current rendering buffer. No lock is required, since the
884  * rendered picture has been determined as existant, and will only be destroyed
885  * by the vout thread later.
886  * ???? 24 and 32 bpp should probably be separated
887  *******************************************************************************/
888 static void RenderPicture( vout_thread_t *p_vout, picture_t *p_pic )
889 {
890 #ifdef DEBUG_VIDEO
891     /* Send picture informations */
892     intf_DbgMsg("picture %p\n", p_pic );
893
894     /* Store rendering start date */
895     p_vout->picture_render_time = mdate();    
896 #endif
897
898     switch( p_pic->i_type )
899     {
900     case YUV_420_PICTURE:                   /* YUV picture: YUV transformation */        
901     case YUV_422_PICTURE:
902     case YUV_444_PICTURE:
903         if( p_vout->b_grayscale )                                 /* grayscale */
904         {
905             RenderYUVGrayPicture( p_vout, p_pic );            
906         }
907         else if( p_vout->i_bytes_per_pixel == 2 )        /* color 15 or 16 bpp */
908         {
909             RenderYUV16Picture( p_vout, p_pic );        
910         }
911         else if( p_vout->i_bytes_per_pixel == 4 )              /* color 32 bpp */
912         {
913             RenderYUV32Picture( p_vout, p_pic );            
914         }
915         break;        
916 #ifdef DEBUG
917     default:        
918         intf_DbgMsg("error: unknown picture type %d\n", p_pic->i_type );
919         break;        
920 #endif
921     }
922
923 #ifdef DEBUG_VIDEO
924     /* Computes rendering time */
925     p_vout->picture_render_time = mdate() - p_vout->picture_render_time;    
926 #endif
927 }
928
929 /*******************************************************************************
930  * RenderYUVGrayPicture: render YUV picture in grayscale
931  *******************************************************************************
932  * Performs the YUV convertion. The picture sent to this function should only
933  * have YUV_420, YUV_422 or YUV_444 types.
934  *******************************************************************************/
935 static void RenderYUVGrayPicture( vout_thread_t *p_vout, picture_t *p_pic )
936 {
937     int         i_pic_x, i_pic_y;                /* x,y coordinates in picture */
938     int         i_width, i_height;                             /* picture size */
939     int         i_eol_offset;          /* pixels from end of line to next line */   
940     yuv_data_t *p_y;                                     /* Y data base adress */
941     u16 *       p_pic16;                  /* destination picture, 15 or 16 bpp */
942     u32 *       p_pic32;                        /* destination picture, 32 bpp */
943     u16 *       p_trans16_gray;          /* transformation table, 15 or 16 bpp */
944     u32 *       p_trans32_gray;                /* transformation table, 32 bpp */
945  
946     /* Set the base pointers and transformation parameters */
947     p_y =               p_pic->p_y;
948     i_width =           p_pic->i_width;
949     i_height =          p_pic->i_height;
950     i_eol_offset =      p_vout->i_bytes_per_line / p_vout->i_bytes_per_pixel - i_width;
951
952     /* Get base adress for destination image and translation tables, then
953      * transform image */
954     switch( p_vout->i_screen_depth )
955     {
956     case 15:
957     case 16:
958         p_trans16_gray =       (u16 *) p_vout->p_trans_gray;
959         p_pic16 =              (u16 *) vout_SysGetPicture( p_vout );
960         YUV_GRAYSCALE( p_trans16_gray, p_pic16 );
961         break;        
962     case 32:
963         p_trans32_gray =       (u32 *) p_vout->p_trans_gray;
964         p_pic32 =              (u32 *) vout_SysGetPicture( p_vout );
965         YUV_GRAYSCALE( p_trans32_gray, p_pic32 );
966         break;        
967 #ifdef DEBUG
968     default:
969         intf_DbgMsg("error: invalid screen depth %d\n", p_vout->i_screen_depth );
970         break;    
971 #endif      
972     }
973 }
974
975
976 /*******************************************************************************
977  * RenderYUV16Picture: render a 15 or 16 bpp YUV picture
978  *******************************************************************************
979  * Performs the YUV convertion. The picture sent to this function should only
980  * have YUV_420, YUV_422 or YUV_444 types.
981  *******************************************************************************/
982 static void RenderYUV16Picture( vout_thread_t *p_vout, picture_t *p_pic )
983 {
984     int         i_crv, i_cbu, i_cgu, i_cgv;     /* transformation coefficients */
985     int         i_pic_x, i_pic_y;                /* x,y coordinates in picture */
986     int         i_y, i_u, i_v;                           /* Y, U and V samples */
987     int         i_width, i_height;                             /* picture size */
988     int         i_chroma_width;                                /* chroma width */    
989     int         i_eol_offset;          /* pixels from end of line to next line */
990     yuv_data_t *p_y;                                     /* Y data base adress */
991     yuv_data_t *p_u;                                     /* U data base adress */
992     yuv_data_t *p_v;                                     /* V data base adress */
993     u16 *       p_data;                 /* base adress for destination picture */
994     u16 *       p_trans_red;                       /* red transformation table */
995     u16 *       p_trans_green;                   /* green transformation table */
996     u16 *       p_trans_blue;                     /* blue transformation table */
997  
998     /* Choose transformation matrix coefficients */
999     i_crv = MATRIX_COEFFICIENTS_TABLE[p_pic->i_matrix_coefficients][0];
1000     i_cbu = MATRIX_COEFFICIENTS_TABLE[p_pic->i_matrix_coefficients][1];
1001     i_cgu = MATRIX_COEFFICIENTS_TABLE[p_pic->i_matrix_coefficients][2];
1002     i_cgv = MATRIX_COEFFICIENTS_TABLE[p_pic->i_matrix_coefficients][3];
1003
1004     /* Choose the conversions tables and picture address */
1005     p_trans_red =       (u16 *) p_vout->p_trans_red;
1006     p_trans_green =     (u16 *) p_vout->p_trans_green;
1007     p_trans_blue =      (u16 *) p_vout->p_trans_blue;    
1008     p_data =            (u16 *) vout_SysGetPicture( p_vout );
1009
1010     /* Set the base pointers and transformation parameters */
1011     p_y =               p_pic->p_y;
1012     p_u =               p_pic->p_u;
1013     p_v =               p_pic->p_v;
1014     i_width =           p_pic->i_width;
1015     i_height =          p_pic->i_height;
1016     i_chroma_width =    i_width / 2;
1017     i_eol_offset =      p_vout->i_bytes_per_line / 2 - i_width;    
1018         
1019     /* Do YUV transformation - the loops are repeated for optimization */
1020     switch( p_pic->i_type )
1021     {
1022     case YUV_420_PICTURE:                   /* 15 or 16 bpp 420 transformation */
1023 #ifdef HAVE_MMX
1024         vout_YUV420_16_MMX( p_y, p_u, p_v, 
1025                             i_width, i_height, 
1026                             i_width, i_chroma_width,
1027                             0, p_data, 
1028                             0, 0, p_vout->i_bytes_per_line, 
1029                             p_vout->i_screen_depth == 15 );
1030 #else
1031         YUV_TRANSFORM( 420,
1032                        p_trans_red, 
1033                        p_trans_green, 
1034                        p_trans_blue,
1035                        p_data );            
1036 #endif
1037         break;
1038     case YUV_422_PICTURE:                   /* 15 or 16 bpp 422 transformation */
1039         YUV_TRANSFORM( 422,
1040                        p_trans_red, 
1041                        p_trans_green, 
1042                        p_trans_blue,
1043                        p_data );            
1044         break;
1045     case YUV_444_PICTURE:                   /* 15 or 16 bpp 444 transformation */
1046         YUV_TRANSFORM( 444,
1047                        p_trans_red, 
1048                        p_trans_green, 
1049                        p_trans_blue,
1050                        p_data );            
1051         break;                 
1052     }
1053 }
1054
1055 /*******************************************************************************
1056  * RenderYUV32Picture: render a 32 bpp YUV picture
1057  *******************************************************************************
1058  * Performs the YUV convertion. The picture sent to this function should only
1059  * have YUV_420, YUV_422 or YUV_444 types.
1060  *******************************************************************************/
1061 static void RenderYUV32Picture( vout_thread_t *p_vout, picture_t *p_pic )
1062 {
1063     int         i_crv, i_cbu, i_cgu, i_cgv;     /* transformation coefficients */
1064     int         i_pic_x, i_pic_y;                /* x,y coordinates in picture */
1065     int         i_y, i_u, i_v;                           /* Y, U and V samples */
1066     int         i_width, i_height;                             /* picture size */
1067     int         i_chroma_width;                                /* chroma width */    
1068     int         i_eol_offset;          /* pixels from end of line to next line */
1069     yuv_data_t *p_y;                                     /* Y data base adress */
1070     yuv_data_t *p_u;                                     /* U data base adress */
1071     yuv_data_t *p_v;                                     /* V data base adress */
1072     u32 *       p_data;                 /* base adress for destination picture */
1073     u32 *       p_trans_red;                       /* red transformation table */
1074     u32 *       p_trans_green;                   /* green transformation table */
1075     u32 *       p_trans_blue;                     /* blue transformation table */
1076  
1077     /* Choose transformation matrix coefficients */
1078     i_crv = MATRIX_COEFFICIENTS_TABLE[p_pic->i_matrix_coefficients][0];
1079     i_cbu = MATRIX_COEFFICIENTS_TABLE[p_pic->i_matrix_coefficients][1];
1080     i_cgu = MATRIX_COEFFICIENTS_TABLE[p_pic->i_matrix_coefficients][2];
1081     i_cgv = MATRIX_COEFFICIENTS_TABLE[p_pic->i_matrix_coefficients][3];
1082
1083     /* Choose the conversions tables and picture address */
1084     p_trans_red =       (u32 *) p_vout->p_trans_red;
1085     p_trans_green =     (u32 *) p_vout->p_trans_green;
1086     p_trans_blue =      (u32 *) p_vout->p_trans_blue;    
1087     p_data =            (u32 *) vout_SysGetPicture( p_vout );
1088
1089     /* Set the base pointers and transformation parameters */
1090     p_y =               p_pic->p_y;
1091     p_u =               p_pic->p_u;
1092     p_v =               p_pic->p_v;
1093     i_width =           p_pic->i_width;
1094     i_height =          p_pic->i_height;
1095     i_chroma_width =    i_width / 2;
1096     i_eol_offset =      p_vout->i_bytes_per_line / p_vout->i_bytes_per_pixel - i_width;
1097         
1098     /* Do YUV transformation - the loops are repeated for optimization */
1099     switch( p_pic->i_type )
1100     {
1101     case YUV_420_PICTURE:                         /* 32 bpp 420 transformation */
1102         YUV_TRANSFORM( 420,
1103                        p_trans_red, 
1104                        p_trans_green, 
1105                        p_trans_blue,
1106                        p_data );            
1107         break;
1108     case YUV_422_PICTURE:                         /* 32 bpp 422 transformation */
1109         YUV_TRANSFORM( 422,
1110                        p_trans_red, 
1111                        p_trans_green, 
1112                        p_trans_blue,
1113                        p_data );            
1114         break;
1115     case YUV_444_PICTURE:                         /* 32 bpp 444 transformation */
1116         YUV_TRANSFORM( 444,
1117                        p_trans_red, 
1118                        p_trans_green, 
1119                        p_trans_blue,
1120                        p_data );            
1121         break;                 
1122     }
1123 }
1124
1125 /*******************************************************************************
1126  * RenderInfo: print additionnal informations on a picture
1127  *******************************************************************************
1128  * This function will add informations such as fps and buffer size on a picture
1129  *******************************************************************************/
1130 static void RenderInfo( vout_thread_t *p_vout )
1131 {
1132     char        psz_buffer[256];                              /* string buffer */
1133 #ifdef DEBUG
1134     int         i_ready_pic = 0;                             /* ready pictures */
1135     int         i_reserved_pic = 0;                       /* reserved pictures */
1136     int         i_picture;                                    /* picture index */
1137 #endif
1138
1139 #ifdef STATS
1140     /* Print FPS rate in upper right corner */
1141     if( p_vout->c_fps_samples > VOUT_FPS_SAMPLES )
1142     {        
1143         sprintf( psz_buffer, "%.2f fps", (double) VOUT_FPS_SAMPLES * 1000000 /
1144                  ( p_vout->fps_sample[ (p_vout->c_fps_samples - 1) % VOUT_FPS_SAMPLES ] -
1145                    p_vout->fps_sample[ p_vout->c_fps_samples % VOUT_FPS_SAMPLES ] ) );        
1146         vout_SysPrint( p_vout, p_vout->i_width, 0, 1, -1, psz_buffer );
1147     }
1148
1149     /* Print statistics in upper left corner */
1150     sprintf( psz_buffer, "gamma=%.2f   %ld frames (%.1f %% idle)", 
1151              p_vout->f_gamma, p_vout->c_fps_samples, p_vout->c_loops ? 
1152              (double ) p_vout->c_idle_loops * 100 / p_vout->c_loops : 100. );    
1153     vout_SysPrint( p_vout, 0, 0, -1, -1, psz_buffer );    
1154 #endif
1155     
1156 #ifdef DEBUG
1157     /* Print heap state in lower left corner  */
1158     for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
1159     {
1160         switch( p_vout->p_picture[i_picture].i_status )
1161         {
1162         case RESERVED_PICTURE:
1163             i_reserved_pic++;            
1164             break;            
1165         case READY_PICTURE:
1166             i_ready_pic++;            
1167             break;            
1168         }        
1169     }
1170     sprintf( psz_buffer, "video heap: %d/%d/%d", i_reserved_pic, i_ready_pic, 
1171              VOUT_MAX_PICTURES );
1172     vout_SysPrint( p_vout, 0, p_vout->i_height, -1, 1, psz_buffer );    
1173 #endif
1174
1175 #ifdef DEBUG_VIDEO
1176     /* Print rendering statistics in lower right corner */
1177     sprintf( psz_buffer, "picture rendering time: %lu us", 
1178              (unsigned long) p_vout->picture_render_time );    
1179     vout_SysPrint( p_vout, p_vout->i_width, p_vout->i_height, 1, 1, psz_buffer );    
1180 #endif
1181 }
1182
1183 /*******************************************************************************
1184  * RenderIdle: render idle picture
1185  *******************************************************************************
1186  * This function will clear the display or print a logo. Level will vary from 0
1187  * to a very high value that noone should never reach. It returns non 0 if 
1188  * something needs to be displayed and 0 if the previous picture can be kept.
1189  *******************************************************************************/
1190 static int RenderIdle( vout_thread_t *p_vout, int i_level )
1191 {
1192     byte_t      *pi_pic;                            /* pointer to picture data */
1193     
1194     /* Get frame pointer and clear display */
1195     pi_pic = vout_SysGetPicture( p_vout );    
1196      
1197     
1198     switch( i_level )
1199     {
1200     case 0:                                           /* level 0: clear screen */
1201         memset( pi_pic, 0, p_vout->i_bytes_per_line * p_vout->i_height );
1202         break;                
1203     case 1:                                            /* level 1: "no stream" */        
1204         memset( pi_pic, 0, p_vout->i_bytes_per_line * p_vout->i_height );
1205         vout_SysPrint( p_vout, p_vout->i_width / 2, p_vout->i_height / 2,
1206                        0, 0, "no stream" );     
1207         break;
1208     case 50:                                    /* level 50: copyright message */
1209         memset( pi_pic, 0, p_vout->i_bytes_per_line * p_vout->i_height );
1210         vout_SysPrint( p_vout, p_vout->i_width / 2, p_vout->i_height / 2,
1211                        0, 0, COPYRIGHT_MESSAGE );        
1212         break; 
1213     default:                            /* other levels: keep previous picture */
1214         return( 0 );
1215         break;        
1216     }
1217
1218     return( 1 );    
1219 }
1220