1 /*****************************************************************************
2  * interlacing.c
3  *****************************************************************************
4  * Copyright (C) 2010 Laurent Aimar
5  * $Id: f07ef8ef5a016521bd4732f90185027cb5dda9ed $
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 #include "vout_internal.h"
34 
35 /*****************************************************************************
36  * Deinterlacing
37  *****************************************************************************/
38 /* XXX
39  * You can use the non vout filter if and only if the video properties stay the
40  * same (width/height/chroma/fps), at least for now.
41  */
42 static const char deinterlace_modes[][9]= {
43     "auto",
44     "discard",
45     "blend",
46     "mean",
47     "bob",
48     "linear",
49     "x",
50     "yadif",
51     "yadif2x",
52     "phosphor",
53     "ivtc",
54 };
55 
DeinterlaceIsModeValid(const char * mode)56 static bool DeinterlaceIsModeValid(const char *mode)
57 {
58     for (unsigned i = 0; i < ARRAY_SIZE(deinterlace_modes); i++) {
59         if (!strcmp(deinterlace_modes[i], mode))
60             return true;
61     }
62     return false;
63 }
64 
DeinterlaceCallback(vlc_object_t * object,char const * cmd,vlc_value_t oldval,vlc_value_t newval,void * data)65 static int DeinterlaceCallback(vlc_object_t *object, char const *cmd,
66                                vlc_value_t oldval, vlc_value_t newval, void *data)
67 {
68     VLC_UNUSED(cmd); VLC_UNUSED(oldval); VLC_UNUSED(newval); VLC_UNUSED(data);
69     vout_thread_t *vout = (vout_thread_t *)object;
70 
71     /* */
72     const int  deinterlace_state = var_GetInteger(vout, "deinterlace");
73     char       *mode             = var_GetString(vout,  "deinterlace-mode");
74     const bool is_needed         = var_GetBool(vout,    "deinterlace-needed");
75     if (!mode || !DeinterlaceIsModeValid(mode))
76     {
77         free(mode);
78         return VLC_EGENERIC;
79     }
80 
81     /* */
82     char *old = var_CreateGetString(vout, "sout-deinterlace-mode");
83     var_SetString(vout, "sout-deinterlace-mode", mode);
84 
85     msg_Dbg(vout, "deinterlace %d, mode %s, is_needed %d", deinterlace_state, mode, is_needed);
86     if (deinterlace_state == 0 || (deinterlace_state < 0 && !is_needed))
87         vout_control_PushBool(&vout->p->control,
88                               VOUT_CONTROL_CHANGE_INTERLACE, false);
89     else
90         vout_control_PushBool(&vout->p->control,
91                               VOUT_CONTROL_CHANGE_INTERLACE, true);
92 
93     /* */
94     free(old);
95     free(mode);
96     return VLC_SUCCESS;
97 }
98 
vout_InitInterlacingSupport(vout_thread_t * vout,bool is_interlaced)99 void vout_InitInterlacingSupport(vout_thread_t *vout, bool is_interlaced)
100 {
101     vlc_value_t val, text;
102 
103     msg_Dbg(vout, "Deinterlacing available");
104 
105     vout->p->filter.has_deint = false;
106 
107     /* Create the configuration variables */
108     /* */
109     var_Create(vout, "deinterlace", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
110     int deinterlace_state = var_GetInteger(vout, "deinterlace");
111 
112     text.psz_string = _("Deinterlace");
113     var_Change(vout, "deinterlace", VLC_VAR_SETTEXT, &text, NULL);
114 
115     const module_config_t *optd = config_FindConfig("deinterlace");
116     var_Change(vout, "deinterlace", VLC_VAR_CLEARCHOICES, NULL, NULL);
117     if (likely(optd != NULL))
118         for (unsigned i = 0; i < optd->list_count; i++) {
119             val.i_int = optd->list.i[i];
120             text.psz_string = vlc_gettext(optd->list_text[i]);
121             var_Change(vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text);
122         }
123     var_AddCallback(vout, "deinterlace", DeinterlaceCallback, NULL);
124     /* */
125     var_Create(vout, "deinterlace-mode", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
126     char *deinterlace_mode = var_GetNonEmptyString(vout, "deinterlace-mode");
127 
128     text.psz_string = _("Deinterlace mode");
129     var_Change(vout, "deinterlace-mode", VLC_VAR_SETTEXT, &text, NULL);
130 
131     const module_config_t *optm = config_FindConfig("deinterlace-mode");
132     var_Change(vout, "deinterlace-mode", VLC_VAR_CLEARCHOICES, NULL, NULL);
133     if (likely(optm != NULL))
134         for (unsigned i = 0; i < optm->list_count; i++) {
135              if (!DeinterlaceIsModeValid(optm->list.psz[i]))
136                  continue;
137 
138              val.psz_string  = (char *)optm->list.psz[i];
139              text.psz_string = vlc_gettext(optm->list_text[i]);
140              var_Change(vout, "deinterlace-mode", VLC_VAR_ADDCHOICE,
141                         &val, &text);
142          }
143     var_AddCallback(vout, "deinterlace-mode", DeinterlaceCallback, NULL);
144     /* */
145     var_Create(vout, "deinterlace-needed", VLC_VAR_BOOL);
146     var_AddCallback(vout, "deinterlace-needed", DeinterlaceCallback, NULL);
147 
148     /* Override the initial value from filters if present */
149     char *filter_mode = NULL;
150     if (vout->p->filter.has_deint)
151         filter_mode = var_CreateGetNonEmptyString(vout, "sout-deinterlace-mode");
152     if (filter_mode) {
153         deinterlace_state = 1;
154         free(deinterlace_mode);
155         deinterlace_mode = filter_mode;
156     }
157 
158     /* */
159     val.psz_string = deinterlace_mode ? deinterlace_mode : optm->orig.psz;
160     var_Change(vout, "deinterlace-mode", VLC_VAR_SETVALUE, &val, NULL);
161     val.b_bool = is_interlaced;
162     var_Change(vout, "deinterlace-needed", VLC_VAR_SETVALUE, &val, NULL);
163 
164     var_SetInteger(vout, "deinterlace", deinterlace_state);
165     free(deinterlace_mode);
166 
167     vout->p->interlacing.is_interlaced = is_interlaced;
168     if (is_interlaced)
169         vout->p->interlacing.date = mdate();
170 }
171 
vout_ReinitInterlacingSupport(vout_thread_t * vout)172 void vout_ReinitInterlacingSupport(vout_thread_t *vout)
173 {
174     vout->p->interlacing.is_interlaced = false;
175     var_SetBool(vout, "deinterlace-needed", false);
176 }
177 
vout_SetInterlacingState(vout_thread_t * vout,bool is_interlaced)178 void vout_SetInterlacingState(vout_thread_t *vout, bool is_interlaced)
179 {
180      /* Wait 30s before quiting interlacing mode */
181     const int interlacing_change = (!!is_interlaced)
182                                  - (!!vout->p->interlacing.is_interlaced);
183     if (interlacing_change == 1 ||
184         (interlacing_change == -1 &&
185         vout->p->interlacing.date + 30000000 < mdate()))
186     {
187         msg_Dbg(vout, "Detected %s video",
188                  is_interlaced ? "interlaced" : "progressive");
189         var_SetBool(vout, "deinterlace-needed", is_interlaced);
190         vout->p->interlacing.is_interlaced = is_interlaced;
191     }
192     if (is_interlaced)
193         vout->p->interlacing.date = mdate();
194 }
195