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