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