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 );
1451 vlc_object_destroy( p_this );
1454 /*****************************************************************************
1455 * object variables callbacks: a bunch of object variables are used by the
1456 * interfaces to interact with the vout.
1457 *****************************************************************************/
1458 static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd,
1459 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1461 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1462 input_thread_t *p_input;
1465 char *psz_mode = newval.psz_string;
1466 char *psz_filter, *psz_deinterlace = NULL;
1467 (void)psz_cmd; (void)oldval; (void)p_data;
1469 var_Get( p_vout, "vout-filter", &val );
1470 psz_filter = val.psz_string;
1471 if( psz_filter ) psz_deinterlace = strstr( psz_filter, "deinterlace" );
1473 if( !psz_mode || !*psz_mode )
1475 if( psz_deinterlace )
1477 char *psz_src = psz_deinterlace + sizeof("deinterlace") - 1;
1478 if( psz_src[0] == ':' ) psz_src++;
1479 memmove( psz_deinterlace, psz_src, strlen(psz_src) + 1 );
1482 else if( !psz_deinterlace )
1484 psz_filter = realloc( psz_filter, strlen( psz_filter ) +
1485 sizeof(":deinterlace") );
1486 if( psz_filter && *psz_filter ) strcat( psz_filter, ":" );
1487 strcat( psz_filter, "deinterlace" );
1490 p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1492 if( !p_input ) return VLC_EGENERIC;
1494 if( psz_mode && *psz_mode )
1496 /* Modify input as well because the vout might have to be restarted */
1497 val.psz_string = psz_mode;
1498 var_Create( p_input, "deinterlace-mode", VLC_VAR_STRING );
1499 var_Set( p_input, "deinterlace-mode", val );
1501 vlc_object_release( p_input );
1503 val.b_bool = VLC_TRUE;
1504 var_Set( p_vout, "intf-change", val );
1506 val.psz_string = psz_filter;
1507 var_Set( p_vout, "vout-filter", val );
1508 if( psz_filter ) free( psz_filter );
1513 static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd,
1514 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1516 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1517 input_thread_t *p_input;
1519 (void)psz_cmd; (void)oldval; (void)p_data;
1521 p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1525 msg_Err( p_vout, "Input not found" );
1526 return( VLC_EGENERIC );
1529 val.b_bool = VLC_TRUE;
1530 var_Set( p_vout, "intf-change", val );
1532 /* Modify input as well because the vout might have to be restarted */
1533 val.psz_string = newval.psz_string;
1534 var_Create( p_input, "vout-filter", VLC_VAR_STRING );
1536 var_Set( p_input, "vout-filter", val );
1538 /* Now restart current video stream */
1539 var_Get( p_input, "video-es", &val );
1540 if( val.i_int >= 0 )
1542 suxor_thread_t *p_suxor =
1543 vlc_object_create( p_vout, sizeof(suxor_thread_t) );
1544 p_suxor->p_input = p_input;
1545 p_vout->b_filter_change = VLC_TRUE;
1546 vlc_object_yield( p_input );
1547 vlc_thread_create( p_suxor, "suxor", SuxorRestartVideoES,
1548 VLC_THREAD_PRIORITY_LOW, VLC_FALSE );
1551 vlc_object_release( p_input );
1556 /*****************************************************************************
1557 * Video Filter2 stuff
1558 *****************************************************************************/
1559 static int ParseVideoFilter2Chain( vout_thread_t *p_vout, char *psz_vfilters )
1562 for( i = 0; i < p_vout->i_vfilters_cfg; i++ )
1564 struct config_chain_t *p_cfg =
1565 p_vout->p_vfilters_cfg[p_vout->i_vfilters_cfg];
1566 config_ChainDestroy( p_cfg );
1567 if( p_vout->psz_vfilters[p_vout->i_vfilters_cfg] )
1569 free( p_vout->psz_vfilters[p_vout->i_vfilters_cfg] );
1570 p_vout->psz_vfilters[p_vout->i_vfilters_cfg] = NULL;
1573 p_vout->i_vfilters_cfg = 0;
1574 if( psz_vfilters && *psz_vfilters )
1576 char *psz_parser = psz_vfilters;
1578 while( psz_parser && *psz_parser )
1580 psz_parser = config_ChainCreate(
1581 &p_vout->psz_vfilters[p_vout->i_vfilters_cfg],
1582 &p_vout->p_vfilters_cfg[p_vout->i_vfilters_cfg],
1584 msg_Dbg( p_vout, "adding vfilter: %s",
1585 p_vout->psz_vfilters[p_vout->i_vfilters_cfg] );
1586 p_vout->i_vfilters_cfg++;
1587 if( psz_parser && *psz_parser )
1589 if( p_vout->i_vfilters_cfg == MAX_VFILTERS )
1592 "maximum number of video filters reached. \"%s\" discarded",
1602 static int VideoFilter2Callback( vlc_object_t *p_this, char const *psz_cmd,
1603 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1605 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1606 (void)psz_cmd; (void)oldval; (void)p_data;
1608 vlc_mutex_lock( &p_vout->vfilter_lock );
1609 ParseVideoFilter2Chain( p_vout, newval.psz_string );
1610 p_vout->b_vfilter_change = VLC_TRUE;
1611 vlc_mutex_unlock( &p_vout->vfilter_lock );
1616 static void RemoveVideoFilters2( vout_thread_t *p_vout )
1619 for( i = 0; i < p_vout->i_vfilters; i++ )
1621 vlc_object_detach( p_vout->pp_vfilters[i] );
1622 if( p_vout->pp_vfilters[i]->p_module )
1624 module_Unneed( p_vout->pp_vfilters[i],
1625 p_vout->pp_vfilters[i]->p_module );
1628 free( p_vout->pp_vfilters[i]->p_owner );
1629 vlc_object_destroy( p_vout->pp_vfilters[i] );
1631 p_vout->i_vfilters = 0;
1634 static void DisplayTitleOnOSD( vout_thread_t *p_vout )
1636 input_thread_t *p_input;
1637 mtime_t i_now, i_stop;
1639 p_input = (input_thread_t *)vlc_object_find( p_vout,
1640 VLC_OBJECT_INPUT, FIND_ANYWHERE );
1644 i_stop = i_now + (mtime_t)(p_vout->i_title_timeout * 1000);
1645 char *psz_nowplaying =
1646 input_item_GetNowPlaying( input_GetItem( p_input ) );
1647 char *psz_artist = input_item_GetArtist( input_GetItem( p_input ) );
1648 char *psz_name = input_item_GetTitle( input_GetItem( p_input ) );
1649 if( EMPTY_STR( psz_name ) )
1652 psz_name = input_item_GetName( input_GetItem( p_input ) );
1654 if( !EMPTY_STR( psz_nowplaying ) )
1656 vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1657 psz_nowplaying, NULL,
1658 p_vout->i_title_position,
1659 30 + p_vout->fmt_in.i_width
1660 - p_vout->fmt_in.i_visible_width
1661 - p_vout->fmt_in.i_x_offset,
1662 20 + p_vout->fmt_in.i_y_offset,
1665 else if( !EMPTY_STR( psz_artist ) )
1667 char *psz_string = NULL;
1669 psz_string = malloc( strlen( psz_name ) + strlen( psz_artist ) );
1672 sprintf( psz_string, "%s - %s", psz_name, psz_artist );
1674 vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1676 p_vout->i_title_position,
1677 30 + p_vout->fmt_in.i_width
1678 - p_vout->fmt_in.i_visible_width
1679 - p_vout->fmt_in.i_x_offset,
1680 20 + p_vout->fmt_in.i_y_offset,
1687 vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
1689 p_vout->i_title_position,
1690 30 + p_vout->fmt_in.i_width
1691 - p_vout->fmt_in.i_visible_width
1692 - p_vout->fmt_in.i_x_offset,
1693 20 + p_vout->fmt_in.i_y_offset,
1696 vlc_object_release( p_input );
1699 free( psz_nowplaying );