]> git.sesse.net Git - vlc/blob - src/video_output/video_output.c
Removed dead code.
[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         vout_chrono_Start(&vout->p->render);
842
843         picture_t *filtered = NULL;
844         if (decoded) {
845             vlc_mutex_lock(&vout->p->vfilter_lock);
846             filtered = filter_chain_VideoFilter(vout->p->p_vf2_chain, decoded);
847             //assert(filtered == decoded); // TODO implement
848             vlc_mutex_unlock(&vout->p->vfilter_lock);
849             if (!filtered)
850                 continue;
851         }
852
853         /*
854          * Check for subpictures to display
855          */
856         const bool do_snapshot = vout_snapshot_IsRequested(&vout->p->snapshot);
857         mtime_t spu_render_time = is_forced ? mdate() : filtered->date;
858         if (vout->p->b_paused)
859             spu_render_time = vout->p->i_pause_date;
860         else
861             spu_render_time = filtered->date > 1 ? filtered->date : mdate();
862
863         subpicture_t *subpic = spu_SortSubpictures(vout->p->p_spu,
864                                                    spu_render_time,
865                                                    do_snapshot);
866         /*
867          * Perform rendering
868          *
869          * We have to:
870          * - be sure to end up with a direct buffer.
871          * - blend subtitles, and in a fast access buffer
872          */
873         picture_t *direct = NULL;
874         if (filtered &&
875             (vout->p->decoder_pool != vout->p->display_pool || subpic)) {
876             picture_t *render;
877             if (vout->p->is_decoder_pool_slow)
878                 render = picture_NewFromFormat(&vout->fmt_out);
879             else if (vout->p->decoder_pool != vout->p->display_pool)
880                 render = picture_pool_Get(vout->p->display_pool);
881             else
882                 render = picture_pool_Get(vout->p->private_pool);
883
884             if (render) {
885                 picture_Copy(render, filtered);
886
887                 spu_RenderSubpictures(vout->p->p_spu,
888                                       render, &vout->fmt_out,
889                                       subpic, &vout->fmt_in, spu_render_time);
890             }
891             if (vout->p->is_decoder_pool_slow) {
892                 direct = picture_pool_Get(vout->p->display_pool);
893                 if (direct)
894                     picture_Copy(direct, render);
895                 picture_Release(render);
896
897             } else {
898                 direct = render;
899             }
900             picture_Release(filtered);
901             filtered = NULL;
902         } else {
903             direct = filtered;
904         }
905
906         /*
907          * Take a snapshot if requested
908          */
909         if (direct && do_snapshot)
910             vout_snapshot_Set(&vout->p->snapshot, &vout->fmt_out, direct);
911
912         /* Render the direct buffer returned by vout_RenderPicture */
913         if (direct) {
914             vout_RenderWrapper(vout, direct);
915
916             vout_chrono_Stop(&vout->p->render);
917 #if 0
918             {
919             static int i = 0;
920             if (((i++)%10) == 0)
921                 msg_Info(vout, "render: avg %d ms var %d ms",
922                          (int)(vout->p->render.avg/1000), (int)(vout->p->render.var/1000));
923             }
924 #endif
925         }
926
927         /* Wait the real date (for rendering jitter) */
928         if (!is_forced)
929             mwait(decoded->date);
930
931         /* Display the direct buffer returned by vout_RenderPicture */
932         vout->p->displayed.clock = mdate();
933         if (direct)
934             vout_DisplayWrapper(vout, direct);
935
936         displayed_count++;
937         break;
938     }
939
940     vout_statistic_Update(&vout->p->statistic, displayed_count, lost_count);
941     if (displayed_count <= 0)
942         return VLC_EGENERIC;
943     return VLC_SUCCESS;
944 }
945
946 /*****************************************************************************
947  * RunThread: video output thread
948  *****************************************************************************
949  * Video output thread. This function does only returns when the thread is
950  * terminated. It handles the pictures arriving in the video heap and the
951  * display device events.
952  *****************************************************************************/
953 static void *RunThread(void *object)
954 {
955     vout_thread_t *vout = object;
956     bool          has_wrapper;
957
958     /*
959      * Initialize thread
960      */
961     has_wrapper = !vout_OpenWrapper(vout, vout->p->psz_module_name);
962
963     vlc_mutex_lock(&vout->p->change_lock);
964
965     if (has_wrapper)
966         vout->p->b_error = InitThread(vout);
967     else
968         vout->p->b_error = true;
969
970     /* signal the creation of the vout */
971     vout->p->b_ready = true;
972     vlc_cond_signal(&vout->p->change_wait);
973
974     if (vout->p->b_error)
975         goto exit_thread;
976
977     /* */
978     bool    last_picture_interlaced      = false;
979     int     last_picture_qtype           = QTYPE_NONE;
980     mtime_t last_picture_interlaced_date = mdate();
981
982     /*
983      * Main loop - it is not executed if an error occurred during
984      * initialization
985      */
986     while (!vout->p->b_done && !vout->p->b_error) {
987         /* */
988         if(vout->p->b_title_show && vout->p->psz_title)
989             DisplayTitleOnOSD(vout);
990
991         vlc_mutex_lock(&vout->p->picture_lock);
992
993         mtime_t deadline = VLC_TS_INVALID;
994         bool has_displayed = !ThreadDisplayPicture(vout, vout->p->step.is_requested, &deadline);
995
996         if (has_displayed) {
997             vout->p->step.timestamp = vout->p->displayed.timestampX;
998             if (vout->p->step.last <= VLC_TS_INVALID)
999                 vout->p->step.last = vout->p->step.timestamp;
1000         }
1001         if (vout->p->step.is_requested) {
1002             if (!has_displayed && !vout->p->b_picture_empty) {
1003                 picture_t *peek = picture_fifo_Peek(vout->p->decoder_fifo);
1004                 if (peek)
1005                     picture_Release(peek);
1006                 if (!peek) {
1007                     vout->p->b_picture_empty = true;
1008                     vlc_cond_signal(&vout->p->picture_wait);
1009                 }
1010             }
1011             if (has_displayed) {
1012                 vout->p->step.is_requested = false;
1013                 vlc_cond_signal(&vout->p->picture_wait);
1014             }
1015         }
1016
1017         const int  picture_qtype      = vout->p->displayed.qtype;
1018         const bool picture_interlaced = vout->p->displayed.is_interlaced;
1019
1020         vlc_mutex_unlock(&vout->p->picture_lock);
1021
1022         if (vout_ManageWrapper(vout)) {
1023             /* A fatal error occurred, and the thread must terminate
1024              * immediately, without displaying anything - setting b_error to 1
1025              * causes the immediate end of the main while() loop. */
1026             // FIXME pf_end
1027             vout->p->b_error = true;
1028             break;
1029         }
1030
1031         /* Post processing */
1032         const int postproc_change = (picture_qtype != QTYPE_NONE) - (last_picture_qtype != QTYPE_NONE);
1033         if (postproc_change == 1)
1034             PostProcessEnable(vout);
1035         else if (postproc_change == -1)
1036             PostProcessDisable(vout);
1037         if (postproc_change)
1038             last_picture_qtype = picture_qtype;
1039
1040         /* Deinterlacing
1041          * Wait 30s before quiting interlacing mode */
1042         const int interlacing_change = (!!picture_interlaced) - (!!last_picture_interlaced);
1043         if ((interlacing_change == 1) ||
1044             (interlacing_change == -1 && last_picture_interlaced_date + 30000000 < mdate())) {
1045             DeinterlaceNeeded(vout, picture_interlaced);
1046             last_picture_interlaced = picture_interlaced;
1047         }
1048         if (picture_interlaced)
1049             last_picture_interlaced_date = mdate();
1050
1051         /* Check for "video filter2" changes */
1052         vlc_mutex_lock(&vout->p->vfilter_lock);
1053         if (vout->p->psz_vf2) {
1054             es_format_t fmt;
1055
1056             es_format_Init(&fmt, VIDEO_ES, vout->fmt_render.i_chroma);
1057             fmt.video = vout->fmt_render;
1058             filter_chain_Reset(vout->p->p_vf2_chain, &fmt, &fmt);
1059
1060             if (filter_chain_AppendFromString(vout->p->p_vf2_chain,
1061                                               vout->p->psz_vf2) < 0)
1062                 msg_Err(vout, "Video filter chain creation failed");
1063
1064             free(vout->p->psz_vf2);
1065             vout->p->psz_vf2 = NULL;
1066
1067             if (last_picture_qtype != QTYPE_NONE)
1068                 PostProcessSetFilterQuality(vout);
1069         }
1070         vlc_mutex_unlock(&vout->p->vfilter_lock);
1071
1072         vlc_mutex_unlock(&vout->p->change_lock);
1073
1074         if (deadline > VLC_TS_INVALID) {
1075             vlc_mutex_lock(&vout->p->picture_lock);
1076             vlc_cond_timedwait(&vout->p->picture_wait, &vout->p->picture_lock, deadline);
1077             vlc_mutex_unlock(&vout->p->picture_lock);
1078         }
1079
1080         vlc_mutex_lock(&vout->p->change_lock);
1081     }
1082
1083     /*
1084      * Error loop - wait until the thread destruction is requested
1085      *
1086      * XXX I wonder if we should periodically clean the decoder_fifo
1087      * or have a way to prevent it filling up.
1088      */
1089     while (vout->p->b_error && !vout->p->b_done)
1090         vlc_cond_wait(&vout->p->change_wait, &vout->p->change_lock);
1091
1092     /* Clean thread */
1093     CleanThread(vout);
1094
1095 exit_thread:
1096     /* End of thread */
1097     EndThread(vout);
1098     vlc_mutex_unlock(&vout->p->change_lock);
1099
1100     if (has_wrapper)
1101         vout_CloseWrapper(vout);
1102
1103     return NULL;
1104 }
1105
1106 /*****************************************************************************
1107  * CleanThread: clean up after InitThread
1108  *****************************************************************************
1109  * This function is called after a sucessful
1110  * initialization. It frees all resources allocated by InitThread.
1111  * XXX You have to enter it with change_lock taken.
1112  *****************************************************************************/
1113 static void CleanThread( vout_thread_t *p_vout )
1114 {
1115     /* Destroy translation tables */
1116     if( !p_vout->p->b_error )
1117     {
1118         picture_fifo_Flush( p_vout->p->decoder_fifo, INT64_MAX, false );
1119         vout_EndWrapper( p_vout );
1120     }
1121 }
1122
1123 /*****************************************************************************
1124  * EndThread: thread destruction
1125  *****************************************************************************
1126  * This function is called when the thread ends.
1127  * It frees all resources not allocated by InitThread.
1128  * XXX You have to enter it with change_lock taken.
1129  *****************************************************************************/
1130 static void EndThread( vout_thread_t *p_vout )
1131 {
1132     /* FIXME does that function *really* need to be called inside the thread ? */
1133
1134     /* Detach subpicture unit from both input and vout */
1135     spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), false );
1136     vlc_object_detach( p_vout->p->p_spu );
1137
1138     /* Destroy the video filters2 */
1139     filter_chain_Delete( p_vout->p->p_vf2_chain );
1140 }
1141
1142 /* following functions are local */
1143
1144 /*****************************************************************************
1145  * object variables callbacks: a bunch of object variables are used by the
1146  * interfaces to interact with the vout.
1147  *****************************************************************************/
1148 static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd,
1149                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1150 {
1151     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1152     input_thread_t *p_input;
1153     (void)psz_cmd; (void)oldval; (void)p_data;
1154
1155     p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1156                                                  FIND_PARENT );
1157     if (!p_input)
1158     {
1159         msg_Err( p_vout, "Input not found" );
1160         return VLC_EGENERIC;
1161     }
1162
1163     var_SetBool( p_vout, "intf-change", true );
1164
1165     /* Modify input as well because the vout might have to be restarted */
1166     var_Create( p_input, "vout-filter", VLC_VAR_STRING );
1167     var_SetString( p_input, "vout-filter", newval.psz_string );
1168
1169     /* Now restart current video stream */
1170     input_Control( p_input, INPUT_RESTART_ES, -VIDEO_ES );
1171     vlc_object_release( p_input );
1172
1173     return VLC_SUCCESS;
1174 }
1175
1176 /*****************************************************************************
1177  * Video Filter2 stuff
1178  *****************************************************************************/
1179 static int VideoFilter2Callback( vlc_object_t *p_this, char const *psz_cmd,
1180                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1181 {
1182     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1183     (void)psz_cmd; (void)oldval; (void)p_data;
1184
1185     vlc_mutex_lock( &p_vout->p->vfilter_lock );
1186     p_vout->p->psz_vf2 = strdup( newval.psz_string );
1187     vlc_mutex_unlock( &p_vout->p->vfilter_lock );
1188
1189     return VLC_SUCCESS;
1190 }
1191
1192 /*****************************************************************************
1193  * Post-processing
1194  *****************************************************************************/
1195 static bool PostProcessIsPresent( const char *psz_filter )
1196 {
1197     const char  *psz_pp = "postproc";
1198     const size_t i_pp = strlen(psz_pp);
1199     return psz_filter &&
1200            !strncmp( psz_filter, psz_pp, strlen(psz_pp) ) &&
1201            ( psz_filter[i_pp] == '\0' || psz_filter[i_pp] == ':' );
1202 }
1203
1204 static int PostProcessCallback( vlc_object_t *p_this, char const *psz_cmd,
1205                                 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1206 {
1207     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1208     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1209
1210     static const char *psz_pp = "postproc";
1211
1212     char *psz_vf2 = var_GetString( p_vout, "video-filter" );
1213
1214     if( newval.i_int <= 0 )
1215     {
1216         if( PostProcessIsPresent( psz_vf2 ) )
1217         {
1218             strcpy( psz_vf2, &psz_vf2[strlen(psz_pp)] );
1219             if( *psz_vf2 == ':' )
1220                 strcpy( psz_vf2, &psz_vf2[1] );
1221         }
1222     }
1223     else
1224     {
1225         if( !PostProcessIsPresent( psz_vf2 ) )
1226         {
1227             if( psz_vf2 )
1228             {
1229                 char *psz_tmp = psz_vf2;
1230                 if( asprintf( &psz_vf2, "%s:%s", psz_pp, psz_tmp ) < 0 )
1231                     psz_vf2 = psz_tmp;
1232                 else
1233                     free( psz_tmp );
1234             }
1235             else
1236             {
1237                 psz_vf2 = strdup( psz_pp );
1238             }
1239         }
1240     }
1241     if( psz_vf2 )
1242     {
1243         var_SetString( p_vout, "video-filter", psz_vf2 );
1244         free( psz_vf2 );
1245     }
1246
1247     return VLC_SUCCESS;
1248 }
1249 static void PostProcessEnable( vout_thread_t *p_vout )
1250 {
1251     vlc_value_t text;
1252     msg_Dbg( p_vout, "Post-processing available" );
1253     var_Create( p_vout, "postprocess", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
1254     text.psz_string = _("Post processing");
1255     var_Change( p_vout, "postprocess", VLC_VAR_SETTEXT, &text, NULL );
1256
1257     for( int i = 0; i <= 6; i++ )
1258     {
1259         vlc_value_t val;
1260         vlc_value_t text;
1261         char psz_text[1+1];
1262
1263         val.i_int = i;
1264         snprintf( psz_text, sizeof(psz_text), "%d", i );
1265         if( i == 0 )
1266             text.psz_string = _("Disable");
1267         else
1268             text.psz_string = psz_text;
1269         var_Change( p_vout, "postprocess", VLC_VAR_ADDCHOICE, &val, &text );
1270     }
1271     var_AddCallback( p_vout, "postprocess", PostProcessCallback, NULL );
1272
1273     /* */
1274     char *psz_filter = var_GetNonEmptyString( p_vout, "video-filter" );
1275     int i_postproc_q = 0;
1276     if( PostProcessIsPresent( psz_filter ) )
1277         i_postproc_q = var_CreateGetInteger( p_vout, "postproc-q" );
1278
1279     var_SetInteger( p_vout, "postprocess", i_postproc_q );
1280
1281     free( psz_filter );
1282 }
1283 static void PostProcessDisable( vout_thread_t *p_vout )
1284 {
1285     msg_Dbg( p_vout, "Post-processing no more available" );
1286     var_Destroy( p_vout, "postprocess" );
1287 }
1288 static void PostProcessSetFilterQuality( vout_thread_t *p_vout )
1289 {
1290     vlc_object_t *p_pp = vlc_object_find_name( p_vout, "postproc", FIND_CHILD );
1291     if( !p_pp )
1292         return;
1293
1294     var_SetInteger( p_pp, "postproc-q", var_GetInteger( p_vout, "postprocess" ) );
1295     vlc_object_release( p_pp );
1296 }
1297
1298
1299 static void DisplayTitleOnOSD( vout_thread_t *p_vout )
1300 {
1301     const mtime_t i_start = mdate();
1302     const mtime_t i_stop = i_start + INT64_C(1000) * p_vout->p->i_title_timeout;
1303
1304     if( i_stop <= i_start )
1305         return;
1306
1307     vlc_assert_locked( &p_vout->p->change_lock );
1308
1309     vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1310                            p_vout->p->psz_title, NULL,
1311                            p_vout->p->i_title_position,
1312                            30 + p_vout->fmt_in.i_width
1313                               - p_vout->fmt_in.i_visible_width
1314                               - p_vout->fmt_in.i_x_offset,
1315                            20 + p_vout->fmt_in.i_y_offset,
1316                            i_start, i_stop );
1317
1318     free( p_vout->p->psz_title );
1319
1320     p_vout->p->psz_title = NULL;
1321 }
1322
1323 /*****************************************************************************
1324  * Deinterlacing
1325  *****************************************************************************/
1326 typedef struct
1327 {
1328     const char *psz_mode;
1329     bool       b_vout_filter;
1330 } deinterlace_mode_t;
1331
1332 /* XXX
1333  * You can use the non vout filter if and only if the video properties stay the
1334  * same (width/height/chroma/fps), at least for now.
1335  */
1336 static const deinterlace_mode_t p_deinterlace_mode[] = {
1337     { "",        false },
1338     //{ "discard", true },
1339     { "blend",   false },
1340     //{ "mean",    true  },
1341     //{ "bob",     true },
1342     //{ "linear",  true },
1343     { "x",       false },
1344     //{ "yadif",   true },
1345     //{ "yadif2x", true },
1346     { NULL,      false }
1347 };
1348 static const deinterlace_mode_t *DeinterlaceGetMode( const char *psz_mode )
1349 {
1350     for( const deinterlace_mode_t *p_mode = &p_deinterlace_mode[0]; p_mode->psz_mode; p_mode++ )
1351     {
1352         if( !strcmp( p_mode->psz_mode, psz_mode ) )
1353             return p_mode;
1354     }
1355     return NULL;
1356 }
1357
1358 static char *FilterFind( char *psz_filter_base, const char *psz_module )
1359 {
1360     const size_t i_module = strlen( psz_module );
1361     const char *psz_filter = psz_filter_base;
1362
1363     if( !psz_filter || i_module <= 0 )
1364         return NULL;
1365
1366     for( ;; )
1367     {
1368         char *psz_find = strstr( psz_filter, psz_module );
1369         if( !psz_find )
1370             return NULL;
1371         if( psz_find[i_module] == '\0' || psz_find[i_module] == ':' )
1372             return psz_find;
1373         psz_filter = &psz_find[i_module];
1374     }
1375 }
1376
1377 static bool DeinterlaceIsPresent( vout_thread_t *p_vout, bool b_vout_filter )
1378 {
1379     char *psz_filter = var_GetNonEmptyString( p_vout, b_vout_filter ? "vout-filter" : "video-filter" );
1380
1381     bool b_found = FilterFind( psz_filter, "deinterlace" ) != NULL;
1382
1383     free( psz_filter );
1384
1385     return b_found;
1386 }
1387
1388 static void DeinterlaceRemove( vout_thread_t *p_vout, bool b_vout_filter )
1389 {
1390     const char *psz_variable = b_vout_filter ? "vout-filter" : "video-filter";
1391     char *psz_filter = var_GetNonEmptyString( p_vout, psz_variable );
1392
1393     char *psz = FilterFind( psz_filter, "deinterlace" );
1394     if( !psz )
1395     {
1396         free( psz_filter );
1397         return;
1398     }
1399
1400     /* */
1401     strcpy( &psz[0], &psz[strlen("deinterlace")] );
1402     if( *psz == ':' )
1403         strcpy( &psz[0], &psz[1] );
1404
1405     var_SetString( p_vout, psz_variable, psz_filter );
1406     free( psz_filter );
1407 }
1408 static void DeinterlaceAdd( vout_thread_t *p_vout, bool b_vout_filter )
1409 {
1410     const char *psz_variable = b_vout_filter ? "vout-filter" : "video-filter";
1411
1412     char *psz_filter = var_GetNonEmptyString( p_vout, psz_variable );
1413
1414     if( FilterFind( psz_filter, "deinterlace" ) )
1415     {
1416         free( psz_filter );
1417         return;
1418     }
1419
1420     /* */
1421     if( psz_filter )
1422     {
1423         char *psz_tmp = psz_filter;
1424         if( asprintf( &psz_filter, "%s:%s", psz_tmp, "deinterlace" ) < 0 )
1425             psz_filter = psz_tmp;
1426         else
1427             free( psz_tmp );
1428     }
1429     else
1430     {
1431         psz_filter = strdup( "deinterlace" );
1432     }
1433
1434     if( psz_filter )
1435     {
1436         var_SetString( p_vout, psz_variable, psz_filter );
1437         free( psz_filter );
1438     }
1439 }
1440
1441 static void DeinterlaceSave( vout_thread_t *p_vout, int i_deinterlace, const char *psz_mode, bool is_needed )
1442 {
1443     /* We have to set input variable to ensure restart support
1444      * XXX it is only needed because of vout-filter but must be done
1445      * for non video filter anyway */
1446     vlc_object_t *p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, FIND_PARENT );
1447     if( !p_input )
1448         return;
1449
1450     /* Another hack for "vout filter" mode */
1451     if( i_deinterlace < 0 )
1452         i_deinterlace = is_needed ? -2 : -3;
1453
1454     var_Create( p_input, "deinterlace", VLC_VAR_INTEGER );
1455     var_SetInteger( p_input, "deinterlace", i_deinterlace );
1456
1457     static const char * const ppsz_variable[] = {
1458         "deinterlace-mode",
1459         "filter-deinterlace-mode",
1460         "sout-deinterlace-mode",
1461         NULL
1462     };
1463     for( int i = 0; ppsz_variable[i]; i++ )
1464     {
1465         var_Create( p_input, ppsz_variable[i], VLC_VAR_STRING );
1466         var_SetString( p_input, ppsz_variable[i], psz_mode );
1467     }
1468
1469     vlc_object_release( p_input );
1470 }
1471 static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd,
1472                                 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1473 {
1474     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(newval); VLC_UNUSED(p_data);
1475     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1476
1477     /* */
1478     const int  i_deinterlace = var_GetInteger( p_this, "deinterlace" );
1479     char       *psz_mode     = var_GetString( p_this, "deinterlace-mode" );
1480     const bool is_needed     = var_GetBool( p_this, "deinterlace-needed" );
1481     if( !psz_mode )
1482         return VLC_EGENERIC;
1483
1484     DeinterlaceSave( p_vout, i_deinterlace, psz_mode, is_needed );
1485
1486     /* */
1487     bool b_vout_filter = false;
1488     const deinterlace_mode_t *p_mode = DeinterlaceGetMode( psz_mode );
1489     if( p_mode )
1490         b_vout_filter = p_mode->b_vout_filter;
1491
1492     /* */
1493     char *psz_old;
1494     if( b_vout_filter )
1495     {
1496         psz_old = var_CreateGetString( p_vout, "filter-deinterlace-mode" );
1497     }
1498     else
1499     {
1500         psz_old = var_CreateGetString( p_vout, "sout-deinterlace-mode" );
1501         var_SetString( p_vout, "sout-deinterlace-mode", psz_mode );
1502     }
1503
1504     msg_Dbg( p_vout, "deinterlace %d, mode %s, is_needed %d", i_deinterlace, psz_mode, is_needed );
1505     if( i_deinterlace == 0 || ( i_deinterlace == -1 && !is_needed ) )
1506     {
1507         DeinterlaceRemove( p_vout, false );
1508         DeinterlaceRemove( p_vout, true );
1509     }
1510     else
1511     {
1512         if( !DeinterlaceIsPresent( p_vout, b_vout_filter ) )
1513         {
1514             DeinterlaceRemove( p_vout, !b_vout_filter );
1515             DeinterlaceAdd( p_vout, b_vout_filter );
1516         }
1517         else
1518         {
1519             /* The deinterlace filter was already inserted but we have changed the mode */
1520             DeinterlaceRemove( p_vout, !b_vout_filter );
1521             if( psz_old && strcmp( psz_old, psz_mode ) )
1522                 var_TriggerCallback( p_vout, b_vout_filter ? "vout-filter" : "video-filter" );
1523         }
1524     }
1525
1526     /* */
1527     free( psz_old );
1528     free( psz_mode );
1529     return VLC_SUCCESS;
1530 }
1531
1532 static void DeinterlaceEnable( vout_thread_t *p_vout )
1533 {
1534     vlc_value_t val, text;
1535
1536     if( !p_vout->p->b_first_vout )
1537         return;
1538
1539     msg_Dbg( p_vout, "Deinterlacing available" );
1540
1541     /* Create the configuration variables */
1542     /* */
1543     var_Create( p_vout, "deinterlace", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT | VLC_VAR_HASCHOICE );
1544     int i_deinterlace = var_GetInteger( p_vout, "deinterlace" );
1545
1546     text.psz_string = _("Deinterlace");
1547     var_Change( p_vout, "deinterlace", VLC_VAR_SETTEXT, &text, NULL );
1548
1549     const module_config_t *p_optd = config_FindConfig( VLC_OBJECT(p_vout), "deinterlace" );
1550     var_Change( p_vout, "deinterlace", VLC_VAR_CLEARCHOICES, NULL, NULL );
1551     for( int i = 0; p_optd && i < p_optd->i_list; i++ )
1552     {
1553         val.i_int  = p_optd->pi_list[i];
1554         text.psz_string = (char*)vlc_gettext(p_optd->ppsz_list_text[i]);
1555         var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
1556     }
1557     var_AddCallback( p_vout, "deinterlace", DeinterlaceCallback, NULL );
1558     /* */
1559     var_Create( p_vout, "deinterlace-mode", VLC_VAR_STRING | VLC_VAR_DOINHERIT | VLC_VAR_HASCHOICE );
1560     char *psz_deinterlace = var_GetNonEmptyString( p_vout, "deinterlace-mode" );
1561
1562     text.psz_string = _("Deinterlace mode");
1563     var_Change( p_vout, "deinterlace-mode", VLC_VAR_SETTEXT, &text, NULL );
1564
1565     const module_config_t *p_optm = config_FindConfig( VLC_OBJECT(p_vout), "deinterlace-mode" );
1566     var_Change( p_vout, "deinterlace-mode", VLC_VAR_CLEARCHOICES, NULL, NULL );
1567     for( int i = 0; p_optm && i < p_optm->i_list; i++ )
1568     {
1569         if( !DeinterlaceGetMode( p_optm->ppsz_list[i] ) )
1570             continue;
1571
1572         val.psz_string  = p_optm->ppsz_list[i];
1573         text.psz_string = (char*)vlc_gettext(p_optm->ppsz_list_text[i]);
1574         var_Change( p_vout, "deinterlace-mode", VLC_VAR_ADDCHOICE, &val, &text );
1575     }
1576     var_AddCallback( p_vout, "deinterlace-mode", DeinterlaceCallback, NULL );
1577     /* */
1578     var_Create( p_vout, "deinterlace-needed", VLC_VAR_BOOL );
1579     var_AddCallback( p_vout, "deinterlace-needed", DeinterlaceCallback, NULL );
1580
1581     /* Override the initial value from filters if present */
1582     char *psz_filter_mode = NULL;
1583     if( DeinterlaceIsPresent( p_vout, true ) )
1584         psz_filter_mode = var_CreateGetNonEmptyString( p_vout, "filter-deinterlace-mode" );
1585     else if( DeinterlaceIsPresent( p_vout, false ) )
1586         psz_filter_mode = var_CreateGetNonEmptyString( p_vout, "sout-deinterlace-mode" );
1587     if( psz_filter_mode )
1588     {
1589         free( psz_deinterlace );
1590         if( i_deinterlace >= -1 )
1591             i_deinterlace = 1;
1592         psz_deinterlace = psz_filter_mode;
1593     }
1594
1595     /* */
1596     if( i_deinterlace == -2 )
1597         p_vout->p->displayed.is_interlaced = true;
1598     else if( i_deinterlace == -3 )
1599         p_vout->p->displayed.is_interlaced = false;
1600     if( i_deinterlace < 0 )
1601         i_deinterlace = -1;
1602
1603     /* */
1604     val.psz_string = psz_deinterlace ? psz_deinterlace : p_optm->orig.psz;
1605     var_Change( p_vout, "deinterlace-mode", VLC_VAR_SETVALUE, &val, NULL );
1606     val.b_bool = p_vout->p->displayed.is_interlaced;
1607     var_Change( p_vout, "deinterlace-needed", VLC_VAR_SETVALUE, &val, NULL );
1608
1609     var_SetInteger( p_vout, "deinterlace", i_deinterlace );
1610     free( psz_deinterlace );
1611 }
1612
1613 static void DeinterlaceNeeded( vout_thread_t *p_vout, bool is_interlaced )
1614 {
1615     msg_Dbg( p_vout, "Detected %s video",
1616              is_interlaced ? "interlaced" : "progressive" );
1617     var_SetBool( p_vout, "deinterlace-needed", is_interlaced );
1618 }
1619