]> git.sesse.net Git - vlc/blob - src/video_output/video_output.c
b2197ec2f058dc0305a699c0eb1894d288bebd23
[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-2001 VideoLAN
8  * $Id: video_output.c,v 1.187 2002/07/23 00:39:17 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 <errno.h>                                                 /* ENOMEM */
31 #include <stdlib.h>                                                /* free() */
32 #include <stdio.h>                                              /* sprintf() */
33 #include <string.h>                                            /* strerror() */
34
35 #include <vlc/vlc.h>
36
37 #ifdef HAVE_SYS_TIMES_H
38 #   include <sys/times.h>
39 #endif
40
41 #include "video.h"
42 #include "video_output.h"
43
44 #if defined( SYS_DARWIN )
45 #include "darwin_specific.h"
46 #endif
47
48 /*****************************************************************************
49  * Local prototypes
50  *****************************************************************************/
51 static int      InitThread        ( vout_thread_t * );
52 static void     RunThread         ( vout_thread_t * );
53 static void     ErrorThread       ( vout_thread_t * );
54 static void     EndThread         ( vout_thread_t * );
55 static void     DestroyThread     ( vout_thread_t * );
56
57 static int      ReduceHeight      ( int );
58 static int      BinaryLog         ( u32 );
59 static void     MaskToShift       ( int *, int *, u32 );
60 static void     InitWindowSize    ( vout_thread_t *, int *, int * );
61
62 /*****************************************************************************
63  * vout_CreateThread: creates a new video output thread
64  *****************************************************************************
65  * This function creates a new video output thread, and returns a pointer
66  * to its description. On error, it returns NULL.
67  *****************************************************************************/
68 vout_thread_t * __vout_CreateThread ( vlc_object_t *p_parent,
69                                       int i_width, int i_height,
70                                       vlc_fourcc_t i_chroma, int i_aspect )
71 {
72     vout_thread_t * p_vout;                             /* thread descriptor */
73     int             i_index;                                /* loop variable */
74     char          * psz_plugin;
75
76     /* Allocate descriptor */
77     p_vout = vlc_object_create( p_parent, VLC_OBJECT_VOUT );
78     if( p_vout == NULL )
79     {
80         msg_Err( p_parent, "out of memory" );
81         return( NULL );
82     }
83
84     /* If the parent is not a VOUT object, that means we are at the start of
85      * the video output pipe */
86     if( p_parent->i_object_type != VLC_OBJECT_VOUT )
87     {
88         /* look for the default filter configuration */
89         p_vout->psz_filter_chain = config_GetPsz( p_parent, "filter" );
90     }
91     else
92     {
93         /* continue the parent's filter chain */
94         char *psz_end;
95
96         psz_end = strchr( ((vout_thread_t *)p_parent)->psz_filter_chain, ':' );
97         if( psz_end && *(psz_end+1) )
98             p_vout->psz_filter_chain = strdup( psz_end+1 );
99         else p_vout->psz_filter_chain = NULL;
100     }
101
102     /* Choose the video output module */
103     if( !p_vout->psz_filter_chain )
104     {
105         psz_plugin = config_GetPsz( p_parent, "vout" );
106     }
107     else
108     {
109         /* the filter chain is a string list of filters separated by double
110          * colons */
111         char *psz_end;
112
113         psz_end = strchr( p_vout->psz_filter_chain, ':' );
114         if( psz_end )
115             psz_plugin = strndup( p_vout->psz_filter_chain,
116                                   psz_end - p_vout->psz_filter_chain );
117         else psz_plugin = strdup( p_vout->psz_filter_chain );
118     }
119
120     /* Initialize pictures and subpictures - translation tables and functions
121      * will be initialized later in InitThread */
122     for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES; i_index++)
123     {
124         p_vout->p_picture[i_index].i_status = FREE_PICTURE;
125         p_vout->p_picture[i_index].i_type   = EMPTY_PICTURE;
126     }
127
128     for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++)
129     {
130         p_vout->p_subpicture[i_index].i_status = FREE_SUBPICTURE;
131         p_vout->p_subpicture[i_index].i_type   = EMPTY_SUBPICTURE;
132     }
133
134     /* No images in the heap */
135     p_vout->i_heap_size = 0;
136
137     /* Initialize the rendering heap */
138     I_RENDERPICTURES = 0;
139     p_vout->render.i_width    = i_width;
140     p_vout->render.i_height   = i_height;
141     p_vout->render.i_chroma   = i_chroma;
142     p_vout->render.i_aspect   = i_aspect;
143
144     p_vout->render.i_rmask    = 0;
145     p_vout->render.i_gmask    = 0;
146     p_vout->render.i_bmask    = 0;
147
148     /* Zero the output heap */
149     I_OUTPUTPICTURES = 0;
150     p_vout->output.i_width    = 0;
151     p_vout->output.i_height   = 0;
152     p_vout->output.i_chroma   = 0;
153     p_vout->output.i_aspect   = 0;
154
155     p_vout->output.i_rmask    = 0;
156     p_vout->output.i_gmask    = 0;
157     p_vout->output.i_bmask    = 0;
158
159     /* Initialize misc stuff */
160     p_vout->i_changes    = 0;
161     p_vout->f_gamma      = 0;
162     p_vout->b_grayscale  = 0;
163     p_vout->b_info       = 0;
164     p_vout->b_interface  = 0;
165     p_vout->b_scale      = 1;
166     p_vout->b_fullscreen = 0;
167     p_vout->render_time  = 10;
168     p_vout->c_fps_samples= 0;
169
170     p_vout->i_mouse_x = 0;
171     p_vout->i_mouse_y = 0;
172     p_vout->i_mouse_button = 0;
173
174     /* user requested fullscreen? */
175     if( config_GetInt( p_vout, "fullscreen" ) )
176     {
177         p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
178     }
179
180     /* Initialize the dimensions of the video window */
181     InitWindowSize( p_vout, &p_vout->i_window_width,
182                     &p_vout->i_window_height );
183
184
185     p_vout->p_module = module_Need( p_vout,
186                            ( p_vout->psz_filter_chain ) ?
187                            MODULE_CAPABILITY_VOUT_FILTER :
188                            MODULE_CAPABILITY_VOUT,
189                            psz_plugin, (void *)p_vout );
190
191     if( psz_plugin ) free( psz_plugin );
192     if( p_vout->p_module == NULL )
193     {
194         msg_Err( p_vout, "no suitable vout module" );
195         vlc_object_destroy( p_vout );
196         return( NULL );
197     }
198
199 #define f p_vout->p_module->p_functions->vout.functions.vout
200     p_vout->pf_create     = f.pf_create;
201     p_vout->pf_init       = f.pf_init;
202     p_vout->pf_end        = f.pf_end;
203     p_vout->pf_destroy    = f.pf_destroy;
204     p_vout->pf_manage     = f.pf_manage;
205     p_vout->pf_render     = f.pf_render;
206     p_vout->pf_display    = f.pf_display;
207 #undef f
208
209     /* Create thread and set locks */
210     vlc_mutex_init( p_vout, &p_vout->picture_lock );
211     vlc_mutex_init( p_vout, &p_vout->subpicture_lock );
212     vlc_mutex_init( p_vout, &p_vout->change_lock );
213
214     vlc_object_attach( p_vout, p_parent );
215
216     if( vlc_thread_create( p_vout, "video output", RunThread, 0 ) )
217     {
218         msg_Err( p_vout, "%s", strerror(ENOMEM) );
219         p_vout->pf_destroy( p_vout );
220         module_Unneed( p_vout->p_module );
221         vlc_object_destroy( p_vout );
222         return NULL;
223     }
224
225     return p_vout;
226 }
227
228 /*****************************************************************************
229  * vout_DestroyThread: destroys a previously created thread
230  *****************************************************************************
231  * Destroy a terminated thread.
232  * The function will request a destruction of the specified thread. If pi_error
233  * is NULL, it will return once the thread is destroyed. Else, it will be
234  * update using one of the THREAD_* constants.
235  *****************************************************************************/
236 void vout_DestroyThread( vout_thread_t *p_vout )
237 {
238     /* Unlink object */
239     vlc_object_detach_all( p_vout );
240
241     /* Request thread destruction */
242     p_vout->b_die = 1;
243     vlc_thread_join( p_vout );
244
245     /* Free structure */
246     vlc_object_destroy( p_vout );
247 }
248
249 /*****************************************************************************
250  * InitThread: initialize video output thread
251  *****************************************************************************
252  * This function is called from RunThread and performs the second step of the
253  * initialization. It returns 0 on success. Note that the thread's flag are not
254  * modified inside this function.
255  *****************************************************************************/
256 static int InitThread( vout_thread_t *p_vout )
257 {
258     int i, i_pgcd;
259
260     vlc_mutex_lock( &p_vout->change_lock );
261
262 #ifdef STATS
263     p_vout->c_loops = 0;
264 #endif
265
266     /* Initialize output method, it allocates direct buffers for us */
267     if( p_vout->pf_init( p_vout ) )
268     {
269         vlc_mutex_unlock( &p_vout->change_lock );
270         return( 1 );
271     }
272
273     if( !I_OUTPUTPICTURES )
274     {
275         msg_Err( p_vout, "plugin was unable to allocate at least "
276                          "one direct buffer" );
277         p_vout->pf_end( p_vout );
278         vlc_mutex_unlock( &p_vout->change_lock );
279         return( 1 );
280     }
281
282     msg_Dbg( p_vout, "got %i direct buffer(s)", I_OUTPUTPICTURES );
283
284     i_pgcd = ReduceHeight( p_vout->render.i_aspect );
285     msg_Dbg( p_vout,
286              "picture in %ix%i, chroma 0x%.8x (%4.4s), aspect ratio %i:%i",
287              p_vout->render.i_width, p_vout->render.i_height,
288              p_vout->render.i_chroma, (char*)&p_vout->render.i_chroma,
289              p_vout->render.i_aspect / i_pgcd, VOUT_ASPECT_FACTOR / i_pgcd );
290
291     i_pgcd = ReduceHeight( p_vout->output.i_aspect );
292     msg_Dbg( p_vout,
293              "picture out %ix%i, chroma 0x%.8x (%4.4s), aspect ratio %i:%i",
294              p_vout->output.i_width, p_vout->output.i_height,
295              p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma,
296              p_vout->output.i_aspect / i_pgcd, VOUT_ASPECT_FACTOR / i_pgcd );
297
298     /* Calculate shifts from system-updated masks */
299     MaskToShift( &p_vout->output.i_lrshift, &p_vout->output.i_rrshift,
300                  p_vout->output.i_rmask );
301     MaskToShift( &p_vout->output.i_lgshift, &p_vout->output.i_rgshift,
302                  p_vout->output.i_gmask );
303     MaskToShift( &p_vout->output.i_lbshift, &p_vout->output.i_rbshift,
304                  p_vout->output.i_bmask );
305
306     /* Check whether we managed to create direct buffers similar to
307      * the render buffers, ie same size, chroma and aspect ratio */
308     if( ( p_vout->output.i_width == p_vout->render.i_width )
309      && ( p_vout->output.i_height == p_vout->render.i_height )
310      && ( vout_ChromaCmp( p_vout->output.i_chroma, p_vout->render.i_chroma ) )
311      && ( p_vout->output.i_aspect == p_vout->render.i_aspect ) )
312     {
313         /* Cool ! We have direct buffers, we can ask the decoder to
314          * directly decode into them ! Map the first render buffers to
315          * the first direct buffers, but keep the first direct buffer
316          * for memcpy operations */
317         p_vout->b_direct = 1;
318
319         msg_Dbg( p_vout, "direct render, mapping "
320                  "render pictures 0-%i to system pictures 1-%i",
321                  VOUT_MAX_PICTURES - 2, VOUT_MAX_PICTURES - 1 );
322
323         for( i = 1; i < VOUT_MAX_PICTURES; i++ )
324         {
325             PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
326             I_RENDERPICTURES++;
327         }
328     }
329     else
330     {
331         /* Rats... Something is wrong here, we could not find an output
332          * plugin able to directly render what we decode. See if we can
333          * find a chroma plugin to do the conversion */
334         p_vout->b_direct = 0;
335
336         /* Choose the best module */
337         p_vout->chroma.p_module =
338             module_Need( p_vout, MODULE_CAPABILITY_CHROMA,
339                          NULL, (void *)p_vout );
340
341         if( p_vout->chroma.p_module == NULL )
342         {
343             msg_Err( p_vout, "no chroma module for %4.4s to %4.4s",
344                      &p_vout->render.i_chroma, &p_vout->output.i_chroma );
345             p_vout->pf_end( p_vout );
346             vlc_mutex_unlock( &p_vout->change_lock );
347             return( 1 );
348         }
349
350 #define f p_vout->chroma.p_module->p_functions->chroma.functions.chroma
351         p_vout->chroma.pf_init       = f.pf_init;
352         p_vout->chroma.pf_end        = f.pf_end;
353 #undef f
354
355         if( I_OUTPUTPICTURES < 2 * VOUT_MAX_PICTURES )
356         {
357             msg_Dbg( p_vout, "indirect render, mapping "
358                      "render pictures %i-%i to system pictures %i-%i",
359                      I_OUTPUTPICTURES - 1, 2 * VOUT_MAX_PICTURES - 2,
360                      I_OUTPUTPICTURES, 2 * VOUT_MAX_PICTURES - 1 );
361         }
362         else
363         {
364             /* FIXME: if this happens, we don't have any render pictures left */
365             msg_Dbg( p_vout, "indirect render, no system pictures needed,"
366                      " we have %i directbuffers", I_OUTPUTPICTURES );
367             msg_Err( p_vout, "this is a bug!" );
368         }
369
370         /* Append render buffers after the direct buffers */
371         for( i = I_OUTPUTPICTURES; i < 2 * VOUT_MAX_PICTURES; i++ )
372         {
373             PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
374             I_RENDERPICTURES++;
375         }
376     }
377
378     /* Link pictures back to their heap */
379     for( i = 0 ; i < I_RENDERPICTURES ; i++ )
380     {
381         PP_RENDERPICTURE[ i ]->p_heap = &p_vout->render;
382     }
383
384     for( i = 0 ; i < I_OUTPUTPICTURES ; i++ )
385     {
386         PP_OUTPUTPICTURE[ i ]->p_heap = &p_vout->output;
387     }
388
389 /* XXX XXX mark thread ready */
390     return( 0 );
391 }
392
393 /*****************************************************************************
394  * RunThread: video output thread
395  *****************************************************************************
396  * Video output thread. This function does only returns when the thread is
397  * terminated. It handles the pictures arriving in the video heap and the
398  * display device events.
399  *****************************************************************************/
400 static void RunThread( vout_thread_t *p_vout)
401 {
402     int             i_index;                                /* index in heap */
403     int             i_idle_loops = 0;  /* loops without displaying a picture */
404     mtime_t         current_date;                            /* current date */
405     mtime_t         display_date;                            /* display date */
406
407     picture_t *     p_picture;                            /* picture pointer */
408     picture_t *     p_last_picture = NULL;                   /* last picture */
409     picture_t *     p_directbuffer;              /* direct buffer to display */
410
411     subpicture_t *  p_subpic;                          /* subpicture pointer */
412
413     /*
414      * Initialize thread
415      */
416     p_vout->b_error = InitThread( p_vout );
417     if( p_vout->b_error )
418     {
419         /* Destroy thread structures allocated by Create and InitThread */
420         p_vout->pf_destroy( p_vout );
421
422         DestroyThread( p_vout );
423         return;
424     }
425
426     /*
427      * Main loop - it is not executed if an error occured during
428      * initialization
429      */
430     while( (!p_vout->b_die) && (!p_vout->b_error) )
431     {
432         /* Initialize loop variables */
433         display_date = 0;
434         current_date = mdate();
435
436 #ifdef STATS
437         p_vout->c_loops++;
438         if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
439         {
440             msg_Dbg( p_vout, "picture heap: %d/%d",
441                      I_RENDERPICTURES, p_vout->i_heap_size );
442         }
443 #endif
444
445         /*
446          * Find the picture to display - this operation does not need lock,
447          * since only READY_PICTUREs are handled
448          */
449         p_picture = NULL;
450
451         for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
452         {
453             if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
454                 && ( (p_picture == NULL) ||
455                      (PP_RENDERPICTURE[i_index]->date < display_date) ) )
456             {
457                 p_picture = PP_RENDERPICTURE[i_index];
458                 display_date = p_picture->date;
459             }
460         }
461
462         if( p_picture != NULL )
463         {
464             /* If we met the last picture, parse again to see whether there is
465              * a more appropriate one. */
466             if( p_picture == p_last_picture )
467             {
468                 for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
469                 {
470                     if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
471                         && (PP_RENDERPICTURE[i_index] != p_last_picture)
472                         && ((p_picture == p_last_picture) ||
473                             (PP_RENDERPICTURE[i_index]->date < display_date)) )
474                     {
475                         p_picture = PP_RENDERPICTURE[i_index];
476                         display_date = p_picture->date;
477                     }
478                 }
479             }
480     
481             /* If we found better than the last picture, destroy it */
482             if( p_last_picture && p_picture != p_last_picture )
483             {
484                 vlc_mutex_lock( &p_vout->picture_lock );
485                 if( p_last_picture->i_refcount )
486                 {
487                     p_last_picture->i_status = DISPLAYED_PICTURE;
488                 }
489                 else
490                 {
491                     p_last_picture->i_status = DESTROYED_PICTURE;
492                     p_vout->i_heap_size--;
493                 }
494                 vlc_mutex_unlock( &p_vout->picture_lock );
495                 p_last_picture = NULL;
496             }
497
498             /* Compute FPS rate */
499             p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ]
500                 = display_date;
501
502             if( !p_picture->b_force &&
503                 p_picture != p_last_picture &&
504                 display_date < current_date + p_vout->render_time )
505             {
506                 /* Picture is late: it will be destroyed and the thread
507                  * will directly choose the next picture */
508                 vlc_mutex_lock( &p_vout->picture_lock );
509                 if( p_picture->i_refcount )
510                 {
511                     /* Pretend we displayed the picture, but don't destroy
512                      * it since the decoder might still need it. */
513                     p_picture->i_status = DISPLAYED_PICTURE;
514                 }
515                 else
516                 {
517                     /* Destroy the picture without displaying it */
518                     p_picture->i_status = DESTROYED_PICTURE;
519                     p_vout->i_heap_size--;
520                 }
521                 msg_Warn( p_vout, "late picture skipped (%lld)",
522                                   current_date - display_date );
523                 vlc_mutex_unlock( &p_vout->picture_lock );
524
525                 continue;
526             }
527 #if 0
528             /* Removed because it causes problems for some people --Meuuh */
529             if( display_date > current_date + VOUT_BOGUS_DELAY )
530             {
531                 /* Picture is waaay too early: it will be destroyed */
532                 vlc_mutex_lock( &p_vout->picture_lock );
533                 if( p_picture->i_refcount )
534                 {
535                     /* Pretend we displayed the picture, but don't destroy
536                      * it since the decoder might still need it. */
537                     p_picture->i_status = DISPLAYED_PICTURE;
538                 }
539                 else
540                 {
541                     /* Destroy the picture without displaying it */
542                     p_picture->i_status = DESTROYED_PICTURE;
543                     p_vout->i_heap_size--;
544                 }
545                 intf_WarnMsg( 1, "vout warning: early picture skipped (%lld)",
546                               display_date - current_date );
547                 vlc_mutex_unlock( &p_vout->picture_lock );
548
549                 continue;
550             }
551 #endif
552             if( display_date > current_date + VOUT_DISPLAY_DELAY )
553             {
554                 /* A picture is ready to be rendered, but its rendering date
555                  * is far from the current one so the thread will perform an
556                  * empty loop as if no picture were found. The picture state
557                  * is unchanged */
558                 p_picture    = NULL;
559                 display_date = 0;
560             }
561             else if( p_picture == p_last_picture )
562             {
563                 /* We are asked to repeat the previous picture, but we first
564                  * wait for a couple of idle loops */
565                 if( i_idle_loops < 4 )
566                 {
567                     p_picture    = NULL;
568                     display_date = 0;
569                 }
570                 else
571                 {
572                     //intf_WarnMsg( 6, "vout info: duplicating picture" );
573                 }
574             }
575         }
576
577         if( p_picture == NULL )
578         {
579             i_idle_loops++;
580         }
581
582         /*
583          * Check for subpictures to display
584          */
585         p_subpic = vout_SortSubPictures( p_vout, display_date );
586
587         /*
588          * Perform rendering
589          */
590         p_directbuffer = vout_RenderPicture( p_vout, p_picture, p_subpic );
591
592         /*
593          * Call the plugin-specific rendering method
594          */
595         if( p_picture != NULL )
596         {
597             /* Render the direct buffer returned by vout_RenderPicture */
598             p_vout->pf_render( p_vout, p_directbuffer );
599         }
600
601         /*
602          * Sleep, wake up
603          */
604         if( display_date != 0 )
605         {
606             /* Store render time using Bresenham algorithm */
607             p_vout->render_time += mdate() - current_date;
608             p_vout->render_time >>= 1;
609         }
610
611         /* Give back change lock */
612         vlc_mutex_unlock( &p_vout->change_lock );
613
614         /* Sleep a while or until a given date */
615         if( display_date != 0 )
616         {
617             mwait( display_date - VOUT_MWAIT_TOLERANCE );
618         }
619         else
620         {
621             msleep( VOUT_IDLE_SLEEP );
622         }
623
624         /* On awakening, take back lock and send immediately picture
625          * to display. */
626         vlc_mutex_lock( &p_vout->change_lock );
627
628         /*
629          * Display the previously rendered picture
630          */
631         if( p_picture != NULL )
632         {
633             /* Display the direct buffer returned by vout_RenderPicture */
634             p_vout->pf_display( p_vout, p_directbuffer );
635
636             /* Reinitialize idle loop count */
637             i_idle_loops = 0;
638
639             /* Tell the vout this was the last picture and that it does not
640              * need to be forced anymore. */
641             p_last_picture = p_picture;
642             p_last_picture->b_force = 0;
643         }
644
645         /*
646          * Check events and manage thread
647          */
648         if( p_vout->pf_manage( p_vout ) )
649         {
650             /* A fatal error occured, and the thread must terminate
651              * immediately, without displaying anything - setting b_error to 1
652              * causes the immediate end of the main while() loop. */
653             p_vout->b_error = 1;
654         }
655
656         if( p_vout->i_changes & VOUT_SIZE_CHANGE )
657         {
658             /* this must only happen when the vout plugin is incapable of
659              * rescaling the picture itself. In this case we need to destroy
660              * the current picture buffers and recreate new ones with the right
661              * dimensions */
662             int i;
663
664             p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
665
666             p_vout->pf_end( p_vout );
667             for( i = 0; i < I_OUTPUTPICTURES; i++ )
668                  p_vout->p_picture[ i ].i_status = FREE_PICTURE;
669
670             I_OUTPUTPICTURES = 0;
671             if( p_vout->pf_init( p_vout ) )
672             {
673                 msg_Err( p_vout, "cannot resize display" );
674                 /* FixMe: p_vout->pf_end will be called again in EndThread() */
675                 p_vout->b_error = 1;
676             }
677
678             /* Need to reinitialise the chroma plugin */
679             p_vout->chroma.pf_end( p_vout );
680             p_vout->chroma.pf_init( p_vout );
681         }
682
683     }
684
685     /*
686      * Error loop - wait until the thread destruction is requested
687      */
688     if( p_vout->b_error )
689     {
690         ErrorThread( p_vout );
691     }
692
693     /* End of thread */
694     EndThread( p_vout );
695
696     /* Destroy method-dependant resources */
697     p_vout->pf_destroy( p_vout );
698
699     /* Destroy thread structures allocated by CreateThread */
700     DestroyThread( p_vout );
701 }
702
703 /*****************************************************************************
704  * ErrorThread: RunThread() error loop
705  *****************************************************************************
706  * This function is called when an error occured during thread main's loop. The
707  * thread can still receive feed, but must be ready to terminate as soon as
708  * possible.
709  *****************************************************************************/
710 static void ErrorThread( vout_thread_t *p_vout )
711 {
712     /* Wait until a `die' order */
713     while( !p_vout->b_die )
714     {
715         /* Sleep a while */
716         msleep( VOUT_IDLE_SLEEP );
717     }
718 }
719
720 /*****************************************************************************
721  * EndThread: thread destruction
722  *****************************************************************************
723  * This function is called when the thread ends after a sucessful
724  * initialization. It frees all ressources allocated by InitThread.
725  *****************************************************************************/
726 static void EndThread( vout_thread_t *p_vout )
727 {
728     int     i_index;                                        /* index in heap */
729
730 #ifdef STATS
731     {
732         struct tms cpu_usage;
733         times( &cpu_usage );
734
735         msg_Dbg( p_vout, "cpu usage (user: %d, system: %d)",
736                  cpu_usage.tms_utime, cpu_usage.tms_stime );
737     }
738 #endif
739
740     if( !p_vout->b_direct )
741     {
742         p_vout->chroma.pf_end( p_vout );
743         module_Unneed( p_vout->chroma.p_module );
744     }
745
746     /* Destroy all remaining pictures */
747     for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES; i_index++ )
748     {
749         if ( p_vout->p_picture[i_index].i_type == MEMORY_PICTURE )
750         {
751             free( p_vout->p_picture[i_index].p_data_orig );
752         }
753     }
754
755     /* Destroy all remaining subpictures */
756     for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
757     {
758         if( p_vout->p_subpicture[i_index].i_status != FREE_SUBPICTURE )
759         {
760             free( p_vout->p_subpicture[i_index].p_sys );
761         }
762     }
763
764     /* Destroy translation tables */
765     p_vout->pf_end( p_vout );
766
767     /* Release the change lock */
768     vlc_mutex_unlock( &p_vout->change_lock );
769 }
770
771 /*****************************************************************************
772  * DestroyThread: thread destruction
773  *****************************************************************************
774  * This function is called when the thread ends. It frees all ressources
775  * allocated by CreateThread. Status is available at this stage.
776  *****************************************************************************/
777 static void DestroyThread( vout_thread_t *p_vout )
778 {
779     /* Destroy the locks */
780     vlc_mutex_destroy( &p_vout->picture_lock );
781     vlc_mutex_destroy( &p_vout->subpicture_lock );
782     vlc_mutex_destroy( &p_vout->change_lock );
783
784     /* Release the module */
785     module_Unneed( p_vout->p_module );
786 }
787
788 /* following functions are local */
789
790 static int ReduceHeight( int i_ratio )
791 {
792     int i_dummy = VOUT_ASPECT_FACTOR;
793     int i_pgcd  = 1;
794  
795     if( !i_ratio )
796     {
797         return i_pgcd;
798     }
799
800     /* VOUT_ASPECT_FACTOR is (2^7 * 3^3 * 5^3), we just check for 2, 3 and 5 */
801     while( !(i_ratio & 1) && !(i_dummy & 1) )
802     {
803         i_ratio >>= 1;
804         i_dummy >>= 1;
805         i_pgcd  <<= 1;
806     }
807
808     while( !(i_ratio % 3) && !(i_dummy % 3) )
809     {
810         i_ratio /= 3;
811         i_dummy /= 3;
812         i_pgcd  *= 3;
813     }
814
815     while( !(i_ratio % 5) && !(i_dummy % 5) )
816     {
817         i_ratio /= 5;
818         i_dummy /= 5;
819         i_pgcd  *= 5;
820     }
821
822     return i_pgcd;
823 }
824
825 /*****************************************************************************
826  * BinaryLog: computes the base 2 log of a binary value
827  *****************************************************************************
828  * This functions is used by MaskToShift, to get a bit index from a binary
829  * value.
830  *****************************************************************************/
831 static int BinaryLog(u32 i)
832 {
833     int i_log = 0;
834
835     if( i == 0 ) return -31337;
836
837     if( i & 0xffff0000 ) i_log += 16;
838     if( i & 0xff00ff00 ) i_log += 8;
839     if( i & 0xf0f0f0f0 ) i_log += 4;
840     if( i & 0xcccccccc ) i_log += 2;
841     if( i & 0xaaaaaaaa ) i_log += 1;
842
843     return( i_log );
844 }
845
846 /*****************************************************************************
847  * MaskToShift: transform a color mask into right and left shifts
848  *****************************************************************************
849  * This function is used for obtaining color shifts from masks.
850  *****************************************************************************/
851 static void MaskToShift( int *pi_left, int *pi_right, u32 i_mask )
852 {
853     u32 i_low, i_high;                 /* lower hand higher bits of the mask */
854
855     if( !i_mask )
856     {
857         *pi_left = *pi_right = 0;
858         return;
859     }
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  * InitWindowSize: find the initial dimensions the video window should have.
876  *****************************************************************************
877  * This function will check the "width", "height" and "zoom" config options and
878  * will calculate the size that the video window should have.
879  *****************************************************************************/
880 static void InitWindowSize( vout_thread_t *p_vout, int *pi_width,
881                             int *pi_height )
882 {
883     int i_width, i_height;
884     double f_zoom;
885
886     i_width = config_GetInt( p_vout, "width" );
887     i_height = config_GetInt( p_vout, "height" );
888     f_zoom = config_GetFloat( p_vout, "zoom" );
889
890     if( (i_width >= 0) && (i_height >= 0))
891     {
892         *pi_width = i_width * f_zoom;
893         *pi_height = i_height * f_zoom;
894         return;
895     }
896     else if( i_width >= 0 )
897     {
898         *pi_width = i_width * f_zoom;
899         *pi_height = i_width * f_zoom * VOUT_ASPECT_FACTOR /
900                         p_vout->render.i_aspect;
901         return;
902     }
903     else if( i_height >= 0 )
904     {
905         *pi_height = i_height * f_zoom;
906         *pi_width = i_height * f_zoom * p_vout->render.i_aspect /
907                        VOUT_ASPECT_FACTOR;
908         return;
909     }
910
911     if( p_vout->render.i_height * p_vout->render.i_aspect
912         >= p_vout->render.i_width * VOUT_ASPECT_FACTOR )
913     {
914         *pi_width = p_vout->render.i_height * f_zoom
915           * p_vout->render.i_aspect / VOUT_ASPECT_FACTOR;
916         *pi_height = p_vout->render.i_height * f_zoom;
917     }
918     else
919     {
920         *pi_width = p_vout->render.i_width * f_zoom;
921         *pi_height = p_vout->render.i_width * f_zoom
922           * VOUT_ASPECT_FACTOR / p_vout->render.i_aspect;
923     }
924 }