video_output.c 55.8 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 "vout_pictures.h"
Laurent Aimar's avatar
Laurent Aimar committed
63
#include "vout_internal.h"
64

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

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

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

79
80
static void     vout_Destructor   ( vlc_object_t * p_this );

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

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

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

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

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

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

    return p_picture;
}

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

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

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

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

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

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

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

160
161
        vlc_mutex_lock( &p_vout->change_lock );

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

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

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

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

188
189
190
        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 ||
191
            p_vout->p->b_filter_change )
192
        {
193
194
            vlc_mutex_unlock( &p_vout->change_lock );

195
            /* We are not interested in this format, close this vout */
196
            vout_CloseAndRelease( p_vout );
197
            vlc_object_release( p_vout );
198
199
200
201
202
            p_vout = NULL;
        }
        else
        {
            /* This video output is cool! Hijack it. */
203
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
            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" );

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

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

            /* 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 */
258
            if( p_vout->p->b_title_show && !b_vout_provided )
259
                DisplayTitleOnOSD( p_vout );
260
261
262
263
264
265
266
        }
    }

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

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

    return p_vout;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

396
397
    spu_Init( p_vout->p_spu );

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

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

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

418
        /* Ugly hack to jump to our configuration chain */
419
420
421
422
        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 );
423
424
        config_ChainDestroy( p_cfg );
        free( psz_tmp );
425
426

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

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

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

gbazin's avatar
   
gbazin committed
449
    /* Create the vout thread */
450
    char* psz_tmp = config_ChainCreate( &psz_name, &p_cfg, psz_parser );
dionoea's avatar
dionoea committed
451
    free( psz_parser );
452
    free( psz_tmp );
453
    p_vout->p_cfg = p_cfg;
454
    p_vout->p_module = module_need( p_vout,
455
456
        ( 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 );
457
    free( psz_name );
Sam Hocevar's avatar
   
Sam Hocevar committed
458
459
460

    if( p_vout->p_module == NULL )
    {
461
        msg_Err( p_vout, "no suitable vout module" );
Laurent Aimar's avatar
Laurent Aimar committed
462
        vlc_object_set_destructor( p_vout, vout_Destructor );
463
        vlc_object_release( p_vout );
464
        return NULL;
Sam Hocevar's avatar
   
Sam Hocevar committed
465
    }
gbazin's avatar
   
gbazin committed
466

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

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

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

498
    if( vlc_thread_create( p_vout, "video output", RunThread,
499
                           VLC_THREAD_PRIORITY_OUTPUT, true ) )
Michel Kaempf's avatar
Michel Kaempf committed
500
    {
501
        module_unneed( p_vout, p_vout->p_module );
Laurent Aimar's avatar
Laurent Aimar committed
502
503
        p_vout->p_module = NULL;
        vlc_object_set_destructor( p_vout, vout_Destructor );
504
        vlc_object_release( p_vout );
505
506
507
        return NULL;
    }

508
509
    vlc_object_set_destructor( p_vout, vout_Destructor );

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

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

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

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

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

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

Laurent Aimar's avatar
Laurent Aimar committed
546
547
548
    /* */
    spu_Destroy( p_vout->p_spu );

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

554
    free( p_vout->p->psz_filter_chain );
gbazin's avatar
   
gbazin committed
555

556
557
    config_ChainDestroy( p_vout->p_cfg );

558
559
    free( p_vout->p );

560
#ifndef __APPLE__
561
    vout_thread_t *p_another_vout;
562

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

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

582
    assert( !p_vout->p->b_paused || !b_paused );
Laurent Aimar's avatar
Laurent Aimar committed
583
584
585
586
587
588
589
590
591
592
593
    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
594
        spu_OffsetSubtitleDate( p_vout->p_spu, i_duration );
Laurent Aimar's avatar
Laurent Aimar committed
595
596
597
598
599
600
    }
    p_vout->p->b_paused = b_paused;
    p_vout->p->i_pause_date = i_date;

    vlc_object_unlock( p_vout );
}
601
602
603
604
605
606
607
608
609
610
611
612
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
613
614
615
616
617
618
void vout_Flush( vout_thread_t *p_vout, mtime_t i_date )
{
    vlc_mutex_lock( &p_vout->picture_lock );
    for( int i = 0; i < p_vout->render.i_pictures; i++ )
    {
        picture_t *p_pic = p_vout->render.pp_picture[i];
Laurent Aimar's avatar
Laurent Aimar committed
619

Laurent Aimar's avatar
Laurent Aimar committed
620
621
622
623
624
625
626
627
628
629
630
        if( p_pic->i_status == READY_PICTURE ||
            p_pic->i_status == DISPLAYED_PICTURE )
        {
            /* We cannot change picture status if it is in READY_PICTURE state,
             * Just make sure they won't be displayed */
            if( p_pic->date > i_date )
                p_pic->date = i_date;
        }
    }
    vlc_mutex_unlock( &p_vout->picture_lock );
}
631
/*****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
632
 * InitThread: initialize video output thread
633
 *****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
634
635
636
 * 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.
637
 * XXX You have to enter it with change_lock taken.
638
 *****************************************************************************/
639
640
static int ChromaCreate( vout_thread_t *p_vout );
static void ChromaDestroy( vout_thread_t *p_vout );
641

642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
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
658
static int InitThread( vout_thread_t *p_vout )
Vincent Seguin's avatar
Vincent Seguin committed
659
{
660
    int i, i_aspect_x, i_aspect_y;
Sam Hocevar's avatar
   
Sam Hocevar committed
661

Sam Hocevar's avatar
   
Sam Hocevar committed
662
663
#ifdef STATS
    p_vout->c_loops = 0;
Vincent Seguin's avatar
Vincent Seguin committed
664
665
#endif

Sam Hocevar's avatar
   
Sam Hocevar committed
666
    /* Initialize output method, it allocates direct buffers for us */
Sam Hocevar's avatar
   
Sam Hocevar committed
667
    if( p_vout->pf_init( p_vout ) )
668
        return VLC_EGENERIC;
Sam Hocevar's avatar
   
Sam Hocevar committed
669

Sam Hocevar's avatar
   
Sam Hocevar committed
670
    if( !I_OUTPUTPICTURES )
Sam Hocevar's avatar
   
Sam Hocevar committed
671
    {
672
673
        msg_Err( p_vout, "plugin was unable to allocate at least "
                         "one direct buffer" );
Sam Hocevar's avatar
   
Sam Hocevar committed
674
        p_vout->pf_end( p_vout );
675
        return VLC_EGENERIC;
676
    }
Vincent Seguin's avatar
Vincent Seguin committed
677

gbazin's avatar
   
gbazin committed
678
679
680
681
682
683
684
685
    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;
    }

686
    msg_Dbg( p_vout, "got %i direct buffer(s)", I_OUTPUTPICTURES );
Sam Hocevar's avatar
   
Sam Hocevar committed
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
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
    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
731
732
    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 );
733
734

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

736
737
    msg_Dbg( p_vout, "picture out %ix%i (%i,%i,%ix%i), "
             "chroma %4.4s, ar %i:%i, sar %i:%i",
738
             p_vout->fmt_out.i_width, p_vout->fmt_out.i_height,
739
740
741
742
743
             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,
744
             p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den );
Sam Hocevar's avatar
   
Sam Hocevar committed
745

746
    /* FIXME removed the need of both fmt_* and heap infos */
Sam Hocevar's avatar
   
Sam Hocevar committed
747
    /* Calculate shifts from system-updated masks */
748
749
750
751
752
    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
753

Sam Hocevar's avatar
   
Sam Hocevar committed
754
    /* Check whether we managed to create direct buffers similar to
755
     * the render buffers, ie same size and chroma */
Sam Hocevar's avatar
   
Sam Hocevar committed
756
757
    if( ( p_vout->output.i_width == p_vout->render.i_width )
     && ( p_vout->output.i_height == p_vout->render.i_height )
758
     && ( ChromaIsEqual( &p_vout->output, &p_vout->render ) ) )
Sam Hocevar's avatar
   
Sam Hocevar committed
759
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
760
761
762
763
        /* 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 */
764
        p_vout->p->b_direct = true;
Sam Hocevar's avatar
   
Sam Hocevar committed
765

Sam Hocevar's avatar
   
Sam Hocevar committed
766
767
        for( i = 1; i < VOUT_MAX_PICTURES; i++ )
        {
gbazin's avatar
   
gbazin committed
768
769
770
771
772
773
774
775
            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
776
777
778
            PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
            I_RENDERPICTURES++;
        }
gbazin's avatar
   
gbazin committed
779
780
781
782

        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
783
784
785
    }
    else
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
786
787
788
        /* 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 */
789
        p_vout->p->b_direct = false;
Sam Hocevar's avatar
   
Sam Hocevar committed
790

791
        if( ChromaCreate( p_vout ) )
Sam Hocevar's avatar
   
Sam Hocevar committed
792
793
        {
            p_vout->pf_end( p_vout );
794
            return VLC_EGENERIC;
Sam Hocevar's avatar
   
Sam Hocevar committed
795
        }
Sam Hocevar's avatar
   
Sam Hocevar committed
796

gbazin's avatar
   
gbazin committed
797
798
799
800
        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
801
802

        /* Append render buffers after the direct buffers */
Sam Hocevar's avatar
   
Sam Hocevar committed
803
        for( i = I_OUTPUTPICTURES; i < 2 * VOUT_MAX_PICTURES; i++ )
Sam Hocevar's avatar
   
Sam Hocevar committed
804
805
806
        {
            PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
            I_RENDERPICTURES++;
gbazin's avatar
   
gbazin committed
807
808
809
810

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

Sam Hocevar's avatar
   
Sam Hocevar committed
814
815
816
817
818
819
820
821
822
823
824
    /* 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;
    }

825
    return VLC_SUCCESS;
Vincent Seguin's avatar
Vincent Seguin committed
826
827
}

828
/*****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
829
 * RunThread: video output thread
830
 *****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
831
832
833
 * 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.
834
 *****************************************************************************/
835
static void* RunThread( vlc_object_t *p_this )
Vincent Seguin's avatar
Vincent Seguin committed
836
{
837
    vout_thread_t *p_vout = (vout_thread_t *)p_this;
838
    int             i_idle_loops = 0;  /* loops without displaying a picture */
Sam Hocevar's avatar
   
Sam Hocevar committed
839

840
    picture_t *     p_last_picture = NULL;                   /* last picture */
Sam Hocevar's avatar
   
Sam Hocevar committed
841

842
    subpicture_t *  p_subpic = NULL;                   /* subpicture pointer */
Sam Hocevar's avatar
   
Sam Hocevar committed
843

Laurent Aimar's avatar
Laurent Aimar committed
844
    bool            b_drop_late;
845

846
    int canc = vlc_savecancel ();
847

Sam Hocevar's avatar
   
Sam Hocevar committed
848
849
850
    /*
     * Initialize thread
     */
851
    vlc_mutex_lock( &p_vout->change_lock );
Sam Hocevar's avatar
   
Sam Hocevar committed
852
    p_vout->b_error = InitThread( p_vout );
gbazin's avatar
   
gbazin committed
853

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

gbazin's avatar
   
gbazin committed
856
857
858
    /* signal the creation of the vout */
    vlc_thread_ready( p_vout );

Sam Hocevar's avatar
   
Sam Hocevar committed
859
    if( p_vout->b_error )
860
    {
Laurent Aimar's avatar
Laurent Aimar committed
861
        EndThread( p_vout );
862
        vlc_mutex_unlock( &p_vout->change_lock );
863
        vlc_restorecancel (canc);
864
        return NULL;
865
    }
866

867
868
    vlc_object_lock( p_vout );

869
    if( p_vout->p->b_title_show )
870
        DisplayTitleOnOSD( p_vout );
871

872
    /*
Sam Hocevar's avatar
Sam Hocevar committed
873
     * Main loop - it is not executed if an error occurred during
Sam Hocevar's avatar
   
Sam Hocevar committed
874
     * initialization
875
     */
Laurent Aimar's avatar
Laurent Aimar committed
876
    while( vlc_object_alive( p_vout ) && !p_vout->b_error )
877
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
878
        /* Initialize loop variables */
Laurent Aimar's avatar
Laurent Aimar committed
879
880
        const mtime_t current_date = mdate();
        picture_t *p_picture = NULL;
881
        picture_t *p_filtered_picture;
Laurent Aimar's avatar
Laurent Aimar committed
882
883
884
        mtime_t display_date = 0;
        picture_t *p_directbuffer;
        int i_index;
Sam Hocevar's avatar
   
Sam Hocevar committed
885

Laurent Aimar's avatar
Laurent Aimar committed
886
887
888
#if 0
        p_vout->c_loops++;
        if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
Laurent Aimar's avatar
Laurent Aimar committed
889
        {
Laurent Aimar's avatar
Laurent Aimar committed
890
891
            msg_Dbg( p_vout, "picture heap: %d/%d",
                     I_RENDERPICTURES, p_vout->i_heap_size );
Laurent Aimar's avatar
Laurent Aimar committed
892
        }
Laurent Aimar's avatar
Laurent Aimar committed
893
894
#endif

Sam Hocevar's avatar
   
Sam Hocevar committed
895
        /*
896
897
898
         * Find the picture to display (the one with the earliest date).
         * This operation does not need lock, since only READY_PICTUREs
         * are handled. */
899
        for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
Sam Hocevar's avatar
   
Sam Hocevar committed
900
        {
Laurent Aimar's avatar
Laurent Aimar committed
901
902
903
904
            picture_t *p_pic = PP_RENDERPICTURE[i_index];

            if( p_pic->i_status == READY_PICTURE &&
                ( p_picture == NULL || p_pic->date < display_date ) )
905
            {
Laurent Aimar's avatar
Laurent Aimar committed
906
                p_picture = p_pic;
Sam Hocevar's avatar
   
Sam Hocevar committed
907
                display_date = p_picture->date;
908
909
            }
        }
Laurent Aimar's avatar
Laurent Aimar committed
910
911
        if( p_vout->p->b_paused && p_last_picture != NULL )
            p_picture = p_last_picture;
Sam Hocevar's avatar
   
Sam Hocevar committed
912