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 if( p_vout->b_title_show )
212 DisplayTitleOnOSD( p_vout );
213 vlc_object_release( p_vout );
219 msg_Dbg( p_this, "no usable vout present, spawning one" );
221 p_vout = vout_Create( p_this, p_fmt );
227 /*****************************************************************************
228 * vout_Create: creates a new video output thread
229 *****************************************************************************
230 * This function creates a new video output thread, and returns a pointer
231 * to its description. On error, it returns NULL.
232 *****************************************************************************/
233 vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt )
235 vout_thread_t * p_vout; /* thread descriptor */
236 input_thread_t * p_input_thread;
237 int i_index; /* loop variable */
238 vlc_value_t val, text;
240 unsigned int i_width = p_fmt->i_width;
241 unsigned int i_height = p_fmt->i_height;
242 vlc_fourcc_t i_chroma = p_fmt->i_chroma;
243 unsigned int i_aspect = p_fmt->i_aspect;
245 config_chain_t *p_cfg;
249 /* Allocate descriptor */
250 p_vout = vlc_object_create( p_parent, VLC_OBJECT_VOUT );
253 msg_Err( p_parent, "out of memory" );
257 /* Initialize pictures - translation tables and functions
258 * will be initialized later in InitThread */
259 for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++)
261 p_vout->p_picture[i_index].pf_lock = NULL;
262 p_vout->p_picture[i_index].pf_unlock = NULL;
263 p_vout->p_picture[i_index].i_status = FREE_PICTURE;
264 p_vout->p_picture[i_index].i_type = EMPTY_PICTURE;
265 p_vout->p_picture[i_index].b_slow = 0;
268 /* No images in the heap */
269 p_vout->i_heap_size = 0;
271 /* Initialize the rendering heap */
272 I_RENDERPICTURES = 0;
274 vlc_ureduce( &p_fmt->i_sar_num, &p_fmt->i_sar_den,
275 p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 );
276 p_vout->fmt_render = *p_fmt; /* FIXME palette */
277 p_vout->fmt_in = *p_fmt; /* FIXME palette */
279 p_vout->render.i_width = i_width;
280 p_vout->render.i_height = i_height;
281 p_vout->render.i_chroma = i_chroma;
282 p_vout->render.i_aspect = i_aspect;
284 p_vout->render.i_rmask = 0;
285 p_vout->render.i_gmask = 0;
286 p_vout->render.i_bmask = 0;
288 p_vout->render.i_last_used_pic = -1;
289 p_vout->render.b_allow_modify_pics = 1;
291 /* Zero the output heap */
292 I_OUTPUTPICTURES = 0;
293 p_vout->output.i_width = 0;
294 p_vout->output.i_height = 0;
295 p_vout->output.i_chroma = 0;
296 p_vout->output.i_aspect = 0;
298 p_vout->output.i_rmask = 0;
299 p_vout->output.i_gmask = 0;
300 p_vout->output.i_bmask = 0;
302 /* Initialize misc stuff */
303 p_vout->i_changes = 0;
305 p_vout->b_grayscale = 0;
307 p_vout->b_interface = 0;
309 p_vout->b_fullscreen = 0;
310 p_vout->i_alignment = 0;
311 p_vout->render_time = 10;
312 p_vout->c_fps_samples = 0;
313 p_vout->b_filter_change = 0;
314 p_vout->pf_control = 0;
315 p_vout->p_parent_intf = 0;
316 p_vout->i_par_num = p_vout->i_par_den = 1;
318 /* Initialize locks */
319 vlc_mutex_init( p_vout, &p_vout->picture_lock );
320 vlc_mutex_init( p_vout, &p_vout->change_lock );
321 vlc_mutex_init( p_vout, &p_vout->vfilter_lock );
323 /* Mouse coordinates */
324 var_Create( p_vout, "mouse-x", VLC_VAR_INTEGER );
325 var_Create( p_vout, "mouse-y", VLC_VAR_INTEGER );
326 var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER );
327 var_Create( p_vout, "mouse-moved", VLC_VAR_BOOL );
328 var_Create( p_vout, "mouse-clicked", VLC_VAR_INTEGER );
330 /* Initialize subpicture unit */
331 p_vout->p_spu = spu_Create( p_vout );
332 spu_Attach( p_vout->p_spu, p_parent, VLC_TRUE );
334 /* Attach the new object now so we can use var inheritance below */
335 vlc_object_attach( p_vout, p_parent );
337 spu_Init( p_vout->p_spu );
339 /* Take care of some "interface/control" related initialisations */
340 vout_IntfInit( p_vout );
342 /* If the parent is not a VOUT object, that means we are at the start of
343 * the video output pipe */
344 if( p_parent->i_object_type != VLC_OBJECT_VOUT )
346 /* Look for the default filter configuration */
347 var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
348 var_Get( p_vout, "vout-filter", &val );
349 p_vout->psz_filter_chain = val.psz_string;
351 /* Apply video filter2 objects on the first vout */
352 var_Create( p_vout, "video-filter",
353 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
354 var_Get( p_vout, "video-filter", &val );
355 ParseVideoFilter2Chain( p_vout, val.psz_string );
356 free( val.psz_string );
360 /* continue the parent's filter chain */
363 /* Ugly hack to jump to our configuration chain */
364 p_vout->psz_filter_chain
365 = ((vout_thread_t *)p_parent)->psz_filter_chain;
366 p_vout->psz_filter_chain
367 = config_ChainCreate( &psz_tmp, &p_cfg, p_vout->psz_filter_chain );
368 config_ChainDestroy( p_cfg );
371 /* Create a video filter2 var ... but don't inherit values */
372 var_Create( p_vout, "video-filter", VLC_VAR_STRING );
373 ParseVideoFilter2Chain( p_vout, NULL );
376 var_AddCallback( p_vout, "video-filter", VideoFilter2Callback, NULL );
377 p_vout->b_vfilter_change = VLC_TRUE;
378 p_vout->i_vfilters = 0;
380 /* Choose the video output module */
381 if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain )
383 var_Create( p_vout, "vout", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
384 var_Get( p_vout, "vout", &val );
385 psz_parser = val.psz_string;
389 psz_parser = strdup( p_vout->psz_filter_chain );
392 /* Create the vout thread */
393 config_ChainCreate( &psz_name, &p_cfg, psz_parser );
395 p_vout->p_cfg = p_cfg;
396 p_vout->p_module = module_Need( p_vout,
397 ( p_vout->psz_filter_chain && *p_vout->psz_filter_chain ) ?
398 "video filter" : "video output", psz_name, 0 );
401 if( p_vout->p_module == NULL )
403 msg_Err( p_vout, "no suitable vout module" );
404 vlc_object_detach( p_vout );
405 vlc_object_destroy( p_vout );
409 /* Create a few object variables for interface interaction */
410 var_Create( p_vout, "deinterlace", VLC_VAR_STRING | VLC_VAR_HASCHOICE );
411 text.psz_string = _("Deinterlace");
412 var_Change( p_vout, "deinterlace", VLC_VAR_SETTEXT, &text, NULL );
413 val.psz_string = (char *)""; text.psz_string = _("Disable");
414 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
415 val.psz_string = (char *)"discard"; text.psz_string = _("Discard");
416 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
417 val.psz_string = (char *)"blend"; text.psz_string = _("Blend");
418 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
419 val.psz_string = (char *)"mean"; text.psz_string = _("Mean");
420 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
421 val.psz_string = (char *)"bob"; text.psz_string = _("Bob");
422 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
423 val.psz_string = (char *)"linear"; text.psz_string = _("Linear");
424 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
425 val.psz_string = (char *)"x"; text.psz_string = (char *)"X";
426 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
428 if( var_Get( p_vout, "deinterlace-mode", &val ) == VLC_SUCCESS )
430 var_Set( p_vout, "deinterlace", val );
431 if( val.psz_string ) free( val.psz_string );
433 var_AddCallback( p_vout, "deinterlace", DeinterlaceCallback, NULL );
435 var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
436 text.psz_string = _("Filters");
437 var_Change( p_vout, "vout-filter", VLC_VAR_SETTEXT, &text, NULL );
438 var_AddCallback( p_vout, "vout-filter", FilterCallback, NULL );
440 /* Calculate delay created by internal caching */
441 p_input_thread = (input_thread_t *)vlc_object_find( p_vout,
442 VLC_OBJECT_INPUT, FIND_ANYWHERE );
445 p_vout->i_pts_delay = p_input_thread->i_pts_delay;
446 vlc_object_release( p_input_thread );
450 p_vout->i_pts_delay = DEFAULT_PTS_DELAY;
453 if( vlc_thread_create( p_vout, "video output", RunThread,
454 VLC_THREAD_PRIORITY_OUTPUT, VLC_TRUE ) )
456 msg_Err( p_vout, "out of memory" );
457 module_Unneed( p_vout, p_vout->p_module );
458 vlc_object_detach( p_vout );
459 vlc_object_destroy( p_vout );
463 if( p_vout->b_error )
465 msg_Err( p_vout, "video output creation failed" );
467 /* Make sure the thread is destroyed */
468 vlc_object_kill( p_vout );
469 vlc_thread_join( p_vout );
471 vlc_object_detach( p_vout );
472 vlc_object_destroy( p_vout );
479 /*****************************************************************************
480 * vout_Destroy: destroys a previously created video output
481 *****************************************************************************
482 * Destroy a terminated thread.
483 * The function will request a destruction of the specified thread. If pi_error
484 * is NULL, it will return once the thread is destroyed. Else, it will be
485 * update using one of the THREAD_* constants.
486 *****************************************************************************/
487 void vout_Destroy( vout_thread_t *p_vout )
489 vout_thread_t *p_another_vout;
490 playlist_t *p_playlist = pl_Yield( p_vout );
492 /* Request thread destruction */
493 vlc_object_kill( p_vout );
494 vlc_thread_join( p_vout );
496 var_Destroy( p_vout, "intf-change" );
498 if( p_vout->psz_filter_chain ) free( p_vout->psz_filter_chain );
500 config_ChainDestroy( p_vout->p_cfg );
503 vlc_object_destroy( p_vout );
505 /* This is a dirty hack for mostly Linux, where there is no way to get the GUI
506 back if you closed it while playing video. This is solved in Mac OS X,
507 where we have this novelty called menubar, that will always allow you access
508 to the applications main functionality. They should try that on linux sometime */
509 p_another_vout = vlc_object_find( p_playlist,
510 VLC_OBJECT_VOUT, FIND_ANYWHERE );
511 if( p_another_vout == NULL )
514 val.b_bool = VLC_TRUE;
515 var_Set( p_playlist, "intf-show", val );
519 vlc_object_release( p_another_vout );
522 vlc_object_release( p_playlist );
525 /*****************************************************************************
526 * InitThread: initialize video output thread
527 *****************************************************************************
528 * This function is called from RunThread and performs the second step of the
529 * initialization. It returns 0 on success. Note that the thread's flag are not
530 * modified inside this function.
531 *****************************************************************************/
532 static int InitThread( vout_thread_t *p_vout )
534 int i, i_aspect_x, i_aspect_y;
536 vlc_mutex_lock( &p_vout->change_lock );
542 /* Initialize output method, it allocates direct buffers for us */
543 if( p_vout->pf_init( p_vout ) )
545 vlc_mutex_unlock( &p_vout->change_lock );
549 if( !I_OUTPUTPICTURES )
551 msg_Err( p_vout, "plugin was unable to allocate at least "
552 "one direct buffer" );
553 p_vout->pf_end( p_vout );
554 vlc_mutex_unlock( &p_vout->change_lock );
558 if( I_OUTPUTPICTURES > VOUT_MAX_PICTURES )
560 msg_Err( p_vout, "plugin allocated too many direct buffers, "
561 "our internal buffers must have overflown." );
562 p_vout->pf_end( p_vout );
563 vlc_mutex_unlock( &p_vout->change_lock );
567 msg_Dbg( p_vout, "got %i direct buffer(s)", I_OUTPUTPICTURES );
569 AspectRatio( p_vout->fmt_render.i_aspect, &i_aspect_x, &i_aspect_y );
571 msg_Dbg( p_vout, "picture in %ix%i (%i,%i,%ix%i), "
572 "chroma %4.4s, ar %i:%i, sar %i:%i",
573 p_vout->fmt_render.i_width, p_vout->fmt_render.i_height,
574 p_vout->fmt_render.i_x_offset, p_vout->fmt_render.i_y_offset,
575 p_vout->fmt_render.i_visible_width,
576 p_vout->fmt_render.i_visible_height,
577 (char*)&p_vout->fmt_render.i_chroma,
578 i_aspect_x, i_aspect_y,
579 p_vout->fmt_render.i_sar_num, p_vout->fmt_render.i_sar_den );
581 AspectRatio( p_vout->fmt_in.i_aspect, &i_aspect_x, &i_aspect_y );
583 msg_Dbg( p_vout, "picture user %ix%i (%i,%i,%ix%i), "
584 "chroma %4.4s, ar %i:%i, sar %i:%i",
585 p_vout->fmt_in.i_width, p_vout->fmt_in.i_height,
586 p_vout->fmt_in.i_x_offset, p_vout->fmt_in.i_y_offset,
587 p_vout->fmt_in.i_visible_width,
588 p_vout->fmt_in.i_visible_height,
589 (char*)&p_vout->fmt_in.i_chroma,
590 i_aspect_x, i_aspect_y,
591 p_vout->fmt_in.i_sar_num, p_vout->fmt_in.i_sar_den );
593 if( !p_vout->fmt_out.i_width || !p_vout->fmt_out.i_height )
595 p_vout->fmt_out.i_width = p_vout->fmt_out.i_visible_width =
596 p_vout->output.i_width;
597 p_vout->fmt_out.i_height = p_vout->fmt_out.i_visible_height =
598 p_vout->output.i_height;
599 p_vout->fmt_out.i_x_offset = p_vout->fmt_out.i_y_offset = 0;
601 p_vout->fmt_out.i_aspect = p_vout->output.i_aspect;
602 p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
604 if( !p_vout->fmt_out.i_sar_num || !p_vout->fmt_out.i_sar_num )
606 p_vout->fmt_out.i_sar_num = p_vout->fmt_out.i_aspect *
607 p_vout->fmt_out.i_height;
608 p_vout->fmt_out.i_sar_den = VOUT_ASPECT_FACTOR *
609 p_vout->fmt_out.i_width;
612 vlc_ureduce( &p_vout->fmt_out.i_sar_num, &p_vout->fmt_out.i_sar_den,
613 p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den, 0 );
615 AspectRatio( p_vout->fmt_out.i_aspect, &i_aspect_x, &i_aspect_y );
617 msg_Dbg( p_vout, "picture out %ix%i (%i,%i,%ix%i), "
618 "chroma %4.4s, ar %i:%i, sar %i:%i",
619 p_vout->fmt_out.i_width, p_vout->fmt_out.i_height,
620 p_vout->fmt_out.i_x_offset, p_vout->fmt_out.i_y_offset,
621 p_vout->fmt_out.i_visible_width,
622 p_vout->fmt_out.i_visible_height,
623 (char*)&p_vout->fmt_out.i_chroma,
624 i_aspect_x, i_aspect_y,
625 p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den );
627 /* Calculate shifts from system-updated masks */
628 MaskToShift( &p_vout->output.i_lrshift, &p_vout->output.i_rrshift,
629 p_vout->output.i_rmask );
630 MaskToShift( &p_vout->output.i_lgshift, &p_vout->output.i_rgshift,
631 p_vout->output.i_gmask );
632 MaskToShift( &p_vout->output.i_lbshift, &p_vout->output.i_rbshift,
633 p_vout->output.i_bmask );
635 /* Check whether we managed to create direct buffers similar to
636 * the render buffers, ie same size and chroma */
637 if( ( p_vout->output.i_width == p_vout->render.i_width )
638 && ( p_vout->output.i_height == p_vout->render.i_height )
639 && ( vout_ChromaCmp( p_vout->output.i_chroma, p_vout->render.i_chroma ) ) )
641 /* Cool ! We have direct buffers, we can ask the decoder to
642 * directly decode into them ! Map the first render buffers to
643 * the first direct buffers, but keep the first direct buffer
644 * for memcpy operations */
645 p_vout->b_direct = 1;
647 for( i = 1; i < VOUT_MAX_PICTURES; i++ )
649 if( p_vout->p_picture[ i ].i_type != DIRECT_PICTURE &&
650 I_RENDERPICTURES >= VOUT_MIN_DIRECT_PICTURES - 1 &&
651 p_vout->p_picture[ i - 1 ].i_type == DIRECT_PICTURE )
653 /* We have enough direct buffers so there's no need to
654 * try to use system memory buffers. */
657 PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
661 msg_Dbg( p_vout, "direct render, mapping "
662 "render pictures 0-%i to system pictures 1-%i",
663 VOUT_MAX_PICTURES - 2, VOUT_MAX_PICTURES - 1 );
667 /* Rats... Something is wrong here, we could not find an output
668 * plugin able to directly render what we decode. See if we can
669 * find a chroma plugin to do the conversion */
670 p_vout->b_direct = 0;
672 /* Choose the best module */
673 p_vout->chroma.p_module = module_Need( p_vout, "chroma", NULL, 0 );
675 if( p_vout->chroma.p_module == NULL )
677 msg_Err( p_vout, "no chroma module for %4.4s to %4.4s",
678 (char*)&p_vout->render.i_chroma,
679 (char*)&p_vout->output.i_chroma );
680 p_vout->pf_end( p_vout );
681 vlc_mutex_unlock( &p_vout->change_lock );
685 msg_Dbg( p_vout, "indirect render, mapping "
686 "render pictures 0-%i to system pictures %i-%i",
687 VOUT_MAX_PICTURES - 1, I_OUTPUTPICTURES,
688 I_OUTPUTPICTURES + VOUT_MAX_PICTURES - 1 );
690 /* Append render buffers after the direct buffers */
691 for( i = I_OUTPUTPICTURES; i < 2 * VOUT_MAX_PICTURES; i++ )
693 PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
696 /* Check if we have enough render pictures */
697 if( I_RENDERPICTURES == VOUT_MAX_PICTURES )
702 /* Link pictures back to their heap */
703 for( i = 0 ; i < I_RENDERPICTURES ; i++ )
705 PP_RENDERPICTURE[ i ]->p_heap = &p_vout->render;
708 for( i = 0 ; i < I_OUTPUTPICTURES ; i++ )
710 PP_OUTPUTPICTURE[ i ]->p_heap = &p_vout->output;
713 /* XXX XXX mark thread ready */
717 /*****************************************************************************
718 * RunThread: video output thread
719 *****************************************************************************
720 * Video output thread. This function does only returns when the thread is
721 * terminated. It handles the pictures arriving in the video heap and the
722 * display device events.
723 *****************************************************************************/
724 static void RunThread( vout_thread_t *p_vout)
726 int i_index; /* index in heap */
727 int i_idle_loops = 0; /* loops without displaying a picture */
728 mtime_t current_date; /* current date */
729 mtime_t display_date; /* display date */
731 picture_t * p_picture; /* picture pointer */
732 picture_t * p_last_picture = NULL; /* last picture */
733 picture_t * p_directbuffer; /* direct buffer to display */
735 subpicture_t * p_subpic = NULL; /* subpicture pointer */
737 input_thread_t *p_input = NULL ; /* Parent input, if it exists */
740 vlc_bool_t b_drop_late;
742 int i_displayed = 0, i_lost = 0, i_loops = 0;
747 p_vout->b_error = InitThread( p_vout );
749 var_Create( p_vout, "drop-late-frames", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
750 var_Get( p_vout, "drop-late-frames", &val );
751 b_drop_late = val.b_bool;
753 /* signal the creation of the vout */
754 vlc_thread_ready( p_vout );
756 if( p_vout->b_error )
758 /* Destroy thread structures allocated by Create and InitThread */
759 DestroyThread( p_vout );
763 if( p_vout->b_title_show )
764 DisplayTitleOnOSD( p_vout );
767 * Main loop - it is not executed if an error occurred during
770 while( (!p_vout->b_die) && (!p_vout->b_error) )
772 /* Initialize loop variables */
775 current_date = mdate();
777 if( p_input && p_input->b_die )
779 vlc_object_release( p_input );
784 if( i_loops % 20 == 0 )
788 p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT,
793 vlc_mutex_lock( &p_input->p->counters.counters_lock );
794 stats_UpdateInteger( p_vout, p_input->p->counters.p_lost_pictures,
796 stats_UpdateInteger( p_vout,
797 p_input->p->counters.p_displayed_pictures,
799 i_displayed = i_lost = 0;
800 vlc_mutex_unlock( &p_input->p->counters.counters_lock );
805 if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
807 msg_Dbg( p_vout, "picture heap: %d/%d",
808 I_RENDERPICTURES, p_vout->i_heap_size );
813 * Find the picture to display (the one with the earliest date).
814 * This operation does not need lock, since only READY_PICTUREs
816 for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
818 if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
819 && ( (p_picture == NULL) ||
820 (PP_RENDERPICTURE[i_index]->date < display_date) ) )
822 p_picture = PP_RENDERPICTURE[i_index];
823 display_date = p_picture->date;
829 /* If we met the last picture, parse again to see whether there is
830 * a more appropriate one. */
831 if( p_picture == p_last_picture )
833 for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
835 if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
836 && (PP_RENDERPICTURE[i_index] != p_last_picture)
837 && ((p_picture == p_last_picture) ||
838 (PP_RENDERPICTURE[i_index]->date < display_date)) )
840 p_picture = PP_RENDERPICTURE[i_index];
841 display_date = p_picture->date;
846 /* If we found better than the last picture, destroy it */
847 if( p_last_picture && p_picture != p_last_picture )
849 vlc_mutex_lock( &p_vout->picture_lock );
850 if( p_last_picture->i_refcount )
852 p_last_picture->i_status = DISPLAYED_PICTURE;
856 p_last_picture->i_status = DESTROYED_PICTURE;
857 p_vout->i_heap_size--;
859 vlc_mutex_unlock( &p_vout->picture_lock );
860 p_last_picture = NULL;
863 /* Compute FPS rate */
864 p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ]
867 if( !p_picture->b_force &&
868 p_picture != p_last_picture &&
869 display_date < current_date + p_vout->render_time &&
872 /* Picture is late: it will be destroyed and the thread
873 * will directly choose the next picture */
874 vlc_mutex_lock( &p_vout->picture_lock );
875 if( p_picture->i_refcount )
877 /* Pretend we displayed the picture, but don't destroy
878 * it since the decoder might still need it. */
879 p_picture->i_status = DISPLAYED_PICTURE;
883 /* Destroy the picture without displaying it */
884 p_picture->i_status = DESTROYED_PICTURE;
885 p_vout->i_heap_size--;
887 msg_Warn( p_vout, "late picture skipped ("I64Fd")",
888 current_date - display_date );
890 vlc_mutex_unlock( &p_vout->picture_lock );
896 current_date + p_vout->i_pts_delay + VOUT_BOGUS_DELAY )
898 /* Picture is waaay too early: it will be destroyed */
899 vlc_mutex_lock( &p_vout->picture_lock );
900 if( p_picture->i_refcount )
902 /* Pretend we displayed the picture, but don't destroy
903 * it since the decoder might still need it. */
904 p_picture->i_status = DISPLAYED_PICTURE;
908 /* Destroy the picture without displaying it */
909 p_picture->i_status = DESTROYED_PICTURE;
910 p_vout->i_heap_size--;
913 msg_Warn( p_vout, "vout warning: early picture skipped "
914 "("I64Fd")", display_date - current_date
915 - p_vout->i_pts_delay );
916 vlc_mutex_unlock( &p_vout->picture_lock );
921 if( display_date > current_date + VOUT_DISPLAY_DELAY )
923 /* A picture is ready to be rendered, but its rendering date
924 * is far from the current one so the thread will perform an
925 * empty loop as if no picture were found. The picture state
930 else if( p_picture == p_last_picture )
932 /* We are asked to repeat the previous picture, but we first
933 * wait for a couple of idle loops */
934 if( i_idle_loops < 4 )
941 /* We set the display date to something high, otherwise
942 * we'll have lots of problems with late pictures */
943 display_date = current_date + p_vout->render_time;
948 if( p_picture == NULL )
953 /* Video Filter2 stuff */
954 if( p_vout->b_vfilter_change == VLC_TRUE )
957 vlc_mutex_lock( &p_vout->vfilter_lock );
958 RemoveVideoFilters2( p_vout );
959 for( i = 0; i < p_vout->i_vfilters_cfg; i++ )
961 filter_t *p_vfilter =
962 p_vout->pp_vfilters[p_vout->i_vfilters] =
963 vlc_object_create( p_vout, VLC_OBJECT_FILTER );
965 vlc_object_attach( p_vfilter, p_vout );
967 p_vfilter->pf_vout_buffer_new = video_new_buffer_filter;
968 p_vfilter->pf_vout_buffer_del = video_del_buffer_filter;
970 if( !p_vout->i_vfilters )
972 p_vfilter->fmt_in.video = p_vout->fmt_render;
976 p_vfilter->fmt_in.video = (p_vfilter-1)->fmt_out.video;
978 /* TODO: one day filters in the middle of the chain might
979 * have a different fmt_out.video than fmt_render ... */
980 p_vfilter->fmt_out.video = p_vout->fmt_render;
982 p_vfilter->p_cfg = p_vout->p_vfilters_cfg[i];
983 p_vfilter->p_module = module_Need( p_vfilter, "video filter2",
984 p_vout->psz_vfilters[i],
987 if( p_vfilter->p_module )
990 malloc( sizeof( filter_owner_sys_t ) );
991 p_vfilter->p_owner->p_vout = p_vout;
992 p_vout->i_vfilters++;
993 msg_Dbg( p_vout, "video filter found (%s)",
994 p_vout->psz_vfilters[i] );
998 msg_Err( p_vout, "no video filter found (%s)",
999 p_vout->psz_vfilters[i] );
1000 vlc_object_detach( p_vfilter );
1001 vlc_object_destroy( p_vfilter );
1004 p_vout->b_vfilter_change = VLC_FALSE;
1005 vlc_mutex_unlock( &p_vout->vfilter_lock );
1011 for( i = 0; i < p_vout->i_vfilters; i++ )
1013 picture_t *p_old = p_picture;
1014 p_picture = p_vout->pp_vfilters[i]->pf_video_filter(
1015 p_vout->pp_vfilters[i], p_picture );
1020 /* FIXME: this is kind of wrong
1021 * if you have 2 or more vfilters and the 2nd breaks,
1022 * on the next loop the 1st one will be applied again */
1024 /* if p_old and p_picture are the same (ie the filter
1025 * worked on the old picture), then following code is
1026 * still alright since i_status gets changed back to
1027 * the right value */
1028 if( p_old->i_refcount )
1030 p_old->i_status = DISPLAYED_PICTURE;
1034 p_old->i_status = DESTROYED_PICTURE;
1036 p_picture->i_status = READY_PICTURE;
1040 if( p_picture && p_vout->b_snapshot )
1042 p_vout->b_snapshot = VLC_FALSE;
1043 vout_Snapshot( p_vout, p_picture );
1047 * Check for subpictures to display
1049 if( display_date > 0 )
1053 p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT,
1056 p_subpic = spu_SortSubpictures( p_vout->p_spu, display_date,
1057 p_input ? var_GetBool( p_input, "state" ) == PAUSE_S : VLC_FALSE );
1064 p_directbuffer = vout_RenderPicture( p_vout, p_picture, p_subpic );
1067 * Call the plugin-specific rendering method if there is one
1069 if( p_picture != NULL && p_directbuffer != NULL && p_vout->pf_render )
1071 /* Render the direct buffer returned by vout_RenderPicture */
1072 p_vout->pf_render( p_vout, p_directbuffer );
1078 if( display_date != 0 && p_directbuffer != NULL )
1080 mtime_t current_render_time = mdate() - current_date;
1081 /* if render time is very large we don't include it in the mean */
1082 if( current_render_time < p_vout->render_time +
1083 VOUT_DISPLAY_DELAY )
1085 /* Store render time using a sliding mean weighting to
1086 * current value in a 3 to 1 ratio*/
1087 p_vout->render_time *= 3;
1088 p_vout->render_time += current_render_time;
1089 p_vout->render_time >>= 2;
1093 /* Give back change lock */
1094 vlc_mutex_unlock( &p_vout->change_lock );
1096 /* Sleep a while or until a given date */
1097 if( display_date != 0 )
1099 /* If there are filters in the chain, better give them the picture
1101 if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain )
1103 mwait( display_date - VOUT_MWAIT_TOLERANCE );
1108 msleep( VOUT_IDLE_SLEEP );
1111 /* On awakening, take back lock and send immediately picture
1113 vlc_mutex_lock( &p_vout->change_lock );
1116 * Display the previously rendered picture
1118 if( p_picture != NULL && p_directbuffer != NULL )
1120 /* Display the direct buffer returned by vout_RenderPicture */
1121 if( p_vout->pf_display )
1123 p_vout->pf_display( p_vout, p_directbuffer );
1126 /* Tell the vout this was the last picture and that it does not
1127 * need to be forced anymore. */
1128 p_last_picture = p_picture;
1129 p_last_picture->b_force = 0;
1132 if( p_picture != NULL )
1134 /* Reinitialize idle loop count */
1139 * Check events and manage thread
1141 if( p_vout->pf_manage && p_vout->pf_manage( p_vout ) )
1143 /* A fatal error occurred, and the thread must terminate
1144 * immediately, without displaying anything - setting b_error to 1
1145 * causes the immediate end of the main while() loop. */
1146 p_vout->b_error = 1;
1149 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
1151 /* this must only happen when the vout plugin is incapable of
1152 * rescaling the picture itself. In this case we need to destroy
1153 * the current picture buffers and recreate new ones with the right
1157 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
1159 p_vout->pf_end( p_vout );
1160 for( i = 0; i < I_OUTPUTPICTURES; i++ )
1161 p_vout->p_picture[ i ].i_status = FREE_PICTURE;
1163 I_OUTPUTPICTURES = 0;
1164 if( p_vout->pf_init( p_vout ) )
1166 msg_Err( p_vout, "cannot resize display" );
1167 /* FIXME: pf_end will be called again in EndThread() */
1168 p_vout->b_error = 1;
1171 /* Need to reinitialise the chroma plugin */
1172 if( p_vout->chroma.p_module )
1174 if( p_vout->chroma.p_module->pf_deactivate )
1175 p_vout->chroma.p_module->pf_deactivate( VLC_OBJECT(p_vout) );
1176 p_vout->chroma.p_module->pf_activate( VLC_OBJECT(p_vout) );
1180 if( p_vout->i_changes & VOUT_PICTURE_BUFFERS_CHANGE )
1182 /* This happens when the picture buffers need to be recreated.
1183 * This is useful on multimonitor displays for instance.
1185 * Warning: This only works when the vout creates only 1 picture
1187 p_vout->i_changes &= ~VOUT_PICTURE_BUFFERS_CHANGE;
1189 if( !p_vout->b_direct )
1191 module_Unneed( p_vout, p_vout->chroma.p_module );
1194 vlc_mutex_lock( &p_vout->picture_lock );
1196 p_vout->pf_end( p_vout );
1198 I_OUTPUTPICTURES = I_RENDERPICTURES = 0;
1200 p_vout->b_error = InitThread( p_vout );
1202 vlc_mutex_unlock( &p_vout->picture_lock );
1208 vlc_object_release( p_input );
1212 * Error loop - wait until the thread destruction is requested
1214 if( p_vout->b_error )
1216 ErrorThread( p_vout );
1220 EndThread( p_vout );
1222 /* Destroy thread structures allocated by CreateThread */
1223 DestroyThread( p_vout );
1226 /*****************************************************************************
1227 * ErrorThread: RunThread() error loop
1228 *****************************************************************************
1229 * This function is called when an error occurred during thread main's loop.
1230 * The thread can still receive feed, but must be ready to terminate as soon
1232 *****************************************************************************/
1233 static void ErrorThread( vout_thread_t *p_vout )
1235 /* Wait until a `die' order */
1236 while( !p_vout->b_die )
1239 msleep( VOUT_IDLE_SLEEP );
1243 /*****************************************************************************
1244 * EndThread: thread destruction
1245 *****************************************************************************
1246 * This function is called when the thread ends after a sucessful
1247 * initialization. It frees all resources allocated by InitThread.
1248 *****************************************************************************/
1249 static void EndThread( vout_thread_t *p_vout )
1251 int i_index; /* index in heap */
1255 struct tms cpu_usage;
1256 times( &cpu_usage );
1258 msg_Dbg( p_vout, "cpu usage (user: %d, system: %d)",
1259 cpu_usage.tms_utime, cpu_usage.tms_stime );
1263 if( !p_vout->b_direct )
1265 module_Unneed( p_vout, p_vout->chroma.p_module );
1268 /* Destroy all remaining pictures */
1269 for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++ )
1271 if ( p_vout->p_picture[i_index].i_type == MEMORY_PICTURE )
1273 free( p_vout->p_picture[i_index].p_data_orig );
1277 /* Destroy subpicture unit */
1278 spu_Attach( p_vout->p_spu, VLC_OBJECT(p_vout), VLC_FALSE );
1279 spu_Destroy( p_vout->p_spu );
1281 /* Destroy the video filters2 */
1282 RemoveVideoFilters2( p_vout );
1284 /* Destroy translation tables */
1285 p_vout->pf_end( p_vout );
1287 /* Release the change lock */
1288 vlc_mutex_unlock( &p_vout->change_lock );
1291 /*****************************************************************************
1292 * DestroyThread: thread destruction
1293 *****************************************************************************
1294 * This function is called when the thread ends. It frees all ressources
1295 * allocated by CreateThread. Status is available at this stage.
1296 *****************************************************************************/
1297 static void DestroyThread( vout_thread_t *p_vout )
1299 /* Destroy the locks */
1300 vlc_mutex_destroy( &p_vout->picture_lock );
1301 vlc_mutex_destroy( &p_vout->change_lock );
1302 vlc_mutex_destroy( &p_vout->vfilter_lock );
1304 /* Release the module */
1305 if( p_vout && p_vout->p_module )
1307 module_Unneed( p_vout, p_vout->p_module );
1311 /* following functions are local */
1313 static int ReduceHeight( int i_ratio )
1315 int i_dummy = VOUT_ASPECT_FACTOR;
1323 /* VOUT_ASPECT_FACTOR is (2^7 * 3^3 * 5^3), we just check for 2, 3 and 5 */
1324 while( !(i_ratio & 1) && !(i_dummy & 1) )
1331 while( !(i_ratio % 3) && !(i_dummy % 3) )
1338 while( !(i_ratio % 5) && !(i_dummy % 5) )
1348 static void AspectRatio( int i_aspect, int *i_aspect_x, int *i_aspect_y )
1350 unsigned int i_pgcd = ReduceHeight( i_aspect );
1351 *i_aspect_x = i_aspect / i_pgcd;
1352 *i_aspect_y = VOUT_ASPECT_FACTOR / i_pgcd;
1355 /*****************************************************************************
1356 * BinaryLog: computes the base 2 log of a binary value
1357 *****************************************************************************
1358 * This functions is used by MaskToShift, to get a bit index from a binary
1360 *****************************************************************************/
1361 static int BinaryLog( uint32_t i )
1365 if( i == 0 ) return -31337;
1367 if( i & 0xffff0000 ) i_log += 16;
1368 if( i & 0xff00ff00 ) i_log += 8;
1369 if( i & 0xf0f0f0f0 ) i_log += 4;
1370 if( i & 0xcccccccc ) i_log += 2;
1371 if( i & 0xaaaaaaaa ) i_log += 1;
1376 /*****************************************************************************
1377 * MaskToShift: transform a color mask into right and left shifts
1378 *****************************************************************************
1379 * This function is used for obtaining color shifts from masks.
1380 *****************************************************************************/
1381 static void MaskToShift( int *pi_left, int *pi_right, uint32_t i_mask )
1383 uint32_t i_low, i_high; /* lower hand higher bits of the mask */
1387 *pi_left = *pi_right = 0;
1392 i_low = i_high = i_mask;
1394 i_low &= - (int32_t)i_low; /* lower bit of the mask */
1395 i_high += i_low; /* higher bit of the mask */
1397 /* Transform bits into an index. Also deal with i_high overflow, which
1398 * is faster than changing the BinaryLog code to handle 64 bit integers. */
1399 i_low = BinaryLog (i_low);
1400 i_high = i_high ? BinaryLog (i_high) : 32;
1402 /* Update pointers and return */
1404 *pi_right = (8 - i_high + i_low);
1407 /*****************************************************************************
1408 * vout_VarCallback: generic callback for intf variables
1409 *****************************************************************************/
1410 int vout_VarCallback( vlc_object_t * p_this, const char * psz_variable,
1411 vlc_value_t oldval, vlc_value_t newval,
1414 vout_thread_t * p_vout = (vout_thread_t *)p_this;
1416 (void)psz_variable; (void)newval; (void)oldval; (void)p_data;
1417 val.b_bool = VLC_TRUE;
1418 var_Set( p_vout, "intf-change", val );
1422 /*****************************************************************************
1423 * Helper thread for object variables callbacks.
1424 * Only used to avoid deadlocks when using the video embedded mode.
1425 *****************************************************************************/
1426 typedef struct suxor_thread_t
1429 input_thread_t *p_input;
1433 static void SuxorRestartVideoES( suxor_thread_t *p_this )
1437 vlc_thread_ready( p_this );
1439 /* Now restart current video stream */
1440 var_Get( p_this->p_input, "video-es", &val );
1441 if( val.i_int >= 0 )
1444 val_es.i_int = -VIDEO_ES;
1445 var_Set( p_this->p_input, "video-es", val_es );
1446 var_Set( p_this->p_input, "video-es", val );
1449 vlc_object_release( p_this->p_input );
1452 CloseHandle( p_this->thread_id );
1455 vlc_object_destroy( p_this );
1458 /*****************************************************************************
1459 * object variables callbacks: a bunch of object variables are used by the
1460 * interfaces to interact with the vout.
1461 *****************************************************************************/
1462 static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd,
1463 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1465 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1466 input_thread_t *p_input;
1469 char *psz_mode = newval.psz_string;
1470 char *psz_filter, *psz_deinterlace = NULL;
1471 (void)psz_cmd; (void)oldval; (void)p_data;
1473 var_Get( p_vout, "vout-filter", &val );
1474 psz_filter = val.psz_string;
1475 if( psz_filter ) psz_deinterlace = strstr( psz_filter, "deinterlace" );
1477 if( !psz_mode || !*psz_mode )
1479 if( psz_deinterlace )
1481 char *psz_src = psz_deinterlace + sizeof("deinterlace") - 1;
1482 if( psz_src[0] == ':' ) psz_src++;
1483 memmove( psz_deinterlace, psz_src, strlen(psz_src) + 1 );
1486 else if( !psz_deinterlace )
1488 psz_filter = realloc( psz_filter, strlen( psz_filter ) +
1489 sizeof(":deinterlace") );
1490 if( psz_filter && *psz_filter ) strcat( psz_filter, ":" );
1491 strcat( psz_filter, "deinterlace" );
1494 p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1496 if( !p_input ) return VLC_EGENERIC;
1498 if( psz_mode && *psz_mode )
1500 /* Modify input as well because the vout might have to be restarted */
1501 val.psz_string = psz_mode;
1502 var_Create( p_input, "deinterlace-mode", VLC_VAR_STRING );
1503 var_Set( p_input, "deinterlace-mode", val );
1505 vlc_object_release( p_input );
1507 val.b_bool = VLC_TRUE;
1508 var_Set( p_vout, "intf-change", val );
1510 val.psz_string = psz_filter;
1511 var_Set( p_vout, "vout-filter", val );
1512 if( psz_filter ) free( psz_filter );
1517 static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd,
1518 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1520 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1521 input_thread_t *p_input;
1523 (void)psz_cmd; (void)oldval; (void)p_data;
1525 p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1529 msg_Err( p_vout, "Input not found" );
1530 return( VLC_EGENERIC );
1533 val.b_bool = VLC_TRUE;
1534 var_Set( p_vout, "intf-change", val );
1536 /* Modify input as well because the vout might have to be restarted */
1537 val.psz_string = newval.psz_string;
1538 var_Create( p_input, "vout-filter", VLC_VAR_STRING );
1540 var_Set( p_input, "vout-filter", val );
1542 /* Now restart current video stream */
1543 var_Get( p_input, "video-es", &val );
1544 if( val.i_int >= 0 )
1546 suxor_thread_t *p_suxor =
1547 vlc_object_create( p_vout, sizeof(suxor_thread_t) );
1548 p_suxor->p_input = p_input;
1549 p_vout->b_filter_change = VLC_TRUE;
1550 vlc_object_yield( p_input );
1551 vlc_thread_create( p_suxor, "suxor", SuxorRestartVideoES,
1552 VLC_THREAD_PRIORITY_LOW, VLC_FALSE );
1555 vlc_object_release( p_input );
1560 /*****************************************************************************
1561 * Video Filter2 stuff
1562 *****************************************************************************/
1563 static int ParseVideoFilter2Chain( vout_thread_t *p_vout, char *psz_vfilters )
1566 for( i = 0; i < p_vout->i_vfilters_cfg; i++ )
1568 struct config_chain_t *p_cfg =
1569 p_vout->p_vfilters_cfg[p_vout->i_vfilters_cfg];
1570 config_ChainDestroy( p_cfg );
1571 if( p_vout->psz_vfilters[p_vout->i_vfilters_cfg] )
1573 free( p_vout->psz_vfilters[p_vout->i_vfilters_cfg] );
1574 p_vout->psz_vfilters[p_vout->i_vfilters_cfg] = NULL;
1577 p_vout->i_vfilters_cfg = 0;
1578 if( psz_vfilters && *psz_vfilters )
1580 char *psz_parser = psz_vfilters;
1582 while( psz_parser && *psz_parser )
1584 psz_parser = config_ChainCreate(
1585 &p_vout->psz_vfilters[p_vout->i_vfilters_cfg],
1586 &p_vout->p_vfilters_cfg[p_vout->i_vfilters_cfg],
1588 msg_Dbg( p_vout, "adding vfilter: %s",
1589 p_vout->psz_vfilters[p_vout->i_vfilters_cfg] );
1590 p_vout->i_vfilters_cfg++;
1591 if( psz_parser && *psz_parser )
1593 if( p_vout->i_vfilters_cfg == MAX_VFILTERS )
1596 "maximum number of video filters reached. \"%s\" discarded",
1606 static int VideoFilter2Callback( vlc_object_t *p_this, char const *psz_cmd,
1607 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1609 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1610 (void)psz_cmd; (void)oldval; (void)p_data;
1612 vlc_mutex_lock( &p_vout->vfilter_lock );
1613 ParseVideoFilter2Chain( p_vout, newval.psz_string );
1614 p_vout->b_vfilter_change = VLC_TRUE;
1615 vlc_mutex_unlock( &p_vout->vfilter_lock );
1620 static void RemoveVideoFilters2( vout_thread_t *p_vout )
1623 for( i = 0; i < p_vout->i_vfilters; i++ )
1625 vlc_object_detach( p_vout->pp_vfilters[i] );
1626 if( p_vout->pp_vfilters[i]->p_module )
1628 module_Unneed( p_vout->pp_vfilters[i],
1629 p_vout->pp_vfilters[i]->p_module );
1632 free( p_vout->pp_vfilters[i]->p_owner );
1633 vlc_object_destroy( p_vout->pp_vfilters[i] );
1635 p_vout->i_vfilters = 0;
1638 static void DisplayTitleOnOSD( vout_thread_t *p_vout )
1640 input_thread_t *p_input;
1641 mtime_t i_now, i_stop;
1643 p_input = (input_thread_t *)vlc_object_find( p_vout,
1644 VLC_OBJECT_INPUT, FIND_ANYWHERE );
1648 i_stop = i_now + (mtime_t)(p_vout->i_title_timeout * 1000);
1649 if( !EMPTY_STR(input_item_GetNowPlaying(input_GetItem(p_input))) )
1651 vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1652 input_item_GetNowPlaying(input_GetItem(p_input)), 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( !EMPTY_STR(input_item_GetArtist(input_GetItem(p_input))) )
1662 char *psz_string = NULL;
1664 psz_string = malloc( strlen(input_GetItem(p_input)->psz_name) ) +
1665 strlen( input_item_GetArtist(input_GetItem(p_input)) );
1668 sprintf( psz_string, "%s - %s",
1669 input_GetItem(p_input)->psz_name,
1670 input_item_GetArtist(input_GetItem(p_input)) );
1672 vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1674 p_vout->i_title_position,
1675 30 + p_vout->fmt_in.i_width
1676 - p_vout->fmt_in.i_visible_width
1677 - p_vout->fmt_in.i_x_offset,
1678 20 + p_vout->fmt_in.i_y_offset,
1685 vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1686 input_GetItem(p_input)->psz_name, NULL,
1687 p_vout->i_title_position,
1688 30 + p_vout->fmt_in.i_width
1689 - p_vout->fmt_in.i_visible_width
1690 - p_vout->fmt_in.i_x_offset,
1691 20 + p_vout->fmt_in.i_y_offset,
1694 vlc_object_release( p_input );