]> git.sesse.net Git - vlc/blob - src/video_output/video_output.c
Compiler warnings rampage
[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 #include <vlc/vlc.h>
33
34 #include <stdlib.h>                                                /* free() */
35 #include <string.h>
36
37
38 #ifdef HAVE_SYS_TIMES_H
39 #   include <sys/times.h>
40 #endif
41
42 #include <vlc_vout.h>
43 #include <vlc_playlist.h>
44
45 #include <vlc_filter.h>
46 #include <vlc_osd.h>
47
48 #if defined( __APPLE__ )
49 /* Include darwin_specific.h here if needed */
50 #endif
51
52 /** FIXME This is quite ugly but needed while we don't have counters
53  * helpers */
54 #include "input/input_internal.h"
55
56 /*****************************************************************************
57  * Local prototypes
58  *****************************************************************************/
59 static int      InitThread        ( vout_thread_t * );
60 static void     RunThread         ( vout_thread_t * );
61 static void     ErrorThread       ( vout_thread_t * );
62 static void     EndThread         ( vout_thread_t * );
63 static void     DestroyThread     ( vout_thread_t * );
64
65 static void     AspectRatio       ( int, int *, int * );
66 static int      BinaryLog         ( uint32_t );
67 static void     MaskToShift       ( int *, int *, uint32_t );
68
69 /* Object variables callbacks */
70 static int DeinterlaceCallback( vlc_object_t *, char const *,
71                                 vlc_value_t, vlc_value_t, void * );
72 static int FilterCallback( vlc_object_t *, char const *,
73                            vlc_value_t, vlc_value_t, void * );
74 static int VideoFilter2Callback( vlc_object_t *, char const *,
75                                  vlc_value_t, vlc_value_t, void * );
76
77 /* From vout_intf.c */
78 int vout_Snapshot( vout_thread_t *, picture_t * );
79
80 /* Video filter2 parsing */
81 static int ParseVideoFilter2Chain( vout_thread_t *, char * );
82 static void RemoveVideoFilters2( vout_thread_t *p_vout );
83
84 /*****************************************************************************
85  * Video Filter2 functions
86  *****************************************************************************/
87 struct filter_owner_sys_t
88 {
89     vout_thread_t *p_vout;
90 };
91
92 static picture_t *video_new_buffer_filter( filter_t *p_filter )
93 {
94     picture_t *p_picture;
95     vout_thread_t *p_vout = p_filter->p_owner->p_vout;
96
97     p_picture = vout_CreatePicture( p_vout, 0, 0, 0 );
98
99     return p_picture;
100 }
101
102 static void video_del_buffer_filter( filter_t *p_filter, picture_t *p_pic )
103 {
104     vout_DestroyPicture( p_filter->p_owner->p_vout, p_pic );
105 }
106
107 /*****************************************************************************
108  * vout_Request: find a video output thread, create one, or destroy one.
109  *****************************************************************************
110  * This function looks for a video output thread matching the current
111  * properties. If not found, it spawns a new one.
112  *****************************************************************************/
113 vout_thread_t *__vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout,
114                                video_format_t *p_fmt )
115 {
116     if( !p_fmt )
117     {
118         /* Reattach video output to playlist before bailing out */
119         if( p_vout )
120         {
121             playlist_t  *p_playlist = pl_Yield( p_this );
122             spu_Attach( p_vout->p_spu, p_this, VLC_FALSE );
123             vlc_object_detach( p_vout );
124             vlc_object_attach( p_vout, p_playlist );
125             pl_Release( p_this );
126         }
127         return NULL;
128     }
129
130     /* If a video output was provided, lock it, otherwise look for one. */
131     if( p_vout )
132     {
133         vlc_object_yield( p_vout );
134     }
135     else
136     {
137         p_vout = vlc_object_find( p_this, VLC_OBJECT_VOUT, FIND_CHILD );
138
139         if( !p_vout )
140         {
141             playlist_t *p_playlist = pl_Yield( p_this );
142             vlc_mutex_lock( &p_playlist->gc_lock );
143             p_vout = vlc_object_find( p_playlist,
144                                       VLC_OBJECT_VOUT, FIND_CHILD );
145             /* only first children of p_input for unused vout */
146             if( p_vout && p_vout->p_parent != (vlc_object_t *)p_playlist )
147             {
148                 vlc_object_release( p_vout );
149                 p_vout = NULL;
150             }
151             if( p_vout )
152                 vlc_object_detach( p_vout );    /* Remove it from the GC */
153             vlc_mutex_unlock( &p_playlist->gc_lock );
154             pl_Release( p_this );
155         }
156     }
157
158     /* If we now have a video output, check it has the right properties */
159     if( p_vout )
160     {
161         char *psz_filter_chain;
162         vlc_value_t val;
163
164         /* We don't directly check for the "vout-filter" variable for obvious
165          * performance reasons. */
166         if( p_vout->b_filter_change )
167         {
168             var_Get( p_vout, "vout-filter", &val );
169             psz_filter_chain = val.psz_string;
170
171             if( psz_filter_chain && !*psz_filter_chain )
172             {
173                 free( psz_filter_chain );
174                 psz_filter_chain = NULL;
175             }
176             if( p_vout->psz_filter_chain && !*p_vout->psz_filter_chain )
177             {
178                 free( p_vout->psz_filter_chain );
179                 p_vout->psz_filter_chain = NULL;
180             }
181
182             if( !psz_filter_chain && !p_vout->psz_filter_chain )
183             {
184                 p_vout->b_filter_change = VLC_FALSE;
185             }
186
187             if( psz_filter_chain ) free( psz_filter_chain );
188         }
189
190         if( ( p_vout->fmt_render.i_width != p_fmt->i_width ) ||
191             ( p_vout->fmt_render.i_height != p_fmt->i_height ) ||
192             ( p_vout->fmt_render.i_chroma != p_fmt->i_chroma ) ||
193             ( p_vout->fmt_render.i_aspect != p_fmt->i_aspect ) ||
194             p_vout->b_filter_change )
195         {
196             /* We are not interested in this format, close this vout */
197             vlc_object_release( p_vout );
198             vout_Destroy( p_vout );
199             p_vout = NULL;
200         }
201         else
202         {
203             /* This video output is cool! Hijack it. */
204             spu_Attach( p_vout->p_spu, p_this, VLC_TRUE );
205             vlc_object_attach( p_vout, p_this );
206             vlc_object_release( p_vout );
207         }
208     }
209
210     if( !p_vout )
211     {
212         msg_Dbg( p_this, "no usable vout present, spawning one" );
213
214         p_vout = vout_Create( p_this, p_fmt );
215     }
216
217     return p_vout;
218 }
219
220 /*****************************************************************************
221  * vout_Create: creates a new video output thread
222  *****************************************************************************
223  * This function creates a new video output thread, and returns a pointer
224  * to its description. On error, it returns NULL.
225  *****************************************************************************/
226 vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt )
227 {
228     vout_thread_t  * p_vout;                            /* thread descriptor */
229     input_thread_t * p_input_thread;
230     int              i_index;                               /* loop variable */
231     vlc_value_t      val, text;
232
233     unsigned int i_width = p_fmt->i_width;
234     unsigned int i_height = p_fmt->i_height;
235     vlc_fourcc_t i_chroma = p_fmt->i_chroma;
236     unsigned int i_aspect = p_fmt->i_aspect;
237
238     config_chain_t *p_cfg;
239     char *psz_parser;
240     char *psz_name;
241
242     /* Allocate descriptor */
243     p_vout = vlc_object_create( p_parent, VLC_OBJECT_VOUT );
244     if( p_vout == NULL )
245     {
246         msg_Err( p_parent, "out of memory" );
247         return NULL;
248     }
249
250     /* Initialize pictures - translation tables and functions
251      * will be initialized later in InitThread */
252     for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++)
253     {
254         p_vout->p_picture[i_index].pf_lock = NULL;
255         p_vout->p_picture[i_index].pf_unlock = NULL;
256         p_vout->p_picture[i_index].i_status = FREE_PICTURE;
257         p_vout->p_picture[i_index].i_type   = EMPTY_PICTURE;
258         p_vout->p_picture[i_index].b_slow   = 0;
259     }
260
261     /* No images in the heap */
262     p_vout->i_heap_size = 0;
263
264     /* Initialize the rendering heap */
265     I_RENDERPICTURES = 0;
266
267     vlc_ureduce( &p_fmt->i_sar_num, &p_fmt->i_sar_den,
268                  p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 );
269     p_vout->fmt_render        = *p_fmt;   /* FIXME palette */
270     p_vout->fmt_in            = *p_fmt;   /* FIXME palette */
271
272     p_vout->render.i_width    = i_width;
273     p_vout->render.i_height   = i_height;
274     p_vout->render.i_chroma   = i_chroma;
275     p_vout->render.i_aspect   = i_aspect;
276
277     p_vout->render.i_rmask    = 0;
278     p_vout->render.i_gmask    = 0;
279     p_vout->render.i_bmask    = 0;
280
281     p_vout->render.i_last_used_pic = -1;
282     p_vout->render.b_allow_modify_pics = 1;
283
284     /* Zero the output heap */
285     I_OUTPUTPICTURES = 0;
286     p_vout->output.i_width    = 0;
287     p_vout->output.i_height   = 0;
288     p_vout->output.i_chroma   = 0;
289     p_vout->output.i_aspect   = 0;
290
291     p_vout->output.i_rmask    = 0;
292     p_vout->output.i_gmask    = 0;
293     p_vout->output.i_bmask    = 0;
294
295     /* Initialize misc stuff */
296     p_vout->i_changes    = 0;
297     p_vout->f_gamma      = 0;
298     p_vout->b_grayscale  = 0;
299     p_vout->b_info       = 0;
300     p_vout->b_interface  = 0;
301     p_vout->b_scale      = 1;
302     p_vout->b_fullscreen = 0;
303     p_vout->i_alignment  = 0;
304     p_vout->render_time  = 10;
305     p_vout->c_fps_samples = 0;
306     p_vout->b_filter_change = 0;
307     p_vout->pf_control = 0;
308     p_vout->p_parent_intf = 0;
309     p_vout->i_par_num = p_vout->i_par_den = 1;
310
311     /* Initialize locks */
312     vlc_mutex_init( p_vout, &p_vout->picture_lock );
313     vlc_mutex_init( p_vout, &p_vout->change_lock );
314     vlc_mutex_init( p_vout, &p_vout->vfilter_lock );
315
316     /* Mouse coordinates */
317     var_Create( p_vout, "mouse-x", VLC_VAR_INTEGER );
318     var_Create( p_vout, "mouse-y", VLC_VAR_INTEGER );
319     var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER );
320     var_Create( p_vout, "mouse-moved", VLC_VAR_BOOL );
321     var_Create( p_vout, "mouse-clicked", VLC_VAR_INTEGER );
322
323     /* Initialize subpicture unit */
324     p_vout->p_spu = spu_Create( p_vout );
325     spu_Attach( p_vout->p_spu, p_parent, VLC_TRUE );
326
327     /* Attach the new object now so we can use var inheritance below */
328     vlc_object_attach( p_vout, p_parent );
329
330     spu_Init( p_vout->p_spu );
331
332     /* Take care of some "interface/control" related initialisations */
333     vout_IntfInit( p_vout );
334
335     /* If the parent is not a VOUT object, that means we are at the start of
336      * the video output pipe */
337     if( p_parent->i_object_type != VLC_OBJECT_VOUT )
338     {
339         /* Look for the default filter configuration */
340         var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
341         var_Get( p_vout, "vout-filter", &val );
342         p_vout->psz_filter_chain = val.psz_string;
343
344         /* Apply video filter2 objects on the first vout */
345         var_Create( p_vout, "video-filter",
346                     VLC_VAR_STRING | VLC_VAR_DOINHERIT );
347         var_Get( p_vout, "video-filter", &val );
348         ParseVideoFilter2Chain( p_vout, val.psz_string );
349         free( val.psz_string );
350     }
351     else
352     {
353         /* continue the parent's filter chain */
354         char *psz_tmp;
355
356         /* Ugly hack to jump to our configuration chain */
357         p_vout->psz_filter_chain
358             = ((vout_thread_t *)p_parent)->psz_filter_chain;
359         p_vout->psz_filter_chain
360             = config_ChainCreate( &psz_tmp, &p_cfg, p_vout->psz_filter_chain );
361         config_ChainDestroy( p_cfg );
362         free( psz_tmp );
363
364         /* Create a video filter2 var ... but don't inherit values */
365         var_Create( p_vout, "video-filter", VLC_VAR_STRING );
366         ParseVideoFilter2Chain( p_vout, NULL );
367     }
368
369     var_AddCallback( p_vout, "video-filter", VideoFilter2Callback, NULL );
370     p_vout->b_vfilter_change = VLC_TRUE;
371     p_vout->i_vfilters = 0;
372
373     /* Choose the video output module */
374     if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain )
375     {
376         var_Create( p_vout, "vout", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
377         var_Get( p_vout, "vout", &val );
378         psz_parser = val.psz_string;
379     }
380     else
381     {
382         psz_parser = strdup( p_vout->psz_filter_chain );
383     }
384
385     /* Create the vout thread */
386     config_ChainCreate( &psz_name, &p_cfg, psz_parser );
387     free( psz_parser );
388     p_vout->p_cfg = p_cfg;
389     p_vout->p_module = module_Need( p_vout,
390         ( p_vout->psz_filter_chain && *p_vout->psz_filter_chain ) ?
391         "video filter" : "video output", psz_name, 0 );
392     free( psz_name );
393
394     if( p_vout->p_module == NULL )
395     {
396         msg_Err( p_vout, "no suitable vout module" );
397         vlc_object_detach( p_vout );
398         vlc_object_destroy( p_vout );
399         return NULL;
400     }
401
402     /* Create a few object variables for interface interaction */
403     var_Create( p_vout, "deinterlace", VLC_VAR_STRING | VLC_VAR_HASCHOICE );
404     text.psz_string = _("Deinterlace");
405     var_Change( p_vout, "deinterlace", VLC_VAR_SETTEXT, &text, NULL );
406     val.psz_string = (char *)""; text.psz_string = _("Disable");
407     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
408     val.psz_string = (char *)"discard"; text.psz_string = _("Discard");
409     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
410     val.psz_string = (char *)"blend"; text.psz_string = _("Blend");
411     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
412     val.psz_string = (char *)"mean"; text.psz_string = _("Mean");
413     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
414     val.psz_string = (char *)"bob"; text.psz_string = _("Bob");
415     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
416     val.psz_string = (char *)"linear"; text.psz_string = _("Linear");
417     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
418     val.psz_string = (char *)"x"; text.psz_string = (char *)"X";
419     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
420
421     if( var_Get( p_vout, "deinterlace-mode", &val ) == VLC_SUCCESS )
422     {
423         var_Set( p_vout, "deinterlace", val );
424         if( val.psz_string ) free( val.psz_string );
425     }
426     var_AddCallback( p_vout, "deinterlace", DeinterlaceCallback, NULL );
427
428
429     var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
430     text.psz_string = _("Filters");
431     var_Change( p_vout, "vout-filter", VLC_VAR_SETTEXT, &text, NULL );
432     var_AddCallback( p_vout, "vout-filter", FilterCallback, NULL );
433
434     /* Calculate delay created by internal caching */
435     p_input_thread = (input_thread_t *)vlc_object_find( p_vout,
436                                            VLC_OBJECT_INPUT, FIND_ANYWHERE );
437     if( p_input_thread )
438     {
439         p_vout->i_pts_delay = p_input_thread->i_pts_delay;
440         vlc_object_release( p_input_thread );
441     }
442     else
443     {
444         p_vout->i_pts_delay = DEFAULT_PTS_DELAY;
445     }
446
447     if( vlc_thread_create( p_vout, "video output", RunThread,
448                            VLC_THREAD_PRIORITY_OUTPUT, VLC_TRUE ) )
449     {
450         msg_Err( p_vout, "out of memory" );
451         module_Unneed( p_vout, p_vout->p_module );
452         vlc_object_detach( p_vout );
453         vlc_object_destroy( p_vout );
454         return NULL;
455     }
456
457     if( p_vout->b_error )
458     {
459         msg_Err( p_vout, "video output creation failed" );
460
461         /* Make sure the thread is destroyed */
462         p_vout->b_die = VLC_TRUE;
463
464         vlc_thread_join( p_vout );
465
466         vlc_object_detach( p_vout );
467         vlc_object_destroy( p_vout );
468         return NULL;
469     }
470
471     return p_vout;
472 }
473
474 /*****************************************************************************
475  * vout_Destroy: destroys a previously created video output
476  *****************************************************************************
477  * Destroy a terminated thread.
478  * The function will request a destruction of the specified thread. If pi_error
479  * is NULL, it will return once the thread is destroyed. Else, it will be
480  * update using one of the THREAD_* constants.
481  *****************************************************************************/
482 void vout_Destroy( vout_thread_t *p_vout )
483 {
484     vout_thread_t *p_another_vout;
485     playlist_t *p_playlist = pl_Yield( p_vout );
486
487     /* Request thread destruction */
488     p_vout->b_die = VLC_TRUE;
489     vlc_thread_join( p_vout );
490
491     var_Destroy( p_vout, "intf-change" );
492
493     if( p_vout->psz_filter_chain ) free( p_vout->psz_filter_chain );
494
495     config_ChainDestroy( p_vout->p_cfg );
496
497     /* Free structure */
498     vlc_object_destroy( p_vout );
499 #ifndef __APPLE__
500     /* This is a dirty hack for mostly Linux, where there is no way to get the GUI
501        back if you closed it while playing video. This is solved in Mac OS X,
502        where we have this novelty called menubar, that will always allow you access
503        to the applications main functionality. They should try that on linux sometime */
504     p_another_vout = vlc_object_find( p_playlist,
505                                       VLC_OBJECT_VOUT, FIND_ANYWHERE );
506     if( p_another_vout == NULL )
507     {
508         vlc_value_t val;
509         val.b_bool = VLC_TRUE;
510         var_Set( p_playlist, "intf-show", val );
511     }
512     else
513     {
514         vlc_object_release( p_another_vout );
515     }
516 #endif
517     vlc_object_release( p_playlist );
518 }
519
520 /*****************************************************************************
521  * InitThread: initialize video output thread
522  *****************************************************************************
523  * This function is called from RunThread and performs the second step of the
524  * initialization. It returns 0 on success. Note that the thread's flag are not
525  * modified inside this function.
526  *****************************************************************************/
527 static int InitThread( vout_thread_t *p_vout )
528 {
529     int i, i_aspect_x, i_aspect_y;
530
531     vlc_mutex_lock( &p_vout->change_lock );
532
533 #ifdef STATS
534     p_vout->c_loops = 0;
535 #endif
536
537     /* Initialize output method, it allocates direct buffers for us */
538     if( p_vout->pf_init( p_vout ) )
539     {
540         vlc_mutex_unlock( &p_vout->change_lock );
541         return VLC_EGENERIC;
542     }
543
544     if( !I_OUTPUTPICTURES )
545     {
546         msg_Err( p_vout, "plugin was unable to allocate at least "
547                          "one direct buffer" );
548         p_vout->pf_end( p_vout );
549         vlc_mutex_unlock( &p_vout->change_lock );
550         return VLC_EGENERIC;
551     }
552
553     if( I_OUTPUTPICTURES > VOUT_MAX_PICTURES )
554     {
555         msg_Err( p_vout, "plugin allocated too many direct buffers, "
556                          "our internal buffers must have overflown." );
557         p_vout->pf_end( p_vout );
558         vlc_mutex_unlock( &p_vout->change_lock );
559         return VLC_EGENERIC;
560     }
561
562     msg_Dbg( p_vout, "got %i direct buffer(s)", I_OUTPUTPICTURES );
563
564     AspectRatio( p_vout->fmt_render.i_aspect, &i_aspect_x, &i_aspect_y );
565
566     msg_Dbg( p_vout, "picture in %ix%i (%i,%i,%ix%i), "
567              "chroma %4.4s, ar %i:%i, sar %i:%i",
568              p_vout->fmt_render.i_width, p_vout->fmt_render.i_height,
569              p_vout->fmt_render.i_x_offset, p_vout->fmt_render.i_y_offset,
570              p_vout->fmt_render.i_visible_width,
571              p_vout->fmt_render.i_visible_height,
572              (char*)&p_vout->fmt_render.i_chroma,
573              i_aspect_x, i_aspect_y,
574              p_vout->fmt_render.i_sar_num, p_vout->fmt_render.i_sar_den );
575
576     AspectRatio( p_vout->fmt_in.i_aspect, &i_aspect_x, &i_aspect_y );
577
578     msg_Dbg( p_vout, "picture user %ix%i (%i,%i,%ix%i), "
579              "chroma %4.4s, ar %i:%i, sar %i:%i",
580              p_vout->fmt_in.i_width, p_vout->fmt_in.i_height,
581              p_vout->fmt_in.i_x_offset, p_vout->fmt_in.i_y_offset,
582              p_vout->fmt_in.i_visible_width,
583              p_vout->fmt_in.i_visible_height,
584              (char*)&p_vout->fmt_in.i_chroma,
585              i_aspect_x, i_aspect_y,
586              p_vout->fmt_in.i_sar_num, p_vout->fmt_in.i_sar_den );
587
588     if( !p_vout->fmt_out.i_width || !p_vout->fmt_out.i_height )
589     {
590         p_vout->fmt_out.i_width = p_vout->fmt_out.i_visible_width =
591             p_vout->output.i_width;
592         p_vout->fmt_out.i_height = p_vout->fmt_out.i_visible_height =
593             p_vout->output.i_height;
594         p_vout->fmt_out.i_x_offset =  p_vout->fmt_out.i_y_offset = 0;
595
596         p_vout->fmt_out.i_aspect = p_vout->output.i_aspect;
597         p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
598     }
599     if( !p_vout->fmt_out.i_sar_num || !p_vout->fmt_out.i_sar_num )
600     {
601         p_vout->fmt_out.i_sar_num = p_vout->fmt_out.i_aspect *
602             p_vout->fmt_out.i_height;
603         p_vout->fmt_out.i_sar_den = VOUT_ASPECT_FACTOR *
604             p_vout->fmt_out.i_width;
605     }
606
607     vlc_ureduce( &p_vout->fmt_out.i_sar_num, &p_vout->fmt_out.i_sar_den,
608                  p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den, 0 );
609
610     AspectRatio( p_vout->fmt_out.i_aspect, &i_aspect_x, &i_aspect_y );
611
612     msg_Dbg( p_vout, "picture out %ix%i (%i,%i,%ix%i), "
613              "chroma %4.4s, ar %i:%i, sar %i:%i",
614              p_vout->fmt_out.i_width, p_vout->fmt_out.i_height,
615              p_vout->fmt_out.i_x_offset, p_vout->fmt_out.i_y_offset,
616              p_vout->fmt_out.i_visible_width,
617              p_vout->fmt_out.i_visible_height,
618              (char*)&p_vout->fmt_out.i_chroma,
619              i_aspect_x, i_aspect_y,
620              p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den );
621
622     /* Calculate shifts from system-updated masks */
623     MaskToShift( &p_vout->output.i_lrshift, &p_vout->output.i_rrshift,
624                  p_vout->output.i_rmask );
625     MaskToShift( &p_vout->output.i_lgshift, &p_vout->output.i_rgshift,
626                  p_vout->output.i_gmask );
627     MaskToShift( &p_vout->output.i_lbshift, &p_vout->output.i_rbshift,
628                  p_vout->output.i_bmask );
629
630     /* Check whether we managed to create direct buffers similar to
631      * the render buffers, ie same size and chroma */
632     if( ( p_vout->output.i_width == p_vout->render.i_width )
633      && ( p_vout->output.i_height == p_vout->render.i_height )
634      && ( vout_ChromaCmp( p_vout->output.i_chroma, p_vout->render.i_chroma ) ) )
635     {
636         /* Cool ! We have direct buffers, we can ask the decoder to
637          * directly decode into them ! Map the first render buffers to
638          * the first direct buffers, but keep the first direct buffer
639          * for memcpy operations */
640         p_vout->b_direct = 1;
641
642         for( i = 1; i < VOUT_MAX_PICTURES; i++ )
643         {
644             if( p_vout->p_picture[ i ].i_type != DIRECT_PICTURE &&
645                 I_RENDERPICTURES >= VOUT_MIN_DIRECT_PICTURES - 1 &&
646                 p_vout->p_picture[ i - 1 ].i_type == DIRECT_PICTURE )
647             {
648                 /* We have enough direct buffers so there's no need to
649                  * try to use system memory buffers. */
650                 break;
651             }
652             PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
653             I_RENDERPICTURES++;
654         }
655
656         msg_Dbg( p_vout, "direct render, mapping "
657                  "render pictures 0-%i to system pictures 1-%i",
658                  VOUT_MAX_PICTURES - 2, VOUT_MAX_PICTURES - 1 );
659     }
660     else
661     {
662         /* Rats... Something is wrong here, we could not find an output
663          * plugin able to directly render what we decode. See if we can
664          * find a chroma plugin to do the conversion */
665         p_vout->b_direct = 0;
666
667         /* Choose the best module */
668         p_vout->chroma.p_module = module_Need( p_vout, "chroma", NULL, 0 );
669
670         if( p_vout->chroma.p_module == NULL )
671         {
672             msg_Err( p_vout, "no chroma module for %4.4s to %4.4s",
673                      (char*)&p_vout->render.i_chroma,
674                      (char*)&p_vout->output.i_chroma );
675             p_vout->pf_end( p_vout );
676             vlc_mutex_unlock( &p_vout->change_lock );
677             return VLC_EGENERIC;
678         }
679
680         msg_Dbg( p_vout, "indirect render, mapping "
681                  "render pictures 0-%i to system pictures %i-%i",
682                  VOUT_MAX_PICTURES - 1, I_OUTPUTPICTURES,
683                  I_OUTPUTPICTURES + VOUT_MAX_PICTURES - 1 );
684
685         /* Append render buffers after the direct buffers */
686         for( i = I_OUTPUTPICTURES; i < 2 * VOUT_MAX_PICTURES; i++ )
687         {
688             PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
689             I_RENDERPICTURES++;
690
691             /* Check if we have enough render pictures */
692             if( I_RENDERPICTURES == VOUT_MAX_PICTURES )
693                 break;
694         }
695     }
696
697     /* Link pictures back to their heap */
698     for( i = 0 ; i < I_RENDERPICTURES ; i++ )
699     {
700         PP_RENDERPICTURE[ i ]->p_heap = &p_vout->render;
701     }
702
703     for( i = 0 ; i < I_OUTPUTPICTURES ; i++ )
704     {
705         PP_OUTPUTPICTURE[ i ]->p_heap = &p_vout->output;
706     }
707
708 /* XXX XXX mark thread ready */
709     return VLC_SUCCESS;
710 }
711
712 /*****************************************************************************
713  * RunThread: video output thread
714  *****************************************************************************
715  * Video output thread. This function does only returns when the thread is
716  * terminated. It handles the pictures arriving in the video heap and the
717  * display device events.
718  *****************************************************************************/
719 static void RunThread( vout_thread_t *p_vout)
720 {
721     int             i_index;                                /* index in heap */
722     int             i_idle_loops = 0;  /* loops without displaying a picture */
723     mtime_t         current_date;                            /* current date */
724     mtime_t         display_date;                            /* display date */
725
726     picture_t *     p_picture;                            /* picture pointer */
727     picture_t *     p_last_picture = NULL;                   /* last picture */
728     picture_t *     p_directbuffer;              /* direct buffer to display */
729
730     subpicture_t *  p_subpic = NULL;                   /* subpicture pointer */
731
732     input_thread_t *p_input = NULL ;           /* Parent input, if it exists */
733
734     vlc_value_t     val;
735     vlc_bool_t      b_drop_late;
736
737     int             i_displayed = 0, i_lost = 0, i_loops = 0;
738
739     /*
740      * Initialize thread
741      */
742     p_vout->b_error = InitThread( p_vout );
743
744     var_Create( p_vout, "drop-late-frames", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
745     var_Get( p_vout, "drop-late-frames", &val );
746     b_drop_late = val.b_bool;
747
748     /* signal the creation of the vout */
749     vlc_thread_ready( p_vout );
750
751     if( p_vout->b_error )
752     {
753         /* Destroy thread structures allocated by Create and InitThread */
754         DestroyThread( p_vout );
755         return;
756     }
757
758     /*
759      * Main loop - it is not executed if an error occurred during
760      * initialization
761      */
762     while( (!p_vout->b_die) && (!p_vout->b_error) )
763     {
764         /* Initialize loop variables */
765         p_picture = NULL;
766         display_date = 0;
767         current_date = mdate();
768
769         if( p_input && p_input->b_die )
770         {
771             vlc_object_release( p_input );
772             p_input = NULL;
773         }
774
775         i_loops++;
776         if( i_loops % 20 == 0 )
777         {
778             if( !p_input )
779             {
780                 p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT,
781                                            FIND_PARENT );
782             }
783             if( p_input )
784             {
785                 vlc_mutex_lock( &p_input->p->counters.counters_lock );
786                 stats_UpdateInteger( p_vout, p_input->p->counters.p_lost_pictures,
787                                      i_lost , NULL);
788                 stats_UpdateInteger( p_vout,
789                                      p_input->p->counters.p_displayed_pictures,
790                                      i_displayed , NULL);
791                 i_displayed = i_lost = 0;
792                 vlc_mutex_unlock( &p_input->p->counters.counters_lock );
793             }
794         }
795 #if 0
796         p_vout->c_loops++;
797         if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
798         {
799             msg_Dbg( p_vout, "picture heap: %d/%d",
800                      I_RENDERPICTURES, p_vout->i_heap_size );
801         }
802 #endif
803
804         /*
805          * Find the picture to display (the one with the earliest date).
806          * This operation does not need lock, since only READY_PICTUREs
807          * are handled. */
808         for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
809         {
810             if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
811                 && ( (p_picture == NULL) ||
812                      (PP_RENDERPICTURE[i_index]->date < display_date) ) )
813             {
814                 p_picture = PP_RENDERPICTURE[i_index];
815                 display_date = p_picture->date;
816             }
817         }
818
819         if( p_picture )
820         {
821             /* If we met the last picture, parse again to see whether there is
822              * a more appropriate one. */
823             if( p_picture == p_last_picture )
824             {
825                 for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
826                 {
827                     if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
828                         && (PP_RENDERPICTURE[i_index] != p_last_picture)
829                         && ((p_picture == p_last_picture) ||
830                             (PP_RENDERPICTURE[i_index]->date < display_date)) )
831                     {
832                         p_picture = PP_RENDERPICTURE[i_index];
833                         display_date = p_picture->date;
834                     }
835                 }
836             }
837
838             /* If we found better than the last picture, destroy it */
839             if( p_last_picture && p_picture != p_last_picture )
840             {
841                 vlc_mutex_lock( &p_vout->picture_lock );
842                 if( p_last_picture->i_refcount )
843                 {
844                     p_last_picture->i_status = DISPLAYED_PICTURE;
845                 }
846                 else
847                 {
848                     p_last_picture->i_status = DESTROYED_PICTURE;
849                     p_vout->i_heap_size--;
850                 }
851                 vlc_mutex_unlock( &p_vout->picture_lock );
852                 p_last_picture = NULL;
853             }
854
855             /* Compute FPS rate */
856             p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ]
857                 = display_date;
858
859             if( !p_picture->b_force &&
860                 p_picture != p_last_picture &&
861                 display_date < current_date + p_vout->render_time &&
862                 b_drop_late )
863             {
864                 /* Picture is late: it will be destroyed and the thread
865                  * will directly choose the next picture */
866                 vlc_mutex_lock( &p_vout->picture_lock );
867                 if( p_picture->i_refcount )
868                 {
869                     /* Pretend we displayed the picture, but don't destroy
870                      * it since the decoder might still need it. */
871                     p_picture->i_status = DISPLAYED_PICTURE;
872                 }
873                 else
874                 {
875                     /* Destroy the picture without displaying it */
876                     p_picture->i_status = DESTROYED_PICTURE;
877                     p_vout->i_heap_size--;
878                 }
879                 msg_Warn( p_vout, "late picture skipped ("I64Fd")",
880                                   current_date - display_date );
881                 i_lost++;
882                 vlc_mutex_unlock( &p_vout->picture_lock );
883
884                 continue;
885             }
886
887             if( display_date >
888                 current_date + p_vout->i_pts_delay + VOUT_BOGUS_DELAY )
889             {
890                 /* Picture is waaay too early: it will be destroyed */
891                 vlc_mutex_lock( &p_vout->picture_lock );
892                 if( p_picture->i_refcount )
893                 {
894                     /* Pretend we displayed the picture, but don't destroy
895                      * it since the decoder might still need it. */
896                     p_picture->i_status = DISPLAYED_PICTURE;
897                 }
898                 else
899                 {
900                     /* Destroy the picture without displaying it */
901                     p_picture->i_status = DESTROYED_PICTURE;
902                     p_vout->i_heap_size--;
903                 }
904                 i_lost++;
905                 msg_Warn( p_vout, "vout warning: early picture skipped "
906                           "("I64Fd")", display_date - current_date
907                           - p_vout->i_pts_delay );
908                 vlc_mutex_unlock( &p_vout->picture_lock );
909
910                 continue;
911             }
912
913             if( display_date > current_date + VOUT_DISPLAY_DELAY )
914             {
915                 /* A picture is ready to be rendered, but its rendering date
916                  * is far from the current one so the thread will perform an
917                  * empty loop as if no picture were found. The picture state
918                  * is unchanged */
919                 p_picture    = NULL;
920                 display_date = 0;
921             }
922             else if( p_picture == p_last_picture )
923             {
924                 /* We are asked to repeat the previous picture, but we first
925                  * wait for a couple of idle loops */
926                 if( i_idle_loops < 4 )
927                 {
928                     p_picture    = NULL;
929                     display_date = 0;
930                 }
931                 else
932                 {
933                     /* We set the display date to something high, otherwise
934                      * we'll have lots of problems with late pictures */
935                     display_date = current_date + p_vout->render_time;
936                 }
937             }
938         }
939
940         if( p_picture == NULL )
941         {
942             i_idle_loops++;
943         }
944
945         /* Video Filter2 stuff */
946         if( p_vout->b_vfilter_change == VLC_TRUE )
947         {
948             int i;
949             vlc_mutex_lock( &p_vout->vfilter_lock );
950             RemoveVideoFilters2( p_vout );
951             for( i = 0; i < p_vout->i_vfilters_cfg; i++ )
952             {
953                 filter_t *p_vfilter =
954                     p_vout->pp_vfilters[p_vout->i_vfilters] =
955                         vlc_object_create( p_vout, VLC_OBJECT_FILTER );
956
957                 vlc_object_attach( p_vfilter, p_vout );
958
959                 p_vfilter->pf_vout_buffer_new = video_new_buffer_filter;
960                 p_vfilter->pf_vout_buffer_del = video_del_buffer_filter;
961
962                 if( !p_vout->i_vfilters )
963                 {
964                     p_vfilter->fmt_in.video = p_vout->fmt_render;
965                 }
966                 else
967                 {
968                     p_vfilter->fmt_in.video = (p_vfilter-1)->fmt_out.video;
969                 }
970                 /* TODO: one day filters in the middle of the chain might
971                  * have a different fmt_out.video than fmt_render ... */
972                 p_vfilter->fmt_out.video = p_vout->fmt_render;
973
974                 p_vfilter->p_cfg = p_vout->p_vfilters_cfg[i];
975                 p_vfilter->p_module = module_Need( p_vfilter, "video filter2",
976                                                  p_vout->psz_vfilters[i], 0 );
977
978                 if( p_vfilter->p_module )
979                 {
980                     p_vfilter->p_owner =
981                         malloc( sizeof( filter_owner_sys_t ) );
982                     p_vfilter->p_owner->p_vout = p_vout;
983                     p_vout->i_vfilters++;
984                     msg_Dbg( p_vout, "video filter found (%s)",
985                              p_vout->psz_vfilters[i] );
986                 }
987                 else
988                 {
989                     msg_Err( p_vout, "no video filter found (%s)",
990                              p_vout->psz_vfilters[i] );
991                     vlc_object_detach( p_vfilter );
992                     vlc_object_destroy( p_vfilter );
993                 }
994             }
995             p_vout->b_vfilter_change = VLC_FALSE;
996             vlc_mutex_unlock( &p_vout->vfilter_lock );
997         }
998
999         if( p_picture )
1000         {
1001             int i;
1002             for( i = 0; i < p_vout->i_vfilters; i++ )
1003             {
1004                 picture_t *p_old = p_picture;
1005                 p_picture  = p_vout->pp_vfilters[i]->pf_video_filter(
1006                                  p_vout->pp_vfilters[i], p_picture );
1007                 if( !p_picture )
1008                 {
1009                     break;
1010                 }
1011                 /* FIXME: this is kind of wrong
1012                  * if you have 2 or more vfilters and the 2nd breaks,
1013                  * on the next loop the 1st one will be applied again */
1014
1015                 /* if p_old and p_picture are the same (ie the filter
1016                  * worked on the old picture), then following code is
1017                  * still alright since i_status gets changed back to
1018                  * the right value */
1019                 if( p_old->i_refcount )
1020                 {
1021                     p_old->i_status = DISPLAYED_PICTURE;
1022                 }
1023                 else
1024                 {
1025                     p_old->i_status = DESTROYED_PICTURE;
1026                 }
1027                 p_picture->i_status = READY_PICTURE;
1028             }
1029         }
1030
1031         if( p_picture && p_vout->b_snapshot )
1032         {
1033             p_vout->b_snapshot = VLC_FALSE;
1034             vout_Snapshot( p_vout, p_picture );
1035         }
1036
1037         /*
1038          * Check for subpictures to display
1039          */
1040         if( display_date > 0 )
1041         {
1042             if( !p_input )
1043             {
1044                 p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT,
1045                                            FIND_PARENT );
1046             }
1047             p_subpic = spu_SortSubpictures( p_vout->p_spu, display_date,
1048             p_input ? var_GetBool( p_input, "state" ) == PAUSE_S : VLC_FALSE );
1049         }
1050
1051         /*
1052          * Perform rendering
1053          */
1054         i_displayed++;
1055         p_directbuffer = vout_RenderPicture( p_vout, p_picture, p_subpic );
1056
1057         /*
1058          * Call the plugin-specific rendering method if there is one
1059          */
1060         if( p_picture != NULL && p_directbuffer != NULL && p_vout->pf_render )
1061         {
1062             /* Render the direct buffer returned by vout_RenderPicture */
1063             p_vout->pf_render( p_vout, p_directbuffer );
1064         }
1065
1066         /*
1067          * Sleep, wake up
1068          */
1069         if( display_date != 0 && p_directbuffer != NULL )
1070         {
1071             mtime_t current_render_time = mdate() - current_date;
1072             /* if render time is very large we don't include it in the mean */
1073             if( current_render_time < p_vout->render_time +
1074                 VOUT_DISPLAY_DELAY )
1075             {
1076                 /* Store render time using a sliding mean weighting to
1077                  * current value in a 3 to 1 ratio*/
1078                 p_vout->render_time *= 3;
1079                 p_vout->render_time += current_render_time;
1080                 p_vout->render_time >>= 2;
1081             }
1082         }
1083
1084         /* Give back change lock */
1085         vlc_mutex_unlock( &p_vout->change_lock );
1086
1087         /* Sleep a while or until a given date */
1088         if( display_date != 0 )
1089         {
1090             /* If there are filters in the chain, better give them the picture
1091              * in advance */
1092             if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain )
1093             {
1094                 mwait( display_date - VOUT_MWAIT_TOLERANCE );
1095             }
1096         }
1097         else
1098         {
1099             msleep( VOUT_IDLE_SLEEP );
1100         }
1101
1102         /* On awakening, take back lock and send immediately picture
1103          * to display. */
1104         vlc_mutex_lock( &p_vout->change_lock );
1105
1106         /*
1107          * Display the previously rendered picture
1108          */
1109         if( p_picture != NULL && p_directbuffer != NULL )
1110         {
1111             /* Display the direct buffer returned by vout_RenderPicture */
1112             if( p_vout->pf_display )
1113             {
1114                 p_vout->pf_display( p_vout, p_directbuffer );
1115             }
1116
1117             /* Tell the vout this was the last picture and that it does not
1118              * need to be forced anymore. */
1119             p_last_picture = p_picture;
1120             p_last_picture->b_force = 0;
1121         }
1122
1123         if( p_picture != NULL )
1124         {
1125             /* Reinitialize idle loop count */
1126             i_idle_loops = 0;
1127         }
1128
1129         /*
1130          * Check events and manage thread
1131          */
1132         if( p_vout->pf_manage && p_vout->pf_manage( p_vout ) )
1133         {
1134             /* A fatal error occurred, and the thread must terminate
1135              * immediately, without displaying anything - setting b_error to 1
1136              * causes the immediate end of the main while() loop. */
1137             p_vout->b_error = 1;
1138         }
1139
1140         if( p_vout->i_changes & VOUT_SIZE_CHANGE )
1141         {
1142             /* this must only happen when the vout plugin is incapable of
1143              * rescaling the picture itself. In this case we need to destroy
1144              * the current picture buffers and recreate new ones with the right
1145              * dimensions */
1146             int i;
1147
1148             p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
1149
1150             p_vout->pf_end( p_vout );
1151             for( i = 0; i < I_OUTPUTPICTURES; i++ )
1152                  p_vout->p_picture[ i ].i_status = FREE_PICTURE;
1153
1154             I_OUTPUTPICTURES = 0;
1155             if( p_vout->pf_init( p_vout ) )
1156             {
1157                 msg_Err( p_vout, "cannot resize display" );
1158                 /* FIXME: pf_end will be called again in EndThread() */
1159                 p_vout->b_error = 1;
1160             }
1161
1162             /* Need to reinitialise the chroma plugin */
1163             if( p_vout->chroma.p_module )
1164             {
1165                 if( p_vout->chroma.p_module->pf_deactivate )
1166                     p_vout->chroma.p_module->pf_deactivate( VLC_OBJECT(p_vout) );
1167                 p_vout->chroma.p_module->pf_activate( VLC_OBJECT(p_vout) );
1168             }
1169         }
1170
1171         if( p_vout->i_changes & VOUT_PICTURE_BUFFERS_CHANGE )
1172         {
1173             /* This happens when the picture buffers need to be recreated.
1174              * This is useful on multimonitor displays for instance.
1175              *
1176              * Warning: This only works when the vout creates only 1 picture
1177              * buffer!! */
1178             p_vout->i_changes &= ~VOUT_PICTURE_BUFFERS_CHANGE;
1179
1180             if( !p_vout->b_direct )
1181             {
1182                 module_Unneed( p_vout, p_vout->chroma.p_module );
1183             }
1184
1185             vlc_mutex_lock( &p_vout->picture_lock );
1186
1187             p_vout->pf_end( p_vout );
1188
1189             I_OUTPUTPICTURES = I_RENDERPICTURES = 0;
1190
1191             p_vout->b_error = InitThread( p_vout );
1192
1193             vlc_mutex_unlock( &p_vout->picture_lock );
1194         }
1195     }
1196
1197     if( p_input )
1198     {
1199         vlc_object_release( p_input );
1200     }
1201
1202     /*
1203      * Error loop - wait until the thread destruction is requested
1204      */
1205     if( p_vout->b_error )
1206     {
1207         ErrorThread( p_vout );
1208     }
1209
1210     /* End of thread */
1211     EndThread( p_vout );
1212
1213     /* Destroy thread structures allocated by CreateThread */
1214     DestroyThread( p_vout );
1215 }
1216
1217 /*****************************************************************************
1218  * ErrorThread: RunThread() error loop
1219  *****************************************************************************
1220  * This function is called when an error occurred during thread main's loop.
1221  * The thread can still receive feed, but must be ready to terminate as soon
1222  * as possible.
1223  *****************************************************************************/
1224 static void ErrorThread( vout_thread_t *p_vout )
1225 {
1226     /* Wait until a `die' order */
1227     while( !p_vout->b_die )
1228     {
1229         /* Sleep a while */
1230         msleep( VOUT_IDLE_SLEEP );
1231     }
1232 }
1233
1234 /*****************************************************************************
1235  * EndThread: thread destruction
1236  *****************************************************************************
1237  * This function is called when the thread ends after a sucessful
1238  * initialization. It frees all resources allocated by InitThread.
1239  *****************************************************************************/
1240 static void EndThread( vout_thread_t *p_vout )
1241 {
1242     int     i_index;                                        /* index in heap */
1243
1244 #ifdef STATS
1245     {
1246         struct tms cpu_usage;
1247         times( &cpu_usage );
1248
1249         msg_Dbg( p_vout, "cpu usage (user: %d, system: %d)",
1250                  cpu_usage.tms_utime, cpu_usage.tms_stime );
1251     }
1252 #endif
1253
1254     if( !p_vout->b_direct )
1255     {
1256         module_Unneed( p_vout, p_vout->chroma.p_module );
1257     }
1258
1259     /* Destroy all remaining pictures */
1260     for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++ )
1261     {
1262         if ( p_vout->p_picture[i_index].i_type == MEMORY_PICTURE )
1263         {
1264             free( p_vout->p_picture[i_index].p_data_orig );
1265         }
1266     }
1267
1268     /* Destroy subpicture unit */
1269     spu_Attach( p_vout->p_spu, VLC_OBJECT(p_vout), VLC_FALSE );
1270     spu_Destroy( p_vout->p_spu );
1271
1272     /* Destroy the video filters2 */
1273     RemoveVideoFilters2( p_vout );
1274
1275     /* Destroy translation tables */
1276     p_vout->pf_end( p_vout );
1277
1278     /* Release the change lock */
1279     vlc_mutex_unlock( &p_vout->change_lock );
1280 }
1281
1282 /*****************************************************************************
1283  * DestroyThread: thread destruction
1284  *****************************************************************************
1285  * This function is called when the thread ends. It frees all ressources
1286  * allocated by CreateThread. Status is available at this stage.
1287  *****************************************************************************/
1288 static void DestroyThread( vout_thread_t *p_vout )
1289 {
1290     /* Destroy the locks */
1291     vlc_mutex_destroy( &p_vout->picture_lock );
1292     vlc_mutex_destroy( &p_vout->change_lock );
1293     vlc_mutex_destroy( &p_vout->vfilter_lock );
1294
1295     /* Release the module */
1296     if( p_vout && p_vout->p_module )
1297     {
1298         module_Unneed( p_vout, p_vout->p_module );
1299     }
1300 }
1301
1302 /* following functions are local */
1303
1304 static int ReduceHeight( int i_ratio )
1305 {
1306     int i_dummy = VOUT_ASPECT_FACTOR;
1307     int i_pgcd  = 1;
1308
1309     if( !i_ratio )
1310     {
1311         return i_pgcd;
1312     }
1313
1314     /* VOUT_ASPECT_FACTOR is (2^7 * 3^3 * 5^3), we just check for 2, 3 and 5 */
1315     while( !(i_ratio & 1) && !(i_dummy & 1) )
1316     {
1317         i_ratio >>= 1;
1318         i_dummy >>= 1;
1319         i_pgcd  <<= 1;
1320     }
1321
1322     while( !(i_ratio % 3) && !(i_dummy % 3) )
1323     {
1324         i_ratio /= 3;
1325         i_dummy /= 3;
1326         i_pgcd  *= 3;
1327     }
1328
1329     while( !(i_ratio % 5) && !(i_dummy % 5) )
1330     {
1331         i_ratio /= 5;
1332         i_dummy /= 5;
1333         i_pgcd  *= 5;
1334     }
1335
1336     return i_pgcd;
1337 }
1338
1339 static void AspectRatio( int i_aspect, int *i_aspect_x, int *i_aspect_y )
1340 {
1341     unsigned int i_pgcd = ReduceHeight( i_aspect );
1342     *i_aspect_x = i_aspect / i_pgcd;
1343     *i_aspect_y = VOUT_ASPECT_FACTOR / i_pgcd;
1344 }
1345
1346 /*****************************************************************************
1347  * BinaryLog: computes the base 2 log of a binary value
1348  *****************************************************************************
1349  * This functions is used by MaskToShift, to get a bit index from a binary
1350  * value.
1351  *****************************************************************************/
1352 static int BinaryLog( uint32_t i )
1353 {
1354     int i_log = 0;
1355
1356     if( i == 0 ) return -31337;
1357
1358     if( i & 0xffff0000 ) i_log += 16;
1359     if( i & 0xff00ff00 ) i_log += 8;
1360     if( i & 0xf0f0f0f0 ) i_log += 4;
1361     if( i & 0xcccccccc ) i_log += 2;
1362     if( i & 0xaaaaaaaa ) i_log += 1;
1363
1364     return i_log;
1365 }
1366
1367 /*****************************************************************************
1368  * MaskToShift: transform a color mask into right and left shifts
1369  *****************************************************************************
1370  * This function is used for obtaining color shifts from masks.
1371  *****************************************************************************/
1372 static void MaskToShift( int *pi_left, int *pi_right, uint32_t i_mask )
1373 {
1374     uint32_t i_low, i_high;            /* lower hand higher bits of the mask */
1375
1376     if( !i_mask )
1377     {
1378         *pi_left = *pi_right = 0;
1379         return;
1380     }
1381
1382     /* Get bits */
1383     i_low = i_high = i_mask;
1384
1385     i_low &= - (int32_t)i_low;          /* lower bit of the mask */
1386     i_high += i_low;                    /* higher bit of the mask */
1387
1388     /* Transform bits into an index. Also deal with i_high overflow, which
1389      * is faster than changing the BinaryLog code to handle 64 bit integers. */
1390     i_low =  BinaryLog (i_low);
1391     i_high = i_high ? BinaryLog (i_high) : 32;
1392
1393     /* Update pointers and return */
1394     *pi_left =   i_low;
1395     *pi_right = (8 - i_high + i_low);
1396 }
1397
1398 /*****************************************************************************
1399  * vout_VarCallback: generic callback for intf variables
1400  *****************************************************************************/
1401 int vout_VarCallback( vlc_object_t * p_this, const char * psz_variable,
1402                       vlc_value_t oldval, vlc_value_t newval,
1403                       void *p_data )
1404 {
1405     vout_thread_t * p_vout = (vout_thread_t *)p_this;
1406     vlc_value_t val;
1407     (void)psz_variable; (void)newval; (void)oldval; (void)p_data;
1408     val.b_bool = VLC_TRUE;
1409     var_Set( p_vout, "intf-change", val );
1410     return VLC_SUCCESS;
1411 }
1412
1413 /*****************************************************************************
1414  * Helper thread for object variables callbacks.
1415  * Only used to avoid deadlocks when using the video embedded mode.
1416  *****************************************************************************/
1417 typedef struct suxor_thread_t
1418 {
1419     VLC_COMMON_MEMBERS
1420     input_thread_t *p_input;
1421
1422 } suxor_thread_t;
1423
1424 static void SuxorRestartVideoES( suxor_thread_t *p_this )
1425 {
1426     vlc_value_t val;
1427
1428     vlc_thread_ready( p_this );
1429
1430     /* Now restart current video stream */
1431     var_Get( p_this->p_input, "video-es", &val );
1432     if( val.i_int >= 0 )
1433     {
1434         vlc_value_t val_es;
1435         val_es.i_int = -VIDEO_ES;
1436         var_Set( p_this->p_input, "video-es", val_es );
1437         var_Set( p_this->p_input, "video-es", val );
1438     }
1439
1440     vlc_object_release( p_this->p_input );
1441
1442 #ifdef WIN32
1443     CloseHandle( p_this->thread_id );
1444 #endif
1445
1446     vlc_object_destroy( p_this );
1447 }
1448
1449 /*****************************************************************************
1450  * object variables callbacks: a bunch of object variables are used by the
1451  * interfaces to interact with the vout.
1452  *****************************************************************************/
1453 static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd,
1454                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1455 {
1456     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1457     input_thread_t *p_input;
1458     vlc_value_t val;
1459
1460     char *psz_mode = newval.psz_string;
1461     char *psz_filter, *psz_deinterlace = NULL;
1462     (void)psz_cmd; (void)oldval; (void)p_data;
1463
1464     var_Get( p_vout, "vout-filter", &val );
1465     psz_filter = val.psz_string;
1466     if( psz_filter ) psz_deinterlace = strstr( psz_filter, "deinterlace" );
1467
1468     if( !psz_mode || !*psz_mode )
1469     {
1470         if( psz_deinterlace )
1471         {
1472             char *psz_src = psz_deinterlace + sizeof("deinterlace") - 1;
1473             if( psz_src[0] == ':' ) psz_src++;
1474             memmove( psz_deinterlace, psz_src, strlen(psz_src) + 1 );
1475         }
1476     }
1477     else if( !psz_deinterlace )
1478     {
1479         psz_filter = realloc( psz_filter, strlen( psz_filter ) +
1480                               sizeof(":deinterlace") );
1481         if( psz_filter && *psz_filter ) strcat( psz_filter, ":" );
1482         strcat( psz_filter, "deinterlace" );
1483     }
1484
1485     p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1486                                                  FIND_PARENT );
1487     if( !p_input ) return VLC_EGENERIC;
1488
1489     if( psz_mode && *psz_mode )
1490     {
1491         /* Modify input as well because the vout might have to be restarted */
1492         val.psz_string = psz_mode;
1493         var_Create( p_input, "deinterlace-mode", VLC_VAR_STRING );
1494         var_Set( p_input, "deinterlace-mode", val );
1495     }
1496     vlc_object_release( p_input );
1497
1498     val.b_bool = VLC_TRUE;
1499     var_Set( p_vout, "intf-change", val );
1500
1501     val.psz_string = psz_filter;
1502     var_Set( p_vout, "vout-filter", val );
1503     if( psz_filter ) free( psz_filter );
1504
1505     return VLC_SUCCESS;
1506 }
1507
1508 static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd,
1509                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1510 {
1511     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1512     input_thread_t *p_input;
1513     vlc_value_t val;
1514     (void)psz_cmd; (void)oldval; (void)p_data;
1515
1516     p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1517                                                  FIND_PARENT );
1518     if (!p_input)
1519     {
1520         msg_Err( p_vout, "Input not found" );
1521         return( VLC_EGENERIC );
1522     }
1523
1524     val.b_bool = VLC_TRUE;
1525     var_Set( p_vout, "intf-change", val );
1526
1527     /* Modify input as well because the vout might have to be restarted */
1528     val.psz_string = newval.psz_string;
1529     var_Create( p_input, "vout-filter", VLC_VAR_STRING );
1530
1531     var_Set( p_input, "vout-filter", val );
1532
1533     /* Now restart current video stream */
1534     var_Get( p_input, "video-es", &val );
1535     if( val.i_int >= 0 )
1536     {
1537         suxor_thread_t *p_suxor =
1538             vlc_object_create( p_vout, sizeof(suxor_thread_t) );
1539         p_suxor->p_input = p_input;
1540         p_vout->b_filter_change = VLC_TRUE;
1541         vlc_object_yield( p_input );
1542         vlc_thread_create( p_suxor, "suxor", SuxorRestartVideoES,
1543                            VLC_THREAD_PRIORITY_LOW, VLC_FALSE );
1544     }
1545
1546     vlc_object_release( p_input );
1547
1548     return VLC_SUCCESS;
1549 }
1550
1551 /*****************************************************************************
1552  * Video Filter2 stuff
1553  *****************************************************************************/
1554 static int ParseVideoFilter2Chain( vout_thread_t *p_vout, char *psz_vfilters )
1555 {
1556     int i;
1557     for( i = 0; i < p_vout->i_vfilters_cfg; i++ )
1558     {
1559         struct config_chain_t *p_cfg =
1560             p_vout->p_vfilters_cfg[p_vout->i_vfilters_cfg];
1561         config_ChainDestroy( p_cfg );
1562         if( p_vout->psz_vfilters[p_vout->i_vfilters_cfg] )
1563         {
1564             free( p_vout->psz_vfilters[p_vout->i_vfilters_cfg] );
1565             p_vout->psz_vfilters[p_vout->i_vfilters_cfg] = NULL;
1566         }
1567     }
1568     p_vout->i_vfilters_cfg = 0;
1569     if( psz_vfilters && *psz_vfilters )
1570     {
1571         char *psz_parser = psz_vfilters;
1572
1573         while( psz_parser && *psz_parser )
1574         {
1575             psz_parser = config_ChainCreate(
1576                             &p_vout->psz_vfilters[p_vout->i_vfilters_cfg],
1577                             &p_vout->p_vfilters_cfg[p_vout->i_vfilters_cfg],
1578                             psz_parser );
1579             msg_Dbg( p_vout, "adding vfilter: %s",
1580                      p_vout->psz_vfilters[p_vout->i_vfilters_cfg] );
1581             p_vout->i_vfilters_cfg++;
1582             if( psz_parser && *psz_parser )
1583             {
1584                 if( p_vout->i_vfilters_cfg == MAX_VFILTERS )
1585                 {
1586                     msg_Warn( p_vout,
1587                   "maximum number of video filters reached. \"%s\" discarded",
1588                               psz_parser );
1589                     break;
1590                 }
1591             }
1592         }
1593     }
1594     return VLC_SUCCESS;
1595 }
1596
1597 static int VideoFilter2Callback( vlc_object_t *p_this, char const *psz_cmd,
1598                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1599 {
1600     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1601     (void)psz_cmd; (void)oldval; (void)p_data;
1602
1603     vlc_mutex_lock( &p_vout->vfilter_lock );
1604     ParseVideoFilter2Chain( p_vout, newval.psz_string );
1605     p_vout->b_vfilter_change = VLC_TRUE;
1606     vlc_mutex_unlock( &p_vout->vfilter_lock );
1607
1608     return VLC_SUCCESS;
1609 }
1610
1611 static void RemoveVideoFilters2( vout_thread_t *p_vout )
1612 {
1613     int i;
1614     for( i = 0; i < p_vout->i_vfilters; i++ )
1615     {
1616         vlc_object_detach( p_vout->pp_vfilters[i] );
1617         if( p_vout->pp_vfilters[i]->p_module )
1618         {
1619             module_Unneed( p_vout->pp_vfilters[i],
1620                            p_vout->pp_vfilters[i]->p_module );
1621         }
1622
1623         free( p_vout->pp_vfilters[i]->p_owner );
1624         vlc_object_destroy( p_vout->pp_vfilters[i] );
1625     }
1626     p_vout->i_vfilters = 0;
1627 }