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