FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/vf_addroi.c
Date: 2025-01-20 09:27:23
Exec Total Coverage
Lines: 0 91 0.0%
Functions: 0 4 0.0%
Branches: 0 33 0.0%

Line Branch Exec Source
1 /*
2 * This file is part of FFmpeg.
3 *
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 #include "libavutil/avassert.h"
20 #include "libavutil/eval.h"
21 #include "libavutil/opt.h"
22 #include "avfilter.h"
23 #include "filters.h"
24 #include "video.h"
25
26 enum {
27 X, Y, W, H,
28 NB_PARAMS,
29 };
30 static const char addroi_param_names[] = {
31 'x', 'y', 'w', 'h',
32 };
33
34 enum {
35 VAR_IW,
36 VAR_IH,
37 NB_VARS,
38 };
39 static const char *const addroi_var_names[] = {
40 "iw",
41 "ih",
42 NULL,
43 };
44
45 typedef struct AddROIContext {
46 const AVClass *class;
47
48 char *region_str[NB_PARAMS];
49 AVExpr *region_expr[NB_PARAMS];
50
51 int region[NB_PARAMS];
52 AVRational qoffset;
53
54 int clear;
55 } AddROIContext;
56
57 static int addroi_config_input(AVFilterLink *inlink)
58 {
59 AVFilterContext *avctx = inlink->dst;
60 AddROIContext *ctx = avctx->priv;
61 int i;
62 double vars[NB_VARS];
63 double val;
64
65 vars[VAR_IW] = inlink->w;
66 vars[VAR_IH] = inlink->h;
67
68 for (i = 0; i < NB_PARAMS; i++) {
69 int max_value;
70 switch (i) {
71 case X: max_value = inlink->w; break;
72 case Y: max_value = inlink->h; break;
73 case W: max_value = inlink->w - ctx->region[X]; break;
74 case H: max_value = inlink->h - ctx->region[Y]; break;
75 }
76
77 val = av_expr_eval(ctx->region_expr[i], vars, NULL);
78 if (val < 0.0) {
79 av_log(avctx, AV_LOG_WARNING, "Calculated value %g for %c is "
80 "less than zero - using zero instead.\n", val,
81 addroi_param_names[i]);
82 val = 0.0;
83 } else if (val > max_value) {
84 av_log(avctx, AV_LOG_WARNING, "Calculated value %g for %c is "
85 "greater than maximum allowed value %d - "
86 "using %d instead.\n", val, addroi_param_names[i],
87 max_value, max_value);
88 val = max_value;
89 }
90 ctx->region[i] = val;
91 }
92
93 return 0;
94 }
95
96 static int addroi_filter_frame(AVFilterLink *inlink, AVFrame *frame)
97 {
98 AVFilterContext *avctx = inlink->dst;
99 AVFilterLink *outlink = avctx->outputs[0];
100 AddROIContext *ctx = avctx->priv;
101 AVRegionOfInterest *roi;
102 AVFrameSideData *sd;
103 int err;
104
105 if (ctx->clear) {
106 av_frame_remove_side_data(frame, AV_FRAME_DATA_REGIONS_OF_INTEREST);
107 sd = NULL;
108 } else {
109 sd = av_frame_get_side_data(frame, AV_FRAME_DATA_REGIONS_OF_INTEREST);
110 }
111 if (sd) {
112 const AVRegionOfInterest *old_roi;
113 uint32_t old_roi_size;
114 AVBufferRef *roi_ref;
115 int nb_roi, i;
116
117 old_roi = (const AVRegionOfInterest*)sd->data;
118 old_roi_size = old_roi->self_size;
119 av_assert0(old_roi_size && sd->size % old_roi_size == 0);
120 nb_roi = sd->size / old_roi_size + 1;
121
122 roi_ref = av_buffer_alloc(sizeof(*roi) * nb_roi);
123 if (!roi_ref) {
124 err = AVERROR(ENOMEM);
125 goto fail;
126 }
127 roi = (AVRegionOfInterest*)roi_ref->data;
128
129 for (i = 0; i < nb_roi - 1; i++) {
130 old_roi = (const AVRegionOfInterest*)
131 (sd->data + old_roi_size * i);
132
133 roi[i] = (AVRegionOfInterest) {
134 .self_size = sizeof(*roi),
135 .top = old_roi->top,
136 .bottom = old_roi->bottom,
137 .left = old_roi->left,
138 .right = old_roi->right,
139 .qoffset = old_roi->qoffset,
140 };
141 }
142
143 roi[nb_roi - 1] = (AVRegionOfInterest) {
144 .self_size = sizeof(*roi),
145 .top = ctx->region[Y],
146 .bottom = ctx->region[Y] + ctx->region[H],
147 .left = ctx->region[X],
148 .right = ctx->region[X] + ctx->region[W],
149 .qoffset = ctx->qoffset,
150 };
151
152 av_frame_remove_side_data(frame, AV_FRAME_DATA_REGIONS_OF_INTEREST);
153
154 sd = av_frame_new_side_data_from_buf(frame,
155 AV_FRAME_DATA_REGIONS_OF_INTEREST,
156 roi_ref);
157 if (!sd) {
158 av_buffer_unref(&roi_ref);
159 err = AVERROR(ENOMEM);
160 goto fail;
161 }
162
163 } else {
164 sd = av_frame_new_side_data(frame, AV_FRAME_DATA_REGIONS_OF_INTEREST,
165 sizeof(AVRegionOfInterest));
166 if (!sd) {
167 err = AVERROR(ENOMEM);
168 goto fail;
169 }
170 roi = (AVRegionOfInterest*)sd->data;
171 *roi = (AVRegionOfInterest) {
172 .self_size = sizeof(*roi),
173 .top = ctx->region[Y],
174 .bottom = ctx->region[Y] + ctx->region[H],
175 .left = ctx->region[X],
176 .right = ctx->region[X] + ctx->region[W],
177 .qoffset = ctx->qoffset,
178 };
179 }
180
181 return ff_filter_frame(outlink, frame);
182
183 fail:
184 av_frame_free(&frame);
185 return err;
186 }
187
188 static av_cold int addroi_init(AVFilterContext *avctx)
189 {
190 AddROIContext *ctx = avctx->priv;
191 int i, err;
192
193 for (i = 0; i < NB_PARAMS; i++) {
194 err = av_expr_parse(&ctx->region_expr[i], ctx->region_str[i],
195 addroi_var_names, NULL, NULL, NULL, NULL,
196 0, avctx);
197 if (err < 0) {
198 av_log(ctx, AV_LOG_ERROR,
199 "Error parsing %c expression '%s'.\n",
200 addroi_param_names[i], ctx->region_str[i]);
201 return err;
202 }
203 }
204
205 return 0;
206 }
207
208 static av_cold void addroi_uninit(AVFilterContext *avctx)
209 {
210 AddROIContext *ctx = avctx->priv;
211 int i;
212
213 for (i = 0; i < NB_PARAMS; i++) {
214 av_expr_free(ctx->region_expr[i]);
215 ctx->region_expr[i] = NULL;
216 }
217 }
218
219 #define OFFSET(x) offsetof(AddROIContext, x)
220 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
221 static const AVOption addroi_options[] = {
222 { "x", "Region distance from left edge of frame.",
223 OFFSET(region_str[X]), AV_OPT_TYPE_STRING, { .str = "0" }, .flags = FLAGS },
224 { "y", "Region distance from top edge of frame.",
225 OFFSET(region_str[Y]), AV_OPT_TYPE_STRING, { .str = "0" }, .flags = FLAGS },
226 { "w", "Region width.",
227 OFFSET(region_str[W]), AV_OPT_TYPE_STRING, { .str = "0" }, .flags = FLAGS },
228 { "h", "Region height.",
229 OFFSET(region_str[H]), AV_OPT_TYPE_STRING, { .str = "0" }, .flags = FLAGS },
230
231 { "qoffset", "Quantisation offset to apply in the region.",
232 OFFSET(qoffset), AV_OPT_TYPE_RATIONAL, { .dbl = -0.1 }, -1, +1, FLAGS },
233
234 { "clear", "Remove any existing regions of interest before adding the new one.",
235 OFFSET(clear), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
236
237 { NULL }
238 };
239
240 AVFILTER_DEFINE_CLASS(addroi);
241
242 static const AVFilterPad addroi_inputs[] = {
243 {
244 .name = "default",
245 .type = AVMEDIA_TYPE_VIDEO,
246 .config_props = addroi_config_input,
247 .filter_frame = addroi_filter_frame,
248 },
249 };
250
251 const FFFilter ff_vf_addroi = {
252 .p.name = "addroi",
253 .p.description = NULL_IF_CONFIG_SMALL("Add region of interest to frame."),
254 .p.priv_class = &addroi_class,
255 .p.flags = AVFILTER_FLAG_METADATA_ONLY,
256
257 .init = addroi_init,
258 .uninit = addroi_uninit,
259
260 .priv_size = sizeof(AddROIContext),
261
262 FILTER_INPUTS(addroi_inputs),
263 FILTER_OUTPUTS(ff_video_default_filterpad),
264 };
265