]> git.sesse.net Git - vlc/blob - src/video_output/video_output.c
Changement de l'API de vout (chroma_width)
[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 #ifdef VIDEO_X11
19 #include <X11/Xlib.h>                           /* for video_sys.h in X11 mode */
20 #endif
21
22 #include "common.h"
23 #include "config.h"
24 #include "mtime.h"
25 #include "vlc_thread.h"
26 #include "video.h"
27 #include "video_output.h"
28 #include "video_sys.h"
29 #include "video_yuv.h"
30 #include "intf_msg.h"
31 #include "main.h"
32
33 /*******************************************************************************
34  * Local prototypes
35  *******************************************************************************/
36 static int      InitThread              ( vout_thread_t *p_vout );
37 static void     RunThread               ( vout_thread_t *p_vout );
38 static void     ErrorThread             ( vout_thread_t *p_vout );
39 static void     EndThread               ( vout_thread_t *p_vout );
40 static void     RenderPicture           ( vout_thread_t *p_vout, picture_t *p_pic );
41 static void     RenderPictureInfo       ( vout_thread_t *p_vout, picture_t *p_pic );
42 static int      RenderIdle              ( vout_thread_t *p_vout, int i_level );
43
44 /*******************************************************************************
45  * vout_CreateThread: creates a new video output thread
46  *******************************************************************************
47  * This function creates a new video output thread, and returns a pointer
48  * to its description. On error, it returns NULL.
49  * If pi_status is NULL, then the function will block until the thread is ready.
50  * If not, it will be updated using one of the THREAD_* constants.
51  *******************************************************************************/
52 vout_thread_t * vout_CreateThread               ( 
53 #ifdef VIDEO_X11
54                                                   char *psz_display, Window root_window, 
55 #endif
56                                                   int i_width, int i_height, int *pi_status 
57                                                 )
58 {
59     vout_thread_t * p_vout;                               /* thread descriptor */
60     int             i_status;                                 /* thread status */
61
62     /* Allocate descriptor */
63     p_vout = (vout_thread_t *) malloc( sizeof(vout_thread_t) );
64     if( p_vout == NULL )
65     {
66         intf_ErrMsg("error: %s\n", strerror(ENOMEM));        
67         return( NULL );
68     }
69
70     /* Initialize thread properties */
71     p_vout->b_die               = 0;
72     p_vout->b_error             = 0;    
73     p_vout->b_active            = 0;
74     p_vout->pi_status           = (pi_status != NULL) ? pi_status : &i_status;
75     *p_vout->pi_status          = THREAD_CREATE;    
76
77     /* Initialize some fields used by the system-dependant method - these fields will
78      * probably be modified by the method, and are only preferences */
79 #ifdef DEBUG
80     p_vout->b_info              = 1;    
81 #else
82     p_vout->b_info              = 0;    
83 #endif
84     p_vout->b_grayscale         = main_GetIntVariable( VOUT_GRAYSCALE_VAR, 
85                                                        VOUT_GRAYSCALE_DEFAULT );
86     p_vout->i_width             = i_width;
87     p_vout->i_height            = i_height;
88     p_vout->i_bytes_per_line    = i_width * 2;    
89     p_vout->i_screen_depth      = 15;
90     p_vout->i_bytes_per_pixel   = 2;
91     p_vout->f_x_ratio           = 1;
92     p_vout->f_y_ratio           = 1;
93     p_vout->f_gamma             = VOUT_GAMMA;    
94     intf_DbgMsg("wished configuration: %dx%d,%d (%d bytes/pixel, %d bytes/line), ratio %.2f:%.2f, gray=%d\n",
95                 p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
96                 p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line,
97                 p_vout->f_x_ratio, p_vout->f_y_ratio, p_vout->b_grayscale );
98    
99     /* Create and initialize system-dependant method - this function issues its
100      * own error messages */
101     if( vout_SysCreate( p_vout
102 #if defined(VIDEO_X11)
103                         , psz_display, root_window 
104 #endif
105         ) )
106     {
107       free( p_vout );
108       return( NULL );
109     }
110     intf_DbgMsg("actual configuration: %dx%d,%d (%d bytes/pixel, %d bytes/line), ratio %.2f:%.2f, gray=%d\n",
111                 p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
112                 p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line,
113                 p_vout->f_x_ratio, p_vout->f_y_ratio, p_vout->b_grayscale );
114
115     /* Initialize changement properties */
116     p_vout->b_gamma_change      = 0;
117     p_vout->i_new_width         = p_vout->i_width;
118     p_vout->i_new_height        = p_vout->i_height; 
119
120 #ifdef STATS
121     /* Initialize statistics fields */
122     p_vout->c_loops             = 0;
123     p_vout->c_idle_loops        = 0;
124     p_vout->c_fps_samples       = 0;
125 #endif      
126
127     /* Create thread and set locks */
128     vlc_mutex_init( &p_vout->lock );
129     if( vlc_thread_create( &p_vout->thread_id, "video output", 
130                            (void *) RunThread, (void *) p_vout) )
131     {
132         intf_ErrMsg("error: %s\n", strerror(ENOMEM));
133         vout_SysDestroy( p_vout );
134         free( p_vout );
135         return( NULL );
136     }   
137
138     intf_Msg("Video: display initialized (%dx%d, %d bpp)\n", 
139              p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth );    
140
141     /* If status is NULL, wait until the thread is created */
142     if( pi_status == NULL )
143     {
144         do
145         {            
146             msleep( THREAD_SLEEP );
147         }while( (i_status != THREAD_READY) && (i_status != THREAD_ERROR) 
148                 && (i_status != THREAD_FATAL) );
149         if( i_status != THREAD_READY )
150         {
151             return( NULL );            
152         }        
153     }
154     return( p_vout );
155 }
156
157 /*******************************************************************************
158  * vout_DestroyThread: destroys a previously created thread
159  *******************************************************************************
160  * Destroy a terminated thread. 
161  * The function will request a destruction of the specified thread. If pi_error
162  * is NULL, it will return once the thread is destroyed. Else, it will be 
163  * update using one of the THREAD_* constants.
164  *******************************************************************************/
165 void vout_DestroyThread( vout_thread_t *p_vout, int *pi_status )
166 {  
167     int     i_status;                                         /* thread status */
168
169     /* Set status */
170     p_vout->pi_status = (pi_status != NULL) ? pi_status : &i_status;
171     *p_vout->pi_status = THREAD_DESTROY;    
172      
173     /* Request thread destruction */
174     p_vout->b_die = 1;
175
176     /* If status is NULL, wait until thread has been destroyed */
177     if( pi_status == NULL )
178     {
179         do
180         {
181             msleep( THREAD_SLEEP );
182         }while( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR) 
183                 && (i_status != THREAD_FATAL) );   
184     }
185 }
186
187 /*******************************************************************************
188  * vout_DisplayPicture: display a picture
189  *******************************************************************************
190  * Remove the reservation flag of a picture, which will cause it to be ready for
191  * display. The picture does not need to be locked, since it is ignored by
192  * the output thread if is reserved.
193  *******************************************************************************/
194 void  vout_DisplayPicture( vout_thread_t *p_vout, picture_t *p_pic )
195 {
196 #ifdef DEBUG_VIDEO
197     char        psz_date[MSTRTIME_MAX_SIZE];         /* buffer for date string */
198 #endif
199
200 #ifdef DEBUG
201     /* Check if picture status is valid */
202     if( p_pic->i_status != RESERVED_PICTURE )
203     {
204         intf_DbgMsg("error: picture %d has invalid status %d\n", p_pic, p_pic->i_status );       
205     }   
206 #endif
207
208     /* Remove reservation flag */
209     p_pic->i_status = READY_PICTURE;
210
211 #ifdef DEBUG_VIDEO
212     /* Send picture informations */
213     intf_DbgMsg("picture %p: type=%d, %dx%d, date=%s\n", p_pic, p_pic->i_type, 
214                 p_pic->i_width,p_pic->i_height, mstrtime( psz_date, p_pic->date ) );    
215 #endif
216 }
217
218 /*******************************************************************************
219  * vout_CreatePicture: allocate a picture in the video output heap.
220  *******************************************************************************
221  * This function create a reserved image in the video output heap. 
222  * A null pointer is returned if the function fails. This method provides an
223  * already allocated zone of memory in the picture data fields. It needs locking
224  * since several pictures can be created by several producers threads. 
225  *******************************************************************************/
226 picture_t *vout_CreatePicture( vout_thread_t *p_vout, int i_type, 
227                                int i_width, int i_height )
228 {
229     int         i_picture;                                    /* picture index */
230     int         i_chroma_width;                                /* chroma width */    
231     picture_t * p_free_picture = NULL;                   /* first free picture */    
232     picture_t * p_destroyed_picture = NULL;         /* first destroyed picture */    
233
234     /* Get lock */
235     vlc_mutex_lock( &p_vout->lock );
236
237     /* 
238      * Look for an empty place 
239      */
240     for( i_picture = 0; 
241          i_picture < VOUT_MAX_PICTURES; 
242          i_picture++ )
243     {
244         if( p_vout->p_picture[i_picture].i_status == DESTROYED_PICTURE )
245         {
246             /* Picture is marked for destruction, but is still allocated - note
247              * that if width and type are the same for two pictures, chroma_width 
248              * should also be the same */
249             if( (p_vout->p_picture[i_picture].i_type           == i_type)   &&
250                 (p_vout->p_picture[i_picture].i_height         == i_height) &&
251                 (p_vout->p_picture[i_picture].i_width          == i_width) )
252             {
253                 /* Memory size do match : memory will not be reallocated, and function
254                  * can end immediately - this is the best possible case, since no
255                  * memory allocation needs to be done */
256                 p_vout->p_picture[i_picture].i_status = RESERVED_PICTURE;
257 #ifdef DEBUG_VIDEO
258                 intf_DbgMsg("picture %p (in destroyed picture slot)\n", 
259                             &p_vout->p_picture[i_picture] );                
260 #endif
261                 vlc_mutex_unlock( &p_vout->lock );
262                 return( &p_vout->p_picture[i_picture] );
263             }
264             else if( p_destroyed_picture == NULL )
265             {
266                 /* Memory size do not match, but picture index will be kept in
267                  * case no other place are left */
268                 p_destroyed_picture = &p_vout->p_picture[i_picture];                
269             }       
270         }
271         else if( (p_free_picture == NULL) && 
272                  (p_vout->p_picture[i_picture].i_status == FREE_PICTURE ))
273         {
274             /* Picture is empty and ready for allocation */
275             p_free_picture = &p_vout->p_picture[i_picture];            
276         }
277     }
278
279     /* If no free picture is available, use a destroyed picture */
280     if( (p_free_picture == NULL) && (p_destroyed_picture != NULL ) )
281     { 
282         /* No free picture or matching destroyed picture has been found, but
283          * a destroyed picture is still avalaible */
284         free( p_destroyed_picture->p_data );        
285         p_free_picture = p_destroyed_picture;        
286     }
287
288     /*
289      * Prepare picture
290      */
291     if( p_free_picture != NULL )
292     {
293         /* Allocate memory */
294         switch( i_type )
295         {
296         case YUV_420_PICTURE:          /* YUV 420: 1,1/4,1/4 samples per pixel */
297             i_chroma_width = i_width / 4;            
298             p_free_picture->p_data = malloc( i_height * i_chroma_width * 6 * sizeof( yuv_data_t ) );
299             p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
300             p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data + i_height * i_chroma_width * 4;
301             p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data + i_height * i_chroma_width * 5;
302             break;
303         case YUV_422_PICTURE:          /* YUV 422: 1,1/2,1/2 samples per pixel */
304             i_chroma_width = i_width / 2;            
305             p_free_picture->p_data = malloc( i_height * i_chroma_width * 4 * sizeof( yuv_data_t ) );
306             p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
307             p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data + i_height * i_chroma_width * 2;
308             p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data + i_height * i_chroma_width * 3;
309             break;
310         case YUV_444_PICTURE:              /* YUV 444: 1,1,1 samples per pixel */
311             i_chroma_width = i_width;            
312             p_free_picture->p_data = malloc( i_height * i_chroma_width * 3 * sizeof( yuv_data_t ) );
313             p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
314             p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data + i_height * i_chroma_width;
315             p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data + i_height * i_chroma_width * 2;
316             break;                
317 #ifdef DEBUG
318         default:
319             intf_DbgMsg("error: unknown picture type %d\n", i_type );
320             p_free_picture->p_data   =  NULL;            
321             break;            
322 #endif    
323         }
324
325         if( p_free_picture->p_data != NULL )
326         {        
327             /* Copy picture informations, set some default values */
328             p_free_picture->i_type                      = i_type;
329             p_free_picture->i_status                    = RESERVED_PICTURE;
330             p_free_picture->i_matrix_coefficients       = 1; 
331             p_free_picture->i_width                     = i_width;
332             p_free_picture->i_height                    = i_height;
333             p_free_picture->i_chroma_width              = i_chroma_width;            
334             p_free_picture->i_display_horizontal_offset = 0;
335             p_free_picture->i_display_vertical_offset   = 0;            
336             p_free_picture->i_display_width             = i_width;
337             p_free_picture->i_display_height            = i_height;
338             p_free_picture->i_aspect_ratio              = AR_SQUARE_PICTURE;            
339             p_free_picture->i_refcount                  = 0;            
340         }
341         else
342         {
343             /* Memory allocation failed : set picture as empty */
344             p_free_picture->i_type   =  EMPTY_PICTURE;            
345             p_free_picture->i_status =  FREE_PICTURE;            
346             p_free_picture =            NULL;            
347             intf_ErrMsg("warning: %s\n", strerror( ENOMEM ) );            
348         }
349         
350 #ifdef DEBUG_VIDEO
351         intf_DbgMsg("picture %p (in free picture slot)\n", p_free_picture );        
352 #endif
353         vlc_mutex_unlock( &p_vout->lock );
354         return( p_free_picture );
355     }
356     
357     // No free or destroyed picture could be found
358     intf_DbgMsg( "warning: heap is full\n" );
359     vlc_mutex_unlock( &p_vout->lock );
360     return( NULL );
361 }
362
363 /*******************************************************************************
364  * vout_DestroyPicture: remove a permanent or reserved picture from the heap
365  *******************************************************************************
366  * This function frees a previously reserved picture or a permanent
367  * picture. It is meant to be used when the construction of a picture aborted.
368  * Note that the picture will be destroyed even if it is linked !
369  * This function does not need locking since reserved pictures are ignored by
370  * the output thread.
371  *******************************************************************************/
372 void vout_DestroyPicture( vout_thread_t *p_vout, picture_t *p_pic )
373 {
374 #ifdef DEBUG
375    /* Check if picture status is valid */
376    if( p_pic->i_status != RESERVED_PICTURE )
377    {
378        intf_DbgMsg("error: picture %d has invalid status %d\n", p_pic, p_pic->i_status );       
379    }   
380 #endif
381
382     p_pic->i_status = DESTROYED_PICTURE;
383
384 #ifdef DEBUG_VIDEO
385     intf_DbgMsg("picture %p\n", p_pic);    
386 #endif
387 }
388
389 /*******************************************************************************
390  * vout_LinkPicture: increment reference counter of a picture
391  *******************************************************************************
392  * This function increment the reference counter of a picture in the video
393  * heap. It needs a lock since several producer threads can access the picture.
394  *******************************************************************************/
395 void vout_LinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
396 {
397     vlc_mutex_lock( &p_vout->lock );
398     p_pic->i_refcount++;
399     vlc_mutex_unlock( &p_vout->lock );
400
401 #ifdef DEBUG_VIDEO
402     intf_DbgMsg("picture %p\n", p_pic);    
403 #endif
404 }
405
406 /*******************************************************************************
407  * vout_UnlinkPicture: decrement reference counter of a picture
408  *******************************************************************************
409  * This function decrement the reference counter of a picture in the video heap.
410  *******************************************************************************/
411 void vout_UnlinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
412 {
413     vlc_mutex_lock( &p_vout->lock );
414     p_pic->i_refcount--;
415     if( (p_pic->i_refcount == 0) && (p_pic->i_status == DISPLAYED_PICTURE) )
416     {
417         p_pic->i_status = DESTROYED_PICTURE;
418     }
419     vlc_mutex_unlock( &p_vout->lock );
420
421 #ifdef DEBUG_VIDEO
422     intf_DbgMsg("picture %p\n", p_pic);    
423 #endif
424 }
425
426 /* following functions are local */
427
428 /*******************************************************************************
429  * InitThread: initialize video output thread
430  *******************************************************************************
431  * This function is called from RunThread and performs the second step of the
432  * initialization. It returns 0 on success. Note that the thread's flag are not
433  * modified inside this function.
434  *******************************************************************************/
435 static int InitThread( vout_thread_t *p_vout )
436 {
437     int     i_index;                                          /* generic index */    
438
439     /* Update status */
440     *p_vout->pi_status = THREAD_START;    
441
442     /* Initialize output method - this function issues its own error messages */
443     if( vout_SysInit( p_vout ) )
444     {
445         *p_vout->pi_status = THREAD_ERROR;        
446         return( 1 );
447     } 
448
449     /* Initialize pictures */    
450     for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++)
451     {
452         p_vout->p_picture[i_index].i_type  = EMPTY_PICTURE;
453         p_vout->p_picture[i_index].i_status= FREE_PICTURE;
454     }
455
456     /* Initialize convertion tables and functions */
457     if( vout_InitTables( p_vout ) )
458     {
459         intf_ErrMsg("error: can't allocate translation tables\n");
460         return( 1 );                
461     }
462     
463     /* Mark thread as running and return */
464     p_vout->b_active =          1;    
465     *p_vout->pi_status =        THREAD_READY;    
466     intf_DbgMsg("thread ready\n");    
467     return( 0 );    
468 }
469
470 /*******************************************************************************
471  * RunThread: video output thread
472  *******************************************************************************
473  * Video output thread. This function does only returns when the thread is
474  * terminated. It handles the pictures arriving in the video heap and the
475  * display device events.
476  *******************************************************************************/
477 static void RunThread( vout_thread_t *p_vout)
478 {
479     int             i_picture;                                /* picture index */
480     int             i_err;                                       /* error code */
481     int             i_idle_level = 0;                            /* idle level */
482     mtime_t         current_date;                              /* current date */
483     mtime_t         pic_date = 0;                              /* picture date */    
484     mtime_t         last_date = 0;                        /* last picture date */    
485     boolean_t       b_display;                                 /* display flag */    
486     picture_t *     p_pic;                                  /* picture pointer */
487      
488     /* 
489      * Initialize thread and free configuration 
490      */
491     p_vout->b_error = InitThread( p_vout );
492     if( p_vout->b_error )
493     {
494         free( p_vout );                                  /* destroy descriptor */
495         return;        
496     }    
497
498     /*
499      * Main loop - it is not executed if an error occured during
500      * initialization
501      */
502     while( (!p_vout->b_die) && (!p_vout->b_error) )
503     {            
504         /* 
505          * Find the picture to display - this operation does not need lock,
506          * since only READY_PICTURES are handled 
507          */
508         p_pic = NULL;         
509         for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
510         {
511             if( (p_vout->p_picture[i_picture].i_status == READY_PICTURE) &&
512                 ( (p_pic == NULL) || 
513                   (p_vout->p_picture[i_picture].date < pic_date) ) )
514             {
515                 p_pic = &p_vout->p_picture[i_picture];
516                 pic_date = p_pic->date;                
517             }
518         }
519         current_date = mdate();
520
521         /* 
522          * Render picture if any
523          */
524         if( p_pic )
525         {
526 #ifdef STATS
527             /* Computes FPS rate */
528             p_vout->fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ] = pic_date;
529 #endif      
530             if( pic_date < current_date )
531             {
532                 /* Picture is late: it will be destroyed and the thread will sleep and
533                  * go to next picture */
534                 vlc_mutex_lock( &p_vout->lock );
535                 p_pic->i_status = p_pic->i_refcount ? DISPLAYED_PICTURE : DESTROYED_PICTURE;
536                 vlc_mutex_unlock( &p_vout->lock );
537 #ifdef DEBUG_VIDEO
538                 intf_DbgMsg( "warning: late picture %p skipped\n", p_pic );
539 #endif
540                 p_pic =         NULL;                
541             }
542             else if( pic_date > current_date + VOUT_DISPLAY_DELAY )
543             {
544                 /* A picture is ready to be rendered, but its rendering date is
545                  * far from the current one so the thread will perform an empty loop
546                  * as if no picture were found. The picture state is unchanged */
547                 p_pic =         NULL;                
548             }
549             else
550             {
551                 /* Picture has not yet been displayed, and has a valid display
552                  * date : render it, then mark it as displayed */
553                 if( p_vout->b_active )
554                 {                    
555                     RenderPicture( p_vout, p_pic );
556                     if( p_vout->b_info )
557                     {
558                         RenderPictureInfo( p_vout, p_pic );                        
559                     }                    
560                 }                
561                 vlc_mutex_lock( &p_vout->lock );
562                 p_pic->i_status = p_pic->i_refcount ? DISPLAYED_PICTURE : DESTROYED_PICTURE;
563                 vlc_mutex_unlock( &p_vout->lock );
564             }
565         }
566
567         /* 
568          * Rebuild tables if gamma has changed
569          */
570         if( p_vout->b_gamma_change )
571         {
572             //??
573             p_vout->b_gamma_change = 0;            
574             vout_ResetTables( p_vout );            // ?? test return value
575         }        
576
577         /*
578          * Check events, sleep and display picture
579          */
580         i_err = vout_SysManage( p_vout );
581         if( i_err < 0 )
582         {
583             /* A fatal error occured, and the thread must terminate immediately,
584              * without displaying anything - setting b_error to 1 cause the
585              * immediate end of the main while() loop. */
586             p_vout->b_error = 1;
587         }
588         else 
589         {            
590             if( p_pic )
591             {
592                 /* A picture is ready to be displayed : remove blank screen flag */
593                 last_date =     pic_date;
594                 i_idle_level =  0;
595                 b_display =     1;                
596                 
597                 /* Sleep until its display date */
598                 mwait( pic_date );
599             }
600             else
601             {
602                 /* If last picture was a long time ago, increase idle level, reset
603                  * date and render idle screen */
604                 if( !i_err && (current_date - last_date > VOUT_IDLE_DELAY) )
605                 {       
606                     last_date = current_date;                    
607                     b_display = p_vout->b_active && RenderIdle( p_vout, i_idle_level++ );
608                 }
609                 else
610                 {
611                     b_display = 0;                    
612                 }
613                 
614 #ifdef STATS
615                 /* Update counters */
616                 p_vout->c_idle_loops++;
617 #endif
618
619                 /* Sleep to wait for new pictures */
620                 msleep( VOUT_IDLE_SLEEP );
621             }
622
623             /* On awakening, send immediately picture to display */
624             if( b_display && p_vout->b_active )
625             {
626                 vout_SysDisplay( p_vout );
627             }
628         }
629
630 #ifdef STATS
631         /* Update counters */
632         p_vout->c_loops++;
633 #endif
634     } 
635
636     /*
637      * Error loop
638      */
639     if( p_vout->b_error )
640     {
641         ErrorThread( p_vout );        
642     }
643
644     /* End of thread */
645     EndThread( p_vout );
646     intf_DbgMsg( "thread end\n" );
647 }
648
649 /*******************************************************************************
650  * ErrorThread: RunThread() error loop
651  *******************************************************************************
652  * This function is called when an error occured during thread main's loop. The
653  * thread can still receive feed, but must be ready to terminate as soon as
654  * possible.
655  *******************************************************************************/
656 static void ErrorThread( vout_thread_t *p_vout )
657 {
658     /* Wait until a `die' order */
659     while( !p_vout->b_die )
660     {
661         /* Sleep a while */
662         msleep( VOUT_IDLE_SLEEP );                
663     }
664 }
665
666 /*******************************************************************************
667  * EndThread: thread destruction
668  *******************************************************************************
669  * This function is called when the thread ends after a sucessfull 
670  * initialization.
671  *******************************************************************************/
672 static void EndThread( vout_thread_t *p_vout )
673 {
674     int *   pi_status;                                        /* thread status */
675     int     i_picture;
676         
677     /* Store status */
678     pi_status = p_vout->pi_status;    
679     *pi_status = THREAD_END;    
680
681     /* Destroy all remaining pictures */
682     for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
683     {
684         if( p_vout->p_picture[i_picture].i_status != FREE_PICTURE )
685         {
686             free( p_vout->p_picture[i_picture].p_data );
687         }
688     }
689
690     /* Destroy translation tables */
691     vout_EndTables( p_vout );
692     
693     /* Destroy thread structures allocated by InitThread */
694     vout_SysEnd( p_vout );
695     vout_SysDestroy( p_vout );
696     free( p_vout );
697
698     /* Update status */
699     *pi_status = THREAD_OVER;    
700 }
701
702 /*******************************************************************************
703  * RenderPicture: render a picture
704  *******************************************************************************
705  * This function convert a picture from a video heap to a pixel-encoded image
706  * and copy it to the current rendering buffer. No lock is required, since the
707  * rendered picture has been determined as existant, and will only be destroyed
708  * by the vout thread later.
709  *******************************************************************************/
710 static void RenderPicture( vout_thread_t *p_vout, picture_t *p_pic )
711 {
712 #ifdef DEBUG_VIDEO
713     /* Send picture informations and store rendering start date */
714     intf_DbgMsg("picture %p\n", p_pic );
715     p_vout->picture_render_time = mdate();    
716 #endif
717
718     /* 
719      * Prepare scaling 
720      */
721     if( (p_pic->i_width > p_vout->i_width) || (p_pic->i_height > p_vout->i_height) )
722     {
723 #ifdef VIDEO_X11
724         /* X11: window can be resized, so resize it - the picture won't be 
725          * rendered since any alteration of the window size means recreating the
726          * XImages */
727         p_vout->i_new_width =   p_pic->i_width;
728         p_vout->i_new_height =  p_pic->i_height;
729         return;        
730 #else
731         /* Other drivers: the video output thread can't change its size, so
732          * we need to change the aspect ratio */
733         //????
734 #endif
735     }    
736
737     /*
738      * Choose appropriate rendering function and render picture
739      */
740     switch( p_pic->i_type )
741     {
742     case YUV_420_PICTURE:
743         p_vout->p_ConvertYUV420( p_vout, vout_SysGetPicture( p_vout ),
744                                  p_pic->p_y, p_pic->p_u, p_pic->p_v,
745                                  p_pic->i_width, p_pic->i_height, 0, 0,
746                                  4 );
747         break;        
748     case YUV_422_PICTURE:
749 /*     ???   p_vout->p_convert_yuv_420( p_vout, 
750                                    p_pic->p_y, p_pic->p_u, p_pic->p_v,
751                                    i_chroma_width, i_chroma_height,
752                                    p_vout->i_width / 2, p_vout->i_height,
753                                    p_vout->i_bytes_per_line,
754                                    0, 0, 0 );
755   */      break;        
756     case YUV_444_PICTURE:
757 /*  ???      p_vout->p_convert_yuv_420( p_vout, 
758                                    p_pic->p_y, p_pic->p_u, p_pic->p_v,
759                                    i_chroma_width, i_chroma_height,
760                                    p_vout->i_width, p_vout->i_height,
761                                    p_vout->i_bytes_per_line,
762                                    0, 0, 0 );
763   */      break;                
764 #ifdef DEBUG
765     default:        
766         intf_DbgMsg("error: unknown picture type %d\n", p_pic->i_type );
767         break;        
768 #endif
769     }
770
771     /* 
772      * Terminate scaling 
773      */
774     //??
775
776 #ifdef DEBUG_VIDEO
777     /* Computes rendering time */
778     p_vout->picture_render_time = mdate() - p_vout->picture_render_time;    
779 #endif
780 }
781
782
783
784 /*******************************************************************************
785  * RenderPictureInfo: print additionnal informations on a picture
786  *******************************************************************************
787  * This function will add informations such as fps and buffer size on a picture
788  *******************************************************************************/
789 static void RenderPictureInfo( vout_thread_t *p_vout, picture_t *p_pic )
790 {
791     char        psz_buffer[256];                              /* string buffer */
792 #ifdef DEBUG
793     int         i_ready_pic = 0;                             /* ready pictures */
794     int         i_reserved_pic = 0;                       /* reserved pictures */
795     int         i_picture;                                    /* picture index */
796 #endif
797
798 #ifdef STATS
799     /* 
800      * Print FPS rate in upper right corner 
801      */
802     if( p_vout->c_fps_samples > VOUT_FPS_SAMPLES )
803     {        
804         sprintf( psz_buffer, "%.2f fps", (double) VOUT_FPS_SAMPLES * 1000000 /
805                  ( p_vout->fps_sample[ (p_vout->c_fps_samples - 1) % VOUT_FPS_SAMPLES ] -
806                    p_vout->fps_sample[ p_vout->c_fps_samples % VOUT_FPS_SAMPLES ] ) );        
807         vout_SysPrint( p_vout, p_vout->i_width, 0, 1, -1, psz_buffer );
808     }
809
810     /* 
811      * Print statistics in upper left corner 
812      */
813     sprintf( psz_buffer, "gamma=%.2f   %ld frames (%.1f %% idle)", 
814              p_vout->f_gamma, p_vout->c_fps_samples, p_vout->c_loops ? 
815              (double ) p_vout->c_idle_loops * 100 / p_vout->c_loops : 100. );    
816     vout_SysPrint( p_vout, 0, 0, -1, -1, psz_buffer );    
817 #endif
818     
819 #ifdef DEBUG
820     /* 
821      * Print heap state in lower left corner  
822      */
823     for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
824     {
825         switch( p_vout->p_picture[i_picture].i_status )
826         {
827         case RESERVED_PICTURE:
828             i_reserved_pic++;            
829             break;            
830         case READY_PICTURE:
831             i_ready_pic++;            
832             break;            
833         }        
834     }
835     sprintf( psz_buffer, "video heap: %d/%d/%d", i_reserved_pic, i_ready_pic, 
836              VOUT_MAX_PICTURES );
837     vout_SysPrint( p_vout, 0, p_vout->i_height, -1, 1, psz_buffer );    
838 #endif
839
840 #ifdef DEBUG_VIDEO
841     /* 
842      * Print picture info in lower right corner 
843      */
844     switch( p_pic->i_type )
845     {
846     case YUV_420_PICTURE:
847         sprintf( psz_buffer, "YUV 4:2:0 picture, rendering time: %lu us", 
848                  (unsigned long) p_vout->picture_render_time );
849         break;        
850     case YUV_422_PICTURE:
851         sprintf( psz_buffer, "YUV 4:2:2 picture, rendering time: %lu us", 
852                  (unsigned long) p_vout->picture_render_time );
853         break;        
854     case YUV_444_PICTURE:
855         sprintf( psz_buffer, "YUV 4:4:4 picture, rendering time: %lu us", 
856                  (unsigned long) p_vout->picture_render_time );
857         break;
858     default:
859         sprintf( psz_buffer, "unknown picture, rendering time: %lu us", 
860                  (unsigned long) p_vout->picture_render_time );    
861         break;        
862     }    
863     vout_SysPrint( p_vout, p_vout->i_width, p_vout->i_height, 1, 1, psz_buffer );    
864 #endif
865 }
866
867 /*******************************************************************************
868  * RenderIdle: render idle picture
869  *******************************************************************************
870  * This function will clear the display or print a logo. Level will vary from 0
871  * to a very high value that noone should never reach. It returns non 0 if 
872  * something needs to be displayed and 0 if the previous picture can be kept.
873  *******************************************************************************/
874 static int RenderIdle( vout_thread_t *p_vout, int i_level )
875 {
876     byte_t      *pi_pic;                            /* pointer to picture data */
877     
878     /* Get frame pointer and clear display */
879     pi_pic = vout_SysGetPicture( p_vout );    
880      
881     
882     switch( i_level )
883     {
884     case 0:                                           /* level 0: clear screen */
885         memset( pi_pic, 0, p_vout->i_bytes_per_line * p_vout->i_height );
886         break;                
887     case 1:                                            /* level 1: "no stream" */        
888         memset( pi_pic, 0, p_vout->i_bytes_per_line * p_vout->i_height );
889         vout_SysPrint( p_vout, p_vout->i_width / 2, p_vout->i_height / 2,
890                        0, 0, "no stream" );     
891         break;
892     case 50:                                    /* level 50: copyright message */
893         memset( pi_pic, 0, p_vout->i_bytes_per_line * p_vout->i_height );
894         vout_SysPrint( p_vout, p_vout->i_width / 2, p_vout->i_height / 2,
895                        0, 0, COPYRIGHT_MESSAGE );        
896         break; 
897     default:                            /* other levels: keep previous picture */
898         return( 0 );
899         break;        
900     }
901
902     return( 1 );    
903 }
904