video_output.c 33.2 KB
Newer Older
1
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
2 3 4
 * video_output.c : video output thread
 * This module describes the programming interface for video output threads.
 * It includes functions allowing to open a new thread, send pictures to a
5 6
 * thread, and destroy a previously oppened video output thread.
 *****************************************************************************
7
 * Copyright (C) 2000-2001 VideoLAN
8
 * $Id: video_output.c,v 1.186 2002/07/02 22:07:02 jlj Exp $
9
 *
Sam Hocevar's avatar
 
Sam Hocevar committed
10
 * Authors: Vincent Seguin <seguin@via.ecp.fr>
11 12 13 14 15
 *
 * 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.
Sam Hocevar's avatar
Sam Hocevar committed
16
 *
17 18
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
21
 *
22 23 24
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
25
 *****************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
26

27
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
28
 * Preamble
29
 *****************************************************************************/
30 31 32 33
#include <errno.h>                                                 /* ENOMEM */
#include <stdlib.h>                                                /* free() */
#include <stdio.h>                                              /* sprintf() */
#include <string.h>                                            /* strerror() */
Vincent Seguin's avatar
Vincent Seguin committed
34

35
#include <vlc/vlc.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
36

37 38
#ifdef HAVE_SYS_TIMES_H
#   include <sys/times.h>
39
#endif
40

Michel Kaempf's avatar
Michel Kaempf committed
41 42
#include "video.h"
#include "video_output.h"
43

44 45 46 47
#if defined( SYS_DARWIN )
#include "darwin_specific.h"
#endif

48
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
49
 * Local prototypes
50
 *****************************************************************************/
51 52 53 54 55
static int      InitThread        ( vout_thread_t * );
static void     RunThread         ( vout_thread_t * );
static void     ErrorThread       ( vout_thread_t * );
static void     EndThread         ( vout_thread_t * );
static void     DestroyThread     ( vout_thread_t * );
Sam Hocevar's avatar
 
Sam Hocevar committed
56

Sam Hocevar's avatar
 
Sam Hocevar committed
57
static int      ReduceHeight      ( int );
Sam Hocevar's avatar
 
Sam Hocevar committed
58 59
static int      BinaryLog         ( u32 );
static void     MaskToShift       ( int *, int *, u32 );
Gildas Bazin's avatar
 
Gildas Bazin committed
60
static void     InitWindowSize    ( vout_thread_t *, int *, int * );
Michel Kaempf's avatar
Michel Kaempf committed
61

62
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
63
 * vout_CreateThread: creates a new video output thread
64
 *****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
65 66
 * This function creates a new video output thread, and returns a pointer
 * to its description. On error, it returns NULL.
67
 *****************************************************************************/
68
vout_thread_t * __vout_CreateThread ( vlc_object_t *p_parent,
Sam Hocevar's avatar
 
Sam Hocevar committed
69
                                      int i_width, int i_height,
Sam Hocevar's avatar
 
Sam Hocevar committed
70
                                      u32 i_chroma, int i_aspect )
Michel Kaempf's avatar
Michel Kaempf committed
71
{
72
    vout_thread_t * p_vout;                             /* thread descriptor */
Sam Hocevar's avatar
 
Sam Hocevar committed
73
    int             i_index;                                /* loop variable */
Sam Hocevar's avatar
 
Sam Hocevar committed
74
    char          * psz_plugin;
Michel Kaempf's avatar
Michel Kaempf committed
75

76
    /* Allocate descriptor */
77
    p_vout = vlc_object_create( p_parent, VLC_OBJECT_VOUT );
78
    if( p_vout == NULL )
Michel Kaempf's avatar
Michel Kaempf committed
79
    {
80
        msg_Err( p_parent, "out of memory" );
Michel Kaempf's avatar
Michel Kaempf committed
81 82
        return( NULL );
    }
83

Gildas Bazin's avatar
 
Gildas Bazin committed
84 85 86
    /* If the parent is not a VOUT object, that means we are at the start of
     * the video output pipe */
    if( p_parent->i_object_type != VLC_OBJECT_VOUT )
Sam Hocevar's avatar
 
Sam Hocevar committed
87
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
        /* look for the default filter configuration */
        p_vout->psz_filter_chain = config_GetPsz( p_parent, "filter" );
    }
    else
    {
        /* continue the parent's filter chain */
        char *psz_end;

        psz_end = strchr( ((vout_thread_t *)p_parent)->psz_filter_chain, ':' );
        if( psz_end && *(psz_end+1) )
            p_vout->psz_filter_chain = strdup( psz_end+1 );
        else p_vout->psz_filter_chain = NULL;
    }

    /* Choose the video output module */
    if( !p_vout->psz_filter_chain )
    {
        psz_plugin = config_GetPsz( p_parent, "vout" );
    }
    else
    {
        /* the filter chain is a string list of filters separated by double
         * colons */
        char *psz_end;

        psz_end = strchr( p_vout->psz_filter_chain, ':' );
        if( psz_end )
            psz_plugin = strndup( p_vout->psz_filter_chain,
                                  psz_end - p_vout->psz_filter_chain );
        else psz_plugin = strdup( p_vout->psz_filter_chain );
Sam Hocevar's avatar
 
Sam Hocevar committed
118 119
    }

120
    /* Initialize pictures and subpictures - translation tables and functions
121
     * will be initialized later in InitThread */
Sam Hocevar's avatar
 
Sam Hocevar committed
122
    for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES; i_index++)
123
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
124
        p_vout->p_picture[i_index].i_status = FREE_PICTURE;
Sam Hocevar's avatar
 
Sam Hocevar committed
125
        p_vout->p_picture[i_index].i_type   = EMPTY_PICTURE;
126
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
127

128 129
    for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++)
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
130
        p_vout->p_subpicture[i_index].i_status = FREE_SUBPICTURE;
Sam Hocevar's avatar
 
Sam Hocevar committed
131
        p_vout->p_subpicture[i_index].i_type   = EMPTY_SUBPICTURE;
132
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
133

Sam Hocevar's avatar
 
Sam Hocevar committed
134
    /* No images in the heap */
Sam Hocevar's avatar
 
Sam Hocevar committed
135 136
    p_vout->i_heap_size = 0;

Sam Hocevar's avatar
 
Sam Hocevar committed
137
    /* Initialize the rendering heap */
Sam Hocevar's avatar
 
Sam Hocevar committed
138 139 140 141 142
    I_RENDERPICTURES = 0;
    p_vout->render.i_width    = i_width;
    p_vout->render.i_height   = i_height;
    p_vout->render.i_chroma   = i_chroma;
    p_vout->render.i_aspect   = i_aspect;
Sam Hocevar's avatar
 
Sam Hocevar committed
143

Sam Hocevar's avatar
 
Sam Hocevar committed
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
    p_vout->render.i_rmask    = 0;
    p_vout->render.i_gmask    = 0;
    p_vout->render.i_bmask    = 0;

    /* Zero the output heap */
    I_OUTPUTPICTURES = 0;
    p_vout->output.i_width    = 0;
    p_vout->output.i_height   = 0;
    p_vout->output.i_chroma   = 0;
    p_vout->output.i_aspect   = 0;

    p_vout->output.i_rmask    = 0;
    p_vout->output.i_gmask    = 0;
    p_vout->output.i_bmask    = 0;

Sam Hocevar's avatar
 
Sam Hocevar committed
159
    /* Initialize misc stuff */
Sam Hocevar's avatar
 
Sam Hocevar committed
160 161 162 163 164 165
    p_vout->i_changes    = 0;
    p_vout->f_gamma      = 0;
    p_vout->b_grayscale  = 0;
    p_vout->b_info       = 0;
    p_vout->b_interface  = 0;
    p_vout->b_scale      = 1;
Gildas Bazin's avatar
 
Gildas Bazin committed
166
    p_vout->b_fullscreen = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
167
    p_vout->render_time  = 10;
Gildas Bazin's avatar
 
Gildas Bazin committed
168
    p_vout->c_fps_samples= 0;
169

170 171 172 173
    p_vout->i_mouse_x = 0;
    p_vout->i_mouse_y = 0;
    p_vout->i_mouse_button = 0;

Gildas Bazin's avatar
 
Gildas Bazin committed
174
    /* user requested fullscreen? */
175 176
    if( config_GetInt( p_vout, "fullscreen" ) )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
177
        p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
178
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
179

Gildas Bazin's avatar
 
Gildas Bazin committed
180 181 182 183 184
    /* Initialize the dimensions of the video window */
    InitWindowSize( p_vout, &p_vout->i_window_width,
                    &p_vout->i_window_height );


Gildas Bazin's avatar
 
Gildas Bazin committed
185 186 187 188 189
    p_vout->p_module = module_Need( p_vout,
                           ( p_vout->psz_filter_chain ) ?
                           MODULE_CAPABILITY_VOUT_FILTER :
                           MODULE_CAPABILITY_VOUT,
                           psz_plugin, (void *)p_vout );
Sam Hocevar's avatar
 
Sam Hocevar committed
190

Gildas Bazin's avatar
 
Gildas Bazin committed
191
    if( psz_plugin ) free( psz_plugin );
Sam Hocevar's avatar
 
Sam Hocevar committed
192 193
    if( p_vout->p_module == NULL )
    {
194 195
        msg_Err( p_vout, "no suitable vout module" );
        vlc_object_destroy( p_vout );
Sam Hocevar's avatar
 
Sam Hocevar committed
196 197 198 199 200 201 202 203 204 205 206 207 208
        return( NULL );
    }

#define f p_vout->p_module->p_functions->vout.functions.vout
    p_vout->pf_create     = f.pf_create;
    p_vout->pf_init       = f.pf_init;
    p_vout->pf_end        = f.pf_end;
    p_vout->pf_destroy    = f.pf_destroy;
    p_vout->pf_manage     = f.pf_manage;
    p_vout->pf_render     = f.pf_render;
    p_vout->pf_display    = f.pf_display;
#undef f

Michel Kaempf's avatar
Michel Kaempf committed
209
    /* Create thread and set locks */
210 211 212
    vlc_mutex_init( p_vout, &p_vout->picture_lock );
    vlc_mutex_init( p_vout, &p_vout->subpicture_lock );
    vlc_mutex_init( p_vout, &p_vout->change_lock );
213

214 215 216
    vlc_object_attach( p_vout, p_parent );

    if( vlc_thread_create( p_vout, "video output", RunThread, 0 ) )
Michel Kaempf's avatar
Michel Kaempf committed
217
    {
218
        msg_Err( p_vout, "%s", strerror(ENOMEM) );
Sam Hocevar's avatar
 
Sam Hocevar committed
219
        p_vout->pf_destroy( p_vout );
Gildas Bazin's avatar
 
Gildas Bazin committed
220
        module_Unneed( p_vout->p_module );
221 222
        vlc_object_destroy( p_vout );
        return NULL;
223
    }
Michel Kaempf's avatar
Michel Kaempf committed
224

225
    return p_vout;
Michel Kaempf's avatar
Michel Kaempf committed
226 227
}

228
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
229
 * vout_DestroyThread: destroys a previously created thread
230 231
 *****************************************************************************
 * Destroy a terminated thread.
Michel Kaempf's avatar
Michel Kaempf committed
232
 * The function will request a destruction of the specified thread. If pi_error
233
 * is NULL, it will return once the thread is destroyed. Else, it will be
Michel Kaempf's avatar
Michel Kaempf committed
234
 * update using one of the THREAD_* constants.
235
 *****************************************************************************/
236
void vout_DestroyThread( vout_thread_t *p_vout )
237
{
238
    /* Unlink object */
239
    vlc_object_detach_all( p_vout );
240

Michel Kaempf's avatar
Michel Kaempf committed
241 242
    /* Request thread destruction */
    p_vout->b_die = 1;
243
    vlc_thread_join( p_vout );
Michel Kaempf's avatar
Michel Kaempf committed
244

245
    /* Free structure */
246
    vlc_object_destroy( p_vout );
Michel Kaempf's avatar
Michel Kaempf committed
247 248
}

249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
/*****************************************************************************
 * vout_ChromaCmp: compare two chroma values
 *****************************************************************************
 * This function returns 1 if the two fourcc values given as argument are
 * the same format (eg. UYVY / UYNV) or almost the same format (eg. I420/YV12)
 *****************************************************************************/
int vout_ChromaCmp( u32 i_chroma, u32 i_amorhc )
{
    /* If they are the same, they are the same ! */
    if( i_chroma == i_amorhc )
    {
        return 1;
    }

    /* Check for equivalence classes */
    switch( i_chroma )
    {
        case FOURCC_I420:
        case FOURCC_IYUV:
        case FOURCC_YV12:
            switch( i_amorhc )
            {
                case FOURCC_I420:
                case FOURCC_IYUV:
                case FOURCC_YV12:
                    return 1;

                default:
                    return 0;
            }

        case FOURCC_UYVY:
        case FOURCC_UYNV:
        case FOURCC_Y422:
            switch( i_amorhc )
            {
                case FOURCC_UYVY:
                case FOURCC_UYNV:
                case FOURCC_Y422:
                    return 1;

                default:
                    return 0;
            }

        case FOURCC_YUY2:
        case FOURCC_YUNV:
            switch( i_amorhc )
            {
                case FOURCC_YUY2:
                case FOURCC_YUNV:
                    return 1;

                default:
                    return 0;
            }

        default:
            return 0;
    }
}

311
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
312
 * InitThread: initialize video output thread
313
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
314 315 316
 * This function is called from RunThread and performs the second step of the
 * initialization. It returns 0 on success. Note that the thread's flag are not
 * modified inside this function.
317
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
318
static int InitThread( vout_thread_t *p_vout )
Vincent Seguin's avatar
Vincent Seguin committed
319
{
Sam Hocevar's avatar
 
Sam Hocevar committed
320
    int i, i_pgcd;
Sam Hocevar's avatar
 
Sam Hocevar committed
321

Sam Hocevar's avatar
 
Sam Hocevar committed
322 323 324 325
    vlc_mutex_lock( &p_vout->change_lock );

#ifdef STATS
    p_vout->c_loops = 0;
Vincent Seguin's avatar
Vincent Seguin committed
326 327
#endif

Sam Hocevar's avatar
 
Sam Hocevar committed
328
    /* Initialize output method, it allocates direct buffers for us */
Sam Hocevar's avatar
 
Sam Hocevar committed
329
    if( p_vout->pf_init( p_vout ) )
Vincent Seguin's avatar
Vincent Seguin committed
330
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
331 332 333 334
        vlc_mutex_unlock( &p_vout->change_lock );
        return( 1 );
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
335
    if( !I_OUTPUTPICTURES )
Sam Hocevar's avatar
 
Sam Hocevar committed
336
    {
337 338
        msg_Err( p_vout, "plugin was unable to allocate at least "
                         "one direct buffer" );
Sam Hocevar's avatar
 
Sam Hocevar committed
339
        p_vout->pf_end( p_vout );
Sam Hocevar's avatar
 
Sam Hocevar committed
340 341
        vlc_mutex_unlock( &p_vout->change_lock );
        return( 1 );
342
    }
Vincent Seguin's avatar
Vincent Seguin committed
343

344
    msg_Dbg( p_vout, "got %i direct buffer(s)", I_OUTPUTPICTURES );
Sam Hocevar's avatar
 
Sam Hocevar committed
345 346

    i_pgcd = ReduceHeight( p_vout->render.i_aspect );
347 348 349 350 351
    msg_Dbg( p_vout,
             "picture in %ix%i, chroma 0x%.8x (%4.4s), aspect ratio %i:%i",
             p_vout->render.i_width, p_vout->render.i_height,
             p_vout->render.i_chroma, (char*)&p_vout->render.i_chroma,
             p_vout->render.i_aspect / i_pgcd, VOUT_ASPECT_FACTOR / i_pgcd );
Sam Hocevar's avatar
 
Sam Hocevar committed
352 353

    i_pgcd = ReduceHeight( p_vout->output.i_aspect );
354 355 356 357 358
    msg_Dbg( p_vout,
             "picture out %ix%i, chroma 0x%.8x (%4.4s), aspect ratio %i:%i",
             p_vout->output.i_width, p_vout->output.i_height,
             p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma,
             p_vout->output.i_aspect / i_pgcd, VOUT_ASPECT_FACTOR / i_pgcd );
Sam Hocevar's avatar
 
Sam Hocevar committed
359

Sam Hocevar's avatar
 
Sam Hocevar committed
360 361 362 363 364 365 366 367
    /* Calculate shifts from system-updated masks */
    MaskToShift( &p_vout->output.i_lrshift, &p_vout->output.i_rrshift,
                 p_vout->output.i_rmask );
    MaskToShift( &p_vout->output.i_lgshift, &p_vout->output.i_rgshift,
                 p_vout->output.i_gmask );
    MaskToShift( &p_vout->output.i_lbshift, &p_vout->output.i_rbshift,
                 p_vout->output.i_bmask );

Sam Hocevar's avatar
 
Sam Hocevar committed
368 369 370 371
    /* Check whether we managed to create direct buffers similar to
     * the render buffers, ie same size, chroma and aspect ratio */
    if( ( p_vout->output.i_width == p_vout->render.i_width )
     && ( p_vout->output.i_height == p_vout->render.i_height )
Sam Hocevar's avatar
 
Sam Hocevar committed
372
     && ( vout_ChromaCmp( p_vout->output.i_chroma, p_vout->render.i_chroma ) )
Sam Hocevar's avatar
 
Sam Hocevar committed
373 374
     && ( p_vout->output.i_aspect == p_vout->render.i_aspect ) )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
375 376 377 378
        /* Cool ! We have direct buffers, we can ask the decoder to
         * directly decode into them ! Map the first render buffers to
         * the first direct buffers, but keep the first direct buffer
         * for memcpy operations */
Sam Hocevar's avatar
 
Sam Hocevar committed
379 380
        p_vout->b_direct = 1;

381 382 383
        msg_Dbg( p_vout, "direct render, mapping "
                 "render pictures 0-%i to system pictures 1-%i",
                 VOUT_MAX_PICTURES - 2, VOUT_MAX_PICTURES - 1 );
Sam Hocevar's avatar
 
Sam Hocevar committed
384 385 386 387 388 389

        for( i = 1; i < VOUT_MAX_PICTURES; i++ )
        {
            PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
            I_RENDERPICTURES++;
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
390 391 392
    }
    else
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
393 394 395
        /* Rats... Something is wrong here, we could not find an output
         * plugin able to directly render what we decode. See if we can
         * find a chroma plugin to do the conversion */
Sam Hocevar's avatar
 
Sam Hocevar committed
396 397
        p_vout->b_direct = 0;

Sam Hocevar's avatar
 
Sam Hocevar committed
398
        /* Choose the best module */
399 400 401
        p_vout->chroma.p_module =
            module_Need( p_vout, MODULE_CAPABILITY_CHROMA,
                         NULL, (void *)p_vout );
Sam Hocevar's avatar
 
Sam Hocevar committed
402

Sam Hocevar's avatar
 
Sam Hocevar committed
403 404
        if( p_vout->chroma.p_module == NULL )
        {
405 406
            msg_Err( p_vout, "no chroma module for %4.4s to %4.4s",
                     &p_vout->render.i_chroma, &p_vout->output.i_chroma );
Sam Hocevar's avatar
 
Sam Hocevar committed
407 408 409 410
            p_vout->pf_end( p_vout );
            vlc_mutex_unlock( &p_vout->change_lock );
            return( 1 );
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
411

Sam Hocevar's avatar
 
Sam Hocevar committed
412
#define f p_vout->chroma.p_module->p_functions->chroma.functions.chroma
Gildas Bazin's avatar
 
Gildas Bazin committed
413
        p_vout->chroma.pf_init       = f.pf_init;
Sam Hocevar's avatar
 
Sam Hocevar committed
414 415 416
        p_vout->chroma.pf_end        = f.pf_end;
#undef f

Sam Hocevar's avatar
 
Sam Hocevar committed
417
        if( I_OUTPUTPICTURES < 2 * VOUT_MAX_PICTURES )
Sam Hocevar's avatar
 
Sam Hocevar committed
418
        {
419 420 421 422
            msg_Dbg( p_vout, "indirect render, mapping "
                     "render pictures %i-%i to system pictures %i-%i",
                     I_OUTPUTPICTURES - 1, 2 * VOUT_MAX_PICTURES - 2,
                     I_OUTPUTPICTURES, 2 * VOUT_MAX_PICTURES - 1 );
Sam Hocevar's avatar
 
Sam Hocevar committed
423 424 425
        }
        else
        {
426 427 428 429
            /* FIXME: if this happens, we don't have any render pictures left */
            msg_Dbg( p_vout, "indirect render, no system pictures needed,"
                     " we have %i directbuffers", I_OUTPUTPICTURES );
            msg_Err( p_vout, "this is a bug!" );
Sam Hocevar's avatar
 
Sam Hocevar committed
430
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
431 432

        /* Append render buffers after the direct buffers */
Sam Hocevar's avatar
 
Sam Hocevar committed
433
        for( i = I_OUTPUTPICTURES; i < 2 * VOUT_MAX_PICTURES; i++ )
Sam Hocevar's avatar
 
Sam Hocevar committed
434 435 436 437 438
        {
            PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
            I_RENDERPICTURES++;
        }
    }
Vincent Seguin's avatar
Vincent Seguin committed
439

Sam Hocevar's avatar
 
Sam Hocevar committed
440 441 442 443 444 445 446 447 448 449 450
    /* Link pictures back to their heap */
    for( i = 0 ; i < I_RENDERPICTURES ; i++ )
    {
        PP_RENDERPICTURE[ i ]->p_heap = &p_vout->render;
    }

    for( i = 0 ; i < I_OUTPUTPICTURES ; i++ )
    {
        PP_OUTPUTPICTURE[ i ]->p_heap = &p_vout->output;
    }

451
/* XXX XXX mark thread ready */
Sam Hocevar's avatar
 
Sam Hocevar committed
452
    return( 0 );
Vincent Seguin's avatar
Vincent Seguin committed
453 454
}

455
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
456
 * RunThread: video output thread
457
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
458 459 460
 * Video output thread. This function does only returns when the thread is
 * terminated. It handles the pictures arriving in the video heap and the
 * display device events.
461
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
462
static void RunThread( vout_thread_t *p_vout)
Vincent Seguin's avatar
Vincent Seguin committed
463
{
Sam Hocevar's avatar
 
Sam Hocevar committed
464
    int             i_index;                                /* index in heap */
465
    int             i_idle_loops = 0;  /* loops without displaying a picture */
Sam Hocevar's avatar
 
Sam Hocevar committed
466 467 468 469
    mtime_t         current_date;                            /* current date */
    mtime_t         display_date;                            /* display date */

    picture_t *     p_picture;                            /* picture pointer */
470
    picture_t *     p_last_picture = NULL;                   /* last picture */
Sam Hocevar's avatar
 
Sam Hocevar committed
471 472 473 474 475 476 477 478 479 480 481 482
    picture_t *     p_directbuffer;              /* direct buffer to display */

    subpicture_t *  p_subpic;                          /* subpicture pointer */

    /*
     * Initialize thread
     */
    p_vout->b_error = InitThread( p_vout );
    if( p_vout->b_error )
    {
        /* Destroy thread structures allocated by Create and InitThread */
        p_vout->pf_destroy( p_vout );
483

484
        DestroyThread( p_vout );
Sam Hocevar's avatar
 
Sam Hocevar committed
485 486
        return;
    }
487

488
    /*
Sam Hocevar's avatar
 
Sam Hocevar committed
489 490
     * Main loop - it is not executed if an error occured during
     * initialization
491
     */
Sam Hocevar's avatar
 
Sam Hocevar committed
492
    while( (!p_vout->b_die) && (!p_vout->b_error) )
493
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
494
        /* Initialize loop variables */
Sam Hocevar's avatar
 
Sam Hocevar committed
495 496
        display_date = 0;
        current_date = mdate();
Sam Hocevar's avatar
 
Sam Hocevar committed
497 498 499 500

#ifdef STATS
        p_vout->c_loops++;
        if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
501
        {
502 503
            msg_Dbg( p_vout, "picture heap: %d/%d",
                     I_RENDERPICTURES, p_vout->i_heap_size );
Sam Hocevar's avatar
 
Sam Hocevar committed
504
        }
505
#endif
Sam Hocevar's avatar
 
Sam Hocevar committed
506 507 508 509 510

        /*
         * Find the picture to display - this operation does not need lock,
         * since only READY_PICTUREs are handled
         */
Sam Hocevar's avatar
 
Sam Hocevar committed
511 512
        p_picture = NULL;

513
        for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
Sam Hocevar's avatar
 
Sam Hocevar committed
514
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
515 516 517
            if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
                && ( (p_picture == NULL) ||
                     (PP_RENDERPICTURE[i_index]->date < display_date) ) )
518
            {
Sam Hocevar's avatar
 
Sam Hocevar committed
519
                p_picture = PP_RENDERPICTURE[i_index];
Sam Hocevar's avatar
 
Sam Hocevar committed
520
                display_date = p_picture->date;
521 522
            }
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
523 524

        if( p_picture != NULL )
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
            /* If we met the last picture, parse again to see whether there is
             * a more appropriate one. */
            if( p_picture == p_last_picture )
            {
                for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
                {
                    if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
                        && (PP_RENDERPICTURE[i_index] != p_last_picture)
                        && ((p_picture == p_last_picture) ||
                            (PP_RENDERPICTURE[i_index]->date < display_date)) )
                    {
                        p_picture = PP_RENDERPICTURE[i_index];
                        display_date = p_picture->date;
                    }
                }
            }
    
            /* If we found better than the last picture, destroy it */
            if( p_last_picture && p_picture != p_last_picture )
            {
                vlc_mutex_lock( &p_vout->picture_lock );
                if( p_last_picture->i_refcount )
                {
                    p_last_picture->i_status = DISPLAYED_PICTURE;
                }
                else
                {
                    p_last_picture->i_status = DESTROYED_PICTURE;
                    p_vout->i_heap_size--;
                }
                vlc_mutex_unlock( &p_vout->picture_lock );
557
                p_last_picture = NULL;
558 559
            }

Sam Hocevar's avatar
 
Sam Hocevar committed
560 561 562 563
            /* Compute FPS rate */
            p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ]
                = display_date;

564
            if( !p_picture->b_force &&
565
                p_picture != p_last_picture &&
566
                display_date < current_date + p_vout->render_time )
Sam Hocevar's avatar
 
Sam Hocevar committed
567 568 569 570 571 572 573 574 575 576 577 578 579 580
            {
                /* Picture is late: it will be destroyed and the thread
                 * will directly choose the next picture */
                vlc_mutex_lock( &p_vout->picture_lock );
                if( p_picture->i_refcount )
                {
                    /* Pretend we displayed the picture, but don't destroy
                     * it since the decoder might still need it. */
                    p_picture->i_status = DISPLAYED_PICTURE;
                }
                else
                {
                    /* Destroy the picture without displaying it */
                    p_picture->i_status = DESTROYED_PICTURE;
Sam Hocevar's avatar
 
Sam Hocevar committed
581
                    p_vout->i_heap_size--;
Sam Hocevar's avatar
 
Sam Hocevar committed
582
                }
583 584
                msg_Warn( p_vout, "late picture skipped (%lld)",
                                  current_date - display_date );
585 586 587 588
                vlc_mutex_unlock( &p_vout->picture_lock );

                continue;
            }
Christophe Massiot's avatar
Christophe Massiot committed
589 590
#if 0
            /* Removed because it causes problems for some people --Meuuh */
591
            if( display_date > current_date + VOUT_BOGUS_DELAY )
592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
            {
                /* Picture is waaay too early: it will be destroyed */
                vlc_mutex_lock( &p_vout->picture_lock );
                if( p_picture->i_refcount )
                {
                    /* Pretend we displayed the picture, but don't destroy
                     * it since the decoder might still need it. */
                    p_picture->i_status = DISPLAYED_PICTURE;
                }
                else
                {
                    /* Destroy the picture without displaying it */
                    p_picture->i_status = DESTROYED_PICTURE;
                    p_vout->i_heap_size--;
                }
                intf_WarnMsg( 1, "vout warning: early picture skipped (%lld)",
                              display_date - current_date );
Sam Hocevar's avatar
 
Sam Hocevar committed
609 610 611 612
                vlc_mutex_unlock( &p_vout->picture_lock );

                continue;
            }
Christophe Massiot's avatar
Christophe Massiot committed
613
#endif
614
            if( display_date > current_date + VOUT_DISPLAY_DELAY )
Sam Hocevar's avatar
 
Sam Hocevar committed
615 616 617 618 619 620 621 622
            {
                /* A picture is ready to be rendered, but its rendering date
                 * is far from the current one so the thread will perform an
                 * empty loop as if no picture were found. The picture state
                 * is unchanged */
                p_picture    = NULL;
                display_date = 0;
            }
623 624
            else if( p_picture == p_last_picture )
            {
625 626
                /* We are asked to repeat the previous picture, but we first
                 * wait for a couple of idle loops */
627 628 629 630 631 632 633 634 635 636 637 638 639 640 641
                if( i_idle_loops < 4 )
                {
                    p_picture    = NULL;
                    display_date = 0;
                }
                else
                {
                    //intf_WarnMsg( 6, "vout info: duplicating picture" );
                }
            }
        }

        if( p_picture == NULL )
        {
            i_idle_loops++;
642 643
        }

Sam Hocevar's avatar
 
Sam Hocevar committed
644 645 646 647
        /*
         * Check for subpictures to display
         */
        p_subpic = vout_SortSubPictures( p_vout, display_date );
648

Sam Hocevar's avatar
 
Sam Hocevar committed
649 650 651 652 653
        /*
         * Perform rendering
         */
        p_directbuffer = vout_RenderPicture( p_vout, p_picture, p_subpic );

Sam Hocevar's avatar
 
Sam Hocevar committed
654 655 656 657 658 659 660 661 662
        /*
         * Call the plugin-specific rendering method
         */
        if( p_picture != NULL )
        {
            /* Render the direct buffer returned by vout_RenderPicture */
            p_vout->pf_render( p_vout, p_directbuffer );
        }

Sam Hocevar's avatar
 
Sam Hocevar committed
663 664 665 666
        /*
         * Sleep, wake up
         */
        if( display_date != 0 )
667
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
668 669 670
            /* Store render time using Bresenham algorithm */
            p_vout->render_time += mdate() - current_date;
            p_vout->render_time >>= 1;
671 672
        }

Sam Hocevar's avatar
 
Sam Hocevar committed
673 674 675 676 677
        /* Give back change lock */
        vlc_mutex_unlock( &p_vout->change_lock );

        /* Sleep a while or until a given date */
        if( display_date != 0 )
Sam Hocevar's avatar
Sam Hocevar committed
678
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
679
            mwait( display_date - VOUT_MWAIT_TOLERANCE );
680 681 682
        }
        else
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
683
            msleep( VOUT_IDLE_SLEEP );
684
        }
685

Sam Hocevar's avatar
 
Sam Hocevar committed
686 687 688 689 690 691 692 693 694 695 696
        /* On awakening, take back lock and send immediately picture
         * to display. */
        vlc_mutex_lock( &p_vout->change_lock );

        /*
         * Display the previously rendered picture
         */
        if( p_picture != NULL )
        {
            /* Display the direct buffer returned by vout_RenderPicture */
            p_vout->pf_display( p_vout, p_directbuffer );
Vincent Seguin's avatar
Vincent Seguin committed
697

698 699 700 701 702 703 704
            /* Reinitialize idle loop count */
            i_idle_loops = 0;

            /* Tell the vout this was the last picture and that it does not
             * need to be forced anymore. */
            p_last_picture = p_picture;
            p_last_picture->b_force = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
705
        }
Vincent Seguin's avatar
Vincent Seguin committed
706

Sam Hocevar's avatar
 
Sam Hocevar committed
707 708 709 710 711
        /*
         * Check events and manage thread
         */
        if( p_vout->pf_manage( p_vout ) )
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
712 713 714
            /* A fatal error occured, and the thread must terminate
             * immediately, without displaying anything - setting b_error to 1
             * causes the immediate end of the main while() loop. */
Sam Hocevar's avatar
 
Sam Hocevar committed
715 716
            p_vout->b_error = 1;
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734

        if( p_vout->i_changes & VOUT_SIZE_CHANGE )
        {
            /* this must only happen when the vout plugin is incapable of
             * rescaling the picture itself. In this case we need to destroy
             * the current picture buffers and recreate new ones with the right
             * dimensions */
            int i;

            p_vout->i_changes &= ~VOUT_SIZE_CHANGE;

            p_vout->pf_end( p_vout );
            for( i = 0; i < I_OUTPUTPICTURES; i++ )
                 p_vout->p_picture[ i ].i_status = FREE_PICTURE;

            I_OUTPUTPICTURES = 0;
            if( p_vout->pf_init( p_vout ) )
            {
735
                msg_Err( p_vout, "cannot resize display" );
Gildas Bazin's avatar
 
Gildas Bazin committed
736 737 738 739 740 741 742 743 744
                /* FixMe: p_vout->pf_end will be called again in EndThread() */
                p_vout->b_error = 1;
            }

            /* Need to reinitialise the chroma plugin */
            p_vout->chroma.pf_end( p_vout );
            p_vout->chroma.pf_init( p_vout );
        }

Sam Hocevar's avatar
 
Sam Hocevar committed
745
    }
Vincent Seguin's avatar
Vincent Seguin committed
746

Sam Hocevar's avatar
 
Sam Hocevar committed
747 748 749 750
    /*
     * Error loop - wait until the thread destruction is requested
     */
    if( p_vout->b_error )
751
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
752
        ErrorThread( p_vout );
753
    }
Michel Kaempf's avatar
Michel Kaempf committed
754

Sam Hocevar's avatar
 
Sam Hocevar committed
755 756 757 758 759 760 761
    /* End of thread */
    EndThread( p_vout );

    /* Destroy method-dependant resources */
    p_vout->pf_destroy( p_vout );

    /* Destroy thread structures allocated by CreateThread */
762
    DestroyThread( p_vout );
Michel Kaempf's avatar
Michel Kaempf committed
763 764
}

765
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
766
 * ErrorThread: RunThread() error loop
767
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
768 769 770
 * This function is called when an error occured during thread main's loop. The
 * thread can still receive feed, but must be ready to terminate as soon as
 * possible.
771
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
772
static void ErrorThread( vout_thread_t *p_vout )
773
{
Sam Hocevar's avatar
 
Sam Hocevar committed
774 775
    /* Wait until a `die' order */
    while( !p_vout->b_die )
776
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
777 778
        /* Sleep a while */
        msleep( VOUT_IDLE_SLEEP );
779
    }
780 781
}

782
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
783
 * EndThread: thread destruction
784
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
785 786
 * This function is called when the thread ends after a sucessful
 * initialization. It frees all ressources allocated by InitThread.
787
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
788
static void EndThread( vout_thread_t *p_vout )
Michel Kaempf's avatar
Michel Kaempf committed
789
{
Sam Hocevar's avatar
 
Sam Hocevar committed
790
    int     i_index;                                        /* index in heap */
Michel Kaempf's avatar
Michel Kaempf committed
791

Sam Hocevar's avatar
 
Sam Hocevar committed
792
#ifdef STATS
Vincent Seguin's avatar
Vincent Seguin committed
793
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
794 795
        struct tms cpu_usage;
        times( &cpu_usage );
Michel Kaempf's avatar
Michel Kaempf committed
796

797 798
        msg_Dbg( p_vout, "cpu usage (user: %d, system: %d)",
                 cpu_usage.tms_utime, cpu_usage.tms_stime );
Michel Kaempf's avatar
Michel Kaempf committed
799
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
800
#endif
Michel Kaempf's avatar
Michel Kaempf committed
801

Sam Hocevar's avatar
 
Sam Hocevar committed
802 803 804 805 806 807
    if( !p_vout->b_direct )
    {
        p_vout->chroma.pf_end( p_vout );
        module_Unneed( p_vout->chroma.p_module );
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
808
    /* Destroy all remaining pictures */
Sam Hocevar's avatar
 
Sam Hocevar committed
809
    for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES; i_index++ )
Vincent Seguin's avatar
Vincent Seguin committed
810
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
811
        if ( p_vout->p_picture[i_index].i_type == MEMORY_PICTURE )
Michel Kaempf's avatar
Michel Kaempf committed
812
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
813
            free( p_vout->p_picture[i_index].p_data_orig );
Michel Kaempf's avatar
Michel Kaempf committed
814
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
815
    }
Michel Kaempf's avatar
Michel Kaempf committed
816

Sam Hocevar's avatar
 
Sam Hocevar committed
817 818 819 820
    /* Destroy all remaining subpictures */
    for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
    {
        if( p_vout->p_subpicture[i_index].i_status != FREE_SUBPICTURE )
Vincent Seguin's avatar
Vincent Seguin committed
821
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
822
            free( p_vout->p_subpicture[i_index].p_sys );
Vincent Seguin's avatar
Vincent Seguin committed
823
        }
Michel Kaempf's avatar
Michel Kaempf committed
824
    }
825

Sam Hocevar's avatar
 
Sam Hocevar committed
826 827 828 829 830
    /* Destroy translation tables */
    p_vout->pf_end( p_vout );

    /* Release the change lock */
    vlc_mutex_unlock( &p_vout->change_lock );
Michel Kaempf's avatar
Michel Kaempf committed
831 832
}

833
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
834
 * DestroyThread: thread destruction
835
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
836 837
 * This function is called when the thread ends. It frees all ressources
 * allocated by CreateThread. Status is available at this stage.
838
 *****************************************************************************/
839
static void DestroyThread( vout_thread_t *p_vout )
Michel Kaempf's avatar
Michel Kaempf committed
840
{
Sam Hocevar's avatar
 
Sam Hocevar committed
841 842 843 844
    /* Destroy the locks */
    vlc_mutex_destroy( &p_vout->picture_lock );
    vlc_mutex_destroy( &p_vout->subpicture_lock );
    vlc_mutex_destroy( &p_vout->change_lock );
845

Sam Hocevar's avatar
 
Sam Hocevar committed
846 847
    /* Release the module */
    module_Unneed( p_vout->p_module );
848 849
}

Sam Hocevar's avatar
 
Sam Hocevar committed
850 851
/* following functions are local */

Sam Hocevar's avatar
 
Sam Hocevar committed
852 853 854 855 856
static int ReduceHeight( int i_ratio )
{
    int i_dummy = VOUT_ASPECT_FACTOR;
    int i_pgcd  = 1;
 
Sam Hocevar's avatar
 
Sam Hocevar committed
857 858 859 860 861
    if( !i_ratio )
    {
        return i_pgcd;
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
862 863 864 865 866 867 868 869
    /* VOUT_ASPECT_FACTOR is (2^7 * 3^3 * 5^3), we just check for 2, 3 and 5 */
    while( !(i_ratio & 1) && !(i_dummy & 1) )
    {
        i_ratio >>= 1;
        i_dummy >>= 1;
        i_pgcd  <<= 1;
    }

870
    while( !(i_ratio % 3) && !(i_dummy % 3) )
Sam Hocevar's avatar
 
Sam Hocevar committed
871 872 873 874 875 876
    {
        i_ratio /= 3;
        i_dummy /= 3;
        i_pgcd  *= 3;
    }

877
    while( !(i_ratio % 5) && !(i_dummy % 5) )
Sam Hocevar's avatar
 
Sam Hocevar committed
878 879 880 881 882 883 884 885 886
    {
        i_ratio /= 5;
        i_dummy /= 5;
        i_pgcd  *= 5;
    }

    return i_pgcd;
}

Sam Hocevar's avatar
 
Sam Hocevar committed
887 888 889 890 891 892 893 894 895 896
/*****************************************************************************
 * BinaryLog: computes the base 2 log of a binary value
 *****************************************************************************
 * This functions is used by MaskToShift, to get a bit index from a binary
 * value.
 *****************************************************************************/
static int BinaryLog(u32 i)
{
    int i_log = 0;

897
    if( i == 0 ) return -31337;
Sam Hocevar's avatar
 
Sam Hocevar committed
898

899 900 901 902 903
    if( i & 0xffff0000 ) i_log += 16;
    if( i & 0xff00ff00 ) i_log += 8;
    if( i & 0xf0f0f0f0 ) i_log += 4;
    if( i & 0xcccccccc ) i_log += 2;
    if( i & 0xaaaaaaaa ) i_log += 1;
Sam Hocevar's avatar
 
Sam Hocevar committed
904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935

    return( i_log );
}

/*****************************************************************************
 * MaskToShift: transform a color mask into right and left shifts
 *****************************************************************************
 * This function is used for obtaining color shifts from masks.
 *****************************************************************************/
static void MaskToShift( int *pi_left, int *pi_right, u32 i_mask )
{
    u32 i_low, i_high;                 /* lower hand higher bits of the mask */

    if( !i_mask )
    {
        *pi_left = *pi_right = 0;
        return;
    }

    /* Get bits */
    i_low =  i_mask & (- i_mask);                   /* lower bit of the mask */
    i_high = i_mask + i_low;                       /* higher bit of the mask */

    /* Transform bits into an index */
    i_low =  BinaryLog (i_low);
    i_high = BinaryLog (i_high);

    /* Update pointers and return */
    *pi_left =   i_low;
    *pi_right = (8 - i_high + i_low);
}

Gildas Bazin's avatar
 
Gildas Bazin committed
936 937 938
/*****************************************************************************
 * InitWindowSize: find the initial dimensions the video window should have.
 *****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
939
 * This function will check the "width", "height" and "zoom" config options and
Gildas Bazin's avatar
 
Gildas Bazin committed
940 941 942 943 944 945
 * will calculate the size that the video window should have.
 *****************************************************************************/
static void InitWindowSize( vout_thread_t *p_vout, int *pi_width,
                            int *pi_height )
{
    int i_width, i_height;
Gildas Bazin's avatar
 
Gildas Bazin committed
946
    double f_zoom;
Gildas Bazin's avatar
 
Gildas Bazin committed
947

948 949 950
    i_width = config_GetInt( p_vout, "width" );
    i_height = config_GetInt( p_vout, "height" );
    f_zoom = config_GetFloat( p_vout, "zoom" );
Gildas Bazin's avatar
 
Gildas Bazin committed
951 952 953

    if( (i_width >= 0) && (i_height >= 0))
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
954 955
        *pi_width = i_width * f_zoom;
        *pi_height = i_height * f_zoom;
Gildas Bazin's avatar
 
Gildas Bazin committed
956 957 958 959
        return;
    }
    else if( i_width >= 0 )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
960 961 962
        *pi_width = i_width * f_zoom;
        *pi_height = i_width * f_zoom * VOUT_ASPECT_FACTOR /
                        p_vout->render.i_aspect;
Gildas Bazin's avatar
 
Gildas Bazin committed
963 964 965 966
        return;
    }
    else if( i_height >= 0 )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
967 968 969
        *pi_height = i_height * f_zoom;
        *pi_width = i_height * f_zoom * p_vout->render.i_aspect /
                       VOUT_ASPECT_FACTOR;
Gildas Bazin's avatar
 
Gildas Bazin committed
970 971 972 973 974 975
        return;
    }

    if( p_vout->render.i_height * p_vout->render.i_aspect
        >= p_vout->render.i_width * VOUT_ASPECT_FACTOR )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
976
        *pi_width = p_vout->render.i_height * f_zoom
Gildas Bazin's avatar
 
Gildas Bazin committed
977
          * p_vout->render.i_aspect / VOUT_ASPECT_FACTOR;
Gildas Bazin's avatar
 
Gildas Bazin committed
978
        *pi_height = p_vout->render.i_height * f_zoom;
Gildas Bazin's avatar
 
Gildas Bazin committed
979 980 981
    }
    else
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
982 983
        *pi_width = p_vout->render.i_width * f_zoom;
        *pi_height = p_vout->render.i_width * f_zoom
Gildas Bazin's avatar
 
Gildas Bazin committed
984 985 986
          * VOUT_ASPECT_FACTOR / p_vout->render.i_aspect;
    }
}