video_output.c 55 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

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

71
static void     AspectRatio       ( int, int *, int * );
72
73
static int      BinaryLog         ( uint32_t );
static void     MaskToShift       ( int *, int *, uint32_t );
Michel Kaempf's avatar
Michel Kaempf committed
74

75
76
static void     vout_Destructor   ( vlc_object_t * p_this );

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

85
86
87
/* From vout_intf.c */
int vout_Snapshot( vout_thread_t *, picture_t * );

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

91
92
93
94
95
96
/*****************************************************************************
 * Video Filter2 functions
 *****************************************************************************/
static picture_t *video_new_buffer_filter( filter_t *p_filter )
{
    picture_t *p_picture;
97
    vout_thread_t *p_vout = (vout_thread_t*)p_filter->p_owner;
98
99
100
101
102
103
104
105

    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 )
{
106
107
108
109
110
111
112
113
114
    vout_DestroyPicture( (vout_thread_t*)p_filter->p_owner, p_pic );
}

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;
115
116
}

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

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

142
    /* TODO: find a suitable unused video output */
143
144
145
146

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

150
        /* We don't directly check for the "vout-filter" variable for obvious
gbazin's avatar
   
gbazin committed
151
152
153
         * performance reasons. */
        if( p_vout->b_filter_change )
        {
154
            var_Get( p_vout, "vout-filter", &val );
155
            psz_filter_chain = val.psz_string;
gbazin's avatar
   
gbazin committed
156

157
            if( psz_filter_chain && !*psz_filter_chain )
gbazin's avatar
   
gbazin committed
158
159
160
161
            {
                free( psz_filter_chain );
                psz_filter_chain = NULL;
            }
162
            if( p_vout->psz_filter_chain && !*p_vout->psz_filter_chain )
gbazin's avatar
   
gbazin committed
163
164
165
166
167
            {
                free( p_vout->psz_filter_chain );
                p_vout->psz_filter_chain = NULL;
            }

168
            if( !psz_filter_chain && !p_vout->psz_filter_chain )
gbazin's avatar
   
gbazin committed
169
            {
170
                p_vout->b_filter_change = false;
gbazin's avatar
   
gbazin committed
171
172
            }

173
            free( psz_filter_chain );
gbazin's avatar
   
gbazin committed
174
175
        }

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

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

201
        p_vout = vout_Create( p_this, p_fmt );
202
203
204
205
206
207
208
    }

    return p_vout;
}

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

220
221
    unsigned int i_width = p_fmt->i_width;
    unsigned int i_height = p_fmt->i_height;
222
223
224
    vlc_fourcc_t i_chroma = p_fmt->i_chroma;
    unsigned int i_aspect = p_fmt->i_aspect;

225
226
227
228
    config_chain_t *p_cfg;
    char *psz_parser;
    char *psz_name;

229
    /* Allocate descriptor */
230
231
232
    static const char typename[] = "video output";
    p_vout = vlc_custom_create( p_parent, sizeof( *p_vout ), VLC_OBJECT_VOUT,
                                typename );
233
    if( p_vout == NULL )
234
        return NULL;
235

236
    /* Initialize pictures - translation tables and functions
gbazin's avatar
   
gbazin committed
237
     * will be initialized later in InitThread */
238
    for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++)
gbazin's avatar
   
gbazin committed
239
240
241
242
243
    {
        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;
244
        p_vout->p_picture[i_index].b_slow   = 0;
gbazin's avatar
   
gbazin committed
245
246
247
248
249
250
251
    }

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

    /* Initialize the rendering heap */
    I_RENDERPICTURES = 0;
252
253
254

    vlc_ureduce( &p_fmt->i_sar_num, &p_fmt->i_sar_den,
                 p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 );
255
256
    p_vout->fmt_render        = *p_fmt;   /* FIXME palette */
    p_vout->fmt_in            = *p_fmt;   /* FIXME palette */
257

gbazin's avatar
   
gbazin committed
258
259
260
261
262
    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;

263
264
265
    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
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288

    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
289
    p_vout->i_alignment  = 0;
gbazin's avatar
   
gbazin committed
290
291
292
    p_vout->render_time  = 10;
    p_vout->c_fps_samples = 0;
    p_vout->b_filter_change = 0;
293
294
    p_vout->pf_control = NULL;
    p_vout->p_window = NULL;
295
    p_vout->i_par_num = p_vout->i_par_den = 1;
gbazin's avatar
   
gbazin committed
296
297

    /* Initialize locks */
298
299
300
    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
301

302
303
304
305
306
307
308
    /* 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 );

309
    /* Initialize subpicture unit */
310
    p_vout->p_spu = spu_Create( p_vout );
311
    spu_Attach( p_vout->p_spu, p_parent, true );
312

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

316
317
    spu_Init( p_vout->p_spu );

318
319
    /* Take care of some "interface/control" related initialisations */
    vout_IntfInit( p_vout );
gbazin's avatar
   
gbazin committed
320

gbazin's avatar
   
gbazin committed
321
322
323
    /* 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
324
    {
325
        /* Look for the default filter configuration */
326
327
        p_vout->psz_filter_chain =
            var_CreateGetStringCommand( p_vout, "vout-filter" );
328
329

        /* Apply video filter2 objects on the first vout */
330
331
        p_vout->psz_vf2 =
            var_CreateGetStringCommand( p_vout, "video-filter" );
gbazin's avatar
   
gbazin committed
332
333
334
335
    }
    else
    {
        /* continue the parent's filter chain */
336
        char *psz_tmp;
gbazin's avatar
   
gbazin committed
337

338
339
340
341
342
343
344
        /* 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 );
345
346

        /* Create a video filter2 var ... but don't inherit values */
347
348
349
        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
350
351
    }

352
    var_AddCallback( p_vout, "video-filter", VideoFilter2Callback, NULL );
353
354
    p_vout->p_vf2_chain = filter_chain_New( p_vout, "video filter2",
        false, video_filter_buffer_allocation_init, NULL, p_vout );
355

gbazin's avatar
   
gbazin committed
356
    /* Choose the video output module */
gbazin's avatar
   
gbazin committed
357
    if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain )
gbazin's avatar
   
gbazin committed
358
    {
gbazin's avatar
   
gbazin committed
359
360
        var_Create( p_vout, "vout", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
        var_Get( p_vout, "vout", &val );
dionoea's avatar
dionoea committed
361
        psz_parser = val.psz_string;
gbazin's avatar
   
gbazin committed
362
363
364
    }
    else
    {
dionoea's avatar
dionoea committed
365
        psz_parser = strdup( p_vout->psz_filter_chain );
Sam Hocevar's avatar
   
Sam Hocevar committed
366
367
    }

gbazin's avatar
   
gbazin committed
368
    /* Create the vout thread */
dionoea's avatar
dionoea committed
369
370
    config_ChainCreate( &psz_name, &p_cfg, psz_parser );
    free( psz_parser );
371
    p_vout->p_cfg = p_cfg;
gbazin's avatar
   
gbazin committed
372
    p_vout->p_module = module_Need( p_vout,
gbazin's avatar
   
gbazin committed
373
        ( p_vout->psz_filter_chain && *p_vout->psz_filter_chain ) ?
374
        "video filter" : "video output", psz_name, p_vout->psz_filter_chain && *p_vout->psz_filter_chain );
375
    free( psz_name );
Sam Hocevar's avatar
   
Sam Hocevar committed
376
377
378

    if( p_vout->p_module == NULL )
    {
379
        msg_Err( p_vout, "no suitable vout module" );
380
381
        // FIXME it's ugly but that's exactly the function that need to be called.
        EndThread( p_vout );
382
        vlc_object_detach( p_vout );
383
        vlc_object_release( p_vout );
384
        return NULL;
Sam Hocevar's avatar
   
Sam Hocevar committed
385
    }
gbazin's avatar
   
gbazin committed
386

gbazin's avatar
   
gbazin committed
387
    /* Create a few object variables for interface interaction */
gbazin's avatar
   
gbazin committed
388
389
390
    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 );
391
    val.psz_string = (char *)""; text.psz_string = _("Disable");
gbazin's avatar
   
gbazin committed
392
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
393
    val.psz_string = (char *)"discard"; text.psz_string = _("Discard");
gbazin's avatar
   
gbazin committed
394
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
395
    val.psz_string = (char *)"blend"; text.psz_string = _("Blend");
gbazin's avatar
   
gbazin committed
396
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
397
    val.psz_string = (char *)"mean"; text.psz_string = _("Mean");
gbazin's avatar
   
gbazin committed
398
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
399
    val.psz_string = (char *)"bob"; text.psz_string = _("Bob");
gbazin's avatar
   
gbazin committed
400
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
401
    val.psz_string = (char *)"linear"; text.psz_string = _("Linear");
gbazin's avatar
   
gbazin committed
402
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
403
    val.psz_string = (char *)"x"; text.psz_string = (char *)"X";
404
405
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );

gbazin's avatar
   
gbazin committed
406
407
408
    if( var_Get( p_vout, "deinterlace-mode", &val ) == VLC_SUCCESS )
    {
        var_Set( p_vout, "deinterlace", val );
409
        free( val.psz_string );
gbazin's avatar
   
gbazin committed
410
    }
gbazin's avatar
   
gbazin committed
411
412
    var_AddCallback( p_vout, "deinterlace", DeinterlaceCallback, NULL );

413
    var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
414
    text.psz_string = _("Filters");
415
416
    var_Change( p_vout, "vout-filter", VLC_VAR_SETTEXT, &text, NULL );
    var_AddCallback( p_vout, "vout-filter", FilterCallback, NULL );
417

gbazin's avatar
   
gbazin committed
418
419
    /* Calculate delay created by internal caching */
    p_input_thread = (input_thread_t *)vlc_object_find( p_vout,
gbazin's avatar
   
gbazin committed
420
                                           VLC_OBJECT_INPUT, FIND_ANYWHERE );
gbazin's avatar
   
gbazin committed
421
422
    if( p_input_thread )
    {
gbazin's avatar
   
gbazin committed
423
        p_vout->i_pts_delay = p_input_thread->i_pts_delay;
gbazin's avatar
   
gbazin committed
424
425
426
427
        vlc_object_release( p_input_thread );
    }
    else
    {
gbazin's avatar
   
gbazin committed
428
        p_vout->i_pts_delay = DEFAULT_PTS_DELAY;
gbazin's avatar
   
gbazin committed
429
430
    }

431
    if( vlc_thread_create( p_vout, "video output", RunThread,
432
                           VLC_THREAD_PRIORITY_OUTPUT, true ) )
Michel Kaempf's avatar
Michel Kaempf committed
433
    {
434
        module_Unneed( p_vout, p_vout->p_module );
435
        vlc_object_release( p_vout );
436
437
438
        return NULL;
    }

439
440
    vlc_object_set_destructor( p_vout, vout_Destructor );

441
442
443
    if( p_vout->b_error )
    {
        msg_Err( p_vout, "video output creation failed" );
444
        vout_CloseAndRelease( p_vout );
445
        return NULL;
446
    }
Michel Kaempf's avatar
Michel Kaempf committed
447

448
    return p_vout;
Michel Kaempf's avatar
Michel Kaempf committed
449
450
}

451
/*****************************************************************************
452
 * vout_Close: Close a vout created by vout_Create.
453
 *****************************************************************************
454
455
456
457
 * You HAVE to call it on vout created by vout_Create before vlc_object_release.
 * You should NEVER call it on vout not obtained though vout_Create
 * (like with vout_Request or vlc_object_find.)
 * You can use vout_CloseAndRelease() as a convenient method.
458
 *****************************************************************************/
459
void vout_Close( vout_thread_t *p_vout )
460
461
462
463
464
465
{
    assert( p_vout );

    vlc_object_kill( p_vout );
    vlc_thread_join( p_vout );
    module_Unneed( p_vout, p_vout->p_module );
466
    p_vout->p_module = NULL;
467
468
469
}

/* */
470
471
static void vout_Destructor( vlc_object_t * p_this )
{
472
    vout_thread_t *p_vout = (vout_thread_t *)p_this;
473

474
475
476
    /* Make sure the vout was stopped first */
    assert( !p_vout->p_module );

477
478
479
480
481
    /* Destroy the locks */
    vlc_mutex_destroy( &p_vout->picture_lock );
    vlc_mutex_destroy( &p_vout->change_lock );
    vlc_mutex_destroy( &p_vout->vfilter_lock );

482
    free( p_vout->psz_filter_chain );
gbazin's avatar
   
gbazin committed
483

484
485
    config_ChainDestroy( p_vout->p_cfg );

486
#ifndef __APPLE__
487
    vout_thread_t *p_another_vout;
488

489
490
491
492
493
494
495
496
    /* 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 )
497
        var_SetBool( p_this->p_libvlc, "intf-show", true );
498
499
    else
        vlc_object_release( p_another_vout );
500
#endif
Michel Kaempf's avatar
Michel Kaempf committed
501
502
}

503
/*****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
504
 * InitThread: initialize video output thread
505
 *****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
506
507
508
 * 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.
509
 * XXX You have to enter it with change_lock taken.
510
 *****************************************************************************/
511
512
static int ChromaCreate( vout_thread_t *p_vout );
static void ChromaDestroy( vout_thread_t *p_vout );
Laurent Aimar's avatar
Laurent Aimar committed
513
static void DropPicture( vout_thread_t *p_vout, picture_t *p_picture );
514

Sam Hocevar's avatar
   
Sam Hocevar committed
515
static int InitThread( vout_thread_t *p_vout )
Vincent Seguin's avatar
Vincent Seguin committed
516
{
517
    int i, i_aspect_x, i_aspect_y;
Sam Hocevar's avatar
   
Sam Hocevar committed
518

Sam Hocevar's avatar
   
Sam Hocevar committed
519
520
#ifdef STATS
    p_vout->c_loops = 0;
Vincent Seguin's avatar
Vincent Seguin committed
521
522
#endif

Sam Hocevar's avatar
   
Sam Hocevar committed
523
    /* Initialize output method, it allocates direct buffers for us */
Sam Hocevar's avatar
   
Sam Hocevar committed
524
    if( p_vout->pf_init( p_vout ) )
525
        return VLC_EGENERIC;
Sam Hocevar's avatar
   
Sam Hocevar committed
526

Sam Hocevar's avatar
   
Sam Hocevar committed
527
    if( !I_OUTPUTPICTURES )
Sam Hocevar's avatar
   
Sam Hocevar committed
528
    {
529
530
        msg_Err( p_vout, "plugin was unable to allocate at least "
                         "one direct buffer" );
Sam Hocevar's avatar
   
Sam Hocevar committed
531
        p_vout->pf_end( p_vout );
532
        return VLC_EGENERIC;
533
    }
Vincent Seguin's avatar
Vincent Seguin committed
534

gbazin's avatar
   
gbazin committed
535
536
537
538
539
540
541
542
    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;
    }

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

545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
    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
588
589
    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 );
590
591

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

593
594
    msg_Dbg( p_vout, "picture out %ix%i (%i,%i,%ix%i), "
             "chroma %4.4s, ar %i:%i, sar %i:%i",
595
             p_vout->fmt_out.i_width, p_vout->fmt_out.i_height,
596
597
598
599
600
             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,
601
             p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den );
Sam Hocevar's avatar
   
Sam Hocevar committed
602

Sam Hocevar's avatar
   
Sam Hocevar committed
603
    /* Calculate shifts from system-updated masks */
604
605
606
607
608
609
610
    MaskToShift( &p_vout->render.i_lrshift, &p_vout->output.i_rrshift,
                 p_vout->render.i_rmask );
    MaskToShift( &p_vout->render.i_lgshift, &p_vout->output.i_rgshift,
                 p_vout->render.i_gmask );
    MaskToShift( &p_vout->render.i_lbshift, &p_vout->output.i_rbshift,
                 p_vout->render.i_bmask );

Sam Hocevar's avatar
   
Sam Hocevar committed
611
612
613
614
615
616
617
    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
618
    /* Check whether we managed to create direct buffers similar to
619
     * the render buffers, ie same size and chroma */
Sam Hocevar's avatar
   
Sam Hocevar committed
620
621
    if( ( p_vout->output.i_width == p_vout->render.i_width )
     && ( p_vout->output.i_height == p_vout->render.i_height )
622
     && ( vout_ChromaCmp( p_vout->output.i_chroma, p_vout->render.i_chroma ) ) )
Sam Hocevar's avatar
   
Sam Hocevar committed
623
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
624
625
626
627
        /* 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
628
629
        p_vout->b_direct = 1;

Sam Hocevar's avatar
   
Sam Hocevar committed
630
631
        for( i = 1; i < VOUT_MAX_PICTURES; i++ )
        {
gbazin's avatar
   
gbazin committed
632
633
634
635
636
637
638
639
            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
640
641
642
            PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
            I_RENDERPICTURES++;
        }
gbazin's avatar
   
gbazin committed
643
644
645
646

        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
647
648
649
    }
    else
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
650
651
652
        /* 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
653
654
        p_vout->b_direct = 0;

655
        if( ChromaCreate( p_vout ) )
Sam Hocevar's avatar
   
Sam Hocevar committed
656
657
        {
            p_vout->pf_end( p_vout );
658
            return VLC_EGENERIC;
Sam Hocevar's avatar
   
Sam Hocevar committed
659
        }
Sam Hocevar's avatar
   
Sam Hocevar committed
660

gbazin's avatar
   
gbazin committed
661
662
663
664
        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
665
666

        /* Append render buffers after the direct buffers */
Sam Hocevar's avatar
   
Sam Hocevar committed
667
        for( i = I_OUTPUTPICTURES; i < 2 * VOUT_MAX_PICTURES; i++ )
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

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

Sam Hocevar's avatar
   
Sam Hocevar committed
678
679
680
681
682
683
684
685
686
687
688
    /* 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;
    }

689
    return VLC_SUCCESS;
Vincent Seguin's avatar
Vincent Seguin committed
690
691
}

692
/*****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
693
 * RunThread: video output thread
694
 *****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
695
696
697
 * 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.
698
 *****************************************************************************/
Sam Hocevar's avatar
   
Sam Hocevar committed
699
static void RunThread( vout_thread_t *p_vout)
Vincent Seguin's avatar
Vincent Seguin committed
700
{
Sam Hocevar's avatar
   
Sam Hocevar committed
701
    int             i_index;                                /* index in heap */
702
    int             i_idle_loops = 0;  /* loops without displaying a picture */
Sam Hocevar's avatar
   
Sam Hocevar committed
703
704
705
706
    mtime_t         current_date;                            /* current date */
    mtime_t         display_date;                            /* display date */

    picture_t *     p_picture;                            /* picture pointer */
707
    picture_t *     p_last_picture = NULL;                   /* last picture */
Sam Hocevar's avatar
   
Sam Hocevar committed
708
709
    picture_t *     p_directbuffer;              /* direct buffer to display */

710
    subpicture_t *  p_subpic = NULL;                   /* subpicture pointer */
Sam Hocevar's avatar
   
Sam Hocevar committed
711

712
    input_thread_t *p_input = NULL ;           /* Parent input, if it exists */
713

714
    vlc_value_t     val;
715
    bool      b_drop_late;
716

717
718
    int             i_displayed = 0, i_lost = 0, i_loops = 0;

Sam Hocevar's avatar
   
Sam Hocevar committed
719
720
721
    /*
     * Initialize thread
     */
722
    vlc_mutex_lock( &p_vout->change_lock );
Sam Hocevar's avatar
   
Sam Hocevar committed
723
    p_vout->b_error = InitThread( p_vout );
gbazin's avatar
   
gbazin committed
724

725
726
727
728
    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
729
730
731
    /* signal the creation of the vout */
    vlc_thread_ready( p_vout );

Sam Hocevar's avatar
   
Sam Hocevar committed
732
    if( p_vout->b_error )
733
    {
Laurent Aimar's avatar
Laurent Aimar committed
734
        EndThread( p_vout );
735
        vlc_mutex_unlock( &p_vout->change_lock );
Sam Hocevar's avatar
   
Sam Hocevar committed
736
        return;
737
    }
738

739
740
    vlc_object_lock( p_vout );

741
742
    if( p_vout->b_title_show )
        DisplayTitleOnOSD( p_vout );
743

744
    /*
Sam Hocevar's avatar
Sam Hocevar committed
745
     * Main loop - it is not executed if an error occurred during
Sam Hocevar's avatar
   
Sam Hocevar committed
746
     * initialization
747
     */
Laurent Aimar's avatar
Laurent Aimar committed
748
    while( vlc_object_alive( p_vout ) && !p_vout->b_error )
749
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
750
        /* Initialize loop variables */
751
        p_picture = NULL;
Sam Hocevar's avatar
   
Sam Hocevar committed
752
753
        display_date = 0;
        current_date = mdate();
Sam Hocevar's avatar
   
Sam Hocevar committed
754

755
        i_loops++;
Laurent Aimar's avatar
Laurent Aimar committed
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
        if( !p_input )
        {
            p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT,
                                       FIND_PARENT );
        }
        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 );
            p_input = NULL;
        }
774
#if 0
Sam Hocevar's avatar
   
Sam Hocevar committed
775
776
        p_vout->c_loops++;
        if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
777
        {
778
779
            msg_Dbg( p_vout, "picture heap: %d/%d",
                     I_RENDERPICTURES, p_vout->i_heap_size );
Sam Hocevar's avatar
   
Sam Hocevar committed
780
        }
781
#endif
Sam Hocevar's avatar
   
Sam Hocevar committed
782
783

        /*
784
785
786
         * Find the picture to display (the one with the earliest date).
         * This operation does not need lock, since only READY_PICTUREs
         * are handled. */
787
        for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
Sam Hocevar's avatar
   
Sam Hocevar committed
788
        {
Sam Hocevar's avatar
   
Sam Hocevar committed
789
790
791
            if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
                && ( (p_picture == NULL) ||
                     (PP_RENDERPICTURE[i_index]->date < display_date) ) )
792
            {
Sam Hocevar's avatar
   
Sam Hocevar committed
793
                p_picture = PP_RENDERPICTURE[i_index];
Sam Hocevar's avatar
   
Sam Hocevar committed
794
                display_date = p_picture->date;
795
796
            }
        }
Sam Hocevar's avatar
   
Sam Hocevar committed
797

798
        if( p_picture )
799
        {
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
            /* 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;
                    }
                }
            }
816

817
818
819
            /* If we found better than the last picture, destroy it */
            if( p_last_picture && p_picture != p_last_picture )
            {
Laurent Aimar's avatar
Laurent Aimar committed
820
                DropPicture( p_vout, p_last_picture );
821
                p_last_picture = NULL;
822
823
            }

Sam Hocevar's avatar
   
Sam Hocevar committed
824
825
826
827
            /* Compute FPS rate */
            p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ]
                = display_date;

828
            if( !p_picture->b_force &&
829
                p_picture != p_last_picture &&
830
                display_date < current_date + p_vout->render_time &&
831
                b_drop_late )
Sam Hocevar's avatar
   
Sam Hocevar committed
832
833
834
            {
                /* Picture is late: it will be destroyed and the thread
                 * will directly choose the next picture */
Laurent Aimar's avatar
Laurent Aimar committed
835
836
                DropPicture( p_vout, p_picture );
                i_lost++;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
837
                msg_Warn( p_vout, "late picture skipped (%"PRId64")",
838
                                  current_date - display_date );
839
840
                continue;
            }
841

gbazin's avatar
   
gbazin committed
842
            if( display_date >
sigmunau's avatar
sigmunau committed
843
                current_date + p_vout->i_pts_delay + VOUT_BOGUS_DELAY )
844
845
            {
                /* Picture is waaay too early: it will be destroyed */
Laurent Aimar's avatar
Laurent Aimar committed
846
                DropPicture( p_vout, p_picture );
847
                i_lost++;
848
                msg_Warn( p_vout, "vout warning: early picture skipped "
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
849
                          "(%"PRId64")", display_date - current_date
gbazin's avatar
   
gbazin committed
850
                          - p_vout->i_pts_delay );
Sam Hocevar's avatar
   
Sam Hocevar committed
851
852
                continue;
            }
853

854
            if( display_date > current_date + VOUT_DISPLAY_DELAY )
Sam Hocevar's avatar
   
Sam Hocevar committed
855
856
857
858
859
860
861
862
            {
                /* 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;
            }
863
864
            else if( p_picture == p_last_picture )
            {
865
866
                /* We are asked to repeat the previous picture, but we first
                 * wait for a couple of idle loops */
867
868
869
870
871
872
873
                if( i_idle_loops < 4 )
                {
                    p_picture    = NULL;
                    display_date = 0;
                }
                else
                {
874
875
876
                    /* We set the display date to something high, otherwise
                     * we'll have lots of problems with late pictures */
                    display_date = current_date + p_vout->render_time;
877
878
879
880
881
882
883
                }
            }
        }

        if( p_picture == NULL )
        {
            i_idle_loops++;
884
885
        }

886
887
        if( p_picture )
        {
888
889
            p_picture = filter_chain_VideoFilter( p_vout->p_vf2_chain,
                                                  p_picture );
890
891
        }

892
893
        if( p_picture && p_vout->b_snapshot )
        {
894
            p_vout->b_snapshot = false;
895
896
897
            vout_Snapshot( p_vout, p_picture );
        }

Sam Hocevar's avatar
   
Sam Hocevar committed
898
899
900
        /*
         * Check for subpictures to display
         */
hartman's avatar
hartman committed
901
902
        if( display_date > 0 )
        {
903
            if( !p_input )
904
            {
905
906
                p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT,
                                           FIND_PARENT );
907
            }
908
            p_subpic = spu_SortSubpictures( p_vout->p_spu, display_date,
909
            p_input ? var_GetBool( p_input, "state" ) == PAUSE_S : false );