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