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-2004 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 /*****************************************************************************
58 *****************************************************************************/
59 static int InitThread ( vout_thread_t * );
60 static void RunThread ( vout_thread_t * );
61 static void ErrorThread ( vout_thread_t * );
62 static void EndThread ( vout_thread_t * );
63 static void DestroyThread ( vout_thread_t * );
65 static void AspectRatio ( int, int *, int * );
66 static int BinaryLog ( uint32_t );
67 static void MaskToShift ( int *, int *, uint32_t );
69 /* Object variables callbacks */
70 static int DeinterlaceCallback( vlc_object_t *, char const *,
71 vlc_value_t, vlc_value_t, void * );
72 static int FilterCallback( vlc_object_t *, char const *,
73 vlc_value_t, vlc_value_t, void * );
74 static int VideoFilter2Callback( vlc_object_t *, char const *,
75 vlc_value_t, vlc_value_t, void * );
77 /* From vout_intf.c */
78 int vout_Snapshot( vout_thread_t *, picture_t * );
80 /* Video filter2 parsing */
81 static int ParseVideoFilter2Chain( vout_thread_t *, char * );
82 static void RemoveVideoFilters2( vout_thread_t *p_vout );
84 /*****************************************************************************
85 * Video Filter2 functions
86 *****************************************************************************/
87 struct filter_owner_sys_t
89 vout_thread_t *p_vout;
92 static picture_t *video_new_buffer_filter( filter_t *p_filter )
95 vout_thread_t *p_vout = p_filter->p_owner->p_vout;
97 p_picture = vout_CreatePicture( p_vout, 0, 0, 0 );
102 static void video_del_buffer_filter( filter_t *p_filter, picture_t *p_pic )
104 vout_DestroyPicture( p_filter->p_owner->p_vout, p_pic );
107 /*****************************************************************************
108 * vout_Request: find a video output thread, create one, or destroy one.
109 *****************************************************************************
110 * This function looks for a video output thread matching the current
111 * properties. If not found, it spawns a new one.
112 *****************************************************************************/
113 vout_thread_t *__vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout,
114 video_format_t *p_fmt )
118 /* Reattach video output to playlist before bailing out */
121 playlist_t *p_playlist = pl_Yield( p_this );
122 spu_Attach( p_vout->p_spu, p_this, VLC_FALSE );
123 vlc_object_detach( p_vout );
124 vlc_object_attach( p_vout, p_playlist );
125 pl_Release( p_this );
130 /* If a video output was provided, lock it, otherwise look for one. */
133 vlc_object_yield( p_vout );
137 p_vout = vlc_object_find( p_this, VLC_OBJECT_VOUT, FIND_CHILD );
141 playlist_t *p_playlist = pl_Yield( p_this );
142 vlc_mutex_lock( &p_playlist->gc_lock );
143 p_vout = vlc_object_find( p_playlist,
144 VLC_OBJECT_VOUT, FIND_CHILD );
145 /* only first children of p_input for unused vout */
146 if( p_vout && p_vout->p_parent != (vlc_object_t *)p_playlist )
148 vlc_object_release( p_vout );
152 vlc_object_detach( p_vout ); /* Remove it from the GC */
153 vlc_mutex_unlock( &p_playlist->gc_lock );
154 pl_Release( p_this );
158 /* If we now have a video output, check it has the right properties */
161 char *psz_filter_chain;
164 /* We don't directly check for the "vout-filter" variable for obvious
165 * performance reasons. */
166 if( p_vout->b_filter_change )
168 var_Get( p_vout, "vout-filter", &val );
169 psz_filter_chain = val.psz_string;
171 if( psz_filter_chain && !*psz_filter_chain )
173 free( psz_filter_chain );
174 psz_filter_chain = NULL;
176 if( p_vout->psz_filter_chain && !*p_vout->psz_filter_chain )
178 free( p_vout->psz_filter_chain );
179 p_vout->psz_filter_chain = NULL;
182 if( !psz_filter_chain && !p_vout->psz_filter_chain )
184 p_vout->b_filter_change = VLC_FALSE;
187 if( psz_filter_chain ) free( psz_filter_chain );
190 if( ( p_vout->fmt_render.i_width != p_fmt->i_width ) ||
191 ( p_vout->fmt_render.i_height != p_fmt->i_height ) ||
192 ( p_vout->fmt_render.i_chroma != p_fmt->i_chroma ) ||
193 ( p_vout->fmt_render.i_aspect != p_fmt->i_aspect ) ||
194 p_vout->b_filter_change )
196 /* We are not interested in this format, close this vout */
197 vlc_object_release( p_vout );
198 vout_Destroy( p_vout );
203 /* This video output is cool! Hijack it. */
204 spu_Attach( p_vout->p_spu, p_this, VLC_TRUE );
205 vlc_object_attach( p_vout, p_this );
206 vlc_object_release( p_vout );
212 msg_Dbg( p_this, "no usable vout present, spawning one" );
214 p_vout = vout_Create( p_this, p_fmt );
220 /*****************************************************************************
221 * vout_Create: creates a new video output thread
222 *****************************************************************************
223 * This function creates a new video output thread, and returns a pointer
224 * to its description. On error, it returns NULL.
225 *****************************************************************************/
226 vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt )
228 vout_thread_t * p_vout; /* thread descriptor */
229 input_thread_t * p_input_thread;
230 int i_index; /* loop variable */
231 char * psz_plugin = NULL;
232 vlc_value_t val, text;
234 unsigned int i_width = p_fmt->i_width;
235 unsigned int i_height = p_fmt->i_height;
236 vlc_fourcc_t i_chroma = p_fmt->i_chroma;
237 unsigned int i_aspect = p_fmt->i_aspect;
239 config_chain_t *p_cfg;
243 /* Allocate descriptor */
244 p_vout = vlc_object_create( p_parent, VLC_OBJECT_VOUT );
247 msg_Err( p_parent, "out of memory" );
251 /* Initialize pictures - translation tables and functions
252 * will be initialized later in InitThread */
253 for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++)
255 p_vout->p_picture[i_index].pf_lock = NULL;
256 p_vout->p_picture[i_index].pf_unlock = NULL;
257 p_vout->p_picture[i_index].i_status = FREE_PICTURE;
258 p_vout->p_picture[i_index].i_type = EMPTY_PICTURE;
259 p_vout->p_picture[i_index].b_slow = 0;
262 /* No images in the heap */
263 p_vout->i_heap_size = 0;
265 /* Initialize the rendering heap */
266 I_RENDERPICTURES = 0;
268 vlc_ureduce( &p_fmt->i_sar_num, &p_fmt->i_sar_den,
269 p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 );
270 p_vout->fmt_render = *p_fmt; /* FIXME palette */
271 p_vout->fmt_in = *p_fmt; /* FIXME palette */
273 p_vout->render.i_width = i_width;
274 p_vout->render.i_height = i_height;
275 p_vout->render.i_chroma = i_chroma;
276 p_vout->render.i_aspect = i_aspect;
278 p_vout->render.i_rmask = 0;
279 p_vout->render.i_gmask = 0;
280 p_vout->render.i_bmask = 0;
282 p_vout->render.i_last_used_pic = -1;
283 p_vout->render.b_allow_modify_pics = 1;
285 /* Zero the output heap */
286 I_OUTPUTPICTURES = 0;
287 p_vout->output.i_width = 0;
288 p_vout->output.i_height = 0;
289 p_vout->output.i_chroma = 0;
290 p_vout->output.i_aspect = 0;
292 p_vout->output.i_rmask = 0;
293 p_vout->output.i_gmask = 0;
294 p_vout->output.i_bmask = 0;
296 /* Initialize misc stuff */
297 p_vout->i_changes = 0;
299 p_vout->b_grayscale = 0;
301 p_vout->b_interface = 0;
303 p_vout->b_fullscreen = 0;
304 p_vout->i_alignment = 0;
305 p_vout->render_time = 10;
306 p_vout->c_fps_samples = 0;
307 p_vout->b_filter_change = 0;
308 p_vout->pf_control = 0;
309 p_vout->p_parent_intf = 0;
310 p_vout->i_par_num = p_vout->i_par_den = 1;
312 /* Initialize locks */
313 vlc_mutex_init( p_vout, &p_vout->picture_lock );
314 vlc_mutex_init( p_vout, &p_vout->change_lock );
315 vlc_mutex_init( p_vout, &p_vout->vfilter_lock );
317 /* Mouse coordinates */
318 var_Create( p_vout, "mouse-x", VLC_VAR_INTEGER );
319 var_Create( p_vout, "mouse-y", VLC_VAR_INTEGER );
320 var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER );
321 var_Create( p_vout, "mouse-moved", VLC_VAR_BOOL );
322 var_Create( p_vout, "mouse-clicked", VLC_VAR_INTEGER );
324 /* Initialize subpicture unit */
325 p_vout->p_spu = spu_Create( p_vout );
326 spu_Attach( p_vout->p_spu, p_parent, VLC_TRUE );
328 /* Attach the new object now so we can use var inheritance below */
329 vlc_object_attach( p_vout, p_parent );
331 spu_Init( p_vout->p_spu );
333 /* Take care of some "interface/control" related initialisations */
334 vout_IntfInit( p_vout );
336 /* If the parent is not a VOUT object, that means we are at the start of
337 * the video output pipe */
338 if( p_parent->i_object_type != VLC_OBJECT_VOUT )
340 /* Look for the default filter configuration */
341 var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
342 var_Get( p_vout, "vout-filter", &val );
343 p_vout->psz_filter_chain = val.psz_string;
345 /* Apply video filter2 objects on the first vout */
346 var_Create( p_vout, "video-filter",
347 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
348 var_Get( p_vout, "video-filter", &val );
349 ParseVideoFilter2Chain( p_vout, val.psz_string );
350 free( val.psz_string );
354 /* continue the parent's filter chain */
357 psz_end = strchr( ((vout_thread_t *)p_parent)->psz_filter_chain, ':' );
358 if( psz_end && *(psz_end+1) )
359 p_vout->psz_filter_chain = strdup( psz_end+1 );
360 else p_vout->psz_filter_chain = NULL;
362 /* Create a video filter2 var ... but don't inherit values */
363 var_Create( p_vout, "video-filter", VLC_VAR_STRING );
364 ParseVideoFilter2Chain( p_vout, NULL );
367 var_AddCallback( p_vout, "video-filter", VideoFilter2Callback, NULL );
368 p_vout->b_vfilter_change = VLC_TRUE;
369 p_vout->i_vfilters = 0;
371 /* Choose the video output module */
372 if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain )
374 var_Create( p_vout, "vout", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
375 var_Get( p_vout, "vout", &val );
376 psz_plugin = val.psz_string;
381 /* the filter chain is a string list of filters separated by double
385 psz_end = strchr( p_vout->psz_filter_chain, ':' );
387 psz_plugin = strndup( p_vout->psz_filter_chain,
388 psz_end - p_vout->psz_filter_chain );
389 else psz_plugin = strdup( p_vout->psz_filter_chain );
393 /* Create the vout thread */
394 psz_parser = p_vout->psz_filter_chain;
395 printf("psz_parser: %s\n", psz_parser );
396 psz_parser = config_ChainCreate( &psz_name, &p_cfg, psz_parser );
397 printf("psz_parser: %s\n", 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, 0 );
403 if( psz_plugin ) free( psz_plugin );
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 );
439 var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
440 text.psz_string = _("Filters");
441 var_Change( p_vout, "vout-filter", VLC_VAR_SETTEXT, &text, NULL );
442 var_AddCallback( p_vout, "vout-filter", FilterCallback, NULL );
444 /* Calculate delay created by internal caching */
445 p_input_thread = (input_thread_t *)vlc_object_find( p_vout,
446 VLC_OBJECT_INPUT, FIND_ANYWHERE );
449 p_vout->i_pts_delay = p_input_thread->i_pts_delay;
450 vlc_object_release( p_input_thread );
454 p_vout->i_pts_delay = DEFAULT_PTS_DELAY;
457 if( vlc_thread_create( p_vout, "video output", RunThread,
458 VLC_THREAD_PRIORITY_OUTPUT, VLC_TRUE ) )
460 msg_Err( p_vout, "out of memory" );
461 module_Unneed( p_vout, p_vout->p_module );
462 vlc_object_detach( p_vout );
463 vlc_object_destroy( p_vout );
467 if( p_vout->b_error )
469 msg_Err( p_vout, "video output creation failed" );
471 /* Make sure the thread is destroyed */
472 p_vout->b_die = VLC_TRUE;
474 vlc_thread_join( p_vout );
476 vlc_object_detach( p_vout );
477 vlc_object_destroy( p_vout );
484 /*****************************************************************************
485 * vout_Destroy: destroys a previously created video output
486 *****************************************************************************
487 * Destroy a terminated thread.
488 * The function will request a destruction of the specified thread. If pi_error
489 * is NULL, it will return once the thread is destroyed. Else, it will be
490 * update using one of the THREAD_* constants.
491 *****************************************************************************/
492 void vout_Destroy( vout_thread_t *p_vout )
494 vout_thread_t *p_another_vout;
495 playlist_t *p_playlist = pl_Yield( p_vout );
497 /* Request thread destruction */
498 p_vout->b_die = VLC_TRUE;
499 vlc_thread_join( p_vout );
501 var_Destroy( p_vout, "intf-change" );
503 if( p_vout->psz_filter_chain ) free( p_vout->psz_filter_chain );
505 config_ChainDestroy( p_vout->p_cfg );
508 vlc_object_destroy( p_vout );
510 /* This is a dirty hack for mostly Linux, where there is no way to get the GUI
511 back if you closed it while playing video. This is solved in Mac OS X,
512 where we have this novelty called menubar, that will always allow you access
513 to the applications main functionality. They should try that on linux sometime */
514 p_another_vout = vlc_object_find( p_playlist,
515 VLC_OBJECT_VOUT, FIND_ANYWHERE );
516 if( p_another_vout == NULL )
519 val.b_bool = VLC_TRUE;
520 var_Set( p_playlist, "intf-show", val );
524 vlc_object_release( p_another_vout );
527 vlc_object_release( p_playlist );
530 /*****************************************************************************
531 * InitThread: initialize video output thread
532 *****************************************************************************
533 * This function is called from RunThread and performs the second step of the
534 * initialization. It returns 0 on success. Note that the thread's flag are not
535 * modified inside this function.
536 *****************************************************************************/
537 static int InitThread( vout_thread_t *p_vout )
539 int i, i_aspect_x, i_aspect_y;
541 vlc_mutex_lock( &p_vout->change_lock );
547 /* Initialize output method, it allocates direct buffers for us */
548 if( p_vout->pf_init( p_vout ) )
550 vlc_mutex_unlock( &p_vout->change_lock );
554 if( !I_OUTPUTPICTURES )
556 msg_Err( p_vout, "plugin was unable to allocate at least "
557 "one direct buffer" );
558 p_vout->pf_end( p_vout );
559 vlc_mutex_unlock( &p_vout->change_lock );
563 if( I_OUTPUTPICTURES > VOUT_MAX_PICTURES )
565 msg_Err( p_vout, "plugin allocated too many direct buffers, "
566 "our internal buffers must have overflown." );
567 p_vout->pf_end( p_vout );
568 vlc_mutex_unlock( &p_vout->change_lock );
572 msg_Dbg( p_vout, "got %i direct buffer(s)", I_OUTPUTPICTURES );
574 AspectRatio( p_vout->fmt_render.i_aspect, &i_aspect_x, &i_aspect_y );
576 msg_Dbg( p_vout, "picture in %ix%i (%i,%i,%ix%i), "
577 "chroma %4.4s, ar %i:%i, sar %i:%i",
578 p_vout->fmt_render.i_width, p_vout->fmt_render.i_height,
579 p_vout->fmt_render.i_x_offset, p_vout->fmt_render.i_y_offset,
580 p_vout->fmt_render.i_visible_width,
581 p_vout->fmt_render.i_visible_height,
582 (char*)&p_vout->fmt_render.i_chroma,
583 i_aspect_x, i_aspect_y,
584 p_vout->fmt_render.i_sar_num, p_vout->fmt_render.i_sar_den );
586 AspectRatio( p_vout->fmt_in.i_aspect, &i_aspect_x, &i_aspect_y );
588 msg_Dbg( p_vout, "picture user %ix%i (%i,%i,%ix%i), "
589 "chroma %4.4s, ar %i:%i, sar %i:%i",
590 p_vout->fmt_in.i_width, p_vout->fmt_in.i_height,
591 p_vout->fmt_in.i_x_offset, p_vout->fmt_in.i_y_offset,
592 p_vout->fmt_in.i_visible_width,
593 p_vout->fmt_in.i_visible_height,
594 (char*)&p_vout->fmt_in.i_chroma,
595 i_aspect_x, i_aspect_y,
596 p_vout->fmt_in.i_sar_num, p_vout->fmt_in.i_sar_den );
598 if( !p_vout->fmt_out.i_width || !p_vout->fmt_out.i_height )
600 p_vout->fmt_out.i_width = p_vout->fmt_out.i_visible_width =
601 p_vout->output.i_width;
602 p_vout->fmt_out.i_height = p_vout->fmt_out.i_visible_height =
603 p_vout->output.i_height;
604 p_vout->fmt_out.i_x_offset = p_vout->fmt_out.i_y_offset = 0;
606 p_vout->fmt_out.i_aspect = p_vout->output.i_aspect;
607 p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
609 if( !p_vout->fmt_out.i_sar_num || !p_vout->fmt_out.i_sar_num )
611 p_vout->fmt_out.i_sar_num = p_vout->fmt_out.i_aspect *
612 p_vout->fmt_out.i_height;
613 p_vout->fmt_out.i_sar_den = VOUT_ASPECT_FACTOR *
614 p_vout->fmt_out.i_width;
617 vlc_ureduce( &p_vout->fmt_out.i_sar_num, &p_vout->fmt_out.i_sar_den,
618 p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den, 0 );
620 AspectRatio( p_vout->fmt_out.i_aspect, &i_aspect_x, &i_aspect_y );
622 msg_Dbg( p_vout, "picture out %ix%i (%i,%i,%ix%i), "
623 "chroma %4.4s, ar %i:%i, sar %i:%i",
624 p_vout->fmt_out.i_width, p_vout->fmt_out.i_height,
625 p_vout->fmt_out.i_x_offset, p_vout->fmt_out.i_y_offset,
626 p_vout->fmt_out.i_visible_width,
627 p_vout->fmt_out.i_visible_height,
628 (char*)&p_vout->fmt_out.i_chroma,
629 i_aspect_x, i_aspect_y,
630 p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den );
632 /* Calculate shifts from system-updated masks */
633 MaskToShift( &p_vout->output.i_lrshift, &p_vout->output.i_rrshift,
634 p_vout->output.i_rmask );
635 MaskToShift( &p_vout->output.i_lgshift, &p_vout->output.i_rgshift,
636 p_vout->output.i_gmask );
637 MaskToShift( &p_vout->output.i_lbshift, &p_vout->output.i_rbshift,
638 p_vout->output.i_bmask );
640 /* Check whether we managed to create direct buffers similar to
641 * the render buffers, ie same size and chroma */
642 if( ( p_vout->output.i_width == p_vout->render.i_width )
643 && ( p_vout->output.i_height == p_vout->render.i_height )
644 && ( vout_ChromaCmp( p_vout->output.i_chroma, p_vout->render.i_chroma ) ) )
646 /* Cool ! We have direct buffers, we can ask the decoder to
647 * directly decode into them ! Map the first render buffers to
648 * the first direct buffers, but keep the first direct buffer
649 * for memcpy operations */
650 p_vout->b_direct = 1;
652 for( i = 1; i < VOUT_MAX_PICTURES; i++ )
654 if( p_vout->p_picture[ i ].i_type != DIRECT_PICTURE &&
655 I_RENDERPICTURES >= VOUT_MIN_DIRECT_PICTURES - 1 &&
656 p_vout->p_picture[ i - 1 ].i_type == DIRECT_PICTURE )
658 /* We have enough direct buffers so there's no need to
659 * try to use system memory buffers. */
662 PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
666 msg_Dbg( p_vout, "direct render, mapping "
667 "render pictures 0-%i to system pictures 1-%i",
668 VOUT_MAX_PICTURES - 2, VOUT_MAX_PICTURES - 1 );
672 /* Rats... Something is wrong here, we could not find an output
673 * plugin able to directly render what we decode. See if we can
674 * find a chroma plugin to do the conversion */
675 p_vout->b_direct = 0;
677 /* Choose the best module */
678 p_vout->chroma.p_module = module_Need( p_vout, "chroma", NULL, 0 );
680 if( p_vout->chroma.p_module == NULL )
682 msg_Err( p_vout, "no chroma module for %4.4s to %4.4s",
683 (char*)&p_vout->render.i_chroma,
684 (char*)&p_vout->output.i_chroma );
685 p_vout->pf_end( p_vout );
686 vlc_mutex_unlock( &p_vout->change_lock );
690 msg_Dbg( p_vout, "indirect render, mapping "
691 "render pictures 0-%i to system pictures %i-%i",
692 VOUT_MAX_PICTURES - 1, I_OUTPUTPICTURES,
693 I_OUTPUTPICTURES + VOUT_MAX_PICTURES - 1 );
695 /* Append render buffers after the direct buffers */
696 for( i = I_OUTPUTPICTURES; i < 2 * VOUT_MAX_PICTURES; i++ )
698 PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
701 /* Check if we have enough render pictures */
702 if( I_RENDERPICTURES == VOUT_MAX_PICTURES )
707 /* Link pictures back to their heap */
708 for( i = 0 ; i < I_RENDERPICTURES ; i++ )
710 PP_RENDERPICTURE[ i ]->p_heap = &p_vout->render;
713 for( i = 0 ; i < I_OUTPUTPICTURES ; i++ )
715 PP_OUTPUTPICTURE[ i ]->p_heap = &p_vout->output;
718 /* XXX XXX mark thread ready */
722 /*****************************************************************************
723 * RunThread: video output thread
724 *****************************************************************************
725 * Video output thread. This function does only returns when the thread is
726 * terminated. It handles the pictures arriving in the video heap and the
727 * display device events.
728 *****************************************************************************/
729 static void RunThread( vout_thread_t *p_vout)
731 int i_index; /* index in heap */
732 int i_idle_loops = 0; /* loops without displaying a picture */
733 mtime_t current_date; /* current date */
734 mtime_t display_date; /* display date */
736 picture_t * p_picture; /* picture pointer */
737 picture_t * p_last_picture = NULL; /* last picture */
738 picture_t * p_directbuffer; /* direct buffer to display */
740 subpicture_t * p_subpic = NULL; /* subpicture pointer */
742 input_thread_t *p_input = NULL ; /* Parent input, if it exists */
745 vlc_bool_t b_drop_late;
747 int i_displayed = 0, i_lost = 0, i_loops = 0;
752 p_vout->b_error = InitThread( p_vout );
754 var_Create( p_vout, "drop-late-frames", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
755 var_Get( p_vout, "drop-late-frames", &val );
756 b_drop_late = val.b_bool;
758 /* signal the creation of the vout */
759 vlc_thread_ready( p_vout );
761 if( p_vout->b_error )
763 /* Destroy thread structures allocated by Create and InitThread */
764 DestroyThread( p_vout );
769 * Main loop - it is not executed if an error occurred during
772 while( (!p_vout->b_die) && (!p_vout->b_error) )
774 /* Initialize loop variables */
777 current_date = mdate();
779 if( p_input && p_input->b_die )
781 vlc_object_release( p_input );
786 if( i_loops % 20 == 0 )
790 p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT,
795 vlc_mutex_lock( &p_input->p->counters.counters_lock );
796 stats_UpdateInteger( p_vout, p_input->p->counters.p_lost_pictures,
798 stats_UpdateInteger( p_vout,
799 p_input->p->counters.p_displayed_pictures,
801 i_displayed = i_lost = 0;
802 vlc_mutex_unlock( &p_input->p->counters.counters_lock );
807 if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
809 msg_Dbg( p_vout, "picture heap: %d/%d",
810 I_RENDERPICTURES, p_vout->i_heap_size );
815 * Find the picture to display (the one with the earliest date).
816 * This operation does not need lock, since only READY_PICTUREs
818 for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
820 if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
821 && ( (p_picture == NULL) ||
822 (PP_RENDERPICTURE[i_index]->date < display_date) ) )
824 p_picture = PP_RENDERPICTURE[i_index];
825 display_date = p_picture->date;
831 /* If we met the last picture, parse again to see whether there is
832 * a more appropriate one. */
833 if( p_picture == p_last_picture )
835 for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
837 if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
838 && (PP_RENDERPICTURE[i_index] != p_last_picture)
839 && ((p_picture == p_last_picture) ||
840 (PP_RENDERPICTURE[i_index]->date < display_date)) )
842 p_picture = PP_RENDERPICTURE[i_index];
843 display_date = p_picture->date;
848 /* If we found better than the last picture, destroy it */
849 if( p_last_picture && p_picture != p_last_picture )
851 vlc_mutex_lock( &p_vout->picture_lock );
852 if( p_last_picture->i_refcount )
854 p_last_picture->i_status = DISPLAYED_PICTURE;
858 p_last_picture->i_status = DESTROYED_PICTURE;
859 p_vout->i_heap_size--;
861 vlc_mutex_unlock( &p_vout->picture_lock );
862 p_last_picture = NULL;
865 /* Compute FPS rate */
866 p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ]
869 if( !p_picture->b_force &&
870 p_picture != p_last_picture &&
871 display_date < current_date + p_vout->render_time &&
874 /* Picture is late: it will be destroyed and the thread
875 * will directly choose the next picture */
876 vlc_mutex_lock( &p_vout->picture_lock );
877 if( p_picture->i_refcount )
879 /* Pretend we displayed the picture, but don't destroy
880 * it since the decoder might still need it. */
881 p_picture->i_status = DISPLAYED_PICTURE;
885 /* Destroy the picture without displaying it */
886 p_picture->i_status = DESTROYED_PICTURE;
887 p_vout->i_heap_size--;
889 msg_Warn( p_vout, "late picture skipped ("I64Fd")",
890 current_date - display_date );
892 vlc_mutex_unlock( &p_vout->picture_lock );
898 current_date + p_vout->i_pts_delay + VOUT_BOGUS_DELAY )
900 /* Picture is waaay too early: it will be destroyed */
901 vlc_mutex_lock( &p_vout->picture_lock );
902 if( p_picture->i_refcount )
904 /* Pretend we displayed the picture, but don't destroy
905 * it since the decoder might still need it. */
906 p_picture->i_status = DISPLAYED_PICTURE;
910 /* Destroy the picture without displaying it */
911 p_picture->i_status = DESTROYED_PICTURE;
912 p_vout->i_heap_size--;
915 msg_Warn( p_vout, "vout warning: early picture skipped "
916 "("I64Fd")", display_date - current_date
917 - p_vout->i_pts_delay );
918 vlc_mutex_unlock( &p_vout->picture_lock );
923 if( display_date > current_date + VOUT_DISPLAY_DELAY )
925 /* A picture is ready to be rendered, but its rendering date
926 * is far from the current one so the thread will perform an
927 * empty loop as if no picture were found. The picture state
932 else if( p_picture == p_last_picture )
934 /* We are asked to repeat the previous picture, but we first
935 * wait for a couple of idle loops */
936 if( i_idle_loops < 4 )
943 /* We set the display date to something high, otherwise
944 * we'll have lots of problems with late pictures */
945 display_date = current_date + p_vout->render_time;
950 if( p_picture == NULL )
955 /* Video Filter2 stuff */
956 if( p_vout->b_vfilter_change == VLC_TRUE )
959 vlc_mutex_lock( &p_vout->vfilter_lock );
960 RemoveVideoFilters2( p_vout );
961 for( i = 0; i < p_vout->i_vfilters_cfg; i++ )
963 filter_t *p_vfilter =
964 p_vout->pp_vfilters[p_vout->i_vfilters] =
965 vlc_object_create( p_vout, VLC_OBJECT_FILTER );
967 vlc_object_attach( p_vfilter, p_vout );
969 p_vfilter->pf_vout_buffer_new = video_new_buffer_filter;
970 p_vfilter->pf_vout_buffer_del = video_del_buffer_filter;
972 if( !p_vout->i_vfilters )
974 p_vfilter->fmt_in.video = p_vout->fmt_render;
978 p_vfilter->fmt_in.video = (p_vfilter-1)->fmt_out.video;
980 /* TODO: one day filters in the middle of the chain might
981 * have a different fmt_out.video than fmt_render ... */
982 p_vfilter->fmt_out.video = p_vout->fmt_render;
984 p_vfilter->p_cfg = p_vout->p_vfilters_cfg[i];
985 p_vfilter->p_module = module_Need( p_vfilter, "video filter2",
986 p_vout->psz_vfilters[i], 0 );
988 if( p_vfilter->p_module )
991 malloc( sizeof( filter_owner_sys_t ) );
992 p_vfilter->p_owner->p_vout = p_vout;
993 p_vout->i_vfilters++;
994 msg_Dbg( p_vout, "video filter found (%s)",
995 p_vout->psz_vfilters[i] );
999 msg_Err( p_vout, "no video filter found (%s)",
1000 p_vout->psz_vfilters[i] );
1001 vlc_object_detach( p_vfilter );
1002 vlc_object_destroy( p_vfilter );
1005 p_vout->b_vfilter_change = VLC_FALSE;
1006 vlc_mutex_unlock( &p_vout->vfilter_lock );
1012 for( i = 0; i < p_vout->i_vfilters; i++ )
1014 picture_t *p_old = p_picture;
1015 p_picture = p_vout->pp_vfilters[i]->pf_video_filter(
1016 p_vout->pp_vfilters[i], p_picture );
1021 /* FIXME: this is kind of wrong
1022 * if you have 2 or more vfilters and the 2nd breaks,
1023 * on the next loop the 1st one will be applied again */
1025 /* if p_old and p_picture are the same (ie the filter
1026 * worked on the old picture), then following code is
1027 * still alright since i_status gets changed back to
1028 * the right value */
1029 if( p_old->i_refcount )
1031 p_old->i_status = DISPLAYED_PICTURE;
1035 p_old->i_status = DESTROYED_PICTURE;
1037 p_picture->i_status = READY_PICTURE;
1041 if( p_picture && p_vout->b_snapshot )
1043 p_vout->b_snapshot = VLC_FALSE;
1044 vout_Snapshot( p_vout, p_picture );
1048 * Check for subpictures to display
1050 if( display_date > 0 )
1054 p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT,
1057 p_subpic = spu_SortSubpictures( p_vout->p_spu, display_date,
1058 p_input ? var_GetBool( p_input, "state" ) == PAUSE_S : VLC_FALSE );
1065 p_directbuffer = vout_RenderPicture( p_vout, p_picture, p_subpic );
1068 * Call the plugin-specific rendering method if there is one
1070 if( p_picture != NULL && p_directbuffer != NULL && p_vout->pf_render )
1072 /* Render the direct buffer returned by vout_RenderPicture */
1073 p_vout->pf_render( p_vout, p_directbuffer );
1079 if( display_date != 0 && p_directbuffer != NULL )
1081 mtime_t current_render_time = mdate() - current_date;
1082 /* if render time is very large we don't include it in the mean */
1083 if( current_render_time < p_vout->render_time +
1084 VOUT_DISPLAY_DELAY )
1086 /* Store render time using a sliding mean weighting to
1087 * current value in a 3 to 1 ratio*/
1088 p_vout->render_time *= 3;
1089 p_vout->render_time += current_render_time;
1090 p_vout->render_time >>= 2;
1094 /* Give back change lock */
1095 vlc_mutex_unlock( &p_vout->change_lock );
1097 /* Sleep a while or until a given date */
1098 if( display_date != 0 )
1100 /* If there are filters in the chain, better give them the picture
1102 if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain )
1104 mwait( display_date - VOUT_MWAIT_TOLERANCE );
1109 msleep( VOUT_IDLE_SLEEP );
1112 /* On awakening, take back lock and send immediately picture
1114 vlc_mutex_lock( &p_vout->change_lock );
1117 * Display the previously rendered picture
1119 if( p_picture != NULL && p_directbuffer != NULL )
1121 /* Display the direct buffer returned by vout_RenderPicture */
1122 if( p_vout->pf_display )
1124 p_vout->pf_display( p_vout, p_directbuffer );
1127 /* Tell the vout this was the last picture and that it does not
1128 * need to be forced anymore. */
1129 p_last_picture = p_picture;
1130 p_last_picture->b_force = 0;
1133 if( p_picture != NULL )
1135 /* Reinitialize idle loop count */
1140 * Check events and manage thread
1142 if( p_vout->pf_manage && p_vout->pf_manage( p_vout ) )
1144 /* A fatal error occurred, and the thread must terminate
1145 * immediately, without displaying anything - setting b_error to 1
1146 * causes the immediate end of the main while() loop. */
1147 p_vout->b_error = 1;
1150 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
1152 /* this must only happen when the vout plugin is incapable of
1153 * rescaling the picture itself. In this case we need to destroy
1154 * the current picture buffers and recreate new ones with the right
1158 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
1160 p_vout->pf_end( p_vout );
1161 for( i = 0; i < I_OUTPUTPICTURES; i++ )
1162 p_vout->p_picture[ i ].i_status = FREE_PICTURE;
1164 I_OUTPUTPICTURES = 0;
1165 if( p_vout->pf_init( p_vout ) )
1167 msg_Err( p_vout, "cannot resize display" );
1168 /* FIXME: pf_end will be called again in EndThread() */
1169 p_vout->b_error = 1;
1172 /* Need to reinitialise the chroma plugin */
1173 if( p_vout->chroma.p_module )
1175 if( p_vout->chroma.p_module->pf_deactivate )
1176 p_vout->chroma.p_module->pf_deactivate( VLC_OBJECT(p_vout) );
1177 p_vout->chroma.p_module->pf_activate( VLC_OBJECT(p_vout) );
1181 if( p_vout->i_changes & VOUT_PICTURE_BUFFERS_CHANGE )
1183 /* This happens when the picture buffers need to be recreated.
1184 * This is useful on multimonitor displays for instance.
1186 * Warning: This only works when the vout creates only 1 picture
1188 p_vout->i_changes &= ~VOUT_PICTURE_BUFFERS_CHANGE;
1190 if( !p_vout->b_direct )
1192 module_Unneed( p_vout, p_vout->chroma.p_module );
1195 vlc_mutex_lock( &p_vout->picture_lock );
1197 p_vout->pf_end( p_vout );
1199 I_OUTPUTPICTURES = I_RENDERPICTURES = 0;
1201 p_vout->b_error = InitThread( p_vout );
1203 vlc_mutex_unlock( &p_vout->picture_lock );
1209 vlc_object_release( p_input );
1213 * Error loop - wait until the thread destruction is requested
1215 if( p_vout->b_error )
1217 ErrorThread( p_vout );
1221 EndThread( p_vout );
1223 /* Destroy thread structures allocated by CreateThread */
1224 DestroyThread( p_vout );
1227 /*****************************************************************************
1228 * ErrorThread: RunThread() error loop
1229 *****************************************************************************
1230 * This function is called when an error occurred during thread main's loop.
1231 * The thread can still receive feed, but must be ready to terminate as soon
1233 *****************************************************************************/
1234 static void ErrorThread( vout_thread_t *p_vout )
1236 /* Wait until a `die' order */
1237 while( !p_vout->b_die )
1240 msleep( VOUT_IDLE_SLEEP );
1244 /*****************************************************************************
1245 * EndThread: thread destruction
1246 *****************************************************************************
1247 * This function is called when the thread ends after a sucessful
1248 * initialization. It frees all resources allocated by InitThread.
1249 *****************************************************************************/
1250 static void EndThread( vout_thread_t *p_vout )
1252 int i_index; /* index in heap */
1256 struct tms cpu_usage;
1257 times( &cpu_usage );
1259 msg_Dbg( p_vout, "cpu usage (user: %d, system: %d)",
1260 cpu_usage.tms_utime, cpu_usage.tms_stime );
1264 if( !p_vout->b_direct )
1266 module_Unneed( p_vout, p_vout->chroma.p_module );
1269 /* Destroy all remaining pictures */
1270 for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++ )
1272 if ( p_vout->p_picture[i_index].i_type == MEMORY_PICTURE )
1274 free( p_vout->p_picture[i_index].p_data_orig );
1278 /* Destroy subpicture unit */
1279 spu_Attach( p_vout->p_spu, VLC_OBJECT(p_vout), VLC_FALSE );
1280 spu_Destroy( p_vout->p_spu );
1282 /* Destroy the video filters2 */
1283 RemoveVideoFilters2( p_vout );
1285 /* Destroy translation tables */
1286 p_vout->pf_end( p_vout );
1288 /* Release the change lock */
1289 vlc_mutex_unlock( &p_vout->change_lock );
1292 /*****************************************************************************
1293 * DestroyThread: thread destruction
1294 *****************************************************************************
1295 * This function is called when the thread ends. It frees all ressources
1296 * allocated by CreateThread. Status is available at this stage.
1297 *****************************************************************************/
1298 static void DestroyThread( vout_thread_t *p_vout )
1300 /* Destroy the locks */
1301 vlc_mutex_destroy( &p_vout->picture_lock );
1302 vlc_mutex_destroy( &p_vout->change_lock );
1303 vlc_mutex_destroy( &p_vout->vfilter_lock );
1305 /* Release the module */
1306 if( p_vout && p_vout->p_module )
1308 module_Unneed( p_vout, p_vout->p_module );
1312 /* following functions are local */
1314 static int ReduceHeight( int i_ratio )
1316 int i_dummy = VOUT_ASPECT_FACTOR;
1324 /* VOUT_ASPECT_FACTOR is (2^7 * 3^3 * 5^3), we just check for 2, 3 and 5 */
1325 while( !(i_ratio & 1) && !(i_dummy & 1) )
1332 while( !(i_ratio % 3) && !(i_dummy % 3) )
1339 while( !(i_ratio % 5) && !(i_dummy % 5) )
1349 static void AspectRatio( int i_aspect, int *i_aspect_x, int *i_aspect_y )
1351 unsigned int i_pgcd = ReduceHeight( i_aspect );
1352 *i_aspect_x = i_aspect / i_pgcd;
1353 *i_aspect_y = VOUT_ASPECT_FACTOR / i_pgcd;
1356 /*****************************************************************************
1357 * BinaryLog: computes the base 2 log of a binary value
1358 *****************************************************************************
1359 * This functions is used by MaskToShift, to get a bit index from a binary
1361 *****************************************************************************/
1362 static int BinaryLog( uint32_t i )
1366 if( i == 0 ) return -31337;
1368 if( i & 0xffff0000 ) i_log += 16;
1369 if( i & 0xff00ff00 ) i_log += 8;
1370 if( i & 0xf0f0f0f0 ) i_log += 4;
1371 if( i & 0xcccccccc ) i_log += 2;
1372 if( i & 0xaaaaaaaa ) i_log += 1;
1377 /*****************************************************************************
1378 * MaskToShift: transform a color mask into right and left shifts
1379 *****************************************************************************
1380 * This function is used for obtaining color shifts from masks.
1381 *****************************************************************************/
1382 static void MaskToShift( int *pi_left, int *pi_right, uint32_t i_mask )
1384 uint32_t i_low, i_high; /* lower hand higher bits of the mask */
1388 *pi_left = *pi_right = 0;
1393 i_low = i_high = i_mask;
1395 i_low &= - (int32_t)i_low; /* lower bit of the mask */
1396 i_high += i_low; /* higher bit of the mask */
1398 /* Transform bits into an index. Also deal with i_high overflow, which
1399 * is faster than changing the BinaryLog code to handle 64 bit integers. */
1400 i_low = BinaryLog (i_low);
1401 i_high = i_high ? BinaryLog (i_high) : 32;
1403 /* Update pointers and return */
1405 *pi_right = (8 - i_high + i_low);
1408 /*****************************************************************************
1409 * vout_VarCallback: generic callback for intf variables
1410 *****************************************************************************/
1411 int vout_VarCallback( vlc_object_t * p_this, const char * psz_variable,
1412 vlc_value_t old_value, vlc_value_t new_value,
1415 vout_thread_t * p_vout = (vout_thread_t *)p_this;
1417 val.b_bool = VLC_TRUE;
1418 var_Set( p_vout, "intf-change", val );
1422 /*****************************************************************************
1423 * Helper thread for object variables callbacks.
1424 * Only used to avoid deadlocks when using the video embedded mode.
1425 *****************************************************************************/
1426 typedef struct suxor_thread_t
1429 input_thread_t *p_input;
1433 static void SuxorRestartVideoES( suxor_thread_t *p_this )
1437 vlc_thread_ready( p_this );
1439 /* Now restart current video stream */
1440 var_Get( p_this->p_input, "video-es", &val );
1441 if( val.i_int >= 0 )
1444 val_es.i_int = -VIDEO_ES;
1445 var_Set( p_this->p_input, "video-es", val_es );
1446 var_Set( p_this->p_input, "video-es", val );
1449 vlc_object_release( p_this->p_input );
1452 CloseHandle( p_this->thread_id );
1455 vlc_object_destroy( p_this );
1458 /*****************************************************************************
1459 * object variables callbacks: a bunch of object variables are used by the
1460 * interfaces to interact with the vout.
1461 *****************************************************************************/
1462 static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd,
1463 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1465 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1466 input_thread_t *p_input;
1469 char *psz_mode = newval.psz_string;
1470 char *psz_filter, *psz_deinterlace = NULL;
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;
1523 p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
1527 msg_Err( p_vout, "Input not found" );
1528 return( VLC_EGENERIC );
1531 val.b_bool = VLC_TRUE;
1532 var_Set( p_vout, "intf-change", val );
1534 /* Modify input as well because the vout might have to be restarted */
1535 val.psz_string = newval.psz_string;
1536 var_Create( p_input, "vout-filter", VLC_VAR_STRING );
1538 var_Set( p_input, "vout-filter", val );
1540 /* Now restart current video stream */
1541 var_Get( p_input, "video-es", &val );
1542 if( val.i_int >= 0 )
1544 suxor_thread_t *p_suxor =
1545 vlc_object_create( p_vout, sizeof(suxor_thread_t) );
1546 p_suxor->p_input = p_input;
1547 p_vout->b_filter_change = VLC_TRUE;
1548 vlc_object_yield( p_input );
1549 vlc_thread_create( p_suxor, "suxor", SuxorRestartVideoES,
1550 VLC_THREAD_PRIORITY_LOW, VLC_FALSE );
1553 vlc_object_release( p_input );
1558 /*****************************************************************************
1559 * Video Filter2 stuff
1560 *****************************************************************************/
1561 static int ParseVideoFilter2Chain( vout_thread_t *p_vout, char *psz_vfilters )
1564 for( i = 0; i < p_vout->i_vfilters_cfg; i++ )
1566 struct config_chain_t *p_cfg =
1567 p_vout->p_vfilters_cfg[p_vout->i_vfilters_cfg];
1568 config_ChainDestroy( p_cfg );
1569 if( p_vout->psz_vfilters[p_vout->i_vfilters_cfg] )
1571 free( p_vout->psz_vfilters[p_vout->i_vfilters_cfg] );
1572 p_vout->psz_vfilters[p_vout->i_vfilters_cfg] = NULL;
1575 p_vout->i_vfilters_cfg = 0;
1576 if( psz_vfilters && *psz_vfilters )
1578 char *psz_parser = psz_vfilters;
1580 while( psz_parser && *psz_parser )
1582 psz_parser = config_ChainCreate(
1583 &p_vout->psz_vfilters[p_vout->i_vfilters_cfg],
1584 &p_vout->p_vfilters_cfg[p_vout->i_vfilters_cfg],
1586 msg_Dbg( p_vout, "adding vfilter: %s",
1587 p_vout->psz_vfilters[p_vout->i_vfilters_cfg] );
1588 p_vout->i_vfilters_cfg++;
1589 if( psz_parser && *psz_parser )
1591 if( p_vout->i_vfilters_cfg == MAX_VFILTERS )
1594 "maximum number of video filters reached. \"%s\" discarded",
1604 static int VideoFilter2Callback( vlc_object_t *p_this, char const *psz_cmd,
1605 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1607 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1609 vlc_mutex_lock( &p_vout->vfilter_lock );
1610 ParseVideoFilter2Chain( p_vout, newval.psz_string );
1611 p_vout->b_vfilter_change = VLC_TRUE;
1612 vlc_mutex_unlock( &p_vout->vfilter_lock );
1617 static void RemoveVideoFilters2( vout_thread_t *p_vout )
1620 for( i = 0; i < p_vout->i_vfilters; i++ )
1622 vlc_object_detach( p_vout->pp_vfilters[i] );
1623 if( p_vout->pp_vfilters[i]->p_module )
1625 module_Unneed( p_vout->pp_vfilters[i],
1626 p_vout->pp_vfilters[i]->p_module );
1629 free( p_vout->pp_vfilters[i]->p_owner );
1630 vlc_object_destroy( p_vout->pp_vfilters[i] );
1632 p_vout->i_vfilters = 0;