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