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 *****************************************************************************/
36 #include <vlc_common.h>
38 #include <stdlib.h> /* free() */
42 #ifdef HAVE_SYS_TIMES_H
43 # include <sys/times.h>
48 #include <vlc_filter.h>
51 #if defined( __APPLE__ )
52 /* Include darwin_specific.h here if needed */
55 /** FIXME This is quite ugly but needed while we don't have counters
57 #include "input/input_internal.h"
59 #include "modules/modules.h"
62 /*****************************************************************************
64 *****************************************************************************/
65 static int InitThread ( vout_thread_t
* );
66 static void* RunThread ( vlc_object_t
* );
67 static void ErrorThread ( vout_thread_t
* );
68 static void CleanThread ( vout_thread_t
* );
69 static void EndThread ( vout_thread_t
* );
71 static void AspectRatio ( int, int *, int * );
73 static void VideoFormatImportRgb( video_format_t
*, const picture_heap_t
* );
74 static void PictureHeapFixRgb( picture_heap_t
* );
76 static void vout_Destructor ( vlc_object_t
* p_this
);
78 /* Object variables callbacks */
79 static int DeinterlaceCallback( vlc_object_t
*, char const *,
80 vlc_value_t
, vlc_value_t
, void * );
81 static int FilterCallback( vlc_object_t
*, char const *,
82 vlc_value_t
, vlc_value_t
, void * );
83 static int VideoFilter2Callback( vlc_object_t
*, char const *,
84 vlc_value_t
, vlc_value_t
, void * );
86 /* From vout_intf.c */
87 int vout_Snapshot( vout_thread_t
*, picture_t
* );
89 /* Display media title in OSD */
90 static void DisplayTitleOnOSD( vout_thread_t
*p_vout
);
92 /*****************************************************************************
93 * Video Filter2 functions
94 *****************************************************************************/
95 static picture_t
*video_new_buffer_filter( filter_t
*p_filter
)
98 vout_thread_t
*p_vout
= (vout_thread_t
*)p_filter
->p_owner
;
100 p_picture
= vout_CreatePicture( p_vout
, 0, 0, 0 );
105 static void video_del_buffer_filter( filter_t
*p_filter
, picture_t
*p_pic
)
107 vout_DestroyPicture( (vout_thread_t
*)p_filter
->p_owner
, p_pic
);
110 static int video_filter_buffer_allocation_init( filter_t
*p_filter
, void *p_data
)
112 p_filter
->pf_vout_buffer_new
= video_new_buffer_filter
;
113 p_filter
->pf_vout_buffer_del
= video_del_buffer_filter
;
114 p_filter
->p_owner
= p_data
; /* p_vout */
118 /*****************************************************************************
119 * vout_Request: find a video output thread, create one, or destroy one.
120 *****************************************************************************
121 * This function looks for a video output thread matching the current
122 * properties. If not found, it spawns a new one.
123 *****************************************************************************/
124 vout_thread_t
*__vout_Request( vlc_object_t
*p_this
, vout_thread_t
*p_vout
,
125 video_format_t
*p_fmt
)
129 /* Video output is no longer used.
130 * TODO: support for reusing video outputs with proper _thread-safe_
131 * reference handling. */
133 vout_CloseAndRelease( p_vout
);
137 /* If a video output was provided, lock it, otherwise look for one. */
140 vlc_object_yield( p_vout
);
143 /* TODO: find a suitable unused video output */
145 /* If we now have a video output, check it has the right properties */
148 char *psz_filter_chain
;
151 /* We don't directly check for the "vout-filter" variable for obvious
152 * performance reasons. */
153 if( p_vout
->b_filter_change
)
155 var_Get( p_vout
, "vout-filter", &val
);
156 psz_filter_chain
= val
.psz_string
;
158 if( psz_filter_chain
&& !*psz_filter_chain
)
160 free( psz_filter_chain
);
161 psz_filter_chain
= NULL
;
163 if( p_vout
->psz_filter_chain
&& !*p_vout
->psz_filter_chain
)
165 free( p_vout
->psz_filter_chain
);
166 p_vout
->psz_filter_chain
= NULL
;
169 if( !psz_filter_chain
&& !p_vout
->psz_filter_chain
)
171 p_vout
->b_filter_change
= false;
174 free( psz_filter_chain
);
177 if( ( p_vout
->fmt_render
.i_width
!= p_fmt
->i_width
) ||
178 ( p_vout
->fmt_render
.i_height
!= p_fmt
->i_height
) ||
179 ( p_vout
->fmt_render
.i_aspect
!= p_fmt
->i_aspect
) ||
180 p_vout
->b_filter_change
)
182 /* We are not interested in this format, close this vout */
183 vout_CloseAndRelease( p_vout
);
184 vlc_object_release( p_vout
);
189 /* This video output is cool! Hijack it. */
190 spu_Attach( p_vout
->p_spu
, p_this
, true );
191 vlc_object_attach( p_vout
, p_this
);
192 if( p_vout
->b_title_show
)
193 DisplayTitleOnOSD( p_vout
);
194 vlc_object_release( p_vout
);
200 msg_Dbg( p_this
, "no usable vout present, spawning one" );
202 p_vout
= vout_Create( p_this
, p_fmt
);
208 /*****************************************************************************
209 * vout_Create: creates a new video output thread
210 *****************************************************************************
211 * This function creates a new video output thread, and returns a pointer
212 * to its description. On error, it returns NULL.
213 *****************************************************************************/
214 vout_thread_t
* __vout_Create( vlc_object_t
*p_parent
, video_format_t
*p_fmt
)
216 vout_thread_t
* p_vout
; /* thread descriptor */
217 input_thread_t
* p_input_thread
;
218 int i_index
; /* loop variable */
219 vlc_value_t val
, text
;
221 unsigned int i_width
= p_fmt
->i_width
;
222 unsigned int i_height
= p_fmt
->i_height
;
223 vlc_fourcc_t i_chroma
= p_fmt
->i_chroma
;
224 unsigned int i_aspect
= p_fmt
->i_aspect
;
226 config_chain_t
*p_cfg
;
230 if( i_width
<= 0 || i_height
<= 0 || i_aspect
<= 0 )
233 vlc_ureduce( &p_fmt
->i_sar_num
, &p_fmt
->i_sar_den
,
234 p_fmt
->i_sar_num
, p_fmt
->i_sar_den
, 50000 );
235 if( p_fmt
->i_sar_num
<= 0 || p_fmt
->i_sar_den
<= 0 )
238 /* Allocate descriptor */
239 static const char typename
[] = "video output";
240 p_vout
= vlc_custom_create( p_parent
, sizeof( *p_vout
), VLC_OBJECT_VOUT
,
245 /* Initialize pictures - translation tables and functions
246 * will be initialized later in InitThread */
247 for( i_index
= 0; i_index
< 2 * VOUT_MAX_PICTURES
+ 1; i_index
++)
249 p_vout
->p_picture
[i_index
].pf_lock
= NULL
;
250 p_vout
->p_picture
[i_index
].pf_unlock
= NULL
;
251 p_vout
->p_picture
[i_index
].i_status
= FREE_PICTURE
;
252 p_vout
->p_picture
[i_index
].i_type
= EMPTY_PICTURE
;
253 p_vout
->p_picture
[i_index
].b_slow
= 0;
256 /* No images in the heap */
257 p_vout
->i_heap_size
= 0;
259 /* Initialize the rendering heap */
260 I_RENDERPICTURES
= 0;
262 p_vout
->fmt_render
= *p_fmt
; /* FIXME palette */
263 p_vout
->fmt_in
= *p_fmt
; /* FIXME palette */
265 p_vout
->render
.i_width
= i_width
;
266 p_vout
->render
.i_height
= i_height
;
267 p_vout
->render
.i_chroma
= i_chroma
;
268 p_vout
->render
.i_aspect
= i_aspect
;
270 p_vout
->render
.i_rmask
= p_fmt
->i_rmask
;
271 p_vout
->render
.i_gmask
= p_fmt
->i_gmask
;
272 p_vout
->render
.i_bmask
= p_fmt
->i_bmask
;
274 p_vout
->render
.i_last_used_pic
= -1;
275 p_vout
->render
.b_allow_modify_pics
= 1;
277 /* Zero the output heap */
278 I_OUTPUTPICTURES
= 0;
279 p_vout
->output
.i_width
= 0;
280 p_vout
->output
.i_height
= 0;
281 p_vout
->output
.i_chroma
= 0;
282 p_vout
->output
.i_aspect
= 0;
284 p_vout
->output
.i_rmask
= 0;
285 p_vout
->output
.i_gmask
= 0;
286 p_vout
->output
.i_bmask
= 0;
288 /* Initialize misc stuff */
289 p_vout
->i_changes
= 0;
291 p_vout
->b_grayscale
= 0;
293 p_vout
->b_interface
= 0;
295 p_vout
->b_fullscreen
= 0;
296 p_vout
->i_alignment
= 0;
297 p_vout
->render_time
= 10;
298 p_vout
->c_fps_samples
= 0;
299 p_vout
->b_filter_change
= 0;
300 p_vout
->pf_control
= NULL
;
301 p_vout
->p_window
= NULL
;
302 p_vout
->i_par_num
= p_vout
->i_par_den
= 1;
304 /* Initialize locks */
305 vlc_mutex_init( &p_vout
->picture_lock
);
306 vlc_mutex_init( &p_vout
->change_lock
);
307 vlc_mutex_init( &p_vout
->vfilter_lock
);
309 /* Mouse coordinates */
310 var_Create( p_vout
, "mouse-x", VLC_VAR_INTEGER
);
311 var_Create( p_vout
, "mouse-y", VLC_VAR_INTEGER
);
312 var_Create( p_vout
, "mouse-button-down", VLC_VAR_INTEGER
);
313 var_Create( p_vout
, "mouse-moved", VLC_VAR_BOOL
);
314 var_Create( p_vout
, "mouse-clicked", VLC_VAR_INTEGER
);
316 /* Initialize subpicture unit */
317 p_vout
->p_spu
= spu_Create( p_vout
);
318 spu_Attach( p_vout
->p_spu
, p_parent
, true );
320 /* Attach the new object now so we can use var inheritance below */
321 vlc_object_attach( p_vout
, p_parent
);
323 spu_Init( p_vout
->p_spu
);
325 /* Take care of some "interface/control" related initialisations */
326 vout_IntfInit( p_vout
);
328 /* If the parent is not a VOUT object, that means we are at the start of
329 * the video output pipe */
330 if( p_parent
->i_object_type
!= VLC_OBJECT_VOUT
)
332 /* Look for the default filter configuration */
333 p_vout
->psz_filter_chain
=
334 var_CreateGetStringCommand( p_vout
, "vout-filter" );
336 /* Apply video filter2 objects on the first vout */
338 var_CreateGetStringCommand( p_vout
, "video-filter" );
342 /* continue the parent's filter chain */
345 /* Ugly hack to jump to our configuration chain */
346 p_vout
->psz_filter_chain
347 = ((vout_thread_t
*)p_parent
)->psz_filter_chain
;
348 p_vout
->psz_filter_chain
349 = config_ChainCreate( &psz_tmp
, &p_cfg
, p_vout
->psz_filter_chain
);
350 config_ChainDestroy( p_cfg
);
353 /* Create a video filter2 var ... but don't inherit values */
354 var_Create( p_vout
, "video-filter",
355 VLC_VAR_STRING
| VLC_VAR_ISCOMMAND
);
356 p_vout
->psz_vf2
= var_GetString( p_vout
, "video-filter" );
359 var_AddCallback( p_vout
, "video-filter", VideoFilter2Callback
, NULL
);
360 p_vout
->p_vf2_chain
= filter_chain_New( p_vout
, "video filter2",
361 false, video_filter_buffer_allocation_init
, NULL
, p_vout
);
363 /* Choose the video output module */
364 if( !p_vout
->psz_filter_chain
|| !*p_vout
->psz_filter_chain
)
366 var_Create( p_vout
, "vout", VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
367 var_Get( p_vout
, "vout", &val
);
368 psz_parser
= val
.psz_string
;
372 psz_parser
= strdup( p_vout
->psz_filter_chain
);
375 /* Create the vout thread */
376 char* psz_tmp
= config_ChainCreate( &psz_name
, &p_cfg
, psz_parser
);
379 p_vout
->p_cfg
= p_cfg
;
380 p_vout
->p_module
= module_Need( p_vout
,
381 ( p_vout
->psz_filter_chain
&& *p_vout
->psz_filter_chain
) ?
382 "video filter" : "video output", psz_name
, p_vout
->psz_filter_chain
&& *p_vout
->psz_filter_chain
);
385 if( p_vout
->p_module
== NULL
)
387 msg_Err( p_vout
, "no suitable vout module" );
388 // FIXME it's ugly but that's exactly the function that need to be called.
390 vlc_object_detach( p_vout
);
391 vlc_object_release( p_vout
);
395 /* Create a few object variables for interface interaction */
396 var_Create( p_vout
, "deinterlace", VLC_VAR_STRING
| VLC_VAR_HASCHOICE
);
397 text
.psz_string
= _("Deinterlace");
398 var_Change( p_vout
, "deinterlace", VLC_VAR_SETTEXT
, &text
, NULL
);
399 val
.psz_string
= (char *)""; text
.psz_string
= _("Disable");
400 var_Change( p_vout
, "deinterlace", VLC_VAR_ADDCHOICE
, &val
, &text
);
401 val
.psz_string
= (char *)"discard"; text
.psz_string
= _("Discard");
402 var_Change( p_vout
, "deinterlace", VLC_VAR_ADDCHOICE
, &val
, &text
);
403 val
.psz_string
= (char *)"blend"; text
.psz_string
= _("Blend");
404 var_Change( p_vout
, "deinterlace", VLC_VAR_ADDCHOICE
, &val
, &text
);
405 val
.psz_string
= (char *)"mean"; text
.psz_string
= _("Mean");
406 var_Change( p_vout
, "deinterlace", VLC_VAR_ADDCHOICE
, &val
, &text
);
407 val
.psz_string
= (char *)"bob"; text
.psz_string
= _("Bob");
408 var_Change( p_vout
, "deinterlace", VLC_VAR_ADDCHOICE
, &val
, &text
);
409 val
.psz_string
= (char *)"linear"; text
.psz_string
= _("Linear");
410 var_Change( p_vout
, "deinterlace", VLC_VAR_ADDCHOICE
, &val
, &text
);
411 val
.psz_string
= (char *)"x"; text
.psz_string
= (char *)"X";
412 var_Change( p_vout
, "deinterlace", VLC_VAR_ADDCHOICE
, &val
, &text
);
414 if( var_Get( p_vout
, "deinterlace-mode", &val
) == VLC_SUCCESS
)
416 var_Set( p_vout
, "deinterlace", val
);
417 free( val
.psz_string
);
419 var_AddCallback( p_vout
, "deinterlace", DeinterlaceCallback
, NULL
);
421 var_Create( p_vout
, "vout-filter", VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
422 text
.psz_string
= _("Filters");
423 var_Change( p_vout
, "vout-filter", VLC_VAR_SETTEXT
, &text
, NULL
);
424 var_AddCallback( p_vout
, "vout-filter", FilterCallback
, NULL
);
426 /* Calculate delay created by internal caching */
427 p_input_thread
= (input_thread_t
*)vlc_object_find( p_vout
,
428 VLC_OBJECT_INPUT
, FIND_ANYWHERE
);
431 p_vout
->i_pts_delay
= p_input_thread
->i_pts_delay
;
432 vlc_object_release( p_input_thread
);
436 p_vout
->i_pts_delay
= DEFAULT_PTS_DELAY
;
439 if( vlc_thread_create( p_vout
, "video output", RunThread
,
440 VLC_THREAD_PRIORITY_OUTPUT
, true ) )
442 module_Unneed( p_vout
, p_vout
->p_module
);
443 vlc_object_release( p_vout
);
447 vlc_object_set_destructor( p_vout
, vout_Destructor
);
449 if( p_vout
->b_error
)
451 msg_Err( p_vout
, "video output creation failed" );
452 vout_CloseAndRelease( p_vout
);
459 /*****************************************************************************
460 * vout_Close: Close a vout created by vout_Create.
461 *****************************************************************************
462 * You HAVE to call it on vout created by vout_Create before vlc_object_release.
463 * You should NEVER call it on vout not obtained though vout_Create
464 * (like with vout_Request or vlc_object_find.)
465 * You can use vout_CloseAndRelease() as a convenient method.
466 *****************************************************************************/
467 void vout_Close( vout_thread_t
*p_vout
)
471 vlc_object_kill( p_vout
);
472 vlc_thread_join( p_vout
);
473 module_Unneed( p_vout
, p_vout
->p_module
);
474 p_vout
->p_module
= NULL
;
478 static void vout_Destructor( vlc_object_t
* p_this
)
480 vout_thread_t
*p_vout
= (vout_thread_t
*)p_this
;
482 /* Make sure the vout was stopped first */
483 assert( !p_vout
->p_module
);
485 /* Destroy the locks */
486 vlc_mutex_destroy( &p_vout
->picture_lock
);
487 vlc_mutex_destroy( &p_vout
->change_lock
);
488 vlc_mutex_destroy( &p_vout
->vfilter_lock
);
490 free( p_vout
->psz_filter_chain
);
492 config_ChainDestroy( p_vout
->p_cfg
);
495 vout_thread_t
*p_another_vout
;
497 /* This is a dirty hack mostly for Linux, where there is no way to get the
498 * GUI back if you closed it while playing video. This is solved in
499 * Mac OS X, where we have this novelty called menubar, that will always
500 * allow you access to the applications main functionality. They should try
501 * that on linux sometime. */
502 p_another_vout
= vlc_object_find( p_this
->p_libvlc
,
503 VLC_OBJECT_VOUT
, FIND_ANYWHERE
);
504 if( p_another_vout
== NULL
)
505 var_SetBool( p_this
->p_libvlc
, "intf-show", true );
507 vlc_object_release( p_another_vout
);
511 /*****************************************************************************
512 * InitThread: initialize video output thread
513 *****************************************************************************
514 * This function is called from RunThread and performs the second step of the
515 * initialization. It returns 0 on success. Note that the thread's flag are not
516 * modified inside this function.
517 * XXX You have to enter it with change_lock taken.
518 *****************************************************************************/
519 static int ChromaCreate( vout_thread_t
*p_vout
);
520 static void ChromaDestroy( vout_thread_t
*p_vout
);
521 static void DropPicture( vout_thread_t
*p_vout
, picture_t
*p_picture
);
523 static int InitThread( vout_thread_t
*p_vout
)
525 int i
, i_aspect_x
, i_aspect_y
;
531 /* Initialize output method, it allocates direct buffers for us */
532 if( p_vout
->pf_init( p_vout
) )
535 if( !I_OUTPUTPICTURES
)
537 msg_Err( p_vout
, "plugin was unable to allocate at least "
538 "one direct buffer" );
539 p_vout
->pf_end( p_vout
);
543 if( I_OUTPUTPICTURES
> VOUT_MAX_PICTURES
)
545 msg_Err( p_vout
, "plugin allocated too many direct buffers, "
546 "our internal buffers must have overflown." );
547 p_vout
->pf_end( p_vout
);
551 msg_Dbg( p_vout
, "got %i direct buffer(s)", I_OUTPUTPICTURES
);
553 AspectRatio( p_vout
->fmt_render
.i_aspect
, &i_aspect_x
, &i_aspect_y
);
555 msg_Dbg( p_vout
, "picture in %ix%i (%i,%i,%ix%i), "
556 "chroma %4.4s, ar %i:%i, sar %i:%i",
557 p_vout
->fmt_render
.i_width
, p_vout
->fmt_render
.i_height
,
558 p_vout
->fmt_render
.i_x_offset
, p_vout
->fmt_render
.i_y_offset
,
559 p_vout
->fmt_render
.i_visible_width
,
560 p_vout
->fmt_render
.i_visible_height
,
561 (char*)&p_vout
->fmt_render
.i_chroma
,
562 i_aspect_x
, i_aspect_y
,
563 p_vout
->fmt_render
.i_sar_num
, p_vout
->fmt_render
.i_sar_den
);
565 AspectRatio( p_vout
->fmt_in
.i_aspect
, &i_aspect_x
, &i_aspect_y
);
567 msg_Dbg( p_vout
, "picture user %ix%i (%i,%i,%ix%i), "
568 "chroma %4.4s, ar %i:%i, sar %i:%i",
569 p_vout
->fmt_in
.i_width
, p_vout
->fmt_in
.i_height
,
570 p_vout
->fmt_in
.i_x_offset
, p_vout
->fmt_in
.i_y_offset
,
571 p_vout
->fmt_in
.i_visible_width
,
572 p_vout
->fmt_in
.i_visible_height
,
573 (char*)&p_vout
->fmt_in
.i_chroma
,
574 i_aspect_x
, i_aspect_y
,
575 p_vout
->fmt_in
.i_sar_num
, p_vout
->fmt_in
.i_sar_den
);
577 if( !p_vout
->fmt_out
.i_width
|| !p_vout
->fmt_out
.i_height
)
579 p_vout
->fmt_out
.i_width
= p_vout
->fmt_out
.i_visible_width
=
580 p_vout
->output
.i_width
;
581 p_vout
->fmt_out
.i_height
= p_vout
->fmt_out
.i_visible_height
=
582 p_vout
->output
.i_height
;
583 p_vout
->fmt_out
.i_x_offset
= p_vout
->fmt_out
.i_y_offset
= 0;
585 p_vout
->fmt_out
.i_aspect
= p_vout
->output
.i_aspect
;
586 p_vout
->fmt_out
.i_chroma
= p_vout
->output
.i_chroma
;
588 if( !p_vout
->fmt_out
.i_sar_num
|| !p_vout
->fmt_out
.i_sar_num
)
590 p_vout
->fmt_out
.i_sar_num
= p_vout
->fmt_out
.i_aspect
*
591 p_vout
->fmt_out
.i_height
;
592 p_vout
->fmt_out
.i_sar_den
= VOUT_ASPECT_FACTOR
*
593 p_vout
->fmt_out
.i_width
;
596 vlc_ureduce( &p_vout
->fmt_out
.i_sar_num
, &p_vout
->fmt_out
.i_sar_den
,
597 p_vout
->fmt_out
.i_sar_num
, p_vout
->fmt_out
.i_sar_den
, 0 );
599 AspectRatio( p_vout
->fmt_out
.i_aspect
, &i_aspect_x
, &i_aspect_y
);
601 msg_Dbg( p_vout
, "picture out %ix%i (%i,%i,%ix%i), "
602 "chroma %4.4s, ar %i:%i, sar %i:%i",
603 p_vout
->fmt_out
.i_width
, p_vout
->fmt_out
.i_height
,
604 p_vout
->fmt_out
.i_x_offset
, p_vout
->fmt_out
.i_y_offset
,
605 p_vout
->fmt_out
.i_visible_width
,
606 p_vout
->fmt_out
.i_visible_height
,
607 (char*)&p_vout
->fmt_out
.i_chroma
,
608 i_aspect_x
, i_aspect_y
,
609 p_vout
->fmt_out
.i_sar_num
, p_vout
->fmt_out
.i_sar_den
);
611 /* FIXME removed the need of both fmt_* and heap infos */
612 /* Calculate shifts from system-updated masks */
613 PictureHeapFixRgb( &p_vout
->render
);
614 VideoFormatImportRgb( &p_vout
->fmt_render
, &p_vout
->render
);
616 PictureHeapFixRgb( &p_vout
->output
);
617 VideoFormatImportRgb( &p_vout
->fmt_out
, &p_vout
->output
);
619 /* Check whether we managed to create direct buffers similar to
620 * the render buffers, ie same size and chroma */
621 if( ( p_vout
->output
.i_width
== p_vout
->render
.i_width
)
622 && ( p_vout
->output
.i_height
== p_vout
->render
.i_height
)
623 && ( vout_ChromaCmp( p_vout
->output
.i_chroma
, p_vout
->render
.i_chroma
) ) )
625 /* Cool ! We have direct buffers, we can ask the decoder to
626 * directly decode into them ! Map the first render buffers to
627 * the first direct buffers, but keep the first direct buffer
628 * for memcpy operations */
629 p_vout
->b_direct
= 1;
631 for( i
= 1; i
< VOUT_MAX_PICTURES
; i
++ )
633 if( p_vout
->p_picture
[ i
].i_type
!= DIRECT_PICTURE
&&
634 I_RENDERPICTURES
>= VOUT_MIN_DIRECT_PICTURES
- 1 &&
635 p_vout
->p_picture
[ i
- 1 ].i_type
== DIRECT_PICTURE
)
637 /* We have enough direct buffers so there's no need to
638 * try to use system memory buffers. */
641 PP_RENDERPICTURE
[ I_RENDERPICTURES
] = &p_vout
->p_picture
[ i
];
645 msg_Dbg( p_vout
, "direct render, mapping "
646 "render pictures 0-%i to system pictures 1-%i",
647 VOUT_MAX_PICTURES
- 2, VOUT_MAX_PICTURES
- 1 );
651 /* Rats... Something is wrong here, we could not find an output
652 * plugin able to directly render what we decode. See if we can
653 * find a chroma plugin to do the conversion */
654 p_vout
->b_direct
= 0;
656 if( ChromaCreate( p_vout
) )
658 p_vout
->pf_end( p_vout
);
662 msg_Dbg( p_vout
, "indirect render, mapping "
663 "render pictures 0-%i to system pictures %i-%i",
664 VOUT_MAX_PICTURES
- 1, I_OUTPUTPICTURES
,
665 I_OUTPUTPICTURES
+ VOUT_MAX_PICTURES
- 1 );
667 /* Append render buffers after the direct buffers */
668 for( i
= I_OUTPUTPICTURES
; i
< 2 * VOUT_MAX_PICTURES
; i
++ )
670 PP_RENDERPICTURE
[ I_RENDERPICTURES
] = &p_vout
->p_picture
[ i
];
673 /* Check if we have enough render pictures */
674 if( I_RENDERPICTURES
== VOUT_MAX_PICTURES
)
679 /* Link pictures back to their heap */
680 for( i
= 0 ; i
< I_RENDERPICTURES
; i
++ )
682 PP_RENDERPICTURE
[ i
]->p_heap
= &p_vout
->render
;
685 for( i
= 0 ; i
< I_OUTPUTPICTURES
; i
++ )
687 PP_OUTPUTPICTURE
[ i
]->p_heap
= &p_vout
->output
;
693 /*****************************************************************************
694 * RunThread: video output thread
695 *****************************************************************************
696 * Video output thread. This function does only returns when the thread is
697 * terminated. It handles the pictures arriving in the video heap and the
698 * display device events.
699 *****************************************************************************/
700 static void* RunThread( vlc_object_t
*p_this
)
702 vout_thread_t
*p_vout
= (vout_thread_t
*)p_this
;
703 int i_index
; /* index in heap */
704 int i_idle_loops
= 0; /* loops without displaying a picture */
705 mtime_t current_date
; /* current date */
706 mtime_t display_date
; /* display date */
708 picture_t
* p_picture
; /* picture pointer */
709 picture_t
* p_last_picture
= NULL
; /* last picture */
710 picture_t
* p_directbuffer
; /* direct buffer to display */
712 subpicture_t
* p_subpic
= NULL
; /* subpicture pointer */
714 input_thread_t
*p_input
= NULL
; /* Parent input, if it exists */
719 int i_displayed
= 0, i_lost
= 0, i_loops
= 0;
720 int canc
= vlc_savecancel ();
725 vlc_mutex_lock( &p_vout
->change_lock
);
726 p_vout
->b_error
= InitThread( p_vout
);
728 var_Create( p_vout
, "drop-late-frames", VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
729 var_Get( p_vout
, "drop-late-frames", &val
);
730 b_drop_late
= val
.b_bool
;
732 /* signal the creation of the vout */
733 vlc_thread_ready( p_vout
);
735 if( p_vout
->b_error
)
738 vlc_mutex_unlock( &p_vout
->change_lock
);
739 vlc_restorecancel (canc
);
743 vlc_object_lock( p_vout
);
745 if( p_vout
->b_title_show
)
746 DisplayTitleOnOSD( p_vout
);
749 * Main loop - it is not executed if an error occurred during
752 while( vlc_object_alive( p_vout
) && !p_vout
->b_error
)
754 /* Initialize loop variables */
757 current_date
= mdate();
762 p_input
= vlc_object_find( p_vout
, VLC_OBJECT_INPUT
,
767 vlc_mutex_lock( &p_input
->p
->counters
.counters_lock
);
768 stats_UpdateInteger( p_vout
, p_input
->p
->counters
.p_lost_pictures
,
770 stats_UpdateInteger( p_vout
,
771 p_input
->p
->counters
.p_displayed_pictures
,
773 i_displayed
= i_lost
= 0;
774 vlc_mutex_unlock( &p_input
->p
->counters
.counters_lock
);
775 vlc_object_release( p_input
);
780 if( !(p_vout
->c_loops
% VOUT_STATS_NB_LOOPS
) )
782 msg_Dbg( p_vout
, "picture heap: %d/%d",
783 I_RENDERPICTURES
, p_vout
->i_heap_size
);
788 * Find the picture to display (the one with the earliest date).
789 * This operation does not need lock, since only READY_PICTUREs
791 for( i_index
= 0; i_index
< I_RENDERPICTURES
; i_index
++ )
793 if( (PP_RENDERPICTURE
[i_index
]->i_status
== READY_PICTURE
)
794 && ( (p_picture
== NULL
) ||
795 (PP_RENDERPICTURE
[i_index
]->date
< display_date
) ) )
797 p_picture
= PP_RENDERPICTURE
[i_index
];
798 display_date
= p_picture
->date
;
804 /* If we met the last picture, parse again to see whether there is
805 * a more appropriate one. */
806 if( p_picture
== p_last_picture
)
808 for( i_index
= 0; i_index
< I_RENDERPICTURES
; i_index
++ )
810 if( (PP_RENDERPICTURE
[i_index
]->i_status
== READY_PICTURE
)
811 && (PP_RENDERPICTURE
[i_index
] != p_last_picture
)
812 && ((p_picture
== p_last_picture
) ||
813 (PP_RENDERPICTURE
[i_index
]->date
< display_date
)) )
815 p_picture
= PP_RENDERPICTURE
[i_index
];
816 display_date
= p_picture
->date
;
821 /* If we found better than the last picture, destroy it */
822 if( p_last_picture
&& p_picture
!= p_last_picture
)
824 DropPicture( p_vout
, p_last_picture
);
825 p_last_picture
= NULL
;
828 /* Compute FPS rate */
829 p_vout
->p_fps_sample
[ p_vout
->c_fps_samples
++ % VOUT_FPS_SAMPLES
]
832 if( !p_picture
->b_force
&&
833 p_picture
!= p_last_picture
&&
834 display_date
< current_date
+ p_vout
->render_time
&&
837 /* Picture is late: it will be destroyed and the thread
838 * will directly choose the next picture */
839 DropPicture( p_vout
, p_picture
);
841 msg_Warn( p_vout
, "late picture skipped (%"PRId64
")",
842 current_date
- display_date
);
847 current_date
+ p_vout
->i_pts_delay
+ VOUT_BOGUS_DELAY
)
849 /* Picture is waaay too early: it will be destroyed */
850 DropPicture( p_vout
, p_picture
);
852 msg_Warn( p_vout
, "vout warning: early picture skipped "
853 "(%"PRId64
")", display_date
- current_date
854 - p_vout
->i_pts_delay
);
858 if( display_date
> current_date
+ VOUT_DISPLAY_DELAY
)
860 /* A picture is ready to be rendered, but its rendering date
861 * is far from the current one so the thread will perform an
862 * empty loop as if no picture were found. The picture state
867 else if( p_picture
== p_last_picture
)
869 /* We are asked to repeat the previous picture, but we first
870 * wait for a couple of idle loops */
871 if( i_idle_loops
< 4 )
878 /* We set the display date to something high, otherwise
879 * we'll have lots of problems with late pictures */
880 display_date
= current_date
+ p_vout
->render_time
;
885 if( p_picture
== NULL
)
892 p_picture
= filter_chain_VideoFilter( p_vout
->p_vf2_chain
,
896 if( p_picture
&& p_vout
->b_snapshot
)
898 p_vout
->b_snapshot
= false;
899 vout_Snapshot( p_vout
, p_picture
);
903 * Check for subpictures to display
905 if( display_date
> 0 )
909 p_input
= vlc_object_find( p_vout
, VLC_OBJECT_INPUT
,
912 p_subpic
= spu_SortSubpictures( p_vout
->p_spu
, display_date
,
913 p_input
? var_GetBool( p_input
, "state" ) == PAUSE_S
: false );
915 vlc_object_release( p_input
);
923 p_directbuffer
= vout_RenderPicture( p_vout
, p_picture
, p_subpic
);
926 * Call the plugin-specific rendering method if there is one
928 if( p_picture
!= NULL
&& p_directbuffer
!= NULL
&& p_vout
->pf_render
)
930 /* Render the direct buffer returned by vout_RenderPicture */
931 p_vout
->pf_render( p_vout
, p_directbuffer
);
937 if( display_date
!= 0 && p_directbuffer
!= NULL
)
939 mtime_t current_render_time
= mdate() - current_date
;
940 /* if render time is very large we don't include it in the mean */
941 if( current_render_time
< p_vout
->render_time
+
944 /* Store render time using a sliding mean weighting to
945 * current value in a 3 to 1 ratio*/
946 p_vout
->render_time
*= 3;
947 p_vout
->render_time
+= current_render_time
;
948 p_vout
->render_time
>>= 2;
952 /* Give back change lock */
953 vlc_mutex_unlock( &p_vout
->change_lock
);
955 vlc_object_unlock( p_vout
);
957 /* Sleep a while or until a given date */
958 if( display_date
!= 0 )
960 /* If there are filters in the chain, better give them the picture
962 if( !p_vout
->psz_filter_chain
|| !*p_vout
->psz_filter_chain
)
964 mwait( display_date
- VOUT_MWAIT_TOLERANCE
);
969 msleep( VOUT_IDLE_SLEEP
);
972 /* On awakening, take back lock and send immediately picture
974 vlc_object_lock( p_vout
);
975 /* Note: vlc_object_alive() could be false here, and we
977 vlc_mutex_lock( &p_vout
->change_lock
);
980 * Display the previously rendered picture
982 if( p_picture
!= NULL
&& p_directbuffer
!= NULL
)
984 /* Display the direct buffer returned by vout_RenderPicture */
985 if( p_vout
->pf_display
)
987 p_vout
->pf_display( p_vout
, p_directbuffer
);
990 /* Tell the vout this was the last picture and that it does not
991 * need to be forced anymore. */
992 p_last_picture
= p_picture
;
993 p_last_picture
->b_force
= 0;
996 if( p_picture
!= NULL
)
998 /* Reinitialize idle loop count */
1003 * Check events and manage thread
1005 if( p_vout
->pf_manage
&& p_vout
->pf_manage( p_vout
) )
1007 /* A fatal error occurred, and the thread must terminate
1008 * immediately, without displaying anything - setting b_error to 1
1009 * causes the immediate end of the main while() loop. */
1011 p_vout
->b_error
= 1;
1015 if( p_vout
->i_changes
& VOUT_SIZE_CHANGE
)
1017 /* this must only happen when the vout plugin is incapable of
1018 * rescaling the picture itself. In this case we need to destroy
1019 * the current picture buffers and recreate new ones with the right
1023 p_vout
->i_changes
&= ~VOUT_SIZE_CHANGE
;
1025 assert( !p_vout
->b_direct
);
1027 ChromaDestroy( p_vout
);
1029 vlc_mutex_lock( &p_vout
->picture_lock
);
1031 p_vout
->pf_end( p_vout
);
1033 for( i
= 0; i
< I_OUTPUTPICTURES
; i
++ )
1034 p_vout
->p_picture
[ i
].i_status
= FREE_PICTURE
;
1036 I_OUTPUTPICTURES
= 0;
1038 if( p_vout
->pf_init( p_vout
) )
1040 msg_Err( p_vout
, "cannot resize display" );
1041 /* FIXME: pf_end will be called again in EndThread() */
1042 p_vout
->b_error
= 1;
1045 vlc_mutex_unlock( &p_vout
->picture_lock
);
1047 /* Need to reinitialise the chroma plugin. Since we might need
1048 * resizing too and it's not sure that we already had it,
1049 * recreate the chroma plugin chain from scratch. */
1051 if( ChromaCreate( p_vout
) )
1053 msg_Err( p_vout
, "WOW THIS SUCKS BIG TIME!!!!!" );
1054 p_vout
->b_error
= 1;
1056 if( p_vout
->b_error
)
1060 if( p_vout
->i_changes
& VOUT_PICTURE_BUFFERS_CHANGE
)
1062 /* This happens when the picture buffers need to be recreated.
1063 * This is useful on multimonitor displays for instance.
1065 * Warning: This only works when the vout creates only 1 picture
1067 p_vout
->i_changes
&= ~VOUT_PICTURE_BUFFERS_CHANGE
;
1069 if( !p_vout
->b_direct
)
1070 ChromaDestroy( p_vout
);
1072 vlc_mutex_lock( &p_vout
->picture_lock
);
1074 p_vout
->pf_end( p_vout
);
1076 I_OUTPUTPICTURES
= I_RENDERPICTURES
= 0;
1078 p_vout
->b_error
= InitThread( p_vout
);
1079 if( p_vout
->b_error
)
1080 msg_Err( p_vout
, "InitThread after VOUT_PICTURE_BUFFERS_CHANGE failed\n" );
1082 vlc_mutex_unlock( &p_vout
->picture_lock
);
1084 if( p_vout
->b_error
)
1088 /* Check for "video filter2" changes */
1089 vlc_mutex_lock( &p_vout
->vfilter_lock
);
1090 if( p_vout
->psz_vf2
)
1094 es_format_Init( &fmt
, VIDEO_ES
, p_vout
->fmt_render
.i_chroma
);
1095 fmt
.video
= p_vout
->fmt_render
;
1096 filter_chain_Reset( p_vout
->p_vf2_chain
, &fmt
, &fmt
);
1098 if( filter_chain_AppendFromString( p_vout
->p_vf2_chain
,
1099 p_vout
->psz_vf2
) < 0 )
1100 msg_Err( p_vout
, "Video filter chain creation failed" );
1102 free( p_vout
->psz_vf2
);
1103 p_vout
->psz_vf2
= NULL
;
1105 vlc_mutex_unlock( &p_vout
->vfilter_lock
);
1111 vlc_object_release( p_input
);
1115 * Error loop - wait until the thread destruction is requested
1117 if( p_vout
->b_error
)
1118 ErrorThread( p_vout
);
1121 CleanThread( p_vout
);
1122 EndThread( p_vout
);
1123 vlc_mutex_unlock( &p_vout
->change_lock
);
1125 vlc_object_unlock( p_vout
);
1126 vlc_restorecancel (canc
);
1130 /*****************************************************************************
1131 * ErrorThread: RunThread() error loop
1132 *****************************************************************************
1133 * This function is called when an error occurred during thread main's loop.
1134 * The thread can still receive feed, but must be ready to terminate as soon
1136 *****************************************************************************/
1137 static void ErrorThread( vout_thread_t
*p_vout
)
1139 /* Wait until a `die' order */
1140 while( vlc_object_alive( p_vout
) )
1141 vlc_object_wait( p_vout
);
1144 /*****************************************************************************
1145 * CleanThread: clean up after InitThread
1146 *****************************************************************************
1147 * This function is called after a sucessful
1148 * initialization. It frees all resources allocated by InitThread.
1149 * XXX You have to enter it with change_lock taken.
1150 *****************************************************************************/
1151 static void CleanThread( vout_thread_t
*p_vout
)
1153 int i_index
; /* index in heap */
1155 if( !p_vout
->b_direct
)
1156 ChromaDestroy( p_vout
);
1158 /* Destroy all remaining pictures */
1159 for( i_index
= 0; i_index
< 2 * VOUT_MAX_PICTURES
+ 1; i_index
++ )
1161 if ( p_vout
->p_picture
[i_index
].i_type
== MEMORY_PICTURE
)
1163 free( p_vout
->p_picture
[i_index
].p_data_orig
);
1167 /* Destroy translation tables */
1168 if( !p_vout
->b_error
)
1169 p_vout
->pf_end( p_vout
);
1172 /*****************************************************************************
1173 * EndThread: thread destruction
1174 *****************************************************************************
1175 * This function is called when the thread ends.
1176 * It frees all resources not allocated by InitThread.
1177 * XXX You have to enter it with change_lock taken.
1178 *****************************************************************************/
1179 static void EndThread( vout_thread_t
*p_vout
)
1183 struct tms cpu_usage
;
1184 times( &cpu_usage
);
1186 msg_Dbg( p_vout
, "cpu usage (user: %d, system: %d)",
1187 cpu_usage
.tms_utime
, cpu_usage
.tms_stime
);
1191 /* FIXME does that function *really* need to be called inside the thread ? */
1193 /* Destroy subpicture unit */
1194 spu_Attach( p_vout
->p_spu
, VLC_OBJECT(p_vout
), false );
1195 spu_Destroy( p_vout
->p_spu
);
1197 /* Destroy the video filters2 */
1198 filter_chain_Delete( p_vout
->p_vf2_chain
);
1201 /* Thread helpers */
1202 static picture_t
*ChromaGetPicture( filter_t
*p_filter
)
1204 picture_t
*p_pic
= (picture_t
*)p_filter
->p_owner
;
1205 p_filter
->p_owner
= NULL
;
1209 static int ChromaCreate( vout_thread_t
*p_vout
)
1211 static const char typename
[] = "chroma";
1214 /* Choose the best module */
1215 p_chroma
= p_vout
->p_chroma
=
1216 vlc_custom_create( p_vout
, sizeof(filter_t
), VLC_OBJECT_GENERIC
,
1219 vlc_object_attach( p_chroma
, p_vout
);
1221 /* TODO: Set the fmt_in and fmt_out stuff here */
1222 p_chroma
->fmt_in
.video
= p_vout
->fmt_render
;
1223 p_chroma
->fmt_out
.video
= p_vout
->fmt_out
;
1224 VideoFormatImportRgb( &p_chroma
->fmt_in
.video
, &p_vout
->render
);
1225 VideoFormatImportRgb( &p_chroma
->fmt_out
.video
, &p_vout
->output
);
1227 p_chroma
->p_module
= module_Need( p_chroma
, "video filter2", NULL
, 0 );
1229 if( p_chroma
->p_module
== NULL
)
1231 msg_Err( p_vout
, "no chroma module for %4.4s to %4.4s i=%dx%d o=%dx%d",
1232 (char*)&p_vout
->render
.i_chroma
,
1233 (char*)&p_vout
->output
.i_chroma
,
1234 p_chroma
->fmt_in
.video
.i_width
, p_chroma
->fmt_in
.video
.i_height
,
1235 p_chroma
->fmt_out
.video
.i_width
, p_chroma
->fmt_out
.video
.i_height
1238 vlc_object_release( p_vout
->p_chroma
);
1239 p_vout
->p_chroma
= NULL
;
1240 return VLC_EGENERIC
;
1242 p_chroma
->pf_vout_buffer_new
= ChromaGetPicture
;
1246 static void ChromaDestroy( vout_thread_t
*p_vout
)
1248 assert( !p_vout
->b_direct
);
1250 if( !p_vout
->p_chroma
)
1253 module_Unneed( p_vout
->p_chroma
, p_vout
->p_chroma
->p_module
);
1254 vlc_object_release( p_vout
->p_chroma
);
1255 p_vout
->p_chroma
= NULL
;
1258 static void DropPicture( vout_thread_t
*p_vout
, picture_t
*p_picture
)
1260 vlc_mutex_lock( &p_vout
->picture_lock
);
1261 if( p_picture
->i_refcount
)
1263 /* Pretend we displayed the picture, but don't destroy
1264 * it since the decoder might still need it. */
1265 p_picture
->i_status
= DISPLAYED_PICTURE
;
1269 /* Destroy the picture without displaying it */
1270 p_picture
->i_status
= DESTROYED_PICTURE
;
1271 p_vout
->i_heap_size
--;
1273 vlc_mutex_unlock( &p_vout
->picture_lock
);
1278 /* following functions are local */
1280 static int ReduceHeight( int i_ratio
)
1282 int i_dummy
= VOUT_ASPECT_FACTOR
;
1290 /* VOUT_ASPECT_FACTOR is (2^7 * 3^3 * 5^3), we just check for 2, 3 and 5 */
1291 while( !(i_ratio
& 1) && !(i_dummy
& 1) )
1298 while( !(i_ratio
% 3) && !(i_dummy
% 3) )
1305 while( !(i_ratio
% 5) && !(i_dummy
% 5) )
1315 static void AspectRatio( int i_aspect
, int *i_aspect_x
, int *i_aspect_y
)
1317 unsigned int i_pgcd
= ReduceHeight( i_aspect
);
1318 *i_aspect_x
= i_aspect
/ i_pgcd
;
1319 *i_aspect_y
= VOUT_ASPECT_FACTOR
/ i_pgcd
;
1323 * This function copies all RGB informations from a picture_heap_t into
1326 static void VideoFormatImportRgb( video_format_t
*p_fmt
, const picture_heap_t
*p_heap
)
1328 p_fmt
->i_rmask
= p_heap
->i_rmask
;
1329 p_fmt
->i_gmask
= p_heap
->i_gmask
;
1330 p_fmt
->i_bmask
= p_heap
->i_bmask
;
1331 p_fmt
->i_rrshift
= p_heap
->i_rrshift
;
1332 p_fmt
->i_lrshift
= p_heap
->i_lrshift
;
1333 p_fmt
->i_rgshift
= p_heap
->i_rgshift
;
1334 p_fmt
->i_lgshift
= p_heap
->i_lgshift
;
1335 p_fmt
->i_rbshift
= p_heap
->i_rbshift
;
1336 p_fmt
->i_lbshift
= p_heap
->i_lbshift
;
1340 * This funtion copes all RGB informations from a video_format_t into
1343 static void VideoFormatExportRgb( const video_format_t
*p_fmt
, picture_heap_t
*p_heap
)
1345 p_heap
->i_rmask
= p_fmt
->i_rmask
;
1346 p_heap
->i_gmask
= p_fmt
->i_gmask
;
1347 p_heap
->i_bmask
= p_fmt
->i_bmask
;
1348 p_heap
->i_rrshift
= p_fmt
->i_rrshift
;
1349 p_heap
->i_lrshift
= p_fmt
->i_lrshift
;
1350 p_heap
->i_rgshift
= p_fmt
->i_rgshift
;
1351 p_heap
->i_lgshift
= p_fmt
->i_lgshift
;
1352 p_heap
->i_rbshift
= p_fmt
->i_rbshift
;
1353 p_heap
->i_lbshift
= p_fmt
->i_lbshift
;
1357 * This function computes rgb shifts from masks
1359 static void PictureHeapFixRgb( picture_heap_t
*p_heap
)
1364 fmt
.i_chroma
= p_heap
->i_chroma
;
1365 VideoFormatImportRgb( &fmt
, p_heap
);
1368 video_format_FixRgb( &fmt
);
1370 VideoFormatExportRgb( &fmt
, p_heap
);
1373 /*****************************************************************************
1374 * object variables callbacks: a bunch of object variables are used by the
1375 * interfaces to interact with the vout.
1376 *****************************************************************************/
1377 static int DeinterlaceCallback( vlc_object_t
*p_this
, char const *psz_cmd
,
1378 vlc_value_t oldval
, vlc_value_t newval
, void *p_data
)
1380 vout_thread_t
*p_vout
= (vout_thread_t
*)p_this
;
1381 input_thread_t
*p_input
;
1384 char *psz_mode
= newval
.psz_string
;
1385 char *psz_filter
, *psz_deinterlace
= NULL
;
1386 (void)psz_cmd
; (void)oldval
; (void)p_data
;
1388 var_Get( p_vout
, "vout-filter", &val
);
1389 psz_filter
= val
.psz_string
;
1390 if( psz_filter
) psz_deinterlace
= strstr( psz_filter
, "deinterlace" );
1392 if( !psz_mode
|| !*psz_mode
)
1394 if( psz_deinterlace
)
1396 char *psz_src
= psz_deinterlace
+ sizeof("deinterlace") - 1;
1397 if( psz_src
[0] == ':' ) psz_src
++;
1398 memmove( psz_deinterlace
, psz_src
, strlen(psz_src
) + 1 );
1401 else if( !psz_deinterlace
)
1403 psz_filter
= realloc( psz_filter
, strlen( psz_filter
) +
1404 sizeof(":deinterlace") );
1405 if( psz_filter
&& *psz_filter
) strcat( psz_filter
, ":" );
1406 strcat( psz_filter
, "deinterlace" );
1409 p_input
= (input_thread_t
*)vlc_object_find( p_this
, VLC_OBJECT_INPUT
,
1411 if( !p_input
) return VLC_EGENERIC
;
1413 if( psz_mode
&& *psz_mode
)
1415 /* Modify input as well because the vout might have to be restarted */
1416 val
.psz_string
= psz_mode
;
1417 var_Create( p_input
, "deinterlace-mode", VLC_VAR_STRING
);
1418 var_Set( p_input
, "deinterlace-mode", val
);
1420 vlc_object_release( p_input
);
1423 var_Set( p_vout
, "intf-change", val
);
1425 val
.psz_string
= psz_filter
;
1426 var_Set( p_vout
, "vout-filter", val
);
1432 static int FilterCallback( vlc_object_t
*p_this
, char const *psz_cmd
,
1433 vlc_value_t oldval
, vlc_value_t newval
, void *p_data
)
1435 vout_thread_t
*p_vout
= (vout_thread_t
*)p_this
;
1436 input_thread_t
*p_input
;
1438 (void)psz_cmd
; (void)oldval
; (void)p_data
;
1440 p_input
= (input_thread_t
*)vlc_object_find( p_this
, VLC_OBJECT_INPUT
,
1444 msg_Err( p_vout
, "Input not found" );
1445 return( VLC_EGENERIC
);
1449 var_Set( p_vout
, "intf-change", val
);
1451 /* Modify input as well because the vout might have to be restarted */
1452 val
.psz_string
= newval
.psz_string
;
1453 var_Create( p_input
, "vout-filter", VLC_VAR_STRING
);
1455 var_Set( p_input
, "vout-filter", val
);
1457 /* Now restart current video stream */
1458 input_Control( p_input
, INPUT_RESTART_ES
, -VIDEO_ES
);
1459 vlc_object_release( p_input
);
1464 /*****************************************************************************
1465 * Video Filter2 stuff
1466 *****************************************************************************/
1467 static int VideoFilter2Callback( vlc_object_t
*p_this
, char const *psz_cmd
,
1468 vlc_value_t oldval
, vlc_value_t newval
, void *p_data
)
1470 vout_thread_t
*p_vout
= (vout_thread_t
*)p_this
;
1471 (void)psz_cmd
; (void)oldval
; (void)p_data
;
1473 vlc_mutex_lock( &p_vout
->vfilter_lock
);
1474 p_vout
->psz_vf2
= strdup( newval
.psz_string
);
1475 vlc_mutex_unlock( &p_vout
->vfilter_lock
);
1480 static void DisplayTitleOnOSD( vout_thread_t
*p_vout
)
1482 input_thread_t
*p_input
;
1483 mtime_t i_now
, i_stop
;
1485 if( !config_GetInt( p_vout
, "osd" ) ) return;
1487 p_input
= (input_thread_t
*)vlc_object_find( p_vout
,
1488 VLC_OBJECT_INPUT
, FIND_ANYWHERE
);
1492 i_stop
= i_now
+ (mtime_t
)(p_vout
->i_title_timeout
* 1000);
1493 char *psz_nowplaying
=
1494 input_item_GetNowPlaying( input_GetItem( p_input
) );
1495 char *psz_artist
= input_item_GetArtist( input_GetItem( p_input
) );
1496 char *psz_name
= input_item_GetTitle( input_GetItem( p_input
) );
1497 if( EMPTY_STR( psz_name
) )
1500 psz_name
= input_item_GetName( input_GetItem( p_input
) );
1502 if( !EMPTY_STR( psz_nowplaying
) )
1504 vout_ShowTextAbsolute( p_vout
, DEFAULT_CHAN
,
1505 psz_nowplaying
, NULL
,
1506 p_vout
->i_title_position
,
1507 30 + p_vout
->fmt_in
.i_width
1508 - p_vout
->fmt_in
.i_visible_width
1509 - p_vout
->fmt_in
.i_x_offset
,
1510 20 + p_vout
->fmt_in
.i_y_offset
,
1513 else if( !EMPTY_STR( psz_artist
) )
1515 char *psz_string
= NULL
;
1516 if( asprintf( &psz_string
, "%s - %s", psz_name
, psz_artist
) != -1 )
1518 vout_ShowTextAbsolute( p_vout
, DEFAULT_CHAN
,
1520 p_vout
->i_title_position
,
1521 30 + p_vout
->fmt_in
.i_width
1522 - p_vout
->fmt_in
.i_visible_width
1523 - p_vout
->fmt_in
.i_x_offset
,
1524 20 + p_vout
->fmt_in
.i_y_offset
,
1531 vout_ShowTextAbsolute( p_vout
, DEFAULT_CHAN
,
1533 p_vout
->i_title_position
,
1534 30 + p_vout
->fmt_in
.i_width
1535 - p_vout
->fmt_in
.i_visible_width
1536 - p_vout
->fmt_in
.i_x_offset
,
1537 20 + p_vout
->fmt_in
.i_y_offset
,
1540 vlc_object_release( p_input
);
1543 free( psz_nowplaying
);