]> git.sesse.net Git - vlc/blob - src/video_output/vout_intf.c
dba906723a4f4be6c410918708fa93e6f0a3aa76
[vlc] / src / video_output / vout_intf.c
1 /*****************************************************************************
2  * vout_intf.c : video output interface
3  *****************************************************************************
4  * Copyright (C) 2000-2004 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@videolan.org>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>                                                /* free() */
28
29 #include <vlc/vlc.h>
30 #include <vlc/intf.h>
31 #include <vlc_block.h>
32
33 #include "vlc_video.h"
34 #include "video_output.h"
35 #include "vlc_image.h"
36 #include "vlc_spu.h"
37
38 #include <snapshot.h>
39
40 /*****************************************************************************
41  * Local prototypes
42  *****************************************************************************/
43 static void InitWindowSize( vout_thread_t *, unsigned *, unsigned * );
44
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 * );
58
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
64  * window by itself.
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 )
70 {
71     intf_thread_t *p_intf = NULL;
72     vlc_list_t *p_list;
73     void *p_window;
74     vlc_value_t val;
75     int i;
76
77     /* Small kludge */
78     if( !var_Type( p_vout, "aspect-ratio" ) ) vout_IntfInit( p_vout );
79
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;
85
86     *pi_width_hint = p_vout->i_window_width;
87     *pi_height_hint = p_vout->i_window_height;
88
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;
92
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;
96
97     for( i = 0; i < p_list->i_count; i++ )
98     {
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;
101         p_intf = NULL;
102     }
103
104     if( !p_intf )
105     {
106         vlc_list_release( p_list );
107         return NULL;
108     }
109
110     vlc_object_yield( p_intf );
111     vlc_list_release( p_list );
112
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 );
115
116     if( !p_window ) vlc_object_release( p_intf );
117     else p_vout->p_parent_intf = p_intf;
118
119     return p_window;
120 }
121
122 void vout_ReleaseWindow( vout_thread_t *p_vout, void *p_window )
123 {
124     intf_thread_t *p_intf = p_vout->p_parent_intf;
125
126     if( !p_intf ) return;
127
128     vlc_mutex_lock( &p_intf->object_lock );
129     if( p_intf->b_dead )
130     {
131         vlc_mutex_unlock( &p_intf->object_lock );
132         return;
133     }
134
135     if( !p_intf->pf_release_window )
136     {
137         msg_Err( p_vout, "no pf_release_window");
138         vlc_mutex_unlock( &p_intf->object_lock );
139         vlc_object_release( p_intf );
140         return;
141     }
142
143     p_intf->pf_release_window( p_intf, p_window );
144
145     p_vout->p_parent_intf = NULL;
146     vlc_mutex_unlock( &p_intf->object_lock );
147     vlc_object_release( p_intf );
148 }
149
150 int vout_ControlWindow( vout_thread_t *p_vout, void *p_window,
151                         int i_query, va_list args )
152 {
153     intf_thread_t *p_intf = p_vout->p_parent_intf;
154     int i_ret;
155
156     if( !p_intf ) return VLC_EGENERIC;
157
158     vlc_mutex_lock( &p_intf->object_lock );
159     if( p_intf->b_dead )
160     {
161         vlc_mutex_unlock( &p_intf->object_lock );
162         return VLC_EGENERIC;
163     }
164
165     if( !p_intf->pf_control_window )
166     {
167         msg_Err( p_vout, "no pf_control_window");
168         vlc_mutex_unlock( &p_intf->object_lock );
169         return VLC_EGENERIC;
170     }
171
172     i_ret = p_intf->pf_control_window( p_intf, p_window, i_query, args );
173     vlc_mutex_unlock( &p_intf->object_lock );
174     return i_ret;
175 }
176
177 /*****************************************************************************
178  * vout_IntfInit: called during the vout creation to initialise misc things.
179  *****************************************************************************/
180 void vout_IntfInit( vout_thread_t *p_vout )
181 {
182     vlc_value_t val, text, old_val;
183     vlc_bool_t b_force_par = VLC_FALSE;
184
185     /* Create a few object variables we'll need later on */
186     var_Create( p_vout, "snapshot-path", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
187     var_Create( p_vout, "snapshot-format", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
188     var_Create( p_vout, "snapshot-preview", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
189     var_Create( p_vout, "width", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
190     var_Create( p_vout, "height", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
191     var_Create( p_vout, "align", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
192     var_Get( p_vout, "align", &val );
193     p_vout->i_alignment = val.i_int;
194
195     var_Create( p_vout, "video-x", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
196     var_Create( p_vout, "video-y", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
197
198     /* Zoom object var */
199     var_Create( p_vout, "zoom", VLC_VAR_FLOAT | VLC_VAR_ISCOMMAND |
200                 VLC_VAR_HASCHOICE | VLC_VAR_DOINHERIT );
201
202     text.psz_string = _("Zoom");
203     var_Change( p_vout, "zoom", VLC_VAR_SETTEXT, &text, NULL );
204
205     var_Get( p_vout, "zoom", &old_val );
206     if( old_val.f_float == 0.25 ||
207         old_val.f_float == 0.5 ||
208         old_val.f_float == 1 ||
209         old_val.f_float == 2 )
210     {
211         var_Change( p_vout, "zoom", VLC_VAR_DELCHOICE, &old_val, NULL );
212     }
213
214     val.f_float = 0.25; text.psz_string = _("1:4 Quarter");
215     var_Change( p_vout, "zoom", VLC_VAR_ADDCHOICE, &val, &text );
216     val.f_float = 0.5; text.psz_string = _("1:2 Half");
217     var_Change( p_vout, "zoom", VLC_VAR_ADDCHOICE, &val, &text );
218     val.f_float = 1; text.psz_string = _("1:1 Original");
219     var_Change( p_vout, "zoom", VLC_VAR_ADDCHOICE, &val, &text );
220     val.f_float = 2; text.psz_string = _("2:1 Double");
221     var_Change( p_vout, "zoom", VLC_VAR_ADDCHOICE, &val, &text );
222
223     var_Set( p_vout, "zoom", old_val );
224
225     var_AddCallback( p_vout, "zoom", ZoomCallback, NULL );
226
227     /* Crop object var */
228     var_Create( p_vout, "crop", VLC_VAR_STRING |
229                 VLC_VAR_HASCHOICE | VLC_VAR_DOINHERIT );
230
231     text.psz_string = _("Crop");
232     var_Change( p_vout, "crop", VLC_VAR_SETTEXT, &text, NULL );
233
234     val.psz_string = "";
235     var_Change( p_vout, "crop", VLC_VAR_DELCHOICE, &val, 0 );
236     val.psz_string = ""; text.psz_string = _("Default");
237     var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text );
238     val.psz_string = "001:1"; text.psz_string = "1:1";
239     var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text );
240     val.psz_string = "004:3"; text.psz_string = "4:3";
241     var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text );
242     val.psz_string = "16:09"; text.psz_string = "16:9";
243     var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text );
244     val.psz_string = "16:10"; text.psz_string = "16:10";
245     var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text );
246     val.psz_string = "221:100"; text.psz_string = "221:100";
247     var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text );
248     val.psz_string = "5:4"; text.psz_string = "5:4";
249     var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text );
250
251     var_AddCallback( p_vout, "crop", CropCallback, NULL );
252     var_Get( p_vout, "crop", &old_val );
253     if( old_val.psz_string && *old_val.psz_string )
254         var_Change( p_vout, "crop", VLC_VAR_TRIGGER_CALLBACKS, 0, 0 );
255     if( old_val.psz_string ) free( old_val.psz_string );
256
257     /* Monitor pixel aspect-ratio */
258     var_Create( p_vout, "monitor-par", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
259     var_Get( p_vout, "monitor-par", &val );
260     if( val.psz_string && *val.psz_string )
261     {
262         char *psz_parser = strchr( val.psz_string, ':' );
263         unsigned int i_aspect_num = 0, i_aspect_den = 0;
264         float i_aspect = 0;
265         if( psz_parser )
266         {
267             i_aspect_num = strtol( val.psz_string, 0, 0 );
268             i_aspect_den = strtol( ++psz_parser, 0, 0 );
269         }
270         else
271         {
272             i_aspect = atof( val.psz_string );
273             vlc_ureduce( &i_aspect_num, &i_aspect_den,
274                          i_aspect *VOUT_ASPECT_FACTOR, VOUT_ASPECT_FACTOR, 0 );
275         }
276         if( !i_aspect_num || !i_aspect_den ) i_aspect_num = i_aspect_den = 1;
277
278         p_vout->i_par_num = i_aspect_num;
279         p_vout->i_par_den = i_aspect_den;
280
281         vlc_ureduce( &p_vout->i_par_num, &p_vout->i_par_den,
282                      p_vout->i_par_num, p_vout->i_par_den, 0 );
283
284         msg_Dbg( p_vout, "monitor pixel aspect-ratio overriding: %i:%i",
285                  p_vout->i_par_num, p_vout->i_par_den );
286         b_force_par = VLC_TRUE;
287     }
288     if( val.psz_string ) free( val.psz_string );
289
290     /* Aspect-ratio object var */
291     var_Create( p_vout, "aspect-ratio", VLC_VAR_STRING |
292                 VLC_VAR_HASCHOICE | VLC_VAR_DOINHERIT );
293
294     text.psz_string = _("Aspect-ratio");
295     var_Change( p_vout, "aspect-ratio", VLC_VAR_SETTEXT, &text, NULL );
296
297     val.psz_string = "";
298     var_Change( p_vout, "aspect-ratio", VLC_VAR_DELCHOICE, &val, 0 );
299     val.psz_string = ""; text.psz_string = _("Default");
300     var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
301     val.psz_string = "001:1"; text.psz_string = "1:1";
302     var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
303     val.psz_string = "004:3"; text.psz_string = "4:3";
304     var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
305     val.psz_string = "16:09"; text.psz_string = "16:9";
306     var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
307     val.psz_string = "16:10"; text.psz_string = "16:10";
308     var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
309     val.psz_string = "221:100"; text.psz_string = "221:100";
310     var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
311     val.psz_string = "5:4"; text.psz_string = "5:4";
312     var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
313
314     var_AddCallback( p_vout, "aspect-ratio", AspectCallback, NULL );
315     var_Get( p_vout, "aspect-ratio", &old_val );
316     if( (old_val.psz_string && *old_val.psz_string) || b_force_par )
317         var_Change( p_vout, "aspect-ratio", VLC_VAR_TRIGGER_CALLBACKS, 0, 0 );
318     if( old_val.psz_string ) free( old_val.psz_string );
319
320     /* Initialize the dimensions of the video window */
321     InitWindowSize( p_vout, &p_vout->i_window_width,
322                     &p_vout->i_window_height );
323
324     /* Add a variable to indicate if the window should be on top of others */
325     var_Create( p_vout, "video-on-top", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
326     text.psz_string = _("Always on top");
327     var_Change( p_vout, "video-on-top", VLC_VAR_SETTEXT, &text, NULL );
328     var_AddCallback( p_vout, "video-on-top", OnTopCallback, NULL );
329
330     /* Add a variable to indicate whether we want window decoration or not */
331     var_Create( p_vout, "video-deco", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
332
333     /* Add a fullscreen variable */
334     var_Create( p_vout, "fullscreen", VLC_VAR_BOOL );
335     text.psz_string = _("Fullscreen");
336     var_Change( p_vout, "fullscreen", VLC_VAR_SETTEXT, &text, NULL );
337     var_Change( p_vout, "fullscreen", VLC_VAR_INHERITVALUE, &val, NULL );
338     if( val.b_bool )
339     {
340         /* user requested fullscreen */
341         p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
342     }
343     var_AddCallback( p_vout, "fullscreen", FullscreenCallback, NULL );
344
345     /* Add a snapshot variable */
346     var_Create( p_vout, "video-snapshot", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
347     text.psz_string = _("Snapshot");
348     var_Change( p_vout, "video-snapshot", VLC_VAR_SETTEXT, &text, NULL );
349     var_AddCallback( p_vout, "video-snapshot", SnapshotCallback, NULL );
350
351     /* Mouse coordinates */
352     var_Create( p_vout, "mouse-x", VLC_VAR_INTEGER );
353     var_Create( p_vout, "mouse-y", VLC_VAR_INTEGER );
354     var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER );
355     var_Create( p_vout, "mouse-moved", VLC_VAR_BOOL );
356     var_Create( p_vout, "mouse-clicked", VLC_VAR_INTEGER );
357
358     var_Create( p_vout, "intf-change", VLC_VAR_BOOL );
359     val.b_bool = VLC_TRUE;
360     var_Set( p_vout, "intf-change", val );
361 }
362
363 /*****************************************************************************
364  * vout_Snapshot: generates a snapshot.
365  *****************************************************************************/
366 int vout_Snapshot( vout_thread_t *p_vout, picture_t *p_pic )
367 {
368     image_handler_t *p_image = image_HandlerCreate( p_vout );
369     video_format_t fmt_in = {0}, fmt_out = {0};
370     char *psz_filename;
371     subpicture_t *p_subpic;
372     picture_t *p_pif;
373     vlc_value_t val, format;
374     int i_ret;
375
376     var_Get( p_vout, "snapshot-path", &val );
377     if( val.psz_string && !*val.psz_string )
378     {
379         free( val.psz_string );
380         val.psz_string = 0;
381     }
382
383     /* Embedded snapshot : if snapshot-path == object:object-id, then
384        create a snapshot_t* and store it in
385        object(object-id)->p_private, then unlock and signal the
386        waiting object.
387      */
388     if( val.psz_string && !strncmp( val.psz_string, "object:", 7 ) )
389     {
390         int i_id;
391         vlc_object_t* p_dest;
392         block_t *p_block;
393         snapshot_t *p_snapshot;
394         int i_size;
395
396         /* Destination object-id is following object: */
397         i_id = atoi( &val.psz_string[7] );
398         p_dest = ( vlc_object_t* )vlc_current_object( i_id );
399         if( !p_dest )
400         {
401             msg_Err( p_vout, "Cannot find calling object" );
402             image_HandlerDelete( p_image );
403             return VLC_EGENERIC;
404         }
405         /* Object must be locked. We will unlock it once we get the
406            snapshot and written it to p_private */
407         p_dest->p_private = NULL;
408
409         /* Save the snapshot to a memory zone */
410         fmt_in = p_vout->fmt_in;
411         fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
412         /* FIXME: should not be hardcoded. We should be able to
413            specify the snapshot size (snapshot-width and snapshot-height). */
414         fmt_out.i_width = 320;
415         fmt_out.i_height = 200;
416         fmt_out.i_chroma = VLC_FOURCC( 'p','n','g',' ' );
417         p_block = ( block_t* ) image_Write( p_image, p_pic, &fmt_in, &fmt_out );
418         if( !p_block ) 
419         {
420             msg_Err( p_vout, "Could not get snapshot" );
421             image_HandlerDelete( p_image );
422             vlc_cond_signal( &p_dest->object_wait );
423             vlc_mutex_unlock( &p_dest->object_lock );
424             vlc_object_release( p_dest );
425             return VLC_EGENERIC;
426         }
427
428         /* Copy the p_block data to a snapshot structure */
429         /* FIXME: get the timestamp */
430         p_snapshot = ( snapshot_t* ) malloc( sizeof( snapshot_t ) );
431         if( !p_snapshot )
432         {
433             block_Release( p_block );
434             image_HandlerDelete( p_image );
435             vlc_cond_signal( &p_dest->object_wait );
436             vlc_mutex_unlock( &p_dest->object_lock );
437             vlc_object_release( p_dest );
438             return VLC_ENOMEM;
439         }
440
441         i_size = p_block->i_buffer;
442
443         p_snapshot->i_width = fmt_out.i_width;
444         p_snapshot->i_height = fmt_out.i_height;
445         p_snapshot->i_datasize = i_size;
446         p_snapshot->date = p_block->i_pts; /* FIXME ?? */
447         p_snapshot->p_data = ( char* ) malloc( i_size );
448         if( !p_snapshot->p_data )
449         {
450             block_Release( p_block );
451             free( p_snapshot );
452             image_HandlerDelete( p_image );
453             vlc_cond_signal( &p_dest->object_wait );
454             vlc_mutex_unlock( &p_dest->object_lock );
455             vlc_object_release( p_dest );
456             return VLC_ENOMEM;
457         }
458         memcpy( p_snapshot->p_data, p_block->p_buffer, p_block->i_buffer );
459
460         p_dest->p_private = p_snapshot;
461
462         block_Release( p_block );
463         
464         /* Unlock the object */
465         vlc_cond_signal( &p_dest->object_wait );
466         vlc_mutex_unlock( &p_dest->object_lock );
467         vlc_object_release( p_dest );
468
469         image_HandlerDelete( p_image );
470         return VLC_SUCCESS;
471     }
472
473
474 #if defined(__APPLE__) || defined(SYS_BEOS)
475     if( !val.psz_string && p_vout->p_vlc->psz_homedir )
476     {
477         asprintf( &val.psz_string, "%s/Desktop",
478                   p_vout->p_vlc->psz_homedir );
479     }
480
481 #elif defined(WIN32) && !defined(UNDER_CE)
482     if( !val.psz_string && p_vout->p_vlc->psz_homedir )
483     {
484         /* Get the My Pictures folder path */
485
486         char *p_mypicturesdir = NULL;
487         typedef HRESULT (WINAPI *SHGETFOLDERPATH)( HWND, int, HANDLE, DWORD,
488                                                    LPSTR );
489         #ifndef CSIDL_FLAG_CREATE
490         #   define CSIDL_FLAG_CREATE 0x8000
491         #endif
492         #ifndef CSIDL_MYPICTURES
493         #   define CSIDL_MYPICTURES 0x27
494         #endif
495         #ifndef SHGFP_TYPE_CURRENT
496         #   define SHGFP_TYPE_CURRENT 0
497         #endif
498
499         HINSTANCE shfolder_dll;
500         SHGETFOLDERPATH SHGetFolderPath ;
501
502         /* load the shfolder dll to retrieve SHGetFolderPath */
503         if( ( shfolder_dll = LoadLibrary( _T("SHFolder.dll") ) ) != NULL )
504         {
505             SHGetFolderPath = (void *)GetProcAddress( shfolder_dll,
506                                                       _T("SHGetFolderPathA") );
507             if( SHGetFolderPath != NULL )
508             {
509                 p_mypicturesdir = (char *)malloc( MAX_PATH );
510                 if( p_mypicturesdir ) 
511                 {
512
513                     if( S_OK != SHGetFolderPath( NULL,
514                                         CSIDL_MYPICTURES | CSIDL_FLAG_CREATE,
515                                         NULL, SHGFP_TYPE_CURRENT,
516                                         p_mypicturesdir ) )
517                     {
518                         free( p_mypicturesdir );
519                         p_mypicturesdir = NULL;
520                     }
521                 }
522             }
523             FreeLibrary( shfolder_dll );
524         }
525
526         if( p_mypicturesdir == NULL )
527         {
528             asprintf( &val.psz_string, "%s/" CONFIG_DIR,
529                       p_vout->p_vlc->psz_homedir );
530         }
531         else
532         {
533             asprintf( &val.psz_string, p_mypicturesdir );
534             free( p_mypicturesdir );
535         }
536     }
537
538 #else
539     if( !val.psz_string && p_vout->p_vlc->psz_homedir )
540     {
541         asprintf( &val.psz_string, "%s/" CONFIG_DIR,
542                   p_vout->p_vlc->psz_homedir );
543     }
544 #endif
545
546     if( !val.psz_string )
547     {
548         msg_Err( p_vout, "no directory specified for snapshots" );
549         return VLC_EGENERIC;
550     }
551     var_Get( p_vout, "snapshot-format", &format );
552     if( !format.psz_string || !*format.psz_string )
553     {
554         if( format.psz_string ) free( format.psz_string );
555         format.psz_string = strdup( "png" );
556     }
557
558     asprintf( &psz_filename, "%s/vlcsnap-%u.%s", val.psz_string,
559               (unsigned int)(p_pic->date / 100000) & 0xFFFFFF,
560               format.psz_string );
561     free( val.psz_string );
562     free( format.psz_string );
563
564     /* Save the snapshot */
565     fmt_in = p_vout->fmt_in;
566     fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
567     i_ret = image_WriteUrl( p_image, p_pic, &fmt_in, &fmt_out, psz_filename );
568     if( i_ret != VLC_SUCCESS )
569     {
570         msg_Err( p_vout, "could not create snapshot %s", psz_filename );
571         free( psz_filename );
572         image_HandlerDelete( p_image );
573         return VLC_EGENERIC;
574     }
575
576     msg_Dbg( p_vout, "snapshot taken (%s)", psz_filename );
577     free( psz_filename );
578
579     if( var_GetBool( p_vout, "snapshot-preview" ) )
580     {
581         /* Inject a subpicture with the snapshot */
582         memset( &fmt_out, 0, sizeof(fmt_out) );
583         fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
584         p_pif = image_Convert( p_image, p_pic, &fmt_in, &fmt_out );
585         image_HandlerDelete( p_image );
586         if( !p_pif ) return VLC_EGENERIC;
587
588         p_subpic = spu_CreateSubpicture( p_vout->p_spu );
589         if( p_subpic == NULL )
590         {
591              p_pif->pf_release( p_pif );
592              return VLC_EGENERIC;
593         }
594
595         p_subpic->i_channel = 0;
596         p_subpic->i_start = mdate();
597         p_subpic->i_stop = mdate() + 4000000;
598         p_subpic->b_ephemer = VLC_TRUE;
599         p_subpic->b_fade = VLC_TRUE;
600         p_subpic->i_original_picture_width = p_vout->render.i_width * 4;
601         p_subpic->i_original_picture_height = p_vout->render.i_height * 4;
602
603         p_subpic->p_region = spu_CreateRegion( p_vout->p_spu, &fmt_out );
604         vout_CopyPicture( p_image->p_parent, &p_subpic->p_region->picture,
605                           p_pif );
606         p_pif->pf_release( p_pif );
607
608         spu_DisplaySubpicture( p_vout->p_spu, p_subpic );
609     }
610     else
611     {
612         image_HandlerDelete( p_image );
613     }
614
615     return VLC_SUCCESS;
616 }
617
618 /*****************************************************************************
619  * vout_ControlDefault: default methods for video output control.
620  *****************************************************************************/
621 int vout_vaControlDefault( vout_thread_t *p_vout, int i_query, va_list args )
622 {
623     switch( i_query )
624     {
625     case VOUT_REPARENT:
626     case VOUT_CLOSE:
627         if( p_vout->p_parent_intf )
628         {
629             vlc_object_release( p_vout->p_parent_intf );
630             p_vout->p_parent_intf = NULL;
631         }
632         return VLC_SUCCESS;
633         break;
634
635     case VOUT_SNAPSHOT:
636         p_vout->b_snapshot = VLC_TRUE;
637         return VLC_SUCCESS;
638         break;
639
640     default:
641         msg_Dbg( p_vout, "control query not supported" );
642         return VLC_EGENERIC;
643     }
644 }
645
646 /*****************************************************************************
647  * InitWindowSize: find the initial dimensions the video window should have.
648  *****************************************************************************
649  * This function will check the "width", "height" and "zoom" config options and
650  * will calculate the size that the video window should have.
651  *****************************************************************************/
652 static void InitWindowSize( vout_thread_t *p_vout, unsigned *pi_width,
653                             unsigned *pi_height )
654 {
655     vlc_value_t val;
656     int i_width, i_height;
657     uint64_t ll_zoom;
658
659 #define FP_FACTOR 1000                             /* our fixed point factor */
660
661     var_Get( p_vout, "width", &val );
662     i_width = val.i_int;
663     var_Get( p_vout, "height", &val );
664     i_height = val.i_int;
665     var_Get( p_vout, "zoom", &val );
666     ll_zoom = (uint64_t)( FP_FACTOR * val.f_float );
667
668     if( i_width > 0 && i_height > 0)
669     {
670         *pi_width = (int)( i_width * ll_zoom / FP_FACTOR );
671         *pi_height = (int)( i_height * ll_zoom / FP_FACTOR );
672         goto initwsize_end;
673     }
674     else if( i_width > 0 )
675     {
676         *pi_width = (int)( i_width * ll_zoom / FP_FACTOR );
677         *pi_height = (int)( p_vout->fmt_in.i_visible_height * ll_zoom *
678             p_vout->fmt_in.i_sar_den * i_width / p_vout->fmt_in.i_sar_num /
679             FP_FACTOR / p_vout->fmt_in.i_visible_width );
680         goto initwsize_end;
681     }
682     else if( i_height > 0 )
683     {
684         *pi_height = (int)( i_height * ll_zoom / FP_FACTOR );
685         *pi_width = (int)( p_vout->fmt_in.i_visible_width * ll_zoom *
686             p_vout->fmt_in.i_sar_num * i_height / p_vout->fmt_in.i_sar_den /
687             FP_FACTOR / p_vout->fmt_in.i_visible_height );
688         goto initwsize_end;
689     }
690
691     if( p_vout->fmt_in.i_sar_num >= p_vout->fmt_in.i_sar_den )
692     {
693         *pi_width = (int)( p_vout->fmt_in.i_visible_width * ll_zoom *
694             p_vout->fmt_in.i_sar_num / p_vout->fmt_in.i_sar_den / FP_FACTOR );
695         *pi_height = (int)( p_vout->fmt_in.i_visible_height * ll_zoom 
696             / FP_FACTOR );
697     }
698     else
699     {
700         *pi_width = (int)( p_vout->fmt_in.i_visible_width * ll_zoom 
701             / FP_FACTOR );
702         *pi_height = (int)( p_vout->fmt_in.i_visible_height * ll_zoom *
703             p_vout->fmt_in.i_sar_den / p_vout->fmt_in.i_sar_num / FP_FACTOR );
704     }
705
706 initwsize_end:
707     msg_Dbg( p_vout, "window size: %dx%d", p_vout->i_window_width, 
708              p_vout->i_window_height );
709
710 #undef FP_FACTOR
711 }
712
713 /*****************************************************************************
714  * Object variables callbacks
715  *****************************************************************************/
716 static int ZoomCallback( vlc_object_t *p_this, char const *psz_cmd,
717                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
718 {
719     vout_thread_t *p_vout = (vout_thread_t *)p_this;
720     InitWindowSize( p_vout, &p_vout->i_window_width,
721                     &p_vout->i_window_height );
722     vout_Control( p_vout, VOUT_SET_SIZE, p_vout->i_window_width, 
723                   p_vout->i_window_height );
724     return VLC_SUCCESS;
725 }
726
727 static int CropCallback( vlc_object_t *p_this, char const *psz_cmd,
728                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
729 {
730     vout_thread_t *p_vout = (vout_thread_t *)p_this;
731     int64_t i_aspect_num, i_aspect_den;
732     unsigned int i_width, i_height;
733
734     char *psz_end, *psz_parser = strchr( newval.psz_string, ':' );
735
736     /* Restore defaults */
737     p_vout->fmt_in.i_x_offset = p_vout->fmt_render.i_x_offset;
738     p_vout->fmt_in.i_visible_width = p_vout->fmt_render.i_visible_width;
739     p_vout->fmt_in.i_y_offset = p_vout->fmt_render.i_y_offset;
740     p_vout->fmt_in.i_visible_height = p_vout->fmt_render.i_visible_height;
741
742     if( !psz_parser ) goto crop_end;
743
744     i_aspect_num = strtol( newval.psz_string, &psz_end, 0 );
745     if( psz_end == newval.psz_string || !i_aspect_num ) goto crop_end;
746
747     i_aspect_den = strtol( ++psz_parser, &psz_end, 0 );
748     if( psz_end == psz_parser || !i_aspect_den ) goto crop_end;
749
750     i_width = p_vout->fmt_in.i_sar_den * p_vout->fmt_render.i_visible_height *
751         i_aspect_num / i_aspect_den / p_vout->fmt_in.i_sar_num;
752     i_height = p_vout->fmt_render.i_visible_width * p_vout->fmt_in.i_sar_num *
753         i_aspect_den / i_aspect_num / p_vout->fmt_in.i_sar_den;
754
755     if( i_width < p_vout->fmt_render.i_visible_width )
756     {
757         p_vout->fmt_in.i_x_offset = p_vout->fmt_render.i_x_offset +
758             (p_vout->fmt_render.i_visible_width - i_width) / 2;
759         p_vout->fmt_in.i_visible_width = i_width;
760     }
761     else
762     {
763         p_vout->fmt_in.i_y_offset = p_vout->fmt_render.i_y_offset +
764             (p_vout->fmt_render.i_visible_height - i_height) / 2;
765         p_vout->fmt_in.i_visible_height = i_height;
766     }
767
768  crop_end:
769     InitWindowSize( p_vout, &p_vout->i_window_width,
770                     &p_vout->i_window_height );
771
772     p_vout->i_changes |= VOUT_CROP_CHANGE;
773
774     msg_Dbg( p_vout, "cropping picture %ix%i to %i,%i,%ix%i",
775              p_vout->fmt_in.i_width, p_vout->fmt_in.i_height,
776              p_vout->fmt_in.i_x_offset, p_vout->fmt_in.i_y_offset,
777              p_vout->fmt_in.i_visible_width,
778              p_vout->fmt_in.i_visible_height );
779
780     return VLC_SUCCESS;
781 }
782
783 static int AspectCallback( vlc_object_t *p_this, char const *psz_cmd,
784                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
785 {
786     vout_thread_t *p_vout = (vout_thread_t *)p_this;
787     unsigned int i_aspect_num, i_aspect_den, i_sar_num, i_sar_den;
788     vlc_value_t val;
789
790     char *psz_end, *psz_parser = strchr( newval.psz_string, ':' );
791
792     /* Restore defaults */
793     p_vout->fmt_in.i_sar_num = p_vout->fmt_render.i_sar_num;
794     p_vout->fmt_in.i_sar_den = p_vout->fmt_render.i_sar_den;
795     p_vout->fmt_in.i_aspect = p_vout->fmt_render.i_aspect;
796     p_vout->render.i_aspect = p_vout->fmt_render.i_aspect;
797
798     if( !psz_parser ) goto aspect_end;
799
800     i_aspect_num = strtol( newval.psz_string, &psz_end, 0 );
801     if( psz_end == newval.psz_string || !i_aspect_num ) goto aspect_end;
802
803     i_aspect_den = strtol( ++psz_parser, &psz_end, 0 );
804     if( psz_end == psz_parser || !i_aspect_den ) goto aspect_end;
805
806     i_sar_num = i_aspect_num * p_vout->fmt_render.i_visible_height;
807     i_sar_den = i_aspect_den * p_vout->fmt_render.i_visible_width;
808     vlc_ureduce( &i_sar_num, &i_sar_den, i_sar_num, i_sar_den, 0 );
809     p_vout->fmt_in.i_sar_num = i_sar_num;
810     p_vout->fmt_in.i_sar_den = i_sar_den;
811     p_vout->fmt_in.i_aspect = i_aspect_num * VOUT_ASPECT_FACTOR / i_aspect_den;
812     p_vout->render.i_aspect = p_vout->fmt_in.i_aspect;
813
814  aspect_end:
815     if( p_vout->i_par_num && p_vout->i_par_den )
816     {
817         p_vout->fmt_in.i_sar_num *= p_vout->i_par_den;
818         p_vout->fmt_in.i_sar_den *= p_vout->i_par_num;
819         p_vout->fmt_in.i_aspect = p_vout->fmt_in.i_aspect *
820             p_vout->i_par_den / p_vout->i_par_num;
821         p_vout->render.i_aspect = p_vout->fmt_in.i_aspect;
822     }
823
824     p_vout->i_changes |= VOUT_ASPECT_CHANGE;
825
826     vlc_ureduce( &i_aspect_num, &i_aspect_den,
827                  p_vout->fmt_in.i_aspect, VOUT_ASPECT_FACTOR, 0 );
828     msg_Dbg( p_vout, "new aspect-ratio %i:%i, sample aspect-ratio %i:%i",
829              i_aspect_num, i_aspect_den,
830              p_vout->fmt_in.i_sar_num, p_vout->fmt_in.i_sar_den );
831
832     var_Get( p_vout, "crop", &val );
833     return CropCallback( p_this, 0, val, val, 0 );
834
835     return VLC_SUCCESS;
836 }
837
838 static int OnTopCallback( vlc_object_t *p_this, char const *psz_cmd,
839                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
840 {
841     vout_thread_t *p_vout = (vout_thread_t *)p_this;
842     playlist_t *p_playlist;
843     vout_Control( p_vout, VOUT_SET_STAY_ON_TOP, newval.b_bool );
844
845     p_playlist = (playlist_t *)vlc_object_find( p_this, VLC_OBJECT_PLAYLIST,
846                                                  FIND_PARENT );
847     if( p_playlist )
848     {
849         /* Modify playlist as well because the vout might have to be restarted */
850         var_Create( p_playlist, "video-on-top", VLC_VAR_BOOL );
851         var_Set( p_playlist, "video-on-top", newval );
852
853         vlc_object_release( p_playlist );
854     }
855     return VLC_SUCCESS;
856 }
857
858 static int FullscreenCallback( vlc_object_t *p_this, char const *psz_cmd,
859                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
860 {
861     vout_thread_t *p_vout = (vout_thread_t *)p_this;
862     playlist_t *p_playlist;
863     vlc_value_t val;
864
865     p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
866
867     p_playlist = (playlist_t *)vlc_object_find( p_this, VLC_OBJECT_PLAYLIST,
868                                                  FIND_PARENT );
869     if( p_playlist )
870     {
871         /* Modify playlist as well because the vout might have to be restarted */
872         var_Create( p_playlist, "fullscreen", VLC_VAR_BOOL );
873         var_Set( p_playlist, "fullscreen", newval );
874
875         vlc_object_release( p_playlist );
876     }
877
878     /* Disable "always on top" in fullscreen mode */
879     var_Get( p_vout, "video-on-top", &val );
880     if( newval.b_bool && val.b_bool )
881     {
882         val.b_bool = VLC_FALSE;
883         vout_Control( p_vout, VOUT_SET_STAY_ON_TOP, val.b_bool );
884     }
885     else if( !newval.b_bool && val.b_bool )
886     {
887         vout_Control( p_vout, VOUT_SET_STAY_ON_TOP, val.b_bool );
888     }
889
890     val.b_bool = VLC_TRUE;
891     var_Set( p_vout, "intf-change", val );
892     return VLC_SUCCESS;
893 }
894
895 static int SnapshotCallback( vlc_object_t *p_this, char const *psz_cmd,
896                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
897 {
898     vout_thread_t *p_vout = (vout_thread_t *)p_this;
899     vout_Control( p_vout, VOUT_SNAPSHOT );
900     return VLC_SUCCESS;
901 }