]> git.sesse.net Git - vlc/blob - src/video_output/video_output.c
Split out interlacing/post-processing management from video_output.c (vout).
[vlc] / src / video_output / video_output.c
1 /*****************************************************************************
2  * video_output.c : video output thread
3  *
4  * This module describes the programming interface for video output threads.
5  * It includes functions allowing to open a new thread, send pictures to a
6  * thread, and destroy a previously oppened video output thread.
7  *****************************************************************************
8  * Copyright (C) 2000-2007 the VideoLAN team
9  * $Id$
10  *
11  * Authors: Vincent Seguin <seguin@via.ecp.fr>
12  *          Gildas Bazin <gbazin@videolan.org>
13  *          Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
14  *
15  * This program is free software; you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation; either version 2 of the License, or
18  * (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, write to the Free Software
27  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
28  *****************************************************************************/
29
30 /*****************************************************************************
31  * Preamble
32  *****************************************************************************/
33 #ifdef HAVE_CONFIG_H
34 # include "config.h"
35 #endif
36
37 #include <vlc_common.h>
38
39 #include <stdlib.h>                                                /* free() */
40 #include <string.h>
41 #include <assert.h>
42
43 #include <vlc_vout.h>
44
45 #include <vlc_filter.h>
46 #include <vlc_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 /* Display media title in OSD */
68 static void DisplayTitleOnOSD( vout_thread_t *p_vout );
69
70 /* */
71 static void PrintVideoFormat(vout_thread_t *, const char *, const video_format_t *);
72
73 /* Maximum delay between 2 displayed pictures.
74  * XXX it is needed for now but should be removed in the long term.
75  */
76 #define VOUT_REDISPLAY_DELAY (INT64_C(80000))
77
78 /**
79  * Late pictures having a delay higher than this value are thrashed.
80  */
81 #define VOUT_DISPLAY_LATE_THRESHOLD (INT64_C(20000))
82
83 /* Better be in advance when awakening than late... */
84 #define VOUT_MWAIT_TOLERANCE (INT64_C(1000))
85
86 /*****************************************************************************
87  * Video Filter2 functions
88  *****************************************************************************/
89 static picture_t *video_new_buffer_filter( filter_t *p_filter )
90 {
91     vout_thread_t *p_vout = (vout_thread_t*)p_filter->p_owner;
92     return picture_pool_Get(p_vout->p->private_pool);
93 }
94
95 static void video_del_buffer_filter( filter_t *p_filter, picture_t *p_pic )
96 {
97     VLC_UNUSED(p_filter);
98     picture_Release(p_pic);
99 }
100
101 static int video_filter_buffer_allocation_init( filter_t *p_filter, void *p_data )
102 {
103     p_filter->pf_video_buffer_new = video_new_buffer_filter;
104     p_filter->pf_video_buffer_del = video_del_buffer_filter;
105     p_filter->p_owner = p_data; /* p_vout */
106     return VLC_SUCCESS;
107 }
108
109 #undef vout_Request
110 /*****************************************************************************
111  * vout_Request: find a video output thread, create one, or destroy one.
112  *****************************************************************************
113  * This function looks for a video output thread matching the current
114  * properties. If not found, it spawns a new one.
115  *****************************************************************************/
116 vout_thread_t *vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout,
117                              video_format_t *p_fmt )
118 {
119     if( !p_fmt )
120     {
121         /* Video output is no longer used.
122          * TODO: support for reusing video outputs with proper _thread-safe_
123          * reference handling. */
124         if( p_vout )
125             vout_CloseAndRelease( p_vout );
126         return NULL;
127     }
128
129     /* If a video output was provided, lock it, otherwise look for one. */
130     if( p_vout )
131     {
132         vlc_object_hold( p_vout );
133     }
134
135     /* TODO: find a suitable unused video output */
136
137     /* If we now have a video output, check it has the right properties */
138     if( p_vout )
139     {
140         vlc_mutex_lock( &p_vout->p->change_lock );
141
142         /* We don't directly check for the "vout-filter" variable for obvious
143          * performance reasons. */
144         if( p_vout->p->b_filter_change )
145         {
146             char *psz_filter_chain = var_GetString( p_vout, "vout-filter" );
147
148             if( psz_filter_chain && !*psz_filter_chain )
149             {
150                 free( psz_filter_chain );
151                 psz_filter_chain = NULL;
152             }
153             if( p_vout->p->psz_filter_chain && !*p_vout->p->psz_filter_chain )
154             {
155                 free( p_vout->p->psz_filter_chain );
156                 p_vout->p->psz_filter_chain = NULL;
157             }
158
159             if( !psz_filter_chain && !p_vout->p->psz_filter_chain )
160             {
161                 p_vout->p->b_filter_change = false;
162             }
163
164             free( psz_filter_chain );
165         }
166
167 #warning "FIXME: Check RGB masks in vout_Request"
168         /* FIXME: check RGB masks */
169         if( p_vout->fmt_render.i_chroma != vlc_fourcc_GetCodec( VIDEO_ES, p_fmt->i_chroma ) ||
170             p_vout->fmt_render.i_width != p_fmt->i_width ||
171             p_vout->fmt_render.i_height != p_fmt->i_height ||
172             p_vout->p->b_filter_change )
173         {
174             vlc_mutex_unlock( &p_vout->p->change_lock );
175
176             /* We are not interested in this format, close this vout */
177             vout_CloseAndRelease( p_vout );
178             vlc_object_release( p_vout );
179             p_vout = NULL;
180         }
181         else
182         {
183             /* This video output is cool! Hijack it. */
184             /* Correct aspect ratio on change
185              * FIXME factorize this code with other aspect ration related code */
186             unsigned int i_sar_num;
187             unsigned int i_sar_den;
188             vlc_ureduce( &i_sar_num, &i_sar_den,
189                          p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 );
190 #if 0
191             /* What's that, it does not seems to be used correcly everywhere */
192             if( p_vout->i_par_num > 0 && p_vout->i_par_den > 0 )
193             {
194                 i_sar_num *= p_vout->i_par_den;
195                 i_sar_den *= p_vout->i_par_num;
196             }
197 #endif
198
199             if( i_sar_num > 0 && i_sar_den > 0 &&
200                 ( i_sar_num != p_vout->fmt_render.i_sar_num ||
201                   i_sar_den != p_vout->fmt_render.i_sar_den ) )
202             {
203                 p_vout->fmt_in.i_sar_num = i_sar_num;
204                 p_vout->fmt_in.i_sar_den = i_sar_den;
205
206                 p_vout->fmt_render.i_sar_num = i_sar_num;
207                 p_vout->fmt_render.i_sar_den = i_sar_den;
208                 p_vout->p->i_changes |= VOUT_ASPECT_CHANGE;
209             }
210             vlc_mutex_unlock( &p_vout->p->change_lock );
211
212             vlc_object_release( p_vout );
213         }
214
215         if( p_vout )
216         {
217             msg_Dbg( p_this, "reusing provided vout" );
218
219             spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), false );
220             vlc_object_detach( p_vout );
221
222             vlc_object_attach( p_vout, p_this );
223             spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), true );
224         }
225     }
226
227     if( !p_vout )
228     {
229         msg_Dbg( p_this, "no usable vout present, spawning one" );
230
231         p_vout = vout_Create( p_this, p_fmt );
232     }
233
234     return p_vout;
235 }
236
237 /*****************************************************************************
238  * vout_Create: creates a new video output thread
239  *****************************************************************************
240  * This function creates a new video output thread, and returns a pointer
241  * to its description. On error, it returns NULL.
242  *****************************************************************************/
243 vout_thread_t * (vout_Create)( vlc_object_t *p_parent, video_format_t *p_fmt )
244 {
245     vout_thread_t  *p_vout;                            /* thread descriptor */
246     vlc_value_t     text;
247
248
249     config_chain_t *p_cfg;
250     char *psz_parser;
251     char *psz_name;
252
253     if( p_fmt->i_width <= 0 || p_fmt->i_height <= 0 )
254         return NULL;
255     const vlc_fourcc_t i_chroma = vlc_fourcc_GetCodec( VIDEO_ES, p_fmt->i_chroma );
256
257     vlc_ureduce( &p_fmt->i_sar_num, &p_fmt->i_sar_den,
258                  p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 );
259     if( p_fmt->i_sar_num <= 0 || p_fmt->i_sar_den <= 0 )
260         return NULL;
261
262     /* Allocate descriptor */
263     static const char typename[] = "video output";
264     p_vout = vlc_custom_create( p_parent, sizeof( *p_vout ), VLC_OBJECT_VOUT,
265                                 typename );
266     if( p_vout == NULL )
267         return NULL;
268
269     /* */
270     p_vout->p = calloc( 1, sizeof(*p_vout->p) );
271     if( !p_vout->p )
272     {
273         vlc_object_release( p_vout );
274         return NULL;
275     }
276
277     /* */
278     p_vout->fmt_render        = *p_fmt;   /* FIXME palette */
279     p_vout->fmt_in            = *p_fmt;   /* FIXME palette */
280
281     p_vout->fmt_render.i_chroma = 
282     p_vout->fmt_in.i_chroma     = i_chroma;
283     video_format_FixRgb( &p_vout->fmt_render );
284     video_format_FixRgb( &p_vout->fmt_in );
285
286     /* Initialize misc stuff */
287     p_vout->p->i_changes    = 0;
288     p_vout->p->b_fullscreen = 0;
289     vout_chrono_Init( &p_vout->p->render, 5, 10000 ); /* Arbitrary initial time */
290     vout_statistic_Init( &p_vout->p->statistic );
291     p_vout->p->b_filter_change = 0;
292     p_vout->p->i_par_num =
293     p_vout->p->i_par_den = 1;
294     p_vout->p->b_picture_empty = false;
295     p_vout->p->displayed.date = VLC_TS_INVALID;
296     p_vout->p->displayed.decoded = NULL;
297     p_vout->p->displayed.timestamp = VLC_TS_INVALID;
298     p_vout->p->displayed.qtype = QTYPE_NONE;
299     p_vout->p->displayed.is_interlaced = false;
300
301     p_vout->p->step.is_requested = false;
302     p_vout->p->step.last         = VLC_TS_INVALID;
303     p_vout->p->step.timestamp    = VLC_TS_INVALID;
304
305     p_vout->p->pause.is_on  = false;
306     p_vout->p->pause.date   = VLC_TS_INVALID;
307
308     p_vout->p->decoder_fifo = picture_fifo_New();
309     p_vout->p->decoder_pool = NULL;
310
311     vlc_mouse_Init( &p_vout->p->mouse );
312
313     vout_snapshot_Init( &p_vout->p->snapshot );
314
315     /* Initialize locks */
316     vlc_mutex_init( &p_vout->p->picture_lock );
317     vlc_cond_init( &p_vout->p->picture_wait );
318     vlc_mutex_init( &p_vout->p->change_lock );
319     vlc_mutex_init( &p_vout->p->vfilter_lock );
320
321     /* Mouse coordinates */
322     var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER );
323     var_Create( p_vout, "mouse-moved", VLC_VAR_COORDS );
324     var_Create( p_vout, "mouse-clicked", VLC_VAR_COORDS );
325     /* Mouse object (area of interest in a video filter) */
326     var_Create( p_vout, "mouse-object", VLC_VAR_BOOL );
327
328     /* Attach the new object now so we can use var inheritance below */
329     vlc_object_attach( p_vout, p_parent );
330
331     /* Initialize subpicture unit */
332     p_vout->p->p_spu = spu_Create( p_vout );
333
334     /* */
335     spu_Init( p_vout->p->p_spu );
336
337     spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), true );
338
339     p_vout->p->is_late_dropped = var_InheritBool( p_vout, "drop-late-frames" );
340
341     /* Take care of some "interface/control" related initialisations */
342     vout_IntfInit( p_vout );
343
344     /* Look for the default filter configuration */
345     p_vout->p->psz_filter_chain =
346         var_CreateGetStringCommand( p_vout, "vout-filter" );
347
348     /* Apply video filter2 objects on the first vout */
349     p_vout->p->psz_vf2 =
350         var_CreateGetStringCommand( p_vout, "video-filter" );
351
352     var_AddCallback( p_vout, "video-filter", VideoFilter2Callback, NULL );
353     p_vout->p->p_vf2_chain = filter_chain_New( p_vout, "video filter2",
354         false, video_filter_buffer_allocation_init, NULL, p_vout );
355
356     /* Choose the video output module */
357     if( !p_vout->p->psz_filter_chain || !*p_vout->p->psz_filter_chain )
358     {
359         psz_parser = NULL;
360     }
361     else
362     {
363         psz_parser = strdup( p_vout->p->psz_filter_chain );
364         p_vout->p->title.show = false;
365     }
366
367     /* Create the vout thread */
368     char *psz_tmp = config_ChainCreate( &psz_name, &p_cfg, psz_parser );
369     free( psz_parser );
370     free( psz_tmp );
371     p_vout->p->p_cfg = p_cfg;
372
373     /* Create a few object variables for interface interaction */
374     var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
375     text.psz_string = _("Filters");
376     var_Change( p_vout, "vout-filter", VLC_VAR_SETTEXT, &text, NULL );
377     var_AddCallback( p_vout, "vout-filter", FilterCallback, NULL );
378
379     /* */
380     vout_InitInterlacingSupport( p_vout, p_vout->p->displayed.is_interlaced );
381
382     if( p_vout->p->psz_filter_chain && *p_vout->p->psz_filter_chain )
383     {
384         char *psz_tmp;
385         if( asprintf( &psz_tmp, "%s,none", psz_name ) < 0 )
386             psz_tmp = strdup( "" );
387         free( psz_name );
388         psz_name = psz_tmp;
389     }
390     p_vout->p->psz_module_name = psz_name;
391
392     /* */
393     vlc_object_set_destructor( p_vout, vout_Destructor );
394
395     /* */
396     vlc_cond_init( &p_vout->p->change_wait );
397     if( vlc_clone( &p_vout->p->thread, Thread, p_vout,
398                    VLC_THREAD_PRIORITY_OUTPUT ) )
399     {
400         spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), false );
401         spu_Destroy( p_vout->p->p_spu );
402         p_vout->p->p_spu = NULL;
403         vlc_object_release( p_vout );
404         return NULL;
405     }
406
407     vlc_mutex_lock( &p_vout->p->change_lock );
408     while( !p_vout->p->b_ready )
409     {   /* We are (ab)using the same condition in opposite directions for
410          * b_ready and b_done. This works because of the strict ordering. */
411         assert( !p_vout->p->b_done );
412         vlc_cond_wait( &p_vout->p->change_wait, &p_vout->p->change_lock );
413     }
414     vlc_mutex_unlock( &p_vout->p->change_lock );
415
416     if( p_vout->p->b_error )
417     {
418         msg_Err( p_vout, "video output creation failed" );
419         vout_CloseAndRelease( p_vout );
420         return NULL;
421     }
422
423     return p_vout;
424 }
425
426 /*****************************************************************************
427  * vout_Close: Close a vout created by vout_Create.
428  *****************************************************************************
429  * You HAVE to call it on vout created by vout_Create before vlc_object_release.
430  * You should NEVER call it on vout not obtained through vout_Create
431  * (like with vout_Request or vlc_object_find.)
432  * You can use vout_CloseAndRelease() as a convenience method.
433  *****************************************************************************/
434 void vout_Close( vout_thread_t *p_vout )
435 {
436     assert( p_vout );
437
438     vlc_mutex_lock( &p_vout->p->change_lock );
439     p_vout->p->b_done = true;
440     vlc_cond_signal( &p_vout->p->change_wait );
441     vlc_mutex_unlock( &p_vout->p->change_lock );
442
443     vout_snapshot_End( &p_vout->p->snapshot );
444
445     vlc_join( p_vout->p->thread, NULL );
446 }
447
448 /* */
449 static void vout_Destructor( vlc_object_t * p_this )
450 {
451     vout_thread_t *p_vout = (vout_thread_t *)p_this;
452
453     /* Make sure the vout was stopped first */
454     //assert( !p_vout->p_module );
455
456     free( p_vout->p->psz_module_name );
457
458     /* */
459     if( p_vout->p->p_spu )
460         spu_Destroy( p_vout->p->p_spu );
461
462     vout_chrono_Clean( &p_vout->p->render );
463
464     if( p_vout->p->decoder_fifo )
465         picture_fifo_Delete( p_vout->p->decoder_fifo );
466     assert( !p_vout->p->decoder_pool );
467
468     /* Destroy the locks */
469     vlc_cond_destroy( &p_vout->p->change_wait );
470     vlc_cond_destroy( &p_vout->p->picture_wait );
471     vlc_mutex_destroy( &p_vout->p->picture_lock );
472     vlc_mutex_destroy( &p_vout->p->change_lock );
473     vlc_mutex_destroy( &p_vout->p->vfilter_lock );
474
475     /* */
476     vout_statistic_Clean( &p_vout->p->statistic );
477
478     /* */
479     vout_snapshot_Clean( &p_vout->p->snapshot );
480
481     /* */
482     free( p_vout->p->psz_filter_chain );
483     free( p_vout->p->title.value );
484
485     config_ChainDestroy( p_vout->p->p_cfg );
486
487     free( p_vout->p );
488
489 }
490
491 /* */
492 void vout_ChangePause( vout_thread_t *p_vout, bool b_paused, mtime_t i_date )
493 {
494     vlc_mutex_lock( &p_vout->p->change_lock );
495
496     assert( !p_vout->p->pause.is_on || !b_paused );
497
498     vlc_mutex_lock( &p_vout->p->picture_lock );
499
500     if( p_vout->p->pause.is_on )
501     {
502         const mtime_t i_duration = i_date - p_vout->p->pause.date;
503
504         if (p_vout->p->step.timestamp > VLC_TS_INVALID)
505             p_vout->p->step.timestamp += i_duration;
506         if (!b_paused)
507             p_vout->p->step.last = p_vout->p->step.timestamp;
508         picture_fifo_OffsetDate( p_vout->p->decoder_fifo, i_duration );
509         if (p_vout->p->displayed.decoded)
510             p_vout->p->displayed.decoded->date += i_duration;
511
512         vlc_cond_signal( &p_vout->p->picture_wait );
513         vlc_mutex_unlock( &p_vout->p->picture_lock );
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_cond_signal(&vout->p->picture_wait);
561     vlc_mutex_unlock(&vout->p->picture_lock);
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_cond_signal( &vout->p->picture_wait );
575     vlc_mutex_unlock(&vout->p->picture_lock);
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_cond_signal(&vout->p->picture_wait);
603     vlc_mutex_unlock(&vout->p->picture_lock);
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     /* FIXME I highly doubt that it can works with only one cond_t FIXME */
613     vlc_cond_signal(&vout->p->picture_wait);
614
615     while (vout->p->step.is_requested && !vout->p->b_picture_empty)
616         vlc_cond_wait(&vout->p->picture_wait, &vout->p->picture_lock);
617
618     if (vout->p->step.last > VLC_TS_INVALID &&
619         vout->p->step.timestamp > vout->p->step.last) {
620         *duration = vout->p->step.timestamp - vout->p->step.last;
621         vout->p->step.last = vout->p->step.timestamp;
622     } else {
623         *duration = 0;
624     }
625
626     /* TODO advance subpicture by the duration ... */
627     vlc_mutex_unlock(&vout->p->picture_lock);
628 }
629
630 void vout_DisplayTitle( vout_thread_t *p_vout, const char *psz_title )
631 {
632     assert( psz_title );
633
634     if( !var_InheritBool( p_vout, "osd" ) )
635         return;
636
637     vlc_mutex_lock( &p_vout->p->change_lock );
638     free( p_vout->p->title.value );
639     p_vout->p->title.value = strdup( psz_title );
640     vlc_mutex_unlock( &p_vout->p->change_lock );
641 }
642
643 spu_t *vout_GetSpu( vout_thread_t *p_vout )
644 {
645     return p_vout->p->p_spu;
646 }
647
648 /*****************************************************************************
649  * InitThread: initialize video output thread
650  *****************************************************************************
651  * This function is called from Thread and performs the second step of the
652  * initialization. It returns 0 on success. Note that the thread's flag are not
653  * modified inside this function.
654  * XXX You have to enter it with change_lock taken.
655  *****************************************************************************/
656 static int ThreadInit(vout_thread_t *vout)
657 {
658     /* Initialize output method, it allocates direct buffers for us */
659     if (vout_InitWrapper(vout))
660         return VLC_EGENERIC;
661     assert(vout->p->decoder_pool);
662
663     vout->p->displayed.decoded = NULL;
664
665     /* print some usefull debug info about different vout formats
666      */
667     PrintVideoFormat(vout, "pic render", &vout->fmt_render);
668     PrintVideoFormat(vout, "pic in",     &vout->fmt_in);
669     PrintVideoFormat(vout, "pic out",    &vout->fmt_out);
670
671     assert(vout->fmt_out.i_width  == vout->fmt_render.i_width &&
672            vout->fmt_out.i_height == vout->fmt_render.i_height &&
673            vout->fmt_out.i_chroma == vout->fmt_render.i_chroma);
674     return VLC_SUCCESS;
675 }
676
677 /*****************************************************************************
678  * CleanThread: clean up after InitThread
679  *****************************************************************************
680  * This function is called after a sucessful
681  * initialization. It frees all resources allocated by InitThread.
682  * XXX You have to enter it with change_lock taken.
683  *****************************************************************************/
684 static void ThreadClean(vout_thread_t *vout)
685 {
686     /* Destroy translation tables */
687     if (!vout->p->b_error) {
688         picture_fifo_Flush(vout->p->decoder_fifo, INT64_MAX, false);
689         vout_EndWrapper(vout);
690     }
691 }
692
693 static int ThreadDisplayPicture(vout_thread_t *vout,
694                                 bool now, mtime_t *deadline)
695 {
696     int displayed_count = 0;
697     int lost_count = 0;
698
699     for (;;) {
700         const mtime_t date = mdate();
701         const bool is_paused = vout->p->pause.is_on;
702         bool redisplay = is_paused && !now;
703         bool is_forced;
704
705         /* FIXME/XXX we must redisplay the last decoded picture (because
706          * of potential vout updated, or filters update or SPU update)
707          * For now a high update period is needed but it coulmd be removed
708          * if and only if:
709          * - vout module emits events from theselves.
710          * - *and* SPU is modified to emit an event or a deadline when needed.
711          *
712          * So it will be done latter.
713          */
714         if (!redisplay) {
715             picture_t *peek = picture_fifo_Peek(vout->p->decoder_fifo);
716             if (peek) {
717                 is_forced = peek->b_force || is_paused || now;
718                 *deadline = (is_forced ? date : peek->date) - vout_chrono_GetHigh(&vout->p->render);
719                 picture_Release(peek);
720             } else {
721                 redisplay = true;
722             }
723         }
724         if (redisplay) {
725              /* FIXME a better way for this delay is needed */
726             const mtime_t date_update = vout->p->displayed.date + VOUT_REDISPLAY_DELAY;
727             if (date_update > date || !vout->p->displayed.decoded) {
728                 *deadline = vout->p->displayed.decoded ? date_update : VLC_TS_INVALID;
729                 break;
730             }
731             /* */
732             is_forced = true;
733             *deadline = date - vout_chrono_GetHigh(&vout->p->render);
734         }
735         if (*deadline > VOUT_MWAIT_TOLERANCE)
736             *deadline -= VOUT_MWAIT_TOLERANCE;
737
738         /* If we are too early and can wait, do it */
739         if (date < *deadline && !now)
740             break;
741
742         picture_t *decoded;
743         if (redisplay) {
744             decoded = vout->p->displayed.decoded;
745             vout->p->displayed.decoded = NULL;
746         } else {
747             decoded = picture_fifo_Pop(vout->p->decoder_fifo);
748             assert(decoded);
749             if (!is_forced && !vout->p->is_late_dropped) {
750                 const mtime_t predicted = date + vout_chrono_GetLow(&vout->p->render);
751                 const mtime_t late = predicted - decoded->date;
752                 if (late > 0) {
753                     msg_Dbg(vout, "picture might be displayed late (missing %d ms)", (int)(late/1000));
754                     if (late > VOUT_DISPLAY_LATE_THRESHOLD) {
755                         msg_Warn(vout, "rejected picture because of render time");
756                         /* TODO */
757                         picture_Release(decoded);
758                         lost_count++;
759                         break;
760                     }
761                 }
762             }
763
764             vout->p->displayed.is_interlaced = !decoded->b_progressive;
765             vout->p->displayed.qtype         = decoded->i_qtype;
766         }
767         vout->p->displayed.timestamp = decoded->date;
768
769         /* */
770         if (vout->p->displayed.decoded)
771             picture_Release(vout->p->displayed.decoded);
772         picture_Hold(decoded);
773         vout->p->displayed.decoded = decoded;
774
775         /* */
776         vout_chrono_Start(&vout->p->render);
777
778         picture_t *filtered = NULL;
779         if (decoded) {
780             vlc_mutex_lock(&vout->p->vfilter_lock);
781             filtered = filter_chain_VideoFilter(vout->p->p_vf2_chain, decoded);
782             //assert(filtered == decoded); // TODO implement
783             vlc_mutex_unlock(&vout->p->vfilter_lock);
784             if (!filtered)
785                 continue;
786         }
787
788         /*
789          * Check for subpictures to display
790          */
791         const bool do_snapshot = vout_snapshot_IsRequested(&vout->p->snapshot);
792         mtime_t spu_render_time = is_forced ? mdate() : filtered->date;
793         if (vout->p->pause.is_on)
794             spu_render_time = vout->p->pause.date;
795         else
796             spu_render_time = filtered->date > 1 ? filtered->date : mdate();
797
798         subpicture_t *subpic = spu_SortSubpictures(vout->p->p_spu,
799                                                    spu_render_time,
800                                                    do_snapshot);
801         /*
802          * Perform rendering
803          *
804          * We have to:
805          * - be sure to end up with a direct buffer.
806          * - blend subtitles, and in a fast access buffer
807          */
808         picture_t *direct = NULL;
809         if (filtered &&
810             (vout->p->decoder_pool != vout->p->display_pool || subpic)) {
811             picture_t *render;
812             if (vout->p->is_decoder_pool_slow)
813                 render = picture_NewFromFormat(&vout->fmt_out);
814             else if (vout->p->decoder_pool != vout->p->display_pool)
815                 render = picture_pool_Get(vout->p->display_pool);
816             else
817                 render = picture_pool_Get(vout->p->private_pool);
818
819             if (render) {
820                 picture_Copy(render, filtered);
821
822                 spu_RenderSubpictures(vout->p->p_spu,
823                                       render, &vout->fmt_out,
824                                       subpic, &vout->fmt_in, spu_render_time);
825             }
826             if (vout->p->is_decoder_pool_slow) {
827                 direct = picture_pool_Get(vout->p->display_pool);
828                 if (direct)
829                     picture_Copy(direct, render);
830                 picture_Release(render);
831
832             } else {
833                 direct = render;
834             }
835             picture_Release(filtered);
836             filtered = NULL;
837         } else {
838             direct = filtered;
839         }
840
841         /*
842          * Take a snapshot if requested
843          */
844         if (direct && do_snapshot)
845             vout_snapshot_Set(&vout->p->snapshot, &vout->fmt_out, direct);
846
847         /* Render the direct buffer returned by vout_RenderPicture */
848         if (direct) {
849             vout_RenderWrapper(vout, direct);
850
851             vout_chrono_Stop(&vout->p->render);
852 #if 0
853             {
854             static int i = 0;
855             if (((i++)%10) == 0)
856                 msg_Info(vout, "render: avg %d ms var %d ms",
857                          (int)(vout->p->render.avg/1000), (int)(vout->p->render.var/1000));
858             }
859 #endif
860         }
861
862         /* Wait the real date (for rendering jitter) */
863         if (!is_forced)
864             mwait(decoded->date);
865
866         /* Display the direct buffer returned by vout_RenderPicture */
867         vout->p->displayed.date = mdate();
868         if (direct)
869             vout_DisplayWrapper(vout, direct);
870
871         displayed_count++;
872         break;
873     }
874
875     vout_statistic_Update(&vout->p->statistic, displayed_count, lost_count);
876     if (displayed_count <= 0)
877         return VLC_EGENERIC;
878     return VLC_SUCCESS;
879 }
880
881 /*****************************************************************************
882  * Thread: video output thread
883  *****************************************************************************
884  * Video output thread. This function does only returns when the thread is
885  * terminated. It handles the pictures arriving in the video heap and the
886  * display device events.
887  *****************************************************************************/
888 static void *Thread(void *object)
889 {
890     vout_thread_t *vout = object;
891     bool          has_wrapper;
892
893     /*
894      * Initialize thread
895      */
896     has_wrapper = !vout_OpenWrapper(vout, vout->p->psz_module_name);
897
898     vlc_mutex_lock(&vout->p->change_lock);
899
900     if (has_wrapper)
901         vout->p->b_error = ThreadInit(vout);
902     else
903         vout->p->b_error = true;
904
905     /* signal the creation of the vout */
906     vout->p->b_ready = true;
907     vlc_cond_signal(&vout->p->change_wait);
908
909     if (vout->p->b_error)
910         goto exit_thread;
911
912     /* */
913     vout_interlacing_support_t interlacing = {
914         .is_interlaced = false,
915         .date = mdate(),
916     };
917     vout_postprocessing_support_t postprocessing = {
918         .qtype = QTYPE_NONE,
919     };
920
921     /*
922      * Main loop - it is not executed if an error occurred during
923      * initialization
924      */
925     while (!vout->p->b_done && !vout->p->b_error) {
926         /* */
927         if(vout->p->title.show && vout->p->title.value)
928             DisplayTitleOnOSD(vout);
929
930         vlc_mutex_lock(&vout->p->picture_lock);
931
932         mtime_t deadline = VLC_TS_INVALID;
933         bool has_displayed = !ThreadDisplayPicture(vout, vout->p->step.is_requested, &deadline);
934
935         if (has_displayed) {
936             vout->p->step.timestamp = vout->p->displayed.timestamp;
937             if (vout->p->step.last <= VLC_TS_INVALID)
938                 vout->p->step.last = vout->p->step.timestamp;
939         }
940         if (vout->p->step.is_requested) {
941             if (!has_displayed && !vout->p->b_picture_empty) {
942                 picture_t *peek = picture_fifo_Peek(vout->p->decoder_fifo);
943                 if (peek)
944                     picture_Release(peek);
945                 if (!peek) {
946                     vout->p->b_picture_empty = true;
947                     vlc_cond_signal(&vout->p->picture_wait);
948                 }
949             }
950             if (has_displayed) {
951                 vout->p->step.is_requested = false;
952                 vlc_cond_signal(&vout->p->picture_wait);
953             }
954         }
955
956         const int  picture_qtype      = vout->p->displayed.qtype;
957         const bool picture_interlaced = vout->p->displayed.is_interlaced;
958
959         vlc_mutex_unlock(&vout->p->picture_lock);
960
961         if (vout_ManageWrapper(vout)) {
962             /* A fatal error occurred, and the thread must terminate
963              * immediately, without displaying anything - setting b_error to 1
964              * causes the immediate end of the main while() loop. */
965             // FIXME pf_end
966             vout->p->b_error = true;
967             break;
968         }
969
970         /* Post processing */
971         vout_SetPostProcessingState(vout, &postprocessing, picture_qtype);
972
973         /* Deinterlacing */
974         vout_SetInterlacingState(vout, &interlacing, picture_interlaced);
975
976         /* Check for "video filter2" changes */
977         vlc_mutex_lock(&vout->p->vfilter_lock);
978         if (vout->p->psz_vf2) {
979             es_format_t fmt;
980
981             es_format_Init(&fmt, VIDEO_ES, vout->fmt_render.i_chroma);
982             fmt.video = vout->fmt_render;
983             filter_chain_Reset(vout->p->p_vf2_chain, &fmt, &fmt);
984
985             if (filter_chain_AppendFromString(vout->p->p_vf2_chain,
986                                               vout->p->psz_vf2) < 0)
987                 msg_Err(vout, "Video filter chain creation failed");
988
989             free(vout->p->psz_vf2);
990             vout->p->psz_vf2 = NULL;
991         }
992         vlc_mutex_unlock(&vout->p->vfilter_lock);
993
994         vlc_mutex_unlock(&vout->p->change_lock);
995
996         if (deadline > VLC_TS_INVALID) {
997             vlc_mutex_lock(&vout->p->picture_lock);
998             vlc_cond_timedwait(&vout->p->picture_wait, &vout->p->picture_lock, deadline);
999             vlc_mutex_unlock(&vout->p->picture_lock);
1000         }
1001
1002         vlc_mutex_lock(&vout->p->change_lock);
1003     }
1004
1005     /*
1006      * Error loop - wait until the thread destruction is requested
1007      *
1008      * XXX I wonder if we should periodically clean the decoder_fifo
1009      * or have a way to prevent it filling up.
1010      */
1011     while (vout->p->b_error && !vout->p->b_done)
1012         vlc_cond_wait(&vout->p->change_wait, &vout->p->change_lock);
1013
1014     /* Clean thread */
1015     ThreadClean(vout);
1016
1017 exit_thread:
1018     /* Detach subpicture unit from both input and vout */
1019     spu_Attach(vout->p->p_spu, VLC_OBJECT(vout), false);
1020     vlc_object_detach(vout->p->p_spu);
1021
1022     /* Destroy the video filters2 */
1023     filter_chain_Delete(vout->p->p_vf2_chain);
1024
1025     vlc_mutex_unlock(&vout->p->change_lock);
1026
1027     if (has_wrapper)
1028         vout_CloseWrapper(vout);
1029
1030     return NULL;
1031 }
1032
1033 /*****************************************************************************
1034  * object variables callbacks: a bunch of object variables are used by the
1035  * interfaces to interact with the vout.
1036  *****************************************************************************/
1037 static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd,
1038                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1039 {
1040     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1041     input_thread_t *p_input;
1042     (void)psz_cmd; (void)oldval; (void)p_data;
1043
1044     p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1045                                                  FIND_PARENT );
1046     if (!p_input)
1047     {
1048         msg_Err( p_vout, "Input not found" );
1049         return VLC_EGENERIC;
1050     }
1051
1052     var_SetBool( p_vout, "intf-change", true );
1053
1054     /* Modify input as well because the vout might have to be restarted */
1055     var_Create( p_input, "vout-filter", VLC_VAR_STRING );
1056     var_SetString( p_input, "vout-filter", newval.psz_string );
1057
1058     /* Now restart current video stream */
1059     input_Control( p_input, INPUT_RESTART_ES, -VIDEO_ES );
1060     vlc_object_release( p_input );
1061
1062     return VLC_SUCCESS;
1063 }
1064
1065 /*****************************************************************************
1066  * Video Filter2 stuff
1067  *****************************************************************************/
1068 static int VideoFilter2Callback( vlc_object_t *p_this, char const *psz_cmd,
1069                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1070 {
1071     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1072     (void)psz_cmd; (void)oldval; (void)p_data;
1073
1074     vlc_mutex_lock( &p_vout->p->vfilter_lock );
1075     p_vout->p->psz_vf2 = strdup( newval.psz_string );
1076     vlc_mutex_unlock( &p_vout->p->vfilter_lock );
1077
1078     return VLC_SUCCESS;
1079 }
1080
1081 static void DisplayTitleOnOSD( vout_thread_t *p_vout )
1082 {
1083     const mtime_t i_start = mdate();
1084     const mtime_t i_stop = i_start + INT64_C(1000) * p_vout->p->title.timeout;
1085
1086     if( i_stop <= i_start )
1087         return;
1088
1089     vlc_assert_locked( &p_vout->p->change_lock );
1090
1091     vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1092                            p_vout->p->title.value, NULL,
1093                            p_vout->p->title.position,
1094                            30 + p_vout->fmt_in.i_width
1095                               - p_vout->fmt_in.i_visible_width
1096                               - p_vout->fmt_in.i_x_offset,
1097                            20 + p_vout->fmt_in.i_y_offset,
1098                            i_start, i_stop );
1099
1100     free( p_vout->p->title.value );
1101
1102     p_vout->p->title.value = NULL;
1103 }
1104
1105 /* */
1106 static void PrintVideoFormat(vout_thread_t *vout,
1107                              const char *description,
1108                              const video_format_t *fmt)
1109 {
1110     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",
1111             description,
1112             fmt->i_width, fmt->i_height, fmt->i_x_offset, fmt->i_y_offset,
1113             fmt->i_visible_width, fmt->i_visible_height,
1114             (char*)&fmt->i_chroma,
1115             fmt->i_sar_num, fmt->i_sar_den,
1116             fmt->i_rmask, fmt->i_gmask, fmt->i_bmask);
1117 }
1118