Main Page
Related Pages
Modules
Data Structures
Files
Examples
File List
Globals
libavfilter
vf_crop.c
Go to the documentation of this file.
1
/*
2
* Copyright (c) 2007 Bobby Bingham
3
*
4
* This file is part of Libav.
5
*
6
* Libav is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU Lesser General Public
8
* License as published by the Free Software Foundation; either
9
* version 2.1 of the License, or (at your option) any later version.
10
*
11
* Libav is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
* Lesser General Public License for more details.
15
*
16
* You should have received a copy of the GNU Lesser General Public
17
* License along with Libav; if not, write to the Free Software
18
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
*/
20
26
/* #define DEBUG */
27
28
#include "
avfilter.h
"
29
#include "
libavutil/eval.h
"
30
#include "
libavutil/avstring.h
"
31
#include "
libavutil/libm.h
"
32
#include "
libavutil/imgutils.h
"
33
#include "
libavutil/mathematics.h
"
34
35
static
const
char
*
var_names
[] = {
36
"E"
,
37
"PHI"
,
38
"PI"
,
39
"in_w"
,
"iw"
,
40
"in_h"
,
"ih"
,
41
"out_w"
,
"ow"
,
42
"out_h"
,
"oh"
,
43
"x"
,
44
"y"
,
45
"n"
,
46
"pos"
,
47
"t"
,
48
NULL
49
};
50
51
enum
var_name
{
52
VAR_E
,
53
VAR_PHI
,
54
VAR_PI
,
55
VAR_IN_W
,
VAR_IW
,
56
VAR_IN_H
,
VAR_IH
,
57
VAR_OUT_W
,
VAR_OW
,
58
VAR_OUT_H
,
VAR_OH
,
59
VAR_X
,
60
VAR_Y
,
61
VAR_N
,
62
VAR_POS
,
63
VAR_T
,
64
VAR_VARS_NB
65
};
66
67
typedef
struct
{
68
int
x
;
69
int
y
;
70
int
w
;
71
int
h
;
72
73
int
max_step[4];
74
int
hsub,
vsub
;
75
char
x_expr[256], y_expr[256], ow_expr[256], oh_expr[256];
76
AVExpr
*x_pexpr, *
y_pexpr
;
/* parsed expressions for x and y */
77
double
var_values[
VAR_VARS_NB
];
78
}
CropContext
;
79
80
static
int
query_formats
(
AVFilterContext
*ctx)
81
{
82
static
const
enum
PixelFormat
pix_fmts[] = {
83
PIX_FMT_RGB48BE
,
PIX_FMT_RGB48LE
,
84
PIX_FMT_BGR48BE
,
PIX_FMT_BGR48LE
,
85
PIX_FMT_ARGB
,
PIX_FMT_RGBA
,
86
PIX_FMT_ABGR
,
PIX_FMT_BGRA
,
87
PIX_FMT_RGB24
,
PIX_FMT_BGR24
,
88
PIX_FMT_RGB565BE
,
PIX_FMT_RGB565LE
,
89
PIX_FMT_RGB555BE
,
PIX_FMT_RGB555LE
,
90
PIX_FMT_BGR565BE
,
PIX_FMT_BGR565LE
,
91
PIX_FMT_BGR555BE
,
PIX_FMT_BGR555LE
,
92
PIX_FMT_GRAY16BE
,
PIX_FMT_GRAY16LE
,
93
PIX_FMT_YUV420P16LE
,
PIX_FMT_YUV420P16BE
,
94
PIX_FMT_YUV422P16LE
,
PIX_FMT_YUV422P16BE
,
95
PIX_FMT_YUV444P16LE
,
PIX_FMT_YUV444P16BE
,
96
PIX_FMT_YUV444P
,
PIX_FMT_YUV422P
,
97
PIX_FMT_YUV420P
,
PIX_FMT_YUV411P
,
98
PIX_FMT_YUV410P
,
PIX_FMT_YUV440P
,
99
PIX_FMT_YUVJ444P
,
PIX_FMT_YUVJ422P
,
100
PIX_FMT_YUVJ420P
,
PIX_FMT_YUVJ440P
,
101
PIX_FMT_YUVA420P
,
102
PIX_FMT_RGB8
,
PIX_FMT_BGR8
,
103
PIX_FMT_RGB4_BYTE
,
PIX_FMT_BGR4_BYTE
,
104
PIX_FMT_PAL8
,
PIX_FMT_GRAY8
,
105
PIX_FMT_NONE
106
};
107
108
avfilter_set_common_formats
(ctx,
avfilter_make_format_list
(pix_fmts));
109
110
return
0;
111
}
112
113
static
av_cold
int
init
(
AVFilterContext
*ctx,
const
char
*args,
void
*opaque)
114
{
115
CropContext
*crop = ctx->
priv
;
116
117
av_strlcpy
(crop->
ow_expr
,
"iw"
,
sizeof
(crop->
ow_expr
));
118
av_strlcpy
(crop->
oh_expr
,
"ih"
,
sizeof
(crop->
oh_expr
));
119
av_strlcpy
(crop->
x_expr
,
"(in_w-out_w)/2"
,
sizeof
(crop->
x_expr
));
120
av_strlcpy
(crop->
y_expr
,
"(in_h-out_h)/2"
,
sizeof
(crop->
y_expr
));
121
122
if
(args)
123
sscanf(args,
"%255[^:]:%255[^:]:%255[^:]:%255[^:]"
, crop->
ow_expr
, crop->
oh_expr
, crop->
x_expr
, crop->
y_expr
);
124
125
return
0;
126
}
127
128
static
av_cold
void
uninit
(
AVFilterContext
*ctx)
129
{
130
CropContext
*crop = ctx->
priv
;
131
132
av_expr_free
(crop->
x_pexpr
); crop->
x_pexpr
=
NULL
;
133
av_expr_free
(crop->
y_pexpr
); crop->
y_pexpr
=
NULL
;
134
}
135
136
static
inline
int
normalize_double
(
int
*n,
double
d)
137
{
138
int
ret = 0;
139
140
if
(isnan(d)) {
141
ret =
AVERROR
(EINVAL);
142
}
else
if
(d > INT_MAX || d < INT_MIN) {
143
*n = d > INT_MAX ? INT_MAX : INT_MIN;
144
ret =
AVERROR
(EINVAL);
145
}
else
146
*n =
round
(d);
147
148
return
ret;
149
}
150
151
static
int
config_input
(
AVFilterLink
*link)
152
{
153
AVFilterContext
*ctx = link->
dst
;
154
CropContext
*crop = ctx->
priv
;
155
const
AVPixFmtDescriptor
*pix_desc = &
av_pix_fmt_descriptors
[link->
format
];
156
int
ret;
157
const
char
*expr;
158
double
res;
159
160
crop->
var_values
[
VAR_E
] =
M_E
;
161
crop->
var_values
[
VAR_PHI
] =
M_PHI
;
162
crop->
var_values
[
VAR_PI
] =
M_PI
;
163
crop->
var_values
[
VAR_IN_W
] = crop->
var_values
[
VAR_IW
] = ctx->
inputs
[0]->
w
;
164
crop->
var_values
[
VAR_IN_H
] = crop->
var_values
[
VAR_IH
] = ctx->
inputs
[0]->
h
;
165
crop->
var_values
[
VAR_X
] =
NAN
;
166
crop->
var_values
[
VAR_Y
] =
NAN
;
167
crop->
var_values
[
VAR_OUT_W
] = crop->
var_values
[
VAR_OW
] =
NAN
;
168
crop->
var_values
[
VAR_OUT_H
] = crop->
var_values
[
VAR_OH
] =
NAN
;
169
crop->
var_values
[
VAR_N
] = 0;
170
crop->
var_values
[
VAR_T
] =
NAN
;
171
crop->
var_values
[
VAR_POS
] =
NAN
;
172
173
av_image_fill_max_pixsteps
(crop->
max_step
,
NULL
, pix_desc);
174
crop->
hsub
=
av_pix_fmt_descriptors
[link->
format
].
log2_chroma_w
;
175
crop->
vsub
=
av_pix_fmt_descriptors
[link->
format
].
log2_chroma_h
;
176
177
if
((ret =
av_expr_parse_and_eval
(&res, (expr = crop->
ow_expr
),
178
var_names
, crop->
var_values
,
179
NULL
,
NULL
,
NULL
,
NULL
,
NULL
, 0, ctx)) < 0)
goto
fail_expr;
180
crop->
var_values
[
VAR_OUT_W
] = crop->
var_values
[
VAR_OW
] = res;
181
if
((ret =
av_expr_parse_and_eval
(&res, (expr = crop->
oh_expr
),
182
var_names
, crop->
var_values
,
183
NULL
,
NULL
,
NULL
,
NULL
,
NULL
, 0, ctx)) < 0)
goto
fail_expr;
184
crop->
var_values
[
VAR_OUT_H
] = crop->
var_values
[
VAR_OH
] = res;
185
/* evaluate again ow as it may depend on oh */
186
if
((ret =
av_expr_parse_and_eval
(&res, (expr = crop->
ow_expr
),
187
var_names
, crop->
var_values
,
188
NULL
,
NULL
,
NULL
,
NULL
,
NULL
, 0, ctx)) < 0)
goto
fail_expr;
189
crop->
var_values
[
VAR_OUT_W
] = crop->
var_values
[
VAR_OW
] = res;
190
if
(
normalize_double
(&crop->
w
, crop->
var_values
[
VAR_OUT_W
]) < 0 ||
191
normalize_double
(&crop->
h
, crop->
var_values
[
VAR_OUT_H
]) < 0) {
192
av_log
(ctx,
AV_LOG_ERROR
,
193
"Too big value or invalid expression for out_w/ow or out_h/oh. "
194
"Maybe the expression for out_w:'%s' or for out_h:'%s' is self-referencing.\n"
,
195
crop->
ow_expr
, crop->
oh_expr
);
196
return
AVERROR
(EINVAL);
197
}
198
crop->
w
&= ~((1 << crop->
hsub
) - 1);
199
crop->
h
&= ~((1 << crop->
vsub
) - 1);
200
201
if
((ret =
av_expr_parse
(&crop->
x_pexpr
, crop->
x_expr
,
var_names
,
202
NULL
,
NULL
,
NULL
,
NULL
, 0, ctx)) < 0 ||
203
(ret =
av_expr_parse
(&crop->
y_pexpr
, crop->
y_expr
,
var_names
,
204
NULL
,
NULL
,
NULL
,
NULL
, 0, ctx)) < 0)
205
return
AVERROR
(EINVAL);
206
207
av_log
(ctx,
AV_LOG_INFO
,
"w:%d h:%d -> w:%d h:%d\n"
,
208
link->
w
, link->
h
, crop->
w
, crop->
h
);
209
210
if
(crop->
w
<= 0 || crop->
h
<= 0 ||
211
crop->
w
> link->
w
|| crop->
h
> link->
h
) {
212
av_log
(ctx,
AV_LOG_ERROR
,
213
"Invalid too big or non positive size for width '%d' or height '%d'\n"
,
214
crop->
w
, crop->
h
);
215
return
AVERROR
(EINVAL);
216
}
217
218
/* set default, required in the case the first computed value for x/y is NAN */
219
crop->
x
= (link->
w
- crop->
w
) / 2;
220
crop->
y
= (link->
h
- crop->
h
) / 2;
221
crop->
x
&= ~((1 << crop->
hsub
) - 1);
222
crop->
y
&= ~((1 << crop->
vsub
) - 1);
223
return
0;
224
225
fail_expr:
226
av_log
(
NULL
,
AV_LOG_ERROR
,
"Error when evaluating the expression '%s'\n"
, expr);
227
return
ret;
228
}
229
230
static
int
config_output
(
AVFilterLink
*link)
231
{
232
CropContext
*crop = link->
src
->
priv
;
233
234
link->
w
= crop->
w
;
235
link->
h
= crop->
h
;
236
237
return
0;
238
}
239
240
static
void
start_frame
(
AVFilterLink
*link,
AVFilterBufferRef
*picref)
241
{
242
AVFilterContext
*ctx = link->
dst
;
243
CropContext
*crop = ctx->
priv
;
244
AVFilterBufferRef
*ref2;
245
int
i;
246
247
ref2 =
avfilter_ref_buffer
(picref, ~0);
248
ref2->
video
->
w
= crop->
w
;
249
ref2->
video
->
h
= crop->
h
;
250
251
crop->
var_values
[
VAR_T
] = picref->
pts
==
AV_NOPTS_VALUE
?
252
NAN
: picref->
pts
*
av_q2d
(link->
time_base
);
253
crop->
var_values
[
VAR_POS
] = picref->
pos
== -1 ?
NAN
: picref->
pos
;
254
crop->
var_values
[
VAR_X
] =
av_expr_eval
(crop->
x_pexpr
, crop->
var_values
,
NULL
);
255
crop->
var_values
[
VAR_Y
] =
av_expr_eval
(crop->
y_pexpr
, crop->
var_values
,
NULL
);
256
crop->
var_values
[
VAR_X
] =
av_expr_eval
(crop->
x_pexpr
, crop->
var_values
,
NULL
);
257
258
normalize_double
(&crop->
x
, crop->
var_values
[
VAR_X
]);
259
normalize_double
(&crop->
y
, crop->
var_values
[
VAR_Y
]);
260
261
if
(crop->
x
< 0) crop->
x
= 0;
262
if
(crop->
y
< 0) crop->
y
= 0;
263
if
((
unsigned
)crop->
x
+ (
unsigned
)crop->
w
> link->
w
) crop->
x
= link->
w
- crop->
w
;
264
if
((
unsigned
)crop->
y
+ (
unsigned
)crop->
h
> link->
h
) crop->
y
= link->
h
- crop->
h
;
265
crop->
x
&= ~((1 << crop->
hsub
) - 1);
266
crop->
y
&= ~((1 << crop->
vsub
) - 1);
267
268
av_dlog
(ctx,
"n:%d t:%f x:%d y:%d x+w:%d y+h:%d\n"
,
269
(
int
)crop->
var_values
[
VAR_N
], crop->
var_values
[
VAR_T
], crop->
x
,
270
crop->
y
, crop->
x
+crop->
w
, crop->
y
+crop->
h
);
271
272
ref2->
data
[0] += crop->
y
* ref2->
linesize
[0];
273
ref2->
data
[0] += crop->
x
* crop->
max_step
[0];
274
275
if
(!(
av_pix_fmt_descriptors
[link->
format
].
flags
&
PIX_FMT_PAL
)) {
276
for
(i = 1; i < 3; i ++) {
277
if
(ref2->
data
[i]) {
278
ref2->
data
[i] += (crop->
y
>> crop->
vsub
) * ref2->
linesize
[i];
279
ref2->
data
[i] += (crop->
x
* crop->
max_step
[i]) >> crop->
hsub
;
280
}
281
}
282
}
283
284
/* alpha plane */
285
if
(ref2->
data
[3]) {
286
ref2->
data
[3] += crop->
y
* ref2->
linesize
[3];
287
ref2->
data
[3] += crop->
x
* crop->
max_step
[3];
288
}
289
290
avfilter_start_frame
(link->
dst
->
outputs
[0], ref2);
291
}
292
293
static
void
draw_slice
(
AVFilterLink
*link,
int
y,
int
h,
int
slice_dir)
294
{
295
AVFilterContext
*ctx = link->
dst
;
296
CropContext
*crop = ctx->
priv
;
297
298
if
(y >= crop->
y
+ crop->
h
|| y + h <= crop->y)
299
return
;
300
301
if
(y < crop->y) {
302
h -= crop->
y
- y;
303
y = crop->
y
;
304
}
305
if
(y + h > crop->
y
+ crop->
h
)
306
h = crop->
y
+ crop->
h
- y;
307
308
avfilter_draw_slice
(ctx->
outputs
[0], y - crop->
y
, h, slice_dir);
309
}
310
311
static
void
end_frame
(
AVFilterLink
*link)
312
{
313
CropContext
*crop = link->
dst
->
priv
;
314
315
crop->
var_values
[
VAR_N
] += 1.0;
316
avfilter_unref_buffer
(link->
cur_buf
);
317
avfilter_end_frame
(link->
dst
->
outputs
[0]);
318
}
319
320
AVFilter
avfilter_vf_crop
= {
321
.
name
=
"crop"
,
322
.description =
NULL_IF_CONFIG_SMALL
(
"Crop the input video to width:height:x:y."
),
323
324
.priv_size =
sizeof
(
CropContext
),
325
326
.
query_formats
=
query_formats
,
327
.
init
=
init
,
328
.
uninit
=
uninit
,
329
330
.inputs = (
AVFilterPad
[]) {{ .name =
"default"
,
331
.type =
AVMEDIA_TYPE_VIDEO
,
332
.start_frame =
start_frame
,
333
.draw_slice =
draw_slice
,
334
.end_frame =
end_frame
,
335
.get_video_buffer =
avfilter_null_get_video_buffer
,
336
.config_props =
config_input
, },
337
{ .name =
NULL
}},
338
.outputs = (
AVFilterPad
[]) {{ .name =
"default"
,
339
.type =
AVMEDIA_TYPE_VIDEO
,
340
.config_props =
config_output
, },
341
{ .name =
NULL
}},
342
};