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