]> git.sesse.net Git - vlc/blob - src/video_output/video_output.c
Removed write only vout_thread_t::i_window_width/height/i_alignment/b_autoscale.
[vlc] / src / video_output / video_output.c
1 /*****************************************************************************
2  * video_output.c : video output thread
3  *
4  * This module describes the programming interface for video output threads.
5  * It includes functions allowing to open a new thread, send pictures to a
6  * thread, and destroy a previously oppened video output thread.
7  *****************************************************************************
8  * Copyright (C) 2000-2007 the VideoLAN team
9  * $Id$
10  *
11  * Authors: Vincent Seguin <seguin@via.ecp.fr>
12  *          Gildas Bazin <gbazin@videolan.org>
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
27  *****************************************************************************/
28
29 /*****************************************************************************
30  * Preamble
31  *****************************************************************************/
32 #ifdef HAVE_CONFIG_H
33 # include "config.h"
34 #endif
35
36 #include <vlc_common.h>
37
38 #include <stdlib.h>                                                /* free() */
39 #include <string.h>
40
41 #include <vlc_vout.h>
42
43 #include <vlc_filter.h>
44 #include <vlc_osd.h>
45 #include <assert.h>
46
47 #if defined( __APPLE__ )
48 /* Include darwin_specific.h here if needed */
49 #endif
50
51 /** FIXME This is quite ugly but needed while we don't have counters
52  * helpers */
53 //#include "input/input_internal.h"
54
55 #include <libvlc.h>
56 #include <vlc_input.h>
57 #include "vout_pictures.h"
58 #include "vout_internal.h"
59
60 /*****************************************************************************
61  * Local prototypes
62  *****************************************************************************/
63 static int      InitThread        ( vout_thread_t * );
64 static void*    RunThread         ( void *  );
65 static void     ErrorThread       ( vout_thread_t * );
66 static void     CleanThread       ( vout_thread_t * );
67 static void     EndThread         ( vout_thread_t * );
68
69 static void VideoFormatImportRgb( video_format_t *, const picture_heap_t * );
70 static void PictureHeapFixRgb( picture_heap_t * );
71
72 static void     vout_Destructor   ( vlc_object_t * p_this );
73
74 /* Object variables callbacks */
75 static int FilterCallback( vlc_object_t *, char const *,
76                            vlc_value_t, vlc_value_t, void * );
77 static int VideoFilter2Callback( vlc_object_t *, char const *,
78                                  vlc_value_t, vlc_value_t, void * );
79
80 /* */
81 static void PostProcessEnable( vout_thread_t * );
82 static void PostProcessDisable( vout_thread_t * );
83 static void PostProcessSetFilterQuality( vout_thread_t *p_vout );
84 static int  PostProcessCallback( vlc_object_t *, char const *,
85                                  vlc_value_t, vlc_value_t, void * );
86 /* */
87 static void DeinterlaceEnable( vout_thread_t * );
88 static void DeinterlaceNeeded( vout_thread_t *, bool );
89
90 /* From vout_intf.c */
91 int vout_Snapshot( vout_thread_t *, picture_t * );
92
93 /* Display media title in OSD */
94 static void DisplayTitleOnOSD( vout_thread_t *p_vout );
95
96 /* Time during which the thread will sleep if it has nothing to
97  * display (in micro-seconds) */
98 #define VOUT_IDLE_SLEEP                 ((int)(0.020*CLOCK_FREQ))
99
100 /* Maximum lap of time allowed between the beginning of rendering and
101  * display. If, compared to the current date, the next image is too
102  * late, the thread will perform an idle loop. This time should be
103  * at least VOUT_IDLE_SLEEP plus the time required to render a few
104  * images, to avoid trashing of decoded images */
105 #define VOUT_DISPLAY_DELAY              ((int)(0.200*CLOCK_FREQ))
106
107 /* Better be in advance when awakening than late... */
108 #define VOUT_MWAIT_TOLERANCE            ((mtime_t)(0.020*CLOCK_FREQ))
109
110 /* Minimum number of direct pictures the video output will accept without
111  * creating additional pictures in system memory */
112 #ifdef OPTIMIZE_MEMORY
113 #   define VOUT_MIN_DIRECT_PICTURES        (VOUT_MAX_PICTURES/2)
114 #else
115 #   define VOUT_MIN_DIRECT_PICTURES        (3*VOUT_MAX_PICTURES/4)
116 #endif
117
118 /*****************************************************************************
119  * Video Filter2 functions
120  *****************************************************************************/
121 static picture_t *video_new_buffer_filter( filter_t *p_filter )
122 {
123     vout_thread_t *p_vout = (vout_thread_t*)p_filter->p_owner;
124     picture_t *p_picture = vout_CreatePicture( p_vout, 0, 0, 0 );
125
126     p_picture->i_status = READY_PICTURE;
127
128     return p_picture;
129 }
130
131 static void video_del_buffer_filter( filter_t *p_filter, picture_t *p_pic )
132 {
133     vout_thread_t *p_vout = (vout_thread_t*)p_filter->p_owner;
134
135     vlc_mutex_lock( &p_vout->picture_lock );
136     vout_UsePictureLocked( p_vout, p_pic );
137     vlc_mutex_unlock( &p_vout->picture_lock );
138 }
139
140 static int video_filter_buffer_allocation_init( filter_t *p_filter, void *p_data )
141 {
142     p_filter->pf_video_buffer_new = video_new_buffer_filter;
143     p_filter->pf_video_buffer_del = video_del_buffer_filter;
144     p_filter->p_owner = p_data; /* p_vout */
145     return VLC_SUCCESS;
146 }
147
148 #undef vout_Request
149 /*****************************************************************************
150  * vout_Request: find a video output thread, create one, or destroy one.
151  *****************************************************************************
152  * This function looks for a video output thread matching the current
153  * properties. If not found, it spawns a new one.
154  *****************************************************************************/
155 vout_thread_t *vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout,
156                              video_format_t *p_fmt )
157 {
158     if( !p_fmt )
159     {
160         /* Video output is no longer used.
161          * TODO: support for reusing video outputs with proper _thread-safe_
162          * reference handling. */
163         if( p_vout )
164             vout_CloseAndRelease( p_vout );
165         return NULL;
166     }
167
168     /* If a video output was provided, lock it, otherwise look for one. */
169     if( p_vout )
170     {
171         vlc_object_hold( p_vout );
172     }
173
174     /* TODO: find a suitable unused video output */
175
176     /* If we now have a video output, check it has the right properties */
177     if( p_vout )
178     {
179         vlc_mutex_lock( &p_vout->change_lock );
180
181         /* We don't directly check for the "vout-filter" variable for obvious
182          * performance reasons. */
183         if( p_vout->p->b_filter_change )
184         {
185             char *psz_filter_chain = var_GetString( p_vout, "vout-filter" );
186
187             if( psz_filter_chain && !*psz_filter_chain )
188             {
189                 free( psz_filter_chain );
190                 psz_filter_chain = NULL;
191             }
192             if( p_vout->p->psz_filter_chain && !*p_vout->p->psz_filter_chain )
193             {
194                 free( p_vout->p->psz_filter_chain );
195                 p_vout->p->psz_filter_chain = NULL;
196             }
197
198             if( !psz_filter_chain && !p_vout->p->psz_filter_chain )
199             {
200                 p_vout->p->b_filter_change = false;
201             }
202
203             free( psz_filter_chain );
204         }
205
206         if( p_vout->fmt_render.i_chroma != vlc_fourcc_GetCodec( VIDEO_ES, p_fmt->i_chroma ) ||
207             p_vout->fmt_render.i_width != p_fmt->i_width ||
208             p_vout->fmt_render.i_height != p_fmt->i_height ||
209             p_vout->p->b_filter_change )
210         {
211             vlc_mutex_unlock( &p_vout->change_lock );
212
213             /* We are not interested in this format, close this vout */
214             vout_CloseAndRelease( p_vout );
215             vlc_object_release( p_vout );
216             p_vout = NULL;
217         }
218         else
219         {
220             /* This video output is cool! Hijack it. */
221             /* Correct aspect ratio on change
222              * FIXME factorize this code with other aspect ration related code */
223             unsigned int i_sar_num;
224             unsigned int i_sar_den;
225             vlc_ureduce( &i_sar_num, &i_sar_den,
226                          p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 );
227 #if 0
228             /* What's that, it does not seems to be used correcly everywhere */
229             if( p_vout->i_par_num > 0 && p_vout->i_par_den > 0 )
230             {
231                 i_sar_num *= p_vout->i_par_den;
232                 i_sar_den *= p_vout->i_par_num;
233             }
234 #endif
235
236             if( i_sar_num > 0 && i_sar_den > 0 &&
237                 ( i_sar_num != p_vout->fmt_render.i_sar_num ||
238                   i_sar_den != p_vout->fmt_render.i_sar_den ) )
239             {
240                 p_vout->fmt_in.i_sar_num = i_sar_num;
241                 p_vout->fmt_in.i_sar_den = i_sar_den;
242
243                 p_vout->fmt_render.i_sar_num = i_sar_num;
244                 p_vout->fmt_render.i_sar_den = i_sar_den;
245
246                 p_vout->render.i_aspect = (int64_t)i_sar_num *
247                                                    p_vout->fmt_render.i_width *
248                                                    VOUT_ASPECT_FACTOR /
249                                                    i_sar_den /
250                                                    p_vout->fmt_render.i_height;
251                 p_vout->i_changes |= VOUT_ASPECT_CHANGE;
252             }
253             vlc_mutex_unlock( &p_vout->change_lock );
254
255             vlc_object_release( p_vout );
256         }
257
258         if( p_vout )
259         {
260             msg_Dbg( p_this, "reusing provided vout" );
261
262             spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), false );
263             vlc_object_detach( p_vout );
264
265             vlc_object_attach( p_vout, p_this );
266             spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), true );
267         }
268     }
269
270     if( !p_vout )
271     {
272         msg_Dbg( p_this, "no usable vout present, spawning one" );
273
274         p_vout = vout_Create( p_this, p_fmt );
275     }
276
277     return p_vout;
278 }
279
280 #undef vout_Create
281 /*****************************************************************************
282  * vout_Create: creates a new video output thread
283  *****************************************************************************
284  * This function creates a new video output thread, and returns a pointer
285  * to its description. On error, it returns NULL.
286  *****************************************************************************/
287 vout_thread_t * vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt )
288 {
289     vout_thread_t  * p_vout;                            /* thread descriptor */
290     int              i_index;                               /* loop variable */
291     vlc_value_t      text;
292
293     unsigned int i_width = p_fmt->i_width;
294     unsigned int i_height = p_fmt->i_height;
295     vlc_fourcc_t i_chroma = vlc_fourcc_GetCodec( VIDEO_ES, p_fmt->i_chroma );
296
297     config_chain_t *p_cfg;
298     char *psz_parser;
299     char *psz_name;
300
301     if( i_width <= 0 || i_height <= 0 )
302         return NULL;
303
304     vlc_ureduce( &p_fmt->i_sar_num, &p_fmt->i_sar_den,
305                  p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 );
306     if( p_fmt->i_sar_num <= 0 || p_fmt->i_sar_den <= 0 )
307         return NULL;
308     unsigned int i_aspect = (int64_t)p_fmt->i_sar_num *
309                                      i_width *
310                                      VOUT_ASPECT_FACTOR /
311                                      p_fmt->i_sar_den /
312                                      i_height;
313
314     /* Allocate descriptor */
315     static const char typename[] = "video output";
316     p_vout = vlc_custom_create( p_parent, sizeof( *p_vout ), VLC_OBJECT_VOUT,
317                                 typename );
318     if( p_vout == NULL )
319         return NULL;
320
321     /* */
322     p_vout->p = calloc( 1, sizeof(*p_vout->p) );
323     if( !p_vout->p )
324     {
325         vlc_object_release( p_vout );
326         return NULL;
327     }
328
329     /* Initialize pictures - translation tables and functions
330      * will be initialized later in InitThread */
331     for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++)
332     {
333         p_vout->p_picture[i_index].i_status = FREE_PICTURE;
334         p_vout->p_picture[i_index].i_type   = EMPTY_PICTURE;
335         p_vout->p_picture[i_index].b_slow   = 0;
336     }
337
338     /* Initialize the rendering heap */
339     I_RENDERPICTURES = 0;
340
341     p_vout->fmt_render        = *p_fmt;   /* FIXME palette */
342     p_vout->fmt_in            = *p_fmt;   /* FIXME palette */
343
344     p_vout->render.i_width    = i_width;
345     p_vout->render.i_height   = i_height;
346     p_vout->render.i_chroma   = i_chroma;
347     p_vout->render.i_aspect   = i_aspect;
348
349     p_vout->render.i_rmask    = p_fmt->i_rmask;
350     p_vout->render.i_gmask    = p_fmt->i_gmask;
351     p_vout->render.i_bmask    = p_fmt->i_bmask;
352
353     p_vout->render.i_last_used_pic = -1;
354
355     /* Zero the output heap */
356     I_OUTPUTPICTURES = 0;
357     p_vout->output.i_width    = 0;
358     p_vout->output.i_height   = 0;
359     p_vout->output.i_chroma   = 0;
360     p_vout->output.i_aspect   = 0;
361
362     p_vout->output.i_rmask    = 0;
363     p_vout->output.i_gmask    = 0;
364     p_vout->output.i_bmask    = 0;
365
366     /* Initialize misc stuff */
367     p_vout->i_changes    = 0;
368     p_vout->i_zoom      = ZOOM_FP_FACTOR;
369     p_vout->b_fullscreen = 0;
370     p_vout->p->render_time  = 10;
371     p_vout->p->c_fps_samples = 0;
372     vout_statistic_Init( &p_vout->p->statistic );
373     p_vout->p->b_filter_change = 0;
374     p_vout->p->b_paused = false;
375     p_vout->p->i_pause_date = 0;
376     p_vout->p->i_par_num =
377     p_vout->p->i_par_den = 1;
378     p_vout->p->p_picture_displayed = NULL;
379     p_vout->p->i_picture_displayed_date = 0;
380     p_vout->p->b_picture_displayed = false;
381     p_vout->p->b_picture_empty = false;
382     p_vout->p->i_picture_qtype = QTYPE_NONE;
383     p_vout->p->b_picture_interlaced = false;
384
385     vlc_mouse_Init( &p_vout->p->mouse );
386
387     vout_snapshot_Init( &p_vout->p->snapshot );
388
389     /* Initialize locks */
390     vlc_mutex_init( &p_vout->picture_lock );
391     vlc_cond_init( &p_vout->p->picture_wait );
392     vlc_mutex_init( &p_vout->change_lock );
393     vlc_mutex_init( &p_vout->p->vfilter_lock );
394
395     /* Mouse coordinates */
396     var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER );
397     var_Create( p_vout, "mouse-moved", VLC_VAR_COORDS );
398     var_Create( p_vout, "mouse-clicked", VLC_VAR_COORDS );
399     /* Mouse object (area of interest in a video filter) */
400     var_Create( p_vout, "mouse-object", VLC_VAR_BOOL );
401
402     /* Attach the new object now so we can use var inheritance below */
403     vlc_object_attach( p_vout, p_parent );
404
405     /* Initialize subpicture unit */
406     p_vout->p->p_spu = spu_Create( p_vout );
407
408     /* */
409     spu_Init( p_vout->p->p_spu );
410
411     spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), true );
412
413     /* Take care of some "interface/control" related initialisations */
414     vout_IntfInit( p_vout );
415
416     /* If the parent is not a VOUT object, that means we are at the start of
417      * the video output pipe */
418     if( vlc_internals( p_parent )->i_object_type != VLC_OBJECT_VOUT )
419     {
420         /* Look for the default filter configuration */
421         p_vout->p->psz_filter_chain =
422             var_CreateGetStringCommand( p_vout, "vout-filter" );
423
424         /* Apply video filter2 objects on the first vout */
425         p_vout->p->psz_vf2 =
426             var_CreateGetStringCommand( p_vout, "video-filter" );
427
428         p_vout->p->b_first_vout = true;
429     }
430     else
431     {
432         /* continue the parent's filter chain */
433         char *psz_tmp;
434
435         /* Ugly hack to jump to our configuration chain */
436         p_vout->p->psz_filter_chain
437             = ((vout_thread_t *)p_parent)->p->psz_filter_chain;
438         p_vout->p->psz_filter_chain
439             = config_ChainCreate( &psz_tmp, &p_cfg, p_vout->p->psz_filter_chain );
440         config_ChainDestroy( p_cfg );
441         free( psz_tmp );
442
443         /* Create a video filter2 var ... but don't inherit values */
444         var_Create( p_vout, "video-filter",
445                     VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
446         p_vout->p->psz_vf2 = var_GetString( p_vout, "video-filter" );
447
448         /* */
449         p_vout->p->b_first_vout = false;
450     }
451
452     var_AddCallback( p_vout, "video-filter", VideoFilter2Callback, NULL );
453     p_vout->p->p_vf2_chain = filter_chain_New( p_vout, "video filter2",
454         false, video_filter_buffer_allocation_init, NULL, p_vout );
455
456     /* Choose the video output module */
457     if( !p_vout->p->psz_filter_chain || !*p_vout->p->psz_filter_chain )
458     {
459         psz_parser = NULL;
460     }
461     else
462     {
463         psz_parser = strdup( p_vout->p->psz_filter_chain );
464         p_vout->p->b_title_show = false;
465     }
466
467     /* Create the vout thread */
468     char *psz_tmp = config_ChainCreate( &psz_name, &p_cfg, psz_parser );
469     free( psz_parser );
470     free( psz_tmp );
471     p_vout->p->p_cfg = p_cfg;
472
473     /* Create a few object variables for interface interaction */
474     var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
475     text.psz_string = _("Filters");
476     var_Change( p_vout, "vout-filter", VLC_VAR_SETTEXT, &text, NULL );
477     var_AddCallback( p_vout, "vout-filter", FilterCallback, NULL );
478
479     /* */
480     DeinterlaceEnable( p_vout );
481
482     if( p_vout->p->psz_filter_chain && *p_vout->p->psz_filter_chain )
483     {
484         char *psz_tmp;
485         if( asprintf( &psz_tmp, "%s,none", psz_name ) < 0 )
486             psz_tmp = strdup( "" );
487         free( psz_name );
488         psz_name = psz_tmp;
489     }
490     p_vout->p->psz_module_name = psz_name;
491
492     /* */
493     vlc_object_set_destructor( p_vout, vout_Destructor );
494
495     /* */
496     vlc_cond_init( &p_vout->p->change_wait );
497     if( vlc_clone( &p_vout->p->thread, RunThread, p_vout,
498                    VLC_THREAD_PRIORITY_OUTPUT ) )
499     {
500         spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), false );
501         spu_Destroy( p_vout->p->p_spu );
502         p_vout->p->p_spu = NULL;
503         vlc_object_release( p_vout );
504         return NULL;
505     }
506
507     vlc_mutex_lock( &p_vout->change_lock );
508     while( !p_vout->p->b_ready )
509     {   /* We are (ab)using the same condition in opposite directions for
510          * b_ready and b_done. This works because of the strict ordering. */
511         assert( !p_vout->p->b_done );
512         vlc_cond_wait( &p_vout->p->change_wait, &p_vout->change_lock );
513     }
514     vlc_mutex_unlock( &p_vout->change_lock );
515
516     if( p_vout->p->b_error )
517     {
518         msg_Err( p_vout, "video output creation failed" );
519         vout_CloseAndRelease( p_vout );
520         return NULL;
521     }
522
523     return p_vout;
524 }
525
526 /*****************************************************************************
527  * vout_Close: Close a vout created by vout_Create.
528  *****************************************************************************
529  * You HAVE to call it on vout created by vout_Create before vlc_object_release.
530  * You should NEVER call it on vout not obtained through vout_Create
531  * (like with vout_Request or vlc_object_find.)
532  * You can use vout_CloseAndRelease() as a convenience method.
533  *****************************************************************************/
534 void vout_Close( vout_thread_t *p_vout )
535 {
536     assert( p_vout );
537
538     vlc_mutex_lock( &p_vout->change_lock );
539     p_vout->p->b_done = true;
540     vlc_cond_signal( &p_vout->p->change_wait );
541     vlc_mutex_unlock( &p_vout->change_lock );
542
543     vout_snapshot_End( &p_vout->p->snapshot );
544
545     vlc_join( p_vout->p->thread, NULL );
546 }
547
548 /* */
549 static void vout_Destructor( vlc_object_t * p_this )
550 {
551     vout_thread_t *p_vout = (vout_thread_t *)p_this;
552
553     /* Make sure the vout was stopped first */
554     //assert( !p_vout->p_module );
555
556     free( p_vout->p->psz_module_name );
557
558     /* */
559     if( p_vout->p->p_spu )
560         spu_Destroy( p_vout->p->p_spu );
561
562     /* Destroy the locks */
563     vlc_cond_destroy( &p_vout->p->change_wait );
564     vlc_cond_destroy( &p_vout->p->picture_wait );
565     vlc_mutex_destroy( &p_vout->picture_lock );
566     vlc_mutex_destroy( &p_vout->change_lock );
567     vlc_mutex_destroy( &p_vout->p->vfilter_lock );
568
569     /* */
570     vout_statistic_Clean( &p_vout->p->statistic );
571
572     /* */
573     vout_snapshot_Clean( &p_vout->p->snapshot );
574
575     /* */
576     free( p_vout->p->psz_filter_chain );
577     free( p_vout->p->psz_title );
578
579     config_ChainDestroy( p_vout->p->p_cfg );
580
581     free( p_vout->p );
582
583 }
584
585 /* */
586 void vout_ChangePause( vout_thread_t *p_vout, bool b_paused, mtime_t i_date )
587 {
588     vlc_mutex_lock( &p_vout->change_lock );
589
590     assert( !p_vout->p->b_paused || !b_paused );
591
592     vlc_mutex_lock( &p_vout->picture_lock );
593
594     p_vout->p->i_picture_displayed_date = 0;
595
596     if( p_vout->p->b_paused )
597     {
598         const mtime_t i_duration = i_date - p_vout->p->i_pause_date;
599
600         for( int i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
601         {
602             picture_t *p_pic = PP_RENDERPICTURE[i_index];
603
604             if( p_pic->i_status == READY_PICTURE )
605                 p_pic->date += i_duration;
606         }
607         vlc_cond_signal( &p_vout->p->picture_wait );
608         vlc_mutex_unlock( &p_vout->picture_lock );
609
610         spu_OffsetSubtitleDate( p_vout->p->p_spu, i_duration );
611     }
612     else
613     {
614         vlc_mutex_unlock( &p_vout->picture_lock );
615     }
616     p_vout->p->b_paused = b_paused;
617     p_vout->p->i_pause_date = i_date;
618
619     vlc_mutex_unlock( &p_vout->change_lock );
620 }
621
622 void vout_GetResetStatistic( vout_thread_t *p_vout, int *pi_displayed, int *pi_lost )
623 {
624     vout_statistic_GetReset( &p_vout->p->statistic,
625                              pi_displayed, pi_lost );
626 }
627
628 void vout_Flush( vout_thread_t *p_vout, mtime_t i_date )
629 {
630     vlc_mutex_lock( &p_vout->picture_lock );
631     p_vout->p->i_picture_displayed_date = 0;
632     for( int i = 0; i < p_vout->render.i_pictures; i++ )
633     {
634         picture_t *p_pic = p_vout->render.pp_picture[i];
635
636         if( p_pic->i_status == READY_PICTURE ||
637             p_pic->i_status == DISPLAYED_PICTURE )
638         {
639             /* We cannot change picture status if it is in READY_PICTURE state,
640              * Just make sure they won't be displayed */
641             if( p_pic->date > i_date )
642                 p_pic->date = i_date;
643         }
644     }
645     vlc_cond_signal( &p_vout->p->picture_wait );
646     vlc_mutex_unlock( &p_vout->picture_lock );
647 }
648
649 void vout_FixLeaks( vout_thread_t *p_vout, bool b_forced )
650 {
651     int i_pic, i_ready_pic;
652
653     vlc_mutex_lock( &p_vout->picture_lock );
654
655     for( i_pic = 0, i_ready_pic = 0; i_pic < p_vout->render.i_pictures && !b_forced; i_pic++ )
656     {
657         const picture_t *p_pic = p_vout->render.pp_picture[i_pic];
658
659         if( p_pic->i_status == READY_PICTURE )
660         {
661             i_ready_pic++;
662             /* If we have at least 2 ready pictures, wait for the vout thread to
663              * process one */
664             if( i_ready_pic >= 2 )
665                 break;
666
667             continue;
668         }
669
670         if( p_pic->i_status == DISPLAYED_PICTURE )
671         {
672             /* If at least one displayed picture is not referenced
673              * let vout free it */
674             if( p_pic->i_refcount == 0 )
675                 break;
676         }
677     }
678     if( i_pic < p_vout->render.i_pictures && !b_forced )
679     {
680         vlc_mutex_unlock( &p_vout->picture_lock );
681         return;
682     }
683
684     /* Too many pictures are still referenced, there is probably a bug
685      * with the decoder */
686     if( !b_forced )
687         msg_Err( p_vout, "pictures leaked, resetting the heap" );
688
689     /* Just free all the pictures */
690     for( i_pic = 0; i_pic < p_vout->render.i_pictures; i_pic++ )
691     {
692         picture_t *p_pic = p_vout->render.pp_picture[i_pic];
693
694         msg_Dbg( p_vout, "[%d] %d %d", i_pic, p_pic->i_status, p_pic->i_refcount );
695         p_pic->i_refcount = 0;
696
697         switch( p_pic->i_status )
698         {
699         case READY_PICTURE:
700         case DISPLAYED_PICTURE:
701         case RESERVED_PICTURE:
702             if( p_pic != p_vout->p->p_picture_displayed )
703                 vout_UsePictureLocked( p_vout, p_pic );
704             break;
705         }
706     }
707     vlc_cond_signal( &p_vout->p->picture_wait );
708     vlc_mutex_unlock( &p_vout->picture_lock );
709 }
710 void vout_NextPicture( vout_thread_t *p_vout, mtime_t *pi_duration )
711 {
712     vlc_mutex_lock( &p_vout->picture_lock );
713
714     const mtime_t i_displayed_date = p_vout->p->i_picture_displayed_date;
715
716     p_vout->p->b_picture_displayed = false;
717     p_vout->p->b_picture_empty = false;
718     if( p_vout->p->p_picture_displayed )
719     {
720         p_vout->p->p_picture_displayed->date = 1;
721         vlc_cond_signal( &p_vout->p->picture_wait );
722     }
723
724     while( !p_vout->p->b_picture_displayed && !p_vout->p->b_picture_empty )
725         vlc_cond_wait( &p_vout->p->picture_wait, &p_vout->picture_lock );
726
727     *pi_duration = __MAX( p_vout->p->i_picture_displayed_date - i_displayed_date, 0 );
728
729     /* TODO advance subpicture by the duration ... */
730
731     vlc_mutex_unlock( &p_vout->picture_lock );
732 }
733
734 void vout_DisplayTitle( vout_thread_t *p_vout, const char *psz_title )
735 {
736     assert( psz_title );
737
738     if( !var_InheritBool( p_vout, "osd" ) )
739         return;
740
741     vlc_mutex_lock( &p_vout->change_lock );
742     free( p_vout->p->psz_title );
743     p_vout->p->psz_title = strdup( psz_title );
744     vlc_mutex_unlock( &p_vout->change_lock );
745 }
746
747 spu_t *vout_GetSpu( vout_thread_t *p_vout )
748 {
749     return p_vout->p->p_spu;
750 }
751
752 /*****************************************************************************
753  * InitThread: initialize video output thread
754  *****************************************************************************
755  * This function is called from RunThread and performs the second step of the
756  * initialization. It returns 0 on success. Note that the thread's flag are not
757  * modified inside this function.
758  * XXX You have to enter it with change_lock taken.
759  *****************************************************************************/
760 static int ChromaCreate( vout_thread_t *p_vout );
761 static void ChromaDestroy( vout_thread_t *p_vout );
762
763 static int InitThread( vout_thread_t *p_vout )
764 {
765     int i;
766
767     /* Initialize output method, it allocates direct buffers for us */
768     if( vout_InitWrapper( p_vout ) )
769         return VLC_EGENERIC;
770
771     p_vout->p->p_picture_displayed = NULL;
772
773     if( !I_OUTPUTPICTURES )
774     {
775         msg_Err( p_vout, "plugin was unable to allocate at least "
776                          "one direct buffer" );
777         vout_EndWrapper( p_vout );
778         return VLC_EGENERIC;
779     }
780
781     if( I_OUTPUTPICTURES > VOUT_MAX_PICTURES )
782     {
783         msg_Err( p_vout, "plugin allocated too many direct buffers, "
784                          "our internal buffers must have overflown." );
785         vout_EndWrapper( p_vout );
786         return VLC_EGENERIC;
787     }
788
789     msg_Dbg( p_vout, "got %i direct buffer(s)", I_OUTPUTPICTURES );
790
791     if( !p_vout->fmt_out.i_width || !p_vout->fmt_out.i_height )
792     {
793         p_vout->fmt_out.i_width = p_vout->fmt_out.i_visible_width =
794             p_vout->output.i_width;
795         p_vout->fmt_out.i_height = p_vout->fmt_out.i_visible_height =
796             p_vout->output.i_height;
797         p_vout->fmt_out.i_x_offset =  p_vout->fmt_out.i_y_offset = 0;
798
799         p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
800     }
801     if( !p_vout->fmt_out.i_sar_num || !p_vout->fmt_out.i_sar_num )
802     {
803         p_vout->fmt_out.i_sar_num = p_vout->output.i_aspect *
804             p_vout->fmt_out.i_height;
805         p_vout->fmt_out.i_sar_den = VOUT_ASPECT_FACTOR *
806             p_vout->fmt_out.i_width;
807     }
808
809     vlc_ureduce( &p_vout->fmt_out.i_sar_num, &p_vout->fmt_out.i_sar_den,
810                  p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den, 0 );
811
812     /* FIXME removed the need of both fmt_* and heap infos */
813     /* Calculate shifts from system-updated masks */
814     PictureHeapFixRgb( &p_vout->render );
815     VideoFormatImportRgb( &p_vout->fmt_render, &p_vout->render );
816
817     PictureHeapFixRgb( &p_vout->output );
818     VideoFormatImportRgb( &p_vout->fmt_out, &p_vout->output );
819
820     /* print some usefull debug info about different vout formats
821      */
822     msg_Dbg( p_vout, "pic render sz %ix%i, of (%i,%i), vsz %ix%i, 4cc %4.4s, sar %i:%i, msk r0x%x g0x%x b0x%x",
823              p_vout->fmt_render.i_width, p_vout->fmt_render.i_height,
824              p_vout->fmt_render.i_x_offset, p_vout->fmt_render.i_y_offset,
825              p_vout->fmt_render.i_visible_width,
826              p_vout->fmt_render.i_visible_height,
827              (char*)&p_vout->fmt_render.i_chroma,
828              p_vout->fmt_render.i_sar_num, p_vout->fmt_render.i_sar_den,
829              p_vout->fmt_render.i_rmask, p_vout->fmt_render.i_gmask, p_vout->fmt_render.i_bmask );
830
831     msg_Dbg( p_vout, "pic in sz %ix%i, of (%i,%i), vsz %ix%i, 4cc %4.4s, sar %i:%i, msk r0x%x g0x%x b0x%x",
832              p_vout->fmt_in.i_width, p_vout->fmt_in.i_height,
833              p_vout->fmt_in.i_x_offset, p_vout->fmt_in.i_y_offset,
834              p_vout->fmt_in.i_visible_width,
835              p_vout->fmt_in.i_visible_height,
836              (char*)&p_vout->fmt_in.i_chroma,
837              p_vout->fmt_in.i_sar_num, p_vout->fmt_in.i_sar_den,
838              p_vout->fmt_in.i_rmask, p_vout->fmt_in.i_gmask, p_vout->fmt_in.i_bmask );
839
840     msg_Dbg( p_vout, "pic out sz %ix%i, of (%i,%i), vsz %ix%i, 4cc %4.4s, sar %i:%i, msk r0x%x g0x%x b0x%x",
841              p_vout->fmt_out.i_width, p_vout->fmt_out.i_height,
842              p_vout->fmt_out.i_x_offset, p_vout->fmt_out.i_y_offset,
843              p_vout->fmt_out.i_visible_width,
844              p_vout->fmt_out.i_visible_height,
845              (char*)&p_vout->fmt_out.i_chroma,
846              p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den,
847              p_vout->fmt_out.i_rmask, p_vout->fmt_out.i_gmask, p_vout->fmt_out.i_bmask );
848
849     assert( p_vout->output.i_width == p_vout->render.i_width &&
850             p_vout->output.i_height == p_vout->render.i_height &&
851             p_vout->output.i_chroma == p_vout->render.i_chroma );
852     /* Check whether we managed to create direct buffers similar to
853      * the render buffers, ie same size and chroma */
854
855     /* Cool ! We have direct buffers, we can ask the decoder to
856      * directly decode into them ! Map the first render buffers to
857      * the first direct buffers, but keep the first direct buffer
858      * for memcpy operations */
859     for( i = 1; i < VOUT_MAX_PICTURES; i++ )
860     {
861         if( p_vout->p_picture[ i ].i_type != DIRECT_PICTURE &&
862             I_RENDERPICTURES >= VOUT_MIN_DIRECT_PICTURES - 1 &&
863             p_vout->p_picture[ i - 1 ].i_type == DIRECT_PICTURE )
864         {
865             /* We have enough direct buffers so there's no need to
866              * try to use system memory buffers. */
867             break;
868         }
869         PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
870         I_RENDERPICTURES++;
871     }
872
873     msg_Dbg( p_vout, "direct render, mapping "
874              "render pictures 0-%i to system pictures 1-%i",
875              VOUT_MAX_PICTURES - 2, VOUT_MAX_PICTURES - 1 );
876
877     return VLC_SUCCESS;
878 }
879
880 /*****************************************************************************
881  * RunThread: video output thread
882  *****************************************************************************
883  * Video output thread. This function does only returns when the thread is
884  * terminated. It handles the pictures arriving in the video heap and the
885  * display device events.
886  *****************************************************************************/
887 static void* RunThread( void *p_this )
888 {
889     vout_thread_t *p_vout = p_this;
890     bool            b_has_wrapper;
891     int             i_idle_loops = 0;  /* loops without displaying a picture */
892     int             i_picture_qtype_last = QTYPE_NONE;
893     bool            b_picture_interlaced_last = false;
894     mtime_t         i_picture_interlaced_last_date;
895
896     /*
897      * Initialize thread
898      */
899     b_has_wrapper = !vout_OpenWrapper( p_vout, p_vout->p->psz_module_name );
900
901     vlc_mutex_lock( &p_vout->change_lock );
902
903     if( b_has_wrapper )
904         p_vout->p->b_error = InitThread( p_vout );
905     else
906         p_vout->p->b_error = true;
907
908     /* signal the creation of the vout */
909     p_vout->p->b_ready = true;
910     vlc_cond_signal( &p_vout->p->change_wait );
911
912     if( p_vout->p->b_error )
913         goto exit_thread;
914
915     /* */
916     const bool b_drop_late = var_CreateGetBool( p_vout, "drop-late-frames" );
917     i_picture_interlaced_last_date = mdate();
918
919     /*
920      * Main loop - it is not executed if an error occurred during
921      * initialization
922      */
923     while( !p_vout->p->b_done && !p_vout->p->b_error )
924     {
925         /* Initialize loop variables */
926         const mtime_t current_date = mdate();
927         picture_t *p_picture;
928         picture_t *p_filtered_picture;
929         mtime_t display_date;
930         picture_t *p_directbuffer;
931         int i_index;
932
933         if( p_vout->p->b_title_show && p_vout->p->psz_title )
934             DisplayTitleOnOSD( p_vout );
935
936         vlc_mutex_lock( &p_vout->picture_lock );
937
938         /* Look for the earliest picture but after the last displayed one */
939         picture_t *p_last = p_vout->p->p_picture_displayed;;
940
941         p_picture = NULL;
942         for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
943         {
944             picture_t *p_pic = PP_RENDERPICTURE[i_index];
945
946             if( p_pic->i_status != READY_PICTURE )
947                 continue;
948
949             if( p_vout->p->b_paused && p_last && p_last->date > 1 )
950                 continue;
951
952             if( p_last && p_pic != p_last && p_pic->date <= p_last->date )
953             {
954                 /* Drop old picture */
955                 vout_UsePictureLocked( p_vout, p_pic );
956             }
957             else if( !p_vout->p->b_paused && !p_pic->b_force && p_pic != p_last &&
958                      p_pic->date < current_date + p_vout->p->render_time &&
959                      b_drop_late )
960             {
961                 /* Picture is late: it will be destroyed and the thread
962                  * will directly choose the next picture */
963                 vout_UsePictureLocked( p_vout, p_pic );
964                 vout_statistic_Update( &p_vout->p->statistic, 0, 1 );
965
966                 msg_Warn( p_vout, "late picture skipped (%"PRId64" > %d)",
967                                   current_date - p_pic->date, - p_vout->p->render_time );
968             }
969             else if( ( !p_last || p_last->date < p_pic->date ) &&
970                      ( p_picture == NULL || p_pic->date < p_picture->date ) )
971             {
972                 p_picture = p_pic;
973             }
974         }
975         if( !p_picture )
976         {
977             p_picture = p_last;
978
979             if( !p_vout->p->b_picture_empty )
980             {
981                 p_vout->p->b_picture_empty = true;
982                 vlc_cond_signal( &p_vout->p->picture_wait );
983             }
984         }
985
986         display_date = 0;
987         if( p_picture )
988         {
989             display_date = p_picture->date;
990
991             /* If we found better than the last picture, destroy it */
992             if( p_last && p_picture != p_last )
993             {
994                 vout_UsePictureLocked( p_vout, p_last );
995                 p_vout->p->p_picture_displayed = p_last = NULL;
996             }
997
998             /* Compute FPS rate */
999             p_vout->p->p_fps_sample[ p_vout->p->c_fps_samples++ % VOUT_FPS_SAMPLES ] = display_date;
1000
1001             if( !p_vout->p->b_paused && display_date > current_date + VOUT_DISPLAY_DELAY )
1002             {
1003                 /* A picture is ready to be rendered, but its rendering date
1004                  * is far from the current one so the thread will perform an
1005                  * empty loop as if no picture were found. The picture state
1006                  * is unchanged */
1007                 p_picture    = NULL;
1008                 display_date = 0;
1009             }
1010             else if( p_picture == p_last )
1011             {
1012                 /* We are asked to repeat the previous picture, but we first
1013                  * wait for a couple of idle loops */
1014                 if( i_idle_loops < 4 )
1015                 {
1016                     p_picture    = NULL;
1017                     display_date = 0;
1018                 }
1019                 else
1020                 {
1021                     /* We set the display date to something high, otherwise
1022                      * we'll have lots of problems with late pictures */
1023                     display_date = current_date + p_vout->p->render_time;
1024                 }
1025             }
1026             else if( p_vout->p->b_paused && display_date > current_date + VOUT_DISPLAY_DELAY )
1027             {
1028                 display_date = current_date + VOUT_DISPLAY_DELAY;
1029             }
1030
1031             if( p_picture )
1032             {
1033                 if( p_picture->date > 1 )
1034                 {
1035                     p_vout->p->i_picture_displayed_date = p_picture->date;
1036                     if( p_picture != p_last && !p_vout->p->b_picture_displayed )
1037                     {
1038                         p_vout->p->b_picture_displayed = true;
1039                         vlc_cond_signal( &p_vout->p->picture_wait );
1040                     }
1041                 }
1042                 p_vout->p->p_picture_displayed = p_picture;
1043             }
1044         }
1045
1046         /* */
1047         const int i_postproc_type = p_vout->p->i_picture_qtype;
1048         const int i_postproc_state = (p_vout->p->i_picture_qtype != QTYPE_NONE) - (i_picture_qtype_last != QTYPE_NONE);
1049
1050         const bool b_picture_interlaced = p_vout->p->b_picture_interlaced;
1051         const int  i_picture_interlaced_state = (!!p_vout->p->b_picture_interlaced) - (!!b_picture_interlaced_last);
1052
1053         vlc_mutex_unlock( &p_vout->picture_lock );
1054
1055         if( p_picture == NULL )
1056             i_idle_loops++;
1057
1058         p_filtered_picture = NULL;
1059         if( p_picture )
1060         {
1061             vlc_mutex_lock( &p_vout->p->vfilter_lock );
1062             p_filtered_picture = filter_chain_VideoFilter( p_vout->p->p_vf2_chain,
1063                                                            p_picture );
1064             vlc_mutex_unlock( &p_vout->p->vfilter_lock );
1065         }
1066
1067         const bool b_snapshot = vout_snapshot_IsRequested( &p_vout->p->snapshot );
1068
1069         /*
1070          * Check for subpictures to display
1071          */
1072         mtime_t spu_render_time;
1073         if( p_vout->p->b_paused )
1074             spu_render_time = p_vout->p->i_pause_date;
1075         else if( p_picture )
1076             spu_render_time = p_picture->date > 1 ? p_picture->date : mdate();
1077         else
1078             spu_render_time = 0;
1079
1080         subpicture_t *p_subpic = spu_SortSubpictures( p_vout->p->p_spu,
1081                                                       spu_render_time,
1082                                                       b_snapshot );
1083         /*
1084          * Perform rendering
1085          */
1086         vout_statistic_Update( &p_vout->p->statistic, 1, 0 );
1087         p_directbuffer = vout_RenderPicture( p_vout,
1088                                              p_filtered_picture, p_subpic,
1089                                              spu_render_time );
1090
1091         /*
1092          * Take a snapshot if requested
1093          */
1094         if( p_directbuffer && b_snapshot )
1095             vout_snapshot_Set( &p_vout->p->snapshot,
1096                                &p_vout->fmt_out, p_directbuffer );
1097
1098         /*
1099          * Call the plugin-specific rendering method if there is one
1100          */
1101         if( p_filtered_picture != NULL && p_directbuffer != NULL )
1102         {
1103             /* Render the direct buffer returned by vout_RenderPicture */
1104             vout_RenderWrapper( p_vout, p_directbuffer );
1105         }
1106
1107         /*
1108          * Sleep, wake up
1109          */
1110         if( display_date != 0 && p_directbuffer != NULL )
1111         {
1112             mtime_t current_render_time = mdate() - current_date;
1113             /* if render time is very large we don't include it in the mean */
1114             if( current_render_time < p_vout->p->render_time +
1115                 VOUT_DISPLAY_DELAY )
1116             {
1117                 /* Store render time using a sliding mean weighting to
1118                  * current value in a 3 to 1 ratio*/
1119                 p_vout->p->render_time *= 3;
1120                 p_vout->p->render_time += current_render_time;
1121                 p_vout->p->render_time >>= 2;
1122             }
1123             else
1124                 msg_Dbg( p_vout, "skipped big render time %d > %d", (int) current_render_time,
1125                  (int) (p_vout->p->render_time +VOUT_DISPLAY_DELAY ) ) ;
1126         }
1127
1128         /* Give back change lock */
1129         vlc_mutex_unlock( &p_vout->change_lock );
1130
1131         /* Sleep a while or until a given date */
1132         if( display_date != 0 )
1133         {
1134             /* If there are *vout* filters in the chain, better give them the picture
1135              * in advance */
1136             if( !p_vout->p->psz_filter_chain || !*p_vout->p->psz_filter_chain )
1137             {
1138                 mwait( display_date - VOUT_MWAIT_TOLERANCE );
1139             }
1140         }
1141         else
1142         {
1143             /* Wait until a frame is being sent or a spurious wakeup (not a problem here) */
1144             vlc_mutex_lock( &p_vout->picture_lock );
1145             vlc_cond_timedwait( &p_vout->p->picture_wait, &p_vout->picture_lock, current_date + VOUT_IDLE_SLEEP );
1146             vlc_mutex_unlock( &p_vout->picture_lock );
1147         }
1148
1149         /* On awakening, take back lock and send immediately picture
1150          * to display. */
1151         /* Note: p_vout->p->b_done could be true here and now */
1152         vlc_mutex_lock( &p_vout->change_lock );
1153
1154         /*
1155          * Display the previously rendered picture
1156          */
1157         if( p_filtered_picture != NULL && p_directbuffer != NULL )
1158         {
1159             /* Display the direct buffer returned by vout_RenderPicture */
1160             vout_DisplayWrapper( p_vout, p_directbuffer );
1161
1162             /* Tell the vout this was the last picture and that it does not
1163              * need to be forced anymore. */
1164             p_picture->b_force = false;
1165         }
1166
1167         /* Drop the filtered picture if created by video filters */
1168         if( p_filtered_picture != NULL && p_filtered_picture != p_picture )
1169         {
1170             vlc_mutex_lock( &p_vout->picture_lock );
1171             vout_UsePictureLocked( p_vout, p_filtered_picture );
1172             vlc_mutex_unlock( &p_vout->picture_lock );
1173         }
1174
1175         if( p_picture != NULL )
1176         {
1177             /* Reinitialize idle loop count */
1178             i_idle_loops = 0;
1179         }
1180
1181         /*
1182          * Check events and manage thread
1183          */
1184         if( vout_ManageWrapper( p_vout ) )
1185         {
1186             /* A fatal error occurred, and the thread must terminate
1187              * immediately, without displaying anything - setting b_error to 1
1188              * causes the immediate end of the main while() loop. */
1189             // FIXME pf_end
1190             p_vout->p->b_error = 1;
1191             break;
1192         }
1193
1194         if( p_vout->i_changes & VOUT_ON_TOP_CHANGE )
1195             p_vout->i_changes &= ~VOUT_ON_TOP_CHANGE;
1196
1197         if( p_vout->i_changes & VOUT_PICTURE_BUFFERS_CHANGE )
1198         {
1199             /* This happens when the picture buffers need to be recreated.
1200              * This is useful on multimonitor displays for instance.
1201              *
1202              * Warning: This only works when the vout creates only 1 picture
1203              * buffer!! */
1204             p_vout->i_changes &= ~VOUT_PICTURE_BUFFERS_CHANGE;
1205
1206             vlc_mutex_lock( &p_vout->picture_lock );
1207
1208             vout_EndWrapper( p_vout );
1209
1210             I_OUTPUTPICTURES = I_RENDERPICTURES = 0;
1211
1212             p_vout->p->b_error = InitThread( p_vout );
1213             if( p_vout->p->b_error )
1214                 msg_Err( p_vout, "InitThread after VOUT_PICTURE_BUFFERS_CHANGE failed" );
1215
1216             vlc_cond_signal( &p_vout->p->picture_wait );
1217             vlc_mutex_unlock( &p_vout->picture_lock );
1218
1219             if( p_vout->p->b_error )
1220                 break;
1221         }
1222
1223         /* Post processing */
1224         if( i_postproc_state == 1 )
1225             PostProcessEnable( p_vout );
1226         else if( i_postproc_state == -1 )
1227             PostProcessDisable( p_vout );
1228         if( i_postproc_state != 0 )
1229             i_picture_qtype_last = i_postproc_type;
1230
1231         /* Deinterlacing
1232          * Wait 30s before quiting interlacing mode */
1233         if( ( i_picture_interlaced_state == 1 ) ||
1234             ( i_picture_interlaced_state == -1 && i_picture_interlaced_last_date + 30000000 < current_date ) )
1235         {
1236             DeinterlaceNeeded( p_vout, b_picture_interlaced );
1237             b_picture_interlaced_last = b_picture_interlaced;
1238         }
1239         if( b_picture_interlaced )
1240             i_picture_interlaced_last_date = current_date;
1241
1242
1243         /* Check for "video filter2" changes */
1244         vlc_mutex_lock( &p_vout->p->vfilter_lock );
1245         if( p_vout->p->psz_vf2 )
1246         {
1247             es_format_t fmt;
1248
1249             es_format_Init( &fmt, VIDEO_ES, p_vout->fmt_render.i_chroma );
1250             fmt.video = p_vout->fmt_render;
1251             filter_chain_Reset( p_vout->p->p_vf2_chain, &fmt, &fmt );
1252
1253             if( filter_chain_AppendFromString( p_vout->p->p_vf2_chain,
1254                                                p_vout->p->psz_vf2 ) < 0 )
1255                 msg_Err( p_vout, "Video filter chain creation failed" );
1256
1257             free( p_vout->p->psz_vf2 );
1258             p_vout->p->psz_vf2 = NULL;
1259
1260             if( i_picture_qtype_last != QTYPE_NONE )
1261                 PostProcessSetFilterQuality( p_vout );
1262         }
1263         vlc_mutex_unlock( &p_vout->p->vfilter_lock );
1264     }
1265
1266     /*
1267      * Error loop - wait until the thread destruction is requested
1268      */
1269     if( p_vout->p->b_error )
1270         ErrorThread( p_vout );
1271
1272     /* Clean thread */
1273     CleanThread( p_vout );
1274
1275 exit_thread:
1276     /* End of thread */
1277     EndThread( p_vout );
1278     vlc_mutex_unlock( &p_vout->change_lock );
1279
1280     if( b_has_wrapper )
1281         vout_CloseWrapper( p_vout );
1282
1283     return NULL;
1284 }
1285
1286 /*****************************************************************************
1287  * ErrorThread: RunThread() error loop
1288  *****************************************************************************
1289  * This function is called when an error occurred during thread main's loop.
1290  * The thread can still receive feed, but must be ready to terminate as soon
1291  * as possible.
1292  *****************************************************************************/
1293 static void ErrorThread( vout_thread_t *p_vout )
1294 {
1295     /* Wait until a `close' order */
1296     while( !p_vout->p->b_done )
1297         vlc_cond_wait( &p_vout->p->change_wait, &p_vout->change_lock );
1298 }
1299
1300 /*****************************************************************************
1301  * CleanThread: clean up after InitThread
1302  *****************************************************************************
1303  * This function is called after a sucessful
1304  * initialization. It frees all resources allocated by InitThread.
1305  * XXX You have to enter it with change_lock taken.
1306  *****************************************************************************/
1307 static void CleanThread( vout_thread_t *p_vout )
1308 {
1309     int     i_index;                                        /* index in heap */
1310
1311     /* Destroy all remaining pictures */
1312     for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++ )
1313     {
1314         if ( p_vout->p_picture[i_index].i_type == MEMORY_PICTURE )
1315         {
1316             free( p_vout->p_picture[i_index].p_data_orig );
1317         }
1318     }
1319
1320     /* Destroy translation tables */
1321     if( !p_vout->p->b_error )
1322         vout_EndWrapper( p_vout );
1323 }
1324
1325 /*****************************************************************************
1326  * EndThread: thread destruction
1327  *****************************************************************************
1328  * This function is called when the thread ends.
1329  * It frees all resources not allocated by InitThread.
1330  * XXX You have to enter it with change_lock taken.
1331  *****************************************************************************/
1332 static void EndThread( vout_thread_t *p_vout )
1333 {
1334     /* FIXME does that function *really* need to be called inside the thread ? */
1335
1336     /* Detach subpicture unit from both input and vout */
1337     spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), false );
1338     vlc_object_detach( p_vout->p->p_spu );
1339
1340     /* Destroy the video filters2 */
1341     filter_chain_Delete( p_vout->p->p_vf2_chain );
1342 }
1343
1344 /* following functions are local */
1345
1346 /**
1347  * This function copies all RGB informations from a picture_heap_t into
1348  * a video_format_t
1349  */
1350 static void VideoFormatImportRgb( video_format_t *p_fmt, const picture_heap_t *p_heap )
1351 {
1352     p_fmt->i_rmask = p_heap->i_rmask;
1353     p_fmt->i_gmask = p_heap->i_gmask;
1354     p_fmt->i_bmask = p_heap->i_bmask;
1355     p_fmt->i_rrshift = p_heap->i_rrshift;
1356     p_fmt->i_lrshift = p_heap->i_lrshift;
1357     p_fmt->i_rgshift = p_heap->i_rgshift;
1358     p_fmt->i_lgshift = p_heap->i_lgshift;
1359     p_fmt->i_rbshift = p_heap->i_rbshift;
1360     p_fmt->i_lbshift = p_heap->i_lbshift;
1361 }
1362
1363 /**
1364  * This funtion copes all RGB informations from a video_format_t into
1365  * a picture_heap_t
1366  */
1367 static void VideoFormatExportRgb( const video_format_t *p_fmt, picture_heap_t *p_heap )
1368 {
1369     p_heap->i_rmask = p_fmt->i_rmask;
1370     p_heap->i_gmask = p_fmt->i_gmask;
1371     p_heap->i_bmask = p_fmt->i_bmask;
1372     p_heap->i_rrshift = p_fmt->i_rrshift;
1373     p_heap->i_lrshift = p_fmt->i_lrshift;
1374     p_heap->i_rgshift = p_fmt->i_rgshift;
1375     p_heap->i_lgshift = p_fmt->i_lgshift;
1376     p_heap->i_rbshift = p_fmt->i_rbshift;
1377     p_heap->i_lbshift = p_fmt->i_lbshift;
1378 }
1379
1380 /**
1381  * This function computes rgb shifts from masks
1382  */
1383 static void PictureHeapFixRgb( picture_heap_t *p_heap )
1384 {
1385     video_format_t fmt;
1386
1387     /* */
1388     fmt.i_chroma = p_heap->i_chroma;
1389     VideoFormatImportRgb( &fmt, p_heap );
1390
1391     /* */
1392     video_format_FixRgb( &fmt );
1393
1394     VideoFormatExportRgb( &fmt, p_heap );
1395 }
1396
1397 /*****************************************************************************
1398  * object variables callbacks: a bunch of object variables are used by the
1399  * interfaces to interact with the vout.
1400  *****************************************************************************/
1401 static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd,
1402                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1403 {
1404     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1405     input_thread_t *p_input;
1406     (void)psz_cmd; (void)oldval; (void)p_data;
1407
1408     p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1409                                                  FIND_PARENT );
1410     if (!p_input)
1411     {
1412         msg_Err( p_vout, "Input not found" );
1413         return VLC_EGENERIC;
1414     }
1415
1416     var_SetBool( p_vout, "intf-change", true );
1417
1418     /* Modify input as well because the vout might have to be restarted */
1419     var_Create( p_input, "vout-filter", VLC_VAR_STRING );
1420     var_SetString( p_input, "vout-filter", newval.psz_string );
1421
1422     /* Now restart current video stream */
1423     input_Control( p_input, INPUT_RESTART_ES, -VIDEO_ES );
1424     vlc_object_release( p_input );
1425
1426     return VLC_SUCCESS;
1427 }
1428
1429 /*****************************************************************************
1430  * Video Filter2 stuff
1431  *****************************************************************************/
1432 static int VideoFilter2Callback( vlc_object_t *p_this, char const *psz_cmd,
1433                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1434 {
1435     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1436     (void)psz_cmd; (void)oldval; (void)p_data;
1437
1438     vlc_mutex_lock( &p_vout->p->vfilter_lock );
1439     p_vout->p->psz_vf2 = strdup( newval.psz_string );
1440     vlc_mutex_unlock( &p_vout->p->vfilter_lock );
1441
1442     return VLC_SUCCESS;
1443 }
1444
1445 /*****************************************************************************
1446  * Post-processing
1447  *****************************************************************************/
1448 static bool PostProcessIsPresent( const char *psz_filter )
1449 {
1450     const char  *psz_pp = "postproc";
1451     const size_t i_pp = strlen(psz_pp);
1452     return psz_filter &&
1453            !strncmp( psz_filter, psz_pp, strlen(psz_pp) ) &&
1454            ( psz_filter[i_pp] == '\0' || psz_filter[i_pp] == ':' );
1455 }
1456
1457 static int PostProcessCallback( vlc_object_t *p_this, char const *psz_cmd,
1458                                 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1459 {
1460     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1461     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1462
1463     static const char *psz_pp = "postproc";
1464
1465     char *psz_vf2 = var_GetString( p_vout, "video-filter" );
1466
1467     if( newval.i_int <= 0 )
1468     {
1469         if( PostProcessIsPresent( psz_vf2 ) )
1470         {
1471             strcpy( psz_vf2, &psz_vf2[strlen(psz_pp)] );
1472             if( *psz_vf2 == ':' )
1473                 strcpy( psz_vf2, &psz_vf2[1] );
1474         }
1475     }
1476     else
1477     {
1478         if( !PostProcessIsPresent( psz_vf2 ) )
1479         {
1480             if( psz_vf2 )
1481             {
1482                 char *psz_tmp = psz_vf2;
1483                 if( asprintf( &psz_vf2, "%s:%s", psz_pp, psz_tmp ) < 0 )
1484                     psz_vf2 = psz_tmp;
1485                 else
1486                     free( psz_tmp );
1487             }
1488             else
1489             {
1490                 psz_vf2 = strdup( psz_pp );
1491             }
1492         }
1493     }
1494     if( psz_vf2 )
1495     {
1496         var_SetString( p_vout, "video-filter", psz_vf2 );
1497         free( psz_vf2 );
1498     }
1499
1500     return VLC_SUCCESS;
1501 }
1502 static void PostProcessEnable( vout_thread_t *p_vout )
1503 {
1504     vlc_value_t text;
1505     msg_Dbg( p_vout, "Post-processing available" );
1506     var_Create( p_vout, "postprocess", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
1507     text.psz_string = _("Post processing");
1508     var_Change( p_vout, "postprocess", VLC_VAR_SETTEXT, &text, NULL );
1509
1510     for( int i = 0; i <= 6; i++ )
1511     {
1512         vlc_value_t val;
1513         vlc_value_t text;
1514         char psz_text[1+1];
1515
1516         val.i_int = i;
1517         snprintf( psz_text, sizeof(psz_text), "%d", i );
1518         if( i == 0 )
1519             text.psz_string = _("Disable");
1520         else
1521             text.psz_string = psz_text;
1522         var_Change( p_vout, "postprocess", VLC_VAR_ADDCHOICE, &val, &text );
1523     }
1524     var_AddCallback( p_vout, "postprocess", PostProcessCallback, NULL );
1525
1526     /* */
1527     char *psz_filter = var_GetNonEmptyString( p_vout, "video-filter" );
1528     int i_postproc_q = 0;
1529     if( PostProcessIsPresent( psz_filter ) )
1530         i_postproc_q = var_CreateGetInteger( p_vout, "postproc-q" );
1531
1532     var_SetInteger( p_vout, "postprocess", i_postproc_q );
1533
1534     free( psz_filter );
1535 }
1536 static void PostProcessDisable( vout_thread_t *p_vout )
1537 {
1538     msg_Dbg( p_vout, "Post-processing no more available" );
1539     var_Destroy( p_vout, "postprocess" );
1540 }
1541 static void PostProcessSetFilterQuality( vout_thread_t *p_vout )
1542 {
1543     vlc_object_t *p_pp = vlc_object_find_name( p_vout, "postproc", FIND_CHILD );
1544     if( !p_pp )
1545         return;
1546
1547     var_SetInteger( p_pp, "postproc-q", var_GetInteger( p_vout, "postprocess" ) );
1548     vlc_object_release( p_pp );
1549 }
1550
1551
1552 static void DisplayTitleOnOSD( vout_thread_t *p_vout )
1553 {
1554     const mtime_t i_start = mdate();
1555     const mtime_t i_stop = i_start + INT64_C(1000) * p_vout->p->i_title_timeout;
1556
1557     if( i_stop <= i_start )
1558         return;
1559
1560     vlc_assert_locked( &p_vout->change_lock );
1561
1562     vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1563                            p_vout->p->psz_title, NULL,
1564                            p_vout->p->i_title_position,
1565                            30 + p_vout->fmt_in.i_width
1566                               - p_vout->fmt_in.i_visible_width
1567                               - p_vout->fmt_in.i_x_offset,
1568                            20 + p_vout->fmt_in.i_y_offset,
1569                            i_start, i_stop );
1570
1571     free( p_vout->p->psz_title );
1572
1573     p_vout->p->psz_title = NULL;
1574 }
1575
1576 /*****************************************************************************
1577  * Deinterlacing
1578  *****************************************************************************/
1579 typedef struct
1580 {
1581     const char *psz_mode;
1582     bool       b_vout_filter;
1583 } deinterlace_mode_t;
1584
1585 /* XXX
1586  * You can use the non vout filter if and only if the video properties stay the
1587  * same (width/height/chroma/fps), at least for now.
1588  */
1589 static const deinterlace_mode_t p_deinterlace_mode[] = {
1590     { "",        false },
1591     //{ "discard", true },
1592     { "blend",   false },
1593     //{ "mean",    true  },
1594     //{ "bob",     true },
1595     //{ "linear",  true },
1596     { "x",       false },
1597     //{ "yadif",   true },
1598     //{ "yadif2x", true },
1599     { NULL,      false }
1600 };
1601 static const deinterlace_mode_t *DeinterlaceGetMode( const char *psz_mode )
1602 {
1603     for( const deinterlace_mode_t *p_mode = &p_deinterlace_mode[0]; p_mode->psz_mode; p_mode++ )
1604     {
1605         if( !strcmp( p_mode->psz_mode, psz_mode ) )
1606             return p_mode;
1607     }
1608     return NULL;
1609 }
1610
1611 static char *FilterFind( char *psz_filter_base, const char *psz_module )
1612 {
1613     const size_t i_module = strlen( psz_module );
1614     const char *psz_filter = psz_filter_base;
1615
1616     if( !psz_filter || i_module <= 0 )
1617         return NULL;
1618
1619     for( ;; )
1620     {
1621         char *psz_find = strstr( psz_filter, psz_module );
1622         if( !psz_find )
1623             return NULL;
1624         if( psz_find[i_module] == '\0' || psz_find[i_module] == ':' )
1625             return psz_find;
1626         psz_filter = &psz_find[i_module];
1627     }
1628 }
1629
1630 static bool DeinterlaceIsPresent( vout_thread_t *p_vout, bool b_vout_filter )
1631 {
1632     char *psz_filter = var_GetNonEmptyString( p_vout, b_vout_filter ? "vout-filter" : "video-filter" );
1633
1634     bool b_found = FilterFind( psz_filter, "deinterlace" ) != NULL;
1635
1636     free( psz_filter );
1637
1638     return b_found;
1639 }
1640
1641 static void DeinterlaceRemove( vout_thread_t *p_vout, bool b_vout_filter )
1642 {
1643     const char *psz_variable = b_vout_filter ? "vout-filter" : "video-filter";
1644     char *psz_filter = var_GetNonEmptyString( p_vout, psz_variable );
1645
1646     char *psz = FilterFind( psz_filter, "deinterlace" );
1647     if( !psz )
1648     {
1649         free( psz_filter );
1650         return;
1651     }
1652
1653     /* */
1654     strcpy( &psz[0], &psz[strlen("deinterlace")] );
1655     if( *psz == ':' )
1656         strcpy( &psz[0], &psz[1] );
1657
1658     var_SetString( p_vout, psz_variable, psz_filter );
1659     free( psz_filter );
1660 }
1661 static void DeinterlaceAdd( vout_thread_t *p_vout, bool b_vout_filter )
1662 {
1663     const char *psz_variable = b_vout_filter ? "vout-filter" : "video-filter";
1664
1665     char *psz_filter = var_GetNonEmptyString( p_vout, psz_variable );
1666
1667     if( FilterFind( psz_filter, "deinterlace" ) )
1668     {
1669         free( psz_filter );
1670         return;
1671     }
1672
1673     /* */
1674     if( psz_filter )
1675     {
1676         char *psz_tmp = psz_filter;
1677         if( asprintf( &psz_filter, "%s:%s", psz_tmp, "deinterlace" ) < 0 )
1678             psz_filter = psz_tmp;
1679         else
1680             free( psz_tmp );
1681     }
1682     else
1683     {
1684         psz_filter = strdup( "deinterlace" );
1685     }
1686
1687     if( psz_filter )
1688     {
1689         var_SetString( p_vout, psz_variable, psz_filter );
1690         free( psz_filter );
1691     }
1692 }
1693
1694 static void DeinterlaceSave( vout_thread_t *p_vout, int i_deinterlace, const char *psz_mode, bool is_needed )
1695 {
1696     /* We have to set input variable to ensure restart support
1697      * XXX it is only needed because of vout-filter but must be done
1698      * for non video filter anyway */
1699     vlc_object_t *p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, FIND_PARENT );
1700     if( !p_input )
1701         return;
1702
1703     /* Another hack for "vout filter" mode */
1704     if( i_deinterlace < 0 )
1705         i_deinterlace = is_needed ? -2 : -3;
1706
1707     var_Create( p_input, "deinterlace", VLC_VAR_INTEGER );
1708     var_SetInteger( p_input, "deinterlace", i_deinterlace );
1709
1710     static const char * const ppsz_variable[] = {
1711         "deinterlace-mode",
1712         "filter-deinterlace-mode",
1713         "sout-deinterlace-mode",
1714         NULL
1715     };
1716     for( int i = 0; ppsz_variable[i]; i++ )
1717     {
1718         var_Create( p_input, ppsz_variable[i], VLC_VAR_STRING );
1719         var_SetString( p_input, ppsz_variable[i], psz_mode );
1720     }
1721
1722     vlc_object_release( p_input );
1723 }
1724 static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd,
1725                                 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1726 {
1727     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(newval); VLC_UNUSED(p_data);
1728     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1729
1730     /* */
1731     const int  i_deinterlace = var_GetInteger( p_this, "deinterlace" );
1732     char       *psz_mode     = var_GetString( p_this, "deinterlace-mode" );
1733     const bool is_needed     = var_GetBool( p_this, "deinterlace-needed" );
1734     if( !psz_mode )
1735         return VLC_EGENERIC;
1736
1737     DeinterlaceSave( p_vout, i_deinterlace, psz_mode, is_needed );
1738
1739     /* */
1740     bool b_vout_filter = false;
1741     const deinterlace_mode_t *p_mode = DeinterlaceGetMode( psz_mode );
1742     if( p_mode )
1743         b_vout_filter = p_mode->b_vout_filter;
1744
1745     /* */
1746     char *psz_old;
1747     if( b_vout_filter )
1748     {
1749         psz_old = var_CreateGetString( p_vout, "filter-deinterlace-mode" );
1750     }
1751     else
1752     {
1753         psz_old = var_CreateGetString( p_vout, "sout-deinterlace-mode" );
1754         var_SetString( p_vout, "sout-deinterlace-mode", psz_mode );
1755     }
1756
1757     msg_Dbg( p_vout, "deinterlace %d, mode %s, is_needed %d", i_deinterlace, psz_mode, is_needed );
1758     if( i_deinterlace == 0 || ( i_deinterlace == -1 && !is_needed ) )
1759     {
1760         DeinterlaceRemove( p_vout, false );
1761         DeinterlaceRemove( p_vout, true );
1762     }
1763     else
1764     {
1765         if( !DeinterlaceIsPresent( p_vout, b_vout_filter ) )
1766         {
1767             DeinterlaceRemove( p_vout, !b_vout_filter );
1768             DeinterlaceAdd( p_vout, b_vout_filter );
1769         }
1770         else
1771         {
1772             /* The deinterlace filter was already inserted but we have changed the mode */
1773             DeinterlaceRemove( p_vout, !b_vout_filter );
1774             if( psz_old && strcmp( psz_old, psz_mode ) )
1775                 var_TriggerCallback( p_vout, b_vout_filter ? "vout-filter" : "video-filter" );
1776         }
1777     }
1778
1779     /* */
1780     free( psz_old );
1781     free( psz_mode );
1782     return VLC_SUCCESS;
1783 }
1784
1785 static void DeinterlaceEnable( vout_thread_t *p_vout )
1786 {
1787     vlc_value_t val, text;
1788
1789     if( !p_vout->p->b_first_vout )
1790         return;
1791
1792     msg_Dbg( p_vout, "Deinterlacing available" );
1793
1794     /* Create the configuration variables */
1795     /* */
1796     var_Create( p_vout, "deinterlace", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT | VLC_VAR_HASCHOICE );
1797     int i_deinterlace = var_GetInteger( p_vout, "deinterlace" );
1798
1799     text.psz_string = _("Deinterlace");
1800     var_Change( p_vout, "deinterlace", VLC_VAR_SETTEXT, &text, NULL );
1801
1802     const module_config_t *p_optd = config_FindConfig( VLC_OBJECT(p_vout), "deinterlace" );
1803     var_Change( p_vout, "deinterlace", VLC_VAR_CLEARCHOICES, NULL, NULL );
1804     for( int i = 0; p_optd && i < p_optd->i_list; i++ )
1805     {
1806         val.i_int  = p_optd->pi_list[i];
1807         text.psz_string = (char*)vlc_gettext(p_optd->ppsz_list_text[i]);
1808         var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
1809     }
1810     var_AddCallback( p_vout, "deinterlace", DeinterlaceCallback, NULL );
1811     /* */
1812     var_Create( p_vout, "deinterlace-mode", VLC_VAR_STRING | VLC_VAR_DOINHERIT | VLC_VAR_HASCHOICE );
1813     char *psz_deinterlace = var_GetNonEmptyString( p_vout, "deinterlace-mode" );
1814
1815     text.psz_string = _("Deinterlace mode");
1816     var_Change( p_vout, "deinterlace-mode", VLC_VAR_SETTEXT, &text, NULL );
1817
1818     const module_config_t *p_optm = config_FindConfig( VLC_OBJECT(p_vout), "deinterlace-mode" );
1819     var_Change( p_vout, "deinterlace-mode", VLC_VAR_CLEARCHOICES, NULL, NULL );
1820     for( int i = 0; p_optm && i < p_optm->i_list; i++ )
1821     {
1822         if( !DeinterlaceGetMode( p_optm->ppsz_list[i] ) )
1823             continue;
1824
1825         val.psz_string  = p_optm->ppsz_list[i];
1826         text.psz_string = (char*)vlc_gettext(p_optm->ppsz_list_text[i]);
1827         var_Change( p_vout, "deinterlace-mode", VLC_VAR_ADDCHOICE, &val, &text );
1828     }
1829     var_AddCallback( p_vout, "deinterlace-mode", DeinterlaceCallback, NULL );
1830     /* */
1831     var_Create( p_vout, "deinterlace-needed", VLC_VAR_BOOL );
1832     var_AddCallback( p_vout, "deinterlace-needed", DeinterlaceCallback, NULL );
1833
1834     /* Override the initial value from filters if present */
1835     char *psz_filter_mode = NULL;
1836     if( DeinterlaceIsPresent( p_vout, true ) )
1837         psz_filter_mode = var_CreateGetNonEmptyString( p_vout, "filter-deinterlace-mode" );
1838     else if( DeinterlaceIsPresent( p_vout, false ) )
1839         psz_filter_mode = var_CreateGetNonEmptyString( p_vout, "sout-deinterlace-mode" );
1840     if( psz_filter_mode )
1841     {
1842         free( psz_deinterlace );
1843         if( i_deinterlace >= -1 )
1844             i_deinterlace = 1;
1845         psz_deinterlace = psz_filter_mode;
1846     }
1847
1848     /* */
1849     if( i_deinterlace == -2 )
1850         p_vout->p->b_picture_interlaced = true;
1851     else if( i_deinterlace == -3 )
1852         p_vout->p->b_picture_interlaced = false;
1853     if( i_deinterlace < 0 )
1854         i_deinterlace = -1;
1855
1856     /* */
1857     val.psz_string = psz_deinterlace ? psz_deinterlace : p_optm->orig.psz;
1858     var_Change( p_vout, "deinterlace-mode", VLC_VAR_SETVALUE, &val, NULL );
1859     val.b_bool = p_vout->p->b_picture_interlaced;
1860     var_Change( p_vout, "deinterlace-needed", VLC_VAR_SETVALUE, &val, NULL );
1861
1862     var_SetInteger( p_vout, "deinterlace", i_deinterlace );
1863     free( psz_deinterlace );
1864 }
1865
1866 static void DeinterlaceNeeded( vout_thread_t *p_vout, bool is_interlaced )
1867 {
1868     msg_Dbg( p_vout, "Detected %s video",
1869              is_interlaced ? "interlaced" : "progressive" );
1870     var_SetBool( p_vout, "deinterlace-needed", is_interlaced );
1871 }
1872