]> git.sesse.net Git - vlc/blob - src/video_output/video_output.c
Split vout_FixLeaks into vout_FixLeaks and vout_Reset.
[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  *          Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
14  *
15  * This program is free software; you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation; either version 2 of the License, or
18  * (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, write to the Free Software
27  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
28  *****************************************************************************/
29
30 /*****************************************************************************
31  * Preamble
32  *****************************************************************************/
33 #ifdef HAVE_CONFIG_H
34 # include "config.h"
35 #endif
36
37 #include <vlc_common.h>
38
39 #include <stdlib.h>                                                /* free() */
40 #include <string.h>
41 #include <assert.h>
42
43 #include <vlc_vout.h>
44
45 #include <vlc_filter.h>
46 #include <vlc_osd.h>
47
48 #include <libvlc.h>
49 #include <vlc_input.h>
50 #include "vout_pictures.h"
51 #include "vout_internal.h"
52
53 /*****************************************************************************
54  * Local prototypes
55  *****************************************************************************/
56 static void     *Thread(void *);
57 static void     vout_Destructor(vlc_object_t *);
58
59 /* Object variables callbacks */
60 static int FilterCallback( vlc_object_t *, char const *,
61                            vlc_value_t, vlc_value_t, void * );
62 static int VideoFilter2Callback( vlc_object_t *, char const *,
63                                  vlc_value_t, vlc_value_t, void * );
64
65 /* */
66 static void PostProcessEnable( vout_thread_t * );
67 static void PostProcessDisable( vout_thread_t * );
68 static void PostProcessSetFilterQuality( vout_thread_t *p_vout );
69 static int  PostProcessCallback( vlc_object_t *, char const *,
70                                  vlc_value_t, vlc_value_t, void * );
71 /* */
72 static void DeinterlaceEnable( vout_thread_t * );
73 static void DeinterlaceNeeded( vout_thread_t *, bool );
74
75 /* Display media title in OSD */
76 static void DisplayTitleOnOSD( vout_thread_t *p_vout );
77
78 /* */
79 static void PrintVideoFormat(vout_thread_t *, const char *, const video_format_t *);
80
81 /* Maximum delay between 2 displayed pictures.
82  * XXX it is needed for now but should be removed in the long term.
83  */
84 #define VOUT_REDISPLAY_DELAY (INT64_C(80000))
85
86 /**
87  * Late pictures having a delay higher than this value are thrashed.
88  */
89 #define VOUT_DISPLAY_LATE_THRESHOLD (INT64_C(20000))
90
91 /* Better be in advance when awakening than late... */
92 #define VOUT_MWAIT_TOLERANCE (INT64_C(1000))
93
94 /*****************************************************************************
95  * Video Filter2 functions
96  *****************************************************************************/
97 static picture_t *video_new_buffer_filter( filter_t *p_filter )
98 {
99     vout_thread_t *p_vout = (vout_thread_t*)p_filter->p_owner;
100     return picture_pool_Get(p_vout->p->private_pool);
101 }
102
103 static void video_del_buffer_filter( filter_t *p_filter, picture_t *p_pic )
104 {
105     VLC_UNUSED(p_filter);
106     picture_Release(p_pic);
107 }
108
109 static int video_filter_buffer_allocation_init( filter_t *p_filter, void *p_data )
110 {
111     p_filter->pf_video_buffer_new = video_new_buffer_filter;
112     p_filter->pf_video_buffer_del = video_del_buffer_filter;
113     p_filter->p_owner = p_data; /* p_vout */
114     return VLC_SUCCESS;
115 }
116
117 #undef vout_Request
118 /*****************************************************************************
119  * vout_Request: find a video output thread, create one, or destroy one.
120  *****************************************************************************
121  * This function looks for a video output thread matching the current
122  * properties. If not found, it spawns a new one.
123  *****************************************************************************/
124 vout_thread_t *vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout,
125                              video_format_t *p_fmt )
126 {
127     if( !p_fmt )
128     {
129         /* Video output is no longer used.
130          * TODO: support for reusing video outputs with proper _thread-safe_
131          * reference handling. */
132         if( p_vout )
133             vout_CloseAndRelease( p_vout );
134         return NULL;
135     }
136
137     /* If a video output was provided, lock it, otherwise look for one. */
138     if( p_vout )
139     {
140         vlc_object_hold( p_vout );
141     }
142
143     /* TODO: find a suitable unused video output */
144
145     /* If we now have a video output, check it has the right properties */
146     if( p_vout )
147     {
148         vlc_mutex_lock( &p_vout->p->change_lock );
149
150         /* We don't directly check for the "vout-filter" variable for obvious
151          * performance reasons. */
152         if( p_vout->p->b_filter_change )
153         {
154             char *psz_filter_chain = var_GetString( p_vout, "vout-filter" );
155
156             if( psz_filter_chain && !*psz_filter_chain )
157             {
158                 free( psz_filter_chain );
159                 psz_filter_chain = NULL;
160             }
161             if( p_vout->p->psz_filter_chain && !*p_vout->p->psz_filter_chain )
162             {
163                 free( p_vout->p->psz_filter_chain );
164                 p_vout->p->psz_filter_chain = NULL;
165             }
166
167             if( !psz_filter_chain && !p_vout->p->psz_filter_chain )
168             {
169                 p_vout->p->b_filter_change = false;
170             }
171
172             free( psz_filter_chain );
173         }
174
175 #warning "FIXME: Check RGB masks in vout_Request"
176         /* FIXME: check RGB masks */
177         if( p_vout->fmt_render.i_chroma != vlc_fourcc_GetCodec( VIDEO_ES, p_fmt->i_chroma ) ||
178             p_vout->fmt_render.i_width != p_fmt->i_width ||
179             p_vout->fmt_render.i_height != p_fmt->i_height ||
180             p_vout->p->b_filter_change )
181         {
182             vlc_mutex_unlock( &p_vout->p->change_lock );
183
184             /* We are not interested in this format, close this vout */
185             vout_CloseAndRelease( p_vout );
186             vlc_object_release( p_vout );
187             p_vout = NULL;
188         }
189         else
190         {
191             /* This video output is cool! Hijack it. */
192             /* Correct aspect ratio on change
193              * FIXME factorize this code with other aspect ration related code */
194             unsigned int i_sar_num;
195             unsigned int i_sar_den;
196             vlc_ureduce( &i_sar_num, &i_sar_den,
197                          p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 );
198 #if 0
199             /* What's that, it does not seems to be used correcly everywhere */
200             if( p_vout->i_par_num > 0 && p_vout->i_par_den > 0 )
201             {
202                 i_sar_num *= p_vout->i_par_den;
203                 i_sar_den *= p_vout->i_par_num;
204             }
205 #endif
206
207             if( i_sar_num > 0 && i_sar_den > 0 &&
208                 ( i_sar_num != p_vout->fmt_render.i_sar_num ||
209                   i_sar_den != p_vout->fmt_render.i_sar_den ) )
210             {
211                 p_vout->fmt_in.i_sar_num = i_sar_num;
212                 p_vout->fmt_in.i_sar_den = i_sar_den;
213
214                 p_vout->fmt_render.i_sar_num = i_sar_num;
215                 p_vout->fmt_render.i_sar_den = i_sar_den;
216                 p_vout->p->i_changes |= VOUT_ASPECT_CHANGE;
217             }
218             vlc_mutex_unlock( &p_vout->p->change_lock );
219
220             vlc_object_release( p_vout );
221         }
222
223         if( p_vout )
224         {
225             msg_Dbg( p_this, "reusing provided vout" );
226
227             spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), false );
228             vlc_object_detach( p_vout );
229
230             vlc_object_attach( p_vout, p_this );
231             spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), true );
232         }
233     }
234
235     if( !p_vout )
236     {
237         msg_Dbg( p_this, "no usable vout present, spawning one" );
238
239         p_vout = vout_Create( p_this, p_fmt );
240     }
241
242     return p_vout;
243 }
244
245 /*****************************************************************************
246  * vout_Create: creates a new video output thread
247  *****************************************************************************
248  * This function creates a new video output thread, and returns a pointer
249  * to its description. On error, it returns NULL.
250  *****************************************************************************/
251 vout_thread_t * (vout_Create)( vlc_object_t *p_parent, video_format_t *p_fmt )
252 {
253     vout_thread_t  *p_vout;                            /* thread descriptor */
254     vlc_value_t     text;
255
256
257     config_chain_t *p_cfg;
258     char *psz_parser;
259     char *psz_name;
260
261     if( p_fmt->i_width <= 0 || p_fmt->i_height <= 0 )
262         return NULL;
263     const vlc_fourcc_t i_chroma = vlc_fourcc_GetCodec( VIDEO_ES, p_fmt->i_chroma );
264
265     vlc_ureduce( &p_fmt->i_sar_num, &p_fmt->i_sar_den,
266                  p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 );
267     if( p_fmt->i_sar_num <= 0 || p_fmt->i_sar_den <= 0 )
268         return NULL;
269
270     /* Allocate descriptor */
271     static const char typename[] = "video output";
272     p_vout = vlc_custom_create( p_parent, sizeof( *p_vout ), VLC_OBJECT_VOUT,
273                                 typename );
274     if( p_vout == NULL )
275         return NULL;
276
277     /* */
278     p_vout->p = calloc( 1, sizeof(*p_vout->p) );
279     if( !p_vout->p )
280     {
281         vlc_object_release( p_vout );
282         return NULL;
283     }
284
285     /* */
286     p_vout->fmt_render        = *p_fmt;   /* FIXME palette */
287     p_vout->fmt_in            = *p_fmt;   /* FIXME palette */
288
289     p_vout->fmt_render.i_chroma = 
290     p_vout->fmt_in.i_chroma     = i_chroma;
291     video_format_FixRgb( &p_vout->fmt_render );
292     video_format_FixRgb( &p_vout->fmt_in );
293
294     /* Initialize misc stuff */
295     p_vout->p->i_changes    = 0;
296     p_vout->p->b_fullscreen = 0;
297     vout_chrono_Init( &p_vout->p->render, 5, 10000 ); /* Arbitrary initial time */
298     vout_statistic_Init( &p_vout->p->statistic );
299     p_vout->p->b_filter_change = 0;
300     p_vout->p->i_par_num =
301     p_vout->p->i_par_den = 1;
302     p_vout->p->is_late_dropped = var_InheritBool( p_vout, "drop-late-frames" );
303     p_vout->p->b_picture_empty = false;
304     p_vout->p->displayed.date = VLC_TS_INVALID;
305     p_vout->p->displayed.decoded = NULL;
306     p_vout->p->displayed.timestamp = VLC_TS_INVALID;
307     p_vout->p->displayed.qtype = QTYPE_NONE;
308     p_vout->p->displayed.is_interlaced = false;
309
310     p_vout->p->step.is_requested = false;
311     p_vout->p->step.last         = VLC_TS_INVALID;
312     p_vout->p->step.timestamp    = VLC_TS_INVALID;
313
314     p_vout->p->pause.is_on  = false;
315     p_vout->p->pause.date   = VLC_TS_INVALID;
316
317     p_vout->p->decoder_fifo = picture_fifo_New();
318     p_vout->p->decoder_pool = NULL;
319
320     vlc_mouse_Init( &p_vout->p->mouse );
321
322     vout_snapshot_Init( &p_vout->p->snapshot );
323
324     /* Initialize locks */
325     vlc_mutex_init( &p_vout->p->picture_lock );
326     vlc_cond_init( &p_vout->p->picture_wait );
327     vlc_mutex_init( &p_vout->p->change_lock );
328     vlc_mutex_init( &p_vout->p->vfilter_lock );
329
330     /* Mouse coordinates */
331     var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER );
332     var_Create( p_vout, "mouse-moved", VLC_VAR_COORDS );
333     var_Create( p_vout, "mouse-clicked", VLC_VAR_COORDS );
334     /* Mouse object (area of interest in a video filter) */
335     var_Create( p_vout, "mouse-object", VLC_VAR_BOOL );
336
337     /* Attach the new object now so we can use var inheritance below */
338     vlc_object_attach( p_vout, p_parent );
339
340     /* Initialize subpicture unit */
341     p_vout->p->p_spu = spu_Create( p_vout );
342
343     /* */
344     spu_Init( p_vout->p->p_spu );
345
346     spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), true );
347
348     /* Take care of some "interface/control" related initialisations */
349     vout_IntfInit( p_vout );
350
351     /* If the parent is not a VOUT object, that means we are at the start of
352      * the video output pipe */
353     if( vlc_internals( p_parent )->i_object_type != VLC_OBJECT_VOUT )
354     {
355         /* Look for the default filter configuration */
356         p_vout->p->psz_filter_chain =
357             var_CreateGetStringCommand( p_vout, "vout-filter" );
358
359         /* Apply video filter2 objects on the first vout */
360         p_vout->p->psz_vf2 =
361             var_CreateGetStringCommand( p_vout, "video-filter" );
362
363         p_vout->p->b_first_vout = true;
364     }
365     else
366     {
367         /* continue the parent's filter chain */
368         char *psz_tmp;
369
370         /* Ugly hack to jump to our configuration chain */
371         p_vout->p->psz_filter_chain
372             = ((vout_thread_t *)p_parent)->p->psz_filter_chain;
373         p_vout->p->psz_filter_chain
374             = config_ChainCreate( &psz_tmp, &p_cfg, p_vout->p->psz_filter_chain );
375         config_ChainDestroy( p_cfg );
376         free( psz_tmp );
377
378         /* Create a video filter2 var ... but don't inherit values */
379         var_Create( p_vout, "video-filter",
380                     VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
381         p_vout->p->psz_vf2 = var_GetString( p_vout, "video-filter" );
382
383         /* */
384         p_vout->p->b_first_vout = false;
385     }
386
387     var_AddCallback( p_vout, "video-filter", VideoFilter2Callback, NULL );
388     p_vout->p->p_vf2_chain = filter_chain_New( p_vout, "video filter2",
389         false, video_filter_buffer_allocation_init, NULL, p_vout );
390
391     /* Choose the video output module */
392     if( !p_vout->p->psz_filter_chain || !*p_vout->p->psz_filter_chain )
393     {
394         psz_parser = NULL;
395     }
396     else
397     {
398         psz_parser = strdup( p_vout->p->psz_filter_chain );
399         p_vout->p->title.show = false;
400     }
401
402     /* Create the vout thread */
403     char *psz_tmp = config_ChainCreate( &psz_name, &p_cfg, psz_parser );
404     free( psz_parser );
405     free( psz_tmp );
406     p_vout->p->p_cfg = p_cfg;
407
408     /* Create a few object variables for interface interaction */
409     var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
410     text.psz_string = _("Filters");
411     var_Change( p_vout, "vout-filter", VLC_VAR_SETTEXT, &text, NULL );
412     var_AddCallback( p_vout, "vout-filter", FilterCallback, NULL );
413
414     /* */
415     DeinterlaceEnable( p_vout );
416
417     if( p_vout->p->psz_filter_chain && *p_vout->p->psz_filter_chain )
418     {
419         char *psz_tmp;
420         if( asprintf( &psz_tmp, "%s,none", psz_name ) < 0 )
421             psz_tmp = strdup( "" );
422         free( psz_name );
423         psz_name = psz_tmp;
424     }
425     p_vout->p->psz_module_name = psz_name;
426
427     /* */
428     vlc_object_set_destructor( p_vout, vout_Destructor );
429
430     /* */
431     vlc_cond_init( &p_vout->p->change_wait );
432     if( vlc_clone( &p_vout->p->thread, Thread, p_vout,
433                    VLC_THREAD_PRIORITY_OUTPUT ) )
434     {
435         spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), false );
436         spu_Destroy( p_vout->p->p_spu );
437         p_vout->p->p_spu = NULL;
438         vlc_object_release( p_vout );
439         return NULL;
440     }
441
442     vlc_mutex_lock( &p_vout->p->change_lock );
443     while( !p_vout->p->b_ready )
444     {   /* We are (ab)using the same condition in opposite directions for
445          * b_ready and b_done. This works because of the strict ordering. */
446         assert( !p_vout->p->b_done );
447         vlc_cond_wait( &p_vout->p->change_wait, &p_vout->p->change_lock );
448     }
449     vlc_mutex_unlock( &p_vout->p->change_lock );
450
451     if( p_vout->p->b_error )
452     {
453         msg_Err( p_vout, "video output creation failed" );
454         vout_CloseAndRelease( p_vout );
455         return NULL;
456     }
457
458     return p_vout;
459 }
460
461 /*****************************************************************************
462  * vout_Close: Close a vout created by vout_Create.
463  *****************************************************************************
464  * You HAVE to call it on vout created by vout_Create before vlc_object_release.
465  * You should NEVER call it on vout not obtained through vout_Create
466  * (like with vout_Request or vlc_object_find.)
467  * You can use vout_CloseAndRelease() as a convenience method.
468  *****************************************************************************/
469 void vout_Close( vout_thread_t *p_vout )
470 {
471     assert( p_vout );
472
473     vlc_mutex_lock( &p_vout->p->change_lock );
474     p_vout->p->b_done = true;
475     vlc_cond_signal( &p_vout->p->change_wait );
476     vlc_mutex_unlock( &p_vout->p->change_lock );
477
478     vout_snapshot_End( &p_vout->p->snapshot );
479
480     vlc_join( p_vout->p->thread, NULL );
481 }
482
483 /* */
484 static void vout_Destructor( vlc_object_t * p_this )
485 {
486     vout_thread_t *p_vout = (vout_thread_t *)p_this;
487
488     /* Make sure the vout was stopped first */
489     //assert( !p_vout->p_module );
490
491     free( p_vout->p->psz_module_name );
492
493     /* */
494     if( p_vout->p->p_spu )
495         spu_Destroy( p_vout->p->p_spu );
496
497     vout_chrono_Clean( &p_vout->p->render );
498
499     if( p_vout->p->decoder_fifo )
500         picture_fifo_Delete( p_vout->p->decoder_fifo );
501     assert( !p_vout->p->decoder_pool );
502
503     /* Destroy the locks */
504     vlc_cond_destroy( &p_vout->p->change_wait );
505     vlc_cond_destroy( &p_vout->p->picture_wait );
506     vlc_mutex_destroy( &p_vout->p->picture_lock );
507     vlc_mutex_destroy( &p_vout->p->change_lock );
508     vlc_mutex_destroy( &p_vout->p->vfilter_lock );
509
510     /* */
511     vout_statistic_Clean( &p_vout->p->statistic );
512
513     /* */
514     vout_snapshot_Clean( &p_vout->p->snapshot );
515
516     /* */
517     free( p_vout->p->psz_filter_chain );
518     free( p_vout->p->title.value );
519
520     config_ChainDestroy( p_vout->p->p_cfg );
521
522     free( p_vout->p );
523
524 }
525
526 /* */
527 void vout_ChangePause( vout_thread_t *p_vout, bool b_paused, mtime_t i_date )
528 {
529     vlc_mutex_lock( &p_vout->p->change_lock );
530
531     assert( !p_vout->p->pause.is_on || !b_paused );
532
533     vlc_mutex_lock( &p_vout->p->picture_lock );
534
535     if( p_vout->p->pause.is_on )
536     {
537         const mtime_t i_duration = i_date - p_vout->p->pause.date;
538
539         if (p_vout->p->step.timestamp > VLC_TS_INVALID)
540             p_vout->p->step.timestamp += i_duration;
541         if (!b_paused)
542             p_vout->p->step.last = p_vout->p->step.timestamp;
543         picture_fifo_OffsetDate( p_vout->p->decoder_fifo, i_duration );
544         if (p_vout->p->displayed.decoded)
545             p_vout->p->displayed.decoded->date += i_duration;
546
547         vlc_cond_signal( &p_vout->p->picture_wait );
548         vlc_mutex_unlock( &p_vout->p->picture_lock );
549
550         spu_OffsetSubtitleDate( p_vout->p->p_spu, i_duration );
551     }
552     else
553     {
554         if (b_paused)
555             p_vout->p->step.last = p_vout->p->step.timestamp;
556         vlc_mutex_unlock( &p_vout->p->picture_lock );
557     }
558     p_vout->p->pause.is_on = b_paused;
559     p_vout->p->pause.date  = i_date;
560
561     vlc_mutex_unlock( &p_vout->p->change_lock );
562 }
563
564 void vout_GetResetStatistic( vout_thread_t *p_vout, int *pi_displayed, int *pi_lost )
565 {
566     vout_statistic_GetReset( &p_vout->p->statistic,
567                              pi_displayed, pi_lost );
568 }
569
570 static void Flush(vout_thread_t *vout, mtime_t date, bool below)
571 {
572     vlc_assert_locked(&vout->p->picture_lock);
573     vout->p->step.timestamp = VLC_TS_INVALID;
574     vout->p->step.last      = VLC_TS_INVALID;
575
576     picture_t *last = vout->p->displayed.decoded;
577     if (last &&
578         (( below  && last->date <= date) ||
579           (!below && last->date >= date))) {
580         vout->p->step.is_requested = true;
581     }
582     picture_fifo_Flush( vout->p->decoder_fifo, date, below );
583 }
584
585 void vout_Flush(vout_thread_t *vout, mtime_t date)
586 {
587     vlc_mutex_lock(&vout->p->picture_lock);
588
589     Flush(vout, date, false);
590
591     vlc_cond_signal(&vout->p->picture_wait);
592     vlc_mutex_unlock(&vout->p->picture_lock);
593 }
594
595 void vout_Reset(vout_thread_t *vout)
596 {
597     vlc_mutex_lock(&vout->p->picture_lock);
598
599     Flush(vout, INT64_MAX, true);
600     if (vout->p->decoder_pool)
601         picture_pool_NonEmpty(vout->p->decoder_pool, true);
602     vout->p->pause.is_on = false;
603     vout->p->pause.date  = mdate();
604
605     vlc_cond_signal( &vout->p->picture_wait );
606     vlc_mutex_unlock(&vout->p->picture_lock);
607 }
608
609 void vout_FixLeaks( vout_thread_t *vout )
610 {
611     vlc_mutex_lock(&vout->p->picture_lock);
612
613     picture_t *picture = picture_fifo_Peek(vout->p->decoder_fifo);
614     if (!picture) {
615         picture = picture_pool_Get(vout->p->decoder_pool);
616     }
617
618     if (picture) {
619         picture_Release(picture);
620         /* Not all pictures has been displayed yet or some are
621          * free */
622         vlc_mutex_unlock(&vout->p->picture_lock);
623         return;
624     }
625
626     /* There is no reason that no pictures are available, force one
627      * from the pool, becarefull with it though */
628     msg_Err(vout, "pictures leaked, trying to workaround");
629
630     /* */
631     picture_pool_NonEmpty(vout->p->decoder_pool, false);
632
633     vlc_cond_signal(&vout->p->picture_wait);
634     vlc_mutex_unlock(&vout->p->picture_lock);
635 }
636 void vout_NextPicture(vout_thread_t *vout, mtime_t *duration)
637 {
638     vlc_mutex_lock(&vout->p->picture_lock);
639
640     vout->p->b_picture_empty = false;
641     vout->p->step.is_requested = true;
642
643     /* FIXME I highly doubt that it can works with only one cond_t FIXME */
644     vlc_cond_signal(&vout->p->picture_wait);
645
646     while (vout->p->step.is_requested && !vout->p->b_picture_empty)
647         vlc_cond_wait(&vout->p->picture_wait, &vout->p->picture_lock);
648
649     if (vout->p->step.last > VLC_TS_INVALID &&
650         vout->p->step.timestamp > vout->p->step.last) {
651         *duration = vout->p->step.timestamp - vout->p->step.last;
652         vout->p->step.last = vout->p->step.timestamp;
653     } else {
654         *duration = 0;
655     }
656
657     /* TODO advance subpicture by the duration ... */
658     vlc_mutex_unlock(&vout->p->picture_lock);
659 }
660
661 void vout_DisplayTitle( vout_thread_t *p_vout, const char *psz_title )
662 {
663     assert( psz_title );
664
665     if( !var_InheritBool( p_vout, "osd" ) )
666         return;
667
668     vlc_mutex_lock( &p_vout->p->change_lock );
669     free( p_vout->p->title.value );
670     p_vout->p->title.value = strdup( psz_title );
671     vlc_mutex_unlock( &p_vout->p->change_lock );
672 }
673
674 spu_t *vout_GetSpu( vout_thread_t *p_vout )
675 {
676     return p_vout->p->p_spu;
677 }
678
679 /*****************************************************************************
680  * InitThread: initialize video output thread
681  *****************************************************************************
682  * This function is called from Thread and performs the second step of the
683  * initialization. It returns 0 on success. Note that the thread's flag are not
684  * modified inside this function.
685  * XXX You have to enter it with change_lock taken.
686  *****************************************************************************/
687 static int ThreadInit(vout_thread_t *vout)
688 {
689     /* Initialize output method, it allocates direct buffers for us */
690     if (vout_InitWrapper(vout))
691         return VLC_EGENERIC;
692     assert(vout->p->decoder_pool);
693
694     vout->p->displayed.decoded = NULL;
695
696     /* print some usefull debug info about different vout formats
697      */
698     PrintVideoFormat(vout, "pic render", &vout->fmt_render);
699     PrintVideoFormat(vout, "pic in",     &vout->fmt_in);
700     PrintVideoFormat(vout, "pic out",    &vout->fmt_out);
701
702     assert(vout->fmt_out.i_width  == vout->fmt_render.i_width &&
703            vout->fmt_out.i_height == vout->fmt_render.i_height &&
704            vout->fmt_out.i_chroma == vout->fmt_render.i_chroma);
705     return VLC_SUCCESS;
706 }
707
708 /*****************************************************************************
709  * CleanThread: clean up after InitThread
710  *****************************************************************************
711  * This function is called after a sucessful
712  * initialization. It frees all resources allocated by InitThread.
713  * XXX You have to enter it with change_lock taken.
714  *****************************************************************************/
715 static void ThreadClean(vout_thread_t *vout)
716 {
717     /* Destroy translation tables */
718     if (!vout->p->b_error) {
719         picture_fifo_Flush(vout->p->decoder_fifo, INT64_MAX, false);
720         vout_EndWrapper(vout);
721     }
722 }
723
724 static int ThreadDisplayPicture(vout_thread_t *vout,
725                                 bool now, mtime_t *deadline)
726 {
727     int displayed_count = 0;
728     int lost_count = 0;
729
730     for (;;) {
731         const mtime_t date = mdate();
732         const bool is_paused = vout->p->pause.is_on;
733         bool redisplay = is_paused && !now;
734         bool is_forced;
735
736         /* FIXME/XXX we must redisplay the last decoded picture (because
737          * of potential vout updated, or filters update or SPU update)
738          * For now a high update period is needed but it coulmd be removed
739          * if and only if:
740          * - vout module emits events from theselves.
741          * - *and* SPU is modified to emit an event or a deadline when needed.
742          *
743          * So it will be done latter.
744          */
745         if (!redisplay) {
746             picture_t *peek = picture_fifo_Peek(vout->p->decoder_fifo);
747             if (peek) {
748                 is_forced = peek->b_force || is_paused || now;
749                 *deadline = (is_forced ? date : peek->date) - vout_chrono_GetHigh(&vout->p->render);
750                 picture_Release(peek);
751             } else {
752                 redisplay = true;
753             }
754         }
755         if (redisplay) {
756              /* FIXME a better way for this delay is needed */
757             const mtime_t date_update = vout->p->displayed.date + VOUT_REDISPLAY_DELAY;
758             if (date_update > date || !vout->p->displayed.decoded) {
759                 *deadline = vout->p->displayed.decoded ? date_update : VLC_TS_INVALID;
760                 break;
761             }
762             /* */
763             is_forced = true;
764             *deadline = date - vout_chrono_GetHigh(&vout->p->render);
765         }
766         if (*deadline > VOUT_MWAIT_TOLERANCE)
767             *deadline -= VOUT_MWAIT_TOLERANCE;
768
769         /* If we are too early and can wait, do it */
770         if (date < *deadline && !now)
771             break;
772
773         picture_t *decoded;
774         if (redisplay) {
775             decoded = vout->p->displayed.decoded;
776             vout->p->displayed.decoded = NULL;
777         } else {
778             decoded = picture_fifo_Pop(vout->p->decoder_fifo);
779             assert(decoded);
780             if (!is_forced && !vout->p->is_late_dropped) {
781                 const mtime_t predicted = date + vout_chrono_GetLow(&vout->p->render);
782                 const mtime_t late = predicted - decoded->date;
783                 if (late > 0) {
784                     msg_Dbg(vout, "picture might be displayed late (missing %d ms)", (int)(late/1000));
785                     if (late > VOUT_DISPLAY_LATE_THRESHOLD) {
786                         msg_Warn(vout, "rejected picture because of render time");
787                         /* TODO */
788                         picture_Release(decoded);
789                         lost_count++;
790                         break;
791                     }
792                 }
793             }
794
795             vout->p->displayed.is_interlaced = !decoded->b_progressive;
796             vout->p->displayed.qtype         = decoded->i_qtype;
797         }
798         vout->p->displayed.timestamp = decoded->date;
799
800         /* */
801         if (vout->p->displayed.decoded)
802             picture_Release(vout->p->displayed.decoded);
803         picture_Hold(decoded);
804         vout->p->displayed.decoded = decoded;
805
806         /* */
807         vout_chrono_Start(&vout->p->render);
808
809         picture_t *filtered = NULL;
810         if (decoded) {
811             vlc_mutex_lock(&vout->p->vfilter_lock);
812             filtered = filter_chain_VideoFilter(vout->p->p_vf2_chain, decoded);
813             //assert(filtered == decoded); // TODO implement
814             vlc_mutex_unlock(&vout->p->vfilter_lock);
815             if (!filtered)
816                 continue;
817         }
818
819         /*
820          * Check for subpictures to display
821          */
822         const bool do_snapshot = vout_snapshot_IsRequested(&vout->p->snapshot);
823         mtime_t spu_render_time = is_forced ? mdate() : filtered->date;
824         if (vout->p->pause.is_on)
825             spu_render_time = vout->p->pause.date;
826         else
827             spu_render_time = filtered->date > 1 ? filtered->date : mdate();
828
829         subpicture_t *subpic = spu_SortSubpictures(vout->p->p_spu,
830                                                    spu_render_time,
831                                                    do_snapshot);
832         /*
833          * Perform rendering
834          *
835          * We have to:
836          * - be sure to end up with a direct buffer.
837          * - blend subtitles, and in a fast access buffer
838          */
839         picture_t *direct = NULL;
840         if (filtered &&
841             (vout->p->decoder_pool != vout->p->display_pool || subpic)) {
842             picture_t *render;
843             if (vout->p->is_decoder_pool_slow)
844                 render = picture_NewFromFormat(&vout->fmt_out);
845             else if (vout->p->decoder_pool != vout->p->display_pool)
846                 render = picture_pool_Get(vout->p->display_pool);
847             else
848                 render = picture_pool_Get(vout->p->private_pool);
849
850             if (render) {
851                 picture_Copy(render, filtered);
852
853                 spu_RenderSubpictures(vout->p->p_spu,
854                                       render, &vout->fmt_out,
855                                       subpic, &vout->fmt_in, spu_render_time);
856             }
857             if (vout->p->is_decoder_pool_slow) {
858                 direct = picture_pool_Get(vout->p->display_pool);
859                 if (direct)
860                     picture_Copy(direct, render);
861                 picture_Release(render);
862
863             } else {
864                 direct = render;
865             }
866             picture_Release(filtered);
867             filtered = NULL;
868         } else {
869             direct = filtered;
870         }
871
872         /*
873          * Take a snapshot if requested
874          */
875         if (direct && do_snapshot)
876             vout_snapshot_Set(&vout->p->snapshot, &vout->fmt_out, direct);
877
878         /* Render the direct buffer returned by vout_RenderPicture */
879         if (direct) {
880             vout_RenderWrapper(vout, direct);
881
882             vout_chrono_Stop(&vout->p->render);
883 #if 0
884             {
885             static int i = 0;
886             if (((i++)%10) == 0)
887                 msg_Info(vout, "render: avg %d ms var %d ms",
888                          (int)(vout->p->render.avg/1000), (int)(vout->p->render.var/1000));
889             }
890 #endif
891         }
892
893         /* Wait the real date (for rendering jitter) */
894         if (!is_forced)
895             mwait(decoded->date);
896
897         /* Display the direct buffer returned by vout_RenderPicture */
898         vout->p->displayed.date = mdate();
899         if (direct)
900             vout_DisplayWrapper(vout, direct);
901
902         displayed_count++;
903         break;
904     }
905
906     vout_statistic_Update(&vout->p->statistic, displayed_count, lost_count);
907     if (displayed_count <= 0)
908         return VLC_EGENERIC;
909     return VLC_SUCCESS;
910 }
911
912 /*****************************************************************************
913  * Thread: video output thread
914  *****************************************************************************
915  * Video output thread. This function does only returns when the thread is
916  * terminated. It handles the pictures arriving in the video heap and the
917  * display device events.
918  *****************************************************************************/
919 static void *Thread(void *object)
920 {
921     vout_thread_t *vout = object;
922     bool          has_wrapper;
923
924     /*
925      * Initialize thread
926      */
927     has_wrapper = !vout_OpenWrapper(vout, vout->p->psz_module_name);
928
929     vlc_mutex_lock(&vout->p->change_lock);
930
931     if (has_wrapper)
932         vout->p->b_error = ThreadInit(vout);
933     else
934         vout->p->b_error = true;
935
936     /* signal the creation of the vout */
937     vout->p->b_ready = true;
938     vlc_cond_signal(&vout->p->change_wait);
939
940     if (vout->p->b_error)
941         goto exit_thread;
942
943     /* */
944     bool    last_picture_interlaced      = false;
945     int     last_picture_qtype           = QTYPE_NONE;
946     mtime_t last_picture_interlaced_date = mdate();
947
948     /*
949      * Main loop - it is not executed if an error occurred during
950      * initialization
951      */
952     while (!vout->p->b_done && !vout->p->b_error) {
953         /* */
954         if(vout->p->title.show && vout->p->title.value)
955             DisplayTitleOnOSD(vout);
956
957         vlc_mutex_lock(&vout->p->picture_lock);
958
959         mtime_t deadline = VLC_TS_INVALID;
960         bool has_displayed = !ThreadDisplayPicture(vout, vout->p->step.is_requested, &deadline);
961
962         if (has_displayed) {
963             vout->p->step.timestamp = vout->p->displayed.timestamp;
964             if (vout->p->step.last <= VLC_TS_INVALID)
965                 vout->p->step.last = vout->p->step.timestamp;
966         }
967         if (vout->p->step.is_requested) {
968             if (!has_displayed && !vout->p->b_picture_empty) {
969                 picture_t *peek = picture_fifo_Peek(vout->p->decoder_fifo);
970                 if (peek)
971                     picture_Release(peek);
972                 if (!peek) {
973                     vout->p->b_picture_empty = true;
974                     vlc_cond_signal(&vout->p->picture_wait);
975                 }
976             }
977             if (has_displayed) {
978                 vout->p->step.is_requested = false;
979                 vlc_cond_signal(&vout->p->picture_wait);
980             }
981         }
982
983         const int  picture_qtype      = vout->p->displayed.qtype;
984         const bool picture_interlaced = vout->p->displayed.is_interlaced;
985
986         vlc_mutex_unlock(&vout->p->picture_lock);
987
988         if (vout_ManageWrapper(vout)) {
989             /* A fatal error occurred, and the thread must terminate
990              * immediately, without displaying anything - setting b_error to 1
991              * causes the immediate end of the main while() loop. */
992             // FIXME pf_end
993             vout->p->b_error = true;
994             break;
995         }
996
997         /* Post processing */
998         const int postproc_change = (picture_qtype != QTYPE_NONE) - (last_picture_qtype != QTYPE_NONE);
999         if (postproc_change == 1)
1000             PostProcessEnable(vout);
1001         else if (postproc_change == -1)
1002             PostProcessDisable(vout);
1003         if (postproc_change)
1004             last_picture_qtype = picture_qtype;
1005
1006         /* Deinterlacing
1007          * Wait 30s before quiting interlacing mode */
1008         const int interlacing_change = (!!picture_interlaced) - (!!last_picture_interlaced);
1009         if ((interlacing_change == 1) ||
1010             (interlacing_change == -1 && last_picture_interlaced_date + 30000000 < mdate())) {
1011             DeinterlaceNeeded(vout, picture_interlaced);
1012             last_picture_interlaced = picture_interlaced;
1013         }
1014         if (picture_interlaced)
1015             last_picture_interlaced_date = mdate();
1016
1017         /* Check for "video filter2" changes */
1018         vlc_mutex_lock(&vout->p->vfilter_lock);
1019         if (vout->p->psz_vf2) {
1020             es_format_t fmt;
1021
1022             es_format_Init(&fmt, VIDEO_ES, vout->fmt_render.i_chroma);
1023             fmt.video = vout->fmt_render;
1024             filter_chain_Reset(vout->p->p_vf2_chain, &fmt, &fmt);
1025
1026             if (filter_chain_AppendFromString(vout->p->p_vf2_chain,
1027                                               vout->p->psz_vf2) < 0)
1028                 msg_Err(vout, "Video filter chain creation failed");
1029
1030             free(vout->p->psz_vf2);
1031             vout->p->psz_vf2 = NULL;
1032
1033             if (last_picture_qtype != QTYPE_NONE)
1034                 PostProcessSetFilterQuality(vout);
1035         }
1036         vlc_mutex_unlock(&vout->p->vfilter_lock);
1037
1038         vlc_mutex_unlock(&vout->p->change_lock);
1039
1040         if (deadline > VLC_TS_INVALID) {
1041             vlc_mutex_lock(&vout->p->picture_lock);
1042             vlc_cond_timedwait(&vout->p->picture_wait, &vout->p->picture_lock, deadline);
1043             vlc_mutex_unlock(&vout->p->picture_lock);
1044         }
1045
1046         vlc_mutex_lock(&vout->p->change_lock);
1047     }
1048
1049     /*
1050      * Error loop - wait until the thread destruction is requested
1051      *
1052      * XXX I wonder if we should periodically clean the decoder_fifo
1053      * or have a way to prevent it filling up.
1054      */
1055     while (vout->p->b_error && !vout->p->b_done)
1056         vlc_cond_wait(&vout->p->change_wait, &vout->p->change_lock);
1057
1058     /* Clean thread */
1059     ThreadClean(vout);
1060
1061 exit_thread:
1062     /* Detach subpicture unit from both input and vout */
1063     spu_Attach(vout->p->p_spu, VLC_OBJECT(vout), false);
1064     vlc_object_detach(vout->p->p_spu);
1065
1066     /* Destroy the video filters2 */
1067     filter_chain_Delete(vout->p->p_vf2_chain);
1068
1069     vlc_mutex_unlock(&vout->p->change_lock);
1070
1071     if (has_wrapper)
1072         vout_CloseWrapper(vout);
1073
1074     return NULL;
1075 }
1076
1077 /*****************************************************************************
1078  * object variables callbacks: a bunch of object variables are used by the
1079  * interfaces to interact with the vout.
1080  *****************************************************************************/
1081 static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd,
1082                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1083 {
1084     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1085     input_thread_t *p_input;
1086     (void)psz_cmd; (void)oldval; (void)p_data;
1087
1088     p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1089                                                  FIND_PARENT );
1090     if (!p_input)
1091     {
1092         msg_Err( p_vout, "Input not found" );
1093         return VLC_EGENERIC;
1094     }
1095
1096     var_SetBool( p_vout, "intf-change", true );
1097
1098     /* Modify input as well because the vout might have to be restarted */
1099     var_Create( p_input, "vout-filter", VLC_VAR_STRING );
1100     var_SetString( p_input, "vout-filter", newval.psz_string );
1101
1102     /* Now restart current video stream */
1103     input_Control( p_input, INPUT_RESTART_ES, -VIDEO_ES );
1104     vlc_object_release( p_input );
1105
1106     return VLC_SUCCESS;
1107 }
1108
1109 /*****************************************************************************
1110  * Video Filter2 stuff
1111  *****************************************************************************/
1112 static int VideoFilter2Callback( vlc_object_t *p_this, char const *psz_cmd,
1113                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1114 {
1115     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1116     (void)psz_cmd; (void)oldval; (void)p_data;
1117
1118     vlc_mutex_lock( &p_vout->p->vfilter_lock );
1119     p_vout->p->psz_vf2 = strdup( newval.psz_string );
1120     vlc_mutex_unlock( &p_vout->p->vfilter_lock );
1121
1122     return VLC_SUCCESS;
1123 }
1124
1125 /*****************************************************************************
1126  * Post-processing
1127  *****************************************************************************/
1128 static bool PostProcessIsPresent( const char *psz_filter )
1129 {
1130     const char  *psz_pp = "postproc";
1131     const size_t i_pp = strlen(psz_pp);
1132     return psz_filter &&
1133            !strncmp( psz_filter, psz_pp, strlen(psz_pp) ) &&
1134            ( psz_filter[i_pp] == '\0' || psz_filter[i_pp] == ':' );
1135 }
1136
1137 static int PostProcessCallback( vlc_object_t *p_this, char const *psz_cmd,
1138                                 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1139 {
1140     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1141     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1142
1143     static const char *psz_pp = "postproc";
1144
1145     char *psz_vf2 = var_GetString( p_vout, "video-filter" );
1146
1147     if( newval.i_int <= 0 )
1148     {
1149         if( PostProcessIsPresent( psz_vf2 ) )
1150         {
1151             strcpy( psz_vf2, &psz_vf2[strlen(psz_pp)] );
1152             if( *psz_vf2 == ':' )
1153                 strcpy( psz_vf2, &psz_vf2[1] );
1154         }
1155     }
1156     else
1157     {
1158         if( !PostProcessIsPresent( psz_vf2 ) )
1159         {
1160             if( psz_vf2 )
1161             {
1162                 char *psz_tmp = psz_vf2;
1163                 if( asprintf( &psz_vf2, "%s:%s", psz_pp, psz_tmp ) < 0 )
1164                     psz_vf2 = psz_tmp;
1165                 else
1166                     free( psz_tmp );
1167             }
1168             else
1169             {
1170                 psz_vf2 = strdup( psz_pp );
1171             }
1172         }
1173     }
1174     if( psz_vf2 )
1175     {
1176         var_SetString( p_vout, "video-filter", psz_vf2 );
1177         free( psz_vf2 );
1178     }
1179
1180     return VLC_SUCCESS;
1181 }
1182 static void PostProcessEnable( vout_thread_t *p_vout )
1183 {
1184     vlc_value_t text;
1185     msg_Dbg( p_vout, "Post-processing available" );
1186     var_Create( p_vout, "postprocess", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
1187     text.psz_string = _("Post processing");
1188     var_Change( p_vout, "postprocess", VLC_VAR_SETTEXT, &text, NULL );
1189
1190     for( int i = 0; i <= 6; i++ )
1191     {
1192         vlc_value_t val;
1193         vlc_value_t text;
1194         char psz_text[1+1];
1195
1196         val.i_int = i;
1197         snprintf( psz_text, sizeof(psz_text), "%d", i );
1198         if( i == 0 )
1199             text.psz_string = _("Disable");
1200         else
1201             text.psz_string = psz_text;
1202         var_Change( p_vout, "postprocess", VLC_VAR_ADDCHOICE, &val, &text );
1203     }
1204     var_AddCallback( p_vout, "postprocess", PostProcessCallback, NULL );
1205
1206     /* */
1207     char *psz_filter = var_GetNonEmptyString( p_vout, "video-filter" );
1208     int i_postproc_q = 0;
1209     if( PostProcessIsPresent( psz_filter ) )
1210         i_postproc_q = var_CreateGetInteger( p_vout, "postproc-q" );
1211
1212     var_SetInteger( p_vout, "postprocess", i_postproc_q );
1213
1214     free( psz_filter );
1215 }
1216 static void PostProcessDisable( vout_thread_t *p_vout )
1217 {
1218     msg_Dbg( p_vout, "Post-processing no more available" );
1219     var_Destroy( p_vout, "postprocess" );
1220 }
1221 static void PostProcessSetFilterQuality( vout_thread_t *p_vout )
1222 {
1223     vlc_object_t *p_pp = vlc_object_find_name( p_vout, "postproc", FIND_CHILD );
1224     if( !p_pp )
1225         return;
1226
1227     var_SetInteger( p_pp, "postproc-q", var_GetInteger( p_vout, "postprocess" ) );
1228     vlc_object_release( p_pp );
1229 }
1230
1231
1232 static void DisplayTitleOnOSD( vout_thread_t *p_vout )
1233 {
1234     const mtime_t i_start = mdate();
1235     const mtime_t i_stop = i_start + INT64_C(1000) * p_vout->p->title.timeout;
1236
1237     if( i_stop <= i_start )
1238         return;
1239
1240     vlc_assert_locked( &p_vout->p->change_lock );
1241
1242     vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1243                            p_vout->p->title.value, NULL,
1244                            p_vout->p->title.position,
1245                            30 + p_vout->fmt_in.i_width
1246                               - p_vout->fmt_in.i_visible_width
1247                               - p_vout->fmt_in.i_x_offset,
1248                            20 + p_vout->fmt_in.i_y_offset,
1249                            i_start, i_stop );
1250
1251     free( p_vout->p->title.value );
1252
1253     p_vout->p->title.value = NULL;
1254 }
1255
1256 /*****************************************************************************
1257  * Deinterlacing
1258  *****************************************************************************/
1259 typedef struct
1260 {
1261     const char *psz_mode;
1262     bool       b_vout_filter;
1263 } deinterlace_mode_t;
1264
1265 /* XXX
1266  * You can use the non vout filter if and only if the video properties stay the
1267  * same (width/height/chroma/fps), at least for now.
1268  */
1269 static const deinterlace_mode_t p_deinterlace_mode[] = {
1270     { "",        false },
1271     //{ "discard", true },
1272     { "blend",   false },
1273     //{ "mean",    true  },
1274     //{ "bob",     true },
1275     //{ "linear",  true },
1276     { "x",       false },
1277     //{ "yadif",   true },
1278     //{ "yadif2x", true },
1279     { NULL,      false }
1280 };
1281 static const deinterlace_mode_t *DeinterlaceGetMode( const char *psz_mode )
1282 {
1283     for( const deinterlace_mode_t *p_mode = &p_deinterlace_mode[0]; p_mode->psz_mode; p_mode++ )
1284     {
1285         if( !strcmp( p_mode->psz_mode, psz_mode ) )
1286             return p_mode;
1287     }
1288     return NULL;
1289 }
1290
1291 static char *FilterFind( char *psz_filter_base, const char *psz_module )
1292 {
1293     const size_t i_module = strlen( psz_module );
1294     const char *psz_filter = psz_filter_base;
1295
1296     if( !psz_filter || i_module <= 0 )
1297         return NULL;
1298
1299     for( ;; )
1300     {
1301         char *psz_find = strstr( psz_filter, psz_module );
1302         if( !psz_find )
1303             return NULL;
1304         if( psz_find[i_module] == '\0' || psz_find[i_module] == ':' )
1305             return psz_find;
1306         psz_filter = &psz_find[i_module];
1307     }
1308 }
1309
1310 static bool DeinterlaceIsPresent( vout_thread_t *p_vout, bool b_vout_filter )
1311 {
1312     char *psz_filter = var_GetNonEmptyString( p_vout, b_vout_filter ? "vout-filter" : "video-filter" );
1313
1314     bool b_found = FilterFind( psz_filter, "deinterlace" ) != NULL;
1315
1316     free( psz_filter );
1317
1318     return b_found;
1319 }
1320
1321 static void DeinterlaceRemove( vout_thread_t *p_vout, bool b_vout_filter )
1322 {
1323     const char *psz_variable = b_vout_filter ? "vout-filter" : "video-filter";
1324     char *psz_filter = var_GetNonEmptyString( p_vout, psz_variable );
1325
1326     char *psz = FilterFind( psz_filter, "deinterlace" );
1327     if( !psz )
1328     {
1329         free( psz_filter );
1330         return;
1331     }
1332
1333     /* */
1334     strcpy( &psz[0], &psz[strlen("deinterlace")] );
1335     if( *psz == ':' )
1336         strcpy( &psz[0], &psz[1] );
1337
1338     var_SetString( p_vout, psz_variable, psz_filter );
1339     free( psz_filter );
1340 }
1341 static void DeinterlaceAdd( vout_thread_t *p_vout, bool b_vout_filter )
1342 {
1343     const char *psz_variable = b_vout_filter ? "vout-filter" : "video-filter";
1344
1345     char *psz_filter = var_GetNonEmptyString( p_vout, psz_variable );
1346
1347     if( FilterFind( psz_filter, "deinterlace" ) )
1348     {
1349         free( psz_filter );
1350         return;
1351     }
1352
1353     /* */
1354     if( psz_filter )
1355     {
1356         char *psz_tmp = psz_filter;
1357         if( asprintf( &psz_filter, "%s:%s", psz_tmp, "deinterlace" ) < 0 )
1358             psz_filter = psz_tmp;
1359         else
1360             free( psz_tmp );
1361     }
1362     else
1363     {
1364         psz_filter = strdup( "deinterlace" );
1365     }
1366
1367     if( psz_filter )
1368     {
1369         var_SetString( p_vout, psz_variable, psz_filter );
1370         free( psz_filter );
1371     }
1372 }
1373
1374 static void DeinterlaceSave( vout_thread_t *p_vout, int i_deinterlace, const char *psz_mode, bool is_needed )
1375 {
1376     /* We have to set input variable to ensure restart support
1377      * XXX it is only needed because of vout-filter but must be done
1378      * for non video filter anyway */
1379     vlc_object_t *p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, FIND_PARENT );
1380     if( !p_input )
1381         return;
1382
1383     /* Another hack for "vout filter" mode */
1384     if( i_deinterlace < 0 )
1385         i_deinterlace = is_needed ? -2 : -3;
1386
1387     var_Create( p_input, "deinterlace", VLC_VAR_INTEGER );
1388     var_SetInteger( p_input, "deinterlace", i_deinterlace );
1389
1390     static const char * const ppsz_variable[] = {
1391         "deinterlace-mode",
1392         "filter-deinterlace-mode",
1393         "sout-deinterlace-mode",
1394         NULL
1395     };
1396     for( int i = 0; ppsz_variable[i]; i++ )
1397     {
1398         var_Create( p_input, ppsz_variable[i], VLC_VAR_STRING );
1399         var_SetString( p_input, ppsz_variable[i], psz_mode );
1400     }
1401
1402     vlc_object_release( p_input );
1403 }
1404 static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd,
1405                                 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1406 {
1407     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(newval); VLC_UNUSED(p_data);
1408     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1409
1410     /* */
1411     const int  i_deinterlace = var_GetInteger( p_this, "deinterlace" );
1412     char       *psz_mode     = var_GetString( p_this, "deinterlace-mode" );
1413     const bool is_needed     = var_GetBool( p_this, "deinterlace-needed" );
1414     if( !psz_mode )
1415         return VLC_EGENERIC;
1416
1417     DeinterlaceSave( p_vout, i_deinterlace, psz_mode, is_needed );
1418
1419     /* */
1420     bool b_vout_filter = false;
1421     const deinterlace_mode_t *p_mode = DeinterlaceGetMode( psz_mode );
1422     if( p_mode )
1423         b_vout_filter = p_mode->b_vout_filter;
1424
1425     /* */
1426     char *psz_old;
1427     if( b_vout_filter )
1428     {
1429         psz_old = var_CreateGetString( p_vout, "filter-deinterlace-mode" );
1430     }
1431     else
1432     {
1433         psz_old = var_CreateGetString( p_vout, "sout-deinterlace-mode" );
1434         var_SetString( p_vout, "sout-deinterlace-mode", psz_mode );
1435     }
1436
1437     msg_Dbg( p_vout, "deinterlace %d, mode %s, is_needed %d", i_deinterlace, psz_mode, is_needed );
1438     if( i_deinterlace == 0 || ( i_deinterlace == -1 && !is_needed ) )
1439     {
1440         DeinterlaceRemove( p_vout, false );
1441         DeinterlaceRemove( p_vout, true );
1442     }
1443     else
1444     {
1445         if( !DeinterlaceIsPresent( p_vout, b_vout_filter ) )
1446         {
1447             DeinterlaceRemove( p_vout, !b_vout_filter );
1448             DeinterlaceAdd( p_vout, b_vout_filter );
1449         }
1450         else
1451         {
1452             /* The deinterlace filter was already inserted but we have changed the mode */
1453             DeinterlaceRemove( p_vout, !b_vout_filter );
1454             if( psz_old && strcmp( psz_old, psz_mode ) )
1455                 var_TriggerCallback( p_vout, b_vout_filter ? "vout-filter" : "video-filter" );
1456         }
1457     }
1458
1459     /* */
1460     free( psz_old );
1461     free( psz_mode );
1462     return VLC_SUCCESS;
1463 }
1464
1465 static void DeinterlaceEnable( vout_thread_t *p_vout )
1466 {
1467     vlc_value_t val, text;
1468
1469     if( !p_vout->p->b_first_vout )
1470         return;
1471
1472     msg_Dbg( p_vout, "Deinterlacing available" );
1473
1474     /* Create the configuration variables */
1475     /* */
1476     var_Create( p_vout, "deinterlace", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT | VLC_VAR_HASCHOICE );
1477     int i_deinterlace = var_GetInteger( p_vout, "deinterlace" );
1478
1479     text.psz_string = _("Deinterlace");
1480     var_Change( p_vout, "deinterlace", VLC_VAR_SETTEXT, &text, NULL );
1481
1482     const module_config_t *p_optd = config_FindConfig( VLC_OBJECT(p_vout), "deinterlace" );
1483     var_Change( p_vout, "deinterlace", VLC_VAR_CLEARCHOICES, NULL, NULL );
1484     for( int i = 0; p_optd && i < p_optd->i_list; i++ )
1485     {
1486         val.i_int  = p_optd->pi_list[i];
1487         text.psz_string = (char*)vlc_gettext(p_optd->ppsz_list_text[i]);
1488         var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
1489     }
1490     var_AddCallback( p_vout, "deinterlace", DeinterlaceCallback, NULL );
1491     /* */
1492     var_Create( p_vout, "deinterlace-mode", VLC_VAR_STRING | VLC_VAR_DOINHERIT | VLC_VAR_HASCHOICE );
1493     char *psz_deinterlace = var_GetNonEmptyString( p_vout, "deinterlace-mode" );
1494
1495     text.psz_string = _("Deinterlace mode");
1496     var_Change( p_vout, "deinterlace-mode", VLC_VAR_SETTEXT, &text, NULL );
1497
1498     const module_config_t *p_optm = config_FindConfig( VLC_OBJECT(p_vout), "deinterlace-mode" );
1499     var_Change( p_vout, "deinterlace-mode", VLC_VAR_CLEARCHOICES, NULL, NULL );
1500     for( int i = 0; p_optm && i < p_optm->i_list; i++ )
1501     {
1502         if( !DeinterlaceGetMode( p_optm->ppsz_list[i] ) )
1503             continue;
1504
1505         val.psz_string  = p_optm->ppsz_list[i];
1506         text.psz_string = (char*)vlc_gettext(p_optm->ppsz_list_text[i]);
1507         var_Change( p_vout, "deinterlace-mode", VLC_VAR_ADDCHOICE, &val, &text );
1508     }
1509     var_AddCallback( p_vout, "deinterlace-mode", DeinterlaceCallback, NULL );
1510     /* */
1511     var_Create( p_vout, "deinterlace-needed", VLC_VAR_BOOL );
1512     var_AddCallback( p_vout, "deinterlace-needed", DeinterlaceCallback, NULL );
1513
1514     /* Override the initial value from filters if present */
1515     char *psz_filter_mode = NULL;
1516     if( DeinterlaceIsPresent( p_vout, true ) )
1517         psz_filter_mode = var_CreateGetNonEmptyString( p_vout, "filter-deinterlace-mode" );
1518     else if( DeinterlaceIsPresent( p_vout, false ) )
1519         psz_filter_mode = var_CreateGetNonEmptyString( p_vout, "sout-deinterlace-mode" );
1520     if( psz_filter_mode )
1521     {
1522         free( psz_deinterlace );
1523         if( i_deinterlace >= -1 )
1524             i_deinterlace = 1;
1525         psz_deinterlace = psz_filter_mode;
1526     }
1527
1528     /* */
1529     if( i_deinterlace == -2 )
1530         p_vout->p->displayed.is_interlaced = true;
1531     else if( i_deinterlace == -3 )
1532         p_vout->p->displayed.is_interlaced = false;
1533     if( i_deinterlace < 0 )
1534         i_deinterlace = -1;
1535
1536     /* */
1537     val.psz_string = psz_deinterlace ? psz_deinterlace : p_optm->orig.psz;
1538     var_Change( p_vout, "deinterlace-mode", VLC_VAR_SETVALUE, &val, NULL );
1539     val.b_bool = p_vout->p->displayed.is_interlaced;
1540     var_Change( p_vout, "deinterlace-needed", VLC_VAR_SETVALUE, &val, NULL );
1541
1542     var_SetInteger( p_vout, "deinterlace", i_deinterlace );
1543     free( psz_deinterlace );
1544 }
1545
1546 static void DeinterlaceNeeded( vout_thread_t *p_vout, bool is_interlaced )
1547 {
1548     msg_Dbg( p_vout, "Detected %s video",
1549              is_interlaced ? "interlaced" : "progressive" );
1550     var_SetBool( p_vout, "deinterlace-needed", is_interlaced );
1551 }
1552
1553 /* */
1554 static void PrintVideoFormat(vout_thread_t *vout,
1555                              const char *description,
1556                              const video_format_t *fmt)
1557 {
1558     msg_Dbg(vout, "%s sz %ix%i, of (%i,%i), vsz %ix%i, 4cc %4.4s, sar %i:%i, msk r0x%x g0x%x b0x%x",
1559             description,
1560             fmt->i_width, fmt->i_height, fmt->i_x_offset, fmt->i_y_offset,
1561             fmt->i_visible_width, fmt->i_visible_height,
1562             (char*)&fmt->i_chroma,
1563             fmt->i_sar_num, fmt->i_sar_den,
1564             fmt->i_rmask, fmt->i_gmask, fmt->i_bmask);
1565 }
1566