]> git.sesse.net Git - vlc/blob - src/video_output/video_output.c
At input EOF, wait for all pictures to be displayed.
[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 #include "interlacing.h"
53 #include "postprocessing.h"
54 #include "display.h"
55
56 /*****************************************************************************
57  * Local prototypes
58  *****************************************************************************/
59 static void     *Thread(void *);
60 static void     vout_Destructor(vlc_object_t *);
61
62 /* Object variables callbacks */
63 static int FilterCallback( vlc_object_t *, char const *,
64                            vlc_value_t, vlc_value_t, void * );
65 static int VideoFilter2Callback( vlc_object_t *, char const *,
66                                  vlc_value_t, vlc_value_t, void * );
67
68 /* */
69 static void PrintVideoFormat(vout_thread_t *, const char *, const video_format_t *);
70
71 /* Maximum delay between 2 displayed pictures.
72  * XXX it is needed for now but should be removed in the long term.
73  */
74 #define VOUT_REDISPLAY_DELAY (INT64_C(80000))
75
76 /**
77  * Late pictures having a delay higher than this value are thrashed.
78  */
79 #define VOUT_DISPLAY_LATE_THRESHOLD (INT64_C(20000))
80
81 /* Better be in advance when awakening than late... */
82 #define VOUT_MWAIT_TOLERANCE (INT64_C(1000))
83
84 /*****************************************************************************
85  * Video Filter2 functions
86  *****************************************************************************/
87 static picture_t *video_new_buffer_filter( filter_t *p_filter )
88 {
89     vout_thread_t *p_vout = (vout_thread_t*)p_filter->p_owner;
90     return picture_pool_Get(p_vout->p->private_pool);
91 }
92
93 static void video_del_buffer_filter( filter_t *p_filter, picture_t *p_pic )
94 {
95     VLC_UNUSED(p_filter);
96     picture_Release(p_pic);
97 }
98
99 static int video_filter_buffer_allocation_init( filter_t *p_filter, void *p_data )
100 {
101     p_filter->pf_video_buffer_new = video_new_buffer_filter;
102     p_filter->pf_video_buffer_del = video_del_buffer_filter;
103     p_filter->p_owner = p_data; /* p_vout */
104     return VLC_SUCCESS;
105 }
106
107 #undef vout_Request
108 /*****************************************************************************
109  * vout_Request: find a video output thread, create one, or destroy one.
110  *****************************************************************************
111  * This function looks for a video output thread matching the current
112  * properties. If not found, it spawns a new one.
113  *****************************************************************************/
114 vout_thread_t *vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout,
115                              video_format_t *p_fmt )
116 {
117     if( !p_fmt )
118     {
119         /* Video output is no longer used.
120          * TODO: support for reusing video outputs with proper _thread-safe_
121          * reference handling. */
122         if( p_vout )
123             vout_CloseAndRelease( p_vout );
124         return NULL;
125     }
126
127     /* If a video output was provided, lock it, otherwise look for one. */
128     if( p_vout )
129     {
130         vlc_object_hold( p_vout );
131     }
132
133     /* TODO: find a suitable unused video output */
134
135     /* If we now have a video output, check it has the right properties */
136     if( p_vout )
137     {
138         vlc_mutex_lock( &p_vout->p->change_lock );
139
140         /* We don't directly check for the "vout-filter" variable for obvious
141          * performance reasons. */
142         if( p_vout->p->b_filter_change )
143         {
144             char *psz_filter_chain = var_GetString( p_vout, "vout-filter" );
145
146             if( psz_filter_chain && !*psz_filter_chain )
147             {
148                 free( psz_filter_chain );
149                 psz_filter_chain = NULL;
150             }
151             if( p_vout->p->psz_filter_chain && !*p_vout->p->psz_filter_chain )
152             {
153                 free( p_vout->p->psz_filter_chain );
154                 p_vout->p->psz_filter_chain = NULL;
155             }
156
157             if( !psz_filter_chain && !p_vout->p->psz_filter_chain )
158             {
159                 p_vout->p->b_filter_change = false;
160             }
161
162             free( psz_filter_chain );
163         }
164
165 #warning "FIXME: Check RGB masks in vout_Request"
166         /* FIXME: check RGB masks */
167         if( p_vout->fmt_render.i_chroma != vlc_fourcc_GetCodec( VIDEO_ES, p_fmt->i_chroma ) ||
168             p_vout->fmt_render.i_width != p_fmt->i_width ||
169             p_vout->fmt_render.i_height != p_fmt->i_height ||
170             p_vout->p->b_filter_change )
171         {
172             vlc_mutex_unlock( &p_vout->p->change_lock );
173
174             /* We are not interested in this format, close this vout */
175             vout_CloseAndRelease( p_vout );
176             vlc_object_release( p_vout );
177             p_vout = NULL;
178         }
179         else
180         {
181             /* This video output is cool! Hijack it. */
182             /* Correct aspect ratio on change
183              * FIXME factorize this code with other aspect ration related code */
184             unsigned int i_sar_num;
185             unsigned int i_sar_den;
186             vlc_ureduce( &i_sar_num, &i_sar_den,
187                          p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 );
188 #if 0
189             /* What's that, it does not seems to be used correcly everywhere */
190             if( p_vout->i_par_num > 0 && p_vout->i_par_den > 0 )
191             {
192                 i_sar_num *= p_vout->i_par_den;
193                 i_sar_den *= p_vout->i_par_num;
194             }
195 #endif
196
197             if( i_sar_num > 0 && i_sar_den > 0 &&
198                 ( i_sar_num != p_vout->fmt_render.i_sar_num ||
199                   i_sar_den != p_vout->fmt_render.i_sar_den ) )
200             {
201                 p_vout->fmt_in.i_sar_num = i_sar_num;
202                 p_vout->fmt_in.i_sar_den = i_sar_den;
203
204                 p_vout->fmt_render.i_sar_num = i_sar_num;
205                 p_vout->fmt_render.i_sar_den = i_sar_den;
206                 p_vout->p->i_changes |= VOUT_ASPECT_CHANGE;
207             }
208             vlc_mutex_unlock( &p_vout->p->change_lock );
209
210             vlc_object_release( p_vout );
211         }
212
213         if( p_vout )
214         {
215             msg_Dbg( p_this, "reusing provided vout" );
216
217             spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), false );
218             vlc_object_detach( p_vout );
219
220             vlc_object_attach( p_vout, p_this );
221             spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), true );
222         }
223     }
224
225     if( !p_vout )
226     {
227         msg_Dbg( p_this, "no usable vout present, spawning one" );
228
229         p_vout = vout_Create( p_this, p_fmt );
230     }
231
232     return p_vout;
233 }
234
235 /*****************************************************************************
236  * vout_Create: creates a new video output thread
237  *****************************************************************************
238  * This function creates a new video output thread, and returns a pointer
239  * to its description. On error, it returns NULL.
240  *****************************************************************************/
241 vout_thread_t * (vout_Create)( vlc_object_t *p_parent, video_format_t *p_fmt )
242 {
243     vout_thread_t  *p_vout;                            /* thread descriptor */
244     vlc_value_t     text;
245
246
247     config_chain_t *p_cfg;
248     char *psz_parser;
249     char *psz_name;
250
251     if( p_fmt->i_width <= 0 || p_fmt->i_height <= 0 )
252         return NULL;
253     const vlc_fourcc_t i_chroma = vlc_fourcc_GetCodec( VIDEO_ES, p_fmt->i_chroma );
254
255     vlc_ureduce( &p_fmt->i_sar_num, &p_fmt->i_sar_den,
256                  p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 );
257     if( p_fmt->i_sar_num <= 0 || p_fmt->i_sar_den <= 0 )
258         return NULL;
259
260     /* Allocate descriptor */
261     static const char typename[] = "video output";
262     p_vout = vlc_custom_create( p_parent, sizeof( *p_vout ), VLC_OBJECT_VOUT,
263                                 typename );
264     if( p_vout == NULL )
265         return NULL;
266
267     /* */
268     p_vout->p = calloc( 1, sizeof(*p_vout->p) );
269     if( !p_vout->p )
270     {
271         vlc_object_release( p_vout );
272         return NULL;
273     }
274
275     /* */
276     p_vout->fmt_render        = *p_fmt;   /* FIXME palette */
277     p_vout->fmt_in            = *p_fmt;   /* FIXME palette */
278
279     p_vout->fmt_render.i_chroma = 
280     p_vout->fmt_in.i_chroma     = i_chroma;
281     video_format_FixRgb( &p_vout->fmt_render );
282     video_format_FixRgb( &p_vout->fmt_in );
283
284     /* Initialize misc stuff */
285     vout_control_Init( &p_vout->p->control );
286     p_vout->p->i_changes    = 0;
287     vout_chrono_Init( &p_vout->p->render, 5, 10000 ); /* Arbitrary initial time */
288     vout_statistic_Init( &p_vout->p->statistic );
289     p_vout->p->b_filter_change = 0;
290     p_vout->p->i_par_num =
291     p_vout->p->i_par_den = 1;
292     p_vout->p->displayed.date = VLC_TS_INVALID;
293     p_vout->p->displayed.decoded = NULL;
294     p_vout->p->displayed.timestamp = VLC_TS_INVALID;
295     p_vout->p->displayed.qtype = QTYPE_NONE;
296     p_vout->p->displayed.is_interlaced = false;
297
298     p_vout->p->step.last         = VLC_TS_INVALID;
299     p_vout->p->step.timestamp    = VLC_TS_INVALID;
300
301     p_vout->p->pause.is_on  = false;
302     p_vout->p->pause.date   = VLC_TS_INVALID;
303
304     p_vout->p->decoder_fifo = picture_fifo_New();
305     p_vout->p->decoder_pool = NULL;
306
307     vlc_mouse_Init( &p_vout->p->mouse );
308
309     vout_snapshot_Init( &p_vout->p->snapshot );
310
311     p_vout->p->p_vf2_chain = filter_chain_New( p_vout, "video filter2",
312         false, video_filter_buffer_allocation_init, NULL, p_vout );
313
314     /* Initialize locks */
315     vlc_mutex_init( &p_vout->p->picture_lock );
316     vlc_mutex_init( &p_vout->p->change_lock );
317     vlc_mutex_init( &p_vout->p->vfilter_lock );
318
319     /* Mouse coordinates */
320     var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER );
321     var_Create( p_vout, "mouse-moved", VLC_VAR_COORDS );
322     var_Create( p_vout, "mouse-clicked", VLC_VAR_COORDS );
323     /* Mouse object (area of interest in a video filter) */
324     var_Create( p_vout, "mouse-object", VLC_VAR_BOOL );
325
326     /* Attach the new object now so we can use var inheritance below */
327     vlc_object_attach( p_vout, p_parent );
328
329     /* Initialize subpicture unit */
330     p_vout->p->p_spu = spu_Create( p_vout );
331
332     /* */
333     spu_Init( p_vout->p->p_spu );
334
335     spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), true );
336
337     p_vout->p->is_late_dropped = var_InheritBool( p_vout, "drop-late-frames" );
338
339     /* Take care of some "interface/control" related initialisations */
340     vout_IntfInit( p_vout );
341
342     /* Look for the default filter configuration */
343     p_vout->p->psz_filter_chain =
344         var_CreateGetStringCommand( p_vout, "vout-filter" );
345
346     /* Apply video filter2 objects on the first vout */
347     var_Create( p_vout, "video-filter",
348                 VLC_VAR_STRING | VLC_VAR_DOINHERIT | VLC_VAR_ISCOMMAND );
349     var_AddCallback( p_vout, "video-filter", VideoFilter2Callback, NULL );
350     var_TriggerCallback( p_vout, "video-filter" );
351
352     /* Choose the video output module */
353     if( !p_vout->p->psz_filter_chain || !*p_vout->p->psz_filter_chain )
354     {
355         psz_parser = NULL;
356     }
357     else
358     {
359         psz_parser = strdup( p_vout->p->psz_filter_chain );
360         p_vout->p->title.show = false;
361     }
362
363     /* Create the vout thread */
364     char *psz_tmp = config_ChainCreate( &psz_name, &p_cfg, psz_parser );
365     free( psz_parser );
366     free( psz_tmp );
367     p_vout->p->p_cfg = p_cfg;
368
369     /* Create a few object variables for interface interaction */
370     var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
371     text.psz_string = _("Filters");
372     var_Change( p_vout, "vout-filter", VLC_VAR_SETTEXT, &text, NULL );
373     var_AddCallback( p_vout, "vout-filter", FilterCallback, NULL );
374
375     /* */
376     vout_InitInterlacingSupport( p_vout, p_vout->p->displayed.is_interlaced );
377
378     if( p_vout->p->psz_filter_chain && *p_vout->p->psz_filter_chain )
379     {
380         char *psz_tmp;
381         if( asprintf( &psz_tmp, "%s,none", psz_name ) < 0 )
382             psz_tmp = strdup( "" );
383         free( psz_name );
384         psz_name = psz_tmp;
385     }
386     p_vout->p->psz_module_name = psz_name;
387
388     /* */
389     vlc_object_set_destructor( p_vout, vout_Destructor );
390
391     /* */
392     vlc_cond_init( &p_vout->p->change_wait );
393     if( vlc_clone( &p_vout->p->thread, Thread, p_vout,
394                    VLC_THREAD_PRIORITY_OUTPUT ) )
395     {
396         spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), false );
397         spu_Destroy( p_vout->p->p_spu );
398         p_vout->p->p_spu = NULL;
399         vlc_object_release( p_vout );
400         return NULL;
401     }
402
403     vlc_mutex_lock( &p_vout->p->change_lock );
404     while( !p_vout->p->b_ready )
405     {   /* We are (ab)using the same condition in opposite directions for
406          * b_ready and b_done. This works because of the strict ordering. */
407         assert( !p_vout->p->b_done );
408         vlc_cond_wait( &p_vout->p->change_wait, &p_vout->p->change_lock );
409     }
410     vlc_mutex_unlock( &p_vout->p->change_lock );
411
412     if( p_vout->p->b_error )
413     {
414         msg_Err( p_vout, "video output creation failed" );
415         vout_CloseAndRelease( p_vout );
416         return NULL;
417     }
418
419     return p_vout;
420 }
421
422 /*****************************************************************************
423  * vout_Close: Close a vout created by vout_Create.
424  *****************************************************************************
425  * You HAVE to call it on vout created by vout_Create before vlc_object_release.
426  * You should NEVER call it on vout not obtained through vout_Create
427  * (like with vout_Request or vlc_object_find.)
428  * You can use vout_CloseAndRelease() as a convenience method.
429  *****************************************************************************/
430 void vout_Close( vout_thread_t *p_vout )
431 {
432     assert( p_vout );
433
434     vlc_mutex_lock( &p_vout->p->change_lock );
435     p_vout->p->b_done = true;
436     vlc_cond_signal( &p_vout->p->change_wait );
437     vlc_mutex_unlock( &p_vout->p->change_lock );
438
439     vout_snapshot_End( &p_vout->p->snapshot );
440
441     vlc_join( p_vout->p->thread, NULL );
442 }
443
444 /* */
445 static void vout_Destructor( vlc_object_t * p_this )
446 {
447     vout_thread_t *p_vout = (vout_thread_t *)p_this;
448
449     /* Make sure the vout was stopped first */
450     //assert( !p_vout->p_module );
451
452     free( p_vout->p->psz_module_name );
453
454     /* */
455     if( p_vout->p->p_spu )
456         spu_Destroy( p_vout->p->p_spu );
457
458     vout_chrono_Clean( &p_vout->p->render );
459
460     if( p_vout->p->decoder_fifo )
461         picture_fifo_Delete( p_vout->p->decoder_fifo );
462     assert( !p_vout->p->decoder_pool );
463
464     /* Destroy the locks */
465     vlc_cond_destroy( &p_vout->p->change_wait );
466     vlc_mutex_destroy( &p_vout->p->picture_lock );
467     vlc_mutex_destroy( &p_vout->p->change_lock );
468     vlc_mutex_destroy( &p_vout->p->vfilter_lock );
469     vout_control_Clean( &p_vout->p->control );
470
471     /* */
472     vout_statistic_Clean( &p_vout->p->statistic );
473
474     /* */
475     vout_snapshot_Clean( &p_vout->p->snapshot );
476
477     /* */
478     free( p_vout->p->psz_filter_chain );
479
480     config_ChainDestroy( p_vout->p->p_cfg );
481
482     free( p_vout->p );
483
484 }
485
486 /* */
487 void vout_ChangePause(vout_thread_t *vout, bool is_paused, mtime_t date)
488 {
489     vout_control_cmd_t cmd;
490     vout_control_cmd_Init(&cmd, VOUT_CONTROL_PAUSE);
491     cmd.u.pause.is_on = is_paused;
492     cmd.u.pause.date  = date;
493     vout_control_Push(&vout->p->control, &cmd);
494
495     vout_control_WaitEmpty(&vout->p->control);
496 }
497
498 void vout_GetResetStatistic( vout_thread_t *p_vout, int *pi_displayed, int *pi_lost )
499 {
500     vout_statistic_GetReset( &p_vout->p->statistic,
501                              pi_displayed, pi_lost );
502 }
503
504 void vout_Flush(vout_thread_t *vout, mtime_t date)
505 {
506     vout_control_PushTime(&vout->p->control, VOUT_CONTROL_FLUSH, date);
507     vout_control_WaitEmpty(&vout->p->control);
508 }
509
510 void vout_Reset(vout_thread_t *vout)
511 {
512     vout_control_PushVoid(&vout->p->control, VOUT_CONTROL_RESET);
513     vout_control_WaitEmpty(&vout->p->control);
514 }
515
516 bool vout_IsEmpty(vout_thread_t *vout)
517 {
518     vlc_mutex_lock(&vout->p->picture_lock);
519
520     picture_t *picture = picture_fifo_Peek(vout->p->decoder_fifo);
521     if (picture)
522         picture_Release(picture);
523
524     vlc_mutex_unlock(&vout->p->picture_lock);
525
526     return !picture;
527 }
528
529 void vout_FixLeaks( vout_thread_t *vout )
530 {
531     vlc_mutex_lock(&vout->p->picture_lock);
532
533     picture_t *picture = picture_fifo_Peek(vout->p->decoder_fifo);
534     if (!picture) {
535         picture = picture_pool_Get(vout->p->decoder_pool);
536     }
537
538     if (picture) {
539         picture_Release(picture);
540         /* Not all pictures has been displayed yet or some are
541          * free */
542         vlc_mutex_unlock(&vout->p->picture_lock);
543         return;
544     }
545
546     /* There is no reason that no pictures are available, force one
547      * from the pool, becarefull with it though */
548     msg_Err(vout, "pictures leaked, trying to workaround");
549
550     /* */
551     picture_pool_NonEmpty(vout->p->decoder_pool, false);
552
553     vlc_mutex_unlock(&vout->p->picture_lock);
554 }
555 void vout_NextPicture(vout_thread_t *vout, mtime_t *duration)
556 {
557     vout_control_cmd_t cmd;
558     vout_control_cmd_Init(&cmd, VOUT_CONTROL_STEP);
559     cmd.u.time_ptr = duration;
560
561     vout_control_Push(&vout->p->control, &cmd);
562     vout_control_WaitEmpty(&vout->p->control);
563 }
564
565 void vout_DisplayTitle(vout_thread_t *vout, const char *title)
566 {
567     assert(title);
568     vout_control_PushString(&vout->p->control, VOUT_CONTROL_OSD_TITLE, title);
569 }
570
571 spu_t *vout_GetSpu( vout_thread_t *p_vout )
572 {
573     return p_vout->p->p_spu;
574 }
575
576 /* vout_Control* are usable by anyone at anytime */
577 void vout_ControlChangeFullscreen(vout_thread_t *vout, bool fullscreen)
578 {
579     vout_control_PushBool(&vout->p->control, VOUT_CONTROL_FULLSCREEN,
580                           fullscreen);
581 }
582 void vout_ControlChangeOnTop(vout_thread_t *vout, bool is_on_top)
583 {
584     vout_control_PushBool(&vout->p->control, VOUT_CONTROL_ON_TOP,
585                           is_on_top);
586 }
587 void vout_ControlChangeDisplayFilled(vout_thread_t *vout, bool is_filled)
588 {
589     vout_control_PushBool(&vout->p->control, VOUT_CONTROL_DISPLAY_FILLED,
590                           is_filled);
591 }
592 void vout_ControlChangeZoom(vout_thread_t *vout, int num, int den)
593 {
594     vout_control_PushPair(&vout->p->control, VOUT_CONTROL_ZOOM,
595                           num, den);
596 }
597
598 /*****************************************************************************
599  * InitThread: initialize video output thread
600  *****************************************************************************
601  * This function is called from Thread and performs the second step of the
602  * initialization. It returns 0 on success. Note that the thread's flag are not
603  * modified inside this function.
604  * XXX You have to enter it with change_lock taken.
605  *****************************************************************************/
606 static int ThreadInit(vout_thread_t *vout)
607 {
608     /* Initialize output method, it allocates direct buffers for us */
609     if (vout_InitWrapper(vout))
610         return VLC_EGENERIC;
611     assert(vout->p->decoder_pool);
612
613     vout->p->displayed.decoded = NULL;
614
615     /* print some usefull debug info about different vout formats
616      */
617     PrintVideoFormat(vout, "pic render", &vout->fmt_render);
618     PrintVideoFormat(vout, "pic in",     &vout->fmt_in);
619     PrintVideoFormat(vout, "pic out",    &vout->fmt_out);
620
621     assert(vout->fmt_out.i_width  == vout->fmt_render.i_width &&
622            vout->fmt_out.i_height == vout->fmt_render.i_height &&
623            vout->fmt_out.i_chroma == vout->fmt_render.i_chroma);
624     return VLC_SUCCESS;
625 }
626
627 /*****************************************************************************
628  * CleanThread: clean up after InitThread
629  *****************************************************************************
630  * This function is called after a sucessful
631  * initialization. It frees all resources allocated by InitThread.
632  * XXX You have to enter it with change_lock taken.
633  *****************************************************************************/
634 static void ThreadClean(vout_thread_t *vout)
635 {
636     /* Destroy translation tables */
637     if (!vout->p->b_error) {
638         picture_fifo_Flush(vout->p->decoder_fifo, INT64_MAX, false);
639         vout_EndWrapper(vout);
640     }
641 }
642
643 static int ThreadDisplayPicture(vout_thread_t *vout,
644                                 bool now, mtime_t *deadline)
645 {
646     int displayed_count = 0;
647     int lost_count = 0;
648
649     for (;;) {
650         const mtime_t date = mdate();
651         const bool is_paused = vout->p->pause.is_on;
652         bool redisplay = is_paused && !now && vout->p->displayed.decoded;
653         bool is_forced;
654
655         /* FIXME/XXX we must redisplay the last decoded picture (because
656          * of potential vout updated, or filters update or SPU update)
657          * For now a high update period is needed but it coulmd be removed
658          * if and only if:
659          * - vout module emits events from theselves.
660          * - *and* SPU is modified to emit an event or a deadline when needed.
661          *
662          * So it will be done latter.
663          */
664         if (!redisplay) {
665             picture_t *peek = picture_fifo_Peek(vout->p->decoder_fifo);
666             if (peek) {
667                 is_forced = peek->b_force || is_paused || now;
668                 *deadline = (is_forced ? date : peek->date) - vout_chrono_GetHigh(&vout->p->render);
669                 picture_Release(peek);
670             } else {
671                 redisplay = true;
672             }
673         }
674         if (redisplay) {
675              /* FIXME a better way for this delay is needed */
676             const mtime_t date_update = vout->p->displayed.date + VOUT_REDISPLAY_DELAY;
677             if (date_update > date || !vout->p->displayed.decoded) {
678                 *deadline = vout->p->displayed.decoded ? date_update : VLC_TS_INVALID;
679                 break;
680             }
681             /* */
682             is_forced = true;
683             *deadline = date - vout_chrono_GetHigh(&vout->p->render);
684         }
685         if (*deadline > VOUT_MWAIT_TOLERANCE)
686             *deadline -= VOUT_MWAIT_TOLERANCE;
687
688         /* If we are too early and can wait, do it */
689         if (date < *deadline && !now)
690             break;
691
692         picture_t *decoded;
693         if (redisplay) {
694             decoded = vout->p->displayed.decoded;
695             vout->p->displayed.decoded = NULL;
696         } else {
697             decoded = picture_fifo_Pop(vout->p->decoder_fifo);
698             assert(decoded);
699             if (!is_forced && !vout->p->is_late_dropped) {
700                 const mtime_t predicted = date + vout_chrono_GetLow(&vout->p->render);
701                 const mtime_t late = predicted - decoded->date;
702                 if (late > 0) {
703                     msg_Dbg(vout, "picture might be displayed late (missing %d ms)", (int)(late/1000));
704                     if (late > VOUT_DISPLAY_LATE_THRESHOLD) {
705                         msg_Warn(vout, "rejected picture because of render time");
706                         /* TODO */
707                         picture_Release(decoded);
708                         lost_count++;
709                         break;
710                     }
711                 }
712             }
713
714             vout->p->displayed.is_interlaced = !decoded->b_progressive;
715             vout->p->displayed.qtype         = decoded->i_qtype;
716         }
717         vout->p->displayed.timestamp = decoded->date;
718
719         /* */
720         if (vout->p->displayed.decoded)
721             picture_Release(vout->p->displayed.decoded);
722         picture_Hold(decoded);
723         vout->p->displayed.decoded = decoded;
724
725         /* */
726         vout_chrono_Start(&vout->p->render);
727
728         picture_t *filtered = NULL;
729         if (decoded) {
730             vlc_mutex_lock(&vout->p->vfilter_lock);
731             filtered = filter_chain_VideoFilter(vout->p->p_vf2_chain, decoded);
732             //assert(filtered == decoded); // TODO implement
733             vlc_mutex_unlock(&vout->p->vfilter_lock);
734             if (!filtered)
735                 continue;
736         }
737
738         /*
739          * Check for subpictures to display
740          */
741         const bool do_snapshot = vout_snapshot_IsRequested(&vout->p->snapshot);
742         mtime_t spu_render_time = is_forced ? mdate() : filtered->date;
743         if (vout->p->pause.is_on)
744             spu_render_time = vout->p->pause.date;
745         else
746             spu_render_time = filtered->date > 1 ? filtered->date : mdate();
747
748         subpicture_t *subpic = spu_SortSubpictures(vout->p->p_spu,
749                                                    spu_render_time,
750                                                    do_snapshot);
751         /*
752          * Perform rendering
753          *
754          * We have to:
755          * - be sure to end up with a direct buffer.
756          * - blend subtitles, and in a fast access buffer
757          */
758         picture_t *direct = NULL;
759         if (filtered &&
760             (vout->p->decoder_pool != vout->p->display_pool || subpic)) {
761             picture_t *render;
762             if (vout->p->is_decoder_pool_slow)
763                 render = picture_NewFromFormat(&vout->fmt_out);
764             else if (vout->p->decoder_pool != vout->p->display_pool)
765                 render = picture_pool_Get(vout->p->display_pool);
766             else
767                 render = picture_pool_Get(vout->p->private_pool);
768
769             if (render) {
770                 picture_Copy(render, filtered);
771
772                 spu_RenderSubpictures(vout->p->p_spu,
773                                       render, &vout->fmt_out,
774                                       subpic, &vout->fmt_in, spu_render_time);
775             }
776             if (vout->p->is_decoder_pool_slow) {
777                 direct = picture_pool_Get(vout->p->display_pool);
778                 if (direct)
779                     picture_Copy(direct, render);
780                 picture_Release(render);
781
782             } else {
783                 direct = render;
784             }
785             picture_Release(filtered);
786             filtered = NULL;
787         } else {
788             direct = filtered;
789         }
790
791         /*
792          * Take a snapshot if requested
793          */
794         if (direct && do_snapshot)
795             vout_snapshot_Set(&vout->p->snapshot, &vout->fmt_out, direct);
796
797         /* Render the direct buffer returned by vout_RenderPicture */
798         if (direct) {
799             vout_RenderWrapper(vout, direct);
800
801             vout_chrono_Stop(&vout->p->render);
802 #if 0
803             {
804             static int i = 0;
805             if (((i++)%10) == 0)
806                 msg_Info(vout, "render: avg %d ms var %d ms",
807                          (int)(vout->p->render.avg/1000), (int)(vout->p->render.var/1000));
808             }
809 #endif
810         }
811
812         /* Wait the real date (for rendering jitter) */
813         if (!is_forced)
814             mwait(decoded->date);
815
816         /* Display the direct buffer returned by vout_RenderPicture */
817         vout->p->displayed.date = mdate();
818         if (direct)
819             vout_DisplayWrapper(vout, direct);
820
821         displayed_count++;
822         break;
823     }
824
825     vout_statistic_Update(&vout->p->statistic, displayed_count, lost_count);
826     if (displayed_count <= 0)
827         return VLC_EGENERIC;
828     return VLC_SUCCESS;
829 }
830
831 static int ThreadManage(vout_thread_t *vout,
832                         mtime_t *deadline,
833                         vout_interlacing_support_t *interlacing,
834                         vout_postprocessing_support_t *postprocessing)
835 {
836     vlc_mutex_lock(&vout->p->picture_lock);
837
838     *deadline = VLC_TS_INVALID;
839     ThreadDisplayPicture(vout, false, deadline);
840
841     const int  picture_qtype      = vout->p->displayed.qtype;
842     const bool picture_interlaced = vout->p->displayed.is_interlaced;
843
844     vlc_mutex_unlock(&vout->p->picture_lock);
845
846     /* Post processing */
847     vout_SetPostProcessingState(vout, postprocessing, picture_qtype);
848
849     /* Deinterlacing */
850     vout_SetInterlacingState(vout, interlacing, picture_interlaced);
851
852     if (vout_ManageWrapper(vout)) {
853         /* A fatal error occurred, and the thread must terminate
854          * immediately, without displaying anything - setting b_error to 1
855          * causes the immediate end of the main while() loop. */
856         // FIXME pf_end
857         return VLC_EGENERIC;
858     }
859     return VLC_SUCCESS;
860 }
861
862 static void ThreadDisplayOsdTitle(vout_thread_t *vout, const char *string)
863 {
864     if( !var_InheritBool(vout, "osd"))
865         return;
866     if (!vout->p->title.show)
867         return;
868
869     vlc_assert_locked(&vout->p->change_lock);
870
871     const mtime_t start = mdate();
872     const mtime_t stop = start +
873                          INT64_C(1000) * vout->p->title.timeout;
874
875     if (stop > start)
876         vout_ShowTextAbsolute(vout, DEFAULT_CHAN,
877                               string, NULL,
878                               vout->p->title.position,
879                               30 + vout->fmt_in.i_width
880                                  - vout->fmt_in.i_visible_width
881                                  - vout->fmt_in.i_x_offset,
882                               20 + vout->fmt_in.i_y_offset,
883                               start, stop);
884 }
885
886 static void ThreadChangeFilters(vout_thread_t *vout, const char *filters)
887 {
888     es_format_t fmt;
889     es_format_Init(&fmt, VIDEO_ES, vout->fmt_render.i_chroma);
890     fmt.video = vout->fmt_render;
891
892     vlc_mutex_lock(&vout->p->vfilter_lock);
893
894     filter_chain_Reset(vout->p->p_vf2_chain, &fmt, &fmt);
895     if (filter_chain_AppendFromString(vout->p->p_vf2_chain,
896                                       filters) < 0)
897         msg_Err(vout, "Video filter chain creation failed");
898
899     vlc_mutex_unlock(&vout->p->vfilter_lock);
900 }
901
902 static void ThreadChangePause(vout_thread_t *vout, bool is_paused, mtime_t date)
903 {
904     vlc_assert_locked(&vout->p->change_lock);
905     assert(!vout->p->pause.is_on || !is_paused);
906
907     if (vout->p->pause.is_on) {
908         const mtime_t duration = date - vout->p->pause.date;
909
910         if (vout->p->step.timestamp > VLC_TS_INVALID)
911             vout->p->step.timestamp += duration;
912         if (vout->p->step.last > VLC_TS_INVALID)
913             vout->p->step.last += duration;
914         picture_fifo_OffsetDate(vout->p->decoder_fifo, duration);
915         if (vout->p->displayed.decoded)
916             vout->p->displayed.decoded->date += duration;
917
918         spu_OffsetSubtitleDate(vout->p->p_spu, duration);
919     } else {
920         vout->p->step.timestamp = VLC_TS_INVALID;
921         vout->p->step.last      = VLC_TS_INVALID;
922     }
923     vout->p->pause.is_on = is_paused;
924     vout->p->pause.date  = date;
925 }
926
927 static void ThreadFlush(vout_thread_t *vout, bool below, mtime_t date)
928 {
929     vout->p->step.timestamp = VLC_TS_INVALID;
930     vout->p->step.last      = VLC_TS_INVALID;
931
932     picture_t *last = vout->p->displayed.decoded;
933     if (last) {
934         if (( below && last->date <= date) ||
935             (!below && last->date >= date)) {
936             picture_Release(last);
937
938             vout->p->displayed.decoded   = NULL;
939             vout->p->displayed.date      = VLC_TS_INVALID;
940             vout->p->displayed.timestamp = VLC_TS_INVALID;
941         }
942     }
943     picture_fifo_Flush(vout->p->decoder_fifo, date, below);
944 }
945
946 static void ThreadReset(vout_thread_t *vout)
947 {
948     ThreadFlush(vout, true, INT64_MAX);
949     if (vout->p->decoder_pool)
950         picture_pool_NonEmpty(vout->p->decoder_pool, true);
951     vout->p->pause.is_on = false;
952     vout->p->pause.date  = mdate();
953 }
954
955 static void ThreadStep(vout_thread_t *vout, mtime_t *duration)
956 {
957     *duration = 0;
958
959     if (vout->p->step.last <= VLC_TS_INVALID)
960         vout->p->step.last = vout->p->displayed.timestamp;
961
962     mtime_t dummy;
963     if (ThreadDisplayPicture(vout, true, &dummy))
964         return;
965
966     vout->p->step.timestamp = vout->p->displayed.timestamp;
967
968     if (vout->p->step.last > VLC_TS_INVALID &&
969         vout->p->step.timestamp > vout->p->step.last) {
970         *duration = vout->p->step.timestamp - vout->p->step.last;
971         vout->p->step.last = vout->p->step.timestamp;
972         /* TODO advance subpicture by the duration ... */
973     }
974 }
975
976 static void ThreadChangeFullscreen(vout_thread_t *vout, bool fullscreen)
977 {
978     /* FIXME not sure setting "fullscreen" is good ... */
979     var_SetBool(vout, "fullscreen", fullscreen);
980     vout_SetDisplayFullscreen(vout->p->display.vd, fullscreen);
981 }
982
983 static void ThreadChangeOnTop(vout_thread_t *vout, bool is_on_top)
984 {
985     vout_SetWindowState(vout->p->display.vd,
986                         is_on_top ? VOUT_WINDOW_STATE_ABOVE :
987                                     VOUT_WINDOW_STATE_NORMAL);
988 }
989
990 static void ThreadChangeDisplayFilled(vout_thread_t *vout, bool is_filled)
991 {
992     vout_SetDisplayFilled(vout->p->display.vd, is_filled);
993 }
994
995 static void ThreadChangeZoom(vout_thread_t *vout, int num, int den)
996 {
997     if (num * 10 < den) {
998         num = den;
999         den *= 10;
1000     } else if (num > den * 10) {
1001         num = den * 10;
1002     }
1003
1004     vout_SetDisplayZoom(vout->p->display.vd, num, den);
1005 }
1006
1007 /*****************************************************************************
1008  * Thread: video output thread
1009  *****************************************************************************
1010  * Video output thread. This function does only returns when the thread is
1011  * terminated. It handles the pictures arriving in the video heap and the
1012  * display device events.
1013  *****************************************************************************/
1014 static void *Thread(void *object)
1015 {
1016     vout_thread_t *vout = object;
1017     bool          has_wrapper;
1018
1019     /*
1020      * Initialize thread
1021      */
1022     has_wrapper = !vout_OpenWrapper(vout, vout->p->psz_module_name);
1023
1024     vlc_mutex_lock(&vout->p->change_lock);
1025
1026     if (has_wrapper)
1027         vout->p->b_error = ThreadInit(vout);
1028     else
1029         vout->p->b_error = true;
1030
1031     /* signal the creation of the vout */
1032     vout->p->b_ready = true;
1033     vlc_cond_signal(&vout->p->change_wait);
1034
1035     if (vout->p->b_error)
1036         goto exit_thread;
1037
1038     /* */
1039     vout_interlacing_support_t interlacing = {
1040         .is_interlaced = false,
1041         .date = mdate(),
1042     };
1043     vout_postprocessing_support_t postprocessing = {
1044         .qtype = QTYPE_NONE,
1045     };
1046
1047     /*
1048      * Main loop - it is not executed if an error occurred during
1049      * initialization
1050      */
1051     mtime_t deadline = VLC_TS_INVALID;
1052     while (!vout->p->b_done && !vout->p->b_error) {
1053         vout_control_cmd_t cmd;
1054
1055         vlc_mutex_unlock(&vout->p->change_lock);
1056         /* FIXME remove thoses ugly timeouts
1057          */
1058         while (!vout_control_Pop(&vout->p->control, &cmd, deadline, 100000)) {
1059             /* TODO remove the lock when possible (ie when
1060              * vout->fmt_* are not protected by it anymore) */
1061             vlc_mutex_lock(&vout->p->change_lock);
1062             switch(cmd.type) {
1063             case VOUT_CONTROL_OSD_TITLE:
1064                 ThreadDisplayOsdTitle(vout, cmd.u.string);
1065                 break;
1066             case VOUT_CONTROL_CHANGE_FILTERS:
1067                 ThreadChangeFilters(vout, cmd.u.string);
1068                 break;
1069             case VOUT_CONTROL_PAUSE:
1070                 ThreadChangePause(vout, cmd.u.pause.is_on, cmd.u.pause.date);
1071                 break;
1072             case VOUT_CONTROL_FLUSH:
1073                 ThreadFlush(vout, false, cmd.u.time);
1074                 break;
1075             case VOUT_CONTROL_RESET:
1076                 ThreadReset(vout);
1077                 break;
1078             case VOUT_CONTROL_STEP:
1079                 ThreadStep(vout, cmd.u.time_ptr);
1080                 break;
1081             case VOUT_CONTROL_FULLSCREEN:
1082                 ThreadChangeFullscreen(vout, cmd.u.boolean);
1083                 break;
1084             case VOUT_CONTROL_ON_TOP:
1085                 ThreadChangeOnTop(vout, cmd.u.boolean);
1086                 break;
1087             case VOUT_CONTROL_DISPLAY_FILLED:
1088                 ThreadChangeDisplayFilled(vout, cmd.u.boolean);
1089                 break;
1090             case VOUT_CONTROL_ZOOM:
1091                 ThreadChangeZoom(vout, cmd.u.pair.a, cmd.u.pair.b);
1092                 break;
1093             default:
1094                 break;
1095             }
1096             vlc_mutex_unlock(&vout->p->change_lock);
1097             vout_control_cmd_Clean(&cmd);
1098         }
1099         vlc_mutex_lock(&vout->p->change_lock);
1100
1101         /* */
1102         if (ThreadManage(vout, &deadline,
1103                          &interlacing, &postprocessing)) {
1104             vout->p->b_error = true;
1105             break;
1106         }
1107     }
1108
1109     /*
1110      * Error loop - wait until the thread destruction is requested
1111      *
1112      * XXX I wonder if we should periodically clean the decoder_fifo
1113      * or have a way to prevent it filling up.
1114      */
1115     while (vout->p->b_error && !vout->p->b_done)
1116         vlc_cond_wait(&vout->p->change_wait, &vout->p->change_lock);
1117
1118     /* Clean thread */
1119     ThreadClean(vout);
1120
1121 exit_thread:
1122     /* Detach subpicture unit from both input and vout */
1123     spu_Attach(vout->p->p_spu, VLC_OBJECT(vout), false);
1124     vlc_object_detach(vout->p->p_spu);
1125
1126     vlc_mutex_unlock(&vout->p->change_lock);
1127
1128     if (has_wrapper)
1129         vout_CloseWrapper(vout);
1130     vout_control_Dead(&vout->p->control);
1131
1132     /* Destroy the video filters2 */
1133     filter_chain_Delete(vout->p->p_vf2_chain);
1134
1135     return NULL;
1136 }
1137
1138 /*****************************************************************************
1139  * object variables callbacks: a bunch of object variables are used by the
1140  * interfaces to interact with the vout.
1141  *****************************************************************************/
1142 static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd,
1143                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1144 {
1145     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1146     input_thread_t *p_input;
1147     (void)psz_cmd; (void)oldval; (void)p_data;
1148
1149     p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1150                                                  FIND_PARENT );
1151     if (!p_input)
1152     {
1153         msg_Err( p_vout, "Input not found" );
1154         return VLC_EGENERIC;
1155     }
1156
1157     /* Modify input as well because the vout might have to be restarted */
1158     var_Create( p_input, "vout-filter", VLC_VAR_STRING );
1159     var_SetString( p_input, "vout-filter", newval.psz_string );
1160
1161     /* Now restart current video stream */
1162     input_Control( p_input, INPUT_RESTART_ES, -VIDEO_ES );
1163     vlc_object_release( p_input );
1164
1165     return VLC_SUCCESS;
1166 }
1167
1168 /*****************************************************************************
1169  * Video Filter2 stuff
1170  *****************************************************************************/
1171 static int VideoFilter2Callback(vlc_object_t *object, char const *cmd,
1172                                 vlc_value_t oldval, vlc_value_t newval,
1173                                 void *data)
1174 {
1175     vout_thread_t *vout = (vout_thread_t *)object;
1176     (void)cmd; (void)oldval; (void)data;
1177
1178     vout_control_PushString(&vout->p->control, VOUT_CONTROL_CHANGE_FILTERS,
1179                             newval.psz_string);
1180     return VLC_SUCCESS;
1181 }
1182
1183 /* */
1184 static void PrintVideoFormat(vout_thread_t *vout,
1185                              const char *description,
1186                              const video_format_t *fmt)
1187 {
1188     msg_Dbg(vout, "%s sz %ix%i, of (%i,%i), vsz %ix%i, 4cc %4.4s, sar %i:%i, msk r0x%x g0x%x b0x%x",
1189             description,
1190             fmt->i_width, fmt->i_height, fmt->i_x_offset, fmt->i_y_offset,
1191             fmt->i_visible_width, fmt->i_visible_height,
1192             (char*)&fmt->i_chroma,
1193             fmt->i_sar_num, fmt->i_sar_den,
1194             fmt->i_rmask, fmt->i_gmask, fmt->i_bmask);
1195 }
1196