+/*****************************************************************************
+ * Deinterlacing
+ *****************************************************************************/
+typedef struct
+{
+ const char *psz_mode;
+ const char *psz_description;
+ bool b_vout_filter;
+} deinterlace_mode_t;
+
+/* XXX
+ * You can use the non vout filter if and only if the video properties stay the
+ * same (width/height/chroma/fps), at least for now.
+ */
+static const deinterlace_mode_t p_deinterlace_mode[] = {
+ { "", "Disable", false },
+ { "discard", "Discard", true },
+ { "blend", "Blend", false },
+ { "mean", "Mean", true },
+ { "bob", "Bob", true },
+ { "linear", "Linear", true },
+ { "x", "X", false },
+ { NULL, NULL, true }
+};
+
+static char *FilterFind( char *psz_filter_base, const char *psz_module )
+{
+ const size_t i_module = strlen( psz_module );
+ const char *psz_filter = psz_filter_base;
+
+ if( !psz_filter || i_module <= 0 )
+ return NULL;
+
+ for( ;; )
+ {
+ char *psz_find = strstr( psz_filter, psz_module );
+ if( !psz_find )
+ return NULL;
+ if( psz_find[i_module] == '\0' || psz_find[i_module] == ':' )
+ return psz_find;
+ psz_filter = &psz_find[i_module];
+ }
+}
+
+static bool DeinterlaceIsPresent( vout_thread_t *p_vout, bool b_vout_filter )
+{
+ char *psz_filter = var_GetNonEmptyString( p_vout, b_vout_filter ? "vout-filter" : "video-filter" );
+
+ bool b_found = FilterFind( psz_filter, "deinterlace" ) != NULL;
+
+ free( psz_filter );
+
+ return b_found;
+}
+
+static void DeinterlaceRemove( vout_thread_t *p_vout, bool b_vout_filter )
+{
+ const char *psz_variable = b_vout_filter ? "vout-filter" : "video-filter";
+ char *psz_filter = var_GetNonEmptyString( p_vout, psz_variable );
+
+ char *psz = FilterFind( psz_filter, "deinterlace" );
+ if( !psz )
+ {
+ free( psz_filter );
+ return;
+ }
+
+ /* */
+ strcpy( &psz[0], &psz[strlen("deinterlace")] );
+ if( *psz == ':' )
+ strcpy( &psz[0], &psz[1] );
+
+ var_SetString( p_vout, psz_variable, psz_filter );
+ free( psz_filter );
+}
+static void DeinterlaceAdd( vout_thread_t *p_vout, bool b_vout_filter )
+{
+ const char *psz_variable = b_vout_filter ? "vout-filter" : "video-filter";
+
+ char *psz_filter = var_GetNonEmptyString( p_vout, psz_variable );
+
+ if( FilterFind( psz_filter, "deinterlace" ) )
+ {
+ free( psz_filter );
+ return;
+ }
+
+ /* */
+ if( psz_filter )
+ {
+ char *psz_tmp = psz_filter;
+ if( asprintf( &psz_filter, "%s:%s", psz_tmp, "deinterlace" ) < 0 )
+ psz_filter = psz_tmp;
+ else
+ free( psz_tmp );
+ }
+ else
+ {
+ psz_filter = strdup( "deinterlace" );
+ }
+
+ if( psz_filter )
+ {
+ var_SetString( p_vout, psz_variable, psz_filter );
+ free( psz_filter );
+ }
+}
+
+static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd,
+ vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+ vout_thread_t *p_vout = (vout_thread_t *)p_this;
+
+ /* */
+ const deinterlace_mode_t *p_mode;
+ for( p_mode = &p_deinterlace_mode[0]; p_mode->psz_mode; p_mode++ )
+ {
+ if( !strcmp( p_mode->psz_mode,
+ newval.psz_string ? newval.psz_string : "" ) )
+ break;
+ }
+ if( !p_mode->psz_mode )
+ {
+ msg_Err( p_this, "Invalid value (%s) ignored", newval.psz_string );
+ return VLC_EGENERIC;
+ }
+
+ /* We have to set input variable to ensure restart support
+ * XXX it is only needed because of vout-filter but must be done
+ * for non video filter anyway */
+ input_thread_t *p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_PARENT );
+ if( p_input )
+ {
+ var_Create( p_input, "vout-deinterlace", VLC_VAR_STRING );
+ var_SetString( p_input, "vout-deinterlace", p_mode->psz_mode );
+
+ var_Create( p_input, "deinterlace-mode", VLC_VAR_STRING );
+ var_SetString( p_input, "deinterlace-mode", p_mode->psz_mode );
+
+ var_Create( p_input, "sout-deinterlace-mode", VLC_VAR_STRING );
+ var_SetString( p_input, "sout-deinterlace-mode", p_mode->psz_mode );
+
+ vlc_object_release( p_input );
+ }
+
+ char *psz_old;
+
+ if( p_mode->b_vout_filter )
+ {
+ psz_old = var_CreateGetString( p_vout, "deinterlace-mode" );
+ }
+ else
+ {
+ psz_old = var_CreateGetString( p_vout, "sout-deinterlace-mode" );
+ var_SetString( p_vout, "sout-deinterlace-mode", p_mode->psz_mode );
+ }
+
+ /* */
+ if( !strcmp( p_mode->psz_mode, "" ) )
+ {
+ DeinterlaceRemove( p_vout, false );
+ DeinterlaceRemove( p_vout, true );
+ }
+ else if( !DeinterlaceIsPresent( p_vout, p_mode->b_vout_filter ) )
+ {
+ DeinterlaceRemove( p_vout, !p_mode->b_vout_filter );
+ DeinterlaceAdd( p_vout, p_mode->b_vout_filter );
+ }
+ else
+ {
+ DeinterlaceRemove( p_vout, !p_mode->b_vout_filter );
+ if( psz_old && strcmp( psz_old, p_mode->psz_mode ) )
+ var_TriggerCallback( p_vout, p_mode->b_vout_filter ? "vout-filter" : "video-filter" );
+ }
+ free( psz_old );
+
+ (void)psz_cmd; (void) oldval; (void) p_data;
+ return VLC_SUCCESS;
+}
+
+static void DeinterlaceEnable( vout_thread_t *p_vout )
+{
+ vlc_value_t val, text;
+
+ if( !p_vout->p->b_first_vout )
+ return;
+
+ msg_Dbg( p_vout, "Deinterlacing available" );
+
+ /* Create the configuration variable */
+ var_Create( p_vout, "deinterlace", VLC_VAR_STRING | VLC_VAR_HASCHOICE );
+ text.psz_string = _("Deinterlace");
+ var_Change( p_vout, "deinterlace", VLC_VAR_SETTEXT, &text, NULL );
+
+ for( int i = 0; p_deinterlace_mode[i].psz_mode; i++ )
+ {
+ val.psz_string = (char*)p_deinterlace_mode[i].psz_mode;
+ text.psz_string = (char*)vlc_gettext(p_deinterlace_mode[i].psz_description);
+ var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
+ }
+ var_AddCallback( p_vout, "deinterlace", DeinterlaceCallback, NULL );
+
+ /* */
+ char *psz_mode = NULL;
+ if( var_Type( p_vout, "vout-deinterlace" ) != 0 )
+ psz_mode = var_CreateGetNonEmptyString( p_vout, "vout-deinterlace" );
+ if( !psz_mode )
+ {
+ /* Get the initial value */
+ if( DeinterlaceIsPresent( p_vout, true ) )
+ psz_mode = var_CreateGetNonEmptyString( p_vout, "deinterlace-mode" );
+ else if( DeinterlaceIsPresent( p_vout, false ) )
+ psz_mode = var_CreateGetNonEmptyString( p_vout, "sout-deinterlace-mode" );
+ }
+ var_SetString( p_vout, "deinterlace", psz_mode ? psz_mode : "" );
+ free( psz_mode );
+}
+