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