]> git.sesse.net Git - vlc/blob - src/video_output/vout_intf.c
add boolean option "snapshot-preview" to enable/disable the snapshot's
[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:10"; text.psz_string = "16:10";
243     var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text );
244     val.psz_string = "16:9"; text.psz_string = "16:9";
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
249     var_AddCallback( p_vout, "crop", CropCallback, NULL );
250     var_Get( p_vout, "crop", &old_val );
251     if( old_val.psz_string && *old_val.psz_string )
252         var_Change( p_vout, "crop", VLC_VAR_TRIGGER_CALLBACKS, 0, 0 );
253     if( old_val.psz_string ) free( old_val.psz_string );
254
255     /* Monitor pixel aspect-ratio */
256     var_Create( p_vout, "monitor-par", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
257     var_Get( p_vout, "monitor-par", &val );
258     if( val.psz_string && *val.psz_string )
259     {
260         char *psz_parser = strchr( val.psz_string, ':' );
261         unsigned int i_aspect_num = 0, i_aspect_den = 0;
262         float i_aspect = 0;
263         if( psz_parser )
264         {
265             i_aspect_num = strtol( val.psz_string, 0, 0 );
266             i_aspect_den = strtol( ++psz_parser, 0, 0 );
267         }
268         else
269         {
270             i_aspect = atof( val.psz_string );
271             vlc_ureduce( &i_aspect_num, &i_aspect_den,
272                          i_aspect *VOUT_ASPECT_FACTOR, VOUT_ASPECT_FACTOR, 0 );
273         }
274         if( !i_aspect_num || !i_aspect_den ) i_aspect_num = i_aspect_den = 1;
275
276         p_vout->i_par_num = i_aspect_num;
277         p_vout->i_par_den = i_aspect_den;
278
279         vlc_ureduce( &p_vout->i_par_num, &p_vout->i_par_den,
280                      p_vout->i_par_num, p_vout->i_par_den, 0 );
281
282         msg_Dbg( p_vout, "monitor pixel aspect-ratio overriding: %i:%i",
283                  p_vout->i_par_num, p_vout->i_par_den );
284         b_force_par = VLC_TRUE;
285     }
286     if( val.psz_string ) free( val.psz_string );
287
288     /* Aspect-ratio object var */
289     var_Create( p_vout, "aspect-ratio", VLC_VAR_STRING |
290                 VLC_VAR_HASCHOICE | VLC_VAR_DOINHERIT );
291
292     text.psz_string = _("Aspect-ratio");
293     var_Change( p_vout, "aspect-ratio", VLC_VAR_SETTEXT, &text, NULL );
294
295     val.psz_string = "";
296     var_Change( p_vout, "aspect-ratio", VLC_VAR_DELCHOICE, &val, 0 );
297     val.psz_string = ""; text.psz_string = _("Default");
298     var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
299     val.psz_string = "001:1"; text.psz_string = "1:1";
300     var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
301     val.psz_string = "004:3"; text.psz_string = "4:3";
302     var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
303     val.psz_string = "16:10"; text.psz_string = "16:10";
304     var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
305     val.psz_string = "16:9"; text.psz_string = "16:9";
306     var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
307     val.psz_string = "221:100"; text.psz_string = "221:100";
308     var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
309
310     var_AddCallback( p_vout, "aspect-ratio", AspectCallback, NULL );
311     var_Get( p_vout, "aspect-ratio", &old_val );
312     if( (old_val.psz_string && *old_val.psz_string) || b_force_par )
313         var_Change( p_vout, "aspect-ratio", VLC_VAR_TRIGGER_CALLBACKS, 0, 0 );
314     if( old_val.psz_string ) free( old_val.psz_string );
315
316     /* Initialize the dimensions of the video window */
317     InitWindowSize( p_vout, &p_vout->i_window_width,
318                     &p_vout->i_window_height );
319
320     /* Add a variable to indicate if the window should be on top of others */
321     var_Create( p_vout, "video-on-top", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
322     text.psz_string = _("Always on top");
323     var_Change( p_vout, "video-on-top", VLC_VAR_SETTEXT, &text, NULL );
324     var_AddCallback( p_vout, "video-on-top", OnTopCallback, NULL );
325
326     /* Add a variable to indicate whether we want window decoration or not */
327     var_Create( p_vout, "video-deco", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
328
329     /* Add a fullscreen variable */
330     var_Create( p_vout, "fullscreen", VLC_VAR_BOOL );
331     text.psz_string = _("Fullscreen");
332     var_Change( p_vout, "fullscreen", VLC_VAR_SETTEXT, &text, NULL );
333     var_Change( p_vout, "fullscreen", VLC_VAR_INHERITVALUE, &val, NULL );
334     if( val.b_bool )
335     {
336         /* user requested fullscreen */
337         p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
338     }
339     var_AddCallback( p_vout, "fullscreen", FullscreenCallback, NULL );
340
341     /* Add a snapshot variable */
342     var_Create( p_vout, "video-snapshot", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
343     text.psz_string = _("Snapshot");
344     var_Change( p_vout, "video-snapshot", VLC_VAR_SETTEXT, &text, NULL );
345     var_AddCallback( p_vout, "video-snapshot", SnapshotCallback, NULL );
346
347     /* Mouse coordinates */
348     var_Create( p_vout, "mouse-x", VLC_VAR_INTEGER );
349     var_Create( p_vout, "mouse-y", VLC_VAR_INTEGER );
350     var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER );
351     var_Create( p_vout, "mouse-moved", VLC_VAR_BOOL );
352     var_Create( p_vout, "mouse-clicked", VLC_VAR_INTEGER );
353
354     var_Create( p_vout, "intf-change", VLC_VAR_BOOL );
355     val.b_bool = VLC_TRUE;
356     var_Set( p_vout, "intf-change", val );
357 }
358
359 /*****************************************************************************
360  * vout_Snapshot: generates a snapshot.
361  *****************************************************************************/
362 int vout_Snapshot( vout_thread_t *p_vout, picture_t *p_pic )
363 {
364     image_handler_t *p_image = image_HandlerCreate( p_vout );
365     video_format_t fmt_in = {0}, fmt_out = {0};
366     char *psz_filename;
367     subpicture_t *p_subpic;
368     picture_t *p_pif;
369     vlc_value_t val, format;
370     int i_ret;
371
372     var_Get( p_vout, "snapshot-path", &val );
373     if( val.psz_string && !*val.psz_string )
374     {
375         free( val.psz_string );
376         val.psz_string = 0;
377     }
378
379 #if defined(SYS_DARWIN) || defined(SYS_BEOS)
380     if( !val.psz_string && p_vout->p_vlc->psz_homedir )
381     {
382         asprintf( &val.psz_string, "%s/Desktop",
383                   p_vout->p_vlc->psz_homedir );
384     }
385
386 #elif defined(WIN32) && !defined(UNDER_CE)
387     if( !val.psz_string && p_vout->p_vlc->psz_homedir )
388     {
389         /* Get the My Pictures folder path */
390
391         char *p_mypicturesdir = NULL;
392         typedef HRESULT (WINAPI *SHGETFOLDERPATH)( HWND, int, HANDLE, DWORD,
393                                                    LPSTR );
394         #ifndef CSIDL_FLAG_CREATE
395         #   define CSIDL_FLAG_CREATE 0x8000
396         #endif
397         #ifndef CSIDL_MYPICTURES
398         #   define CSIDL_MYPICTURES 0x27
399         #endif
400         #ifndef SHGFP_TYPE_CURRENT
401         #   define SHGFP_TYPE_CURRENT 0
402         #endif
403
404         HINSTANCE shfolder_dll;
405         SHGETFOLDERPATH SHGetFolderPath ;
406
407         /* load the shfolder dll to retrieve SHGetFolderPath */
408         if( ( shfolder_dll = LoadLibrary( _T("SHFolder.dll") ) ) != NULL )
409         {
410             SHGetFolderPath = (void *)GetProcAddress( shfolder_dll,
411                                                       _T("SHGetFolderPathA") );
412             if( SHGetFolderPath != NULL )
413             {
414                 p_mypicturesdir = (char *)malloc( MAX_PATH );
415                 if( p_mypicturesdir ) 
416                 {
417
418                     if( S_OK != SHGetFolderPath( NULL,
419                                         CSIDL_MYPICTURES | CSIDL_FLAG_CREATE,
420                                         NULL, SHGFP_TYPE_CURRENT,
421                                         p_mypicturesdir ) )
422                     {
423                         free( p_mypicturesdir );
424                         p_mypicturesdir = NULL;
425                     }
426                 }
427             }
428             FreeLibrary( shfolder_dll );
429         }
430
431         if( p_mypicturesdir == NULL )
432         {
433             asprintf( &val.psz_string, "%s/" CONFIG_DIR,
434                       p_vout->p_vlc->psz_homedir );
435         }
436         else
437         {
438             asprintf( &val.psz_string, p_mypicturesdir );
439             free( p_mypicturesdir );
440         }
441     }
442
443 #else
444     if( !val.psz_string && p_vout->p_vlc->psz_homedir )
445     {
446         asprintf( &val.psz_string, "%s/" CONFIG_DIR,
447                   p_vout->p_vlc->psz_homedir );
448     }
449 #endif
450
451     if( !val.psz_string )
452     {
453         msg_Err( p_vout, "no directory specified for snapshots" );
454         return VLC_EGENERIC;
455     }
456     var_Get( p_vout, "snapshot-format", &format );
457     if( !format.psz_string || !*format.psz_string )
458     {
459         if( format.psz_string ) free( format.psz_string );
460         format.psz_string = strdup( "png" );
461     }
462
463     /* Embedded snapshot : if snapshot-path == object:object-id, then
464        create a snapshot_t* and store it in
465        object(object-id)->p_private, then unlock and signal the
466        waiting object.
467      */
468     if( !strncmp( val.psz_string, "object:", 7 ) )
469     {
470         int i_id;
471         vlc_object_t* p_dest;
472         block_t *p_block;
473         snapshot_t *p_snapshot;
474         int i_size;
475
476         /* Destination object-id is following object: */
477         i_id = atoi( &val.psz_string[7] );
478         p_dest = ( vlc_object_t* )vlc_current_object( i_id );
479         if( !p_dest )
480         {
481             msg_Err( p_vout, "Cannot find calling object" );
482             image_HandlerDelete( p_image );
483             return VLC_EGENERIC;
484         }
485         /* Object must be locked. We will unlock it once we get the
486            snapshot and written it to p_private */
487         p_dest->p_private = NULL;
488
489         /* Save the snapshot to a memory zone */
490         fmt_in = p_vout->fmt_in;
491         fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
492         fmt_out.i_width = 320;
493         fmt_out.i_height = 200;
494         fmt_out.i_chroma = VLC_FOURCC( 'p','n','g',' ' );
495         p_block = ( block_t* ) image_Write( p_image, p_pic, &fmt_in, &fmt_out );
496         if( !p_block ) 
497         {
498             msg_Err( p_vout, "Could not get snapshot" );
499             image_HandlerDelete( p_image );
500             vlc_cond_signal( &p_dest->object_wait );
501             vlc_mutex_unlock( &p_dest->object_lock );
502             vlc_object_release( p_dest );
503             return VLC_EGENERIC;
504         }
505
506         /* Copy the p_block data to a snapshot structure */
507         /* FIXME: get the timestamp */
508         p_snapshot = ( snapshot_t* ) malloc( sizeof( snapshot_t ) );
509         if( !p_snapshot )
510         {
511             block_Release( p_block );
512             image_HandlerDelete( p_image );
513             vlc_cond_signal( &p_dest->object_wait );
514             vlc_mutex_unlock( &p_dest->object_lock );
515             vlc_object_release( p_dest );
516             return VLC_ENOMEM;
517         }
518
519         i_size = p_block->i_buffer;
520
521         p_snapshot->i_width = fmt_out.i_width;
522         p_snapshot->i_height = fmt_out.i_height;
523         p_snapshot->i_datasize = i_size;
524         p_snapshot->date = p_block->i_pts; /* FIXME ?? */
525         p_snapshot->p_data = ( char* ) malloc( i_size );
526         if( !p_snapshot->p_data )
527         {
528             block_Release( p_block );
529             free( p_snapshot );
530             image_HandlerDelete( p_image );
531             vlc_cond_signal( &p_dest->object_wait );
532             vlc_mutex_unlock( &p_dest->object_lock );
533             vlc_object_release( p_dest );
534             return VLC_ENOMEM;
535         }
536         memcpy( p_snapshot->p_data, p_block->p_buffer, p_block->i_buffer );
537
538         p_dest->p_private = p_snapshot;
539
540         block_Release( p_block );
541         
542         /* Unlock the object */
543         vlc_cond_signal( &p_dest->object_wait );
544         vlc_mutex_unlock( &p_dest->object_lock );
545         vlc_object_release( p_dest );
546
547         image_HandlerDelete( p_image );
548         return VLC_SUCCESS;
549     }
550
551     asprintf( &psz_filename, "%s/vlcsnap-%u.%s", val.psz_string,
552               (unsigned int)(p_pic->date / 100000) & 0xFFFFFF,
553               format.psz_string );
554     free( val.psz_string );
555     free( format.psz_string );
556
557     /* Save the snapshot */
558     fmt_in = p_vout->fmt_in;
559     fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
560     i_ret = image_WriteUrl( p_image, p_pic, &fmt_in, &fmt_out, psz_filename );
561     if( i_ret != VLC_SUCCESS )
562     {
563         msg_Err( p_vout, "could not create snapshot %s", psz_filename );
564         free( psz_filename );
565         image_HandlerDelete( p_image );
566         return VLC_EGENERIC;
567     }
568
569     msg_Dbg( p_vout, "snapshot taken (%s)", psz_filename );
570     free( psz_filename );
571
572     if( var_GetBool( p_vout, "snapshot-preview" ) )
573     {
574         /* Inject a subpicture with the snapshot */
575         memset( &fmt_out, 0, sizeof(fmt_out) );
576         fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
577         p_pif = image_Convert( p_image, p_pic, &fmt_in, &fmt_out );
578         image_HandlerDelete( p_image );
579         if( !p_pif ) return VLC_EGENERIC;
580
581         p_subpic = spu_CreateSubpicture( p_vout->p_spu );
582         if( p_subpic == NULL )
583         {
584              p_pif->pf_release( p_pif );
585              return VLC_EGENERIC;
586         }
587
588         p_subpic->i_channel = 0;
589         p_subpic->i_start = mdate();
590         p_subpic->i_stop = mdate() + 4000000;
591         p_subpic->b_ephemer = VLC_TRUE;
592         p_subpic->b_fade = VLC_TRUE;
593         p_subpic->i_original_picture_width = p_vout->render.i_width * 4;
594         p_subpic->i_original_picture_height = p_vout->render.i_height * 4;
595
596         p_subpic->p_region = spu_CreateRegion( p_vout->p_spu, &fmt_out );
597         vout_CopyPicture( p_image->p_parent, &p_subpic->p_region->picture,
598                           p_pif );
599         p_pif->pf_release( p_pif );
600
601         spu_DisplaySubpicture( p_vout->p_spu, p_subpic );
602     }
603     else
604     {
605         image_HandlerDelete( p_image );
606     }
607
608     return VLC_SUCCESS;
609 }
610
611 /*****************************************************************************
612  * vout_ControlDefault: default methods for video output control.
613  *****************************************************************************/
614 int vout_vaControlDefault( vout_thread_t *p_vout, int i_query, va_list args )
615 {
616     switch( i_query )
617     {
618     case VOUT_REPARENT:
619     case VOUT_CLOSE:
620         if( p_vout->p_parent_intf )
621         {
622             vlc_object_release( p_vout->p_parent_intf );
623             p_vout->p_parent_intf = NULL;
624         }
625         return VLC_SUCCESS;
626         break;
627
628     case VOUT_SNAPSHOT:
629         p_vout->b_snapshot = VLC_TRUE;
630         return VLC_SUCCESS;
631         break;
632
633     default:
634         msg_Dbg( p_vout, "control query not supported" );
635         return VLC_EGENERIC;
636     }
637 }
638
639 /*****************************************************************************
640  * InitWindowSize: find the initial dimensions the video window should have.
641  *****************************************************************************
642  * This function will check the "width", "height" and "zoom" config options and
643  * will calculate the size that the video window should have.
644  *****************************************************************************/
645 static void InitWindowSize( vout_thread_t *p_vout, unsigned *pi_width,
646                             unsigned *pi_height )
647 {
648     vlc_value_t val;
649     int i_width, i_height;
650     uint64_t ll_zoom;
651
652 #define FP_FACTOR 1000                             /* our fixed point factor */
653
654     var_Get( p_vout, "width", &val );
655     i_width = val.i_int;
656     var_Get( p_vout, "height", &val );
657     i_height = val.i_int;
658     var_Get( p_vout, "zoom", &val );
659     ll_zoom = (uint64_t)( FP_FACTOR * val.f_float );
660
661     if( i_width > 0 && i_height > 0)
662     {
663         *pi_width = (int)( i_width * ll_zoom / FP_FACTOR );
664         *pi_height = (int)( i_height * ll_zoom / FP_FACTOR );
665         goto initwsize_end;
666     }
667     else if( i_width > 0 )
668     {
669         *pi_width = (int)( i_width * ll_zoom / FP_FACTOR );
670         *pi_height = (int)( p_vout->fmt_in.i_visible_height * ll_zoom *
671             p_vout->fmt_in.i_sar_den * i_width / p_vout->fmt_in.i_sar_num /
672             FP_FACTOR / p_vout->fmt_in.i_visible_width );
673         goto initwsize_end;
674     }
675     else if( i_height > 0 )
676     {
677         *pi_height = (int)( i_height * ll_zoom / FP_FACTOR );
678         *pi_width = (int)( p_vout->fmt_in.i_visible_width * ll_zoom *
679             p_vout->fmt_in.i_sar_num * i_height / p_vout->fmt_in.i_sar_den /
680             FP_FACTOR / p_vout->fmt_in.i_visible_height );
681         goto initwsize_end;
682     }
683
684     if( p_vout->fmt_in.i_sar_num >= p_vout->fmt_in.i_sar_den )
685     {
686         *pi_width = (int)( p_vout->fmt_in.i_visible_width * ll_zoom *
687             p_vout->fmt_in.i_sar_num / p_vout->fmt_in.i_sar_den / FP_FACTOR );
688         *pi_height = (int)( p_vout->fmt_in.i_visible_height * ll_zoom 
689             / FP_FACTOR );
690     }
691     else
692     {
693         *pi_width = (int)( p_vout->fmt_in.i_visible_width * ll_zoom 
694             / FP_FACTOR );
695         *pi_height = (int)( p_vout->fmt_in.i_visible_height * ll_zoom *
696             p_vout->fmt_in.i_sar_den / p_vout->fmt_in.i_sar_num / FP_FACTOR );
697     }
698
699 initwsize_end:
700     msg_Dbg( p_vout, "window size: %dx%d", p_vout->i_window_width, 
701              p_vout->i_window_height );
702
703 #undef FP_FACTOR
704 }
705
706 /*****************************************************************************
707  * Object variables callbacks
708  *****************************************************************************/
709 static int ZoomCallback( vlc_object_t *p_this, char const *psz_cmd,
710                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
711 {
712     vout_thread_t *p_vout = (vout_thread_t *)p_this;
713     InitWindowSize( p_vout, &p_vout->i_window_width,
714                     &p_vout->i_window_height );
715     vout_Control( p_vout, VOUT_SET_SIZE, 0, 0 );
716     return VLC_SUCCESS;
717 }
718
719 static int CropCallback( vlc_object_t *p_this, char const *psz_cmd,
720                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
721 {
722     vout_thread_t *p_vout = (vout_thread_t *)p_this;
723     int64_t i_aspect_num, i_aspect_den;
724     unsigned int i_width, i_height;
725
726     char *psz_end, *psz_parser = strchr( newval.psz_string, ':' );
727
728     /* Restore defaults */
729     p_vout->fmt_in.i_x_offset = p_vout->fmt_render.i_x_offset;
730     p_vout->fmt_in.i_visible_width = p_vout->fmt_render.i_visible_width;
731     p_vout->fmt_in.i_y_offset = p_vout->fmt_render.i_y_offset;
732     p_vout->fmt_in.i_visible_height = p_vout->fmt_render.i_visible_height;
733
734     if( !psz_parser ) goto crop_end;
735
736     i_aspect_num = strtol( newval.psz_string, &psz_end, 0 );
737     if( psz_end == newval.psz_string || !i_aspect_num ) goto crop_end;
738
739     i_aspect_den = strtol( ++psz_parser, &psz_end, 0 );
740     if( psz_end == psz_parser || !i_aspect_den ) goto crop_end;
741
742     i_width = p_vout->fmt_in.i_sar_den * p_vout->fmt_render.i_visible_height *
743         i_aspect_num / i_aspect_den / p_vout->fmt_in.i_sar_num;
744     i_height = p_vout->fmt_render.i_visible_width * p_vout->fmt_in.i_sar_num *
745         i_aspect_den / i_aspect_num / p_vout->fmt_in.i_sar_den;
746
747     if( i_width < p_vout->fmt_render.i_visible_width )
748     {
749         p_vout->fmt_in.i_x_offset = p_vout->fmt_render.i_x_offset +
750             (p_vout->fmt_render.i_visible_width - i_width) / 2;
751         p_vout->fmt_in.i_visible_width = i_width;
752     }
753     else
754     {
755         p_vout->fmt_in.i_y_offset = p_vout->fmt_render.i_y_offset +
756             (p_vout->fmt_render.i_visible_height - i_height) / 2;
757         p_vout->fmt_in.i_visible_height = i_height;
758     }
759
760  crop_end:
761     InitWindowSize( p_vout, &p_vout->i_window_width,
762                     &p_vout->i_window_height );
763
764     p_vout->i_changes |= VOUT_CROP_CHANGE;
765
766     msg_Dbg( p_vout, "cropping picture %ix%i to %i,%i,%ix%i",
767              p_vout->fmt_in.i_width, p_vout->fmt_in.i_height,
768              p_vout->fmt_in.i_x_offset, p_vout->fmt_in.i_y_offset,
769              p_vout->fmt_in.i_visible_width,
770              p_vout->fmt_in.i_visible_height );
771
772     return VLC_SUCCESS;
773 }
774
775 static int AspectCallback( vlc_object_t *p_this, char const *psz_cmd,
776                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
777 {
778     vout_thread_t *p_vout = (vout_thread_t *)p_this;
779     unsigned int i_aspect_num, i_aspect_den, i_sar_num, i_sar_den;
780     vlc_value_t val;
781
782     char *psz_end, *psz_parser = strchr( newval.psz_string, ':' );
783
784     /* Restore defaults */
785     p_vout->fmt_in.i_sar_num = p_vout->fmt_render.i_sar_num;
786     p_vout->fmt_in.i_sar_den = p_vout->fmt_render.i_sar_den;
787     p_vout->fmt_in.i_aspect = p_vout->fmt_render.i_aspect;
788     p_vout->render.i_aspect = p_vout->fmt_render.i_aspect;
789
790     if( !psz_parser ) goto aspect_end;
791
792     i_aspect_num = strtol( newval.psz_string, &psz_end, 0 );
793     if( psz_end == newval.psz_string || !i_aspect_num ) goto aspect_end;
794
795     i_aspect_den = strtol( ++psz_parser, &psz_end, 0 );
796     if( psz_end == psz_parser || !i_aspect_den ) goto aspect_end;
797
798     i_sar_num = i_aspect_num * p_vout->fmt_render.i_visible_height;
799     i_sar_den = i_aspect_den * p_vout->fmt_render.i_visible_width;
800     vlc_ureduce( &i_sar_num, &i_sar_den, i_sar_num, i_sar_den, 0 );
801     p_vout->fmt_in.i_sar_num = i_sar_num;
802     p_vout->fmt_in.i_sar_den = i_sar_den;
803     p_vout->fmt_in.i_aspect = i_aspect_num * VOUT_ASPECT_FACTOR / i_aspect_den;
804     p_vout->render.i_aspect = p_vout->fmt_in.i_aspect;
805
806  aspect_end:
807     if( p_vout->i_par_num && p_vout->i_par_den )
808     {
809         p_vout->fmt_in.i_sar_num *= p_vout->i_par_den;
810         p_vout->fmt_in.i_sar_den *= p_vout->i_par_num;
811         p_vout->fmt_in.i_aspect = p_vout->fmt_in.i_aspect *
812             p_vout->i_par_den / p_vout->i_par_num;
813         p_vout->render.i_aspect = p_vout->fmt_in.i_aspect;
814     }
815
816     p_vout->i_changes |= VOUT_ASPECT_CHANGE;
817
818     vlc_ureduce( &i_aspect_num, &i_aspect_den,
819                  p_vout->fmt_in.i_aspect, VOUT_ASPECT_FACTOR, 0 );
820     msg_Dbg( p_vout, "new aspect-ratio %i:%i, sample aspect-ratio %i:%i",
821              i_aspect_num, i_aspect_den,
822              p_vout->fmt_in.i_sar_num, p_vout->fmt_in.i_sar_den );
823
824     var_Get( p_vout, "crop", &val );
825     return CropCallback( p_this, 0, val, val, 0 );
826
827     return VLC_SUCCESS;
828 }
829
830 static int OnTopCallback( vlc_object_t *p_this, char const *psz_cmd,
831                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
832 {
833     vout_thread_t *p_vout = (vout_thread_t *)p_this;
834     playlist_t *p_playlist;
835     vout_Control( p_vout, VOUT_SET_STAY_ON_TOP, newval.b_bool );
836
837     p_playlist = (playlist_t *)vlc_object_find( p_this, VLC_OBJECT_PLAYLIST,
838                                                  FIND_PARENT );
839     if( p_playlist )
840     {
841         /* Modify playlist as well because the vout might have to be restarted */
842         var_Create( p_playlist, "video-on-top", VLC_VAR_BOOL );
843         var_Set( p_playlist, "video-on-top", newval );
844
845         vlc_object_release( p_playlist );
846     }
847     return VLC_SUCCESS;
848 }
849
850 static int FullscreenCallback( vlc_object_t *p_this, char const *psz_cmd,
851                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
852 {
853     vout_thread_t *p_vout = (vout_thread_t *)p_this;
854     playlist_t *p_playlist;
855     vlc_value_t val;
856
857     p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
858
859     p_playlist = (playlist_t *)vlc_object_find( p_this, VLC_OBJECT_PLAYLIST,
860                                                  FIND_PARENT );
861     if( p_playlist )
862     {
863         /* Modify playlist as well because the vout might have to be restarted */
864         var_Create( p_playlist, "fullscreen", VLC_VAR_BOOL );
865         var_Set( p_playlist, "fullscreen", newval );
866
867         vlc_object_release( p_playlist );
868     }
869
870     /* Disable "always on top" in fullscreen mode */
871     var_Get( p_vout, "video-on-top", &val );
872     if( newval.b_bool && val.b_bool )
873     {
874         val.b_bool = VLC_FALSE;
875         vout_Control( p_vout, VOUT_SET_STAY_ON_TOP, val.b_bool );
876     }
877     else if( !newval.b_bool && val.b_bool )
878     {
879         vout_Control( p_vout, VOUT_SET_STAY_ON_TOP, val.b_bool );
880     }
881
882     val.b_bool = VLC_TRUE;
883     var_Set( p_vout, "intf-change", val );
884     return VLC_SUCCESS;
885 }
886
887 static int SnapshotCallback( vlc_object_t *p_this, char const *psz_cmd,
888                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
889 {
890     vout_thread_t *p_vout = (vout_thread_t *)p_this;
891     vout_Control( p_vout, VOUT_SNAPSHOT );
892     return VLC_SUCCESS;
893 }