]> git.sesse.net Git - vlc/blob - src/video_output/video_output.c
Removed now unused picture_t fields and associated enums.
[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 reset, 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         if (reset) {
579             picture_Release(last);
580             vout->p->displayed.decoded = NULL;
581         } else if (( below  && last->date <= date) ||
582                    (!below && last->date >= date)) {
583             vout->p->step.is_requested = true;
584         }
585     }
586     picture_fifo_Flush( vout->p->decoder_fifo, date, below );
587 }
588
589 void vout_Flush(vout_thread_t *vout, mtime_t date)
590 {
591     vlc_mutex_lock(&vout->p->picture_lock);
592
593     Flush(vout, date, false, false);
594
595     vlc_cond_signal(&vout->p->picture_wait);
596     vlc_mutex_unlock(&vout->p->picture_lock);
597 }
598
599 void vout_Reset(vout_thread_t *vout)
600 {
601     vlc_mutex_lock(&vout->p->picture_lock);
602
603     Flush(vout, INT64_MAX, true, true);
604     if (vout->p->decoder_pool)
605         picture_pool_NonEmpty(vout->p->decoder_pool, true);
606     vout->p->pause.is_on = false;
607     vout->p->pause.date  = mdate();
608
609     vlc_cond_signal( &vout->p->picture_wait );
610     vlc_mutex_unlock(&vout->p->picture_lock);
611 }
612
613 void vout_FixLeaks( vout_thread_t *vout )
614 {
615     vlc_mutex_lock(&vout->p->picture_lock);
616
617     picture_t *picture = picture_fifo_Peek(vout->p->decoder_fifo);
618     if (!picture) {
619         picture = picture_pool_Get(vout->p->decoder_pool);
620     }
621
622     if (picture) {
623         picture_Release(picture);
624         /* Not all pictures has been displayed yet or some are
625          * free */
626         vlc_mutex_unlock(&vout->p->picture_lock);
627         return;
628     }
629
630     /* There is no reason that no pictures are available, force one
631      * from the pool, becarefull with it though */
632     msg_Err(vout, "pictures leaked, trying to workaround");
633
634     /* */
635     picture_pool_NonEmpty(vout->p->decoder_pool, false);
636
637     vlc_cond_signal(&vout->p->picture_wait);
638     vlc_mutex_unlock(&vout->p->picture_lock);
639 }
640 void vout_NextPicture(vout_thread_t *vout, mtime_t *duration)
641 {
642     vlc_mutex_lock(&vout->p->picture_lock);
643
644     vout->p->b_picture_empty = false;
645     vout->p->step.is_requested = true;
646
647     /* FIXME I highly doubt that it can works with only one cond_t FIXME */
648     vlc_cond_signal(&vout->p->picture_wait);
649
650     while (vout->p->step.is_requested && !vout->p->b_picture_empty)
651         vlc_cond_wait(&vout->p->picture_wait, &vout->p->picture_lock);
652
653     if (vout->p->step.last > VLC_TS_INVALID &&
654         vout->p->step.timestamp > vout->p->step.last) {
655         *duration = vout->p->step.timestamp - vout->p->step.last;
656         vout->p->step.last = vout->p->step.timestamp;
657     } else {
658         *duration = 0;
659     }
660
661     /* TODO advance subpicture by the duration ... */
662     vlc_mutex_unlock(&vout->p->picture_lock);
663 }
664
665 void vout_DisplayTitle( vout_thread_t *p_vout, const char *psz_title )
666 {
667     assert( psz_title );
668
669     if( !var_InheritBool( p_vout, "osd" ) )
670         return;
671
672     vlc_mutex_lock( &p_vout->p->change_lock );
673     free( p_vout->p->title.value );
674     p_vout->p->title.value = strdup( psz_title );
675     vlc_mutex_unlock( &p_vout->p->change_lock );
676 }
677
678 spu_t *vout_GetSpu( vout_thread_t *p_vout )
679 {
680     return p_vout->p->p_spu;
681 }
682
683 /*****************************************************************************
684  * InitThread: initialize video output thread
685  *****************************************************************************
686  * This function is called from Thread and performs the second step of the
687  * initialization. It returns 0 on success. Note that the thread's flag are not
688  * modified inside this function.
689  * XXX You have to enter it with change_lock taken.
690  *****************************************************************************/
691 static int ThreadInit(vout_thread_t *vout)
692 {
693     /* Initialize output method, it allocates direct buffers for us */
694     if (vout_InitWrapper(vout))
695         return VLC_EGENERIC;
696     assert(vout->p->decoder_pool);
697
698     vout->p->displayed.decoded = NULL;
699
700     /* print some usefull debug info about different vout formats
701      */
702     PrintVideoFormat(vout, "pic render", &vout->fmt_render);
703     PrintVideoFormat(vout, "pic in",     &vout->fmt_in);
704     PrintVideoFormat(vout, "pic out",    &vout->fmt_out);
705
706     assert(vout->fmt_out.i_width  == vout->fmt_render.i_width &&
707            vout->fmt_out.i_height == vout->fmt_render.i_height &&
708            vout->fmt_out.i_chroma == vout->fmt_render.i_chroma);
709     return VLC_SUCCESS;
710 }
711
712 /*****************************************************************************
713  * CleanThread: clean up after InitThread
714  *****************************************************************************
715  * This function is called after a sucessful
716  * initialization. It frees all resources allocated by InitThread.
717  * XXX You have to enter it with change_lock taken.
718  *****************************************************************************/
719 static void ThreadClean(vout_thread_t *vout)
720 {
721     /* Destroy translation tables */
722     if (!vout->p->b_error) {
723         picture_fifo_Flush(vout->p->decoder_fifo, INT64_MAX, false);
724         vout_EndWrapper(vout);
725     }
726 }
727
728 static int ThreadDisplayPicture(vout_thread_t *vout,
729                                 bool now, mtime_t *deadline)
730 {
731     int displayed_count = 0;
732     int lost_count = 0;
733
734     for (;;) {
735         const mtime_t date = mdate();
736         const bool is_paused = vout->p->pause.is_on;
737         bool redisplay = is_paused && !now;
738         bool is_forced;
739
740         /* FIXME/XXX we must redisplay the last decoded picture (because
741          * of potential vout updated, or filters update or SPU update)
742          * For now a high update period is needed but it coulmd be removed
743          * if and only if:
744          * - vout module emits events from theselves.
745          * - *and* SPU is modified to emit an event or a deadline when needed.
746          *
747          * So it will be done latter.
748          */
749         if (!redisplay) {
750             picture_t *peek = picture_fifo_Peek(vout->p->decoder_fifo);
751             if (peek) {
752                 is_forced = peek->b_force || is_paused || now;
753                 *deadline = (is_forced ? date : peek->date) - vout_chrono_GetHigh(&vout->p->render);
754                 picture_Release(peek);
755             } else {
756                 redisplay = true;
757             }
758         }
759         if (redisplay) {
760              /* FIXME a better way for this delay is needed */
761             const mtime_t date_update = vout->p->displayed.date + VOUT_REDISPLAY_DELAY;
762             if (date_update > date || !vout->p->displayed.decoded) {
763                 *deadline = vout->p->displayed.decoded ? date_update : VLC_TS_INVALID;
764                 break;
765             }
766             /* */
767             is_forced = true;
768             *deadline = date - vout_chrono_GetHigh(&vout->p->render);
769         }
770         if (*deadline > VOUT_MWAIT_TOLERANCE)
771             *deadline -= VOUT_MWAIT_TOLERANCE;
772
773         /* If we are too early and can wait, do it */
774         if (date < *deadline && !now)
775             break;
776
777         picture_t *decoded;
778         if (redisplay) {
779             decoded = vout->p->displayed.decoded;
780             vout->p->displayed.decoded = NULL;
781         } else {
782             decoded = picture_fifo_Pop(vout->p->decoder_fifo);
783             assert(decoded);
784             if (!is_forced && !vout->p->is_late_dropped) {
785                 const mtime_t predicted = date + vout_chrono_GetLow(&vout->p->render);
786                 const mtime_t late = predicted - decoded->date;
787                 if (late > 0) {
788                     msg_Dbg(vout, "picture might be displayed late (missing %d ms)", (int)(late/1000));
789                     if (late > VOUT_DISPLAY_LATE_THRESHOLD) {
790                         msg_Warn(vout, "rejected picture because of render time");
791                         /* TODO */
792                         picture_Release(decoded);
793                         lost_count++;
794                         break;
795                     }
796                 }
797             }
798
799             vout->p->displayed.is_interlaced = !decoded->b_progressive;
800             vout->p->displayed.qtype         = decoded->i_qtype;
801         }
802         vout->p->displayed.timestamp = decoded->date;
803
804         /* */
805         if (vout->p->displayed.decoded)
806             picture_Release(vout->p->displayed.decoded);
807         picture_Hold(decoded);
808         vout->p->displayed.decoded = decoded;
809
810         /* */
811         vout_chrono_Start(&vout->p->render);
812
813         picture_t *filtered = NULL;
814         if (decoded) {
815             vlc_mutex_lock(&vout->p->vfilter_lock);
816             filtered = filter_chain_VideoFilter(vout->p->p_vf2_chain, decoded);
817             //assert(filtered == decoded); // TODO implement
818             vlc_mutex_unlock(&vout->p->vfilter_lock);
819             if (!filtered)
820                 continue;
821         }
822
823         /*
824          * Check for subpictures to display
825          */
826         const bool do_snapshot = vout_snapshot_IsRequested(&vout->p->snapshot);
827         mtime_t spu_render_time = is_forced ? mdate() : filtered->date;
828         if (vout->p->pause.is_on)
829             spu_render_time = vout->p->pause.date;
830         else
831             spu_render_time = filtered->date > 1 ? filtered->date : mdate();
832
833         subpicture_t *subpic = spu_SortSubpictures(vout->p->p_spu,
834                                                    spu_render_time,
835                                                    do_snapshot);
836         /*
837          * Perform rendering
838          *
839          * We have to:
840          * - be sure to end up with a direct buffer.
841          * - blend subtitles, and in a fast access buffer
842          */
843         picture_t *direct = NULL;
844         if (filtered &&
845             (vout->p->decoder_pool != vout->p->display_pool || subpic)) {
846             picture_t *render;
847             if (vout->p->is_decoder_pool_slow)
848                 render = picture_NewFromFormat(&vout->fmt_out);
849             else if (vout->p->decoder_pool != vout->p->display_pool)
850                 render = picture_pool_Get(vout->p->display_pool);
851             else
852                 render = picture_pool_Get(vout->p->private_pool);
853
854             if (render) {
855                 picture_Copy(render, filtered);
856
857                 spu_RenderSubpictures(vout->p->p_spu,
858                                       render, &vout->fmt_out,
859                                       subpic, &vout->fmt_in, spu_render_time);
860             }
861             if (vout->p->is_decoder_pool_slow) {
862                 direct = picture_pool_Get(vout->p->display_pool);
863                 if (direct)
864                     picture_Copy(direct, render);
865                 picture_Release(render);
866
867             } else {
868                 direct = render;
869             }
870             picture_Release(filtered);
871             filtered = NULL;
872         } else {
873             direct = filtered;
874         }
875
876         /*
877          * Take a snapshot if requested
878          */
879         if (direct && do_snapshot)
880             vout_snapshot_Set(&vout->p->snapshot, &vout->fmt_out, direct);
881
882         /* Render the direct buffer returned by vout_RenderPicture */
883         if (direct) {
884             vout_RenderWrapper(vout, direct);
885
886             vout_chrono_Stop(&vout->p->render);
887 #if 0
888             {
889             static int i = 0;
890             if (((i++)%10) == 0)
891                 msg_Info(vout, "render: avg %d ms var %d ms",
892                          (int)(vout->p->render.avg/1000), (int)(vout->p->render.var/1000));
893             }
894 #endif
895         }
896
897         /* Wait the real date (for rendering jitter) */
898         if (!is_forced)
899             mwait(decoded->date);
900
901         /* Display the direct buffer returned by vout_RenderPicture */
902         vout->p->displayed.date = mdate();
903         if (direct)
904             vout_DisplayWrapper(vout, direct);
905
906         displayed_count++;
907         break;
908     }
909
910     vout_statistic_Update(&vout->p->statistic, displayed_count, lost_count);
911     if (displayed_count <= 0)
912         return VLC_EGENERIC;
913     return VLC_SUCCESS;
914 }
915
916 /*****************************************************************************
917  * Thread: video output thread
918  *****************************************************************************
919  * Video output thread. This function does only returns when the thread is
920  * terminated. It handles the pictures arriving in the video heap and the
921  * display device events.
922  *****************************************************************************/
923 static void *Thread(void *object)
924 {
925     vout_thread_t *vout = object;
926     bool          has_wrapper;
927
928     /*
929      * Initialize thread
930      */
931     has_wrapper = !vout_OpenWrapper(vout, vout->p->psz_module_name);
932
933     vlc_mutex_lock(&vout->p->change_lock);
934
935     if (has_wrapper)
936         vout->p->b_error = ThreadInit(vout);
937     else
938         vout->p->b_error = true;
939
940     /* signal the creation of the vout */
941     vout->p->b_ready = true;
942     vlc_cond_signal(&vout->p->change_wait);
943
944     if (vout->p->b_error)
945         goto exit_thread;
946
947     /* */
948     bool    last_picture_interlaced      = false;
949     int     last_picture_qtype           = QTYPE_NONE;
950     mtime_t last_picture_interlaced_date = mdate();
951
952     /*
953      * Main loop - it is not executed if an error occurred during
954      * initialization
955      */
956     while (!vout->p->b_done && !vout->p->b_error) {
957         /* */
958         if(vout->p->title.show && vout->p->title.value)
959             DisplayTitleOnOSD(vout);
960
961         vlc_mutex_lock(&vout->p->picture_lock);
962
963         mtime_t deadline = VLC_TS_INVALID;
964         bool has_displayed = !ThreadDisplayPicture(vout, vout->p->step.is_requested, &deadline);
965
966         if (has_displayed) {
967             vout->p->step.timestamp = vout->p->displayed.timestamp;
968             if (vout->p->step.last <= VLC_TS_INVALID)
969                 vout->p->step.last = vout->p->step.timestamp;
970         }
971         if (vout->p->step.is_requested) {
972             if (!has_displayed && !vout->p->b_picture_empty) {
973                 picture_t *peek = picture_fifo_Peek(vout->p->decoder_fifo);
974                 if (peek)
975                     picture_Release(peek);
976                 if (!peek) {
977                     vout->p->b_picture_empty = true;
978                     vlc_cond_signal(&vout->p->picture_wait);
979                 }
980             }
981             if (has_displayed) {
982                 vout->p->step.is_requested = false;
983                 vlc_cond_signal(&vout->p->picture_wait);
984             }
985         }
986
987         const int  picture_qtype      = vout->p->displayed.qtype;
988         const bool picture_interlaced = vout->p->displayed.is_interlaced;
989
990         vlc_mutex_unlock(&vout->p->picture_lock);
991
992         if (vout_ManageWrapper(vout)) {
993             /* A fatal error occurred, and the thread must terminate
994              * immediately, without displaying anything - setting b_error to 1
995              * causes the immediate end of the main while() loop. */
996             // FIXME pf_end
997             vout->p->b_error = true;
998             break;
999         }
1000
1001         /* Post processing */
1002         const int postproc_change = (picture_qtype != QTYPE_NONE) - (last_picture_qtype != QTYPE_NONE);
1003         if (postproc_change == 1)
1004             PostProcessEnable(vout);
1005         else if (postproc_change == -1)
1006             PostProcessDisable(vout);
1007         if (postproc_change)
1008             last_picture_qtype = picture_qtype;
1009
1010         /* Deinterlacing
1011          * Wait 30s before quiting interlacing mode */
1012         const int interlacing_change = (!!picture_interlaced) - (!!last_picture_interlaced);
1013         if ((interlacing_change == 1) ||
1014             (interlacing_change == -1 && last_picture_interlaced_date + 30000000 < mdate())) {
1015             DeinterlaceNeeded(vout, picture_interlaced);
1016             last_picture_interlaced = picture_interlaced;
1017         }
1018         if (picture_interlaced)
1019             last_picture_interlaced_date = mdate();
1020
1021         /* Check for "video filter2" changes */
1022         vlc_mutex_lock(&vout->p->vfilter_lock);
1023         if (vout->p->psz_vf2) {
1024             es_format_t fmt;
1025
1026             es_format_Init(&fmt, VIDEO_ES, vout->fmt_render.i_chroma);
1027             fmt.video = vout->fmt_render;
1028             filter_chain_Reset(vout->p->p_vf2_chain, &fmt, &fmt);
1029
1030             if (filter_chain_AppendFromString(vout->p->p_vf2_chain,
1031                                               vout->p->psz_vf2) < 0)
1032                 msg_Err(vout, "Video filter chain creation failed");
1033
1034             free(vout->p->psz_vf2);
1035             vout->p->psz_vf2 = NULL;
1036
1037             if (last_picture_qtype != QTYPE_NONE)
1038                 PostProcessSetFilterQuality(vout);
1039         }
1040         vlc_mutex_unlock(&vout->p->vfilter_lock);
1041
1042         vlc_mutex_unlock(&vout->p->change_lock);
1043
1044         if (deadline > VLC_TS_INVALID) {
1045             vlc_mutex_lock(&vout->p->picture_lock);
1046             vlc_cond_timedwait(&vout->p->picture_wait, &vout->p->picture_lock, deadline);
1047             vlc_mutex_unlock(&vout->p->picture_lock);
1048         }
1049
1050         vlc_mutex_lock(&vout->p->change_lock);
1051     }
1052
1053     /*
1054      * Error loop - wait until the thread destruction is requested
1055      *
1056      * XXX I wonder if we should periodically clean the decoder_fifo
1057      * or have a way to prevent it filling up.
1058      */
1059     while (vout->p->b_error && !vout->p->b_done)
1060         vlc_cond_wait(&vout->p->change_wait, &vout->p->change_lock);
1061
1062     /* Clean thread */
1063     ThreadClean(vout);
1064
1065 exit_thread:
1066     /* Detach subpicture unit from both input and vout */
1067     spu_Attach(vout->p->p_spu, VLC_OBJECT(vout), false);
1068     vlc_object_detach(vout->p->p_spu);
1069
1070     /* Destroy the video filters2 */
1071     filter_chain_Delete(vout->p->p_vf2_chain);
1072
1073     vlc_mutex_unlock(&vout->p->change_lock);
1074
1075     if (has_wrapper)
1076         vout_CloseWrapper(vout);
1077
1078     return NULL;
1079 }
1080
1081 /*****************************************************************************
1082  * object variables callbacks: a bunch of object variables are used by the
1083  * interfaces to interact with the vout.
1084  *****************************************************************************/
1085 static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd,
1086                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1087 {
1088     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1089     input_thread_t *p_input;
1090     (void)psz_cmd; (void)oldval; (void)p_data;
1091
1092     p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1093                                                  FIND_PARENT );
1094     if (!p_input)
1095     {
1096         msg_Err( p_vout, "Input not found" );
1097         return VLC_EGENERIC;
1098     }
1099
1100     var_SetBool( p_vout, "intf-change", true );
1101
1102     /* Modify input as well because the vout might have to be restarted */
1103     var_Create( p_input, "vout-filter", VLC_VAR_STRING );
1104     var_SetString( p_input, "vout-filter", newval.psz_string );
1105
1106     /* Now restart current video stream */
1107     input_Control( p_input, INPUT_RESTART_ES, -VIDEO_ES );
1108     vlc_object_release( p_input );
1109
1110     return VLC_SUCCESS;
1111 }
1112
1113 /*****************************************************************************
1114  * Video Filter2 stuff
1115  *****************************************************************************/
1116 static int VideoFilter2Callback( vlc_object_t *p_this, char const *psz_cmd,
1117                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1118 {
1119     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1120     (void)psz_cmd; (void)oldval; (void)p_data;
1121
1122     vlc_mutex_lock( &p_vout->p->vfilter_lock );
1123     p_vout->p->psz_vf2 = strdup( newval.psz_string );
1124     vlc_mutex_unlock( &p_vout->p->vfilter_lock );
1125
1126     return VLC_SUCCESS;
1127 }
1128
1129 /*****************************************************************************
1130  * Post-processing
1131  *****************************************************************************/
1132 static bool PostProcessIsPresent( const char *psz_filter )
1133 {
1134     const char  *psz_pp = "postproc";
1135     const size_t i_pp = strlen(psz_pp);
1136     return psz_filter &&
1137            !strncmp( psz_filter, psz_pp, strlen(psz_pp) ) &&
1138            ( psz_filter[i_pp] == '\0' || psz_filter[i_pp] == ':' );
1139 }
1140
1141 static int PostProcessCallback( vlc_object_t *p_this, char const *psz_cmd,
1142                                 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1143 {
1144     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1145     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1146
1147     static const char *psz_pp = "postproc";
1148
1149     char *psz_vf2 = var_GetString( p_vout, "video-filter" );
1150
1151     if( newval.i_int <= 0 )
1152     {
1153         if( PostProcessIsPresent( psz_vf2 ) )
1154         {
1155             strcpy( psz_vf2, &psz_vf2[strlen(psz_pp)] );
1156             if( *psz_vf2 == ':' )
1157                 strcpy( psz_vf2, &psz_vf2[1] );
1158         }
1159     }
1160     else
1161     {
1162         if( !PostProcessIsPresent( psz_vf2 ) )
1163         {
1164             if( psz_vf2 )
1165             {
1166                 char *psz_tmp = psz_vf2;
1167                 if( asprintf( &psz_vf2, "%s:%s", psz_pp, psz_tmp ) < 0 )
1168                     psz_vf2 = psz_tmp;
1169                 else
1170                     free( psz_tmp );
1171             }
1172             else
1173             {
1174                 psz_vf2 = strdup( psz_pp );
1175             }
1176         }
1177     }
1178     if( psz_vf2 )
1179     {
1180         var_SetString( p_vout, "video-filter", psz_vf2 );
1181         free( psz_vf2 );
1182     }
1183
1184     return VLC_SUCCESS;
1185 }
1186 static void PostProcessEnable( vout_thread_t *p_vout )
1187 {
1188     vlc_value_t text;
1189     msg_Dbg( p_vout, "Post-processing available" );
1190     var_Create( p_vout, "postprocess", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
1191     text.psz_string = _("Post processing");
1192     var_Change( p_vout, "postprocess", VLC_VAR_SETTEXT, &text, NULL );
1193
1194     for( int i = 0; i <= 6; i++ )
1195     {
1196         vlc_value_t val;
1197         vlc_value_t text;
1198         char psz_text[1+1];
1199
1200         val.i_int = i;
1201         snprintf( psz_text, sizeof(psz_text), "%d", i );
1202         if( i == 0 )
1203             text.psz_string = _("Disable");
1204         else
1205             text.psz_string = psz_text;
1206         var_Change( p_vout, "postprocess", VLC_VAR_ADDCHOICE, &val, &text );
1207     }
1208     var_AddCallback( p_vout, "postprocess", PostProcessCallback, NULL );
1209
1210     /* */
1211     char *psz_filter = var_GetNonEmptyString( p_vout, "video-filter" );
1212     int i_postproc_q = 0;
1213     if( PostProcessIsPresent( psz_filter ) )
1214         i_postproc_q = var_CreateGetInteger( p_vout, "postproc-q" );
1215
1216     var_SetInteger( p_vout, "postprocess", i_postproc_q );
1217
1218     free( psz_filter );
1219 }
1220 static void PostProcessDisable( vout_thread_t *p_vout )
1221 {
1222     msg_Dbg( p_vout, "Post-processing no more available" );
1223     var_Destroy( p_vout, "postprocess" );
1224 }
1225 static void PostProcessSetFilterQuality( vout_thread_t *p_vout )
1226 {
1227     vlc_object_t *p_pp = vlc_object_find_name( p_vout, "postproc", FIND_CHILD );
1228     if( !p_pp )
1229         return;
1230
1231     var_SetInteger( p_pp, "postproc-q", var_GetInteger( p_vout, "postprocess" ) );
1232     vlc_object_release( p_pp );
1233 }
1234
1235
1236 static void DisplayTitleOnOSD( vout_thread_t *p_vout )
1237 {
1238     const mtime_t i_start = mdate();
1239     const mtime_t i_stop = i_start + INT64_C(1000) * p_vout->p->title.timeout;
1240
1241     if( i_stop <= i_start )
1242         return;
1243
1244     vlc_assert_locked( &p_vout->p->change_lock );
1245
1246     vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1247                            p_vout->p->title.value, NULL,
1248                            p_vout->p->title.position,
1249                            30 + p_vout->fmt_in.i_width
1250                               - p_vout->fmt_in.i_visible_width
1251                               - p_vout->fmt_in.i_x_offset,
1252                            20 + p_vout->fmt_in.i_y_offset,
1253                            i_start, i_stop );
1254
1255     free( p_vout->p->title.value );
1256
1257     p_vout->p->title.value = NULL;
1258 }
1259
1260 /*****************************************************************************
1261  * Deinterlacing
1262  *****************************************************************************/
1263 typedef struct
1264 {
1265     const char *psz_mode;
1266     bool       b_vout_filter;
1267 } deinterlace_mode_t;
1268
1269 /* XXX
1270  * You can use the non vout filter if and only if the video properties stay the
1271  * same (width/height/chroma/fps), at least for now.
1272  */
1273 static const deinterlace_mode_t p_deinterlace_mode[] = {
1274     { "",        false },
1275     //{ "discard", true },
1276     { "blend",   false },
1277     //{ "mean",    true  },
1278     //{ "bob",     true },
1279     //{ "linear",  true },
1280     { "x",       false },
1281     //{ "yadif",   true },
1282     //{ "yadif2x", true },
1283     { NULL,      false }
1284 };
1285 static const deinterlace_mode_t *DeinterlaceGetMode( const char *psz_mode )
1286 {
1287     for( const deinterlace_mode_t *p_mode = &p_deinterlace_mode[0]; p_mode->psz_mode; p_mode++ )
1288     {
1289         if( !strcmp( p_mode->psz_mode, psz_mode ) )
1290             return p_mode;
1291     }
1292     return NULL;
1293 }
1294
1295 static char *FilterFind( char *psz_filter_base, const char *psz_module )
1296 {
1297     const size_t i_module = strlen( psz_module );
1298     const char *psz_filter = psz_filter_base;
1299
1300     if( !psz_filter || i_module <= 0 )
1301         return NULL;
1302
1303     for( ;; )
1304     {
1305         char *psz_find = strstr( psz_filter, psz_module );
1306         if( !psz_find )
1307             return NULL;
1308         if( psz_find[i_module] == '\0' || psz_find[i_module] == ':' )
1309             return psz_find;
1310         psz_filter = &psz_find[i_module];
1311     }
1312 }
1313
1314 static bool DeinterlaceIsPresent( vout_thread_t *p_vout, bool b_vout_filter )
1315 {
1316     char *psz_filter = var_GetNonEmptyString( p_vout, b_vout_filter ? "vout-filter" : "video-filter" );
1317
1318     bool b_found = FilterFind( psz_filter, "deinterlace" ) != NULL;
1319
1320     free( psz_filter );
1321
1322     return b_found;
1323 }
1324
1325 static void DeinterlaceRemove( vout_thread_t *p_vout, bool b_vout_filter )
1326 {
1327     const char *psz_variable = b_vout_filter ? "vout-filter" : "video-filter";
1328     char *psz_filter = var_GetNonEmptyString( p_vout, psz_variable );
1329
1330     char *psz = FilterFind( psz_filter, "deinterlace" );
1331     if( !psz )
1332     {
1333         free( psz_filter );
1334         return;
1335     }
1336
1337     /* */
1338     strcpy( &psz[0], &psz[strlen("deinterlace")] );
1339     if( *psz == ':' )
1340         strcpy( &psz[0], &psz[1] );
1341
1342     var_SetString( p_vout, psz_variable, psz_filter );
1343     free( psz_filter );
1344 }
1345 static void DeinterlaceAdd( vout_thread_t *p_vout, bool b_vout_filter )
1346 {
1347     const char *psz_variable = b_vout_filter ? "vout-filter" : "video-filter";
1348
1349     char *psz_filter = var_GetNonEmptyString( p_vout, psz_variable );
1350
1351     if( FilterFind( psz_filter, "deinterlace" ) )
1352     {
1353         free( psz_filter );
1354         return;
1355     }
1356
1357     /* */
1358     if( psz_filter )
1359     {
1360         char *psz_tmp = psz_filter;
1361         if( asprintf( &psz_filter, "%s:%s", psz_tmp, "deinterlace" ) < 0 )
1362             psz_filter = psz_tmp;
1363         else
1364             free( psz_tmp );
1365     }
1366     else
1367     {
1368         psz_filter = strdup( "deinterlace" );
1369     }
1370
1371     if( psz_filter )
1372     {
1373         var_SetString( p_vout, psz_variable, psz_filter );
1374         free( psz_filter );
1375     }
1376 }
1377
1378 static void DeinterlaceSave( vout_thread_t *p_vout, int i_deinterlace, const char *psz_mode, bool is_needed )
1379 {
1380     /* We have to set input variable to ensure restart support
1381      * XXX it is only needed because of vout-filter but must be done
1382      * for non video filter anyway */
1383     vlc_object_t *p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, FIND_PARENT );
1384     if( !p_input )
1385         return;
1386
1387     /* Another hack for "vout filter" mode */
1388     if( i_deinterlace < 0 )
1389         i_deinterlace = is_needed ? -2 : -3;
1390
1391     var_Create( p_input, "deinterlace", VLC_VAR_INTEGER );
1392     var_SetInteger( p_input, "deinterlace", i_deinterlace );
1393
1394     static const char * const ppsz_variable[] = {
1395         "deinterlace-mode",
1396         "filter-deinterlace-mode",
1397         "sout-deinterlace-mode",
1398         NULL
1399     };
1400     for( int i = 0; ppsz_variable[i]; i++ )
1401     {
1402         var_Create( p_input, ppsz_variable[i], VLC_VAR_STRING );
1403         var_SetString( p_input, ppsz_variable[i], psz_mode );
1404     }
1405
1406     vlc_object_release( p_input );
1407 }
1408 static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd,
1409                                 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1410 {
1411     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(newval); VLC_UNUSED(p_data);
1412     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1413
1414     /* */
1415     const int  i_deinterlace = var_GetInteger( p_this, "deinterlace" );
1416     char       *psz_mode     = var_GetString( p_this, "deinterlace-mode" );
1417     const bool is_needed     = var_GetBool( p_this, "deinterlace-needed" );
1418     if( !psz_mode )
1419         return VLC_EGENERIC;
1420
1421     DeinterlaceSave( p_vout, i_deinterlace, psz_mode, is_needed );
1422
1423     /* */
1424     bool b_vout_filter = false;
1425     const deinterlace_mode_t *p_mode = DeinterlaceGetMode( psz_mode );
1426     if( p_mode )
1427         b_vout_filter = p_mode->b_vout_filter;
1428
1429     /* */
1430     char *psz_old;
1431     if( b_vout_filter )
1432     {
1433         psz_old = var_CreateGetString( p_vout, "filter-deinterlace-mode" );
1434     }
1435     else
1436     {
1437         psz_old = var_CreateGetString( p_vout, "sout-deinterlace-mode" );
1438         var_SetString( p_vout, "sout-deinterlace-mode", psz_mode );
1439     }
1440
1441     msg_Dbg( p_vout, "deinterlace %d, mode %s, is_needed %d", i_deinterlace, psz_mode, is_needed );
1442     if( i_deinterlace == 0 || ( i_deinterlace == -1 && !is_needed ) )
1443     {
1444         DeinterlaceRemove( p_vout, false );
1445         DeinterlaceRemove( p_vout, true );
1446     }
1447     else
1448     {
1449         if( !DeinterlaceIsPresent( p_vout, b_vout_filter ) )
1450         {
1451             DeinterlaceRemove( p_vout, !b_vout_filter );
1452             DeinterlaceAdd( p_vout, b_vout_filter );
1453         }
1454         else
1455         {
1456             /* The deinterlace filter was already inserted but we have changed the mode */
1457             DeinterlaceRemove( p_vout, !b_vout_filter );
1458             if( psz_old && strcmp( psz_old, psz_mode ) )
1459                 var_TriggerCallback( p_vout, b_vout_filter ? "vout-filter" : "video-filter" );
1460         }
1461     }
1462
1463     /* */
1464     free( psz_old );
1465     free( psz_mode );
1466     return VLC_SUCCESS;
1467 }
1468
1469 static void DeinterlaceEnable( vout_thread_t *p_vout )
1470 {
1471     vlc_value_t val, text;
1472
1473     if( !p_vout->p->b_first_vout )
1474         return;
1475
1476     msg_Dbg( p_vout, "Deinterlacing available" );
1477
1478     /* Create the configuration variables */
1479     /* */
1480     var_Create( p_vout, "deinterlace", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT | VLC_VAR_HASCHOICE );
1481     int i_deinterlace = var_GetInteger( p_vout, "deinterlace" );
1482
1483     text.psz_string = _("Deinterlace");
1484     var_Change( p_vout, "deinterlace", VLC_VAR_SETTEXT, &text, NULL );
1485
1486     const module_config_t *p_optd = config_FindConfig( VLC_OBJECT(p_vout), "deinterlace" );
1487     var_Change( p_vout, "deinterlace", VLC_VAR_CLEARCHOICES, NULL, NULL );
1488     for( int i = 0; p_optd && i < p_optd->i_list; i++ )
1489     {
1490         val.i_int  = p_optd->pi_list[i];
1491         text.psz_string = (char*)vlc_gettext(p_optd->ppsz_list_text[i]);
1492         var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
1493     }
1494     var_AddCallback( p_vout, "deinterlace", DeinterlaceCallback, NULL );
1495     /* */
1496     var_Create( p_vout, "deinterlace-mode", VLC_VAR_STRING | VLC_VAR_DOINHERIT | VLC_VAR_HASCHOICE );
1497     char *psz_deinterlace = var_GetNonEmptyString( p_vout, "deinterlace-mode" );
1498
1499     text.psz_string = _("Deinterlace mode");
1500     var_Change( p_vout, "deinterlace-mode", VLC_VAR_SETTEXT, &text, NULL );
1501
1502     const module_config_t *p_optm = config_FindConfig( VLC_OBJECT(p_vout), "deinterlace-mode" );
1503     var_Change( p_vout, "deinterlace-mode", VLC_VAR_CLEARCHOICES, NULL, NULL );
1504     for( int i = 0; p_optm && i < p_optm->i_list; i++ )
1505     {
1506         if( !DeinterlaceGetMode( p_optm->ppsz_list[i] ) )
1507             continue;
1508
1509         val.psz_string  = p_optm->ppsz_list[i];
1510         text.psz_string = (char*)vlc_gettext(p_optm->ppsz_list_text[i]);
1511         var_Change( p_vout, "deinterlace-mode", VLC_VAR_ADDCHOICE, &val, &text );
1512     }
1513     var_AddCallback( p_vout, "deinterlace-mode", DeinterlaceCallback, NULL );
1514     /* */
1515     var_Create( p_vout, "deinterlace-needed", VLC_VAR_BOOL );
1516     var_AddCallback( p_vout, "deinterlace-needed", DeinterlaceCallback, NULL );
1517
1518     /* Override the initial value from filters if present */
1519     char *psz_filter_mode = NULL;
1520     if( DeinterlaceIsPresent( p_vout, true ) )
1521         psz_filter_mode = var_CreateGetNonEmptyString( p_vout, "filter-deinterlace-mode" );
1522     else if( DeinterlaceIsPresent( p_vout, false ) )
1523         psz_filter_mode = var_CreateGetNonEmptyString( p_vout, "sout-deinterlace-mode" );
1524     if( psz_filter_mode )
1525     {
1526         free( psz_deinterlace );
1527         if( i_deinterlace >= -1 )
1528             i_deinterlace = 1;
1529         psz_deinterlace = psz_filter_mode;
1530     }
1531
1532     /* */
1533     if( i_deinterlace == -2 )
1534         p_vout->p->displayed.is_interlaced = true;
1535     else if( i_deinterlace == -3 )
1536         p_vout->p->displayed.is_interlaced = false;
1537     if( i_deinterlace < 0 )
1538         i_deinterlace = -1;
1539
1540     /* */
1541     val.psz_string = psz_deinterlace ? psz_deinterlace : p_optm->orig.psz;
1542     var_Change( p_vout, "deinterlace-mode", VLC_VAR_SETVALUE, &val, NULL );
1543     val.b_bool = p_vout->p->displayed.is_interlaced;
1544     var_Change( p_vout, "deinterlace-needed", VLC_VAR_SETVALUE, &val, NULL );
1545
1546     var_SetInteger( p_vout, "deinterlace", i_deinterlace );
1547     free( psz_deinterlace );
1548 }
1549
1550 static void DeinterlaceNeeded( vout_thread_t *p_vout, bool is_interlaced )
1551 {
1552     msg_Dbg( p_vout, "Detected %s video",
1553              is_interlaced ? "interlaced" : "progressive" );
1554     var_SetBool( p_vout, "deinterlace-needed", is_interlaced );
1555 }
1556
1557 /* */
1558 static void PrintVideoFormat(vout_thread_t *vout,
1559                              const char *description,
1560                              const video_format_t *fmt)
1561 {
1562     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",
1563             description,
1564             fmt->i_width, fmt->i_height, fmt->i_x_offset, fmt->i_y_offset,
1565             fmt->i_visible_width, fmt->i_visible_height,
1566             (char*)&fmt->i_chroma,
1567             fmt->i_sar_num, fmt->i_sar_den,
1568             fmt->i_rmask, fmt->i_gmask, fmt->i_bmask);
1569 }
1570