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