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

29
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
30
 * Preamble
31
 *****************************************************************************/
32 33
#include <vlc/vlc.h>

34
#include <stdlib.h>                                                /* free() */
35
#include <string.h>
Vincent Seguin's avatar
Vincent Seguin committed
36

Sam Hocevar's avatar
 
Sam Hocevar committed
37

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

42
#include "vlc_video.h"
Michel Kaempf's avatar
Michel Kaempf committed
43
#include "video_output.h"
44
#include "vlc_spu.h"
gbazin's avatar
 
gbazin committed
45
#include <vlc/input.h>                 /* for input_thread_t and i_pts_delay */
46
#include "vlc_playlist.h"
47

48 49 50
#include "vlc_filter.h"
#include <vlc/sout.h> /* sout_CfgParse */

51
#if defined( __APPLE__ )
52 53 54
#include "darwin_specific.h"
#endif

55
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
56
 * Local prototypes
57
 *****************************************************************************/
58 59 60 61 62
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
63

64
static void     AspectRatio       ( int, int *, int * );
65 66
static int      BinaryLog         ( uint32_t );
static void     MaskToShift       ( int *, int *, uint32_t );
Michel Kaempf's avatar
Michel Kaempf committed
67

gbazin's avatar
 
gbazin committed
68
/* Object variables callbacks */
gbazin's avatar
 
gbazin committed
69 70
static int DeinterlaceCallback( vlc_object_t *, char const *,
                                vlc_value_t, vlc_value_t, void * );
71 72
static int FilterCallback( vlc_object_t *, char const *,
                           vlc_value_t, vlc_value_t, void * );
73 74
static int VideoFilter2Callback( vlc_object_t *, char const *,
                                 vlc_value_t, vlc_value_t, void * );
gbazin's avatar
 
gbazin committed
75

76 77 78
/* From vout_intf.c */
int vout_Snapshot( vout_thread_t *, picture_t * );

79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
/* Video filter2 parsing */
static int ParseVideoFilter2Chain( vout_thread_t *, char * );
static void RemoveVideoFilters2( vout_thread_t *p_vout );

/*****************************************************************************
 * Video Filter2 functions
 *****************************************************************************/
struct filter_owner_sys_t
{
    vout_thread_t *p_vout;
};

static picture_t *video_new_buffer_filter( filter_t *p_filter )
{
    picture_t *p_picture;
    vout_thread_t *p_vout = p_filter->p_owner->p_vout;

    p_picture = vout_CreatePicture( p_vout, 0, 0, 0 );

    return p_picture;
}

static void video_del_buffer_filter( filter_t *p_filter, picture_t *p_pic )
{
    vout_DestroyPicture( p_filter->p_owner->p_vout, p_pic );
}

106
/*****************************************************************************
107 108 109 110 111
 * vout_Request: find a video output thread, create one, or destroy one.
 *****************************************************************************
 * This function looks for a video output thread matching the current
 * properties. If not found, it spawns a new one.
 *****************************************************************************/
112 113
vout_thread_t *__vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout,
                               video_format_t *p_fmt )
114
{
115
    if( !p_fmt )
116
    {
gbazin's avatar
 
gbazin committed
117
        /* Reattach video output to input before bailing out */
118 119
        if( p_vout )
        {
120
            vlc_object_t *p_playlist;
121

122 123
            p_playlist = vlc_object_find( p_this, VLC_OBJECT_PLAYLIST,
                                          FIND_ANYWHERE );
gbazin's avatar
 
gbazin committed
124

125
            if( p_playlist )
126
            {
127
                spu_Attach( p_vout->p_spu, p_this, VLC_FALSE );
128
                vlc_object_detach( p_vout );
129
                vlc_object_attach( p_vout, p_playlist );
130

131
                vlc_object_release( p_playlist );
132 133 134
            }
            else
            {
zorglub's avatar
zorglub committed
135
                msg_Dbg( p_this, "cannot find playlist, destroying vout" );
136 137 138
                vlc_object_detach( p_vout );
                vout_Destroy( p_vout );
            }
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
        }
        return NULL;
    }

    /* If a video output was provided, lock it, otherwise look for one. */
    if( p_vout )
    {
        vlc_object_yield( p_vout );
    }
    else
    {
        p_vout = vlc_object_find( p_this, VLC_OBJECT_VOUT, FIND_CHILD );

        if( !p_vout )
        {
154
            playlist_t *p_playlist;
gbazin's avatar
 
gbazin committed
155

156 157 158
            p_playlist = vlc_object_find( p_this,
                                          VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
            if( p_playlist )
gbazin's avatar
 
gbazin committed
159
            {
160
                vlc_mutex_lock( &p_playlist->gc_lock );
161 162
                p_vout = vlc_object_find( p_playlist,
                                          VLC_OBJECT_VOUT, FIND_CHILD );
163
                /* only first children of p_input for unused vout */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
164
                if( p_vout && p_vout->p_parent != (vlc_object_t *)p_playlist )
165 166 167 168
                {
                    vlc_object_release( p_vout );
                    p_vout = NULL;
                }
169
                vlc_mutex_unlock( &p_playlist->gc_lock );
170
                vlc_object_release( p_playlist );
gbazin's avatar
 
gbazin committed
171
            }
172 173 174 175 176 177
        }
    }

    /* If we now have a video output, check it has the right properties */
    if( p_vout )
    {
gbazin's avatar
 
gbazin committed
178
        char *psz_filter_chain;
179
        vlc_value_t val;
gbazin's avatar
 
gbazin committed
180

181
        /* We don't directly check for the "vout-filter" variable for obvious
gbazin's avatar
 
gbazin committed
182 183 184
         * performance reasons. */
        if( p_vout->b_filter_change )
        {
185
            var_Get( p_vout, "vout-filter", &val );
186
            psz_filter_chain = val.psz_string;
gbazin's avatar
 
gbazin committed
187 188 189 190 191 192 193 194 195 196 197 198

            if( psz_filter_chain && !*psz_filter_chain )
            {
                free( psz_filter_chain );
                psz_filter_chain = NULL;
            }
            if( p_vout->psz_filter_chain && !*p_vout->psz_filter_chain )
            {
                free( p_vout->psz_filter_chain );
                p_vout->psz_filter_chain = NULL;
            }

199
            if( !psz_filter_chain && !p_vout->psz_filter_chain )
gbazin's avatar
 
gbazin committed
200 201 202 203
            {
                p_vout->b_filter_change = VLC_FALSE;
            }

gbazin's avatar
 
gbazin committed
204
            if( psz_filter_chain ) free( psz_filter_chain );
gbazin's avatar
 
gbazin committed
205 206
        }

207 208 209
        if( ( p_vout->fmt_render.i_width != p_fmt->i_width ) ||
            ( p_vout->fmt_render.i_height != p_fmt->i_height ) ||
            ( p_vout->fmt_render.i_chroma != p_fmt->i_chroma ) ||
210
            ( p_vout->fmt_render.i_aspect != p_fmt->i_aspect ) ||
gbazin's avatar
 
gbazin committed
211
            p_vout->b_filter_change )
212 213 214 215 216 217 218 219 220 221 222
        {
            /* We are not interested in this format, close this vout */
            vlc_object_detach( p_vout );
            vlc_object_release( p_vout );
            vout_Destroy( p_vout );
            p_vout = NULL;
        }
        else
        {
            /* This video output is cool! Hijack it. */
            vlc_object_detach( p_vout );
223
            spu_Attach( p_vout->p_spu, p_this, VLC_TRUE );
224 225 226 227 228 229 230 231 232
            vlc_object_attach( p_vout, p_this );
            vlc_object_release( p_vout );
        }
    }

    if( !p_vout )
    {
        msg_Dbg( p_this, "no usable vout present, spawning one" );

233
        p_vout = vout_Create( p_this, p_fmt );
234 235 236 237 238 239 240
    }

    return p_vout;
}

/*****************************************************************************
 * vout_Create: creates a new video output thread
241
 *****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
242 243
 * This function creates a new video output thread, and returns a pointer
 * to its description. On error, it returns NULL.
244
 *****************************************************************************/
245
vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt )
Michel Kaempf's avatar
Michel Kaempf committed
246
{
gbazin's avatar
 
gbazin committed
247 248 249 250
    vout_thread_t  * p_vout;                            /* thread descriptor */
    input_thread_t * p_input_thread;
    int              i_index;                               /* loop variable */
    char           * psz_plugin;
251
    vlc_value_t      val, text;
Michel Kaempf's avatar
Michel Kaempf committed
252

253 254
    unsigned int i_width = p_fmt->i_width;
    unsigned int i_height = p_fmt->i_height;
255 256 257
    vlc_fourcc_t i_chroma = p_fmt->i_chroma;
    unsigned int i_aspect = p_fmt->i_aspect;

258
    /* Allocate descriptor */
259
    p_vout = vlc_object_create( p_parent, VLC_OBJECT_VOUT );
260
    if( p_vout == NULL )
Michel Kaempf's avatar
Michel Kaempf committed
261
    {
262
        msg_Err( p_parent, "out of memory" );
263
        return NULL;
Michel Kaempf's avatar
Michel Kaempf committed
264
    }
265

266
    /* Initialize pictures - translation tables and functions
gbazin's avatar
 
gbazin committed
267
     * will be initialized later in InitThread */
268
    for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++)
gbazin's avatar
 
gbazin committed
269 270 271 272 273
    {
        p_vout->p_picture[i_index].pf_lock = NULL;
        p_vout->p_picture[i_index].pf_unlock = NULL;
        p_vout->p_picture[i_index].i_status = FREE_PICTURE;
        p_vout->p_picture[i_index].i_type   = EMPTY_PICTURE;
274
        p_vout->p_picture[i_index].b_slow   = 0;
gbazin's avatar
 
gbazin committed
275 276 277 278 279 280 281
    }

    /* No images in the heap */
    p_vout->i_heap_size = 0;

    /* Initialize the rendering heap */
    I_RENDERPICTURES = 0;
282 283 284

    vlc_ureduce( &p_fmt->i_sar_num, &p_fmt->i_sar_den,
                 p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 );
285 286
    p_vout->fmt_render        = *p_fmt;   /* FIXME palette */
    p_vout->fmt_in            = *p_fmt;   /* FIXME palette */
287

gbazin's avatar
 
gbazin committed
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
    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;

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

    p_vout->render.i_last_used_pic = -1;
    p_vout->render.b_allow_modify_pics = 1;

    /* 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;

    /* Initialize misc stuff */
    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;
    p_vout->b_fullscreen = 0;
gbazin's avatar
 
gbazin committed
319
    p_vout->i_alignment  = 0;
gbazin's avatar
 
gbazin committed
320 321 322
    p_vout->render_time  = 10;
    p_vout->c_fps_samples = 0;
    p_vout->b_filter_change = 0;
323
    p_vout->pf_control = 0;
324
    p_vout->p_parent_intf = 0;
325
    p_vout->i_par_num = p_vout->i_par_den = 1;
gbazin's avatar
 
gbazin committed
326 327 328 329 330

    /* Initialize locks */
    vlc_mutex_init( p_vout, &p_vout->picture_lock );
    vlc_mutex_init( p_vout, &p_vout->change_lock );

331 332 333 334 335 336 337
    /* Mouse coordinates */
    var_Create( p_vout, "mouse-x", VLC_VAR_INTEGER );
    var_Create( p_vout, "mouse-y", VLC_VAR_INTEGER );
    var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER );
    var_Create( p_vout, "mouse-moved", VLC_VAR_BOOL );
    var_Create( p_vout, "mouse-clicked", VLC_VAR_INTEGER );

338
    /* Initialize subpicture unit */
339
    p_vout->p_spu = spu_Create( p_vout );
340
    spu_Attach( p_vout->p_spu, p_parent, VLC_TRUE );
341

gbazin's avatar
 
gbazin committed
342 343 344
    /* Attach the new object now so we can use var inheritance below */
    vlc_object_attach( p_vout, p_parent );

345 346
    spu_Init( p_vout->p_spu );

347 348
    /* Take care of some "interface/control" related initialisations */
    vout_IntfInit( p_vout );
gbazin's avatar
 
gbazin committed
349

gbazin's avatar
 
gbazin committed
350 351 352
    /* If the parent is not a VOUT object, that means we are at the start of
     * the video output pipe */
    if( p_parent->i_object_type != VLC_OBJECT_VOUT )
Sam Hocevar's avatar
 
Sam Hocevar committed
353
    {
354
        /* Look for the default filter configuration */
355 356
        var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
        var_Get( p_vout, "vout-filter", &val );
gbazin's avatar
 
gbazin committed
357
        p_vout->psz_filter_chain = val.psz_string;
358 359 360 361 362 363 364

        /* Apply video filter2 objects on the first vout */
        var_Create( p_vout, "video-filter",
                    VLC_VAR_STRING | VLC_VAR_DOINHERIT );
        var_Get( p_vout, "video-filter", &val );
        ParseVideoFilter2Chain( p_vout, val.psz_string );
        free( val.psz_string );
gbazin's avatar
 
gbazin committed
365 366 367 368 369 370
    }
    else
    {
        /* continue the parent's filter chain */
        char *psz_end;

371
        psz_end = strchr( ((vout_thread_t *)p_parent)->psz_filter_chain, ':' );
gbazin's avatar
 
gbazin committed
372 373 374
        if( psz_end && *(psz_end+1) )
            p_vout->psz_filter_chain = strdup( psz_end+1 );
        else p_vout->psz_filter_chain = NULL;
375 376 377 378

        /* Create a video filter2 var ... but don't inherit values */
        var_Create( p_vout, "video-filter", VLC_VAR_STRING );
        ParseVideoFilter2Chain( p_vout, NULL );
gbazin's avatar
 
gbazin committed
379 380
    }

381 382 383 384
    var_AddCallback( p_vout, "video-filter", VideoFilter2Callback, NULL );
    p_vout->b_vfilter_change = VLC_TRUE;
    p_vout->i_vfilters = 0;

gbazin's avatar
 
gbazin committed
385
    /* Choose the video output module */
gbazin's avatar
 
gbazin committed
386
    if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain )
gbazin's avatar
 
gbazin committed
387
    {
gbazin's avatar
 
gbazin committed
388 389 390
        var_Create( p_vout, "vout", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
        var_Get( p_vout, "vout", &val );
        psz_plugin = val.psz_string;
gbazin's avatar
 
gbazin committed
391 392 393 394 395 396 397
    }
    else
    {
        /* the filter chain is a string list of filters separated by double
         * colons */
        char *psz_end;

398
        psz_end = strchr( p_vout->psz_filter_chain, ':' );
gbazin's avatar
 
gbazin committed
399 400 401 402
        if( psz_end )
            psz_plugin = strndup( p_vout->psz_filter_chain,
                                  psz_end - p_vout->psz_filter_chain );
        else psz_plugin = strdup( p_vout->psz_filter_chain );
Sam Hocevar's avatar
 
Sam Hocevar committed
403 404
    }

gbazin's avatar
 
gbazin committed
405
    /* Create the vout thread */
gbazin's avatar
 
gbazin committed
406
    p_vout->p_module = module_Need( p_vout,
gbazin's avatar
 
gbazin committed
407 408
        ( p_vout->psz_filter_chain && *p_vout->psz_filter_chain ) ?
        "video filter" : "video output", psz_plugin, 0 );
Sam Hocevar's avatar
 
Sam Hocevar committed
409

gbazin's avatar
 
gbazin committed
410
    if( psz_plugin ) free( psz_plugin );
Sam Hocevar's avatar
 
Sam Hocevar committed
411 412
    if( p_vout->p_module == NULL )
    {
413
        msg_Err( p_vout, "no suitable vout module" );
414
        vlc_object_detach( p_vout );
415
        vlc_object_destroy( p_vout );
416
        return NULL;
Sam Hocevar's avatar
 
Sam Hocevar committed
417
    }
gbazin's avatar
 
gbazin committed
418

gbazin's avatar
 
gbazin committed
419
    /* Create a few object variables for interface interaction */
gbazin's avatar
 
gbazin committed
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
    var_Create( p_vout, "deinterlace", VLC_VAR_STRING | VLC_VAR_HASCHOICE );
    text.psz_string = _("Deinterlace");
    var_Change( p_vout, "deinterlace", VLC_VAR_SETTEXT, &text, NULL );
    val.psz_string = ""; text.psz_string = _("Disable");
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
    val.psz_string = "discard"; text.psz_string = _("Discard");
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
    val.psz_string = "blend"; text.psz_string = _("Blend");
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
    val.psz_string = "mean"; text.psz_string = _("Mean");
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
    val.psz_string = "bob"; text.psz_string = _("Bob");
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
    val.psz_string = "linear"; text.psz_string = _("Linear");
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
435 436 437
    val.psz_string = "x"; text.psz_string = "X";
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );

gbazin's avatar
 
gbazin committed
438 439 440
    if( var_Get( p_vout, "deinterlace-mode", &val ) == VLC_SUCCESS )
    {
        var_Set( p_vout, "deinterlace", val );
gbazin's avatar
 
gbazin committed
441
        if( val.psz_string ) free( val.psz_string );
gbazin's avatar
 
gbazin committed
442
    }
gbazin's avatar
 
gbazin committed
443 444
    var_AddCallback( p_vout, "deinterlace", DeinterlaceCallback, NULL );

445

446
    var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
447
    text.psz_string = _("Filters");
448 449
    var_Change( p_vout, "vout-filter", VLC_VAR_SETTEXT, &text, NULL );
    var_AddCallback( p_vout, "vout-filter", FilterCallback, NULL );
450

gbazin's avatar
 
gbazin committed
451 452
    /* Calculate delay created by internal caching */
    p_input_thread = (input_thread_t *)vlc_object_find( p_vout,
gbazin's avatar
 
gbazin committed
453
                                           VLC_OBJECT_INPUT, FIND_ANYWHERE );
gbazin's avatar
 
gbazin committed
454 455
    if( p_input_thread )
    {
gbazin's avatar
 
gbazin committed
456
        p_vout->i_pts_delay = p_input_thread->i_pts_delay;
gbazin's avatar
 
gbazin committed
457 458 459 460
        vlc_object_release( p_input_thread );
    }
    else
    {
gbazin's avatar
 
gbazin committed
461
        p_vout->i_pts_delay = DEFAULT_PTS_DELAY;
gbazin's avatar
 
gbazin committed
462 463
    }

464
    if( vlc_thread_create( p_vout, "video output", RunThread,
gbazin's avatar
 
gbazin committed
465
                           VLC_THREAD_PRIORITY_OUTPUT, VLC_TRUE ) )
Michel Kaempf's avatar
Michel Kaempf committed
466
    {
467
        msg_Err( p_vout, "out of memory" );
468
        module_Unneed( p_vout, p_vout->p_module );
469 470 471 472 473 474 475 476 477 478 479
        vlc_object_detach( p_vout );
        vlc_object_destroy( p_vout );
        return NULL;
    }

    if( p_vout->b_error )
    {
        msg_Err( p_vout, "video output creation failed" );

        /* Make sure the thread is destroyed */
        p_vout->b_die = VLC_TRUE;
480

481 482 483
        vlc_thread_join( p_vout );

        vlc_object_detach( p_vout );
484 485
        vlc_object_destroy( p_vout );
        return NULL;
486
    }
Michel Kaempf's avatar
Michel Kaempf committed
487

488
    return p_vout;
Michel Kaempf's avatar
Michel Kaempf committed
489 490
}

491
/*****************************************************************************
492
 * vout_Destroy: destroys a previously created video output
493 494
 *****************************************************************************
 * Destroy a terminated thread.
Michel Kaempf's avatar
Michel Kaempf committed
495
 * The function will request a destruction of the specified thread. If pi_error
496
 * is NULL, it will return once the thread is destroyed. Else, it will be
Michel Kaempf's avatar
Michel Kaempf committed
497
 * update using one of the THREAD_* constants.
498
 *****************************************************************************/
499
void vout_Destroy( vout_thread_t *p_vout )
zorglub's avatar
zorglub committed
500
{
501
    vlc_object_t *p_playlist;
zorglub's avatar
zorglub committed
502

Michel Kaempf's avatar
Michel Kaempf committed
503
    /* Request thread destruction */
504
    p_vout->b_die = VLC_TRUE;
505
    vlc_thread_join( p_vout );
Michel Kaempf's avatar
Michel Kaempf committed
506

507 508
    var_Destroy( p_vout, "intf-change" );

zorglub's avatar
zorglub committed
509
    p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
510 511
                                  FIND_ANYWHERE );

gbazin's avatar
 
gbazin committed
512 513
    if( p_vout->psz_filter_chain ) free( p_vout->psz_filter_chain );

514
    /* Free structure */
515
    vlc_object_destroy( p_vout );
516 517 518 519

    /* If it was the last vout, tell the interface to show up */
    if( p_playlist != NULL )
    {
zorglub's avatar
zorglub committed
520
        vout_thread_t *p_another_vout = vlc_object_find( p_playlist,
521 522 523 524 525 526 527 528 529 530 531 532 533
                                            VLC_OBJECT_VOUT, FIND_ANYWHERE );
        if( p_another_vout == NULL )
        {
            vlc_value_t val;
            val.b_bool = VLC_TRUE;
            var_Set( p_playlist, "intf-show", val );
        }
        else
        {
            vlc_object_release( p_another_vout );
        }
        vlc_object_release( p_playlist );
    }
Michel Kaempf's avatar
Michel Kaempf committed
534 535
}

536
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
537
 * InitThread: initialize video output thread
538
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
539 540 541
 * 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.
542
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
543
static int InitThread( vout_thread_t *p_vout )
Vincent Seguin's avatar
Vincent Seguin committed
544
{
545
    int i, i_aspect_x, i_aspect_y;
Sam Hocevar's avatar
 
Sam Hocevar committed
546

Sam Hocevar's avatar
 
Sam Hocevar committed
547 548 549 550
    vlc_mutex_lock( &p_vout->change_lock );

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

Sam Hocevar's avatar
 
Sam Hocevar committed
553
    /* Initialize output method, it allocates direct buffers for us */
Sam Hocevar's avatar
 
Sam Hocevar committed
554
    if( p_vout->pf_init( p_vout ) )
Vincent Seguin's avatar
Vincent Seguin committed
555
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
556
        vlc_mutex_unlock( &p_vout->change_lock );
557
        return VLC_EGENERIC;
Sam Hocevar's avatar
 
Sam Hocevar committed
558 559
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
560
    if( !I_OUTPUTPICTURES )
Sam Hocevar's avatar
 
Sam Hocevar committed
561
    {
562 563
        msg_Err( p_vout, "plugin was unable to allocate at least "
                         "one direct buffer" );
Sam Hocevar's avatar
 
Sam Hocevar committed
564
        p_vout->pf_end( p_vout );
Sam Hocevar's avatar
 
Sam Hocevar committed
565
        vlc_mutex_unlock( &p_vout->change_lock );
566
        return VLC_EGENERIC;
567
    }
Vincent Seguin's avatar
Vincent Seguin committed
568

gbazin's avatar
 
gbazin committed
569 570 571 572 573 574 575 576 577
    if( I_OUTPUTPICTURES > VOUT_MAX_PICTURES )
    {
        msg_Err( p_vout, "plugin allocated too many direct buffers, "
                         "our internal buffers must have overflown." );
        p_vout->pf_end( p_vout );
        vlc_mutex_unlock( &p_vout->change_lock );
        return VLC_EGENERIC;
    }

578
    msg_Dbg( p_vout, "got %i direct buffer(s)", I_OUTPUTPICTURES );
Sam Hocevar's avatar
 
Sam Hocevar committed
579

580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622
    AspectRatio( p_vout->fmt_render.i_aspect, &i_aspect_x, &i_aspect_y );

    msg_Dbg( p_vout, "picture in %ix%i (%i,%i,%ix%i), "
             "chroma %4.4s, ar %i:%i, sar %i:%i",
             p_vout->fmt_render.i_width, p_vout->fmt_render.i_height,
             p_vout->fmt_render.i_x_offset, p_vout->fmt_render.i_y_offset,
             p_vout->fmt_render.i_visible_width,
             p_vout->fmt_render.i_visible_height,
             (char*)&p_vout->fmt_render.i_chroma,
             i_aspect_x, i_aspect_y,
             p_vout->fmt_render.i_sar_num, p_vout->fmt_render.i_sar_den );

    AspectRatio( p_vout->fmt_in.i_aspect, &i_aspect_x, &i_aspect_y );

    msg_Dbg( p_vout, "picture user %ix%i (%i,%i,%ix%i), "
             "chroma %4.4s, ar %i:%i, sar %i:%i",
             p_vout->fmt_in.i_width, p_vout->fmt_in.i_height,
             p_vout->fmt_in.i_x_offset, p_vout->fmt_in.i_y_offset,
             p_vout->fmt_in.i_visible_width,
             p_vout->fmt_in.i_visible_height,
             (char*)&p_vout->fmt_in.i_chroma,
             i_aspect_x, i_aspect_y,
             p_vout->fmt_in.i_sar_num, p_vout->fmt_in.i_sar_den );

    if( !p_vout->fmt_out.i_width || !p_vout->fmt_out.i_height )
    {
        p_vout->fmt_out.i_width = p_vout->fmt_out.i_visible_width =
            p_vout->output.i_width;
        p_vout->fmt_out.i_height = p_vout->fmt_out.i_visible_height =
            p_vout->output.i_height;
        p_vout->fmt_out.i_x_offset =  p_vout->fmt_out.i_y_offset = 0;

        p_vout->fmt_out.i_aspect = p_vout->output.i_aspect;
        p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
    }
    if( !p_vout->fmt_out.i_sar_num || !p_vout->fmt_out.i_sar_num )
    {
        p_vout->fmt_out.i_sar_num = p_vout->fmt_out.i_aspect *
            p_vout->fmt_out.i_height;
        p_vout->fmt_out.i_sar_den = VOUT_ASPECT_FACTOR *
            p_vout->fmt_out.i_width;
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
623 624
    vlc_ureduce( &p_vout->fmt_out.i_sar_num, &p_vout->fmt_out.i_sar_den,
                 p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den, 0 );
625 626

    AspectRatio( p_vout->fmt_out.i_aspect, &i_aspect_x, &i_aspect_y );
Sam Hocevar's avatar
 
Sam Hocevar committed
627

628 629
    msg_Dbg( p_vout, "picture out %ix%i (%i,%i,%ix%i), "
             "chroma %4.4s, ar %i:%i, sar %i:%i",
630
             p_vout->fmt_out.i_width, p_vout->fmt_out.i_height,
631 632 633 634 635
             p_vout->fmt_out.i_x_offset, p_vout->fmt_out.i_y_offset,
             p_vout->fmt_out.i_visible_width,
             p_vout->fmt_out.i_visible_height,
             (char*)&p_vout->fmt_out.i_chroma,
             i_aspect_x, i_aspect_y,
636
             p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den );
Sam Hocevar's avatar
 
Sam Hocevar committed
637

Sam Hocevar's avatar
 
Sam Hocevar committed
638 639 640 641 642 643 644 645
    /* 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
646
    /* Check whether we managed to create direct buffers similar to
647
     * the render buffers, ie same size and chroma */
Sam Hocevar's avatar
 
Sam Hocevar committed
648 649
    if( ( p_vout->output.i_width == p_vout->render.i_width )
     && ( p_vout->output.i_height == p_vout->render.i_height )
650
     && ( vout_ChromaCmp( p_vout->output.i_chroma, p_vout->render.i_chroma ) ) )
Sam Hocevar's avatar
 
Sam Hocevar committed
651
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
652 653 654 655
        /* 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
656 657
        p_vout->b_direct = 1;

Sam Hocevar's avatar
 
Sam Hocevar committed
658 659
        for( i = 1; i < VOUT_MAX_PICTURES; i++ )
        {
gbazin's avatar
 
gbazin committed
660 661 662 663 664 665 666 667
            if( p_vout->p_picture[ i ].i_type != DIRECT_PICTURE &&
                I_RENDERPICTURES >= VOUT_MIN_DIRECT_PICTURES - 1 &&
                p_vout->p_picture[ i - 1 ].i_type == DIRECT_PICTURE )
            {
                /* We have enough direct buffers so there's no need to
                 * try to use system memory buffers. */
                break;
            }
Sam Hocevar's avatar
 
Sam Hocevar committed
668 669 670
            PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
            I_RENDERPICTURES++;
        }
gbazin's avatar
 
gbazin committed
671 672 673 674

        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
675 676 677
    }
    else
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
678 679 680
        /* 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
681 682
        p_vout->b_direct = 0;

Sam Hocevar's avatar
 
Sam Hocevar committed
683
        /* Choose the best module */
gbazin's avatar
 
gbazin committed
684
        p_vout->chroma.p_module = module_Need( p_vout, "chroma", NULL, 0 );
Sam Hocevar's avatar
 
Sam Hocevar committed
685

Sam Hocevar's avatar
 
Sam Hocevar committed
686 687
        if( p_vout->chroma.p_module == NULL )
        {
688
            msg_Err( p_vout, "no chroma module for %4.4s to %4.4s",
689 690
                     (char*)&p_vout->render.i_chroma,
                     (char*)&p_vout->output.i_chroma );
Sam Hocevar's avatar
 
Sam Hocevar committed
691 692
            p_vout->pf_end( p_vout );
            vlc_mutex_unlock( &p_vout->change_lock );
693
            return VLC_EGENERIC;
Sam Hocevar's avatar
 
Sam Hocevar committed
694
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
695

gbazin's avatar
 
gbazin committed
696 697 698 699
        msg_Dbg( p_vout, "indirect render, mapping "
                 "render pictures 0-%i to system pictures %i-%i",
                 VOUT_MAX_PICTURES - 1, I_OUTPUTPICTURES,
                 I_OUTPUTPICTURES + VOUT_MAX_PICTURES - 1 );
Sam Hocevar's avatar
 
Sam Hocevar committed
700 701

        /* Append render buffers after the direct buffers */
Sam Hocevar's avatar
 
Sam Hocevar committed
702
        for( i = I_OUTPUTPICTURES; i < 2 * VOUT_MAX_PICTURES; i++ )
Sam Hocevar's avatar
 
Sam Hocevar committed
703 704 705
        {
            PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
            I_RENDERPICTURES++;
gbazin's avatar
 
gbazin committed
706 707 708 709

            /* Check if we have enough render pictures */
            if( I_RENDERPICTURES == VOUT_MAX_PICTURES )
                break;
Sam Hocevar's avatar
 
Sam Hocevar committed
710 711
        }
    }
Vincent Seguin's avatar
Vincent Seguin committed
712

Sam Hocevar's avatar
 
Sam Hocevar committed
713 714 715 716 717 718 719 720 721 722 723
    /* 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;
    }

724
/* XXX XXX mark thread ready */
725
    return VLC_SUCCESS;
Vincent Seguin's avatar
Vincent Seguin committed
726 727
}

728
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
729
 * RunThread: video output thread
730
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
731 732 733
 * 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.
734
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
735
static void RunThread( vout_thread_t *p_vout)
Vincent Seguin's avatar
Vincent Seguin committed
736
{
Sam Hocevar's avatar
 
Sam Hocevar committed
737
    int             i_index;                                /* index in heap */
738
    int             i_idle_loops = 0;  /* loops without displaying a picture */
Sam Hocevar's avatar
 
Sam Hocevar committed
739 740 741 742
    mtime_t         current_date;                            /* current date */
    mtime_t         display_date;                            /* display date */

    picture_t *     p_picture;                            /* picture pointer */
743
    picture_t *     p_last_picture = NULL;                   /* last picture */
Sam Hocevar's avatar
 
Sam Hocevar committed
744 745
    picture_t *     p_directbuffer;              /* direct buffer to display */

746
    subpicture_t *  p_subpic = NULL;                   /* subpicture pointer */
Sam Hocevar's avatar
 
Sam Hocevar committed
747

748
    input_thread_t *p_input = NULL ;           /* Parent input, if it exists */
749

750 751 752
    vlc_value_t     val;
    vlc_bool_t      b_drop_late;

753 754
    int             i_displayed = 0, i_lost = 0, i_loops = 0;

Sam Hocevar's avatar
 
Sam Hocevar committed
755 756 757 758
    /*
     * Initialize thread
     */
    p_vout->b_error = InitThread( p_vout );
gbazin's avatar
 
gbazin committed
759

760 761 762 763
    var_Create( p_vout, "drop-late-frames", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
    var_Get( p_vout, "drop-late-frames", &val );
    b_drop_late = val.b_bool;

gbazin's avatar
 
gbazin committed
764 765 766
    /* signal the creation of the vout */
    vlc_thread_ready( p_vout );

Sam Hocevar's avatar
 
Sam Hocevar committed
767 768 769
    if( p_vout->b_error )
    {
        /* Destroy thread structures allocated by Create and InitThread */
770
        DestroyThread( p_vout );
Sam Hocevar's avatar
 
Sam Hocevar committed
771 772
        return;
    }
773

774
    /*
Sam Hocevar's avatar
Sam Hocevar committed
775
     * Main loop - it is not executed if an error occurred during
Sam Hocevar's avatar
 
Sam Hocevar committed
776
     * initialization
777
     */
Sam Hocevar's avatar
 
Sam Hocevar committed
778
    while( (!p_vout->b_die) && (!p_vout->b_error) )
779
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
780
        /* Initialize loop variables */
781
        p_picture = NULL;
Sam Hocevar's avatar
 
Sam Hocevar committed
782 783
        display_date = 0;
        current_date = mdate();
Sam Hocevar's avatar
 
Sam Hocevar committed
784

785 786 787 788 789
        if( p_input && p_input->b_die )
        {
            vlc_object_release( p_input );
            p_input = NULL;
        }
790

791 792 793
        i_loops++;
        if( i_loops % 20 == 0 )
        {
794 795 796 797 798
            if( !p_input )
            {
                p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT,
                                           FIND_PARENT );
            }
799 800 801 802 803 804 805 806 807 808 809 810
            if( p_input )
            {
                vlc_mutex_lock( &p_input->counters.counters_lock );
                stats_UpdateInteger( p_vout, p_input->counters.p_lost_pictures,
                                     i_lost , NULL);
                stats_UpdateInteger( p_vout,
                                     p_input->counters.p_displayed_pictures,
                                     i_displayed , NULL);
                i_displayed = i_lost = 0;
                vlc_mutex_unlock( &p_input->counters.counters_lock );
            }
        }
811
#if 0
Sam Hocevar's avatar
 
Sam Hocevar committed
812 813
        p_vout->c_loops++;
        if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
814
        {
815 816
            msg_Dbg( p_vout, "picture heap: %d/%d",
                     I_RENDERPICTURES, p_vout->i_heap_size );
Sam Hocevar's avatar
 
Sam Hocevar committed
817
        }
818
#endif
Sam Hocevar's avatar
 
Sam Hocevar committed
819 820

        /*
821 822 823
         * Find the picture to display (the one with the earliest date).
         * This operation does not need lock, since only READY_PICTUREs
         * are handled. */
824
        for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
Sam Hocevar's avatar
 
Sam Hocevar committed
825
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
826 827 828
            if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
                && ( (p_picture == NULL) ||
                     (PP_RENDERPICTURE[i_index]->date < display_date) ) )
829
            {
Sam Hocevar's avatar
 
Sam Hocevar committed
830
                p_picture = PP_RENDERPICTURE[i_index];
Sam Hocevar's avatar
 
Sam Hocevar committed
831
                display_date = p_picture->date;
832 833
            }
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
834

835
        if( p_picture )
836
        {
837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852
            /* 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;
                    }
                }
            }
853

854 855 856 857 858 859 860 861 862 863 864 865 866 867
            /* 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 );
868
                p_last_picture = NULL;
869 870
            }

Sam Hocevar's avatar
 
Sam Hocevar committed
871 872 873 874
            /* Compute FPS rate */
            p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ]
                = display_date;

875
            if( !p_picture->b_force &&
876
                p_picture != p_last_picture &&
877
                display_date < current_date + p_vout->render_time &&
878
                b_drop_late )
Sam Hocevar's avatar
 
Sam Hocevar committed
879 880 881 882 883 884 885 886 887 888 889 890 891 892
            {
                /* 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
893
                    p_vout->i_heap_size--;
Sam Hocevar's avatar
 
Sam Hocevar committed
894
                }
gbazin's avatar
 
gbazin committed
895
                msg_Warn( p_vout, "late picture skipped ("I64Fd")",
896
                                  current_date - display_date );
897
                i_lost++;
898 899 900 901
                vlc_mutex_unlock( &p_vout->picture_lock );

                continue;
            }
902