video_output.c 37.9 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
Gildas Bazin's avatar
Gildas Bazin 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
 *          Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
14 15 16 17 18
 *
 * 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
19
 *
20 21
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
24
 *
25 26
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Antoine Cellerier's avatar
Antoine Cellerier committed
27
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
28
 *****************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
29

30
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
31
 * Preamble
32
 *****************************************************************************/
33 34 35 36
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

37
#include <vlc_common.h>
38

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

Clément Stenac's avatar
Clément Stenac committed
43
#include <vlc_vout.h>
44

Clément Stenac's avatar
Clément Stenac committed
45
#include <vlc_filter.h>
46
#include <vlc_vout_osd.h>
Clément Stenac's avatar
Clément Stenac committed
47

48 49
#include <libvlc.h>
#include <vlc_input.h>
Laurent Aimar's avatar
Laurent Aimar committed
50
#include "vout_internal.h"
51 52
#include "interlacing.h"
#include "postprocessing.h"
53
#include "display.h"
54

55
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
56
 * Local prototypes
57
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
58 59
static void     *Thread(void *);
static void     vout_Destructor(vlc_object_t *);
60

Gildas Bazin's avatar
 
Gildas Bazin committed
61
/* Object variables callbacks */
62 63
static int FilterCallback( vlc_object_t *, char const *,
                           vlc_value_t, vlc_value_t, void * );
64 65
static int VideoFilter2Callback( vlc_object_t *, char const *,
                                 vlc_value_t, vlc_value_t, void * );
Laurent Aimar's avatar
Laurent Aimar committed
66

Laurent Aimar's avatar
Laurent Aimar committed
67 68 69 70
/* Maximum delay between 2 displayed pictures.
 * XXX it is needed for now but should be removed in the long term.
 */
#define VOUT_REDISPLAY_DELAY (INT64_C(80000))
71

Laurent Aimar's avatar
Laurent Aimar committed
72 73 74 75
/**
 * Late pictures having a delay higher than this value are thrashed.
 */
#define VOUT_DISPLAY_LATE_THRESHOLD (INT64_C(20000))
76 77

/* Better be in advance when awakening than late... */
Laurent Aimar's avatar
Laurent Aimar committed
78
#define VOUT_MWAIT_TOLERANCE (INT64_C(1000))
79

80 81 82 83 84
/*****************************************************************************
 * Video Filter2 functions
 *****************************************************************************/
static picture_t *video_new_buffer_filter( filter_t *p_filter )
{
85
    vout_thread_t *p_vout = (vout_thread_t*)p_filter->p_owner;
86
    return picture_pool_Get(p_vout->p->private_pool);
87 88 89 90
}

static void video_del_buffer_filter( filter_t *p_filter, picture_t *p_pic )
{
Laurent Aimar's avatar
Laurent Aimar committed
91
    VLC_UNUSED(p_filter);
92
    picture_Release(p_pic);
93 94 95 96
}

static int video_filter_buffer_allocation_init( filter_t *p_filter, void *p_data )
{
97 98
    p_filter->pf_video_buffer_new = video_new_buffer_filter;
    p_filter->pf_video_buffer_del = video_del_buffer_filter;
99 100
    p_filter->p_owner = p_data; /* p_vout */
    return VLC_SUCCESS;
101 102
}

103
#undef vout_Request
104
/*****************************************************************************
105 106 107 108 109
 * 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.
 *****************************************************************************/
110 111
vout_thread_t *vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout,
                             video_format_t *p_fmt )
112
{
113
    if( !p_fmt )
114
    {
115 116 117
        /* Video output is no longer used.
         * TODO: support for reusing video outputs with proper _thread-safe_
         * reference handling. */
118
        if( p_vout )
119
            vout_CloseAndRelease( p_vout );
120 121 122 123 124 125
        return NULL;
    }

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

129
    /* TODO: find a suitable unused video output */
130 131 132 133

    /* If we now have a video output, check it has the right properties */
    if( p_vout )
    {
Laurent Aimar's avatar
Laurent Aimar committed
134
        if( !video_format_IsSimilar( &p_vout->p->original, p_fmt ) )
135 136
        {
            /* We are not interested in this format, close this vout */
137
            vout_CloseAndRelease( p_vout );
138
            vlc_object_release( p_vout );
139 140 141 142 143
            p_vout = NULL;
        }
        else
        {
            /* This video output is cool! Hijack it. */
144 145 146 147 148 149 150
            vlc_object_release( p_vout );
        }

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

151
            spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), false );
152
            vlc_object_detach( p_vout );
153

154
            vlc_object_attach( p_vout, p_this );
155
            spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), true );
156 157 158 159 160 161 162
        }
    }

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

163
        p_vout = vout_Create( p_this, p_fmt );
164 165 166 167 168 169 170
    }

    return p_vout;
}

/*****************************************************************************
 * vout_Create: creates a new video output thread
171
 *****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
172 173
 * This function creates a new video output thread, and returns a pointer
 * to its description. On error, it returns NULL.
174
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
175
vout_thread_t * (vout_Create)( vlc_object_t *p_parent, video_format_t *p_fmt )
Michel Kaempf's avatar
Michel Kaempf committed
176
{
177 178
    vout_thread_t  *p_vout;                            /* thread descriptor */
    vlc_value_t     text;
Michel Kaempf's avatar
Michel Kaempf committed
179

180

181 182 183 184
    config_chain_t *p_cfg;
    char *psz_parser;
    char *psz_name;

185
    if( p_fmt->i_width <= 0 || p_fmt->i_height <= 0 )
186
        return NULL;
187
    const vlc_fourcc_t i_chroma = vlc_fourcc_GetCodec( VIDEO_ES, p_fmt->i_chroma );
188 189 190 191 192 193

    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;

194
    /* Allocate descriptor */
195 196 197
    static const char typename[] = "video output";
    p_vout = vlc_custom_create( p_parent, sizeof( *p_vout ), VLC_OBJECT_VOUT,
                                typename );
198
    if( p_vout == NULL )
199
        return NULL;
200

201
    /* */
Laurent Aimar's avatar
Laurent Aimar committed
202
    p_vout->p = calloc( 1, sizeof(*p_vout->p) );
203 204 205 206 207 208
    if( !p_vout->p )
    {
        vlc_object_release( p_vout );
        return NULL;
    }

209
    /* */
210 211 212
    p_vout->p->original = *p_fmt;   /* FIXME palette */
    p_vout->p->original.i_chroma = i_chroma;
    video_format_FixRgb( &p_vout->p->original );
213

Gildas Bazin's avatar
 
Gildas Bazin committed
214
    /* Initialize misc stuff */
215
    vout_control_Init( &p_vout->p->control );
Laurent Aimar's avatar
Laurent Aimar committed
216
    vout_control_PushVoid( &p_vout->p->control, VOUT_CONTROL_INIT );
217
    vout_chrono_Init( &p_vout->p->render, 5, 10000 ); /* Arbitrary initial time */
218
    vout_statistic_Init( &p_vout->p->statistic );
219 220
    p_vout->p->i_par_num =
    p_vout->p->i_par_den = 1;
Laurent Aimar's avatar
Laurent Aimar committed
221
    p_vout->p->displayed.date = VLC_TS_INVALID;
222
    p_vout->p->displayed.decoded = NULL;
Laurent Aimar's avatar
Laurent Aimar committed
223
    p_vout->p->displayed.timestamp = VLC_TS_INVALID;
224 225
    p_vout->p->displayed.qtype = QTYPE_NONE;
    p_vout->p->displayed.is_interlaced = false;
Laurent Aimar's avatar
Laurent Aimar committed
226 227 228 229 230 231

    p_vout->p->step.last         = VLC_TS_INVALID;
    p_vout->p->step.timestamp    = VLC_TS_INVALID;

    p_vout->p->pause.is_on  = false;
    p_vout->p->pause.date   = VLC_TS_INVALID;
232 233 234

    p_vout->p->decoder_fifo = picture_fifo_New();
    p_vout->p->decoder_pool = NULL;
Gildas Bazin's avatar
 
Gildas Bazin committed
235

236 237
    vlc_mouse_Init( &p_vout->p->mouse );

238
    vout_snapshot_Init( &p_vout->p->snapshot );
239

Laurent Aimar's avatar
Laurent Aimar committed
240 241 242
    p_vout->p->vfilter_chain =
        filter_chain_New( p_vout, "video filter2", false,
                          video_filter_buffer_allocation_init, NULL, p_vout );
243

Gildas Bazin's avatar
 
Gildas Bazin committed
244
    /* Initialize locks */
245
    vlc_mutex_init( &p_vout->p->picture_lock );
246
    vlc_mutex_init( &p_vout->p->vfilter_lock );
Gildas Bazin's avatar
 
Gildas Bazin committed
247

248 249
    /* Mouse coordinates */
    var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER );
250 251
    var_Create( p_vout, "mouse-moved", VLC_VAR_COORDS );
    var_Create( p_vout, "mouse-clicked", VLC_VAR_COORDS );
252 253
    /* Mouse object (area of interest in a video filter) */
    var_Create( p_vout, "mouse-object", VLC_VAR_BOOL );
254

Gildas Bazin's avatar
 
Gildas Bazin committed
255 256 257
    /* Attach the new object now so we can use var inheritance below */
    vlc_object_attach( p_vout, p_parent );

258
    /* Initialize subpicture unit */
259
    p_vout->p->p_spu = spu_Create( p_vout );
260

261
    /* */
262
    spu_Init( p_vout->p->p_spu );
263

264
    spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), true );
265

266 267
    p_vout->p->is_late_dropped = var_InheritBool( p_vout, "drop-late-frames" );

268 269
    /* Take care of some "interface/control" related initialisations */
    vout_IntfInit( p_vout );
Gildas Bazin's avatar
 
Gildas Bazin committed
270

Laurent Aimar's avatar
Laurent Aimar committed
271 272 273
    /* Look for the default filter configuration */
    p_vout->p->psz_filter_chain =
        var_CreateGetStringCommand( p_vout, "vout-filter" );
274

Laurent Aimar's avatar
Laurent Aimar committed
275
    /* Apply video filter2 objects on the first vout */
276 277
    var_Create( p_vout, "video-filter",
                VLC_VAR_STRING | VLC_VAR_DOINHERIT | VLC_VAR_ISCOMMAND );
278
    var_AddCallback( p_vout, "video-filter", VideoFilter2Callback, NULL );
279
    var_TriggerCallback( p_vout, "video-filter" );
280

Gildas Bazin's avatar
 
Gildas Bazin committed
281
    /* Choose the video output module */
282
    if( !p_vout->p->psz_filter_chain || !*p_vout->p->psz_filter_chain )
Gildas Bazin's avatar
 
Gildas Bazin committed
283
    {
284
        psz_parser = NULL;
Gildas Bazin's avatar
 
Gildas Bazin committed
285 286 287
    }
    else
    {
288
        psz_parser = strdup( p_vout->p->psz_filter_chain );
Laurent Aimar's avatar
Laurent Aimar committed
289
        p_vout->p->title.show = false;
Sam Hocevar's avatar
 
Sam Hocevar committed
290 291
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
292
    /* Create the vout thread */
293
    char *psz_tmp = config_ChainCreate( &psz_name, &p_cfg, psz_parser );
Antoine Cellerier's avatar
Antoine Cellerier committed
294
    free( psz_parser );
295
    free( psz_tmp );
296
    p_vout->p->p_cfg = p_cfg;
Laurent Aimar's avatar
Laurent Aimar committed
297 298 299 300 301 302 303 304

    /* Create a few object variables for interface interaction */
    var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
    text.psz_string = _("Filters");
    var_Change( p_vout, "vout-filter", VLC_VAR_SETTEXT, &text, NULL );
    var_AddCallback( p_vout, "vout-filter", FilterCallback, NULL );

    /* */
305
    vout_InitInterlacingSupport( p_vout, p_vout->p->displayed.is_interlaced );
Laurent Aimar's avatar
Laurent Aimar committed
306

307
    if( p_vout->p->psz_filter_chain && *p_vout->p->psz_filter_chain )
308 309 310 311 312 313 314
    {
        char *psz_tmp;
        if( asprintf( &psz_tmp, "%s,none", psz_name ) < 0 )
            psz_tmp = strdup( "" );
        free( psz_name );
        psz_name = psz_tmp;
    }
315
    p_vout->p->psz_module_name = psz_name;
Laurent Aimar's avatar
Laurent Aimar committed
316 317

    /* */
318
    vlc_object_set_destructor( p_vout, vout_Destructor );
Gildas Bazin's avatar
 
Gildas Bazin committed
319

Laurent Aimar's avatar
Laurent Aimar committed
320
    /* */
Laurent Aimar's avatar
Laurent Aimar committed
321
    if( vlc_clone( &p_vout->p->thread, Thread, p_vout,
322
                   VLC_THREAD_PRIORITY_OUTPUT ) )
Michel Kaempf's avatar
Michel Kaempf committed
323
    {
324 325 326
        spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), false );
        spu_Destroy( p_vout->p->p_spu );
        p_vout->p->p_spu = NULL;
327
        vlc_object_release( p_vout );
328 329 330
        return NULL;
    }

Laurent Aimar's avatar
Laurent Aimar committed
331
    vout_control_WaitEmpty( &p_vout->p->control );
332

Laurent Aimar's avatar
Laurent Aimar committed
333
    if (p_vout->p->dead )
334 335
    {
        msg_Err( p_vout, "video output creation failed" );
336
        vout_CloseAndRelease( p_vout );
337
        return NULL;
338
    }
Michel Kaempf's avatar
Michel Kaempf committed
339

340
    return p_vout;
Michel Kaempf's avatar
Michel Kaempf committed
341 342
}

343
/*****************************************************************************
344
 * vout_Close: Close a vout created by vout_Create.
345
 *****************************************************************************
346
 * 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
347
 * You should NEVER call it on vout not obtained through vout_Create
348
 * (like with vout_Request or vlc_object_find.)
349
 * You can use vout_CloseAndRelease() as a convenience method.
350
 *****************************************************************************/
351
void vout_Close( vout_thread_t *p_vout )
352 353 354
{
    assert( p_vout );

355
    vout_snapshot_End( &p_vout->p->snapshot );
356

Laurent Aimar's avatar
Laurent Aimar committed
357
    vout_control_PushVoid( &p_vout->p->control, VOUT_CONTROL_CLEAN );
358
    vlc_join( p_vout->p->thread, NULL );
359 360 361
}

/* */
362 363
static void vout_Destructor( vlc_object_t * p_this )
{
364
    vout_thread_t *p_vout = (vout_thread_t *)p_this;
365

366
    /* Make sure the vout was stopped first */
367
    //assert( !p_vout->p_module );
368

369 370
    free( p_vout->p->psz_module_name );

Laurent Aimar's avatar
Laurent Aimar committed
371
    /* */
372 373
    if( p_vout->p->p_spu )
        spu_Destroy( p_vout->p->p_spu );
Laurent Aimar's avatar
Laurent Aimar committed
374

375 376 377 378 379 380
    vout_chrono_Clean( &p_vout->p->render );

    if( p_vout->p->decoder_fifo )
        picture_fifo_Delete( p_vout->p->decoder_fifo );
    assert( !p_vout->p->decoder_pool );

381
    /* Destroy the locks */
382
    vlc_mutex_destroy( &p_vout->p->picture_lock );
383
    vlc_mutex_destroy( &p_vout->p->vfilter_lock );
384
    vout_control_Clean( &p_vout->p->control );
385

386 387 388
    /* */
    vout_statistic_Clean( &p_vout->p->statistic );

389
    /* */
390
    vout_snapshot_Clean( &p_vout->p->snapshot );
391 392

    /* */
393
    free( p_vout->p->psz_filter_chain );
Gildas Bazin's avatar
 
Gildas Bazin committed
394

395
    config_ChainDestroy( p_vout->p->p_cfg );
396

397 398
    free( p_vout->p );

Michel Kaempf's avatar
Michel Kaempf committed
399 400
}

Laurent Aimar's avatar
Laurent Aimar committed
401
/* */
402
void vout_ChangePause(vout_thread_t *vout, bool is_paused, mtime_t date)
Laurent Aimar's avatar
Laurent Aimar committed
403
{
404 405 406 407 408
    vout_control_cmd_t cmd;
    vout_control_cmd_Init(&cmd, VOUT_CONTROL_PAUSE);
    cmd.u.pause.is_on = is_paused;
    cmd.u.pause.date  = date;
    vout_control_Push(&vout->p->control, &cmd);
Laurent Aimar's avatar
Laurent Aimar committed
409

410
    vout_control_WaitEmpty(&vout->p->control);
Laurent Aimar's avatar
Laurent Aimar committed
411
}
412

413 414
void vout_GetResetStatistic( vout_thread_t *p_vout, int *pi_displayed, int *pi_lost )
{
415 416
    vout_statistic_GetReset( &p_vout->p->statistic,
                             pi_displayed, pi_lost );
417
}
418

419
void vout_Flush(vout_thread_t *vout, mtime_t date)
420
{
421 422
    vout_control_PushTime(&vout->p->control, VOUT_CONTROL_FLUSH, date);
    vout_control_WaitEmpty(&vout->p->control);
423
}
424

425
void vout_Reset(vout_thread_t *vout)
426
{
427 428
    vout_control_PushVoid(&vout->p->control, VOUT_CONTROL_RESET);
    vout_control_WaitEmpty(&vout->p->control);
429
}
430

431 432 433 434 435 436 437 438 439 440 441 442 443
bool vout_IsEmpty(vout_thread_t *vout)
{
    vlc_mutex_lock(&vout->p->picture_lock);

    picture_t *picture = picture_fifo_Peek(vout->p->decoder_fifo);
    if (picture)
        picture_Release(picture);

    vlc_mutex_unlock(&vout->p->picture_lock);

    return !picture;
}

444
void vout_FixLeaks( vout_thread_t *vout )
445 446 447 448 449 450
{
    vlc_mutex_lock(&vout->p->picture_lock);

    picture_t *picture = picture_fifo_Peek(vout->p->decoder_fifo);
    if (!picture) {
        picture = picture_pool_Get(vout->p->decoder_pool);
451
    }
452 453 454 455 456 457

    if (picture) {
        picture_Release(picture);
        /* Not all pictures has been displayed yet or some are
         * free */
        vlc_mutex_unlock(&vout->p->picture_lock);
458 459 460
        return;
    }

461 462 463
    /* There is no reason that no pictures are available, force one
     * from the pool, becarefull with it though */
    msg_Err(vout, "pictures leaked, trying to workaround");
Laurent Aimar's avatar
Laurent Aimar committed
464

465 466
    /* */
    picture_pool_NonEmpty(vout->p->decoder_pool, false);
Laurent Aimar's avatar
Laurent Aimar committed
467

468
    vlc_mutex_unlock(&vout->p->picture_lock);
469
}
470
void vout_NextPicture(vout_thread_t *vout, mtime_t *duration)
Laurent Aimar's avatar
Laurent Aimar committed
471
{
472 473 474
    vout_control_cmd_t cmd;
    vout_control_cmd_Init(&cmd, VOUT_CONTROL_STEP);
    cmd.u.time_ptr = duration;
Laurent Aimar's avatar
Laurent Aimar committed
475

476 477
    vout_control_Push(&vout->p->control, &cmd);
    vout_control_WaitEmpty(&vout->p->control);
Laurent Aimar's avatar
Laurent Aimar committed
478
}
479

480
void vout_DisplayTitle(vout_thread_t *vout, const char *title)
481
{
482 483
    assert(title);
    vout_control_PushString(&vout->p->control, VOUT_CONTROL_OSD_TITLE, title);
484
}
485

486 487 488 489 490 491 492 493 494 495 496 497 498
void vout_PutSubpicture( vout_thread_t *vout, subpicture_t *subpic )
{
    spu_DisplaySubpicture(vout->p->p_spu, subpic);
}
int vout_RegisterSubpictureChannel( vout_thread_t *vout )
{
    return spu_RegisterChannel(vout->p->p_spu);
}
void vout_FlushSubpictureChannel( vout_thread_t *vout, int channel )
{
    spu_ClearChannel(vout->p->p_spu, channel);
}

499 500
spu_t *vout_GetSpu( vout_thread_t *p_vout )
{
501
    return p_vout->p->p_spu;
502 503
}

504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
/* vout_Control* are usable by anyone at anytime */
void vout_ControlChangeFullscreen(vout_thread_t *vout, bool fullscreen)
{
    vout_control_PushBool(&vout->p->control, VOUT_CONTROL_FULLSCREEN,
                          fullscreen);
}
void vout_ControlChangeOnTop(vout_thread_t *vout, bool is_on_top)
{
    vout_control_PushBool(&vout->p->control, VOUT_CONTROL_ON_TOP,
                          is_on_top);
}
void vout_ControlChangeDisplayFilled(vout_thread_t *vout, bool is_filled)
{
    vout_control_PushBool(&vout->p->control, VOUT_CONTROL_DISPLAY_FILLED,
                          is_filled);
}
void vout_ControlChangeZoom(vout_thread_t *vout, int num, int den)
{
    vout_control_PushPair(&vout->p->control, VOUT_CONTROL_ZOOM,
                          num, den);
}
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
void vout_ControlChangeSampleAspectRatio(vout_thread_t *vout,
                                         unsigned num, unsigned den)
{
    vout_control_PushPair(&vout->p->control, VOUT_CONTROL_ASPECT_RATIO,
                          num, den);
}
void vout_ControlChangeCropRatio(vout_thread_t *vout,
                                 unsigned num, unsigned den)
{
    vout_control_PushPair(&vout->p->control, VOUT_CONTROL_CROP_RATIO,
                          num, den);
}
void vout_ControlChangeCropWindow(vout_thread_t *vout,
                                  int x, int y, int width, int height)
{
    vout_control_cmd_t cmd;
    vout_control_cmd_Init(&cmd, VOUT_CONTROL_CROP_WINDOW);
    cmd.u.window.x      = x;
    cmd.u.window.y      = y;
    cmd.u.window.width  = width;
    cmd.u.window.height = height;

    vout_control_Push(&vout->p->control, &cmd);
}
void vout_ControlChangeCropBorder(vout_thread_t *vout,
                                  int left, int top, int right, int bottom)
{
    vout_control_cmd_t cmd;
    vout_control_cmd_Init(&cmd, VOUT_CONTROL_CROP_BORDER);
    cmd.u.border.left   = left;
    cmd.u.border.top    = top;
    cmd.u.border.right  = right;
    cmd.u.border.bottom = bottom;

    vout_control_Push(&vout->p->control, &cmd);
}
561

Laurent Aimar's avatar
Laurent Aimar committed
562
/* */
563 564
static int ThreadDisplayPicture(vout_thread_t *vout,
                                bool now, mtime_t *deadline)
Vincent Seguin's avatar
Vincent Seguin committed
565
{
566
    vout_display_t *vd = vout->p->display.vd;
567 568 569 570 571
    int displayed_count = 0;
    int lost_count = 0;

    for (;;) {
        const mtime_t date = mdate();
Laurent Aimar's avatar
Laurent Aimar committed
572
        const bool is_paused = vout->p->pause.is_on;
573
        bool redisplay = is_paused && !now && vout->p->displayed.decoded;
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592
        bool is_forced;

        /* FIXME/XXX we must redisplay the last decoded picture (because
         * of potential vout updated, or filters update or SPU update)
         * For now a high update period is needed but it coulmd be removed
         * if and only if:
         * - vout module emits events from theselves.
         * - *and* SPU is modified to emit an event or a deadline when needed.
         *
         * So it will be done latter.
         */
        if (!redisplay) {
            picture_t *peek = picture_fifo_Peek(vout->p->decoder_fifo);
            if (peek) {
                is_forced = peek->b_force || is_paused || now;
                *deadline = (is_forced ? date : peek->date) - vout_chrono_GetHigh(&vout->p->render);
                picture_Release(peek);
            } else {
                redisplay = true;
593 594
            }
        }
595 596
        if (redisplay) {
             /* FIXME a better way for this delay is needed */
Laurent Aimar's avatar
Laurent Aimar committed
597
            const mtime_t date_update = vout->p->displayed.date + VOUT_REDISPLAY_DELAY;
598 599 600
            if (date_update > date || !vout->p->displayed.decoded) {
                *deadline = vout->p->displayed.decoded ? date_update : VLC_TS_INVALID;
                break;
Laurent Aimar's avatar
Laurent Aimar committed
601
            }
602 603 604
            /* */
            is_forced = true;
            *deadline = date - vout_chrono_GetHigh(&vout->p->render);
Laurent Aimar's avatar
Laurent Aimar committed
605
        }
Laurent Aimar's avatar
Laurent Aimar committed
606 607
        if (*deadline > VOUT_MWAIT_TOLERANCE)
            *deadline -= VOUT_MWAIT_TOLERANCE;
Laurent Aimar's avatar
Laurent Aimar committed
608

Laurent Aimar's avatar
Laurent Aimar committed
609
        /* If we are too early and can wait, do it */
610 611
        if (date < *deadline && !now)
            break;
612

613 614 615 616 617 618 619 620 621 622 623 624
        picture_t *decoded;
        if (redisplay) {
            decoded = vout->p->displayed.decoded;
            vout->p->displayed.decoded = NULL;
        } else {
            decoded = picture_fifo_Pop(vout->p->decoder_fifo);
            assert(decoded);
            if (!is_forced && !vout->p->is_late_dropped) {
                const mtime_t predicted = date + vout_chrono_GetLow(&vout->p->render);
                const mtime_t late = predicted - decoded->date;
                if (late > 0) {
                    msg_Dbg(vout, "picture might be displayed late (missing %d ms)", (int)(late/1000));
Laurent Aimar's avatar
Laurent Aimar committed
625
                    if (late > VOUT_DISPLAY_LATE_THRESHOLD) {
626 627 628 629 630
                        msg_Warn(vout, "rejected picture because of render time");
                        /* TODO */
                        picture_Release(decoded);
                        lost_count++;
                        break;
Laurent Aimar's avatar
Laurent Aimar committed
631 632 633
                    }
                }
            }
634 635 636

            vout->p->displayed.is_interlaced = !decoded->b_progressive;
            vout->p->displayed.qtype         = decoded->i_qtype;
637
        }
Laurent Aimar's avatar
Laurent Aimar committed
638
        vout->p->displayed.timestamp = decoded->date;
639 640

        /* */
641 642 643 644
        if (vout->p->displayed.decoded)
            picture_Release(vout->p->displayed.decoded);
        picture_Hold(decoded);
        vout->p->displayed.decoded = decoded;
645

646 647
        /* */
        vout_chrono_Start(&vout->p->render);
648

649 650 651
        picture_t *filtered = NULL;
        if (decoded) {
            vlc_mutex_lock(&vout->p->vfilter_lock);
Laurent Aimar's avatar
Laurent Aimar committed
652
            filtered = filter_chain_VideoFilter(vout->p->vfilter_chain, decoded);
653 654 655 656
            //assert(filtered == decoded); // TODO implement
            vlc_mutex_unlock(&vout->p->vfilter_lock);
            if (!filtered)
                continue;
657
        }
658

Sam Hocevar's avatar
 
Sam Hocevar committed
659 660 661
        /*
         * Check for subpictures to display
         */
662 663
        const bool do_snapshot = vout_snapshot_IsRequested(&vout->p->snapshot);
        mtime_t spu_render_time = is_forced ? mdate() : filtered->date;
Laurent Aimar's avatar
Laurent Aimar committed
664 665
        if (vout->p->pause.is_on)
            spu_render_time = vout->p->pause.date;
666
        else
667
            spu_render_time = filtered->date > 1 ? filtered->date : mdate();
668

669 670 671
        subpicture_t *subpic = spu_SortSubpictures(vout->p->p_spu,
                                                   spu_render_time,
                                                   do_snapshot);
Sam Hocevar's avatar
 
Sam Hocevar committed
672 673
        /*
         * Perform rendering
674 675 676 677
         *
         * We have to:
         * - be sure to end up with a direct buffer.
         * - blend subtitles, and in a fast access buffer
Sam Hocevar's avatar
 
Sam Hocevar committed
678
         */
679 680 681 682 683
        picture_t *direct = NULL;
        if (filtered &&
            (vout->p->decoder_pool != vout->p->display_pool || subpic)) {
            picture_t *render;
            if (vout->p->is_decoder_pool_slow)
684
                render = picture_NewFromFormat(&vd->source);
685 686 687 688
            else if (vout->p->decoder_pool != vout->p->display_pool)
                render = picture_pool_Get(vout->p->display_pool);
            else
                render = picture_pool_Get(vout->p->private_pool);
Sam Hocevar's avatar
 
Sam Hocevar committed
689

690 691
            if (render) {
                picture_Copy(render, filtered);
692

693
                spu_RenderSubpictures(vout->p->p_spu,
694 695
                                      render, &vd->source,
                                      subpic, &vd->source, spu_render_time);
696 697 698 699 700 701 702 703 704 705 706 707 708 709
            }
            if (vout->p->is_decoder_pool_slow) {
                direct = picture_pool_Get(vout->p->display_pool);
                if (direct)
                    picture_Copy(direct, render);
                picture_Release(render);

            } else {
                direct = render;
            }
            picture_Release(filtered);
            filtered = NULL;
        } else {
            direct = filtered;
Sam Hocevar's avatar
 
Sam Hocevar committed
710 711
        }

Sam Hocevar's avatar
 
Sam Hocevar committed
712
        /*
713
         * Take a snapshot if requested
Sam Hocevar's avatar
 
Sam Hocevar committed
714
         */
715
        if (direct && do_snapshot)
716
            vout_snapshot_Set(&vout->p->snapshot, &vd->source, direct);
717

718 719 720
        /* Render the direct buffer returned by vout_RenderPicture */
        if (direct) {
            vout_RenderWrapper(vout, direct);
Sam Hocevar's avatar
 
Sam Hocevar committed
721

722 723
            vout_chrono_Stop(&vout->p->render);
#if 0
Gildas Bazin's avatar
 
Gildas Bazin committed
724
            {
725 726 727 728
            static int i = 0;
            if (((i++)%10) == 0)
                msg_Info(vout, "render: avg %d ms var %d ms",
                         (int)(vout->p->render.avg/1000), (int)(vout->p->render.var/1000));
Gildas Bazin's avatar
 
Gildas Bazin committed
729
            }
730
#endif
731
        }
732

733 734 735
        /* Wait the real date (for rendering jitter) */
        if (!is_forced)
            mwait(decoded->date);
Sam Hocevar's avatar
 
Sam Hocevar committed
736

737
        /* Display the direct buffer returned by vout_RenderPicture */
Laurent Aimar's avatar
Laurent Aimar committed
738
        vout->p->displayed.date = mdate();
739 740
        if (direct)
            vout_DisplayWrapper(vout, direct);
Vincent Seguin's avatar
Vincent Seguin committed
741

742 743 744
        displayed_count++;
        break;
    }
Vincent Seguin's avatar
Vincent Seguin committed
745

746 747 748 749 750
    vout_statistic_Update(&vout->p->statistic, displayed_count, lost_count);
    if (displayed_count <= 0)
        return VLC_EGENERIC;
    return VLC_SUCCESS;
}
751

Laurent Aimar's avatar
Laurent Aimar committed
752 753 754 755
static void ThreadManage(vout_thread_t *vout,
                         mtime_t *deadline,
                         vout_interlacing_support_t *interlacing,
                         vout_postprocessing_support_t *postprocessing)
Laurent Aimar's avatar
Laurent Aimar committed
756 757 758 759
{
    vlc_mutex_lock(&vout->p->picture_lock);

    *deadline = VLC_TS_INVALID;
760
    ThreadDisplayPicture(vout, false, deadline);
Laurent Aimar's avatar
Laurent Aimar committed
761 762 763 764 765 766 767 768 769 770 771 772

    const int  picture_qtype      = vout->p->displayed.qtype;
    const bool picture_interlaced = vout->p->displayed.is_interlaced;

    vlc_mutex_unlock(&vout->p->picture_lock);

    /* Post processing */
    vout_SetPostProcessingState(vout, postprocessing, picture_qtype);

    /* Deinterlacing */
    vout_SetInterlacingState(vout, interlacing, picture_interlaced);

Laurent Aimar's avatar
Laurent Aimar committed
773
    vout_ManageWrapper(vout);
Laurent Aimar's avatar
Laurent Aimar committed
774 775
}

776
static void ThreadDisplayOsdTitle(vout_thread_t *vout, const char *string)
Laurent Aimar's avatar
Laurent Aimar committed
777
{
778
    if (!vout->p->title.show)
Laurent Aimar's avatar
Laurent Aimar committed
779 780
        return;

781 782 783
    vout_OSDText(vout, SPU_DEFAULT_CHANNEL,
                 vout->p->title.position, INT64_C(1000) * vout->p->title.timeout,
                 string);
Laurent Aimar's avatar
Laurent Aimar committed
784 785
}

786
static void ThreadChangeFilters(vout_thread_t *vout, const char *filters)
Laurent Aimar's avatar
Laurent Aimar committed
787
{
788
    es_format_t fmt;
789 790
    es_format_Init(&fmt, VIDEO_ES, vout->p->original.i_chroma);
    fmt.video = vout->p->original;
791

Laurent Aimar's avatar
Laurent Aimar committed
792 793
    vlc_mutex_lock(&vout->p->vfilter_lock);

Laurent Aimar's avatar
Laurent Aimar committed
794 795
    filter_chain_Reset(vout->p->vfilter_chain, &fmt, &fmt);
    if (filter_chain_AppendFromString(vout->p->vfilter_chain,
796 797
                                      filters) < 0)
        msg_Err(vout, "Video filter chain creation failed");
Laurent Aimar's avatar
Laurent Aimar committed
798

799 800
    vlc_mutex_unlock(&vout->p->vfilter_lock);
}
Laurent Aimar's avatar
Laurent Aimar committed
801

802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820
static void ThreadChangePause(vout_thread_t *vout, bool is_paused, mtime_t date)
{
    assert(!vout->p->pause.is_on || !is_paused);

    if (vout->p->pause.is_on) {
        const mtime_t duration = date - vout->p->pause.date;

        if (vout->p->step.timestamp > VLC_TS_INVALID)
            vout->p->step.timestamp += duration;
        if (vout->p->step.last > VLC_TS_INVALID)
            vout->p->step.last += duration;
        picture_fifo_OffsetDate(vout->p->decoder_fifo, duration);
        if (vout->p->displayed.decoded)
            vout->p->displayed.decoded->date += duration;

        spu_OffsetSubtitleDate(vout->p->p_spu, duration);
    } else {
        vout->p->step.timestamp = VLC_TS_INVALID;
        vout->p->step.last      = VLC_TS_INVALID;
Laurent Aimar's avatar
Laurent Aimar committed
821
    }
822 823
    vout->p->pause.is_on = is_paused;
    vout->p->pause.date  = date;
Laurent Aimar's avatar
Laurent Aimar committed
824 825
}

826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874
static void ThreadFlush(vout_thread_t *vout, bool below, mtime_t date)
{
    vout->p->step.timestamp = VLC_TS_INVALID;
    vout->p->step.last      = VLC_TS_INVALID;

    picture_t *last = vout->p->displayed.decoded;
    if (last) {
        if (( below && last->date <= date) ||
            (!below && last->date >= date)) {
            picture_Release(last);

            vout->p->displayed.decoded   = NULL;
            vout->p->displayed.date      = VLC_TS_INVALID;
            vout->p->displayed.timestamp = VLC_TS_INVALID;
        }
    }
    picture_fifo_Flush(vout->p->decoder_fifo, date, below);
}

static void ThreadReset(vout_thread_t *vout)
{
    ThreadFlush(vout, true, INT64_MAX);
    if (vout->p->decoder_pool)
        picture_pool_NonEmpty(vout->p->decoder_pool, true);
    vout->p->pause.is_on = false;
    vout->p->pause.date  = mdate();
}

static void ThreadStep(vout_thread_t *vout, mtime_t *duration)
{
    *duration = 0;

    if (vout->p->step.last <= VLC_TS_INVALID)
        vout->p->step.last = vout->p->displayed.timestamp;

    mtime_t dummy;
    if (ThreadDisplayPicture(vout, true, &dummy))
        return;

    vout->p->step.timestamp = vout->p->displayed.timestamp;

    if (vout->p->step.last > VLC_TS_INVALID &&
        vout->p->step.timestamp > vout->p->step.last) {
        *duration = vout->p->step.timestamp - vout->p->step.last;
        vout->p->step.last = vout->p->step.timestamp;
        /* TODO advance subpicture by the duration ... */
    }
}

875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904
static void ThreadChangeFullscreen(vout_thread_t *vout, bool fullscreen)
{
    /* FIXME not sure setting "fullscreen" is good ... */
    var_SetBool(vout, "fullscreen", fullscreen);
    vout_SetDisplayFullscreen(vout->p->display.vd, fullscreen);
}

static void ThreadChangeOnTop(vout_thread_t *vout, bool is_on_top)
{
    vout_SetWindowState(vout->p->display.vd,
                        is_on_top ? VOUT_WINDOW_STATE_ABOVE :
                                    VOUT_WINDOW_STATE_NORMAL);
}

static void ThreadChangeDisplayFilled(vout_thread_t *vout, bool is_filled)
{
    vout_SetDisplayFilled(vout->p->display.vd, is_filled);
}

static void ThreadChangeZoom(vout_thread_t *vout, int num, int den)
{
    if (num * 10 < den) {
        num = den;
        den *= 10;
    } else if (num > den * 10) {
        num = den * 10;
    }

    vout_SetDisplayZoom(vout->p->display.vd, num, den);
}
905

906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978
static void ThreadChangeAspectRatio(vout_thread_t *vout,
                                    unsigned num, unsigned den)
{
    const video_format_t *source = &vout->p->original;

    if (num > 0 && den > 0) {
        num *= source->i_visible_height;
        den *= source->i_visible_width;
        vlc_ureduce(&num, &den, num, den, 0);
    }
    vout_SetDisplayAspect(vout->p->display.vd, num, den);
}


static void ThreadExecuteCropWindow(vout_thread_t *vout,
                                    unsigned crop_num, unsigned crop_den,
                                    unsigned x, unsigned y,
                                    unsigned width, unsigned height)
{
    const video_format_t *source = &vout->p->original;

    vout_SetDisplayCrop(vout->p->display.vd,
                        crop_num, crop_den,
                        source->i_x_offset + x,
                        source->i_y_offset + y,
                        width, height);
}
static