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