+/*****************************************************************************
+ * Deinterlacing
+ *****************************************************************************/
+typedef struct
+{
+ const char *psz_mode;
+ 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[] = {
+ { "", false },
+ //{ "discard", true },
+ { "blend", false },
+ //{ "mean", true },
+ //{ "bob", true },
+ //{ "linear", true },
+ { "x", false },
+ //{ "yadif", true },
+ //{ "yadif2x", true },
+ { NULL, false }
+};
+static const deinterlace_mode_t *DeinterlaceGetMode( const char *psz_mode )
+{
+ for( const deinterlace_mode_t *p_mode = &p_deinterlace_mode[0]; p_mode->psz_mode; p_mode++ )
+ {
+ if( !strcmp( p_mode->psz_mode, psz_mode ) )
+ return p_mode;
+ }
+ return NULL;
+}
+
+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 void DeinterlaceSave( vout_thread_t *p_vout, int i_deinterlace, const char *psz_mode, bool is_needed )
+{
+ /* 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 */
+ vlc_object_t *p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, FIND_PARENT );
+ if( !p_input )
+ return;
+
+ /* Another hack for "vout filter" mode */
+ if( i_deinterlace < 0 )
+ i_deinterlace = is_needed ? -2 : -3;
+
+ var_Create( p_input, "deinterlace", VLC_VAR_INTEGER );
+ var_SetInteger( p_input, "deinterlace", i_deinterlace );
+
+ static const char * const ppsz_variable[] = {
+ "deinterlace-mode",
+ "filter-deinterlace-mode",
+ "sout-deinterlace-mode",
+ NULL
+ };
+ for( int i = 0; ppsz_variable[i]; i++ )
+ {
+ var_Create( p_input, ppsz_variable[i], VLC_VAR_STRING );
+ var_SetString( p_input, ppsz_variable[i], psz_mode );
+ }
+
+ vlc_object_release( p_input );
+}
+static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd,
+ vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+ VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(newval); VLC_UNUSED(p_data);
+ vout_thread_t *p_vout = (vout_thread_t *)p_this;
+
+ /* */
+ const int i_deinterlace = var_GetInteger( p_this, "deinterlace" );
+ char *psz_mode = var_GetString( p_this, "deinterlace-mode" );
+ const bool is_needed = var_GetBool( p_this, "deinterlace-needed" );
+ if( !psz_mode )
+ return VLC_EGENERIC;
+
+ DeinterlaceSave( p_vout, i_deinterlace, psz_mode, is_needed );
+
+ /* */
+ bool b_vout_filter = false;
+ const deinterlace_mode_t *p_mode = DeinterlaceGetMode( psz_mode );
+ if( p_mode )
+ b_vout_filter = p_mode->b_vout_filter;
+
+ /* */
+ char *psz_old;
+ if( b_vout_filter )
+ {
+ psz_old = var_CreateGetString( p_vout, "filter-deinterlace-mode" );
+ }
+ else
+ {
+ psz_old = var_CreateGetString( p_vout, "sout-deinterlace-mode" );
+ var_SetString( p_vout, "sout-deinterlace-mode", psz_mode );
+ }
+
+ msg_Dbg( p_vout, "deinterlace %d, mode %s, is_needed %d", i_deinterlace, psz_mode, is_needed );
+ if( i_deinterlace == 0 || ( i_deinterlace == -1 && !is_needed ) )
+ {
+ DeinterlaceRemove( p_vout, false );
+ DeinterlaceRemove( p_vout, true );
+ }
+ else
+ {
+ if( !DeinterlaceIsPresent( p_vout, b_vout_filter ) )
+ {
+ DeinterlaceRemove( p_vout, !b_vout_filter );
+ DeinterlaceAdd( p_vout, b_vout_filter );
+ }
+ else
+ {
+ /* The deinterlace filter was already inserted but we have changed the mode */
+ DeinterlaceRemove( p_vout, !b_vout_filter );
+ if( psz_old && strcmp( psz_old, psz_mode ) )
+ var_TriggerCallback( p_vout, b_vout_filter ? "vout-filter" : "video-filter" );
+ }
+ }
+
+ /* */
+ free( psz_old );
+ free( psz_mode );
+ 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 variables */
+ /* */
+ var_Create( p_vout, "deinterlace", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT | VLC_VAR_HASCHOICE );
+ int i_deinterlace = var_GetInteger( p_vout, "deinterlace" );
+
+ text.psz_string = _("Deinterlace");
+ var_Change( p_vout, "deinterlace", VLC_VAR_SETTEXT, &text, NULL );
+
+ const module_config_t *p_optd = config_FindConfig( VLC_OBJECT(p_vout), "deinterlace" );
+ var_Change( p_vout, "deinterlace", VLC_VAR_CLEARCHOICES, NULL, NULL );
+ for( int i = 0; p_optd && i < p_optd->i_list; i++ )
+ {
+ val.i_int = p_optd->pi_list[i];
+ text.psz_string = (char*)vlc_gettext(p_optd->ppsz_list_text[i]);
+ var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
+ }
+ var_AddCallback( p_vout, "deinterlace", DeinterlaceCallback, NULL );
+ /* */
+ var_Create( p_vout, "deinterlace-mode", VLC_VAR_STRING | VLC_VAR_DOINHERIT | VLC_VAR_HASCHOICE );
+ char *psz_deinterlace = var_GetNonEmptyString( p_vout, "deinterlace-mode" );
+
+ text.psz_string = _("Deinterlace mode");
+ var_Change( p_vout, "deinterlace-mode", VLC_VAR_SETTEXT, &text, NULL );
+
+ const module_config_t *p_optm = config_FindConfig( VLC_OBJECT(p_vout), "deinterlace-mode" );
+ var_Change( p_vout, "deinterlace-mode", VLC_VAR_CLEARCHOICES, NULL, NULL );
+ for( int i = 0; p_optm && i < p_optm->i_list; i++ )
+ {
+ if( !DeinterlaceGetMode( p_optm->ppsz_list[i] ) )
+ continue;
+
+ val.psz_string = p_optm->ppsz_list[i];
+ text.psz_string = (char*)vlc_gettext(p_optm->ppsz_list_text[i]);
+ var_Change( p_vout, "deinterlace-mode", VLC_VAR_ADDCHOICE, &val, &text );
+ }
+ var_AddCallback( p_vout, "deinterlace-mode", DeinterlaceCallback, NULL );
+ /* */
+ var_Create( p_vout, "deinterlace-needed", VLC_VAR_BOOL );
+ var_AddCallback( p_vout, "deinterlace-needed", DeinterlaceCallback, NULL );
+
+ /* Override the initial value from filters if present */
+ char *psz_filter_mode = NULL;
+ if( DeinterlaceIsPresent( p_vout, true ) )
+ psz_filter_mode = var_CreateGetNonEmptyString( p_vout, "filter-deinterlace-mode" );
+ else if( DeinterlaceIsPresent( p_vout, false ) )
+ psz_filter_mode = var_CreateGetNonEmptyString( p_vout, "sout-deinterlace-mode" );
+ if( psz_filter_mode )
+ {
+ free( psz_deinterlace );
+ if( i_deinterlace >= -1 )
+ i_deinterlace = 1;
+ psz_deinterlace = psz_filter_mode;
+ }
+
+ /* */
+ if( i_deinterlace == -2 )
+ p_vout->p->b_picture_interlaced = true;
+ else if( i_deinterlace == -3 )
+ p_vout->p->b_picture_interlaced = false;
+ if( i_deinterlace < 0 )
+ i_deinterlace = -1;
+
+ /* */
+ val.psz_string = psz_deinterlace ? psz_deinterlace : p_optm->orig.psz;
+ var_Change( p_vout, "deinterlace-mode", VLC_VAR_SETVALUE, &val, NULL );
+ val.b_bool = p_vout->p->b_picture_interlaced;
+ var_Change( p_vout, "deinterlace-needed", VLC_VAR_SETVALUE, &val, NULL );
+
+ var_SetInteger( p_vout, "deinterlace", i_deinterlace );
+ free( psz_deinterlace );
+}
+
+static void DeinterlaceNeeded( vout_thread_t *p_vout, bool is_interlaced )
+{
+ msg_Dbg( p_vout, "Detected %s video",
+ is_interlaced ? "interlaced" : "progressive" );
+ var_SetBool( p_vout, "deinterlace-needed", is_interlaced );
+}
+