vout_intf.c 45.2 KB
Newer Older
Gildas Bazin's avatar
Gildas Bazin committed
1 2 3
/*****************************************************************************
 * vout_intf.c : video output interface
 *****************************************************************************
4
 * Copyright (C) 2000-2007 the VideoLAN team
Gildas Bazin's avatar
Gildas Bazin committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * Authors: Gildas Bazin <gbazin@videolan.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Antoine Cellerier's avatar
Antoine Cellerier committed
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
Gildas Bazin's avatar
Gildas Bazin committed
21 22 23 24 25
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
26

27 28 29 30
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

31
#include <vlc_common.h>
32 33

#include <stdio.h>
Gildas Bazin's avatar
Gildas Bazin committed
34
#include <stdlib.h>                                                /* free() */
Filippo Carone's avatar
Filippo Carone committed
35
#include <sys/types.h>                                          /* opendir() */
36
#include <sys/stat.h>
Filippo Carone's avatar
Filippo Carone committed
37
#include <dirent.h>                                             /* opendir() */
38
#include <assert.h>
39
#include <time.h>                                           /* strftime */
Gildas Bazin's avatar
Gildas Bazin committed
40

Clément Stenac's avatar
Clément Stenac committed
41
#include <vlc_interface.h>
42
#include <vlc_block.h>
Clément Stenac's avatar
Clément Stenac committed
43
#include <vlc_playlist.h>
Gildas Bazin's avatar
Gildas Bazin committed
44

Clément Stenac's avatar
Clément Stenac committed
45
#include <vlc_vout.h>
46
#include <vlc_window.h>
Clément Stenac's avatar
Clément Stenac committed
47 48
#include <vlc_image.h>
#include <vlc_osd.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
49
#include <vlc_charset.h>
50

Clément Stenac's avatar
Clément Stenac committed
51 52
#include <vlc_strings.h>
#include <vlc_charset.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
53
#include "../libvlc.h"
54
#include "vout_internal.h"
55

Gildas Bazin's avatar
Gildas Bazin committed
56 57 58
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
59
static void InitWindowSize( vout_thread_t *, unsigned *, unsigned * );
Gildas Bazin's avatar
Gildas Bazin committed
60

61 62 63
/* Object variables callbacks */
static int ZoomCallback( vlc_object_t *, char const *,
                         vlc_value_t, vlc_value_t, void * );
64 65
static int CropCallback( vlc_object_t *, char const *,
                         vlc_value_t, vlc_value_t, void * );
66 67
static int AspectCallback( vlc_object_t *, char const *,
                           vlc_value_t, vlc_value_t, void * );
68 69
static int OnTopCallback( vlc_object_t *, char const *,
                          vlc_value_t, vlc_value_t, void * );
70 71
static int FullscreenCallback( vlc_object_t *, char const *,
                               vlc_value_t, vlc_value_t, void * );
72 73
static int SnapshotCallback( vlc_object_t *, char const *,
                             vlc_value_t, vlc_value_t, void * );
74 75 76 77 78 79 80

static int TitleShowCallback( vlc_object_t *, char const *,
                              vlc_value_t, vlc_value_t, void * );
static int TitleTimeoutCallback( vlc_object_t *, char const *,
                                 vlc_value_t, vlc_value_t, void * );
static int TitlePositionCallback( vlc_object_t *, char const *,
                                  vlc_value_t, vlc_value_t, void * );
81

Gildas Bazin's avatar
Gildas Bazin committed
82 83 84 85 86 87 88 89 90 91 92 93
/*****************************************************************************
 * vout_RequestWindow: Create/Get a video window if possible.
 *****************************************************************************
 * This function looks for the main interface and tries to request
 * a new video window. If it fails then the vout will still need to create the
 * window by itself.
 *****************************************************************************/
void *vout_RequestWindow( vout_thread_t *p_vout,
                          int *pi_x_hint, int *pi_y_hint,
                          unsigned int *pi_width_hint,
                          unsigned int *pi_height_hint )
{
94 95 96
    /* Small kludge */
    if( !var_Type( p_vout, "aspect-ratio" ) ) vout_IntfInit( p_vout );

Gildas Bazin's avatar
Gildas Bazin committed
97
    /* Get requested coordinates */
98 99
    *pi_x_hint = var_GetInteger( p_vout, "video-x" );
    *pi_y_hint = var_GetInteger( p_vout, "video-y" );
Gildas Bazin's avatar
Gildas Bazin committed
100 101 102 103 104

    *pi_width_hint = p_vout->i_window_width;
    *pi_height_hint = p_vout->i_window_height;

    /* Check whether someone provided us with a window ID */
105 106
    int drawable = var_CreateGetInteger( p_vout, "drawable" );
    if( drawable ) return (void *)(intptr_t)drawable;
Gildas Bazin's avatar
Gildas Bazin committed
107

108 109 110 111 112 113 114 115 116 117 118 119
    vout_window_t *wnd = vlc_custom_create (VLC_OBJECT(p_vout), sizeof (*wnd),
                                            VLC_OBJECT_GENERIC, "window");
    if (wnd == NULL)
        return NULL;

    wnd->vout = p_vout;
    wnd->width = *pi_width_hint;
    wnd->height = *pi_height_hint;
    wnd->pos_x = *pi_x_hint;
    wnd->pos_y = *pi_y_hint;
    vlc_object_attach (wnd, p_vout);

120
    wnd->module = module_need (wnd, "vout_window", NULL, false);
121 122 123 124 125 126 127 128 129 130 131 132
    if (wnd->module == NULL)
    {
        msg_Dbg (wnd, "no window provider available");
        vlc_object_release (wnd);
        return NULL;
    }
    p_vout->p_window = wnd;
    *pi_width_hint = wnd->width;
    *pi_height_hint = wnd->height;
    *pi_x_hint = wnd->pos_x;
    *pi_y_hint = wnd->pos_y;
    return wnd->handle;
Gildas Bazin's avatar
Gildas Bazin committed
133 134
}

135
void vout_ReleaseWindow( vout_thread_t *p_vout, void *dummy )
Gildas Bazin's avatar
Gildas Bazin committed
136
{
137 138 139 140 141 142 143
    vout_window_t *wnd = p_vout->p_window;

    if (wnd == NULL)
        return;
    p_vout->p_window = NULL;

    assert (wnd->module);
144
    module_unneed (wnd, wnd->module);
145 146 147

    vlc_object_release (wnd);
    (void)dummy;
Gildas Bazin's avatar
Gildas Bazin committed
148
}
149

150
int vout_ControlWindow( vout_thread_t *p_vout, void *dummy,
151 152
                        int i_query, va_list args )
{
Rémi Duraffort's avatar
Rémi Duraffort committed
153
    (void)dummy;
154 155 156 157 158 159 160
    vout_window_t *wnd = p_vout->p_window;

    if (wnd == NULL)
        return VLC_EGENERIC;

    assert (wnd->control);
    return wnd->control (wnd, i_query, args);
161 162
}

163 164 165
/*****************************************************************************
 * vout_IntfInit: called during the vout creation to initialise misc things.
 *****************************************************************************/
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
static const struct
{
    double f_value;
    const char *psz_label;
} p_zoom_values[] = {
    { 0.25, N_("1:4 Quarter") },
    { 0.5, N_("1:2 Half") },
    { 1, N_("1:1 Original") },
    { 2, N_("2:1 Double") },
    { 0, NULL } };

static const struct
{
    const char *psz_value;
    const char *psz_label;
} p_crop_values[] = {
    { "", N_("Default") },
    { "16:10", "16:10" },
184
    { "16:9", "16:9" },
185
    { "185:100", "1.85:1" },
186 187 188
    { "221:100", "2.21:1" },
    { "235:100", "2.35:1" },
    { "239:100", "2.39:1" },
189 190 191 192
    { "5:3", "5:3" },
    { "4:3", "4:3" },
    { "5:4", "5:4" },
    { "1:1", "1:1" },
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
    { NULL, NULL } };

static const struct
{
    const char *psz_value;
    const char *psz_label;
} p_aspect_ratio_values[] = {
    { "", N_("Default") },
    { "1:1", "1:1" },
    { "4:3", "4:3" },
    { "16:9", "16:9" },
    { "16:10", "16:10" },
    { "221:100", "2.21:1" },
    { "5:4", "5:4" },
    { NULL, NULL } };

static void AddCustomRatios( vout_thread_t *p_vout, const char *psz_var,
                             char *psz_list )
{
    if( psz_list && *psz_list )
    {
        char *psz_cur = psz_list;
        char *psz_next;
        while( psz_cur && *psz_cur )
        {
            vlc_value_t val, text;
            psz_next = strchr( psz_cur, ',' );
            if( psz_next )
            {
                *psz_next = '\0';
                psz_next++;
            }
            val.psz_string = psz_cur;
            text.psz_string = psz_cur;
            var_Change( p_vout, psz_var, VLC_VAR_ADDCHOICE, &val, &text);
            psz_cur = psz_next;
        }
    }
}

233 234 235
void vout_IntfInit( vout_thread_t *p_vout )
{
    vlc_value_t val, text, old_val;
236
    bool b_force_par = false;
237
    char *psz_buf;
238
    int i;
239 240

    /* Create a few object variables we'll need later on */
241
    var_Create( p_vout, "snapshot-path", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
242
    var_Create( p_vout, "snapshot-prefix", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
243
    var_Create( p_vout, "snapshot-format", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
244
    var_Create( p_vout, "snapshot-preview", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
245 246 247 248
    var_Create( p_vout, "snapshot-sequential",
                VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
    var_Create( p_vout, "snapshot-num", VLC_VAR_INTEGER );
    var_SetInteger( p_vout, "snapshot-num", 1 );
249 250
    var_Create( p_vout, "snapshot-width", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Create( p_vout, "snapshot-height", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
251

252 253
    var_Create( p_vout, "width", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Create( p_vout, "height", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
254
    p_vout->i_alignment = var_CreateGetInteger( p_vout, "align" );
255

256 257 258
    var_Create( p_vout, "video-x", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Create( p_vout, "video-y", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );

259 260 261
    var_Create( p_vout, "mouse-hide-timeout",
                VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );

262 263
    p_vout->p->b_title_show = var_CreateGetBool( p_vout, "video-title-show" );
    p_vout->p->i_title_timeout =
264
        (mtime_t)var_CreateGetInteger( p_vout, "video-title-timeout" );
265
    p_vout->p->i_title_position =
266
        var_CreateGetInteger( p_vout, "video-title-position" );
267
    p_vout->p->psz_title =  NULL;
268

269 270 271
    var_AddCallback( p_vout, "video-title-show", TitleShowCallback, NULL );
    var_AddCallback( p_vout, "video-title-timeout", TitleTimeoutCallback, NULL );
    var_AddCallback( p_vout, "video-title-position", TitlePositionCallback, NULL );
272

273
    /* Zoom object var */
274 275 276 277 278 279 280
    var_Create( p_vout, "zoom", VLC_VAR_FLOAT | VLC_VAR_ISCOMMAND |
                VLC_VAR_HASCHOICE | VLC_VAR_DOINHERIT );

    text.psz_string = _("Zoom");
    var_Change( p_vout, "zoom", VLC_VAR_SETTEXT, &text, NULL );

    var_Get( p_vout, "zoom", &old_val );
281 282

    for( i = 0; p_zoom_values[i].f_value; i++ )
283
    {
284 285 286 287 288
        if( old_val.f_float == p_zoom_values[i].f_value )
            var_Change( p_vout, "zoom", VLC_VAR_DELCHOICE, &old_val, NULL );
        val.f_float = p_zoom_values[i].f_value;
        text.psz_string = _( p_zoom_values[i].psz_label );
        var_Change( p_vout, "zoom", VLC_VAR_ADDCHOICE, &val, &text );
289 290
    }

291
    var_Set( p_vout, "zoom", old_val ); /* Is this really needed? */
292 293

    var_AddCallback( p_vout, "zoom", ZoomCallback, NULL );
294

295
    /* Crop offset vars */
296 297 298 299
    var_Create( p_vout, "crop-left", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_Create( p_vout, "crop-top", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_Create( p_vout, "crop-right", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_Create( p_vout, "crop-bottom", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
300 301 302 303 304 305

    var_AddCallback( p_vout, "crop-left", CropCallback, NULL );
    var_AddCallback( p_vout, "crop-top", CropCallback, NULL );
    var_AddCallback( p_vout, "crop-right", CropCallback, NULL );
    var_AddCallback( p_vout, "crop-bottom", CropCallback, NULL );

306
    /* Crop object var */
307
    var_Create( p_vout, "crop", VLC_VAR_STRING | VLC_VAR_ISCOMMAND |
308 309 310 311 312
                VLC_VAR_HASCHOICE | VLC_VAR_DOINHERIT );

    text.psz_string = _("Crop");
    var_Change( p_vout, "crop", VLC_VAR_SETTEXT, &text, NULL );

313
    val.psz_string = (char*)"";
314 315
    var_Change( p_vout, "crop", VLC_VAR_DELCHOICE, &val, 0 );

316
    for( i = 0; p_crop_values[i].psz_value; i++ )
317
    {
318 319 320
        val.psz_string = (char*)p_crop_values[i].psz_value;
        text.psz_string = _( p_crop_values[i].psz_label );
        var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text );
321
    }
322

323
    /* update triggered every time the vout's crop parameters are changed */
Antoine Cellerier's avatar
Antoine Cellerier committed
324
    var_Create( p_vout, "crop-update", VLC_VAR_VOID );
325

326 327 328 329
    /* Add custom crop ratios */
    psz_buf = config_GetPsz( p_vout, "custom-crop-ratios" );
    AddCustomRatios( p_vout, "crop", psz_buf );
    free( psz_buf );
330

331
    var_AddCallback( p_vout, "crop", CropCallback, NULL );
332 333
    var_Get( p_vout, "crop", &old_val );
    if( old_val.psz_string && *old_val.psz_string )
334
        var_TriggerCallback( p_vout, "crop" );
335
    free( old_val.psz_string );
336 337

    /* Monitor pixel aspect-ratio */
338 339 340
    var_Create( p_vout, "monitor-par", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
    var_Get( p_vout, "monitor-par", &val );
    if( val.psz_string && *val.psz_string )
341 342 343 344 345 346
    {
        char *psz_parser = strchr( val.psz_string, ':' );
        unsigned int i_aspect_num = 0, i_aspect_den = 0;
        float i_aspect = 0;
        if( psz_parser )
        {
347 348
            i_aspect_num = strtol( val.psz_string, 0, 10 );
            i_aspect_den = strtol( ++psz_parser, 0, 10 );
349 350 351 352 353 354 355
        }
        else
        {
            i_aspect = atof( val.psz_string );
            vlc_ureduce( &i_aspect_num, &i_aspect_den,
                         i_aspect *VOUT_ASPECT_FACTOR, VOUT_ASPECT_FACTOR, 0 );
        }
356 357
        if( !i_aspect_num || !i_aspect_den ) i_aspect_num = i_aspect_den = 1;

358 359
        p_vout->p->i_par_num = i_aspect_num;
        p_vout->p->i_par_den = i_aspect_den;
360

361 362
        vlc_ureduce( &p_vout->p->i_par_num, &p_vout->p->i_par_den,
                     p_vout->p->i_par_num, p_vout->p->i_par_den, 0 );
363

364
        msg_Dbg( p_vout, "overriding monitor pixel aspect-ratio: %i:%i",
365
                 p_vout->p->i_par_num, p_vout->p->i_par_den );
366
        b_force_par = true;
367
    }
368
    free( val.psz_string );
369 370

    /* Aspect-ratio object var */
371
    var_Create( p_vout, "aspect-ratio", VLC_VAR_STRING | VLC_VAR_ISCOMMAND |
372 373 374 375 376
                VLC_VAR_HASCHOICE | VLC_VAR_DOINHERIT );

    text.psz_string = _("Aspect-ratio");
    var_Change( p_vout, "aspect-ratio", VLC_VAR_SETTEXT, &text, NULL );

377
    val.psz_string = (char*)"";
378 379
    var_Change( p_vout, "aspect-ratio", VLC_VAR_DELCHOICE, &val, 0 );

380
    for( i = 0; p_aspect_ratio_values[i].psz_value; i++ )
381
    {
382 383 384
        val.psz_string = (char*)p_aspect_ratio_values[i].psz_value;
        text.psz_string = _( p_aspect_ratio_values[i].psz_label );
        var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text );
385
    }
386 387 388 389 390

    /* Add custom aspect ratios */
    psz_buf = config_GetPsz( p_vout, "custom-aspect-ratios" );
    AddCustomRatios( p_vout, "aspect-ratio", psz_buf );
    free( psz_buf );
391

392 393
    var_AddCallback( p_vout, "aspect-ratio", AspectCallback, NULL );
    var_Get( p_vout, "aspect-ratio", &old_val );
394
    if( (old_val.psz_string && *old_val.psz_string) || b_force_par )
395
        var_TriggerCallback( p_vout, "aspect-ratio" );
396
    free( old_val.psz_string );
397

398 399 400 401
    /* Initialize the dimensions of the video window */
    InitWindowSize( p_vout, &p_vout->i_window_width,
                    &p_vout->i_window_height );

402
    /* Add a variable to indicate if the window should be on top of others */
403 404
    var_Create( p_vout, "video-on-top", VLC_VAR_BOOL | VLC_VAR_DOINHERIT
                | VLC_VAR_ISCOMMAND );
405 406 407
    text.psz_string = _("Always on top");
    var_Change( p_vout, "video-on-top", VLC_VAR_SETTEXT, &text, NULL );
    var_AddCallback( p_vout, "video-on-top", OnTopCallback, NULL );
408

409 410 411
    /* Add a variable to indicate whether we want window decoration or not */
    var_Create( p_vout, "video-deco", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );

412
    /* Add a fullscreen variable */
413
    if( var_CreateGetBoolCommand( p_vout, "fullscreen" ) )
414 415 416 417
    {
        /* user requested fullscreen */
        p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
    }
418 419
    text.psz_string = _("Fullscreen");
    var_Change( p_vout, "fullscreen", VLC_VAR_SETTEXT, &text, NULL );
420
    var_AddCallback( p_vout, "fullscreen", FullscreenCallback, NULL );
421

422 423 424 425 426 427
    /* Add a snapshot variable */
    var_Create( p_vout, "video-snapshot", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
    text.psz_string = _("Snapshot");
    var_Change( p_vout, "video-snapshot", VLC_VAR_SETTEXT, &text, NULL );
    var_AddCallback( p_vout, "video-snapshot", SnapshotCallback, NULL );

428 429 430 431 432 433 434
    /* Mouse coordinates */
    var_Create( p_vout, "mouse-x", VLC_VAR_INTEGER );
    var_Create( p_vout, "mouse-y", VLC_VAR_INTEGER );
    var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER );
    var_Create( p_vout, "mouse-moved", VLC_VAR_BOOL );
    var_Create( p_vout, "mouse-clicked", VLC_VAR_INTEGER );

435
    var_Create( p_vout, "intf-change", VLC_VAR_BOOL );
436
    var_SetBool( p_vout, "intf-change", true );
437 438
}

439 440 441
/*****************************************************************************
 * vout_Snapshot: generates a snapshot.
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
/**
 * This function will inject a subpicture into the vout with the provided
 * picture
 */
static int VoutSnapshotPip( vout_thread_t *p_vout, image_handler_t *p_image, picture_t *p_pic, const video_format_t *p_fmt_in )
{
    video_format_t fmt_in = *p_fmt_in;
    video_format_t fmt_out;
    picture_t *p_pip;
    subpicture_t *p_subpic;

    /* */
    memset( &fmt_out, 0, sizeof(fmt_out) );
    fmt_out = fmt_in;
    fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');

    /* */
    p_pip = image_Convert( p_image, p_pic, &fmt_in, &fmt_out );
    if( !p_pip )
        return VLC_EGENERIC;

463
    p_subpic = subpicture_New();
Laurent Aimar's avatar
Laurent Aimar committed
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
    if( p_subpic == NULL )
    {
         picture_Release( p_pip );
         return VLC_EGENERIC;
    }

    p_subpic->i_channel = 0;
    p_subpic->i_start = mdate();
    p_subpic->i_stop = mdate() + 4000000;
    p_subpic->b_ephemer = true;
    p_subpic->b_fade = true;
    p_subpic->i_original_picture_width = fmt_out.i_width * 4;
    p_subpic->i_original_picture_height = fmt_out.i_height * 4;
    fmt_out.i_aspect = 0;
    fmt_out.i_sar_num =
    fmt_out.i_sar_den = 0;

481
    p_subpic->p_region = subpicture_region_New( &fmt_out );
Laurent Aimar's avatar
Laurent Aimar committed
482
    if( p_subpic->p_region )
483 484 485 486 487 488 489 490
    {
        picture_Release( p_subpic->p_region->p_picture );
        p_subpic->p_region->p_picture = p_pip;
    }
    else
    {
        picture_Release( p_pip );
    }
Laurent Aimar's avatar
Laurent Aimar committed
491 492 493 494 495 496 497

    spu_DisplaySubpicture( p_vout->p_spu, p_subpic );
    return VLC_SUCCESS;
}
/**
 * This function will return the default directory used for snapshots
 */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
498
static char *VoutSnapshotGetDefaultDirectory( void )
Laurent Aimar's avatar
Laurent Aimar committed
499
{
500
    char *psz_path = NULL;
Laurent Aimar's avatar
Laurent Aimar committed
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
#if defined(__APPLE__) || defined(SYS_BEOS)

    if( asprintf( &psz_path, "%s/Desktop",
                  config_GetHomeDir() ) == -1 )
        psz_path = NULL;

#elif defined(WIN32) && !defined(UNDER_CE)

    /* Get the My Pictures folder path */
    char *p_mypicturesdir = NULL;
    typedef HRESULT (WINAPI *SHGETFOLDERPATH)( HWND, int, HANDLE, DWORD,
                                               LPWSTR );
    #ifndef CSIDL_FLAG_CREATE
    #   define CSIDL_FLAG_CREATE 0x8000
    #endif
    #ifndef CSIDL_MYPICTURES
    #   define CSIDL_MYPICTURES 0x27
    #endif
    #ifndef SHGFP_TYPE_CURRENT
    #   define SHGFP_TYPE_CURRENT 0
    #endif

    HINSTANCE shfolder_dll;
    SHGETFOLDERPATH SHGetFolderPath ;

    /* load the shfolder dll to retrieve SHGetFolderPath */
    if( ( shfolder_dll = LoadLibrary( _T("SHFolder.dll") ) ) != NULL )
    {
       wchar_t wdir[PATH_MAX];
       SHGetFolderPath = (void *)GetProcAddress( shfolder_dll,
                                                  _T("SHGetFolderPathW") );
        if ((SHGetFolderPath != NULL )
         && SUCCEEDED (SHGetFolderPath (NULL,
                                       CSIDL_MYPICTURES | CSIDL_FLAG_CREATE,
                                       NULL, SHGFP_TYPE_CURRENT,
                                       wdir)))
            p_mypicturesdir = FromWide (wdir);

        FreeLibrary( shfolder_dll );
    }

    if( p_mypicturesdir == NULL )
        psz_path = strdup( config_GetHomeDir() );
    else
        psz_path = p_mypicturesdir;

#else

    /* XXX: This saves in the data directory. Shouldn't we try saving
     *      to psz_homedir/Desktop or something nicer ? */
    char *psz_datadir = config_GetUserDataDir();
    if( psz_datadir )
    {
        if( asprintf( &psz_path, "%s", psz_datadir ) == -1 )
            psz_path = NULL;
        free( psz_datadir );
    }

#endif

    return psz_path;
}

564 565 566
int vout_Snapshot( vout_thread_t *p_vout, picture_t *p_pic )
{
    image_handler_t *p_image = image_HandlerCreate( p_vout );
567
    video_format_t fmt_in, fmt_out;
568
    char *psz_filename = NULL;
569
    vlc_value_t val, format;
Filippo Carone's avatar
Filippo Carone committed
570
    DIR *path;
571
    int i_ret;
Laurent Aimar's avatar
Laurent Aimar committed
572
    bool b_embedded_snapshot;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
573
    uintmax_t i_id = (uintptr_t)NULL;
Laurent Aimar's avatar
Laurent Aimar committed
574 575 576

    /* */
    val.psz_string = var_GetNonEmptyString( p_vout, "snapshot-path" );
577

Laurent Aimar's avatar
Laurent Aimar committed
578 579 580 581 582 583 584
    /* Embedded snapshot : if snapshot-path == object:object_ptr */
    if( val.psz_string && sscanf( val.psz_string, "object:%ju", &i_id ) > 0 )
        b_embedded_snapshot = true;
    else
        b_embedded_snapshot = false;

    /* */
585
    memset( &fmt_in, 0, sizeof(video_format_t) );
586
    fmt_in = p_vout->fmt_out;
Laurent Aimar's avatar
Laurent Aimar committed
587 588 589 590 591 592 593
    if( fmt_in.i_sar_num <= 0 || fmt_in.i_sar_den <= 0 )
    {
        fmt_in.i_sar_num =
        fmt_in.i_sar_den = 1;
    }

    /* */
594
    memset( &fmt_out, 0, sizeof(video_format_t) );
Laurent Aimar's avatar
Laurent Aimar committed
595 596 597 598 599
    fmt_out.i_sar_num =
    fmt_out.i_sar_den = 1;
    fmt_out.i_chroma = b_embedded_snapshot ? VLC_FOURCC('p','n','g',' ') : 0;
    fmt_out.i_width = var_GetInteger( p_vout, "snapshot-width" );
    fmt_out.i_height = var_GetInteger( p_vout, "snapshot-height" );
600

Laurent Aimar's avatar
Laurent Aimar committed
601 602
    if( b_embedded_snapshot &&
        fmt_out.i_width == 0 && fmt_out.i_height == 0 )
603
    {
Laurent Aimar's avatar
Laurent Aimar committed
604 605 606
        /* If snapshot-width and/or snapshot height were not specified,
           use a default snapshot width of 320 */
        fmt_out.i_width = 320;
607
    }
608

Laurent Aimar's avatar
Laurent Aimar committed
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632
    if( fmt_out.i_height == 0 && fmt_out.i_width > 0 )
    {
        fmt_out.i_height = fmt_in.i_height * fmt_out.i_width / fmt_in.i_width;
        const int i_height = fmt_out.i_height * fmt_in.i_sar_den / fmt_in.i_sar_num;
        if( i_height > 0 )
            fmt_out.i_height = i_height;
    }
    else
    {
        if( fmt_out.i_width == 0 && fmt_out.i_height > 0 )
        {
            fmt_out.i_width = fmt_in.i_width * fmt_out.i_height / fmt_in.i_height;
        }
        else
        {
            fmt_out.i_width = fmt_in.i_width;
            fmt_out.i_height = fmt_in.i_height;
        }
        const int i_width = fmt_out.i_width * fmt_in.i_sar_num / fmt_in.i_sar_den;
        if( i_width > 0 )
            fmt_out.i_width = i_width;
    }

    /* Embedded snapshot
633
       create a snapshot_t* and store it in
634
       object_ptr->p_private, then unlock and signal the
635 636
       waiting object.
     */
Laurent Aimar's avatar
Laurent Aimar committed
637
    if( b_embedded_snapshot )
638
    {
639
        vlc_object_t* p_dest = (vlc_object_t *)(uintptr_t)i_id;
640 641
        block_t *p_block;
        snapshot_t *p_snapshot;
Laurent Aimar's avatar
Laurent Aimar committed
642
        size_t i_size;
643

644
	vlc_object_lock( p_dest );
645 646
        p_dest->p_private = NULL;

647
        /* Save the snapshot to a memory zone */
Laurent Aimar's avatar
Laurent Aimar committed
648
        p_block = image_Write( p_image, p_pic, &fmt_in, &fmt_out );
649
        if( !p_block )
650 651 652
        {
            msg_Err( p_vout, "Could not get snapshot" );
            image_HandlerDelete( p_image );
653
            vlc_object_signal_unlocked( p_dest );
654
	    vlc_object_unlock( p_dest );
655 656 657 658 659
            return VLC_EGENERIC;
        }

        /* Copy the p_block data to a snapshot structure */
        /* FIXME: get the timestamp */
Laurent Aimar's avatar
Laurent Aimar committed
660
        p_snapshot = malloc( sizeof( snapshot_t ) );
661 662 663 664
        if( !p_snapshot )
        {
            block_Release( p_block );
            image_HandlerDelete( p_image );
665
            vlc_object_signal_unlocked( p_dest );
666
	    vlc_object_unlock( p_dest );
667 668 669
            return VLC_ENOMEM;
        }

Jean-Paul Saman's avatar
Jean-Paul Saman committed
670
        i_size = p_block->i_buffer;
671 672 673 674 675

        p_snapshot->i_width = fmt_out.i_width;
        p_snapshot->i_height = fmt_out.i_height;
        p_snapshot->i_datasize = i_size;
        p_snapshot->date = p_block->i_pts; /* FIXME ?? */
Laurent Aimar's avatar
Laurent Aimar committed
676
        p_snapshot->p_data = malloc( i_size );
677 678 679 680 681
        if( !p_snapshot->p_data )
        {
            block_Release( p_block );
            free( p_snapshot );
            image_HandlerDelete( p_image );
682
            vlc_object_signal_unlocked( p_dest );
683
	    vlc_object_unlock( p_dest );
684 685
            return VLC_ENOMEM;
        }
686
        memcpy( p_snapshot->p_data, p_block->p_buffer, p_block->i_buffer );
687

688
        p_dest->p_private = p_snapshot;
689 690

        block_Release( p_block );
691

692
        /* Unlock the object */
693
        vlc_object_signal_unlocked( p_dest );
694
	vlc_object_unlock( p_dest );
695 696

        image_HandlerDelete( p_image );
697
        return VLC_SUCCESS;
698 699
    }

Laurent Aimar's avatar
Laurent Aimar committed
700
    /* Get default directory if none provided */
701
    if( !val.psz_string )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
702
        val.psz_string = VoutSnapshotGetDefaultDirectory( );
703 704
    if( !val.psz_string )
    {
Filippo Carone's avatar
Filippo Carone committed
705
        msg_Err( p_vout, "no path specified for snapshots" );
706
        image_HandlerDelete( p_image );
707 708
        return VLC_EGENERIC;
    }
Laurent Aimar's avatar
Laurent Aimar committed
709 710 711 712

    /* Get snapshot format, default being "png" */
    format.psz_string = var_GetNonEmptyString( p_vout, "snapshot-format" );
    if( !format.psz_string )
713
        format.psz_string = strdup( "png" );
Laurent Aimar's avatar
Laurent Aimar committed
714 715 716 717 718
    if( !format.psz_string )
    {
        free( val.psz_string );
        image_HandlerDelete( p_image );
        return VLC_ENOMEM;
719 720
    }

Filippo Carone's avatar
Filippo Carone committed
721 722 723
    /*
     * Did the user specify a directory? If not, path = NULL.
     */
724
    path = utf8_opendir ( (const char *)val.psz_string  );
725
    if( path != NULL )
Filippo Carone's avatar
Filippo Carone committed
726
    {
727 728 729
        char *psz_prefix = var_GetNonEmptyString( p_vout, "snapshot-prefix" );
        if( psz_prefix == NULL )
            psz_prefix = strdup( "vlcsnap-" );
730 731
        else
        {
732 733
            char *psz_tmp = str_format( p_vout, psz_prefix );
            filename_sanitize( psz_tmp );
734
            free( psz_prefix );
735
            psz_prefix = psz_tmp;
736
        }
737

738
        closedir( path );
739
        if( var_GetBool( p_vout, "snapshot-sequential" ) == true )
740 741
        {
            int i_num = var_GetInteger( p_vout, "snapshot-num" );
742 743
            struct stat st;

744 745
            do
            {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
746
                free( psz_filename );
747 748 749 750 751 752 753 754
                if( asprintf( &psz_filename, "%s" DIR_SEP "%s%05d.%s",
                              val.psz_string, psz_prefix, i_num++,
                              format.psz_string ) == -1 )
                {
                    msg_Err( p_vout, "could not create snapshot" );
                    image_HandlerDelete( p_image );
                    return VLC_EGENERIC;
                }
755
            }
756 757
            while( utf8_stat( psz_filename, &st ) == 0 );

758 759 760 761
            var_SetInteger( p_vout, "snapshot-num", i_num );
        }
        else
        {
basOS G's avatar
basOS G committed
762
            struct tm    curtime;
763 764
            time_t        lcurtime ;
            lcurtime = time( NULL ) ;
basOS G's avatar
basOS G committed
765
            if ( ( localtime_r( &lcurtime, &curtime ) == NULL ) )
766 767 768 769
            {
                msg_Warn( p_vout, "failed to get current time. Falling back to legacy snapshot naming" );
                /* failed to get current time. Fallback to old format */
                if( asprintf( &psz_filename, "%s" DIR_SEP "%s%u.%s",
770 771 772
                          val.psz_string, psz_prefix,
                          (unsigned int)(p_pic->date / 100000) & 0xFFFFFF,
                          format.psz_string ) == -1 )
773 774 775 776 777
                {
                    msg_Err( p_vout, "could not create snapshot" );
                    image_HandlerDelete( p_image );
                    return VLC_EGENERIC;
                }
778
            }
779 780 781
            else
            {
                char psz_curtime[15] ;
basOS G's avatar
basOS G committed
782
                if( strftime( psz_curtime, 15, "%y%m%d-%H%M%S", &curtime ) == 0 )
783 784 785 786 787 788
                {
                    msg_Warn( p_vout, "snapshot date string truncated" ) ;
                }
                if( asprintf( &psz_filename, "%s" DIR_SEP "%s%s%1u.%s",
                      val.psz_string, psz_prefix, psz_curtime,
                     /* suffix with the last decimal digit in 10s of seconds resolution */
basOS G's avatar
basOS G committed
789
                     (unsigned int)(p_pic->date / (100*1000)) & 0xFF,
790 791 792 793 794 795 796 797
                      format.psz_string ) == -1 )
                {
                    msg_Err( p_vout, "could not create snapshot" );
                    image_HandlerDelete( p_image );
                    return VLC_EGENERIC;
                }
            } //end if time() < 0
        } //end snapshot sequential
798
        free( psz_prefix );
Filippo Carone's avatar
Filippo Carone committed
799 800 801
    }
    else // The user specified a full path name (including file name)
    {
802 803
        psz_filename = str_format( p_vout, val.psz_string );
        path_sanitize( psz_filename );
Filippo Carone's avatar
Filippo Carone committed
804
    }
805

806
    free( val.psz_string );
807
    free( format.psz_string );
808 809 810 811 812 813 814 815 816 817 818

    /* Save the snapshot */
    i_ret = image_WriteUrl( p_image, p_pic, &fmt_in, &fmt_out, psz_filename );
    if( i_ret != VLC_SUCCESS )
    {
        msg_Err( p_vout, "could not create snapshot %s", psz_filename );
        free( psz_filename );
        image_HandlerDelete( p_image );
        return VLC_EGENERIC;
    }

Laurent Aimar's avatar
Laurent Aimar committed
819
    /* */
820
    msg_Dbg( p_vout, "snapshot taken (%s)", psz_filename );
821
    vout_OSDMessage( VLC_OBJECT( p_vout ), DEFAULT_CHAN,
822
                     "%s", psz_filename );
823 824 825 826 827 828 829 830

    /* Generate a media player event  - Right now just trigger a global libvlc var
        CHECK: Could not find a more local object. The goal is to communicate
        vout_thread with libvlc_media_player or its input_thread*/
    val.psz_string =  psz_filename  ;

    var_Set( p_vout->p_libvlc, "vout-snapshottaken", val );
    /* var_Set duplicates data for transport so we can free*/
831 832
    free( psz_filename );

Laurent Aimar's avatar
Laurent Aimar committed
833
    /* */
834
    if( var_GetBool( p_vout, "snapshot-preview" ) )
835
    {
Laurent Aimar's avatar
Laurent Aimar committed
836 837
        if( VoutSnapshotPip( p_vout, p_image, p_pic, &fmt_in ) )
            msg_Warn( p_vout, "Failed to display snapshot" );
838
    }
Laurent Aimar's avatar
Laurent Aimar committed
839
    image_HandlerDelete( p_image );
840 841 842 843

    return VLC_SUCCESS;
}

844 845 846 847 848
/*****************************************************************************
 * Handle filters
 *****************************************************************************/

void vout_EnableFilter( vout_thread_t *p_vout, char *psz_name,
849
                        bool b_add, bool b_setconfig )
850 851 852 853 854 855 856 857 858 859 860 861 862
{
    char *psz_parser;
    char *psz_string = config_GetPsz( p_vout, "vout-filter" );

    /* Todo : Use some generic chain manipulation functions */
    if( !psz_string ) psz_string = strdup("");

    psz_parser = strstr( psz_string, psz_name );
    if( b_add )
    {
        if( !psz_parser )
        {
            psz_parser = psz_string;
863 864 865 866 867 868
            if( asprintf( &psz_string, (*psz_string) ? "%s:%s" : "%s%s",
                          psz_string, psz_name ) == -1 )
            {
                free( psz_parser );
                return;
            }
869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900
            free( psz_parser );
        }
        else
            return;
    }
    else
    {
        if( psz_parser )
        {
            memmove( psz_parser, psz_parser + strlen(psz_name) +
                            (*(psz_parser + strlen(psz_name)) == ':' ? 1 : 0 ),
                            strlen(psz_parser + strlen(psz_name)) + 1 );

            /* Remove trailing : : */
            if( *(psz_string+strlen(psz_string ) -1 ) == ':' )
            {
                *(psz_string+strlen(psz_string ) -1 ) = '\0';
            }
         }
         else
         {
             free( psz_string );
             return;
         }
    }
    if( b_setconfig )
        config_PutPsz( p_vout, "vout-filter", psz_string );

    var_SetString( p_vout, "vout-filter", psz_string );
    free( psz_string );
}

901 902 903 904 905
/*****************************************************************************
 * vout_ControlDefault: default methods for video output control.
 *****************************************************************************/
int vout_vaControlDefault( vout_thread_t *p_vout, int i_query, va_list args )
{
906
    (void)args;
907 908 909 910
    switch( i_query )
    {
    case VOUT_REPARENT:
    case VOUT_CLOSE:
911
        vout_ReleaseWindow( p_vout, NULL );
912 913
        return VLC_SUCCESS;

914
    case VOUT_SNAPSHOT:
915
        p_vout->p->b_snapshot = true;
916 917
        return VLC_SUCCESS;

918 919 920
    default:
        msg_Dbg( p_vout, "control query not supported" );
    }
921
    return VLC_EGENERIC;
922 923
}

924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949
/*****************************************************************************
 * InitWindowSize: find the initial dimensions the video window should have.
 *****************************************************************************
 * This function will check the "width", "height" and "zoom" config options and
 * will calculate the size that the video window should have.
 *****************************************************************************/
static void InitWindowSize( vout_thread_t *p_vout, unsigned *pi_width,
                            unsigned *pi_height )
{
    vlc_value_t val;
    int i_width, i_height;
    uint64_t ll_zoom;

#define FP_FACTOR 1000                             /* our fixed point factor */

    var_Get( p_vout, "width", &val );
    i_width = val.i_int;
    var_Get( p_vout, "height", &val );
    i_height = val.i_int;
    var_Get( p_vout, "zoom", &val );
    ll_zoom = (uint64_t)( FP_FACTOR * val.f_float );

    if( i_width > 0 && i_height > 0)
    {
        *pi_width = (int)( i_width * ll_zoom / FP_FACTOR );
        *pi_height = (int)( i_height * ll_zoom / FP_FACTOR );
950
        goto initwsize_end;
951 952 953 954 955 956 957
    }
    else if( i_width > 0 )
    {
        *pi_width = (int)( i_width * ll_zoom / FP_FACTOR );
        *pi_height = (int)( p_vout->fmt_in.i_visible_height * ll_zoom *
            p_vout->fmt_in.i_sar_den * i_width / p_vout->fmt_in.i_sar_num /
            FP_FACTOR / p_vout->fmt_in.i_visible_width );
958
        goto initwsize_end;
959 960 961 962 963 964 965
    }
    else if( i_height > 0 )
    {
        *pi_height = (int)( i_height * ll_zoom / FP_FACTOR );
        *pi_width = (int)( p_vout->fmt_in.i_visible_width * ll_zoom *
            p_vout->fmt_in.i_sar_num * i_height / p_vout->fmt_in.i_sar_den /
            FP_FACTOR / p_vout->fmt_in.i_visible_height );
966
        goto initwsize_end;
967 968
    }

969 970 971 972 973 974
    if( p_vout->fmt_in.i_sar_num == 0 || p_vout->fmt_in.i_sar_den == 0 ) {
        msg_Warn( p_vout, "fucked up aspect" );
        *pi_width = (int)( p_vout->fmt_in.i_visible_width * ll_zoom / FP_FACTOR );
        *pi_height = (int)( p_vout->fmt_in.i_visible_height * ll_zoom /FP_FACTOR);
    }
    else if( p_vout->fmt_in.i_sar_num >= p_vout->fmt_in.i_sar_den )
975 976 977
    {
        *pi_width = (int)( p_vout->fmt_in.i_visible_width * ll_zoom *
            p_vout->fmt_in.i_sar_num / p_vout->fmt_in.i_sar_den / FP_FACTOR );
978
        *pi_height = (int)( p_vout->fmt_in.i_visible_height * ll_zoom
979 980 981 982
            / FP_FACTOR );
    }
    else
    {
983
        *pi_width = (int)( p_vout->fmt_in.i_visible_width * ll_zoom
984 985 986 987 988
            / FP_FACTOR );
        *pi_height = (int)( p_vout->fmt_in.i_visible_height * ll_zoom *
            p_vout->fmt_in.i_sar_den / p_vout->fmt_in.i_sar_num / FP_FACTOR );
    }

989
initwsize_end:
990
    msg_Dbg( p_vout, "window size: %dx%d", p_vout->i_window_width,
991 992
             p_vout->i_window_height );

993 994 995
#undef FP_FACTOR
}

996 997 998 999 1000 1001 1002
/*****************************************************************************
 * Object variables callbacks
 *****************************************************************************/
static int ZoomCallback( vlc_object_t *p_this, char const *psz_cmd,
                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
    vout_thread_t *p_vout = (vout_thread_t *)p_this;
1003
    (void)psz_cmd; (void)oldval; (void)newval; (void)p_data;
1004 1005
    InitWindowSize( p_vout, &p_vout->i_window_width,
                    &p_vout->i_window_height );
1006
    vout_Control( p_vout, VOUT_SET_SIZE, p_vout->i_window_width,
1007
                  p_vout->i_window_height );
1008 1009
    return VLC_SUCCESS;
}
1010

1011 1012 1013 1014 1015 1016 1017
static int CropCallback( vlc_object_t *p_this, char const *psz_cmd,
                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
    vout_thread_t *p_vout = (vout_thread_t *)p_this;
    int64_t i_aspect_num, i_aspect_den;
    unsigned int i_width, i_height;

1018 1019
    (void)oldval; (void)p_data;

1020 1021 1022 1023 1024 1025
    /* Restore defaults */
    p_vout->fmt_in.i_x_offset = p_vout->fmt_render.i_x_offset;
    p_vout->fmt_in.i_visible_width = p_vout->fmt_render.i_visible_width;
    p_vout->fmt_in.i_y_offset = p_vout->fmt_render.i_y_offset;
    p_vout->fmt_in.i_visible_height = p_vout->fmt_render.i_visible_height;

1026
    if( !strcmp( psz_cmd, "crop" ) )
1027
    {
Jean-Paul Saman's avatar
Jean-Paul Saman committed
1028
        char *psz_end = NULL, *psz_parser = strchr( newval.psz_string, ':' );
1029 1030 1031 1032 1033
        if( psz_parser )
        {
            /* We're using the 3:4 syntax */
            i_aspect_num = strtol( newval.psz_string, &psz_end, 10 );
            if( psz_end == newval.psz_string || !i_aspect_num ) goto crop_end;
1034

1035 1036
            i_aspect_den = strtol( ++psz_parser, &psz_end, 10 );
            if( psz_end == psz_parser || !i_aspect_den ) goto crop_end;
1037

1038 1039 1040 1041
            i_width = p_vout->fmt_in.i_sar_den*p_vout->fmt_render.i_visible_height *
                i_aspect_num / i_aspect_den / p_vout->fmt_in.i_sar_num;
            i_height = p_vout->fmt_render.i_visible_width*p_vout->fmt_in.i_sar_num *
                i_aspect_den / i_aspect_num / p_vout->fmt_in.i_sar_den;
1042

1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054
            if( i_width < p_vout->fmt_render.i_visible_width )
            {
                p_vout->fmt_in.i_x_offset = p_vout->fmt_render.i_x_offset +
                    (p_vout->fmt_render.i_visible_width - i_width) / 2;
                p_vout->fmt_in.i_visible_width = i_width;
            }
            else
            {
                p_vout->fmt_in.i_y_offset = p_vout->fmt_render.i_y_offset +
                    (p_vout->fmt_render.i_visible_height - i_height) / 2;
                p_vout->fmt_in.i_visible_height = i_height;
            }
1055 1056 1057
        }
        else
        {
1058 1059 1060 1061 1062
            psz_parser = strchr( newval.psz_string, 'x' );
            if( psz_parser )
            {
                /* Maybe we're using the <width>x<height>+<left>+<top> syntax */
                unsigned int i_crop_width, i_crop_height, i_crop_top, i_crop_left;
1063

1064 1065
                i_crop_width = strtol( newval.psz_string, &psz_end, 10 );
                if( psz_end != psz_parser ) goto crop_end;
1066

1067 1068 1069
                psz_parser = strchr( ++psz_end, '+' );
                i_crop_height = strtol( psz_end, &psz_end, 10 );
                if( psz_end != psz_parser ) goto crop_end;
1070

1071 1072 1073
                psz_parser = strchr( ++psz_end, '+' );
                i_crop_left = strtol( psz_end, &psz_end, 10 );
                if( psz_end != psz_parser ) goto crop_end;
1074

Jean-Paul Saman's avatar
Jean-Paul Saman committed
1075 1076
                psz_end++;
                i_crop_top = strtol( psz_end, &psz_end, 10 );
1077
                if( *psz_end != '\0' ) goto crop_end;
1078

1079 1080
                i_width = i_crop_width;
                p_vout->fmt_in.i_visible_width = i_width;
1081

1082 1083
                i_height = i_crop_height;
                p_vout->fmt_in.i_visible_height = i_height;
1084

1085 1086 1087 1088 1089 1090 1091
                p_vout->fmt_in.i_x_offset = i_crop_left;
                p_vout->fmt_in.i_y_offset = i_crop_top;
            }
            else
            {
                /* Maybe we're using the <left>+<top>+<right>+<bottom> syntax */
                unsigned int i_crop_top, i_crop_left, i_crop_bottom, i_crop_right;
1092

1093 1094 1095
                psz_parser = strchr( newval.psz_string, '+' );
                i_crop_left = strtol( newval.psz_string, &psz_end, 10 );
                if( psz_end != psz_parser ) goto crop_end;
1096

1097 1098 1099
                psz_parser = strchr( ++psz_end, '+' );
                i_crop_top = strtol( psz_end, &psz_end, 10 );
                if( psz_end != psz_parser ) goto crop_end;
1100

1101 1102 1103
                psz_parser = strchr( ++psz_end, '+' );
                i_crop_right = strtol( psz_end, &psz_end, 10 );
                if( psz_end != psz_parser ) goto crop_end;
1104

Jean-Paul Saman's avatar
Jean-Paul Saman committed
1105 1106
                psz_end++;
                i_crop_bottom = strtol( psz_end, &psz_end, 10 );
1107
                if( *psz_end != '\0' ) goto crop_end;
1108

1109 1110 1111
                i_width = p_vout->fmt_render.i_visible_width
                          - i_crop_left - i_crop_right;
                p_vout->fmt_in.i_visible_width = i_width;
1112

1113 1114 1115
                i_height = p_vout->fmt_render.i_visible_height
                           - i_crop_top - i_crop_bottom;
                p_vout->fmt_in.i_visible_height = i_height;
1116

1117 1118 1119
                p_vout->fmt_in.i_x_offset = i_crop_left;
                p_vout->fmt_in.i_y_offset = i_crop_top;
            }
1120
        }
1121
    }
1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144
    else if( !strcmp( psz_cmd, "crop-top" )
          || !strcmp( psz_cmd, "crop-left" )
          || !strcmp( psz_cmd, "crop-bottom" )
          || !strcmp( psz_cmd, "crop-right" ) )
    {
        unsigned int i_crop_top, i_crop_left, i_crop_bottom, i_crop_right;

        i_crop_top = var_GetInteger( p_vout, "crop-top" );
        i_crop_left = var_GetInteger( p_vout, "crop-left" );
        i_crop_right = var_GetInteger( p_vout, "crop-right" );
        i_crop_bottom = var_GetInteger( p_vout, "crop-bottom" );

        i_width = p_vout->fmt_render.i_visible_width
                  - i_crop_left - i_crop_right;
        p_vout->fmt_in.i_visible_width = i_width;

        i_height = p_vout->fmt_render.i_visible_height
                   - i_crop_top - i_crop_bottom;
        p_vout->fmt_in.i_visible_height = i_height;

        p_vout->fmt_in.i_x_offset = i_crop_left;
        p_vout->fmt_in.i_y_offset = i_crop_top;
    }
1145 1146

 crop_end:
1147 1148 1149
    InitWindowSize( p_vout, &p_vout->i_window_width,
                    &p_vout->i_window_height );

1150 1151
    p_vout->i_changes |= VOUT_CROP_CHANGE;

1152 1153 1154 1155 1156 1157
    msg_Dbg( p_vout, "cropping picture %ix%i to %i,%i,%ix%i",
             p_vout->fmt_in.i_width, p_vout->fmt_in.i_height,
             p_vout->fmt_in.i_x_offset, p_vout->fmt_in.i_y_offset,
             p_vout->fmt_in.i_visible_width,
             p_vout->fmt_in.i_visible_height );

1158 1159
    var_SetVoid( p_vout, "crop-update" );

1160 1161 1162
    return VLC_SUCCESS;
}

1163 1164 1165 1166 1167 1168 1169 1170
static int AspectCallback( vlc_object_t *p_this, char const *psz_cmd,
                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
    vout_thread_t *p_vout = (vout_thread_t *)p_this;
    unsigned int i_aspect_num, i_aspect_den, i_sar_num, i_sar_den;
    vlc_value_t val;

    char *psz_end, *psz_parser = strchr( newval.psz_string, ':' );
1171
    (void)psz_cmd; (void)oldval; (void)p_data;
1172 1173 1174 1175 1176 1177 1178 1179 1180

    /* Restore defaults */
    p_vout->fmt_in.i_sar_num = p_vout->fmt_render.i_sar_num;
    p_vout->