2 * Copyright (c) 2014-2015 Michael Niedermayer <michaelni@gmx.at>
4 * This file is part of FFmpeg.
6 * FFmpeg is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * FFmpeg 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
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 * @todo switch to dualinput
25 #include "libavutil/mem.h"
26 #include "libavutil/opt.h"
30 #include "lavfutils.h"
38 typedef struct CoverContext
{
46 #define OFFSET(x) offsetof(CoverContext, x)
47 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
48 static const AVOption cover_rect_options
[] = {
49 { "cover", "cover bitmap filename", OFFSET(cover_filename
), AV_OPT_TYPE_STRING
, {.str
= NULL
}, .flags
= FLAGS
},
50 { "mode", "set removal mode", OFFSET(mode
), AV_OPT_TYPE_INT
, {.i64
= MODE_BLUR
}, 0, NB_MODES
- 1, FLAGS
, .unit
= "mode" },
51 { "cover", "cover area with bitmap", 0, AV_OPT_TYPE_CONST
, {.i64
= MODE_COVER
}, INT_MIN
, INT_MAX
, FLAGS
, .unit
= "mode" },
52 { "blur", "blur area", 0, AV_OPT_TYPE_CONST
, {.i64
= MODE_BLUR
}, INT_MIN
, INT_MAX
, FLAGS
, .unit
= "mode" },
56 AVFILTER_DEFINE_CLASS(cover_rect
);
58 static int config_input(AVFilterLink
*inlink
)
63 static void cover_rect(CoverContext
*cover
, AVFrame
*in
, int offx
, int offy
)
67 for (p
= 0; p
< 3; p
++) {
68 uint8_t *data
= in
->data
[p
] + (offx
>>!!p
) + (offy
>>!!p
) * in
->linesize
[p
];
69 const uint8_t *src
= cover
->cover_frame
->data
[p
];
70 int w
= AV_CEIL_RSHIFT(cover
->cover_frame
->width
, !!p
);
71 int h
= AV_CEIL_RSHIFT(cover
->cover_frame
->height
, !!p
);
72 for (y
= 0; y
< h
; y
++) {
73 for (x
= 0; x
< w
; x
++) {
76 data
+= in
->linesize
[p
];
77 src
+= cover
->cover_frame
->linesize
[p
];
81 static void blur(CoverContext
*cover
, AVFrame
*in
, int offx
, int offy
)
88 int stride
= in
->linesize
[p
];
89 uint8_t *data
= in
->data
[p
] + ox
+ oy
* stride
;
90 int w
= AV_CEIL_RSHIFT(cover
->width
, !!p
);
91 int h
= AV_CEIL_RSHIFT(cover
->height
, !!p
);
92 int iw
= AV_CEIL_RSHIFT(in
->width
, !!p
);
93 int ih
= AV_CEIL_RSHIFT(in
->height
, !!p
);
94 for (y
= 0; y
< h
; y
++) {
95 for (x
= 0; x
< w
; x
++) {
99 int scale
= 65536 / (x
+ 1);
100 s
+= data
[-1 + y
*stride
] * scale
;
104 int scale
= 65536 / (y
+ 1);
105 s
+= data
[x
- stride
] * scale
;
109 int scale
= 65536 / (w
- x
);
110 s
+= data
[w
+ y
*stride
] * scale
;
114 int scale
= 65536 / (h
- y
);
115 s
+= data
[x
+ h
*stride
] * scale
;
118 data
[x
+ y
*stride
] = c
? (s
+ (c
>>1)) / c
: 0;
124 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
126 AVFilterContext
*ctx
= inlink
->dst
;
127 CoverContext
*cover
= ctx
->priv
;
128 AVDictionaryEntry
*ex
, *ey
, *ew
, *eh
;
129 int ret
, x
= -1, y
= -1, w
= -1, h
= -1;
130 char *xendptr
= NULL
, *yendptr
= NULL
, *wendptr
= NULL
, *hendptr
= NULL
;
132 ex
= av_dict_get(in
->metadata
, "lavfi.rect.x", NULL
, AV_DICT_MATCH_CASE
);
133 ey
= av_dict_get(in
->metadata
, "lavfi.rect.y", NULL
, AV_DICT_MATCH_CASE
);
134 ew
= av_dict_get(in
->metadata
, "lavfi.rect.w", NULL
, AV_DICT_MATCH_CASE
);
135 eh
= av_dict_get(in
->metadata
, "lavfi.rect.h", NULL
, AV_DICT_MATCH_CASE
);
136 if (ex
&& ey
&& ew
&& eh
) {
137 x
= strtol(ex
->value
, &xendptr
, 10);
138 y
= strtol(ey
->value
, ¥dptr
, 10);
139 w
= strtol(ew
->value
, &wendptr
, 10);
140 h
= strtol(eh
->value
, &hendptr
, 10);
143 if (!xendptr
|| *xendptr
|| !yendptr
|| *yendptr
||
144 !wendptr
|| *wendptr
|| !hendptr
|| *hendptr
146 return ff_filter_frame(ctx
->outputs
[0], in
);
157 w
= FFMIN(w
, in
->width
- x
);
158 h
= FFMIN(h
, in
->height
- y
);
160 if (w
> in
->width
|| h
> in
->height
|| w
<= 0 || h
<= 0)
161 return AVERROR(EINVAL
);
163 if (cover
->cover_frame
) {
164 if (w
!= cover
->cover_frame
->width
|| h
!= cover
->cover_frame
->height
)
165 return AVERROR(EINVAL
);
171 x
= av_clip(x
, 0, in
->width
- w
);
172 y
= av_clip(y
, 0, in
->height
- h
);
174 ret
= ff_inlink_make_frame_writable(inlink
, &in
);
180 if (cover
->mode
== MODE_BLUR
) {
181 blur (cover
, in
, x
, y
);
183 cover_rect(cover
, in
, x
, y
);
185 return ff_filter_frame(ctx
->outputs
[0], in
);
188 static av_cold
void uninit(AVFilterContext
*ctx
)
190 CoverContext
*cover
= ctx
->priv
;
192 if (cover
->cover_frame
)
193 av_freep(&cover
->cover_frame
->data
[0]);
194 av_frame_free(&cover
->cover_frame
);
197 static av_cold
int init(AVFilterContext
*ctx
)
199 CoverContext
*cover
= ctx
->priv
;
202 if (cover
->mode
== MODE_COVER
) {
203 if (!cover
->cover_filename
) {
204 av_log(ctx
, AV_LOG_ERROR
, "cover filename not set\n");
205 return AVERROR(EINVAL
);
208 cover
->cover_frame
= av_frame_alloc();
209 if (!cover
->cover_frame
)
210 return AVERROR(ENOMEM
);
212 if ((ret
= ff_load_image(cover
->cover_frame
->data
, cover
->cover_frame
->linesize
,
213 &cover
->cover_frame
->width
, &cover
->cover_frame
->height
,
214 &cover
->cover_frame
->format
, cover
->cover_filename
, ctx
)) < 0)
217 if (cover
->cover_frame
->format
!= AV_PIX_FMT_YUV420P
&& cover
->cover_frame
->format
!= AV_PIX_FMT_YUVJ420P
) {
218 av_log(ctx
, AV_LOG_ERROR
, "cover image is not a YUV420 image\n");
219 return AVERROR(EINVAL
);
226 static const AVFilterPad cover_rect_inputs
[] = {
229 .type
= AVMEDIA_TYPE_VIDEO
,
230 .config_props
= config_input
,
231 .filter_frame
= filter_frame
,
235 const FFFilter ff_vf_cover_rect
= {
236 .p
.name
= "cover_rect",
237 .p
.description
= NULL_IF_CONFIG_SMALL("Find and cover a user specified object."),
238 .p
.priv_class
= &cover_rect_class
,
239 .priv_size
= sizeof(CoverContext
),
242 FILTER_INPUTS(cover_rect_inputs
),
243 FILTER_OUTPUTS(ff_video_default_filterpad
),
244 FILTER_PIXFMTS(AV_PIX_FMT_YUV420P
, AV_PIX_FMT_YUVJ420P
),