video_output.c 53 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-2007 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 34 35
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

36
#include <vlc_common.h>
37

38
#include <stdlib.h>                                                /* free() */
39
#include <string.h>
Vincent Seguin's avatar
Vincent Seguin committed
40

Sam Hocevar's avatar
 
Sam Hocevar committed
41

42 43
#ifdef HAVE_SYS_TIMES_H
#   include <sys/times.h>
44
#endif
45

zorglub's avatar
zorglub committed
46
#include <vlc_vout.h>
47

zorglub's avatar
zorglub committed
48 49
#include <vlc_filter.h>
#include <vlc_osd.h>
50

51
#if defined( __APPLE__ )
zorglub's avatar
zorglub committed
52
/* Include darwin_specific.h here if needed */
53 54
#endif

zorglub's avatar
zorglub committed
55 56 57 58
/** FIXME This is quite ugly but needed while we don't have counters
 * helpers */
#include "input/input_internal.h"

59
#include "modules/modules.h"
60
#include <assert.h>
61
#include "vout_pictures.h"
62

63
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
64
 * Local prototypes
65
 *****************************************************************************/
66
static int      InitThread        ( vout_thread_t * );
67
static void*    RunThread         ( vlc_object_t *  );
68
static void     ErrorThread       ( vout_thread_t * );
Laurent Aimar's avatar
Laurent Aimar committed
69
static void     CleanThread       ( vout_thread_t * );
70
static void     EndThread         ( vout_thread_t * );
Sam Hocevar's avatar
 
Sam Hocevar committed
71

72
static void     AspectRatio       ( int, int *, int * );
Michel Kaempf's avatar
Michel Kaempf committed
73

Laurent Aimar's avatar
Laurent Aimar committed
74
static void VideoFormatImportRgb( video_format_t *, const picture_heap_t * );
75 76
static void PictureHeapFixRgb( picture_heap_t * );

77 78
static void     vout_Destructor   ( vlc_object_t * p_this );

gbazin's avatar
 
gbazin committed
79
/* Object variables callbacks */
gbazin's avatar
 
gbazin committed
80 81
static int DeinterlaceCallback( vlc_object_t *, char const *,
                                vlc_value_t, vlc_value_t, void * );
82 83
static int FilterCallback( vlc_object_t *, char const *,
                           vlc_value_t, vlc_value_t, void * );
84 85
static int VideoFilter2Callback( vlc_object_t *, char const *,
                                 vlc_value_t, vlc_value_t, void * );
gbazin's avatar
 
gbazin committed
86

87 88 89
/* From vout_intf.c */
int vout_Snapshot( vout_thread_t *, picture_t * );

90 91 92
/* Display media title in OSD */
static void DisplayTitleOnOSD( vout_thread_t *p_vout );

93 94 95
/* */
static void DropPicture( vout_thread_t *p_vout, picture_t *p_picture );

96 97 98 99 100
/*****************************************************************************
 * Video Filter2 functions
 *****************************************************************************/
static picture_t *video_new_buffer_filter( filter_t *p_filter )
{
101
    vout_thread_t *p_vout = (vout_thread_t*)p_filter->p_owner;
102
    picture_t *p_picture = vout_CreatePicture( p_vout, 0, 0, 0 );
103

104
    p_picture->i_status = READY_PICTURE;
105 106 107 108 109 110

    return p_picture;
}

static void video_del_buffer_filter( filter_t *p_filter, picture_t *p_pic )
{
111 112 113
    vout_thread_t *p_vout = (vout_thread_t*)p_filter->p_owner;

    DropPicture( p_vout, p_pic );
114 115 116 117 118 119 120 121
}

static int video_filter_buffer_allocation_init( filter_t *p_filter, void *p_data )
{
    p_filter->pf_vout_buffer_new = video_new_buffer_filter;
    p_filter->pf_vout_buffer_del = video_del_buffer_filter;
    p_filter->p_owner = p_data; /* p_vout */
    return VLC_SUCCESS;
122 123
}

124
/*****************************************************************************
125 126 127 128 129
 * 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.
 *****************************************************************************/
130 131
vout_thread_t *__vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout,
                               video_format_t *p_fmt )
132
{
133
    if( !p_fmt )
134
    {
135 136 137
        /* Video output is no longer used.
         * TODO: support for reusing video outputs with proper _thread-safe_
         * reference handling. */
138
        if( p_vout )
139
            vout_CloseAndRelease( p_vout );
140 141 142 143 144 145 146 147 148
        return NULL;
    }

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

149
    /* TODO: find a suitable unused video output */
150 151 152 153

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

157
        /* We don't directly check for the "vout-filter" variable for obvious
gbazin's avatar
 
gbazin committed
158 159 160
         * performance reasons. */
        if( p_vout->b_filter_change )
        {
161
            var_Get( p_vout, "vout-filter", &val );
162
            psz_filter_chain = val.psz_string;
gbazin's avatar
 
gbazin committed
163

164
            if( psz_filter_chain && !*psz_filter_chain )
gbazin's avatar
 
gbazin committed
165 166 167 168
            {
                free( psz_filter_chain );
                psz_filter_chain = NULL;
            }
169
            if( p_vout->psz_filter_chain && !*p_vout->psz_filter_chain )
gbazin's avatar
 
gbazin committed
170 171 172 173 174
            {
                free( p_vout->psz_filter_chain );
                p_vout->psz_filter_chain = NULL;
            }

175
            if( !psz_filter_chain && !p_vout->psz_filter_chain )
gbazin's avatar
 
gbazin committed
176
            {
177
                p_vout->b_filter_change = false;
gbazin's avatar
 
gbazin committed
178 179
            }

180
            free( psz_filter_chain );
gbazin's avatar
 
gbazin committed
181 182
        }

183 184
        if( ( p_vout->fmt_render.i_width != p_fmt->i_width ) ||
            ( p_vout->fmt_render.i_height != p_fmt->i_height ) ||
185
            ( p_vout->fmt_render.i_aspect != p_fmt->i_aspect ) ||
gbazin's avatar
 
gbazin committed
186
            p_vout->b_filter_change )
187 188
        {
            /* We are not interested in this format, close this vout */
189
            vout_CloseAndRelease( p_vout );
190
            vlc_object_release( p_vout );
191 192 193 194 195
            p_vout = NULL;
        }
        else
        {
            /* This video output is cool! Hijack it. */
196
            spu_Attach( p_vout->p_spu, p_this, true );
197
            vlc_object_attach( p_vout, p_this );
198 199
            if( p_vout->b_title_show )
                DisplayTitleOnOSD( p_vout );
200 201 202 203 204 205 206 207
            vlc_object_release( p_vout );
        }
    }

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

208
        p_vout = vout_Create( p_this, p_fmt );
209 210 211 212 213 214 215
    }

    return p_vout;
}

/*****************************************************************************
 * vout_Create: creates a new video output thread
216
 *****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
217 218
 * This function creates a new video output thread, and returns a pointer
 * to its description. On error, it returns NULL.
219
 *****************************************************************************/
220
vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt )
Michel Kaempf's avatar
Michel Kaempf committed
221
{
gbazin's avatar
 
gbazin committed
222 223 224
    vout_thread_t  * p_vout;                            /* thread descriptor */
    input_thread_t * p_input_thread;
    int              i_index;                               /* loop variable */
225
    vlc_value_t      val, text;
Michel Kaempf's avatar
Michel Kaempf committed
226

227 228
    unsigned int i_width = p_fmt->i_width;
    unsigned int i_height = p_fmt->i_height;
229 230 231
    vlc_fourcc_t i_chroma = p_fmt->i_chroma;
    unsigned int i_aspect = p_fmt->i_aspect;

232 233 234 235
    config_chain_t *p_cfg;
    char *psz_parser;
    char *psz_name;

236 237 238 239 240 241 242 243
    if( i_width <= 0 || i_height <= 0 || i_aspect <= 0 )
        return NULL;

    vlc_ureduce( &p_fmt->i_sar_num, &p_fmt->i_sar_den,
                 p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 );
    if( p_fmt->i_sar_num <= 0 || p_fmt->i_sar_den <= 0 )
        return NULL;

244
    /* Allocate descriptor */
245 246 247
    static const char typename[] = "video output";
    p_vout = vlc_custom_create( p_parent, sizeof( *p_vout ), VLC_OBJECT_VOUT,
                                typename );
248
    if( p_vout == NULL )
249
        return NULL;
250

251
    /* Initialize pictures - translation tables and functions
gbazin's avatar
 
gbazin committed
252
     * will be initialized later in InitThread */
253
    for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++)
gbazin's avatar
 
gbazin committed
254 255 256 257 258
    {
        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;
259
        p_vout->p_picture[i_index].b_slow   = 0;
gbazin's avatar
 
gbazin committed
260 261 262 263 264 265 266
    }

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

    /* Initialize the rendering heap */
    I_RENDERPICTURES = 0;
267

268 269
    p_vout->fmt_render        = *p_fmt;   /* FIXME palette */
    p_vout->fmt_in            = *p_fmt;   /* FIXME palette */
270

gbazin's avatar
 
gbazin committed
271 272 273 274 275
    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;

276 277 278
    p_vout->render.i_rmask    = p_fmt->i_rmask;
    p_vout->render.i_gmask    = p_fmt->i_gmask;
    p_vout->render.i_bmask    = p_fmt->i_bmask;
gbazin's avatar
 
gbazin committed
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301

    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
302
    p_vout->i_alignment  = 0;
gbazin's avatar
 
gbazin committed
303 304 305
    p_vout->render_time  = 10;
    p_vout->c_fps_samples = 0;
    p_vout->b_filter_change = 0;
306 307
    p_vout->pf_control = NULL;
    p_vout->p_window = NULL;
308
    p_vout->i_par_num = p_vout->i_par_den = 1;
gbazin's avatar
 
gbazin committed
309 310

    /* Initialize locks */
311 312 313
    vlc_mutex_init( &p_vout->picture_lock );
    vlc_mutex_init( &p_vout->change_lock );
    vlc_mutex_init( &p_vout->vfilter_lock );
gbazin's avatar
 
gbazin committed
314

315 316 317 318 319 320 321
    /* 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 );

322
    /* Initialize subpicture unit */
323
    p_vout->p_spu = spu_Create( p_vout );
324
    spu_Attach( p_vout->p_spu, p_parent, true );
325

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

329 330
    spu_Init( p_vout->p_spu );

331 332
    /* Take care of some "interface/control" related initialisations */
    vout_IntfInit( p_vout );
gbazin's avatar
 
gbazin committed
333

gbazin's avatar
 
gbazin committed
334 335 336
    /* 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
337
    {
338
        /* Look for the default filter configuration */
339 340
        p_vout->psz_filter_chain =
            var_CreateGetStringCommand( p_vout, "vout-filter" );
341 342

        /* Apply video filter2 objects on the first vout */
343 344
        p_vout->psz_vf2 =
            var_CreateGetStringCommand( p_vout, "video-filter" );
gbazin's avatar
 
gbazin committed
345 346 347 348
    }
    else
    {
        /* continue the parent's filter chain */
349
        char *psz_tmp;
gbazin's avatar
 
gbazin committed
350

351 352 353 354 355 356 357
        /* Ugly hack to jump to our configuration chain */
        p_vout->psz_filter_chain
            = ((vout_thread_t *)p_parent)->psz_filter_chain;
        p_vout->psz_filter_chain
            = config_ChainCreate( &psz_tmp, &p_cfg, p_vout->psz_filter_chain );
        config_ChainDestroy( p_cfg );
        free( psz_tmp );
358 359

        /* Create a video filter2 var ... but don't inherit values */
360 361 362
        var_Create( p_vout, "video-filter",
                    VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
        p_vout->psz_vf2 = var_GetString( p_vout, "video-filter" );
gbazin's avatar
 
gbazin committed
363 364
    }

365
    var_AddCallback( p_vout, "video-filter", VideoFilter2Callback, NULL );
366 367
    p_vout->p_vf2_chain = filter_chain_New( p_vout, "video filter2",
        false, video_filter_buffer_allocation_init, NULL, p_vout );
368

gbazin's avatar
 
gbazin committed
369
    /* Choose the video output module */
gbazin's avatar
 
gbazin committed
370
    if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain )
gbazin's avatar
 
gbazin committed
371
    {
gbazin's avatar
 
gbazin committed
372 373
        var_Create( p_vout, "vout", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
        var_Get( p_vout, "vout", &val );
dionoea's avatar
dionoea committed
374
        psz_parser = val.psz_string;
gbazin's avatar
 
gbazin committed
375 376 377
    }
    else
    {
dionoea's avatar
dionoea committed
378
        psz_parser = strdup( p_vout->psz_filter_chain );
Sam Hocevar's avatar
 
Sam Hocevar committed
379 380
    }

gbazin's avatar
 
gbazin committed
381
    /* Create the vout thread */
382
    char* psz_tmp = config_ChainCreate( &psz_name, &p_cfg, psz_parser );
dionoea's avatar
dionoea committed
383
    free( psz_parser );
384
    free( psz_tmp );
385
    p_vout->p_cfg = p_cfg;
gbazin's avatar
 
gbazin committed
386
    p_vout->p_module = module_Need( p_vout,
gbazin's avatar
 
gbazin committed
387
        ( p_vout->psz_filter_chain && *p_vout->psz_filter_chain ) ?
388
        "video filter" : "video output", psz_name, p_vout->psz_filter_chain && *p_vout->psz_filter_chain );
389
    free( psz_name );
Sam Hocevar's avatar
 
Sam Hocevar committed
390 391 392

    if( p_vout->p_module == NULL )
    {
393
        msg_Err( p_vout, "no suitable vout module" );
394 395
        // FIXME it's ugly but that's exactly the function that need to be called.
        EndThread( p_vout );
396
        vlc_object_detach( p_vout );
397
        vlc_object_release( p_vout );
398
        return NULL;
Sam Hocevar's avatar
 
Sam Hocevar committed
399
    }
gbazin's avatar
 
gbazin committed
400

gbazin's avatar
 
gbazin committed
401
    /* Create a few object variables for interface interaction */
gbazin's avatar
 
gbazin committed
402 403 404
    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 );
405
    val.psz_string = (char *)""; text.psz_string = _("Disable");
gbazin's avatar
 
gbazin committed
406
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
407
    val.psz_string = (char *)"discard"; text.psz_string = _("Discard");
gbazin's avatar
 
gbazin committed
408
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
409
    val.psz_string = (char *)"blend"; text.psz_string = _("Blend");
gbazin's avatar
 
gbazin committed
410
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
411
    val.psz_string = (char *)"mean"; text.psz_string = _("Mean");
gbazin's avatar
 
gbazin committed
412
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
413
    val.psz_string = (char *)"bob"; text.psz_string = _("Bob");
gbazin's avatar
 
gbazin committed
414
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
415
    val.psz_string = (char *)"linear"; text.psz_string = _("Linear");
gbazin's avatar
 
gbazin committed
416
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
417
    val.psz_string = (char *)"x"; text.psz_string = (char *)"X";
418 419
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );

gbazin's avatar
 
gbazin committed
420 421 422
    if( var_Get( p_vout, "deinterlace-mode", &val ) == VLC_SUCCESS )
    {
        var_Set( p_vout, "deinterlace", val );
423
        free( val.psz_string );
gbazin's avatar
 
gbazin committed
424
    }
gbazin's avatar
 
gbazin committed
425 426
    var_AddCallback( p_vout, "deinterlace", DeinterlaceCallback, NULL );

427
    var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
428
    text.psz_string = _("Filters");
429 430
    var_Change( p_vout, "vout-filter", VLC_VAR_SETTEXT, &text, NULL );
    var_AddCallback( p_vout, "vout-filter", FilterCallback, NULL );
431

gbazin's avatar
 
gbazin committed
432 433
    /* Calculate delay created by internal caching */
    p_input_thread = (input_thread_t *)vlc_object_find( p_vout,
gbazin's avatar
 
gbazin committed
434
                                           VLC_OBJECT_INPUT, FIND_ANYWHERE );
gbazin's avatar
 
gbazin committed
435 436
    if( p_input_thread )
    {
gbazin's avatar
 
gbazin committed
437
        p_vout->i_pts_delay = p_input_thread->i_pts_delay;
gbazin's avatar
 
gbazin committed
438 439 440 441
        vlc_object_release( p_input_thread );
    }
    else
    {
gbazin's avatar
 
gbazin committed
442
        p_vout->i_pts_delay = DEFAULT_PTS_DELAY;
gbazin's avatar
 
gbazin committed
443 444
    }

445
    if( vlc_thread_create( p_vout, "video output", RunThread,
446
                           VLC_THREAD_PRIORITY_OUTPUT, true ) )
Michel Kaempf's avatar
Michel Kaempf committed
447
    {
448
        module_Unneed( p_vout, p_vout->p_module );
449
        vlc_object_release( p_vout );
450 451 452
        return NULL;
    }

453 454
    vlc_object_set_destructor( p_vout, vout_Destructor );

455 456 457
    if( p_vout->b_error )
    {
        msg_Err( p_vout, "video output creation failed" );
458
        vout_CloseAndRelease( p_vout );
459
        return NULL;
460
    }
Michel Kaempf's avatar
Michel Kaempf committed
461

462
    return p_vout;
Michel Kaempf's avatar
Michel Kaempf committed
463 464
}

465
/*****************************************************************************
466
 * vout_Close: Close a vout created by vout_Create.
467
 *****************************************************************************
468
 * You HAVE to call it on vout created by vout_Create before vlc_object_release.
Rémi Denis-Courmont's avatar
Typo  
Rémi Denis-Courmont committed
469
 * You should NEVER call it on vout not obtained through vout_Create
470 471
 * (like with vout_Request or vlc_object_find.)
 * You can use vout_CloseAndRelease() as a convenient method.
472
 *****************************************************************************/
473
void vout_Close( vout_thread_t *p_vout )
474 475 476 477 478 479
{
    assert( p_vout );

    vlc_object_kill( p_vout );
    vlc_thread_join( p_vout );
    module_Unneed( p_vout, p_vout->p_module );
480
    p_vout->p_module = NULL;
481 482 483
}

/* */
484 485
static void vout_Destructor( vlc_object_t * p_this )
{
486
    vout_thread_t *p_vout = (vout_thread_t *)p_this;
487

488 489 490
    /* Make sure the vout was stopped first */
    assert( !p_vout->p_module );

491 492 493 494 495
    /* Destroy the locks */
    vlc_mutex_destroy( &p_vout->picture_lock );
    vlc_mutex_destroy( &p_vout->change_lock );
    vlc_mutex_destroy( &p_vout->vfilter_lock );

496
    free( p_vout->psz_filter_chain );
gbazin's avatar
 
gbazin committed
497

498 499
    config_ChainDestroy( p_vout->p_cfg );

500
#ifndef __APPLE__
501
    vout_thread_t *p_another_vout;
502

503 504 505 506 507 508 509 510
    /* This is a dirty hack mostly for Linux, where there is no way to get the
     * GUI back if you closed it while playing video. This is solved in
     * Mac OS X, where we have this novelty called menubar, that will always
     * allow you access to the applications main functionality. They should try
     * that on linux sometime. */
    p_another_vout = vlc_object_find( p_this->p_libvlc,
                                      VLC_OBJECT_VOUT, FIND_ANYWHERE );
    if( p_another_vout == NULL )
511
        var_SetBool( p_this->p_libvlc, "intf-show", true );
512 513
    else
        vlc_object_release( p_another_vout );
514
#endif
Michel Kaempf's avatar
Michel Kaempf committed
515 516
}

517
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
518
 * InitThread: initialize video output thread
519
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
520 521 522
 * 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.
523
 * XXX You have to enter it with change_lock taken.
524
 *****************************************************************************/
525 526
static int ChromaCreate( vout_thread_t *p_vout );
static void ChromaDestroy( vout_thread_t *p_vout );
527

528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543
static bool ChromaIsEqual( const picture_heap_t *p_output, const picture_heap_t *p_render )
{
     if( !vout_ChromaCmp( p_output->i_chroma, p_render->i_chroma ) )
         return false;

     if( p_output->i_chroma != FOURCC_RV15 &&
         p_output->i_chroma != FOURCC_RV16 &&
         p_output->i_chroma != FOURCC_RV24 &&
         p_output->i_chroma != FOURCC_RV32 )
         return true;

     return p_output->i_rmask == p_render->i_rmask &&
            p_output->i_gmask == p_render->i_gmask &&
            p_output->i_bmask == p_render->i_bmask;
}

Sam Hocevar's avatar
 
Sam Hocevar committed
544
static int InitThread( vout_thread_t *p_vout )
Vincent Seguin's avatar
Vincent Seguin committed
545
{
546
    int i, i_aspect_x, i_aspect_y;
Sam Hocevar's avatar
 
Sam Hocevar committed
547

Sam Hocevar's avatar
 
Sam Hocevar committed
548 549
#ifdef STATS
    p_vout->c_loops = 0;
Vincent Seguin's avatar
Vincent Seguin committed
550 551
#endif

Sam Hocevar's avatar
 
Sam Hocevar committed
552
    /* Initialize output method, it allocates direct buffers for us */
Sam Hocevar's avatar
 
Sam Hocevar committed
553
    if( p_vout->pf_init( p_vout ) )
554
        return VLC_EGENERIC;
Sam Hocevar's avatar
 
Sam Hocevar committed
555

Sam Hocevar's avatar
 
Sam Hocevar committed
556
    if( !I_OUTPUTPICTURES )
Sam Hocevar's avatar
 
Sam Hocevar committed
557
    {
558 559
        msg_Err( p_vout, "plugin was unable to allocate at least "
                         "one direct buffer" );
Sam Hocevar's avatar
 
Sam Hocevar committed
560
        p_vout->pf_end( p_vout );
561
        return VLC_EGENERIC;
562
    }
Vincent Seguin's avatar
Vincent Seguin committed
563

gbazin's avatar
 
gbazin committed
564 565 566 567 568 569 570 571
    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 );
        return VLC_EGENERIC;
    }

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

574 575 576 577 578 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
    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
617 618
    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 );
619 620

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

622 623
    msg_Dbg( p_vout, "picture out %ix%i (%i,%i,%ix%i), "
             "chroma %4.4s, ar %i:%i, sar %i:%i",
624
             p_vout->fmt_out.i_width, p_vout->fmt_out.i_height,
625 626 627 628 629
             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,
630
             p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den );
Sam Hocevar's avatar
 
Sam Hocevar committed
631

632
    /* FIXME removed the need of both fmt_* and heap infos */
Sam Hocevar's avatar
 
Sam Hocevar committed
633
    /* Calculate shifts from system-updated masks */
634 635 636 637 638
    PictureHeapFixRgb( &p_vout->render );
    VideoFormatImportRgb( &p_vout->fmt_render, &p_vout->render );

    PictureHeapFixRgb( &p_vout->output );
    VideoFormatImportRgb( &p_vout->fmt_out, &p_vout->output );
Sam Hocevar's avatar
 
Sam Hocevar committed
639

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

Sam Hocevar's avatar
 
Sam Hocevar committed
652 653
        for( i = 1; i < VOUT_MAX_PICTURES; i++ )
        {
gbazin's avatar
 
gbazin committed
654 655 656 657 658 659 660 661
            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
662 663 664
            PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
            I_RENDERPICTURES++;
        }
gbazin's avatar
 
gbazin committed
665 666 667 668

        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
669 670 671
    }
    else
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
672 673 674
        /* 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
675 676
        p_vout->b_direct = 0;

677
        if( ChromaCreate( p_vout ) )
Sam Hocevar's avatar
 
Sam Hocevar committed
678 679
        {
            p_vout->pf_end( p_vout );
680
            return VLC_EGENERIC;
Sam Hocevar's avatar
 
Sam Hocevar committed
681
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
682

gbazin's avatar
 
gbazin committed
683 684 685 686
        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
687 688

        /* Append render buffers after the direct buffers */
Sam Hocevar's avatar
 
Sam Hocevar committed
689
        for( i = I_OUTPUTPICTURES; i < 2 * VOUT_MAX_PICTURES; i++ )
Sam Hocevar's avatar
 
Sam Hocevar committed
690 691 692
        {
            PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
            I_RENDERPICTURES++;
gbazin's avatar
 
gbazin committed
693 694 695 696

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

Sam Hocevar's avatar
 
Sam Hocevar committed
700 701 702 703 704 705 706 707 708 709 710
    /* 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;
    }

711
    return VLC_SUCCESS;
Vincent Seguin's avatar
Vincent Seguin committed
712 713
}

714
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
715
 * RunThread: video output thread
716
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
717 718 719
 * 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.
720
 *****************************************************************************/
721
static void* RunThread( vlc_object_t *p_this )
Vincent Seguin's avatar
Vincent Seguin committed
722
{
723
    vout_thread_t *p_vout = (vout_thread_t *)p_this;
724
    int             i_idle_loops = 0;  /* loops without displaying a picture */
Sam Hocevar's avatar
 
Sam Hocevar committed
725

726
    picture_t *     p_last_picture = NULL;                   /* last picture */
Sam Hocevar's avatar
 
Sam Hocevar committed
727

728
    subpicture_t *  p_subpic = NULL;                   /* subpicture pointer */
Sam Hocevar's avatar
 
Sam Hocevar committed
729

Laurent Aimar's avatar
Laurent Aimar committed
730
    bool            b_drop_late;
731

Laurent Aimar's avatar
Laurent Aimar committed
732
    int             i_displayed = 0, i_lost = 0;
733
    int canc = vlc_savecancel ();
734

Sam Hocevar's avatar
 
Sam Hocevar committed
735 736 737
    /*
     * Initialize thread
     */
738
    vlc_mutex_lock( &p_vout->change_lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
739
    p_vout->b_error = InitThread( p_vout );
gbazin's avatar
 
gbazin committed
740

Laurent Aimar's avatar
Laurent Aimar committed
741
    b_drop_late = var_CreateGetBool( p_vout, "drop-late-frames" );
742

gbazin's avatar
 
gbazin committed
743 744 745
    /* signal the creation of the vout */
    vlc_thread_ready( p_vout );

Sam Hocevar's avatar
 
Sam Hocevar committed
746
    if( p_vout->b_error )
747
    {
Laurent Aimar's avatar
Laurent Aimar committed
748
        EndThread( p_vout );
749
        vlc_mutex_unlock( &p_vout->change_lock );
750
        vlc_restorecancel (canc);
751
        return NULL;
752
    }
753

754 755
    vlc_object_lock( p_vout );

756 757
    if( p_vout->b_title_show )
        DisplayTitleOnOSD( p_vout );
758

759
    /*
Sam Hocevar's avatar
Sam Hocevar committed
760
     * Main loop - it is not executed if an error occurred during
Sam Hocevar's avatar
 
Sam Hocevar committed
761
     * initialization
762
     */
Laurent Aimar's avatar
Laurent Aimar committed
763
    while( vlc_object_alive( p_vout ) && !p_vout->b_error )
764
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
765
        /* Initialize loop variables */
Laurent Aimar's avatar
Laurent Aimar committed
766 767
        const mtime_t current_date = mdate();
        picture_t *p_picture = NULL;
768
        picture_t *p_filtered_picture;
Laurent Aimar's avatar
Laurent Aimar committed
769 770 771 772
        mtime_t display_date = 0;
        picture_t *p_directbuffer;
        input_thread_t *p_input;
        int i_index;
Sam Hocevar's avatar
 
Sam Hocevar committed
773

Laurent Aimar's avatar
Laurent Aimar committed
774 775 776
#if 0
        p_vout->c_loops++;
        if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
Laurent Aimar's avatar
Laurent Aimar committed
777
        {
Laurent Aimar's avatar
Laurent Aimar committed
778 779
            msg_Dbg( p_vout, "picture heap: %d/%d",
                     I_RENDERPICTURES, p_vout->i_heap_size );
Laurent Aimar's avatar
Laurent Aimar committed
780
        }
Laurent Aimar's avatar
Laurent Aimar committed
781 782 783 784
#endif

        /* Update statistics */
        p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, FIND_PARENT );
Laurent Aimar's avatar
Laurent Aimar committed
785 786 787 788 789 790 791 792 793 794 795
        if( p_input )
        {
            vlc_mutex_lock( &p_input->p->counters.counters_lock );
            stats_UpdateInteger( p_vout, p_input->p->counters.p_lost_pictures,
                                 i_lost , NULL);
            stats_UpdateInteger( p_vout,
                                 p_input->p->counters.p_displayed_pictures,
                                 i_displayed , NULL);
            i_displayed = i_lost = 0;
            vlc_mutex_unlock( &p_input->p->counters.counters_lock );
            vlc_object_release( p_input );
Sam Hocevar's avatar
 
Sam Hocevar committed
796 797 798
        }

        /*
799 800 801
         * Find the picture to display (the one with the earliest date).
         * This operation does not need lock, since only READY_PICTUREs
         * are handled. */
802
        for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
Sam Hocevar's avatar
 
Sam Hocevar committed
803
        {
Laurent Aimar's avatar
Laurent Aimar committed
804 805 806 807
            picture_t *p_pic = PP_RENDERPICTURE[i_index];

            if( p_pic->i_status == READY_PICTURE &&
                ( p_picture == NULL || p_pic->date < display_date ) )
808
            {
Laurent Aimar's avatar
Laurent Aimar committed
809
                p_picture = p_pic;
Sam Hocevar's avatar
 
Sam Hocevar committed
810
                display_date = p_picture->date;
811 812
            }
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
813

814
        if( p_picture )
815
        {
816 817 818 819