video_output.c 31.6 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.187 2002/07/23 00:39:17 sam 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,
70
                                      vlc_fourcc_t 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
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
250
 * InitThread: initialize video output thread
251
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
252 253 254
 * 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.
255
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
256
static int InitThread( vout_thread_t *p_vout )
Vincent Seguin's avatar
Vincent Seguin committed
257
{
Sam Hocevar's avatar
 
Sam Hocevar committed
258
    int i, i_pgcd;
Sam Hocevar's avatar
 
Sam Hocevar committed
259

Sam Hocevar's avatar
 
Sam Hocevar committed
260 261 262 263
    vlc_mutex_lock( &p_vout->change_lock );

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

Sam Hocevar's avatar
 
Sam Hocevar committed
266
    /* Initialize output method, it allocates direct buffers for us */
Sam Hocevar's avatar
 
Sam Hocevar committed
267
    if( p_vout->pf_init( p_vout ) )
Vincent Seguin's avatar
Vincent Seguin committed
268
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
269 270 271 272
        vlc_mutex_unlock( &p_vout->change_lock );
        return( 1 );
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
273
    if( !I_OUTPUTPICTURES )
Sam Hocevar's avatar
 
Sam Hocevar committed
274
    {
275 276
        msg_Err( p_vout, "plugin was unable to allocate at least "
                         "one direct buffer" );
Sam Hocevar's avatar
 
Sam Hocevar committed
277
        p_vout->pf_end( p_vout );
Sam Hocevar's avatar
 
Sam Hocevar committed
278 279
        vlc_mutex_unlock( &p_vout->change_lock );
        return( 1 );
280
    }
Vincent Seguin's avatar
Vincent Seguin committed
281

282
    msg_Dbg( p_vout, "got %i direct buffer(s)", I_OUTPUTPICTURES );
Sam Hocevar's avatar
 
Sam Hocevar committed
283 284

    i_pgcd = ReduceHeight( p_vout->render.i_aspect );
285 286 287 288 289
    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
290 291

    i_pgcd = ReduceHeight( p_vout->output.i_aspect );
292 293 294 295 296
    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
297

Sam Hocevar's avatar
 
Sam Hocevar committed
298 299 300 301 302 303 304 305
    /* 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
306 307 308 309
    /* 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
310
     && ( vout_ChromaCmp( p_vout->output.i_chroma, p_vout->render.i_chroma ) )
Sam Hocevar's avatar
 
Sam Hocevar committed
311 312
     && ( p_vout->output.i_aspect == p_vout->render.i_aspect ) )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
313 314 315 316
        /* 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
317 318
        p_vout->b_direct = 1;

319 320 321
        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
322 323 324 325 326 327

        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
328 329 330
    }
    else
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
331 332 333
        /* 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
334 335
        p_vout->b_direct = 0;

Sam Hocevar's avatar
 
Sam Hocevar committed
336
        /* Choose the best module */
337 338 339
        p_vout->chroma.p_module =
            module_Need( p_vout, MODULE_CAPABILITY_CHROMA,
                         NULL, (void *)p_vout );
Sam Hocevar's avatar
 
Sam Hocevar committed
340

Sam Hocevar's avatar
 
Sam Hocevar committed
341 342
        if( p_vout->chroma.p_module == NULL )
        {
343 344
            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
345 346 347 348
            p_vout->pf_end( p_vout );
            vlc_mutex_unlock( &p_vout->change_lock );
            return( 1 );
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
349

Sam Hocevar's avatar
 
Sam Hocevar committed
350
#define f p_vout->chroma.p_module->p_functions->chroma.functions.chroma
Gildas Bazin's avatar
 
Gildas Bazin committed
351
        p_vout->chroma.pf_init       = f.pf_init;
Sam Hocevar's avatar
 
Sam Hocevar committed
352 353 354
        p_vout->chroma.pf_end        = f.pf_end;
#undef f

Sam Hocevar's avatar
 
Sam Hocevar committed
355
        if( I_OUTPUTPICTURES < 2 * VOUT_MAX_PICTURES )
Sam Hocevar's avatar
 
Sam Hocevar committed
356
        {
357 358 359 360
            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
361 362 363
        }
        else
        {
364 365 366 367
            /* 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
368
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
369 370

        /* Append render buffers after the direct buffers */
Sam Hocevar's avatar
 
Sam Hocevar committed
371
        for( i = I_OUTPUTPICTURES; i < 2 * VOUT_MAX_PICTURES; i++ )
Sam Hocevar's avatar
 
Sam Hocevar committed
372 373 374 375 376
        {
            PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
            I_RENDERPICTURES++;
        }
    }
Vincent Seguin's avatar
Vincent Seguin committed
377

Sam Hocevar's avatar
 
Sam Hocevar committed
378 379 380 381 382 383 384 385 386 387 388
    /* 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;
    }

389
/* XXX XXX mark thread ready */
Sam Hocevar's avatar
 
Sam Hocevar committed
390
    return( 0 );
Vincent Seguin's avatar
Vincent Seguin committed
391 392
}

393
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
394
 * RunThread: video output thread
395
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
396 397 398
 * 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.
399
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
400
static void RunThread( vout_thread_t *p_vout)
Vincent Seguin's avatar
Vincent Seguin committed
401
{
Sam Hocevar's avatar
 
Sam Hocevar committed
402
    int             i_index;                                /* index in heap */
403
    int             i_idle_loops = 0;  /* loops without displaying a picture */
Sam Hocevar's avatar
 
Sam Hocevar committed
404 405 406 407
    mtime_t         current_date;                            /* current date */
    mtime_t         display_date;                            /* display date */

    picture_t *     p_picture;                            /* picture pointer */
408
    picture_t *     p_last_picture = NULL;                   /* last picture */
Sam Hocevar's avatar
 
Sam Hocevar committed
409 410 411 412 413 414 415 416 417 418 419 420
    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 );
421

422
        DestroyThread( p_vout );
Sam Hocevar's avatar
 
Sam Hocevar committed
423 424
        return;
    }
425

426
    /*
Sam Hocevar's avatar
 
Sam Hocevar committed
427 428
     * Main loop - it is not executed if an error occured during
     * initialization
429
     */
Sam Hocevar's avatar
 
Sam Hocevar committed
430
    while( (!p_vout->b_die) && (!p_vout->b_error) )
431
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
432
        /* Initialize loop variables */
Sam Hocevar's avatar
 
Sam Hocevar committed
433 434
        display_date = 0;
        current_date = mdate();
Sam Hocevar's avatar
 
Sam Hocevar committed
435 436 437 438

#ifdef STATS
        p_vout->c_loops++;
        if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
439
        {
440 441
            msg_Dbg( p_vout, "picture heap: %d/%d",
                     I_RENDERPICTURES, p_vout->i_heap_size );
Sam Hocevar's avatar
 
Sam Hocevar committed
442
        }
443
#endif
Sam Hocevar's avatar
 
Sam Hocevar committed
444 445 446 447 448

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

451
        for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
Sam Hocevar's avatar
 
Sam Hocevar committed
452
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
453 454 455
            if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
                && ( (p_picture == NULL) ||
                     (PP_RENDERPICTURE[i_index]->date < display_date) ) )
456
            {
Sam Hocevar's avatar
 
Sam Hocevar committed
457
                p_picture = PP_RENDERPICTURE[i_index];
Sam Hocevar's avatar
 
Sam Hocevar committed
458
                display_date = p_picture->date;
459 460
            }
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
461 462

        if( p_picture != NULL )
463
        {
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
            /* 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 );
495
                p_last_picture = NULL;
496 497
            }

Sam Hocevar's avatar
 
Sam Hocevar committed
498 499 500 501
            /* Compute FPS rate */
            p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ]
                = display_date;

502
            if( !p_picture->b_force &&
503
                p_picture != p_last_picture &&
504
                display_date < current_date + p_vout->render_time )
Sam Hocevar's avatar
 
Sam Hocevar committed
505 506 507 508 509 510 511 512 513 514 515 516 517 518
            {
                /* 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
519
                    p_vout->i_heap_size--;
Sam Hocevar's avatar
 
Sam Hocevar committed
520
                }
521 522
                msg_Warn( p_vout, "late picture skipped (%lld)",
                                  current_date - display_date );
523 524 525 526
                vlc_mutex_unlock( &p_vout->picture_lock );

                continue;
            }
Christophe Massiot's avatar
Christophe Massiot committed
527 528
#if 0
            /* Removed because it causes problems for some people --Meuuh */
529
            if( display_date > current_date + VOUT_BOGUS_DELAY )
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
            {
                /* 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
547 548 549 550
                vlc_mutex_unlock( &p_vout->picture_lock );

                continue;
            }
Christophe Massiot's avatar
Christophe Massiot committed
551
#endif
552
            if( display_date > current_date + VOUT_DISPLAY_DELAY )
Sam Hocevar's avatar
 
Sam Hocevar committed
553 554 555 556 557 558 559 560
            {
                /* 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;
            }
561 562
            else if( p_picture == p_last_picture )
            {
563 564
                /* We are asked to repeat the previous picture, but we first
                 * wait for a couple of idle loops */
565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
                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++;
580 581
        }

Sam Hocevar's avatar
 
Sam Hocevar committed
582 583 584 585
        /*
         * Check for subpictures to display
         */
        p_subpic = vout_SortSubPictures( p_vout, display_date );
586

Sam Hocevar's avatar
 
Sam Hocevar committed
587 588 589 590 591
        /*
         * Perform rendering
         */
        p_directbuffer = vout_RenderPicture( p_vout, p_picture, p_subpic );

Sam Hocevar's avatar
 
Sam Hocevar committed
592 593 594 595 596 597 598 599 600
        /*
         * 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
601 602 603 604
        /*
         * Sleep, wake up
         */
        if( display_date != 0 )
605
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
606 607 608
            /* Store render time using Bresenham algorithm */
            p_vout->render_time += mdate() - current_date;
            p_vout->render_time >>= 1;
609 610
        }

Sam Hocevar's avatar
 
Sam Hocevar committed
611 612 613 614 615
        /* 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
616
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
617
            mwait( display_date - VOUT_MWAIT_TOLERANCE );
618 619 620
        }
        else
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
621
            msleep( VOUT_IDLE_SLEEP );
622
        }
623

Sam Hocevar's avatar
 
Sam Hocevar committed
624 625 626 627 628 629 630 631 632 633 634
        /* 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
635

636 637 638 639 640 641 642
            /* 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
643
        }
Vincent Seguin's avatar
Vincent Seguin committed
644

Sam Hocevar's avatar
 
Sam Hocevar committed
645 646 647 648 649
        /*
         * Check events and manage thread
         */
        if( p_vout->pf_manage( p_vout ) )
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
650 651 652
            /* 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
653 654
            p_vout->b_error = 1;
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672

        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 ) )
            {
673
                msg_Err( p_vout, "cannot resize display" );
Gildas Bazin's avatar
 
Gildas Bazin committed
674 675 676 677 678 679 680 681 682
                /* 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
683
    }
Vincent Seguin's avatar
Vincent Seguin committed
684

Sam Hocevar's avatar
 
Sam Hocevar committed
685 686 687 688
    /*
     * Error loop - wait until the thread destruction is requested
     */
    if( p_vout->b_error )
689
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
690
        ErrorThread( p_vout );
691
    }
Michel Kaempf's avatar
Michel Kaempf committed
692

Sam Hocevar's avatar
 
Sam Hocevar committed
693 694 695 696 697 698 699
    /* End of thread */
    EndThread( p_vout );

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

    /* Destroy thread structures allocated by CreateThread */
700
    DestroyThread( p_vout );
Michel Kaempf's avatar
Michel Kaempf committed
701 702
}

703
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
704
 * ErrorThread: RunThread() error loop
705
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
706 707 708
 * 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.
709
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
710
static void ErrorThread( vout_thread_t *p_vout )
711
{
Sam Hocevar's avatar
 
Sam Hocevar committed
712 713
    /* Wait until a `die' order */
    while( !p_vout->b_die )
714
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
715 716
        /* Sleep a while */
        msleep( VOUT_IDLE_SLEEP );
717
    }
718 719
}

720
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
721
 * EndThread: thread destruction
722
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
723 724
 * This function is called when the thread ends after a sucessful
 * initialization. It frees all ressources allocated by InitThread.
725
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
726
static void EndThread( vout_thread_t *p_vout )
Michel Kaempf's avatar
Michel Kaempf committed
727
{
Sam Hocevar's avatar
 
Sam Hocevar committed
728
    int     i_index;                                        /* index in heap */
Michel Kaempf's avatar
Michel Kaempf committed
729

Sam Hocevar's avatar
 
Sam Hocevar committed
730
#ifdef STATS
Vincent Seguin's avatar
Vincent Seguin committed
731
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
732 733
        struct tms cpu_usage;
        times( &cpu_usage );
Michel Kaempf's avatar
Michel Kaempf committed
734

735 736
        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
737
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
738
#endif
Michel Kaempf's avatar
Michel Kaempf committed
739

Sam Hocevar's avatar
 
Sam Hocevar committed
740 741 742 743 744 745
    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
746
    /* Destroy all remaining pictures */
Sam Hocevar's avatar
 
Sam Hocevar committed
747
    for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES; i_index++ )
Vincent Seguin's avatar
Vincent Seguin committed
748
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
749
        if ( p_vout->p_picture[i_index].i_type == MEMORY_PICTURE )
Michel Kaempf's avatar
Michel Kaempf committed
750
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
751
            free( p_vout->p_picture[i_index].p_data_orig );
Michel Kaempf's avatar
Michel Kaempf committed
752
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
753
    }
Michel Kaempf's avatar
Michel Kaempf committed
754

Sam Hocevar's avatar
 
Sam Hocevar committed
755 756 757 758
    /* 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
759
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
760
            free( p_vout->p_subpicture[i_index].p_sys );
Vincent Seguin's avatar
Vincent Seguin committed
761
        }
Michel Kaempf's avatar
Michel Kaempf committed
762
    }
763

Sam Hocevar's avatar
 
Sam Hocevar committed
764 765 766 767 768
    /* 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
769 770
}

771
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
772
 * DestroyThread: thread destruction
773
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
774 775
 * This function is called when the thread ends. It frees all ressources
 * allocated by CreateThread. Status is available at this stage.
776
 *****************************************************************************/
777
static void DestroyThread( vout_thread_t *p_vout )
Michel Kaempf's avatar
Michel Kaempf committed
778
{
Sam Hocevar's avatar
 
Sam Hocevar committed
779 780 781 782
    /* Destroy the locks */
    vlc_mutex_destroy( &p_vout->picture_lock );
    vlc_mutex_destroy( &p_vout->subpicture_lock );
    vlc_mutex_destroy( &p_vout->change_lock );
783

Sam Hocevar's avatar
 
Sam Hocevar committed
784 785
    /* Release the module */
    module_Unneed( p_vout->p_module );
786 787
}

Sam Hocevar's avatar
 
Sam Hocevar committed
788 789
/* following functions are local */

Sam Hocevar's avatar
 
Sam Hocevar committed
790 791 792 793 794
static int ReduceHeight( int i_ratio )
{
    int i_dummy = VOUT_ASPECT_FACTOR;
    int i_pgcd  = 1;
 
Sam Hocevar's avatar
 
Sam Hocevar committed
795 796 797 798 799
    if( !i_ratio )
    {
        return i_pgcd;
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
800 801 802 803 804 805 806 807
    /* 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;
    }

808
    while( !(i_ratio % 3) && !(i_dummy % 3) )
Sam Hocevar's avatar
 
Sam Hocevar committed
809 810 811 812 813 814
    {
        i_ratio /= 3;
        i_dummy /= 3;
        i_pgcd  *= 3;
    }

815
    while( !(i_ratio % 5) && !(i_dummy % 5) )
Sam Hocevar's avatar
 
Sam Hocevar committed
816 817 818 819 820 821 822 823 824
    {
        i_ratio /= 5;
        i_dummy /= 5;
        i_pgcd  *= 5;
    }

    return i_pgcd;
}

Sam Hocevar's avatar
 
Sam Hocevar committed
825 826 827 828 829 830 831 832 833 834
/*****************************************************************************
 * 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;

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

837 838 839 840 841
    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
842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873

    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
874 875 876
/*****************************************************************************
 * InitWindowSize: find the initial dimensions the video window should have.
 *****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
877
 * This function will check the "width", "height" and "zoom" config options and
Gildas Bazin's avatar
 
Gildas Bazin committed
878 879 880 881 882 883
 * 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
884
    double f_zoom;
Gildas Bazin's avatar
 
Gildas Bazin committed
885

886 887 888
    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
889 890 891

    if( (i_width >= 0) && (i_height >= 0))
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
892 893
        *pi_width = i_width * f_zoom;
        *pi_height = i_height * f_zoom;
Gildas Bazin's avatar
 
Gildas Bazin committed
894 895 896 897
        return;
    }
    else if( i_width >= 0 )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
898 899 900
        *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
901 902 903 904
        return;
    }
    else if( i_height >= 0 )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
905 906 907
        *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
908 909 910 911 912 913
        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
914
        *pi_width = p_vout->render.i_height * f_zoom
Gildas Bazin's avatar
 
Gildas Bazin committed
915
          * p_vout->render.i_aspect / VOUT_ASPECT_FACTOR;
Gildas Bazin's avatar
 
Gildas Bazin committed
916
        *pi_height = p_vout->render.i_height * f_zoom;
Gildas Bazin's avatar
 
Gildas Bazin committed
917 918 919
    }
    else
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
920 921
        *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
922 923 924
          * VOUT_ASPECT_FACTOR / p_vout->render.i_aspect;
    }
}