]> git.sesse.net Git - vlc/blob - modules/video_filter/deinterlace/deinterlace.c
100cc19edc56db4bad35df3847f61d6e45dcc21b
[vlc] / modules / video_filter / deinterlace / deinterlace.c
1 /*****************************************************************************
2  * deinterlace.c : deinterlacer plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000-2011 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Author: Sam Hocevar <sam@zoy.org>
8  *         Christophe Massiot <massiot@via.ecp.fr>
9  *         Laurent Aimar <fenrir@videolan.org>
10  *         Juha Jeronen <juha.jeronen@jyu.fi>
11  *         ...and others
12  *
13  * This program is free software; you can redistribute it and/or modify it
14  * under the terms of the GNU Lesser General Public License as published by
15  * the Free Software Foundation; either version 2.1 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU Lesser General Public License for more details.
22  *
23  * You should have received a copy of the GNU Lesser General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31
32 #ifdef HAVE_CONFIG_H
33 # include "config.h"
34 #endif
35
36 #include <assert.h>
37 #include <stdint.h>
38
39 #include <vlc_common.h>
40 #include <vlc_plugin.h>
41 #include <vlc_filter.h>
42 #include <vlc_cpu.h>
43 #include <vlc_mouse.h>
44
45 #include "deinterlace.h"
46 #include "helpers.h"
47 #include "merge.h"
48
49 /*****************************************************************************
50  * Module descriptor
51  *****************************************************************************/
52
53 #define MODE_TEXT N_("Deinterlace mode")
54
55 #define SOUT_MODE_TEXT N_("Streaming deinterlace mode")
56 #define SOUT_MODE_LONGTEXT N_("Deinterlace method to use for streaming.")
57
58 #define FILTER_CFG_PREFIX "sout-deinterlace-"
59
60 /* Tooltips drop linefeeds (at least in the Qt GUI);
61    thus the space before each set of consecutive \n.
62
63    See phosphor.h for phosphor_chroma_list and phosphor_dimmer_list.
64 */
65 #define PHOSPHOR_CHROMA_TEXT N_("Phosphor chroma mode for 4:2:0 input")
66 #define PHOSPHOR_CHROMA_LONGTEXT N_("Choose handling for colours in those "\
67                                     "output frames that fall across input "\
68                                     "frame boundaries. \n"\
69                                     "\n"\
70                                     "Latest: take chroma from new (bright) "\
71                                     "field only. Good for interlaced input, "\
72                                     "such as videos from a camcorder. \n"\
73                                     "\n"\
74                                     "AltLine: take chroma line 1 from top "\
75                                     "field, line 2 from bottom field, etc. \n"\
76                                     "Default, good for NTSC telecined input "\
77                                     "(anime DVDs, etc.). \n"\
78                                     "\n"\
79                                     "Blend: average input field chromas. "\
80                                     "May distort the colours of the new "\
81                                     "(bright) field, too. \n"\
82                                     "\n"\
83                                     "Upconvert: output in 4:2:2 format "\
84                                     "(independent chroma for each field). "\
85                                     "Best simulation, but requires more CPU "\
86                                     "and memory bandwidth.")
87
88 #define PHOSPHOR_DIMMER_TEXT N_("Phosphor old field dimmer strength")
89 #define PHOSPHOR_DIMMER_LONGTEXT N_("This controls the strength of the "\
90                                     "darkening filter that simulates CRT TV "\
91                                     "phosphor light decay for the old field "\
92                                     "in the Phosphor framerate doubler. "\
93                                     "Default: Low.")
94
95 vlc_module_begin ()
96     set_description( N_("Deinterlacing video filter") )
97     set_shortname( N_("Deinterlace" ))
98     set_capability( "video filter2", 0 )
99     set_category( CAT_VIDEO )
100     set_subcategory( SUBCAT_VIDEO_VFILTER )
101
102     add_string( FILTER_CFG_PREFIX "mode", "blend", SOUT_MODE_TEXT,
103                 SOUT_MODE_LONGTEXT, false )
104         change_string_list( mode_list, mode_list_text )
105         change_safe ()
106     add_integer( FILTER_CFG_PREFIX "phosphor-chroma", 2, PHOSPHOR_CHROMA_TEXT,
107                 PHOSPHOR_CHROMA_LONGTEXT, true )
108         change_integer_list( phosphor_chroma_list, phosphor_chroma_list_text )
109         change_safe ()
110     add_integer( FILTER_CFG_PREFIX "phosphor-dimmer", 2, PHOSPHOR_DIMMER_TEXT,
111                 PHOSPHOR_DIMMER_LONGTEXT, true )
112         change_integer_list( phosphor_dimmer_list, phosphor_dimmer_list_text )
113         change_safe ()
114     add_shortcut( "deinterlace" )
115     set_callbacks( Open, Close )
116 vlc_module_end ()
117
118 /*****************************************************************************
119  * Local data
120  *****************************************************************************/
121
122 /**
123  * Available config options for the deinterlacer module.
124  *
125  * Note that also algorithm-specific options must be listed here,
126  * and reading logic for them implemented in Open().
127  */
128 static const char *const ppsz_filter_options[] = {
129     "mode", "phosphor-chroma", "phosphor-dimmer",
130     NULL
131 };
132
133 /*****************************************************************************
134  * SetFilterMethod: setup the deinterlace method to use.
135  *****************************************************************************/
136
137 void SetFilterMethod( filter_t *p_filter, const char *psz_method )
138 {
139     filter_sys_t *p_sys = p_filter->p_sys;
140
141     if( !psz_method )
142         psz_method = "";
143
144     p_sys->b_double_rate = false;
145     p_sys->b_half_height = false;
146     p_sys->b_use_frame_history = false;
147
148     if( !strcmp( psz_method, "mean" ) )
149     {
150         p_sys->i_mode = DEINTERLACE_MEAN;
151         p_sys->b_half_height = true;
152     }
153     else if( !strcmp( psz_method, "bob" )
154              || !strcmp( psz_method, "progressive-scan" ) )
155     {
156         p_sys->i_mode = DEINTERLACE_BOB;
157         p_sys->b_double_rate = true;
158     }
159     else if( !strcmp( psz_method, "linear" ) )
160     {
161         p_sys->i_mode = DEINTERLACE_LINEAR;
162         p_sys->b_double_rate = true;
163     }
164     else if( !strcmp( psz_method, "x" ) && p_sys->chroma->pixel_size == 1 )
165     {
166         p_sys->i_mode = DEINTERLACE_X;
167     }
168     else if( !strcmp( psz_method, "yadif" ) )
169     {
170         p_sys->i_mode = DEINTERLACE_YADIF;
171         p_sys->b_use_frame_history = true;
172     }
173     else if( !strcmp( psz_method, "yadif2x" ) )
174     {
175         p_sys->i_mode = DEINTERLACE_YADIF2X;
176         p_sys->b_double_rate = true;
177         p_sys->b_use_frame_history = true;
178     }
179     else if( !strcmp( psz_method, "phosphor" ) && p_sys->chroma->pixel_size == 1 )
180     {
181         p_sys->i_mode = DEINTERLACE_PHOSPHOR;
182         p_sys->b_double_rate = true;
183         p_sys->b_use_frame_history = true;
184     }
185     else if( !strcmp( psz_method, "ivtc" ) && p_sys->chroma->pixel_size == 1 )
186     {
187         p_sys->i_mode = DEINTERLACE_IVTC;
188         p_sys->b_use_frame_history = true;
189     }
190     else if( !strcmp( psz_method, "discard" ) )
191     {
192         p_sys->i_mode = DEINTERLACE_DISCARD;
193         p_sys->b_half_height = true;
194     }
195     else
196     {
197         if( strcmp( psz_method, "blend" ) )
198             msg_Err( p_filter,
199                      "no valid/compatible deinterlace mode provided, using \"blend\"" );
200
201         p_sys->i_mode = DEINTERLACE_BLEND;
202     }
203
204     p_sys->i_frame_offset = 0; /* reset to default when method changes */
205
206     msg_Dbg( p_filter, "using %s deinterlace method", psz_method );
207 }
208
209 /*****************************************************************************
210  * GetOutputFormat: return which format the chosen algorithm outputs.
211  *****************************************************************************/
212
213 void GetOutputFormat( filter_t *p_filter,
214                       video_format_t *p_dst, const video_format_t *p_src )
215 {
216     filter_sys_t *p_sys = p_filter->p_sys;
217     *p_dst = *p_src;
218
219     if( p_sys->b_half_height )
220     {
221         p_dst->i_height /= 2;
222         p_dst->i_visible_height /= 2;
223         p_dst->i_y_offset /= 2;
224         p_dst->i_sar_den *= 2;
225     }
226
227     if( p_sys->b_double_rate )
228     {
229         p_dst->i_frame_rate *= 2;
230     }
231
232     if( p_sys->i_mode == DEINTERLACE_PHOSPHOR  &&
233         2 * p_sys->chroma->p[1].h.num == p_sys->chroma->p[1].h.den &&
234         2 * p_sys->chroma->p[2].h.num == p_sys->chroma->p[2].h.den &&
235         p_sys->phosphor.i_chroma_for_420 == PC_UPCONVERT )
236     {
237         p_dst->i_chroma = p_src->i_chroma == VLC_CODEC_J420 ? VLC_CODEC_J422 :
238                                                               VLC_CODEC_I422;
239     }
240     else
241     {
242         p_dst->i_chroma = p_src->i_chroma;
243     }
244
245 }
246
247 /*****************************************************************************
248  * video filter2 functions
249  *****************************************************************************/
250
251 #define DEINTERLACE_DST_SIZE 3
252
253 /* This is the filter function. See Open(). */
254 picture_t *Deinterlace( filter_t *p_filter, picture_t *p_pic )
255 {
256     filter_sys_t *p_sys = p_filter->p_sys;
257     picture_t *p_dst[DEINTERLACE_DST_SIZE];
258
259     /* Request output picture */
260     p_dst[0] = filter_NewPicture( p_filter );
261     if( p_dst[0] == NULL )
262     {
263         picture_Release( p_pic );
264         return NULL;
265     }
266     picture_CopyProperties( p_dst[0], p_pic );
267
268     /* Any unused p_dst pointers must be NULL, because they are used to
269        check how many output frames we have. */
270     for( int i = 1; i < DEINTERLACE_DST_SIZE; ++i )
271         p_dst[i] = NULL;
272
273     /* Update the input frame history, if the currently active algorithm
274        needs it. */
275     if( p_sys->b_use_frame_history )
276     {
277         /* Duplicate the picture
278          * TODO when the vout rework is finished, picture_Hold() might be enough
279          * but becarefull, the pitches must match */
280         picture_t *p_dup = picture_NewFromFormat( &p_pic->format );
281         if( p_dup )
282             picture_Copy( p_dup, p_pic );
283
284         /* Slide the history */
285         if( p_sys->pp_history[0] )
286             picture_Release( p_sys->pp_history[0] );
287         for( int i = 1; i < HISTORY_SIZE; i++ )
288             p_sys->pp_history[i-1] = p_sys->pp_history[i];
289         p_sys->pp_history[HISTORY_SIZE-1] = p_dup;
290     }
291
292     /* Slide the metadata history. */
293     for( int i = 1; i < METADATA_SIZE; i++ )
294     {
295         p_sys->meta.pi_date[i-1]            = p_sys->meta.pi_date[i];
296         p_sys->meta.pi_nb_fields[i-1]       = p_sys->meta.pi_nb_fields[i];
297         p_sys->meta.pb_top_field_first[i-1] = p_sys->meta.pb_top_field_first[i];
298     }
299     /* The last element corresponds to the current input frame. */
300     p_sys->meta.pi_date[METADATA_SIZE-1]            = p_pic->date;
301     p_sys->meta.pi_nb_fields[METADATA_SIZE-1]       = p_pic->i_nb_fields;
302     p_sys->meta.pb_top_field_first[METADATA_SIZE-1] = p_pic->b_top_field_first;
303
304     /* Remember the frame offset that we should use for this frame.
305        The value in p_sys will be updated to reflect the correct value
306        for the *next* frame when we call the renderer. */
307     int i_frame_offset = p_sys->i_frame_offset;
308     int i_meta_idx     = (METADATA_SIZE-1) - i_frame_offset;
309
310     /* These correspond to the current *outgoing* frame. */
311     bool b_top_field_first;
312     int i_nb_fields;
313     if( i_frame_offset != CUSTOM_PTS )
314     {
315         /* Pick the correct values from the history. */
316         b_top_field_first = p_sys->meta.pb_top_field_first[i_meta_idx];
317         i_nb_fields       = p_sys->meta.pi_nb_fields[i_meta_idx];
318     }
319     else
320     {
321         /* Framerate doublers must not request CUSTOM_PTS, as they need the
322            original field timings, and need Deinterlace() to allocate the
323            correct number of output frames. */
324         assert( !p_sys->b_double_rate );
325
326         /* NOTE: i_nb_fields is only used for framerate doublers, so it is
327                  unused in this case. b_top_field_first is only passed to the
328                  algorithm. We assume that algorithms that request CUSTOM_PTS
329                  will, if necessary, extract the TFF/BFF information themselves.
330         */
331         b_top_field_first = p_pic->b_top_field_first; /* this is not guaranteed
332                                                          to be meaningful */
333         i_nb_fields       = p_pic->i_nb_fields;       /* unused */
334     }
335
336     /* For framerate doublers, determine field duration and allocate
337        output frames. */
338     mtime_t i_field_dur = 0;
339     int i_double_rate_alloc_end = 0; /* One past last for allocated output
340                                         frames in p_dst[]. Used only for
341                                         framerate doublers. Will be inited
342                                         below. Declared here because the
343                                         PTS logic needs the result. */
344     if( p_sys->b_double_rate )
345     {
346         /* Calculate one field duration. */
347         int i = 0;
348         int iend = METADATA_SIZE-1;
349         /* Find oldest valid logged date.
350            The current input frame doesn't count. */
351         for( ; i < iend; i++ )
352             if( p_sys->meta.pi_date[i] > VLC_TS_INVALID )
353                 break;
354         if( i < iend )
355         {
356             /* Count how many fields the valid history entries
357                (except the new frame) represent. */
358             int i_fields_total = 0;
359             for( int j = i ; j < iend; j++ )
360                 i_fields_total += p_sys->meta.pi_nb_fields[j];
361             /* One field took this long. */
362             i_field_dur = (p_pic->date - p_sys->meta.pi_date[i]) / i_fields_total;
363         }
364         /* Note that we default to field duration 0 if it could not be
365            determined. This behaves the same as the old code - leaving the
366            extra output frame dates the same as p_pic->date if the last cached
367            date was not valid.
368         */
369
370         i_double_rate_alloc_end = i_nb_fields;
371         if( i_nb_fields > DEINTERLACE_DST_SIZE )
372         {
373             /* Note that the effective buffer size depends also on the constant
374                private_picture in vout_wrapper.c, since that determines the
375                maximum number of output pictures filter_NewPicture() will
376                successfully allocate for one input frame.
377             */
378             msg_Err( p_filter, "Framerate doubler: output buffer too small; "\
379                                "fields = %d, buffer size = %d. Dropping the "\
380                                "remaining fields.",
381                                i_nb_fields, DEINTERLACE_DST_SIZE );
382             i_double_rate_alloc_end = DEINTERLACE_DST_SIZE;
383         }
384
385         /* Allocate output frames. */
386         for( int i = 1; i < i_double_rate_alloc_end ; ++i )
387         {
388             p_dst[i-1]->p_next =
389             p_dst[i]           = filter_NewPicture( p_filter );
390             if( p_dst[i] )
391             {
392                 picture_CopyProperties( p_dst[i], p_pic );
393             }
394             else
395             {
396                 msg_Err( p_filter, "Framerate doubler: could not allocate "\
397                                    "output frame %d", i+1 );
398                 i_double_rate_alloc_end = i; /* Inform the PTS logic about the
399                                                 correct end position. */
400                 break; /* If this happens, the rest of the allocations
401                           aren't likely to work, either... */
402             }
403         }
404         /* Now we have allocated *up to* the correct number of frames;
405            normally, exactly the correct number. Upon alloc failure,
406            we may have succeeded in allocating *some* output frames,
407            but fewer than were desired. In such a case, as many will
408            be rendered as were successfully allocated.
409
410            Note that now p_dst[i] != NULL
411            for 0 <= i < i_double_rate_alloc_end. */
412     }
413     assert( p_sys->b_double_rate  ||  p_dst[1] == NULL );
414     assert( i_nb_fields > 2  ||  p_dst[2] == NULL );
415
416     /* Render */
417     switch( p_sys->i_mode )
418     {
419         case DEINTERLACE_DISCARD:
420             RenderDiscard( p_dst[0], p_pic, 0 );
421             break;
422
423         case DEINTERLACE_BOB:
424             RenderBob( p_dst[0], p_pic, !b_top_field_first );
425             if( p_dst[1] )
426                 RenderBob( p_dst[1], p_pic, b_top_field_first );
427             if( p_dst[2] )
428                 RenderBob( p_dst[2], p_pic, !b_top_field_first );
429             break;;
430
431         case DEINTERLACE_LINEAR:
432             RenderLinear( p_filter, p_dst[0], p_pic, !b_top_field_first );
433             if( p_dst[1] )
434                 RenderLinear( p_filter, p_dst[1], p_pic, b_top_field_first );
435             if( p_dst[2] )
436                 RenderLinear( p_filter, p_dst[2], p_pic, !b_top_field_first );
437             break;
438
439         case DEINTERLACE_MEAN:
440             RenderMean( p_filter, p_dst[0], p_pic );
441             break;
442
443         case DEINTERLACE_BLEND:
444             RenderBlend( p_filter, p_dst[0], p_pic );
445             break;
446
447         case DEINTERLACE_X:
448             RenderX( p_dst[0], p_pic );
449             break;
450
451         case DEINTERLACE_YADIF:
452             if( RenderYadif( p_filter, p_dst[0], p_pic, 0, 0 ) )
453                 goto drop;
454             break;
455
456         case DEINTERLACE_YADIF2X:
457             if( RenderYadif( p_filter, p_dst[0], p_pic, 0, !b_top_field_first ) )
458                 goto drop;
459             if( p_dst[1] )
460                 RenderYadif( p_filter, p_dst[1], p_pic, 1, b_top_field_first );
461             if( p_dst[2] )
462                 RenderYadif( p_filter, p_dst[2], p_pic, 2, !b_top_field_first );
463             break;
464
465         case DEINTERLACE_PHOSPHOR:
466             if( RenderPhosphor( p_filter, p_dst[0], 0,
467                                 !b_top_field_first ) )
468                 goto drop;
469             if( p_dst[1] )
470                 RenderPhosphor( p_filter, p_dst[1], 1,
471                                 b_top_field_first );
472             if( p_dst[2] )
473                 RenderPhosphor( p_filter, p_dst[2], 2,
474                                 !b_top_field_first );
475             break;
476
477         case DEINTERLACE_IVTC:
478             /* Note: RenderIVTC will automatically drop the duplicate frames
479                      produced by IVTC. This is part of normal operation. */
480             if( RenderIVTC( p_filter, p_dst[0] ) )
481                 goto drop;
482             break;
483     }
484
485     /* Set output timestamps, if the algorithm didn't request CUSTOM_PTS
486        for this frame. */
487     assert( i_frame_offset <= METADATA_SIZE  ||  i_frame_offset == CUSTOM_PTS );
488     if( i_frame_offset != CUSTOM_PTS )
489     {
490         mtime_t i_base_pts = p_sys->meta.pi_date[i_meta_idx];
491
492         /* Note: in the usual case (i_frame_offset = 0  and
493                  b_double_rate = false), this effectively does nothing.
494                  This is needed to correct the timestamp
495                  when i_frame_offset > 0. */
496         p_dst[0]->date = i_base_pts;
497
498         if( p_sys->b_double_rate )
499         {
500             /* Processing all actually allocated output frames. */
501             for( int i = 1; i < i_double_rate_alloc_end; ++i )
502             {
503                 /* XXX it's not really good especially for the first picture, but
504                  * I don't think that delaying by one frame is worth it */
505                 if( i_base_pts > VLC_TS_INVALID )
506                     p_dst[i]->date = i_base_pts + i * i_field_dur;
507                 else
508                     p_dst[i]->date = VLC_TS_INVALID;
509             }
510         }
511     }
512
513     for( int i = 0; i < DEINTERLACE_DST_SIZE; ++i )
514     {
515         if( p_dst[i] )
516         {
517             p_dst[i]->b_progressive = true;
518             p_dst[i]->i_nb_fields = 2;
519         }
520     }
521
522     picture_Release( p_pic );
523     return p_dst[0];
524
525 drop:
526     picture_Release( p_dst[0] );
527     for( int i = 1; i < DEINTERLACE_DST_SIZE; ++i )
528     {
529         if( p_dst[i] )
530             picture_Release( p_dst[i] );
531     }
532     picture_Release( p_pic );
533     return NULL;
534 }
535
536 /*****************************************************************************
537  * Flush
538  *****************************************************************************/
539
540 void Flush( filter_t *p_filter )
541 {
542     filter_sys_t *p_sys = p_filter->p_sys;
543
544     for( int i = 0; i < METADATA_SIZE; i++ )
545     {
546         p_sys->meta.pi_date[i] = VLC_TS_INVALID;
547         p_sys->meta.pi_nb_fields[i] = 2;
548         p_sys->meta.pb_top_field_first[i] = true;
549     }
550     p_sys->i_frame_offset = 0; /* reset to default value (first frame after
551                                   flush cannot have offset) */
552     for( int i = 0; i < HISTORY_SIZE; i++ )
553     {
554         if( p_sys->pp_history[i] )
555             picture_Release( p_sys->pp_history[i] );
556         p_sys->pp_history[i] = NULL;
557     }
558     IVTCClearState( p_filter );
559 }
560
561 /*****************************************************************************
562  * Mouse event callback
563  *****************************************************************************/
564
565 int Mouse( filter_t *p_filter,
566            vlc_mouse_t *p_mouse,
567            const vlc_mouse_t *p_old, const vlc_mouse_t *p_new )
568 {
569     VLC_UNUSED(p_old);
570     *p_mouse = *p_new;
571     if( p_filter->p_sys->b_half_height )
572         p_mouse->i_y *= 2;
573     return VLC_SUCCESS;
574 }
575
576
577 /*****************************************************************************
578  * Open
579  *****************************************************************************/
580
581 int Open( vlc_object_t *p_this )
582 {
583     filter_t *p_filter = (filter_t*)p_this;
584     filter_sys_t *p_sys;
585
586     const vlc_fourcc_t fourcc = p_filter->fmt_in.video.i_chroma;
587     const vlc_chroma_description_t *chroma = vlc_fourcc_GetChromaDescription( fourcc );
588     if( !vlc_fourcc_IsYUV( fourcc ) ||
589         !chroma || chroma->plane_count != 3 || chroma->pixel_size > 2 )
590     {
591         msg_Err( p_filter, "Unsupported chroma (%4.4s)", (char*)&fourcc );
592         return VLC_EGENERIC;
593     }
594
595     /* */
596     p_sys = p_filter->p_sys = malloc( sizeof( *p_sys ) );
597     if( !p_sys )
598         return VLC_ENOMEM;
599
600     config_ChainParse( p_filter, FILTER_CFG_PREFIX, ppsz_filter_options,
601                        p_filter->p_cfg );
602     char *psz_mode = var_InheritString( p_filter, FILTER_CFG_PREFIX "mode" );
603     SetFilterMethod( p_filter, psz_mode );
604     free( psz_mode );
605
606     p_sys->chroma = chroma;
607     for( int i = 0; i < METADATA_SIZE; i++ )
608     {
609         p_sys->meta.pi_date[i] = VLC_TS_INVALID;
610         p_sys->meta.pi_nb_fields[i] = 2;
611         p_sys->meta.pb_top_field_first[i] = true;
612     }
613     p_sys->i_frame_offset = 0; /* start with default value (first-ever frame
614                                   cannot have offset) */
615     for( int i = 0; i < HISTORY_SIZE; i++ )
616         p_sys->pp_history[i] = NULL;
617
618     IVTCClearState( p_filter );
619
620 #if defined(CAN_COMPILE_C_ALTIVEC)
621     if( chroma->pixel_size == 1 && vlc_CPU_ALTIVEC() )
622         p_sys->pf_merge = MergeAltivec;
623     else
624 #endif
625 #if defined(CAN_COMPILE_SSE2)
626     if( vlc_CPU_SSE2() )
627     {
628         p_sys->pf_merge = chroma->pixel_size == 1 ? Merge8BitSSE2 : Merge16BitSSE2;
629         p_sys->pf_end_merge = EndMMX;
630     }
631     else
632 #endif
633 #if defined(CAN_COMPILE_MMXEXT)
634     if( chroma->pixel_size == 1 && vlc_CPU_MMXEXT() )
635     {
636         p_sys->pf_merge = MergeMMXEXT;
637         p_sys->pf_end_merge = EndMMX;
638     }
639     else
640 #endif
641 #if defined(CAN_COMPILE_3DNOW)
642     if( chroma->pixel_size == 1 && vlc_CPU_3dNOW() )
643     {
644         p_sys->pf_merge = Merge3DNow;
645         p_sys->pf_end_merge = End3DNow;
646     }
647     else
648 #endif
649 #if defined(CAN_COMPILE_ARM)
650     if( vlc_CPU_ARM_NEON() )
651         p_sys->pf_merge =
652             (chroma->pixel_size == 1) ? merge8_arm_neon : merge16_arm_neon;
653     else
654     if( vlc_CPU_ARMv6() )
655         p_sys->pf_merge =
656             (chroma->pixel_size == 1) ? merge8_armv6 : merge16_armv6;
657     else
658 #endif
659     {
660         p_sys->pf_merge = chroma->pixel_size == 1 ? Merge8BitGeneric : Merge16BitGeneric;
661 #if defined(__i386__) || defined(__x86_64__)
662         p_sys->pf_end_merge = NULL;
663 #endif
664     }
665
666     /* */
667     if( p_sys->i_mode == DEINTERLACE_PHOSPHOR )
668     {
669         int i_c420 = var_GetInteger( p_filter,
670                                      FILTER_CFG_PREFIX "phosphor-chroma" );
671         if( i_c420 != PC_LATEST  &&  i_c420 != PC_ALTLINE  &&
672             i_c420 != PC_BLEND   && i_c420 != PC_UPCONVERT )
673         {
674             msg_Dbg( p_filter, "Phosphor 4:2:0 input chroma mode not set"\
675                                "or out of range (valid: 1, 2, 3 or 4), "\
676                                "using default" );
677             i_c420 = PC_ALTLINE;
678         }
679         msg_Dbg( p_filter, "using Phosphor 4:2:0 input chroma mode %d",
680                            i_c420 );
681         /* This maps directly to the phosphor_chroma_t enum. */
682         p_sys->phosphor.i_chroma_for_420 = i_c420;
683
684         int i_dimmer = var_GetInteger( p_filter,
685                                        FILTER_CFG_PREFIX "phosphor-dimmer" );
686         if( i_dimmer < 1  ||  i_dimmer > 4 )
687         {
688             msg_Dbg( p_filter, "Phosphor dimmer strength not set "\
689                                "or out of range (valid: 1, 2, 3 or 4), "\
690                                "using default" );
691             i_dimmer = 2; /* low */
692         }
693         msg_Dbg( p_filter, "using Phosphor dimmer strength %d", i_dimmer );
694         /* The internal value ranges from 0 to 3. */
695         p_sys->phosphor.i_dimmer_strength = i_dimmer - 1;
696     }
697     else
698     {
699         p_sys->phosphor.i_chroma_for_420 = PC_ALTLINE;
700         p_sys->phosphor.i_dimmer_strength = 1;
701     }
702
703     /* */
704     video_format_t fmt;
705     GetOutputFormat( p_filter, &fmt, &p_filter->fmt_in.video );
706     if( !p_filter->b_allow_fmt_out_change &&
707         ( fmt.i_chroma != p_filter->fmt_in.video.i_chroma ||
708           fmt.i_height != p_filter->fmt_in.video.i_height ) )
709     {
710         Close( VLC_OBJECT(p_filter) );
711         return VLC_EGENERIC;
712     }
713     p_filter->fmt_out.video = fmt;
714     p_filter->fmt_out.i_codec = fmt.i_chroma;
715     p_filter->pf_video_filter = Deinterlace;
716     p_filter->pf_video_flush  = Flush;
717     p_filter->pf_video_mouse  = Mouse;
718
719     msg_Dbg( p_filter, "deinterlacing" );
720
721     return VLC_SUCCESS;
722 }
723
724 /*****************************************************************************
725  * Close: clean up the filter
726  *****************************************************************************/
727
728 void Close( vlc_object_t *p_this )
729 {
730     filter_t *p_filter = (filter_t*)p_this;
731
732     Flush( p_filter );
733     free( p_filter->p_sys );
734 }