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