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