video_output.c 31.9 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.183 2002/06/02 09:03:54 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
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
45
 * Local prototypes
46
 *****************************************************************************/
47 48 49 50 51
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
52

Sam Hocevar's avatar
 
Sam Hocevar committed
53
static int      ReduceHeight      ( int );
Sam Hocevar's avatar
 
Sam Hocevar committed
54 55
static int      BinaryLog         ( u32 );
static void     MaskToShift       ( int *, int *, u32 );
Gildas Bazin's avatar
 
Gildas Bazin committed
56
static void     InitWindowSize    ( vout_thread_t *, int *, int * );
Michel Kaempf's avatar
Michel Kaempf committed
57

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

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

Sam Hocevar's avatar
 
Sam Hocevar committed
80
    /* Choose the best module */
81
    if( !(psz_plugin = config_GetPsz( p_vout, "filter" )) )
Sam Hocevar's avatar
 
Sam Hocevar committed
82
    {
83
        psz_plugin = config_GetPsz( p_vout, "vout" );
Sam Hocevar's avatar
 
Sam Hocevar committed
84 85
    }

86
    /* Initialize pictures and subpictures - translation tables and functions
87
     * will be initialized later in InitThread */
Sam Hocevar's avatar
 
Sam Hocevar committed
88
    for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES; i_index++)
89
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
90
        p_vout->p_picture[i_index].i_status = FREE_PICTURE;
Sam Hocevar's avatar
 
Sam Hocevar committed
91
        p_vout->p_picture[i_index].i_type   = EMPTY_PICTURE;
92
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
93

94 95
    for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++)
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
96
        p_vout->p_subpicture[i_index].i_status = FREE_SUBPICTURE;
Sam Hocevar's avatar
 
Sam Hocevar committed
97
        p_vout->p_subpicture[i_index].i_type   = EMPTY_SUBPICTURE;
98
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
99

Sam Hocevar's avatar
 
Sam Hocevar committed
100
    /* No images in the heap */
Sam Hocevar's avatar
 
Sam Hocevar committed
101 102
    p_vout->i_heap_size = 0;

Sam Hocevar's avatar
 
Sam Hocevar committed
103
    /* Initialize the rendering heap */
Sam Hocevar's avatar
 
Sam Hocevar committed
104 105 106 107 108
    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
109

Sam Hocevar's avatar
 
Sam Hocevar committed
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
    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
125
    /* Initialize misc stuff */
Sam Hocevar's avatar
 
Sam Hocevar committed
126 127 128 129 130 131
    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
132
    p_vout->b_fullscreen = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
133
    p_vout->render_time  = 10;
Gildas Bazin's avatar
 
Gildas Bazin committed
134
    p_vout->c_fps_samples= 0;
135

Gildas Bazin's avatar
 
Gildas Bazin committed
136
    /* user requested fullscreen? */
137 138
    if( config_GetInt( p_vout, "fullscreen" ) )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
139
        p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
140
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
141

Gildas Bazin's avatar
 
Gildas Bazin committed
142 143 144 145 146
    /* Initialize the dimensions of the video window */
    InitWindowSize( p_vout, &p_vout->i_window_width,
                    &p_vout->i_window_height );


147 148
    p_vout->p_module = module_Need( p_vout, MODULE_CAPABILITY_VOUT,
                                    psz_plugin, (void *)p_vout );
Sam Hocevar's avatar
 
Sam Hocevar committed
149

Gildas Bazin's avatar
 
Gildas Bazin committed
150
    if( psz_plugin ) free( psz_plugin );
Sam Hocevar's avatar
 
Sam Hocevar committed
151 152
    if( p_vout->p_module == NULL )
    {
153 154
        msg_Err( p_vout, "no suitable vout module" );
        vlc_object_destroy( p_vout );
Sam Hocevar's avatar
 
Sam Hocevar committed
155 156 157 158 159 160 161 162 163 164 165 166 167
        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
168
    /* Create thread and set locks */
169 170 171
    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 );
172

173 174 175
    vlc_object_attach( p_vout, p_parent );

    if( vlc_thread_create( p_vout, "video output", RunThread, 0 ) )
Michel Kaempf's avatar
Michel Kaempf committed
176
    {
177
        msg_Err( p_vout, "%s", strerror(ENOMEM) );
Sam Hocevar's avatar
 
Sam Hocevar committed
178
        p_vout->pf_destroy( p_vout );
Gildas Bazin's avatar
 
Gildas Bazin committed
179
        module_Unneed( p_vout->p_module );
180 181
        vlc_object_destroy( p_vout );
        return NULL;
182
    }
Michel Kaempf's avatar
Michel Kaempf committed
183

184
    return p_vout;
Michel Kaempf's avatar
Michel Kaempf committed
185 186
}

187
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
188
 * vout_DestroyThread: destroys a previously created thread
189 190
 *****************************************************************************
 * Destroy a terminated thread.
Michel Kaempf's avatar
Michel Kaempf committed
191
 * The function will request a destruction of the specified thread. If pi_error
192
 * is NULL, it will return once the thread is destroyed. Else, it will be
Michel Kaempf's avatar
Michel Kaempf committed
193
 * update using one of the THREAD_* constants.
194
 *****************************************************************************/
195
void vout_DestroyThread( vout_thread_t *p_vout )
196
{
197
    /* Unlink object */
198
    vlc_object_detach_all( p_vout );
199

Michel Kaempf's avatar
Michel Kaempf committed
200 201
    /* Request thread destruction */
    p_vout->b_die = 1;
202
    vlc_thread_join( p_vout );
Michel Kaempf's avatar
Michel Kaempf committed
203

204
    /* Free structure */
205
    vlc_object_destroy( p_vout );
Michel Kaempf's avatar
Michel Kaempf committed
206 207
}

208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
/*****************************************************************************
 * 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;
    }
}

270
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
271
 * InitThread: initialize video output thread
272
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
273 274 275
 * 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.
276
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
277
static int InitThread( vout_thread_t *p_vout )
Vincent Seguin's avatar
Vincent Seguin committed
278
{
Sam Hocevar's avatar
 
Sam Hocevar committed
279
    int i, i_pgcd;
Sam Hocevar's avatar
 
Sam Hocevar committed
280

Sam Hocevar's avatar
 
Sam Hocevar committed
281 282 283 284
    vlc_mutex_lock( &p_vout->change_lock );

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

Sam Hocevar's avatar
 
Sam Hocevar committed
287
    /* Initialize output method, it allocates direct buffers for us */
Sam Hocevar's avatar
 
Sam Hocevar committed
288
    if( p_vout->pf_init( p_vout ) )
Vincent Seguin's avatar
Vincent Seguin committed
289
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
290 291 292 293
        vlc_mutex_unlock( &p_vout->change_lock );
        return( 1 );
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
294
    if( !I_OUTPUTPICTURES )
Sam Hocevar's avatar
 
Sam Hocevar committed
295
    {
296 297
        msg_Err( p_vout, "plugin was unable to allocate at least "
                         "one direct buffer" );
Sam Hocevar's avatar
 
Sam Hocevar committed
298
        p_vout->pf_end( p_vout );
Sam Hocevar's avatar
 
Sam Hocevar committed
299 300
        vlc_mutex_unlock( &p_vout->change_lock );
        return( 1 );
301
    }
Vincent Seguin's avatar
Vincent Seguin committed
302

303
    msg_Dbg( p_vout, "got %i direct buffer(s)", I_OUTPUTPICTURES );
Sam Hocevar's avatar
 
Sam Hocevar committed
304 305

    i_pgcd = ReduceHeight( p_vout->render.i_aspect );
306 307 308 309 310
    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
311 312

    i_pgcd = ReduceHeight( p_vout->output.i_aspect );
313 314 315 316 317
    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
318

Sam Hocevar's avatar
 
Sam Hocevar committed
319 320 321 322 323 324 325 326
    /* 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
327 328 329 330
    /* 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
331
     && ( vout_ChromaCmp( p_vout->output.i_chroma, p_vout->render.i_chroma ) )
Sam Hocevar's avatar
 
Sam Hocevar committed
332 333
     && ( p_vout->output.i_aspect == p_vout->render.i_aspect ) )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
334 335 336 337
        /* 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
338 339
        p_vout->b_direct = 1;

340 341 342
        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
343 344 345 346 347 348

        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
349 350 351
    }
    else
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
352 353 354
        /* 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
355 356
        p_vout->b_direct = 0;

Sam Hocevar's avatar
 
Sam Hocevar committed
357
        /* Choose the best module */
358 359 360
        p_vout->chroma.p_module =
            module_Need( p_vout, MODULE_CAPABILITY_CHROMA,
                         NULL, (void *)p_vout );
Sam Hocevar's avatar
 
Sam Hocevar committed
361

Sam Hocevar's avatar
 
Sam Hocevar committed
362 363
        if( p_vout->chroma.p_module == NULL )
        {
364 365
            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
366 367 368 369
            p_vout->pf_end( p_vout );
            vlc_mutex_unlock( &p_vout->change_lock );
            return( 1 );
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
370

Sam Hocevar's avatar
 
Sam Hocevar committed
371
#define f p_vout->chroma.p_module->p_functions->chroma.functions.chroma
Gildas Bazin's avatar
 
Gildas Bazin committed
372
        p_vout->chroma.pf_init       = f.pf_init;
Sam Hocevar's avatar
 
Sam Hocevar committed
373 374 375
        p_vout->chroma.pf_end        = f.pf_end;
#undef f

Sam Hocevar's avatar
 
Sam Hocevar committed
376
        if( I_OUTPUTPICTURES < 2 * VOUT_MAX_PICTURES )
Sam Hocevar's avatar
 
Sam Hocevar committed
377
        {
378 379 380 381
            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
382 383 384
        }
        else
        {
385 386 387 388
            /* 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
389
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
390 391

        /* Append render buffers after the direct buffers */
Sam Hocevar's avatar
 
Sam Hocevar committed
392
        for( i = I_OUTPUTPICTURES; i < 2 * VOUT_MAX_PICTURES; i++ )
Sam Hocevar's avatar
 
Sam Hocevar committed
393 394 395 396 397
        {
            PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
            I_RENDERPICTURES++;
        }
    }
Vincent Seguin's avatar
Vincent Seguin committed
398

Sam Hocevar's avatar
 
Sam Hocevar committed
399 400 401 402 403 404 405 406 407 408 409
    /* 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;
    }

410
/* XXX XXX mark thread ready */
Sam Hocevar's avatar
 
Sam Hocevar committed
411
    return( 0 );
Vincent Seguin's avatar
Vincent Seguin committed
412 413
}

414
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
415
 * RunThread: video output thread
416
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
417 418 419
 * 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.
420
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
421
static void RunThread( vout_thread_t *p_vout)
Vincent Seguin's avatar
Vincent Seguin committed
422
{
Sam Hocevar's avatar
 
Sam Hocevar committed
423
    int             i_index;                                /* index in heap */
424
    int             i_idle_loops = 0;  /* loops without displaying a picture */
Sam Hocevar's avatar
 
Sam Hocevar committed
425 426 427 428
    mtime_t         current_date;                            /* current date */
    mtime_t         display_date;                            /* display date */

    picture_t *     p_picture;                            /* picture pointer */
429
    picture_t *     p_last_picture = NULL;                   /* last picture */
Sam Hocevar's avatar
 
Sam Hocevar committed
430 431 432 433 434 435 436 437 438 439 440 441
    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 );
442

443
        DestroyThread( p_vout );
Sam Hocevar's avatar
 
Sam Hocevar committed
444 445
        return;
    }
446

447
    /*
Sam Hocevar's avatar
 
Sam Hocevar committed
448 449
     * Main loop - it is not executed if an error occured during
     * initialization
450
     */
Sam Hocevar's avatar
 
Sam Hocevar committed
451
    while( (!p_vout->b_die) && (!p_vout->b_error) )
452
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
453
        /* Initialize loop variables */
Sam Hocevar's avatar
 
Sam Hocevar committed
454 455
        display_date = 0;
        current_date = mdate();
Sam Hocevar's avatar
 
Sam Hocevar committed
456 457 458 459

#ifdef STATS
        p_vout->c_loops++;
        if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
460
        {
461 462
            msg_Dbg( p_vout, "picture heap: %d/%d",
                     I_RENDERPICTURES, p_vout->i_heap_size );
Sam Hocevar's avatar
 
Sam Hocevar committed
463
        }
464
#endif
Sam Hocevar's avatar
 
Sam Hocevar committed
465 466 467 468 469

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

472
        for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
Sam Hocevar's avatar
 
Sam Hocevar committed
473
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
474 475 476
            if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
                && ( (p_picture == NULL) ||
                     (PP_RENDERPICTURE[i_index]->date < display_date) ) )
477
            {
Sam Hocevar's avatar
 
Sam Hocevar committed
478
                p_picture = PP_RENDERPICTURE[i_index];
Sam Hocevar's avatar
 
Sam Hocevar committed
479
                display_date = p_picture->date;
480 481
            }
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
482 483

        if( p_picture != NULL )
484
        {
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
            /* 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 );
516
                p_last_picture = NULL;
517 518
            }

Sam Hocevar's avatar
 
Sam Hocevar committed
519 520 521 522
            /* Compute FPS rate */
            p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ]
                = display_date;

523
            if( !p_picture->b_force &&
524
                p_picture != p_last_picture &&
525
                display_date < current_date + p_vout->render_time )
Sam Hocevar's avatar
 
Sam Hocevar committed
526 527 528 529 530 531 532 533 534 535 536 537 538 539
            {
                /* 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
540
                    p_vout->i_heap_size--;
Sam Hocevar's avatar
 
Sam Hocevar committed
541
                }
542 543
                msg_Warn( p_vout, "late picture skipped (%lld)",
                                  current_date - display_date );
544 545 546 547
                vlc_mutex_unlock( &p_vout->picture_lock );

                continue;
            }
Christophe Massiot's avatar
Christophe Massiot committed
548 549
#if 0
            /* Removed because it causes problems for some people --Meuuh */
550
            if( display_date > current_date + VOUT_BOGUS_DELAY )
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567
            {
                /* 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
568 569 570 571
                vlc_mutex_unlock( &p_vout->picture_lock );

                continue;
            }
Christophe Massiot's avatar
Christophe Massiot committed
572
#endif
573
            if( display_date > current_date + VOUT_DISPLAY_DELAY )
Sam Hocevar's avatar
 
Sam Hocevar committed
574 575 576 577 578 579 580 581
            {
                /* 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;
            }
582 583
            else if( p_picture == p_last_picture )
            {
584 585
                /* We are asked to repeat the previous picture, but we first
                 * wait for a couple of idle loops */
586 587 588 589 590 591 592 593 594 595 596 597 598 599 600
                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++;
601 602
        }

Sam Hocevar's avatar
 
Sam Hocevar committed
603 604 605 606
        /*
         * Check for subpictures to display
         */
        p_subpic = vout_SortSubPictures( p_vout, display_date );
607

Sam Hocevar's avatar
 
Sam Hocevar committed
608 609 610 611 612
        /*
         * Perform rendering
         */
        p_directbuffer = vout_RenderPicture( p_vout, p_picture, p_subpic );

Sam Hocevar's avatar
 
Sam Hocevar committed
613 614 615 616 617 618 619 620 621
        /*
         * 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
622 623 624 625
        /*
         * Sleep, wake up
         */
        if( display_date != 0 )
626
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
627 628 629
            /* Store render time using Bresenham algorithm */
            p_vout->render_time += mdate() - current_date;
            p_vout->render_time >>= 1;
630 631
        }

Sam Hocevar's avatar
 
Sam Hocevar committed
632 633 634 635 636
        /* 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
637
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
638
            mwait( display_date - VOUT_MWAIT_TOLERANCE );
639 640 641
        }
        else
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
642
            msleep( VOUT_IDLE_SLEEP );
643
        }
644

Sam Hocevar's avatar
 
Sam Hocevar committed
645 646 647 648 649 650 651 652 653 654 655
        /* 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
656

657 658 659 660 661 662 663
            /* 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
664
        }
Vincent Seguin's avatar
Vincent Seguin committed
665

Sam Hocevar's avatar
 
Sam Hocevar committed
666 667 668 669 670
        /*
         * Check events and manage thread
         */
        if( p_vout->pf_manage( p_vout ) )
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
671 672 673
            /* 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
674 675
            p_vout->b_error = 1;
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693

        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 ) )
            {
694
                msg_Err( p_vout, "cannot resize display" );
Gildas Bazin's avatar
 
Gildas Bazin committed
695 696 697 698 699 700 701 702 703
                /* 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
704
    }
Vincent Seguin's avatar
Vincent Seguin committed
705

Sam Hocevar's avatar
 
Sam Hocevar committed
706 707 708 709
    /*
     * Error loop - wait until the thread destruction is requested
     */
    if( p_vout->b_error )
710
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
711
        ErrorThread( p_vout );
712
    }
Michel Kaempf's avatar
Michel Kaempf committed
713

Sam Hocevar's avatar
 
Sam Hocevar committed
714 715 716 717 718 719 720
    /* End of thread */
    EndThread( p_vout );

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

    /* Destroy thread structures allocated by CreateThread */
721
    DestroyThread( p_vout );
Michel Kaempf's avatar
Michel Kaempf committed
722 723
}

724
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
725
 * ErrorThread: RunThread() error loop
726
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
727 728 729
 * 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.
730
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
731
static void ErrorThread( vout_thread_t *p_vout )
732
{
Sam Hocevar's avatar
 
Sam Hocevar committed
733 734
    /* Wait until a `die' order */
    while( !p_vout->b_die )
735
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
736 737
        /* Sleep a while */
        msleep( VOUT_IDLE_SLEEP );
738
    }
739 740
}

741
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
742
 * EndThread: thread destruction
743
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
744 745
 * This function is called when the thread ends after a sucessful
 * initialization. It frees all ressources allocated by InitThread.
746
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
747
static void EndThread( vout_thread_t *p_vout )
Michel Kaempf's avatar
Michel Kaempf committed
748
{
Sam Hocevar's avatar
 
Sam Hocevar committed
749
    int     i_index;                                        /* index in heap */
Michel Kaempf's avatar
Michel Kaempf committed
750

Sam Hocevar's avatar
 
Sam Hocevar committed
751
#ifdef STATS
Vincent Seguin's avatar
Vincent Seguin committed
752
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
753 754
        struct tms cpu_usage;
        times( &cpu_usage );
Michel Kaempf's avatar
Michel Kaempf committed
755

756 757
        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
758
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
759
#endif
Michel Kaempf's avatar
Michel Kaempf committed
760

Sam Hocevar's avatar
 
Sam Hocevar committed
761 762 763 764 765 766
    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
767
    /* Destroy all remaining pictures */
Sam Hocevar's avatar
 
Sam Hocevar committed
768
    for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES; i_index++ )
Vincent Seguin's avatar
Vincent Seguin committed
769
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
770
        if ( p_vout->p_picture[i_index].i_type == MEMORY_PICTURE )
Michel Kaempf's avatar
Michel Kaempf committed
771
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
772
            free( p_vout->p_picture[i_index].p_data_orig );
Michel Kaempf's avatar
Michel Kaempf committed
773
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
774
    }
Michel Kaempf's avatar
Michel Kaempf committed
775

Sam Hocevar's avatar
 
Sam Hocevar committed
776 777 778 779
    /* 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
780
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
781
            free( p_vout->p_subpicture[i_index].p_sys );
Vincent Seguin's avatar
Vincent Seguin committed
782
        }
Michel Kaempf's avatar
Michel Kaempf committed
783
    }
784

Sam Hocevar's avatar
 
Sam Hocevar committed
785 786 787 788 789
    /* 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
790 791
}

792
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
793
 * DestroyThread: thread destruction
794
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
795 796
 * This function is called when the thread ends. It frees all ressources
 * allocated by CreateThread. Status is available at this stage.
797
 *****************************************************************************/
798
static void DestroyThread( vout_thread_t *p_vout )
Michel Kaempf's avatar
Michel Kaempf committed
799
{
Sam Hocevar's avatar
 
Sam Hocevar committed
800 801 802 803
    /* Destroy the locks */
    vlc_mutex_destroy( &p_vout->picture_lock );
    vlc_mutex_destroy( &p_vout->subpicture_lock );
    vlc_mutex_destroy( &p_vout->change_lock );
804

Sam Hocevar's avatar
 
Sam Hocevar committed
805 806
    /* Release the module */
    module_Unneed( p_vout->p_module );
807 808
}

Sam Hocevar's avatar
 
Sam Hocevar committed
809 810
/* following functions are local */

Sam Hocevar's avatar
 
Sam Hocevar committed
811 812 813 814 815
static int ReduceHeight( int i_ratio )
{
    int i_dummy = VOUT_ASPECT_FACTOR;
    int i_pgcd  = 1;
 
Sam Hocevar's avatar
 
Sam Hocevar committed
816 817 818 819 820
    if( !i_ratio )
    {
        return i_pgcd;
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
821 822 823 824 825 826 827 828
    /* 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;
    }

829
    while( !(i_ratio % 3) && !(i_dummy % 3) )
Sam Hocevar's avatar
 
Sam Hocevar committed
830 831 832 833 834 835
    {
        i_ratio /= 3;
        i_dummy /= 3;
        i_pgcd  *= 3;
    }

836
    while( !(i_ratio % 5) && !(i_dummy % 5) )
Sam Hocevar's avatar
 
Sam Hocevar committed
837 838 839 840 841 842 843 844 845
    {
        i_ratio /= 5;
        i_dummy /= 5;
        i_pgcd  *= 5;
    }

    return i_pgcd;
}

Sam Hocevar's avatar
 
Sam Hocevar committed
846 847 848 849 850 851 852 853 854 855
/*****************************************************************************
 * 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;

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

858 859 860 861 862
    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
863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894

    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
895 896 897
/*****************************************************************************
 * InitWindowSize: find the initial dimensions the video window should have.
 *****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
898
 * This function will check the "width", "height" and "zoom" config options and
Gildas Bazin's avatar
 
Gildas Bazin committed
899 900 901 902 903 904
 * 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
905
    double f_zoom;
Gildas Bazin's avatar
 
Gildas Bazin committed
906

907 908 909
    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
910 911 912

    if( (i_width >= 0) && (i_height >= 0))
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
913 914
        *pi_width = i_width * f_zoom;
        *pi_height = i_height * f_zoom;
Gildas Bazin's avatar
 
Gildas Bazin committed
915 916 917 918
        return;
    }
    else if( i_width >= 0 )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
919 920 921
        *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
922 923 924 925
        return;
    }
    else if( i_height >= 0 )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
926 927 928
        *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
929 930 931 932 933 934
        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
935
        *pi_width = p_vout->render.i_height * f_zoom
Gildas Bazin's avatar
 
Gildas Bazin committed
936
          * p_vout->render.i_aspect / VOUT_ASPECT_FACTOR;
Gildas Bazin's avatar
 
Gildas Bazin committed
937
        *pi_height = p_vout->render.i_height * f_zoom;
Gildas Bazin's avatar
 
Gildas Bazin committed
938 939 940
    }
    else
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
941 942
        *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
943 944 945
          * VOUT_ASPECT_FACTOR / p_vout->render.i_aspect;
    }
}