]> git.sesse.net Git - vlc/blob - src/video_output/video_output.c
* libdvdcss enhancements by Billy Biggs <vektor@dumbterm.net>. This breaks
[vlc] / src / video_output / video_output.c
1 /*****************************************************************************
2  * video_output.c : video output thread
3  * This module describes the programming interface for video output threads.
4  * It includes functions allowing to open a new thread, send pictures to a
5  * thread, and destroy a previously oppened video output thread.
6  *****************************************************************************
7  * Copyright (C) 2000 VideoLAN
8  * $Id: video_output.c,v 1.134 2001/07/11 02:01:05 sam Exp $
9  *
10  * Authors: Vincent Seguin <seguin@via.ecp.fr>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #include "defs.h"
31
32 #include <errno.h>                                                 /* ENOMEM */
33 #include <stdlib.h>                                                /* free() */
34 #include <stdio.h>                                              /* sprintf() */
35 #include <string.h>                                            /* strerror() */
36
37 #ifdef STATS
38 #   include <sys/times.h>
39 #endif
40
41 #include "config.h"
42 #include "common.h"
43 #include "threads.h"
44 #include "mtime.h"
45 #include "modules.h"
46
47 #include "video.h"
48 #include "video_output.h"
49 #include "video_text.h"
50 #include "video_spu.h"
51 #include "video_yuv.h"
52
53 #include "intf_msg.h"
54
55 #include "main.h"
56
57 /*****************************************************************************
58  * Local prototypes
59  *****************************************************************************/
60 static int      BinaryLog         ( u32 i );
61 static void     MaskToShift       ( int *pi_left, int *pi_right, u32 i_mask );
62 static int      InitThread        ( vout_thread_t *p_vout );
63 static void     RunThread         ( vout_thread_t *p_vout );
64 static void     ErrorThread       ( vout_thread_t *p_vout );
65 static void     EndThread         ( vout_thread_t *p_vout );
66 static void     DestroyThread     ( vout_thread_t *p_vout, int i_status );
67 static void     Print             ( vout_thread_t *p_vout, int i_x, int i_y,
68                                     int i_h_align, int i_v_align,
69                                     unsigned char *psz_text );
70 static void     SetBuffers        ( vout_thread_t *p_vout, void *, void * );
71 static void     SetBufferArea     ( vout_thread_t *p_vout, int i_x, int i_y,
72                                     int i_w, int i_h );
73 static void     SetBufferPicture  ( vout_thread_t *p_vout, picture_t *p_pic );
74 static void     RenderPicture     ( vout_thread_t *p_vout, picture_t *p_pic );
75 static void     RenderPictureInfo ( vout_thread_t *p_vout, picture_t *p_pic );
76 static void     RenderSubPicture  ( vout_thread_t *p_vout, picture_t *p_pic,
77                                     subpicture_t *p_subpic );
78 static int      RenderIdle        ( vout_thread_t *p_vout );
79 static void     RenderInfo        ( vout_thread_t *p_vout );
80 static int      Manage            ( vout_thread_t *p_vout );
81 static int      Align             ( vout_thread_t *p_vout, int *pi_x,
82                                     int *pi_y, int i_width, int i_height,
83                                     int i_h_align, int i_v_align );
84 static void     SetPalette        ( p_vout_thread_t p_vout, u16 *red,
85                                     u16 *green, u16 *blue, u16 *transp );
86
87 /*****************************************************************************
88  * vout_InitBank: initialize the video output bank.
89  *****************************************************************************/
90 void vout_InitBank ( void )
91 {
92     p_vout_bank->i_count = 0;
93
94     vlc_mutex_init( &p_vout_bank->lock );
95 }
96
97 /*****************************************************************************
98  * vout_EndBank: empty the video output bank.
99  *****************************************************************************
100  * This function ends all unused video outputs and empties the bank in
101  * case of success.
102  *****************************************************************************/
103 void vout_EndBank ( void )
104 {
105     /* Ask all remaining video outputs to die */
106     while( p_vout_bank->i_count )
107     {
108         vout_DestroyThread(
109                 p_vout_bank->pp_vout[ --p_vout_bank->i_count ], NULL );
110     }
111
112     vlc_mutex_destroy( &p_vout_bank->lock );
113 }
114
115 /*****************************************************************************
116  * vout_CreateThread: creates a new video output thread
117  *****************************************************************************
118  * This function creates a new video output thread, and returns a pointer
119  * to its description. On error, it returns NULL.
120  * If pi_status is NULL, then the function will block until the thread is ready.
121  * If not, it will be updated using one of the THREAD_* constants.
122  *****************************************************************************/
123 vout_thread_t * vout_CreateThread   ( int *pi_status )
124 {
125     vout_thread_t * p_vout;                             /* thread descriptor */
126     int             i_status;                               /* thread status */
127     int             i_index;               /* index for array initialization */
128
129     /* Allocate descriptor */
130     p_vout = (vout_thread_t *) malloc( sizeof(vout_thread_t) );
131     if( p_vout == NULL )
132     {
133         intf_ErrMsg( "vout error: vout thread creation returned %s",
134                      strerror(ENOMEM) );
135         return( NULL );
136     }
137
138     /* Choose the best module */
139     p_vout->p_module = module_Need( MODULE_CAPABILITY_VOUT, NULL );
140
141     if( p_vout->p_module == NULL )
142     {
143         intf_ErrMsg( "vout error: no suitable vout module" );
144         free( p_vout );
145         return( NULL );
146     }
147
148 #define f p_vout->p_module->p_functions->vout.functions.vout
149     p_vout->pf_create     = f.pf_create;
150     p_vout->pf_init       = f.pf_init;
151     p_vout->pf_end        = f.pf_end;
152     p_vout->pf_destroy    = f.pf_destroy;
153     p_vout->pf_manage     = f.pf_manage;
154     p_vout->pf_display    = f.pf_display;
155     p_vout->pf_setpalette = f.pf_setpalette;
156 #undef f
157
158     /* Initialize callbacks */
159     p_vout->pf_setbuffers       = SetBuffers;
160
161     if( p_vout->pf_setpalette == NULL )
162     {
163         p_vout->pf_setpalette   = SetPalette;
164     }
165
166     /* Initialize thread properties - thread id and locks will be initialized
167      * later */
168     p_vout->b_die               = 0;
169     p_vout->b_error             = 0;
170     p_vout->b_active            = 0;
171     p_vout->pi_status           = (pi_status != NULL) ? pi_status : &i_status;
172     *p_vout->pi_status          = THREAD_CREATE;
173
174     /* Initialize some fields used by the system-dependant method - these
175      * fields will probably be modified by the method, and are only
176      * preferences */
177     p_vout->i_changes             = 0;
178     p_vout->i_width               = main_GetIntVariable( VOUT_WIDTH_VAR,
179                                                          VOUT_WIDTH_DEFAULT );
180     p_vout->i_height              = main_GetIntVariable( VOUT_HEIGHT_VAR,
181                                                          VOUT_HEIGHT_DEFAULT );
182     p_vout->i_bytes_per_line      = p_vout->i_width * 2;
183     p_vout->i_screen_depth        = main_GetIntVariable( VOUT_DEPTH_VAR,
184                                                          VOUT_DEPTH_DEFAULT );
185     p_vout->i_bytes_per_pixel     = 2;
186     p_vout->f_gamma               = VOUT_GAMMA_DEFAULT; // FIXME: replace with
187                                                         // variable
188     p_vout->b_need_render         = 1;
189     p_vout->b_YCbr                = 0;
190
191     p_vout->b_grayscale           = main_GetIntVariable( VOUT_GRAYSCALE_VAR,
192                                                      VOUT_GRAYSCALE_DEFAULT );
193     p_vout->b_info                = 0;
194     p_vout->b_interface           = 0;
195     p_vout->b_scale               = 1;
196     p_vout->b_fullscreen          = main_GetIntVariable( VOUT_FULLSCREEN_VAR,
197                                                      VOUT_FULLSCREEN_DEFAULT );
198
199     intf_WarnMsg( 3, "vout info: asking for %dx%d, %d/%d bpp (%d Bpl)",
200                   p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
201                   p_vout->i_bytes_per_pixel * 8, p_vout->i_bytes_per_line );
202
203     /* Initialize idle screen */
204     p_vout->last_display_date   = 0;
205     p_vout->last_idle_date      = 0;
206     p_vout->init_display_date   = mdate();
207     p_vout->render_time         = 10000;
208
209     /* Initialize statistics fields */
210     p_vout->c_fps_samples       = 0;
211
212     /* Initialize buffer index */
213     p_vout->i_buffer_index      = 0;
214
215     /* Initialize fonts */
216     p_vout->p_default_font = NULL;
217     p_vout->p_large_font = NULL;
218
219     /* Initialize pictures and subpictures - translation tables and functions
220      * will be initialized later in InitThread */
221     for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++)
222     {
223         p_vout->p_picture[i_index].i_type   = EMPTY_PICTURE;
224         p_vout->p_picture[i_index].i_status = FREE_PICTURE;
225     }
226
227     for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++)
228     {
229         p_vout->p_subpicture[i_index].i_type   = EMPTY_SUBPICTURE;
230         p_vout->p_subpicture[i_index].i_status = FREE_SUBPICTURE;
231     }
232
233     p_vout->i_pictures = 0;
234
235     /* Create thread and set locks */
236     vlc_mutex_init( &p_vout->picture_lock );
237     vlc_mutex_init( &p_vout->subpicture_lock );
238     vlc_mutex_init( &p_vout->change_lock );
239
240     if( vlc_thread_create( &p_vout->thread_id, "video output",
241                            (void *) RunThread, (void *) p_vout) )
242     {
243         intf_ErrMsg("vout error: %s", strerror(ENOMEM));
244         module_Unneed( p_vout->p_module );
245         free( p_vout );
246         return( NULL );
247     }
248
249     /* If status is NULL, wait until the thread is created */
250     if( pi_status == NULL )
251     {
252         do
253         {
254             msleep( THREAD_SLEEP );
255         }while( (i_status != THREAD_READY) && (i_status != THREAD_ERROR)
256                 && (i_status != THREAD_FATAL) );
257         if( i_status != THREAD_READY )
258         {
259             return( NULL );
260         }
261     }
262     return( p_vout );
263 }
264
265 /*****************************************************************************
266  * vout_DestroyThread: destroys a previously created thread
267  *****************************************************************************
268  * Destroy a terminated thread.
269  * The function will request a destruction of the specified thread. If pi_error
270  * is NULL, it will return once the thread is destroyed. Else, it will be
271  * update using one of the THREAD_* constants.
272  *****************************************************************************/
273 void vout_DestroyThread( vout_thread_t *p_vout, int *pi_status )
274 {
275     int     i_status;                                       /* thread status */
276
277     /* Set status */
278     intf_DbgMsg("");
279     p_vout->pi_status = (pi_status != NULL) ? pi_status : &i_status;
280     *p_vout->pi_status = THREAD_DESTROY;
281
282     /* Request thread destruction */
283     p_vout->b_die = 1;
284
285     /* If status is NULL, wait until thread has been destroyed */
286     if( pi_status == NULL )
287     {
288         do
289         {
290             msleep( THREAD_SLEEP );
291         }while( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR)
292                 && (i_status != THREAD_FATAL) );
293     }
294 }
295
296 /*****************************************************************************
297  * vout_DisplaySubPicture: display a subpicture unit
298  *****************************************************************************
299  * Remove the reservation flag of a subpicture, which will cause it to be ready
300  * for display. The picture does not need to be locked, since it is ignored by
301  * the output thread if is reserved.
302  *****************************************************************************/
303 void  vout_DisplaySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
304 {
305 #ifdef TRACE_VOUT
306     char        psz_start[ MSTRTIME_MAX_SIZE ];    /* buffer for date string */
307     char        psz_stop[ MSTRTIME_MAX_SIZE ];     /* buffer for date string */
308 #endif
309
310 #ifdef DEBUG
311     /* Check if status is valid */
312     if( p_subpic->i_status != RESERVED_SUBPICTURE )
313     {
314         intf_ErrMsg("error: subpicture %p has invalid status %d", p_subpic,
315                     p_subpic->i_status );
316     }
317 #endif
318
319     /* Remove reservation flag */
320     p_subpic->i_status = READY_SUBPICTURE;
321
322 #ifdef TRACE_VOUT
323     /* Send subpicture information */
324     intf_DbgMsg("subpicture %p: type=%d, begin date=%s, end date=%s",
325                 p_subpic, p_subpic->i_type,
326                 mstrtime( psz_start, p_subpic->i_start ),
327                 mstrtime( psz_stop, p_subpic->i_stop ) );
328 #endif
329 }
330
331 /*****************************************************************************
332  * vout_CreateSubPicture: allocate a subpicture in the video output heap.
333  *****************************************************************************
334  * This function create a reserved subpicture in the video output heap.
335  * A null pointer is returned if the function fails. This method provides an
336  * already allocated zone of memory in the spu data fields. It needs locking
337  * since several pictures can be created by several producers threads.
338  *****************************************************************************/
339 subpicture_t *vout_CreateSubPicture( vout_thread_t *p_vout, int i_type,
340                                      int i_size )
341 {
342     int                 i_subpic;                        /* subpicture index */
343     subpicture_t *      p_free_subpic = NULL;       /* first free subpicture */
344     subpicture_t *      p_destroyed_subpic = NULL; /* first destroyed subpic */
345
346     /* Get lock */
347     vlc_mutex_lock( &p_vout->subpicture_lock );
348
349     /*
350      * Look for an empty place
351      */
352     for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
353     {
354         if( p_vout->p_subpicture[i_subpic].i_status == DESTROYED_SUBPICTURE )
355         {
356             /* Subpicture is marked for destruction, but is still allocated */
357             if( (p_vout->p_subpicture[i_subpic].i_type  == i_type)   &&
358                 (p_vout->p_subpicture[i_subpic].i_size  >= i_size) )
359             {
360                 /* Memory size do match or is smaller : memory will not be
361                  * reallocated, and function can end immediately - this is
362                  * the best possible case, since no memory allocation needs
363                  * to be done */
364                 p_vout->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE;
365 #ifdef TRACE_VOUT
366                 intf_DbgMsg("subpicture %p (in destroyed subpicture slot)",
367                             &p_vout->p_subpicture[i_subpic] );
368 #endif
369                 vlc_mutex_unlock( &p_vout->subpicture_lock );
370                 return( &p_vout->p_subpicture[i_subpic] );
371             }
372             else if( p_destroyed_subpic == NULL )
373             {
374                 /* Memory size do not match, but subpicture index will be kept
375                  * in case we find no other place */
376                 p_destroyed_subpic = &p_vout->p_subpicture[i_subpic];
377             }
378         }
379         else if( (p_free_subpic == NULL) &&
380                  (p_vout->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE ))
381         {
382             /* Subpicture is empty and ready for allocation */
383             p_free_subpic = &p_vout->p_subpicture[i_subpic];
384         }
385     }
386
387     /* If no free subpictures are available, use a destroyed subpicture */
388     if( (p_free_subpic == NULL) && (p_destroyed_subpic != NULL ) )
389     {
390         /* No free subpicture or matching destroyed subpictures have been
391          * found, but a destroyed subpicture is still avalaible */
392         free( p_destroyed_subpic->p_data );
393         p_free_subpic = p_destroyed_subpic;
394     }
395
396     /*
397      * Prepare subpicture
398      */
399     if( p_free_subpic != NULL )
400     {
401         /* Allocate memory */
402         switch( i_type )
403         {
404         case TEXT_SUBPICTURE:                             /* text subpicture */
405             p_free_subpic->p_data = malloc( i_size + 1 );
406             break;
407         case DVD_SUBPICTURE:                          /* DVD subpicture unit */
408             p_free_subpic->p_data = malloc( i_size );
409             break;
410 #ifdef DEBUG
411         default:
412             intf_ErrMsg("error: unknown subpicture type %d", i_type );
413             p_free_subpic->p_data   =  NULL;
414             break;
415 #endif
416         }
417
418         if( p_free_subpic->p_data != NULL )
419         {
420             /* Copy subpicture information, set some default values */
421             p_free_subpic->i_type                      = i_type;
422             p_free_subpic->i_status                    = RESERVED_SUBPICTURE;
423             p_free_subpic->i_size                      = i_size;
424             p_free_subpic->i_x                         = 0;
425             p_free_subpic->i_y                         = 0;
426             p_free_subpic->i_width                     = 0;
427             p_free_subpic->i_height                    = 0;
428             p_free_subpic->i_horizontal_align          = CENTER_RALIGN;
429             p_free_subpic->i_vertical_align            = CENTER_RALIGN;
430         }
431         else
432         {
433             /* Memory allocation failed : set subpicture as empty */
434             p_free_subpic->i_type   =  EMPTY_SUBPICTURE;
435             p_free_subpic->i_status =  FREE_SUBPICTURE;
436             p_free_subpic =            NULL;
437             intf_ErrMsg( "vout error: spu allocation returned %s",
438                          strerror( ENOMEM ) );
439         }
440
441 #ifdef TRACE_VOUT
442         intf_DbgMsg("subpicture %p (in free subpicture slot)", p_free_subpic );
443 #endif
444         vlc_mutex_unlock( &p_vout->subpicture_lock );
445         return( p_free_subpic );
446     }
447
448     /* No free or destroyed subpicture could be found */
449     intf_DbgMsg( "warning: subpicture heap is full" );
450     vlc_mutex_unlock( &p_vout->subpicture_lock );
451     return( NULL );
452 }
453
454 /*****************************************************************************
455  * vout_DestroySubPicture: remove a subpicture from the heap
456  *****************************************************************************
457  * This function frees a previously reserved subpicture.
458  * It is meant to be used when the construction of a picture aborted.
459  * This function does not need locking since reserved subpictures are ignored
460  * by the output thread.
461  *****************************************************************************/
462 void vout_DestroySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
463 {
464 #ifdef DEBUG
465    /* Check if status is valid */
466    if( p_subpic->i_status != RESERVED_SUBPICTURE )
467    {
468        intf_ErrMsg("error: subpicture %p has invalid status %d",
469                    p_subpic, p_subpic->i_status );
470    }
471 #endif
472
473     p_subpic->i_status = DESTROYED_SUBPICTURE;
474
475 #ifdef TRACE_VOUT
476     intf_DbgMsg("subpicture %p", p_subpic);
477 #endif
478 }
479
480 /*****************************************************************************
481  * vout_DisplayPicture: display a picture
482  *****************************************************************************
483  * Remove the reservation flag of a picture, which will cause it to be ready for
484  * display. The picture won't be displayed until vout_DatePicture has been
485  * called.
486  *****************************************************************************/
487 void  vout_DisplayPicture( vout_thread_t *p_vout, picture_t *p_pic )
488 {
489     vlc_mutex_lock( &p_vout->picture_lock );
490     switch( p_pic->i_status )
491     {
492     case RESERVED_PICTURE:
493         p_pic->i_status = RESERVED_DISP_PICTURE;
494         break;
495     case RESERVED_DATED_PICTURE:
496         p_pic->i_status = READY_PICTURE;
497         break;
498 #ifdef DEBUG
499     default:
500         intf_ErrMsg("error: picture %p has invalid status %d", p_pic, p_pic->i_status );
501         break;
502 #endif
503     }
504
505 #ifdef TRACE_VOUT
506     intf_DbgMsg("picture %p", p_pic);
507 #endif
508     vlc_mutex_unlock( &p_vout->picture_lock );
509 }
510
511 /*****************************************************************************
512  * vout_DatePicture: date a picture
513  *****************************************************************************
514  * Remove the reservation flag of a picture, which will cause it to be ready for
515  * display. The picture won't be displayed until vout_DisplayPicture has been
516  * called.
517  *****************************************************************************/
518 void  vout_DatePicture( vout_thread_t *p_vout, picture_t *p_pic, mtime_t date )
519 {
520 #ifdef TRACE_VOUT
521     char        psz_date[MSTRTIME_MAX_SIZE];                         /* date */
522 #endif
523
524     vlc_mutex_lock( &p_vout->picture_lock );
525     p_pic->date = date;
526     switch( p_pic->i_status )
527     {
528     case RESERVED_PICTURE:
529         p_pic->i_status = RESERVED_DATED_PICTURE;
530         break;
531     case RESERVED_DISP_PICTURE:
532         p_pic->i_status = READY_PICTURE;
533         break;
534 #ifdef DEBUG
535     default:
536         intf_ErrMsg("error: picture %p has invalid status %d", p_pic, p_pic->i_status );
537         break;
538 #endif
539     }
540
541 #ifdef TRACE_VOUT
542     intf_DbgMsg("picture %p, display date: %s", p_pic, mstrtime( psz_date, p_pic->date) );
543 #endif
544     vlc_mutex_unlock( &p_vout->picture_lock );
545 }
546
547 /*****************************************************************************
548  * vout_CreatePicture: allocate a picture in the video output heap.
549  *****************************************************************************
550  * This function create a reserved image in the video output heap.
551  * A null pointer is returned if the function fails. This method provides an
552  * already allocated zone of memory in the picture data fields. It needs locking
553  * since several pictures can be created by several producers threads.
554  *****************************************************************************/
555 picture_t *vout_CreatePicture( vout_thread_t *p_vout, int i_type,
556                                int i_width, int i_height )
557 {
558     int         i_picture;                                  /* picture index */
559     int         i_chroma_width = 0;                          /* chroma width */
560     picture_t * p_free_picture = NULL;                 /* first free picture */
561     picture_t * p_destroyed_picture = NULL;       /* first destroyed picture */
562
563     /* Get lock */
564     vlc_mutex_lock( &p_vout->picture_lock );
565
566     /*
567      * Look for an empty place
568      */
569     for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
570     {
571         if( p_vout->p_picture[i_picture].i_status == DESTROYED_PICTURE )
572         {
573             /* Picture is marked for destruction, but is still allocated - note
574              * that if width and type are the same for two pictures, chroma_width
575              * should also be the same */
576             if( (p_vout->p_picture[i_picture].i_type           == i_type)   &&
577                 (p_vout->p_picture[i_picture].i_height         == i_height) &&
578                 (p_vout->p_picture[i_picture].i_width          == i_width) )
579             {
580                 /* Memory size do match : memory will not be reallocated, and function
581                  * can end immediately - this is the best possible case, since no
582                  * memory allocation needs to be done */
583                 p_vout->p_picture[i_picture].i_status = RESERVED_PICTURE;
584                 p_vout->i_pictures++;
585 #ifdef TRACE_VOUT
586                 intf_DbgMsg("picture %p (in destroyed picture slot)",
587                             &p_vout->p_picture[i_picture] );
588 #endif
589                 vlc_mutex_unlock( &p_vout->picture_lock );
590                 return( &p_vout->p_picture[i_picture] );
591             }
592             else if( p_destroyed_picture == NULL )
593             {
594                 /* Memory size do not match, but picture index will be kept in
595                  * case no other place are left */
596                 p_destroyed_picture = &p_vout->p_picture[i_picture];
597             }
598         }
599         else if( (p_free_picture == NULL) &&
600                  (p_vout->p_picture[i_picture].i_status == FREE_PICTURE ))
601         {
602             /* Picture is empty and ready for allocation */
603             p_free_picture = &p_vout->p_picture[i_picture];
604         }
605     }
606
607     /* If no free picture is available, use a destroyed picture */
608     if( (p_free_picture == NULL) && (p_destroyed_picture != NULL ) )
609     {
610         /* No free picture or matching destroyed picture has been found, but
611          * a destroyed picture is still avalaible */
612         free( p_destroyed_picture->p_data );
613         p_free_picture = p_destroyed_picture;
614     }
615
616     /*
617      * Prepare picture
618      */
619     if( p_free_picture != NULL )
620     {
621         /* Allocate memory */
622         switch( i_type )
623         {
624         case YUV_420_PICTURE:        /* YUV 420: 1,1/4,1/4 samples per pixel */
625             i_chroma_width = i_width / 2;
626             p_free_picture->p_data = malloc( i_height * i_chroma_width * 3 * sizeof( yuv_data_t ) );
627             p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
628             p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*4/2;
629             p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*5/2;
630             break;
631         case YUV_422_PICTURE:        /* YUV 422: 1,1/2,1/2 samples per pixel */
632             i_chroma_width = i_width / 2;
633             p_free_picture->p_data = malloc( i_height * i_chroma_width * 4 * sizeof( yuv_data_t ) );
634             p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
635             p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*2;
636             p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*3;
637             break;
638         case YUV_444_PICTURE:            /* YUV 444: 1,1,1 samples per pixel */
639             i_chroma_width = i_width;
640             p_free_picture->p_data = malloc( i_height * i_chroma_width * 3 * sizeof( yuv_data_t ) );
641             p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
642             p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width;
643             p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*2;
644             break;
645 #ifdef DEBUG
646         default:
647             intf_ErrMsg("error: unknown picture type %d", i_type );
648             p_free_picture->p_data   =  NULL;
649             break;
650 #endif
651         }
652
653         if( p_free_picture->p_data != NULL )
654         {
655             /* Copy picture information, set some default values */
656             p_free_picture->i_type                      = i_type;
657             p_free_picture->i_status                    = RESERVED_PICTURE;
658             p_free_picture->i_matrix_coefficients       = 1;
659             p_free_picture->i_width                     = i_width;
660             p_free_picture->i_height                    = i_height;
661             p_free_picture->i_chroma_width              = i_chroma_width;
662             p_free_picture->i_size                      = i_width * i_height;
663             p_free_picture->i_chroma_size               = i_chroma_width
664                                                             * i_height;
665             p_free_picture->i_display_horizontal_offset = 0;
666             p_free_picture->i_display_vertical_offset   = 0;
667             p_free_picture->i_display_width             = i_width;
668             p_free_picture->i_display_height            = i_height;
669             p_free_picture->i_aspect_ratio              = AR_SQUARE_PICTURE;
670             p_free_picture->i_refcount                  = 0;
671             p_vout->i_pictures++;
672         }
673         else
674         {
675             /* Memory allocation failed : set picture as empty */
676             p_free_picture->i_type   =  EMPTY_PICTURE;
677             p_free_picture->i_status =  FREE_PICTURE;
678             p_free_picture =            NULL;
679             intf_ErrMsg( "vout error: picture allocation returned %s",
680                          strerror( ENOMEM ) );
681         }
682
683 #ifdef TRACE_VOUT
684         intf_DbgMsg("picture %p (in free picture slot)", p_free_picture );
685 #endif
686         vlc_mutex_unlock( &p_vout->picture_lock );
687
688         /* Initialize mutex */
689         vlc_mutex_init( &(p_free_picture->lock_deccount) );
690
691         return( p_free_picture );
692     }
693
694     /* No free or destroyed picture could be found */
695     intf_DbgMsg( "warning: picture heap is full" );
696     vlc_mutex_unlock( &p_vout->picture_lock );
697     return( NULL );
698 }
699
700 /*****************************************************************************
701  * vout_DestroyPicture: remove a permanent or reserved picture from the heap
702  *****************************************************************************
703  * This function frees a previously reserved picture or a permanent
704  * picture. It is meant to be used when the construction of a picture aborted.
705  * Note that the picture will be destroyed even if it is linked !
706  *****************************************************************************/
707 void vout_DestroyPicture( vout_thread_t *p_vout, picture_t *p_pic )
708 {
709     vlc_mutex_lock( &p_vout->picture_lock );
710
711 #ifdef DEBUG
712     /* Check if picture status is valid */
713     if( (p_pic->i_status != RESERVED_PICTURE) &&
714         (p_pic->i_status != RESERVED_DATED_PICTURE) &&
715         (p_pic->i_status != RESERVED_DISP_PICTURE) )
716     {
717         intf_ErrMsg("error: picture %p has invalid status %d", p_pic, p_pic->i_status );
718     }
719 #endif
720
721     p_pic->i_status = DESTROYED_PICTURE;
722     p_vout->i_pictures--;
723
724 #ifdef TRACE_VOUT
725     intf_DbgMsg("picture %p", p_pic);
726 #endif
727
728     /* destroy the lock that had been initialized in CreatePicture */
729     vlc_mutex_destroy( &(p_pic->lock_deccount) );
730
731     vlc_mutex_unlock( &p_vout->picture_lock );
732 }
733
734 /*****************************************************************************
735  * vout_LinkPicture: increment reference counter of a picture
736  *****************************************************************************
737  * This function increment the reference counter of a picture in the video
738  * heap. It needs a lock since several producer threads can access the picture.
739  *****************************************************************************/
740 void vout_LinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
741 {
742     vlc_mutex_lock( &p_vout->picture_lock );
743     p_pic->i_refcount++;
744
745 #ifdef TRACE_VOUT
746     intf_DbgMsg("picture %p refcount=%d", p_pic, p_pic->i_refcount );
747 #endif
748
749     vlc_mutex_unlock( &p_vout->picture_lock );
750 }
751
752 /*****************************************************************************
753  * vout_UnlinkPicture: decrement reference counter of a picture
754  *****************************************************************************
755  * This function decrement the reference counter of a picture in the video heap.
756  *****************************************************************************/
757 void vout_UnlinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
758 {
759     vlc_mutex_lock( &p_vout->picture_lock );
760     p_pic->i_refcount--;
761
762 #ifdef TRACE_VOUT
763     if( p_pic->i_refcount < 0 )
764     {
765         intf_DbgMsg("error: refcount < 0");
766         p_pic->i_refcount = 0;
767     }
768 #endif
769
770     if( (p_pic->i_refcount == 0) && (p_pic->i_status == DISPLAYED_PICTURE) )
771     {
772         p_pic->i_status = DESTROYED_PICTURE;
773         p_vout->i_pictures--;
774     }
775
776 #ifdef TRACE_VOUT
777     intf_DbgMsg("picture %p refcount=%d", p_pic, p_pic->i_refcount );
778 #endif
779
780     vlc_mutex_unlock( &p_vout->picture_lock );
781 }
782
783 /*****************************************************************************
784  * vout_Pixel2RGB: return red, green and blue from pixel value
785  *****************************************************************************
786  * Return color values, in 0-255 range, of the decomposition of a pixel. This
787  * is a slow routine and should only be used for initialization phase.
788  *****************************************************************************/
789 void vout_Pixel2RGB( vout_thread_t *p_vout, u32 i_pixel, int *pi_red, int *pi_green, int *pi_blue )
790 {
791     *pi_red =   i_pixel & p_vout->i_red_mask;
792     *pi_green = i_pixel & p_vout->i_green_mask;
793     *pi_blue =  i_pixel & p_vout->i_blue_mask;
794 }
795
796 /* following functions are local */
797
798 /*****************************************************************************
799  * BinaryLog: computes the base 2 log of a binary value
800  *****************************************************************************
801  * This functions is used by MaskToShift, to get a bit index from a binary
802  * value.
803  *****************************************************************************/
804 static int BinaryLog(u32 i)
805 {
806     int i_log = 0;
807
808     if(i & 0xffff0000)
809     {
810         i_log += 16;
811     }
812     if(i & 0xff00ff00)
813     {
814         i_log += 8;
815     }
816     if(i & 0xf0f0f0f0)
817     {
818         i_log += 4;
819     }
820     if(i & 0xcccccccc)
821     {
822         i_log += 2;
823     }
824     if(i & 0xaaaaaaaa)
825     {
826         i_log += 1;
827     }
828
829     if (i != ((u32)1 << i_log))
830     {
831         intf_DbgMsg("internal error: binary log overflow");
832     }
833
834     return( i_log );
835 }
836
837 /*****************************************************************************
838  * MaskToShift: transform a color mask into right and left shifts
839  *****************************************************************************
840  * This function is used for obtaining color shifts from masks.
841  *****************************************************************************/
842 static void MaskToShift( int *pi_left, int *pi_right, u32 i_mask )
843 {
844     u32 i_low, i_high;                 /* lower hand higher bits of the mask */
845
846     /* Get bits */
847     i_low =  i_mask & (- i_mask);                   /* lower bit of the mask */
848     i_high = i_mask + i_low;                       /* higher bit of the mask */
849
850     /* Transform bits into an index */
851     i_low =  BinaryLog (i_low);
852     i_high = BinaryLog (i_high);
853
854     /* Update pointers and return */
855     *pi_left =   i_low;
856     *pi_right = (8 - i_high + i_low);
857 }
858
859 /*****************************************************************************
860  * InitThread: initialize video output thread
861  *****************************************************************************
862  * This function is called from RunThread and performs the second step of the
863  * initialization. It returns 0 on success. Note that the thread's flag are not
864  * modified inside this function.
865  *****************************************************************************/
866 static int InitThread( vout_thread_t *p_vout )
867 {
868     /* Update status */
869     *p_vout->pi_status = THREAD_START;
870
871     vlc_mutex_lock( &p_vout->change_lock );
872
873 #ifdef STATS
874     p_vout->c_loops = 0;
875 #endif
876
877     /* Create and initialize system-dependant method - this function issues its
878      * own error messages */
879     if( p_vout->pf_create( p_vout ) )
880     {
881         /* If pf_create has failed then we have to make sure
882          * pf_destroy won't be called, because the plugin should have
883          * cleaned up all its mess */
884         p_vout->pf_destroy = NULL;
885         return( 1 );
886     }
887
888     intf_WarnMsg( 1, "vout: video display initialized (%dx%d, %d/%d bpp)",
889               p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
890               p_vout->i_bytes_per_pixel * 8 );
891
892     intf_WarnMsg( 3, "vout info: got %dx%d, %d/%d bpp (%d Bpl), "
893                   "masks: 0x%x/0x%x/0x%x",
894                   p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
895                   p_vout->i_bytes_per_pixel * 8, p_vout->i_bytes_per_line,
896                   p_vout->i_red_mask, p_vout->i_green_mask,
897                   p_vout->i_blue_mask );
898
899     /* Calculate shifts from system-updated masks */
900     MaskToShift( &p_vout->i_red_lshift, &p_vout->i_red_rshift,
901                  p_vout->i_red_mask );
902     MaskToShift( &p_vout->i_green_lshift, &p_vout->i_green_rshift,
903                  p_vout->i_green_mask );
904     MaskToShift( &p_vout->i_blue_lshift, &p_vout->i_blue_rshift,
905                  p_vout->i_blue_mask );
906
907     /* Set some useful colors */
908     p_vout->i_white_pixel = RGB2PIXEL( p_vout, 255, 255, 255 );
909     p_vout->i_black_pixel = RGB2PIXEL( p_vout, 0, 0, 0 );
910     p_vout->i_gray_pixel  = RGB2PIXEL( p_vout, 128, 128, 128 );
911     p_vout->i_blue_pixel  = RGB2PIXEL( p_vout, 0, 0, 50 );
912
913     /* Load fonts - fonts must be initialized after the system method since
914      * they may be dependant on screen depth and other thread properties */
915     p_vout->p_default_font = vout_LoadFont( VOUT_DEFAULT_FONT );
916     if( p_vout->p_default_font == NULL )
917     {
918         intf_ErrMsg( "vout error: could not load default font" );
919     }
920
921     p_vout->p_large_font = vout_LoadFont( VOUT_LARGE_FONT );
922     if( p_vout->p_large_font == NULL )
923     {
924         intf_ErrMsg( "vout error: could not load large font" );
925     }
926
927     /* Initialize output method. This function issues its own error messages */
928     if( p_vout->pf_init( p_vout ) )
929     {
930         /* If pf_init has failed then we have to make sure
931          * pf_destroy won't be called, because the plugin should have
932          * cleaned up all its mess */
933         p_vout->pf_destroy = NULL;
934         return( 1 );
935     }
936
937     /* Initialize convertion tables and functions */
938     if( vout_InitYUV( p_vout ) )
939     {
940         intf_ErrMsg("vout error: can't allocate YUV translation tables");
941         p_vout->pf_destroy( p_vout );
942         /* Make sure pf_destroy won't be called again */
943         p_vout->pf_destroy = NULL;
944         return( 1 );
945     }
946
947     /* Mark thread as running and return */
948     p_vout->b_active =          1;
949     *p_vout->pi_status =        THREAD_READY;
950
951
952     intf_DbgMsg("thread ready");
953     return( 0 );
954 }
955
956 /*****************************************************************************
957  * RunThread: video output thread
958  *****************************************************************************
959  * Video output thread. This function does only returns when the thread is
960  * terminated. It handles the pictures arriving in the video heap and the
961  * display device events.
962  *****************************************************************************/
963 static void RunThread( vout_thread_t *p_vout)
964 {
965     int             i_index;                                /* index in heap */
966     mtime_t         current_date;                            /* current date */
967     mtime_t         display_date;                            /* display date */
968     boolean_t       b_display;                               /* display flag */
969
970     picture_t *     p_pic;                                /* picture pointer */
971
972     subpicture_t *  p_subpic;                          /* subpicture pointer */
973     subpicture_t *  p_ephemer;        /* youngest ephemer subpicture pointer */
974     mtime_t         ephemer_date;                /* earliest subpicture date */
975
976     /*
977      * Initialize thread
978      */
979     if( InitThread( p_vout ) )
980     {
981         /* Something bad happened */
982         DestroyThread( p_vout, THREAD_ERROR );
983         return;
984     }
985     /*
986      * Main loop - it is not executed if an error occured during
987      * initialization
988      */
989     while( (!p_vout->b_die) && (!p_vout->b_error) )
990     {
991         /* Initialize loop variables */
992         p_vout->p_rendered_pic = NULL;
993         p_pic =         NULL;
994         p_subpic =      NULL;
995         p_ephemer =     NULL;
996         ephemer_date =  0;
997         display_date =  0;
998         current_date =  mdate();
999 #ifdef STATS
1000         p_vout->c_loops++;
1001         if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
1002         {
1003             intf_Msg("vout stats: picture heap: %d/%d",
1004                      p_vout->i_pictures, VOUT_MAX_PICTURES);
1005         }
1006 #endif
1007
1008         /*
1009          * Find the picture to display - this operation does not need lock,
1010          * since only READY_PICTUREs are handled
1011          */
1012         for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
1013         {
1014             if( (p_vout->p_picture[i_index].i_status == READY_PICTURE) &&
1015             ( (p_pic == NULL) ||
1016               (p_vout->p_picture[i_index].date < display_date) ) )
1017             {
1018                 p_pic = &p_vout->p_picture[i_index];
1019                 display_date = p_pic->date;
1020             }
1021         }
1022
1023         if( p_pic )
1024         {
1025             /* Computes FPS rate */
1026             p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ] = display_date;
1027
1028             if( display_date < current_date - p_vout->render_time )
1029             {
1030                 /* Picture is late: it will be destroyed and the thread
1031                  * will sleep and go to next picture */
1032
1033                 vlc_mutex_lock( &p_vout->picture_lock );
1034                 if( p_pic->i_refcount )
1035                 {
1036                     p_pic->i_status = DISPLAYED_PICTURE;
1037                 }
1038                 else
1039                 {
1040                     p_pic->i_status = DESTROYED_PICTURE;
1041                     p_vout->i_pictures--;
1042                 }
1043                 intf_WarnMsg( 1,
1044                         "vout warning: late picture skipped (%p)", p_pic );
1045                 vlc_mutex_unlock( &p_vout->picture_lock );
1046
1047                 continue;
1048             }
1049             else if( display_date > current_date + VOUT_DISPLAY_DELAY )
1050             {
1051                 /* A picture is ready to be rendered, but its rendering date
1052                  * is far from the current one so the thread will perform an
1053                  * empty loop as if no picture were found. The picture state
1054                  * is unchanged */
1055                 p_pic =         NULL;
1056                 display_date =  0;
1057             }
1058         }
1059
1060         /*
1061          * Find the subpictures to display - this operation does not need
1062          * lock, since only READY_SUBPICTURE are handled. If no picture
1063          * has been selected, display_date will depend on the subpicture.
1064          *
1065          * We also check for ephemer DVD subpictures (subpictures that have
1066          * to be removed if a newer one is available), which makes it a lot
1067          * more difficult to guess if a subpicture has to be rendered or not.
1068          *
1069          * We get an easily parsable chained list of subpictures which
1070          * ends with NULL since p_subpic was initialized to NULL.
1071          */
1072         for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
1073         {
1074             if( p_vout->p_subpicture[i_index].i_status == READY_SUBPICTURE )
1075             {
1076                 /* If it is a DVD subpicture, check its date */
1077                 if( p_vout->p_subpicture[i_index].i_type == DVD_SUBPICTURE )
1078                 {
1079                     if( display_date > p_vout->p_subpicture[i_index].i_stop )
1080                     {
1081                         /* Too late, destroy the subpic */
1082                         vout_DestroySubPicture( p_vout,
1083                                         &p_vout->p_subpicture[i_index] );
1084                         continue;
1085                     }
1086
1087                     if( display_date < p_vout->p_subpicture[i_index].i_start )
1088                     {
1089                         /* Too early, come back next monday */
1090                         continue;
1091                     }
1092
1093                     /* If this is an ephemer subpic, see if it's the
1094                      * youngest we have */
1095                     if( p_vout->p_subpicture[i_index].b_ephemer )
1096                     {
1097                         if( p_ephemer == NULL )
1098                         {
1099                             p_ephemer = &p_vout->p_subpicture[i_index];
1100                             continue;
1101                         }
1102
1103                         if( p_vout->p_subpicture[i_index].i_start
1104                                                          < p_ephemer->i_start )
1105                         {
1106                             /* Link the previous ephemer subpicture and
1107                              * replace it with the current one */
1108                             p_ephemer->p_next = p_subpic;
1109                             p_subpic = p_ephemer;
1110                             p_ephemer = &p_vout->p_subpicture[i_index];
1111
1112                             /* If it's the 2nd youngest subpicture,
1113                              * register its date */
1114                             if( !ephemer_date
1115                                   || ephemer_date > p_subpic->i_start )
1116                             {
1117                                 ephemer_date = p_subpic->i_start;
1118                             }
1119
1120                             continue;
1121                         }
1122                     }
1123
1124                     p_vout->p_subpicture[i_index].p_next = p_subpic;
1125                     p_subpic = &p_vout->p_subpicture[i_index];
1126
1127                     /* If it's the 2nd youngest subpicture, register its date */
1128                     if( !ephemer_date || ephemer_date > p_subpic->i_start )
1129                     {
1130                         ephemer_date = p_subpic->i_start;
1131                     }
1132                 }
1133                 /* If it's not a DVD subpicture, just register it */
1134                 else
1135                 {
1136                     p_vout->p_subpicture[i_index].p_next = p_subpic;
1137                     p_subpic = &p_vout->p_subpicture[i_index];
1138                 }
1139             }
1140         }
1141
1142         /* If we found an ephemer subpicture, check if it has to be
1143          * displayed */
1144         if( p_ephemer != NULL )
1145         {
1146             if( p_ephemer->i_start < ephemer_date )
1147             {
1148                 /* Ephemer subpicture has lived too long */
1149                 vout_DestroySubPicture( p_vout, p_ephemer );
1150             }
1151             else
1152             {
1153                 /* Ephemer subpicture can still live a bit */
1154                 p_ephemer->p_next = p_subpic;
1155                 p_subpic = p_ephemer;
1156             }
1157         }
1158
1159         /*
1160          * Perform rendering, sleep and display rendered picture
1161          */
1162         if( p_pic )                        /* picture and perhaps subpicture */
1163         {
1164             b_display = p_vout->b_active;
1165             p_vout->last_display_date = display_date;
1166             p_vout->p_rendered_pic = p_pic;
1167
1168             /* Set picture dimensions and clear buffer */
1169             SetBufferPicture( p_vout, p_pic );
1170
1171             /* FIXME: if b_need_render == 0 we need to do something with
1172              * the subpictures one day. */
1173
1174             if( p_vout->b_need_render && b_display )
1175             {
1176                 /* Render picture and information */
1177                 RenderPicture( p_vout, p_pic );
1178                 if( p_vout->b_info )
1179                 {
1180                     RenderPictureInfo( p_vout, p_pic );
1181                     RenderInfo( p_vout );
1182                 }
1183             }
1184             if( b_display ) /* XXX: quick HACK */
1185             {
1186                 if( p_subpic )
1187                 {
1188                     RenderSubPicture( p_vout, p_pic, p_subpic );
1189                 }
1190             }
1191         }
1192         else if( p_vout->b_active && p_vout->b_need_render
1193                   && p_vout->init_display_date == 0)
1194         {
1195             /* Idle or interface screen alone */
1196
1197             if( p_vout->b_interface && 0 /* && XXX?? intf_change */ )
1198             {
1199                 /* Interface has changed, so a new rendering is required - force
1200                  * it by setting last idle date to 0 */
1201                 p_vout->last_idle_date = 0;
1202             }
1203
1204             /* Render idle screen and update idle date, then render interface if
1205              * required */
1206             b_display = RenderIdle( p_vout );
1207             if( b_display )
1208             {
1209                 p_vout->last_idle_date = current_date;
1210             }
1211
1212         }
1213         else
1214         {
1215             b_display = 0;
1216         }
1217
1218
1219         /*
1220          * Check for the current time and
1221          * display splash screen if everything is on time
1222          */
1223         if( p_vout->init_display_date > 0 && p_vout->b_need_render )
1224         {
1225             p_vout->init_display_date = 0;
1226         }
1227
1228
1229         /*
1230          * Sleep, wake up and display rendered picture
1231          */
1232
1233         if( display_date != 0 )
1234         {
1235             /* Store render time using Bresenham algorithm */
1236             p_vout->render_time += mdate() - current_date;
1237             p_vout->render_time >>= 1;
1238         }
1239
1240         /* Give back change lock */
1241         vlc_mutex_unlock( &p_vout->change_lock );
1242
1243         /* Sleep a while or until a given date */
1244         if( display_date != 0 )
1245         {
1246             mwait( display_date - VOUT_MWAIT_TOLERANCE );
1247         }
1248         else
1249         {
1250             msleep( VOUT_IDLE_SLEEP );
1251         }
1252
1253         /* On awakening, take back lock and send immediately picture to display,
1254          * then swap buffers */
1255         vlc_mutex_lock( &p_vout->change_lock );
1256 #ifdef TRACE_VOUT
1257         intf_DbgMsg( "picture %p, subpicture %p in buffer %d, display=%d", p_pic, p_subpic,
1258                      p_vout->i_buffer_index, b_display /* && !(p_vout->i_changes & VOUT_NODISPLAY_CHANGE) */ );
1259 #endif
1260         if( b_display /* && !(p_vout->i_changes & VOUT_NODISPLAY_CHANGE) */ )
1261         {
1262             p_vout->pf_display( p_vout );
1263 #ifndef SYS_BEOS
1264             p_vout->i_buffer_index = ++p_vout->i_buffer_index & 1;
1265 #endif
1266         }
1267
1268         if( p_pic )
1269         {
1270             /* Remove picture from heap */
1271             vlc_mutex_lock( &p_vout->picture_lock );
1272             if( p_pic->i_refcount )
1273             {
1274                 p_pic->i_status = DISPLAYED_PICTURE;
1275             }
1276             else
1277             {
1278                 p_pic->i_status = DESTROYED_PICTURE;
1279                 p_vout->i_pictures--;
1280             }
1281             vlc_mutex_unlock( &p_vout->picture_lock );
1282         }
1283
1284
1285         /*
1286          * Check events and manage thread
1287          */
1288         if( p_vout->pf_manage( p_vout ) | Manage( p_vout ) )
1289         {
1290             /* A fatal error occured, and the thread must terminate immediately
1291              * without displaying anything - setting b_error to 1 cause the
1292              * immediate end of the main while() loop. */
1293             p_vout->b_error = 1;
1294         }
1295     }
1296
1297     /*
1298      * Error loop - wait until the thread destruction is requested
1299      */
1300     if( p_vout->b_error )
1301     {
1302         ErrorThread( p_vout );
1303     }
1304
1305     /* End of thread */
1306     EndThread( p_vout );
1307     DestroyThread( p_vout, THREAD_OVER );
1308     intf_DbgMsg( "thread end" );
1309 }
1310
1311 /*****************************************************************************
1312  * ErrorThread: RunThread() error loop
1313  *****************************************************************************
1314  * This function is called when an error occured during thread main's loop. The
1315  * thread can still receive feed, but must be ready to terminate as soon as
1316  * possible.
1317  *****************************************************************************/
1318 static void ErrorThread( vout_thread_t *p_vout )
1319 {
1320     /* Wait until a `die' order */
1321     while( !p_vout->b_die )
1322     {
1323         /* Sleep a while */
1324         msleep( VOUT_IDLE_SLEEP );
1325     }
1326 }
1327
1328 /*****************************************************************************
1329  * EndThread: thread destruction
1330  *****************************************************************************
1331  * This function is called when the thread ends after a sucessful
1332  * initialization. It frees all ressources allocated by InitThread.
1333  *****************************************************************************/
1334 static void EndThread( vout_thread_t *p_vout )
1335 {
1336     int     i_index;                                        /* index in heap */
1337
1338     /* Store status */
1339     *p_vout->pi_status = THREAD_END;
1340
1341 #ifdef STATS
1342     {
1343         struct tms cpu_usage;
1344         times( &cpu_usage );
1345
1346         intf_Msg( "vout stats: cpu usage (user: %d, system: %d)",
1347                   cpu_usage.tms_utime, cpu_usage.tms_stime );
1348     }
1349 #endif
1350
1351     /* Destroy all remaining pictures and subpictures */
1352     for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
1353     {
1354         if( p_vout->p_picture[i_index].i_status != FREE_PICTURE )
1355         {
1356             free( p_vout->p_picture[i_index].p_data );
1357         }
1358     }
1359
1360     for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
1361     {
1362         if( p_vout->p_subpicture[i_index].i_status != FREE_SUBPICTURE )
1363         {
1364             free( p_vout->p_subpicture[i_index].p_data );
1365         }
1366     }
1367
1368     /* Destroy translation tables */
1369     vout_EndYUV( p_vout );
1370     p_vout->pf_end( p_vout );
1371
1372     /* Release the change lock */
1373     vlc_mutex_unlock( &p_vout->change_lock );
1374 }
1375
1376 /*****************************************************************************
1377  * DestroyThread: thread destruction
1378  *****************************************************************************
1379  * This function is called when the thread ends. It frees all ressources
1380  * allocated by CreateThread. Status is available at this stage.
1381  *****************************************************************************/
1382 static void DestroyThread( vout_thread_t *p_vout, int i_status )
1383 {
1384     int *pi_status;                                         /* status adress */
1385
1386     /* Store status adress */
1387     pi_status = p_vout->pi_status;
1388
1389     /* Destroy thread structures allocated by Create and InitThread */
1390     vout_UnloadFont( p_vout->p_default_font );
1391     vout_UnloadFont( p_vout->p_large_font );
1392     if( p_vout->pf_destroy != NULL ) p_vout->pf_destroy( p_vout );
1393
1394     /* Destroy the locks */
1395     vlc_mutex_destroy( &p_vout->picture_lock );
1396     vlc_mutex_destroy( &p_vout->subpicture_lock );
1397     vlc_mutex_destroy( &p_vout->change_lock );
1398
1399     /* Release the module */
1400     module_Unneed( p_vout->p_module );
1401
1402     /* Free structure */
1403     free( p_vout );
1404     *pi_status = i_status;
1405 }
1406
1407 /*****************************************************************************
1408  * Print: print simple text on a picture
1409  *****************************************************************************
1410  * This function will print a simple text on the picture. It is designed to
1411  * print debugging or general information.
1412  *****************************************************************************/
1413 void Print( vout_thread_t *p_vout, int i_x, int i_y, int i_h_align, int i_v_align, unsigned char *psz_text )
1414 {
1415     int                 i_text_height;                  /* total text height */
1416     int                 i_text_width;                    /* total text width */
1417
1418     /* Update upper left coordinates according to alignment */
1419     vout_TextSize( p_vout->p_default_font, 0, psz_text, &i_text_width, &i_text_height );
1420     if( !Align( p_vout, &i_x, &i_y, i_text_width, i_text_height, i_h_align, i_v_align ) )
1421     {
1422         /* Set area and print text */
1423         SetBufferArea( p_vout, i_x, i_y, i_text_width, i_text_height );
1424         vout_Print( p_vout->p_default_font, p_vout->p_buffer[ p_vout->i_buffer_index ].p_data +
1425                     i_y * p_vout->i_bytes_per_line + i_x * p_vout->i_bytes_per_pixel,
1426                     p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line,
1427                     p_vout->i_white_pixel, 0, 0,
1428                     0, psz_text, 100 );
1429     }
1430 }
1431
1432 /*****************************************************************************
1433  * SetBuffers: set buffers adresses
1434  *****************************************************************************
1435  * This function is called by system drivers to set buffers video memory
1436  * adresses.
1437  *****************************************************************************/
1438 static void SetBuffers( vout_thread_t *p_vout, void *p_buf1, void *p_buf2 )
1439 {
1440     /* No picture previously */
1441     p_vout->p_buffer[0].i_pic_x =         0;
1442     p_vout->p_buffer[0].i_pic_y =         0;
1443     p_vout->p_buffer[0].i_pic_width =     0;
1444     p_vout->p_buffer[0].i_pic_height =    0;
1445     p_vout->p_buffer[1].i_pic_x =         0;
1446     p_vout->p_buffer[1].i_pic_y =         0;
1447     p_vout->p_buffer[1].i_pic_width =     0;
1448     p_vout->p_buffer[1].i_pic_height =    0;
1449
1450     /* The first area covers all the screen */
1451     p_vout->p_buffer[0].i_areas =                 1;
1452     p_vout->p_buffer[0].pi_area_begin[0] =        0;
1453     p_vout->p_buffer[0].pi_area_end[0] =          p_vout->i_height - 1;
1454     p_vout->p_buffer[1].i_areas =                 1;
1455     p_vout->p_buffer[1].pi_area_begin[0] =        0;
1456     p_vout->p_buffer[1].pi_area_end[0] =          p_vout->i_height - 1;
1457
1458     /* Set adresses */
1459     p_vout->p_buffer[0].p_data = p_buf1;
1460     p_vout->p_buffer[1].p_data = p_buf2;
1461 }
1462
1463 /*****************************************************************************
1464  * SetBufferArea: activate an area in current buffer
1465  *****************************************************************************
1466  * This function is called when something is rendered on the current buffer.
1467  * It set the area as active and prepare it to be cleared on next rendering.
1468  * Pay attention to the fact that in this functions, i_h is in fact the end y
1469  * coordinate of the new area.
1470  *****************************************************************************/
1471 static void SetBufferArea( vout_thread_t *p_vout, int i_x, int i_y, int i_w, int i_h )
1472 {
1473     vout_buffer_t *     p_buffer;                          /* current buffer */
1474     int                 i_area_begin, i_area_end; /* area vertical extension */
1475     int                 i_area, i_area_copy;                   /* area index */
1476     int                 i_area_shift;            /* shift distance for areas */
1477
1478     /* Choose buffer and modify h to end of area position */
1479     p_buffer =  &p_vout->p_buffer[ p_vout->i_buffer_index ];
1480     i_h +=      i_y - 1;
1481
1482     /*
1483      * Remove part of the area which is inside the picture - this is done
1484      * by calling again SetBufferArea with the correct areas dimensions.
1485      */
1486     if( (i_x >= p_buffer->i_pic_x) && (i_x + i_w <= p_buffer->i_pic_x + p_buffer->i_pic_width) )
1487     {
1488         i_area_begin =  p_buffer->i_pic_y;
1489         i_area_end =    i_area_begin + p_buffer->i_pic_height - 1;
1490
1491         if( ((i_y >= i_area_begin) && (i_y <= i_area_end)) ||
1492             ((i_h >= i_area_begin) && (i_h <= i_area_end)) ||
1493             ((i_y <  i_area_begin) && (i_h > i_area_end)) )
1494         {
1495             /* Keep the stripe above the picture, if any */
1496             if( i_y < i_area_begin )
1497             {
1498                 SetBufferArea( p_vout, i_x, i_y, i_w, i_area_begin - i_y );
1499             }
1500             /* Keep the stripe below the picture, if any */
1501             if( i_h > i_area_end )
1502             {
1503                 SetBufferArea( p_vout, i_x, i_area_end, i_w, i_h - i_area_end );
1504             }
1505             return;
1506         }
1507     }
1508
1509     /* Skip some extensions until interesting areas */
1510     for( i_area = 0;
1511          (i_area < p_buffer->i_areas) &&
1512              (p_buffer->pi_area_end[i_area] + 1 <= i_y);
1513          i_area++ )
1514     {
1515         ;
1516     }
1517
1518     if( i_area == p_buffer->i_areas )
1519     {
1520         /* New area is below all existing ones: just add it at the end of the
1521          * array, if possible - otherwise, append it to the last one */
1522         if( i_area < VOUT_MAX_AREAS )
1523         {
1524             p_buffer->pi_area_begin[i_area] = i_y;
1525             p_buffer->pi_area_end[i_area] = i_h;
1526             p_buffer->i_areas++;
1527         }
1528         else
1529         {
1530 #ifdef TRACE_VOUT
1531             intf_DbgMsg("area overflow");
1532 #endif
1533             p_buffer->pi_area_end[VOUT_MAX_AREAS - 1] = i_h;
1534         }
1535     }
1536     else
1537     {
1538         i_area_begin =  p_buffer->pi_area_begin[i_area];
1539         i_area_end =    p_buffer->pi_area_end[i_area];
1540
1541         if( i_y < i_area_begin )
1542         {
1543             if( i_h >= i_area_begin - 1 )
1544             {
1545                 /* Extend area above */
1546                 p_buffer->pi_area_begin[i_area] = i_y;
1547             }
1548             else
1549             {
1550                 /* Create a new area above : merge last area if overflow, then
1551                  * move all old areas down */
1552                 if( p_buffer->i_areas == VOUT_MAX_AREAS )
1553                 {
1554 #ifdef TRACE_VOUT
1555                     intf_DbgMsg("areas overflow");
1556 #endif
1557                     p_buffer->pi_area_end[VOUT_MAX_AREAS - 2] = p_buffer->pi_area_end[VOUT_MAX_AREAS - 1];
1558                 }
1559                 else
1560                 {
1561                     p_buffer->i_areas++;
1562                 }
1563                 for( i_area_copy = p_buffer->i_areas - 1; i_area_copy > i_area; i_area_copy-- )
1564                 {
1565                     p_buffer->pi_area_begin[i_area_copy] = p_buffer->pi_area_begin[i_area_copy - 1];
1566                     p_buffer->pi_area_end[i_area_copy] =   p_buffer->pi_area_end[i_area_copy - 1];
1567                 }
1568                 p_buffer->pi_area_begin[i_area] = i_y;
1569                 p_buffer->pi_area_end[i_area] = i_h;
1570                 return;
1571             }
1572         }
1573         if( i_h > i_area_end )
1574         {
1575             /* Find further areas which can be merged with the new one */
1576             for( i_area_copy = i_area + 1;
1577                  (i_area_copy < p_buffer->i_areas) &&
1578                      (p_buffer->pi_area_begin[i_area] <= i_h);
1579                  i_area_copy++ )
1580             {
1581                 ;
1582             }
1583             i_area_copy--;
1584
1585             if( i_area_copy != i_area )
1586             {
1587                 /* Merge with last possible areas */
1588                 //p_buffer->pi_area_end[i_area] = MAX( i_h, p_buffer->pi_area_end[i_area_copy] );
1589
1590                 /* Shift lower areas upward */
1591                 i_area_shift = i_area_copy - i_area;
1592                 p_buffer->i_areas -= i_area_shift;
1593                 for( i_area_copy = i_area + 1; i_area_copy < p_buffer->i_areas; i_area_copy++ )
1594                 {
1595                     p_buffer->pi_area_begin[i_area_copy] = p_buffer->pi_area_begin[i_area_copy + i_area_shift];
1596                     p_buffer->pi_area_end[i_area_copy] =   p_buffer->pi_area_end[i_area_copy + i_area_shift];
1597                 }
1598             }
1599             else
1600             {
1601                 /* Extend area below */
1602                 p_buffer->pi_area_end[i_area] = i_h;
1603             }
1604         }
1605     }
1606 }
1607
1608 /*****************************************************************************
1609  * SetBufferPicture: clear buffer and set picture area
1610  *****************************************************************************
1611  * This function is called before any rendering. It clears the current
1612  * rendering buffer and set the new picture area. If the picture pointer is
1613  * NULL, then no picture area is defined. Floating operations are avoided since
1614  * some MMX calculations may follow.
1615  *****************************************************************************/
1616 static void SetBufferPicture( vout_thread_t *p_vout, picture_t *p_pic )
1617 {
1618     vout_buffer_t *     p_buffer;                          /* current buffer */
1619     int                 i_pic_x, i_pic_y;                /* picture position */
1620     int                 i_pic_width, i_pic_height;     /* picture dimensions */
1621     int                 i_old_pic_y, i_old_pic_height;   /* old picture area */
1622     int                 i_vout_width, i_vout_height;   /* display dimensions */
1623     int                 i_area;                                /* area index */
1624     int                 i_data_index;                     /* area data index */
1625     int                 i_data_size;   /* area data size, in 256 bytes blocs */
1626     u64 *               p_data;                   /* area data, for clearing */
1627     byte_t *            p_data8;           /* area data, for clearing (slow) */
1628
1629     /* Choose buffer and set display dimensions */
1630     p_buffer =          &p_vout->p_buffer[ p_vout->i_buffer_index ];
1631     i_vout_width =      p_vout->i_width;
1632     i_vout_height =     p_vout->i_height;
1633
1634     /*
1635      * Computes new picture size
1636      */
1637     if( p_pic != NULL )
1638     {
1639         /* Try horizontal scaling first - width must be a mutiple of 16 */
1640         i_pic_width = (( p_vout->b_scale || (p_pic->i_width > i_vout_width)) ?
1641                        i_vout_width : p_pic->i_width) & ~0xf;
1642         switch( p_pic->i_aspect_ratio )
1643         {
1644         case AR_3_4_PICTURE:
1645             i_pic_height = i_pic_width * 3 / 4;
1646             break;
1647         case AR_16_9_PICTURE:
1648             i_pic_height = i_pic_width * 9 / 16;
1649             break;
1650         case AR_221_1_PICTURE:
1651             i_pic_height = i_pic_width * 100 / 221;
1652             break;
1653         case AR_SQUARE_PICTURE:
1654         default:
1655             i_pic_height = p_pic->i_height * i_pic_width / p_pic->i_width;
1656             break;
1657         }
1658
1659         /* If picture dimensions using horizontal scaling are too large, use
1660          * vertical scaling. Since width must be a multiple of 16, height is
1661          * adjusted again after. */
1662         if( i_pic_height > i_vout_height )
1663         {
1664             i_pic_height = ( p_vout->b_scale || (p_pic->i_height > i_vout_height)) ?
1665                 i_vout_height : p_pic->i_height;
1666             switch( p_pic->i_aspect_ratio )
1667             {
1668             case AR_3_4_PICTURE:
1669                 i_pic_width = (i_pic_height * 4 / 3) & ~0xf;
1670                 i_pic_height = i_pic_width * 3 / 4;
1671                 break;
1672             case AR_16_9_PICTURE:
1673                 i_pic_width = (i_pic_height * 16 / 9) & ~0xf;
1674                 i_pic_height = i_pic_width * 9 / 16;
1675                 break;
1676             case AR_221_1_PICTURE:
1677                 i_pic_width = (i_pic_height * 221 / 100) & ~0xf;
1678                 i_pic_height = i_pic_width * 100 / 221;
1679                 break;
1680             case AR_SQUARE_PICTURE:
1681             default:
1682                 i_pic_width = (p_pic->i_width * i_pic_height / p_pic->i_height) & ~0xf;
1683                 i_pic_height = p_pic->i_height * i_pic_width / p_pic->i_width;
1684                 break;
1685             }
1686         }
1687
1688         /* Set picture position */
1689         i_pic_x = (p_vout->i_width - i_pic_width) / 2;
1690         i_pic_y = (p_vout->i_height - i_pic_height) / 2;
1691
1692     }
1693     else
1694     {
1695         /* No picture: size is 0 */
1696         i_pic_x =       0;
1697         i_pic_y =       0;
1698         i_pic_width =   0;
1699         i_pic_height =  0;
1700     }
1701
1702     /*
1703      * Set new picture size - if it is smaller than the previous one, clear
1704      * around it. Since picture are centered, only their size is tested.
1705      */
1706     if( (p_buffer->i_pic_width > i_pic_width) || (p_buffer->i_pic_height > i_pic_height) )
1707     {
1708         i_old_pic_y =            p_buffer->i_pic_y;
1709         i_old_pic_height =       p_buffer->i_pic_height;
1710         p_buffer->i_pic_x =      i_pic_x;
1711         p_buffer->i_pic_y =      i_pic_y;
1712         p_buffer->i_pic_width =  i_pic_width;
1713         p_buffer->i_pic_height = i_pic_height;
1714         SetBufferArea( p_vout, 0, i_old_pic_y, p_vout->i_width, i_old_pic_height );
1715     }
1716     else
1717     {
1718         p_buffer->i_pic_x =      i_pic_x;
1719         p_buffer->i_pic_y =      i_pic_y;
1720         p_buffer->i_pic_width =  i_pic_width;
1721         p_buffer->i_pic_height = i_pic_height;
1722     }
1723
1724     /*
1725      * Clear areas
1726      */
1727     for( i_area = 0; i_area < p_buffer->i_areas; i_area++ )
1728     {
1729 #ifdef TRACE_VOUT
1730         intf_DbgMsg("clearing picture %p area in buffer %d: %d-%d", p_pic,
1731                     p_vout->i_buffer_index, p_buffer->pi_area_begin[i_area], p_buffer->pi_area_end[i_area] );
1732 #endif
1733         i_data_size = (p_buffer->pi_area_end[i_area] - p_buffer->pi_area_begin[i_area] + 1) * p_vout->i_bytes_per_line;
1734         p_data = (u64*) (p_buffer->p_data + p_vout->i_bytes_per_line * p_buffer->pi_area_begin[i_area]);
1735
1736         for( i_data_index = i_data_size / 256; i_data_index-- ; )
1737         {
1738             /* Clear 256 bytes block */
1739             *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;
1740             *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;
1741             *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;
1742             *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;
1743             *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;
1744             *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;
1745             *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;
1746             *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;  *p_data++ = 0;
1747         }
1748         for( i_data_index = (i_data_size % 256) / 16; i_data_index--; )
1749         {
1750             /* Clear remaining 16 bytes blocks */
1751             *p_data++ = 0;  *p_data++ = 0;
1752         }
1753         p_data8 = (byte_t *)p_data;
1754         for( i_data_index = i_data_size % 16; i_data_index--; )
1755         {
1756             /* Clear remaining bytes */
1757             *p_data8++ = 0;
1758         }
1759     }
1760
1761     /*
1762      * Clear areas array
1763      */
1764     p_buffer->i_areas = 0;
1765 }
1766
1767 /*****************************************************************************
1768  * RenderPicture: render a picture
1769  *****************************************************************************
1770  * This function converts a picture from a video heap to a pixel-encoded image
1771  * and copies it to the current rendering buffer. No lock is required, since
1772  * the * rendered picture has been determined as existant, and will only be
1773  * destroyed by the vout thread later.
1774  *****************************************************************************/
1775 static void RenderPicture( vout_thread_t *p_vout, picture_t *p_pic )
1776 {
1777 #ifdef TRACE_VOUT
1778     char                psz_date[MSTRTIME_MAX_SIZE];         /* picture date */
1779     mtime_t             render_time;               /* picture rendering time */
1780 #endif
1781     vout_buffer_t *     p_buffer;                        /* rendering buffer */
1782     byte_t *            p_pic_data;                /* convertion destination */
1783
1784     /* Get and set rendering information */
1785     p_buffer =          &p_vout->p_buffer[ p_vout->i_buffer_index ];
1786     p_pic_data =        p_buffer->p_data +
1787                         p_buffer->i_pic_x * p_vout->i_bytes_per_pixel +
1788                         p_buffer->i_pic_y * p_vout->i_bytes_per_line;
1789 #ifdef TRACE_VOUT
1790     render_time = mdate();
1791 #endif
1792
1793
1794
1795     /*
1796      * Choose appropriate rendering function and render picture
1797      */
1798     switch( p_pic->i_type )
1799     {
1800     case YUV_420_PICTURE:
1801         p_vout->yuv.pf_yuv420( p_vout, p_pic_data,
1802                                p_pic->p_y, p_pic->p_u, p_pic->p_v,
1803                                p_pic->i_width, p_pic->i_height,
1804                                p_buffer->i_pic_width, p_buffer->i_pic_height,
1805                                p_vout->i_bytes_per_line / p_vout->i_bytes_per_pixel,
1806                                p_pic->i_matrix_coefficients );
1807         break;
1808     case YUV_422_PICTURE:
1809         p_vout->yuv.pf_yuv422( p_vout, p_pic_data,
1810                                p_pic->p_y, p_pic->p_u, p_pic->p_v,
1811                                p_pic->i_width, p_pic->i_height,
1812                                p_buffer->i_pic_width, p_buffer->i_pic_height,
1813                                p_vout->i_bytes_per_line / p_vout->i_bytes_per_pixel,
1814                                p_pic->i_matrix_coefficients );
1815         break;
1816     case YUV_444_PICTURE:
1817         p_vout->yuv.pf_yuv444( p_vout, p_pic_data,
1818                                p_pic->p_y, p_pic->p_u, p_pic->p_v,
1819                                p_pic->i_width, p_pic->i_height,
1820                                p_buffer->i_pic_width, p_buffer->i_pic_height,
1821                                p_vout->i_bytes_per_line / p_vout->i_bytes_per_pixel,
1822                                p_pic->i_matrix_coefficients );
1823         break;
1824 #ifdef DEBUG
1825     default:
1826         intf_ErrMsg("error: unknown picture type %d", p_pic->i_type );
1827         break;
1828 #endif
1829     }
1830
1831 #ifdef TRACE_VOUT
1832     /* Print picture date and rendering time */
1833     intf_DbgMsg("picture %p rendered in buffer %d (%ld us), display date: %s", p_pic,
1834                 p_vout->i_buffer_index, (long) (mdate() - render_time),
1835                 mstrtime( psz_date, p_pic->date ));
1836 #endif
1837 }
1838
1839 /*****************************************************************************
1840  * RenderPictureInfo: print additionnal information on a picture
1841  *****************************************************************************
1842  * This function will print information such as fps and other picture
1843  * dependant information.
1844  *****************************************************************************/
1845 static void RenderPictureInfo( vout_thread_t *p_vout, picture_t *p_pic )
1846 {
1847     char        psz_buffer[256];                            /* string buffer */
1848
1849     /*
1850      * Print FPS rate in upper right corner
1851      */
1852     if( p_vout->c_fps_samples > VOUT_FPS_SAMPLES )
1853     {
1854         long i_fps = VOUT_FPS_SAMPLES * 1000000 * 10 /
1855                            ( p_vout->p_fps_sample[ (p_vout->c_fps_samples - 1)
1856                                                    % VOUT_FPS_SAMPLES ] -
1857                              p_vout->p_fps_sample[ p_vout->c_fps_samples
1858                                                    % VOUT_FPS_SAMPLES ] );
1859         sprintf( psz_buffer, "%li.%i fps", i_fps / 10, (int)i_fps % 10 );
1860         Print( p_vout, 0, 0, RIGHT_RALIGN, TOP_RALIGN, psz_buffer );
1861     }
1862
1863     /*
1864      * Print frames count and loop time in upper left corner
1865      */
1866     sprintf( psz_buffer, "%ld frames, render: %ldus",
1867              (long) p_vout->c_fps_samples, (long) p_vout->render_time );
1868     Print( p_vout, 0, 0, LEFT_RALIGN, TOP_RALIGN, psz_buffer );
1869
1870 #ifdef STATS
1871     /*
1872      * Print picture information in lower right corner
1873      */
1874     sprintf( psz_buffer, "%s picture %dx%d (%dx%d%+d%+d %s) -> %dx%d+%d+%d",
1875              (p_pic->i_type == YUV_420_PICTURE) ? "4:2:0" :
1876              ((p_pic->i_type == YUV_422_PICTURE) ? "4:2:2" :
1877               ((p_pic->i_type == YUV_444_PICTURE) ? "4:4:4" : "ukn-type")),
1878              p_pic->i_width, p_pic->i_height,
1879              p_pic->i_display_width, p_pic->i_display_height,
1880              p_pic->i_display_horizontal_offset, p_pic->i_display_vertical_offset,
1881              (p_pic->i_aspect_ratio == AR_SQUARE_PICTURE) ? "sq" :
1882              ((p_pic->i_aspect_ratio == AR_3_4_PICTURE) ? "4:3" :
1883               ((p_pic->i_aspect_ratio == AR_16_9_PICTURE) ? "16:9" :
1884                ((p_pic->i_aspect_ratio == AR_221_1_PICTURE) ? "2.21:1" : "ukn-ar" ))),
1885              p_vout->p_buffer[ p_vout->i_buffer_index ].i_pic_width,
1886              p_vout->p_buffer[ p_vout->i_buffer_index ].i_pic_height,
1887              p_vout->p_buffer[ p_vout->i_buffer_index ].i_pic_x,
1888              p_vout->p_buffer[ p_vout->i_buffer_index ].i_pic_y );
1889     Print( p_vout, 0, 0, RIGHT_RALIGN, BOTTOM_RALIGN, psz_buffer );
1890 #endif
1891 }
1892
1893 /*****************************************************************************
1894  * RenderIdle: render idle picture
1895  *****************************************************************************
1896  * This function will print something on the screen. It will return 0 if
1897  * nothing has been rendered, or 1 if something has been changed on the screen.
1898  * Note that if you absolutely want something to be printed, you will have
1899  * to force it by setting the last idle date to 0.
1900  * Unlike other rendering functions, this one calls the SetBufferPicture
1901  * function when needed.
1902  *****************************************************************************/
1903 int RenderIdle( vout_thread_t *p_vout )
1904 {
1905 #if 0
1906     int         i_x = 0, i_y = 0;                           /* text position */
1907     int         i_width, i_height;                              /* text size */
1908     int         i_amount = 0;                             /*  amount to draw */
1909     char *psz_text =    "Waiting for stream";            /* text to display */
1910     char *psz_wtext =   "[................]";
1911 #endif
1912     mtime_t     current_date;                                /* current date */
1913
1914
1915     memset( p_vout->p_buffer[ p_vout->i_buffer_index ].p_data,
1916                     p_vout->i_bytes_per_line * p_vout->i_height, 12);
1917
1918
1919     current_date = mdate();
1920     if( (current_date - p_vout->last_display_date) > VOUT_IDLE_DELAY
1921 //            && (current_date - p_vout->last_idle_date) > VOUT_IDLE_DELAY
1922     )
1923     {
1924         /* FIXME: idle screen disabled */
1925 #if 0
1926         SetBufferPicture( p_vout, NULL );
1927         vout_TextSize( p_vout->p_large_font, WIDE_TEXT | OUTLINED_TEXT, psz_text,
1928                        &i_width, &i_height );
1929         if( !Align( p_vout, &i_x, &i_y, i_width, i_height * 2, CENTER_RALIGN, CENTER_RALIGN ) )
1930         {
1931             i_amount = (int) ((current_date - p_vout->last_display_date ) / 5000LL);
1932             vout_Print( p_vout->p_large_font,
1933                         p_vout->p_buffer[ p_vout->i_buffer_index ].p_data +
1934                         i_x * p_vout->i_bytes_per_pixel + i_y * p_vout->i_bytes_per_line,
1935                         p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line,
1936                         p_vout->i_white_pixel, p_vout->i_gray_pixel, 0,
1937                         WIDE_TEXT | OUTLINED_TEXT, psz_text,  (i_amount / 3 ) %110);
1938
1939             vout_Print( p_vout->p_large_font,
1940                     p_vout->p_buffer[ p_vout->i_buffer_index ].p_data +
1941                     i_x * p_vout->i_bytes_per_pixel + (i_y + 16) * p_vout->i_bytes_per_line,
1942                     p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line,
1943                     p_vout->i_white_pixel, p_vout->i_gray_pixel, 0,
1944                     WIDE_TEXT | OUTLINED_TEXT, psz_wtext,  (i_amount/5)%110 );
1945
1946
1947             SetBufferArea( p_vout, i_x, i_y, i_width, i_height * 2 );
1948         }
1949 #endif
1950         return( 1 );
1951     }
1952     return( 0 );
1953 }
1954
1955 /*****************************************************************************
1956  * RenderInfo: render additionnal information
1957  *****************************************************************************
1958  * This function renders information which do not depend on the current
1959  * picture rendered.
1960  *****************************************************************************/
1961 static void RenderInfo( vout_thread_t *p_vout )
1962 {
1963 #ifdef DEBUG
1964     char        psz_buffer[256];                            /* string buffer */
1965     int         i_ready_pic = 0;                           /* ready pictures */
1966     int         i_reserved_pic = 0;                     /* reserved pictures */
1967     int         i_picture;                                  /* picture index */
1968 #endif
1969
1970 #ifdef DEBUG
1971     /*
1972      * Print thread state in lower left corner
1973      */
1974     for( i_picture = 0; i_picture < VOUT_MAX_PICTURES; i_picture++ )
1975     {
1976         switch( p_vout->p_picture[i_picture].i_status )
1977         {
1978         case RESERVED_PICTURE:
1979         case RESERVED_DATED_PICTURE:
1980         case RESERVED_DISP_PICTURE:
1981             i_reserved_pic++;
1982             break;
1983         case READY_PICTURE:
1984             i_ready_pic++;
1985             break;
1986         }
1987     }
1988     sprintf( psz_buffer, "pic: %d (%d/%d)/%d",
1989              p_vout->i_pictures, i_reserved_pic, i_ready_pic, VOUT_MAX_PICTURES );
1990     Print( p_vout, 0, 0, LEFT_RALIGN, BOTTOM_RALIGN, psz_buffer );
1991 #endif
1992 }
1993
1994 /*****************************************************************************
1995  * RenderSubPicture: render a subpicture
1996  *****************************************************************************
1997  * This function renders a sub picture unit.
1998  *****************************************************************************/
1999 static void RenderSubPicture( vout_thread_t *p_vout, picture_t *p_pic,
2000                               subpicture_t *p_subpic )
2001 {
2002     p_vout_font_t       p_font;                                 /* text font */
2003     int                 i_width, i_height;          /* subpicture dimensions */
2004
2005     while( p_subpic != NULL )
2006     {
2007         switch( p_subpic->i_type )
2008         {
2009         case DVD_SUBPICTURE:                          /* DVD subpicture unit */
2010             vout_RenderRGBSPU( p_pic, p_subpic,
2011                                &p_vout->p_buffer[ p_vout->i_buffer_index ],
2012                                p_vout->i_bytes_per_pixel,
2013                                p_vout->i_bytes_per_line );
2014             /* vout_RenderYUVSPU( p_pic, p_subpic ); */
2015             break;
2016
2017         case TEXT_SUBPICTURE:                            /* single line text */
2018             /* Select default font if not specified */
2019             p_font = p_subpic->type.text.p_font;
2020             if( p_font == NULL )
2021             {
2022                 p_font = p_vout->p_default_font;
2023             }
2024
2025             /* Compute text size (width and height fields are ignored)
2026              * and print it */
2027             vout_TextSize( p_font, p_subpic->type.text.i_style,
2028                            p_subpic->p_data, &i_width, &i_height );
2029             if( !Align( p_vout, &p_subpic->i_x, &p_subpic->i_y,
2030                         i_width, i_height, p_subpic->i_horizontal_align,
2031                         p_subpic->i_vertical_align ) )
2032             {
2033                 vout_Print( p_font,
2034                             p_vout->p_buffer[ p_vout->i_buffer_index ].p_data +
2035                             p_subpic->i_x * p_vout->i_bytes_per_pixel +
2036                             p_subpic->i_y * p_vout->i_bytes_per_line,
2037                             p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line,
2038                             p_subpic->type.text.i_char_color,
2039                             p_subpic->type.text.i_border_color,
2040                             p_subpic->type.text.i_bg_color,
2041                             p_subpic->type.text.i_style, p_subpic->p_data, 100 );
2042                 SetBufferArea( p_vout, p_subpic->i_x, p_subpic->i_y,
2043                                i_width, i_height );
2044             }
2045             break;
2046
2047         default:
2048 #ifdef DEBUG
2049             intf_ErrMsg( "error: unknown subpicture %p type %d",
2050                          p_subpic, p_subpic->i_type );
2051 #endif
2052             break;
2053         }
2054
2055         p_subpic = p_subpic->p_next;
2056     }
2057 }
2058
2059 /*****************************************************************************
2060  * Manage: manage thread
2061  *****************************************************************************
2062  * This function will handle changes in thread configuration.
2063  *****************************************************************************/
2064 static int Manage( vout_thread_t *p_vout )
2065 {
2066 #ifdef TRACE_VOUT
2067     if( p_vout->i_changes )
2068     {
2069         intf_DbgMsg("changes: 0x%x (no display: 0x%x)", p_vout->i_changes,
2070                     0 /* p_vout->i_changes & VOUT_NODISPLAY_CHANGE */ );
2071     }
2072 #endif
2073
2074     /* On gamma or grayscale change, rebuild tables */
2075     if( p_vout->i_changes & (VOUT_GAMMA_CHANGE | VOUT_GRAYSCALE_CHANGE |
2076                              VOUT_YUV_CHANGE) )
2077     {
2078         if( vout_ResetYUV( p_vout ) )
2079         {
2080             intf_ErrMsg( "vout error: can't rebuild conversion tables" );
2081             return( 1 );
2082         }
2083     }
2084
2085     /* Clear changes flags which does not need management or have been
2086      * handled */
2087     p_vout->i_changes &= ~(VOUT_GAMMA_CHANGE  | VOUT_GRAYSCALE_CHANGE |
2088                            VOUT_YUV_CHANGE    | VOUT_INFO_CHANGE |
2089                            VOUT_INTF_CHANGE   | VOUT_SCALE_CHANGE |
2090                            VOUT_CURSOR_CHANGE | VOUT_FULLSCREEN_CHANGE );
2091
2092     /* Detect unauthorized changes */
2093     if( p_vout->i_changes )
2094     {
2095         /* Some changes were not acknowledged by p_vout->pf_manage or this
2096          * function, it means they should not be authorized */
2097         intf_ErrMsg( "vout error: unauthorized changes in the vout thread" );
2098         return( 1 );
2099     }
2100
2101     return( 0 );
2102 }
2103
2104 /*****************************************************************************
2105  * Align: align a subpicture in the screen
2106  *****************************************************************************
2107  * This function is used for rendering text or subpictures. It returns non 0
2108  * it the final aera is not fully included in display area. Return coordinates
2109  * are absolute.
2110  *****************************************************************************/
2111 static int Align( vout_thread_t *p_vout, int *pi_x, int *pi_y,
2112                    int i_width, int i_height, int i_h_align, int i_v_align )
2113 {
2114     /* Align horizontally */
2115     switch( i_h_align )
2116     {
2117     case CENTER_ALIGN:
2118         *pi_x -= i_width / 2;
2119         break;
2120     case CENTER_RALIGN:
2121         *pi_x += (p_vout->i_width - i_width) / 2;
2122         break;
2123     case RIGHT_ALIGN:
2124         *pi_x -= i_width;
2125         break;
2126     case RIGHT_RALIGN:
2127         *pi_x += p_vout->i_width - i_width;
2128         break;
2129     }
2130
2131     /* Align vertically */
2132     switch( i_v_align )
2133     {
2134     case CENTER_ALIGN:
2135         *pi_y -= i_height / 2;
2136         break;
2137     case CENTER_RALIGN:
2138         *pi_y += (p_vout->i_height - i_height) / 2;
2139         break;
2140     case BOTTOM_ALIGN:
2141         *pi_y -= i_height;
2142         break;
2143     case BOTTOM_RALIGN:
2144         *pi_y += p_vout->i_height - i_height;
2145         break;
2146     case SUBTITLE_RALIGN:
2147         *pi_y += (p_vout->i_height + p_vout->p_buffer[ p_vout->i_buffer_index ].i_pic_y +
2148                   p_vout->p_buffer[ p_vout->i_buffer_index ].i_pic_height - i_height) / 2;
2149         break;
2150     }
2151
2152     /* Return non 0 if clipping failed */
2153     return( (*pi_x < 0) || (*pi_y < 0) ||
2154             (*pi_x + i_width > p_vout->i_width) || (*pi_y + i_height > p_vout->i_height) );
2155 }
2156
2157 /*****************************************************************************
2158  * SetPalette: sets an 8 bpp palette
2159  *****************************************************************************
2160  * This function is just a prototype that does nothing. Architectures that
2161  * support palette allocation should override it.
2162  *****************************************************************************/
2163 static void     SetPalette        ( p_vout_thread_t p_vout, u16 *red,
2164                                     u16 *green, u16 *blue, u16 *transp )
2165 {
2166     intf_ErrMsg( "vout error: method does not support palette changing" );
2167 }
2168