deinterlace.c 65.2 KB
Newer Older
1
2
3
/*****************************************************************************
 * deinterlace.c : deinterlacer plugin for vlc
 *****************************************************************************
ivoire's avatar
ivoire 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
dionoea's avatar
dionoea 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
#include <assert.h>
33

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

38
#include <vlc_common.h>
39
#include <vlc_plugin.h>
zorglub's avatar
zorglub committed
40
#include <vlc_vout.h>
ivoire's avatar
ivoire committed
41
#include <vlc_filter.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
42
#include <vlc_cpu.h>
43

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

zorglub's avatar
zorglub 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
#define DEINTERLACE_YADIF   7
#define DEINTERLACE_YADIF2X 8
58
59
60
61
62
63
64
65
66
67
68

/*****************************************************************************
 * 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 * );

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

72
static void RenderDiscard( vout_thread_t *, picture_t *, picture_t *, int );
73
74
75
76
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
77
static void RenderX      ( picture_t *, picture_t * );
78
static void RenderYadif  ( vout_thread_t *, picture_t *, picture_t *, int, int );
79

80
static void MergeGeneric ( void *, const void *, const void *, size_t );
sigmunau's avatar
sigmunau committed
81
#if defined(CAN_COMPILE_C_ALTIVEC)
82
static void MergeAltivec ( void *, const void *, const void *, size_t );
sigmunau's avatar
sigmunau committed
83
#endif
84
#if defined(CAN_COMPILE_MMXEXT)
85
86
87
88
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 );
sigmunau's avatar
sigmunau committed
89
#endif
Eric Petit's avatar
Eric Petit committed
90
#if defined(CAN_COMPILE_SSE)
sigmunau's avatar
sigmunau committed
91
static void MergeSSE2    ( void *, const void *, const void *, size_t );
sigmunau's avatar
sigmunau committed
92
#endif
93
#if defined(CAN_COMPILE_MMXEXT) || defined(CAN_COMPILE_SSE)
sigmunau's avatar
sigmunau committed
94
static void EndMMX       ( void );
sigmunau's avatar
sigmunau committed
95
#endif
96
97
98
#if defined(CAN_COMPILE_3DNOW)
static void End3DNow     ( void );
#endif
99
100
101
#if defined __ARM_NEON__
static void MergeNEON (void *, const void *, const void *, size_t);
#endif
102

ivoire's avatar
ivoire committed
103
static void SetFilterMethod( vout_thread_t *p_vout, const char *psz_method );
gbazin's avatar
   
gbazin committed
104
105
static vout_thread_t *SpawnRealVout( vout_thread_t *p_vout );

106
107
108
static int OpenFilter( vlc_object_t *p_this );
static void CloseFilter( vlc_object_t *p_this );

gbazin's avatar
   
gbazin committed
109
110
111
/*****************************************************************************
 * Callback prototypes
 *****************************************************************************/
112
113
static int FilterCallback( vlc_object_t *, char const *,
                           vlc_value_t, vlc_value_t, void * );
gbazin's avatar
   
gbazin committed
114

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

121
122
#define SOUT_MODE_TEXT N_("Streaming deinterlace mode")
#define SOUT_MODE_LONGTEXT N_("Deinterlace method to use for streaming.")
123

124
125
#define FILTER_CFG_PREFIX "sout-deinterlace-"

126
static const char *const mode_list[] = {
127
    "discard", "blend", "mean", "bob", "linear", "x", "yadif", "yadif2x" };
128
static const char *const mode_list_text[] = {
129
    N_("Discard"), N_("Blend"), N_("Mean"), N_("Bob"), N_("Linear"), "X", "Yadif", "Yadif (2x)" };
130

131
132
133
134
135
136
137
138
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)
139
    add_string( "filter-deinterlace-mode", "discard", NULL, MODE_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
140
                MODE_LONGTEXT, false )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
141
        change_string_list( mode_list, mode_list_text, 0 )
142
        change_safe ()
hartman's avatar
hartman committed
143

144
145
    add_shortcut( "deinterlace" )
    set_callbacks( Create, Destroy )
146

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

157
static const char *const ppsz_filter_options[] = {
158
159
160
    "mode", NULL
};

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.
 *****************************************************************************/
167
#define HISTORY_SIZE (3)
168
169
170
struct vout_sys_t
{
    int        i_mode;        /* Deinterlace mode */
171
    bool b_double_rate; /* Shall we double the framerate? */
172
    bool b_half_height; /* Shall be devide the height by 2 */
173
174
175
176
177

    mtime_t    last_date;
    mtime_t    next_date;

    vout_thread_t *p_vout;
gbazin's avatar
   
gbazin committed
178
179

    vlc_mutex_t filter_lock;
180
181

    void (*pf_merge) ( void *, const void *, const void *, size_t );
sigmunau's avatar
sigmunau committed
182
    void (*pf_end_merge) ( void );
183
184
185

    /* Yadif */
    picture_t *pp_history[HISTORY_SIZE];
186
187
};

188
189
190
191
192
193
194
195
/*****************************************************************************
 * 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 );
}

196
197
198
199
200
201
/*****************************************************************************
 * Create: allocates Deinterlace video thread output method
 *****************************************************************************
 * This function allocates and initializes a Deinterlace vout method.
 *****************************************************************************/
static int Create( vlc_object_t *p_this )
202
{
203
    vout_thread_t *p_vout = (vout_thread_t *)p_this;
ivoire's avatar
ivoire committed
204
    vout_sys_t *p_sys;
ivoire's avatar
ivoire committed
205
    char *psz_mode;
206
207

    /* Allocate structure */
ivoire's avatar
ivoire committed
208
    p_sys = p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
209
    if( p_vout->p_sys == NULL )
210
        return VLC_ENOMEM;
211
212
213
214
215
216

    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;
217
    p_vout->pf_control = Control;
218

ivoire's avatar
ivoire committed
219
220
221
222
223
224
    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 );
225

sigmunau's avatar
sigmunau committed
226
#if defined(CAN_COMPILE_C_ALTIVEC)
227
    if( vlc_CPU() & CPU_CAPABILITY_ALTIVEC )
228
    {
ivoire's avatar
ivoire committed
229
230
        p_sys->pf_merge = MergeAltivec;
        p_sys->pf_end_merge = NULL;
sigmunau's avatar
sigmunau committed
231
    }
232
    else
sigmunau's avatar
sigmunau committed
233
#endif
Eric Petit's avatar
Eric Petit committed
234
#if defined(CAN_COMPILE_SSE)
235
    if( vlc_CPU() & CPU_CAPABILITY_SSE2 )
sigmunau's avatar
sigmunau committed
236
    {
ivoire's avatar
ivoire committed
237
238
        p_sys->pf_merge = MergeSSE2;
        p_sys->pf_end_merge = EndMMX;
sigmunau's avatar
sigmunau committed
239
    }
sigmunau's avatar
sigmunau committed
240
241
    else
#endif
242
#if defined(CAN_COMPILE_MMXEXT)
243
    if( vlc_CPU() & CPU_CAPABILITY_MMXEXT )
sigmunau's avatar
sigmunau committed
244
    {
ivoire's avatar
ivoire committed
245
246
        p_sys->pf_merge = MergeMMXEXT;
        p_sys->pf_end_merge = EndMMX;
247
248
    }
    else
249
250
#endif
#if defined(CAN_COMPILE_3DNOW)
251
    if( vlc_CPU() & CPU_CAPABILITY_3DNOW )
252
    {
ivoire's avatar
ivoire committed
253
254
        p_sys->pf_merge = Merge3DNow;
        p_sys->pf_end_merge = End3DNow;
255
256
    }
    else
257
258
259
260
261
262
263
264
#endif
#if defined __ARM_NEON__
    if( vlc_CPU() & CPU_CAPABILITY_NEON )
    {
        p_sys->pf_merge = MergeNEON;
        p_sys->pf_end_merge = NULL;
    }
    else
265
#endif
266
    {
ivoire's avatar
ivoire committed
267
268
        p_sys->pf_merge = MergeGeneric;
        p_sys->pf_end_merge = NULL;
269
270
    }

271
    /* Look what method was requested */
272
    psz_mode = var_CreateGetString( p_vout, "filter-deinterlace-mode" );
gbazin's avatar
   
gbazin committed
273

ivoire's avatar
ivoire committed
274
    if( !psz_mode )
275
    {
276
        msg_Err( p_vout, "configuration variable filter-deinterlace-mode empty" );
277
278
        msg_Err( p_vout, "no deinterlace mode provided, using \"discard\"" );

ivoire's avatar
ivoire committed
279
        psz_mode = strdup( "discard" );
280
281
    }

ivoire's avatar
ivoire committed
282
    SetFilterMethod( p_vout, psz_mode );
gbazin's avatar
   
gbazin committed
283

ivoire's avatar
ivoire committed
284
    free( psz_mode );
gbazin's avatar
   
gbazin committed
285

286
    return VLC_SUCCESS;
287
288
}

gbazin's avatar
   
gbazin committed
289
290
291
/*****************************************************************************
 * SetFilterMethod: setup the deinterlace method to use.
 *****************************************************************************/
ivoire's avatar
ivoire committed
292
static void SetFilterMethod( vout_thread_t *p_vout, const char *psz_method )
gbazin's avatar
   
gbazin committed
293
{
ivoire's avatar
ivoire committed
294
    vout_sys_t *p_sys = p_vout->p_sys;
295
    if( !strcmp( psz_method, "mean" ) )
gbazin's avatar
   
gbazin committed
296
    {
ivoire's avatar
ivoire committed
297
298
299
        p_sys->i_mode = DEINTERLACE_MEAN;
        p_sys->b_double_rate = false;
        p_sys->b_half_height = true;
gbazin's avatar
   
gbazin committed
300
301
302
303
304
    }
    else if( !strcmp( psz_method, "blend" )
             || !strcmp( psz_method, "average" )
             || !strcmp( psz_method, "combine-fields" ) )
    {
ivoire's avatar
ivoire committed
305
306
307
        p_sys->i_mode = DEINTERLACE_BLEND;
        p_sys->b_double_rate = false;
        p_sys->b_half_height = false;
gbazin's avatar
   
gbazin committed
308
309
310
311
    }
    else if( !strcmp( psz_method, "bob" )
             || !strcmp( psz_method, "progressive-scan" ) )
    {
ivoire's avatar
ivoire committed
312
313
314
        p_sys->i_mode = DEINTERLACE_BOB;
        p_sys->b_double_rate = true;
        p_sys->b_half_height = false;
gbazin's avatar
   
gbazin committed
315
316
317
    }
    else if( !strcmp( psz_method, "linear" ) )
    {
ivoire's avatar
ivoire committed
318
319
320
        p_sys->i_mode = DEINTERLACE_LINEAR;
        p_sys->b_double_rate = true;
        p_sys->b_half_height = false;
gbazin's avatar
   
gbazin committed
321
    }
322
323
    else if( !strcmp( psz_method, "x" ) )
    {
ivoire's avatar
ivoire committed
324
325
326
        p_sys->i_mode = DEINTERLACE_X;
        p_sys->b_double_rate = false;
        p_sys->b_half_height = false;
327
    }
328
329
330
331
332
333
334
335
336
337
338
339
    else if( !strcmp( psz_method, "yadif" ) )
    {
        p_sys->i_mode = DEINTERLACE_YADIF;
        p_sys->b_double_rate = false;
        p_sys->b_half_height = false;
    }
    else if( !strcmp( psz_method, "yadif2x" ) )
    {
        p_sys->i_mode = DEINTERLACE_YADIF2X;
        p_sys->b_double_rate = true;
        p_sys->b_half_height = false;
    }
gbazin's avatar
   
gbazin committed
340
341
    else
    {
342
343
        const bool b_i422 = p_vout->render.i_chroma == VLC_CODEC_I422 ||
                            p_vout->render.i_chroma == VLC_CODEC_J422;
344
345
346
347
        if( strcmp( psz_method, "discard" ) )
            msg_Err( p_vout, "no valid deinterlace mode provided, "
                     "using \"discard\"" );

ivoire's avatar
ivoire committed
348
349
350
        p_sys->i_mode = DEINTERLACE_DISCARD;
        p_sys->b_double_rate = false;
        p_sys->b_half_height = !b_i422;
gbazin's avatar
   
gbazin committed
351
    }
gbazin's avatar
   
gbazin committed
352
353

    msg_Dbg( p_vout, "using %s deinterlace method", psz_method );
gbazin's avatar
   
gbazin committed
354
355
}

356
357
358
359
360
361
362
363
364
365
366
367
368
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;
    }

369
370
    if( p_src->i_chroma == VLC_CODEC_I422 ||
        p_src->i_chroma == VLC_CODEC_J422 )
371
372
373
374
375
376
    {
        switch( p_vout->p_sys->i_mode )
        {
        case DEINTERLACE_MEAN:
        case DEINTERLACE_LINEAR:
        case DEINTERLACE_X:
377
378
        case DEINTERLACE_YADIF:
        case DEINTERLACE_YADIF2X:
379
            p_dst->i_chroma = p_src->i_chroma;
380
381
            break;
        default:
382
383
            p_dst->i_chroma = p_src->i_chroma == VLC_CODEC_I422 ? VLC_CODEC_I420 :
                                                                  VLC_CODEC_J420;
384
385
386
387
388
389
390
            break;
        }
    }
}

static bool IsChromaSupported( vlc_fourcc_t i_chroma )
{
391
    return i_chroma == VLC_CODEC_I420 ||
392
           i_chroma == VLC_CODEC_J420 ||
393
           i_chroma == VLC_CODEC_YV12 ||
394
395
           i_chroma == VLC_CODEC_I422 ||
           i_chroma == VLC_CODEC_J422;
396
397
}

398
399
400
401
402
403
404
/*****************************************************************************
 * Init: initialize Deinterlace video thread output method
 *****************************************************************************/
static int Init( vout_thread_t *p_vout )
{
    I_OUTPUTPICTURES = 0;

405
406
407
    if( !IsChromaSupported( p_vout->render.i_chroma ) )
        return VLC_EGENERIC; /* unknown chroma */

408
409
    /* Initialize the output structure, full of directbuffers since we want
     * the decoder to output directly to our structures. */
410
411
412
413
414
    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;
415

gbazin's avatar
   
gbazin committed
416
417
418
419
420
421
422
423
424
425
426
    /* 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;
    }

427
428
429
    for( int i = 0; i < HISTORY_SIZE; i++ )
        p_vout->p_sys->pp_history[i] = NULL;

430
    vout_filter_AllocateDirectBuffers( p_vout, VOUT_MAX_PICTURES );
gbazin's avatar
   
gbazin committed
431

432
    vout_filter_AddChild( p_vout, p_vout->p_sys->p_vout, MouseEvent );
gbazin's avatar
   
gbazin committed
433

434
    var_AddCallback( p_vout, "filter-deinterlace-mode", FilterCallback, NULL );
435

gbazin's avatar
   
gbazin committed
436
437
438
439
440
441
442
443
    return VLC_SUCCESS;
}

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

446
447
    video_format_t fmt;
    GetOutputFormat( p_vout, &fmt, &p_vout->fmt_out );
448

449
    return vout_Create( p_vout, &fmt );
450
451
452
453
454
455
456
}

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

459
    var_DelCallback( p_vout, "filter-deinterlace-mode", FilterCallback, NULL );
460

461
462
463
464
465
466
    for( int i = 0; i < HISTORY_SIZE; i++ )
    {
        if( p_sys->pp_history[i] )
            picture_Release( p_sys->pp_history[i] );
    }

467
    if( p_sys->p_vout )
468
    {
469
470
        vout_filter_DelChild( p_vout, p_sys->p_vout, MouseEvent );
        vout_CloseAndRelease( p_sys->p_vout );
471
472
    }

473
    vout_filter_ReleaseDirectBuffers( p_vout );
474
}
gbazin's avatar
   
gbazin committed
475

476
477
478
479
480
481
482
483
484
/*****************************************************************************
 * 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 );
485
486
487
    free( p_vout->p_sys );
}

488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
/**
 * 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 );
}

503
504
505
506
507
508
509
510
511
/*****************************************************************************
 * 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 )
{
512
    vout_sys_t *p_sys = p_vout->p_sys;
513
514
    picture_t *pp_outpic[2];

515
516
517
518
519
520
521
522
523
524
525
526
    /* 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 )
527
    {
528
        p_sys->p_vout->fmt_in.i_y_offset /= 2;
529
530
        p_sys->p_vout->fmt_in.i_visible_height /= 2;
    }
531

532
    if( p_vout->i_changes & VOUT_ASPECT_CHANGE )
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
    {
        p_vout->i_changes &= ~VOUT_ASPECT_CHANGE;

        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;

551
552
    pp_outpic[0] = pp_outpic[1] = NULL;

gbazin's avatar
   
gbazin committed
553
554
    vlc_mutex_lock( &p_vout->p_sys->filter_lock );

555
556
    /* Get a new picture */
    while( ( pp_outpic[0] = vout_CreatePicture( p_vout->p_sys->p_vout,
557
                                                0, 0, 0 ) )
558
559
              == NULL )
    {
560
        if( !vlc_object_alive( p_vout ) || p_vout->b_error )
561
        {
gbazin's avatar
   
gbazin committed
562
            vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
563
564
565
            return;
        }
        msleep( VOUT_OUTMEM_SLEEP );
566
    }
567

568
    pp_outpic[0]->date = p_pic->date;
569
570
571
572
573
574
575
576

    /* 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 )
        {
577
            if( !vlc_object_alive( p_vout ) || p_vout->b_error )
578
579
            {
                vout_DestroyPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
gbazin's avatar
   
gbazin committed
580
                vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
581
582
                return;
            }
583
            msleep( VOUT_OUTMEM_SLEEP );
584
        }
585
586
587

        /* 20ms is a bit arbitrary, but it's only for the first image we get */
        if( !p_vout->p_sys->last_date )
588
            pp_outpic[1]->date = p_pic->date + 20000;
589
        else
590
            pp_outpic[1]->date = (3 * p_pic->date - p_vout->p_sys->last_date) / 2;
591
592
593
594
595
596
        p_vout->p_sys->last_date = p_pic->date;
    }

    switch( p_vout->p_sys->i_mode )
    {
        case DEINTERLACE_DISCARD:
597
            RenderDiscard( p_vout, pp_outpic[0], p_pic, 0 );
598
599
600
601
            vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
            break;

        case DEINTERLACE_BOB:
602
            RenderBob( p_vout, pp_outpic[0], p_pic, p_pic->b_top_field_first ? 0 : 1 );
603
            vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
604
            RenderBob( p_vout, pp_outpic[1], p_pic, p_pic->b_top_field_first ? 1 : 0 );
605
606
607
608
            vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[1] );
            break;

        case DEINTERLACE_LINEAR:
609
            RenderLinear( p_vout, pp_outpic[0], p_pic, p_pic->b_top_field_first ? 0 : 1 );
610
            vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
611
            RenderLinear( p_vout, pp_outpic[1], p_pic, p_pic->b_top_field_first ? 1 : 0 );
612
613
614
615
616
617
618
619
620
621
622
623
            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;
gbazin's avatar
   
gbazin committed
624

625
        case DEINTERLACE_X:
Rafaël Carré's avatar
Rafaël Carré committed
626
            RenderX( pp_outpic[0], p_pic );
627
628
            vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
            break;
629
630
631
632
633
634
635
636
637
638
639
640

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

        case DEINTERLACE_YADIF2X:
            RenderYadif( p_vout, pp_outpic[0], p_pic, 0, p_pic->b_top_field_first ? 0 : 1 );
            vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
            RenderYadif( p_vout, pp_outpic[1], p_pic, 1, p_pic->b_top_field_first ? 1 : 0 );
            vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[1] );
            break;
641
    }
gbazin's avatar
   
gbazin committed
642
    vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
643
644
645
}

/*****************************************************************************
646
 * RenderDiscard: only keep TOP or BOTTOM field, discard the other.
647
 *****************************************************************************/
648
649
static void RenderDiscard( vout_thread_t *p_vout,
                           picture_t *p_outpic, picture_t *p_pic, int i_field )
650
651
652
653
654
655
{
    int i_plane;

    /* Copy image and skip lines */
    for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
    {
656
        uint8_t *p_in, *p_out_end, *p_out;
657
658
659
660
661
662
663
        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
664
                             * p_outpic->p[i_plane].i_visible_lines;
665
666
667

        switch( p_vout->render.i_chroma )
        {
668
        case VLC_CODEC_I420:
669
        case VLC_CODEC_J420:
670
        case VLC_CODEC_YV12:
671
672
673

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

676
                p_out += p_outpic->p[i_plane].i_pitch;
677
678
679
680
                p_in += 2 * p_pic->p[i_plane].i_pitch;
            }
            break;

681
        case VLC_CODEC_I422:
682
        case VLC_CODEC_J422:
683
684
685
686
687
688
689

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

            if( i_plane == Y_PLANE )
            {
                for( ; p_out < p_out_end ; )
                {
690
                    vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
691
                    p_out += p_outpic->p[i_plane].i_pitch;
692
                    vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
693
                    p_out += p_outpic->p[i_plane].i_pitch;
694
695
696
697
698
699
700
                    p_in += i_increment;
                }
            }
            else
            {
                for( ; p_out < p_out_end ; )
                {
701
                    vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
702
                    p_out += p_outpic->p[i_plane].i_pitch;
703
704
705
706
707
708
709
710
711
712
713
714
                    p_in += i_increment;
                }
            }
            break;

        default:
            break;
        }
    }
}

/*****************************************************************************
715
716
717
718
719
 * 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 )
{
720
    int i_plane;
721
722
723
724

    /* Copy image and skip lines */
    for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
    {
725
        uint8_t *p_in, *p_out_end, *p_out;
726
727
728
729

        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
730
                             * p_outpic->p[i_plane].i_visible_lines;
731

732
        switch( p_vout->render.i_chroma )
733
        {
734
            case VLC_CODEC_I420:
735
            case VLC_CODEC_J420:
736
            case VLC_CODEC_YV12:
737
738
739
                /* For BOTTOM field we need to add the first line */
                if( i_field == 1 )
                {
740
                    vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
741
                    p_in += p_pic->p[i_plane].i_pitch;
742
                    p_out += p_outpic->p[i_plane].i_pitch;
743
                }
744

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

747
748
                for( ; p_out < p_out_end ; )
                {
749
                    vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
750

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

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

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

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

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

770
            case VLC_CODEC_I422:
771
            case VLC_CODEC_J422:
772
773
774
                /* For BOTTOM field we need to add the first line */
                if( i_field == 1 )
                {
775
                    vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
776
                    p_in += p_pic->p[i_plane].i_pitch;
777
                    p_out += p_outpic->p[i_plane].i_pitch;
778
779
780
781
782
783
784
785
                }

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

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

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

790
                        vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
791
792

                        p_in += 2 * p_pic->p[i_plane].i_pitch;
793
                        p_out += p_outpic->p[i_plane].i_pitch;
794
795
796
797
798
799
                    }
                }
                else
                {
                    for( ; p_out < p_out_end ; )
                    {
800
                        vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
801

802
                        p_out += p_outpic->p[i_plane].i_pitch;
803
804
805
806
                        p_in += 2 * p_pic->p[i_plane].i_pitch;
                    }
                }

807
                vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
808
809
810
811
812

                /* For TOP field we need to add the last line */
                if( i_field == 0 )
                {
                    p_in += p_pic->p[i_plane].i_pitch;
813
                    p_out += p_outpic->p[i_plane].i_pitch;
814
                    vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
815
816
                }
                break;
817
        }
818
819
820
    }
}

821
#define Merge p_vout->p_sys->pf_merge
sigmunau's avatar
sigmunau committed
822
#define EndMerge if(p_vout->p_sys->pf_end_merge) p_vout->p_sys->pf_end_merge
823

824
825
/*****************************************************************************
 * RenderLinear: BOB with linear interpolation
826
827
828
829
830
831
832
833
834
 *****************************************************************************/
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++ )
    {
835
        uint8_t *p_in, *p_out_end, *p_out;
836

837
        p_in = p_pic->p[i_plane].p_pixels;
838
839
        p_out = p_outpic->p[i_plane].p_pixels;
        p_out_end = p_out + p_outpic->p[i_plane].i_pitch
840
                             * p_outpic->p[i_plane].i_visible_lines;
841

842
843
        /* For BOTTOM field we need to add the first line */
        if( i_field == 1 )
844
        {
845
            vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
846
            p_in += p_pic->p[i_plane].i_pitch;
847
            p_out += p_outpic->p[i_plane].i_pitch;
848
849
        }

850
        p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
851
852
853

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

856
            p_out += p_outpic->p[i_plane].i_pitch;
857
858
859
860
861

            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;
862
            p_out += p_outpic->p[i_plane].i_pitch;
863
864
        }

865
        vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
866
867

        /* For TOP field we need to add the last line */
868
869
        if( i_field == 0 )
        {
870
            p_in += p_pic->p[i_plane].i_pitch;
871
            p_out += p_outpic->p[i_plane].i_pitch;
872
            vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
873
874
        }
    }
sigmunau's avatar
sigmunau committed
875
    EndMerge();
876
877
878
879
880
881
882
883
884
885
}

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++ )
    {
886
        uint8_t *p_in, *p_out_end, *p_out;
887
888
889
890
891

        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
892
                             * p_outpic->p[i_plane].i_visible_lines;
893
894
895
896
897
898
899

        /* 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 );

900
            p_out += p_outpic->p[i_plane].i_pitch;
901
902
903
            p_in += 2 * p_pic->p[i_plane].i_pitch;
        }
    }
sigmunau's avatar
sigmunau committed
904
    EndMerge();
905
906
907
908
909
910
}

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

912
913
914
    /* Copy image and skip lines */
    for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
    {
915
        uint8_t *p_in, *p_out_end, *p_out;
916
917
918
919
920

        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
921
                             * p_outpic->p[i_plane].i_visible_lines;
922

923
        switch( p_vout->render.i_chroma )
924
        {
925
            case VLC_CODEC_I420:
926
            case VLC_CODEC_J420:
927
            case VLC_CODEC_YV12:
928
                /* First line: simple copy */
929
                vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
930
                p_out += p_outpic->p[i_plane].i_pitch;
931

932
933
934
                /* Remaining lines: mean value */
                for( ; p_out < p_out_end ; )
                {
935
936
                    Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
                           p_pic->p[i_plane].i_pitch );
937

938
                    p_out += p_outpic->p[i_plane].i_pitch;
939
940
941
942
                    p_in += p_pic->p[i_plane].i_pitch;
                }
                break;

943
            case VLC_CODEC_I422:
944
            case VLC_CODEC_J422:
945
                /* First line: simple copy */
946
                vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
947
                p_out += p_outpic->p[i_plane].i_pitch;
948
949
950
951
952
953
954
955
956

                /* Remaining lines: mean value */
                if( i_plane == Y_PLANE )
                {
                    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 );

957
                        p_out += p_outpic->p[i_plane].i_pitch;
958
959
960
961
962
963
964
965
966
967
968
                        p_in += p_pic->p[i_plane].i_pitch;
                    }
                }

                else
                {
                    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 );

969
                        p_out += p_outpic->p[i_plane].i_pitch;
970
971
972
973
                        p_in += 2*p_pic->p[i_plane].i_pitch;
                    }
                }
                break;
974
975
        }
    }
sigmunau's avatar
sigmunau committed
976
    EndMerge();
977
978
}

979
980
981
982
#undef Merge

static void MergeGeneric( void *_p_dest, const void *_p_s1,
                          const void *_p_s2, size_t i_bytes )
983
{
984
985
986
987
    uint8_t* p_dest = (uint8_t*)_p_dest;
    const uint8_t *p_s1 = (const uint8_t *)_p_s1;
    const uint8_t *p_s2 = (const uint8_t *)_p_s2;
    uint8_t* p_end = p_dest + i_bytes - 8;
988

Sam Hocevar's avatar