]> git.sesse.net Git - vlc/blob - src/video_output/video_output.c
8d5b2542ea547246786fdd0319e31b2ca287b933
[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  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
27  *****************************************************************************/
28
29 /*****************************************************************************
30  * Preamble
31  *****************************************************************************/
32 #ifdef HAVE_CONFIG_H
33 # include "config.h"
34 #endif
35
36 #include <vlc_common.h>
37
38 #include <stdlib.h>                                                /* free() */
39 #include <string.h>
40
41
42 #ifdef HAVE_SYS_TIMES_H
43 #   include <sys/times.h>
44 #endif
45
46 #include <vlc_vout.h>
47
48 #include <vlc_filter.h>
49 #include <vlc_osd.h>
50 #include <assert.h>
51
52 #if defined( __APPLE__ )
53 /* Include darwin_specific.h here if needed */
54 #endif
55
56 /** FIXME This is quite ugly but needed while we don't have counters
57  * helpers */
58 //#include "input/input_internal.h"
59
60 #include <libvlc.h>
61 #include <vlc_input.h>
62 #include "vout_pictures.h"
63 #include "vout_internal.h"
64
65 /*****************************************************************************
66  * Local prototypes
67  *****************************************************************************/
68 static int      InitThread        ( vout_thread_t * );
69 static void*    RunThread         ( vlc_object_t *  );
70 static void     ErrorThread       ( vout_thread_t * );
71 static void     CleanThread       ( vout_thread_t * );
72 static void     EndThread         ( vout_thread_t * );
73
74 static void     AspectRatio       ( int, int *, int * );
75
76 static void VideoFormatImportRgb( video_format_t *, const picture_heap_t * );
77 static void PictureHeapFixRgb( picture_heap_t * );
78
79 static void     vout_Destructor   ( vlc_object_t * p_this );
80
81 /* Object variables callbacks */
82 static int DeinterlaceCallback( vlc_object_t *, char const *,
83                                 vlc_value_t, vlc_value_t, void * );
84 static int FilterCallback( vlc_object_t *, char const *,
85                            vlc_value_t, vlc_value_t, void * );
86 static int VideoFilter2Callback( vlc_object_t *, char const *,
87                                  vlc_value_t, vlc_value_t, void * );
88
89 /* From vout_intf.c */
90 int vout_Snapshot( vout_thread_t *, picture_t * );
91
92 /* Display media title in OSD */
93 static void DisplayTitleOnOSD( vout_thread_t *p_vout );
94
95 /* Time during which the thread will sleep if it has nothing to
96  * display (in micro-seconds) */
97 #define VOUT_IDLE_SLEEP                 ((int)(0.020*CLOCK_FREQ))
98
99 /* Maximum lap of time allowed between the beginning of rendering and
100  * display. If, compared to the current date, the next image is too
101  * late, the thread will perform an idle loop. This time should be
102  * at least VOUT_IDLE_SLEEP plus the time required to render a few
103  * images, to avoid trashing of decoded images */
104 #define VOUT_DISPLAY_DELAY              ((int)(0.200*CLOCK_FREQ))
105
106 /* Better be in advance when awakening than late... */
107 #define VOUT_MWAIT_TOLERANCE            ((mtime_t)(0.020*CLOCK_FREQ))
108
109 /* Minimum number of direct pictures the video output will accept without
110  * creating additional pictures in system memory */
111 #ifdef OPTIMIZE_MEMORY
112 #   define VOUT_MIN_DIRECT_PICTURES        (VOUT_MAX_PICTURES/2)
113 #else
114 #   define VOUT_MIN_DIRECT_PICTURES        (3*VOUT_MAX_PICTURES/4)
115 #endif
116
117 /*****************************************************************************
118  * Video Filter2 functions
119  *****************************************************************************/
120 static picture_t *video_new_buffer_filter( filter_t *p_filter )
121 {
122     vout_thread_t *p_vout = (vout_thread_t*)p_filter->p_owner;
123     picture_t *p_picture = vout_CreatePicture( p_vout, 0, 0, 0 );
124
125     p_picture->i_status = READY_PICTURE;
126
127     return p_picture;
128 }
129
130 static void video_del_buffer_filter( filter_t *p_filter, picture_t *p_pic )
131 {
132     vout_thread_t *p_vout = (vout_thread_t*)p_filter->p_owner;
133
134     vlc_mutex_lock( &p_vout->picture_lock );
135     vout_UsePictureLocked( p_vout, p_pic );
136     vlc_mutex_unlock( &p_vout->picture_lock );
137 }
138
139 static int video_filter_buffer_allocation_init( filter_t *p_filter, void *p_data )
140 {
141     p_filter->pf_vout_buffer_new = video_new_buffer_filter;
142     p_filter->pf_vout_buffer_del = video_del_buffer_filter;
143     p_filter->p_owner = p_data; /* p_vout */
144     return VLC_SUCCESS;
145 }
146
147 /*****************************************************************************
148  * vout_Request: find a video output thread, create one, or destroy one.
149  *****************************************************************************
150  * This function looks for a video output thread matching the current
151  * properties. If not found, it spawns a new one.
152  *****************************************************************************/
153 vout_thread_t *__vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout,
154                                video_format_t *p_fmt )
155 {
156     const bool b_vout_provided = p_vout != NULL;
157     if( !p_fmt )
158     {
159         /* Video output is no longer used.
160          * TODO: support for reusing video outputs with proper _thread-safe_
161          * reference handling. */
162         if( p_vout )
163             vout_CloseAndRelease( p_vout );
164         return NULL;
165     }
166
167     /* If a video output was provided, lock it, otherwise look for one. */
168     if( p_vout )
169     {
170         vlc_object_hold( p_vout );
171     }
172
173     /* TODO: find a suitable unused video output */
174
175     /* If we now have a video output, check it has the right properties */
176     if( p_vout )
177     {
178         char *psz_filter_chain;
179         vlc_value_t val;
180
181         vlc_mutex_lock( &p_vout->change_lock );
182
183         /* We don't directly check for the "vout-filter" variable for obvious
184          * performance reasons. */
185         if( p_vout->p->b_filter_change )
186         {
187             var_Get( p_vout, "vout-filter", &val );
188             psz_filter_chain = val.psz_string;
189
190             if( psz_filter_chain && !*psz_filter_chain )
191             {
192                 free( psz_filter_chain );
193                 psz_filter_chain = NULL;
194             }
195             if( p_vout->p->psz_filter_chain && !*p_vout->p->psz_filter_chain )
196             {
197                 free( p_vout->p->psz_filter_chain );
198                 p_vout->p->psz_filter_chain = NULL;
199             }
200
201             if( !psz_filter_chain && !p_vout->p->psz_filter_chain )
202             {
203                 p_vout->p->b_filter_change = false;
204             }
205
206             free( psz_filter_chain );
207         }
208
209         if( p_vout->fmt_render.i_chroma != p_fmt->i_chroma ||
210             p_vout->fmt_render.i_width != p_fmt->i_width ||
211             p_vout->fmt_render.i_height != p_fmt->i_height ||
212             p_vout->p->b_filter_change )
213         {
214             vlc_mutex_unlock( &p_vout->change_lock );
215
216             /* We are not interested in this format, close this vout */
217             vout_CloseAndRelease( p_vout );
218             vlc_object_release( p_vout );
219             p_vout = NULL;
220         }
221         else
222         {
223             /* This video output is cool! Hijack it. */
224             if( p_vout->fmt_render.i_aspect != p_fmt->i_aspect )
225             {
226                 /* Correct aspect ratio on change
227                  * FIXME factorize this code with other aspect ration related code */
228                 unsigned int i_sar_num;
229                 unsigned int i_sar_den;
230                 unsigned int i_aspect;
231
232                 i_aspect = p_fmt->i_aspect;
233                 vlc_ureduce( &i_sar_num, &i_sar_den,
234                              p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 );
235 #if 0
236                 /* What's that, it does not seems to be used correcly everywhere
237                  * beside the previous p_vout->fmt_render.i_aspect != p_fmt->i_aspect
238                  * should be fixed to use it too then */
239                 if( p_vout->i_par_num > 0 && p_vout->i_par_den > 0 )
240                 {
241                     i_sar_num *= p_vout->i_par_den;
242                     i_sar_den *= p_vout->i_par_num;
243                     i_aspect = i_aspect * p_vout->i_par_den / p_vout->i_par_num;
244                 }
245 #endif
246
247                 if( i_sar_num > 0 && i_sar_den > 0 && i_aspect > 0 )
248                 {
249                     p_vout->fmt_in.i_sar_num = i_sar_num;
250                     p_vout->fmt_in.i_sar_den = i_sar_den;
251                     p_vout->fmt_in.i_aspect  = i_aspect;
252
253                     p_vout->fmt_render.i_sar_num = i_sar_num;
254                     p_vout->fmt_render.i_sar_den = i_sar_den;
255                     p_vout->fmt_render.i_aspect  = i_aspect;
256
257                     p_vout->render.i_aspect   = i_aspect;
258
259                     p_vout->i_changes |= VOUT_ASPECT_CHANGE;
260
261                 }
262             }
263             vlc_mutex_unlock( &p_vout->change_lock );
264
265             vlc_object_release( p_vout );
266         }
267
268         if( p_vout )
269         {
270             msg_Dbg( p_this, "reusing provided vout" );
271
272             spu_Attach( p_vout->p_spu, p_this, true );
273
274             vlc_object_detach( p_vout );
275             vlc_object_attach( p_vout, p_this );
276
277             /* Display title if we are not using the vout given to vout_Request.
278              * XXX for now b_vout_provided is always true at this stage */
279             if( p_vout->p->b_title_show && !b_vout_provided )
280                 DisplayTitleOnOSD( p_vout );
281         }
282     }
283
284     if( !p_vout )
285     {
286         msg_Dbg( p_this, "no usable vout present, spawning one" );
287
288         p_vout = vout_Create( p_this, p_fmt );
289     }
290
291     return p_vout;
292 }
293
294 /*****************************************************************************
295  * vout_Create: creates a new video output thread
296  *****************************************************************************
297  * This function creates a new video output thread, and returns a pointer
298  * to its description. On error, it returns NULL.
299  *****************************************************************************/
300 vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt )
301 {
302     vout_thread_t  * p_vout;                            /* thread descriptor */
303     int              i_index;                               /* loop variable */
304     vlc_value_t      val, text;
305
306     unsigned int i_width = p_fmt->i_width;
307     unsigned int i_height = p_fmt->i_height;
308     vlc_fourcc_t i_chroma = p_fmt->i_chroma;
309     unsigned int i_aspect = p_fmt->i_aspect;
310
311     config_chain_t *p_cfg;
312     char *psz_parser;
313     char *psz_name;
314
315     if( i_width <= 0 || i_height <= 0 || i_aspect <= 0 )
316         return NULL;
317
318     vlc_ureduce( &p_fmt->i_sar_num, &p_fmt->i_sar_den,
319                  p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 );
320     if( p_fmt->i_sar_num <= 0 || p_fmt->i_sar_den <= 0 )
321         return NULL;
322
323     /* Allocate descriptor */
324     static const char typename[] = "video output";
325     p_vout = vlc_custom_create( p_parent, sizeof( *p_vout ), VLC_OBJECT_VOUT,
326                                 typename );
327     if( p_vout == NULL )
328         return NULL;
329
330     /* */
331     p_vout->p = calloc( 1, sizeof(*p_vout->p) );
332     if( !p_vout->p )
333     {
334         vlc_object_release( p_vout );
335         return NULL;
336     }
337
338     /* Initialize pictures - translation tables and functions
339      * will be initialized later in InitThread */
340     for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++)
341     {
342         p_vout->p_picture[i_index].pf_lock = NULL;
343         p_vout->p_picture[i_index].pf_unlock = NULL;
344         p_vout->p_picture[i_index].i_status = FREE_PICTURE;
345         p_vout->p_picture[i_index].i_type   = EMPTY_PICTURE;
346         p_vout->p_picture[i_index].b_slow   = 0;
347     }
348
349     /* No images in the heap */
350     p_vout->i_heap_size = 0;
351
352     /* Initialize the rendering heap */
353     I_RENDERPICTURES = 0;
354
355     p_vout->fmt_render        = *p_fmt;   /* FIXME palette */
356     p_vout->fmt_in            = *p_fmt;   /* FIXME palette */
357
358     p_vout->render.i_width    = i_width;
359     p_vout->render.i_height   = i_height;
360     p_vout->render.i_chroma   = i_chroma;
361     p_vout->render.i_aspect   = i_aspect;
362
363     p_vout->render.i_rmask    = p_fmt->i_rmask;
364     p_vout->render.i_gmask    = p_fmt->i_gmask;
365     p_vout->render.i_bmask    = p_fmt->i_bmask;
366
367     p_vout->render.i_last_used_pic = -1;
368     p_vout->render.b_allow_modify_pics = 1;
369
370     /* Zero the output heap */
371     I_OUTPUTPICTURES = 0;
372     p_vout->output.i_width    = 0;
373     p_vout->output.i_height   = 0;
374     p_vout->output.i_chroma   = 0;
375     p_vout->output.i_aspect   = 0;
376
377     p_vout->output.i_rmask    = 0;
378     p_vout->output.i_gmask    = 0;
379     p_vout->output.i_bmask    = 0;
380
381     /* Initialize misc stuff */
382     p_vout->i_changes    = 0;
383     p_vout->b_scale      = 1;
384     p_vout->b_fullscreen = 0;
385     p_vout->i_alignment  = 0;
386     p_vout->p->render_time  = 10;
387     p_vout->p->c_fps_samples = 0;
388     p_vout->p->i_picture_lost = 0;
389     p_vout->p->i_picture_displayed = 0;
390     p_vout->p->b_filter_change = 0;
391     p_vout->p->b_paused = false;
392     p_vout->p->i_pause_date = 0;
393     p_vout->pf_control = NULL;
394     p_vout->p_window = NULL;
395     p_vout->p->i_par_num =
396     p_vout->p->i_par_den = 1;
397     p_vout->p->p_picture_displayed = NULL;
398     p_vout->p->i_picture_displayed_date = 0;
399     p_vout->p->b_picture_displayed = false;
400     p_vout->p->b_picture_empty = false;
401
402     /* Initialize locks */
403     vlc_mutex_init( &p_vout->picture_lock );
404     vlc_cond_init( &p_vout->p->picture_wait );
405     vlc_mutex_init( &p_vout->change_lock );
406     vlc_mutex_init( &p_vout->p->vfilter_lock );
407
408     /* Mouse coordinates */
409     var_Create( p_vout, "mouse-x", VLC_VAR_INTEGER );
410     var_Create( p_vout, "mouse-y", VLC_VAR_INTEGER );
411     var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER );
412     var_Create( p_vout, "mouse-moved", VLC_VAR_BOOL );
413     var_Create( p_vout, "mouse-clicked", VLC_VAR_INTEGER );
414
415     /* Initialize subpicture unit */
416     p_vout->p_spu = spu_Create( p_vout );
417     spu_Attach( p_vout->p_spu, p_parent, true );
418
419     /* Attach the new object now so we can use var inheritance below */
420     vlc_object_attach( p_vout, p_parent );
421
422     spu_Init( p_vout->p_spu );
423
424     /* Take care of some "interface/control" related initialisations */
425     vout_IntfInit( p_vout );
426
427     /* If the parent is not a VOUT object, that means we are at the start of
428      * the video output pipe */
429     if( p_parent->i_object_type != VLC_OBJECT_VOUT )
430     {
431         /* Look for the default filter configuration */
432         p_vout->p->psz_filter_chain =
433             var_CreateGetStringCommand( p_vout, "vout-filter" );
434
435         /* Apply video filter2 objects on the first vout */
436         p_vout->p->psz_vf2 =
437             var_CreateGetStringCommand( p_vout, "video-filter" );
438     }
439     else
440     {
441         /* continue the parent's filter chain */
442         char *psz_tmp;
443
444         /* Ugly hack to jump to our configuration chain */
445         p_vout->p->psz_filter_chain
446             = ((vout_thread_t *)p_parent)->p->psz_filter_chain;
447         p_vout->p->psz_filter_chain
448             = config_ChainCreate( &psz_tmp, &p_cfg, p_vout->p->psz_filter_chain );
449         config_ChainDestroy( p_cfg );
450         free( psz_tmp );
451
452         /* Create a video filter2 var ... but don't inherit values */
453         var_Create( p_vout, "video-filter",
454                     VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
455         p_vout->p->psz_vf2 = var_GetString( p_vout, "video-filter" );
456     }
457
458     var_AddCallback( p_vout, "video-filter", VideoFilter2Callback, NULL );
459     p_vout->p->p_vf2_chain = filter_chain_New( p_vout, "video filter2",
460         false, video_filter_buffer_allocation_init, NULL, p_vout );
461
462     /* Choose the video output module */
463     if( !p_vout->p->psz_filter_chain || !*p_vout->p->psz_filter_chain )
464     {
465         var_Create( p_vout, "vout", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
466         var_Get( p_vout, "vout", &val );
467         psz_parser = val.psz_string;
468     }
469     else
470     {
471         psz_parser = strdup( p_vout->p->psz_filter_chain );
472         p_vout->p->b_title_show = false;
473     }
474
475     /* Create the vout thread */
476     char* psz_tmp = config_ChainCreate( &psz_name, &p_cfg, psz_parser );
477     free( psz_parser );
478     free( psz_tmp );
479     p_vout->p_cfg = p_cfg;
480     p_vout->p_module = module_need( p_vout,
481         ( p_vout->p->psz_filter_chain && *p_vout->p->psz_filter_chain ) ?
482         "video filter" : "video output", psz_name, p_vout->p->psz_filter_chain && *p_vout->p->psz_filter_chain );
483     free( psz_name );
484
485     if( p_vout->p_module == NULL )
486     {
487         msg_Err( p_vout, "no suitable vout module" );
488         vlc_object_set_destructor( p_vout, vout_Destructor );
489         vlc_object_release( p_vout );
490         return NULL;
491     }
492
493     /* Create a few object variables for interface interaction */
494     var_Create( p_vout, "deinterlace", VLC_VAR_STRING | VLC_VAR_HASCHOICE );
495     text.psz_string = _("Deinterlace");
496     var_Change( p_vout, "deinterlace", VLC_VAR_SETTEXT, &text, NULL );
497     val.psz_string = (char *)""; text.psz_string = _("Disable");
498     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
499     val.psz_string = (char *)"discard"; text.psz_string = _("Discard");
500     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
501     val.psz_string = (char *)"blend"; text.psz_string = _("Blend");
502     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
503     val.psz_string = (char *)"mean"; text.psz_string = _("Mean");
504     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
505     val.psz_string = (char *)"bob"; text.psz_string = _("Bob");
506     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
507     val.psz_string = (char *)"linear"; text.psz_string = _("Linear");
508     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
509     val.psz_string = (char *)"x"; text.psz_string = (char *)"X";
510     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
511
512     if( var_Get( p_vout, "deinterlace-mode", &val ) == VLC_SUCCESS )
513     {
514         var_Set( p_vout, "deinterlace", val );
515         free( val.psz_string );
516     }
517     var_AddCallback( p_vout, "deinterlace", DeinterlaceCallback, NULL );
518
519     var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
520     text.psz_string = _("Filters");
521     var_Change( p_vout, "vout-filter", VLC_VAR_SETTEXT, &text, NULL );
522     var_AddCallback( p_vout, "vout-filter", FilterCallback, NULL );
523
524     if( vlc_thread_create( p_vout, "video output", RunThread,
525                            VLC_THREAD_PRIORITY_OUTPUT, true ) )
526     {
527         module_unneed( p_vout, p_vout->p_module );
528         p_vout->p_module = NULL;
529         vlc_object_set_destructor( p_vout, vout_Destructor );
530         vlc_object_release( p_vout );
531         return NULL;
532     }
533
534     vlc_object_set_destructor( p_vout, vout_Destructor );
535
536     if( p_vout->b_error )
537     {
538         msg_Err( p_vout, "video output creation failed" );
539         vout_CloseAndRelease( p_vout );
540         return NULL;
541     }
542
543     return p_vout;
544 }
545
546 /*****************************************************************************
547  * vout_Close: Close a vout created by vout_Create.
548  *****************************************************************************
549  * You HAVE to call it on vout created by vout_Create before vlc_object_release.
550  * You should NEVER call it on vout not obtained through vout_Create
551  * (like with vout_Request or vlc_object_find.)
552  * You can use vout_CloseAndRelease() as a convenient method.
553  *****************************************************************************/
554 void vout_Close( vout_thread_t *p_vout )
555 {
556     assert( p_vout );
557
558     vlc_object_kill( p_vout );
559     vlc_thread_join( p_vout );
560     module_unneed( p_vout, p_vout->p_module );
561     p_vout->p_module = NULL;
562 }
563
564 /* */
565 static void vout_Destructor( vlc_object_t * p_this )
566 {
567     vout_thread_t *p_vout = (vout_thread_t *)p_this;
568
569     /* Make sure the vout was stopped first */
570     assert( !p_vout->p_module );
571
572     /* */
573     spu_Destroy( p_vout->p_spu );
574
575     /* Destroy the locks */
576     vlc_cond_destroy( &p_vout->p->picture_wait );
577     vlc_mutex_destroy( &p_vout->picture_lock );
578     vlc_mutex_destroy( &p_vout->change_lock );
579     vlc_mutex_destroy( &p_vout->p->vfilter_lock );
580
581     free( p_vout->p->psz_filter_chain );
582
583     config_ChainDestroy( p_vout->p_cfg );
584
585     free( p_vout->p );
586
587 #ifndef __APPLE__
588     vout_thread_t *p_another_vout;
589
590     /* This is a dirty hack mostly for Linux, where there is no way to get the
591      * GUI back if you closed it while playing video. This is solved in
592      * Mac OS X, where we have this novelty called menubar, that will always
593      * allow you access to the applications main functionality. They should try
594      * that on linux sometime. */
595     p_another_vout = vlc_object_find( p_this->p_libvlc,
596                                       VLC_OBJECT_VOUT, FIND_ANYWHERE );
597     if( p_another_vout == NULL )
598         var_SetBool( p_this->p_libvlc, "intf-show", true );
599     else
600         vlc_object_release( p_another_vout );
601 #endif
602 }
603
604 /* */
605 void vout_ChangePause( vout_thread_t *p_vout, bool b_paused, mtime_t i_date )
606 {
607     vlc_object_lock( p_vout );
608
609     assert( !p_vout->p->b_paused || !b_paused );
610
611     vlc_mutex_lock( &p_vout->picture_lock );
612
613     p_vout->p->i_picture_displayed_date = 0;
614
615     if( p_vout->p->b_paused )
616     {
617         const mtime_t i_duration = i_date - p_vout->p->i_pause_date;
618
619         for( int i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
620         {
621             picture_t *p_pic = PP_RENDERPICTURE[i_index];
622
623             if( p_pic->i_status == READY_PICTURE )
624                 p_pic->date += i_duration;
625         }
626         vlc_cond_signal( &p_vout->p->picture_wait );
627         vlc_mutex_unlock( &p_vout->picture_lock );
628
629         spu_OffsetSubtitleDate( p_vout->p_spu, i_duration );
630     }
631     else
632     {
633         vlc_mutex_unlock( &p_vout->picture_lock );
634     }
635     p_vout->p->b_paused = b_paused;
636     p_vout->p->i_pause_date = i_date;
637
638     vlc_object_unlock( p_vout );
639 }
640 void vout_GetResetStatistic( vout_thread_t *p_vout, int *pi_displayed, int *pi_lost )
641 {
642     vlc_object_lock( p_vout );
643
644     *pi_displayed = p_vout->p->i_picture_displayed;
645     *pi_lost = p_vout->p->i_picture_lost;
646
647     p_vout->p->i_picture_displayed = 0;
648     p_vout->p->i_picture_lost = 0;
649
650     vlc_object_unlock( p_vout );
651 }
652 void vout_Flush( vout_thread_t *p_vout, mtime_t i_date )
653 {
654     vlc_mutex_lock( &p_vout->picture_lock );
655     p_vout->p->i_picture_displayed_date = 0;
656     for( int i = 0; i < p_vout->render.i_pictures; i++ )
657     {
658         picture_t *p_pic = p_vout->render.pp_picture[i];
659
660         if( p_pic->i_status == READY_PICTURE ||
661             p_pic->i_status == DISPLAYED_PICTURE )
662         {
663             /* We cannot change picture status if it is in READY_PICTURE state,
664              * Just make sure they won't be displayed */
665             if( p_pic->date > i_date )
666                 p_pic->date = i_date;
667         }
668     }
669     vlc_cond_signal( &p_vout->p->picture_wait );
670     vlc_mutex_unlock( &p_vout->picture_lock );
671 }
672 void vout_FixLeaks( vout_thread_t *p_vout, bool b_forced )
673 {
674     int i_pic, i_ready_pic;
675
676     vlc_mutex_lock( &p_vout->picture_lock );
677
678     for( i_pic = 0, i_ready_pic = 0; i_pic < p_vout->render.i_pictures && !b_forced; i_pic++ )
679     {
680         const picture_t *p_pic = p_vout->render.pp_picture[i_pic];
681
682         if( p_pic->i_status == READY_PICTURE )
683         {
684             i_ready_pic++;
685             /* If we have at least 2 ready pictures, wait for the vout thread to
686              * process one */
687             if( i_ready_pic >= 2 )
688                 break;
689
690             continue;
691         }
692
693         if( p_pic->i_status == DISPLAYED_PICTURE )
694         {
695             /* If at least one displayed picture is not referenced
696              * let vout free it */
697             if( p_pic->i_refcount == 0 )
698                 break;
699         }
700     }
701     if( i_pic < p_vout->render.i_pictures && !b_forced )
702     {
703         vlc_mutex_unlock( &p_vout->picture_lock );
704         return;
705     }
706
707     /* Too many pictures are still referenced, there is probably a bug
708      * with the decoder */
709     if( !b_forced )
710         msg_Err( p_vout, "pictures leaked, resetting the heap" );
711
712     /* Just free all the pictures */
713     for( i_pic = 0; i_pic < p_vout->render.i_pictures; i_pic++ )
714     {
715         picture_t *p_pic = p_vout->render.pp_picture[i_pic];
716
717         msg_Dbg( p_vout, "[%d] %d %d", i_pic, p_pic->i_status, p_pic->i_refcount );
718         p_pic->i_refcount = 0;
719         vout_UsePictureLocked( p_vout, p_pic );
720     }
721     vlc_cond_signal( &p_vout->p->picture_wait );
722     vlc_mutex_unlock( &p_vout->picture_lock );
723 }
724 void vout_NextPicture( vout_thread_t *p_vout, mtime_t *pi_duration )
725 {
726     vlc_mutex_lock( &p_vout->picture_lock );
727
728     const mtime_t i_displayed_date = p_vout->p->i_picture_displayed_date;
729
730     p_vout->p->b_picture_displayed = false;
731     p_vout->p->b_picture_empty = false;
732     if( p_vout->p->p_picture_displayed )
733     {
734         p_vout->p->p_picture_displayed->date = 1;
735         vlc_cond_signal( &p_vout->p->picture_wait );
736     }
737
738     while( !p_vout->p->b_picture_displayed && !p_vout->p->b_picture_empty )
739         vlc_cond_wait( &p_vout->p->picture_wait, &p_vout->picture_lock );
740
741     *pi_duration = __MAX( p_vout->p->i_picture_displayed_date - i_displayed_date, 0 );
742
743     /* TODO advance subpicture by the duration ... */
744
745     vlc_mutex_unlock( &p_vout->picture_lock );
746 }
747
748 /*****************************************************************************
749  * InitThread: initialize video output thread
750  *****************************************************************************
751  * This function is called from RunThread and performs the second step of the
752  * initialization. It returns 0 on success. Note that the thread's flag are not
753  * modified inside this function.
754  * XXX You have to enter it with change_lock taken.
755  *****************************************************************************/
756 static int ChromaCreate( vout_thread_t *p_vout );
757 static void ChromaDestroy( vout_thread_t *p_vout );
758
759 static bool ChromaIsEqual( const picture_heap_t *p_output, const picture_heap_t *p_render )
760 {
761      if( !vout_ChromaCmp( p_output->i_chroma, p_render->i_chroma ) )
762          return false;
763
764      if( p_output->i_chroma != FOURCC_RV15 &&
765          p_output->i_chroma != FOURCC_RV16 &&
766          p_output->i_chroma != FOURCC_RV24 &&
767          p_output->i_chroma != FOURCC_RV32 )
768          return true;
769
770      return p_output->i_rmask == p_render->i_rmask &&
771             p_output->i_gmask == p_render->i_gmask &&
772             p_output->i_bmask == p_render->i_bmask;
773 }
774
775 static int InitThread( vout_thread_t *p_vout )
776 {
777     int i, i_aspect_x, i_aspect_y;
778
779     /* Initialize output method, it allocates direct buffers for us */
780     if( p_vout->pf_init( p_vout ) )
781         return VLC_EGENERIC;
782
783     p_vout->p->p_picture_displayed = NULL;
784
785     if( !I_OUTPUTPICTURES )
786     {
787         msg_Err( p_vout, "plugin was unable to allocate at least "
788                          "one direct buffer" );
789         p_vout->pf_end( p_vout );
790         return VLC_EGENERIC;
791     }
792
793     if( I_OUTPUTPICTURES > VOUT_MAX_PICTURES )
794     {
795         msg_Err( p_vout, "plugin allocated too many direct buffers, "
796                          "our internal buffers must have overflown." );
797         p_vout->pf_end( p_vout );
798         return VLC_EGENERIC;
799     }
800
801     msg_Dbg( p_vout, "got %i direct buffer(s)", I_OUTPUTPICTURES );
802
803     AspectRatio( p_vout->fmt_render.i_aspect, &i_aspect_x, &i_aspect_y );
804
805     msg_Dbg( p_vout, "picture in %ix%i (%i,%i,%ix%i), "
806              "chroma %4.4s, ar %i:%i, sar %i:%i",
807              p_vout->fmt_render.i_width, p_vout->fmt_render.i_height,
808              p_vout->fmt_render.i_x_offset, p_vout->fmt_render.i_y_offset,
809              p_vout->fmt_render.i_visible_width,
810              p_vout->fmt_render.i_visible_height,
811              (char*)&p_vout->fmt_render.i_chroma,
812              i_aspect_x, i_aspect_y,
813              p_vout->fmt_render.i_sar_num, p_vout->fmt_render.i_sar_den );
814
815     AspectRatio( p_vout->fmt_in.i_aspect, &i_aspect_x, &i_aspect_y );
816
817     msg_Dbg( p_vout, "picture user %ix%i (%i,%i,%ix%i), "
818              "chroma %4.4s, ar %i:%i, sar %i:%i",
819              p_vout->fmt_in.i_width, p_vout->fmt_in.i_height,
820              p_vout->fmt_in.i_x_offset, p_vout->fmt_in.i_y_offset,
821              p_vout->fmt_in.i_visible_width,
822              p_vout->fmt_in.i_visible_height,
823              (char*)&p_vout->fmt_in.i_chroma,
824              i_aspect_x, i_aspect_y,
825              p_vout->fmt_in.i_sar_num, p_vout->fmt_in.i_sar_den );
826
827     if( !p_vout->fmt_out.i_width || !p_vout->fmt_out.i_height )
828     {
829         p_vout->fmt_out.i_width = p_vout->fmt_out.i_visible_width =
830             p_vout->output.i_width;
831         p_vout->fmt_out.i_height = p_vout->fmt_out.i_visible_height =
832             p_vout->output.i_height;
833         p_vout->fmt_out.i_x_offset =  p_vout->fmt_out.i_y_offset = 0;
834
835         p_vout->fmt_out.i_aspect = p_vout->output.i_aspect;
836         p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
837     }
838     if( !p_vout->fmt_out.i_sar_num || !p_vout->fmt_out.i_sar_num )
839     {
840         p_vout->fmt_out.i_sar_num = p_vout->fmt_out.i_aspect *
841             p_vout->fmt_out.i_height;
842         p_vout->fmt_out.i_sar_den = VOUT_ASPECT_FACTOR *
843             p_vout->fmt_out.i_width;
844     }
845
846     vlc_ureduce( &p_vout->fmt_out.i_sar_num, &p_vout->fmt_out.i_sar_den,
847                  p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den, 0 );
848
849     AspectRatio( p_vout->fmt_out.i_aspect, &i_aspect_x, &i_aspect_y );
850
851     msg_Dbg( p_vout, "picture out %ix%i (%i,%i,%ix%i), "
852              "chroma %4.4s, ar %i:%i, sar %i:%i",
853              p_vout->fmt_out.i_width, p_vout->fmt_out.i_height,
854              p_vout->fmt_out.i_x_offset, p_vout->fmt_out.i_y_offset,
855              p_vout->fmt_out.i_visible_width,
856              p_vout->fmt_out.i_visible_height,
857              (char*)&p_vout->fmt_out.i_chroma,
858              i_aspect_x, i_aspect_y,
859              p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den );
860
861     /* FIXME removed the need of both fmt_* and heap infos */
862     /* Calculate shifts from system-updated masks */
863     PictureHeapFixRgb( &p_vout->render );
864     VideoFormatImportRgb( &p_vout->fmt_render, &p_vout->render );
865
866     PictureHeapFixRgb( &p_vout->output );
867     VideoFormatImportRgb( &p_vout->fmt_out, &p_vout->output );
868
869     /* Check whether we managed to create direct buffers similar to
870      * the render buffers, ie same size and chroma */
871     if( ( p_vout->output.i_width == p_vout->render.i_width )
872      && ( p_vout->output.i_height == p_vout->render.i_height )
873      && ( ChromaIsEqual( &p_vout->output, &p_vout->render ) ) )
874     {
875         /* Cool ! We have direct buffers, we can ask the decoder to
876          * directly decode into them ! Map the first render buffers to
877          * the first direct buffers, but keep the first direct buffer
878          * for memcpy operations */
879         p_vout->p->b_direct = true;
880
881         for( i = 1; i < VOUT_MAX_PICTURES; i++ )
882         {
883             if( p_vout->p_picture[ i ].i_type != DIRECT_PICTURE &&
884                 I_RENDERPICTURES >= VOUT_MIN_DIRECT_PICTURES - 1 &&
885                 p_vout->p_picture[ i - 1 ].i_type == DIRECT_PICTURE )
886             {
887                 /* We have enough direct buffers so there's no need to
888                  * try to use system memory buffers. */
889                 break;
890             }
891             PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
892             I_RENDERPICTURES++;
893         }
894
895         msg_Dbg( p_vout, "direct render, mapping "
896                  "render pictures 0-%i to system pictures 1-%i",
897                  VOUT_MAX_PICTURES - 2, VOUT_MAX_PICTURES - 1 );
898     }
899     else
900     {
901         /* Rats... Something is wrong here, we could not find an output
902          * plugin able to directly render what we decode. See if we can
903          * find a chroma plugin to do the conversion */
904         p_vout->p->b_direct = false;
905
906         if( ChromaCreate( p_vout ) )
907         {
908             p_vout->pf_end( p_vout );
909             return VLC_EGENERIC;
910         }
911
912         msg_Dbg( p_vout, "indirect render, mapping "
913                  "render pictures 0-%i to system pictures %i-%i",
914                  VOUT_MAX_PICTURES - 1, I_OUTPUTPICTURES,
915                  I_OUTPUTPICTURES + VOUT_MAX_PICTURES - 1 );
916
917         /* Append render buffers after the direct buffers */
918         for( i = I_OUTPUTPICTURES; i < 2 * VOUT_MAX_PICTURES; i++ )
919         {
920             PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
921             I_RENDERPICTURES++;
922
923             /* Check if we have enough render pictures */
924             if( I_RENDERPICTURES == VOUT_MAX_PICTURES )
925                 break;
926         }
927     }
928
929     /* Link pictures back to their heap */
930     for( i = 0 ; i < I_RENDERPICTURES ; i++ )
931     {
932         PP_RENDERPICTURE[ i ]->p_heap = &p_vout->render;
933     }
934
935     for( i = 0 ; i < I_OUTPUTPICTURES ; i++ )
936     {
937         PP_OUTPUTPICTURE[ i ]->p_heap = &p_vout->output;
938     }
939
940     return VLC_SUCCESS;
941 }
942
943 /*****************************************************************************
944  * RunThread: video output thread
945  *****************************************************************************
946  * Video output thread. This function does only returns when the thread is
947  * terminated. It handles the pictures arriving in the video heap and the
948  * display device events.
949  *****************************************************************************/
950 static void* RunThread( vlc_object_t *p_this )
951 {
952     vout_thread_t *p_vout = (vout_thread_t *)p_this;
953     int             i_idle_loops = 0;  /* loops without displaying a picture */
954
955     bool            b_drop_late;
956
957     int canc = vlc_savecancel();
958
959     /*
960      * Initialize thread
961      */
962     vlc_mutex_lock( &p_vout->change_lock );
963     p_vout->b_error = InitThread( p_vout );
964
965     b_drop_late = var_CreateGetBool( p_vout, "drop-late-frames" );
966
967     /* signal the creation of the vout */
968     vlc_thread_ready( p_vout );
969
970     if( p_vout->b_error )
971     {
972         EndThread( p_vout );
973         vlc_mutex_unlock( &p_vout->change_lock );
974         vlc_restorecancel( canc );
975         return NULL;
976     }
977
978     vlc_object_lock( p_vout );
979
980     if( p_vout->p->b_title_show )
981         DisplayTitleOnOSD( p_vout );
982
983     /*
984      * Main loop - it is not executed if an error occurred during
985      * initialization
986      */
987     while( vlc_object_alive( p_vout ) && !p_vout->b_error )
988     {
989         /* Initialize loop variables */
990         const mtime_t current_date = mdate();
991         picture_t *p_picture;
992         picture_t *p_filtered_picture;
993         mtime_t display_date;
994         picture_t *p_directbuffer;
995         int i_index;
996
997         vlc_mutex_lock( &p_vout->picture_lock );
998
999         /* Look for the earliest picture but after the last displayed one */
1000         picture_t *p_last = p_vout->p->p_picture_displayed;;
1001
1002         p_picture = NULL;
1003         for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
1004         {
1005             picture_t *p_pic = PP_RENDERPICTURE[i_index];
1006
1007             if( p_pic->i_status != READY_PICTURE )
1008                 continue;
1009
1010             if( p_vout->p->b_paused && p_last && p_last->date > 1 )
1011                 continue;
1012
1013             if( p_last && p_pic != p_last && p_pic->date <= p_last->date )
1014             {
1015                 /* Drop old picture */
1016                 vout_UsePictureLocked( p_vout, p_pic );
1017             }
1018             else if( !p_vout->p->b_paused && !p_pic->b_force && p_pic != p_last &&
1019                      p_pic->date < current_date + p_vout->p->render_time &&
1020                      b_drop_late )
1021             {
1022                 /* Picture is late: it will be destroyed and the thread
1023                  * will directly choose the next picture */
1024                 vout_UsePictureLocked( p_vout, p_pic );
1025                 p_vout->p->i_picture_lost++;
1026
1027                 msg_Warn( p_vout, "late picture skipped (%"PRId64")",
1028                                   current_date - p_pic->date );
1029             }
1030             else if( ( !p_last || p_last->date < p_pic->date ) &&
1031                      ( p_picture == NULL || p_pic->date < p_picture->date ) )
1032             {
1033                 p_picture = p_pic;
1034             }
1035         }
1036         if( !p_picture )
1037         {
1038             p_picture = p_last;
1039
1040             if( !p_vout->p->b_picture_empty )
1041             {
1042                 p_vout->p->b_picture_empty = true;
1043                 vlc_cond_signal( &p_vout->p->picture_wait );
1044             }
1045         }
1046
1047         display_date = 0;
1048         if( p_picture )
1049         {
1050             display_date = p_picture->date;
1051
1052             /* If we found better than the last picture, destroy it */
1053             if( p_last && p_picture != p_last )
1054             {
1055                 vout_UsePictureLocked( p_vout, p_last );
1056                 p_vout->p->p_picture_displayed = p_last = NULL;
1057             }
1058
1059             /* Compute FPS rate */
1060             p_vout->p->p_fps_sample[ p_vout->p->c_fps_samples++ % VOUT_FPS_SAMPLES ] = display_date;
1061
1062             if( !p_vout->p->b_paused && display_date > current_date + VOUT_DISPLAY_DELAY )
1063             {
1064                 /* A picture is ready to be rendered, but its rendering date
1065                  * is far from the current one so the thread will perform an
1066                  * empty loop as if no picture were found. The picture state
1067                  * is unchanged */
1068                 p_picture    = NULL;
1069                 display_date = 0;
1070             }
1071             else if( p_picture == p_last )
1072             {
1073                 /* We are asked to repeat the previous picture, but we first
1074                  * wait for a couple of idle loops */
1075                 if( i_idle_loops < 4 )
1076                 {
1077                     p_picture    = NULL;
1078                     display_date = 0;
1079                 }
1080                 else
1081                 {
1082                     /* We set the display date to something high, otherwise
1083                      * we'll have lots of problems with late pictures */
1084                     display_date = current_date + p_vout->p->render_time;
1085                 }
1086             }
1087             else if( p_vout->p->b_paused && display_date > current_date + VOUT_DISPLAY_DELAY )
1088             {
1089                 display_date = current_date + VOUT_DISPLAY_DELAY;
1090             }
1091
1092             if( p_picture )
1093             {
1094                 if( p_picture->date > 1 )
1095                 {
1096                     p_vout->p->i_picture_displayed_date = p_picture->date;
1097                     if( p_picture != p_last && !p_vout->p->b_picture_displayed )
1098                     {
1099                         p_vout->p->b_picture_displayed = true;
1100                         vlc_cond_signal( &p_vout->p->picture_wait );
1101                     }
1102                 }
1103                 p_vout->p->p_picture_displayed = p_picture;
1104             }
1105         }
1106         vlc_mutex_unlock( &p_vout->picture_lock );
1107
1108         if( p_picture == NULL )
1109             i_idle_loops++;
1110
1111         p_filtered_picture = NULL;
1112         if( p_picture )
1113             p_filtered_picture = filter_chain_VideoFilter( p_vout->p->p_vf2_chain,
1114                                                            p_picture );
1115
1116         /* FIXME it is ugly that b_snapshot is not locked but I do not
1117          * know which lock to use (here and in the snapshot callback) */
1118         const bool b_snapshot = p_vout->p->b_snapshot && p_picture != NULL;
1119
1120         /*
1121          * Check for subpictures to display
1122          */
1123         subpicture_t *p_subpic = NULL;
1124         if( display_date > 0 )
1125             p_subpic = spu_SortSubpictures( p_vout->p_spu, display_date,
1126                                             p_vout->p->b_paused, b_snapshot );
1127
1128         /*
1129          * Perform rendering
1130          */
1131         p_vout->p->i_picture_displayed++;
1132         p_directbuffer = vout_RenderPicture( p_vout, p_filtered_picture,
1133                                              p_subpic, p_vout->p->b_paused );
1134
1135         /*
1136          * Take a snapshot if requested
1137          */
1138         if( p_directbuffer && b_snapshot )
1139         {
1140             /* FIXME lock (see b_snapshot) */
1141             p_vout->p->b_snapshot = false;
1142
1143             vout_Snapshot( p_vout, p_directbuffer );
1144         }
1145
1146         /*
1147          * Call the plugin-specific rendering method if there is one
1148          */
1149         if( p_filtered_picture != NULL && p_directbuffer != NULL && p_vout->pf_render )
1150         {
1151             /* Render the direct buffer returned by vout_RenderPicture */
1152             p_vout->pf_render( p_vout, p_directbuffer );
1153         }
1154
1155         /*
1156          * Sleep, wake up
1157          */
1158         if( display_date != 0 && p_directbuffer != NULL )
1159         {
1160             mtime_t current_render_time = mdate() - current_date;
1161             /* if render time is very large we don't include it in the mean */
1162             if( current_render_time < p_vout->p->render_time +
1163                 VOUT_DISPLAY_DELAY )
1164             {
1165                 /* Store render time using a sliding mean weighting to
1166                  * current value in a 3 to 1 ratio*/
1167                 p_vout->p->render_time *= 3;
1168                 p_vout->p->render_time += current_render_time;
1169                 p_vout->p->render_time >>= 2;
1170             }
1171         }
1172
1173         /* Give back change lock */
1174         vlc_mutex_unlock( &p_vout->change_lock );
1175
1176         vlc_object_unlock( p_vout );
1177
1178         /* Sleep a while or until a given date */
1179         if( display_date != 0 )
1180         {
1181             /* If there are *vout* filters in the chain, better give them the picture
1182              * in advance */
1183             if( !p_vout->p->psz_filter_chain || !*p_vout->p->psz_filter_chain )
1184             {
1185                 mwait( display_date - VOUT_MWAIT_TOLERANCE );
1186             }
1187         }
1188         else
1189         {
1190             /* Wait until a frame is being sent or a spurious wakeup (not a problem here) */
1191             vlc_mutex_lock( &p_vout->picture_lock );
1192             vlc_cond_timedwait( &p_vout->p->picture_wait, &p_vout->picture_lock, current_date + VOUT_IDLE_SLEEP );
1193             vlc_mutex_unlock( &p_vout->picture_lock );
1194         }
1195
1196         /* On awakening, take back lock and send immediately picture
1197          * to display. */
1198         vlc_object_lock( p_vout );
1199         /* Note: vlc_object_alive() could be false here, and we
1200          * could be dead */
1201         vlc_mutex_lock( &p_vout->change_lock );
1202
1203         /*
1204          * Display the previously rendered picture
1205          */
1206         if( p_filtered_picture != NULL && p_directbuffer != NULL )
1207         {
1208             /* Display the direct buffer returned by vout_RenderPicture */
1209             if( p_vout->pf_display )
1210                 p_vout->pf_display( p_vout, p_directbuffer );
1211
1212             /* Tell the vout this was the last picture and that it does not
1213              * need to be forced anymore. */
1214             p_picture->b_force = false;
1215         }
1216
1217         /* Drop the filtered picture if created by video filters */
1218         if( p_filtered_picture != NULL && p_filtered_picture != p_picture )
1219         {
1220             vlc_mutex_lock( &p_vout->picture_lock );
1221             vout_UsePictureLocked( p_vout, p_filtered_picture );
1222             vlc_mutex_unlock( &p_vout->picture_lock );
1223         }
1224
1225         if( p_picture != NULL )
1226         {
1227             /* Reinitialize idle loop count */
1228             i_idle_loops = 0;
1229         }
1230
1231         /*
1232          * Check events and manage thread
1233          */
1234         if( p_vout->pf_manage && p_vout->pf_manage( p_vout ) )
1235         {
1236             /* A fatal error occurred, and the thread must terminate
1237              * immediately, without displaying anything - setting b_error to 1
1238              * causes the immediate end of the main while() loop. */
1239             // FIXME pf_end
1240             p_vout->b_error = 1;
1241             break;
1242         }
1243
1244         if( p_vout->i_changes & VOUT_SIZE_CHANGE )
1245         {
1246             /* this must only happen when the vout plugin is incapable of
1247              * rescaling the picture itself. In this case we need to destroy
1248              * the current picture buffers and recreate new ones with the right
1249              * dimensions */
1250             int i;
1251
1252             p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
1253
1254             assert( !p_vout->p->b_direct );
1255
1256             ChromaDestroy( p_vout );
1257
1258             vlc_mutex_lock( &p_vout->picture_lock );
1259
1260             p_vout->pf_end( p_vout );
1261
1262             p_vout->p->p_picture_displayed = NULL;
1263             for( i = 0; i < I_OUTPUTPICTURES; i++ )
1264                  p_vout->p_picture[ i ].i_status = FREE_PICTURE;
1265             vlc_cond_signal( &p_vout->p->picture_wait );
1266
1267             I_OUTPUTPICTURES = 0;
1268
1269             if( p_vout->pf_init( p_vout ) )
1270             {
1271                 msg_Err( p_vout, "cannot resize display" );
1272                 /* FIXME: pf_end will be called again in EndThread() */
1273                 p_vout->b_error = 1;
1274             }
1275
1276             vlc_mutex_unlock( &p_vout->picture_lock );
1277
1278             /* Need to reinitialise the chroma plugin. Since we might need
1279              * resizing too and it's not sure that we already had it,
1280              * recreate the chroma plugin chain from scratch. */
1281             /* dionoea */
1282             if( ChromaCreate( p_vout ) )
1283             {
1284                 msg_Err( p_vout, "WOW THIS SUCKS BIG TIME!!!!!" );
1285                 p_vout->b_error = 1;
1286             }
1287             if( p_vout->b_error )
1288                 break;
1289         }
1290
1291         if( p_vout->i_changes & VOUT_PICTURE_BUFFERS_CHANGE )
1292         {
1293             /* This happens when the picture buffers need to be recreated.
1294              * This is useful on multimonitor displays for instance.
1295              *
1296              * Warning: This only works when the vout creates only 1 picture
1297              * buffer!! */
1298             p_vout->i_changes &= ~VOUT_PICTURE_BUFFERS_CHANGE;
1299
1300             if( !p_vout->p->b_direct )
1301                 ChromaDestroy( p_vout );
1302
1303             vlc_mutex_lock( &p_vout->picture_lock );
1304
1305             p_vout->pf_end( p_vout );
1306
1307             I_OUTPUTPICTURES = I_RENDERPICTURES = 0;
1308
1309             p_vout->b_error = InitThread( p_vout );
1310             if( p_vout->b_error )
1311                 msg_Err( p_vout, "InitThread after VOUT_PICTURE_BUFFERS_CHANGE failed\n" );
1312
1313             vlc_cond_signal( &p_vout->p->picture_wait );
1314             vlc_mutex_unlock( &p_vout->picture_lock );
1315
1316             if( p_vout->b_error )
1317                 break;
1318         }
1319
1320         /* Check for "video filter2" changes */
1321         vlc_mutex_lock( &p_vout->p->vfilter_lock );
1322         if( p_vout->p->psz_vf2 )
1323         {
1324             es_format_t fmt;
1325
1326             es_format_Init( &fmt, VIDEO_ES, p_vout->fmt_render.i_chroma );
1327             fmt.video = p_vout->fmt_render;
1328             filter_chain_Reset( p_vout->p->p_vf2_chain, &fmt, &fmt );
1329
1330             if( filter_chain_AppendFromString( p_vout->p->p_vf2_chain,
1331                                                p_vout->p->psz_vf2 ) < 0 )
1332                 msg_Err( p_vout, "Video filter chain creation failed" );
1333
1334             free( p_vout->p->psz_vf2 );
1335             p_vout->p->psz_vf2 = NULL;
1336         }
1337         vlc_mutex_unlock( &p_vout->p->vfilter_lock );
1338     }
1339
1340     /*
1341      * Error loop - wait until the thread destruction is requested
1342      */
1343     if( p_vout->b_error )
1344         ErrorThread( p_vout );
1345
1346     /* End of thread */
1347     CleanThread( p_vout );
1348     EndThread( p_vout );
1349     vlc_mutex_unlock( &p_vout->change_lock );
1350
1351     vlc_object_unlock( p_vout );
1352     vlc_restorecancel( canc );
1353     return NULL;
1354 }
1355
1356 /*****************************************************************************
1357  * ErrorThread: RunThread() error loop
1358  *****************************************************************************
1359  * This function is called when an error occurred during thread main's loop.
1360  * The thread can still receive feed, but must be ready to terminate as soon
1361  * as possible.
1362  *****************************************************************************/
1363 static void ErrorThread( vout_thread_t *p_vout )
1364 {
1365     /* Wait until a `die' order */
1366     while( vlc_object_alive( p_vout ) )
1367         vlc_object_wait( p_vout );
1368 }
1369
1370 /*****************************************************************************
1371  * CleanThread: clean up after InitThread
1372  *****************************************************************************
1373  * This function is called after a sucessful
1374  * initialization. It frees all resources allocated by InitThread.
1375  * XXX You have to enter it with change_lock taken.
1376  *****************************************************************************/
1377 static void CleanThread( vout_thread_t *p_vout )
1378 {
1379     int     i_index;                                        /* index in heap */
1380
1381     if( !p_vout->p->b_direct )
1382         ChromaDestroy( p_vout );
1383
1384     /* Destroy all remaining pictures */
1385     for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++ )
1386     {
1387         if ( p_vout->p_picture[i_index].i_type == MEMORY_PICTURE )
1388         {
1389             free( p_vout->p_picture[i_index].p_data_orig );
1390         }
1391     }
1392
1393     /* Destroy translation tables */
1394     if( !p_vout->b_error )
1395         p_vout->pf_end( p_vout );
1396 }
1397
1398 /*****************************************************************************
1399  * EndThread: thread destruction
1400  *****************************************************************************
1401  * This function is called when the thread ends.
1402  * It frees all resources not allocated by InitThread.
1403  * XXX You have to enter it with change_lock taken.
1404  *****************************************************************************/
1405 static void EndThread( vout_thread_t *p_vout )
1406 {
1407 #ifdef STATS
1408     {
1409         struct tms cpu_usage;
1410         times( &cpu_usage );
1411
1412         msg_Dbg( p_vout, "cpu usage (user: %d, system: %d)",
1413                  cpu_usage.tms_utime, cpu_usage.tms_stime );
1414     }
1415 #endif
1416
1417     /* FIXME does that function *really* need to be called inside the thread ? */
1418
1419     /* Detach subpicture unit from both input and vout */
1420     spu_Attach( p_vout->p_spu, VLC_OBJECT(p_vout), false );
1421     vlc_object_detach( p_vout->p_spu );
1422
1423     /* Destroy the video filters2 */
1424     filter_chain_Delete( p_vout->p->p_vf2_chain );
1425 }
1426
1427 /* Thread helpers */
1428 static picture_t *ChromaGetPicture( filter_t *p_filter )
1429 {
1430     picture_t *p_pic = (picture_t *)p_filter->p_owner;
1431     p_filter->p_owner = NULL;
1432     return p_pic;
1433 }
1434
1435 static int ChromaCreate( vout_thread_t *p_vout )
1436 {
1437     static const char typename[] = "chroma";
1438     filter_t *p_chroma;
1439
1440     /* Choose the best module */
1441     p_chroma = p_vout->p->p_chroma =
1442         vlc_custom_create( p_vout, sizeof(filter_t), VLC_OBJECT_GENERIC,
1443                            typename );
1444
1445     vlc_object_attach( p_chroma, p_vout );
1446
1447     /* TODO: Set the fmt_in and fmt_out stuff here */
1448     p_chroma->fmt_in.video = p_vout->fmt_render;
1449     p_chroma->fmt_out.video = p_vout->fmt_out;
1450     VideoFormatImportRgb( &p_chroma->fmt_in.video, &p_vout->render );
1451     VideoFormatImportRgb( &p_chroma->fmt_out.video, &p_vout->output );
1452
1453     p_chroma->p_module = module_need( p_chroma, "video filter2", NULL, 0 );
1454
1455     if( p_chroma->p_module == NULL )
1456     {
1457         msg_Err( p_vout, "no chroma module for %4.4s to %4.4s i=%dx%d o=%dx%d",
1458                  (char*)&p_vout->render.i_chroma,
1459                  (char*)&p_vout->output.i_chroma,
1460                  p_chroma->fmt_in.video.i_width, p_chroma->fmt_in.video.i_height,
1461                  p_chroma->fmt_out.video.i_width, p_chroma->fmt_out.video.i_height
1462                  );
1463
1464         vlc_object_release( p_vout->p->p_chroma );
1465         p_vout->p->p_chroma = NULL;
1466
1467         return VLC_EGENERIC;
1468     }
1469     p_chroma->pf_vout_buffer_new = ChromaGetPicture;
1470     return VLC_SUCCESS;
1471 }
1472
1473 static void ChromaDestroy( vout_thread_t *p_vout )
1474 {
1475     assert( !p_vout->p->b_direct );
1476
1477     if( !p_vout->p->p_chroma )
1478         return;
1479
1480     module_unneed( p_vout->p->p_chroma, p_vout->p->p_chroma->p_module );
1481     vlc_object_release( p_vout->p->p_chroma );
1482     p_vout->p->p_chroma = NULL;
1483 }
1484
1485 /* following functions are local */
1486 static int ReduceHeight( int i_ratio )
1487 {
1488     int i_dummy = VOUT_ASPECT_FACTOR;
1489     int i_pgcd  = 1;
1490
1491     if( !i_ratio )
1492     {
1493         return i_pgcd;
1494     }
1495
1496     /* VOUT_ASPECT_FACTOR is (2^7 * 3^3 * 5^3), we just check for 2, 3 and 5 */
1497     while( !(i_ratio & 1) && !(i_dummy & 1) )
1498     {
1499         i_ratio >>= 1;
1500         i_dummy >>= 1;
1501         i_pgcd  <<= 1;
1502     }
1503
1504     while( !(i_ratio % 3) && !(i_dummy % 3) )
1505     {
1506         i_ratio /= 3;
1507         i_dummy /= 3;
1508         i_pgcd  *= 3;
1509     }
1510
1511     while( !(i_ratio % 5) && !(i_dummy % 5) )
1512     {
1513         i_ratio /= 5;
1514         i_dummy /= 5;
1515         i_pgcd  *= 5;
1516     }
1517
1518     return i_pgcd;
1519 }
1520
1521 static void AspectRatio( int i_aspect, int *i_aspect_x, int *i_aspect_y )
1522 {
1523     unsigned int i_pgcd = ReduceHeight( i_aspect );
1524     *i_aspect_x = i_aspect / i_pgcd;
1525     *i_aspect_y = VOUT_ASPECT_FACTOR / i_pgcd;
1526 }
1527
1528 /**
1529  * This function copies all RGB informations from a picture_heap_t into
1530  * a video_format_t
1531  */
1532 static void VideoFormatImportRgb( video_format_t *p_fmt, const picture_heap_t *p_heap )
1533 {
1534     p_fmt->i_rmask = p_heap->i_rmask;
1535     p_fmt->i_gmask = p_heap->i_gmask;
1536     p_fmt->i_bmask = p_heap->i_bmask;
1537     p_fmt->i_rrshift = p_heap->i_rrshift;
1538     p_fmt->i_lrshift = p_heap->i_lrshift;
1539     p_fmt->i_rgshift = p_heap->i_rgshift;
1540     p_fmt->i_lgshift = p_heap->i_lgshift;
1541     p_fmt->i_rbshift = p_heap->i_rbshift;
1542     p_fmt->i_lbshift = p_heap->i_lbshift;
1543 }
1544
1545 /**
1546  * This funtion copes all RGB informations from a video_format_t into
1547  * a picture_heap_t
1548  */
1549 static void VideoFormatExportRgb( const video_format_t *p_fmt, picture_heap_t *p_heap )
1550 {
1551     p_heap->i_rmask = p_fmt->i_rmask;
1552     p_heap->i_gmask = p_fmt->i_gmask;
1553     p_heap->i_bmask = p_fmt->i_bmask;
1554     p_heap->i_rrshift = p_fmt->i_rrshift;
1555     p_heap->i_lrshift = p_fmt->i_lrshift;
1556     p_heap->i_rgshift = p_fmt->i_rgshift;
1557     p_heap->i_lgshift = p_fmt->i_lgshift;
1558     p_heap->i_rbshift = p_fmt->i_rbshift;
1559     p_heap->i_lbshift = p_fmt->i_lbshift;
1560 }
1561
1562 /**
1563  * This function computes rgb shifts from masks
1564  */
1565 static void PictureHeapFixRgb( picture_heap_t *p_heap )
1566 {
1567     video_format_t fmt;
1568
1569     /* */
1570     fmt.i_chroma = p_heap->i_chroma;
1571     VideoFormatImportRgb( &fmt, p_heap );
1572
1573     /* */
1574     video_format_FixRgb( &fmt );
1575
1576     VideoFormatExportRgb( &fmt, p_heap );
1577 }
1578
1579 /*****************************************************************************
1580  * object variables callbacks: a bunch of object variables are used by the
1581  * interfaces to interact with the vout.
1582  *****************************************************************************/
1583 static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd,
1584                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1585 {
1586     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1587     input_thread_t *p_input;
1588     vlc_value_t val;
1589
1590     char *psz_mode = newval.psz_string;
1591     char *psz_filter, *psz_deinterlace = NULL;
1592     (void)psz_cmd; (void)oldval; (void)p_data;
1593
1594     var_Get( p_vout, "vout-filter", &val );
1595     psz_filter = val.psz_string;
1596     if( psz_filter ) psz_deinterlace = strstr( psz_filter, "deinterlace" );
1597
1598     if( !psz_mode || !*psz_mode )
1599     {
1600         if( psz_deinterlace )
1601         {
1602             char *psz_src = psz_deinterlace + sizeof("deinterlace") - 1;
1603             if( psz_src[0] == ':' ) psz_src++;
1604             memmove( psz_deinterlace, psz_src, strlen(psz_src) + 1 );
1605         }
1606     }
1607     else if( !psz_deinterlace )
1608     {
1609         psz_filter = realloc( psz_filter, strlen( psz_filter ) +
1610                               sizeof(":deinterlace") );
1611         if( psz_filter )
1612         {
1613             if( *psz_filter )
1614                 strcat( psz_filter, ":" );
1615             strcat( psz_filter, "deinterlace" );
1616         }
1617     }
1618
1619     p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1620                                                  FIND_PARENT );
1621     if( !p_input )
1622     {
1623         free( psz_filter );
1624         return VLC_EGENERIC;
1625     }
1626
1627     if( psz_mode && *psz_mode )
1628     {
1629         /* Modify input as well because the vout might have to be restarted */
1630         val.psz_string = psz_mode;
1631         var_Create( p_input, "deinterlace-mode", VLC_VAR_STRING );
1632         var_Set( p_input, "deinterlace-mode", val );
1633     }
1634     vlc_object_release( p_input );
1635
1636     val.b_bool = true;
1637     var_Set( p_vout, "intf-change", val );
1638
1639     val.psz_string = psz_filter;
1640     var_Set( p_vout, "vout-filter", val );
1641     free( psz_filter );
1642
1643     return VLC_SUCCESS;
1644 }
1645
1646 static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd,
1647                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1648 {
1649     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1650     input_thread_t *p_input;
1651     vlc_value_t val;
1652     (void)psz_cmd; (void)oldval; (void)p_data;
1653
1654     p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1655                                                  FIND_PARENT );
1656     if (!p_input)
1657     {
1658         msg_Err( p_vout, "Input not found" );
1659         return( VLC_EGENERIC );
1660     }
1661
1662     val.b_bool = true;
1663     var_Set( p_vout, "intf-change", val );
1664
1665     /* Modify input as well because the vout might have to be restarted */
1666     val.psz_string = newval.psz_string;
1667     var_Create( p_input, "vout-filter", VLC_VAR_STRING );
1668
1669     var_Set( p_input, "vout-filter", val );
1670
1671     /* Now restart current video stream */
1672     input_Control( p_input, INPUT_RESTART_ES, -VIDEO_ES );
1673     vlc_object_release( p_input );
1674
1675     return VLC_SUCCESS;
1676 }
1677
1678 /*****************************************************************************
1679  * Video Filter2 stuff
1680  *****************************************************************************/
1681 static int VideoFilter2Callback( vlc_object_t *p_this, char const *psz_cmd,
1682                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1683 {
1684     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1685     (void)psz_cmd; (void)oldval; (void)p_data;
1686
1687     vlc_mutex_lock( &p_vout->p->vfilter_lock );
1688     p_vout->p->psz_vf2 = strdup( newval.psz_string );
1689     vlc_mutex_unlock( &p_vout->p->vfilter_lock );
1690
1691     return VLC_SUCCESS;
1692 }
1693
1694 static void DisplayTitleOnOSD( vout_thread_t *p_vout )
1695 {
1696     input_thread_t *p_input;
1697     mtime_t i_now, i_stop;
1698
1699     if( !config_GetInt( p_vout, "osd" ) ) return;
1700
1701     p_input = (input_thread_t *)vlc_object_find( p_vout,
1702               VLC_OBJECT_INPUT, FIND_ANYWHERE );
1703     if( p_input )
1704     {
1705         i_now = mdate();
1706         i_stop = i_now + (mtime_t)(p_vout->p->i_title_timeout * 1000);
1707         char *psz_nowplaying =
1708             input_item_GetNowPlaying( input_GetItem( p_input ) );
1709         char *psz_artist = input_item_GetArtist( input_GetItem( p_input ) );
1710         char *psz_name = input_item_GetTitle( input_GetItem( p_input ) );
1711         if( EMPTY_STR( psz_name ) )
1712         {
1713             free( psz_name );
1714             psz_name = input_item_GetName( input_GetItem( p_input ) );
1715         }
1716         if( !EMPTY_STR( psz_nowplaying ) )
1717         {
1718             vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1719                                    psz_nowplaying, NULL,
1720                                    p_vout->p->i_title_position,
1721                                    30 + p_vout->fmt_in.i_width
1722                                       - p_vout->fmt_in.i_visible_width
1723                                       - p_vout->fmt_in.i_x_offset,
1724                                    20 + p_vout->fmt_in.i_y_offset,
1725                                    i_now, i_stop );
1726         }
1727         else if( !EMPTY_STR( psz_artist ) )
1728         {
1729             char *psz_string = NULL;
1730             if( asprintf( &psz_string, "%s - %s", psz_name, psz_artist ) != -1 )
1731             {
1732                 vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1733                                        psz_string, NULL,
1734                                        p_vout->p->i_title_position,
1735                                        30 + p_vout->fmt_in.i_width
1736                                           - p_vout->fmt_in.i_visible_width
1737                                           - p_vout->fmt_in.i_x_offset,
1738                                        20 + p_vout->fmt_in.i_y_offset,
1739                                        i_now, i_stop );
1740                 free( psz_string );
1741             }
1742         }
1743         else
1744         {
1745             vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1746                                    psz_name, NULL,
1747                                    p_vout->p->i_title_position,
1748                                    30 + p_vout->fmt_in.i_width
1749                                       - p_vout->fmt_in.i_visible_width
1750                                       - p_vout->fmt_in.i_x_offset,
1751                                    20 + p_vout->fmt_in.i_y_offset,
1752                                    i_now, i_stop );
1753         }
1754         vlc_object_release( p_input );
1755         free( psz_artist );
1756         free( psz_name );
1757         free( psz_nowplaying );
1758     }
1759 }
1760