44
44
static picture_t *Filter( filter_t *, picture_t * );
45
45
static int alloc_init( filter_t *, void * );
47
#define WIDTH_TEXT N_( "Image width" )
47
/* This module effectively implements a form of picture-in-picture.
48
* - The outer picture is called the canvas.
49
* - The innter picture is callsed the subpicture.
51
* NB, all of the following operatons take into account aspect ratio
53
* A canvas is of canvas_{width,height}.
55
* - The subpicture is upconverted with a inverse scalefactor of:
56
* (The size of subpicture's largest dimension)
57
* --------------------------------------------
58
* (The size of canvas's equivalent dimension)
60
* Ie, The subpicture's largest dimension is made equal to the
61
* equivalent canvas dimension.
63
* - The upconverted subpicture's smallest dimension is then padded
64
* to make the upconverted subpicture have the same dimensions of
68
* - The subpicture is upconverted with an inverse scalefactor of:
69
* (The size of subpicture's smallest dimension)
70
* --------------------------------------------
71
* (The size of canvas's equivalent dimension)
73
* Ie, The subpicture's smallest dimension is made equal to the
74
* equivalent canvas dimension. (The subpicture will then be
75
* larger than the canvas)
77
* - The upconverted subpicture's largest dimension is then cropped
78
* to make the upconverted subpicture have the same dimensions of
82
/* NB, use of `padd' in this module is a 16-17th Century spelling of `pad' */
84
#define WIDTH_TEXT N_( "Output width" )
48
85
#define WIDTH_LONGTEXT N_( \
50
#define HEIGHT_TEXT N_( "Image height" )
86
"Output (canvas) image width" )
87
#define HEIGHT_TEXT N_( "Output height" )
51
88
#define HEIGHT_LONGTEXT N_( \
53
#define ASPECT_TEXT N_( "Aspect ratio" )
89
"Output (canvas) image height" )
90
#define ASPECT_TEXT N_( "Output picture aspect ratio" )
54
91
#define ASPECT_LONGTEXT N_( \
55
"Set aspect (like 4:3) of the video canvas" )
56
#define PADD_TEXT N_( "Padd video" )
92
"Set the canvas' picture aspect ratio. " \
93
"If omitted, the canvas is assumed to have the same SAR as the input." )
94
#define PADD_TEXT N_( "Pad video" )
57
95
#define PADD_LONGTEXT N_( \
58
96
"If enabled, video will be padded to fit in canvas after scaling. " \
59
97
"Otherwise, video will be cropped to fix in canvas after scaling." )
76
114
add_integer_with_range( CFG_PREFIX "height", 0, 0, INT_MAX, NULL,
77
115
HEIGHT_TEXT, HEIGHT_LONGTEXT, false )
79
add_string( CFG_PREFIX "aspect", "4:3", NULL,
117
add_string( CFG_PREFIX "aspect", NULL, NULL,
80
118
ASPECT_TEXT, ASPECT_LONGTEXT, false )
82
120
add_bool( CFG_PREFIX "padd", true, NULL,
98
136
static int Activate( vlc_object_t *p_this )
100
138
filter_t *p_filter = (filter_t *)p_this;
101
unsigned int i_width, i_height;
139
unsigned i_canvas_width; /* width of output canvas */
140
unsigned i_canvas_height; /* height of output canvas */
141
unsigned i_canvas_aspect; /* canvas PictureAspectRatio */
142
es_format_t fmt; /* target format after up/down conversion */
103
143
char psz_croppadd[100];
104
144
int i_padd,i_offset;
105
145
char *psz_aspect, *psz_parser;
109
148
if( !p_filter->b_allow_fmt_out_change )
121
160
config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
122
161
p_filter->p_cfg );
124
i_width = var_CreateGetInteger( p_filter, CFG_PREFIX "width" );
125
i_height = var_CreateGetInteger( p_filter, CFG_PREFIX "height" );
163
i_canvas_width = var_CreateGetInteger( p_filter, CFG_PREFIX "width" );
164
i_canvas_height = var_CreateGetInteger( p_filter, CFG_PREFIX "height" );
127
if( i_width == 0 || i_height == 0 )
166
if( i_canvas_width == 0 || i_canvas_height == 0 )
129
168
msg_Err( p_filter, "Width and height options must be set" );
130
169
return VLC_EGENERIC;
133
if( i_width & 1 || i_height & 1 )
172
if( i_canvas_width & 1 || i_canvas_height & 1 )
174
/* If this restriction were ever relaxed, it is very important to
175
* get the field polatiry correct */
135
176
msg_Err( p_filter, "Width and height options must be even integers" );
136
177
return VLC_EGENERIC;
139
180
psz_aspect = var_CreateGetNonEmptyString( p_filter, CFG_PREFIX "aspect" );
142
msg_Err( p_filter, "Aspect ratio must be set" );
183
psz_parser = strchr( psz_aspect, ':' );
184
int numerator = atoi( psz_aspect );
185
int denominator = psz_parser ? atoi( psz_parser+1 ) : 0;
186
denominator = denominator == 0 ? 1 : denominator;
187
i_canvas_aspect = numerator * VOUT_ASPECT_FACTOR / denominator;
190
if( numerator <= 0 || denominator < 0 )
192
msg_Err( p_filter, "Aspect ratio must be strictly positive" );
145
psz_parser = strchr( psz_aspect, ':' );
146
if( psz_parser ) psz_parser++;
147
if( psz_parser && atoi( psz_parser ) > 0 )
148
i_aspect = atoi( psz_aspect ) * VOUT_ASPECT_FACTOR / atoi( psz_parser );
150
i_aspect = atof( psz_aspect ) * VOUT_ASPECT_FACTOR;
155
msg_Err( p_filter, "Aspect ratio must be strictly positive" );
198
/* if there is no user supplied aspect ratio, assume the canvas
199
* has the same sample aspect ratio as the subpicture */
200
/* aspect = subpic_sar * canvas_width / canvas_height
201
* where subpic_sar = subpic_ph * subpic_par / subpic_pw */
202
i_canvas_aspect = (uint64_t) p_filter->fmt_in.video.i_height
203
* p_filter->fmt_in.video.i_aspect
205
/ (i_canvas_height * p_filter->fmt_in.video.i_width);
159
208
b_padd = var_CreateGetBool( p_filter, CFG_PREFIX "padd" );
175
224
es_format_Copy( &fmt, &p_filter->fmt_in );
177
fmt.video.i_width = i_width;
178
fmt.video.i_height = ( p_filter->fmt_in.video.i_height * i_width )
179
/ p_filter->fmt_in.video.i_width;
180
fmt.video.i_height = ( fmt.video.i_height * i_aspect )
181
/ p_filter->fmt_in.video.i_aspect;
226
/* one dimension will end up with one of the following: */
227
fmt.video.i_width = i_canvas_width;
228
fmt.video.i_height = i_canvas_height;
186
if( fmt.video.i_height > i_height )
233
if( i_canvas_aspect > p_filter->fmt_in.video.i_aspect )
188
fmt.video.i_height = i_height;
189
fmt.video.i_width = ( p_filter->fmt_in.video.i_width * i_height )
190
/ p_filter->fmt_in.video.i_height;
191
fmt.video.i_width = ( fmt.video.i_width * p_filter->fmt_in.video.i_aspect )
235
/* The canvas has a wider aspect than the subpicture:
236
* ie, pillarbox the [scaled] subpicture */
237
/* The following is derived form:
238
* width = upconverted_subpic_height * subpic_par / canvas_sar
239
* where canvas_sar = canvas_width / (canvas_height * canvas_par)
241
fmt.video.i_width = i_canvas_width
242
* p_filter->fmt_in.video.i_aspect
193
244
if( fmt.video.i_width & 1 ) fmt.video.i_width -= 1;
195
i_padd = (i_width - fmt.video.i_width) / 2;
246
i_padd = (i_canvas_width - fmt.video.i_width) / 2;
196
247
i_offset = (i_padd & 1);
198
248
snprintf( psz_croppadd, 100, "croppadd{paddleft=%d,paddright=%d}",
199
249
i_padd - i_offset, i_padd + i_offset );
253
/* The canvas has a taller aspect than the subpicture:
254
* ie, letterbox the [scaled] subpicture */
255
fmt.video.i_height = i_canvas_height
257
/ p_filter->fmt_in.video.i_aspect;
203
258
if( fmt.video.i_height & 1 ) fmt.video.i_height -= 1;
204
i_padd = (i_height - fmt.video.i_height ) / 2;
260
i_padd = (i_canvas_height - fmt.video.i_height ) / 2;
205
261
i_offset = (i_padd & 1);
207
262
snprintf( psz_croppadd, 100, "croppadd{paddtop=%d,paddbottom=%d}",
208
263
i_padd - i_offset, i_padd + i_offset );
214
if( fmt.video.i_height < i_height )
269
if( i_canvas_aspect < p_filter->fmt_in.video.i_aspect )
216
fmt.video.i_height = i_height;
217
fmt.video.i_width = ( p_filter->fmt_in.video.i_width * i_height )
218
/ p_filter->fmt_in.video.i_height;
219
fmt.video.i_width = ( fmt.video.i_width * p_filter->fmt_in.video.i_aspect )
271
/* The canvas has a narrower aspect than the subpicture:
272
* ie, crop the [scaled] subpicture horizontally */
273
fmt.video.i_width = i_canvas_width
274
* p_filter->fmt_in.video.i_aspect
221
276
if( fmt.video.i_width & 1 ) fmt.video.i_width -= 1;
223
i_padd = (fmt.video.i_width - i_width) / 2;
224
i_offset = (i_padd & 1);
278
i_padd = (fmt.video.i_width - i_canvas_width) / 2;
279
i_offset = (i_padd & 1);
226
280
snprintf( psz_croppadd, 100, "croppadd{cropleft=%d,cropright=%d}",
227
281
i_padd - i_offset, i_padd + i_offset );
285
/* The canvas has a shorter aspect than the subpicture:
286
* ie, crop the [scaled] subpicture vertically */
287
fmt.video.i_height = i_canvas_height
289
/ p_filter->fmt_in.video.i_aspect;
231
290
if( fmt.video.i_height & 1 ) fmt.video.i_height -= 1;
232
i_padd = (fmt.video.i_height - i_height) / 2;
292
i_padd = (fmt.video.i_height - i_canvas_height) / 2;
233
293
i_offset = (i_padd & 1);
235
294
snprintf( psz_croppadd, 100, "croppadd{croptop=%d,cropbottom=%d}",
236
295
i_padd - i_offset, i_padd + i_offset );
299
/* xxx, should the clean area include the letter-boxing?
300
* probably not, as some codecs can make use of that information
301
* and it should be a scaled version of the input clean area
240
303
fmt.video.i_visible_width = fmt.video.i_width;
241
304
fmt.video.i_visible_height = fmt.video.i_height;
249
312
fmt = *filter_chain_GetFmtOut( p_sys->p_chain );
250
313
es_format_Copy( &p_filter->fmt_out, &fmt );
252
p_filter->fmt_out.video.i_aspect = i_aspect * i_width / i_height;
315
p_filter->fmt_out.video.i_aspect = i_canvas_aspect;
254
if( p_filter->fmt_out.video.i_width != i_width
255
|| p_filter->fmt_out.video.i_height != i_height )
317
if( p_filter->fmt_out.video.i_width != i_canvas_width
318
|| p_filter->fmt_out.video.i_height != i_canvas_height )
257
320
msg_Warn( p_filter, "Looks like something went wrong. "
258
321
"Output size is %dx%d while we asked for %dx%d",
259
322
p_filter->fmt_out.video.i_width,
260
323
p_filter->fmt_out.video.i_height,
324
i_canvas_width, i_canvas_height );
264
327
p_filter->pf_video_filter = Filter;