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_chroma != p_fmt->i_chroma ) ||
198 ( p_vout->fmt_render.i_aspect != p_fmt->i_aspect ) ||
199 p_vout->b_filter_change )
201 /* We are not interested in this format, close this vout */
202 vlc_object_release( p_vout );
203 vout_Destroy( p_vout );
208 /* This video output is cool! Hijack it. */
209 spu_Attach( p_vout->p_spu, p_this, VLC_TRUE );
210 vlc_object_attach( p_vout, p_this );
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, 0 );
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_destroy( 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 if( val.psz_string ) 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_detach( p_vout );
457 vlc_object_destroy( p_vout );
461 if( p_vout->b_error )
463 msg_Err( p_vout, "video output creation failed" );
465 /* Make sure the thread is destroyed */
466 vlc_object_kill( p_vout );
467 vlc_thread_join( p_vout );
469 vlc_object_detach( p_vout );
470 vlc_object_destroy( p_vout );
477 /*****************************************************************************
478 * vout_Destroy: destroys a previously created video output
479 *****************************************************************************
480 * Destroy a terminated thread.
481 * The function will request a destruction of the specified thread. If pi_error
482 * is NULL, it will return once the thread is destroyed. Else, it will be
483 * update using one of the THREAD_* constants.
484 *****************************************************************************/
485 void vout_Destroy( vout_thread_t *p_vout )
487 vout_thread_t *p_another_vout;
488 playlist_t *p_playlist = pl_Yield( p_vout );
490 /* Request thread destruction */
491 vlc_object_kill( p_vout );
492 vlc_thread_join( p_vout );
494 var_Destroy( p_vout, "intf-change" );
496 if( p_vout->psz_filter_chain ) free( p_vout->psz_filter_chain );
498 config_ChainDestroy( p_vout->p_cfg );
501 vlc_object_destroy( p_vout );
503 /* This is a dirty hack for mostly Linux, where there is no way to get the GUI
504 back if you closed it while playing video. This is solved in Mac OS X,
505 where we have this novelty called menubar, that will always allow you access
506 to the applications main functionality. They should try that on linux sometime */
507 p_another_vout = vlc_object_find( p_playlist,
508 VLC_OBJECT_VOUT, FIND_ANYWHERE );
509 if( p_another_vout == NULL )
512 val.b_bool = VLC_TRUE;
513 var_Set( p_playlist, "intf-show", val );
517 vlc_object_release( p_another_vout );
520 vlc_object_release( p_playlist );
523 /*****************************************************************************
524 * InitThread: initialize video output thread
525 *****************************************************************************
526 * This function is called from RunThread and performs the second step of the
527 * initialization. It returns 0 on success. Note that the thread's flag are not
528 * modified inside this function.
529 *****************************************************************************/
530 static int InitThread( vout_thread_t *p_vout )
532 int i, i_aspect_x, i_aspect_y;
534 vlc_mutex_lock( &p_vout->change_lock );
540 /* Initialize output method, it allocates direct buffers for us */
541 if( p_vout->pf_init( p_vout ) )
543 vlc_mutex_unlock( &p_vout->change_lock );
547 if( !I_OUTPUTPICTURES )
549 msg_Err( p_vout, "plugin was unable to allocate at least "
550 "one direct buffer" );
551 p_vout->pf_end( p_vout );
552 vlc_mutex_unlock( &p_vout->change_lock );
556 if( I_OUTPUTPICTURES > VOUT_MAX_PICTURES )
558 msg_Err( p_vout, "plugin allocated too many direct buffers, "
559 "our internal buffers must have overflown." );
560 p_vout->pf_end( p_vout );
561 vlc_mutex_unlock( &p_vout->change_lock );
565 msg_Dbg( p_vout, "got %i direct buffer(s)", I_OUTPUTPICTURES );
567 AspectRatio( p_vout->fmt_render.i_aspect, &i_aspect_x, &i_aspect_y );
569 msg_Dbg( p_vout, "picture in %ix%i (%i,%i,%ix%i), "
570 "chroma %4.4s, ar %i:%i, sar %i:%i",
571 p_vout->fmt_render.i_width, p_vout->fmt_render.i_height,
572 p_vout->fmt_render.i_x_offset, p_vout->fmt_render.i_y_offset,
573 p_vout->fmt_render.i_visible_width,
574 p_vout->fmt_render.i_visible_height,
575 (char*)&p_vout->fmt_render.i_chroma,
576 i_aspect_x, i_aspect_y,
577 p_vout->fmt_render.i_sar_num, p_vout->fmt_render.i_sar_den );
579 AspectRatio( p_vout->fmt_in.i_aspect, &i_aspect_x, &i_aspect_y );
581 msg_Dbg( p_vout, "picture user %ix%i (%i,%i,%ix%i), "
582 "chroma %4.4s, ar %i:%i, sar %i:%i",
583 p_vout->fmt_in.i_width, p_vout->fmt_in.i_height,
584 p_vout->fmt_in.i_x_offset, p_vout->fmt_in.i_y_offset,
585 p_vout->fmt_in.i_visible_width,
586 p_vout->fmt_in.i_visible_height,
587 (char*)&p_vout->fmt_in.i_chroma,
588 i_aspect_x, i_aspect_y,
589 p_vout->fmt_in.i_sar_num, p_vout->fmt_in.i_sar_den );
591 if( !p_vout->fmt_out.i_width || !p_vout->fmt_out.i_height )
593 p_vout->fmt_out.i_width = p_vout->fmt_out.i_visible_width =
594 p_vout->output.i_width;
595 p_vout->fmt_out.i_height = p_vout->fmt_out.i_visible_height =
596 p_vout->output.i_height;
597 p_vout->fmt_out.i_x_offset = p_vout->fmt_out.i_y_offset = 0;
599 p_vout->fmt_out.i_aspect = p_vout->output.i_aspect;
600 p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
602 if( !p_vout->fmt_out.i_sar_num || !p_vout->fmt_out.i_sar_num )
604 p_vout->fmt_out.i_sar_num = p_vout->fmt_out.i_aspect *
605 p_vout->fmt_out.i_height;
606 p_vout->fmt_out.i_sar_den = VOUT_ASPECT_FACTOR *
607 p_vout->fmt_out.i_width;
610 vlc_ureduce( &p_vout->fmt_out.i_sar_num, &p_vout->fmt_out.i_sar_den,
611 p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den, 0 );
613 AspectRatio( p_vout->fmt_out.i_aspect, &i_aspect_x, &i_aspect_y );
615 msg_Dbg( p_vout, "picture out %ix%i (%i,%i,%ix%i), "
616 "chroma %4.4s, ar %i:%i, sar %i:%i",
617 p_vout->fmt_out.i_width, p_vout->fmt_out.i_height,
618 p_vout->fmt_out.i_x_offset, p_vout->fmt_out.i_y_offset,
619 p_vout->fmt_out.i_visible_width,
620 p_vout->fmt_out.i_visible_height,
621 (char*)&p_vout->fmt_out.i_chroma,
622 i_aspect_x, i_aspect_y,
623 p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den );
625 /* Calculate shifts from system-updated masks */
626 MaskToShift( &p_vout->output.i_lrshift, &p_vout->output.i_rrshift,
627 p_vout->output.i_rmask );
628 MaskToShift( &p_vout->output.i_lgshift, &p_vout->output.i_rgshift,
629 p_vout->output.i_gmask );
630 MaskToShift( &p_vout->output.i_lbshift, &p_vout->output.i_rbshift,
631 p_vout->output.i_bmask );
633 /* Check whether we managed to create direct buffers similar to
634 * the render buffers, ie same size and chroma */
635 if( ( p_vout->output.i_width == p_vout->render.i_width )
636 && ( p_vout->output.i_height == p_vout->render.i_height )
637 && ( vout_ChromaCmp( p_vout->output.i_chroma, p_vout->render.i_chroma ) ) )
639 /* Cool ! We have direct buffers, we can ask the decoder to
640 * directly decode into them ! Map the first render buffers to
641 * the first direct buffers, but keep the first direct buffer
642 * for memcpy operations */
643 p_vout->b_direct = 1;
645 for( i = 1; i < VOUT_MAX_PICTURES; i++ )
647 if( p_vout->p_picture[ i ].i_type != DIRECT_PICTURE &&
648 I_RENDERPICTURES >= VOUT_MIN_DIRECT_PICTURES - 1 &&
649 p_vout->p_picture[ i - 1 ].i_type == DIRECT_PICTURE )
651 /* We have enough direct buffers so there's no need to
652 * try to use system memory buffers. */
655 PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
659 msg_Dbg( p_vout, "direct render, mapping "
660 "render pictures 0-%i to system pictures 1-%i",
661 VOUT_MAX_PICTURES - 2, VOUT_MAX_PICTURES - 1 );
665 /* Rats... Something is wrong here, we could not find an output
666 * plugin able to directly render what we decode. See if we can
667 * find a chroma plugin to do the conversion */
668 p_vout->b_direct = 0;
670 /* Choose the best module */
671 p_vout->chroma.p_module = module_Need( p_vout, "chroma", NULL, 0 );
673 if( p_vout->chroma.p_module == NULL )
675 msg_Err( p_vout, "no chroma module for %4.4s to %4.4s",
676 (char*)&p_vout->render.i_chroma,
677 (char*)&p_vout->output.i_chroma );
678 p_vout->pf_end( p_vout );
679 vlc_mutex_unlock( &p_vout->change_lock );
683 msg_Dbg( p_vout, "indirect render, mapping "
684 "render pictures 0-%i to system pictures %i-%i",
685 VOUT_MAX_PICTURES - 1, I_OUTPUTPICTURES,
686 I_OUTPUTPICTURES + VOUT_MAX_PICTURES - 1 );
688 /* Append render buffers after the direct buffers */
689 for( i = I_OUTPUTPICTURES; i < 2 * VOUT_MAX_PICTURES; i++ )
691 PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
694 /* Check if we have enough render pictures */
695 if( I_RENDERPICTURES == VOUT_MAX_PICTURES )
700 /* Link pictures back to their heap */
701 for( i = 0 ; i < I_RENDERPICTURES ; i++ )
703 PP_RENDERPICTURE[ i ]->p_heap = &p_vout->render;
706 for( i = 0 ; i < I_OUTPUTPICTURES ; i++ )
708 PP_OUTPUTPICTURE[ i ]->p_heap = &p_vout->output;
711 /* XXX XXX mark thread ready */
715 /*****************************************************************************
716 * RunThread: video output thread
717 *****************************************************************************
718 * Video output thread. This function does only returns when the thread is
719 * terminated. It handles the pictures arriving in the video heap and the
720 * display device events.
721 *****************************************************************************/
722 static void RunThread( vout_thread_t *p_vout)
724 int i_index; /* index in heap */
725 int i_idle_loops = 0; /* loops without displaying a picture */
726 mtime_t current_date; /* current date */
727 mtime_t display_date; /* display date */
729 picture_t * p_picture; /* picture pointer */
730 picture_t * p_last_picture = NULL; /* last picture */
731 picture_t * p_directbuffer; /* direct buffer to display */
733 subpicture_t * p_subpic = NULL; /* subpicture pointer */
735 input_thread_t *p_input = NULL ; /* Parent input, if it exists */
738 vlc_bool_t b_drop_late;
740 int i_displayed = 0, i_lost = 0, i_loops = 0;
745 p_vout->b_error = InitThread( p_vout );
747 var_Create( p_vout, "drop-late-frames", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
748 var_Get( p_vout, "drop-late-frames", &val );
749 b_drop_late = val.b_bool;
751 /* signal the creation of the vout */
752 vlc_thread_ready( p_vout );
754 if( p_vout->b_error )
756 /* Destroy thread structures allocated by Create and InitThread */
757 DestroyThread( p_vout );
761 if( p_vout->b_title_show )
762 DisplayTitleOnOSD( p_vout );
765 * Main loop - it is not executed if an error occurred during
768 while( (!p_vout->b_die) && (!p_vout->b_error) )
770 /* Initialize loop variables */
773 current_date = mdate();
775 if( p_input && p_input->b_die )
777 vlc_object_release( p_input );
782 if( i_loops % 20 == 0 )
786 p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT,
791 vlc_mutex_lock( &p_input->p->counters.counters_lock );
792 stats_UpdateInteger( p_vout, p_input->p->counters.p_lost_pictures,
794 stats_UpdateInteger( p_vout,
795 p_input->p->counters.p_displayed_pictures,
797 i_displayed = i_lost = 0;
798 vlc_mutex_unlock( &p_input->p->counters.counters_lock );
803 if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
805 msg_Dbg( p_vout, "picture heap: %d/%d",
806 I_RENDERPICTURES, p_vout->i_heap_size );
811 * Find the picture to display (the one with the earliest date).
812 * This operation does not need lock, since only READY_PICTUREs
814 for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
816 if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
817 && ( (p_picture == NULL) ||
818 (PP_RENDERPICTURE[i_index]->date < display_date) ) )
820 p_picture = PP_RENDERPICTURE[i_index];
821 display_date = p_picture->date;
827 /* If we met the last picture, parse again to see whether there is
828 * a more appropriate one. */
829 if( p_picture == p_last_picture )
831 for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
833 if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
834 && (PP_RENDERPICTURE[i_index] != p_last_picture)
835 && ((p_picture == p_last_picture) ||
836 (PP_RENDERPICTURE[i_index]->date < display_date)) )
838 p_picture = PP_RENDERPICTURE[i_index];
839 display_date = p_picture->date;
844 /* If we found better than the last picture, destroy it */
845 if( p_last_picture && p_picture != p_last_picture )
847 vlc_mutex_lock( &p_vout->picture_lock );
848 if( p_last_picture->i_refcount )
850 p_last_picture->i_status = DISPLAYED_PICTURE;
854 p_last_picture->i_status = DESTROYED_PICTURE;
855 p_vout->i_heap_size--;
857 vlc_mutex_unlock( &p_vout->picture_lock );
858 p_last_picture = NULL;
861 /* Compute FPS rate */
862 p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ]
865 if( !p_picture->b_force &&
866 p_picture != p_last_picture &&
867 display_date < current_date + p_vout->render_time &&
870 /* Picture is late: it will be destroyed and the thread
871 * will directly choose the next picture */
872 vlc_mutex_lock( &p_vout->picture_lock );
873 if( p_picture->i_refcount )
875 /* Pretend we displayed the picture, but don't destroy
876 * it since the decoder might still need it. */
877 p_picture->i_status = DISPLAYED_PICTURE;
881 /* Destroy the picture without displaying it */
882 p_picture->i_status = DESTROYED_PICTURE;
883 p_vout->i_heap_size--;
885 msg_Warn( p_vout, "late picture skipped ("I64Fd")",
886 current_date - display_date );
888 vlc_mutex_unlock( &p_vout->picture_lock );
894 current_date + p_vout->i_pts_delay + VOUT_BOGUS_DELAY )
896 /* Picture is waaay too early: it will be destroyed */
897 vlc_mutex_lock( &p_vout->picture_lock );
898 if( p_picture->i_refcount )
900 /* Pretend we displayed the picture, but don't destroy
901 * it since the decoder might still need it. */
902 p_picture->i_status = DISPLAYED_PICTURE;
906 /* Destroy the picture without displaying it */
907 p_picture->i_status = DESTROYED_PICTURE;
908 p_vout->i_heap_size--;
911 msg_Warn( p_vout, "vout warning: early picture skipped "
912 "("I64Fd")", display_date - current_date
913 - p_vout->i_pts_delay );
914 vlc_mutex_unlock( &p_vout->picture_lock );
919 if( display_date > current_date + VOUT_DISPLAY_DELAY )
921 /* A picture is ready to be rendered, but its rendering date
922 * is far from the current one so the thread will perform an
923 * empty loop as if no picture were found. The picture state
928 else if( p_picture == p_last_picture )
930 /* We are asked to repeat the previous picture, but we first
931 * wait for a couple of idle loops */
932 if( i_idle_loops < 4 )
939 /* We set the display date to something high, otherwise
940 * we'll have lots of problems with late pictures */
941 display_date = current_date + p_vout->render_time;
946 if( p_picture == NULL )
951 /* Video Filter2 stuff */
952 if( p_vout->b_vfilter_change == VLC_TRUE )
955 vlc_mutex_lock( &p_vout->vfilter_lock );
956 RemoveVideoFilters2( p_vout );
957 for( i = 0; i < p_vout->i_vfilters_cfg; i++ )
959 filter_t *p_vfilter =
960 p_vout->pp_vfilters[p_vout->i_vfilters] =
961 vlc_object_create( p_vout, VLC_OBJECT_FILTER );
963 vlc_object_attach( p_vfilter, p_vout );
965 p_vfilter->pf_vout_buffer_new = video_new_buffer_filter;
966 p_vfilter->pf_vout_buffer_del = video_del_buffer_filter;
968 if( !p_vout->i_vfilters )
970 p_vfilter->fmt_in.video = p_vout->fmt_render;
974 p_vfilter->fmt_in.video = (p_vfilter-1)->fmt_out.video;
976 /* TODO: one day filters in the middle of the chain might
977 * have a different fmt_out.video than fmt_render ... */
978 p_vfilter->fmt_out.video = p_vout->fmt_render;
980 p_vfilter->p_cfg = p_vout->p_vfilters_cfg[i];
981 p_vfilter->p_module = module_Need( p_vfilter, "video filter2",
982 p_vout->psz_vfilters[i],
985 if( p_vfilter->p_module )
988 malloc( sizeof( filter_owner_sys_t ) );
989 p_vfilter->p_owner->p_vout = p_vout;
990 p_vout->i_vfilters++;
991 msg_Dbg( p_vout, "video filter found (%s)",
992 p_vout->psz_vfilters[i] );
996 msg_Err( p_vout, "no video filter found (%s)",
997 p_vout->psz_vfilters[i] );
998 vlc_object_detach( p_vfilter );
999 vlc_object_destroy( p_vfilter );
1002 p_vout->b_vfilter_change = VLC_FALSE;
1003 vlc_mutex_unlock( &p_vout->vfilter_lock );
1009 for( i = 0; i < p_vout->i_vfilters; i++ )
1011 picture_t *p_old = p_picture;
1012 p_picture = p_vout->pp_vfilters[i]->pf_video_filter(
1013 p_vout->pp_vfilters[i], p_picture );
1018 /* FIXME: this is kind of wrong
1019 * if you have 2 or more vfilters and the 2nd breaks,
1020 * on the next loop the 1st one will be applied again */
1022 /* if p_old and p_picture are the same (ie the filter
1023 * worked on the old picture), then following code is
1024 * still alright since i_status gets changed back to
1025 * the right value */
1026 if( p_old->i_refcount )
1028 p_old->i_status = DISPLAYED_PICTURE;
1032 p_old->i_status = DESTROYED_PICTURE;
1034 p_picture->i_status = READY_PICTURE;
1038 if( p_picture && p_vout->b_snapshot )
1040 p_vout->b_snapshot = VLC_FALSE;
1041 vout_Snapshot( p_vout, p_picture );
1045 * Check for subpictures to display
1047 if( display_date > 0 )
1051 p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT,
1054 p_subpic = spu_SortSubpictures( p_vout->p_spu, display_date,
1055 p_input ? var_GetBool( p_input, "state" ) == PAUSE_S : VLC_FALSE );
1062 p_directbuffer = vout_RenderPicture( p_vout, p_picture, p_subpic );
1065 * Call the plugin-specific rendering method if there is one
1067 if( p_picture != NULL && p_directbuffer != NULL && p_vout->pf_render )
1069 /* Render the direct buffer returned by vout_RenderPicture */
1070 p_vout->pf_render( p_vout, p_directbuffer );
1076 if( display_date != 0 && p_directbuffer != NULL )
1078 mtime_t current_render_time = mdate() - current_date;
1079 /* if render time is very large we don't include it in the mean */
1080 if( current_render_time < p_vout->render_time +
1081 VOUT_DISPLAY_DELAY )
1083 /* Store render time using a sliding mean weighting to
1084 * current value in a 3 to 1 ratio*/
1085 p_vout->render_time *= 3;
1086 p_vout->render_time += current_render_time;
1087 p_vout->render_time >>= 2;
1091 /* Give back change lock */
1092 vlc_mutex_unlock( &p_vout->change_lock );
1094 /* Sleep a while or until a given date */
1095 if( display_date != 0 )
1097 /* If there are filters in the chain, better give them the picture
1099 if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain )
1101 mwait( display_date - VOUT_MWAIT_TOLERANCE );
1106 msleep( VOUT_IDLE_SLEEP );
1109 /* On awakening, take back lock and send immediately picture
1111 vlc_mutex_lock( &p_vout->change_lock );
1114 * Display the previously rendered picture
1116 if( p_picture != NULL && p_directbuffer != NULL )
1118 /* Display the direct buffer returned by vout_RenderPicture */
1119 if( p_vout->pf_display )
1121 p_vout->pf_display( p_vout, p_directbuffer );
1124 /* Tell the vout this was the last picture and that it does not
1125 * need to be forced anymore. */
1126 p_last_picture = p_picture;
1127 p_last_picture->b_force = 0;
1130 if( p_picture != NULL )
1132 /* Reinitialize idle loop count */
1137 * Check events and manage thread
1139 if( p_vout->pf_manage && p_vout->pf_manage( p_vout ) )
1141 /* A fatal error occurred, and the thread must terminate
1142 * immediately, without displaying anything - setting b_error to 1
1143 * causes the immediate end of the main while() loop. */
1144 p_vout->b_error = 1;
1147 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
1149 /* this must only happen when the vout plugin is incapable of
1150 * rescaling the picture itself. In this case we need to destroy
1151 * the current picture buffers and recreate new ones with the right
1155 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
1157 p_vout->pf_end( p_vout );
1158 for( i = 0; i < I_OUTPUTPICTURES; i++ )
1159 p_vout->p_picture[ i ].i_status = FREE_PICTURE;
1161 I_OUTPUTPICTURES = 0;
1162 if( p_vout->pf_init( p_vout ) )
1164 msg_Err( p_vout, "cannot resize display" );
1165 /* FIXME: pf_end will be called again in EndThread() */
1166 p_vout->b_error = 1;
1169 /* Need to reinitialise the chroma plugin */
1170 if( p_vout->chroma.p_module )
1172 if( p_vout->chroma.p_module->pf_deactivate )
1173 p_vout->chroma.p_module->pf_deactivate( VLC_OBJECT(p_vout) );
1174 p_vout->chroma.p_module->pf_activate( VLC_OBJECT(p_vout) );
1178 if( p_vout->i_changes & VOUT_PICTURE_BUFFERS_CHANGE )
1180 /* This happens when the picture buffers need to be recreated.
1181 * This is useful on multimonitor displays for instance.
1183 * Warning: This only works when the vout creates only 1 picture
1185 p_vout->i_changes &= ~VOUT_PICTURE_BUFFERS_CHANGE;
1187 if( !p_vout->b_direct )
1189 module_Unneed( p_vout, p_vout->chroma.p_module );
1192 vlc_mutex_lock( &p_vout->picture_lock );
1194 p_vout->pf_end( p_vout );
1196 I_OUTPUTPICTURES = I_RENDERPICTURES = 0;
1198 p_vout->b_error = InitThread( p_vout );
1200 vlc_mutex_unlock( &p_vout->picture_lock );
1206 vlc_object_release( p_input );
1210 * Error loop - wait until the thread destruction is requested
1212 if( p_vout->b_error )
1214 ErrorThread( p_vout );
1218 EndThread( p_vout );
1220 /* Destroy thread structures allocated by CreateThread */
1221 DestroyThread( p_vout );
1224 /*****************************************************************************
1225 * ErrorThread: RunThread() error loop
1226 *****************************************************************************
1227 * This function is called when an error occurred during thread main's loop.
1228 * The thread can still receive feed, but must be ready to terminate as soon
1230 *****************************************************************************/
1231 static void ErrorThread( vout_thread_t *p_vout )
1233 /* Wait until a `die' order */
1234 while( !p_vout->b_die )
1237 msleep( VOUT_IDLE_SLEEP );
1241 /*****************************************************************************
1242 * EndThread: thread destruction
1243 *****************************************************************************
1244 * This function is called when the thread ends after a sucessful
1245 * initialization. It frees all resources allocated by InitThread.
1246 *****************************************************************************/
1247 static void EndThread( vout_thread_t *p_vout )
1249 int i_index; /* index in heap */
1253 struct tms cpu_usage;
1254 times( &cpu_usage );
1256 msg_Dbg( p_vout, "cpu usage (user: %d, system: %d)",
1257 cpu_usage.tms_utime, cpu_usage.tms_stime );
1261 if( !p_vout->b_direct )
1263 module_Unneed( p_vout, p_vout->chroma.p_module );
1266 /* Destroy all remaining pictures */
1267 for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++ )
1269 if ( p_vout->p_picture[i_index].i_type == MEMORY_PICTURE )
1271 free( p_vout->p_picture[i_index].p_data_orig );
1275 /* Destroy subpicture unit */
1276 spu_Attach( p_vout->p_spu, VLC_OBJECT(p_vout), VLC_FALSE );
1277 spu_Destroy( p_vout->p_spu );
1279 /* Destroy the video filters2 */
1280 RemoveVideoFilters2( p_vout );
1282 /* Destroy translation tables */
1283 p_vout->pf_end( p_vout );
1285 /* Release the change lock */
1286 vlc_mutex_unlock( &p_vout->change_lock );
1289 /*****************************************************************************
1290 * DestroyThread: thread destruction
1291 *****************************************************************************
1292 * This function is called when the thread ends. It frees all ressources
1293 * allocated by CreateThread. Status is available at this stage.
1294 *****************************************************************************/
1295 static void DestroyThread( vout_thread_t *p_vout )
1297 /* Destroy the locks */
1298 vlc_mutex_destroy( &p_vout->picture_lock );
1299 vlc_mutex_destroy( &p_vout->change_lock );
1300 vlc_mutex_destroy( &p_vout->vfilter_lock );
1302 /* Release the module */
1303 if( p_vout && p_vout->p_module )
1305 module_Unneed( p_vout, p_vout->p_module );
1309 /* following functions are local */
1311 static int ReduceHeight( int i_ratio )
1313 int i_dummy = VOUT_ASPECT_FACTOR;
1321 /* VOUT_ASPECT_FACTOR is (2^7 * 3^3 * 5^3), we just check for 2, 3 and 5 */
1322 while( !(i_ratio & 1) && !(i_dummy & 1) )
1329 while( !(i_ratio % 3) && !(i_dummy % 3) )
1336 while( !(i_ratio % 5) && !(i_dummy % 5) )
1346 static void AspectRatio( int i_aspect, int *i_aspect_x, int *i_aspect_y )
1348 unsigned int i_pgcd = ReduceHeight( i_aspect );
1349 *i_aspect_x = i_aspect / i_pgcd;
1350 *i_aspect_y = VOUT_ASPECT_FACTOR / i_pgcd;
1353 /*****************************************************************************
1354 * BinaryLog: computes the base 2 log of a binary value
1355 *****************************************************************************
1356 * This functions is used by MaskToShift, to get a bit index from a binary
1358 *****************************************************************************/
1359 static int BinaryLog( uint32_t i )
1363 if( i == 0 ) return -31337;
1365 if( i & 0xffff0000 ) i_log += 16;
1366 if( i & 0xff00ff00 ) i_log += 8;
1367 if( i & 0xf0f0f0f0 ) i_log += 4;
1368 if( i & 0xcccccccc ) i_log += 2;
1369 if( i & 0xaaaaaaaa ) i_log += 1;
1374 /*****************************************************************************
1375 * MaskToShift: transform a color mask into right and left shifts
1376 *****************************************************************************
1377 * This function is used for obtaining color shifts from masks.
1378 *****************************************************************************/
1379 static void MaskToShift( int *pi_left, int *pi_right, uint32_t i_mask )
1381 uint32_t i_low, i_high; /* lower hand higher bits of the mask */
1385 *pi_left = *pi_right = 0;
1390 i_low = i_high = i_mask;
1392 i_low &= - (int32_t)i_low; /* lower bit of the mask */
1393 i_high += i_low; /* higher bit of the mask */
1395 /* Transform bits into an index. Also deal with i_high overflow, which
1396 * is faster than changing the BinaryLog code to handle 64 bit integers. */
1397 i_low = BinaryLog (i_low);
1398 i_high = i_high ? BinaryLog (i_high) : 32;
1400 /* Update pointers and return */
1402 *pi_right = (8 - i_high + i_low);
1405 /*****************************************************************************
1406 * vout_VarCallback: generic callback for intf variables
1407 *****************************************************************************/
1408 int vout_VarCallback( vlc_object_t * p_this, const char * psz_variable,
1409 vlc_value_t oldval, vlc_value_t newval,
1412 vout_thread_t * p_vout = (vout_thread_t *)p_this;
1414 (void)psz_variable; (void)newval; (void)oldval; (void)p_data;
1415 val.b_bool = VLC_TRUE;
1416 var_Set( p_vout, "intf-change", val );
1420 /*****************************************************************************
1421 * Helper thread for object variables callbacks.
1422 * Only used to avoid deadlocks when using the video embedded mode.
1423 *****************************************************************************/
1424 typedef struct suxor_thread_t
1427 input_thread_t *p_input;
1431 static void SuxorRestartVideoES( suxor_thread_t *p_this )
1435 vlc_thread_ready( p_this );
1437 /* Now restart current video stream */
1438 var_Get( p_this->p_input, "video-es", &val );
1439 if( val.i_int >= 0 )
1442 val_es.i_int = -VIDEO_ES;
1443 var_Set( p_this->p_input, "video-es", val_es );
1444 var_Set( p_this->p_input, "video-es", val );
1447 vlc_object_release( p_this->p_input );
1450 CloseHandle( p_this->thread_id );
1453 vlc_object_destroy( p_this );
1456 /*****************************************************************************
1457 * object variables callbacks: a bunch of object variables are used by the
1458 * interfaces to interact with the vout.
1459 *****************************************************************************/
1460 static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd,
1461 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1463 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1464 input_thread_t *p_input;
1467 char *psz_mode = newval.psz_string;
1468 char *psz_filter, *psz_deinterlace = NULL;
1469 (void)psz_cmd; (void)oldval; (void)p_data;
1471 var_Get( p_vout, "vout-filter", &val );
1472 psz_filter = val.psz_string;
1473 if( psz_filter ) psz_deinterlace = strstr( psz_filter, "deinterlace" );
1475 if( !psz_mode || !*psz_mode )
1477 if( psz_deinterlace )
1479 char *psz_src = psz_deinterlace + sizeof("deinterlace") - 1;
1480 if( psz_src[0] == ':' ) psz_src++;
1481 memmove( psz_deinterlace, psz_src, strlen(psz_src) + 1 );
1484 else if( !psz_deinterlace )
1486 psz_filter = realloc( psz_filter, strlen( psz_filter ) +
1487 sizeof(":deinterlace") );
1488 if( psz_filter && *psz_filter ) strcat( psz_filter, ":" );
1489 strcat( psz_filter, "deinterlace" );
1492 p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1494 if( !p_input ) return VLC_EGENERIC;
1496 if( psz_mode && *psz_mode )
1498 /* Modify input as well because the vout might have to be restarted */
1499 val.psz_string = psz_mode;
1500 var_Create( p_input, "deinterlace-mode", VLC_VAR_STRING );
1501 var_Set( p_input, "deinterlace-mode", val );
1503 vlc_object_release( p_input );
1505 val.b_bool = VLC_TRUE;
1506 var_Set( p_vout, "intf-change", val );
1508 val.psz_string = psz_filter;
1509 var_Set( p_vout, "vout-filter", val );
1510 if( psz_filter ) free( psz_filter );
1515 static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd,
1516 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1518 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1519 input_thread_t *p_input;
1521 (void)psz_cmd; (void)oldval; (void)p_data;
1523 p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1527 msg_Err( p_vout, "Input not found" );
1528 return( VLC_EGENERIC );
1531 val.b_bool = VLC_TRUE;
1532 var_Set( p_vout, "intf-change", val );
1534 /* Modify input as well because the vout might have to be restarted */
1535 val.psz_string = newval.psz_string;
1536 var_Create( p_input, "vout-filter", VLC_VAR_STRING );
1538 var_Set( p_input, "vout-filter", val );
1540 /* Now restart current video stream */
1541 var_Get( p_input, "video-es", &val );
1542 if( val.i_int >= 0 )
1544 suxor_thread_t *p_suxor =
1545 vlc_object_create( p_vout, sizeof(suxor_thread_t) );
1546 p_suxor->p_input = p_input;
1547 p_vout->b_filter_change = VLC_TRUE;
1548 vlc_object_yield( p_input );
1549 vlc_thread_create( p_suxor, "suxor", SuxorRestartVideoES,
1550 VLC_THREAD_PRIORITY_LOW, VLC_FALSE );
1553 vlc_object_release( p_input );
1558 /*****************************************************************************
1559 * Video Filter2 stuff
1560 *****************************************************************************/
1561 static int ParseVideoFilter2Chain( vout_thread_t *p_vout, char *psz_vfilters )
1564 for( i = 0; i < p_vout->i_vfilters_cfg; i++ )
1566 struct config_chain_t *p_cfg =
1567 p_vout->p_vfilters_cfg[p_vout->i_vfilters_cfg];
1568 config_ChainDestroy( p_cfg );
1569 if( p_vout->psz_vfilters[p_vout->i_vfilters_cfg] )
1571 free( p_vout->psz_vfilters[p_vout->i_vfilters_cfg] );
1572 p_vout->psz_vfilters[p_vout->i_vfilters_cfg] = NULL;
1575 p_vout->i_vfilters_cfg = 0;
1576 if( psz_vfilters && *psz_vfilters )
1578 char *psz_parser = psz_vfilters;
1580 while( psz_parser && *psz_parser )
1582 psz_parser = config_ChainCreate(
1583 &p_vout->psz_vfilters[p_vout->i_vfilters_cfg],
1584 &p_vout->p_vfilters_cfg[p_vout->i_vfilters_cfg],
1586 msg_Dbg( p_vout, "adding vfilter: %s",
1587 p_vout->psz_vfilters[p_vout->i_vfilters_cfg] );
1588 p_vout->i_vfilters_cfg++;
1589 if( psz_parser && *psz_parser )
1591 if( p_vout->i_vfilters_cfg == MAX_VFILTERS )
1594 "maximum number of video filters reached. \"%s\" discarded",
1604 static int VideoFilter2Callback( vlc_object_t *p_this, char const *psz_cmd,
1605 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1607 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1608 (void)psz_cmd; (void)oldval; (void)p_data;
1610 vlc_mutex_lock( &p_vout->vfilter_lock );
1611 ParseVideoFilter2Chain( p_vout, newval.psz_string );
1612 p_vout->b_vfilter_change = VLC_TRUE;
1613 vlc_mutex_unlock( &p_vout->vfilter_lock );
1618 static void RemoveVideoFilters2( vout_thread_t *p_vout )
1621 for( i = 0; i < p_vout->i_vfilters; i++ )
1623 vlc_object_detach( p_vout->pp_vfilters[i] );
1624 if( p_vout->pp_vfilters[i]->p_module )
1626 module_Unneed( p_vout->pp_vfilters[i],
1627 p_vout->pp_vfilters[i]->p_module );
1630 free( p_vout->pp_vfilters[i]->p_owner );
1631 vlc_object_destroy( p_vout->pp_vfilters[i] );
1633 p_vout->i_vfilters = 0;
1636 static void DisplayTitleOnOSD( vout_thread_t *p_vout )
1638 input_thread_t *p_input;
1639 mtime_t i_now, i_stop;
1641 p_input = (input_thread_t *)vlc_object_find( p_vout,
1642 VLC_OBJECT_INPUT, FIND_ANYWHERE );
1646 i_stop = i_now + (mtime_t)(p_vout->i_title_timeout * 1000);
1647 if( input_GetItem(p_input)->p_meta &&
1648 input_GetItem(p_input)->p_meta->psz_nowplaying &&
1649 *input_GetItem(p_input)->p_meta->psz_nowplaying )
1651 vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1652 input_GetItem(p_input)->p_meta->psz_nowplaying, NULL,
1653 p_vout->i_title_position,
1654 30 + p_vout->fmt_in.i_width
1655 - p_vout->fmt_in.i_visible_width
1656 - p_vout->fmt_in.i_x_offset,
1657 20 + p_vout->fmt_in.i_y_offset,
1660 else if( input_GetItem(p_input)->p_meta &&
1661 input_GetItem(p_input)->p_meta->psz_artist &&
1662 *input_GetItem(p_input)->p_meta->psz_artist )
1664 char *psz_string = NULL;
1666 psz_string = malloc( strlen(input_GetItem(p_input)->psz_name) +
1667 strlen(input_GetItem(p_input)->p_meta->psz_artist) );
1670 sprintf( psz_string, "%s - %s",
1671 input_GetItem(p_input)->psz_name,
1672 input_GetItem(p_input)->p_meta->psz_artist );
1674 vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1676 p_vout->i_title_position,
1677 30 + p_vout->fmt_in.i_width
1678 - p_vout->fmt_in.i_visible_width
1679 - p_vout->fmt_in.i_x_offset,
1680 20 + p_vout->fmt_in.i_y_offset,
1687 vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1688 input_GetItem(p_input)->psz_name, NULL,
1689 p_vout->i_title_position,
1690 30 + p_vout->fmt_in.i_width
1691 - p_vout->fmt_in.i_visible_width
1692 - p_vout->fmt_in.i_x_offset,
1693 20 + p_vout->fmt_in.i_y_offset,
1696 vlc_object_release( p_input );