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