deinterlace.c 58.5 KB
Newer Older
1 2 3
/*****************************************************************************
 * deinterlace.c : deinterlacer plugin for vlc
 *****************************************************************************
Rémi Duraffort's avatar
Rémi Duraffort committed
4
 * Copyright (C) 2000-2009 the VideoLAN team
5
 * $Id$
6
 *
7
 * Author: Sam Hocevar <sam@zoy.org>
8 9 10 11 12
 *
 * 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.
13
 *
14 15 16 17 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
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * 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
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 23 24 25 26 27
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

28 29 30 31
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

32 33
#include <errno.h>

34 35 36 37
#ifdef HAVE_ALTIVEC_H
#   include <altivec.h>
#endif

38
#include <vlc_common.h>
39
#include <vlc_plugin.h>
Clément Stenac's avatar
Clément Stenac committed
40 41
#include <vlc_vout.h>
#include <vlc_sout.h>
Rémi Duraffort's avatar
Rémi Duraffort committed
42
#include <vlc_filter.h>
43

44 45 46 47
#ifdef CAN_COMPILE_MMXEXT
#   include "mmx.h"
#endif

Clément Stenac's avatar
Clément Stenac committed
48
#include "filter_common.h"
49 50 51 52 53 54

#define DEINTERLACE_DISCARD 1
#define DEINTERLACE_MEAN    2
#define DEINTERLACE_BLEND   3
#define DEINTERLACE_BOB     4
#define DEINTERLACE_LINEAR  5
55
#define DEINTERLACE_X       6
56 57 58 59 60 61 62 63 64 65 66

/*****************************************************************************
 * Local protypes
 *****************************************************************************/
static int  Create    ( vlc_object_t * );
static void Destroy   ( vlc_object_t * );

static int  Init      ( vout_thread_t * );
static void End       ( vout_thread_t * );
static void Render    ( vout_thread_t *, picture_t * );

67 68 69
static int  MouseEvent( vlc_object_t *p_this, char const *psz_var,
                        vlc_value_t oldval, vlc_value_t newval, void *p_data );

70
static void RenderDiscard( vout_thread_t *, picture_t *, picture_t *, int );
71 72 73 74
static void RenderBob    ( vout_thread_t *, picture_t *, picture_t *, int );
static void RenderMean   ( vout_thread_t *, picture_t *, picture_t * );
static void RenderBlend  ( vout_thread_t *, picture_t *, picture_t * );
static void RenderLinear ( vout_thread_t *, picture_t *, picture_t *, int );
Rafaël Carré's avatar
Rafaël Carré committed
75
static void RenderX      ( picture_t *, picture_t * );
76

77
static void MergeGeneric ( void *, const void *, const void *, size_t );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
78
#if defined(CAN_COMPILE_C_ALTIVEC)
79
static void MergeAltivec ( void *, const void *, const void *, size_t );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
80
#endif
81
#if defined(CAN_COMPILE_MMXEXT)
82 83 84 85
static void MergeMMXEXT  ( void *, const void *, const void *, size_t );
#endif
#if defined(CAN_COMPILE_3DNOW)
static void Merge3DNow   ( void *, const void *, const void *, size_t );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
86
#endif
Eric Petit's avatar
Eric Petit committed
87
#if defined(CAN_COMPILE_SSE)
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
88
static void MergeSSE2    ( void *, const void *, const void *, size_t );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
89
#endif
90
#if defined(CAN_COMPILE_MMXEXT) || defined(CAN_COMPILE_SSE)
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
91
static void EndMMX       ( void );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
92
#endif
93 94 95
#if defined(CAN_COMPILE_3DNOW)
static void End3DNow     ( void );
#endif
96 97 98
#if defined __ARM_NEON__
static void MergeNEON (void *, const void *, const void *, size_t);
#endif
99

100
static void SetFilterMethod( vout_thread_t *p_vout, const char *psz_method );
Gildas Bazin's avatar
 
Gildas Bazin committed
101 102
static vout_thread_t *SpawnRealVout( vout_thread_t *p_vout );

103 104 105
static int OpenFilter( vlc_object_t *p_this );
static void CloseFilter( vlc_object_t *p_this );

Gildas Bazin's avatar
 
Gildas Bazin committed
106 107 108
/*****************************************************************************
 * Callback prototypes
 *****************************************************************************/
109 110
static int FilterCallback( vlc_object_t *, char const *,
                           vlc_value_t, vlc_value_t, void * );
Gildas Bazin's avatar
 
Gildas Bazin committed
111

112 113 114
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
Christophe Massiot's avatar
Christophe Massiot committed
115
#define MODE_TEXT N_("Deinterlace mode")
116
#define MODE_LONGTEXT N_("Deinterlace method to use for local playback.")
117

118 119
#define SOUT_MODE_TEXT N_("Streaming deinterlace mode")
#define SOUT_MODE_LONGTEXT N_("Deinterlace method to use for streaming.")
120

121 122
#define FILTER_CFG_PREFIX "sout-deinterlace-"

123 124 125 126
static const char *const mode_list[] = {
    "discard", "blend", "mean", "bob", "linear", "x" };
static const char *const mode_list_text[] = {
    N_("Discard"), N_("Blend"), N_("Mean"), N_("Bob"), N_("Linear"), "X" };
127

128 129 130 131 132 133 134 135
vlc_module_begin ()
    set_description( N_("Deinterlacing video filter") )
    set_shortname( N_("Deinterlace" ))
    set_capability( "video filter", 0 )
    set_category( CAT_VIDEO )
    set_subcategory( SUBCAT_VIDEO_VFILTER )

    set_section( N_("Display"),NULL)
136
    add_string( "deinterlace-mode", "discard", NULL, MODE_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
137
                MODE_LONGTEXT, false )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
138
        change_string_list( mode_list, mode_list_text, 0 )
139
        change_safe ()
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
140

141 142
    add_shortcut( "deinterlace" )
    set_callbacks( Create, Destroy )
143

144 145 146
    add_submodule ()
    set_capability( "video filter2", 0 )
    set_section( N_("Streaming"),NULL)
147
    add_string( FILTER_CFG_PREFIX "mode", "blend", NULL, SOUT_MODE_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
148
                SOUT_MODE_LONGTEXT, false )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
149
        change_string_list( mode_list, mode_list_text, 0 )
150
    add_shortcut( "deinterlace" )
151 152
    set_callbacks( OpenFilter, CloseFilter )
vlc_module_end ()
153

154
static const char *const ppsz_filter_options[] = {
155 156 157
    "mode", NULL
};

158 159 160 161 162 163 164 165 166
/*****************************************************************************
 * vout_sys_t: Deinterlace video output method descriptor
 *****************************************************************************
 * This structure is part of the video output thread descriptor.
 * It describes the Deinterlace specific properties of an output thread.
 *****************************************************************************/
struct vout_sys_t
{
    int        i_mode;        /* Deinterlace mode */
167
    bool b_double_rate; /* Shall we double the framerate? */
168
    bool b_half_height; /* Shall be devide the height by 2 */
169 170 171 172 173

    mtime_t    last_date;
    mtime_t    next_date;

    vout_thread_t *p_vout;
Gildas Bazin's avatar
 
Gildas Bazin committed
174 175

    vlc_mutex_t filter_lock;
176 177

    void (*pf_merge) ( void *, const void *, const void *, size_t );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
178
    void (*pf_end_merge) ( void );
179 180
};

181 182 183 184 185 186 187 188
/*****************************************************************************
 * Control: control facility for the vout (forwards to child vout)
 *****************************************************************************/
static int Control( vout_thread_t *p_vout, int i_query, va_list args )
{
    return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
}

189 190 191 192 193 194
/*****************************************************************************
 * Create: allocates Deinterlace video thread output method
 *****************************************************************************
 * This function allocates and initializes a Deinterlace vout method.
 *****************************************************************************/
static int Create( vlc_object_t *p_this )
195
{
196
    vout_thread_t *p_vout = (vout_thread_t *)p_this;
Rémi Duraffort's avatar
Rémi Duraffort committed
197
    vout_sys_t *p_sys;
198
    char *psz_mode;
199 200

    /* Allocate structure */
Rémi Duraffort's avatar
Rémi Duraffort committed
201
    p_sys = p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
202
    if( p_vout->p_sys == NULL )
203
        return VLC_ENOMEM;
204 205 206 207 208 209

    p_vout->pf_init = Init;
    p_vout->pf_end = End;
    p_vout->pf_manage = NULL;
    p_vout->pf_render = Render;
    p_vout->pf_display = NULL;
210
    p_vout->pf_control = Control;
211

Rémi Duraffort's avatar
Rémi Duraffort committed
212 213 214 215 216 217
    p_sys->i_mode = DEINTERLACE_DISCARD;
    p_sys->b_double_rate = false;
    p_sys->b_half_height = true;
    p_sys->last_date = 0;
    p_sys->p_vout = 0;
    vlc_mutex_init( &p_sys->filter_lock );
218

Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
219
#if defined(CAN_COMPILE_C_ALTIVEC)
220
    if( vlc_CPU() & CPU_CAPABILITY_ALTIVEC )
221
    {
Rémi Duraffort's avatar
Rémi Duraffort committed
222 223
        p_sys->pf_merge = MergeAltivec;
        p_sys->pf_end_merge = NULL;
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
224
    }
225
    else
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
226
#endif
Eric Petit's avatar
Eric Petit committed
227
#if defined(CAN_COMPILE_SSE)
228
    if( vlc_CPU() & CPU_CAPABILITY_SSE2 )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
229
    {
Rémi Duraffort's avatar
Rémi Duraffort committed
230 231
        p_sys->pf_merge = MergeSSE2;
        p_sys->pf_end_merge = EndMMX;
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
232
    }
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
233 234
    else
#endif
235
#if defined(CAN_COMPILE_MMXEXT)
236
    if( vlc_CPU() & CPU_CAPABILITY_MMXEXT )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
237
    {
Rémi Duraffort's avatar
Rémi Duraffort committed
238 239
        p_sys->pf_merge = MergeMMXEXT;
        p_sys->pf_end_merge = EndMMX;
240 241
    }
    else
242 243
#endif
#if defined(CAN_COMPILE_3DNOW)
244
    if( vlc_CPU() & CPU_CAPABILITY_3DNOW )
245
    {
Rémi Duraffort's avatar
Rémi Duraffort committed
246 247
        p_sys->pf_merge = Merge3DNow;
        p_sys->pf_end_merge = End3DNow;
248 249
    }
    else
250 251 252 253 254 255 256 257
#endif
#if defined __ARM_NEON__
    if( vlc_CPU() & CPU_CAPABILITY_NEON )
    {
        p_sys->pf_merge = MergeNEON;
        p_sys->pf_end_merge = NULL;
    }
    else
258
#endif
259
    {
Rémi Duraffort's avatar
Rémi Duraffort committed
260 261
        p_sys->pf_merge = MergeGeneric;
        p_sys->pf_end_merge = NULL;
262 263
    }

264
    /* Look what method was requested */
265
    psz_mode = var_CreateGetString( p_vout, "deinterlace-mode" );
Gildas Bazin's avatar
 
Gildas Bazin committed
266

267
    if( !psz_mode )
268
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
269
        msg_Err( p_vout, "configuration variable deinterlace-mode empty" );
270 271
        msg_Err( p_vout, "no deinterlace mode provided, using \"discard\"" );

272
        psz_mode = strdup( "discard" );
273 274
    }

275
    SetFilterMethod( p_vout, psz_mode );
Gildas Bazin's avatar
 
Gildas Bazin committed
276

277
    free( psz_mode );
Gildas Bazin's avatar
 
Gildas Bazin committed
278

279
    return VLC_SUCCESS;
280 281
}

Gildas Bazin's avatar
 
Gildas Bazin committed
282 283 284
/*****************************************************************************
 * SetFilterMethod: setup the deinterlace method to use.
 *****************************************************************************/
285
static void SetFilterMethod( vout_thread_t *p_vout, const char *psz_method )
Gildas Bazin's avatar
 
Gildas Bazin committed
286
{
Rémi Duraffort's avatar
Rémi Duraffort committed
287
    vout_sys_t *p_sys = p_vout->p_sys;
288
    if( !strcmp( psz_method, "mean" ) )
Gildas Bazin's avatar
 
Gildas Bazin committed
289
    {
Rémi Duraffort's avatar
Rémi Duraffort committed
290 291 292
        p_sys->i_mode = DEINTERLACE_MEAN;
        p_sys->b_double_rate = false;
        p_sys->b_half_height = true;
Gildas Bazin's avatar
 
Gildas Bazin committed
293 294 295 296 297
    }
    else if( !strcmp( psz_method, "blend" )
             || !strcmp( psz_method, "average" )
             || !strcmp( psz_method, "combine-fields" ) )
    {
Rémi Duraffort's avatar
Rémi Duraffort committed
298 299 300
        p_sys->i_mode = DEINTERLACE_BLEND;
        p_sys->b_double_rate = false;
        p_sys->b_half_height = false;
Gildas Bazin's avatar
 
Gildas Bazin committed
301 302 303 304
    }
    else if( !strcmp( psz_method, "bob" )
             || !strcmp( psz_method, "progressive-scan" ) )
    {
Rémi Duraffort's avatar
Rémi Duraffort committed
305 306 307
        p_sys->i_mode = DEINTERLACE_BOB;
        p_sys->b_double_rate = true;
        p_sys->b_half_height = false;
Gildas Bazin's avatar
 
Gildas Bazin committed
308 309 310
    }
    else if( !strcmp( psz_method, "linear" ) )
    {
Rémi Duraffort's avatar
Rémi Duraffort committed
311 312 313
        p_sys->i_mode = DEINTERLACE_LINEAR;
        p_sys->b_double_rate = true;
        p_sys->b_half_height = false;
Gildas Bazin's avatar
 
Gildas Bazin committed
314
    }
315 316
    else if( !strcmp( psz_method, "x" ) )
    {
Rémi Duraffort's avatar
Rémi Duraffort committed
317 318 319
        p_sys->i_mode = DEINTERLACE_X;
        p_sys->b_double_rate = false;
        p_sys->b_half_height = false;
320
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
321 322
    else
    {
323
        const bool b_i422 = p_vout->render.i_chroma == VLC_CODEC_I422;
324 325 326 327
        if( strcmp( psz_method, "discard" ) )
            msg_Err( p_vout, "no valid deinterlace mode provided, "
                     "using \"discard\"" );

Rémi Duraffort's avatar
Rémi Duraffort committed
328 329 330
        p_sys->i_mode = DEINTERLACE_DISCARD;
        p_sys->b_double_rate = false;
        p_sys->b_half_height = !b_i422;
Gildas Bazin's avatar
 
Gildas Bazin committed
331
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
332 333

    msg_Dbg( p_vout, "using %s deinterlace method", psz_method );
Gildas Bazin's avatar
 
Gildas Bazin committed
334 335
}

336 337 338 339 340 341 342 343 344 345 346 347 348
static void GetOutputFormat( vout_thread_t *p_vout,
                             video_format_t *p_dst, const video_format_t *p_src )
{
    *p_dst = *p_src;

    if( p_vout->p_sys->b_half_height )
    {
        p_dst->i_height /= 2;
        p_dst->i_visible_height /= 2;
        p_dst->i_y_offset /= 2;
        p_dst->i_sar_den *= 2;
    }

349
    if( p_src->i_chroma == VLC_CODEC_I422 )
350 351 352 353 354 355
    {
        switch( p_vout->p_sys->i_mode )
        {
        case DEINTERLACE_MEAN:
        case DEINTERLACE_LINEAR:
        case DEINTERLACE_X:
356
            p_dst->i_chroma = VLC_CODEC_I422;
357 358
            break;
        default:
359
            p_dst->i_chroma = VLC_CODEC_I420;
360 361 362 363 364 365 366
            break;
        }
    }
}

static bool IsChromaSupported( vlc_fourcc_t i_chroma )
{
367 368 369
    return i_chroma == VLC_CODEC_I420 ||
           i_chroma == VLC_CODEC_YV12 ||
           i_chroma == VLC_CODEC_I422;
370 371
}

372 373 374 375 376 377 378
/*****************************************************************************
 * Init: initialize Deinterlace video thread output method
 *****************************************************************************/
static int Init( vout_thread_t *p_vout )
{
    I_OUTPUTPICTURES = 0;

379 380 381
    if( !IsChromaSupported( p_vout->render.i_chroma ) )
        return VLC_EGENERIC; /* unknown chroma */

382 383
    /* Initialize the output structure, full of directbuffers since we want
     * the decoder to output directly to our structures. */
384 385 386 387 388
    p_vout->output.i_chroma = p_vout->render.i_chroma;
    p_vout->output.i_width  = p_vout->render.i_width;
    p_vout->output.i_height = p_vout->render.i_height;
    p_vout->output.i_aspect = p_vout->render.i_aspect;
    p_vout->fmt_out = p_vout->fmt_in;
389

Gildas Bazin's avatar
 
Gildas Bazin committed
390 391 392 393 394 395 396 397 398 399 400
    /* Try to open the real video output */
    p_vout->p_sys->p_vout = SpawnRealVout( p_vout );

    if( p_vout->p_sys->p_vout == NULL )
    {
        /* Everything failed */
        msg_Err( p_vout, "cannot open vout, aborting" );

        return VLC_EGENERIC;
    }

401
    vout_filter_AllocateDirectBuffers( p_vout, VOUT_MAX_PICTURES );
Gildas Bazin's avatar
 
Gildas Bazin committed
402

403
    vout_filter_AddChild( p_vout, p_vout->p_sys->p_vout, MouseEvent );
Gildas Bazin's avatar
 
Gildas Bazin committed
404

405 406
    var_AddCallback( p_vout, "deinterlace-mode", FilterCallback, NULL );

Gildas Bazin's avatar
 
Gildas Bazin committed
407 408 409 410 411 412 413 414
    return VLC_SUCCESS;
}

/*****************************************************************************
 * SpawnRealVout: spawn the real video output.
 *****************************************************************************/
static vout_thread_t *SpawnRealVout( vout_thread_t *p_vout )
{
415 416
    msg_Dbg( p_vout, "spawning the real video output" );

417 418
    video_format_t fmt;
    GetOutputFormat( p_vout, &fmt, &p_vout->fmt_out );
419

420
    return vout_Create( p_vout, &fmt );
421 422 423 424 425 426 427
}

/*****************************************************************************
 * End: terminate Deinterlace video thread output method
 *****************************************************************************/
static void End( vout_thread_t *p_vout )
{
428
    vout_sys_t *p_sys = p_vout->p_sys;
429

430 431
    var_DelCallback( p_vout, "deinterlace-mode", FilterCallback, NULL );

432
    if( p_sys->p_vout )
433
    {
434 435
        vout_filter_DelChild( p_vout, p_sys->p_vout, MouseEvent );
        vout_CloseAndRelease( p_sys->p_vout );
436 437
    }

438
    vout_filter_ReleaseDirectBuffers( p_vout );
439
}
Gildas Bazin's avatar
 
Gildas Bazin committed
440

441 442 443 444 445 446 447 448 449
/*****************************************************************************
 * Destroy: destroy Deinterlace video thread output method
 *****************************************************************************
 * Terminate an output method created by DeinterlaceCreateOutputMethod
 *****************************************************************************/
static void Destroy( vlc_object_t *p_this )
{
    vout_thread_t *p_vout = (vout_thread_t *)p_this;
    vlc_mutex_destroy( &p_vout->p_sys->filter_lock );
450 451 452
    free( p_vout->p_sys );
}

453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
/**
 * Forward mouse event with proper conversion.
 */
static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
                       vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
    vout_thread_t *p_vout = p_data;
    VLC_UNUSED(p_this); VLC_UNUSED(oldval);

    if( !strcmp( psz_var, "mouse-y" ) && p_vout->p_sys->b_half_height )
        newval.i_int *= 2;

    return var_Set( p_vout, psz_var, newval );
}

468 469 470 471 472 473 474 475 476
/*****************************************************************************
 * Render: displays previously rendered output
 *****************************************************************************
 * This function send the currently rendered image to Deinterlace image,
 * waits until it is displayed and switch the two rendering buffers, preparing
 * next frame.
 *****************************************************************************/
static void Render ( vout_thread_t *p_vout, picture_t *p_pic )
{
477
    vout_sys_t *p_sys = p_vout->p_sys;
478 479
    picture_t *pp_outpic[2];

480 481 482 483 484 485 486 487 488 489 490 491
    /* FIXME are they needed ? */
    p_vout->fmt_out.i_x_offset = p_vout->fmt_in.i_x_offset;
    p_vout->fmt_out.i_y_offset = p_vout->fmt_in.i_y_offset;
    p_vout->fmt_out.i_visible_width = p_vout->fmt_in.i_visible_width;
    p_vout->fmt_out.i_visible_height = p_vout->fmt_in.i_visible_height;

    /* FIXME p_sys->p_vout->* should NOT be changed FIXME */
    p_sys->p_vout->fmt_in.i_x_offset = p_vout->fmt_out.i_x_offset;
    p_sys->p_vout->fmt_in.i_y_offset = p_vout->fmt_out.i_y_offset;
    p_sys->p_vout->fmt_in.i_visible_width = p_vout->fmt_out.i_visible_width;
    p_sys->p_vout->fmt_in.i_visible_height = p_vout->fmt_in.i_visible_height;
    if( p_vout->p_sys->b_half_height )
492
    {
493
        p_sys->p_vout->fmt_in.i_y_offset /= 2;
494 495
        p_sys->p_vout->fmt_in.i_visible_height /= 2;
    }
496

497
    if( p_vout->i_changes & VOUT_ASPECT_CHANGE )
498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516
    {
        p_vout->i_changes &= ~VOUT_ASPECT_CHANGE;

        p_vout->fmt_out.i_aspect = p_vout->fmt_in.i_aspect;
        p_vout->fmt_out.i_sar_num = p_vout->fmt_in.i_sar_num;
        p_vout->fmt_out.i_sar_den = p_vout->fmt_in.i_sar_den;

        video_format_t fmt = p_vout->fmt_out;
        if( p_vout->p_sys->b_half_height )
        {
            fmt.i_height /= 2; fmt.i_visible_height /= 2; fmt.i_y_offset /= 2;
            fmt.i_sar_den *= 2;
        }

        p_sys->p_vout = vout_Request( p_vout, p_sys->p_vout, &fmt );
    }
    if( !p_sys->p_vout )
        return;

517 518
    pp_outpic[0] = pp_outpic[1] = NULL;

Gildas Bazin's avatar
 
Gildas Bazin committed
519 520
    vlc_mutex_lock( &p_vout->p_sys->filter_lock );

521 522
    /* Get a new picture */
    while( ( pp_outpic[0] = vout_CreatePicture( p_vout->p_sys->p_vout,
523
                                                0, 0, 0 ) )
524 525
              == NULL )
    {
526
        if( !vlc_object_alive( p_vout ) || p_vout->b_error )
527
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
528
            vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
529 530 531
            return;
        }
        msleep( VOUT_OUTMEM_SLEEP );
532
    }
533

534
    pp_outpic[0]->date = p_pic->date;
535 536 537 538 539 540 541 542

    /* If we are using double rate, get an additional new picture */
    if( p_vout->p_sys->b_double_rate )
    {
        while( ( pp_outpic[1] = vout_CreatePicture( p_vout->p_sys->p_vout,
                                                 0, 0, 0 ) )
                  == NULL )
        {
543
            if( !vlc_object_alive( p_vout ) || p_vout->b_error )
544 545
            {
                vout_DestroyPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
Gildas Bazin's avatar
 
Gildas Bazin committed
546
                vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
547 548
                return;
            }
549
            msleep( VOUT_OUTMEM_SLEEP );
550
        }
551 552 553

        /* 20ms is a bit arbitrary, but it's only for the first image we get */
        if( !p_vout->p_sys->last_date )
554
            pp_outpic[1]->date = p_pic->date + 20000;
555
        else
556
            pp_outpic[1]->date = (3 * p_pic->date - p_vout->p_sys->last_date) / 2;
557 558 559 560 561 562
        p_vout->p_sys->last_date = p_pic->date;
    }

    switch( p_vout->p_sys->i_mode )
    {
        case DEINTERLACE_DISCARD:
563
            RenderDiscard( p_vout, pp_outpic[0], p_pic, 0 );
564 565 566 567
            vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
            break;

        case DEINTERLACE_BOB:
568
            RenderBob( p_vout, pp_outpic[0], p_pic, p_pic->b_top_field_first ? 0 : 1 );
569
            vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
570
            RenderBob( p_vout, pp_outpic[1], p_pic, p_pic->b_top_field_first ? 1 : 0 );
571 572 573 574
            vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[1] );
            break;

        case DEINTERLACE_LINEAR:
575
            RenderLinear( p_vout, pp_outpic[0], p_pic, p_pic->b_top_field_first ? 0 : 1 );
576
            vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
577
            RenderLinear( p_vout, pp_outpic[1], p_pic, p_pic->b_top_field_first ? 1 : 0 );
578 579 580 581 582 583 584 585 586 587 588 589
            vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[1] );
            break;

        case DEINTERLACE_MEAN:
            RenderMean( p_vout, pp_outpic[0], p_pic );
            vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
            break;

        case DEINTERLACE_BLEND:
            RenderBlend( p_vout, pp_outpic[0], p_pic );
            vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
            break;
Gildas Bazin's avatar
 
Gildas Bazin committed
590

591
        case DEINTERLACE_X:
Rafaël Carré's avatar
Rafaël Carré committed
592
            RenderX( pp_outpic[0], p_pic );
593 594 595
            vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
            break;
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
596
    vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
597 598 599
}

/*****************************************************************************
600
 * RenderDiscard: only keep TOP or BOTTOM field, discard the other.
601
 *****************************************************************************/
602 603
static void RenderDiscard( vout_thread_t *p_vout,
                           picture_t *p_outpic, picture_t *p_pic, int i_field )
604 605 606 607 608 609
{
    int i_plane;

    /* Copy image and skip lines */
    for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
    {
610
        uint8_t *p_in, *p_out_end, *p_out;
611 612 613 614 615 616 617
        int i_increment;

        p_in = p_pic->p[i_plane].p_pixels
                   + i_field * p_pic->p[i_plane].i_pitch;

        p_out = p_outpic->p[i_plane].p_pixels;
        p_out_end = p_out + p_outpic->p[i_plane].i_pitch
618
                             * p_outpic->p[i_plane].i_visible_lines;
619 620 621

        switch( p_vout->render.i_chroma )
        {
622 623
        case VLC_CODEC_I420:
        case VLC_CODEC_YV12:
624 625 626

            for( ; p_out < p_out_end ; )
            {
627
                vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
628

629
                p_out += p_outpic->p[i_plane].i_pitch;
630 631 632 633
                p_in += 2 * p_pic->p[i_plane].i_pitch;
            }
            break;

634
        case VLC_CODEC_I422:
635 636 637 638 639 640 641

            i_increment = 2 * p_pic->p[i_plane].i_pitch;

            if( i_plane == Y_PLANE )
            {
                for( ; p_out < p_out_end ; )
                {
642
                    vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
643
                    p_out += p_outpic->p[i_plane].i_pitch;
644
                    vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
645
                    p_out += p_outpic->p[i_plane].i_pitch;
646 647 648 649 650 651 652
                    p_in += i_increment;
                }
            }
            else
            {
                for( ; p_out < p_out_end ; )
                {
653
                    vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
654
                    p_out += p_outpic->p[i_plane].i_pitch;
655 656 657 658 659 660 661 662 663 664 665 666
                    p_in += i_increment;
                }
            }
            break;

        default:
            break;
        }
    }
}

/*****************************************************************************
667 668 669 670 671
 * RenderBob: renders a BOB picture - simple copy
 *****************************************************************************/
static void RenderBob( vout_thread_t *p_vout,
                       picture_t *p_outpic, picture_t *p_pic, int i_field )
{
672
    int i_plane;
673 674 675 676

    /* Copy image and skip lines */
    for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
    {
677
        uint8_t *p_in, *p_out_end, *p_out;
678 679 680 681

        p_in = p_pic->p[i_plane].p_pixels;
        p_out = p_outpic->p[i_plane].p_pixels;
        p_out_end = p_out + p_outpic->p[i_plane].i_pitch
682
                             * p_outpic->p[i_plane].i_visible_lines;
683

684
        switch( p_vout->render.i_chroma )
685
        {
686 687
            case VLC_CODEC_I420:
            case VLC_CODEC_YV12:
688 689 690
                /* For BOTTOM field we need to add the first line */
                if( i_field == 1 )
                {
691
                    vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
692
                    p_in += p_pic->p[i_plane].i_pitch;
693
                    p_out += p_outpic->p[i_plane].i_pitch;
694
                }
695

696
                p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
697

698 699
                for( ; p_out < p_out_end ; )
                {
700
                    vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
701

702
                    p_out += p_outpic->p[i_plane].i_pitch;
703

704
                    vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
705

706
                    p_in += 2 * p_pic->p[i_plane].i_pitch;
707
                    p_out += p_outpic->p[i_plane].i_pitch;
708
                }
709

710
                vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
711

712 713 714 715
                /* For TOP field we need to add the last line */
                if( i_field == 0 )
                {
                    p_in += p_pic->p[i_plane].i_pitch;
716
                    p_out += p_outpic->p[i_plane].i_pitch;
717
                    vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
718 719 720
                }
                break;

721
            case VLC_CODEC_I422:
722 723 724
                /* For BOTTOM field we need to add the first line */
                if( i_field == 1 )
                {
725
                    vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
726
                    p_in += p_pic->p[i_plane].i_pitch;
727
                    p_out += p_outpic->p[i_plane].i_pitch;
728 729 730 731 732 733 734 735
                }

                p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;

                if( i_plane == Y_PLANE )
                {
                    for( ; p_out < p_out_end ; )
                    {
736
                        vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
737

738
                        p_out += p_outpic->p[i_plane].i_pitch;
739

740
                        vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
741 742

                        p_in += 2 * p_pic->p[i_plane].i_pitch;
743
                        p_out += p_outpic->p[i_plane].i_pitch;
744 745 746 747 748 749
                    }
                }
                else
                {
                    for( ; p_out < p_out_end ; )
                    {
750
                        vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
751

752
                        p_out += p_outpic->p[i_plane].i_pitch;
753 754 755 756
                        p_in += 2 * p_pic->p[i_plane].i_pitch;
                    }
                }

757
                vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
758 759 760 761 762

                /* For TOP field we need to add the last line */
                if( i_field == 0 )
                {
                    p_in += p_pic->p[i_plane].i_pitch;
763
                    p_out += p_outpic->p[i_plane].i_pitch;
764
                    vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
765 766
                }
                break;
767
        }
768 769 770
    }
}

771
#define Merge p_vout->p_sys->pf_merge
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
772
#define EndMerge if(p_vout->p_sys->pf_end_merge) p_vout->p_sys->pf_end_merge
773

774 775
/*****************************************************************************
 * RenderLinear: BOB with linear interpolation
776 777 778 779 780 781 782 783 784
 *****************************************************************************/
static void RenderLinear( vout_thread_t *p_vout,
                          picture_t *p_outpic, picture_t *p_pic, int i_field )
{
    int i_plane;

    /* Copy image and skip lines */
    for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
    {
785
        uint8_t *p_in, *p_out_end, *p_out;
786

787
        p_in = p_pic->p[i_plane].p_pixels;
788 789
        p_out = p_outpic->p[i_plane].p_pixels;
        p_out_end = p_out + p_outpic->p[i_plane].i_pitch
790
                             * p_outpic->p[i_plane].i_visible_lines;
791

792 793
        /* For BOTTOM field we need to add the first line */
        if( i_field == 1 )
794
        {
795
            vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
796
            p_in += p_pic->p[i_plane].i_pitch;
797
            p_out += p_outpic->p[i_plane].i_pitch;
798 799
        }

800
        p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
801 802 803

        for( ; p_out < p_out_end ; )
        {
804
            vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
805

806
            p_out += p_outpic->p[i_plane].i_pitch;
807 808 809 810 811

            Merge( p_out, p_in, p_in + 2 * p_pic->p[i_plane].i_pitch,
                   p_pic->p[i_plane].i_pitch );

            p_in += 2 * p_pic->p[i_plane].i_pitch;
812
            p_out += p_outpic->p[i_plane].i_pitch;
813 814
        }

815
        vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
816 817

        /* For TOP field we need to add the last line */
818 819
        if( i_field == 0 )
        {
820
            p_in += p_pic->p[i_plane].i_pitch;
821
            p_out += p_outpic->p[i_plane].i_pitch;
822
            vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
823 824
        }
    }
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
825
    EndMerge();
826 827 828 829 830 831 832 833 834 835
}

static void RenderMean( vout_thread_t *p_vout,
                        picture_t *p_outpic, picture_t *p_pic )
{
    int i_plane;

    /* Copy image and skip lines */
    for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
    {
836
        uint8_t *p_in, *p_out_end, *p_out;
837 838 839 840 841

        p_in = p_pic->p[i_plane].p_pixels;

        p_out = p_outpic->p[i_plane].p_pixels;
        p_out_end = p_out + p_outpic->p[i_plane].i_pitch
842
                             * p_outpic->p[i_plane].i_visible_lines;
843 844 845 846 847 848 849

        /* All lines: mean value */
        for( ; p_out < p_out_end ; )
        {
            Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
                   p_pic->p[i_plane].i_pitch );

850
            p_out += p_outpic->p[i_plane].i_pitch;
851 852 853
            p_in += 2 * p_pic->p[i_plane].i_pitch;
        }
    }
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
854
    EndMerge();
855 856 857 858 859 860
}

static void RenderBlend( vout_thread_t *p_vout,
                         picture_t *p_outpic, picture_t *p_pic )
{
    int i_plane;
861

862 863 864
    /* Copy image and skip lines */
    for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
    {
865
        uint8_t *p_in, *p_out_end, *p_out;
866 867 868 869 870

        p_in = p_pic->p[i_plane].p_pixels;

        p_out = p_outpic->p[i_plane].p_pixels;
        p_out_end = p_out + p_outpic->p[i_plane].i_pitch
871
                             * p_outpic->p[i_plane].i_visible_lines;
872

873
        switch( p_vout->render.i_chroma )
874
        {
875 876
            case VLC_CODEC_I420:
            case VLC_CODEC_YV12:
877
                /* First line: simple copy */
878
                vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
879
                p_out += p_outpic->p[i_plane].i_pitch;
880

881 882 883
                /* Remaining lines: mean value */
                for( ; p_out < p_out_end ; )
                {
884 885
                    Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
                           p_pic->p[i_plane].i_pitch );
886

887
                    p_out += p_outpic->p[i_plane].i_pitch;