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