]> git.sesse.net Git - vlc/blob - src/video_output/interlacing.c
LGPL
[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 it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * 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     "ivtc",
53     NULL
54 };
55 static bool DeinterlaceIsModeValid(const char *mode)
56 {
57     for (unsigned i = 0; deinterlace_modes[i]; i++) {
58         if (!strcmp(deinterlace_modes[i], mode))
59             return true;
60     }
61     return false;
62 }
63
64 static char *FilterFind(char *filter_base, const char *module_name)
65 {
66     const size_t module_length = strlen(module_name);
67     const char *filter = filter_base;
68
69     if (!filter || module_length <= 0)
70         return NULL;
71
72     for (;;) {
73         char *start = strstr(filter, module_name);
74         if (!start)
75             return NULL;
76         if (start[module_length] == '\0' || start[module_length] == ':')
77             return start;
78         filter = &start[module_length];
79     }
80 }
81
82 static bool DeinterlaceIsPresent(vout_thread_t *vout)
83 {
84     char *filter = var_GetNonEmptyString(vout, "video-filter");
85
86     bool is_found = FilterFind(filter, "deinterlace") != NULL;
87
88     free(filter);
89
90     return is_found;
91 }
92
93 static void DeinterlaceRemove(vout_thread_t *vout)
94 {
95     char *filter = var_GetNonEmptyString(vout, "video-filter");
96
97     char *start = FilterFind(filter, "deinterlace");
98     if (!start) {
99         free(filter);
100         return;
101     }
102
103     /* */
104     strcpy(&start[0], &start[strlen("deinterlace")]);
105     if (*start == ':')
106         strcpy(&start[0], &start[1]);
107
108     var_SetString(vout, "video-filter", filter);
109     free(filter);
110 }
111 static void DeinterlaceAdd(vout_thread_t *vout)
112 {
113     char *filter = var_GetNonEmptyString(vout, "video-filter");
114
115     if (FilterFind(filter, "deinterlace")) {
116         free(filter);
117         return;
118     }
119
120     /* */
121     if (filter) {
122         char *tmp = filter;
123         if (asprintf(&filter, "%s:%s", tmp, "deinterlace") < 0)
124             filter = tmp;
125         else
126             free(tmp);
127     } else {
128         filter = strdup("deinterlace");
129     }
130
131     if (filter) {
132         var_SetString(vout, "video-filter", filter);
133         free(filter);
134     }
135 }
136
137 static int DeinterlaceCallback(vlc_object_t *object, char const *cmd,
138                                vlc_value_t oldval, vlc_value_t newval, void *data)
139 {
140     VLC_UNUSED(cmd); VLC_UNUSED(oldval); VLC_UNUSED(newval); VLC_UNUSED(data);
141     vout_thread_t *vout = (vout_thread_t *)object;
142
143     /* */
144     const int  deinterlace_state = var_GetInteger(vout, "deinterlace");
145     char       *mode             = var_GetString(vout,  "deinterlace-mode");
146     const bool is_needed         = var_GetBool(vout,    "deinterlace-needed");
147     if (!mode || !DeinterlaceIsModeValid(mode))
148         return VLC_EGENERIC;
149
150     /* */
151     char *old = var_CreateGetString(vout, "sout-deinterlace-mode");
152     var_SetString(vout, "sout-deinterlace-mode", mode);
153
154     msg_Dbg(vout, "deinterlace %d, mode %s, is_needed %d", deinterlace_state, mode, is_needed);
155     if (deinterlace_state == 0 || (deinterlace_state == -1 && !is_needed))
156         DeinterlaceRemove(vout);
157     else if (!DeinterlaceIsPresent(vout))
158         DeinterlaceAdd(vout);
159     else if (old && strcmp(old, mode))
160         var_TriggerCallback(vout, "video-filter");
161
162     /* */
163     free(old);
164     free(mode);
165     return VLC_SUCCESS;
166 }
167
168 void vout_InitInterlacingSupport(vout_thread_t *vout, bool is_interlaced)
169 {
170     vlc_value_t val, text;
171
172     msg_Dbg(vout, "Deinterlacing available");
173
174     /* Create the configuration variables */
175     /* */
176     var_Create(vout, "deinterlace", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT | VLC_VAR_HASCHOICE);
177     int deinterlace_state = var_GetInteger(vout, "deinterlace");
178     deinterlace_state = VLC_CLIP(deinterlace_state, -1, 1);
179
180     text.psz_string = _("Deinterlace");
181     var_Change(vout, "deinterlace", VLC_VAR_SETTEXT, &text, NULL);
182
183     const module_config_t *optd = config_FindConfig(VLC_OBJECT(vout), "deinterlace");
184     var_Change(vout, "deinterlace", VLC_VAR_CLEARCHOICES, NULL, NULL);
185     for (int i = 0; optd && i < optd->i_list; i++) {
186         val.i_int  = optd->pi_list[i];
187         text.psz_string = (char*)vlc_gettext(optd->ppsz_list_text[i]);
188         var_Change(vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text);
189     }
190     var_AddCallback(vout, "deinterlace", DeinterlaceCallback, NULL);
191     /* */
192     var_Create(vout, "deinterlace-mode", VLC_VAR_STRING | VLC_VAR_DOINHERIT | VLC_VAR_HASCHOICE);
193     char *deinterlace_mode = var_GetNonEmptyString(vout, "deinterlace-mode");
194
195     text.psz_string = _("Deinterlace mode");
196     var_Change(vout, "deinterlace-mode", VLC_VAR_SETTEXT, &text, NULL);
197
198     const module_config_t *optm = config_FindConfig(VLC_OBJECT(vout), "deinterlace-mode");
199     var_Change(vout, "deinterlace-mode", VLC_VAR_CLEARCHOICES, NULL, NULL);
200     for (int i = 0; optm && i < optm->i_list; i++) {
201         if (!DeinterlaceIsModeValid(optm->ppsz_list[i]))
202             continue;
203
204         val.psz_string  = optm->ppsz_list[i];
205         text.psz_string = (char*)vlc_gettext(optm->ppsz_list_text[i]);
206         var_Change(vout, "deinterlace-mode", VLC_VAR_ADDCHOICE, &val, &text);
207     }
208     var_AddCallback(vout, "deinterlace-mode", DeinterlaceCallback, NULL);
209     /* */
210     var_Create(vout, "deinterlace-needed", VLC_VAR_BOOL);
211     var_AddCallback(vout, "deinterlace-needed", DeinterlaceCallback, NULL);
212
213     /* Override the initial value from filters if present */
214     char *filter_mode = NULL;
215     if (DeinterlaceIsPresent(vout))
216         filter_mode = var_CreateGetNonEmptyString(vout, "sout-deinterlace-mode");
217     if (filter_mode) {
218         deinterlace_state = 1;
219         free(deinterlace_mode);
220         deinterlace_mode = filter_mode;
221     }
222
223     /* */
224     val.psz_string = deinterlace_mode ? deinterlace_mode : optm->orig.psz;
225     var_Change(vout, "deinterlace-mode", VLC_VAR_SETVALUE, &val, NULL);
226     val.b_bool = is_interlaced;
227     var_Change(vout, "deinterlace-needed", VLC_VAR_SETVALUE, &val, NULL);
228
229     var_SetInteger(vout, "deinterlace", deinterlace_state);
230     free(deinterlace_mode);
231 }
232
233 void vout_SetInterlacingState(vout_thread_t *vout, vout_interlacing_support_t *state, bool is_interlaced)
234 {
235      /* Wait 30s before quiting interlacing mode */
236     const int interlacing_change = (!!is_interlaced) - (!!state->is_interlaced);
237     if ((interlacing_change == 1) ||
238         (interlacing_change == -1 && state->date + 30000000 < mdate())) {
239         msg_Dbg(vout, "Detected %s video",
240                  is_interlaced ? "interlaced" : "progressive");
241         var_SetBool(vout, "deinterlace-needed", is_interlaced);
242
243         state->is_interlaced = is_interlaced;
244     }
245     if (is_interlaced)
246         state->date = mdate();
247 }
248
249