1 /*****************************************************************************
2 * video_output.c : video output thread
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
11 * Authors: Vincent Seguin <seguin@via.ecp.fr>
12 * Gildas Bazin <gbazin@videolan.org>
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.
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.
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 *****************************************************************************/
29 /*****************************************************************************
31 *****************************************************************************/
38 #include <stdlib.h> /* free() */
42 #ifdef HAVE_SYS_TIMES_H
43 # include <sys/times.h>
47 #include <vlc_playlist.h>
49 #include <vlc_filter.h>
52 #if defined( __APPLE__ )
53 /* Include darwin_specific.h here if needed */
56 /** FIXME This is quite ugly but needed while we don't have counters
58 #include "input/input_internal.h"
60 #include "modules/modules.h"
62 /*****************************************************************************
64 *****************************************************************************/
65 static int InitThread ( vout_thread_t * );
66 static void RunThread ( vout_thread_t * );
67 static void ErrorThread ( vout_thread_t * );
68 static void EndThread ( vout_thread_t * );
70 static void AspectRatio ( int, int *, int * );
71 static int BinaryLog ( uint32_t );
72 static void MaskToShift ( int *, int *, uint32_t );
74 static void vout_Destructor ( vlc_object_t * p_this );
76 /* Object variables callbacks */
77 static int DeinterlaceCallback( vlc_object_t *, char const *,
78 vlc_value_t, vlc_value_t, void * );
79 static int FilterCallback( vlc_object_t *, char const *,
80 vlc_value_t, vlc_value_t, void * );
81 static int VideoFilter2Callback( vlc_object_t *, char const *,
82 vlc_value_t, vlc_value_t, void * );
84 /* From vout_intf.c */
85 int vout_Snapshot( vout_thread_t *, picture_t * );
87 /* Video filter2 parsing */
88 static int ParseVideoFilter2Chain( vout_thread_t *, char * );
89 static void RemoveVideoFilters2( vout_thread_t *p_vout );
91 /* Display media title in OSD */
92 static void DisplayTitleOnOSD( vout_thread_t *p_vout );
94 /*****************************************************************************
95 * Video Filter2 functions
96 *****************************************************************************/
97 struct filter_owner_sys_t
99 vout_thread_t *p_vout;
102 static picture_t *video_new_buffer_filter( filter_t *p_filter )
104 picture_t *p_picture;
105 vout_thread_t *p_vout = p_filter->p_owner->p_vout;
107 p_picture = vout_CreatePicture( p_vout, 0, 0, 0 );
112 static void video_del_buffer_filter( filter_t *p_filter, picture_t *p_pic )
114 vout_DestroyPicture( p_filter->p_owner->p_vout, p_pic );
117 /*****************************************************************************
118 * vout_Request: find a video output thread, create one, or destroy one.
119 *****************************************************************************
120 * This function looks for a video output thread matching the current
121 * properties. If not found, it spawns a new one.
122 *****************************************************************************/
123 vout_thread_t *__vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout,
124 video_format_t *p_fmt )
128 /* Reattach video output to playlist before bailing out */
131 spu_Attach( p_vout->p_spu, p_this, VLC_FALSE );
132 vlc_object_detach( p_vout );
133 vlc_object_attach( p_vout, p_this->p_libvlc );
138 /* If a video output was provided, lock it, otherwise look for one. */
141 vlc_object_yield( p_vout );
145 p_vout = vlc_object_find( p_this, VLC_OBJECT_VOUT, FIND_CHILD );
149 p_vout = vlc_object_find( p_this->p_libvlc,
150 VLC_OBJECT_VOUT, FIND_CHILD );
151 /* only first children of p_input for unused vout */
152 if( p_vout && p_vout->p_parent != VLC_OBJECT(p_this->p_libvlc) )
154 vlc_object_release( p_vout );
158 vlc_object_detach( p_vout ); /* Remove it from the GC */
162 /* If we now have a video output, check it has the right properties */
165 char *psz_filter_chain;
168 /* We don't directly check for the "vout-filter" variable for obvious
169 * performance reasons. */
170 if( p_vout->b_filter_change )
172 var_Get( p_vout, "vout-filter", &val );
173 psz_filter_chain = val.psz_string;
175 if( psz_filter_chain && !*psz_filter_chain )
177 free( psz_filter_chain );
178 psz_filter_chain = NULL;
180 if( p_vout->psz_filter_chain && !*p_vout->psz_filter_chain )
182 free( p_vout->psz_filter_chain );
183 p_vout->psz_filter_chain = NULL;
186 if( !psz_filter_chain && !p_vout->psz_filter_chain )
188 p_vout->b_filter_change = VLC_FALSE;
191 free( psz_filter_chain );
194 if( ( p_vout->fmt_render.i_width != p_fmt->i_width ) ||
195 ( p_vout->fmt_render.i_height != p_fmt->i_height ) ||
196 ( p_vout->fmt_render.i_aspect != p_fmt->i_aspect ) ||
197 p_vout->b_filter_change )
199 /* We are not interested in this format, close this vout */
200 vlc_object_release( p_vout );
201 vout_Destroy( p_vout );
206 /* This video output is cool! Hijack it. */
207 spu_Attach( p_vout->p_spu, p_this, VLC_TRUE );
208 vlc_object_attach( p_vout, p_this );
209 if( p_vout->b_title_show )
210 DisplayTitleOnOSD( p_vout );
211 vlc_object_release( p_vout );
217 msg_Dbg( p_this, "no usable vout present, spawning one" );
219 p_vout = vout_Create( p_this, p_fmt );
225 /*****************************************************************************
226 * vout_Create: creates a new video output thread
227 *****************************************************************************
228 * This function creates a new video output thread, and returns a pointer
229 * to its description. On error, it returns NULL.
230 *****************************************************************************/
231 vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt )
233 vout_thread_t * p_vout; /* thread descriptor */
234 input_thread_t * p_input_thread;
235 int i_index; /* loop variable */
236 vlc_value_t val, text;
238 unsigned int i_width = p_fmt->i_width;
239 unsigned int i_height = p_fmt->i_height;
240 vlc_fourcc_t i_chroma = p_fmt->i_chroma;
241 unsigned int i_aspect = p_fmt->i_aspect;
243 config_chain_t *p_cfg;
247 /* Allocate descriptor */
248 p_vout = vlc_object_create( p_parent, VLC_OBJECT_VOUT );
251 msg_Err( p_parent, "out of memory" );
255 /* Initialize pictures - translation tables and functions
256 * will be initialized later in InitThread */
257 for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++)
259 p_vout->p_picture[i_index].pf_lock = NULL;
260 p_vout->p_picture[i_index].pf_unlock = NULL;
261 p_vout->p_picture[i_index].i_status = FREE_PICTURE;
262 p_vout->p_picture[i_index].i_type = EMPTY_PICTURE;
263 p_vout->p_picture[i_index].b_slow = 0;
266 /* No images in the heap */
267 p_vout->i_heap_size = 0;
269 /* Initialize the rendering heap */
270 I_RENDERPICTURES = 0;
272 vlc_ureduce( &p_fmt->i_sar_num, &p_fmt->i_sar_den,
273 p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 );
274 p_vout->fmt_render = *p_fmt; /* FIXME palette */
275 p_vout->fmt_in = *p_fmt; /* FIXME palette */
277 p_vout->render.i_width = i_width;
278 p_vout->render.i_height = i_height;
279 p_vout->render.i_chroma = i_chroma;
280 p_vout->render.i_aspect = i_aspect;
282 p_vout->render.i_rmask = 0;
283 p_vout->render.i_gmask = 0;
284 p_vout->render.i_bmask = 0;
286 p_vout->render.i_last_used_pic = -1;
287 p_vout->render.b_allow_modify_pics = 1;
289 /* Zero the output heap */
290 I_OUTPUTPICTURES = 0;
291 p_vout->output.i_width = 0;
292 p_vout->output.i_height = 0;
293 p_vout->output.i_chroma = 0;
294 p_vout->output.i_aspect = 0;
296 p_vout->output.i_rmask = 0;
297 p_vout->output.i_gmask = 0;
298 p_vout->output.i_bmask = 0;
300 /* Initialize misc stuff */
301 p_vout->i_changes = 0;
303 p_vout->b_grayscale = 0;
305 p_vout->b_interface = 0;
307 p_vout->b_fullscreen = 0;
308 p_vout->i_alignment = 0;
309 p_vout->render_time = 10;
310 p_vout->c_fps_samples = 0;
311 p_vout->b_filter_change = 0;
312 p_vout->pf_control = 0;
313 p_vout->p_parent_intf = 0;
314 p_vout->i_par_num = p_vout->i_par_den = 1;
316 /* Initialize locks */
317 vlc_mutex_init( p_vout, &p_vout->picture_lock );
318 vlc_mutex_init( p_vout, &p_vout->change_lock );
319 vlc_mutex_init( p_vout, &p_vout->vfilter_lock );
321 /* Mouse coordinates */
322 var_Create( p_vout, "mouse-x", VLC_VAR_INTEGER );
323 var_Create( p_vout, "mouse-y", VLC_VAR_INTEGER );
324 var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER );
325 var_Create( p_vout, "mouse-moved", VLC_VAR_BOOL );
326 var_Create( p_vout, "mouse-clicked", VLC_VAR_INTEGER );
328 /* Initialize subpicture unit */
329 p_vout->p_spu = spu_Create( p_vout );
330 spu_Attach( p_vout->p_spu, p_parent, VLC_TRUE );
332 /* Attach the new object now so we can use var inheritance below */
333 vlc_object_attach( p_vout, p_parent );
335 spu_Init( p_vout->p_spu );
337 /* Take care of some "interface/control" related initialisations */
338 vout_IntfInit( p_vout );
340 /* If the parent is not a VOUT object, that means we are at the start of
341 * the video output pipe */
342 if( p_parent->i_object_type != VLC_OBJECT_VOUT )
344 /* Look for the default filter configuration */
345 var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
346 var_Get( p_vout, "vout-filter", &val );
347 p_vout->psz_filter_chain = val.psz_string;
349 /* Apply video filter2 objects on the first vout */
350 var_Create( p_vout, "video-filter",
351 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
352 var_Get( p_vout, "video-filter", &val );
353 ParseVideoFilter2Chain( p_vout, val.psz_string );
354 free( val.psz_string );
358 /* continue the parent's filter chain */
361 /* Ugly hack to jump to our configuration chain */
362 p_vout->psz_filter_chain
363 = ((vout_thread_t *)p_parent)->psz_filter_chain;
364 p_vout->psz_filter_chain
365 = config_ChainCreate( &psz_tmp, &p_cfg, p_vout->psz_filter_chain );
366 config_ChainDestroy( p_cfg );
369 /* Create a video filter2 var ... but don't inherit values */
370 var_Create( p_vout, "video-filter", VLC_VAR_STRING );
371 ParseVideoFilter2Chain( p_vout, NULL );
374 var_AddCallback( p_vout, "video-filter", VideoFilter2Callback, NULL );
375 p_vout->b_vfilter_change = VLC_TRUE;
376 p_vout->i_vfilters = 0;
378 /* Choose the video output module */
379 if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain )
381 var_Create( p_vout, "vout", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
382 var_Get( p_vout, "vout", &val );
383 psz_parser = val.psz_string;
387 psz_parser = strdup( p_vout->psz_filter_chain );
390 /* Create the vout thread */
391 config_ChainCreate( &psz_name, &p_cfg, psz_parser );
393 p_vout->p_cfg = p_cfg;
394 p_vout->p_module = module_Need( p_vout,
395 ( p_vout->psz_filter_chain && *p_vout->psz_filter_chain ) ?
396 "video filter" : "video output", psz_name, p_vout->psz_filter_chain && *p_vout->psz_filter_chain );
399 if( p_vout->p_module == NULL )
401 msg_Err( p_vout, "no suitable vout module" );
402 vlc_object_detach( p_vout );
403 vlc_object_release( p_vout );
407 /* Create a few object variables for interface interaction */
408 var_Create( p_vout, "deinterlace", VLC_VAR_STRING | VLC_VAR_HASCHOICE );
409 text.psz_string = _("Deinterlace");
410 var_Change( p_vout, "deinterlace", VLC_VAR_SETTEXT, &text, NULL );
411 val.psz_string = (char *)""; text.psz_string = _("Disable");
412 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
413 val.psz_string = (char *)"discard"; text.psz_string = _("Discard");
414 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
415 val.psz_string = (char *)"blend"; text.psz_string = _("Blend");
416 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
417 val.psz_string = (char *)"mean"; text.psz_string = _("Mean");
418 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
419 val.psz_string = (char *)"bob"; text.psz_string = _("Bob");
420 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
421 val.psz_string = (char *)"linear"; text.psz_string = _("Linear");
422 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
423 val.psz_string = (char *)"x"; text.psz_string = (char *)"X";
424 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
426 if( var_Get( p_vout, "deinterlace-mode", &val ) == VLC_SUCCESS )
428 var_Set( p_vout, "deinterlace", val );
429 free( val.psz_string );
431 var_AddCallback( p_vout, "deinterlace", DeinterlaceCallback, NULL );
433 var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
434 text.psz_string = _("Filters");
435 var_Change( p_vout, "vout-filter", VLC_VAR_SETTEXT, &text, NULL );
436 var_AddCallback( p_vout, "vout-filter", FilterCallback, NULL );
438 /* Calculate delay created by internal caching */
439 p_input_thread = (input_thread_t *)vlc_object_find( p_vout,
440 VLC_OBJECT_INPUT, FIND_ANYWHERE );
443 p_vout->i_pts_delay = p_input_thread->i_pts_delay;
444 vlc_object_release( p_input_thread );
448 p_vout->i_pts_delay = DEFAULT_PTS_DELAY;
451 if( vlc_thread_create( p_vout, "video output", RunThread,
452 VLC_THREAD_PRIORITY_OUTPUT, VLC_TRUE ) )
454 msg_Err( p_vout, "out of memory" );
455 module_Unneed( p_vout, p_vout->p_module );
456 vlc_object_release( p_vout );
460 if( p_vout->b_error )
462 msg_Err( p_vout, "video output creation failed" );
464 /* Make sure the thread is destroyed */
465 vlc_object_release( p_vout );
469 vlc_object_set_destructor( p_vout, vout_Destructor );
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 )
484 /* XXX: should go in the destructor */
485 var_Destroy( p_vout, "intf-change" );
487 vlc_object_release( p_vout );
490 static void vout_Destructor( vlc_object_t * p_this )
492 vout_thread_t *p_vout = (vout_thread_t *)p_this;
494 /* Destroy the locks */
495 vlc_mutex_destroy( &p_vout->picture_lock );
496 vlc_mutex_destroy( &p_vout->change_lock );
497 vlc_mutex_destroy( &p_vout->vfilter_lock );
499 /* Release the module */
500 if( p_vout->p_module )
502 module_Unneed( p_vout, p_vout->p_module );
505 free( p_vout->psz_filter_chain );
507 config_ChainDestroy( p_vout->p_cfg );
510 vout_thread_t *p_another_vout;
512 playlist_t *p_playlist = pl_Get( p_vout );
513 if( p_playlist->b_die ) return;
514 vlc_object_yield( p_playlist );
515 /* This is a dirty hack for mostly Linux, where there is no way to get the GUI
516 back if you closed it while playing video. This is solved in Mac OS X,
517 where we have this novelty called menubar, that will always allow you access
518 to the applications main functionality. They should try that on linux sometime */
519 p_another_vout = vlc_object_find( p_this->p_libvlc,
520 VLC_OBJECT_VOUT, FIND_ANYWHERE );
521 if( p_another_vout == NULL )
524 val.b_bool = VLC_TRUE;
525 var_Set( p_playlist, "intf-show", val );
529 vlc_object_release( p_another_vout );
531 vlc_object_release( p_playlist );
535 /*****************************************************************************
536 * InitThread: initialize video output thread
537 *****************************************************************************
538 * This function is called from RunThread and performs the second step of the
539 * initialization. It returns 0 on success. Note that the thread's flag are not
540 * modified inside this function.
541 *****************************************************************************/
542 static int InitThread( vout_thread_t *p_vout )
544 int i, i_aspect_x, i_aspect_y;
546 vlc_mutex_lock( &p_vout->change_lock );
552 /* Initialize output method, it allocates direct buffers for us */
553 if( p_vout->pf_init( p_vout ) )
555 vlc_mutex_unlock( &p_vout->change_lock );
559 if( !I_OUTPUTPICTURES )
561 msg_Err( p_vout, "plugin was unable to allocate at least "
562 "one direct buffer" );
563 p_vout->pf_end( p_vout );
564 vlc_mutex_unlock( &p_vout->change_lock );
568 if( I_OUTPUTPICTURES > VOUT_MAX_PICTURES )
570 msg_Err( p_vout, "plugin allocated too many direct buffers, "
571 "our internal buffers must have overflown." );
572 p_vout->pf_end( p_vout );
573 vlc_mutex_unlock( &p_vout->change_lock );
577 msg_Dbg( p_vout, "got %i direct buffer(s)", I_OUTPUTPICTURES );
579 AspectRatio( p_vout->fmt_render.i_aspect, &i_aspect_x, &i_aspect_y );
581 msg_Dbg( p_vout, "picture in %ix%i (%i,%i,%ix%i), "
582 "chroma %4.4s, ar %i:%i, sar %i:%i",
583 p_vout->fmt_render.i_width, p_vout->fmt_render.i_height,
584 p_vout->fmt_render.i_x_offset, p_vout->fmt_render.i_y_offset,
585 p_vout->fmt_render.i_visible_width,
586 p_vout->fmt_render.i_visible_height,
587 (char*)&p_vout->fmt_render.i_chroma,
588 i_aspect_x, i_aspect_y,
589 p_vout->fmt_render.i_sar_num, p_vout->fmt_render.i_sar_den );
591 AspectRatio( p_vout->fmt_in.i_aspect, &i_aspect_x, &i_aspect_y );
593 msg_Dbg( p_vout, "picture user %ix%i (%i,%i,%ix%i), "
594 "chroma %4.4s, ar %i:%i, sar %i:%i",
595 p_vout->fmt_in.i_width, p_vout->fmt_in.i_height,
596 p_vout->fmt_in.i_x_offset, p_vout->fmt_in.i_y_offset,
597 p_vout->fmt_in.i_visible_width,
598 p_vout->fmt_in.i_visible_height,
599 (char*)&p_vout->fmt_in.i_chroma,
600 i_aspect_x, i_aspect_y,
601 p_vout->fmt_in.i_sar_num, p_vout->fmt_in.i_sar_den );
603 if( !p_vout->fmt_out.i_width || !p_vout->fmt_out.i_height )
605 p_vout->fmt_out.i_width = p_vout->fmt_out.i_visible_width =
606 p_vout->output.i_width;
607 p_vout->fmt_out.i_height = p_vout->fmt_out.i_visible_height =
608 p_vout->output.i_height;
609 p_vout->fmt_out.i_x_offset = p_vout->fmt_out.i_y_offset = 0;
611 p_vout->fmt_out.i_aspect = p_vout->output.i_aspect;
612 p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
614 if( !p_vout->fmt_out.i_sar_num || !p_vout->fmt_out.i_sar_num )
616 p_vout->fmt_out.i_sar_num = p_vout->fmt_out.i_aspect *
617 p_vout->fmt_out.i_height;
618 p_vout->fmt_out.i_sar_den = VOUT_ASPECT_FACTOR *
619 p_vout->fmt_out.i_width;
622 vlc_ureduce( &p_vout->fmt_out.i_sar_num, &p_vout->fmt_out.i_sar_den,
623 p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den, 0 );
625 AspectRatio( p_vout->fmt_out.i_aspect, &i_aspect_x, &i_aspect_y );
627 msg_Dbg( p_vout, "picture out %ix%i (%i,%i,%ix%i), "
628 "chroma %4.4s, ar %i:%i, sar %i:%i",
629 p_vout->fmt_out.i_width, p_vout->fmt_out.i_height,
630 p_vout->fmt_out.i_x_offset, p_vout->fmt_out.i_y_offset,
631 p_vout->fmt_out.i_visible_width,
632 p_vout->fmt_out.i_visible_height,
633 (char*)&p_vout->fmt_out.i_chroma,
634 i_aspect_x, i_aspect_y,
635 p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den );
637 /* Calculate shifts from system-updated masks */
638 MaskToShift( &p_vout->output.i_lrshift, &p_vout->output.i_rrshift,
639 p_vout->output.i_rmask );
640 MaskToShift( &p_vout->output.i_lgshift, &p_vout->output.i_rgshift,
641 p_vout->output.i_gmask );
642 MaskToShift( &p_vout->output.i_lbshift, &p_vout->output.i_rbshift,
643 p_vout->output.i_bmask );
645 /* Check whether we managed to create direct buffers similar to
646 * the render buffers, ie same size and chroma */
647 if( ( p_vout->output.i_width == p_vout->render.i_width )
648 && ( p_vout->output.i_height == p_vout->render.i_height )
649 && ( vout_ChromaCmp( p_vout->output.i_chroma, p_vout->render.i_chroma ) ) )
651 /* Cool ! We have direct buffers, we can ask the decoder to
652 * directly decode into them ! Map the first render buffers to
653 * the first direct buffers, but keep the first direct buffer
654 * for memcpy operations */
655 p_vout->b_direct = 1;
657 for( i = 1; i < VOUT_MAX_PICTURES; i++ )
659 if( p_vout->p_picture[ i ].i_type != DIRECT_PICTURE &&
660 I_RENDERPICTURES >= VOUT_MIN_DIRECT_PICTURES - 1 &&
661 p_vout->p_picture[ i - 1 ].i_type == DIRECT_PICTURE )
663 /* We have enough direct buffers so there's no need to
664 * try to use system memory buffers. */
667 PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
671 msg_Dbg( p_vout, "direct render, mapping "
672 "render pictures 0-%i to system pictures 1-%i",
673 VOUT_MAX_PICTURES - 2, VOUT_MAX_PICTURES - 1 );
677 /* Rats... Something is wrong here, we could not find an output
678 * plugin able to directly render what we decode. See if we can
679 * find a chroma plugin to do the conversion */
680 p_vout->b_direct = 0;
682 /* Choose the best module */
683 p_vout->chroma.p_module = module_Need( p_vout, "chroma", NULL, 0 );
685 if( p_vout->chroma.p_module == NULL )
687 msg_Err( p_vout, "no chroma module for %4.4s to %4.4s",
688 (char*)&p_vout->render.i_chroma,
689 (char*)&p_vout->output.i_chroma );
690 p_vout->pf_end( p_vout );
691 vlc_mutex_unlock( &p_vout->change_lock );
695 msg_Dbg( p_vout, "indirect render, mapping "
696 "render pictures 0-%i to system pictures %i-%i",
697 VOUT_MAX_PICTURES - 1, I_OUTPUTPICTURES,
698 I_OUTPUTPICTURES + VOUT_MAX_PICTURES - 1 );
700 /* Append render buffers after the direct buffers */
701 for( i = I_OUTPUTPICTURES; i < 2 * VOUT_MAX_PICTURES; i++ )
703 PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
706 /* Check if we have enough render pictures */
707 if( I_RENDERPICTURES == VOUT_MAX_PICTURES )
712 /* Link pictures back to their heap */
713 for( i = 0 ; i < I_RENDERPICTURES ; i++ )
715 PP_RENDERPICTURE[ i ]->p_heap = &p_vout->render;
718 for( i = 0 ; i < I_OUTPUTPICTURES ; i++ )
720 PP_OUTPUTPICTURE[ i ]->p_heap = &p_vout->output;
723 /* XXX XXX mark thread ready */
727 /*****************************************************************************
728 * RunThread: video output thread
729 *****************************************************************************
730 * Video output thread. This function does only returns when the thread is
731 * terminated. It handles the pictures arriving in the video heap and the
732 * display device events.
733 *****************************************************************************/
734 static void RunThread( vout_thread_t *p_vout)
736 int i_index; /* index in heap */
737 int i_idle_loops = 0; /* loops without displaying a picture */
738 mtime_t current_date; /* current date */
739 mtime_t display_date; /* display date */
741 picture_t * p_picture; /* picture pointer */
742 picture_t * p_last_picture = NULL; /* last picture */
743 picture_t * p_directbuffer; /* direct buffer to display */
745 subpicture_t * p_subpic = NULL; /* subpicture pointer */
747 input_thread_t *p_input = NULL ; /* Parent input, if it exists */
750 vlc_bool_t b_drop_late;
752 int i_displayed = 0, i_lost = 0, i_loops = 0;
757 p_vout->b_error = InitThread( p_vout );
759 var_Create( p_vout, "drop-late-frames", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
760 var_Get( p_vout, "drop-late-frames", &val );
761 b_drop_late = val.b_bool;
763 /* signal the creation of the vout */
764 vlc_thread_ready( p_vout );
766 if( p_vout->b_error )
769 if( p_vout->b_title_show )
770 DisplayTitleOnOSD( p_vout );
773 * Main loop - it is not executed if an error occurred during
776 while( (!p_vout->b_die) && (!p_vout->b_error) )
778 /* Initialize loop variables */
781 current_date = mdate();
783 if( p_input && p_input->b_die )
785 vlc_object_release( p_input );
792 p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT,
797 vlc_mutex_lock( &p_input->p->counters.counters_lock );
798 stats_UpdateInteger( p_vout, p_input->p->counters.p_lost_pictures,
800 stats_UpdateInteger( p_vout,
801 p_input->p->counters.p_displayed_pictures,
803 i_displayed = i_lost = 0;
804 vlc_mutex_unlock( &p_input->p->counters.counters_lock );
808 if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
810 msg_Dbg( p_vout, "picture heap: %d/%d",
811 I_RENDERPICTURES, p_vout->i_heap_size );
816 * Find the picture to display (the one with the earliest date).
817 * This operation does not need lock, since only READY_PICTUREs
819 for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
821 if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
822 && ( (p_picture == NULL) ||
823 (PP_RENDERPICTURE[i_index]->date < display_date) ) )
825 p_picture = PP_RENDERPICTURE[i_index];
826 display_date = p_picture->date;
832 /* If we met the last picture, parse again to see whether there is
833 * a more appropriate one. */
834 if( p_picture == p_last_picture )
836 for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
838 if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
839 && (PP_RENDERPICTURE[i_index] != p_last_picture)
840 && ((p_picture == p_last_picture) ||
841 (PP_RENDERPICTURE[i_index]->date < display_date)) )
843 p_picture = PP_RENDERPICTURE[i_index];
844 display_date = p_picture->date;
849 /* If we found better than the last picture, destroy it */
850 if( p_last_picture && p_picture != p_last_picture )
852 vlc_mutex_lock( &p_vout->picture_lock );
853 if( p_last_picture->i_refcount )
855 p_last_picture->i_status = DISPLAYED_PICTURE;
859 p_last_picture->i_status = DESTROYED_PICTURE;
860 p_vout->i_heap_size--;
862 vlc_mutex_unlock( &p_vout->picture_lock );
863 p_last_picture = NULL;
866 /* Compute FPS rate */
867 p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ]
870 if( !p_picture->b_force &&
871 p_picture != p_last_picture &&
872 display_date < current_date + p_vout->render_time &&
875 /* Picture is late: it will be destroyed and the thread
876 * will directly choose the next picture */
877 vlc_mutex_lock( &p_vout->picture_lock );
878 if( p_picture->i_refcount )
880 /* Pretend we displayed the picture, but don't destroy
881 * it since the decoder might still need it. */
882 p_picture->i_status = DISPLAYED_PICTURE;
886 /* Destroy the picture without displaying it */
887 p_picture->i_status = DESTROYED_PICTURE;
888 p_vout->i_heap_size--;
890 msg_Warn( p_vout, "late picture skipped ("I64Fd")",
891 current_date - display_date );
893 vlc_mutex_unlock( &p_vout->picture_lock );
899 current_date + p_vout->i_pts_delay + VOUT_BOGUS_DELAY )
901 /* Picture is waaay too early: it will be destroyed */
902 vlc_mutex_lock( &p_vout->picture_lock );
903 if( p_picture->i_refcount )
905 /* Pretend we displayed the picture, but don't destroy
906 * it since the decoder might still need it. */
907 p_picture->i_status = DISPLAYED_PICTURE;
911 /* Destroy the picture without displaying it */
912 p_picture->i_status = DESTROYED_PICTURE;
913 p_vout->i_heap_size--;
916 msg_Warn( p_vout, "vout warning: early picture skipped "
917 "("I64Fd")", display_date - current_date
918 - p_vout->i_pts_delay );
919 vlc_mutex_unlock( &p_vout->picture_lock );
924 if( display_date > current_date + VOUT_DISPLAY_DELAY )
926 /* A picture is ready to be rendered, but its rendering date
927 * is far from the current one so the thread will perform an
928 * empty loop as if no picture were found. The picture state
933 else if( p_picture == p_last_picture )
935 /* We are asked to repeat the previous picture, but we first
936 * wait for a couple of idle loops */
937 if( i_idle_loops < 4 )
944 /* We set the display date to something high, otherwise
945 * we'll have lots of problems with late pictures */
946 display_date = current_date + p_vout->render_time;
951 if( p_picture == NULL )
956 /* Video Filter2 stuff */
957 if( p_vout->b_vfilter_change == VLC_TRUE )
960 vlc_mutex_lock( &p_vout->vfilter_lock );
961 RemoveVideoFilters2( p_vout );
962 for( i = 0; i < p_vout->i_vfilters_cfg; i++ )
964 filter_t *p_vfilter =
965 p_vout->pp_vfilters[p_vout->i_vfilters] =
966 vlc_object_create( p_vout, VLC_OBJECT_FILTER );
968 vlc_object_attach( p_vfilter, p_vout );
970 p_vfilter->pf_vout_buffer_new = video_new_buffer_filter;
971 p_vfilter->pf_vout_buffer_del = video_del_buffer_filter;
973 if( !p_vout->i_vfilters )
975 p_vfilter->fmt_in.video = p_vout->fmt_render;
979 p_vfilter->fmt_in.video = (p_vfilter-1)->fmt_out.video;
981 /* TODO: one day filters in the middle of the chain might
982 * have a different fmt_out.video than fmt_render ... */
983 p_vfilter->fmt_out.video = p_vout->fmt_render;
985 p_vfilter->p_cfg = p_vout->p_vfilters_cfg[i];
986 p_vfilter->p_module = module_Need( p_vfilter, "video filter2",
987 p_vout->psz_vfilters[i],
990 if( p_vfilter->p_module )
993 malloc( sizeof( filter_owner_sys_t ) );
994 p_vfilter->p_owner->p_vout = p_vout;
995 p_vout->i_vfilters++;
996 msg_Dbg( p_vout, "video filter found (%s)",
997 p_vout->psz_vfilters[i] );
1001 msg_Err( p_vout, "no video filter found (%s)",
1002 p_vout->psz_vfilters[i] );
1003 vlc_object_detach( p_vfilter );
1004 vlc_object_release( p_vfilter );
1007 p_vout->b_vfilter_change = VLC_FALSE;
1008 vlc_mutex_unlock( &p_vout->vfilter_lock );
1014 for( i = 0; i < p_vout->i_vfilters; i++ )
1016 picture_t *p_old = p_picture;
1017 p_picture = p_vout->pp_vfilters[i]->pf_video_filter(
1018 p_vout->pp_vfilters[i], p_picture );
1023 /* FIXME: this is kind of wrong
1024 * if you have 2 or more vfilters and the 2nd breaks,
1025 * on the next loop the 1st one will be applied again */
1027 /* if p_old and p_picture are the same (ie the filter
1028 * worked on the old picture), then following code is
1029 * still alright since i_status gets changed back to
1030 * the right value */
1031 if( p_old->i_refcount )
1033 p_old->i_status = DISPLAYED_PICTURE;
1037 p_old->i_status = DESTROYED_PICTURE;
1039 p_picture->i_status = READY_PICTURE;
1043 if( p_picture && p_vout->b_snapshot )
1045 p_vout->b_snapshot = VLC_FALSE;
1046 vout_Snapshot( p_vout, p_picture );
1050 * Check for subpictures to display
1052 if( display_date > 0 )
1056 p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT,
1059 p_subpic = spu_SortSubpictures( p_vout->p_spu, display_date,
1060 p_input ? var_GetBool( p_input, "state" ) == PAUSE_S : VLC_FALSE );
1067 p_directbuffer = vout_RenderPicture( p_vout, p_picture, p_subpic );
1070 * Call the plugin-specific rendering method if there is one
1072 if( p_picture != NULL && p_directbuffer != NULL && p_vout->pf_render )
1074 /* Render the direct buffer returned by vout_RenderPicture */
1075 p_vout->pf_render( p_vout, p_directbuffer );
1081 if( display_date != 0 && p_directbuffer != NULL )
1083 mtime_t current_render_time = mdate() - current_date;
1084 /* if render time is very large we don't include it in the mean */
1085 if( current_render_time < p_vout->render_time +
1086 VOUT_DISPLAY_DELAY )
1088 /* Store render time using a sliding mean weighting to
1089 * current value in a 3 to 1 ratio*/
1090 p_vout->render_time *= 3;
1091 p_vout->render_time += current_render_time;
1092 p_vout->render_time >>= 2;
1096 /* Give back change lock */
1097 vlc_mutex_unlock( &p_vout->change_lock );
1099 /* Sleep a while or until a given date */
1100 if( display_date != 0 )
1102 /* If there are filters in the chain, better give them the picture
1104 if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain )
1106 mwait( display_date - VOUT_MWAIT_TOLERANCE );
1111 msleep( VOUT_IDLE_SLEEP );
1114 /* On awakening, take back lock and send immediately picture
1116 vlc_mutex_lock( &p_vout->change_lock );
1119 * Display the previously rendered picture
1121 if( p_picture != NULL && p_directbuffer != NULL )
1123 /* Display the direct buffer returned by vout_RenderPicture */
1124 if( p_vout->pf_display )
1126 p_vout->pf_display( p_vout, p_directbuffer );
1129 /* Tell the vout this was the last picture and that it does not
1130 * need to be forced anymore. */
1131 p_last_picture = p_picture;
1132 p_last_picture->b_force = 0;
1135 if( p_picture != NULL )
1137 /* Reinitialize idle loop count */
1142 * Check events and manage thread
1144 if( p_vout->pf_manage && p_vout->pf_manage( p_vout ) )
1146 /* A fatal error occurred, and the thread must terminate
1147 * immediately, without displaying anything - setting b_error to 1
1148 * causes the immediate end of the main while() loop. */
1149 p_vout->b_error = 1;
1152 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
1154 /* this must only happen when the vout plugin is incapable of
1155 * rescaling the picture itself. In this case we need to destroy
1156 * the current picture buffers and recreate new ones with the right
1160 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
1162 p_vout->pf_end( p_vout );
1163 for( i = 0; i < I_OUTPUTPICTURES; i++ )
1164 p_vout->p_picture[ i ].i_status = FREE_PICTURE;
1166 I_OUTPUTPICTURES = 0;
1167 if( p_vout->pf_init( p_vout ) )
1169 msg_Err( p_vout, "cannot resize display" );
1170 /* FIXME: pf_end will be called again in EndThread() */
1171 p_vout->b_error = 1;
1174 /* Need to reinitialise the chroma plugin */
1175 if( p_vout->chroma.p_module )
1177 if( p_vout->chroma.p_module->pf_deactivate )
1178 p_vout->chroma.p_module->pf_deactivate( VLC_OBJECT(p_vout) );
1179 p_vout->chroma.p_module->pf_activate( VLC_OBJECT(p_vout) );
1183 if( p_vout->i_changes & VOUT_PICTURE_BUFFERS_CHANGE )
1185 /* This happens when the picture buffers need to be recreated.
1186 * This is useful on multimonitor displays for instance.
1188 * Warning: This only works when the vout creates only 1 picture
1190 p_vout->i_changes &= ~VOUT_PICTURE_BUFFERS_CHANGE;
1192 if( !p_vout->b_direct )
1194 module_Unneed( p_vout, p_vout->chroma.p_module );
1197 vlc_mutex_lock( &p_vout->picture_lock );
1199 p_vout->pf_end( p_vout );
1201 I_OUTPUTPICTURES = I_RENDERPICTURES = 0;
1203 p_vout->b_error = InitThread( p_vout );
1205 vlc_mutex_unlock( &p_vout->picture_lock );
1211 vlc_object_release( p_input );
1215 * Error loop - wait until the thread destruction is requested
1217 if( p_vout->b_error )
1219 ErrorThread( p_vout );
1223 EndThread( p_vout );
1226 /*****************************************************************************
1227 * ErrorThread: RunThread() error loop
1228 *****************************************************************************
1229 * This function is called when an error occurred during thread main's loop.
1230 * The thread can still receive feed, but must be ready to terminate as soon
1232 *****************************************************************************/
1233 static void ErrorThread( vout_thread_t *p_vout )
1235 /* Wait until a `die' order */
1236 while( !p_vout->b_die )
1239 msleep( VOUT_IDLE_SLEEP );
1243 /*****************************************************************************
1244 * EndThread: thread destruction
1245 *****************************************************************************
1246 * This function is called when the thread ends after a sucessful
1247 * initialization. It frees all resources allocated by InitThread.
1248 *****************************************************************************/
1249 static void EndThread( vout_thread_t *p_vout )
1251 int i_index; /* index in heap */
1255 struct tms cpu_usage;
1256 times( &cpu_usage );
1258 msg_Dbg( p_vout, "cpu usage (user: %d, system: %d)",
1259 cpu_usage.tms_utime, cpu_usage.tms_stime );
1263 if( !p_vout->b_direct )
1265 module_Unneed( p_vout, p_vout->chroma.p_module );
1268 /* Destroy all remaining pictures */
1269 for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++ )
1271 if ( p_vout->p_picture[i_index].i_type == MEMORY_PICTURE )
1273 free( p_vout->p_picture[i_index].p_data_orig );
1277 /* Destroy subpicture unit */
1278 spu_Attach( p_vout->p_spu, VLC_OBJECT(p_vout), VLC_FALSE );
1279 spu_Destroy( p_vout->p_spu );
1281 /* Destroy the video filters2 */
1282 RemoveVideoFilters2( p_vout );
1284 /* Destroy translation tables */
1285 p_vout->pf_end( p_vout );
1287 /* Release the change lock */
1288 vlc_mutex_unlock( &p_vout->change_lock );
1291 /* following functions are local */
1293 static int ReduceHeight( int i_ratio )
1295 int i_dummy = VOUT_ASPECT_FACTOR;
1303 /* VOUT_ASPECT_FACTOR is (2^7 * 3^3 * 5^3), we just check for 2, 3 and 5 */
1304 while( !(i_ratio & 1) && !(i_dummy & 1) )
1311 while( !(i_ratio % 3) && !(i_dummy % 3) )
1318 while( !(i_ratio % 5) && !(i_dummy % 5) )
1328 static void AspectRatio( int i_aspect, int *i_aspect_x, int *i_aspect_y )
1330 unsigned int i_pgcd = ReduceHeight( i_aspect );
1331 *i_aspect_x = i_aspect / i_pgcd;
1332 *i_aspect_y = VOUT_ASPECT_FACTOR / i_pgcd;
1335 /*****************************************************************************
1336 * BinaryLog: computes the base 2 log of a binary value
1337 *****************************************************************************
1338 * This functions is used by MaskToShift, to get a bit index from a binary
1340 *****************************************************************************/
1341 static int BinaryLog( uint32_t i )
1345 if( i == 0 ) return -31337;
1347 if( i & 0xffff0000 ) i_log += 16;
1348 if( i & 0xff00ff00 ) i_log += 8;
1349 if( i & 0xf0f0f0f0 ) i_log += 4;
1350 if( i & 0xcccccccc ) i_log += 2;
1351 if( i & 0xaaaaaaaa ) i_log += 1;
1356 /*****************************************************************************
1357 * MaskToShift: transform a color mask into right and left shifts
1358 *****************************************************************************
1359 * This function is used for obtaining color shifts from masks.
1360 *****************************************************************************/
1361 static void MaskToShift( int *pi_left, int *pi_right, uint32_t i_mask )
1363 uint32_t i_low, i_high; /* lower hand higher bits of the mask */
1367 *pi_left = *pi_right = 0;
1372 i_low = i_high = i_mask;
1374 i_low &= - (int32_t)i_low; /* lower bit of the mask */
1375 i_high += i_low; /* higher bit of the mask */
1377 /* Transform bits into an index. Also deal with i_high overflow, which
1378 * is faster than changing the BinaryLog code to handle 64 bit integers. */
1379 i_low = BinaryLog (i_low);
1380 i_high = i_high ? BinaryLog (i_high) : 32;
1382 /* Update pointers and return */
1384 *pi_right = (8 - i_high + i_low);
1387 /*****************************************************************************
1388 * vout_VarCallback: generic callback for intf variables
1389 *****************************************************************************/
1390 int vout_VarCallback( vlc_object_t * p_this, const char * psz_variable,
1391 vlc_value_t oldval, vlc_value_t newval,
1394 vout_thread_t * p_vout = (vout_thread_t *)p_this;
1396 (void)psz_variable; (void)newval; (void)oldval; (void)p_data;
1397 val.b_bool = VLC_TRUE;
1398 var_Set( p_vout, "intf-change", val );
1402 /*****************************************************************************
1403 * Helper thread for object variables callbacks.
1404 * Only used to avoid deadlocks when using the video embedded mode.
1405 *****************************************************************************/
1406 typedef struct suxor_thread_t
1409 input_thread_t *p_input;
1413 static void SuxorRestartVideoES( suxor_thread_t *p_this )
1417 vlc_thread_ready( p_this );
1419 /* Now restart current video stream */
1420 var_Get( p_this->p_input, "video-es", &val );
1421 if( val.i_int >= 0 )
1424 val_es.i_int = -VIDEO_ES;
1425 var_Set( p_this->p_input, "video-es", val_es );
1426 var_Set( p_this->p_input, "video-es", val );
1429 vlc_object_release( p_this->p_input );
1431 vlc_object_release( p_this );
1434 /*****************************************************************************
1435 * object variables callbacks: a bunch of object variables are used by the
1436 * interfaces to interact with the vout.
1437 *****************************************************************************/
1438 static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd,
1439 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1441 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1442 input_thread_t *p_input;
1445 char *psz_mode = newval.psz_string;
1446 char *psz_filter, *psz_deinterlace = NULL;
1447 (void)psz_cmd; (void)oldval; (void)p_data;
1449 var_Get( p_vout, "vout-filter", &val );
1450 psz_filter = val.psz_string;
1451 if( psz_filter ) psz_deinterlace = strstr( psz_filter, "deinterlace" );
1453 if( !psz_mode || !*psz_mode )
1455 if( psz_deinterlace )
1457 char *psz_src = psz_deinterlace + sizeof("deinterlace") - 1;
1458 if( psz_src[0] == ':' ) psz_src++;
1459 memmove( psz_deinterlace, psz_src, strlen(psz_src) + 1 );
1462 else if( !psz_deinterlace )
1464 psz_filter = realloc( psz_filter, strlen( psz_filter ) +
1465 sizeof(":deinterlace") );
1466 if( psz_filter && *psz_filter ) strcat( psz_filter, ":" );
1467 strcat( psz_filter, "deinterlace" );
1470 p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1472 if( !p_input ) return VLC_EGENERIC;
1474 if( psz_mode && *psz_mode )
1476 /* Modify input as well because the vout might have to be restarted */
1477 val.psz_string = psz_mode;
1478 var_Create( p_input, "deinterlace-mode", VLC_VAR_STRING );
1479 var_Set( p_input, "deinterlace-mode", val );
1481 vlc_object_release( p_input );
1483 val.b_bool = VLC_TRUE;
1484 var_Set( p_vout, "intf-change", val );
1486 val.psz_string = psz_filter;
1487 var_Set( p_vout, "vout-filter", val );
1493 static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd,
1494 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1496 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1497 input_thread_t *p_input;
1499 (void)psz_cmd; (void)oldval; (void)p_data;
1501 p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1505 msg_Err( p_vout, "Input not found" );
1506 return( VLC_EGENERIC );
1509 val.b_bool = VLC_TRUE;
1510 var_Set( p_vout, "intf-change", val );
1512 /* Modify input as well because the vout might have to be restarted */
1513 val.psz_string = newval.psz_string;
1514 var_Create( p_input, "vout-filter", VLC_VAR_STRING );
1516 var_Set( p_input, "vout-filter", val );
1518 /* Now restart current video stream */
1519 var_Get( p_input, "video-es", &val );
1520 if( val.i_int >= 0 )
1522 suxor_thread_t *p_suxor =
1523 vlc_object_create( p_vout, sizeof(suxor_thread_t) );
1524 p_suxor->p_input = p_input;
1525 p_vout->b_filter_change = VLC_TRUE;
1526 vlc_object_yield( p_input );
1527 vlc_thread_create( p_suxor, "suxor", SuxorRestartVideoES,
1528 VLC_THREAD_PRIORITY_LOW, VLC_FALSE );
1531 vlc_object_release( p_input );
1536 /*****************************************************************************
1537 * Video Filter2 stuff
1538 *****************************************************************************/
1539 static int ParseVideoFilter2Chain( vout_thread_t *p_vout, char *psz_vfilters )
1542 for( i = 0; i < p_vout->i_vfilters_cfg; i++ )
1544 struct config_chain_t *p_cfg =
1545 p_vout->p_vfilters_cfg[p_vout->i_vfilters_cfg];
1546 config_ChainDestroy( p_cfg );
1547 free( p_vout->psz_vfilters[p_vout->i_vfilters_cfg] );
1548 p_vout->psz_vfilters[p_vout->i_vfilters_cfg] = NULL;
1550 p_vout->i_vfilters_cfg = 0;
1551 if( psz_vfilters && *psz_vfilters )
1553 char *psz_parser = psz_vfilters;
1555 while( psz_parser && *psz_parser )
1557 psz_parser = config_ChainCreate(
1558 &p_vout->psz_vfilters[p_vout->i_vfilters_cfg],
1559 &p_vout->p_vfilters_cfg[p_vout->i_vfilters_cfg],
1561 msg_Dbg( p_vout, "adding vfilter: %s",
1562 p_vout->psz_vfilters[p_vout->i_vfilters_cfg] );
1563 p_vout->i_vfilters_cfg++;
1564 if( psz_parser && *psz_parser )
1566 if( p_vout->i_vfilters_cfg == MAX_VFILTERS )
1569 "maximum number of video filters reached. \"%s\" discarded",
1579 static int VideoFilter2Callback( vlc_object_t *p_this, char const *psz_cmd,
1580 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1582 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1583 (void)psz_cmd; (void)oldval; (void)p_data;
1585 vlc_mutex_lock( &p_vout->vfilter_lock );
1586 ParseVideoFilter2Chain( p_vout, newval.psz_string );
1587 p_vout->b_vfilter_change = VLC_TRUE;
1588 vlc_mutex_unlock( &p_vout->vfilter_lock );
1593 static void RemoveVideoFilters2( vout_thread_t *p_vout )
1596 for( i = 0; i < p_vout->i_vfilters; i++ )
1598 vlc_object_detach( p_vout->pp_vfilters[i] );
1599 if( p_vout->pp_vfilters[i]->p_module )
1601 module_Unneed( p_vout->pp_vfilters[i],
1602 p_vout->pp_vfilters[i]->p_module );
1605 free( p_vout->pp_vfilters[i]->p_owner );
1606 vlc_object_release( p_vout->pp_vfilters[i] );
1608 p_vout->i_vfilters = 0;
1611 static void DisplayTitleOnOSD( vout_thread_t *p_vout )
1613 input_thread_t *p_input;
1614 mtime_t i_now, i_stop;
1616 p_input = (input_thread_t *)vlc_object_find( p_vout,
1617 VLC_OBJECT_INPUT, FIND_ANYWHERE );
1621 i_stop = i_now + (mtime_t)(p_vout->i_title_timeout * 1000);
1622 char *psz_nowplaying =
1623 input_item_GetNowPlaying( input_GetItem( p_input ) );
1624 char *psz_artist = input_item_GetArtist( input_GetItem( p_input ) );
1625 char *psz_name = input_item_GetTitle( input_GetItem( p_input ) );
1626 if( EMPTY_STR( psz_name ) )
1629 psz_name = input_item_GetName( input_GetItem( p_input ) );
1631 if( !EMPTY_STR( psz_nowplaying ) )
1633 vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1634 psz_nowplaying, NULL,
1635 p_vout->i_title_position,
1636 30 + p_vout->fmt_in.i_width
1637 - p_vout->fmt_in.i_visible_width
1638 - p_vout->fmt_in.i_x_offset,
1639 20 + p_vout->fmt_in.i_y_offset,
1642 else if( !EMPTY_STR( psz_artist ) )
1644 char *psz_string = NULL;
1645 if( asprintf( &psz_string, "%s - %s", psz_name, psz_artist ) != -1 )
1647 vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1649 p_vout->i_title_position,
1650 30 + p_vout->fmt_in.i_width
1651 - p_vout->fmt_in.i_visible_width
1652 - p_vout->fmt_in.i_x_offset,
1653 20 + p_vout->fmt_in.i_y_offset,
1660 vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1662 p_vout->i_title_position,
1663 30 + p_vout->fmt_in.i_width
1664 - p_vout->fmt_in.i_visible_width
1665 - p_vout->fmt_in.i_x_offset,
1666 20 + p_vout->fmt_in.i_y_offset,
1669 vlc_object_release( p_input );
1672 free( psz_nowplaying );