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 *****************************************************************************/
34 #include <stdlib.h> /* free() */
38 #ifdef HAVE_SYS_TIMES_H
39 # include <sys/times.h>
43 #include <vlc_playlist.h>
45 #include <vlc_filter.h>
48 #if defined( __APPLE__ )
49 /* Include darwin_specific.h here if needed */
52 /** FIXME This is quite ugly but needed while we don't have counters
54 #include "input/input_internal.h"
56 #include "modules/modules.h"
58 /*****************************************************************************
60 *****************************************************************************/
61 static int InitThread ( vout_thread_t * );
62 static void RunThread ( vout_thread_t * );
63 static void ErrorThread ( vout_thread_t * );
64 static void EndThread ( vout_thread_t * );
65 static void DestroyThread ( vout_thread_t * );
67 static void AspectRatio ( int, int *, int * );
68 static int BinaryLog ( uint32_t );
69 static void MaskToShift ( int *, int *, uint32_t );
71 /* Object variables callbacks */
72 static int DeinterlaceCallback( vlc_object_t *, char const *,
73 vlc_value_t, vlc_value_t, void * );
74 static int FilterCallback( vlc_object_t *, char const *,
75 vlc_value_t, vlc_value_t, void * );
76 static int VideoFilter2Callback( vlc_object_t *, char const *,
77 vlc_value_t, vlc_value_t, void * );
79 /* From vout_intf.c */
80 int vout_Snapshot( vout_thread_t *, picture_t * );
82 /* Video filter2 parsing */
83 static int ParseVideoFilter2Chain( vout_thread_t *, char * );
84 static void RemoveVideoFilters2( vout_thread_t *p_vout );
86 /* Display media title in OSD */
87 static void DisplayTitleOnOSD( vout_thread_t *p_vout );
89 /*****************************************************************************
90 * Video Filter2 functions
91 *****************************************************************************/
92 struct filter_owner_sys_t
94 vout_thread_t *p_vout;
97 static picture_t *video_new_buffer_filter( filter_t *p_filter )
100 vout_thread_t *p_vout = p_filter->p_owner->p_vout;
102 p_picture = vout_CreatePicture( p_vout, 0, 0, 0 );
107 static void video_del_buffer_filter( filter_t *p_filter, picture_t *p_pic )
109 vout_DestroyPicture( p_filter->p_owner->p_vout, p_pic );
112 /*****************************************************************************
113 * vout_Request: find a video output thread, create one, or destroy one.
114 *****************************************************************************
115 * This function looks for a video output thread matching the current
116 * properties. If not found, it spawns a new one.
117 *****************************************************************************/
118 vout_thread_t *__vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout,
119 video_format_t *p_fmt )
123 /* Reattach video output to playlist before bailing out */
126 playlist_t *p_playlist = pl_Yield( p_this );
127 spu_Attach( p_vout->p_spu, p_this, VLC_FALSE );
128 vlc_object_detach( p_vout );
129 vlc_object_attach( p_vout, p_playlist );
130 pl_Release( p_this );
135 /* If a video output was provided, lock it, otherwise look for one. */
138 vlc_object_yield( p_vout );
142 p_vout = vlc_object_find( p_this, VLC_OBJECT_VOUT, FIND_CHILD );
146 playlist_t *p_playlist = pl_Yield( p_this );
147 vlc_mutex_lock( &p_playlist->gc_lock );
148 p_vout = vlc_object_find( p_playlist,
149 VLC_OBJECT_VOUT, FIND_CHILD );
150 /* only first children of p_input for unused vout */
151 if( p_vout && p_vout->p_parent != (vlc_object_t *)p_playlist )
153 vlc_object_release( p_vout );
157 vlc_object_detach( p_vout ); /* Remove it from the GC */
158 vlc_mutex_unlock( &p_playlist->gc_lock );
159 pl_Release( p_this );
163 /* If we now have a video output, check it has the right properties */
166 char *psz_filter_chain;
169 /* We don't directly check for the "vout-filter" variable for obvious
170 * performance reasons. */
171 if( p_vout->b_filter_change )
173 var_Get( p_vout, "vout-filter", &val );
174 psz_filter_chain = val.psz_string;
176 if( psz_filter_chain && !*psz_filter_chain )
178 free( psz_filter_chain );
179 psz_filter_chain = NULL;
181 if( p_vout->psz_filter_chain && !*p_vout->psz_filter_chain )
183 free( p_vout->psz_filter_chain );
184 p_vout->psz_filter_chain = NULL;
187 if( !psz_filter_chain && !p_vout->psz_filter_chain )
189 p_vout->b_filter_change = VLC_FALSE;
192 if( psz_filter_chain ) free( psz_filter_chain );
195 if( ( p_vout->fmt_render.i_width != p_fmt->i_width ) ||
196 ( p_vout->fmt_render.i_height != p_fmt->i_height ) ||
197 ( p_vout->fmt_render.i_aspect != p_fmt->i_aspect ) ||
198 p_vout->b_filter_change )
200 /* We are not interested in this format, close this vout */
201 vlc_object_release( p_vout );
202 vout_Destroy( p_vout );
207 /* This video output is cool! Hijack it. */
208 spu_Attach( p_vout->p_spu, p_this, VLC_TRUE );
209 vlc_object_attach( p_vout, p_this );
210 if( p_vout->b_title_show )
211 DisplayTitleOnOSD( p_vout );
212 vlc_object_release( p_vout );
218 msg_Dbg( p_this, "no usable vout present, spawning one" );
220 p_vout = vout_Create( p_this, p_fmt );
226 /*****************************************************************************
227 * vout_Create: creates a new video output thread
228 *****************************************************************************
229 * This function creates a new video output thread, and returns a pointer
230 * to its description. On error, it returns NULL.
231 *****************************************************************************/
232 vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt )
234 vout_thread_t * p_vout; /* thread descriptor */
235 input_thread_t * p_input_thread;
236 int i_index; /* loop variable */
237 vlc_value_t val, text;
239 unsigned int i_width = p_fmt->i_width;
240 unsigned int i_height = p_fmt->i_height;
241 vlc_fourcc_t i_chroma = p_fmt->i_chroma;
242 unsigned int i_aspect = p_fmt->i_aspect;
244 config_chain_t *p_cfg;
248 /* Allocate descriptor */
249 p_vout = vlc_object_create( p_parent, VLC_OBJECT_VOUT );
252 msg_Err( p_parent, "out of memory" );
256 /* Initialize pictures - translation tables and functions
257 * will be initialized later in InitThread */
258 for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++)
260 p_vout->p_picture[i_index].pf_lock = NULL;
261 p_vout->p_picture[i_index].pf_unlock = NULL;
262 p_vout->p_picture[i_index].i_status = FREE_PICTURE;
263 p_vout->p_picture[i_index].i_type = EMPTY_PICTURE;
264 p_vout->p_picture[i_index].b_slow = 0;
267 /* No images in the heap */
268 p_vout->i_heap_size = 0;
270 /* Initialize the rendering heap */
271 I_RENDERPICTURES = 0;
273 vlc_ureduce( &p_fmt->i_sar_num, &p_fmt->i_sar_den,
274 p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 );
275 p_vout->fmt_render = *p_fmt; /* FIXME palette */
276 p_vout->fmt_in = *p_fmt; /* FIXME palette */
278 p_vout->render.i_width = i_width;
279 p_vout->render.i_height = i_height;
280 p_vout->render.i_chroma = i_chroma;
281 p_vout->render.i_aspect = i_aspect;
283 p_vout->render.i_rmask = 0;
284 p_vout->render.i_gmask = 0;
285 p_vout->render.i_bmask = 0;
287 p_vout->render.i_last_used_pic = -1;
288 p_vout->render.b_allow_modify_pics = 1;
290 /* Zero the output heap */
291 I_OUTPUTPICTURES = 0;
292 p_vout->output.i_width = 0;
293 p_vout->output.i_height = 0;
294 p_vout->output.i_chroma = 0;
295 p_vout->output.i_aspect = 0;
297 p_vout->output.i_rmask = 0;
298 p_vout->output.i_gmask = 0;
299 p_vout->output.i_bmask = 0;
301 /* Initialize misc stuff */
302 p_vout->i_changes = 0;
304 p_vout->b_grayscale = 0;
306 p_vout->b_interface = 0;
308 p_vout->b_fullscreen = 0;
309 p_vout->i_alignment = 0;
310 p_vout->render_time = 10;
311 p_vout->c_fps_samples = 0;
312 p_vout->b_filter_change = 0;
313 p_vout->pf_control = 0;
314 p_vout->p_parent_intf = 0;
315 p_vout->i_par_num = p_vout->i_par_den = 1;
317 /* Initialize locks */
318 vlc_mutex_init( p_vout, &p_vout->picture_lock );
319 vlc_mutex_init( p_vout, &p_vout->change_lock );
320 vlc_mutex_init( p_vout, &p_vout->vfilter_lock );
322 /* Mouse coordinates */
323 var_Create( p_vout, "mouse-x", VLC_VAR_INTEGER );
324 var_Create( p_vout, "mouse-y", VLC_VAR_INTEGER );
325 var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER );
326 var_Create( p_vout, "mouse-moved", VLC_VAR_BOOL );
327 var_Create( p_vout, "mouse-clicked", VLC_VAR_INTEGER );
329 /* Initialize subpicture unit */
330 p_vout->p_spu = spu_Create( p_vout );
331 spu_Attach( p_vout->p_spu, p_parent, VLC_TRUE );
333 /* Attach the new object now so we can use var inheritance below */
334 vlc_object_attach( p_vout, p_parent );
336 spu_Init( p_vout->p_spu );
338 /* Take care of some "interface/control" related initialisations */
339 vout_IntfInit( p_vout );
341 /* If the parent is not a VOUT object, that means we are at the start of
342 * the video output pipe */
343 if( p_parent->i_object_type != VLC_OBJECT_VOUT )
345 /* Look for the default filter configuration */
346 var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
347 var_Get( p_vout, "vout-filter", &val );
348 p_vout->psz_filter_chain = val.psz_string;
350 /* Apply video filter2 objects on the first vout */
351 var_Create( p_vout, "video-filter",
352 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
353 var_Get( p_vout, "video-filter", &val );
354 ParseVideoFilter2Chain( p_vout, val.psz_string );
355 free( val.psz_string );
359 /* continue the parent's filter chain */
362 /* Ugly hack to jump to our configuration chain */
363 p_vout->psz_filter_chain
364 = ((vout_thread_t *)p_parent)->psz_filter_chain;
365 p_vout->psz_filter_chain
366 = config_ChainCreate( &psz_tmp, &p_cfg, p_vout->psz_filter_chain );
367 config_ChainDestroy( p_cfg );
370 /* Create a video filter2 var ... but don't inherit values */
371 var_Create( p_vout, "video-filter", VLC_VAR_STRING );
372 ParseVideoFilter2Chain( p_vout, NULL );
375 var_AddCallback( p_vout, "video-filter", VideoFilter2Callback, NULL );
376 p_vout->b_vfilter_change = VLC_TRUE;
377 p_vout->i_vfilters = 0;
379 /* Choose the video output module */
380 if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain )
382 var_Create( p_vout, "vout", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
383 var_Get( p_vout, "vout", &val );
384 psz_parser = val.psz_string;
388 psz_parser = strdup( p_vout->psz_filter_chain );
391 /* Create the vout thread */
392 config_ChainCreate( &psz_name, &p_cfg, psz_parser );
394 p_vout->p_cfg = p_cfg;
395 p_vout->p_module = module_Need( p_vout,
396 ( p_vout->psz_filter_chain && *p_vout->psz_filter_chain ) ?
397 "video filter" : "video output", psz_name, p_vout->psz_filter_chain && *p_vout->psz_filter_chain );
400 if( p_vout->p_module == NULL )
402 msg_Err( p_vout, "no suitable vout module" );
403 vlc_object_detach( p_vout );
404 vlc_object_destroy( p_vout );
408 /* Create a few object variables for interface interaction */
409 var_Create( p_vout, "deinterlace", VLC_VAR_STRING | VLC_VAR_HASCHOICE );
410 text.psz_string = _("Deinterlace");
411 var_Change( p_vout, "deinterlace", VLC_VAR_SETTEXT, &text, NULL );
412 val.psz_string = (char *)""; text.psz_string = _("Disable");
413 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
414 val.psz_string = (char *)"discard"; text.psz_string = _("Discard");
415 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
416 val.psz_string = (char *)"blend"; text.psz_string = _("Blend");
417 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
418 val.psz_string = (char *)"mean"; text.psz_string = _("Mean");
419 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
420 val.psz_string = (char *)"bob"; text.psz_string = _("Bob");
421 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
422 val.psz_string = (char *)"linear"; text.psz_string = _("Linear");
423 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
424 val.psz_string = (char *)"x"; text.psz_string = (char *)"X";
425 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
427 if( var_Get( p_vout, "deinterlace-mode", &val ) == VLC_SUCCESS )
429 var_Set( p_vout, "deinterlace", val );
430 if( val.psz_string ) free( val.psz_string );
432 var_AddCallback( p_vout, "deinterlace", DeinterlaceCallback, NULL );
434 var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
435 text.psz_string = _("Filters");
436 var_Change( p_vout, "vout-filter", VLC_VAR_SETTEXT, &text, NULL );
437 var_AddCallback( p_vout, "vout-filter", FilterCallback, NULL );
439 /* Calculate delay created by internal caching */
440 p_input_thread = (input_thread_t *)vlc_object_find( p_vout,
441 VLC_OBJECT_INPUT, FIND_ANYWHERE );
444 p_vout->i_pts_delay = p_input_thread->i_pts_delay;
445 vlc_object_release( p_input_thread );
449 p_vout->i_pts_delay = DEFAULT_PTS_DELAY;
452 if( vlc_thread_create( p_vout, "video output", RunThread,
453 VLC_THREAD_PRIORITY_OUTPUT, VLC_TRUE ) )
455 msg_Err( p_vout, "out of memory" );
456 module_Unneed( p_vout, p_vout->p_module );
457 vlc_object_detach( p_vout );
458 vlc_object_destroy( p_vout );
462 if( p_vout->b_error )
464 msg_Err( p_vout, "video output creation failed" );
466 /* Make sure the thread is destroyed */
467 vlc_object_kill( p_vout );
468 vlc_thread_join( p_vout );
470 vlc_object_detach( p_vout );
471 vlc_object_destroy( p_vout );
478 /*****************************************************************************
479 * vout_Destroy: destroys a previously created video output
480 *****************************************************************************
481 * Destroy a terminated thread.
482 * The function will request a destruction of the specified thread. If pi_error
483 * is NULL, it will return once the thread is destroyed. Else, it will be
484 * update using one of the THREAD_* constants.
485 *****************************************************************************/
486 void vout_Destroy( vout_thread_t *p_vout )
488 vout_thread_t *p_another_vout;
489 playlist_t *p_playlist = pl_Yield( p_vout );
491 /* Request thread destruction */
492 vlc_object_kill( p_vout );
493 vlc_thread_join( p_vout );
495 var_Destroy( p_vout, "intf-change" );
497 if( p_vout->psz_filter_chain ) free( p_vout->psz_filter_chain );
499 config_ChainDestroy( p_vout->p_cfg );
502 vlc_object_destroy( p_vout );
504 /* This is a dirty hack for mostly Linux, where there is no way to get the GUI
505 back if you closed it while playing video. This is solved in Mac OS X,
506 where we have this novelty called menubar, that will always allow you access
507 to the applications main functionality. They should try that on linux sometime */
508 p_another_vout = vlc_object_find( p_playlist,
509 VLC_OBJECT_VOUT, FIND_ANYWHERE );
510 if( p_another_vout == NULL )
513 val.b_bool = VLC_TRUE;
514 var_Set( p_playlist, "intf-show", val );
518 vlc_object_release( p_another_vout );
521 vlc_object_release( p_playlist );
524 /*****************************************************************************
525 * InitThread: initialize video output thread
526 *****************************************************************************
527 * This function is called from RunThread and performs the second step of the
528 * initialization. It returns 0 on success. Note that the thread's flag are not
529 * modified inside this function.
530 *****************************************************************************/
531 static int InitThread( vout_thread_t *p_vout )
533 int i, i_aspect_x, i_aspect_y;
535 vlc_mutex_lock( &p_vout->change_lock );
541 /* Initialize output method, it allocates direct buffers for us */
542 if( p_vout->pf_init( p_vout ) )
544 vlc_mutex_unlock( &p_vout->change_lock );
548 if( !I_OUTPUTPICTURES )
550 msg_Err( p_vout, "plugin was unable to allocate at least "
551 "one direct buffer" );
552 p_vout->pf_end( p_vout );
553 vlc_mutex_unlock( &p_vout->change_lock );
557 if( I_OUTPUTPICTURES > VOUT_MAX_PICTURES )
559 msg_Err( p_vout, "plugin allocated too many direct buffers, "
560 "our internal buffers must have overflown." );
561 p_vout->pf_end( p_vout );
562 vlc_mutex_unlock( &p_vout->change_lock );
566 msg_Dbg( p_vout, "got %i direct buffer(s)", I_OUTPUTPICTURES );
568 AspectRatio( p_vout->fmt_render.i_aspect, &i_aspect_x, &i_aspect_y );
570 msg_Dbg( p_vout, "picture in %ix%i (%i,%i,%ix%i), "
571 "chroma %4.4s, ar %i:%i, sar %i:%i",
572 p_vout->fmt_render.i_width, p_vout->fmt_render.i_height,
573 p_vout->fmt_render.i_x_offset, p_vout->fmt_render.i_y_offset,
574 p_vout->fmt_render.i_visible_width,
575 p_vout->fmt_render.i_visible_height,
576 (char*)&p_vout->fmt_render.i_chroma,
577 i_aspect_x, i_aspect_y,
578 p_vout->fmt_render.i_sar_num, p_vout->fmt_render.i_sar_den );
580 AspectRatio( p_vout->fmt_in.i_aspect, &i_aspect_x, &i_aspect_y );
582 msg_Dbg( p_vout, "picture user %ix%i (%i,%i,%ix%i), "
583 "chroma %4.4s, ar %i:%i, sar %i:%i",
584 p_vout->fmt_in.i_width, p_vout->fmt_in.i_height,
585 p_vout->fmt_in.i_x_offset, p_vout->fmt_in.i_y_offset,
586 p_vout->fmt_in.i_visible_width,
587 p_vout->fmt_in.i_visible_height,
588 (char*)&p_vout->fmt_in.i_chroma,
589 i_aspect_x, i_aspect_y,
590 p_vout->fmt_in.i_sar_num, p_vout->fmt_in.i_sar_den );
592 if( !p_vout->fmt_out.i_width || !p_vout->fmt_out.i_height )
594 p_vout->fmt_out.i_width = p_vout->fmt_out.i_visible_width =
595 p_vout->output.i_width;
596 p_vout->fmt_out.i_height = p_vout->fmt_out.i_visible_height =
597 p_vout->output.i_height;
598 p_vout->fmt_out.i_x_offset = p_vout->fmt_out.i_y_offset = 0;
600 p_vout->fmt_out.i_aspect = p_vout->output.i_aspect;
601 p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
603 if( !p_vout->fmt_out.i_sar_num || !p_vout->fmt_out.i_sar_num )
605 p_vout->fmt_out.i_sar_num = p_vout->fmt_out.i_aspect *
606 p_vout->fmt_out.i_height;
607 p_vout->fmt_out.i_sar_den = VOUT_ASPECT_FACTOR *
608 p_vout->fmt_out.i_width;
611 vlc_ureduce( &p_vout->fmt_out.i_sar_num, &p_vout->fmt_out.i_sar_den,
612 p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den, 0 );
614 AspectRatio( p_vout->fmt_out.i_aspect, &i_aspect_x, &i_aspect_y );
616 msg_Dbg( p_vout, "picture out %ix%i (%i,%i,%ix%i), "
617 "chroma %4.4s, ar %i:%i, sar %i:%i",
618 p_vout->fmt_out.i_width, p_vout->fmt_out.i_height,
619 p_vout->fmt_out.i_x_offset, p_vout->fmt_out.i_y_offset,
620 p_vout->fmt_out.i_visible_width,
621 p_vout->fmt_out.i_visible_height,
622 (char*)&p_vout->fmt_out.i_chroma,
623 i_aspect_x, i_aspect_y,
624 p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den );
626 /* Calculate shifts from system-updated masks */
627 MaskToShift( &p_vout->output.i_lrshift, &p_vout->output.i_rrshift,
628 p_vout->output.i_rmask );
629 MaskToShift( &p_vout->output.i_lgshift, &p_vout->output.i_rgshift,
630 p_vout->output.i_gmask );
631 MaskToShift( &p_vout->output.i_lbshift, &p_vout->output.i_rbshift,
632 p_vout->output.i_bmask );
634 /* Check whether we managed to create direct buffers similar to
635 * the render buffers, ie same size and chroma */
636 if( ( p_vout->output.i_width == p_vout->render.i_width )
637 && ( p_vout->output.i_height == p_vout->render.i_height )
638 && ( vout_ChromaCmp( p_vout->output.i_chroma, p_vout->render.i_chroma ) ) )
640 /* Cool ! We have direct buffers, we can ask the decoder to
641 * directly decode into them ! Map the first render buffers to
642 * the first direct buffers, but keep the first direct buffer
643 * for memcpy operations */
644 p_vout->b_direct = 1;
646 for( i = 1; i < VOUT_MAX_PICTURES; i++ )
648 if( p_vout->p_picture[ i ].i_type != DIRECT_PICTURE &&
649 I_RENDERPICTURES >= VOUT_MIN_DIRECT_PICTURES - 1 &&
650 p_vout->p_picture[ i - 1 ].i_type == DIRECT_PICTURE )
652 /* We have enough direct buffers so there's no need to
653 * try to use system memory buffers. */
656 PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
660 msg_Dbg( p_vout, "direct render, mapping "
661 "render pictures 0-%i to system pictures 1-%i",
662 VOUT_MAX_PICTURES - 2, VOUT_MAX_PICTURES - 1 );
666 /* Rats... Something is wrong here, we could not find an output
667 * plugin able to directly render what we decode. See if we can
668 * find a chroma plugin to do the conversion */
669 p_vout->b_direct = 0;
671 /* Choose the best module */
672 p_vout->chroma.p_module = module_Need( p_vout, "chroma", NULL, 0 );
674 if( p_vout->chroma.p_module == NULL )
676 msg_Err( p_vout, "no chroma module for %4.4s to %4.4s",
677 (char*)&p_vout->render.i_chroma,
678 (char*)&p_vout->output.i_chroma );
679 p_vout->pf_end( p_vout );
680 vlc_mutex_unlock( &p_vout->change_lock );
684 msg_Dbg( p_vout, "indirect render, mapping "
685 "render pictures 0-%i to system pictures %i-%i",
686 VOUT_MAX_PICTURES - 1, I_OUTPUTPICTURES,
687 I_OUTPUTPICTURES + VOUT_MAX_PICTURES - 1 );
689 /* Append render buffers after the direct buffers */
690 for( i = I_OUTPUTPICTURES; i < 2 * VOUT_MAX_PICTURES; i++ )
692 PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
695 /* Check if we have enough render pictures */
696 if( I_RENDERPICTURES == VOUT_MAX_PICTURES )
701 /* Link pictures back to their heap */
702 for( i = 0 ; i < I_RENDERPICTURES ; i++ )
704 PP_RENDERPICTURE[ i ]->p_heap = &p_vout->render;
707 for( i = 0 ; i < I_OUTPUTPICTURES ; i++ )
709 PP_OUTPUTPICTURE[ i ]->p_heap = &p_vout->output;
712 /* XXX XXX mark thread ready */
716 /*****************************************************************************
717 * RunThread: video output thread
718 *****************************************************************************
719 * Video output thread. This function does only returns when the thread is
720 * terminated. It handles the pictures arriving in the video heap and the
721 * display device events.
722 *****************************************************************************/
723 static void RunThread( vout_thread_t *p_vout)
725 int i_index; /* index in heap */
726 int i_idle_loops = 0; /* loops without displaying a picture */
727 mtime_t current_date; /* current date */
728 mtime_t display_date; /* display date */
730 picture_t * p_picture; /* picture pointer */
731 picture_t * p_last_picture = NULL; /* last picture */
732 picture_t * p_directbuffer; /* direct buffer to display */
734 subpicture_t * p_subpic = NULL; /* subpicture pointer */
736 input_thread_t *p_input = NULL ; /* Parent input, if it exists */
739 vlc_bool_t b_drop_late;
741 int i_displayed = 0, i_lost = 0, i_loops = 0;
746 p_vout->b_error = InitThread( p_vout );
748 var_Create( p_vout, "drop-late-frames", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
749 var_Get( p_vout, "drop-late-frames", &val );
750 b_drop_late = val.b_bool;
752 /* signal the creation of the vout */
753 vlc_thread_ready( p_vout );
755 if( p_vout->b_error )
757 /* Destroy thread structures allocated by Create and InitThread */
758 DestroyThread( p_vout );
762 if( p_vout->b_title_show )
763 DisplayTitleOnOSD( p_vout );
766 * Main loop - it is not executed if an error occurred during
769 while( (!p_vout->b_die) && (!p_vout->b_error) )
771 /* Initialize loop variables */
774 current_date = mdate();
776 if( p_input && p_input->b_die )
778 vlc_object_release( p_input );
783 if( i_loops % 20 == 0 )
787 p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT,
792 vlc_mutex_lock( &p_input->p->counters.counters_lock );
793 stats_UpdateInteger( p_vout, p_input->p->counters.p_lost_pictures,
795 stats_UpdateInteger( p_vout,
796 p_input->p->counters.p_displayed_pictures,
798 i_displayed = i_lost = 0;
799 vlc_mutex_unlock( &p_input->p->counters.counters_lock );
804 if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
806 msg_Dbg( p_vout, "picture heap: %d/%d",
807 I_RENDERPICTURES, p_vout->i_heap_size );
812 * Find the picture to display (the one with the earliest date).
813 * This operation does not need lock, since only READY_PICTUREs
815 for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
817 if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
818 && ( (p_picture == NULL) ||
819 (PP_RENDERPICTURE[i_index]->date < display_date) ) )
821 p_picture = PP_RENDERPICTURE[i_index];
822 display_date = p_picture->date;
828 /* If we met the last picture, parse again to see whether there is
829 * a more appropriate one. */
830 if( p_picture == p_last_picture )
832 for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
834 if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
835 && (PP_RENDERPICTURE[i_index] != p_last_picture)
836 && ((p_picture == p_last_picture) ||
837 (PP_RENDERPICTURE[i_index]->date < display_date)) )
839 p_picture = PP_RENDERPICTURE[i_index];
840 display_date = p_picture->date;
845 /* If we found better than the last picture, destroy it */
846 if( p_last_picture && p_picture != p_last_picture )
848 vlc_mutex_lock( &p_vout->picture_lock );
849 if( p_last_picture->i_refcount )
851 p_last_picture->i_status = DISPLAYED_PICTURE;
855 p_last_picture->i_status = DESTROYED_PICTURE;
856 p_vout->i_heap_size--;
858 vlc_mutex_unlock( &p_vout->picture_lock );
859 p_last_picture = NULL;
862 /* Compute FPS rate */
863 p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ]
866 if( !p_picture->b_force &&
867 p_picture != p_last_picture &&
868 display_date < current_date + p_vout->render_time &&
871 /* Picture is late: it will be destroyed and the thread
872 * will directly choose the next picture */
873 vlc_mutex_lock( &p_vout->picture_lock );
874 if( p_picture->i_refcount )
876 /* Pretend we displayed the picture, but don't destroy
877 * it since the decoder might still need it. */
878 p_picture->i_status = DISPLAYED_PICTURE;
882 /* Destroy the picture without displaying it */
883 p_picture->i_status = DESTROYED_PICTURE;
884 p_vout->i_heap_size--;
886 msg_Warn( p_vout, "late picture skipped ("I64Fd")",
887 current_date - display_date );
889 vlc_mutex_unlock( &p_vout->picture_lock );
895 current_date + p_vout->i_pts_delay + VOUT_BOGUS_DELAY )
897 /* Picture is waaay too early: it will be destroyed */
898 vlc_mutex_lock( &p_vout->picture_lock );
899 if( p_picture->i_refcount )
901 /* Pretend we displayed the picture, but don't destroy
902 * it since the decoder might still need it. */
903 p_picture->i_status = DISPLAYED_PICTURE;
907 /* Destroy the picture without displaying it */
908 p_picture->i_status = DESTROYED_PICTURE;
909 p_vout->i_heap_size--;
912 msg_Warn( p_vout, "vout warning: early picture skipped "
913 "("I64Fd")", display_date - current_date
914 - p_vout->i_pts_delay );
915 vlc_mutex_unlock( &p_vout->picture_lock );
920 if( display_date > current_date + VOUT_DISPLAY_DELAY )
922 /* A picture is ready to be rendered, but its rendering date
923 * is far from the current one so the thread will perform an
924 * empty loop as if no picture were found. The picture state
929 else if( p_picture == p_last_picture )
931 /* We are asked to repeat the previous picture, but we first
932 * wait for a couple of idle loops */
933 if( i_idle_loops < 4 )
940 /* We set the display date to something high, otherwise
941 * we'll have lots of problems with late pictures */
942 display_date = current_date + p_vout->render_time;
947 if( p_picture == NULL )
952 /* Video Filter2 stuff */
953 if( p_vout->b_vfilter_change == VLC_TRUE )
956 vlc_mutex_lock( &p_vout->vfilter_lock );
957 RemoveVideoFilters2( p_vout );
958 for( i = 0; i < p_vout->i_vfilters_cfg; i++ )
960 filter_t *p_vfilter =
961 p_vout->pp_vfilters[p_vout->i_vfilters] =
962 vlc_object_create( p_vout, VLC_OBJECT_FILTER );
964 vlc_object_attach( p_vfilter, p_vout );
966 p_vfilter->pf_vout_buffer_new = video_new_buffer_filter;
967 p_vfilter->pf_vout_buffer_del = video_del_buffer_filter;
969 if( !p_vout->i_vfilters )
971 p_vfilter->fmt_in.video = p_vout->fmt_render;
975 p_vfilter->fmt_in.video = (p_vfilter-1)->fmt_out.video;
977 /* TODO: one day filters in the middle of the chain might
978 * have a different fmt_out.video than fmt_render ... */
979 p_vfilter->fmt_out.video = p_vout->fmt_render;
981 p_vfilter->p_cfg = p_vout->p_vfilters_cfg[i];
982 p_vfilter->p_module = module_Need( p_vfilter, "video filter2",
983 p_vout->psz_vfilters[i],
986 if( p_vfilter->p_module )
989 malloc( sizeof( filter_owner_sys_t ) );
990 p_vfilter->p_owner->p_vout = p_vout;
991 p_vout->i_vfilters++;
992 msg_Dbg( p_vout, "video filter found (%s)",
993 p_vout->psz_vfilters[i] );
997 msg_Err( p_vout, "no video filter found (%s)",
998 p_vout->psz_vfilters[i] );
999 vlc_object_detach( p_vfilter );
1000 vlc_object_destroy( p_vfilter );
1003 p_vout->b_vfilter_change = VLC_FALSE;
1004 vlc_mutex_unlock( &p_vout->vfilter_lock );
1010 for( i = 0; i < p_vout->i_vfilters; i++ )
1012 picture_t *p_old = p_picture;
1013 p_picture = p_vout->pp_vfilters[i]->pf_video_filter(
1014 p_vout->pp_vfilters[i], p_picture );
1019 /* FIXME: this is kind of wrong
1020 * if you have 2 or more vfilters and the 2nd breaks,
1021 * on the next loop the 1st one will be applied again */
1023 /* if p_old and p_picture are the same (ie the filter
1024 * worked on the old picture), then following code is
1025 * still alright since i_status gets changed back to
1026 * the right value */
1027 if( p_old->i_refcount )
1029 p_old->i_status = DISPLAYED_PICTURE;
1033 p_old->i_status = DESTROYED_PICTURE;
1035 p_picture->i_status = READY_PICTURE;
1039 if( p_picture && p_vout->b_snapshot )
1041 p_vout->b_snapshot = VLC_FALSE;
1042 vout_Snapshot( p_vout, p_picture );
1046 * Check for subpictures to display
1048 if( display_date > 0 )
1052 p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT,
1055 p_subpic = spu_SortSubpictures( p_vout->p_spu, display_date,
1056 p_input ? var_GetBool( p_input, "state" ) == PAUSE_S : VLC_FALSE );
1063 p_directbuffer = vout_RenderPicture( p_vout, p_picture, p_subpic );
1066 * Call the plugin-specific rendering method if there is one
1068 if( p_picture != NULL && p_directbuffer != NULL && p_vout->pf_render )
1070 /* Render the direct buffer returned by vout_RenderPicture */
1071 p_vout->pf_render( p_vout, p_directbuffer );
1077 if( display_date != 0 && p_directbuffer != NULL )
1079 mtime_t current_render_time = mdate() - current_date;
1080 /* if render time is very large we don't include it in the mean */
1081 if( current_render_time < p_vout->render_time +
1082 VOUT_DISPLAY_DELAY )
1084 /* Store render time using a sliding mean weighting to
1085 * current value in a 3 to 1 ratio*/
1086 p_vout->render_time *= 3;
1087 p_vout->render_time += current_render_time;
1088 p_vout->render_time >>= 2;
1092 /* Give back change lock */
1093 vlc_mutex_unlock( &p_vout->change_lock );
1095 /* Sleep a while or until a given date */
1096 if( display_date != 0 )
1098 /* If there are filters in the chain, better give them the picture
1100 if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain )
1102 mwait( display_date - VOUT_MWAIT_TOLERANCE );
1107 msleep( VOUT_IDLE_SLEEP );
1110 /* On awakening, take back lock and send immediately picture
1112 vlc_mutex_lock( &p_vout->change_lock );
1115 * Display the previously rendered picture
1117 if( p_picture != NULL && p_directbuffer != NULL )
1119 /* Display the direct buffer returned by vout_RenderPicture */
1120 if( p_vout->pf_display )
1122 p_vout->pf_display( p_vout, p_directbuffer );
1125 /* Tell the vout this was the last picture and that it does not
1126 * need to be forced anymore. */
1127 p_last_picture = p_picture;
1128 p_last_picture->b_force = 0;
1131 if( p_picture != NULL )
1133 /* Reinitialize idle loop count */
1138 * Check events and manage thread
1140 if( p_vout->pf_manage && p_vout->pf_manage( p_vout ) )
1142 /* A fatal error occurred, and the thread must terminate
1143 * immediately, without displaying anything - setting b_error to 1
1144 * causes the immediate end of the main while() loop. */
1145 p_vout->b_error = 1;
1148 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
1150 /* this must only happen when the vout plugin is incapable of
1151 * rescaling the picture itself. In this case we need to destroy
1152 * the current picture buffers and recreate new ones with the right
1156 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
1158 p_vout->pf_end( p_vout );
1159 for( i = 0; i < I_OUTPUTPICTURES; i++ )
1160 p_vout->p_picture[ i ].i_status = FREE_PICTURE;
1162 I_OUTPUTPICTURES = 0;
1163 if( p_vout->pf_init( p_vout ) )
1165 msg_Err( p_vout, "cannot resize display" );
1166 /* FIXME: pf_end will be called again in EndThread() */
1167 p_vout->b_error = 1;
1170 /* Need to reinitialise the chroma plugin */
1171 if( p_vout->chroma.p_module )
1173 if( p_vout->chroma.p_module->pf_deactivate )
1174 p_vout->chroma.p_module->pf_deactivate( VLC_OBJECT(p_vout) );
1175 p_vout->chroma.p_module->pf_activate( VLC_OBJECT(p_vout) );
1179 if( p_vout->i_changes & VOUT_PICTURE_BUFFERS_CHANGE )
1181 /* This happens when the picture buffers need to be recreated.
1182 * This is useful on multimonitor displays for instance.
1184 * Warning: This only works when the vout creates only 1 picture
1186 p_vout->i_changes &= ~VOUT_PICTURE_BUFFERS_CHANGE;
1188 if( !p_vout->b_direct )
1190 module_Unneed( p_vout, p_vout->chroma.p_module );
1193 vlc_mutex_lock( &p_vout->picture_lock );
1195 p_vout->pf_end( p_vout );
1197 I_OUTPUTPICTURES = I_RENDERPICTURES = 0;
1199 p_vout->b_error = InitThread( p_vout );
1201 vlc_mutex_unlock( &p_vout->picture_lock );
1207 vlc_object_release( p_input );
1211 * Error loop - wait until the thread destruction is requested
1213 if( p_vout->b_error )
1215 ErrorThread( p_vout );
1219 EndThread( p_vout );
1221 /* Destroy thread structures allocated by CreateThread */
1222 DestroyThread( p_vout );
1225 /*****************************************************************************
1226 * ErrorThread: RunThread() error loop
1227 *****************************************************************************
1228 * This function is called when an error occurred during thread main's loop.
1229 * The thread can still receive feed, but must be ready to terminate as soon
1231 *****************************************************************************/
1232 static void ErrorThread( vout_thread_t *p_vout )
1234 /* Wait until a `die' order */
1235 while( !p_vout->b_die )
1238 msleep( VOUT_IDLE_SLEEP );
1242 /*****************************************************************************
1243 * EndThread: thread destruction
1244 *****************************************************************************
1245 * This function is called when the thread ends after a sucessful
1246 * initialization. It frees all resources allocated by InitThread.
1247 *****************************************************************************/
1248 static void EndThread( vout_thread_t *p_vout )
1250 int i_index; /* index in heap */
1254 struct tms cpu_usage;
1255 times( &cpu_usage );
1257 msg_Dbg( p_vout, "cpu usage (user: %d, system: %d)",
1258 cpu_usage.tms_utime, cpu_usage.tms_stime );
1262 if( !p_vout->b_direct )
1264 module_Unneed( p_vout, p_vout->chroma.p_module );
1267 /* Destroy all remaining pictures */
1268 for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++ )
1270 if ( p_vout->p_picture[i_index].i_type == MEMORY_PICTURE )
1272 free( p_vout->p_picture[i_index].p_data_orig );
1276 /* Destroy subpicture unit */
1277 spu_Attach( p_vout->p_spu, VLC_OBJECT(p_vout), VLC_FALSE );
1278 spu_Destroy( p_vout->p_spu );
1280 /* Destroy the video filters2 */
1281 RemoveVideoFilters2( p_vout );
1283 /* Destroy translation tables */
1284 p_vout->pf_end( p_vout );
1286 /* Release the change lock */
1287 vlc_mutex_unlock( &p_vout->change_lock );
1290 /*****************************************************************************
1291 * DestroyThread: thread destruction
1292 *****************************************************************************
1293 * This function is called when the thread ends. It frees all ressources
1294 * allocated by CreateThread. Status is available at this stage.
1295 *****************************************************************************/
1296 static void DestroyThread( vout_thread_t *p_vout )
1298 /* Destroy the locks */
1299 vlc_mutex_destroy( &p_vout->picture_lock );
1300 vlc_mutex_destroy( &p_vout->change_lock );
1301 vlc_mutex_destroy( &p_vout->vfilter_lock );
1303 /* Release the module */
1304 if( p_vout && p_vout->p_module )
1306 module_Unneed( p_vout, p_vout->p_module );
1310 /* following functions are local */
1312 static int ReduceHeight( int i_ratio )
1314 int i_dummy = VOUT_ASPECT_FACTOR;
1322 /* VOUT_ASPECT_FACTOR is (2^7 * 3^3 * 5^3), we just check for 2, 3 and 5 */
1323 while( !(i_ratio & 1) && !(i_dummy & 1) )
1330 while( !(i_ratio % 3) && !(i_dummy % 3) )
1337 while( !(i_ratio % 5) && !(i_dummy % 5) )
1347 static void AspectRatio( int i_aspect, int *i_aspect_x, int *i_aspect_y )
1349 unsigned int i_pgcd = ReduceHeight( i_aspect );
1350 *i_aspect_x = i_aspect / i_pgcd;
1351 *i_aspect_y = VOUT_ASPECT_FACTOR / i_pgcd;
1354 /*****************************************************************************
1355 * BinaryLog: computes the base 2 log of a binary value
1356 *****************************************************************************
1357 * This functions is used by MaskToShift, to get a bit index from a binary
1359 *****************************************************************************/
1360 static int BinaryLog( uint32_t i )
1364 if( i == 0 ) return -31337;
1366 if( i & 0xffff0000 ) i_log += 16;
1367 if( i & 0xff00ff00 ) i_log += 8;
1368 if( i & 0xf0f0f0f0 ) i_log += 4;
1369 if( i & 0xcccccccc ) i_log += 2;
1370 if( i & 0xaaaaaaaa ) i_log += 1;
1375 /*****************************************************************************
1376 * MaskToShift: transform a color mask into right and left shifts
1377 *****************************************************************************
1378 * This function is used for obtaining color shifts from masks.
1379 *****************************************************************************/
1380 static void MaskToShift( int *pi_left, int *pi_right, uint32_t i_mask )
1382 uint32_t i_low, i_high; /* lower hand higher bits of the mask */
1386 *pi_left = *pi_right = 0;
1391 i_low = i_high = i_mask;
1393 i_low &= - (int32_t)i_low; /* lower bit of the mask */
1394 i_high += i_low; /* higher bit of the mask */
1396 /* Transform bits into an index. Also deal with i_high overflow, which
1397 * is faster than changing the BinaryLog code to handle 64 bit integers. */
1398 i_low = BinaryLog (i_low);
1399 i_high = i_high ? BinaryLog (i_high) : 32;
1401 /* Update pointers and return */
1403 *pi_right = (8 - i_high + i_low);
1406 /*****************************************************************************
1407 * vout_VarCallback: generic callback for intf variables
1408 *****************************************************************************/
1409 int vout_VarCallback( vlc_object_t * p_this, const char * psz_variable,
1410 vlc_value_t oldval, vlc_value_t newval,
1413 vout_thread_t * p_vout = (vout_thread_t *)p_this;
1415 (void)psz_variable; (void)newval; (void)oldval; (void)p_data;
1416 val.b_bool = VLC_TRUE;
1417 var_Set( p_vout, "intf-change", val );
1421 /*****************************************************************************
1422 * Helper thread for object variables callbacks.
1423 * Only used to avoid deadlocks when using the video embedded mode.
1424 *****************************************************************************/
1425 typedef struct suxor_thread_t
1428 input_thread_t *p_input;
1432 static void SuxorRestartVideoES( suxor_thread_t *p_this )
1436 vlc_thread_ready( p_this );
1438 /* Now restart current video stream */
1439 var_Get( p_this->p_input, "video-es", &val );
1440 if( val.i_int >= 0 )
1443 val_es.i_int = -VIDEO_ES;
1444 var_Set( p_this->p_input, "video-es", val_es );
1445 var_Set( p_this->p_input, "video-es", val );
1448 vlc_object_release( p_this->p_input );
1450 vlc_object_destroy( p_this );
1453 /*****************************************************************************
1454 * object variables callbacks: a bunch of object variables are used by the
1455 * interfaces to interact with the vout.
1456 *****************************************************************************/
1457 static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd,
1458 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1460 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1461 input_thread_t *p_input;
1464 char *psz_mode = newval.psz_string;
1465 char *psz_filter, *psz_deinterlace = NULL;
1466 (void)psz_cmd; (void)oldval; (void)p_data;
1468 var_Get( p_vout, "vout-filter", &val );
1469 psz_filter = val.psz_string;
1470 if( psz_filter ) psz_deinterlace = strstr( psz_filter, "deinterlace" );
1472 if( !psz_mode || !*psz_mode )
1474 if( psz_deinterlace )
1476 char *psz_src = psz_deinterlace + sizeof("deinterlace") - 1;
1477 if( psz_src[0] == ':' ) psz_src++;
1478 memmove( psz_deinterlace, psz_src, strlen(psz_src) + 1 );
1481 else if( !psz_deinterlace )
1483 psz_filter = realloc( psz_filter, strlen( psz_filter ) +
1484 sizeof(":deinterlace") );
1485 if( psz_filter && *psz_filter ) strcat( psz_filter, ":" );
1486 strcat( psz_filter, "deinterlace" );
1489 p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1491 if( !p_input ) return VLC_EGENERIC;
1493 if( psz_mode && *psz_mode )
1495 /* Modify input as well because the vout might have to be restarted */
1496 val.psz_string = psz_mode;
1497 var_Create( p_input, "deinterlace-mode", VLC_VAR_STRING );
1498 var_Set( p_input, "deinterlace-mode", val );
1500 vlc_object_release( p_input );
1502 val.b_bool = VLC_TRUE;
1503 var_Set( p_vout, "intf-change", val );
1505 val.psz_string = psz_filter;
1506 var_Set( p_vout, "vout-filter", val );
1507 if( psz_filter ) free( psz_filter );
1512 static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd,
1513 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1515 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1516 input_thread_t *p_input;
1518 (void)psz_cmd; (void)oldval; (void)p_data;
1520 p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1524 msg_Err( p_vout, "Input not found" );
1525 return( VLC_EGENERIC );
1528 val.b_bool = VLC_TRUE;
1529 var_Set( p_vout, "intf-change", val );
1531 /* Modify input as well because the vout might have to be restarted */
1532 val.psz_string = newval.psz_string;
1533 var_Create( p_input, "vout-filter", VLC_VAR_STRING );
1535 var_Set( p_input, "vout-filter", val );
1537 /* Now restart current video stream */
1538 var_Get( p_input, "video-es", &val );
1539 if( val.i_int >= 0 )
1541 suxor_thread_t *p_suxor =
1542 vlc_object_create( p_vout, sizeof(suxor_thread_t) );
1543 p_suxor->p_input = p_input;
1544 p_vout->b_filter_change = VLC_TRUE;
1545 vlc_object_yield( p_input );
1546 vlc_thread_create( p_suxor, "suxor", SuxorRestartVideoES,
1547 VLC_THREAD_PRIORITY_LOW, VLC_FALSE );
1550 vlc_object_release( p_input );
1555 /*****************************************************************************
1556 * Video Filter2 stuff
1557 *****************************************************************************/
1558 static int ParseVideoFilter2Chain( vout_thread_t *p_vout, char *psz_vfilters )
1561 for( i = 0; i < p_vout->i_vfilters_cfg; i++ )
1563 struct config_chain_t *p_cfg =
1564 p_vout->p_vfilters_cfg[p_vout->i_vfilters_cfg];
1565 config_ChainDestroy( p_cfg );
1566 if( p_vout->psz_vfilters[p_vout->i_vfilters_cfg] )
1568 free( p_vout->psz_vfilters[p_vout->i_vfilters_cfg] );
1569 p_vout->psz_vfilters[p_vout->i_vfilters_cfg] = NULL;
1572 p_vout->i_vfilters_cfg = 0;
1573 if( psz_vfilters && *psz_vfilters )
1575 char *psz_parser = psz_vfilters;
1577 while( psz_parser && *psz_parser )
1579 psz_parser = config_ChainCreate(
1580 &p_vout->psz_vfilters[p_vout->i_vfilters_cfg],
1581 &p_vout->p_vfilters_cfg[p_vout->i_vfilters_cfg],
1583 msg_Dbg( p_vout, "adding vfilter: %s",
1584 p_vout->psz_vfilters[p_vout->i_vfilters_cfg] );
1585 p_vout->i_vfilters_cfg++;
1586 if( psz_parser && *psz_parser )
1588 if( p_vout->i_vfilters_cfg == MAX_VFILTERS )
1591 "maximum number of video filters reached. \"%s\" discarded",
1601 static int VideoFilter2Callback( vlc_object_t *p_this, char const *psz_cmd,
1602 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1604 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1605 (void)psz_cmd; (void)oldval; (void)p_data;
1607 vlc_mutex_lock( &p_vout->vfilter_lock );
1608 ParseVideoFilter2Chain( p_vout, newval.psz_string );
1609 p_vout->b_vfilter_change = VLC_TRUE;
1610 vlc_mutex_unlock( &p_vout->vfilter_lock );
1615 static void RemoveVideoFilters2( vout_thread_t *p_vout )
1618 for( i = 0; i < p_vout->i_vfilters; i++ )
1620 vlc_object_detach( p_vout->pp_vfilters[i] );
1621 if( p_vout->pp_vfilters[i]->p_module )
1623 module_Unneed( p_vout->pp_vfilters[i],
1624 p_vout->pp_vfilters[i]->p_module );
1627 free( p_vout->pp_vfilters[i]->p_owner );
1628 vlc_object_destroy( p_vout->pp_vfilters[i] );
1630 p_vout->i_vfilters = 0;
1633 static void DisplayTitleOnOSD( vout_thread_t *p_vout )
1635 input_thread_t *p_input;
1636 mtime_t i_now, i_stop;
1638 p_input = (input_thread_t *)vlc_object_find( p_vout,
1639 VLC_OBJECT_INPUT, FIND_ANYWHERE );
1643 i_stop = i_now + (mtime_t)(p_vout->i_title_timeout * 1000);
1644 char *psz_nowplaying =
1645 input_item_GetNowPlaying( input_GetItem( p_input ) );
1646 char *psz_artist = input_item_GetArtist( input_GetItem( p_input ) );
1647 char *psz_name = input_item_GetTitle( input_GetItem( p_input ) );
1648 if( EMPTY_STR( psz_name ) )
1651 psz_name = input_item_GetName( input_GetItem( p_input ) );
1653 if( !EMPTY_STR( psz_nowplaying ) )
1655 vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1656 psz_nowplaying, NULL,
1657 p_vout->i_title_position,
1658 30 + p_vout->fmt_in.i_width
1659 - p_vout->fmt_in.i_visible_width
1660 - p_vout->fmt_in.i_x_offset,
1661 20 + p_vout->fmt_in.i_y_offset,
1664 else if( !EMPTY_STR( psz_artist ) )
1666 char *psz_string = NULL;
1667 if( asprintf( &psz_string, "%s - %s", psz_name, psz_artist ) != -1 )
1669 vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1671 p_vout->i_title_position,
1672 30 + p_vout->fmt_in.i_width
1673 - p_vout->fmt_in.i_visible_width
1674 - p_vout->fmt_in.i_x_offset,
1675 20 + p_vout->fmt_in.i_y_offset,
1682 vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1684 p_vout->i_title_position,
1685 30 + p_vout->fmt_in.i_width
1686 - p_vout->fmt_in.i_visible_width
1687 - p_vout->fmt_in.i_x_offset,
1688 20 + p_vout->fmt_in.i_y_offset,
1691 vlc_object_release( p_input );
1694 free( psz_nowplaying );