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