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.182 2002/06/01 18:04:49 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 198
    /* Unlink object */
    vlc_object_unlink_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 516 517
            /* 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 );
            }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return i_pgcd;
}

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

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

857 858 859 860 861
    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
862 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

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

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

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