]> git.sesse.net Git - vlc/blob - src/video_output/vout_intf.c
Enabling scaling activation/deactivation
[vlc] / src / video_output / vout_intf.c
1 /*****************************************************************************
2  * vout_intf.c : video output interface
3  *****************************************************************************
4  * Copyright (C) 2000-2007 the VideoLAN team
5  *
6  * Authors: Gildas Bazin <gbazin@videolan.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 /*****************************************************************************
24  * Preamble
25  *****************************************************************************/
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <vlc_common.h>
32
33 #include <stdio.h>
34 #include <stdlib.h>                                                /* free() */
35 #include <sys/types.h>                                          /* opendir() */
36 #include <sys/stat.h>
37 #include <dirent.h>                                             /* opendir() */
38 #include <assert.h>
39 #include <time.h>                                           /* strftime */
40
41 #include <vlc_interface.h>
42 #include <vlc_block.h>
43 #include <vlc_playlist.h>
44
45 #include <vlc_vout.h>
46 #include <vlc_window.h>
47 #include <vlc_image.h>
48 #include <vlc_osd.h>
49 #include <vlc_charset.h>
50
51 #include <vlc_strings.h>
52 #include <vlc_charset.h>
53 #include "../libvlc.h"
54 #include "vout_internal.h"
55
56 /*****************************************************************************
57  * Local prototypes
58  *****************************************************************************/
59 static void InitWindowSize( vout_thread_t *, unsigned *, unsigned * );
60
61 /* Object variables callbacks */
62 static int ZoomCallback( vlc_object_t *, char const *,
63                          vlc_value_t, vlc_value_t, void * );
64 static int CropCallback( vlc_object_t *, char const *,
65                          vlc_value_t, vlc_value_t, void * );
66 static int AspectCallback( vlc_object_t *, char const *,
67                            vlc_value_t, vlc_value_t, void * );
68 static int ScalingCallback( vlc_object_t *, char const *,
69                             vlc_value_t, vlc_value_t, void * );
70 static int OnTopCallback( vlc_object_t *, char const *,
71                           vlc_value_t, vlc_value_t, void * );
72 static int FullscreenCallback( vlc_object_t *, char const *,
73                                vlc_value_t, vlc_value_t, void * );
74 static int SnapshotCallback( vlc_object_t *, char const *,
75                              vlc_value_t, vlc_value_t, void * );
76
77 static int TitleShowCallback( vlc_object_t *, char const *,
78                               vlc_value_t, vlc_value_t, void * );
79 static int TitleTimeoutCallback( vlc_object_t *, char const *,
80                                  vlc_value_t, vlc_value_t, void * );
81 static int TitlePositionCallback( vlc_object_t *, char const *,
82                                   vlc_value_t, vlc_value_t, void * );
83
84 /**
85  * Creates a video output window.
86  * On Unix systems, this is an X11 drawable (handle).
87  * On Windows, this is a Win32 window (handle).
88  * Video output plugins are supposed to called this function and display the
89  * video within the resulting window, while in windowed mode.
90  *
91  * @param p_vout video output thread to create a window for
92  * @param pi_x_hint pointer to store the recommended horizontal position [OUT]
93  * @param pi_y_hint pointer to store the recommended vertical position [OUT]
94  * @param pi_width_hint pointer to store the recommended width [OUT]
95  * @param pi_height_hint pointer to store the recommended height [OUT]
96  *
97  * @return a vout_window_t object, or NULL in case of failure.
98  * The window is released with vout_ReleaseWindow().
99  */
100 vout_window_t *vout_RequestWindow( vout_thread_t *p_vout,
101                           int *pi_x_hint, int *pi_y_hint,
102                           unsigned int *pi_width_hint,
103                           unsigned int *pi_height_hint )
104 {
105     /* Get requested coordinates */
106     *pi_x_hint = var_GetInteger( p_vout, "video-x" );
107     *pi_y_hint = var_GetInteger( p_vout, "video-y" );
108
109     *pi_width_hint = p_vout->i_window_width;
110     *pi_height_hint = p_vout->i_window_height;
111
112     /* Check whether someone provided us with a window ID */
113     int drawable = var_CreateGetInteger( p_vout, "drawable" );
114     if( drawable ) return (void *)(intptr_t)drawable;
115
116     vout_window_t *wnd = vlc_custom_create (VLC_OBJECT(p_vout), sizeof (*wnd),
117                                             VLC_OBJECT_GENERIC, "window");
118     if (wnd == NULL)
119         return NULL;
120
121     wnd->vout = p_vout;
122     wnd->width = *pi_width_hint;
123     wnd->height = *pi_height_hint;
124     wnd->pos_x = *pi_x_hint;
125     wnd->pos_y = *pi_y_hint;
126     vlc_object_attach (wnd, p_vout);
127
128     wnd->module = module_need (wnd, "vout_window", NULL, false);
129     if (wnd->module == NULL)
130     {
131         msg_Dbg (wnd, "no window provider available");
132         vlc_object_release (wnd);
133         return NULL;
134     }
135     *pi_width_hint = wnd->width;
136     *pi_height_hint = wnd->height;
137     *pi_x_hint = wnd->pos_x;
138     *pi_y_hint = wnd->pos_y;
139     return wnd;
140 }
141
142 /**
143  * Releases a window handle obtained with vout_RequestWindow().
144  * @param p_vout video output thread that allocated the window
145  *               (if this is NULL; this fnction is a no-op).
146  */
147 void vout_ReleaseWindow( vout_window_t *wnd )
148 {
149     if (wnd == NULL)
150         return;
151
152     assert (wnd->module);
153     module_unneed (wnd, wnd->module);
154
155     vlc_object_release (wnd);
156 }
157
158 int vout_ControlWindow( vout_window_t *wnd, int i_query, va_list args )
159 {
160     if (wnd == NULL)
161         return VLC_EGENERIC;
162
163     assert (wnd->control);
164     return wnd->control (wnd, i_query, args);
165 }
166
167 /*****************************************************************************
168  * vout_IntfInit: called during the vout creation to initialise misc things.
169  *****************************************************************************/
170 static const struct
171 {
172     double f_value;
173     const char *psz_label;
174 } p_zoom_values[] = {
175     { 0.25, N_("1:4 Quarter") },
176     { 0.5, N_("1:2 Half") },
177     { 1, N_("1:1 Original") },
178     { 2, N_("2:1 Double") },
179     { 0, NULL } };
180
181 static const struct
182 {
183     const char *psz_value;
184     const char *psz_label;
185 } p_crop_values[] = {
186     { "", N_("Default") },
187     { "16:10", "16:10" },
188     { "16:9", "16:9" },
189     { "185:100", "1.85:1" },
190     { "221:100", "2.21:1" },
191     { "235:100", "2.35:1" },
192     { "239:100", "2.39:1" },
193     { "5:3", "5:3" },
194     { "4:3", "4:3" },
195     { "5:4", "5:4" },
196     { "1:1", "1:1" },
197     { NULL, NULL } };
198
199 static const struct
200 {
201     const char *psz_value;
202     const char *psz_label;
203 } p_aspect_ratio_values[] = {
204     { "", N_("Default") },
205     { "1:1", "1:1" },
206     { "4:3", "4:3" },
207     { "16:9", "16:9" },
208     { "16:10", "16:10" },
209     { "221:100", "2.21:1" },
210     { "5:4", "5:4" },
211     { NULL, NULL } };
212
213 static void AddCustomRatios( vout_thread_t *p_vout, const char *psz_var,
214                              char *psz_list )
215 {
216     if( psz_list && *psz_list )
217     {
218         char *psz_cur = psz_list;
219         char *psz_next;
220         while( psz_cur && *psz_cur )
221         {
222             vlc_value_t val, text;
223             psz_next = strchr( psz_cur, ',' );
224             if( psz_next )
225             {
226                 *psz_next = '\0';
227                 psz_next++;
228             }
229             val.psz_string = psz_cur;
230             text.psz_string = psz_cur;
231             var_Change( p_vout, psz_var, VLC_VAR_ADDCHOICE, &val, &text);
232             psz_cur = psz_next;
233         }
234     }
235 }
236
237 void vout_IntfInit( vout_thread_t *p_vout )
238 {
239     vlc_value_t val, text, old_val;
240     bool b_force_par = false;
241     char *psz_buf;
242     int i;
243
244     /* Create a few object variables we'll need later on */
245     var_Create( p_vout, "snapshot-path", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
246     var_Create( p_vout, "snapshot-prefix", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
247     var_Create( p_vout, "snapshot-format", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
248     var_Create( p_vout, "snapshot-preview", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
249     var_Create( p_vout, "snapshot-sequential",
250                 VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
251     var_Create( p_vout, "snapshot-num", VLC_VAR_INTEGER );
252     var_SetInteger( p_vout, "snapshot-num", 1 );
253     var_Create( p_vout, "snapshot-width", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
254     var_Create( p_vout, "snapshot-height", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
255
256     var_Create( p_vout, "width", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
257     var_Create( p_vout, "height", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
258     p_vout->i_alignment = var_CreateGetInteger( p_vout, "align" );
259
260     var_Create( p_vout, "video-x", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
261     var_Create( p_vout, "video-y", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
262
263     var_Create( p_vout, "mouse-hide-timeout",
264                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
265
266     p_vout->p->b_title_show = var_CreateGetBool( p_vout, "video-title-show" );
267     p_vout->p->i_title_timeout =
268         (mtime_t)var_CreateGetInteger( p_vout, "video-title-timeout" );
269     p_vout->p->i_title_position =
270         var_CreateGetInteger( p_vout, "video-title-position" );
271     p_vout->p->psz_title =  NULL;
272
273     var_AddCallback( p_vout, "video-title-show", TitleShowCallback, NULL );
274     var_AddCallback( p_vout, "video-title-timeout", TitleTimeoutCallback, NULL );
275     var_AddCallback( p_vout, "video-title-position", TitlePositionCallback, NULL );
276
277     /* Zoom object var */
278     var_Create( p_vout, "zoom", VLC_VAR_FLOAT | VLC_VAR_ISCOMMAND |
279                 VLC_VAR_HASCHOICE | VLC_VAR_DOINHERIT );
280
281     text.psz_string = _("Zoom");
282     var_Change( p_vout, "zoom", VLC_VAR_SETTEXT, &text, NULL );
283
284     var_Get( p_vout, "zoom", &old_val );
285
286     for( i = 0; p_zoom_values[i].f_value; i++ )
287     {
288         if( old_val.f_float == p_zoom_values[i].f_value )
289             var_Change( p_vout, "zoom", VLC_VAR_DELCHOICE, &old_val, NULL );
290         val.f_float = p_zoom_values[i].f_value;
291         text.psz_string = _( p_zoom_values[i].psz_label );
292         var_Change( p_vout, "zoom", VLC_VAR_ADDCHOICE, &val, &text );
293     }
294
295     var_Set( p_vout, "zoom", old_val ); /* Is this really needed? */
296
297     var_AddCallback( p_vout, "zoom", ZoomCallback, NULL );
298
299     /* Crop offset vars */
300     var_Create( p_vout, "crop-left", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
301     var_Create( p_vout, "crop-top", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
302     var_Create( p_vout, "crop-right", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
303     var_Create( p_vout, "crop-bottom", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
304
305     var_AddCallback( p_vout, "crop-left", CropCallback, NULL );
306     var_AddCallback( p_vout, "crop-top", CropCallback, NULL );
307     var_AddCallback( p_vout, "crop-right", CropCallback, NULL );
308     var_AddCallback( p_vout, "crop-bottom", CropCallback, NULL );
309
310     /* Crop object var */
311     var_Create( p_vout, "crop", VLC_VAR_STRING | VLC_VAR_ISCOMMAND |
312                 VLC_VAR_HASCHOICE | VLC_VAR_DOINHERIT );
313
314     text.psz_string = _("Crop");
315     var_Change( p_vout, "crop", VLC_VAR_SETTEXT, &text, NULL );
316
317     val.psz_string = (char*)"";
318     var_Change( p_vout, "crop", VLC_VAR_DELCHOICE, &val, 0 );
319
320     for( i = 0; p_crop_values[i].psz_value; i++ )
321     {
322         val.psz_string = (char*)p_crop_values[i].psz_value;
323         text.psz_string = _( p_crop_values[i].psz_label );
324         var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text );
325     }
326
327     /* update triggered every time the vout's crop parameters are changed */
328     var_Create( p_vout, "crop-update", VLC_VAR_VOID );
329
330     /* Add custom crop ratios */
331     psz_buf = config_GetPsz( p_vout, "custom-crop-ratios" );
332     AddCustomRatios( p_vout, "crop", psz_buf );
333     free( psz_buf );
334
335     var_AddCallback( p_vout, "crop", CropCallback, NULL );
336     var_Get( p_vout, "crop", &old_val );
337     if( old_val.psz_string && *old_val.psz_string )
338         var_TriggerCallback( p_vout, "crop" );
339     free( old_val.psz_string );
340
341     /* Monitor pixel aspect-ratio */
342     var_Create( p_vout, "monitor-par", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
343     var_Get( p_vout, "monitor-par", &val );
344     if( val.psz_string && *val.psz_string )
345     {
346         char *psz_parser = strchr( val.psz_string, ':' );
347         unsigned int i_aspect_num = 0, i_aspect_den = 0;
348         float i_aspect = 0;
349         if( psz_parser )
350         {
351             i_aspect_num = strtol( val.psz_string, 0, 10 );
352             i_aspect_den = strtol( ++psz_parser, 0, 10 );
353         }
354         else
355         {
356             i_aspect = atof( val.psz_string );
357             vlc_ureduce( &i_aspect_num, &i_aspect_den,
358                          i_aspect *VOUT_ASPECT_FACTOR, VOUT_ASPECT_FACTOR, 0 );
359         }
360         if( !i_aspect_num || !i_aspect_den ) i_aspect_num = i_aspect_den = 1;
361
362         p_vout->p->i_par_num = i_aspect_num;
363         p_vout->p->i_par_den = i_aspect_den;
364
365         vlc_ureduce( &p_vout->p->i_par_num, &p_vout->p->i_par_den,
366                      p_vout->p->i_par_num, p_vout->p->i_par_den, 0 );
367
368         msg_Dbg( p_vout, "overriding monitor pixel aspect-ratio: %i:%i",
369                  p_vout->p->i_par_num, p_vout->p->i_par_den );
370         b_force_par = true;
371     }
372     free( val.psz_string );
373
374     /* Aspect-ratio object var */
375     var_Create( p_vout, "aspect-ratio", VLC_VAR_STRING | VLC_VAR_ISCOMMAND |
376                 VLC_VAR_HASCHOICE | VLC_VAR_DOINHERIT );
377
378     text.psz_string = _("Aspect-ratio");
379     var_Change( p_vout, "aspect-ratio", VLC_VAR_SETTEXT, &text, NULL );
380
381     val.psz_string = (char*)"";
382     var_Change( p_vout, "aspect-ratio", VLC_VAR_DELCHOICE, &val, 0 );
383
384     for( i = 0; p_aspect_ratio_values[i].psz_value; i++ )
385     {
386         val.psz_string = (char*)p_aspect_ratio_values[i].psz_value;
387         text.psz_string = _( p_aspect_ratio_values[i].psz_label );
388         var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
389     }
390
391     /* Add custom aspect ratios */
392     psz_buf = config_GetPsz( p_vout, "custom-aspect-ratios" );
393     AddCustomRatios( p_vout, "aspect-ratio", psz_buf );
394     free( psz_buf );
395
396     var_AddCallback( p_vout, "aspect-ratio", AspectCallback, NULL );
397     var_Get( p_vout, "aspect-ratio", &old_val );
398     if( (old_val.psz_string && *old_val.psz_string) || b_force_par )
399         var_TriggerCallback( p_vout, "aspect-ratio" );
400     free( old_val.psz_string );
401
402     /* Add a variable to indicate if scaling video is activated or not */
403     var_Create( p_vout, "scaling", VLC_VAR_BOOL | VLC_VAR_DOINHERIT
404                 | VLC_VAR_ISCOMMAND );
405     text.psz_string = _("Scaling video");
406     var_Change( p_vout, "scaling", VLC_VAR_SETTEXT, &text, NULL );
407     var_AddCallback( p_vout, "scaling", ScalingCallback, NULL );
408     var_Get( p_vout, "scaling", &val );
409     p_vout->b_scale = val.b_bool;
410
411     /* Initialize the dimensions of the video window */
412     InitWindowSize( p_vout, &p_vout->i_window_width,
413                     &p_vout->i_window_height );
414
415     /* Add a variable to indicate if the window should be on top of others */
416     var_Create( p_vout, "video-on-top", VLC_VAR_BOOL | VLC_VAR_DOINHERIT
417                 | VLC_VAR_ISCOMMAND );
418     text.psz_string = _("Always on top");
419     var_Change( p_vout, "video-on-top", VLC_VAR_SETTEXT, &text, NULL );
420     var_AddCallback( p_vout, "video-on-top", OnTopCallback, NULL );
421
422     /* Add a variable to indicate whether we want window decoration or not */
423     var_Create( p_vout, "video-deco", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
424
425     /* Add a fullscreen variable */
426     if( var_CreateGetBoolCommand( p_vout, "fullscreen" ) )
427     {
428         /* user requested fullscreen */
429         p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
430     }
431     text.psz_string = _("Fullscreen");
432     var_Change( p_vout, "fullscreen", VLC_VAR_SETTEXT, &text, NULL );
433     var_AddCallback( p_vout, "fullscreen", FullscreenCallback, NULL );
434
435     /* Add a snapshot variable */
436     var_Create( p_vout, "video-snapshot", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
437     text.psz_string = _("Snapshot");
438     var_Change( p_vout, "video-snapshot", VLC_VAR_SETTEXT, &text, NULL );
439     var_AddCallback( p_vout, "video-snapshot", SnapshotCallback, NULL );
440
441     /* Mouse coordinates */
442     var_Create( p_vout, "mouse-x", VLC_VAR_INTEGER );
443     var_Create( p_vout, "mouse-y", VLC_VAR_INTEGER );
444     var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER );
445     var_Create( p_vout, "mouse-moved", VLC_VAR_BOOL );
446     var_Create( p_vout, "mouse-clicked", VLC_VAR_BOOL );
447
448     var_Create( p_vout, "intf-change", VLC_VAR_BOOL );
449     var_SetBool( p_vout, "intf-change", true );
450 }
451
452 /*****************************************************************************
453  * vout_Snapshot: generates a snapshot.
454  *****************************************************************************/
455 /**
456  * This function will inject a subpicture into the vout with the provided
457  * picture
458  */
459 static int VoutSnapshotPip( vout_thread_t *p_vout, image_handler_t *p_image, picture_t *p_pic, const video_format_t *p_fmt_in )
460 {
461     video_format_t fmt_in = *p_fmt_in;
462     video_format_t fmt_out;
463     picture_t *p_pip;
464     subpicture_t *p_subpic;
465
466     /* */
467     memset( &fmt_out, 0, sizeof(fmt_out) );
468     fmt_out = fmt_in;
469     fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
470
471     /* */
472     p_pip = image_Convert( p_image, p_pic, &fmt_in, &fmt_out );
473     if( !p_pip )
474         return VLC_EGENERIC;
475
476     p_subpic = subpicture_New();
477     if( p_subpic == NULL )
478     {
479          picture_Release( p_pip );
480          return VLC_EGENERIC;
481     }
482
483     p_subpic->i_channel = 0;
484     p_subpic->i_start = mdate();
485     p_subpic->i_stop = mdate() + 4000000;
486     p_subpic->b_ephemer = true;
487     p_subpic->b_fade = true;
488     p_subpic->i_original_picture_width = fmt_out.i_width * 4;
489     p_subpic->i_original_picture_height = fmt_out.i_height * 4;
490     fmt_out.i_aspect = 0;
491     fmt_out.i_sar_num =
492     fmt_out.i_sar_den = 0;
493
494     p_subpic->p_region = subpicture_region_New( &fmt_out );
495     if( p_subpic->p_region )
496     {
497         picture_Release( p_subpic->p_region->p_picture );
498         p_subpic->p_region->p_picture = p_pip;
499     }
500     else
501     {
502         picture_Release( p_pip );
503     }
504
505     spu_DisplaySubpicture( p_vout->p_spu, p_subpic );
506     return VLC_SUCCESS;
507 }
508 /**
509  * This function will return the default directory used for snapshots
510  */
511 static char *VoutSnapshotGetDefaultDirectory( void )
512 {
513     char *psz_path = NULL;
514 #if defined(__APPLE__) || defined(SYS_BEOS)
515
516     if( asprintf( &psz_path, "%s/Desktop",
517                   config_GetHomeDir() ) == -1 )
518         psz_path = NULL;
519
520 #elif defined(WIN32) && !defined(UNDER_CE)
521
522     /* Get the My Pictures folder path */
523     char *p_mypicturesdir = NULL;
524     typedef HRESULT (WINAPI *SHGETFOLDERPATH)( HWND, int, HANDLE, DWORD,
525                                                LPWSTR );
526     #ifndef CSIDL_FLAG_CREATE
527     #   define CSIDL_FLAG_CREATE 0x8000
528     #endif
529     #ifndef CSIDL_MYPICTURES
530     #   define CSIDL_MYPICTURES 0x27
531     #endif
532     #ifndef SHGFP_TYPE_CURRENT
533     #   define SHGFP_TYPE_CURRENT 0
534     #endif
535
536     HINSTANCE shfolder_dll;
537     SHGETFOLDERPATH SHGetFolderPath ;
538
539     /* load the shfolder dll to retrieve SHGetFolderPath */
540     if( ( shfolder_dll = LoadLibrary( _T("SHFolder.dll") ) ) != NULL )
541     {
542        wchar_t wdir[PATH_MAX];
543        SHGetFolderPath = (void *)GetProcAddress( shfolder_dll,
544                                                   _T("SHGetFolderPathW") );
545         if ((SHGetFolderPath != NULL )
546          && SUCCEEDED (SHGetFolderPath (NULL,
547                                        CSIDL_MYPICTURES | CSIDL_FLAG_CREATE,
548                                        NULL, SHGFP_TYPE_CURRENT,
549                                        wdir)))
550             p_mypicturesdir = FromWide (wdir);
551
552         FreeLibrary( shfolder_dll );
553     }
554
555     if( p_mypicturesdir == NULL )
556         psz_path = strdup( config_GetHomeDir() );
557     else
558         psz_path = p_mypicturesdir;
559
560 #else
561
562     /* XXX: This saves in the data directory. Shouldn't we try saving
563      *      to psz_homedir/Desktop or something nicer ? */
564     char *psz_datadir = config_GetUserDataDir();
565     if( psz_datadir )
566     {
567         if( asprintf( &psz_path, "%s", psz_datadir ) == -1 )
568             psz_path = NULL;
569         free( psz_datadir );
570     }
571
572 #endif
573
574     return psz_path;
575 }
576
577 int vout_Snapshot( vout_thread_t *p_vout, picture_t *p_pic )
578 {
579     image_handler_t *p_image = image_HandlerCreate( p_vout );
580     video_format_t fmt_in, fmt_out;
581     char *psz_filename = NULL;
582     vlc_value_t val, format;
583     DIR *path;
584     int i_ret;
585     bool b_embedded_snapshot;
586     void *p_obj;
587
588     /* */
589     val.psz_string = var_GetNonEmptyString( p_vout, "snapshot-path" );
590
591     /* Embedded snapshot : if snapshot-path == object:object_ptr */
592     if( val.psz_string && sscanf( val.psz_string, "object:%p", &p_obj ) > 0 )
593         b_embedded_snapshot = true;
594     else
595         b_embedded_snapshot = false;
596
597     /* */
598     memset( &fmt_in, 0, sizeof(video_format_t) );
599     fmt_in = p_vout->fmt_out;
600     if( fmt_in.i_sar_num <= 0 || fmt_in.i_sar_den <= 0 )
601     {
602         fmt_in.i_sar_num =
603         fmt_in.i_sar_den = 1;
604     }
605
606     /* */
607     memset( &fmt_out, 0, sizeof(video_format_t) );
608     fmt_out.i_sar_num =
609     fmt_out.i_sar_den = 1;
610     fmt_out.i_chroma = b_embedded_snapshot ? VLC_FOURCC('p','n','g',' ') : 0;
611     fmt_out.i_width = var_GetInteger( p_vout, "snapshot-width" );
612     fmt_out.i_height = var_GetInteger( p_vout, "snapshot-height" );
613
614     if( b_embedded_snapshot &&
615         fmt_out.i_width == 0 && fmt_out.i_height == 0 )
616     {
617         /* If snapshot-width and/or snapshot height were not specified,
618            use a default snapshot width of 320 */
619         fmt_out.i_width = 320;
620     }
621
622     if( fmt_out.i_height == 0 && fmt_out.i_width > 0 )
623     {
624         fmt_out.i_height = fmt_in.i_height * fmt_out.i_width / fmt_in.i_width;
625         const int i_height = fmt_out.i_height * fmt_in.i_sar_den / fmt_in.i_sar_num;
626         if( i_height > 0 )
627             fmt_out.i_height = i_height;
628     }
629     else
630     {
631         if( fmt_out.i_width == 0 && fmt_out.i_height > 0 )
632         {
633             fmt_out.i_width = fmt_in.i_width * fmt_out.i_height / fmt_in.i_height;
634         }
635         else
636         {
637             fmt_out.i_width = fmt_in.i_width;
638             fmt_out.i_height = fmt_in.i_height;
639         }
640         const int i_width = fmt_out.i_width * fmt_in.i_sar_num / fmt_in.i_sar_den;
641         if( i_width > 0 )
642             fmt_out.i_width = i_width;
643     }
644
645     /* Embedded snapshot
646        create a snapshot_t* and store it in
647        object_ptr->p_private, then unlock and signal the
648        waiting object.
649      */
650     if( b_embedded_snapshot )
651     {
652         block_t *p_block;
653         snapshot_t *p_snapshot = p_obj;
654         size_t i_size;
655
656         vlc_mutex_lock( &p_snapshot->p_mutex );
657         p_snapshot->p_data = NULL;
658
659         /* Save the snapshot to a memory zone */
660         p_block = image_Write( p_image, p_pic, &fmt_in, &fmt_out );
661         if( !p_block )
662         {
663             msg_Err( p_vout, "Could not get snapshot" );
664             image_HandlerDelete( p_image );
665             vlc_cond_signal( &p_snapshot->p_condvar );
666             vlc_mutex_unlock( &p_snapshot->p_mutex );
667             return VLC_EGENERIC;
668         }
669
670         i_size = p_block->i_buffer;
671
672         p_snapshot->i_width = fmt_out.i_width;
673         p_snapshot->i_height = fmt_out.i_height;
674         p_snapshot->i_datasize = i_size;
675         p_snapshot->date = p_block->i_pts; /* FIXME ?? */
676         p_snapshot->p_data = malloc( i_size );
677         if( !p_snapshot->p_data )
678         {
679             block_Release( p_block );
680             image_HandlerDelete( p_image );
681             vlc_cond_signal( &p_snapshot->p_condvar );
682             vlc_mutex_unlock( &p_snapshot->p_mutex );
683             return VLC_ENOMEM;
684         }
685         memcpy( p_snapshot->p_data, p_block->p_buffer, p_block->i_buffer );
686
687         block_Release( p_block );
688
689         /* Unlock the object */
690         vlc_cond_signal( &p_snapshot->p_condvar );
691         vlc_mutex_unlock( &p_snapshot->p_mutex );
692
693         image_HandlerDelete( p_image );
694         return VLC_SUCCESS;
695     }
696
697     /* Get default directory if none provided */
698     if( !val.psz_string )
699         val.psz_string = VoutSnapshotGetDefaultDirectory( );
700     if( !val.psz_string )
701     {
702         msg_Err( p_vout, "no path specified for snapshots" );
703         image_HandlerDelete( p_image );
704         return VLC_EGENERIC;
705     }
706
707     /* Get snapshot format, default being "png" */
708     format.psz_string = var_GetNonEmptyString( p_vout, "snapshot-format" );
709     if( !format.psz_string )
710         format.psz_string = strdup( "png" );
711     if( !format.psz_string )
712     {
713         free( val.psz_string );
714         image_HandlerDelete( p_image );
715         return VLC_ENOMEM;
716     }
717
718     /*
719      * Did the user specify a directory? If not, path = NULL.
720      */
721     path = utf8_opendir ( (const char *)val.psz_string  );
722     if( path != NULL )
723     {
724         char *psz_prefix = var_GetNonEmptyString( p_vout, "snapshot-prefix" );
725         if( psz_prefix == NULL )
726             psz_prefix = strdup( "vlcsnap-" );
727         else
728         {
729             char *psz_tmp = str_format( p_vout, psz_prefix );
730             filename_sanitize( psz_tmp );
731             free( psz_prefix );
732             psz_prefix = psz_tmp;
733         }
734
735         closedir( path );
736         if( var_GetBool( p_vout, "snapshot-sequential" ) == true )
737         {
738             int i_num = var_GetInteger( p_vout, "snapshot-num" );
739             struct stat st;
740
741             do
742             {
743                 free( psz_filename );
744                 if( asprintf( &psz_filename, "%s" DIR_SEP "%s%05d.%s",
745                               val.psz_string, psz_prefix, i_num++,
746                               format.psz_string ) == -1 )
747                 {
748                     msg_Err( p_vout, "could not create snapshot" );
749                     image_HandlerDelete( p_image );
750                     return VLC_EGENERIC;
751                 }
752             }
753             while( utf8_stat( psz_filename, &st ) == 0 );
754
755             var_SetInteger( p_vout, "snapshot-num", i_num );
756         }
757         else
758         {
759             struct tm    curtime;
760             time_t        lcurtime ;
761             lcurtime = time( NULL ) ;
762             if ( ( localtime_r( &lcurtime, &curtime ) == NULL ) )
763             {
764                 msg_Warn( p_vout, "failed to get current time. Falling back to legacy snapshot naming" );
765                 /* failed to get current time. Fallback to old format */
766                 if( asprintf( &psz_filename, "%s" DIR_SEP "%s%u.%s",
767                           val.psz_string, psz_prefix,
768                           (unsigned int)(p_pic->date / 100000) & 0xFFFFFF,
769                           format.psz_string ) == -1 )
770                 {
771                     msg_Err( p_vout, "could not create snapshot" );
772                     image_HandlerDelete( p_image );
773                     return VLC_EGENERIC;
774                 }
775             }
776             else
777             {
778                 char psz_curtime[15] ;
779                 if( strftime( psz_curtime, 15, "%y%m%d-%H%M%S", &curtime ) == 0 )
780                 {
781                     msg_Warn( p_vout, "snapshot date string truncated" ) ;
782                 }
783                 if( asprintf( &psz_filename, "%s" DIR_SEP "%s%s%1u.%s",
784                       val.psz_string, psz_prefix, psz_curtime,
785                      /* suffix with the last decimal digit in 10s of seconds resolution */
786                      (unsigned int)(p_pic->date / (100*1000)) & 0xFF,
787                       format.psz_string ) == -1 )
788                 {
789                     msg_Err( p_vout, "could not create snapshot" );
790                     image_HandlerDelete( p_image );
791                     return VLC_EGENERIC;
792                 }
793             } //end if time() < 0
794         } //end snapshot sequential
795         free( psz_prefix );
796     }
797     else // The user specified a full path name (including file name)
798     {
799         psz_filename = str_format( p_vout, val.psz_string );
800         path_sanitize( psz_filename );
801     }
802
803     free( val.psz_string );
804     free( format.psz_string );
805
806     /* Save the snapshot */
807     i_ret = image_WriteUrl( p_image, p_pic, &fmt_in, &fmt_out, psz_filename );
808     if( i_ret != VLC_SUCCESS )
809     {
810         msg_Err( p_vout, "could not create snapshot %s", psz_filename );
811         free( psz_filename );
812         image_HandlerDelete( p_image );
813         return VLC_EGENERIC;
814     }
815
816     /* */
817     msg_Dbg( p_vout, "snapshot taken (%s)", psz_filename );
818     vout_OSDMessage( VLC_OBJECT( p_vout ), DEFAULT_CHAN,
819                      "%s", psz_filename );
820
821     /* Generate a media player event  - Right now just trigger a global libvlc var
822         CHECK: Could not find a more local object. The goal is to communicate
823         vout_thread with libvlc_media_player or its input_thread*/
824     val.psz_string =  psz_filename  ;
825
826     var_Set( p_vout->p_libvlc, "vout-snapshottaken", val );
827     /* var_Set duplicates data for transport so we can free*/
828     free( psz_filename );
829
830     /* */
831     if( var_GetBool( p_vout, "snapshot-preview" ) )
832     {
833         if( VoutSnapshotPip( p_vout, p_image, p_pic, &fmt_in ) )
834             msg_Warn( p_vout, "Failed to display snapshot" );
835     }
836     image_HandlerDelete( p_image );
837
838     return VLC_SUCCESS;
839 }
840
841 /*****************************************************************************
842  * Handle filters
843  *****************************************************************************/
844
845 void vout_EnableFilter( vout_thread_t *p_vout, char *psz_name,
846                         bool b_add, bool b_setconfig )
847 {
848     char *psz_parser;
849     char *psz_string = config_GetPsz( p_vout, "vout-filter" );
850
851     /* Todo : Use some generic chain manipulation functions */
852     if( !psz_string ) psz_string = strdup("");
853
854     psz_parser = strstr( psz_string, psz_name );
855     if( b_add )
856     {
857         if( !psz_parser )
858         {
859             psz_parser = psz_string;
860             if( asprintf( &psz_string, (*psz_string) ? "%s:%s" : "%s%s",
861                           psz_string, psz_name ) == -1 )
862             {
863                 free( psz_parser );
864                 return;
865             }
866             free( psz_parser );
867         }
868         else
869             return;
870     }
871     else
872     {
873         if( psz_parser )
874         {
875             memmove( psz_parser, psz_parser + strlen(psz_name) +
876                             (*(psz_parser + strlen(psz_name)) == ':' ? 1 : 0 ),
877                             strlen(psz_parser + strlen(psz_name)) + 1 );
878
879             /* Remove trailing : : */
880             if( *(psz_string+strlen(psz_string ) -1 ) == ':' )
881             {
882                 *(psz_string+strlen(psz_string ) -1 ) = '\0';
883             }
884          }
885          else
886          {
887              free( psz_string );
888              return;
889          }
890     }
891     if( b_setconfig )
892         config_PutPsz( p_vout, "vout-filter", psz_string );
893
894     var_SetString( p_vout, "vout-filter", psz_string );
895     free( psz_string );
896 }
897
898 /*****************************************************************************
899  * vout_ControlDefault: default methods for video output control.
900  *****************************************************************************/
901 int vout_vaControlDefault( vout_thread_t *p_vout, int i_query, va_list args )
902 {
903     (void)args;
904     switch( i_query )
905     {
906     case VOUT_SNAPSHOT:
907         p_vout->p->b_snapshot = true;
908         return VLC_SUCCESS;
909
910     default:
911         msg_Dbg( p_vout, "control query not supported" );
912     }
913     return VLC_EGENERIC;
914 }
915
916 /*****************************************************************************
917  * InitWindowSize: find the initial dimensions the video window should have.
918  *****************************************************************************
919  * This function will check the "width", "height" and "zoom" config options and
920  * will calculate the size that the video window should have.
921  *****************************************************************************/
922 static void InitWindowSize( vout_thread_t *p_vout, unsigned *pi_width,
923                             unsigned *pi_height )
924 {
925     vlc_value_t val;
926     int i_width, i_height;
927     uint64_t ll_zoom;
928
929 #define FP_FACTOR 1000                             /* our fixed point factor */
930
931     var_Get( p_vout, "width", &val );
932     i_width = val.i_int;
933     var_Get( p_vout, "height", &val );
934     i_height = val.i_int;
935     var_Get( p_vout, "zoom", &val );
936     ll_zoom = (uint64_t)( FP_FACTOR * val.f_float );
937
938     if( i_width > 0 && i_height > 0)
939     {
940         *pi_width = (int)( i_width * ll_zoom / FP_FACTOR );
941         *pi_height = (int)( i_height * ll_zoom / FP_FACTOR );
942         goto initwsize_end;
943     }
944     else if( i_width > 0 )
945     {
946         *pi_width = (int)( i_width * ll_zoom / FP_FACTOR );
947         *pi_height = (int)( p_vout->fmt_in.i_visible_height * ll_zoom *
948             p_vout->fmt_in.i_sar_den * i_width / p_vout->fmt_in.i_sar_num /
949             FP_FACTOR / p_vout->fmt_in.i_visible_width );
950         goto initwsize_end;
951     }
952     else if( i_height > 0 )
953     {
954         *pi_height = (int)( i_height * ll_zoom / FP_FACTOR );
955         *pi_width = (int)( p_vout->fmt_in.i_visible_width * ll_zoom *
956             p_vout->fmt_in.i_sar_num * i_height / p_vout->fmt_in.i_sar_den /
957             FP_FACTOR / p_vout->fmt_in.i_visible_height );
958         goto initwsize_end;
959     }
960
961     if( p_vout->fmt_in.i_sar_num == 0 || p_vout->fmt_in.i_sar_den == 0 ) {
962         msg_Warn( p_vout, "fucked up aspect" );
963         *pi_width = (int)( p_vout->fmt_in.i_visible_width * ll_zoom / FP_FACTOR );
964         *pi_height = (int)( p_vout->fmt_in.i_visible_height * ll_zoom /FP_FACTOR);
965     }
966     else if( p_vout->fmt_in.i_sar_num >= p_vout->fmt_in.i_sar_den )
967     {
968         *pi_width = (int)( p_vout->fmt_in.i_visible_width * ll_zoom *
969             p_vout->fmt_in.i_sar_num / p_vout->fmt_in.i_sar_den / FP_FACTOR );
970         *pi_height = (int)( p_vout->fmt_in.i_visible_height * ll_zoom
971             / FP_FACTOR );
972     }
973     else
974     {
975         *pi_width = (int)( p_vout->fmt_in.i_visible_width * ll_zoom
976             / FP_FACTOR );
977         *pi_height = (int)( p_vout->fmt_in.i_visible_height * ll_zoom *
978             p_vout->fmt_in.i_sar_den / p_vout->fmt_in.i_sar_num / FP_FACTOR );
979     }
980
981 initwsize_end:
982     msg_Dbg( p_vout, "window size: %dx%d", p_vout->i_window_width,
983              p_vout->i_window_height );
984
985 #undef FP_FACTOR
986 }
987
988 /*****************************************************************************
989  * Object variables callbacks
990  *****************************************************************************/
991 static int ZoomCallback( vlc_object_t *p_this, char const *psz_cmd,
992                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
993 {
994     vout_thread_t *p_vout = (vout_thread_t *)p_this;
995     (void)psz_cmd; (void)oldval; (void)newval; (void)p_data;
996     InitWindowSize( p_vout, &p_vout->i_window_width,
997                     &p_vout->i_window_height );
998     vout_Control( p_vout, VOUT_SET_SIZE, p_vout->i_window_width,
999                   p_vout->i_window_height );
1000     return VLC_SUCCESS;
1001 }
1002
1003 static int CropCallback( vlc_object_t *p_this, char const *psz_cmd,
1004                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
1005 {
1006     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1007     int64_t i_aspect_num, i_aspect_den;
1008     unsigned int i_width, i_height;
1009
1010     (void)oldval; (void)p_data;
1011
1012     /* Restore defaults */
1013     p_vout->fmt_in.i_x_offset = p_vout->fmt_render.i_x_offset;
1014     p_vout->fmt_in.i_visible_width = p_vout->fmt_render.i_visible_width;
1015     p_vout->fmt_in.i_y_offset = p_vout->fmt_render.i_y_offset;
1016     p_vout->fmt_in.i_visible_height = p_vout->fmt_render.i_visible_height;
1017
1018     if( !strcmp( psz_cmd, "crop" ) )
1019     {
1020         char *psz_end = NULL, *psz_parser = strchr( newval.psz_string, ':' );
1021         if( psz_parser )
1022         {
1023             /* We're using the 3:4 syntax */
1024             i_aspect_num = strtol( newval.psz_string, &psz_end, 10 );
1025             if( psz_end == newval.psz_string || !i_aspect_num ) goto crop_end;
1026
1027             i_aspect_den = strtol( ++psz_parser, &psz_end, 10 );
1028             if( psz_end == psz_parser || !i_aspect_den ) goto crop_end;
1029
1030             i_width = p_vout->fmt_in.i_sar_den*p_vout->fmt_render.i_visible_height *
1031                 i_aspect_num / i_aspect_den / p_vout->fmt_in.i_sar_num;
1032             i_height = p_vout->fmt_render.i_visible_width*p_vout->fmt_in.i_sar_num *
1033                 i_aspect_den / i_aspect_num / p_vout->fmt_in.i_sar_den;
1034
1035             if( i_width < p_vout->fmt_render.i_visible_width )
1036             {
1037                 p_vout->fmt_in.i_x_offset = p_vout->fmt_render.i_x_offset +
1038                     (p_vout->fmt_render.i_visible_width - i_width) / 2;
1039                 p_vout->fmt_in.i_visible_width = i_width;
1040             }
1041             else
1042             {
1043                 p_vout->fmt_in.i_y_offset = p_vout->fmt_render.i_y_offset +
1044                     (p_vout->fmt_render.i_visible_height - i_height) / 2;
1045                 p_vout->fmt_in.i_visible_height = i_height;
1046             }
1047         }
1048         else
1049         {
1050             psz_parser = strchr( newval.psz_string, 'x' );
1051             if( psz_parser )
1052             {
1053                 /* Maybe we're using the <width>x<height>+<left>+<top> syntax */
1054                 unsigned int i_crop_width, i_crop_height, i_crop_top, i_crop_left;
1055
1056                 i_crop_width = strtol( newval.psz_string, &psz_end, 10 );
1057                 if( psz_end != psz_parser ) goto crop_end;
1058
1059                 psz_parser = strchr( ++psz_end, '+' );
1060                 i_crop_height = strtol( psz_end, &psz_end, 10 );
1061                 if( psz_end != psz_parser ) goto crop_end;
1062
1063                 psz_parser = strchr( ++psz_end, '+' );
1064                 i_crop_left = strtol( psz_end, &psz_end, 10 );
1065                 if( psz_end != psz_parser ) goto crop_end;
1066
1067                 psz_end++;
1068                 i_crop_top = strtol( psz_end, &psz_end, 10 );
1069                 if( *psz_end != '\0' ) goto crop_end;
1070
1071                 i_width = i_crop_width;
1072                 p_vout->fmt_in.i_visible_width = i_width;
1073
1074                 i_height = i_crop_height;
1075                 p_vout->fmt_in.i_visible_height = i_height;
1076
1077                 p_vout->fmt_in.i_x_offset = i_crop_left;
1078                 p_vout->fmt_in.i_y_offset = i_crop_top;
1079             }
1080             else
1081             {
1082                 /* Maybe we're using the <left>+<top>+<right>+<bottom> syntax */
1083                 unsigned int i_crop_top, i_crop_left, i_crop_bottom, i_crop_right;
1084
1085                 psz_parser = strchr( newval.psz_string, '+' );
1086                 i_crop_left = strtol( newval.psz_string, &psz_end, 10 );
1087                 if( psz_end != psz_parser ) goto crop_end;
1088
1089                 psz_parser = strchr( ++psz_end, '+' );
1090                 i_crop_top = strtol( psz_end, &psz_end, 10 );
1091                 if( psz_end != psz_parser ) goto crop_end;
1092
1093                 psz_parser = strchr( ++psz_end, '+' );
1094                 i_crop_right = strtol( psz_end, &psz_end, 10 );
1095                 if( psz_end != psz_parser ) goto crop_end;
1096
1097                 psz_end++;
1098                 i_crop_bottom = strtol( psz_end, &psz_end, 10 );
1099                 if( *psz_end != '\0' ) goto crop_end;
1100
1101                 i_width = p_vout->fmt_render.i_visible_width
1102                           - i_crop_left - i_crop_right;
1103                 p_vout->fmt_in.i_visible_width = i_width;
1104
1105                 i_height = p_vout->fmt_render.i_visible_height
1106                            - i_crop_top - i_crop_bottom;
1107                 p_vout->fmt_in.i_visible_height = i_height;
1108
1109                 p_vout->fmt_in.i_x_offset = i_crop_left;
1110                 p_vout->fmt_in.i_y_offset = i_crop_top;
1111             }
1112         }
1113     }
1114     else if( !strcmp( psz_cmd, "crop-top" )
1115           || !strcmp( psz_cmd, "crop-left" )
1116           || !strcmp( psz_cmd, "crop-bottom" )
1117           || !strcmp( psz_cmd, "crop-right" ) )
1118     {
1119         unsigned int i_crop_top, i_crop_left, i_crop_bottom, i_crop_right;
1120
1121         i_crop_top = var_GetInteger( p_vout, "crop-top" );
1122         i_crop_left = var_GetInteger( p_vout, "crop-left" );
1123         i_crop_right = var_GetInteger( p_vout, "crop-right" );
1124         i_crop_bottom = var_GetInteger( p_vout, "crop-bottom" );
1125
1126         i_width = p_vout->fmt_render.i_visible_width
1127                   - i_crop_left - i_crop_right;
1128         p_vout->fmt_in.i_visible_width = i_width;
1129
1130         i_height = p_vout->fmt_render.i_visible_height
1131                    - i_crop_top - i_crop_bottom;
1132         p_vout->fmt_in.i_visible_height = i_height;
1133
1134         p_vout->fmt_in.i_x_offset = i_crop_left;
1135         p_vout->fmt_in.i_y_offset = i_crop_top;
1136     }
1137
1138  crop_end:
1139     InitWindowSize( p_vout, &p_vout->i_window_width,
1140                     &p_vout->i_window_height );
1141
1142     p_vout->i_changes |= VOUT_CROP_CHANGE;
1143
1144     msg_Dbg( p_vout, "cropping picture %ix%i to %i,%i,%ix%i",
1145              p_vout->fmt_in.i_width, p_vout->fmt_in.i_height,
1146              p_vout->fmt_in.i_x_offset, p_vout->fmt_in.i_y_offset,
1147              p_vout->fmt_in.i_visible_width,
1148              p_vout->fmt_in.i_visible_height );
1149
1150     var_SetVoid( p_vout, "crop-update" );
1151
1152     return VLC_SUCCESS;
1153 }
1154
1155 static int AspectCallback( vlc_object_t *p_this, char const *psz_cmd,
1156                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
1157 {
1158     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1159     unsigned int i_aspect_num, i_aspect_den, i_sar_num, i_sar_den;
1160     vlc_value_t val;
1161
1162     char *psz_end, *psz_parser = strchr( newval.psz_string, ':' );
1163     (void)psz_cmd; (void)oldval; (void)p_data;
1164
1165     /* Restore defaults */
1166     p_vout->fmt_in.i_sar_num = p_vout->fmt_render.i_sar_num;
1167     p_vout->fmt_in.i_sar_den = p_vout->fmt_render.i_sar_den;
1168     p_vout->fmt_in.i_aspect = p_vout->fmt_render.i_aspect;
1169     p_vout->render.i_aspect = p_vout->fmt_render.i_aspect;
1170
1171     if( !psz_parser ) goto aspect_end;
1172
1173     i_aspect_num = strtol( newval.psz_string, &psz_end, 10 );
1174     if( psz_end == newval.psz_string || !i_aspect_num ) goto aspect_end;
1175
1176     i_aspect_den = strtol( ++psz_parser, &psz_end, 10 );
1177     if( psz_end == psz_parser || !i_aspect_den ) goto aspect_end;
1178
1179     i_sar_num = i_aspect_num * p_vout->fmt_render.i_visible_height;
1180     i_sar_den = i_aspect_den * p_vout->fmt_render.i_visible_width;
1181     vlc_ureduce( &i_sar_num, &i_sar_den, i_sar_num, i_sar_den, 0 );
1182     p_vout->fmt_in.i_sar_num = i_sar_num;
1183     p_vout->fmt_in.i_sar_den = i_sar_den;
1184     p_vout->fmt_in.i_aspect = i_aspect_num * VOUT_ASPECT_FACTOR / i_aspect_den;
1185     p_vout->render.i_aspect = p_vout->fmt_in.i_aspect;
1186
1187  aspect_end:
1188     if( p_vout->p->i_par_num && p_vout->p->i_par_den )
1189     {
1190         p_vout->fmt_in.i_sar_num *= p_vout->p->i_par_den;
1191         p_vout->fmt_in.i_sar_den *= p_vout->p->i_par_num;
1192         p_vout->fmt_in.i_aspect = p_vout->fmt_in.i_aspect *
1193             p_vout->p->i_par_den / p_vout->p->i_par_num;
1194         p_vout->render.i_aspect = p_vout->fmt_in.i_aspect;
1195     }
1196
1197     p_vout->i_changes |= VOUT_ASPECT_CHANGE;
1198
1199     vlc_ureduce( &i_aspect_num, &i_aspect_den,
1200                  p_vout->fmt_in.i_aspect, VOUT_ASPECT_FACTOR, 0 );
1201     msg_Dbg( p_vout, "new aspect-ratio %i:%i, sample aspect-ratio %i:%i",
1202              i_aspect_num, i_aspect_den,
1203              p_vout->fmt_in.i_sar_num, p_vout->fmt_in.i_sar_den );
1204
1205     if( var_Get( p_vout, "crop", &val ) )
1206         return VLC_EGENERIC;
1207
1208     int i_ret = CropCallback( p_this, "crop", val, val, 0 );
1209     free( val.psz_string );
1210     return i_ret;
1211 }
1212
1213 static int ScalingCallback( vlc_object_t *p_this, char const *psz_cmd,
1214                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
1215 {
1216     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1217
1218     vlc_mutex_lock( &p_vout->change_lock );
1219
1220     p_vout->b_scale = newval.b_bool;
1221     p_vout->i_changes |= VOUT_SIZE_CHANGE;
1222
1223     vlc_mutex_unlock( &p_vout->change_lock );
1224 }
1225
1226 static int OnTopCallback( vlc_object_t *p_this, char const *psz_cmd,
1227                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
1228 {
1229     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1230     vout_Control( p_vout, VOUT_SET_STAY_ON_TOP, newval.b_bool );
1231     (void)psz_cmd; (void)oldval; (void)p_data;
1232
1233     /* Modify libvlc as well because the vout might have to be restarted */
1234     var_Create( p_vout->p_libvlc, "video-on-top", VLC_VAR_BOOL );
1235     var_Set( p_vout->p_libvlc, "video-on-top", newval );
1236
1237     return VLC_SUCCESS;
1238 }
1239
1240 static int FullscreenCallback( vlc_object_t *p_this, char const *psz_cmd,
1241                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1242 {
1243     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1244     vlc_value_t val;
1245     (void)psz_cmd; (void)oldval; (void)p_data;
1246
1247     p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
1248
1249     /* Modify libvlc as well because the vout might have to be restarted */
1250     var_Create( p_vout->p_libvlc, "fullscreen", VLC_VAR_BOOL );
1251     var_Set( p_vout->p_libvlc, "fullscreen", newval );
1252
1253     val.b_bool = true;
1254     var_Set( p_vout, "intf-change", val );
1255     return VLC_SUCCESS;
1256 }
1257
1258 static int SnapshotCallback( vlc_object_t *p_this, char const *psz_cmd,
1259                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1260 {
1261     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
1262     VLC_UNUSED(newval); VLC_UNUSED(p_data);
1263     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1264     vout_Control( p_vout, VOUT_SNAPSHOT );
1265     return VLC_SUCCESS;
1266 }
1267
1268 static int TitleShowCallback( vlc_object_t *p_this, char const *psz_cmd,
1269                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1270 {
1271     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
1272     VLC_UNUSED(p_data);
1273     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1274     p_vout->p->b_title_show = newval.b_bool;
1275     return VLC_SUCCESS;
1276 }
1277
1278 static int TitleTimeoutCallback( vlc_object_t *p_this, char const *psz_cmd,
1279                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1280 {
1281     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1282     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1283     p_vout->p->i_title_timeout = (mtime_t) newval.i_int;
1284     return VLC_SUCCESS;
1285 }
1286
1287 static int TitlePositionCallback( vlc_object_t *p_this, char const *psz_cmd,
1288                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1289 {
1290     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
1291     VLC_UNUSED(p_data);
1292     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1293     p_vout->p->i_title_position = newval.i_int;
1294     return VLC_SUCCESS;
1295 }