video_output.c 55.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-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>
Laurent Aimar's avatar
Laurent Aimar committed
50
#include <assert.h>
51

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

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

60
61
#include <libvlc.h>
#include <vlc_input.h>
62
#include "modules/modules.h"
63
#include "vout_pictures.h"
Laurent Aimar's avatar
Laurent Aimar committed
64
#include "vout_internal.h"
65

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

75
static void     AspectRatio       ( int, int *, int * );
Michel Kaempf's avatar
Michel Kaempf committed
76

Laurent Aimar's avatar
Laurent Aimar committed
77
static void VideoFormatImportRgb( video_format_t *, const picture_heap_t * );
78
79
static void PictureHeapFixRgb( picture_heap_t * );

80
81
static void     vout_Destructor   ( vlc_object_t * p_this );

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

90
91
92
/* From vout_intf.c */
int vout_Snapshot( vout_thread_t *, picture_t * );

93
94
95
/* Display media title in OSD */
static void DisplayTitleOnOSD( vout_thread_t *p_vout );

96
97
98
/* */
static void DropPicture( vout_thread_t *p_vout, picture_t *p_picture );

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

107
    p_picture->i_status = READY_PICTURE;
108
109
110
111
112
113

    return p_picture;
}

static void video_del_buffer_filter( filter_t *p_filter, picture_t *p_pic )
{
114
115
116
    vout_thread_t *p_vout = (vout_thread_t*)p_filter->p_owner;

    DropPicture( p_vout, p_pic );
117
118
119
120
121
122
123
124
}

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;
125
126
}

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

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

153
    /* TODO: find a suitable unused video output */
154
155
156
157

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

161
162
        vlc_mutex_lock( &p_vout->change_lock );

163
        /* We don't directly check for the "vout-filter" variable for obvious
gbazin's avatar
   
gbazin committed
164
         * performance reasons. */
165
        if( p_vout->p->b_filter_change )
gbazin's avatar
   
gbazin committed
166
        {
167
            var_Get( p_vout, "vout-filter", &val );
168
            psz_filter_chain = val.psz_string;
gbazin's avatar
   
gbazin committed
169

170
            if( psz_filter_chain && !*psz_filter_chain )
gbazin's avatar
   
gbazin committed
171
172
173
174
            {
                free( psz_filter_chain );
                psz_filter_chain = NULL;
            }
175
            if( p_vout->p->psz_filter_chain && !*p_vout->p->psz_filter_chain )
gbazin's avatar
   
gbazin committed
176
            {
177
178
                free( p_vout->p->psz_filter_chain );
                p_vout->p->psz_filter_chain = NULL;
gbazin's avatar
   
gbazin committed
179
180
            }

181
            if( !psz_filter_chain && !p_vout->p->psz_filter_chain )
gbazin's avatar
   
gbazin committed
182
            {
183
                p_vout->p->b_filter_change = false;
gbazin's avatar
   
gbazin committed
184
185
            }

186
            free( psz_filter_chain );
gbazin's avatar
   
gbazin committed
187
188
        }

189
190
191
        if( p_vout->fmt_render.i_chroma != p_fmt->i_chroma ||
            p_vout->fmt_render.i_width != p_fmt->i_width ||
            p_vout->fmt_render.i_height != p_fmt->i_height ||
192
            p_vout->p->b_filter_change )
193
        {
194
195
            vlc_mutex_unlock( &p_vout->change_lock );

196
            /* We are not interested in this format, close this vout */
197
            vout_CloseAndRelease( p_vout );
198
            vlc_object_release( p_vout );
199
200
201
202
203
            p_vout = NULL;
        }
        else
        {
            /* This video output is cool! Hijack it. */
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
            if( p_vout->fmt_render.i_aspect != p_fmt->i_aspect )
            {
                /* Correct aspect ratio on change
                 * FIXME factorize this code with other aspect ration related code */
                unsigned int i_sar_num;
                unsigned int i_sar_den;
                unsigned int i_aspect;

                i_aspect = p_fmt->i_aspect;
                vlc_ureduce( &i_sar_num, &i_sar_den,
                             p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 );
#if 0
                /* What's that, it does not seems to be used correcly everywhere
                 * beside the previous p_vout->fmt_render.i_aspect != p_fmt->i_aspect
                 * should be fixed to use it too then */
                if( p_vout->i_par_num > 0 && p_vout->i_par_den > 0 )
                {
                    i_sar_num *= p_vout->i_par_den;
                    i_sar_den *= p_vout->i_par_num;
                    i_aspect = i_aspect * p_vout->i_par_den / p_vout->i_par_num;
                }
#endif

                if( i_sar_num > 0 && i_sar_den > 0 && i_aspect > 0 )
                {
                    p_vout->fmt_in.i_sar_num = i_sar_num;
                    p_vout->fmt_in.i_sar_den = i_sar_den;
                    p_vout->fmt_in.i_aspect  = i_aspect;

                    p_vout->fmt_render.i_sar_num = i_sar_num;
                    p_vout->fmt_render.i_sar_den = i_sar_den;
                    p_vout->fmt_render.i_aspect  = i_aspect;

                    p_vout->render.i_aspect   = i_aspect;

                    p_vout->i_changes |= VOUT_ASPECT_CHANGE;

                }
            }
            vlc_mutex_unlock( &p_vout->change_lock );

            vlc_object_release( p_vout );
        }

        if( p_vout )
        {
            msg_Dbg( p_this, "reusing provided vout" );

252
            spu_Attach( p_vout->p_spu, p_this, true );
253
254

            vlc_object_detach( p_vout );
255
            vlc_object_attach( p_vout, p_this );
256
257
258

            /* Display title if we are not using the vout given to vout_Request.
             * XXX for now b_vout_provided is always true at this stage */
259
            if( p_vout->p->b_title_show && !b_vout_provided )
260
                DisplayTitleOnOSD( p_vout );
261
262
263
264
265
266
267
        }
    }

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

268
        p_vout = vout_Create( p_this, p_fmt );
269
270
271
272
273
274
275
    }

    return p_vout;
}

/*****************************************************************************
 * vout_Create: creates a new video output thread
276
 *****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
277
278
 * This function creates a new video output thread, and returns a pointer
 * to its description. On error, it returns NULL.
279
 *****************************************************************************/
280
vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt )
Michel Kaempf's avatar
Michel Kaempf committed
281
{
gbazin's avatar
   
gbazin committed
282
283
    vout_thread_t  * p_vout;                            /* thread descriptor */
    int              i_index;                               /* loop variable */
284
    vlc_value_t      val, text;
Michel Kaempf's avatar
Michel Kaempf committed
285

286
287
    unsigned int i_width = p_fmt->i_width;
    unsigned int i_height = p_fmt->i_height;
288
289
290
    vlc_fourcc_t i_chroma = p_fmt->i_chroma;
    unsigned int i_aspect = p_fmt->i_aspect;

291
292
293
294
    config_chain_t *p_cfg;
    char *psz_parser;
    char *psz_name;

295
296
297
298
299
300
301
302
    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;

303
    /* Allocate descriptor */
304
305
306
    static const char typename[] = "video output";
    p_vout = vlc_custom_create( p_parent, sizeof( *p_vout ), VLC_OBJECT_VOUT,
                                typename );
307
    if( p_vout == NULL )
308
        return NULL;
309

310
    /* */
Laurent Aimar's avatar
Laurent Aimar committed
311
    p_vout->p = calloc( 1, sizeof(*p_vout->p) );
312
313
314
315
316
317
    if( !p_vout->p )
    {
        vlc_object_release( p_vout );
        return NULL;
    }

318
    /* Initialize pictures - translation tables and functions
gbazin's avatar
   
gbazin committed
319
     * will be initialized later in InitThread */
320
    for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++)
gbazin's avatar
   
gbazin committed
321
322
323
324
325
    {
        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;
326
        p_vout->p_picture[i_index].b_slow   = 0;
gbazin's avatar
   
gbazin committed
327
328
329
330
331
332
333
    }

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

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

335
336
    p_vout->fmt_render        = *p_fmt;   /* FIXME palette */
    p_vout->fmt_in            = *p_fmt;   /* FIXME palette */
337

gbazin's avatar
   
gbazin committed
338
339
340
341
342
    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;

343
344
345
    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
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364

    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->b_scale      = 1;
    p_vout->b_fullscreen = 0;
gbazin's avatar
   
gbazin committed
365
    p_vout->i_alignment  = 0;
366
367
    p_vout->p->render_time  = 10;
    p_vout->p->c_fps_samples = 0;
368
369
    p_vout->p->i_picture_lost = 0;
    p_vout->p->i_picture_displayed = 0;
370
    p_vout->p->b_filter_change = 0;
Laurent Aimar's avatar
Laurent Aimar committed
371
372
    p_vout->p->b_paused = false;
    p_vout->p->i_pause_date = 0;
373
374
    p_vout->pf_control = NULL;
    p_vout->p_window = NULL;
375
376
    p_vout->p->i_par_num =
    p_vout->p->i_par_den = 1;
gbazin's avatar
   
gbazin committed
377
378

    /* Initialize locks */
379
380
    vlc_mutex_init( &p_vout->picture_lock );
    vlc_mutex_init( &p_vout->change_lock );
381
    vlc_mutex_init( &p_vout->p->vfilter_lock );
gbazin's avatar
   
gbazin committed
382

383
384
385
386
387
388
389
    /* 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 );

390
    /* Initialize subpicture unit */
391
    p_vout->p_spu = spu_Create( p_vout );
392
    spu_Attach( p_vout->p_spu, p_parent, true );
393

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

397
398
    spu_Init( p_vout->p_spu );

399
400
    /* Take care of some "interface/control" related initialisations */
    vout_IntfInit( p_vout );
gbazin's avatar
   
gbazin committed
401

gbazin's avatar
   
gbazin committed
402
403
404
    /* 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
405
    {
406
        /* Look for the default filter configuration */
407
        p_vout->p->psz_filter_chain =
408
            var_CreateGetStringCommand( p_vout, "vout-filter" );
409
410

        /* Apply video filter2 objects on the first vout */
411
        p_vout->p->psz_vf2 =
412
            var_CreateGetStringCommand( p_vout, "video-filter" );
gbazin's avatar
   
gbazin committed
413
414
415
416
    }
    else
    {
        /* continue the parent's filter chain */
417
        char *psz_tmp;
gbazin's avatar
   
gbazin committed
418

419
        /* Ugly hack to jump to our configuration chain */
420
421
422
423
        p_vout->p->psz_filter_chain
            = ((vout_thread_t *)p_parent)->p->psz_filter_chain;
        p_vout->p->psz_filter_chain
            = config_ChainCreate( &psz_tmp, &p_cfg, p_vout->p->psz_filter_chain );
424
425
        config_ChainDestroy( p_cfg );
        free( psz_tmp );
426
427

        /* Create a video filter2 var ... but don't inherit values */
428
429
        var_Create( p_vout, "video-filter",
                    VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
430
        p_vout->p->psz_vf2 = var_GetString( p_vout, "video-filter" );
gbazin's avatar
   
gbazin committed
431
432
    }

433
    var_AddCallback( p_vout, "video-filter", VideoFilter2Callback, NULL );
434
    p_vout->p->p_vf2_chain = filter_chain_New( p_vout, "video filter2",
435
        false, video_filter_buffer_allocation_init, NULL, p_vout );
436

gbazin's avatar
   
gbazin committed
437
    /* Choose the video output module */
438
    if( !p_vout->p->psz_filter_chain || !*p_vout->p->psz_filter_chain )
gbazin's avatar
   
gbazin committed
439
    {
gbazin's avatar
   
gbazin committed
440
441
        var_Create( p_vout, "vout", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
        var_Get( p_vout, "vout", &val );
dionoea's avatar
dionoea committed
442
        psz_parser = val.psz_string;
gbazin's avatar
   
gbazin committed
443
444
445
    }
    else
    {
446
447
        psz_parser = strdup( p_vout->p->psz_filter_chain );
        p_vout->p->b_title_show = false;
Sam Hocevar's avatar
   
Sam Hocevar committed
448
449
    }

gbazin's avatar
   
gbazin committed
450
    /* Create the vout thread */
451
    char* psz_tmp = config_ChainCreate( &psz_name, &p_cfg, psz_parser );
dionoea's avatar
dionoea committed
452
    free( psz_parser );
453
    free( psz_tmp );
454
    p_vout->p_cfg = p_cfg;
455
    p_vout->p_module = module_need( p_vout,
456
457
        ( p_vout->p->psz_filter_chain && *p_vout->p->psz_filter_chain ) ?
        "video filter" : "video output", psz_name, p_vout->p->psz_filter_chain && *p_vout->p->psz_filter_chain );
458
    free( psz_name );
Sam Hocevar's avatar
   
Sam Hocevar committed
459
460
461

    if( p_vout->p_module == NULL )
    {
462
        msg_Err( p_vout, "no suitable vout module" );
463
464
        // FIXME it's ugly but that's exactly the function that need to be called.
        EndThread( p_vout );
465
        vlc_object_detach( p_vout );
466
        vlc_object_release( p_vout );
467
        return NULL;
Sam Hocevar's avatar
   
Sam Hocevar committed
468
    }
gbazin's avatar
   
gbazin committed
469

gbazin's avatar
   
gbazin committed
470
    /* Create a few object variables for interface interaction */
gbazin's avatar
   
gbazin committed
471
472
473
    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 );
474
    val.psz_string = (char *)""; text.psz_string = _("Disable");
gbazin's avatar
   
gbazin committed
475
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
476
    val.psz_string = (char *)"discard"; text.psz_string = _("Discard");
gbazin's avatar
   
gbazin committed
477
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
478
    val.psz_string = (char *)"blend"; text.psz_string = _("Blend");
gbazin's avatar
   
gbazin committed
479
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
480
    val.psz_string = (char *)"mean"; text.psz_string = _("Mean");
gbazin's avatar
   
gbazin committed
481
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
482
    val.psz_string = (char *)"bob"; text.psz_string = _("Bob");
gbazin's avatar
   
gbazin committed
483
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
484
    val.psz_string = (char *)"linear"; text.psz_string = _("Linear");
gbazin's avatar
   
gbazin committed
485
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
486
    val.psz_string = (char *)"x"; text.psz_string = (char *)"X";
487
488
    var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );

gbazin's avatar
   
gbazin committed
489
490
491
    if( var_Get( p_vout, "deinterlace-mode", &val ) == VLC_SUCCESS )
    {
        var_Set( p_vout, "deinterlace", val );
492
        free( val.psz_string );
gbazin's avatar
   
gbazin committed
493
    }
gbazin's avatar
   
gbazin committed
494
495
    var_AddCallback( p_vout, "deinterlace", DeinterlaceCallback, NULL );

496
    var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
497
    text.psz_string = _("Filters");
498
499
    var_Change( p_vout, "vout-filter", VLC_VAR_SETTEXT, &text, NULL );
    var_AddCallback( p_vout, "vout-filter", FilterCallback, NULL );
500

501
    if( vlc_thread_create( p_vout, "video output", RunThread,
502
                           VLC_THREAD_PRIORITY_OUTPUT, true ) )
Michel Kaempf's avatar
Michel Kaempf committed
503
    {
504
        module_unneed( p_vout, p_vout->p_module );
505
        vlc_object_release( p_vout );
506
507
508
        return NULL;
    }

509
510
    vlc_object_set_destructor( p_vout, vout_Destructor );

511
512
513
    if( p_vout->b_error )
    {
        msg_Err( p_vout, "video output creation failed" );
514
        vout_CloseAndRelease( p_vout );
515
        return NULL;
516
    }
Michel Kaempf's avatar
Michel Kaempf committed
517

518
    return p_vout;
Michel Kaempf's avatar
Michel Kaempf committed
519
520
}

521
/*****************************************************************************
522
 * vout_Close: Close a vout created by vout_Create.
523
 *****************************************************************************
524
 * 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
525
 * You should NEVER call it on vout not obtained through vout_Create
526
527
 * (like with vout_Request or vlc_object_find.)
 * You can use vout_CloseAndRelease() as a convenient method.
528
 *****************************************************************************/
529
void vout_Close( vout_thread_t *p_vout )
530
531
532
533
534
{
    assert( p_vout );

    vlc_object_kill( p_vout );
    vlc_thread_join( p_vout );
535
    module_unneed( p_vout, p_vout->p_module );
536
    p_vout->p_module = NULL;
537
538
539
}

/* */
540
541
static void vout_Destructor( vlc_object_t * p_this )
{
542
    vout_thread_t *p_vout = (vout_thread_t *)p_this;
543

544
545
546
    /* Make sure the vout was stopped first */
    assert( !p_vout->p_module );

547
548
549
    /* Destroy the locks */
    vlc_mutex_destroy( &p_vout->picture_lock );
    vlc_mutex_destroy( &p_vout->change_lock );
550
    vlc_mutex_destroy( &p_vout->p->vfilter_lock );
551

552
    free( p_vout->p->psz_filter_chain );
gbazin's avatar
   
gbazin committed
553

554
555
    config_ChainDestroy( p_vout->p_cfg );

556
557
    free( p_vout->p );

558
#ifndef __APPLE__
559
    vout_thread_t *p_another_vout;
560

561
562
563
564
565
566
567
568
    /* 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 )
569
        var_SetBool( p_this->p_libvlc, "intf-show", true );
570
571
    else
        vlc_object_release( p_another_vout );
572
#endif
Michel Kaempf's avatar
Michel Kaempf committed
573
574
}

Laurent Aimar's avatar
Laurent Aimar committed
575
576
577
578
579
/* */
void vout_ChangePause( vout_thread_t *p_vout, bool b_paused, mtime_t i_date )
{
    vlc_object_lock( p_vout );

580
    assert( !p_vout->p->b_paused || !b_paused );
Laurent Aimar's avatar
Laurent Aimar committed
581
582
583
584
585
586
587
588
589
590
591
    if( p_vout->p->b_paused )
    {
        const mtime_t i_duration = i_date - p_vout->p->i_pause_date;

        for( int i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
        {
            picture_t *p_pic = PP_RENDERPICTURE[i_index];

            if( p_pic->i_status == READY_PICTURE )
                p_pic->date += i_duration;
        }
Laurent Aimar's avatar
Laurent Aimar committed
592
        spu_OffsetSubtitleDate( p_vout->p_spu, i_duration );
Laurent Aimar's avatar
Laurent Aimar committed
593
594
595
596
597
598
    }
    p_vout->p->b_paused = b_paused;
    p_vout->p->i_pause_date = i_date;

    vlc_object_unlock( p_vout );
}
599
600
601
602
603
604
605
606
607
608
609
610
void vout_GetResetStatistic( vout_thread_t *p_vout, int *pi_displayed, int *pi_lost )
{
    vlc_object_lock( p_vout );

    *pi_displayed = p_vout->p->i_picture_displayed;
    *pi_lost = p_vout->p->i_picture_lost;

    p_vout->p->i_picture_displayed = 0;
    p_vout->p->i_picture_lost = 0;

    vlc_object_unlock( p_vout );
}
Laurent Aimar's avatar
Laurent Aimar committed
611

612
/*****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
613
 * InitThread: initialize video output thread
614
 *****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
615
616
617
 * 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.
618
 * XXX You have to enter it with change_lock taken.
619
 *****************************************************************************/
620
621
static int ChromaCreate( vout_thread_t *p_vout );
static void ChromaDestroy( vout_thread_t *p_vout );
622

623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
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
639
static int InitThread( vout_thread_t *p_vout )
Vincent Seguin's avatar
Vincent Seguin committed
640
{
641
    int i, i_aspect_x, i_aspect_y;
Sam Hocevar's avatar
   
Sam Hocevar committed
642

Sam Hocevar's avatar
   
Sam Hocevar committed
643
644
#ifdef STATS
    p_vout->c_loops = 0;
Vincent Seguin's avatar
Vincent Seguin committed
645
646
#endif

Sam Hocevar's avatar
   
Sam Hocevar committed
647
    /* Initialize output method, it allocates direct buffers for us */
Sam Hocevar's avatar
   
Sam Hocevar committed
648
    if( p_vout->pf_init( p_vout ) )
649
        return VLC_EGENERIC;
Sam Hocevar's avatar
   
Sam Hocevar committed
650

Sam Hocevar's avatar
   
Sam Hocevar committed
651
    if( !I_OUTPUTPICTURES )
Sam Hocevar's avatar
   
Sam Hocevar committed
652
    {
653
654
        msg_Err( p_vout, "plugin was unable to allocate at least "
                         "one direct buffer" );
Sam Hocevar's avatar
   
Sam Hocevar committed
655
        p_vout->pf_end( p_vout );
656
        return VLC_EGENERIC;
657
    }
Vincent Seguin's avatar
Vincent Seguin committed
658

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

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

669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
    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
712
713
    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 );
714
715

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

717
718
    msg_Dbg( p_vout, "picture out %ix%i (%i,%i,%ix%i), "
             "chroma %4.4s, ar %i:%i, sar %i:%i",
719
             p_vout->fmt_out.i_width, p_vout->fmt_out.i_height,
720
721
722
723
724
             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,
725
             p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den );
Sam Hocevar's avatar
   
Sam Hocevar committed
726

727
    /* FIXME removed the need of both fmt_* and heap infos */
Sam Hocevar's avatar
   
Sam Hocevar committed
728
    /* Calculate shifts from system-updated masks */
729
730
731
732
733
    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
734

Sam Hocevar's avatar
   
Sam Hocevar committed
735
    /* Check whether we managed to create direct buffers similar to
736
     * the render buffers, ie same size and chroma */
Sam Hocevar's avatar
   
Sam Hocevar committed
737
738
    if( ( p_vout->output.i_width == p_vout->render.i_width )
     && ( p_vout->output.i_height == p_vout->render.i_height )
739
     && ( ChromaIsEqual( &p_vout->output, &p_vout->render ) ) )
Sam Hocevar's avatar
   
Sam Hocevar committed
740
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
741
742
743
744
        /* 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 */
745
        p_vout->p->b_direct = true;
Sam Hocevar's avatar
   
Sam Hocevar committed
746

Sam Hocevar's avatar
   
Sam Hocevar committed
747
748
        for( i = 1; i < VOUT_MAX_PICTURES; i++ )
        {
gbazin's avatar
   
gbazin committed
749
750
751
752
753
754
755
756
            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
757
758
759
            PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
            I_RENDERPICTURES++;
        }
gbazin's avatar
   
gbazin committed
760
761
762
763

        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
764
765
766
    }
    else
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
767
768
769
        /* 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 */
770
        p_vout->p->b_direct = false;
Sam Hocevar's avatar
   
Sam Hocevar committed
771

772
        if( ChromaCreate( p_vout ) )
Sam Hocevar's avatar
   
Sam Hocevar committed
773
774
        {
            p_vout->pf_end( p_vout );
775
            return VLC_EGENERIC;
Sam Hocevar's avatar
   
Sam Hocevar committed
776
        }
Sam Hocevar's avatar
   
Sam Hocevar committed
777

gbazin's avatar
   
gbazin committed
778
779
780
781
        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
782
783

        /* Append render buffers after the direct buffers */
Sam Hocevar's avatar
   
Sam Hocevar committed
784
        for( i = I_OUTPUTPICTURES; i < 2 * VOUT_MAX_PICTURES; i++ )
Sam Hocevar's avatar
   
Sam Hocevar committed
785
786
787
        {
            PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
            I_RENDERPICTURES++;
gbazin's avatar
   
gbazin committed
788
789
790
791

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

Sam Hocevar's avatar
   
Sam Hocevar committed
795
796
797
798
799
800
801
802
803
804
805
    /* 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;
    }

806
    return VLC_SUCCESS;
Vincent Seguin's avatar
Vincent Seguin committed
807
808
}

809
/*****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
810
 * RunThread: video output thread
811
 *****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
812
813
814
 * 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.
815
 *****************************************************************************/
816
static void* RunThread( vlc_object_t *p_this )
Vincent Seguin's avatar
Vincent Seguin committed
817
{
818
    vout_thread_t *p_vout = (vout_thread_t *)p_this;
819
    int             i_idle_loops = 0;  /* loops without displaying a picture */
Sam Hocevar's avatar
   
Sam Hocevar committed
820

821
    picture_t *     p_last_picture = NULL;                   /* last picture */
Sam Hocevar's avatar
   
Sam Hocevar committed
822

823
    subpicture_t *  p_subpic = NULL;                   /* subpicture pointer */
Sam Hocevar's avatar
   
Sam Hocevar committed
824

Laurent Aimar's avatar
Laurent Aimar committed
825
    bool            b_drop_late;
826

827
    int canc = vlc_savecancel ();
828

Sam Hocevar's avatar
   
Sam Hocevar committed
829
830
831
    /*
     * Initialize thread
     */
832
    vlc_mutex_lock( &p_vout->change_lock );
Sam Hocevar's avatar
   
Sam Hocevar committed
833
    p_vout->b_error = InitThread( p_vout );
gbazin's avatar
   
gbazin committed
834

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

gbazin's avatar
   
gbazin committed
837
838
839
    /* signal the creation of the vout */
    vlc_thread_ready( p_vout );

Sam Hocevar's avatar
   
Sam Hocevar committed
840
    if( p_vout->b_error )
841
    {
Laurent Aimar's avatar
Laurent Aimar committed
842
        EndThread( p_vout );
843
        vlc_mutex_unlock( &p_vout->change_lock );
844
        vlc_restorecancel (canc);
845
        return NULL;
846
    }
847

848
849
    vlc_object_lock( p_vout );

850
    if( p_vout->p->b_title_show )
851
        DisplayTitleOnOSD( p_vout );
852

853
    /*
Sam Hocevar's avatar
Sam Hocevar committed
854
     * Main loop - it is not executed if an error occurred during
Sam Hocevar's avatar
   
Sam Hocevar committed
855
     * initialization
856
     */
Laurent Aimar's avatar
Laurent Aimar committed
857
    while( vlc_object_alive( p_vout ) && !p_vout->b_error )
858
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
859
        /* Initialize loop variables */
Laurent Aimar's avatar
Laurent Aimar committed
860
861
        const mtime_t current_date = mdate();
        picture_t *p_picture = NULL;
862
        picture_t *p_filtered_picture;
Laurent Aimar's avatar
Laurent Aimar committed
863
864
865
        mtime_t display_date = 0;
        picture_t *p_directbuffer;
        int i_index;
Sam Hocevar's avatar
   
Sam Hocevar committed
866

Laurent Aimar's avatar
Laurent Aimar committed
867
868
869
#if 0
        p_vout->c_loops++;
        if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
Laurent Aimar's avatar
Laurent Aimar committed
870
        {
Laurent Aimar's avatar
Laurent Aimar committed
871
872
            msg_Dbg( p_vout, "picture heap: %d/%d",
                     I_RENDERPICTURES, p_vout->i_heap_size );
Laurent Aimar's avatar
Laurent Aimar committed
873
        }
Laurent Aimar's avatar
Laurent Aimar committed
874
875
#endif

Sam Hocevar's avatar
   
Sam Hocevar committed
876
        /*
877
878
879
         * Find the picture to display (the one with the earliest date).
         * This operation does not need lock, since only READY_PICTUREs
         * are handled. */
880
        for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
Sam Hocevar's avatar
   
Sam Hocevar committed
881
        {
Laurent Aimar's avatar
Laurent Aimar committed
882
883
884
885
            picture_t *p_pic = PP_RENDERPICTURE[i_index];

            if( p_pic->i_status == READY_PICTURE &&
                ( p_picture == NULL || p_pic->date < display_date ) )
886
            {
Laurent Aimar's avatar
Laurent Aimar committed
887
                p_picture = p_pic;
Sam Hocevar's avatar
   
Sam Hocevar committed
888
                display_date = p_picture->date;
889
890
            }
        }
Laurent Aimar's avatar
Laurent Aimar committed
891
892
        if( p_vout->p->b_paused && p_last_picture != NULL )
            p_picture = p_last_picture;
Sam Hocevar's avatar
   
Sam Hocevar committed
893

894
        if( p_picture )
895
        {
896
897
            /* If we met the last picture, parse again to see whether there is
             * a more appropriate one. */