]> git.sesse.net Git - vlc/blob - src/video_output/video_output.c
video_output.c: Don't rely on the playlist to store vout, but store them in libvlc.
[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 #ifdef HAVE_CONFIG_H
33 # include "config.h"
34 #endif
35
36 #include <vlc/vlc.h>
37
38 #include <stdlib.h>                                                /* free() */
39 #include <string.h>
40
41
42 #ifdef HAVE_SYS_TIMES_H
43 #   include <sys/times.h>
44 #endif
45
46 #include <vlc_vout.h>
47 #include <vlc_playlist.h>
48
49 #include <vlc_filter.h>
50 #include <vlc_osd.h>
51
52 #if defined( __APPLE__ )
53 /* Include darwin_specific.h here if needed */
54 #endif
55
56 /** FIXME This is quite ugly but needed while we don't have counters
57  * helpers */
58 #include "input/input_internal.h"
59
60 #include "modules/modules.h"
61
62 /*****************************************************************************
63  * Local prototypes
64  *****************************************************************************/
65 static int      InitThread        ( vout_thread_t * );
66 static void     RunThread         ( vout_thread_t * );
67 static void     ErrorThread       ( vout_thread_t * );
68 static void     EndThread         ( vout_thread_t * );
69 static void     DestroyThread     ( vout_thread_t * );
70
71 static void     AspectRatio       ( int, int *, int * );
72 static int      BinaryLog         ( uint32_t );
73 static void     MaskToShift       ( int *, int *, uint32_t );
74
75 static void     vout_Destructor   ( vlc_object_t * p_this );
76
77 /* Object variables callbacks */
78 static int DeinterlaceCallback( vlc_object_t *, char const *,
79                                 vlc_value_t, vlc_value_t, void * );
80 static int FilterCallback( vlc_object_t *, char const *,
81                            vlc_value_t, vlc_value_t, void * );
82 static int VideoFilter2Callback( vlc_object_t *, char const *,
83                                  vlc_value_t, vlc_value_t, void * );
84
85 /* From vout_intf.c */
86 int vout_Snapshot( vout_thread_t *, picture_t * );
87
88 /* Video filter2 parsing */
89 static int ParseVideoFilter2Chain( vout_thread_t *, char * );
90 static void RemoveVideoFilters2( vout_thread_t *p_vout );
91
92 /* Display media title in OSD */
93 static void DisplayTitleOnOSD( vout_thread_t *p_vout );
94
95 /*****************************************************************************
96  * Video Filter2 functions
97  *****************************************************************************/
98 struct filter_owner_sys_t
99 {
100     vout_thread_t *p_vout;
101 };
102
103 static picture_t *video_new_buffer_filter( filter_t *p_filter )
104 {
105     picture_t *p_picture;
106     vout_thread_t *p_vout = p_filter->p_owner->p_vout;
107
108     p_picture = vout_CreatePicture( p_vout, 0, 0, 0 );
109
110     return p_picture;
111 }
112
113 static void video_del_buffer_filter( filter_t *p_filter, picture_t *p_pic )
114 {
115     vout_DestroyPicture( p_filter->p_owner->p_vout, p_pic );
116 }
117
118 /*****************************************************************************
119  * vout_Request: find a video output thread, create one, or destroy one.
120  *****************************************************************************
121  * This function looks for a video output thread matching the current
122  * properties. If not found, it spawns a new one.
123  *****************************************************************************/
124 vout_thread_t *__vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout,
125                                video_format_t *p_fmt )
126 {
127     if( !p_fmt )
128     {
129         /* Reattach video output to playlist before bailing out */
130         if( p_vout )
131         {
132             spu_Attach( p_vout->p_spu, p_this, VLC_FALSE );
133             vlc_object_detach( p_vout );
134             vlc_object_attach( p_vout, p_this->p_libvlc );
135         }
136         return NULL;
137     }
138
139     /* If a video output was provided, lock it, otherwise look for one. */
140     if( p_vout )
141     {
142         vlc_object_yield( p_vout );
143     }
144     else
145     {
146         p_vout = vlc_object_find( p_this, VLC_OBJECT_VOUT, FIND_CHILD );
147
148         if( !p_vout )
149         {
150             p_vout = vlc_object_find( p_this->p_libvlc,
151                                       VLC_OBJECT_VOUT, FIND_CHILD );
152             /* only first children of p_input for unused vout */
153             if( p_vout && p_vout->p_parent != VLC_OBJECT(p_this->p_libvlc) )
154             {
155                 vlc_object_release( p_vout );
156                 p_vout = NULL;
157             }
158             if( p_vout )
159                 vlc_object_detach( p_vout );    /* Remove it from the GC */
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             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_aspect != p_fmt->i_aspect ) ||
198             p_vout->b_filter_change )
199         {
200             /* We are not interested in this format, close this vout */
201             vlc_object_release( p_vout );
202             vout_Destroy( p_vout );
203             p_vout = NULL;
204         }
205         else
206         {
207             /* This video output is cool! Hijack it. */
208             spu_Attach( p_vout->p_spu, p_this, VLC_TRUE );
209             vlc_object_attach( p_vout, p_this );
210             if( p_vout->b_title_show )
211                 DisplayTitleOnOSD( p_vout );
212             vlc_object_release( p_vout );
213         }
214     }
215
216     if( !p_vout )
217     {
218         msg_Dbg( p_this, "no usable vout present, spawning one" );
219
220         p_vout = vout_Create( p_this, p_fmt );
221     }
222
223     return p_vout;
224 }
225
226 /*****************************************************************************
227  * vout_Create: creates a new video output thread
228  *****************************************************************************
229  * This function creates a new video output thread, and returns a pointer
230  * to its description. On error, it returns NULL.
231  *****************************************************************************/
232 vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt )
233 {
234     vout_thread_t  * p_vout;                            /* thread descriptor */
235     input_thread_t * p_input_thread;
236     int              i_index;                               /* loop variable */
237     vlc_value_t      val, text;
238
239     unsigned int i_width = p_fmt->i_width;
240     unsigned int i_height = p_fmt->i_height;
241     vlc_fourcc_t i_chroma = p_fmt->i_chroma;
242     unsigned int i_aspect = p_fmt->i_aspect;
243
244     config_chain_t *p_cfg;
245     char *psz_parser;
246     char *psz_name;
247
248     /* Allocate descriptor */
249     p_vout = vlc_object_create( p_parent, VLC_OBJECT_VOUT );
250     if( p_vout == NULL )
251     {
252         msg_Err( p_parent, "out of memory" );
253         return NULL;
254     }
255
256     /* Initialize pictures - translation tables and functions
257      * will be initialized later in InitThread */
258     for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++)
259     {
260         p_vout->p_picture[i_index].pf_lock = NULL;
261         p_vout->p_picture[i_index].pf_unlock = NULL;
262         p_vout->p_picture[i_index].i_status = FREE_PICTURE;
263         p_vout->p_picture[i_index].i_type   = EMPTY_PICTURE;
264         p_vout->p_picture[i_index].b_slow   = 0;
265     }
266
267     /* No images in the heap */
268     p_vout->i_heap_size = 0;
269
270     /* Initialize the rendering heap */
271     I_RENDERPICTURES = 0;
272
273     vlc_ureduce( &p_fmt->i_sar_num, &p_fmt->i_sar_den,
274                  p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 );
275     p_vout->fmt_render        = *p_fmt;   /* FIXME palette */
276     p_vout->fmt_in            = *p_fmt;   /* FIXME palette */
277
278     p_vout->render.i_width    = i_width;
279     p_vout->render.i_height   = i_height;
280     p_vout->render.i_chroma   = i_chroma;
281     p_vout->render.i_aspect   = i_aspect;
282
283     p_vout->render.i_rmask    = 0;
284     p_vout->render.i_gmask    = 0;
285     p_vout->render.i_bmask    = 0;
286
287     p_vout->render.i_last_used_pic = -1;
288     p_vout->render.b_allow_modify_pics = 1;
289
290     /* Zero the output heap */
291     I_OUTPUTPICTURES = 0;
292     p_vout->output.i_width    = 0;
293     p_vout->output.i_height   = 0;
294     p_vout->output.i_chroma   = 0;
295     p_vout->output.i_aspect   = 0;
296
297     p_vout->output.i_rmask    = 0;
298     p_vout->output.i_gmask    = 0;
299     p_vout->output.i_bmask    = 0;
300
301     /* Initialize misc stuff */
302     p_vout->i_changes    = 0;
303     p_vout->f_gamma      = 0;
304     p_vout->b_grayscale  = 0;
305     p_vout->b_info       = 0;
306     p_vout->b_interface  = 0;
307     p_vout->b_scale      = 1;
308     p_vout->b_fullscreen = 0;
309     p_vout->i_alignment  = 0;
310     p_vout->render_time  = 10;
311     p_vout->c_fps_samples = 0;
312     p_vout->b_filter_change = 0;
313     p_vout->pf_control = 0;
314     p_vout->p_parent_intf = 0;
315     p_vout->i_par_num = p_vout->i_par_den = 1;
316
317     /* Initialize locks */
318     vlc_mutex_init( p_vout, &p_vout->picture_lock );
319     vlc_mutex_init( p_vout, &p_vout->change_lock );
320     vlc_mutex_init( p_vout, &p_vout->vfilter_lock );
321
322     /* Mouse coordinates */
323     var_Create( p_vout, "mouse-x", VLC_VAR_INTEGER );
324     var_Create( p_vout, "mouse-y", VLC_VAR_INTEGER );
325     var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER );
326     var_Create( p_vout, "mouse-moved", VLC_VAR_BOOL );
327     var_Create( p_vout, "mouse-clicked", VLC_VAR_INTEGER );
328
329     /* Initialize subpicture unit */
330     p_vout->p_spu = spu_Create( p_vout );
331     spu_Attach( p_vout->p_spu, p_parent, VLC_TRUE );
332
333     /* Attach the new object now so we can use var inheritance below */
334     vlc_object_attach( p_vout, p_parent );
335
336     spu_Init( p_vout->p_spu );
337
338     /* Take care of some "interface/control" related initialisations */
339     vout_IntfInit( p_vout );
340
341     /* If the parent is not a VOUT object, that means we are at the start of
342      * the video output pipe */
343     if( p_parent->i_object_type != VLC_OBJECT_VOUT )
344     {
345         /* Look for the default filter configuration */
346         var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
347         var_Get( p_vout, "vout-filter", &val );
348         p_vout->psz_filter_chain = val.psz_string;
349
350         /* Apply video filter2 objects on the first vout */
351         var_Create( p_vout, "video-filter",
352                     VLC_VAR_STRING | VLC_VAR_DOINHERIT );
353         var_Get( p_vout, "video-filter", &val );
354         ParseVideoFilter2Chain( p_vout, val.psz_string );
355         free( val.psz_string );
356     }
357     else
358     {
359         /* continue the parent's filter chain */
360         char *psz_tmp;
361
362         /* Ugly hack to jump to our configuration chain */
363         p_vout->psz_filter_chain
364             = ((vout_thread_t *)p_parent)->psz_filter_chain;
365         p_vout->psz_filter_chain
366             = config_ChainCreate( &psz_tmp, &p_cfg, p_vout->psz_filter_chain );
367         config_ChainDestroy( p_cfg );
368         free( psz_tmp );
369
370         /* Create a video filter2 var ... but don't inherit values */
371         var_Create( p_vout, "video-filter", VLC_VAR_STRING );
372         ParseVideoFilter2Chain( p_vout, NULL );
373     }
374
375     var_AddCallback( p_vout, "video-filter", VideoFilter2Callback, NULL );
376     p_vout->b_vfilter_change = VLC_TRUE;
377     p_vout->i_vfilters = 0;
378
379     /* Choose the video output module */
380     if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain )
381     {
382         var_Create( p_vout, "vout", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
383         var_Get( p_vout, "vout", &val );
384         psz_parser = val.psz_string;
385     }
386     else
387     {
388         psz_parser = strdup( p_vout->psz_filter_chain );
389     }
390
391     /* Create the vout thread */
392     config_ChainCreate( &psz_name, &p_cfg, psz_parser );
393     free( psz_parser );
394     p_vout->p_cfg = p_cfg;
395     p_vout->p_module = module_Need( p_vout,
396         ( p_vout->psz_filter_chain && *p_vout->psz_filter_chain ) ?
397         "video filter" : "video output", psz_name, p_vout->psz_filter_chain && *p_vout->psz_filter_chain );
398     free( psz_name );
399
400     if( p_vout->p_module == NULL )
401     {
402         msg_Err( p_vout, "no suitable vout module" );
403         vlc_object_detach( p_vout );
404         vlc_object_release( p_vout );
405         return NULL;
406     }
407
408     /* Create a few object variables for interface interaction */
409     var_Create( p_vout, "deinterlace", VLC_VAR_STRING | VLC_VAR_HASCHOICE );
410     text.psz_string = _("Deinterlace");
411     var_Change( p_vout, "deinterlace", VLC_VAR_SETTEXT, &text, NULL );
412     val.psz_string = (char *)""; text.psz_string = _("Disable");
413     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
414     val.psz_string = (char *)"discard"; text.psz_string = _("Discard");
415     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
416     val.psz_string = (char *)"blend"; text.psz_string = _("Blend");
417     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
418     val.psz_string = (char *)"mean"; text.psz_string = _("Mean");
419     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
420     val.psz_string = (char *)"bob"; text.psz_string = _("Bob");
421     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
422     val.psz_string = (char *)"linear"; text.psz_string = _("Linear");
423     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
424     val.psz_string = (char *)"x"; text.psz_string = (char *)"X";
425     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
426
427     if( var_Get( p_vout, "deinterlace-mode", &val ) == VLC_SUCCESS )
428     {
429         var_Set( p_vout, "deinterlace", val );
430         free( val.psz_string );
431     }
432     var_AddCallback( p_vout, "deinterlace", DeinterlaceCallback, NULL );
433
434     var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
435     text.psz_string = _("Filters");
436     var_Change( p_vout, "vout-filter", VLC_VAR_SETTEXT, &text, NULL );
437     var_AddCallback( p_vout, "vout-filter", FilterCallback, NULL );
438
439     /* Calculate delay created by internal caching */
440     p_input_thread = (input_thread_t *)vlc_object_find( p_vout,
441                                            VLC_OBJECT_INPUT, FIND_ANYWHERE );
442     if( p_input_thread )
443     {
444         p_vout->i_pts_delay = p_input_thread->i_pts_delay;
445         vlc_object_release( p_input_thread );
446     }
447     else
448     {
449         p_vout->i_pts_delay = DEFAULT_PTS_DELAY;
450     }
451
452     if( vlc_thread_create( p_vout, "video output", RunThread,
453                            VLC_THREAD_PRIORITY_OUTPUT, VLC_TRUE ) )
454     {
455         msg_Err( p_vout, "out of memory" );
456         module_Unneed( p_vout, p_vout->p_module );
457         vlc_object_release( 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_release( p_vout );
467         return NULL;
468     }
469
470     vlc_object_set_destructor( p_vout, vout_Destructor );
471
472     return p_vout;
473 }
474
475 /*****************************************************************************
476  * vout_Destroy: destroys a previously created video output
477  *****************************************************************************
478  * Destroy a terminated thread.
479  * The function will request a destruction of the specified thread. If pi_error
480  * is NULL, it will return once the thread is destroyed. Else, it will be
481  * update using one of the THREAD_* constants.
482  *****************************************************************************/
483 void vout_Destroy( vout_thread_t *p_vout )
484 {
485     /* XXX: should go in the destructor */
486     var_Destroy( p_vout, "intf-change" );
487
488     vlc_object_release( p_vout );
489 }
490
491 static void vout_Destructor( vlc_object_t * p_this )
492 {
493     vout_thread_t *p_vout = (vout_thread_t *)p_this;
494
495     free( p_vout->psz_filter_chain );
496
497     config_ChainDestroy( p_vout->p_cfg );
498
499 #ifndef __APPLE__
500     vout_thread_t *p_another_vout;
501     playlist_t *p_playlist = pl_Yield( p_vout );
502
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_this->p_libvlc,
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     vlc_object_release( p_playlist );
520 #endif
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( !p_input )
783             {
784                 p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT,
785                                            FIND_PARENT );
786             }
787             if( p_input )
788             {
789                 vlc_mutex_lock( &p_input->p->counters.counters_lock );
790                 stats_UpdateInteger( p_vout, p_input->p->counters.p_lost_pictures,
791                                      i_lost , NULL);
792                 stats_UpdateInteger( p_vout,
793                                      p_input->p->counters.p_displayed_pictures,
794                                      i_displayed , NULL);
795                 i_displayed = i_lost = 0;
796                 vlc_mutex_unlock( &p_input->p->counters.counters_lock );
797             }
798 #if 0
799         p_vout->c_loops++;
800         if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
801         {
802             msg_Dbg( p_vout, "picture heap: %d/%d",
803                      I_RENDERPICTURES, p_vout->i_heap_size );
804         }
805 #endif
806
807         /*
808          * Find the picture to display (the one with the earliest date).
809          * This operation does not need lock, since only READY_PICTUREs
810          * are handled. */
811         for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
812         {
813             if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
814                 && ( (p_picture == NULL) ||
815                      (PP_RENDERPICTURE[i_index]->date < display_date) ) )
816             {
817                 p_picture = PP_RENDERPICTURE[i_index];
818                 display_date = p_picture->date;
819             }
820         }
821
822         if( p_picture )
823         {
824             /* If we met the last picture, parse again to see whether there is
825              * a more appropriate one. */
826             if( p_picture == p_last_picture )
827             {
828                 for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
829                 {
830                     if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
831                         && (PP_RENDERPICTURE[i_index] != p_last_picture)
832                         && ((p_picture == p_last_picture) ||
833                             (PP_RENDERPICTURE[i_index]->date < display_date)) )
834                     {
835                         p_picture = PP_RENDERPICTURE[i_index];
836                         display_date = p_picture->date;
837                     }
838                 }
839             }
840
841             /* If we found better than the last picture, destroy it */
842             if( p_last_picture && p_picture != p_last_picture )
843             {
844                 vlc_mutex_lock( &p_vout->picture_lock );
845                 if( p_last_picture->i_refcount )
846                 {
847                     p_last_picture->i_status = DISPLAYED_PICTURE;
848                 }
849                 else
850                 {
851                     p_last_picture->i_status = DESTROYED_PICTURE;
852                     p_vout->i_heap_size--;
853                 }
854                 vlc_mutex_unlock( &p_vout->picture_lock );
855                 p_last_picture = NULL;
856             }
857
858             /* Compute FPS rate */
859             p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ]
860                 = display_date;
861
862             if( !p_picture->b_force &&
863                 p_picture != p_last_picture &&
864                 display_date < current_date + p_vout->render_time &&
865                 b_drop_late )
866             {
867                 /* Picture is late: it will be destroyed and the thread
868                  * will directly choose the next picture */
869                 vlc_mutex_lock( &p_vout->picture_lock );
870                 if( p_picture->i_refcount )
871                 {
872                     /* Pretend we displayed the picture, but don't destroy
873                      * it since the decoder might still need it. */
874                     p_picture->i_status = DISPLAYED_PICTURE;
875                 }
876                 else
877                 {
878                     /* Destroy the picture without displaying it */
879                     p_picture->i_status = DESTROYED_PICTURE;
880                     p_vout->i_heap_size--;
881                 }
882                 msg_Warn( p_vout, "late picture skipped ("I64Fd")",
883                                   current_date - display_date );
884                 i_lost++;
885                 vlc_mutex_unlock( &p_vout->picture_lock );
886
887                 continue;
888             }
889
890             if( display_date >
891                 current_date + p_vout->i_pts_delay + VOUT_BOGUS_DELAY )
892             {
893                 /* Picture is waaay too early: it will be destroyed */
894                 vlc_mutex_lock( &p_vout->picture_lock );
895                 if( p_picture->i_refcount )
896                 {
897                     /* Pretend we displayed the picture, but don't destroy
898                      * it since the decoder might still need it. */
899                     p_picture->i_status = DISPLAYED_PICTURE;
900                 }
901                 else
902                 {
903                     /* Destroy the picture without displaying it */
904                     p_picture->i_status = DESTROYED_PICTURE;
905                     p_vout->i_heap_size--;
906                 }
907                 i_lost++;
908                 msg_Warn( p_vout, "vout warning: early picture skipped "
909                           "("I64Fd")", display_date - current_date
910                           - p_vout->i_pts_delay );
911                 vlc_mutex_unlock( &p_vout->picture_lock );
912
913                 continue;
914             }
915
916             if( display_date > current_date + VOUT_DISPLAY_DELAY )
917             {
918                 /* A picture is ready to be rendered, but its rendering date
919                  * is far from the current one so the thread will perform an
920                  * empty loop as if no picture were found. The picture state
921                  * is unchanged */
922                 p_picture    = NULL;
923                 display_date = 0;
924             }
925             else if( p_picture == p_last_picture )
926             {
927                 /* We are asked to repeat the previous picture, but we first
928                  * wait for a couple of idle loops */
929                 if( i_idle_loops < 4 )
930                 {
931                     p_picture    = NULL;
932                     display_date = 0;
933                 }
934                 else
935                 {
936                     /* We set the display date to something high, otherwise
937                      * we'll have lots of problems with late pictures */
938                     display_date = current_date + p_vout->render_time;
939                 }
940             }
941         }
942
943         if( p_picture == NULL )
944         {
945             i_idle_loops++;
946         }
947
948         /* Video Filter2 stuff */
949         if( p_vout->b_vfilter_change == VLC_TRUE )
950         {
951             int i;
952             vlc_mutex_lock( &p_vout->vfilter_lock );
953             RemoveVideoFilters2( p_vout );
954             for( i = 0; i < p_vout->i_vfilters_cfg; i++ )
955             {
956                 filter_t *p_vfilter =
957                     p_vout->pp_vfilters[p_vout->i_vfilters] =
958                         vlc_object_create( p_vout, VLC_OBJECT_FILTER );
959
960                 vlc_object_attach( p_vfilter, p_vout );
961
962                 p_vfilter->pf_vout_buffer_new = video_new_buffer_filter;
963                 p_vfilter->pf_vout_buffer_del = video_del_buffer_filter;
964
965                 if( !p_vout->i_vfilters )
966                 {
967                     p_vfilter->fmt_in.video = p_vout->fmt_render;
968                 }
969                 else
970                 {
971                     p_vfilter->fmt_in.video = (p_vfilter-1)->fmt_out.video;
972                 }
973                 /* TODO: one day filters in the middle of the chain might
974                  * have a different fmt_out.video than fmt_render ... */
975                 p_vfilter->fmt_out.video = p_vout->fmt_render;
976
977                 p_vfilter->p_cfg = p_vout->p_vfilters_cfg[i];
978                 p_vfilter->p_module = module_Need( p_vfilter, "video filter2",
979                                                    p_vout->psz_vfilters[i],
980                                                    VLC_TRUE );
981
982                 if( p_vfilter->p_module )
983                 {
984                     p_vfilter->p_owner =
985                         malloc( sizeof( filter_owner_sys_t ) );
986                     p_vfilter->p_owner->p_vout = p_vout;
987                     p_vout->i_vfilters++;
988                     msg_Dbg( p_vout, "video filter found (%s)",
989                              p_vout->psz_vfilters[i] );
990                 }
991                 else
992                 {
993                     msg_Err( p_vout, "no video filter found (%s)",
994                              p_vout->psz_vfilters[i] );
995                     vlc_object_detach( p_vfilter );
996                     vlc_object_release( p_vfilter );
997                 }
998             }
999             p_vout->b_vfilter_change = VLC_FALSE;
1000             vlc_mutex_unlock( &p_vout->vfilter_lock );
1001         }
1002
1003         if( p_picture )
1004         {
1005             int i;
1006             for( i = 0; i < p_vout->i_vfilters; i++ )
1007             {
1008                 picture_t *p_old = p_picture;
1009                 p_picture  = p_vout->pp_vfilters[i]->pf_video_filter(
1010                                  p_vout->pp_vfilters[i], p_picture );
1011                 if( !p_picture )
1012                 {
1013                     break;
1014                 }
1015                 /* FIXME: this is kind of wrong
1016                  * if you have 2 or more vfilters and the 2nd breaks,
1017                  * on the next loop the 1st one will be applied again */
1018
1019                 /* if p_old and p_picture are the same (ie the filter
1020                  * worked on the old picture), then following code is
1021                  * still alright since i_status gets changed back to
1022                  * the right value */
1023                 if( p_old->i_refcount )
1024                 {
1025                     p_old->i_status = DISPLAYED_PICTURE;
1026                 }
1027                 else
1028                 {
1029                     p_old->i_status = DESTROYED_PICTURE;
1030                 }
1031                 p_picture->i_status = READY_PICTURE;
1032             }
1033         }
1034
1035         if( p_picture && p_vout->b_snapshot )
1036         {
1037             p_vout->b_snapshot = VLC_FALSE;
1038             vout_Snapshot( p_vout, p_picture );
1039         }
1040
1041         /*
1042          * Check for subpictures to display
1043          */
1044         if( display_date > 0 )
1045         {
1046             if( !p_input )
1047             {
1048                 p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT,
1049                                            FIND_PARENT );
1050             }
1051             p_subpic = spu_SortSubpictures( p_vout->p_spu, display_date,
1052             p_input ? var_GetBool( p_input, "state" ) == PAUSE_S : VLC_FALSE );
1053         }
1054
1055         /*
1056          * Perform rendering
1057          */
1058         i_displayed++;
1059         p_directbuffer = vout_RenderPicture( p_vout, p_picture, p_subpic );
1060
1061         /*
1062          * Call the plugin-specific rendering method if there is one
1063          */
1064         if( p_picture != NULL && p_directbuffer != NULL && p_vout->pf_render )
1065         {
1066             /* Render the direct buffer returned by vout_RenderPicture */
1067             p_vout->pf_render( p_vout, p_directbuffer );
1068         }
1069
1070         /*
1071          * Sleep, wake up
1072          */
1073         if( display_date != 0 && p_directbuffer != NULL )
1074         {
1075             mtime_t current_render_time = mdate() - current_date;
1076             /* if render time is very large we don't include it in the mean */
1077             if( current_render_time < p_vout->render_time +
1078                 VOUT_DISPLAY_DELAY )
1079             {
1080                 /* Store render time using a sliding mean weighting to
1081                  * current value in a 3 to 1 ratio*/
1082                 p_vout->render_time *= 3;
1083                 p_vout->render_time += current_render_time;
1084                 p_vout->render_time >>= 2;
1085             }
1086         }
1087
1088         /* Give back change lock */
1089         vlc_mutex_unlock( &p_vout->change_lock );
1090
1091         /* Sleep a while or until a given date */
1092         if( display_date != 0 )
1093         {
1094             /* If there are filters in the chain, better give them the picture
1095              * in advance */
1096             if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain )
1097             {
1098                 mwait( display_date - VOUT_MWAIT_TOLERANCE );
1099             }
1100         }
1101         else
1102         {
1103             msleep( VOUT_IDLE_SLEEP );
1104         }
1105
1106         /* On awakening, take back lock and send immediately picture
1107          * to display. */
1108         vlc_mutex_lock( &p_vout->change_lock );
1109
1110         /*
1111          * Display the previously rendered picture
1112          */
1113         if( p_picture != NULL && p_directbuffer != NULL )
1114         {
1115             /* Display the direct buffer returned by vout_RenderPicture */
1116             if( p_vout->pf_display )
1117             {
1118                 p_vout->pf_display( p_vout, p_directbuffer );
1119             }
1120
1121             /* Tell the vout this was the last picture and that it does not
1122              * need to be forced anymore. */
1123             p_last_picture = p_picture;
1124             p_last_picture->b_force = 0;
1125         }
1126
1127         if( p_picture != NULL )
1128         {
1129             /* Reinitialize idle loop count */
1130             i_idle_loops = 0;
1131         }
1132
1133         /*
1134          * Check events and manage thread
1135          */
1136         if( p_vout->pf_manage && p_vout->pf_manage( p_vout ) )
1137         {
1138             /* A fatal error occurred, and the thread must terminate
1139              * immediately, without displaying anything - setting b_error to 1
1140              * causes the immediate end of the main while() loop. */
1141             p_vout->b_error = 1;
1142         }
1143
1144         if( p_vout->i_changes & VOUT_SIZE_CHANGE )
1145         {
1146             /* this must only happen when the vout plugin is incapable of
1147              * rescaling the picture itself. In this case we need to destroy
1148              * the current picture buffers and recreate new ones with the right
1149              * dimensions */
1150             int i;
1151
1152             p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
1153
1154             p_vout->pf_end( p_vout );
1155             for( i = 0; i < I_OUTPUTPICTURES; i++ )
1156                  p_vout->p_picture[ i ].i_status = FREE_PICTURE;
1157
1158             I_OUTPUTPICTURES = 0;
1159             if( p_vout->pf_init( p_vout ) )
1160             {
1161                 msg_Err( p_vout, "cannot resize display" );
1162                 /* FIXME: pf_end will be called again in EndThread() */
1163                 p_vout->b_error = 1;
1164             }
1165
1166             /* Need to reinitialise the chroma plugin */
1167             if( p_vout->chroma.p_module )
1168             {
1169                 if( p_vout->chroma.p_module->pf_deactivate )
1170                     p_vout->chroma.p_module->pf_deactivate( VLC_OBJECT(p_vout) );
1171                 p_vout->chroma.p_module->pf_activate( VLC_OBJECT(p_vout) );
1172             }
1173         }
1174
1175         if( p_vout->i_changes & VOUT_PICTURE_BUFFERS_CHANGE )
1176         {
1177             /* This happens when the picture buffers need to be recreated.
1178              * This is useful on multimonitor displays for instance.
1179              *
1180              * Warning: This only works when the vout creates only 1 picture
1181              * buffer!! */
1182             p_vout->i_changes &= ~VOUT_PICTURE_BUFFERS_CHANGE;
1183
1184             if( !p_vout->b_direct )
1185             {
1186                 module_Unneed( p_vout, p_vout->chroma.p_module );
1187             }
1188
1189             vlc_mutex_lock( &p_vout->picture_lock );
1190
1191             p_vout->pf_end( p_vout );
1192
1193             I_OUTPUTPICTURES = I_RENDERPICTURES = 0;
1194
1195             p_vout->b_error = InitThread( p_vout );
1196
1197             vlc_mutex_unlock( &p_vout->picture_lock );
1198         }
1199     }
1200
1201     if( p_input )
1202     {
1203         vlc_object_release( p_input );
1204     }
1205
1206     /*
1207      * Error loop - wait until the thread destruction is requested
1208      */
1209     if( p_vout->b_error )
1210     {
1211         ErrorThread( p_vout );
1212     }
1213
1214     /* End of thread */
1215     EndThread( p_vout );
1216
1217     /* Destroy thread structures allocated by CreateThread */
1218     DestroyThread( p_vout );
1219 }
1220
1221 /*****************************************************************************
1222  * ErrorThread: RunThread() error loop
1223  *****************************************************************************
1224  * This function is called when an error occurred during thread main's loop.
1225  * The thread can still receive feed, but must be ready to terminate as soon
1226  * as possible.
1227  *****************************************************************************/
1228 static void ErrorThread( vout_thread_t *p_vout )
1229 {
1230     /* Wait until a `die' order */
1231     while( !p_vout->b_die )
1232     {
1233         /* Sleep a while */
1234         msleep( VOUT_IDLE_SLEEP );
1235     }
1236 }
1237
1238 /*****************************************************************************
1239  * EndThread: thread destruction
1240  *****************************************************************************
1241  * This function is called when the thread ends after a sucessful
1242  * initialization. It frees all resources allocated by InitThread.
1243  *****************************************************************************/
1244 static void EndThread( vout_thread_t *p_vout )
1245 {
1246     int     i_index;                                        /* index in heap */
1247
1248 #ifdef STATS
1249     {
1250         struct tms cpu_usage;
1251         times( &cpu_usage );
1252
1253         msg_Dbg( p_vout, "cpu usage (user: %d, system: %d)",
1254                  cpu_usage.tms_utime, cpu_usage.tms_stime );
1255     }
1256 #endif
1257
1258     if( !p_vout->b_direct )
1259     {
1260         module_Unneed( p_vout, p_vout->chroma.p_module );
1261     }
1262
1263     /* Destroy all remaining pictures */
1264     for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++ )
1265     {
1266         if ( p_vout->p_picture[i_index].i_type == MEMORY_PICTURE )
1267         {
1268             free( p_vout->p_picture[i_index].p_data_orig );
1269         }
1270     }
1271
1272     /* Destroy subpicture unit */
1273     spu_Attach( p_vout->p_spu, VLC_OBJECT(p_vout), VLC_FALSE );
1274     spu_Destroy( p_vout->p_spu );
1275
1276     /* Destroy the video filters2 */
1277     RemoveVideoFilters2( p_vout );
1278
1279     /* Destroy translation tables */
1280     p_vout->pf_end( p_vout );
1281
1282     /* Release the change lock */
1283     vlc_mutex_unlock( &p_vout->change_lock );
1284 }
1285
1286 /*****************************************************************************
1287  * DestroyThread: thread destruction
1288  *****************************************************************************
1289  * This function is called when the thread ends. It frees all ressources
1290  * allocated by CreateThread. Status is available at this stage.
1291  *****************************************************************************/
1292 static void DestroyThread( vout_thread_t *p_vout )
1293 {
1294     /* Destroy the locks */
1295     vlc_mutex_destroy( &p_vout->picture_lock );
1296     vlc_mutex_destroy( &p_vout->change_lock );
1297     vlc_mutex_destroy( &p_vout->vfilter_lock );
1298
1299     /* Release the module */
1300     if( p_vout && p_vout->p_module )
1301     {
1302         module_Unneed( p_vout, p_vout->p_module );
1303     }
1304 }
1305
1306 /* following functions are local */
1307
1308 static int ReduceHeight( int i_ratio )
1309 {
1310     int i_dummy = VOUT_ASPECT_FACTOR;
1311     int i_pgcd  = 1;
1312
1313     if( !i_ratio )
1314     {
1315         return i_pgcd;
1316     }
1317
1318     /* VOUT_ASPECT_FACTOR is (2^7 * 3^3 * 5^3), we just check for 2, 3 and 5 */
1319     while( !(i_ratio & 1) && !(i_dummy & 1) )
1320     {
1321         i_ratio >>= 1;
1322         i_dummy >>= 1;
1323         i_pgcd  <<= 1;
1324     }
1325
1326     while( !(i_ratio % 3) && !(i_dummy % 3) )
1327     {
1328         i_ratio /= 3;
1329         i_dummy /= 3;
1330         i_pgcd  *= 3;
1331     }
1332
1333     while( !(i_ratio % 5) && !(i_dummy % 5) )
1334     {
1335         i_ratio /= 5;
1336         i_dummy /= 5;
1337         i_pgcd  *= 5;
1338     }
1339
1340     return i_pgcd;
1341 }
1342
1343 static void AspectRatio( int i_aspect, int *i_aspect_x, int *i_aspect_y )
1344 {
1345     unsigned int i_pgcd = ReduceHeight( i_aspect );
1346     *i_aspect_x = i_aspect / i_pgcd;
1347     *i_aspect_y = VOUT_ASPECT_FACTOR / i_pgcd;
1348 }
1349
1350 /*****************************************************************************
1351  * BinaryLog: computes the base 2 log of a binary value
1352  *****************************************************************************
1353  * This functions is used by MaskToShift, to get a bit index from a binary
1354  * value.
1355  *****************************************************************************/
1356 static int BinaryLog( uint32_t i )
1357 {
1358     int i_log = 0;
1359
1360     if( i == 0 ) return -31337;
1361
1362     if( i & 0xffff0000 ) i_log += 16;
1363     if( i & 0xff00ff00 ) i_log += 8;
1364     if( i & 0xf0f0f0f0 ) i_log += 4;
1365     if( i & 0xcccccccc ) i_log += 2;
1366     if( i & 0xaaaaaaaa ) i_log += 1;
1367
1368     return i_log;
1369 }
1370
1371 /*****************************************************************************
1372  * MaskToShift: transform a color mask into right and left shifts
1373  *****************************************************************************
1374  * This function is used for obtaining color shifts from masks.
1375  *****************************************************************************/
1376 static void MaskToShift( int *pi_left, int *pi_right, uint32_t i_mask )
1377 {
1378     uint32_t i_low, i_high;            /* lower hand higher bits of the mask */
1379
1380     if( !i_mask )
1381     {
1382         *pi_left = *pi_right = 0;
1383         return;
1384     }
1385
1386     /* Get bits */
1387     i_low = i_high = i_mask;
1388
1389     i_low &= - (int32_t)i_low;          /* lower bit of the mask */
1390     i_high += i_low;                    /* higher bit of the mask */
1391
1392     /* Transform bits into an index. Also deal with i_high overflow, which
1393      * is faster than changing the BinaryLog code to handle 64 bit integers. */
1394     i_low =  BinaryLog (i_low);
1395     i_high = i_high ? BinaryLog (i_high) : 32;
1396
1397     /* Update pointers and return */
1398     *pi_left =   i_low;
1399     *pi_right = (8 - i_high + i_low);
1400 }
1401
1402 /*****************************************************************************
1403  * vout_VarCallback: generic callback for intf variables
1404  *****************************************************************************/
1405 int vout_VarCallback( vlc_object_t * p_this, const char * psz_variable,
1406                       vlc_value_t oldval, vlc_value_t newval,
1407                       void *p_data )
1408 {
1409     vout_thread_t * p_vout = (vout_thread_t *)p_this;
1410     vlc_value_t val;
1411     (void)psz_variable; (void)newval; (void)oldval; (void)p_data;
1412     val.b_bool = VLC_TRUE;
1413     var_Set( p_vout, "intf-change", val );
1414     return VLC_SUCCESS;
1415 }
1416
1417 /*****************************************************************************
1418  * Helper thread for object variables callbacks.
1419  * Only used to avoid deadlocks when using the video embedded mode.
1420  *****************************************************************************/
1421 typedef struct suxor_thread_t
1422 {
1423     VLC_COMMON_MEMBERS
1424     input_thread_t *p_input;
1425
1426 } suxor_thread_t;
1427
1428 static void SuxorRestartVideoES( suxor_thread_t *p_this )
1429 {
1430     vlc_value_t val;
1431
1432     vlc_thread_ready( p_this );
1433
1434     /* Now restart current video stream */
1435     var_Get( p_this->p_input, "video-es", &val );
1436     if( val.i_int >= 0 )
1437     {
1438         vlc_value_t val_es;
1439         val_es.i_int = -VIDEO_ES;
1440         var_Set( p_this->p_input, "video-es", val_es );
1441         var_Set( p_this->p_input, "video-es", val );
1442     }
1443
1444     vlc_object_release( p_this->p_input );
1445
1446     vlc_object_release( p_this );
1447 }
1448
1449 /*****************************************************************************
1450  * object variables callbacks: a bunch of object variables are used by the
1451  * interfaces to interact with the vout.
1452  *****************************************************************************/
1453 static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd,
1454                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1455 {
1456     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1457     input_thread_t *p_input;
1458     vlc_value_t val;
1459
1460     char *psz_mode = newval.psz_string;
1461     char *psz_filter, *psz_deinterlace = NULL;
1462     (void)psz_cmd; (void)oldval; (void)p_data;
1463
1464     var_Get( p_vout, "vout-filter", &val );
1465     psz_filter = val.psz_string;
1466     if( psz_filter ) psz_deinterlace = strstr( psz_filter, "deinterlace" );
1467
1468     if( !psz_mode || !*psz_mode )
1469     {
1470         if( psz_deinterlace )
1471         {
1472             char *psz_src = psz_deinterlace + sizeof("deinterlace") - 1;
1473             if( psz_src[0] == ':' ) psz_src++;
1474             memmove( psz_deinterlace, psz_src, strlen(psz_src) + 1 );
1475         }
1476     }
1477     else if( !psz_deinterlace )
1478     {
1479         psz_filter = realloc( psz_filter, strlen( psz_filter ) +
1480                               sizeof(":deinterlace") );
1481         if( psz_filter && *psz_filter ) strcat( psz_filter, ":" );
1482         strcat( psz_filter, "deinterlace" );
1483     }
1484
1485     p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1486                                                  FIND_PARENT );
1487     if( !p_input ) return VLC_EGENERIC;
1488
1489     if( psz_mode && *psz_mode )
1490     {
1491         /* Modify input as well because the vout might have to be restarted */
1492         val.psz_string = psz_mode;
1493         var_Create( p_input, "deinterlace-mode", VLC_VAR_STRING );
1494         var_Set( p_input, "deinterlace-mode", val );
1495     }
1496     vlc_object_release( p_input );
1497
1498     val.b_bool = VLC_TRUE;
1499     var_Set( p_vout, "intf-change", val );
1500
1501     val.psz_string = psz_filter;
1502     var_Set( p_vout, "vout-filter", val );
1503     free( psz_filter );
1504
1505     return VLC_SUCCESS;
1506 }
1507
1508 static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd,
1509                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1510 {
1511     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1512     input_thread_t *p_input;
1513     vlc_value_t val;
1514     (void)psz_cmd; (void)oldval; (void)p_data;
1515
1516     p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1517                                                  FIND_PARENT );
1518     if (!p_input)
1519     {
1520         msg_Err( p_vout, "Input not found" );
1521         return( VLC_EGENERIC );
1522     }
1523
1524     val.b_bool = VLC_TRUE;
1525     var_Set( p_vout, "intf-change", val );
1526
1527     /* Modify input as well because the vout might have to be restarted */
1528     val.psz_string = newval.psz_string;
1529     var_Create( p_input, "vout-filter", VLC_VAR_STRING );
1530
1531     var_Set( p_input, "vout-filter", val );
1532
1533     /* Now restart current video stream */
1534     var_Get( p_input, "video-es", &val );
1535     if( val.i_int >= 0 )
1536     {
1537         suxor_thread_t *p_suxor =
1538             vlc_object_create( p_vout, sizeof(suxor_thread_t) );
1539         p_suxor->p_input = p_input;
1540         p_vout->b_filter_change = VLC_TRUE;
1541         vlc_object_yield( p_input );
1542         vlc_thread_create( p_suxor, "suxor", SuxorRestartVideoES,
1543                            VLC_THREAD_PRIORITY_LOW, VLC_FALSE );
1544     }
1545
1546     vlc_object_release( p_input );
1547
1548     return VLC_SUCCESS;
1549 }
1550
1551 /*****************************************************************************
1552  * Video Filter2 stuff
1553  *****************************************************************************/
1554 static int ParseVideoFilter2Chain( vout_thread_t *p_vout, char *psz_vfilters )
1555 {
1556     int i;
1557     for( i = 0; i < p_vout->i_vfilters_cfg; i++ )
1558     {
1559         struct config_chain_t *p_cfg =
1560             p_vout->p_vfilters_cfg[p_vout->i_vfilters_cfg];
1561         config_ChainDestroy( p_cfg );
1562         free( p_vout->psz_vfilters[p_vout->i_vfilters_cfg] );
1563         p_vout->psz_vfilters[p_vout->i_vfilters_cfg] = NULL;
1564     }
1565     p_vout->i_vfilters_cfg = 0;
1566     if( psz_vfilters && *psz_vfilters )
1567     {
1568         char *psz_parser = psz_vfilters;
1569
1570         while( psz_parser && *psz_parser )
1571         {
1572             psz_parser = config_ChainCreate(
1573                             &p_vout->psz_vfilters[p_vout->i_vfilters_cfg],
1574                             &p_vout->p_vfilters_cfg[p_vout->i_vfilters_cfg],
1575                             psz_parser );
1576             msg_Dbg( p_vout, "adding vfilter: %s",
1577                      p_vout->psz_vfilters[p_vout->i_vfilters_cfg] );
1578             p_vout->i_vfilters_cfg++;
1579             if( psz_parser && *psz_parser )
1580             {
1581                 if( p_vout->i_vfilters_cfg == MAX_VFILTERS )
1582                 {
1583                     msg_Warn( p_vout,
1584                   "maximum number of video filters reached. \"%s\" discarded",
1585                               psz_parser );
1586                     break;
1587                 }
1588             }
1589         }
1590     }
1591     return VLC_SUCCESS;
1592 }
1593
1594 static int VideoFilter2Callback( vlc_object_t *p_this, char const *psz_cmd,
1595                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1596 {
1597     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1598     (void)psz_cmd; (void)oldval; (void)p_data;
1599
1600     vlc_mutex_lock( &p_vout->vfilter_lock );
1601     ParseVideoFilter2Chain( p_vout, newval.psz_string );
1602     p_vout->b_vfilter_change = VLC_TRUE;
1603     vlc_mutex_unlock( &p_vout->vfilter_lock );
1604
1605     return VLC_SUCCESS;
1606 }
1607
1608 static void RemoveVideoFilters2( vout_thread_t *p_vout )
1609 {
1610     int i;
1611     for( i = 0; i < p_vout->i_vfilters; i++ )
1612     {
1613         vlc_object_detach( p_vout->pp_vfilters[i] );
1614         if( p_vout->pp_vfilters[i]->p_module )
1615         {
1616             module_Unneed( p_vout->pp_vfilters[i],
1617                            p_vout->pp_vfilters[i]->p_module );
1618         }
1619
1620         free( p_vout->pp_vfilters[i]->p_owner );
1621         vlc_object_release( p_vout->pp_vfilters[i] );
1622     }
1623     p_vout->i_vfilters = 0;
1624 }
1625
1626 static void DisplayTitleOnOSD( vout_thread_t *p_vout )
1627 {
1628     input_thread_t *p_input;
1629     mtime_t i_now, i_stop;
1630
1631     p_input = (input_thread_t *)vlc_object_find( p_vout,
1632               VLC_OBJECT_INPUT, FIND_ANYWHERE );
1633     if( p_input )
1634     {
1635         i_now = mdate();
1636         i_stop = i_now + (mtime_t)(p_vout->i_title_timeout * 1000);
1637         char *psz_nowplaying =
1638             input_item_GetNowPlaying( input_GetItem( p_input ) );
1639         char *psz_artist = input_item_GetArtist( input_GetItem( p_input ) );
1640         char *psz_name = input_item_GetTitle( input_GetItem( p_input ) );
1641         if( EMPTY_STR( psz_name ) )
1642         {
1643             free( psz_name );
1644             psz_name = input_item_GetName( input_GetItem( p_input ) );
1645         }
1646         if( !EMPTY_STR( psz_nowplaying ) )
1647         {
1648             vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1649                                    psz_nowplaying, NULL,
1650                                    p_vout->i_title_position,
1651                                    30 + p_vout->fmt_in.i_width
1652                                       - p_vout->fmt_in.i_visible_width
1653                                       - p_vout->fmt_in.i_x_offset,
1654                                    20 + p_vout->fmt_in.i_y_offset,
1655                                    i_now, i_stop );
1656         }
1657         else if( !EMPTY_STR( psz_artist ) )
1658         {
1659             char *psz_string = NULL;
1660             if( asprintf( &psz_string, "%s - %s", psz_name, psz_artist ) != -1 )
1661             {
1662                 vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1663                                        psz_string, NULL,
1664                                        p_vout->i_title_position,
1665                                        30 + p_vout->fmt_in.i_width
1666                                           - p_vout->fmt_in.i_visible_width
1667                                           - p_vout->fmt_in.i_x_offset,
1668                                        20 + p_vout->fmt_in.i_y_offset,
1669                                        i_now, i_stop );
1670                 free( psz_string );
1671             }
1672         }
1673         else
1674         {
1675             vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1676                                    psz_name, NULL,
1677                                    p_vout->i_title_position,
1678                                    30 + p_vout->fmt_in.i_width
1679                                       - p_vout->fmt_in.i_visible_width
1680                                       - p_vout->fmt_in.i_x_offset,
1681                                    20 + p_vout->fmt_in.i_y_offset,
1682                                    i_now, i_stop );
1683         }
1684         vlc_object_release( p_input );
1685         free( psz_artist );
1686         free( psz_name );
1687         free( psz_nowplaying );
1688     }
1689 }
1690