+ }
+ if( psz_vf2 )
+ {
+ var_SetString( p_vout, "video-filter", psz_vf2 );
+ free( psz_vf2 );
+ }
+
+ return VLC_SUCCESS;
+}
+static void PostProcessEnable( vout_thread_t *p_vout )
+{
+ vlc_value_t text;
+ msg_Dbg( p_vout, "Post-processing available" );
+ var_Create( p_vout, "postprocess", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
+ text.psz_string = _("Post processing");
+ var_Change( p_vout, "postprocess", VLC_VAR_SETTEXT, &text, NULL );
+
+ for( int i = 0; i <= 6; i++ )
+ {
+ vlc_value_t val;
+ vlc_value_t text;
+ char psz_text[1+1];
+
+ val.i_int = i;
+ snprintf( psz_text, sizeof(psz_text), "%d", i );
+ if( i == 0 )
+ text.psz_string = _("Disable");
+ else
+ text.psz_string = psz_text;
+ var_Change( p_vout, "postprocess", VLC_VAR_ADDCHOICE, &val, &text );
+ }
+ var_AddCallback( p_vout, "postprocess", PostProcessCallback, NULL );
+
+ /* */
+ char *psz_filter = var_GetNonEmptyString( p_vout, "video-filter" );
+ int i_postproc_q = 0;
+ if( PostProcessIsPresent( psz_filter ) )
+ i_postproc_q = var_CreateGetInteger( p_vout, "postproc-q" );
+
+ var_SetInteger( p_vout, "postprocess", i_postproc_q );
+
+ free( psz_filter );
+}
+static void PostProcessDisable( vout_thread_t *p_vout )
+{
+ msg_Dbg( p_vout, "Post-processing no more available" );
+ var_Destroy( p_vout, "postprocess" );
+}
+static void PostProcessSetFilterQuality( vout_thread_t *p_vout )
+{
+ vlc_object_t *p_pp = vlc_object_find_name( p_vout, "postproc", FIND_CHILD );
+ if( !p_pp )
+ return;
+
+ var_SetInteger( p_pp, "postproc-q", var_GetInteger( p_vout, "postprocess" ) );
+ vlc_object_release( p_pp );
+}
+
+
+static void DisplayTitleOnOSD( vout_thread_t *p_vout )
+{
+ const mtime_t i_start = mdate();
+ const mtime_t i_stop = i_start + INT64_C(1000) * p_vout->p->i_title_timeout;
+
+ if( i_stop <= i_start )
+ return;
+
+ vlc_assert_locked( &p_vout->change_lock );
+
+ vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
+ p_vout->p->psz_title, NULL,
+ p_vout->p->i_title_position,
+ 30 + p_vout->fmt_in.i_width
+ - p_vout->fmt_in.i_visible_width
+ - p_vout->fmt_in.i_x_offset,
+ 20 + p_vout->fmt_in.i_y_offset,
+ i_start, i_stop );
+
+ free( p_vout->p->psz_title );
+
+ p_vout->p->psz_title = NULL;
+}
+
+/*****************************************************************************
+ * 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, 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 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 = true;
+ 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 ) )
+ {
+ b_vout_filter = p_mode->b_vout_filter;
+ break;
+ }
+ }
+
+ /* */
+ 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 );
+ }