]> git.sesse.net Git - vlc/blob - src/video_output/video_output.c
Include modules.h whenever needed
[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         p_vout->b_die = VLC_TRUE;
465
466         vlc_thread_join( p_vout );
467
468         vlc_object_detach( p_vout );
469         vlc_object_destroy( p_vout );
470         return NULL;
471     }
472
473     return p_vout;
474 }
475
476 /*****************************************************************************
477  * vout_Destroy: destroys a previously created video output
478  *****************************************************************************
479  * Destroy a terminated thread.
480  * The function will request a destruction of the specified thread. If pi_error
481  * is NULL, it will return once the thread is destroyed. Else, it will be
482  * update using one of the THREAD_* constants.
483  *****************************************************************************/
484 void vout_Destroy( vout_thread_t *p_vout )
485 {
486     vout_thread_t *p_another_vout;
487     playlist_t *p_playlist = pl_Yield( p_vout );
488
489     /* Request thread destruction */
490     p_vout->b_die = VLC_TRUE;
491     vlc_thread_join( p_vout );
492
493     var_Destroy( p_vout, "intf-change" );
494
495     if( p_vout->psz_filter_chain ) free( p_vout->psz_filter_chain );
496
497     config_ChainDestroy( p_vout->p_cfg );
498
499     /* Free structure */
500     vlc_object_destroy( p_vout );
501 #ifndef __APPLE__
502     /* This is a dirty hack for mostly Linux, where there is no way to get the GUI
503        back if you closed it while playing video. This is solved in Mac OS X,
504        where we have this novelty called menubar, that will always allow you access
505        to the applications main functionality. They should try that on linux sometime */
506     p_another_vout = vlc_object_find( p_playlist,
507                                       VLC_OBJECT_VOUT, FIND_ANYWHERE );
508     if( p_another_vout == NULL )
509     {
510         vlc_value_t val;
511         val.b_bool = VLC_TRUE;
512         var_Set( p_playlist, "intf-show", val );
513     }
514     else
515     {
516         vlc_object_release( p_another_vout );
517     }
518 #endif
519     vlc_object_release( p_playlist );
520 }
521
522 /*****************************************************************************
523  * InitThread: initialize video output thread
524  *****************************************************************************
525  * This function is called from RunThread and performs the second step of the
526  * initialization. It returns 0 on success. Note that the thread's flag are not
527  * modified inside this function.
528  *****************************************************************************/
529 static int InitThread( vout_thread_t *p_vout )
530 {
531     int i, i_aspect_x, i_aspect_y;
532
533     vlc_mutex_lock( &p_vout->change_lock );
534
535 #ifdef STATS
536     p_vout->c_loops = 0;
537 #endif
538
539     /* Initialize output method, it allocates direct buffers for us */
540     if( p_vout->pf_init( p_vout ) )
541     {
542         vlc_mutex_unlock( &p_vout->change_lock );
543         return VLC_EGENERIC;
544     }
545
546     if( !I_OUTPUTPICTURES )
547     {
548         msg_Err( p_vout, "plugin was unable to allocate at least "
549                          "one direct buffer" );
550         p_vout->pf_end( p_vout );
551         vlc_mutex_unlock( &p_vout->change_lock );
552         return VLC_EGENERIC;
553     }
554
555     if( I_OUTPUTPICTURES > VOUT_MAX_PICTURES )
556     {
557         msg_Err( p_vout, "plugin allocated too many direct buffers, "
558                          "our internal buffers must have overflown." );
559         p_vout->pf_end( p_vout );
560         vlc_mutex_unlock( &p_vout->change_lock );
561         return VLC_EGENERIC;
562     }
563
564     msg_Dbg( p_vout, "got %i direct buffer(s)", I_OUTPUTPICTURES );
565
566     AspectRatio( p_vout->fmt_render.i_aspect, &i_aspect_x, &i_aspect_y );
567
568     msg_Dbg( p_vout, "picture in %ix%i (%i,%i,%ix%i), "
569              "chroma %4.4s, ar %i:%i, sar %i:%i",
570              p_vout->fmt_render.i_width, p_vout->fmt_render.i_height,
571              p_vout->fmt_render.i_x_offset, p_vout->fmt_render.i_y_offset,
572              p_vout->fmt_render.i_visible_width,
573              p_vout->fmt_render.i_visible_height,
574              (char*)&p_vout->fmt_render.i_chroma,
575              i_aspect_x, i_aspect_y,
576              p_vout->fmt_render.i_sar_num, p_vout->fmt_render.i_sar_den );
577
578     AspectRatio( p_vout->fmt_in.i_aspect, &i_aspect_x, &i_aspect_y );
579
580     msg_Dbg( p_vout, "picture user %ix%i (%i,%i,%ix%i), "
581              "chroma %4.4s, ar %i:%i, sar %i:%i",
582              p_vout->fmt_in.i_width, p_vout->fmt_in.i_height,
583              p_vout->fmt_in.i_x_offset, p_vout->fmt_in.i_y_offset,
584              p_vout->fmt_in.i_visible_width,
585              p_vout->fmt_in.i_visible_height,
586              (char*)&p_vout->fmt_in.i_chroma,
587              i_aspect_x, i_aspect_y,
588              p_vout->fmt_in.i_sar_num, p_vout->fmt_in.i_sar_den );
589
590     if( !p_vout->fmt_out.i_width || !p_vout->fmt_out.i_height )
591     {
592         p_vout->fmt_out.i_width = p_vout->fmt_out.i_visible_width =
593             p_vout->output.i_width;
594         p_vout->fmt_out.i_height = p_vout->fmt_out.i_visible_height =
595             p_vout->output.i_height;
596         p_vout->fmt_out.i_x_offset =  p_vout->fmt_out.i_y_offset = 0;
597
598         p_vout->fmt_out.i_aspect = p_vout->output.i_aspect;
599         p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
600     }
601     if( !p_vout->fmt_out.i_sar_num || !p_vout->fmt_out.i_sar_num )
602     {
603         p_vout->fmt_out.i_sar_num = p_vout->fmt_out.i_aspect *
604             p_vout->fmt_out.i_height;
605         p_vout->fmt_out.i_sar_den = VOUT_ASPECT_FACTOR *
606             p_vout->fmt_out.i_width;
607     }
608
609     vlc_ureduce( &p_vout->fmt_out.i_sar_num, &p_vout->fmt_out.i_sar_den,
610                  p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den, 0 );
611
612     AspectRatio( p_vout->fmt_out.i_aspect, &i_aspect_x, &i_aspect_y );
613
614     msg_Dbg( p_vout, "picture out %ix%i (%i,%i,%ix%i), "
615              "chroma %4.4s, ar %i:%i, sar %i:%i",
616              p_vout->fmt_out.i_width, p_vout->fmt_out.i_height,
617              p_vout->fmt_out.i_x_offset, p_vout->fmt_out.i_y_offset,
618              p_vout->fmt_out.i_visible_width,
619              p_vout->fmt_out.i_visible_height,
620              (char*)&p_vout->fmt_out.i_chroma,
621              i_aspect_x, i_aspect_y,
622              p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den );
623
624     /* Calculate shifts from system-updated masks */
625     MaskToShift( &p_vout->output.i_lrshift, &p_vout->output.i_rrshift,
626                  p_vout->output.i_rmask );
627     MaskToShift( &p_vout->output.i_lgshift, &p_vout->output.i_rgshift,
628                  p_vout->output.i_gmask );
629     MaskToShift( &p_vout->output.i_lbshift, &p_vout->output.i_rbshift,
630                  p_vout->output.i_bmask );
631
632     /* Check whether we managed to create direct buffers similar to
633      * the render buffers, ie same size and chroma */
634     if( ( p_vout->output.i_width == p_vout->render.i_width )
635      && ( p_vout->output.i_height == p_vout->render.i_height )
636      && ( vout_ChromaCmp( p_vout->output.i_chroma, p_vout->render.i_chroma ) ) )
637     {
638         /* Cool ! We have direct buffers, we can ask the decoder to
639          * directly decode into them ! Map the first render buffers to
640          * the first direct buffers, but keep the first direct buffer
641          * for memcpy operations */
642         p_vout->b_direct = 1;
643
644         for( i = 1; i < VOUT_MAX_PICTURES; i++ )
645         {
646             if( p_vout->p_picture[ i ].i_type != DIRECT_PICTURE &&
647                 I_RENDERPICTURES >= VOUT_MIN_DIRECT_PICTURES - 1 &&
648                 p_vout->p_picture[ i - 1 ].i_type == DIRECT_PICTURE )
649             {
650                 /* We have enough direct buffers so there's no need to
651                  * try to use system memory buffers. */
652                 break;
653             }
654             PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
655             I_RENDERPICTURES++;
656         }
657
658         msg_Dbg( p_vout, "direct render, mapping "
659                  "render pictures 0-%i to system pictures 1-%i",
660                  VOUT_MAX_PICTURES - 2, VOUT_MAX_PICTURES - 1 );
661     }
662     else
663     {
664         /* Rats... Something is wrong here, we could not find an output
665          * plugin able to directly render what we decode. See if we can
666          * find a chroma plugin to do the conversion */
667         p_vout->b_direct = 0;
668
669         /* Choose the best module */
670         p_vout->chroma.p_module = module_Need( p_vout, "chroma", NULL, 0 );
671
672         if( p_vout->chroma.p_module == NULL )
673         {
674             msg_Err( p_vout, "no chroma module for %4.4s to %4.4s",
675                      (char*)&p_vout->render.i_chroma,
676                      (char*)&p_vout->output.i_chroma );
677             p_vout->pf_end( p_vout );
678             vlc_mutex_unlock( &p_vout->change_lock );
679             return VLC_EGENERIC;
680         }
681
682         msg_Dbg( p_vout, "indirect render, mapping "
683                  "render pictures 0-%i to system pictures %i-%i",
684                  VOUT_MAX_PICTURES - 1, I_OUTPUTPICTURES,
685                  I_OUTPUTPICTURES + VOUT_MAX_PICTURES - 1 );
686
687         /* Append render buffers after the direct buffers */
688         for( i = I_OUTPUTPICTURES; i < 2 * VOUT_MAX_PICTURES; i++ )
689         {
690             PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
691             I_RENDERPICTURES++;
692
693             /* Check if we have enough render pictures */
694             if( I_RENDERPICTURES == VOUT_MAX_PICTURES )
695                 break;
696         }
697     }
698
699     /* Link pictures back to their heap */
700     for( i = 0 ; i < I_RENDERPICTURES ; i++ )
701     {
702         PP_RENDERPICTURE[ i ]->p_heap = &p_vout->render;
703     }
704
705     for( i = 0 ; i < I_OUTPUTPICTURES ; i++ )
706     {
707         PP_OUTPUTPICTURE[ i ]->p_heap = &p_vout->output;
708     }
709
710 /* XXX XXX mark thread ready */
711     return VLC_SUCCESS;
712 }
713
714 /*****************************************************************************
715  * RunThread: video output thread
716  *****************************************************************************
717  * Video output thread. This function does only returns when the thread is
718  * terminated. It handles the pictures arriving in the video heap and the
719  * display device events.
720  *****************************************************************************/
721 static void RunThread( vout_thread_t *p_vout)
722 {
723     int             i_index;                                /* index in heap */
724     int             i_idle_loops = 0;  /* loops without displaying a picture */
725     mtime_t         current_date;                            /* current date */
726     mtime_t         display_date;                            /* display date */
727
728     picture_t *     p_picture;                            /* picture pointer */
729     picture_t *     p_last_picture = NULL;                   /* last picture */
730     picture_t *     p_directbuffer;              /* direct buffer to display */
731
732     subpicture_t *  p_subpic = NULL;                   /* subpicture pointer */
733
734     input_thread_t *p_input = NULL ;           /* Parent input, if it exists */
735
736     vlc_value_t     val;
737     vlc_bool_t      b_drop_late;
738
739     int             i_displayed = 0, i_lost = 0, i_loops = 0;
740
741     /*
742      * Initialize thread
743      */
744     p_vout->b_error = InitThread( p_vout );
745
746     var_Create( p_vout, "drop-late-frames", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
747     var_Get( p_vout, "drop-late-frames", &val );
748     b_drop_late = val.b_bool;
749
750     /* signal the creation of the vout */
751     vlc_thread_ready( p_vout );
752
753     if( p_vout->b_error )
754     {
755         /* Destroy thread structures allocated by Create and InitThread */
756         DestroyThread( p_vout );
757         return;
758     }
759
760     /*
761      * Main loop - it is not executed if an error occurred during
762      * initialization
763      */
764     while( (!p_vout->b_die) && (!p_vout->b_error) )
765     {
766         /* Initialize loop variables */
767         p_picture = NULL;
768         display_date = 0;
769         current_date = mdate();
770
771         if( p_input && p_input->b_die )
772         {
773             vlc_object_release( p_input );
774             p_input = NULL;
775         }
776
777         i_loops++;
778         if( i_loops % 20 == 0 )
779         {
780             if( !p_input )
781             {
782                 p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT,
783                                            FIND_PARENT );
784             }
785             if( p_input )
786             {
787                 vlc_mutex_lock( &p_input->p->counters.counters_lock );
788                 stats_UpdateInteger( p_vout, p_input->p->counters.p_lost_pictures,
789                                      i_lost , NULL);
790                 stats_UpdateInteger( p_vout,
791                                      p_input->p->counters.p_displayed_pictures,
792                                      i_displayed , NULL);
793                 i_displayed = i_lost = 0;
794                 vlc_mutex_unlock( &p_input->p->counters.counters_lock );
795             }
796         }
797 #if 0
798         p_vout->c_loops++;
799         if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
800         {
801             msg_Dbg( p_vout, "picture heap: %d/%d",
802                      I_RENDERPICTURES, p_vout->i_heap_size );
803         }
804 #endif
805
806         /*
807          * Find the picture to display (the one with the earliest date).
808          * This operation does not need lock, since only READY_PICTUREs
809          * are handled. */
810         for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
811         {
812             if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
813                 && ( (p_picture == NULL) ||
814                      (PP_RENDERPICTURE[i_index]->date < display_date) ) )
815             {
816                 p_picture = PP_RENDERPICTURE[i_index];
817                 display_date = p_picture->date;
818             }
819         }
820
821         if( p_picture )
822         {
823             /* If we met the last picture, parse again to see whether there is
824              * a more appropriate one. */
825             if( p_picture == p_last_picture )
826             {
827                 for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
828                 {
829                     if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
830                         && (PP_RENDERPICTURE[i_index] != p_last_picture)
831                         && ((p_picture == p_last_picture) ||
832                             (PP_RENDERPICTURE[i_index]->date < display_date)) )
833                     {
834                         p_picture = PP_RENDERPICTURE[i_index];
835                         display_date = p_picture->date;
836                     }
837                 }
838             }
839
840             /* If we found better than the last picture, destroy it */
841             if( p_last_picture && p_picture != p_last_picture )
842             {
843                 vlc_mutex_lock( &p_vout->picture_lock );
844                 if( p_last_picture->i_refcount )
845                 {
846                     p_last_picture->i_status = DISPLAYED_PICTURE;
847                 }
848                 else
849                 {
850                     p_last_picture->i_status = DESTROYED_PICTURE;
851                     p_vout->i_heap_size--;
852                 }
853                 vlc_mutex_unlock( &p_vout->picture_lock );
854                 p_last_picture = NULL;
855             }
856
857             /* Compute FPS rate */
858             p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ]
859                 = display_date;
860
861             if( !p_picture->b_force &&
862                 p_picture != p_last_picture &&
863                 display_date < current_date + p_vout->render_time &&
864                 b_drop_late )
865             {
866                 /* Picture is late: it will be destroyed and the thread
867                  * will directly choose the next picture */
868                 vlc_mutex_lock( &p_vout->picture_lock );
869                 if( p_picture->i_refcount )
870                 {
871                     /* Pretend we displayed the picture, but don't destroy
872                      * it since the decoder might still need it. */
873                     p_picture->i_status = DISPLAYED_PICTURE;
874                 }
875                 else
876                 {
877                     /* Destroy the picture without displaying it */
878                     p_picture->i_status = DESTROYED_PICTURE;
879                     p_vout->i_heap_size--;
880                 }
881                 msg_Warn( p_vout, "late picture skipped ("I64Fd")",
882                                   current_date - display_date );
883                 i_lost++;
884                 vlc_mutex_unlock( &p_vout->picture_lock );
885
886                 continue;
887             }
888
889             if( display_date >
890                 current_date + p_vout->i_pts_delay + VOUT_BOGUS_DELAY )
891             {
892                 /* Picture is waaay too early: it will be destroyed */
893                 vlc_mutex_lock( &p_vout->picture_lock );
894                 if( p_picture->i_refcount )
895                 {
896                     /* Pretend we displayed the picture, but don't destroy
897                      * it since the decoder might still need it. */
898                     p_picture->i_status = DISPLAYED_PICTURE;
899                 }
900                 else
901                 {
902                     /* Destroy the picture without displaying it */
903                     p_picture->i_status = DESTROYED_PICTURE;
904                     p_vout->i_heap_size--;
905                 }
906                 i_lost++;
907                 msg_Warn( p_vout, "vout warning: early picture skipped "
908                           "("I64Fd")", display_date - current_date
909                           - p_vout->i_pts_delay );
910                 vlc_mutex_unlock( &p_vout->picture_lock );
911
912                 continue;
913             }
914
915             if( display_date > current_date + VOUT_DISPLAY_DELAY )
916             {
917                 /* A picture is ready to be rendered, but its rendering date
918                  * is far from the current one so the thread will perform an
919                  * empty loop as if no picture were found. The picture state
920                  * is unchanged */
921                 p_picture    = NULL;
922                 display_date = 0;
923             }
924             else if( p_picture == p_last_picture )
925             {
926                 /* We are asked to repeat the previous picture, but we first
927                  * wait for a couple of idle loops */
928                 if( i_idle_loops < 4 )
929                 {
930                     p_picture    = NULL;
931                     display_date = 0;
932                 }
933                 else
934                 {
935                     /* We set the display date to something high, otherwise
936                      * we'll have lots of problems with late pictures */
937                     display_date = current_date + p_vout->render_time;
938                 }
939             }
940         }
941
942         if( p_picture == NULL )
943         {
944             i_idle_loops++;
945         }
946
947         /* Video Filter2 stuff */
948         if( p_vout->b_vfilter_change == VLC_TRUE )
949         {
950             int i;
951             vlc_mutex_lock( &p_vout->vfilter_lock );
952             RemoveVideoFilters2( p_vout );
953             for( i = 0; i < p_vout->i_vfilters_cfg; i++ )
954             {
955                 filter_t *p_vfilter =
956                     p_vout->pp_vfilters[p_vout->i_vfilters] =
957                         vlc_object_create( p_vout, VLC_OBJECT_FILTER );
958
959                 vlc_object_attach( p_vfilter, p_vout );
960
961                 p_vfilter->pf_vout_buffer_new = video_new_buffer_filter;
962                 p_vfilter->pf_vout_buffer_del = video_del_buffer_filter;
963
964                 if( !p_vout->i_vfilters )
965                 {
966                     p_vfilter->fmt_in.video = p_vout->fmt_render;
967                 }
968                 else
969                 {
970                     p_vfilter->fmt_in.video = (p_vfilter-1)->fmt_out.video;
971                 }
972                 /* TODO: one day filters in the middle of the chain might
973                  * have a different fmt_out.video than fmt_render ... */
974                 p_vfilter->fmt_out.video = p_vout->fmt_render;
975
976                 p_vfilter->p_cfg = p_vout->p_vfilters_cfg[i];
977                 p_vfilter->p_module = module_Need( p_vfilter, "video filter2",
978                                                  p_vout->psz_vfilters[i], 0 );
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 }