]> git.sesse.net Git - vlc/blob - src/video_output/interlacing.c
Used vout_control_Push for vout_PutSubpicture.
[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     NULL
52 };
53 static bool DeinterlaceIsModeValid(const char *mode)
54 {
55     for (unsigned i = 0; deinterlace_modes[i]; i++) {
56         if( !strcmp(deinterlace_modes[i], mode))
57             return true;
58     }
59     return false;
60 }
61
62 static char *FilterFind( char *psz_filter_base, const char *psz_module )
63 {
64     const size_t i_module = strlen( psz_module );
65     const char *psz_filter = psz_filter_base;
66
67     if( !psz_filter || i_module <= 0 )
68         return NULL;
69
70     for( ;; )
71     {
72         char *psz_find = strstr( psz_filter, psz_module );
73         if( !psz_find )
74             return NULL;
75         if( psz_find[i_module] == '\0' || psz_find[i_module] == ':' )
76             return psz_find;
77         psz_filter = &psz_find[i_module];
78     }
79 }
80
81 static bool DeinterlaceIsPresent( vout_thread_t *p_vout )
82 {
83     char *psz_filter = var_GetNonEmptyString( p_vout, "video-filter" );
84
85     bool b_found = FilterFind( psz_filter, "deinterlace" ) != NULL;
86
87     free( psz_filter );
88
89     return b_found;
90 }
91
92 static void DeinterlaceRemove( vout_thread_t *p_vout )
93 {
94     char *psz_filter = var_GetNonEmptyString( p_vout, "video-filter" );
95
96     char *psz = FilterFind( psz_filter, "deinterlace" );
97     if( !psz )
98     {
99         free( psz_filter );
100         return;
101     }
102
103     /* */
104     strcpy( &psz[0], &psz[strlen("deinterlace")] );
105     if( *psz == ':' )
106         strcpy( &psz[0], &psz[1] );
107
108     var_SetString( p_vout, "video-filter", psz_filter );
109     free( psz_filter );
110 }
111 static void DeinterlaceAdd( vout_thread_t *p_vout )
112 {
113     char *psz_filter = var_GetNonEmptyString( p_vout, "video-filter" );
114
115     if( FilterFind( psz_filter, "deinterlace" ) )
116     {
117         free( psz_filter );
118         return;
119     }
120
121     /* */
122     if( psz_filter )
123     {
124         char *psz_tmp = psz_filter;
125         if( asprintf( &psz_filter, "%s:%s", psz_tmp, "deinterlace" ) < 0 )
126             psz_filter = psz_tmp;
127         else
128             free( psz_tmp );
129     }
130     else
131     {
132         psz_filter = strdup( "deinterlace" );
133     }
134
135     if( psz_filter )
136     {
137         var_SetString( p_vout, "video-filter", psz_filter );
138         free( psz_filter );
139     }
140 }
141
142 static void DeinterlaceSave( vout_thread_t *p_vout, int i_deinterlace, const char *psz_mode )
143 {
144     /* We have to set input variable to ensure restart support
145      * FIXME to be removed when vout_Request does the right job.
146      */
147     vlc_object_t *p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, FIND_PARENT );
148     if( !p_input )
149         return;
150
151     var_Create( p_input, "deinterlace", VLC_VAR_INTEGER );
152     var_SetInteger( p_input, "deinterlace", i_deinterlace );
153
154     static const char * const ppsz_variable[] = {
155         "deinterlace-mode",
156         "sout-deinterlace-mode",
157         NULL
158     };
159     for( int i = 0; ppsz_variable[i]; i++ )
160     {
161         var_Create( p_input, ppsz_variable[i], VLC_VAR_STRING );
162         var_SetString( p_input, ppsz_variable[i], psz_mode );
163     }
164
165     vlc_object_release( p_input );
166 }
167 static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd,
168                                 vlc_value_t oldval, vlc_value_t newval, void *p_data )
169 {
170     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(newval); VLC_UNUSED(p_data);
171     vout_thread_t *p_vout = (vout_thread_t *)p_this;
172
173     /* */
174     const int  i_deinterlace = var_GetInteger( p_this, "deinterlace" );
175     char       *psz_mode     = var_GetString( p_this, "deinterlace-mode" );
176     const bool is_needed     = var_GetBool( p_this, "deinterlace-needed" );
177     if( !psz_mode || !DeinterlaceIsModeValid(psz_mode) )
178         return VLC_EGENERIC;
179
180     DeinterlaceSave( p_vout, i_deinterlace, psz_mode );
181
182     /* */
183     char *psz_old = var_CreateGetString( p_vout, "sout-deinterlace-mode" );
184
185     msg_Dbg( p_vout, "deinterlace %d, mode %s, is_needed %d", i_deinterlace, psz_mode, is_needed );
186     if( i_deinterlace == 0 || ( i_deinterlace == -1 && !is_needed ) )
187     {
188         DeinterlaceRemove( p_vout );
189     }
190     else if( !DeinterlaceIsPresent( p_vout ) )
191     {
192         DeinterlaceAdd( p_vout );
193     }
194     else if( psz_old && strcmp( psz_old, psz_mode ) )
195     {
196         var_TriggerCallback( p_vout, "video-filter" );
197     }
198
199     /* */
200     free( psz_old );
201     free( psz_mode );
202     return VLC_SUCCESS;
203 }
204
205 void vout_InitInterlacingSupport( vout_thread_t *p_vout, bool is_interlaced )
206 {
207     vlc_value_t val, text;
208
209     msg_Dbg( p_vout, "Deinterlacing available" );
210
211     /* Create the configuration variables */
212     /* */
213     var_Create( p_vout, "deinterlace", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT | VLC_VAR_HASCHOICE );
214     int i_deinterlace = var_GetInteger( p_vout, "deinterlace" );
215
216     text.psz_string = _("Deinterlace");
217     var_Change( p_vout, "deinterlace", VLC_VAR_SETTEXT, &text, NULL );
218
219     const module_config_t *p_optd = config_FindConfig( VLC_OBJECT(p_vout), "deinterlace" );
220     var_Change( p_vout, "deinterlace", VLC_VAR_CLEARCHOICES, NULL, NULL );
221     for( int i = 0; p_optd && i < p_optd->i_list; i++ )
222     {
223         val.i_int  = p_optd->pi_list[i];
224         text.psz_string = (char*)vlc_gettext(p_optd->ppsz_list_text[i]);
225         var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
226     }
227     var_AddCallback( p_vout, "deinterlace", DeinterlaceCallback, NULL );
228     /* */
229     var_Create( p_vout, "deinterlace-mode", VLC_VAR_STRING | VLC_VAR_DOINHERIT | VLC_VAR_HASCHOICE );
230     char *psz_deinterlace = var_GetNonEmptyString( p_vout, "deinterlace-mode" );
231
232     text.psz_string = _("Deinterlace mode");
233     var_Change( p_vout, "deinterlace-mode", VLC_VAR_SETTEXT, &text, NULL );
234
235     const module_config_t *p_optm = config_FindConfig( VLC_OBJECT(p_vout), "deinterlace-mode" );
236     var_Change( p_vout, "deinterlace-mode", VLC_VAR_CLEARCHOICES, NULL, NULL );
237     for( int i = 0; p_optm && i < p_optm->i_list; i++ )
238     {
239         if( !DeinterlaceIsModeValid( p_optm->ppsz_list[i] ) )
240             continue;
241
242         val.psz_string  = p_optm->ppsz_list[i];
243         text.psz_string = (char*)vlc_gettext(p_optm->ppsz_list_text[i]);
244         var_Change( p_vout, "deinterlace-mode", VLC_VAR_ADDCHOICE, &val, &text );
245     }
246     var_AddCallback( p_vout, "deinterlace-mode", DeinterlaceCallback, NULL );
247     /* */
248     var_Create( p_vout, "deinterlace-needed", VLC_VAR_BOOL );
249     var_AddCallback( p_vout, "deinterlace-needed", DeinterlaceCallback, NULL );
250
251     /* Override the initial value from filters if present */
252     char *psz_filter_mode = NULL;
253     if( DeinterlaceIsPresent( p_vout ) )
254         psz_filter_mode = var_CreateGetNonEmptyString( p_vout, "sout-deinterlace-mode" );
255     if( psz_filter_mode )
256     {
257         free( psz_deinterlace );
258         if( i_deinterlace >= -1 )
259             i_deinterlace = 1;
260         psz_deinterlace = psz_filter_mode;
261     }
262
263     /* */
264     if( i_deinterlace < 0 )
265         i_deinterlace = -1;
266
267     /* */
268     val.psz_string = psz_deinterlace ? psz_deinterlace : p_optm->orig.psz;
269     var_Change( p_vout, "deinterlace-mode", VLC_VAR_SETVALUE, &val, NULL );
270     val.b_bool = is_interlaced;
271     var_Change( p_vout, "deinterlace-needed", VLC_VAR_SETVALUE, &val, NULL );
272
273     var_SetInteger( p_vout, "deinterlace", i_deinterlace );
274     free( psz_deinterlace );
275 }
276
277 void vout_SetInterlacingState( vout_thread_t *p_vout, vout_interlacing_support_t *state, bool is_interlaced )
278 {
279      /* Wait 30s before quiting interlacing mode */
280     const int interlacing_change = (!!is_interlaced) - (!!state->is_interlaced);
281     if ((interlacing_change == 1) ||
282         (interlacing_change == -1 && state->date + 30000000 < mdate())) {
283
284         msg_Dbg( p_vout, "Detected %s video",
285                  is_interlaced ? "interlaced" : "progressive" );
286         var_SetBool( p_vout, "deinterlace-needed", is_interlaced );
287
288         state->is_interlaced = is_interlaced;
289     }
290     if (is_interlaced)
291         state->date = mdate();
292 }
293
294