]> git.sesse.net Git - vlc/blob - src/video_output/interlacing.c
macosx: fix wrong ref counting in media info
[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[][9]= {
42     ""
43     "discard",
44     "blend",
45     "mean",
46     "bob",
47     "linear",
48     "x",
49     "yadif",
50     "yadif2x",
51     "phosphor",
52     "ivtc",
53 };
54
55 static bool DeinterlaceIsModeValid(const char *mode)
56 {
57     for (unsigned i = 0; i < ARRAY_SIZE(deinterlace_modes); 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         memmove(start, start + 1, strlen(start) /* + 1 - 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     if (likely(optd != NULL))
186         for (unsigned i = 0; i < optd->list_count; i++) {
187             val.i_int = optd->list.i[i];
188             text.psz_string = vlc_gettext(optd->list_text[i]);
189             var_Change(vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text);
190         }
191     var_AddCallback(vout, "deinterlace", DeinterlaceCallback, NULL);
192     /* */
193     var_Create(vout, "deinterlace-mode", VLC_VAR_STRING | VLC_VAR_DOINHERIT | VLC_VAR_HASCHOICE);
194     char *deinterlace_mode = var_GetNonEmptyString(vout, "deinterlace-mode");
195
196     text.psz_string = _("Deinterlace mode");
197     var_Change(vout, "deinterlace-mode", VLC_VAR_SETTEXT, &text, NULL);
198
199     const module_config_t *optm = config_FindConfig(VLC_OBJECT(vout), "deinterlace-mode");
200     var_Change(vout, "deinterlace-mode", VLC_VAR_CLEARCHOICES, NULL, NULL);
201     if (likely(optm != NULL))
202         for (unsigned i = 0; i < optm->list_count; i++) {
203              if (!DeinterlaceIsModeValid(optm->list.psz[i]))
204                  continue;
205
206              val.psz_string  = optm->list.psz[i];
207              text.psz_string = vlc_gettext(optm->list_text[i]);
208              var_Change(vout, "deinterlace-mode", VLC_VAR_ADDCHOICE,
209                         &val, &text);
210          }
211     var_AddCallback(vout, "deinterlace-mode", DeinterlaceCallback, NULL);
212     /* */
213     var_Create(vout, "deinterlace-needed", VLC_VAR_BOOL);
214     var_AddCallback(vout, "deinterlace-needed", DeinterlaceCallback, NULL);
215
216     /* Override the initial value from filters if present */
217     char *filter_mode = NULL;
218     if (DeinterlaceIsPresent(vout))
219         filter_mode = var_CreateGetNonEmptyString(vout, "sout-deinterlace-mode");
220     if (filter_mode) {
221         deinterlace_state = 1;
222         free(deinterlace_mode);
223         deinterlace_mode = filter_mode;
224     }
225
226     /* */
227     val.psz_string = deinterlace_mode ? deinterlace_mode : optm->orig.psz;
228     var_Change(vout, "deinterlace-mode", VLC_VAR_SETVALUE, &val, NULL);
229     val.b_bool = is_interlaced;
230     var_Change(vout, "deinterlace-needed", VLC_VAR_SETVALUE, &val, NULL);
231
232     var_SetInteger(vout, "deinterlace", deinterlace_state);
233     free(deinterlace_mode);
234 }
235
236 void vout_SetInterlacingState(vout_thread_t *vout, vout_interlacing_support_t *state, bool is_interlaced)
237 {
238      /* Wait 30s before quiting interlacing mode */
239     const int interlacing_change = (!!is_interlaced) - (!!state->is_interlaced);
240     if ((interlacing_change == 1) ||
241         (interlacing_change == -1 && state->date + 30000000 < mdate())) {
242         msg_Dbg(vout, "Detected %s video",
243                  is_interlaced ? "interlaced" : "progressive");
244         var_SetBool(vout, "deinterlace-needed", is_interlaced);
245
246         state->is_interlaced = is_interlaced;
247     }
248     if (is_interlaced)
249         state->date = mdate();
250 }
251
252