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