]> git.sesse.net Git - vlc/blob - src/video_output/vout_intf.c
httpd: add video/webm content type (fix #7208)
[vlc] / src / video_output / vout_intf.c
1 /*****************************************************************************
2  * vout_intf.c : video output interface
3  *****************************************************************************
4  * Copyright (C) 2000-2007 VLC authors and VideoLAN
5  *
6  * Authors: Gildas Bazin <gbazin@videolan.org>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * 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 <assert.h>
36
37 #include <vlc_block.h>
38 #include <vlc_modules.h>
39
40 #include <vlc_vout.h>
41 #include <vlc_vout_osd.h>
42 #include <vlc_strings.h>
43 #include <vlc_charset.h>
44 #include "vout_internal.h"
45
46 /*****************************************************************************
47  * Local prototypes
48  *****************************************************************************/
49 /* Object variables callbacks */
50 static int ZoomCallback( vlc_object_t *, char const *,
51                          vlc_value_t, vlc_value_t, void * );
52 static int CropCallback( vlc_object_t *, char const *,
53                          vlc_value_t, vlc_value_t, void * );
54 static int CropBorderCallback( vlc_object_t *, char const *,
55                                vlc_value_t, vlc_value_t, void * );
56 static int AspectCallback( vlc_object_t *, char const *,
57                            vlc_value_t, vlc_value_t, void * );
58 static int ScalingCallback( vlc_object_t *, char const *,
59                             vlc_value_t, vlc_value_t, void * );
60 static int OnTopCallback( vlc_object_t *, char const *,
61                           vlc_value_t, vlc_value_t, void * );
62 static int FullscreenCallback( vlc_object_t *, char const *,
63                                vlc_value_t, vlc_value_t, void * );
64 static int SnapshotCallback( vlc_object_t *, char const *,
65                              vlc_value_t, vlc_value_t, void * );
66 static int VideoFilterCallback( vlc_object_t *, char const *,
67                                 vlc_value_t, vlc_value_t, void * );
68 static int SubSourceCallback( vlc_object_t *, char const *,
69                               vlc_value_t, vlc_value_t, void * );
70 static int SubFilterCallback( vlc_object_t *, char const *,
71                               vlc_value_t, vlc_value_t, void * );
72 static int SubMarginCallback( vlc_object_t *, char const *,
73                               vlc_value_t, vlc_value_t, void * );
74
75 /*****************************************************************************
76  * vout_IntfInit: called during the vout creation to initialise misc things.
77  *****************************************************************************/
78 static const struct
79 {
80     double f_value;
81     char psz_label[13];
82 } p_zoom_values[] = {
83     { 0.25, N_("1:4 Quarter") },
84     { 0.5, N_("1:2 Half") },
85     { 1, N_("1:1 Original") },
86     { 2, N_("2:1 Double") },
87 };
88
89 static const struct
90 {
91     char psz_value[8];
92     char psz_label[8];
93 } p_crop_values[] = {
94     { "", N_("Default") },
95     { "16:10", "16:10" },
96     { "16:9", "16:9" },
97     { "185:100", "1.85:1" },
98     { "221:100", "2.21:1" },
99     { "235:100", "2.35:1" },
100     { "239:100", "2.39:1" },
101     { "5:3", "5:3" },
102     { "4:3", "4:3" },
103     { "5:4", "5:4" },
104     { "1:1", "1:1" },
105 };
106
107 static const struct
108 {
109     char psz_value[8];
110     char psz_label[8];
111 } p_aspect_ratio_values[] = {
112     { "", N_("Default") },
113     { "1:1", "1:1" },
114     { "4:3", "4:3" },
115     { "16:9", "16:9" },
116     { "16:10", "16:10" },
117     { "221:100", "2.21:1" },
118     { "235:100", "2.35:1" },
119     { "239:100", "2.39:1" },
120     { "5:4", "5:4" },
121 };
122
123 static void AddCustomRatios( vout_thread_t *p_vout, const char *psz_var,
124                              char *psz_list )
125 {
126     assert( psz_list );
127
128     char *psz_cur = psz_list;
129     char *psz_next;
130     while( psz_cur && *psz_cur )
131     {
132         vlc_value_t val, text;
133         psz_next = strchr( psz_cur, ',' );
134         if( psz_next )
135         {
136             *psz_next = '\0';
137             psz_next++;
138         }
139         val.psz_string = psz_cur;
140         text.psz_string = psz_cur;
141         var_Change( p_vout, psz_var, VLC_VAR_ADDCHOICE, &val, &text);
142         psz_cur = psz_next;
143     }
144 }
145
146 void vout_IntfInit( vout_thread_t *p_vout )
147 {
148     vlc_value_t val, text, old_val;
149     char *psz_buf;
150
151     /* Create a few object variables we'll need later on */
152     var_Create( p_vout, "snapshot-path", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
153     var_Create( p_vout, "snapshot-prefix", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
154     var_Create( p_vout, "snapshot-format", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
155     var_Create( p_vout, "snapshot-preview", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
156     var_Create( p_vout, "snapshot-sequential",
157                 VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
158     var_Create( p_vout, "snapshot-num", VLC_VAR_INTEGER );
159     var_SetInteger( p_vout, "snapshot-num", 1 );
160     var_Create( p_vout, "snapshot-width", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
161     var_Create( p_vout, "snapshot-height", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
162
163     var_Create( p_vout, "width", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
164     var_Create( p_vout, "height", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
165     var_Create( p_vout, "align", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
166
167     var_Create( p_vout, "video-x", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
168     var_Create( p_vout, "video-y", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
169
170     var_Create( p_vout, "mouse-hide-timeout",
171                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
172
173     var_Create( p_vout, "video-title-show", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
174     var_Create( p_vout, "video-title-timeout", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
175     var_Create( p_vout, "video-title-position", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
176
177     /* Zoom object var */
178     var_Create( p_vout, "zoom", VLC_VAR_FLOAT | VLC_VAR_ISCOMMAND |
179                 VLC_VAR_HASCHOICE | VLC_VAR_DOINHERIT );
180
181     text.psz_string = _("Zoom");
182     var_Change( p_vout, "zoom", VLC_VAR_SETTEXT, &text, NULL );
183
184     var_Get( p_vout, "zoom", &old_val );
185
186     for( size_t i = 0; i < ARRAY_SIZE(p_zoom_values); i++ )
187     {
188         if( old_val.f_float == p_zoom_values[i].f_value )
189             var_Change( p_vout, "zoom", VLC_VAR_DELCHOICE, &old_val, NULL );
190         val.f_float = p_zoom_values[i].f_value;
191         text.psz_string = _( p_zoom_values[i].psz_label );
192         var_Change( p_vout, "zoom", VLC_VAR_ADDCHOICE, &val, &text );
193     }
194
195     var_Set( p_vout, "zoom", old_val ); /* Is this really needed? */
196
197     var_AddCallback( p_vout, "zoom", ZoomCallback, NULL );
198
199     /* Crop offset vars */
200     var_Create( p_vout, "crop-left", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
201     var_Create( p_vout, "crop-top", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
202     var_Create( p_vout, "crop-right", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
203     var_Create( p_vout, "crop-bottom", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
204
205     var_AddCallback( p_vout, "crop-left", CropBorderCallback, NULL );
206     var_AddCallback( p_vout, "crop-top", CropBorderCallback, NULL );
207     var_AddCallback( p_vout, "crop-right", CropBorderCallback, NULL );
208     var_AddCallback( p_vout, "crop-bottom", CropBorderCallback, NULL );
209
210     /* Crop object var */
211     var_Create( p_vout, "crop", VLC_VAR_STRING | VLC_VAR_ISCOMMAND |
212                 VLC_VAR_HASCHOICE | VLC_VAR_DOINHERIT );
213
214     text.psz_string = _("Crop");
215     var_Change( p_vout, "crop", VLC_VAR_SETTEXT, &text, NULL );
216
217     val.psz_string = (char*)"";
218     var_Change( p_vout, "crop", VLC_VAR_DELCHOICE, &val, 0 );
219
220     for( size_t i = 0; i < ARRAY_SIZE(p_crop_values); i++ )
221     {
222         val.psz_string = (char*)p_crop_values[i].psz_value;
223         text.psz_string = _( p_crop_values[i].psz_label );
224         var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text );
225     }
226
227     /* Add custom crop ratios */
228     psz_buf = var_CreateGetNonEmptyString( p_vout, "custom-crop-ratios" );
229     if( psz_buf )
230     {
231         AddCustomRatios( p_vout, "crop", psz_buf );
232         free( psz_buf );
233     }
234
235     var_AddCallback( p_vout, "crop", CropCallback, NULL );
236     var_Get( p_vout, "crop", &old_val );
237     if( old_val.psz_string && *old_val.psz_string )
238         var_TriggerCallback( p_vout, "crop" );
239     free( old_val.psz_string );
240
241     /* Monitor pixel aspect-ratio */
242     var_Create( p_vout, "monitor-par", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
243
244     /* Aspect-ratio object var */
245     var_Create( p_vout, "aspect-ratio", VLC_VAR_STRING | VLC_VAR_ISCOMMAND |
246                 VLC_VAR_HASCHOICE | VLC_VAR_DOINHERIT );
247
248     text.psz_string = _("Aspect-ratio");
249     var_Change( p_vout, "aspect-ratio", VLC_VAR_SETTEXT, &text, NULL );
250
251     val.psz_string = (char*)"";
252     var_Change( p_vout, "aspect-ratio", VLC_VAR_DELCHOICE, &val, 0 );
253
254     for( size_t i = 0; i < ARRAY_SIZE(p_aspect_ratio_values); i++ )
255     {
256         val.psz_string = (char*)p_aspect_ratio_values[i].psz_value;
257         text.psz_string = _( p_aspect_ratio_values[i].psz_label );
258         var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
259     }
260
261     /* Add custom aspect ratios */
262     psz_buf = var_CreateGetNonEmptyString( p_vout, "custom-aspect-ratios" );
263     if( psz_buf )
264     {
265         AddCustomRatios( p_vout, "aspect-ratio", psz_buf );
266         free( psz_buf );
267     }
268
269     var_AddCallback( p_vout, "aspect-ratio", AspectCallback, NULL );
270     var_Get( p_vout, "aspect-ratio", &old_val );
271     if( (old_val.psz_string && *old_val.psz_string) )
272         var_TriggerCallback( p_vout, "aspect-ratio" );
273     free( old_val.psz_string );
274
275     /* Add variables to manage scaling video */
276     var_Create( p_vout, "autoscale", VLC_VAR_BOOL | VLC_VAR_DOINHERIT
277                 | VLC_VAR_ISCOMMAND );
278     text.psz_string = _("Autoscale video");
279     var_Change( p_vout, "autoscale", VLC_VAR_SETTEXT, &text, NULL );
280     var_AddCallback( p_vout, "autoscale", ScalingCallback, NULL );
281
282     var_Create( p_vout, "scale", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT
283                 | VLC_VAR_ISCOMMAND );
284     text.psz_string = _("Scale factor");
285     var_Change( p_vout, "scale", VLC_VAR_SETTEXT, &text, NULL );
286     var_AddCallback( p_vout, "scale", ScalingCallback, NULL );
287
288     /* Add a variable to indicate if the window should be on top of others */
289     var_Create( p_vout, "video-on-top", VLC_VAR_BOOL | VLC_VAR_DOINHERIT
290                 | VLC_VAR_ISCOMMAND );
291     text.psz_string = _("Always on top");
292     var_Change( p_vout, "video-on-top", VLC_VAR_SETTEXT, &text, NULL );
293     var_AddCallback( p_vout, "video-on-top", OnTopCallback, NULL );
294     var_TriggerCallback( p_vout, "video-on-top" );
295
296     /* Add a variable to indicate whether we want window decoration or not */
297     var_Create( p_vout, "video-deco", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
298
299     /* Add a fullscreen variable */
300     var_Create( p_vout, "fullscreen",
301                 VLC_VAR_BOOL | VLC_VAR_DOINHERIT | VLC_VAR_ISCOMMAND );
302     text.psz_string = _("Fullscreen");
303     var_Change( p_vout, "fullscreen", VLC_VAR_SETTEXT, &text, NULL );
304     var_AddCallback( p_vout, "fullscreen", FullscreenCallback, NULL );
305
306     /* Add a snapshot variable */
307     var_Create( p_vout, "video-snapshot", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
308     text.psz_string = _("Snapshot");
309     var_Change( p_vout, "video-snapshot", VLC_VAR_SETTEXT, &text, NULL );
310     var_AddCallback( p_vout, "video-snapshot", SnapshotCallback, NULL );
311
312     /* Add a video-filter variable */
313     var_Create( p_vout, "video-filter",
314                 VLC_VAR_STRING | VLC_VAR_DOINHERIT | VLC_VAR_ISCOMMAND );
315     var_AddCallback( p_vout, "video-filter", VideoFilterCallback, NULL );
316     var_TriggerCallback( p_vout, "video-filter" );
317
318     /* Add a sub-source variable */
319     var_Create( p_vout, "sub-source",
320                 VLC_VAR_STRING | VLC_VAR_DOINHERIT | VLC_VAR_ISCOMMAND );
321     var_AddCallback( p_vout, "sub-source", SubSourceCallback, NULL );
322     var_TriggerCallback( p_vout, "sub-source" );
323
324     /* Add a sub-filter variable */
325     var_Create( p_vout, "sub-filter",
326                 VLC_VAR_STRING | VLC_VAR_DOINHERIT | VLC_VAR_ISCOMMAND );
327     var_AddCallback( p_vout, "sub-filter", SubFilterCallback, NULL );
328     var_TriggerCallback( p_vout, "sub-filter" );
329
330     /* Add sub-margin variable */
331     var_Create( p_vout, "sub-margin",
332                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT | VLC_VAR_ISCOMMAND );
333     var_AddCallback( p_vout, "sub-margin", SubMarginCallback, NULL );
334     var_TriggerCallback( p_vout, "sub-margin" );
335
336     /* Mouse coordinates */
337     var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER );
338     var_Create( p_vout, "mouse-moved", VLC_VAR_COORDS );
339     var_Create( p_vout, "mouse-clicked", VLC_VAR_COORDS );
340     var_Create( p_vout, "mouse-object", VLC_VAR_BOOL );
341 }
342
343 /*****************************************************************************
344  * vout_Snapshot: generates a snapshot.
345  *****************************************************************************/
346 /**
347  * This function will inject a subpicture into the vout with the provided
348  * picture
349  */
350 static int VoutSnapshotPip( vout_thread_t *p_vout, picture_t *p_pic )
351 {
352     subpicture_t *p_subpic = subpicture_NewFromPicture( VLC_OBJECT(p_vout),
353                                                         p_pic, VLC_CODEC_YUVA );
354     if( !p_subpic )
355         return VLC_EGENERIC;
356
357     /* FIXME SPU_DEFAULT_CHANNEL is not good (used by the text) but
358      * hardcoded 0 doesn't seem right */
359     p_subpic->i_channel = 0;
360     p_subpic->i_start = mdate();
361     p_subpic->i_stop  = p_subpic->i_start + 4000000;
362     p_subpic->b_ephemer = true;
363     p_subpic->b_fade = true;
364
365     /* Reduce the picture to 1/4^2 of the screen */
366     p_subpic->i_original_picture_width  *= 4;
367     p_subpic->i_original_picture_height *= 4;
368
369     vout_PutSubpicture( p_vout, p_subpic );
370     return VLC_SUCCESS;
371 }
372
373 /**
374  * This function will display the name and a PIP of the provided snapshot
375  */
376 static void VoutOsdSnapshot( vout_thread_t *p_vout, picture_t *p_pic, const char *psz_filename )
377 {
378     msg_Dbg( p_vout, "snapshot taken (%s)", psz_filename );
379     vout_OSDMessage( p_vout, SPU_DEFAULT_CHANNEL, "%s", psz_filename );
380
381     if( var_GetBool( p_vout, "snapshot-preview" ) )
382     {
383         if( VoutSnapshotPip( p_vout, p_pic ) )
384             msg_Warn( p_vout, "Failed to display snapshot" );
385     }
386 }
387
388 /**
389  * This function will handle a snapshot request
390  */
391 static void VoutSaveSnapshot( vout_thread_t *p_vout )
392 {
393     char *psz_path = var_GetNonEmptyString( p_vout, "snapshot-path" );
394     char *psz_format = var_GetNonEmptyString( p_vout, "snapshot-format" );
395     char *psz_prefix = var_GetNonEmptyString( p_vout, "snapshot-prefix" );
396
397     /* */
398     picture_t *p_picture;
399     block_t *p_image;
400     video_format_t fmt;
401
402     /* 500ms timeout
403      * XXX it will cause trouble with low fps video (< 2fps) */
404     if( vout_GetSnapshot( p_vout, &p_image, &p_picture, &fmt, psz_format, 500*1000 ) )
405     {
406         p_picture = NULL;
407         p_image = NULL;
408         goto exit;
409     }
410
411     if( !psz_path )
412     {
413         psz_path = vout_snapshot_GetDirectory();
414         if( !psz_path )
415         {
416             msg_Err( p_vout, "no path specified for snapshots" );
417             goto exit;
418         }
419     }
420
421     vout_snapshot_save_cfg_t cfg;
422     memset( &cfg, 0, sizeof(cfg) );
423     cfg.is_sequential = var_GetBool( p_vout, "snapshot-sequential" );
424     cfg.sequence = var_GetInteger( p_vout, "snapshot-num" );
425     cfg.path = psz_path;
426     cfg.format = psz_format;
427     cfg.prefix_fmt = psz_prefix;
428
429     char *psz_filename;
430     int  i_sequence;
431     if (vout_snapshot_SaveImage( &psz_filename, &i_sequence,
432                                  p_image, VLC_OBJECT(p_vout), &cfg ) )
433         goto exit;
434     if( cfg.is_sequential )
435         var_SetInteger( p_vout, "snapshot-num", i_sequence + 1 );
436
437     VoutOsdSnapshot( p_vout, p_picture, psz_filename );
438
439     /* signal creation of a new snapshot file */
440     var_SetString( p_vout->p_libvlc, "snapshot-file", psz_filename );
441
442     free( psz_filename );
443
444 exit:
445     if( p_image )
446         block_Release( p_image );
447     if( p_picture )
448         picture_Release( p_picture );
449     free( psz_prefix );
450     free( psz_format );
451     free( psz_path );
452 }
453
454 /*****************************************************************************
455  * Handle filters
456  *****************************************************************************/
457
458 void vout_EnableFilter( vout_thread_t *p_vout, const char *psz_name,
459                         bool b_add, bool b_setconfig )
460 {
461     char *psz_parser;
462     char *psz_string;
463     const char *psz_filter_type;
464
465     module_t *p_obj = module_find( psz_name );
466     if( !p_obj )
467     {
468         msg_Err( p_vout, "Unable to find filter module \"%s\".", psz_name );
469         return;
470     }
471
472     if( module_provides( p_obj, "video filter2" ) )
473     {
474         psz_filter_type = "video-filter";
475     }
476     else if( module_provides( p_obj, "sub source" ) )
477     {
478         psz_filter_type = "sub-source";
479     }
480     else if( module_provides( p_obj, "sub filter" ) )
481     {
482         psz_filter_type = "sub-filter";
483     }
484     else
485     {
486         msg_Err( p_vout, "Unknown video filter type." );
487         return;
488     }
489
490     psz_string = var_GetString( p_vout, psz_filter_type );
491
492     /* Todo : Use some generic chain manipulation functions */
493     if( !psz_string ) psz_string = strdup("");
494
495     psz_parser = strstr( psz_string, psz_name );
496     if( b_add )
497     {
498         if( !psz_parser )
499         {
500             psz_parser = psz_string;
501             if( asprintf( &psz_string, (*psz_string) ? "%s:%s" : "%s%s",
502                           psz_string, psz_name ) == -1 )
503             {
504                 free( psz_parser );
505                 return;
506             }
507             free( psz_parser );
508         }
509         else
510             return;
511     }
512     else
513     {
514         if( psz_parser )
515         {
516             memmove( psz_parser, psz_parser + strlen(psz_name) +
517                             (*(psz_parser + strlen(psz_name)) == ':' ? 1 : 0 ),
518                             strlen(psz_parser + strlen(psz_name)) + 1 );
519
520             /* Remove trailing : : */
521             if( *(psz_string+strlen(psz_string ) -1 ) == ':' )
522             {
523                 *(psz_string+strlen(psz_string ) -1 ) = '\0';
524             }
525          }
526          else
527          {
528              free( psz_string );
529              return;
530          }
531     }
532
533     if( b_setconfig )
534     {
535         config_PutPsz( p_vout, psz_filter_type, psz_string );
536     }
537
538     var_SetString( p_vout, psz_filter_type, psz_string );
539
540     free( psz_string );
541 }
542
543 /*****************************************************************************
544  * Object variables callbacks
545  *****************************************************************************/
546 static int ZoomCallback( vlc_object_t *p_this, char const *psz_cmd,
547                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
548 {
549     (void)psz_cmd; (void)oldval; (void)p_data;
550
551     return var_SetFloat( p_this, "scale", newval.f_float );
552 }
553
554 static int CropCallback( vlc_object_t *object, char const *cmd,
555                          vlc_value_t oldval, vlc_value_t newval, void *data )
556 {
557     vout_thread_t *vout = (vout_thread_t *)object;
558     VLC_UNUSED(cmd); VLC_UNUSED(oldval); VLC_UNUSED(data);
559     unsigned num, den;
560     unsigned y, x;
561     unsigned width, height;
562     unsigned left, top, right, bottom;
563
564     if (sscanf(newval.psz_string, "%u:%u", &num, &den) == 2) {
565         vout_ControlChangeCropRatio(vout, num, den);
566     } else if (sscanf(newval.psz_string, "%ux%u+%u+%u",
567                       &width, &height, &x, &y) == 4) {
568         vout_ControlChangeCropWindow(vout, x, y, width, height);
569     } else if (sscanf(newval.psz_string, "%u+%u+%u+%u",
570                     &left, &top, &right, &bottom) == 4) {
571         vout_ControlChangeCropBorder(vout, left, top, right, bottom);
572     } else if (*newval.psz_string == '\0') {
573         vout_ControlChangeCropRatio(vout, 0, 0);
574     } else {
575         msg_Err(object, "Unknown crop format (%s)", newval.psz_string);
576     }
577     return VLC_SUCCESS;
578 }
579
580 static int CropBorderCallback(vlc_object_t *object, char const *cmd,
581                               vlc_value_t oldval, vlc_value_t newval, void *data)
582 {
583     vout_thread_t *vout = (vout_thread_t *)object;
584     VLC_UNUSED(cmd); VLC_UNUSED(oldval); VLC_UNUSED(data); VLC_UNUSED(newval);
585
586     vout_ControlChangeCropBorder(vout,
587                                  var_GetInteger(object, "crop-left"),
588                                  var_GetInteger(object, "crop-top"),
589                                  var_GetInteger(object, "crop-right"),
590                                  var_GetInteger(object, "crop-bottom"));
591     return VLC_SUCCESS;
592 }
593
594 static int AspectCallback( vlc_object_t *object, char const *cmd,
595                          vlc_value_t oldval, vlc_value_t newval, void *data )
596 {
597     vout_thread_t *vout = (vout_thread_t *)object;
598     VLC_UNUSED(cmd); VLC_UNUSED(oldval); VLC_UNUSED(data);
599     unsigned num, den;
600
601     if (sscanf(newval.psz_string, "%u:%u", &num, &den) == 2 &&
602         (num > 0) == (den > 0))
603         vout_ControlChangeSampleAspectRatio(vout, num, den);
604     else if (*newval.psz_string == '\0')
605         vout_ControlChangeSampleAspectRatio(vout, 0, 0);
606     return VLC_SUCCESS;
607 }
608
609 static int ScalingCallback( vlc_object_t *p_this, char const *psz_cmd,
610                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
611 {
612     vout_thread_t *p_vout = (vout_thread_t *)p_this;
613     (void)oldval; (void)newval; (void)p_data;
614
615     if( !strcmp( psz_cmd, "autoscale" ) )
616         vout_ControlChangeDisplayFilled( p_vout, newval.b_bool );
617     else if( !strcmp( psz_cmd, "scale" ) )
618         vout_ControlChangeZoom( p_vout, 1000 * newval.f_float, 1000 );
619
620     return VLC_SUCCESS;
621 }
622
623 static int OnTopCallback( vlc_object_t *p_this, char const *psz_cmd,
624                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
625 {
626     vout_thread_t *p_vout = (vout_thread_t *)p_this;
627     (void)psz_cmd; (void)oldval; (void)p_data;
628
629     vout_ControlChangeOnTop( p_vout, newval.b_bool );
630     return VLC_SUCCESS;
631 }
632
633 static int FullscreenCallback( vlc_object_t *p_this, char const *psz_cmd,
634                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
635 {
636     vout_thread_t *p_vout = (vout_thread_t *)p_this;
637     (void)psz_cmd; (void)p_data;
638
639     if( oldval.b_bool != newval.b_bool )
640         vout_ControlChangeFullscreen( p_vout, newval.b_bool );
641     return VLC_SUCCESS;
642 }
643
644 static int SnapshotCallback( vlc_object_t *p_this, char const *psz_cmd,
645                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
646 {
647     vout_thread_t *p_vout = (vout_thread_t *)p_this;
648     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
649     VLC_UNUSED(newval); VLC_UNUSED(p_data);
650
651     VoutSaveSnapshot( p_vout );
652     return VLC_SUCCESS;
653 }
654
655 static int VideoFilterCallback( vlc_object_t *p_this, char const *psz_cmd,
656                                 vlc_value_t oldval, vlc_value_t newval, void *p_data)
657 {
658     vout_thread_t *p_vout = (vout_thread_t *)p_this;
659     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data);
660
661     vout_ControlChangeFilters( p_vout, newval.psz_string );
662     return VLC_SUCCESS;
663 }
664
665 static int SubSourceCallback( vlc_object_t *p_this, char const *psz_cmd,
666                               vlc_value_t oldval, vlc_value_t newval, void *p_data)
667 {
668     vout_thread_t *p_vout = (vout_thread_t *)p_this;
669     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data);
670
671     vout_ControlChangeSubSources( p_vout, newval.psz_string );
672     return VLC_SUCCESS;
673 }
674
675 static int SubFilterCallback( vlc_object_t *p_this, char const *psz_cmd,
676                               vlc_value_t oldval, vlc_value_t newval, void *p_data)
677 {
678     vout_thread_t *p_vout = (vout_thread_t *)p_this;
679     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data);
680
681     vout_ControlChangeSubFilters( p_vout, newval.psz_string );
682     return VLC_SUCCESS;
683 }
684
685 static int SubMarginCallback( vlc_object_t *p_this, char const *psz_cmd,
686                               vlc_value_t oldval, vlc_value_t newval, void *p_data)
687 {
688     vout_thread_t *p_vout = (vout_thread_t *)p_this;
689     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data);
690
691     vout_ControlChangeSubMargin( p_vout, newval.i_int );
692     return VLC_SUCCESS;
693 }
694