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