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