1 /*****************************************************************************
2 * video_output.c : video output thread
4 * This module describes the programming interface for video output threads.
5 * It includes functions allowing to open a new thread, send pictures to a
6 * thread, and destroy a previously oppened video output thread.
7 *****************************************************************************
8 * Copyright (C) 2000-2007 the VideoLAN team
11 * Authors: Vincent Seguin <seguin@via.ecp.fr>
12 * Gildas Bazin <gbazin@videolan.org>
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
27 *****************************************************************************/
29 /*****************************************************************************
31 *****************************************************************************/
38 #include <stdlib.h> /* free() */
42 #ifdef HAVE_SYS_TIMES_H
43 # include <sys/times.h>
47 #include <vlc_playlist.h>
49 #include <vlc_filter.h>
52 #if defined( __APPLE__ )
53 /* Include darwin_specific.h here if needed */
56 /** FIXME This is quite ugly but needed while we don't have counters
58 #include "input/input_internal.h"
60 #include "modules/modules.h"
62 /*****************************************************************************
64 *****************************************************************************/
65 static int InitThread ( vout_thread_t * );
66 static void RunThread ( vout_thread_t * );
67 static void ErrorThread ( vout_thread_t * );
68 static void EndThread ( vout_thread_t * );
69 static void DestroyThread ( vout_thread_t * );
71 static void AspectRatio ( int, int *, int * );
72 static int BinaryLog ( uint32_t );
73 static void MaskToShift ( int *, int *, uint32_t );
75 /* Object variables callbacks */
76 static int DeinterlaceCallback( vlc_object_t *, char const *,
77 vlc_value_t, vlc_value_t, void * );
78 static int FilterCallback( vlc_object_t *, char const *,
79 vlc_value_t, vlc_value_t, void * );
80 static int VideoFilter2Callback( vlc_object_t *, char const *,
81 vlc_value_t, vlc_value_t, void * );
83 /* From vout_intf.c */
84 int vout_Snapshot( vout_thread_t *, picture_t * );
86 /* Video filter2 parsing */
87 static int ParseVideoFilter2Chain( vout_thread_t *, char * );
88 static void RemoveVideoFilters2( vout_thread_t *p_vout );
90 /* Display media title in OSD */
91 static void DisplayTitleOnOSD( vout_thread_t *p_vout );
93 /*****************************************************************************
94 * Video Filter2 functions
95 *****************************************************************************/
96 struct filter_owner_sys_t
98 vout_thread_t *p_vout;
101 static picture_t *video_new_buffer_filter( filter_t *p_filter )
103 picture_t *p_picture;
104 vout_thread_t *p_vout = p_filter->p_owner->p_vout;
106 p_picture = vout_CreatePicture( p_vout, 0, 0, 0 );
111 static void video_del_buffer_filter( filter_t *p_filter, picture_t *p_pic )
113 vout_DestroyPicture( p_filter->p_owner->p_vout, p_pic );
116 /*****************************************************************************
117 * vout_Request: find a video output thread, create one, or destroy one.
118 *****************************************************************************
119 * This function looks for a video output thread matching the current
120 * properties. If not found, it spawns a new one.
121 *****************************************************************************/
122 vout_thread_t *__vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout,
123 video_format_t *p_fmt )
127 /* Reattach video output to playlist before bailing out */
130 playlist_t *p_playlist = pl_Yield( p_this );
131 spu_Attach( p_vout->p_spu, p_this, VLC_FALSE );
132 vlc_object_detach( p_vout );
133 vlc_object_attach( p_vout, p_playlist );
134 pl_Release( p_this );
139 /* If a video output was provided, lock it, otherwise look for one. */
142 vlc_object_yield( p_vout );
146 p_vout = vlc_object_find( p_this, VLC_OBJECT_VOUT, FIND_CHILD );
150 playlist_t *p_playlist = pl_Yield( p_this );
151 vlc_mutex_lock( &p_playlist->gc_lock );
152 p_vout = vlc_object_find( p_playlist,
153 VLC_OBJECT_VOUT, FIND_CHILD );
154 /* only first children of p_input for unused vout */
155 if( p_vout && p_vout->p_parent != (vlc_object_t *)p_playlist )
157 vlc_object_release( p_vout );
161 vlc_object_detach( p_vout ); /* Remove it from the GC */
162 vlc_mutex_unlock( &p_playlist->gc_lock );
163 pl_Release( p_this );
167 /* If we now have a video output, check it has the right properties */
170 char *psz_filter_chain;
173 /* We don't directly check for the "vout-filter" variable for obvious
174 * performance reasons. */
175 if( p_vout->b_filter_change )
177 var_Get( p_vout, "vout-filter", &val );
178 psz_filter_chain = val.psz_string;
180 if( psz_filter_chain && !*psz_filter_chain )
182 free( psz_filter_chain );
183 psz_filter_chain = NULL;
185 if( p_vout->psz_filter_chain && !*p_vout->psz_filter_chain )
187 free( p_vout->psz_filter_chain );
188 p_vout->psz_filter_chain = NULL;
191 if( !psz_filter_chain && !p_vout->psz_filter_chain )
193 p_vout->b_filter_change = VLC_FALSE;
196 if( psz_filter_chain ) free( psz_filter_chain );
199 if( ( p_vout->fmt_render.i_width != p_fmt->i_width ) ||
200 ( p_vout->fmt_render.i_height != p_fmt->i_height ) ||
201 ( p_vout->fmt_render.i_aspect != p_fmt->i_aspect ) ||
202 p_vout->b_filter_change )
204 /* We are not interested in this format, close this vout */
205 vlc_object_release( p_vout );
206 vout_Destroy( p_vout );
211 /* This video output is cool! Hijack it. */
212 spu_Attach( p_vout->p_spu, p_this, VLC_TRUE );
213 vlc_object_attach( p_vout, p_this );
214 if( p_vout->b_title_show )
215 DisplayTitleOnOSD( p_vout );
216 vlc_object_release( p_vout );
222 msg_Dbg( p_this, "no usable vout present, spawning one" );
224 p_vout = vout_Create( p_this, p_fmt );
230 /*****************************************************************************
231 * vout_Create: creates a new video output thread
232 *****************************************************************************
233 * This function creates a new video output thread, and returns a pointer
234 * to its description. On error, it returns NULL.
235 *****************************************************************************/
236 vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt )
238 vout_thread_t * p_vout; /* thread descriptor */
239 input_thread_t * p_input_thread;
240 int i_index; /* loop variable */
241 vlc_value_t val, text;
243 unsigned int i_width = p_fmt->i_width;
244 unsigned int i_height = p_fmt->i_height;
245 vlc_fourcc_t i_chroma = p_fmt->i_chroma;
246 unsigned int i_aspect = p_fmt->i_aspect;
248 config_chain_t *p_cfg;
252 /* Allocate descriptor */
253 p_vout = vlc_object_create( p_parent, VLC_OBJECT_VOUT );
256 msg_Err( p_parent, "out of memory" );
260 /* Initialize pictures - translation tables and functions
261 * will be initialized later in InitThread */
262 for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++)
264 p_vout->p_picture[i_index].pf_lock = NULL;
265 p_vout->p_picture[i_index].pf_unlock = NULL;
266 p_vout->p_picture[i_index].i_status = FREE_PICTURE;
267 p_vout->p_picture[i_index].i_type = EMPTY_PICTURE;
268 p_vout->p_picture[i_index].b_slow = 0;
271 /* No images in the heap */
272 p_vout->i_heap_size = 0;
274 /* Initialize the rendering heap */
275 I_RENDERPICTURES = 0;
277 vlc_ureduce( &p_fmt->i_sar_num, &p_fmt->i_sar_den,
278 p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 );
279 p_vout->fmt_render = *p_fmt; /* FIXME palette */
280 p_vout->fmt_in = *p_fmt; /* FIXME palette */
282 p_vout->render.i_width = i_width;
283 p_vout->render.i_height = i_height;
284 p_vout->render.i_chroma = i_chroma;
285 p_vout->render.i_aspect = i_aspect;
287 p_vout->render.i_rmask = 0;
288 p_vout->render.i_gmask = 0;
289 p_vout->render.i_bmask = 0;
291 p_vout->render.i_last_used_pic = -1;
292 p_vout->render.b_allow_modify_pics = 1;
294 /* Zero the output heap */
295 I_OUTPUTPICTURES = 0;
296 p_vout->output.i_width = 0;
297 p_vout->output.i_height = 0;
298 p_vout->output.i_chroma = 0;
299 p_vout->output.i_aspect = 0;
301 p_vout->output.i_rmask = 0;
302 p_vout->output.i_gmask = 0;
303 p_vout->output.i_bmask = 0;
305 /* Initialize misc stuff */
306 p_vout->i_changes = 0;
308 p_vout->b_grayscale = 0;
310 p_vout->b_interface = 0;
312 p_vout->b_fullscreen = 0;
313 p_vout->i_alignment = 0;
314 p_vout->render_time = 10;
315 p_vout->c_fps_samples = 0;
316 p_vout->b_filter_change = 0;
317 p_vout->pf_control = 0;
318 p_vout->p_parent_intf = 0;
319 p_vout->i_par_num = p_vout->i_par_den = 1;
321 /* Initialize locks */
322 vlc_mutex_init( p_vout, &p_vout->picture_lock );
323 vlc_mutex_init( p_vout, &p_vout->change_lock );
324 vlc_mutex_init( p_vout, &p_vout->vfilter_lock );
326 /* Mouse coordinates */
327 var_Create( p_vout, "mouse-x", VLC_VAR_INTEGER );
328 var_Create( p_vout, "mouse-y", VLC_VAR_INTEGER );
329 var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER );
330 var_Create( p_vout, "mouse-moved", VLC_VAR_BOOL );
331 var_Create( p_vout, "mouse-clicked", VLC_VAR_INTEGER );
333 /* Initialize subpicture unit */
334 p_vout->p_spu = spu_Create( p_vout );
335 spu_Attach( p_vout->p_spu, p_parent, VLC_TRUE );
337 /* Attach the new object now so we can use var inheritance below */
338 vlc_object_attach( p_vout, p_parent );
340 spu_Init( p_vout->p_spu );
342 /* Take care of some "interface/control" related initialisations */
343 vout_IntfInit( p_vout );
345 /* If the parent is not a VOUT object, that means we are at the start of
346 * the video output pipe */
347 if( p_parent->i_object_type != VLC_OBJECT_VOUT )
349 /* Look for the default filter configuration */
350 var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
351 var_Get( p_vout, "vout-filter", &val );
352 p_vout->psz_filter_chain = val.psz_string;
354 /* Apply video filter2 objects on the first vout */
355 var_Create( p_vout, "video-filter",
356 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
357 var_Get( p_vout, "video-filter", &val );
358 ParseVideoFilter2Chain( p_vout, val.psz_string );
359 free( val.psz_string );
363 /* continue the parent's filter chain */
366 /* Ugly hack to jump to our configuration chain */
367 p_vout->psz_filter_chain
368 = ((vout_thread_t *)p_parent)->psz_filter_chain;
369 p_vout->psz_filter_chain
370 = config_ChainCreate( &psz_tmp, &p_cfg, p_vout->psz_filter_chain );
371 config_ChainDestroy( p_cfg );
374 /* Create a video filter2 var ... but don't inherit values */
375 var_Create( p_vout, "video-filter", VLC_VAR_STRING );
376 ParseVideoFilter2Chain( p_vout, NULL );
379 var_AddCallback( p_vout, "video-filter", VideoFilter2Callback, NULL );
380 p_vout->b_vfilter_change = VLC_TRUE;
381 p_vout->i_vfilters = 0;
383 /* Choose the video output module */
384 if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain )
386 var_Create( p_vout, "vout", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
387 var_Get( p_vout, "vout", &val );
388 psz_parser = val.psz_string;
392 psz_parser = strdup( p_vout->psz_filter_chain );
395 /* Create the vout thread */
396 config_ChainCreate( &psz_name, &p_cfg, psz_parser );
398 p_vout->p_cfg = p_cfg;
399 p_vout->p_module = module_Need( p_vout,
400 ( p_vout->psz_filter_chain && *p_vout->psz_filter_chain ) ?
401 "video filter" : "video output", psz_name, p_vout->psz_filter_chain && *p_vout->psz_filter_chain );
404 if( p_vout->p_module == NULL )
406 msg_Err( p_vout, "no suitable vout module" );
407 vlc_object_detach( p_vout );
408 vlc_object_destroy( p_vout );
412 /* Create a few object variables for interface interaction */
413 var_Create( p_vout, "deinterlace", VLC_VAR_STRING | VLC_VAR_HASCHOICE );
414 text.psz_string = _("Deinterlace");
415 var_Change( p_vout, "deinterlace", VLC_VAR_SETTEXT, &text, NULL );
416 val.psz_string = (char *)""; text.psz_string = _("Disable");
417 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
418 val.psz_string = (char *)"discard"; text.psz_string = _("Discard");
419 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
420 val.psz_string = (char *)"blend"; text.psz_string = _("Blend");
421 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
422 val.psz_string = (char *)"mean"; text.psz_string = _("Mean");
423 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
424 val.psz_string = (char *)"bob"; text.psz_string = _("Bob");
425 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
426 val.psz_string = (char *)"linear"; text.psz_string = _("Linear");
427 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
428 val.psz_string = (char *)"x"; text.psz_string = (char *)"X";
429 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
431 if( var_Get( p_vout, "deinterlace-mode", &val ) == VLC_SUCCESS )
433 var_Set( p_vout, "deinterlace", val );
434 if( val.psz_string ) free( val.psz_string );
436 var_AddCallback( p_vout, "deinterlace", DeinterlaceCallback, NULL );
438 var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
439 text.psz_string = _("Filters");
440 var_Change( p_vout, "vout-filter", VLC_VAR_SETTEXT, &text, NULL );
441 var_AddCallback( p_vout, "vout-filter", FilterCallback, NULL );
443 /* Calculate delay created by internal caching */
444 p_input_thread = (input_thread_t *)vlc_object_find( p_vout,
445 VLC_OBJECT_INPUT, FIND_ANYWHERE );
448 p_vout->i_pts_delay = p_input_thread->i_pts_delay;
449 vlc_object_release( p_input_thread );
453 p_vout->i_pts_delay = DEFAULT_PTS_DELAY;
456 if( vlc_thread_create( p_vout, "video output", RunThread,
457 VLC_THREAD_PRIORITY_OUTPUT, VLC_TRUE ) )
459 msg_Err( p_vout, "out of memory" );
460 module_Unneed( p_vout, p_vout->p_module );
461 vlc_object_detach( p_vout );
462 vlc_object_destroy( p_vout );
466 if( p_vout->b_error )
468 msg_Err( p_vout, "video output creation failed" );
470 /* Make sure the thread is destroyed */
471 vlc_object_kill( p_vout );
472 vlc_thread_join( p_vout );
474 vlc_object_detach( p_vout );
475 vlc_object_destroy( p_vout );
482 /*****************************************************************************
483 * vout_Destroy: destroys a previously created video output
484 *****************************************************************************
485 * Destroy a terminated thread.
486 * The function will request a destruction of the specified thread. If pi_error
487 * is NULL, it will return once the thread is destroyed. Else, it will be
488 * update using one of the THREAD_* constants.
489 *****************************************************************************/
490 void vout_Destroy( vout_thread_t *p_vout )
492 vout_thread_t *p_another_vout;
493 playlist_t *p_playlist = pl_Yield( p_vout );
495 /* Request thread destruction */
496 vlc_object_kill( p_vout );
497 vlc_thread_join( p_vout );
499 var_Destroy( p_vout, "intf-change" );
501 if( p_vout->psz_filter_chain ) free( p_vout->psz_filter_chain );
503 config_ChainDestroy( p_vout->p_cfg );
506 vlc_object_destroy( p_vout );
508 /* This is a dirty hack for mostly Linux, where there is no way to get the GUI
509 back if you closed it while playing video. This is solved in Mac OS X,
510 where we have this novelty called menubar, that will always allow you access
511 to the applications main functionality. They should try that on linux sometime */
512 p_another_vout = vlc_object_find( p_playlist,
513 VLC_OBJECT_VOUT, FIND_ANYWHERE );
514 if( p_another_vout == NULL )
517 val.b_bool = VLC_TRUE;
518 var_Set( p_playlist, "intf-show", val );
522 vlc_object_release( p_another_vout );
525 vlc_object_release( p_playlist );
528 /*****************************************************************************
529 * InitThread: initialize video output thread
530 *****************************************************************************
531 * This function is called from RunThread and performs the second step of the
532 * initialization. It returns 0 on success. Note that the thread's flag are not
533 * modified inside this function.
534 *****************************************************************************/
535 static int InitThread( vout_thread_t *p_vout )
537 int i, i_aspect_x, i_aspect_y;
539 vlc_mutex_lock( &p_vout->change_lock );
545 /* Initialize output method, it allocates direct buffers for us */
546 if( p_vout->pf_init( p_vout ) )
548 vlc_mutex_unlock( &p_vout->change_lock );
552 if( !I_OUTPUTPICTURES )
554 msg_Err( p_vout, "plugin was unable to allocate at least "
555 "one direct buffer" );
556 p_vout->pf_end( p_vout );
557 vlc_mutex_unlock( &p_vout->change_lock );
561 if( I_OUTPUTPICTURES > VOUT_MAX_PICTURES )
563 msg_Err( p_vout, "plugin allocated too many direct buffers, "
564 "our internal buffers must have overflown." );
565 p_vout->pf_end( p_vout );
566 vlc_mutex_unlock( &p_vout->change_lock );
570 msg_Dbg( p_vout, "got %i direct buffer(s)", I_OUTPUTPICTURES );
572 AspectRatio( p_vout->fmt_render.i_aspect, &i_aspect_x, &i_aspect_y );
574 msg_Dbg( p_vout, "picture in %ix%i (%i,%i,%ix%i), "
575 "chroma %4.4s, ar %i:%i, sar %i:%i",
576 p_vout->fmt_render.i_width, p_vout->fmt_render.i_height,
577 p_vout->fmt_render.i_x_offset, p_vout->fmt_render.i_y_offset,
578 p_vout->fmt_render.i_visible_width,
579 p_vout->fmt_render.i_visible_height,
580 (char*)&p_vout->fmt_render.i_chroma,
581 i_aspect_x, i_aspect_y,
582 p_vout->fmt_render.i_sar_num, p_vout->fmt_render.i_sar_den );
584 AspectRatio( p_vout->fmt_in.i_aspect, &i_aspect_x, &i_aspect_y );
586 msg_Dbg( p_vout, "picture user %ix%i (%i,%i,%ix%i), "
587 "chroma %4.4s, ar %i:%i, sar %i:%i",
588 p_vout->fmt_in.i_width, p_vout->fmt_in.i_height,
589 p_vout->fmt_in.i_x_offset, p_vout->fmt_in.i_y_offset,
590 p_vout->fmt_in.i_visible_width,
591 p_vout->fmt_in.i_visible_height,
592 (char*)&p_vout->fmt_in.i_chroma,
593 i_aspect_x, i_aspect_y,
594 p_vout->fmt_in.i_sar_num, p_vout->fmt_in.i_sar_den );
596 if( !p_vout->fmt_out.i_width || !p_vout->fmt_out.i_height )
598 p_vout->fmt_out.i_width = p_vout->fmt_out.i_visible_width =
599 p_vout->output.i_width;
600 p_vout->fmt_out.i_height = p_vout->fmt_out.i_visible_height =
601 p_vout->output.i_height;
602 p_vout->fmt_out.i_x_offset = p_vout->fmt_out.i_y_offset = 0;
604 p_vout->fmt_out.i_aspect = p_vout->output.i_aspect;
605 p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
607 if( !p_vout->fmt_out.i_sar_num || !p_vout->fmt_out.i_sar_num )
609 p_vout->fmt_out.i_sar_num = p_vout->fmt_out.i_aspect *
610 p_vout->fmt_out.i_height;
611 p_vout->fmt_out.i_sar_den = VOUT_ASPECT_FACTOR *
612 p_vout->fmt_out.i_width;
615 vlc_ureduce( &p_vout->fmt_out.i_sar_num, &p_vout->fmt_out.i_sar_den,
616 p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den, 0 );
618 AspectRatio( p_vout->fmt_out.i_aspect, &i_aspect_x, &i_aspect_y );
620 msg_Dbg( p_vout, "picture out %ix%i (%i,%i,%ix%i), "
621 "chroma %4.4s, ar %i:%i, sar %i:%i",
622 p_vout->fmt_out.i_width, p_vout->fmt_out.i_height,
623 p_vout->fmt_out.i_x_offset, p_vout->fmt_out.i_y_offset,
624 p_vout->fmt_out.i_visible_width,
625 p_vout->fmt_out.i_visible_height,
626 (char*)&p_vout->fmt_out.i_chroma,
627 i_aspect_x, i_aspect_y,
628 p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den );
630 /* Calculate shifts from system-updated masks */
631 MaskToShift( &p_vout->output.i_lrshift, &p_vout->output.i_rrshift,
632 p_vout->output.i_rmask );
633 MaskToShift( &p_vout->output.i_lgshift, &p_vout->output.i_rgshift,
634 p_vout->output.i_gmask );
635 MaskToShift( &p_vout->output.i_lbshift, &p_vout->output.i_rbshift,
636 p_vout->output.i_bmask );
638 /* Check whether we managed to create direct buffers similar to
639 * the render buffers, ie same size and chroma */
640 if( ( p_vout->output.i_width == p_vout->render.i_width )
641 && ( p_vout->output.i_height == p_vout->render.i_height )
642 && ( vout_ChromaCmp( p_vout->output.i_chroma, p_vout->render.i_chroma ) ) )
644 /* Cool ! We have direct buffers, we can ask the decoder to
645 * directly decode into them ! Map the first render buffers to
646 * the first direct buffers, but keep the first direct buffer
647 * for memcpy operations */
648 p_vout->b_direct = 1;
650 for( i = 1; i < VOUT_MAX_PICTURES; i++ )
652 if( p_vout->p_picture[ i ].i_type != DIRECT_PICTURE &&
653 I_RENDERPICTURES >= VOUT_MIN_DIRECT_PICTURES - 1 &&
654 p_vout->p_picture[ i - 1 ].i_type == DIRECT_PICTURE )
656 /* We have enough direct buffers so there's no need to
657 * try to use system memory buffers. */
660 PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
664 msg_Dbg( p_vout, "direct render, mapping "
665 "render pictures 0-%i to system pictures 1-%i",
666 VOUT_MAX_PICTURES - 2, VOUT_MAX_PICTURES - 1 );
670 /* Rats... Something is wrong here, we could not find an output
671 * plugin able to directly render what we decode. See if we can
672 * find a chroma plugin to do the conversion */
673 p_vout->b_direct = 0;
675 /* Choose the best module */
676 p_vout->chroma.p_module = module_Need( p_vout, "chroma", NULL, 0 );
678 if( p_vout->chroma.p_module == NULL )
680 msg_Err( p_vout, "no chroma module for %4.4s to %4.4s",
681 (char*)&p_vout->render.i_chroma,
682 (char*)&p_vout->output.i_chroma );
683 p_vout->pf_end( p_vout );
684 vlc_mutex_unlock( &p_vout->change_lock );
688 msg_Dbg( p_vout, "indirect render, mapping "
689 "render pictures 0-%i to system pictures %i-%i",
690 VOUT_MAX_PICTURES - 1, I_OUTPUTPICTURES,
691 I_OUTPUTPICTURES + VOUT_MAX_PICTURES - 1 );
693 /* Append render buffers after the direct buffers */
694 for( i = I_OUTPUTPICTURES; i < 2 * VOUT_MAX_PICTURES; i++ )
696 PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
699 /* Check if we have enough render pictures */
700 if( I_RENDERPICTURES == VOUT_MAX_PICTURES )
705 /* Link pictures back to their heap */
706 for( i = 0 ; i < I_RENDERPICTURES ; i++ )
708 PP_RENDERPICTURE[ i ]->p_heap = &p_vout->render;
711 for( i = 0 ; i < I_OUTPUTPICTURES ; i++ )
713 PP_OUTPUTPICTURE[ i ]->p_heap = &p_vout->output;
716 /* XXX XXX mark thread ready */
720 /*****************************************************************************
721 * RunThread: video output thread
722 *****************************************************************************
723 * Video output thread. This function does only returns when the thread is
724 * terminated. It handles the pictures arriving in the video heap and the
725 * display device events.
726 *****************************************************************************/
727 static void RunThread( vout_thread_t *p_vout)
729 int i_index; /* index in heap */
730 int i_idle_loops = 0; /* loops without displaying a picture */
731 mtime_t current_date; /* current date */
732 mtime_t display_date; /* display date */
734 picture_t * p_picture; /* picture pointer */
735 picture_t * p_last_picture = NULL; /* last picture */
736 picture_t * p_directbuffer; /* direct buffer to display */
738 subpicture_t * p_subpic = NULL; /* subpicture pointer */
740 input_thread_t *p_input = NULL ; /* Parent input, if it exists */
743 vlc_bool_t b_drop_late;
745 int i_displayed = 0, i_lost = 0, i_loops = 0;
750 p_vout->b_error = InitThread( p_vout );
752 var_Create( p_vout, "drop-late-frames", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
753 var_Get( p_vout, "drop-late-frames", &val );
754 b_drop_late = val.b_bool;
756 /* signal the creation of the vout */
757 vlc_thread_ready( p_vout );
759 if( p_vout->b_error )
761 /* Destroy thread structures allocated by Create and InitThread */
762 DestroyThread( p_vout );
766 if( p_vout->b_title_show )
767 DisplayTitleOnOSD( p_vout );
770 * Main loop - it is not executed if an error occurred during
773 while( (!p_vout->b_die) && (!p_vout->b_error) )
775 /* Initialize loop variables */
778 current_date = mdate();
780 if( p_input && p_input->b_die )
782 vlc_object_release( p_input );
787 if( i_loops % 20 == 0 )
791 p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT,
796 vlc_mutex_lock( &p_input->p->counters.counters_lock );
797 stats_UpdateInteger( p_vout, p_input->p->counters.p_lost_pictures,
799 stats_UpdateInteger( p_vout,
800 p_input->p->counters.p_displayed_pictures,
802 i_displayed = i_lost = 0;
803 vlc_mutex_unlock( &p_input->p->counters.counters_lock );
808 if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
810 msg_Dbg( p_vout, "picture heap: %d/%d",
811 I_RENDERPICTURES, p_vout->i_heap_size );
816 * Find the picture to display (the one with the earliest date).
817 * This operation does not need lock, since only READY_PICTUREs
819 for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
821 if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
822 && ( (p_picture == NULL) ||
823 (PP_RENDERPICTURE[i_index]->date < display_date) ) )
825 p_picture = PP_RENDERPICTURE[i_index];
826 display_date = p_picture->date;
832 /* If we met the last picture, parse again to see whether there is
833 * a more appropriate one. */
834 if( p_picture == p_last_picture )
836 for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
838 if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
839 && (PP_RENDERPICTURE[i_index] != p_last_picture)
840 && ((p_picture == p_last_picture) ||
841 (PP_RENDERPICTURE[i_index]->date < display_date)) )
843 p_picture = PP_RENDERPICTURE[i_index];
844 display_date = p_picture->date;
849 /* If we found better than the last picture, destroy it */
850 if( p_last_picture && p_picture != p_last_picture )
852 vlc_mutex_lock( &p_vout->picture_lock );
853 if( p_last_picture->i_refcount )
855 p_last_picture->i_status = DISPLAYED_PICTURE;
859 p_last_picture->i_status = DESTROYED_PICTURE;
860 p_vout->i_heap_size--;
862 vlc_mutex_unlock( &p_vout->picture_lock );
863 p_last_picture = NULL;
866 /* Compute FPS rate */
867 p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ]
870 if( !p_picture->b_force &&
871 p_picture != p_last_picture &&
872 display_date < current_date + p_vout->render_time &&
875 /* Picture is late: it will be destroyed and the thread
876 * will directly choose the next picture */
877 vlc_mutex_lock( &p_vout->picture_lock );
878 if( p_picture->i_refcount )
880 /* Pretend we displayed the picture, but don't destroy
881 * it since the decoder might still need it. */
882 p_picture->i_status = DISPLAYED_PICTURE;
886 /* Destroy the picture without displaying it */
887 p_picture->i_status = DESTROYED_PICTURE;
888 p_vout->i_heap_size--;
890 msg_Warn( p_vout, "late picture skipped ("I64Fd")",
891 current_date - display_date );
893 vlc_mutex_unlock( &p_vout->picture_lock );
899 current_date + p_vout->i_pts_delay + VOUT_BOGUS_DELAY )
901 /* Picture is waaay too early: it will be destroyed */
902 vlc_mutex_lock( &p_vout->picture_lock );
903 if( p_picture->i_refcount )
905 /* Pretend we displayed the picture, but don't destroy
906 * it since the decoder might still need it. */
907 p_picture->i_status = DISPLAYED_PICTURE;
911 /* Destroy the picture without displaying it */
912 p_picture->i_status = DESTROYED_PICTURE;
913 p_vout->i_heap_size--;
916 msg_Warn( p_vout, "vout warning: early picture skipped "
917 "("I64Fd")", display_date - current_date
918 - p_vout->i_pts_delay );
919 vlc_mutex_unlock( &p_vout->picture_lock );
924 if( display_date > current_date + VOUT_DISPLAY_DELAY )
926 /* A picture is ready to be rendered, but its rendering date
927 * is far from the current one so the thread will perform an
928 * empty loop as if no picture were found. The picture state
933 else if( p_picture == p_last_picture )
935 /* We are asked to repeat the previous picture, but we first
936 * wait for a couple of idle loops */
937 if( i_idle_loops < 4 )
944 /* We set the display date to something high, otherwise
945 * we'll have lots of problems with late pictures */
946 display_date = current_date + p_vout->render_time;
951 if( p_picture == NULL )
956 /* Video Filter2 stuff */
957 if( p_vout->b_vfilter_change == VLC_TRUE )
960 vlc_mutex_lock( &p_vout->vfilter_lock );
961 RemoveVideoFilters2( p_vout );
962 for( i = 0; i < p_vout->i_vfilters_cfg; i++ )
964 filter_t *p_vfilter =
965 p_vout->pp_vfilters[p_vout->i_vfilters] =
966 vlc_object_create( p_vout, VLC_OBJECT_FILTER );
968 vlc_object_attach( p_vfilter, p_vout );
970 p_vfilter->pf_vout_buffer_new = video_new_buffer_filter;
971 p_vfilter->pf_vout_buffer_del = video_del_buffer_filter;
973 if( !p_vout->i_vfilters )
975 p_vfilter->fmt_in.video = p_vout->fmt_render;
979 p_vfilter->fmt_in.video = (p_vfilter-1)->fmt_out.video;
981 /* TODO: one day filters in the middle of the chain might
982 * have a different fmt_out.video than fmt_render ... */
983 p_vfilter->fmt_out.video = p_vout->fmt_render;
985 p_vfilter->p_cfg = p_vout->p_vfilters_cfg[i];
986 p_vfilter->p_module = module_Need( p_vfilter, "video filter2",
987 p_vout->psz_vfilters[i],
990 if( p_vfilter->p_module )
993 malloc( sizeof( filter_owner_sys_t ) );
994 p_vfilter->p_owner->p_vout = p_vout;
995 p_vout->i_vfilters++;
996 msg_Dbg( p_vout, "video filter found (%s)",
997 p_vout->psz_vfilters[i] );
1001 msg_Err( p_vout, "no video filter found (%s)",
1002 p_vout->psz_vfilters[i] );
1003 vlc_object_detach( p_vfilter );
1004 vlc_object_destroy( p_vfilter );
1007 p_vout->b_vfilter_change = VLC_FALSE;
1008 vlc_mutex_unlock( &p_vout->vfilter_lock );
1014 for( i = 0; i < p_vout->i_vfilters; i++ )
1016 picture_t *p_old = p_picture;
1017 p_picture = p_vout->pp_vfilters[i]->pf_video_filter(
1018 p_vout->pp_vfilters[i], p_picture );
1023 /* FIXME: this is kind of wrong
1024 * if you have 2 or more vfilters and the 2nd breaks,
1025 * on the next loop the 1st one will be applied again */
1027 /* if p_old and p_picture are the same (ie the filter
1028 * worked on the old picture), then following code is
1029 * still alright since i_status gets changed back to
1030 * the right value */
1031 if( p_old->i_refcount )
1033 p_old->i_status = DISPLAYED_PICTURE;
1037 p_old->i_status = DESTROYED_PICTURE;
1039 p_picture->i_status = READY_PICTURE;
1043 if( p_picture && p_vout->b_snapshot )
1045 p_vout->b_snapshot = VLC_FALSE;
1046 vout_Snapshot( p_vout, p_picture );
1050 * Check for subpictures to display
1052 if( display_date > 0 )
1056 p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT,
1059 p_subpic = spu_SortSubpictures( p_vout->p_spu, display_date,
1060 p_input ? var_GetBool( p_input, "state" ) == PAUSE_S : VLC_FALSE );
1067 p_directbuffer = vout_RenderPicture( p_vout, p_picture, p_subpic );
1070 * Call the plugin-specific rendering method if there is one
1072 if( p_picture != NULL && p_directbuffer != NULL && p_vout->pf_render )
1074 /* Render the direct buffer returned by vout_RenderPicture */
1075 p_vout->pf_render( p_vout, p_directbuffer );
1081 if( display_date != 0 && p_directbuffer != NULL )
1083 mtime_t current_render_time = mdate() - current_date;
1084 /* if render time is very large we don't include it in the mean */
1085 if( current_render_time < p_vout->render_time +
1086 VOUT_DISPLAY_DELAY )
1088 /* Store render time using a sliding mean weighting to
1089 * current value in a 3 to 1 ratio*/
1090 p_vout->render_time *= 3;
1091 p_vout->render_time += current_render_time;
1092 p_vout->render_time >>= 2;
1096 /* Give back change lock */
1097 vlc_mutex_unlock( &p_vout->change_lock );
1099 /* Sleep a while or until a given date */
1100 if( display_date != 0 )
1102 /* If there are filters in the chain, better give them the picture
1104 if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain )
1106 mwait( display_date - VOUT_MWAIT_TOLERANCE );
1111 msleep( VOUT_IDLE_SLEEP );
1114 /* On awakening, take back lock and send immediately picture
1116 vlc_mutex_lock( &p_vout->change_lock );
1119 * Display the previously rendered picture
1121 if( p_picture != NULL && p_directbuffer != NULL )
1123 /* Display the direct buffer returned by vout_RenderPicture */
1124 if( p_vout->pf_display )
1126 p_vout->pf_display( p_vout, p_directbuffer );
1129 /* Tell the vout this was the last picture and that it does not
1130 * need to be forced anymore. */
1131 p_last_picture = p_picture;
1132 p_last_picture->b_force = 0;
1135 if( p_picture != NULL )
1137 /* Reinitialize idle loop count */
1142 * Check events and manage thread
1144 if( p_vout->pf_manage && p_vout->pf_manage( p_vout ) )
1146 /* A fatal error occurred, and the thread must terminate
1147 * immediately, without displaying anything - setting b_error to 1
1148 * causes the immediate end of the main while() loop. */
1149 p_vout->b_error = 1;
1152 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
1154 /* this must only happen when the vout plugin is incapable of
1155 * rescaling the picture itself. In this case we need to destroy
1156 * the current picture buffers and recreate new ones with the right
1160 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
1162 p_vout->pf_end( p_vout );
1163 for( i = 0; i < I_OUTPUTPICTURES; i++ )
1164 p_vout->p_picture[ i ].i_status = FREE_PICTURE;
1166 I_OUTPUTPICTURES = 0;
1167 if( p_vout->pf_init( p_vout ) )
1169 msg_Err( p_vout, "cannot resize display" );
1170 /* FIXME: pf_end will be called again in EndThread() */
1171 p_vout->b_error = 1;
1174 /* Need to reinitialise the chroma plugin */
1175 if( p_vout->chroma.p_module )
1177 if( p_vout->chroma.p_module->pf_deactivate )
1178 p_vout->chroma.p_module->pf_deactivate( VLC_OBJECT(p_vout) );
1179 p_vout->chroma.p_module->pf_activate( VLC_OBJECT(p_vout) );
1183 if( p_vout->i_changes & VOUT_PICTURE_BUFFERS_CHANGE )
1185 /* This happens when the picture buffers need to be recreated.
1186 * This is useful on multimonitor displays for instance.
1188 * Warning: This only works when the vout creates only 1 picture
1190 p_vout->i_changes &= ~VOUT_PICTURE_BUFFERS_CHANGE;
1192 if( !p_vout->b_direct )
1194 module_Unneed( p_vout, p_vout->chroma.p_module );
1197 vlc_mutex_lock( &p_vout->picture_lock );
1199 p_vout->pf_end( p_vout );
1201 I_OUTPUTPICTURES = I_RENDERPICTURES = 0;
1203 p_vout->b_error = InitThread( p_vout );
1205 vlc_mutex_unlock( &p_vout->picture_lock );
1211 vlc_object_release( p_input );
1215 * Error loop - wait until the thread destruction is requested
1217 if( p_vout->b_error )
1219 ErrorThread( p_vout );
1223 EndThread( p_vout );
1225 /* Destroy thread structures allocated by CreateThread */
1226 DestroyThread( p_vout );
1229 /*****************************************************************************
1230 * ErrorThread: RunThread() error loop
1231 *****************************************************************************
1232 * This function is called when an error occurred during thread main's loop.
1233 * The thread can still receive feed, but must be ready to terminate as soon
1235 *****************************************************************************/
1236 static void ErrorThread( vout_thread_t *p_vout )
1238 /* Wait until a `die' order */
1239 while( !p_vout->b_die )
1242 msleep( VOUT_IDLE_SLEEP );
1246 /*****************************************************************************
1247 * EndThread: thread destruction
1248 *****************************************************************************
1249 * This function is called when the thread ends after a sucessful
1250 * initialization. It frees all resources allocated by InitThread.
1251 *****************************************************************************/
1252 static void EndThread( vout_thread_t *p_vout )
1254 int i_index; /* index in heap */
1258 struct tms cpu_usage;
1259 times( &cpu_usage );
1261 msg_Dbg( p_vout, "cpu usage (user: %d, system: %d)",
1262 cpu_usage.tms_utime, cpu_usage.tms_stime );
1266 if( !p_vout->b_direct )
1268 module_Unneed( p_vout, p_vout->chroma.p_module );
1271 /* Destroy all remaining pictures */
1272 for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++ )
1274 if ( p_vout->p_picture[i_index].i_type == MEMORY_PICTURE )
1276 free( p_vout->p_picture[i_index].p_data_orig );
1280 /* Destroy subpicture unit */
1281 spu_Attach( p_vout->p_spu, VLC_OBJECT(p_vout), VLC_FALSE );
1282 spu_Destroy( p_vout->p_spu );
1284 /* Destroy the video filters2 */
1285 RemoveVideoFilters2( p_vout );
1287 /* Destroy translation tables */
1288 p_vout->pf_end( p_vout );
1290 /* Release the change lock */
1291 vlc_mutex_unlock( &p_vout->change_lock );
1294 /*****************************************************************************
1295 * DestroyThread: thread destruction
1296 *****************************************************************************
1297 * This function is called when the thread ends. It frees all ressources
1298 * allocated by CreateThread. Status is available at this stage.
1299 *****************************************************************************/
1300 static void DestroyThread( vout_thread_t *p_vout )
1302 /* Destroy the locks */
1303 vlc_mutex_destroy( &p_vout->picture_lock );
1304 vlc_mutex_destroy( &p_vout->change_lock );
1305 vlc_mutex_destroy( &p_vout->vfilter_lock );
1307 /* Release the module */
1308 if( p_vout && p_vout->p_module )
1310 module_Unneed( p_vout, p_vout->p_module );
1314 /* following functions are local */
1316 static int ReduceHeight( int i_ratio )
1318 int i_dummy = VOUT_ASPECT_FACTOR;
1326 /* VOUT_ASPECT_FACTOR is (2^7 * 3^3 * 5^3), we just check for 2, 3 and 5 */
1327 while( !(i_ratio & 1) && !(i_dummy & 1) )
1334 while( !(i_ratio % 3) && !(i_dummy % 3) )
1341 while( !(i_ratio % 5) && !(i_dummy % 5) )
1351 static void AspectRatio( int i_aspect, int *i_aspect_x, int *i_aspect_y )
1353 unsigned int i_pgcd = ReduceHeight( i_aspect );
1354 *i_aspect_x = i_aspect / i_pgcd;
1355 *i_aspect_y = VOUT_ASPECT_FACTOR / i_pgcd;
1358 /*****************************************************************************
1359 * BinaryLog: computes the base 2 log of a binary value
1360 *****************************************************************************
1361 * This functions is used by MaskToShift, to get a bit index from a binary
1363 *****************************************************************************/
1364 static int BinaryLog( uint32_t i )
1368 if( i == 0 ) return -31337;
1370 if( i & 0xffff0000 ) i_log += 16;
1371 if( i & 0xff00ff00 ) i_log += 8;
1372 if( i & 0xf0f0f0f0 ) i_log += 4;
1373 if( i & 0xcccccccc ) i_log += 2;
1374 if( i & 0xaaaaaaaa ) i_log += 1;
1379 /*****************************************************************************
1380 * MaskToShift: transform a color mask into right and left shifts
1381 *****************************************************************************
1382 * This function is used for obtaining color shifts from masks.
1383 *****************************************************************************/
1384 static void MaskToShift( int *pi_left, int *pi_right, uint32_t i_mask )
1386 uint32_t i_low, i_high; /* lower hand higher bits of the mask */
1390 *pi_left = *pi_right = 0;
1395 i_low = i_high = i_mask;
1397 i_low &= - (int32_t)i_low; /* lower bit of the mask */
1398 i_high += i_low; /* higher bit of the mask */
1400 /* Transform bits into an index. Also deal with i_high overflow, which
1401 * is faster than changing the BinaryLog code to handle 64 bit integers. */
1402 i_low = BinaryLog (i_low);
1403 i_high = i_high ? BinaryLog (i_high) : 32;
1405 /* Update pointers and return */
1407 *pi_right = (8 - i_high + i_low);
1410 /*****************************************************************************
1411 * vout_VarCallback: generic callback for intf variables
1412 *****************************************************************************/
1413 int vout_VarCallback( vlc_object_t * p_this, const char * psz_variable,
1414 vlc_value_t oldval, vlc_value_t newval,
1417 vout_thread_t * p_vout = (vout_thread_t *)p_this;
1419 (void)psz_variable; (void)newval; (void)oldval; (void)p_data;
1420 val.b_bool = VLC_TRUE;
1421 var_Set( p_vout, "intf-change", val );
1425 /*****************************************************************************
1426 * Helper thread for object variables callbacks.
1427 * Only used to avoid deadlocks when using the video embedded mode.
1428 *****************************************************************************/
1429 typedef struct suxor_thread_t
1432 input_thread_t *p_input;
1436 static void SuxorRestartVideoES( suxor_thread_t *p_this )
1440 vlc_thread_ready( p_this );
1442 /* Now restart current video stream */
1443 var_Get( p_this->p_input, "video-es", &val );
1444 if( val.i_int >= 0 )
1447 val_es.i_int = -VIDEO_ES;
1448 var_Set( p_this->p_input, "video-es", val_es );
1449 var_Set( p_this->p_input, "video-es", val );
1452 vlc_object_release( p_this->p_input );
1454 vlc_object_destroy( p_this );
1457 /*****************************************************************************
1458 * object variables callbacks: a bunch of object variables are used by the
1459 * interfaces to interact with the vout.
1460 *****************************************************************************/
1461 static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd,
1462 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1464 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1465 input_thread_t *p_input;
1468 char *psz_mode = newval.psz_string;
1469 char *psz_filter, *psz_deinterlace = NULL;
1470 (void)psz_cmd; (void)oldval; (void)p_data;
1472 var_Get( p_vout, "vout-filter", &val );
1473 psz_filter = val.psz_string;
1474 if( psz_filter ) psz_deinterlace = strstr( psz_filter, "deinterlace" );
1476 if( !psz_mode || !*psz_mode )
1478 if( psz_deinterlace )
1480 char *psz_src = psz_deinterlace + sizeof("deinterlace") - 1;
1481 if( psz_src[0] == ':' ) psz_src++;
1482 memmove( psz_deinterlace, psz_src, strlen(psz_src) + 1 );
1485 else if( !psz_deinterlace )
1487 psz_filter = realloc( psz_filter, strlen( psz_filter ) +
1488 sizeof(":deinterlace") );
1489 if( psz_filter && *psz_filter ) strcat( psz_filter, ":" );
1490 strcat( psz_filter, "deinterlace" );
1493 p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1495 if( !p_input ) return VLC_EGENERIC;
1497 if( psz_mode && *psz_mode )
1499 /* Modify input as well because the vout might have to be restarted */
1500 val.psz_string = psz_mode;
1501 var_Create( p_input, "deinterlace-mode", VLC_VAR_STRING );
1502 var_Set( p_input, "deinterlace-mode", val );
1504 vlc_object_release( p_input );
1506 val.b_bool = VLC_TRUE;
1507 var_Set( p_vout, "intf-change", val );
1509 val.psz_string = psz_filter;
1510 var_Set( p_vout, "vout-filter", val );
1511 if( psz_filter ) free( psz_filter );
1516 static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd,
1517 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1519 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1520 input_thread_t *p_input;
1522 (void)psz_cmd; (void)oldval; (void)p_data;
1524 p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1528 msg_Err( p_vout, "Input not found" );
1529 return( VLC_EGENERIC );
1532 val.b_bool = VLC_TRUE;
1533 var_Set( p_vout, "intf-change", val );
1535 /* Modify input as well because the vout might have to be restarted */
1536 val.psz_string = newval.psz_string;
1537 var_Create( p_input, "vout-filter", VLC_VAR_STRING );
1539 var_Set( p_input, "vout-filter", val );
1541 /* Now restart current video stream */
1542 var_Get( p_input, "video-es", &val );
1543 if( val.i_int >= 0 )
1545 suxor_thread_t *p_suxor =
1546 vlc_object_create( p_vout, sizeof(suxor_thread_t) );
1547 p_suxor->p_input = p_input;
1548 p_vout->b_filter_change = VLC_TRUE;
1549 vlc_object_yield( p_input );
1550 vlc_thread_create( p_suxor, "suxor", SuxorRestartVideoES,
1551 VLC_THREAD_PRIORITY_LOW, VLC_FALSE );
1554 vlc_object_release( p_input );
1559 /*****************************************************************************
1560 * Video Filter2 stuff
1561 *****************************************************************************/
1562 static int ParseVideoFilter2Chain( vout_thread_t *p_vout, char *psz_vfilters )
1565 for( i = 0; i < p_vout->i_vfilters_cfg; i++ )
1567 struct config_chain_t *p_cfg =
1568 p_vout->p_vfilters_cfg[p_vout->i_vfilters_cfg];
1569 config_ChainDestroy( p_cfg );
1570 if( p_vout->psz_vfilters[p_vout->i_vfilters_cfg] )
1572 free( p_vout->psz_vfilters[p_vout->i_vfilters_cfg] );
1573 p_vout->psz_vfilters[p_vout->i_vfilters_cfg] = NULL;
1576 p_vout->i_vfilters_cfg = 0;
1577 if( psz_vfilters && *psz_vfilters )
1579 char *psz_parser = psz_vfilters;
1581 while( psz_parser && *psz_parser )
1583 psz_parser = config_ChainCreate(
1584 &p_vout->psz_vfilters[p_vout->i_vfilters_cfg],
1585 &p_vout->p_vfilters_cfg[p_vout->i_vfilters_cfg],
1587 msg_Dbg( p_vout, "adding vfilter: %s",
1588 p_vout->psz_vfilters[p_vout->i_vfilters_cfg] );
1589 p_vout->i_vfilters_cfg++;
1590 if( psz_parser && *psz_parser )
1592 if( p_vout->i_vfilters_cfg == MAX_VFILTERS )
1595 "maximum number of video filters reached. \"%s\" discarded",
1605 static int VideoFilter2Callback( vlc_object_t *p_this, char const *psz_cmd,
1606 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1608 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1609 (void)psz_cmd; (void)oldval; (void)p_data;
1611 vlc_mutex_lock( &p_vout->vfilter_lock );
1612 ParseVideoFilter2Chain( p_vout, newval.psz_string );
1613 p_vout->b_vfilter_change = VLC_TRUE;
1614 vlc_mutex_unlock( &p_vout->vfilter_lock );
1619 static void RemoveVideoFilters2( vout_thread_t *p_vout )
1622 for( i = 0; i < p_vout->i_vfilters; i++ )
1624 vlc_object_detach( p_vout->pp_vfilters[i] );
1625 if( p_vout->pp_vfilters[i]->p_module )
1627 module_Unneed( p_vout->pp_vfilters[i],
1628 p_vout->pp_vfilters[i]->p_module );
1631 free( p_vout->pp_vfilters[i]->p_owner );
1632 vlc_object_destroy( p_vout->pp_vfilters[i] );
1634 p_vout->i_vfilters = 0;
1637 static void DisplayTitleOnOSD( vout_thread_t *p_vout )
1639 input_thread_t *p_input;
1640 mtime_t i_now, i_stop;
1642 p_input = (input_thread_t *)vlc_object_find( p_vout,
1643 VLC_OBJECT_INPUT, FIND_ANYWHERE );
1647 i_stop = i_now + (mtime_t)(p_vout->i_title_timeout * 1000);
1648 char *psz_nowplaying =
1649 input_item_GetNowPlaying( input_GetItem( p_input ) );
1650 char *psz_artist = input_item_GetArtist( input_GetItem( p_input ) );
1651 char *psz_name = input_item_GetTitle( input_GetItem( p_input ) );
1652 if( EMPTY_STR( psz_name ) )
1655 psz_name = input_item_GetName( input_GetItem( p_input ) );
1657 if( !EMPTY_STR( psz_nowplaying ) )
1659 vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1660 psz_nowplaying, NULL,
1661 p_vout->i_title_position,
1662 30 + p_vout->fmt_in.i_width
1663 - p_vout->fmt_in.i_visible_width
1664 - p_vout->fmt_in.i_x_offset,
1665 20 + p_vout->fmt_in.i_y_offset,
1668 else if( !EMPTY_STR( psz_artist ) )
1670 char *psz_string = NULL;
1671 if( asprintf( &psz_string, "%s - %s", psz_name, psz_artist ) != -1 )
1673 vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1675 p_vout->i_title_position,
1676 30 + p_vout->fmt_in.i_width
1677 - p_vout->fmt_in.i_visible_width
1678 - p_vout->fmt_in.i_x_offset,
1679 20 + p_vout->fmt_in.i_y_offset,
1686 vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1688 p_vout->i_title_position,
1689 30 + p_vout->fmt_in.i_width
1690 - p_vout->fmt_in.i_visible_width
1691 - p_vout->fmt_in.i_x_offset,
1692 20 + p_vout->fmt_in.i_y_offset,
1695 vlc_object_release( p_input );
1698 free( psz_nowplaying );