]> git.sesse.net Git - vlc/blob - src/video_output/video_output.c
input: DestroyThread()'s content should happen in the input destructor.
[vlc] / src / video_output / video_output.c
1 /*****************************************************************************
2  * video_output.c : video output thread
3  *
4  * This module describes the programming interface for video output threads.
5  * It includes functions allowing to open a new thread, send pictures to a
6  * thread, and destroy a previously oppened video output thread.
7  *****************************************************************************
8  * Copyright (C) 2000-2007 the VideoLAN team
9  * $Id$
10  *
11  * Authors: Vincent Seguin <seguin@via.ecp.fr>
12  *          Gildas Bazin <gbazin@videolan.org>
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
27  *****************************************************************************/
28
29 /*****************************************************************************
30  * Preamble
31  *****************************************************************************/
32 #ifdef HAVE_CONFIG_H
33 # include "config.h"
34 #endif
35
36 #include <vlc/vlc.h>
37
38 #include <stdlib.h>                                                /* free() */
39 #include <string.h>
40
41
42 #ifdef HAVE_SYS_TIMES_H
43 #   include <sys/times.h>
44 #endif
45
46 #include <vlc_vout.h>
47 #include <vlc_playlist.h>
48
49 #include <vlc_filter.h>
50 #include <vlc_osd.h>
51
52 #if defined( __APPLE__ )
53 /* Include darwin_specific.h here if needed */
54 #endif
55
56 /** FIXME This is quite ugly but needed while we don't have counters
57  * helpers */
58 #include "input/input_internal.h"
59
60 #include "modules/modules.h"
61
62 /*****************************************************************************
63  * Local prototypes
64  *****************************************************************************/
65 static int      InitThread        ( vout_thread_t * );
66 static void     RunThread         ( vout_thread_t * );
67 static void     ErrorThread       ( vout_thread_t * );
68 static void     EndThread         ( vout_thread_t * );
69
70 static void     AspectRatio       ( int, int *, int * );
71 static int      BinaryLog         ( uint32_t );
72 static void     MaskToShift       ( int *, int *, uint32_t );
73
74 static void     vout_Destructor   ( vlc_object_t * p_this );
75
76 /* Object variables callbacks */
77 static int DeinterlaceCallback( vlc_object_t *, char const *,
78                                 vlc_value_t, vlc_value_t, void * );
79 static int FilterCallback( vlc_object_t *, char const *,
80                            vlc_value_t, vlc_value_t, void * );
81 static int VideoFilter2Callback( vlc_object_t *, char const *,
82                                  vlc_value_t, vlc_value_t, void * );
83
84 /* From vout_intf.c */
85 int vout_Snapshot( vout_thread_t *, picture_t * );
86
87 /* Video filter2 parsing */
88 static int ParseVideoFilter2Chain( vout_thread_t *, char * );
89 static void RemoveVideoFilters2( vout_thread_t *p_vout );
90
91 /* Display media title in OSD */
92 static void DisplayTitleOnOSD( vout_thread_t *p_vout );
93
94 /*****************************************************************************
95  * Video Filter2 functions
96  *****************************************************************************/
97 struct filter_owner_sys_t
98 {
99     vout_thread_t *p_vout;
100 };
101
102 static picture_t *video_new_buffer_filter( filter_t *p_filter )
103 {
104     picture_t *p_picture;
105     vout_thread_t *p_vout = p_filter->p_owner->p_vout;
106
107     p_picture = vout_CreatePicture( p_vout, 0, 0, 0 );
108
109     return p_picture;
110 }
111
112 static void video_del_buffer_filter( filter_t *p_filter, picture_t *p_pic )
113 {
114     vout_DestroyPicture( p_filter->p_owner->p_vout, p_pic );
115 }
116
117 /*****************************************************************************
118  * vout_Request: find a video output thread, create one, or destroy one.
119  *****************************************************************************
120  * This function looks for a video output thread matching the current
121  * properties. If not found, it spawns a new one.
122  *****************************************************************************/
123 vout_thread_t *__vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout,
124                                video_format_t *p_fmt )
125 {
126     if( !p_fmt )
127     {
128         /* Reattach video output to playlist before bailing out */
129         if( p_vout )
130         {
131             spu_Attach( p_vout->p_spu, p_this, VLC_FALSE );
132             vlc_object_detach( p_vout );
133             vlc_object_attach( p_vout, p_this->p_libvlc );
134         }
135         return NULL;
136     }
137
138     /* If a video output was provided, lock it, otherwise look for one. */
139     if( p_vout )
140     {
141         vlc_object_yield( p_vout );
142     }
143     else
144     {
145         p_vout = vlc_object_find( p_this, VLC_OBJECT_VOUT, FIND_CHILD );
146
147         if( !p_vout )
148         {
149             p_vout = vlc_object_find( p_this->p_libvlc,
150                                       VLC_OBJECT_VOUT, FIND_CHILD );
151             /* only first children of p_input for unused vout */
152             if( p_vout && p_vout->p_parent != VLC_OBJECT(p_this->p_libvlc) )
153             {
154                 vlc_object_release( p_vout );
155                 p_vout = NULL;
156             }
157             if( p_vout )
158                 vlc_object_detach( p_vout );    /* Remove it from the GC */
159         }
160     }
161
162     /* If we now have a video output, check it has the right properties */
163     if( p_vout )
164     {
165         char *psz_filter_chain;
166         vlc_value_t val;
167
168         /* We don't directly check for the "vout-filter" variable for obvious
169          * performance reasons. */
170         if( p_vout->b_filter_change )
171         {
172             var_Get( p_vout, "vout-filter", &val );
173             psz_filter_chain = val.psz_string;
174
175             if( psz_filter_chain && !*psz_filter_chain )
176             {
177                 free( psz_filter_chain );
178                 psz_filter_chain = NULL;
179             }
180             if( p_vout->psz_filter_chain && !*p_vout->psz_filter_chain )
181             {
182                 free( p_vout->psz_filter_chain );
183                 p_vout->psz_filter_chain = NULL;
184             }
185
186             if( !psz_filter_chain && !p_vout->psz_filter_chain )
187             {
188                 p_vout->b_filter_change = VLC_FALSE;
189             }
190
191             free( psz_filter_chain );
192         }
193
194         if( ( p_vout->fmt_render.i_width != p_fmt->i_width ) ||
195             ( p_vout->fmt_render.i_height != p_fmt->i_height ) ||
196             ( p_vout->fmt_render.i_aspect != p_fmt->i_aspect ) ||
197             p_vout->b_filter_change )
198         {
199             /* We are not interested in this format, close this vout */
200             vlc_object_release( p_vout );
201             vout_Destroy( p_vout );
202             p_vout = NULL;
203         }
204         else
205         {
206             /* This video output is cool! Hijack it. */
207             spu_Attach( p_vout->p_spu, p_this, VLC_TRUE );
208             vlc_object_attach( p_vout, p_this );
209             if( p_vout->b_title_show )
210                 DisplayTitleOnOSD( p_vout );
211             vlc_object_release( p_vout );
212         }
213     }
214
215     if( !p_vout )
216     {
217         msg_Dbg( p_this, "no usable vout present, spawning one" );
218
219         p_vout = vout_Create( p_this, p_fmt );
220     }
221
222     return p_vout;
223 }
224
225 /*****************************************************************************
226  * vout_Create: creates a new video output thread
227  *****************************************************************************
228  * This function creates a new video output thread, and returns a pointer
229  * to its description. On error, it returns NULL.
230  *****************************************************************************/
231 vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt )
232 {
233     vout_thread_t  * p_vout;                            /* thread descriptor */
234     input_thread_t * p_input_thread;
235     int              i_index;                               /* loop variable */
236     vlc_value_t      val, text;
237
238     unsigned int i_width = p_fmt->i_width;
239     unsigned int i_height = p_fmt->i_height;
240     vlc_fourcc_t i_chroma = p_fmt->i_chroma;
241     unsigned int i_aspect = p_fmt->i_aspect;
242
243     config_chain_t *p_cfg;
244     char *psz_parser;
245     char *psz_name;
246
247     /* Allocate descriptor */
248     p_vout = vlc_object_create( p_parent, VLC_OBJECT_VOUT );
249     if( p_vout == NULL )
250     {
251         msg_Err( p_parent, "out of memory" );
252         return NULL;
253     }
254
255     /* Initialize pictures - translation tables and functions
256      * will be initialized later in InitThread */
257     for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++)
258     {
259         p_vout->p_picture[i_index].pf_lock = NULL;
260         p_vout->p_picture[i_index].pf_unlock = NULL;
261         p_vout->p_picture[i_index].i_status = FREE_PICTURE;
262         p_vout->p_picture[i_index].i_type   = EMPTY_PICTURE;
263         p_vout->p_picture[i_index].b_slow   = 0;
264     }
265
266     /* No images in the heap */
267     p_vout->i_heap_size = 0;
268
269     /* Initialize the rendering heap */
270     I_RENDERPICTURES = 0;
271
272     vlc_ureduce( &p_fmt->i_sar_num, &p_fmt->i_sar_den,
273                  p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 );
274     p_vout->fmt_render        = *p_fmt;   /* FIXME palette */
275     p_vout->fmt_in            = *p_fmt;   /* FIXME palette */
276
277     p_vout->render.i_width    = i_width;
278     p_vout->render.i_height   = i_height;
279     p_vout->render.i_chroma   = i_chroma;
280     p_vout->render.i_aspect   = i_aspect;
281
282     p_vout->render.i_rmask    = 0;
283     p_vout->render.i_gmask    = 0;
284     p_vout->render.i_bmask    = 0;
285
286     p_vout->render.i_last_used_pic = -1;
287     p_vout->render.b_allow_modify_pics = 1;
288
289     /* Zero the output heap */
290     I_OUTPUTPICTURES = 0;
291     p_vout->output.i_width    = 0;
292     p_vout->output.i_height   = 0;
293     p_vout->output.i_chroma   = 0;
294     p_vout->output.i_aspect   = 0;
295
296     p_vout->output.i_rmask    = 0;
297     p_vout->output.i_gmask    = 0;
298     p_vout->output.i_bmask    = 0;
299
300     /* Initialize misc stuff */
301     p_vout->i_changes    = 0;
302     p_vout->f_gamma      = 0;
303     p_vout->b_grayscale  = 0;
304     p_vout->b_info       = 0;
305     p_vout->b_interface  = 0;
306     p_vout->b_scale      = 1;
307     p_vout->b_fullscreen = 0;
308     p_vout->i_alignment  = 0;
309     p_vout->render_time  = 10;
310     p_vout->c_fps_samples = 0;
311     p_vout->b_filter_change = 0;
312     p_vout->pf_control = 0;
313     p_vout->p_parent_intf = 0;
314     p_vout->i_par_num = p_vout->i_par_den = 1;
315
316     /* Initialize locks */
317     vlc_mutex_init( p_vout, &p_vout->picture_lock );
318     vlc_mutex_init( p_vout, &p_vout->change_lock );
319     vlc_mutex_init( p_vout, &p_vout->vfilter_lock );
320
321     /* Mouse coordinates */
322     var_Create( p_vout, "mouse-x", VLC_VAR_INTEGER );
323     var_Create( p_vout, "mouse-y", VLC_VAR_INTEGER );
324     var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER );
325     var_Create( p_vout, "mouse-moved", VLC_VAR_BOOL );
326     var_Create( p_vout, "mouse-clicked", VLC_VAR_INTEGER );
327
328     /* Initialize subpicture unit */
329     p_vout->p_spu = spu_Create( p_vout );
330     spu_Attach( p_vout->p_spu, p_parent, VLC_TRUE );
331
332     /* Attach the new object now so we can use var inheritance below */
333     vlc_object_attach( p_vout, p_parent );
334
335     spu_Init( p_vout->p_spu );
336
337     /* Take care of some "interface/control" related initialisations */
338     vout_IntfInit( p_vout );
339
340     /* If the parent is not a VOUT object, that means we are at the start of
341      * the video output pipe */
342     if( p_parent->i_object_type != VLC_OBJECT_VOUT )
343     {
344         /* Look for the default filter configuration */
345         var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
346         var_Get( p_vout, "vout-filter", &val );
347         p_vout->psz_filter_chain = val.psz_string;
348
349         /* Apply video filter2 objects on the first vout */
350         var_Create( p_vout, "video-filter",
351                     VLC_VAR_STRING | VLC_VAR_DOINHERIT );
352         var_Get( p_vout, "video-filter", &val );
353         ParseVideoFilter2Chain( p_vout, val.psz_string );
354         free( val.psz_string );
355     }
356     else
357     {
358         /* continue the parent's filter chain */
359         char *psz_tmp;
360
361         /* Ugly hack to jump to our configuration chain */
362         p_vout->psz_filter_chain
363             = ((vout_thread_t *)p_parent)->psz_filter_chain;
364         p_vout->psz_filter_chain
365             = config_ChainCreate( &psz_tmp, &p_cfg, p_vout->psz_filter_chain );
366         config_ChainDestroy( p_cfg );
367         free( psz_tmp );
368
369         /* Create a video filter2 var ... but don't inherit values */
370         var_Create( p_vout, "video-filter", VLC_VAR_STRING );
371         ParseVideoFilter2Chain( p_vout, NULL );
372     }
373
374     var_AddCallback( p_vout, "video-filter", VideoFilter2Callback, NULL );
375     p_vout->b_vfilter_change = VLC_TRUE;
376     p_vout->i_vfilters = 0;
377
378     /* Choose the video output module */
379     if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain )
380     {
381         var_Create( p_vout, "vout", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
382         var_Get( p_vout, "vout", &val );
383         psz_parser = val.psz_string;
384     }
385     else
386     {
387         psz_parser = strdup( p_vout->psz_filter_chain );
388     }
389
390     /* Create the vout thread */
391     config_ChainCreate( &psz_name, &p_cfg, psz_parser );
392     free( psz_parser );
393     p_vout->p_cfg = p_cfg;
394     p_vout->p_module = module_Need( p_vout,
395         ( p_vout->psz_filter_chain && *p_vout->psz_filter_chain ) ?
396         "video filter" : "video output", psz_name, p_vout->psz_filter_chain && *p_vout->psz_filter_chain );
397     free( psz_name );
398
399     if( p_vout->p_module == NULL )
400     {
401         msg_Err( p_vout, "no suitable vout module" );
402         vlc_object_detach( p_vout );
403         vlc_object_release( p_vout );
404         return NULL;
405     }
406
407     /* Create a few object variables for interface interaction */
408     var_Create( p_vout, "deinterlace", VLC_VAR_STRING | VLC_VAR_HASCHOICE );
409     text.psz_string = _("Deinterlace");
410     var_Change( p_vout, "deinterlace", VLC_VAR_SETTEXT, &text, NULL );
411     val.psz_string = (char *)""; text.psz_string = _("Disable");
412     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
413     val.psz_string = (char *)"discard"; text.psz_string = _("Discard");
414     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
415     val.psz_string = (char *)"blend"; text.psz_string = _("Blend");
416     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
417     val.psz_string = (char *)"mean"; text.psz_string = _("Mean");
418     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
419     val.psz_string = (char *)"bob"; text.psz_string = _("Bob");
420     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
421     val.psz_string = (char *)"linear"; text.psz_string = _("Linear");
422     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
423     val.psz_string = (char *)"x"; text.psz_string = (char *)"X";
424     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
425
426     if( var_Get( p_vout, "deinterlace-mode", &val ) == VLC_SUCCESS )
427     {
428         var_Set( p_vout, "deinterlace", val );
429         free( val.psz_string );
430     }
431     var_AddCallback( p_vout, "deinterlace", DeinterlaceCallback, NULL );
432
433     var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
434     text.psz_string = _("Filters");
435     var_Change( p_vout, "vout-filter", VLC_VAR_SETTEXT, &text, NULL );
436     var_AddCallback( p_vout, "vout-filter", FilterCallback, NULL );
437
438     /* Calculate delay created by internal caching */
439     p_input_thread = (input_thread_t *)vlc_object_find( p_vout,
440                                            VLC_OBJECT_INPUT, FIND_ANYWHERE );
441     if( p_input_thread )
442     {
443         p_vout->i_pts_delay = p_input_thread->i_pts_delay;
444         vlc_object_release( p_input_thread );
445     }
446     else
447     {
448         p_vout->i_pts_delay = DEFAULT_PTS_DELAY;
449     }
450
451     if( vlc_thread_create( p_vout, "video output", RunThread,
452                            VLC_THREAD_PRIORITY_OUTPUT, VLC_TRUE ) )
453     {
454         msg_Err( p_vout, "out of memory" );
455         module_Unneed( p_vout, p_vout->p_module );
456         vlc_object_release( p_vout );
457         return NULL;
458     }
459
460     if( p_vout->b_error )
461     {
462         msg_Err( p_vout, "video output creation failed" );
463
464         /* Make sure the thread is destroyed */
465         vlc_object_release( p_vout );
466         return NULL;
467     }
468
469     vlc_object_set_destructor( p_vout, vout_Destructor );
470
471     return p_vout;
472 }
473
474 /*****************************************************************************
475  * vout_Destroy: destroys a previously created video output
476  *****************************************************************************
477  * Destroy a terminated thread.
478  * The function will request a destruction of the specified thread. If pi_error
479  * is NULL, it will return once the thread is destroyed. Else, it will be
480  * update using one of the THREAD_* constants.
481  *****************************************************************************/
482 void vout_Destroy( vout_thread_t *p_vout )
483 {
484     /* XXX: should go in the destructor */
485     var_Destroy( p_vout, "intf-change" );
486
487     vlc_object_release( p_vout );
488 }
489
490 static void vout_Destructor( vlc_object_t * p_this )
491 {
492     vout_thread_t *p_vout = (vout_thread_t *)p_this;
493
494     /* Destroy the locks */
495     vlc_mutex_destroy( &p_vout->picture_lock );
496     vlc_mutex_destroy( &p_vout->change_lock );
497     vlc_mutex_destroy( &p_vout->vfilter_lock );
498
499     /* Release the module */
500     if( p_vout->p_module )
501     {
502         module_Unneed( p_vout, p_vout->p_module );
503     }
504
505     free( p_vout->psz_filter_chain );
506
507     config_ChainDestroy( p_vout->p_cfg );
508
509 #ifndef __APPLE__
510     vout_thread_t *p_another_vout;
511     playlist_t *p_playlist = pl_Yield( p_vout );
512
513     /* This is a dirty hack for mostly Linux, where there is no way to get the GUI
514        back if you closed it while playing video. This is solved in Mac OS X,
515        where we have this novelty called menubar, that will always allow you access
516        to the applications main functionality. They should try that on linux sometime */
517     p_another_vout = vlc_object_find( p_this->p_libvlc,
518                                       VLC_OBJECT_VOUT, FIND_ANYWHERE );
519     if( p_another_vout == NULL )
520     {
521         vlc_value_t val;
522         val.b_bool = VLC_TRUE;
523         var_Set( p_playlist, "intf-show", val );
524     }
525     else
526     {
527         vlc_object_release( p_another_vout );
528     }
529     vlc_object_release( p_playlist );
530 #endif
531 }
532
533 /*****************************************************************************
534  * InitThread: initialize video output thread
535  *****************************************************************************
536  * This function is called from RunThread and performs the second step of the
537  * initialization. It returns 0 on success. Note that the thread's flag are not
538  * modified inside this function.
539  *****************************************************************************/
540 static int InitThread( vout_thread_t *p_vout )
541 {
542     int i, i_aspect_x, i_aspect_y;
543
544     vlc_mutex_lock( &p_vout->change_lock );
545
546 #ifdef STATS
547     p_vout->c_loops = 0;
548 #endif
549
550     /* Initialize output method, it allocates direct buffers for us */
551     if( p_vout->pf_init( p_vout ) )
552     {
553         vlc_mutex_unlock( &p_vout->change_lock );
554         return VLC_EGENERIC;
555     }
556
557     if( !I_OUTPUTPICTURES )
558     {
559         msg_Err( p_vout, "plugin was unable to allocate at least "
560                          "one direct buffer" );
561         p_vout->pf_end( p_vout );
562         vlc_mutex_unlock( &p_vout->change_lock );
563         return VLC_EGENERIC;
564     }
565
566     if( I_OUTPUTPICTURES > VOUT_MAX_PICTURES )
567     {
568         msg_Err( p_vout, "plugin allocated too many direct buffers, "
569                          "our internal buffers must have overflown." );
570         p_vout->pf_end( p_vout );
571         vlc_mutex_unlock( &p_vout->change_lock );
572         return VLC_EGENERIC;
573     }
574
575     msg_Dbg( p_vout, "got %i direct buffer(s)", I_OUTPUTPICTURES );
576
577     AspectRatio( p_vout->fmt_render.i_aspect, &i_aspect_x, &i_aspect_y );
578
579     msg_Dbg( p_vout, "picture in %ix%i (%i,%i,%ix%i), "
580              "chroma %4.4s, ar %i:%i, sar %i:%i",
581              p_vout->fmt_render.i_width, p_vout->fmt_render.i_height,
582              p_vout->fmt_render.i_x_offset, p_vout->fmt_render.i_y_offset,
583              p_vout->fmt_render.i_visible_width,
584              p_vout->fmt_render.i_visible_height,
585              (char*)&p_vout->fmt_render.i_chroma,
586              i_aspect_x, i_aspect_y,
587              p_vout->fmt_render.i_sar_num, p_vout->fmt_render.i_sar_den );
588
589     AspectRatio( p_vout->fmt_in.i_aspect, &i_aspect_x, &i_aspect_y );
590
591     msg_Dbg( p_vout, "picture user %ix%i (%i,%i,%ix%i), "
592              "chroma %4.4s, ar %i:%i, sar %i:%i",
593              p_vout->fmt_in.i_width, p_vout->fmt_in.i_height,
594              p_vout->fmt_in.i_x_offset, p_vout->fmt_in.i_y_offset,
595              p_vout->fmt_in.i_visible_width,
596              p_vout->fmt_in.i_visible_height,
597              (char*)&p_vout->fmt_in.i_chroma,
598              i_aspect_x, i_aspect_y,
599              p_vout->fmt_in.i_sar_num, p_vout->fmt_in.i_sar_den );
600
601     if( !p_vout->fmt_out.i_width || !p_vout->fmt_out.i_height )
602     {
603         p_vout->fmt_out.i_width = p_vout->fmt_out.i_visible_width =
604             p_vout->output.i_width;
605         p_vout->fmt_out.i_height = p_vout->fmt_out.i_visible_height =
606             p_vout->output.i_height;
607         p_vout->fmt_out.i_x_offset =  p_vout->fmt_out.i_y_offset = 0;
608
609         p_vout->fmt_out.i_aspect = p_vout->output.i_aspect;
610         p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
611     }
612     if( !p_vout->fmt_out.i_sar_num || !p_vout->fmt_out.i_sar_num )
613     {
614         p_vout->fmt_out.i_sar_num = p_vout->fmt_out.i_aspect *
615             p_vout->fmt_out.i_height;
616         p_vout->fmt_out.i_sar_den = VOUT_ASPECT_FACTOR *
617             p_vout->fmt_out.i_width;
618     }
619
620     vlc_ureduce( &p_vout->fmt_out.i_sar_num, &p_vout->fmt_out.i_sar_den,
621                  p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den, 0 );
622
623     AspectRatio( p_vout->fmt_out.i_aspect, &i_aspect_x, &i_aspect_y );
624
625     msg_Dbg( p_vout, "picture out %ix%i (%i,%i,%ix%i), "
626              "chroma %4.4s, ar %i:%i, sar %i:%i",
627              p_vout->fmt_out.i_width, p_vout->fmt_out.i_height,
628              p_vout->fmt_out.i_x_offset, p_vout->fmt_out.i_y_offset,
629              p_vout->fmt_out.i_visible_width,
630              p_vout->fmt_out.i_visible_height,
631              (char*)&p_vout->fmt_out.i_chroma,
632              i_aspect_x, i_aspect_y,
633              p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den );
634
635     /* Calculate shifts from system-updated masks */
636     MaskToShift( &p_vout->output.i_lrshift, &p_vout->output.i_rrshift,
637                  p_vout->output.i_rmask );
638     MaskToShift( &p_vout->output.i_lgshift, &p_vout->output.i_rgshift,
639                  p_vout->output.i_gmask );
640     MaskToShift( &p_vout->output.i_lbshift, &p_vout->output.i_rbshift,
641                  p_vout->output.i_bmask );
642
643     /* Check whether we managed to create direct buffers similar to
644      * the render buffers, ie same size and chroma */
645     if( ( p_vout->output.i_width == p_vout->render.i_width )
646      && ( p_vout->output.i_height == p_vout->render.i_height )
647      && ( vout_ChromaCmp( p_vout->output.i_chroma, p_vout->render.i_chroma ) ) )
648     {
649         /* Cool ! We have direct buffers, we can ask the decoder to
650          * directly decode into them ! Map the first render buffers to
651          * the first direct buffers, but keep the first direct buffer
652          * for memcpy operations */
653         p_vout->b_direct = 1;
654
655         for( i = 1; i < VOUT_MAX_PICTURES; i++ )
656         {
657             if( p_vout->p_picture[ i ].i_type != DIRECT_PICTURE &&
658                 I_RENDERPICTURES >= VOUT_MIN_DIRECT_PICTURES - 1 &&
659                 p_vout->p_picture[ i - 1 ].i_type == DIRECT_PICTURE )
660             {
661                 /* We have enough direct buffers so there's no need to
662                  * try to use system memory buffers. */
663                 break;
664             }
665             PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
666             I_RENDERPICTURES++;
667         }
668
669         msg_Dbg( p_vout, "direct render, mapping "
670                  "render pictures 0-%i to system pictures 1-%i",
671                  VOUT_MAX_PICTURES - 2, VOUT_MAX_PICTURES - 1 );
672     }
673     else
674     {
675         /* Rats... Something is wrong here, we could not find an output
676          * plugin able to directly render what we decode. See if we can
677          * find a chroma plugin to do the conversion */
678         p_vout->b_direct = 0;
679
680         /* Choose the best module */
681         p_vout->chroma.p_module = module_Need( p_vout, "chroma", NULL, 0 );
682
683         if( p_vout->chroma.p_module == NULL )
684         {
685             msg_Err( p_vout, "no chroma module for %4.4s to %4.4s",
686                      (char*)&p_vout->render.i_chroma,
687                      (char*)&p_vout->output.i_chroma );
688             p_vout->pf_end( p_vout );
689             vlc_mutex_unlock( &p_vout->change_lock );
690             return VLC_EGENERIC;
691         }
692
693         msg_Dbg( p_vout, "indirect render, mapping "
694                  "render pictures 0-%i to system pictures %i-%i",
695                  VOUT_MAX_PICTURES - 1, I_OUTPUTPICTURES,
696                  I_OUTPUTPICTURES + VOUT_MAX_PICTURES - 1 );
697
698         /* Append render buffers after the direct buffers */
699         for( i = I_OUTPUTPICTURES; i < 2 * VOUT_MAX_PICTURES; i++ )
700         {
701             PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
702             I_RENDERPICTURES++;
703
704             /* Check if we have enough render pictures */
705             if( I_RENDERPICTURES == VOUT_MAX_PICTURES )
706                 break;
707         }
708     }
709
710     /* Link pictures back to their heap */
711     for( i = 0 ; i < I_RENDERPICTURES ; i++ )
712     {
713         PP_RENDERPICTURE[ i ]->p_heap = &p_vout->render;
714     }
715
716     for( i = 0 ; i < I_OUTPUTPICTURES ; i++ )
717     {
718         PP_OUTPUTPICTURE[ i ]->p_heap = &p_vout->output;
719     }
720
721 /* XXX XXX mark thread ready */
722     return VLC_SUCCESS;
723 }
724
725 /*****************************************************************************
726  * RunThread: video output thread
727  *****************************************************************************
728  * Video output thread. This function does only returns when the thread is
729  * terminated. It handles the pictures arriving in the video heap and the
730  * display device events.
731  *****************************************************************************/
732 static void RunThread( vout_thread_t *p_vout)
733 {
734     int             i_index;                                /* index in heap */
735     int             i_idle_loops = 0;  /* loops without displaying a picture */
736     mtime_t         current_date;                            /* current date */
737     mtime_t         display_date;                            /* display date */
738
739     picture_t *     p_picture;                            /* picture pointer */
740     picture_t *     p_last_picture = NULL;                   /* last picture */
741     picture_t *     p_directbuffer;              /* direct buffer to display */
742
743     subpicture_t *  p_subpic = NULL;                   /* subpicture pointer */
744
745     input_thread_t *p_input = NULL ;           /* Parent input, if it exists */
746
747     vlc_value_t     val;
748     vlc_bool_t      b_drop_late;
749
750     int             i_displayed = 0, i_lost = 0, i_loops = 0;
751
752     /*
753      * Initialize thread
754      */
755     p_vout->b_error = InitThread( p_vout );
756
757     var_Create( p_vout, "drop-late-frames", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
758     var_Get( p_vout, "drop-late-frames", &val );
759     b_drop_late = val.b_bool;
760
761     /* signal the creation of the vout */
762     vlc_thread_ready( p_vout );
763
764     if( p_vout->b_error )
765         return;
766
767     if( p_vout->b_title_show )
768         DisplayTitleOnOSD( p_vout );
769
770     /*
771      * Main loop - it is not executed if an error occurred during
772      * initialization
773      */
774     while( (!p_vout->b_die) && (!p_vout->b_error) )
775     {
776         /* Initialize loop variables */
777         p_picture = NULL;
778         display_date = 0;
779         current_date = mdate();
780
781         if( p_input && p_input->b_die )
782         {
783             vlc_object_release( p_input );
784             p_input = NULL;
785         }
786
787         i_loops++;
788             if( !p_input )
789             {
790                 p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT,
791                                            FIND_PARENT );
792             }
793             if( p_input )
794             {
795                 vlc_mutex_lock( &p_input->p->counters.counters_lock );
796                 stats_UpdateInteger( p_vout, p_input->p->counters.p_lost_pictures,
797                                      i_lost , NULL);
798                 stats_UpdateInteger( p_vout,
799                                      p_input->p->counters.p_displayed_pictures,
800                                      i_displayed , NULL);
801                 i_displayed = i_lost = 0;
802                 vlc_mutex_unlock( &p_input->p->counters.counters_lock );
803             }
804 #if 0
805         p_vout->c_loops++;
806         if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
807         {
808             msg_Dbg( p_vout, "picture heap: %d/%d",
809                      I_RENDERPICTURES, p_vout->i_heap_size );
810         }
811 #endif
812
813         /*
814          * Find the picture to display (the one with the earliest date).
815          * This operation does not need lock, since only READY_PICTUREs
816          * are handled. */
817         for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
818         {
819             if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
820                 && ( (p_picture == NULL) ||
821                      (PP_RENDERPICTURE[i_index]->date < display_date) ) )
822             {
823                 p_picture = PP_RENDERPICTURE[i_index];
824                 display_date = p_picture->date;
825             }
826         }
827
828         if( p_picture )
829         {
830             /* If we met the last picture, parse again to see whether there is
831              * a more appropriate one. */
832             if( p_picture == p_last_picture )
833             {
834                 for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
835                 {
836                     if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
837                         && (PP_RENDERPICTURE[i_index] != p_last_picture)
838                         && ((p_picture == p_last_picture) ||
839                             (PP_RENDERPICTURE[i_index]->date < display_date)) )
840                     {
841                         p_picture = PP_RENDERPICTURE[i_index];
842                         display_date = p_picture->date;
843                     }
844                 }
845             }
846
847             /* If we found better than the last picture, destroy it */
848             if( p_last_picture && p_picture != p_last_picture )
849             {
850                 vlc_mutex_lock( &p_vout->picture_lock );
851                 if( p_last_picture->i_refcount )
852                 {
853                     p_last_picture->i_status = DISPLAYED_PICTURE;
854                 }
855                 else
856                 {
857                     p_last_picture->i_status = DESTROYED_PICTURE;
858                     p_vout->i_heap_size--;
859                 }
860                 vlc_mutex_unlock( &p_vout->picture_lock );
861                 p_last_picture = NULL;
862             }
863
864             /* Compute FPS rate */
865             p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ]
866                 = display_date;
867
868             if( !p_picture->b_force &&
869                 p_picture != p_last_picture &&
870                 display_date < current_date + p_vout->render_time &&
871                 b_drop_late )
872             {
873                 /* Picture is late: it will be destroyed and the thread
874                  * will directly choose the next picture */
875                 vlc_mutex_lock( &p_vout->picture_lock );
876                 if( p_picture->i_refcount )
877                 {
878                     /* Pretend we displayed the picture, but don't destroy
879                      * it since the decoder might still need it. */
880                     p_picture->i_status = DISPLAYED_PICTURE;
881                 }
882                 else
883                 {
884                     /* Destroy the picture without displaying it */
885                     p_picture->i_status = DESTROYED_PICTURE;
886                     p_vout->i_heap_size--;
887                 }
888                 msg_Warn( p_vout, "late picture skipped ("I64Fd")",
889                                   current_date - display_date );
890                 i_lost++;
891                 vlc_mutex_unlock( &p_vout->picture_lock );
892
893                 continue;
894             }
895
896             if( display_date >
897                 current_date + p_vout->i_pts_delay + VOUT_BOGUS_DELAY )
898             {
899                 /* Picture is waaay too early: it will be destroyed */
900                 vlc_mutex_lock( &p_vout->picture_lock );
901                 if( p_picture->i_refcount )
902                 {
903                     /* Pretend we displayed the picture, but don't destroy
904                      * it since the decoder might still need it. */
905                     p_picture->i_status = DISPLAYED_PICTURE;
906                 }
907                 else
908                 {
909                     /* Destroy the picture without displaying it */
910                     p_picture->i_status = DESTROYED_PICTURE;
911                     p_vout->i_heap_size--;
912                 }
913                 i_lost++;
914                 msg_Warn( p_vout, "vout warning: early picture skipped "
915                           "("I64Fd")", display_date - current_date
916                           - p_vout->i_pts_delay );
917                 vlc_mutex_unlock( &p_vout->picture_lock );
918
919                 continue;
920             }
921
922             if( display_date > current_date + VOUT_DISPLAY_DELAY )
923             {
924                 /* A picture is ready to be rendered, but its rendering date
925                  * is far from the current one so the thread will perform an
926                  * empty loop as if no picture were found. The picture state
927                  * is unchanged */
928                 p_picture    = NULL;
929                 display_date = 0;
930             }
931             else if( p_picture == p_last_picture )
932             {
933                 /* We are asked to repeat the previous picture, but we first
934                  * wait for a couple of idle loops */
935                 if( i_idle_loops < 4 )
936                 {
937                     p_picture    = NULL;
938                     display_date = 0;
939                 }
940                 else
941                 {
942                     /* We set the display date to something high, otherwise
943                      * we'll have lots of problems with late pictures */
944                     display_date = current_date + p_vout->render_time;
945                 }
946             }
947         }
948
949         if( p_picture == NULL )
950         {
951             i_idle_loops++;
952         }
953
954         /* Video Filter2 stuff */
955         if( p_vout->b_vfilter_change == VLC_TRUE )
956         {
957             int i;
958             vlc_mutex_lock( &p_vout->vfilter_lock );
959             RemoveVideoFilters2( p_vout );
960             for( i = 0; i < p_vout->i_vfilters_cfg; i++ )
961             {
962                 filter_t *p_vfilter =
963                     p_vout->pp_vfilters[p_vout->i_vfilters] =
964                         vlc_object_create( p_vout, VLC_OBJECT_FILTER );
965
966                 vlc_object_attach( p_vfilter, p_vout );
967
968                 p_vfilter->pf_vout_buffer_new = video_new_buffer_filter;
969                 p_vfilter->pf_vout_buffer_del = video_del_buffer_filter;
970
971                 if( !p_vout->i_vfilters )
972                 {
973                     p_vfilter->fmt_in.video = p_vout->fmt_render;
974                 }
975                 else
976                 {
977                     p_vfilter->fmt_in.video = (p_vfilter-1)->fmt_out.video;
978                 }
979                 /* TODO: one day filters in the middle of the chain might
980                  * have a different fmt_out.video than fmt_render ... */
981                 p_vfilter->fmt_out.video = p_vout->fmt_render;
982
983                 p_vfilter->p_cfg = p_vout->p_vfilters_cfg[i];
984                 p_vfilter->p_module = module_Need( p_vfilter, "video filter2",
985                                                    p_vout->psz_vfilters[i],
986                                                    VLC_TRUE );
987
988                 if( p_vfilter->p_module )
989                 {
990                     p_vfilter->p_owner =
991                         malloc( sizeof( filter_owner_sys_t ) );
992                     p_vfilter->p_owner->p_vout = p_vout;
993                     p_vout->i_vfilters++;
994                     msg_Dbg( p_vout, "video filter found (%s)",
995                              p_vout->psz_vfilters[i] );
996                 }
997                 else
998                 {
999                     msg_Err( p_vout, "no video filter found (%s)",
1000                              p_vout->psz_vfilters[i] );
1001                     vlc_object_detach( p_vfilter );
1002                     vlc_object_release( p_vfilter );
1003                 }
1004             }
1005             p_vout->b_vfilter_change = VLC_FALSE;
1006             vlc_mutex_unlock( &p_vout->vfilter_lock );
1007         }
1008
1009         if( p_picture )
1010         {
1011             int i;
1012             for( i = 0; i < p_vout->i_vfilters; i++ )
1013             {
1014                 picture_t *p_old = p_picture;
1015                 p_picture  = p_vout->pp_vfilters[i]->pf_video_filter(
1016                                  p_vout->pp_vfilters[i], p_picture );
1017                 if( !p_picture )
1018                 {
1019                     break;
1020                 }
1021                 /* FIXME: this is kind of wrong
1022                  * if you have 2 or more vfilters and the 2nd breaks,
1023                  * on the next loop the 1st one will be applied again */
1024
1025                 /* if p_old and p_picture are the same (ie the filter
1026                  * worked on the old picture), then following code is
1027                  * still alright since i_status gets changed back to
1028                  * the right value */
1029                 if( p_old->i_refcount )
1030                 {
1031                     p_old->i_status = DISPLAYED_PICTURE;
1032                 }
1033                 else
1034                 {
1035                     p_old->i_status = DESTROYED_PICTURE;
1036                 }
1037                 p_picture->i_status = READY_PICTURE;
1038             }
1039         }
1040
1041         if( p_picture && p_vout->b_snapshot )
1042         {
1043             p_vout->b_snapshot = VLC_FALSE;
1044             vout_Snapshot( p_vout, p_picture );
1045         }
1046
1047         /*
1048          * Check for subpictures to display
1049          */
1050         if( display_date > 0 )
1051         {
1052             if( !p_input )
1053             {
1054                 p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT,
1055                                            FIND_PARENT );
1056             }
1057             p_subpic = spu_SortSubpictures( p_vout->p_spu, display_date,
1058             p_input ? var_GetBool( p_input, "state" ) == PAUSE_S : VLC_FALSE );
1059         }
1060
1061         /*
1062          * Perform rendering
1063          */
1064         i_displayed++;
1065         p_directbuffer = vout_RenderPicture( p_vout, p_picture, p_subpic );
1066
1067         /*
1068          * Call the plugin-specific rendering method if there is one
1069          */
1070         if( p_picture != NULL && p_directbuffer != NULL && p_vout->pf_render )
1071         {
1072             /* Render the direct buffer returned by vout_RenderPicture */
1073             p_vout->pf_render( p_vout, p_directbuffer );
1074         }
1075
1076         /*
1077          * Sleep, wake up
1078          */
1079         if( display_date != 0 && p_directbuffer != NULL )
1080         {
1081             mtime_t current_render_time = mdate() - current_date;
1082             /* if render time is very large we don't include it in the mean */
1083             if( current_render_time < p_vout->render_time +
1084                 VOUT_DISPLAY_DELAY )
1085             {
1086                 /* Store render time using a sliding mean weighting to
1087                  * current value in a 3 to 1 ratio*/
1088                 p_vout->render_time *= 3;
1089                 p_vout->render_time += current_render_time;
1090                 p_vout->render_time >>= 2;
1091             }
1092         }
1093
1094         /* Give back change lock */
1095         vlc_mutex_unlock( &p_vout->change_lock );
1096
1097         /* Sleep a while or until a given date */
1098         if( display_date != 0 )
1099         {
1100             /* If there are filters in the chain, better give them the picture
1101              * in advance */
1102             if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain )
1103             {
1104                 mwait( display_date - VOUT_MWAIT_TOLERANCE );
1105             }
1106         }
1107         else
1108         {
1109             msleep( VOUT_IDLE_SLEEP );
1110         }
1111
1112         /* On awakening, take back lock and send immediately picture
1113          * to display. */
1114         vlc_mutex_lock( &p_vout->change_lock );
1115
1116         /*
1117          * Display the previously rendered picture
1118          */
1119         if( p_picture != NULL && p_directbuffer != NULL )
1120         {
1121             /* Display the direct buffer returned by vout_RenderPicture */
1122             if( p_vout->pf_display )
1123             {
1124                 p_vout->pf_display( p_vout, p_directbuffer );
1125             }
1126
1127             /* Tell the vout this was the last picture and that it does not
1128              * need to be forced anymore. */
1129             p_last_picture = p_picture;
1130             p_last_picture->b_force = 0;
1131         }
1132
1133         if( p_picture != NULL )
1134         {
1135             /* Reinitialize idle loop count */
1136             i_idle_loops = 0;
1137         }
1138
1139         /*
1140          * Check events and manage thread
1141          */
1142         if( p_vout->pf_manage && p_vout->pf_manage( p_vout ) )
1143         {
1144             /* A fatal error occurred, and the thread must terminate
1145              * immediately, without displaying anything - setting b_error to 1
1146              * causes the immediate end of the main while() loop. */
1147             p_vout->b_error = 1;
1148         }
1149
1150         if( p_vout->i_changes & VOUT_SIZE_CHANGE )
1151         {
1152             /* this must only happen when the vout plugin is incapable of
1153              * rescaling the picture itself. In this case we need to destroy
1154              * the current picture buffers and recreate new ones with the right
1155              * dimensions */
1156             int i;
1157
1158             p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
1159
1160             p_vout->pf_end( p_vout );
1161             for( i = 0; i < I_OUTPUTPICTURES; i++ )
1162                  p_vout->p_picture[ i ].i_status = FREE_PICTURE;
1163
1164             I_OUTPUTPICTURES = 0;
1165             if( p_vout->pf_init( p_vout ) )
1166             {
1167                 msg_Err( p_vout, "cannot resize display" );
1168                 /* FIXME: pf_end will be called again in EndThread() */
1169                 p_vout->b_error = 1;
1170             }
1171
1172             /* Need to reinitialise the chroma plugin */
1173             if( p_vout->chroma.p_module )
1174             {
1175                 if( p_vout->chroma.p_module->pf_deactivate )
1176                     p_vout->chroma.p_module->pf_deactivate( VLC_OBJECT(p_vout) );
1177                 p_vout->chroma.p_module->pf_activate( VLC_OBJECT(p_vout) );
1178             }
1179         }
1180
1181         if( p_vout->i_changes & VOUT_PICTURE_BUFFERS_CHANGE )
1182         {
1183             /* This happens when the picture buffers need to be recreated.
1184              * This is useful on multimonitor displays for instance.
1185              *
1186              * Warning: This only works when the vout creates only 1 picture
1187              * buffer!! */
1188             p_vout->i_changes &= ~VOUT_PICTURE_BUFFERS_CHANGE;
1189
1190             if( !p_vout->b_direct )
1191             {
1192                 module_Unneed( p_vout, p_vout->chroma.p_module );
1193             }
1194
1195             vlc_mutex_lock( &p_vout->picture_lock );
1196
1197             p_vout->pf_end( p_vout );
1198
1199             I_OUTPUTPICTURES = I_RENDERPICTURES = 0;
1200
1201             p_vout->b_error = InitThread( p_vout );
1202
1203             vlc_mutex_unlock( &p_vout->picture_lock );
1204         }
1205     }
1206
1207     if( p_input )
1208     {
1209         vlc_object_release( p_input );
1210     }
1211
1212     /*
1213      * Error loop - wait until the thread destruction is requested
1214      */
1215     if( p_vout->b_error )
1216     {
1217         ErrorThread( p_vout );
1218     }
1219
1220     /* End of thread */
1221     EndThread( p_vout );
1222 }
1223
1224 /*****************************************************************************
1225  * ErrorThread: RunThread() error loop
1226  *****************************************************************************
1227  * This function is called when an error occurred during thread main's loop.
1228  * The thread can still receive feed, but must be ready to terminate as soon
1229  * as possible.
1230  *****************************************************************************/
1231 static void ErrorThread( vout_thread_t *p_vout )
1232 {
1233     /* Wait until a `die' order */
1234     while( !p_vout->b_die )
1235     {
1236         /* Sleep a while */
1237         msleep( VOUT_IDLE_SLEEP );
1238     }
1239 }
1240
1241 /*****************************************************************************
1242  * EndThread: thread destruction
1243  *****************************************************************************
1244  * This function is called when the thread ends after a sucessful
1245  * initialization. It frees all resources allocated by InitThread.
1246  *****************************************************************************/
1247 static void EndThread( vout_thread_t *p_vout )
1248 {
1249     int     i_index;                                        /* index in heap */
1250
1251 #ifdef STATS
1252     {
1253         struct tms cpu_usage;
1254         times( &cpu_usage );
1255
1256         msg_Dbg( p_vout, "cpu usage (user: %d, system: %d)",
1257                  cpu_usage.tms_utime, cpu_usage.tms_stime );
1258     }
1259 #endif
1260
1261     if( !p_vout->b_direct )
1262     {
1263         module_Unneed( p_vout, p_vout->chroma.p_module );
1264     }
1265
1266     /* Destroy all remaining pictures */
1267     for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++ )
1268     {
1269         if ( p_vout->p_picture[i_index].i_type == MEMORY_PICTURE )
1270         {
1271             free( p_vout->p_picture[i_index].p_data_orig );
1272         }
1273     }
1274
1275     /* Destroy subpicture unit */
1276     spu_Attach( p_vout->p_spu, VLC_OBJECT(p_vout), VLC_FALSE );
1277     spu_Destroy( p_vout->p_spu );
1278
1279     /* Destroy the video filters2 */
1280     RemoveVideoFilters2( p_vout );
1281
1282     /* Destroy translation tables */
1283     p_vout->pf_end( p_vout );
1284
1285     /* Release the change lock */
1286     vlc_mutex_unlock( &p_vout->change_lock );
1287 }
1288
1289 /* following functions are local */
1290
1291 static int ReduceHeight( int i_ratio )
1292 {
1293     int i_dummy = VOUT_ASPECT_FACTOR;
1294     int i_pgcd  = 1;
1295
1296     if( !i_ratio )
1297     {
1298         return i_pgcd;
1299     }
1300
1301     /* VOUT_ASPECT_FACTOR is (2^7 * 3^3 * 5^3), we just check for 2, 3 and 5 */
1302     while( !(i_ratio & 1) && !(i_dummy & 1) )
1303     {
1304         i_ratio >>= 1;
1305         i_dummy >>= 1;
1306         i_pgcd  <<= 1;
1307     }
1308
1309     while( !(i_ratio % 3) && !(i_dummy % 3) )
1310     {
1311         i_ratio /= 3;
1312         i_dummy /= 3;
1313         i_pgcd  *= 3;
1314     }
1315
1316     while( !(i_ratio % 5) && !(i_dummy % 5) )
1317     {
1318         i_ratio /= 5;
1319         i_dummy /= 5;
1320         i_pgcd  *= 5;
1321     }
1322
1323     return i_pgcd;
1324 }
1325
1326 static void AspectRatio( int i_aspect, int *i_aspect_x, int *i_aspect_y )
1327 {
1328     unsigned int i_pgcd = ReduceHeight( i_aspect );
1329     *i_aspect_x = i_aspect / i_pgcd;
1330     *i_aspect_y = VOUT_ASPECT_FACTOR / i_pgcd;
1331 }
1332
1333 /*****************************************************************************
1334  * BinaryLog: computes the base 2 log of a binary value
1335  *****************************************************************************
1336  * This functions is used by MaskToShift, to get a bit index from a binary
1337  * value.
1338  *****************************************************************************/
1339 static int BinaryLog( uint32_t i )
1340 {
1341     int i_log = 0;
1342
1343     if( i == 0 ) return -31337;
1344
1345     if( i & 0xffff0000 ) i_log += 16;
1346     if( i & 0xff00ff00 ) i_log += 8;
1347     if( i & 0xf0f0f0f0 ) i_log += 4;
1348     if( i & 0xcccccccc ) i_log += 2;
1349     if( i & 0xaaaaaaaa ) i_log += 1;
1350
1351     return i_log;
1352 }
1353
1354 /*****************************************************************************
1355  * MaskToShift: transform a color mask into right and left shifts
1356  *****************************************************************************
1357  * This function is used for obtaining color shifts from masks.
1358  *****************************************************************************/
1359 static void MaskToShift( int *pi_left, int *pi_right, uint32_t i_mask )
1360 {
1361     uint32_t i_low, i_high;            /* lower hand higher bits of the mask */
1362
1363     if( !i_mask )
1364     {
1365         *pi_left = *pi_right = 0;
1366         return;
1367     }
1368
1369     /* Get bits */
1370     i_low = i_high = i_mask;
1371
1372     i_low &= - (int32_t)i_low;          /* lower bit of the mask */
1373     i_high += i_low;                    /* higher bit of the mask */
1374
1375     /* Transform bits into an index. Also deal with i_high overflow, which
1376      * is faster than changing the BinaryLog code to handle 64 bit integers. */
1377     i_low =  BinaryLog (i_low);
1378     i_high = i_high ? BinaryLog (i_high) : 32;
1379
1380     /* Update pointers and return */
1381     *pi_left =   i_low;
1382     *pi_right = (8 - i_high + i_low);
1383 }
1384
1385 /*****************************************************************************
1386  * vout_VarCallback: generic callback for intf variables
1387  *****************************************************************************/
1388 int vout_VarCallback( vlc_object_t * p_this, const char * psz_variable,
1389                       vlc_value_t oldval, vlc_value_t newval,
1390                       void *p_data )
1391 {
1392     vout_thread_t * p_vout = (vout_thread_t *)p_this;
1393     vlc_value_t val;
1394     (void)psz_variable; (void)newval; (void)oldval; (void)p_data;
1395     val.b_bool = VLC_TRUE;
1396     var_Set( p_vout, "intf-change", val );
1397     return VLC_SUCCESS;
1398 }
1399
1400 /*****************************************************************************
1401  * Helper thread for object variables callbacks.
1402  * Only used to avoid deadlocks when using the video embedded mode.
1403  *****************************************************************************/
1404 typedef struct suxor_thread_t
1405 {
1406     VLC_COMMON_MEMBERS
1407     input_thread_t *p_input;
1408
1409 } suxor_thread_t;
1410
1411 static void SuxorRestartVideoES( suxor_thread_t *p_this )
1412 {
1413     vlc_value_t val;
1414
1415     vlc_thread_ready( p_this );
1416
1417     /* Now restart current video stream */
1418     var_Get( p_this->p_input, "video-es", &val );
1419     if( val.i_int >= 0 )
1420     {
1421         vlc_value_t val_es;
1422         val_es.i_int = -VIDEO_ES;
1423         var_Set( p_this->p_input, "video-es", val_es );
1424         var_Set( p_this->p_input, "video-es", val );
1425     }
1426
1427     vlc_object_release( p_this->p_input );
1428
1429     vlc_object_release( p_this );
1430 }
1431
1432 /*****************************************************************************
1433  * object variables callbacks: a bunch of object variables are used by the
1434  * interfaces to interact with the vout.
1435  *****************************************************************************/
1436 static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd,
1437                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1438 {
1439     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1440     input_thread_t *p_input;
1441     vlc_value_t val;
1442
1443     char *psz_mode = newval.psz_string;
1444     char *psz_filter, *psz_deinterlace = NULL;
1445     (void)psz_cmd; (void)oldval; (void)p_data;
1446
1447     var_Get( p_vout, "vout-filter", &val );
1448     psz_filter = val.psz_string;
1449     if( psz_filter ) psz_deinterlace = strstr( psz_filter, "deinterlace" );
1450
1451     if( !psz_mode || !*psz_mode )
1452     {
1453         if( psz_deinterlace )
1454         {
1455             char *psz_src = psz_deinterlace + sizeof("deinterlace") - 1;
1456             if( psz_src[0] == ':' ) psz_src++;
1457             memmove( psz_deinterlace, psz_src, strlen(psz_src) + 1 );
1458         }
1459     }
1460     else if( !psz_deinterlace )
1461     {
1462         psz_filter = realloc( psz_filter, strlen( psz_filter ) +
1463                               sizeof(":deinterlace") );
1464         if( psz_filter && *psz_filter ) strcat( psz_filter, ":" );
1465         strcat( psz_filter, "deinterlace" );
1466     }
1467
1468     p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1469                                                  FIND_PARENT );
1470     if( !p_input ) return VLC_EGENERIC;
1471
1472     if( psz_mode && *psz_mode )
1473     {
1474         /* Modify input as well because the vout might have to be restarted */
1475         val.psz_string = psz_mode;
1476         var_Create( p_input, "deinterlace-mode", VLC_VAR_STRING );
1477         var_Set( p_input, "deinterlace-mode", val );
1478     }
1479     vlc_object_release( p_input );
1480
1481     val.b_bool = VLC_TRUE;
1482     var_Set( p_vout, "intf-change", val );
1483
1484     val.psz_string = psz_filter;
1485     var_Set( p_vout, "vout-filter", val );
1486     free( psz_filter );
1487
1488     return VLC_SUCCESS;
1489 }
1490
1491 static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd,
1492                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1493 {
1494     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1495     input_thread_t *p_input;
1496     vlc_value_t val;
1497     (void)psz_cmd; (void)oldval; (void)p_data;
1498
1499     p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1500                                                  FIND_PARENT );
1501     if (!p_input)
1502     {
1503         msg_Err( p_vout, "Input not found" );
1504         return( VLC_EGENERIC );
1505     }
1506
1507     val.b_bool = VLC_TRUE;
1508     var_Set( p_vout, "intf-change", val );
1509
1510     /* Modify input as well because the vout might have to be restarted */
1511     val.psz_string = newval.psz_string;
1512     var_Create( p_input, "vout-filter", VLC_VAR_STRING );
1513
1514     var_Set( p_input, "vout-filter", val );
1515
1516     /* Now restart current video stream */
1517     var_Get( p_input, "video-es", &val );
1518     if( val.i_int >= 0 )
1519     {
1520         suxor_thread_t *p_suxor =
1521             vlc_object_create( p_vout, sizeof(suxor_thread_t) );
1522         p_suxor->p_input = p_input;
1523         p_vout->b_filter_change = VLC_TRUE;
1524         vlc_object_yield( p_input );
1525         vlc_thread_create( p_suxor, "suxor", SuxorRestartVideoES,
1526                            VLC_THREAD_PRIORITY_LOW, VLC_FALSE );
1527     }
1528
1529     vlc_object_release( p_input );
1530
1531     return VLC_SUCCESS;
1532 }
1533
1534 /*****************************************************************************
1535  * Video Filter2 stuff
1536  *****************************************************************************/
1537 static int ParseVideoFilter2Chain( vout_thread_t *p_vout, char *psz_vfilters )
1538 {
1539     int i;
1540     for( i = 0; i < p_vout->i_vfilters_cfg; i++ )
1541     {
1542         struct config_chain_t *p_cfg =
1543             p_vout->p_vfilters_cfg[p_vout->i_vfilters_cfg];
1544         config_ChainDestroy( p_cfg );
1545         free( p_vout->psz_vfilters[p_vout->i_vfilters_cfg] );
1546         p_vout->psz_vfilters[p_vout->i_vfilters_cfg] = NULL;
1547     }
1548     p_vout->i_vfilters_cfg = 0;
1549     if( psz_vfilters && *psz_vfilters )
1550     {
1551         char *psz_parser = psz_vfilters;
1552
1553         while( psz_parser && *psz_parser )
1554         {
1555             psz_parser = config_ChainCreate(
1556                             &p_vout->psz_vfilters[p_vout->i_vfilters_cfg],
1557                             &p_vout->p_vfilters_cfg[p_vout->i_vfilters_cfg],
1558                             psz_parser );
1559             msg_Dbg( p_vout, "adding vfilter: %s",
1560                      p_vout->psz_vfilters[p_vout->i_vfilters_cfg] );
1561             p_vout->i_vfilters_cfg++;
1562             if( psz_parser && *psz_parser )
1563             {
1564                 if( p_vout->i_vfilters_cfg == MAX_VFILTERS )
1565                 {
1566                     msg_Warn( p_vout,
1567                   "maximum number of video filters reached. \"%s\" discarded",
1568                               psz_parser );
1569                     break;
1570                 }
1571             }
1572         }
1573     }
1574     return VLC_SUCCESS;
1575 }
1576
1577 static int VideoFilter2Callback( vlc_object_t *p_this, char const *psz_cmd,
1578                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1579 {
1580     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1581     (void)psz_cmd; (void)oldval; (void)p_data;
1582
1583     vlc_mutex_lock( &p_vout->vfilter_lock );
1584     ParseVideoFilter2Chain( p_vout, newval.psz_string );
1585     p_vout->b_vfilter_change = VLC_TRUE;
1586     vlc_mutex_unlock( &p_vout->vfilter_lock );
1587
1588     return VLC_SUCCESS;
1589 }
1590
1591 static void RemoveVideoFilters2( vout_thread_t *p_vout )
1592 {
1593     int i;
1594     for( i = 0; i < p_vout->i_vfilters; i++ )
1595     {
1596         vlc_object_detach( p_vout->pp_vfilters[i] );
1597         if( p_vout->pp_vfilters[i]->p_module )
1598         {
1599             module_Unneed( p_vout->pp_vfilters[i],
1600                            p_vout->pp_vfilters[i]->p_module );
1601         }
1602
1603         free( p_vout->pp_vfilters[i]->p_owner );
1604         vlc_object_release( p_vout->pp_vfilters[i] );
1605     }
1606     p_vout->i_vfilters = 0;
1607 }
1608
1609 static void DisplayTitleOnOSD( vout_thread_t *p_vout )
1610 {
1611     input_thread_t *p_input;
1612     mtime_t i_now, i_stop;
1613
1614     p_input = (input_thread_t *)vlc_object_find( p_vout,
1615               VLC_OBJECT_INPUT, FIND_ANYWHERE );
1616     if( p_input )
1617     {
1618         i_now = mdate();
1619         i_stop = i_now + (mtime_t)(p_vout->i_title_timeout * 1000);
1620         char *psz_nowplaying =
1621             input_item_GetNowPlaying( input_GetItem( p_input ) );
1622         char *psz_artist = input_item_GetArtist( input_GetItem( p_input ) );
1623         char *psz_name = input_item_GetTitle( input_GetItem( p_input ) );
1624         if( EMPTY_STR( psz_name ) )
1625         {
1626             free( psz_name );
1627             psz_name = input_item_GetName( input_GetItem( p_input ) );
1628         }
1629         if( !EMPTY_STR( psz_nowplaying ) )
1630         {
1631             vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1632                                    psz_nowplaying, NULL,
1633                                    p_vout->i_title_position,
1634                                    30 + p_vout->fmt_in.i_width
1635                                       - p_vout->fmt_in.i_visible_width
1636                                       - p_vout->fmt_in.i_x_offset,
1637                                    20 + p_vout->fmt_in.i_y_offset,
1638                                    i_now, i_stop );
1639         }
1640         else if( !EMPTY_STR( psz_artist ) )
1641         {
1642             char *psz_string = NULL;
1643             if( asprintf( &psz_string, "%s - %s", psz_name, psz_artist ) != -1 )
1644             {
1645                 vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1646                                        psz_string, NULL,
1647                                        p_vout->i_title_position,
1648                                        30 + p_vout->fmt_in.i_width
1649                                           - p_vout->fmt_in.i_visible_width
1650                                           - p_vout->fmt_in.i_x_offset,
1651                                        20 + p_vout->fmt_in.i_y_offset,
1652                                        i_now, i_stop );
1653                 free( psz_string );
1654             }
1655         }
1656         else
1657         {
1658             vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1659                                    psz_name, NULL,
1660                                    p_vout->i_title_position,
1661                                    30 + p_vout->fmt_in.i_width
1662                                       - p_vout->fmt_in.i_visible_width
1663                                       - p_vout->fmt_in.i_x_offset,
1664                                    20 + p_vout->fmt_in.i_y_offset,
1665                                    i_now, i_stop );
1666         }
1667         vlc_object_release( p_input );
1668         free( psz_artist );
1669         free( psz_name );
1670         free( psz_nowplaying );
1671     }
1672 }
1673