1 /*****************************************************************************
2 * postproc.c: video postprocessing using libpostproc
3 *****************************************************************************
4 * Copyright (C) 1999-2008 the VideoLAN team
7 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8 * Gildas Bazin <gbazin@netcourrier.com>
9 * Antoine Cellerier <dionoea at videolan dot org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
30 #include <vlc_common.h>
31 #include <vlc_plugin.h>
34 #include "filter_picture.h"
36 #ifdef HAVE_POSTPROC_POSTPROCESS_H
37 # include <postproc/postprocess.h>
39 # include <libpostproc/postprocess.h>
42 #ifndef PP_CPU_CAPS_ALTIVEC
43 # define PP_CPU_CAPS_ALTIVEC 0
46 /*****************************************************************************
48 *****************************************************************************/
49 static int OpenPostproc( vlc_object_t
* );
50 static void ClosePostproc( vlc_object_t
* );
52 static picture_t
*PostprocPict( filter_t
*, picture_t
* );
54 static int PPQCallback( vlc_object_t
*, char const *,
55 vlc_value_t
, vlc_value_t
, void * );
56 static int PPNameCallback( vlc_object_t
*, char const *,
57 vlc_value_t
, vlc_value_t
, void * );
59 #define Q_TEXT N_("Post processing quality")
60 #define Q_LONGTEXT N_( \
61 "Quality of post processing. Valid range is 0 to 6\n" \
62 "Higher levels require considerable more CPU power, but produce " \
63 "better looking pictures." )
65 #define NAME_TEXT N_("FFmpeg post processing filter chains")
66 #define NAME_LONGTEXT NAME_TEXT
68 #define FILTER_PREFIX "postproc-"
70 /*****************************************************************************
72 *****************************************************************************/
74 set_description( N_("Video post processing filter") )
75 set_shortname( N_("Postproc" ) )
76 add_shortcut( "postprocess" ) /* name is "postproc" */
78 set_category( CAT_VIDEO
)
79 set_subcategory( SUBCAT_VIDEO_VFILTER
)
81 set_capability( "video filter2", 0 )
83 set_callbacks( OpenPostproc
, ClosePostproc
)
85 add_integer_with_range( FILTER_PREFIX
"q", PP_QUALITY_MAX
, 0,
86 PP_QUALITY_MAX
, NULL
, Q_TEXT
, Q_LONGTEXT
, false )
87 add_deprecated_alias( "ffmpeg-pp-q" )
88 add_string( FILTER_PREFIX
"name", "default", NULL
, NAME_TEXT
,
90 add_deprecated_alias( "ffmpeg-pp-name" )
93 static const char *const ppsz_filter_options
[] = {
97 /*****************************************************************************
98 * filter_sys_t : libpostproc video postprocessing descriptor
99 *****************************************************************************/
102 pp_context_t
*pp_context
; /* Never changes after init */
103 pp_mode_t
*pp_mode
; /* Set to NULL if post processing is disabled */
105 bool b_had_matrix
; /* Set to true if previous pic had a quant matrix (used to prevent spamming warning messages */
107 vlc_mutex_t lock
; /* Lock when using or changing pp_mode */
111 /*****************************************************************************
112 * OpenPostproc: probe and open the postproc
113 *****************************************************************************/
114 static int OpenPostproc( vlc_object_t
*p_this
)
116 filter_t
*p_filter
= (filter_t
*)p_this
;
118 vlc_value_t val
, val_orig
, text
;
119 unsigned i_cpu
= vlc_CPU();
122 if( p_filter
->fmt_in
.video
.i_chroma
!= p_filter
->fmt_out
.video
.i_chroma
||
123 p_filter
->fmt_in
.video
.i_height
!= p_filter
->fmt_out
.video
.i_height
||
124 p_filter
->fmt_in
.video
.i_width
!= p_filter
->fmt_out
.video
.i_width
)
126 msg_Err( p_filter
, "Filter input and output formats must be identical" );
130 /* Set CPU capabilities */
131 if( i_cpu
& CPU_CAPABILITY_MMX
)
132 i_flags
|= PP_CPU_CAPS_MMX
;
133 if( i_cpu
& CPU_CAPABILITY_MMXEXT
)
134 i_flags
|= PP_CPU_CAPS_MMX2
;
135 if( i_cpu
& CPU_CAPABILITY_3DNOW
)
136 i_flags
|= PP_CPU_CAPS_3DNOW
;
137 if( i_cpu
& CPU_CAPABILITY_ALTIVEC
)
138 i_flags
|= PP_CPU_CAPS_ALTIVEC
;
140 switch( p_filter
->fmt_in
.video
.i_chroma
)
142 case VLC_FOURCC('I','4','4','4'):
143 case VLC_FOURCC('J','4','4','4'):
144 /* case VLC_FOURCC('Y','U','V','A'): FIXME Should work but alpha plane needs to be copied manually and I'm kind of feeling too lazy to write the code to do that ATM (i_pitch vs i_visible_pitch...). */
145 i_flags
|= PP_FORMAT_444
;
147 case VLC_FOURCC('I','4','2','2'):
148 case VLC_FOURCC('J','4','2','2'):
149 i_flags
|= PP_FORMAT_422
;
151 case VLC_FOURCC('I','4','1','1'):
152 i_flags
|= PP_FORMAT_411
;
154 case VLC_FOURCC('I','4','2','0'):
155 case VLC_FOURCC('I','Y','U','V'):
156 case VLC_FOURCC('J','4','2','0'):
157 case VLC_FOURCC('Y','V','1','2'):
158 i_flags
|= PP_FORMAT_420
;
161 msg_Err( p_filter
, "Unsupported input chroma (%4s)",
162 (char*)&p_filter
->fmt_in
.video
.i_chroma
);
166 p_sys
= malloc( sizeof( filter_sys_t
) );
167 if( !p_sys
) return VLC_ENOMEM
;
168 p_filter
->p_sys
= p_sys
;
170 p_sys
->pp_context
= pp_get_context( p_filter
->fmt_in
.video
.i_width
,
171 p_filter
->fmt_in
.video
.i_height
,
173 if( !p_sys
->pp_context
)
175 msg_Err( p_filter
, "Error while creating post processing context." );
180 config_ChainParse( p_filter
, FILTER_PREFIX
, ppsz_filter_options
,
183 var_Create( p_filter
, FILTER_PREFIX
"q",
184 VLC_VAR_INTEGER
| VLC_VAR_HASCHOICE
| VLC_VAR_DOINHERIT
|
186 var_Change( p_filter
, FILTER_PREFIX
"q", VLC_VAR_SETISCOMMAND
, NULL
, NULL
); /* For some obscure reason the VLC_VAR_ISCOMMAND isn't taken into account in during var_Create */
187 var_AddCallback( p_filter
, FILTER_PREFIX
"q", PPQCallback
, NULL
);
188 text
.psz_string
= _("Post processing");
189 var_Change( p_filter
, FILTER_PREFIX
"q", VLC_VAR_SETTEXT
, &text
, NULL
);
191 var_Get( p_filter
, FILTER_PREFIX
"q", &val_orig
);
192 var_Change( p_filter
, FILTER_PREFIX
"q", VLC_VAR_DELCHOICE
, &val_orig
, NULL
);
194 val
.psz_string
= var_CreateGetNonEmptyStringCommand(
195 p_filter
, FILTER_PREFIX
"name" );
196 var_AddCallback( p_filter
, FILTER_PREFIX
"name", PPNameCallback
, NULL
);
199 p_sys
->pp_mode
= pp_get_mode_by_name_and_quality( val
.psz_string
?:
203 if( !p_sys
->pp_mode
)
205 msg_Err( p_filter
, "Error while creating post processing mode." );
206 free( val
.psz_string
);
207 var_Destroy( p_filter
, FILTER_PREFIX
"q" );
208 pp_free_context( p_sys
->pp_context
);
215 p_sys
->pp_mode
= NULL
;
217 free( val
.psz_string
);
219 for( val
.i_int
= 0; val
.i_int
<= PP_QUALITY_MAX
; val
.i_int
++ )
224 text
.psz_string
= _("Disable");
227 text
.psz_string
= _("Lowest");
230 text
.psz_string
= _("Highest");
233 text
.psz_string
= NULL
;
236 var_Change( p_filter
, FILTER_PREFIX
"q", VLC_VAR_ADDCHOICE
,
237 &val
, text
.psz_string
?&text
:NULL
);
240 vlc_mutex_init( &p_sys
->lock
);
242 p_filter
->pf_video_filter
= PostprocPict
;
243 p_sys
->b_had_matrix
= true;
248 /*****************************************************************************
250 *****************************************************************************/
251 static void ClosePostproc( vlc_object_t
*p_this
)
253 filter_t
*p_filter
= (filter_t
*)p_this
;
254 filter_sys_t
*p_sys
= p_filter
->p_sys
;
255 vlc_mutex_destroy( &p_sys
->lock
);
256 pp_free_context( p_sys
->pp_context
);
257 if( p_sys
->pp_mode
) pp_free_mode( p_sys
->pp_mode
);
261 /*****************************************************************************
263 *****************************************************************************/
264 static picture_t
*PostprocPict( filter_t
*p_filter
, picture_t
*p_pic
)
266 filter_sys_t
*p_sys
= p_filter
->p_sys
;
268 const uint8_t *src
[3];
271 int i_src_stride
[3], i_dst_stride
[3];
273 vlc_mutex_lock( &p_sys
->lock
); /* Lock to prevent issues if pp_mode is changed */
274 if( !p_sys
->pp_mode
)
276 vlc_mutex_unlock( &p_sys
->lock
);
280 picture_t
*p_outpic
= filter_NewPicture( p_filter
);
283 picture_Release( p_pic
);
284 vlc_mutex_unlock( &p_sys
->lock
);
288 for( i_plane
= 0; i_plane
< p_pic
->i_planes
; i_plane
++ )
290 src
[i_plane
] = p_pic
->p
[i_plane
].p_pixels
;
291 dst
[i_plane
] = p_outpic
->p
[i_plane
].p_pixels
;
293 /* I'm not sure what happens if i_pitch != i_visible_pitch ...
294 * at least it shouldn't crash. */
295 i_src_stride
[i_plane
] = p_pic
->p
[i_plane
].i_pitch
;
296 i_dst_stride
[i_plane
] = p_outpic
->p
[i_plane
].i_pitch
;
299 if( !p_pic
->p_q
&& p_sys
->b_had_matrix
)
301 msg_Warn( p_filter
, "Quantification table was not set by video decoder. Postprocessing won't look good." );
302 p_sys
->b_had_matrix
= false;
304 else if( p_pic
->p_q
)
306 p_sys
->b_had_matrix
= true;
309 pp_postprocess( src
, i_src_stride
, dst
, i_dst_stride
,
310 p_filter
->fmt_in
.video
.i_width
,
311 p_filter
->fmt_in
.video
.i_height
,
312 p_pic
->p_q
, p_pic
->i_qstride
,
313 p_sys
->pp_mode
, p_sys
->pp_context
,
314 p_pic
->i_qtype
== QTYPE_MPEG2
? PP_PICT_TYPE_QP2
: 0 );
315 vlc_mutex_unlock( &p_sys
->lock
);
317 return CopyInfoAndRelease( p_outpic
, p_pic
);
320 /*****************************************************************************
322 *****************************************************************************/
323 static void PPChangeMode( filter_t
*p_filter
, const char *psz_name
,
326 filter_sys_t
*p_sys
= p_filter
->p_sys
;
327 vlc_mutex_lock( &p_sys
->lock
);
330 pp_mode_t
*pp_mode
= pp_get_mode_by_name_and_quality( psz_name
?:
335 pp_free_mode( p_sys
->pp_mode
);
336 p_sys
->pp_mode
= pp_mode
;
339 msg_Warn( p_filter
, "Error while changing post processing mode. "
340 "Keeping previous mode." );
344 pp_free_mode( p_sys
->pp_mode
);
345 p_sys
->pp_mode
= NULL
;
347 vlc_mutex_unlock( &p_sys
->lock
);
350 static int PPQCallback( vlc_object_t
*p_this
, const char *psz_var
,
351 vlc_value_t oldval
, vlc_value_t newval
, void *p_data
)
353 VLC_UNUSED(psz_var
); VLC_UNUSED(oldval
); VLC_UNUSED(p_data
);
354 filter_t
*p_filter
= (filter_t
*)p_this
;
356 char *psz_name
= var_GetNonEmptyString( p_filter
, FILTER_PREFIX
"name" );
357 PPChangeMode( p_filter
, psz_name
, newval
.i_int
);
362 static int PPNameCallback( vlc_object_t
*p_this
, const char *psz_var
,
363 vlc_value_t oldval
, vlc_value_t newval
, void *p_data
)
365 VLC_UNUSED(psz_var
); VLC_UNUSED(oldval
); VLC_UNUSED(p_data
);
366 filter_t
*p_filter
= (filter_t
*)p_this
;
368 int i_quality
= var_GetInteger( p_filter
, FILTER_PREFIX
"q" );
369 PPChangeMode( p_filter
, *newval
.psz_string
?newval
.psz_string
:NULL
, i_quality
);