1 /*****************************************************************************
2 * vout_intf.c : video output interface
3 *****************************************************************************
4 * Copyright (C) 2000-2006 the VideoLAN team
7 * Authors: Gildas Bazin <gbazin@videolan.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
27 #include <stdlib.h> /* free() */
31 #include <vlc_block.h>
33 #include "vlc_video.h"
34 #include "video_output.h"
35 #include "vlc_image.h"
40 /*****************************************************************************
42 *****************************************************************************/
43 static void InitWindowSize( vout_thread_t *, unsigned *, unsigned * );
45 /* Object variables callbacks */
46 static int ZoomCallback( vlc_object_t *, char const *,
47 vlc_value_t, vlc_value_t, void * );
48 static int CropCallback( vlc_object_t *, char const *,
49 vlc_value_t, vlc_value_t, void * );
50 static int AspectCallback( vlc_object_t *, char const *,
51 vlc_value_t, vlc_value_t, void * );
52 static int OnTopCallback( vlc_object_t *, char const *,
53 vlc_value_t, vlc_value_t, void * );
54 static int FullscreenCallback( vlc_object_t *, char const *,
55 vlc_value_t, vlc_value_t, void * );
56 static int SnapshotCallback( vlc_object_t *, char const *,
57 vlc_value_t, vlc_value_t, void * );
59 /*****************************************************************************
60 * vout_RequestWindow: Create/Get a video window if possible.
61 *****************************************************************************
62 * This function looks for the main interface and tries to request
63 * a new video window. If it fails then the vout will still need to create the
65 *****************************************************************************/
66 void *vout_RequestWindow( vout_thread_t *p_vout,
67 int *pi_x_hint, int *pi_y_hint,
68 unsigned int *pi_width_hint,
69 unsigned int *pi_height_hint )
71 intf_thread_t *p_intf = NULL;
78 if( !var_Type( p_vout, "aspect-ratio" ) ) vout_IntfInit( p_vout );
80 /* Get requested coordinates */
81 var_Get( p_vout, "video-x", &val );
82 *pi_x_hint = val.i_int ;
83 var_Get( p_vout, "video-y", &val );
84 *pi_y_hint = val.i_int;
86 *pi_width_hint = p_vout->i_window_width;
87 *pi_height_hint = p_vout->i_window_height;
89 /* Check whether someone provided us with a window ID */
90 var_Get( p_vout->p_vlc, "drawable", &val );
91 if( val.i_int ) return (void *)val.i_int;
93 /* Find if the main interface supports embedding */
94 p_list = vlc_list_find( p_vout, VLC_OBJECT_INTF, FIND_ANYWHERE );
95 if( !p_list ) return NULL;
97 for( i = 0; i < p_list->i_count; i++ )
99 p_intf = (intf_thread_t *)p_list->p_values[i].p_object;
100 if( p_intf->b_block && p_intf->pf_request_window ) break;
106 vlc_list_release( p_list );
110 vlc_object_yield( p_intf );
111 vlc_list_release( p_list );
113 p_window = p_intf->pf_request_window( p_intf, p_vout, pi_x_hint, pi_y_hint,
114 pi_width_hint, pi_height_hint );
116 if( !p_window ) vlc_object_release( p_intf );
117 else p_vout->p_parent_intf = p_intf;
122 void vout_ReleaseWindow( vout_thread_t *p_vout, void *p_window )
124 intf_thread_t *p_intf = p_vout->p_parent_intf;
126 if( !p_intf ) return;
128 vlc_mutex_lock( &p_intf->object_lock );
131 vlc_mutex_unlock( &p_intf->object_lock );
135 if( !p_intf->pf_release_window )
137 msg_Err( p_vout, "no pf_release_window");
138 vlc_mutex_unlock( &p_intf->object_lock );
139 vlc_object_release( p_intf );
143 p_intf->pf_release_window( p_intf, p_window );
145 p_vout->p_parent_intf = NULL;
146 vlc_mutex_unlock( &p_intf->object_lock );
147 vlc_object_release( p_intf );
150 int vout_ControlWindow( vout_thread_t *p_vout, void *p_window,
151 int i_query, va_list args )
153 intf_thread_t *p_intf = p_vout->p_parent_intf;
156 if( !p_intf ) return VLC_EGENERIC;
158 vlc_mutex_lock( &p_intf->object_lock );
161 vlc_mutex_unlock( &p_intf->object_lock );
165 if( !p_intf->pf_control_window )
167 msg_Err( p_vout, "no pf_control_window");
168 vlc_mutex_unlock( &p_intf->object_lock );
172 i_ret = p_intf->pf_control_window( p_intf, p_window, i_query, args );
173 vlc_mutex_unlock( &p_intf->object_lock );
177 /*****************************************************************************
178 * vout_IntfInit: called during the vout creation to initialise misc things.
179 *****************************************************************************/
180 void vout_IntfInit( vout_thread_t *p_vout )
182 vlc_value_t val, text, old_val;
183 vlc_bool_t b_force_par = VLC_FALSE;
186 /* Create a few object variables we'll need later on */
187 var_Create( p_vout, "snapshot-path", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
188 var_Create( p_vout, "snapshot-format", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
189 var_Create( p_vout, "snapshot-preview", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
190 var_Create( p_vout, "width", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
191 var_Create( p_vout, "height", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
192 var_Create( p_vout, "align", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
193 var_Get( p_vout, "align", &val );
194 p_vout->i_alignment = val.i_int;
196 var_Create( p_vout, "video-x", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
197 var_Create( p_vout, "video-y", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
199 /* Zoom object var */
200 var_Create( p_vout, "zoom", VLC_VAR_FLOAT | VLC_VAR_ISCOMMAND |
201 VLC_VAR_HASCHOICE | VLC_VAR_DOINHERIT );
203 text.psz_string = _("Zoom");
204 var_Change( p_vout, "zoom", VLC_VAR_SETTEXT, &text, NULL );
206 var_Get( p_vout, "zoom", &old_val );
207 if( old_val.f_float == 0.25 ||
208 old_val.f_float == 0.5 ||
209 old_val.f_float == 1 ||
210 old_val.f_float == 2 )
212 var_Change( p_vout, "zoom", VLC_VAR_DELCHOICE, &old_val, NULL );
215 val.f_float = 0.25; text.psz_string = _("1:4 Quarter");
216 var_Change( p_vout, "zoom", VLC_VAR_ADDCHOICE, &val, &text );
217 val.f_float = 0.5; text.psz_string = _("1:2 Half");
218 var_Change( p_vout, "zoom", VLC_VAR_ADDCHOICE, &val, &text );
219 val.f_float = 1; text.psz_string = _("1:1 Original");
220 var_Change( p_vout, "zoom", VLC_VAR_ADDCHOICE, &val, &text );
221 val.f_float = 2; text.psz_string = _("2:1 Double");
222 var_Change( p_vout, "zoom", VLC_VAR_ADDCHOICE, &val, &text );
224 var_Set( p_vout, "zoom", old_val );
226 var_AddCallback( p_vout, "zoom", ZoomCallback, NULL );
228 /* Crop object var */
229 var_Create( p_vout, "crop", VLC_VAR_STRING |
230 VLC_VAR_HASCHOICE | VLC_VAR_DOINHERIT );
232 text.psz_string = _("Crop");
233 var_Change( p_vout, "crop", VLC_VAR_SETTEXT, &text, NULL );
236 var_Change( p_vout, "crop", VLC_VAR_DELCHOICE, &val, 0 );
237 val.psz_string = ""; text.psz_string = _("Default");
238 var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text );
239 val.psz_string = "1:1"; text.psz_string = "1:1";
240 var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text );
241 val.psz_string = "4:3"; text.psz_string = "4:3";
242 var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text );
243 val.psz_string = "16:9"; text.psz_string = "16:9";
244 var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text );
245 val.psz_string = "16:10"; text.psz_string = "16:10";
246 var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text );
247 val.psz_string = "221:100"; text.psz_string = "221:100";
248 var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text );
249 val.psz_string = "5:4"; text.psz_string = "5:4";
250 var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text );
252 /* Add custom crop ratios */
253 psz_buf = config_GetPsz( p_vout, "custom-crop-ratios" );
254 if( psz_buf && *psz_buf )
256 char *psz_cur = psz_buf;
258 while( psz_cur && *psz_cur )
260 psz_next = strchr( psz_cur, ',' );
266 val.psz_string = strdup( psz_cur );
267 text.psz_string = strdup( psz_cur );
268 var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text);
269 free( val.psz_string );
270 free( text.psz_string );
274 if( psz_buf ) free( psz_buf );
276 var_AddCallback( p_vout, "crop", CropCallback, NULL );
277 var_Get( p_vout, "crop", &old_val );
278 if( old_val.psz_string && *old_val.psz_string )
279 var_Change( p_vout, "crop", VLC_VAR_TRIGGER_CALLBACKS, 0, 0 );
280 if( old_val.psz_string ) free( old_val.psz_string );
282 /* Monitor pixel aspect-ratio */
283 var_Create( p_vout, "monitor-par", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
284 var_Get( p_vout, "monitor-par", &val );
285 if( val.psz_string && *val.psz_string )
287 char *psz_parser = strchr( val.psz_string, ':' );
288 unsigned int i_aspect_num = 0, i_aspect_den = 0;
292 i_aspect_num = strtol( val.psz_string, 0, 10 );
293 i_aspect_den = strtol( ++psz_parser, 0, 10 );
297 i_aspect = atof( val.psz_string );
298 vlc_ureduce( &i_aspect_num, &i_aspect_den,
299 i_aspect *VOUT_ASPECT_FACTOR, VOUT_ASPECT_FACTOR, 0 );
301 if( !i_aspect_num || !i_aspect_den ) i_aspect_num = i_aspect_den = 1;
303 p_vout->i_par_num = i_aspect_num;
304 p_vout->i_par_den = i_aspect_den;
306 vlc_ureduce( &p_vout->i_par_num, &p_vout->i_par_den,
307 p_vout->i_par_num, p_vout->i_par_den, 0 );
309 msg_Dbg( p_vout, "overriding monitor pixel aspect-ratio: %i:%i",
310 p_vout->i_par_num, p_vout->i_par_den );
311 b_force_par = VLC_TRUE;
313 if( val.psz_string ) free( val.psz_string );
315 /* Aspect-ratio object var */
316 var_Create( p_vout, "aspect-ratio", VLC_VAR_STRING |
317 VLC_VAR_HASCHOICE | VLC_VAR_DOINHERIT );
319 text.psz_string = _("Aspect-ratio");
320 var_Change( p_vout, "aspect-ratio", VLC_VAR_SETTEXT, &text, NULL );
323 var_Change( p_vout, "aspect-ratio", VLC_VAR_DELCHOICE, &val, 0 );
324 val.psz_string = ""; text.psz_string = _("Default");
325 var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
326 val.psz_string = "1:1"; text.psz_string = "1:1";
327 var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
328 val.psz_string = "4:3"; text.psz_string = "4:3";
329 var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
330 val.psz_string = "16:9"; text.psz_string = "16:9";
331 var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
332 val.psz_string = "16:10"; text.psz_string = "16:10";
333 var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
334 val.psz_string = "221:100"; text.psz_string = "221:100";
335 var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
336 val.psz_string = "5:4"; text.psz_string = "5:4";
337 var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
339 /* Add custom aspect ratios */
340 psz_buf = config_GetPsz( p_vout, "custom-aspect-ratios" );
341 if( psz_buf && *psz_buf )
343 char *psz_cur = psz_buf;
345 while( psz_cur && *psz_cur )
347 psz_next = strchr( psz_cur, ',' );
353 val.psz_string = strdup( psz_cur );
354 text.psz_string = strdup( psz_cur );
355 var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text);
356 free( val.psz_string );
357 free( text.psz_string );
361 if( psz_buf ) free( psz_buf );
363 var_AddCallback( p_vout, "aspect-ratio", AspectCallback, NULL );
364 var_Get( p_vout, "aspect-ratio", &old_val );
365 if( (old_val.psz_string && *old_val.psz_string) || b_force_par )
366 var_Change( p_vout, "aspect-ratio", VLC_VAR_TRIGGER_CALLBACKS, 0, 0 );
367 if( old_val.psz_string ) free( old_val.psz_string );
369 /* Initialize the dimensions of the video window */
370 InitWindowSize( p_vout, &p_vout->i_window_width,
371 &p_vout->i_window_height );
373 /* Add a variable to indicate if the window should be on top of others */
374 var_Create( p_vout, "video-on-top", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
375 text.psz_string = _("Always on top");
376 var_Change( p_vout, "video-on-top", VLC_VAR_SETTEXT, &text, NULL );
377 var_AddCallback( p_vout, "video-on-top", OnTopCallback, NULL );
379 /* Add a variable to indicate whether we want window decoration or not */
380 var_Create( p_vout, "video-deco", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
382 /* Add a fullscreen variable */
383 var_Create( p_vout, "fullscreen", VLC_VAR_BOOL );
384 text.psz_string = _("Fullscreen");
385 var_Change( p_vout, "fullscreen", VLC_VAR_SETTEXT, &text, NULL );
386 var_Change( p_vout, "fullscreen", VLC_VAR_INHERITVALUE, &val, NULL );
389 /* user requested fullscreen */
390 p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
392 var_AddCallback( p_vout, "fullscreen", FullscreenCallback, NULL );
394 /* Add a snapshot variable */
395 var_Create( p_vout, "video-snapshot", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
396 text.psz_string = _("Snapshot");
397 var_Change( p_vout, "video-snapshot", VLC_VAR_SETTEXT, &text, NULL );
398 var_AddCallback( p_vout, "video-snapshot", SnapshotCallback, NULL );
400 /* Mouse coordinates */
401 var_Create( p_vout, "mouse-x", VLC_VAR_INTEGER );
402 var_Create( p_vout, "mouse-y", VLC_VAR_INTEGER );
403 var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER );
404 var_Create( p_vout, "mouse-moved", VLC_VAR_BOOL );
405 var_Create( p_vout, "mouse-clicked", VLC_VAR_INTEGER );
407 var_Create( p_vout, "intf-change", VLC_VAR_BOOL );
408 val.b_bool = VLC_TRUE;
409 var_Set( p_vout, "intf-change", val );
412 /*****************************************************************************
413 * vout_Snapshot: generates a snapshot.
414 *****************************************************************************/
415 int vout_Snapshot( vout_thread_t *p_vout, picture_t *p_pic )
417 image_handler_t *p_image = image_HandlerCreate( p_vout );
418 video_format_t fmt_in = {0}, fmt_out = {0};
420 subpicture_t *p_subpic;
422 vlc_value_t val, format;
425 var_Get( p_vout, "snapshot-path", &val );
426 if( val.psz_string && !*val.psz_string )
428 free( val.psz_string );
432 /* Embedded snapshot : if snapshot-path == object:object-id, then
433 create a snapshot_t* and store it in
434 object(object-id)->p_private, then unlock and signal the
437 if( val.psz_string && !strncmp( val.psz_string, "object:", 7 ) )
440 vlc_object_t* p_dest;
442 snapshot_t *p_snapshot;
445 /* Destination object-id is following object: */
446 i_id = atoi( &val.psz_string[7] );
447 p_dest = ( vlc_object_t* )vlc_current_object( i_id );
450 msg_Err( p_vout, "Cannot find calling object" );
451 image_HandlerDelete( p_image );
454 /* Object must be locked. We will unlock it once we get the
455 snapshot and written it to p_private */
456 p_dest->p_private = NULL;
458 /* Save the snapshot to a memory zone */
459 fmt_in = p_vout->fmt_in;
460 fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
461 /* FIXME: should not be hardcoded. We should be able to
462 specify the snapshot size (snapshot-width and snapshot-height). */
463 fmt_out.i_width = 320;
464 fmt_out.i_height = 200;
465 fmt_out.i_chroma = VLC_FOURCC( 'p','n','g',' ' );
466 p_block = ( block_t* ) image_Write( p_image, p_pic, &fmt_in, &fmt_out );
469 msg_Err( p_vout, "Could not get snapshot" );
470 image_HandlerDelete( p_image );
471 vlc_cond_signal( &p_dest->object_wait );
472 vlc_mutex_unlock( &p_dest->object_lock );
473 vlc_object_release( p_dest );
477 /* Copy the p_block data to a snapshot structure */
478 /* FIXME: get the timestamp */
479 p_snapshot = ( snapshot_t* ) malloc( sizeof( snapshot_t ) );
482 block_Release( p_block );
483 image_HandlerDelete( p_image );
484 vlc_cond_signal( &p_dest->object_wait );
485 vlc_mutex_unlock( &p_dest->object_lock );
486 vlc_object_release( p_dest );
490 i_size = p_block->i_buffer;
492 p_snapshot->i_width = fmt_out.i_width;
493 p_snapshot->i_height = fmt_out.i_height;
494 p_snapshot->i_datasize = i_size;
495 p_snapshot->date = p_block->i_pts; /* FIXME ?? */
496 p_snapshot->p_data = ( char* ) malloc( i_size );
497 if( !p_snapshot->p_data )
499 block_Release( p_block );
501 image_HandlerDelete( p_image );
502 vlc_cond_signal( &p_dest->object_wait );
503 vlc_mutex_unlock( &p_dest->object_lock );
504 vlc_object_release( p_dest );
507 memcpy( p_snapshot->p_data, p_block->p_buffer, p_block->i_buffer );
509 p_dest->p_private = p_snapshot;
511 block_Release( p_block );
513 /* Unlock the object */
514 vlc_cond_signal( &p_dest->object_wait );
515 vlc_mutex_unlock( &p_dest->object_lock );
516 vlc_object_release( p_dest );
518 image_HandlerDelete( p_image );
523 #if defined(__APPLE__) || defined(SYS_BEOS)
524 if( !val.psz_string && p_vout->p_vlc->psz_homedir )
526 asprintf( &val.psz_string, "%s/Desktop",
527 p_vout->p_vlc->psz_homedir );
530 #elif defined(WIN32) && !defined(UNDER_CE)
531 if( !val.psz_string && p_vout->p_vlc->psz_homedir )
533 /* Get the My Pictures folder path */
535 char *p_mypicturesdir = NULL;
536 typedef HRESULT (WINAPI *SHGETFOLDERPATH)( HWND, int, HANDLE, DWORD,
538 #ifndef CSIDL_FLAG_CREATE
539 # define CSIDL_FLAG_CREATE 0x8000
541 #ifndef CSIDL_MYPICTURES
542 # define CSIDL_MYPICTURES 0x27
544 #ifndef SHGFP_TYPE_CURRENT
545 # define SHGFP_TYPE_CURRENT 0
548 HINSTANCE shfolder_dll;
549 SHGETFOLDERPATH SHGetFolderPath ;
551 /* load the shfolder dll to retrieve SHGetFolderPath */
552 if( ( shfolder_dll = LoadLibrary( _T("SHFolder.dll") ) ) != NULL )
554 SHGetFolderPath = (void *)GetProcAddress( shfolder_dll,
555 _T("SHGetFolderPathA") );
556 if( SHGetFolderPath != NULL )
558 p_mypicturesdir = (char *)malloc( MAX_PATH );
559 if( p_mypicturesdir )
562 if( S_OK != SHGetFolderPath( NULL,
563 CSIDL_MYPICTURES | CSIDL_FLAG_CREATE,
564 NULL, SHGFP_TYPE_CURRENT,
567 free( p_mypicturesdir );
568 p_mypicturesdir = NULL;
572 FreeLibrary( shfolder_dll );
575 if( p_mypicturesdir == NULL )
577 asprintf( &val.psz_string, "%s/" CONFIG_DIR,
578 p_vout->p_vlc->psz_homedir );
582 asprintf( &val.psz_string, p_mypicturesdir );
583 free( p_mypicturesdir );
588 if( !val.psz_string && p_vout->p_vlc->psz_homedir )
590 asprintf( &val.psz_string, "%s/" CONFIG_DIR,
591 p_vout->p_vlc->psz_homedir );
595 if( !val.psz_string )
597 msg_Err( p_vout, "no directory specified for snapshots" );
600 var_Get( p_vout, "snapshot-format", &format );
601 if( !format.psz_string || !*format.psz_string )
603 if( format.psz_string ) free( format.psz_string );
604 format.psz_string = strdup( "png" );
607 asprintf( &psz_filename, "%s/vlcsnap-%u.%s", val.psz_string,
608 (unsigned int)(p_pic->date / 100000) & 0xFFFFFF,
610 free( val.psz_string );
611 free( format.psz_string );
613 /* Save the snapshot */
614 fmt_in = p_vout->fmt_in;
615 fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
616 i_ret = image_WriteUrl( p_image, p_pic, &fmt_in, &fmt_out, psz_filename );
617 if( i_ret != VLC_SUCCESS )
619 msg_Err( p_vout, "could not create snapshot %s", psz_filename );
620 free( psz_filename );
621 image_HandlerDelete( p_image );
625 msg_Dbg( p_vout, "snapshot taken (%s)", psz_filename );
626 vout_OSDMessage( VLC_OBJECT( p_vout ), DEFAULT_CHAN,
627 _("%s"), psz_filename );
628 free( psz_filename );
630 if( var_GetBool( p_vout, "snapshot-preview" ) )
632 /* Inject a subpicture with the snapshot */
633 memset( &fmt_out, 0, sizeof(fmt_out) );
634 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
635 p_pif = image_Convert( p_image, p_pic, &fmt_in, &fmt_out );
636 image_HandlerDelete( p_image );
637 if( !p_pif ) return VLC_EGENERIC;
639 p_subpic = spu_CreateSubpicture( p_vout->p_spu );
640 if( p_subpic == NULL )
642 p_pif->pf_release( p_pif );
646 p_subpic->i_channel = 0;
647 p_subpic->i_start = mdate();
648 p_subpic->i_stop = mdate() + 4000000;
649 p_subpic->b_ephemer = VLC_TRUE;
650 p_subpic->b_fade = VLC_TRUE;
651 p_subpic->i_original_picture_width = p_vout->render.i_width * 4;
652 p_subpic->i_original_picture_height = p_vout->render.i_height * 4;
654 p_subpic->p_region = spu_CreateRegion( p_vout->p_spu, &fmt_out );
655 vout_CopyPicture( p_image->p_parent, &p_subpic->p_region->picture,
657 p_pif->pf_release( p_pif );
659 spu_DisplaySubpicture( p_vout->p_spu, p_subpic );
663 image_HandlerDelete( p_image );
669 /*****************************************************************************
670 * vout_ControlDefault: default methods for video output control.
671 *****************************************************************************/
672 int vout_vaControlDefault( vout_thread_t *p_vout, int i_query, va_list args )
678 if( p_vout->p_parent_intf )
680 vlc_object_release( p_vout->p_parent_intf );
681 p_vout->p_parent_intf = NULL;
687 p_vout->b_snapshot = VLC_TRUE;
692 msg_Dbg( p_vout, "control query not supported" );
697 /*****************************************************************************
698 * InitWindowSize: find the initial dimensions the video window should have.
699 *****************************************************************************
700 * This function will check the "width", "height" and "zoom" config options and
701 * will calculate the size that the video window should have.
702 *****************************************************************************/
703 static void InitWindowSize( vout_thread_t *p_vout, unsigned *pi_width,
704 unsigned *pi_height )
707 int i_width, i_height;
710 #define FP_FACTOR 1000 /* our fixed point factor */
712 var_Get( p_vout, "width", &val );
714 var_Get( p_vout, "height", &val );
715 i_height = val.i_int;
716 var_Get( p_vout, "zoom", &val );
717 ll_zoom = (uint64_t)( FP_FACTOR * val.f_float );
719 if( i_width > 0 && i_height > 0)
721 *pi_width = (int)( i_width * ll_zoom / FP_FACTOR );
722 *pi_height = (int)( i_height * ll_zoom / FP_FACTOR );
725 else if( i_width > 0 )
727 *pi_width = (int)( i_width * ll_zoom / FP_FACTOR );
728 *pi_height = (int)( p_vout->fmt_in.i_visible_height * ll_zoom *
729 p_vout->fmt_in.i_sar_den * i_width / p_vout->fmt_in.i_sar_num /
730 FP_FACTOR / p_vout->fmt_in.i_visible_width );
733 else if( i_height > 0 )
735 *pi_height = (int)( i_height * ll_zoom / FP_FACTOR );
736 *pi_width = (int)( p_vout->fmt_in.i_visible_width * ll_zoom *
737 p_vout->fmt_in.i_sar_num * i_height / p_vout->fmt_in.i_sar_den /
738 FP_FACTOR / p_vout->fmt_in.i_visible_height );
742 if( p_vout->fmt_in.i_sar_num >= p_vout->fmt_in.i_sar_den )
744 *pi_width = (int)( p_vout->fmt_in.i_visible_width * ll_zoom *
745 p_vout->fmt_in.i_sar_num / p_vout->fmt_in.i_sar_den / FP_FACTOR );
746 *pi_height = (int)( p_vout->fmt_in.i_visible_height * ll_zoom
751 *pi_width = (int)( p_vout->fmt_in.i_visible_width * ll_zoom
753 *pi_height = (int)( p_vout->fmt_in.i_visible_height * ll_zoom *
754 p_vout->fmt_in.i_sar_den / p_vout->fmt_in.i_sar_num / FP_FACTOR );
758 msg_Dbg( p_vout, "window size: %dx%d", p_vout->i_window_width,
759 p_vout->i_window_height );
764 /*****************************************************************************
765 * Object variables callbacks
766 *****************************************************************************/
767 static int ZoomCallback( vlc_object_t *p_this, char const *psz_cmd,
768 vlc_value_t oldval, vlc_value_t newval, void *p_data )
770 vout_thread_t *p_vout = (vout_thread_t *)p_this;
771 InitWindowSize( p_vout, &p_vout->i_window_width,
772 &p_vout->i_window_height );
773 vout_Control( p_vout, VOUT_SET_SIZE, p_vout->i_window_width,
774 p_vout->i_window_height );
778 static int CropCallback( vlc_object_t *p_this, char const *psz_cmd,
779 vlc_value_t oldval, vlc_value_t newval, void *p_data )
781 vout_thread_t *p_vout = (vout_thread_t *)p_this;
782 int64_t i_aspect_num, i_aspect_den;
783 unsigned int i_width, i_height;
785 char *psz_end, *psz_parser = strchr( newval.psz_string, ':' );
787 /* Restore defaults */
788 p_vout->fmt_in.i_x_offset = p_vout->fmt_render.i_x_offset;
789 p_vout->fmt_in.i_visible_width = p_vout->fmt_render.i_visible_width;
790 p_vout->fmt_in.i_y_offset = p_vout->fmt_render.i_y_offset;
791 p_vout->fmt_in.i_visible_height = p_vout->fmt_render.i_visible_height;
795 /* We're using the 3:4 syntax */
796 i_aspect_num = strtol( newval.psz_string, &psz_end, 10 );
797 if( psz_end == newval.psz_string || !i_aspect_num ) goto crop_end;
799 i_aspect_den = strtol( ++psz_parser, &psz_end, 10 );
800 if( psz_end == psz_parser || !i_aspect_den ) goto crop_end;
802 i_width = p_vout->fmt_in.i_sar_den*p_vout->fmt_render.i_visible_height *
803 i_aspect_num / i_aspect_den / p_vout->fmt_in.i_sar_num;
804 i_height = p_vout->fmt_render.i_visible_width*p_vout->fmt_in.i_sar_num *
805 i_aspect_den / i_aspect_num / p_vout->fmt_in.i_sar_den;
807 if( i_width < p_vout->fmt_render.i_visible_width )
809 p_vout->fmt_in.i_x_offset = p_vout->fmt_render.i_x_offset +
810 (p_vout->fmt_render.i_visible_width - i_width) / 2;
811 p_vout->fmt_in.i_visible_width = i_width;
815 p_vout->fmt_in.i_y_offset = p_vout->fmt_render.i_y_offset +
816 (p_vout->fmt_render.i_visible_height - i_height) / 2;
817 p_vout->fmt_in.i_visible_height = i_height;
822 psz_parser = strchr( newval.psz_string, 'x' );
825 /* Maybe we're using the <width>x<height>+<left>+<top> syntax */
826 unsigned int i_crop_width, i_crop_height, i_crop_top, i_crop_left;
828 i_crop_width = strtol( newval.psz_string, &psz_end, 10 );
829 if( psz_end != psz_parser ) goto crop_end;
831 psz_parser = strchr( ++psz_end, '+' );
832 i_crop_height = strtol( psz_end, &psz_end, 10 );
833 if( psz_end != psz_parser ) goto crop_end;
835 psz_parser = strchr( ++psz_end, '+' );
836 i_crop_left = strtol( psz_end, &psz_end, 10 );
837 if( psz_end != psz_parser ) goto crop_end;
839 i_crop_top = strtol( ++psz_end, &psz_end, 10 );
840 if( *psz_end != '\0' ) goto crop_end;
842 i_width = i_crop_width;
843 p_vout->fmt_in.i_visible_width = i_width;
845 i_height = i_crop_height;
846 p_vout->fmt_in.i_visible_height = i_height;
848 p_vout->fmt_in.i_x_offset = i_crop_left;
849 p_vout->fmt_in.i_y_offset = i_crop_top;
853 /* Maybe we're using the <left>+<top>+<right>+<bottom> syntax */
854 unsigned int i_crop_top, i_crop_left, i_crop_bottom, i_crop_right;
856 psz_parser = strchr( newval.psz_string, '+' );
857 i_crop_left = strtol( newval.psz_string, &psz_end, 10 );
858 if( psz_end != psz_parser ) goto crop_end;
860 psz_parser = strchr( ++psz_end, '+' );
861 i_crop_top = strtol( psz_end, &psz_end, 10 );
862 if( psz_end != psz_parser ) goto crop_end;
864 psz_parser = strchr( ++psz_end, '+' );
865 i_crop_right = strtol( psz_end, &psz_end, 10 );
866 if( psz_end != psz_parser ) goto crop_end;
868 i_crop_bottom = strtol( ++psz_end, &psz_end, 10 );
869 if( *psz_end != '\0' ) goto crop_end;
872 i_width = p_vout->fmt_render.i_visible_width
873 - i_crop_left - i_crop_right;
874 p_vout->fmt_in.i_visible_width = i_width;
876 i_height = p_vout->fmt_render.i_visible_height
877 - i_crop_top - i_crop_bottom;
878 p_vout->fmt_in.i_visible_height = i_height;
880 p_vout->fmt_in.i_x_offset = i_crop_left;
881 p_vout->fmt_in.i_y_offset = i_crop_top;
886 InitWindowSize( p_vout, &p_vout->i_window_width,
887 &p_vout->i_window_height );
889 p_vout->i_changes |= VOUT_CROP_CHANGE;
891 msg_Dbg( p_vout, "cropping picture %ix%i to %i,%i,%ix%i",
892 p_vout->fmt_in.i_width, p_vout->fmt_in.i_height,
893 p_vout->fmt_in.i_x_offset, p_vout->fmt_in.i_y_offset,
894 p_vout->fmt_in.i_visible_width,
895 p_vout->fmt_in.i_visible_height );
900 static int AspectCallback( vlc_object_t *p_this, char const *psz_cmd,
901 vlc_value_t oldval, vlc_value_t newval, void *p_data )
903 vout_thread_t *p_vout = (vout_thread_t *)p_this;
904 unsigned int i_aspect_num, i_aspect_den, i_sar_num, i_sar_den;
907 char *psz_end, *psz_parser = strchr( newval.psz_string, ':' );
909 /* Restore defaults */
910 p_vout->fmt_in.i_sar_num = p_vout->fmt_render.i_sar_num;
911 p_vout->fmt_in.i_sar_den = p_vout->fmt_render.i_sar_den;
912 p_vout->fmt_in.i_aspect = p_vout->fmt_render.i_aspect;
913 p_vout->render.i_aspect = p_vout->fmt_render.i_aspect;
915 if( !psz_parser ) goto aspect_end;
917 i_aspect_num = strtol( newval.psz_string, &psz_end, 10 );
918 if( psz_end == newval.psz_string || !i_aspect_num ) goto aspect_end;
920 i_aspect_den = strtol( ++psz_parser, &psz_end, 10 );
921 if( psz_end == psz_parser || !i_aspect_den ) goto aspect_end;
923 i_sar_num = i_aspect_num * p_vout->fmt_render.i_visible_height;
924 i_sar_den = i_aspect_den * p_vout->fmt_render.i_visible_width;
925 vlc_ureduce( &i_sar_num, &i_sar_den, i_sar_num, i_sar_den, 0 );
926 p_vout->fmt_in.i_sar_num = i_sar_num;
927 p_vout->fmt_in.i_sar_den = i_sar_den;
928 p_vout->fmt_in.i_aspect = i_aspect_num * VOUT_ASPECT_FACTOR / i_aspect_den;
929 p_vout->render.i_aspect = p_vout->fmt_in.i_aspect;
932 if( p_vout->i_par_num && p_vout->i_par_den )
934 p_vout->fmt_in.i_sar_num *= p_vout->i_par_den;
935 p_vout->fmt_in.i_sar_den *= p_vout->i_par_num;
936 p_vout->fmt_in.i_aspect = p_vout->fmt_in.i_aspect *
937 p_vout->i_par_den / p_vout->i_par_num;
938 p_vout->render.i_aspect = p_vout->fmt_in.i_aspect;
941 p_vout->i_changes |= VOUT_ASPECT_CHANGE;
943 vlc_ureduce( &i_aspect_num, &i_aspect_den,
944 p_vout->fmt_in.i_aspect, VOUT_ASPECT_FACTOR, 0 );
945 msg_Dbg( p_vout, "new aspect-ratio %i:%i, sample aspect-ratio %i:%i",
946 i_aspect_num, i_aspect_den,
947 p_vout->fmt_in.i_sar_num, p_vout->fmt_in.i_sar_den );
949 var_Get( p_vout, "crop", &val );
950 return CropCallback( p_this, 0, val, val, 0 );
955 static int OnTopCallback( vlc_object_t *p_this, char const *psz_cmd,
956 vlc_value_t oldval, vlc_value_t newval, void *p_data )
958 vout_thread_t *p_vout = (vout_thread_t *)p_this;
959 playlist_t *p_playlist;
960 vout_Control( p_vout, VOUT_SET_STAY_ON_TOP, newval.b_bool );
962 p_playlist = (playlist_t *)vlc_object_find( p_this, VLC_OBJECT_PLAYLIST,
966 /* Modify playlist as well because the vout might have to be restarted */
967 var_Create( p_playlist, "video-on-top", VLC_VAR_BOOL );
968 var_Set( p_playlist, "video-on-top", newval );
970 vlc_object_release( p_playlist );
975 static int FullscreenCallback( vlc_object_t *p_this, char const *psz_cmd,
976 vlc_value_t oldval, vlc_value_t newval, void *p_data )
978 vout_thread_t *p_vout = (vout_thread_t *)p_this;
979 playlist_t *p_playlist;
982 p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
984 p_playlist = (playlist_t *)vlc_object_find( p_this, VLC_OBJECT_PLAYLIST,
988 /* Modify playlist as well because the vout might have to be restarted */
989 var_Create( p_playlist, "fullscreen", VLC_VAR_BOOL );
990 var_Set( p_playlist, "fullscreen", newval );
992 vlc_object_release( p_playlist );
995 /* Disable "always on top" in fullscreen mode */
996 var_Get( p_vout, "video-on-top", &val );
997 if( newval.b_bool && val.b_bool )
999 val.b_bool = VLC_FALSE;
1000 vout_Control( p_vout, VOUT_SET_STAY_ON_TOP, val.b_bool );
1002 else if( !newval.b_bool && val.b_bool )
1004 vout_Control( p_vout, VOUT_SET_STAY_ON_TOP, val.b_bool );
1007 val.b_bool = VLC_TRUE;
1008 var_Set( p_vout, "intf-change", val );
1012 static int SnapshotCallback( vlc_object_t *p_this, char const *psz_cmd,
1013 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1015 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1016 vout_Control( p_vout, VOUT_SNAPSHOT );