]> git.sesse.net Git - vlc/blob - src/video_output/interlacing.c
Phosphor deinterlacer
[vlc] / src / video_output / interlacing.c
1 /*****************************************************************************
2  * interlacing.c
3  *****************************************************************************
4  * Copyright (C) 2010 Laurent Aimar
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27 #include <assert.h>
28
29 #include <vlc_common.h>
30 #include <vlc_vout.h>
31
32 #include "interlacing.h"
33
34 /*****************************************************************************
35  * Deinterlacing
36  *****************************************************************************/
37 /* XXX
38  * You can use the non vout filter if and only if the video properties stay the
39  * same (width/height/chroma/fps), at least for now.
40  */
41 static const char *deinterlace_modes[] = {
42     ""
43     "discard",
44     "blend",
45     "mean",
46     "bob",
47     "linear",
48     "x",
49     "yadif",
50     "yadif2x",
51     "phosphor",
52     NULL
53 };
54 static bool DeinterlaceIsModeValid(const char *mode)
55 {
56     for (unsigned i = 0; deinterlace_modes[i]; i++) {
57         if( !strcmp(deinterlace_modes[i], mode))
58             return true;
59     }
60     return false;
61 }
62
63 static char *FilterFind( char *psz_filter_base, const char *psz_module )
64 {
65     const size_t i_module = strlen( psz_module );
66     const char *psz_filter = psz_filter_base;
67
68     if( !psz_filter || i_module <= 0 )
69         return NULL;
70
71     for( ;; )
72     {
73         char *psz_find = strstr( psz_filter, psz_module );
74         if( !psz_find )
75             return NULL;
76         if( psz_find[i_module] == '\0' || psz_find[i_module] == ':' )
77             return psz_find;
78         psz_filter = &psz_find[i_module];
79     }
80 }
81
82 static bool DeinterlaceIsPresent( vout_thread_t *p_vout )
83 {
84     char *psz_filter = var_GetNonEmptyString( p_vout, "video-filter" );
85
86     bool b_found = FilterFind( psz_filter, "deinterlace" ) != NULL;
87
88     free( psz_filter );
89
90     return b_found;
91 }
92
93 static void DeinterlaceRemove( vout_thread_t *p_vout )
94 {
95     char *psz_filter = var_GetNonEmptyString( p_vout, "video-filter" );
96
97     char *psz = FilterFind( psz_filter, "deinterlace" );
98     if( !psz )
99     {
100         free( psz_filter );
101         return;
102     }
103
104     /* */
105     strcpy( &psz[0], &psz[strlen("deinterlace")] );
106     if( *psz == ':' )
107         strcpy( &psz[0], &psz[1] );
108
109     var_SetString( p_vout, "video-filter", psz_filter );
110     free( psz_filter );
111 }
112 static void DeinterlaceAdd( vout_thread_t *p_vout )
113 {
114     char *psz_filter = var_GetNonEmptyString( p_vout, "video-filter" );
115
116     if( FilterFind( psz_filter, "deinterlace" ) )
117     {
118         free( psz_filter );
119         return;
120     }
121
122     /* */
123     if( psz_filter )
124     {
125         char *psz_tmp = psz_filter;
126         if( asprintf( &psz_filter, "%s:%s", psz_tmp, "deinterlace" ) < 0 )
127             psz_filter = psz_tmp;
128         else
129             free( psz_tmp );
130     }
131     else
132     {
133         psz_filter = strdup( "deinterlace" );
134     }
135
136     if( psz_filter )
137     {
138         var_SetString( p_vout, "video-filter", psz_filter );
139         free( psz_filter );
140     }
141 }
142
143 static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd,
144                                 vlc_value_t oldval, vlc_value_t newval, void *p_data )
145 {
146     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(newval); VLC_UNUSED(p_data);
147     vout_thread_t *p_vout = (vout_thread_t *)p_this;
148
149     /* */
150     const int  i_deinterlace = var_GetInteger( p_this, "deinterlace" );
151     char       *psz_mode     = var_GetString( p_this, "deinterlace-mode" );
152     const bool is_needed     = var_GetBool( p_this, "deinterlace-needed" );
153     if( !psz_mode || !DeinterlaceIsModeValid(psz_mode) )
154         return VLC_EGENERIC;
155
156     /* */
157     char *psz_old = var_CreateGetString( p_vout, "sout-deinterlace-mode" );
158     var_SetString( p_vout, "sout-deinterlace-mode", psz_mode );
159
160     msg_Dbg( p_vout, "deinterlace %d, mode %s, is_needed %d", i_deinterlace, psz_mode, is_needed );
161     if( i_deinterlace == 0 || ( i_deinterlace == -1 && !is_needed ) )
162     {
163         DeinterlaceRemove( p_vout );
164     }
165     else if( !DeinterlaceIsPresent( p_vout ) )
166     {
167         DeinterlaceAdd( p_vout );
168     }
169     else if( psz_old && strcmp( psz_old, psz_mode ) )
170     {
171         var_TriggerCallback( p_vout, "video-filter" );
172     }
173
174     /* */
175     free( psz_old );
176     free( psz_mode );
177     return VLC_SUCCESS;
178 }
179
180 void vout_InitInterlacingSupport( vout_thread_t *p_vout, bool is_interlaced )
181 {
182     vlc_value_t val, text;
183
184     msg_Dbg( p_vout, "Deinterlacing available" );
185
186     /* Create the configuration variables */
187     /* */
188     var_Create( p_vout, "deinterlace", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT | VLC_VAR_HASCHOICE );
189     int i_deinterlace = var_GetInteger( p_vout, "deinterlace" );
190     i_deinterlace = __MAX( __MIN( i_deinterlace, 1 ), -1 );
191
192     text.psz_string = _("Deinterlace");
193     var_Change( p_vout, "deinterlace", VLC_VAR_SETTEXT, &text, NULL );
194
195     const module_config_t *p_optd = config_FindConfig( VLC_OBJECT(p_vout), "deinterlace" );
196     var_Change( p_vout, "deinterlace", VLC_VAR_CLEARCHOICES, NULL, NULL );
197     for( int i = 0; p_optd && i < p_optd->i_list; i++ )
198     {
199         val.i_int  = p_optd->pi_list[i];
200         text.psz_string = (char*)vlc_gettext(p_optd->ppsz_list_text[i]);
201         var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
202     }
203     var_AddCallback( p_vout, "deinterlace", DeinterlaceCallback, NULL );
204     /* */
205     var_Create( p_vout, "deinterlace-mode", VLC_VAR_STRING | VLC_VAR_DOINHERIT | VLC_VAR_HASCHOICE );
206     char *psz_deinterlace = var_GetNonEmptyString( p_vout, "deinterlace-mode" );
207
208     text.psz_string = _("Deinterlace mode");
209     var_Change( p_vout, "deinterlace-mode", VLC_VAR_SETTEXT, &text, NULL );
210
211     const module_config_t *p_optm = config_FindConfig( VLC_OBJECT(p_vout), "deinterlace-mode" );
212     var_Change( p_vout, "deinterlace-mode", VLC_VAR_CLEARCHOICES, NULL, NULL );
213     for( int i = 0; p_optm && i < p_optm->i_list; i++ )
214     {
215         if( !DeinterlaceIsModeValid( p_optm->ppsz_list[i] ) )
216             continue;
217
218         val.psz_string  = p_optm->ppsz_list[i];
219         text.psz_string = (char*)vlc_gettext(p_optm->ppsz_list_text[i]);
220         var_Change( p_vout, "deinterlace-mode", VLC_VAR_ADDCHOICE, &val, &text );
221     }
222     var_AddCallback( p_vout, "deinterlace-mode", DeinterlaceCallback, NULL );
223     /* */
224     var_Create( p_vout, "deinterlace-needed", VLC_VAR_BOOL );
225     var_AddCallback( p_vout, "deinterlace-needed", DeinterlaceCallback, NULL );
226
227     /* Override the initial value from filters if present */
228     char *psz_filter_mode = NULL;
229     if( DeinterlaceIsPresent( p_vout ) )
230         psz_filter_mode = var_CreateGetNonEmptyString( p_vout, "sout-deinterlace-mode" );
231     if( psz_filter_mode )
232     {
233         i_deinterlace = 1;
234         free( psz_deinterlace );
235         psz_deinterlace = psz_filter_mode;
236     }
237
238     /* */
239     val.psz_string = psz_deinterlace ? psz_deinterlace : p_optm->orig.psz;
240     var_Change( p_vout, "deinterlace-mode", VLC_VAR_SETVALUE, &val, NULL );
241     val.b_bool = is_interlaced;
242     var_Change( p_vout, "deinterlace-needed", VLC_VAR_SETVALUE, &val, NULL );
243
244     var_SetInteger( p_vout, "deinterlace", i_deinterlace );
245     free( psz_deinterlace );
246 }
247
248 void vout_SetInterlacingState( vout_thread_t *p_vout, vout_interlacing_support_t *state, bool is_interlaced )
249 {
250      /* Wait 30s before quiting interlacing mode */
251     const int interlacing_change = (!!is_interlaced) - (!!state->is_interlaced);
252     if ((interlacing_change == 1) ||
253         (interlacing_change == -1 && state->date + 30000000 < mdate())) {
254
255         msg_Dbg( p_vout, "Detected %s video",
256                  is_interlaced ? "interlaced" : "progressive" );
257         var_SetBool( p_vout, "deinterlace-needed", is_interlaced );
258
259         state->is_interlaced = is_interlaced;
260     }
261     if (is_interlaced)
262         state->date = mdate();
263 }
264
265