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