FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/vf_feedback.c
Date: 2024-11-20 23:03:26
Exec Total Coverage
Lines: 0 144 0.0%
Functions: 0 8 0.0%
Branches: 0 90 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 /**
20 * @file
21 * feedback video filter
22 */
23
24 #include "libavutil/fifo.h"
25 #include "libavutil/imgutils.h"
26 #include "libavutil/opt.h"
27 #include "libavutil/internal.h"
28 #include "avfilter.h"
29 #include "filters.h"
30 #include "formats.h"
31 #include "video.h"
32
33 typedef struct FeedbackContext {
34 const AVClass *class;
35
36 int x, y;
37 int w, h;
38
39 int max_step[4];
40 int hsub, vsub;
41
42 AVFrame *feed;
43
44 AVFifo *fifo;
45 } FeedbackContext;
46
47 static void adjust_pos(AVFilterContext *ctx, FeedbackContext *s)
48 {
49 if (s->x + s->w > ctx->inputs[0]->w)
50 s->x = ctx->inputs[0]->w - s->w;
51 if (s->y + s->h > ctx->inputs[0]->h)
52 s->y = ctx->inputs[0]->h - s->h;
53 }
54
55 static void adjust_parameters(AVFilterContext *ctx, FeedbackContext *s)
56 {
57 if (s->x >= ctx->inputs[0]->w)
58 s->x = 0;
59 if (s->y >= ctx->inputs[0]->h)
60 s->y = 0;
61
62 if (s->w <= 0)
63 s->w = ctx->inputs[0]->w - s->x;
64 if (s->h <= 0)
65 s->h = ctx->inputs[0]->h - s->y;
66
67 if (s->w > ctx->inputs[0]->w)
68 s->w = ctx->inputs[0]->w;
69 if (s->h > ctx->inputs[0]->h)
70 s->h = ctx->inputs[0]->h;
71
72 adjust_pos(ctx, s);
73 }
74
75 static int config_input(AVFilterLink *inlink)
76 {
77 AVFilterContext *ctx = inlink->dst;
78 const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink->format);
79 FeedbackContext *s = ctx->priv;
80
81 s->hsub = pix_desc->log2_chroma_w;
82 s->vsub = pix_desc->log2_chroma_h;
83
84 av_image_fill_max_pixsteps(s->max_step, NULL, pix_desc);
85
86 adjust_parameters(ctx, s);
87
88 ctx->inputs[1]->w = s->w;
89 ctx->inputs[1]->h = s->h;
90
91 return 0;
92 }
93
94 static int config_output(AVFilterLink *outlink)
95 {
96 AVFilterContext *ctx = outlink->src;
97 FeedbackContext *s = ctx->priv;
98
99 adjust_parameters(ctx, s);
100
101 ctx->outputs[0]->w = ctx->inputs[0]->w;
102 ctx->outputs[0]->h = ctx->inputs[0]->h;
103 ctx->outputs[1]->w = s->w;
104 ctx->outputs[1]->h = s->h;
105
106 return 0;
107 }
108
109 static int query_formats(const AVFilterContext *ctx,
110 AVFilterFormatsConfig **cfg_in,
111 AVFilterFormatsConfig **cfg_out)
112 {
113 return ff_set_common_formats2(ctx, cfg_in, cfg_out,
114 ff_formats_pixdesc_filter(0, AV_PIX_FMT_FLAG_BITSTREAM |
115 AV_PIX_FMT_FLAG_HWACCEL |
116 AV_PIX_FMT_FLAG_PAL));
117 }
118
119 static int activate(AVFilterContext *ctx)
120 {
121 FeedbackContext *s = ctx->priv;
122 int status, ret;
123 int64_t pts;
124
125 adjust_pos(ctx, s);
126
127 for (int i = 0; i < ctx->nb_outputs; i++)
128 FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[i], ctx);
129
130 if (!s->feed) {
131 ret = ff_inlink_consume_frame(ctx->inputs[1], &s->feed);
132 if (ret < 0)
133 return ret;
134 }
135
136 if (s->feed && av_fifo_can_read(s->fifo)) {
137 AVFrame *src = s->feed;
138 AVFrame *dst = NULL;
139
140 av_fifo_read(s->fifo, &dst, 1);
141 if (!dst)
142 return AVERROR_BUG;
143
144 if (!av_frame_is_writable(dst)) {
145 AVFrame *tmp = ff_get_video_buffer(ctx->outputs[0], ctx->outputs[0]->w, ctx->outputs[0]->h);
146
147 if (!tmp) {
148 av_frame_free(&dst);
149 return AVERROR(ENOMEM);
150 }
151
152 ret = av_frame_copy(tmp, dst);
153 if (ret < 0) {
154 av_frame_free(&dst);
155 av_frame_free(&tmp);
156 return ret;
157 }
158
159 av_frame_copy_props(tmp, dst);
160 av_frame_free(&dst);
161 dst = tmp;
162 }
163
164 for (int y = 0; y < src->height; y++) {
165 memmove(dst->data[0] + (s->y + y) * dst->linesize[0] + s->x * s->max_step[0],
166 src->data[0] + y * src->linesize[0], src->width * s->max_step[0]);
167 }
168
169 for (int i = 1; i < 3; i++) {
170 if (dst->data[i]) {
171 for (int y = 0; y < src->height; y++) {
172 memmove(dst->data[i] + ((s->y + y) >> s->vsub) * dst->linesize[i] + (s->x >> s->hsub) * s->max_step[i],
173 src->data[i] + (y >> s->vsub) * src->linesize[i], (src->width >> s->hsub) * s->max_step[i]);
174 }
175 }
176 }
177
178 if (dst->data[3]) {
179 for (int y = 0; y < src->height; y++) {
180 memmove(dst->data[3] + (s->y + y) * dst->linesize[3] + s->x * s->max_step[3],
181 src->data[3] + y * src->linesize[3], src->width * s->max_step[3]);
182 }
183 }
184
185 ret = ff_filter_frame(ctx->outputs[0], dst);
186 av_frame_free(&s->feed);
187 return ret;
188 }
189
190 if (!s->feed || ctx->is_disabled) {
191 AVFrame *in = NULL;
192
193 ret = ff_inlink_consume_frame(ctx->inputs[0], &in);
194 if (ret < 0)
195 return ret;
196
197 if (ret > 0 && ctx->is_disabled)
198 return ff_filter_frame(ctx->outputs[0], in);
199
200 if (ret > 0) {
201 AVFrame *frame;
202
203 ret = av_fifo_write(s->fifo, &in, 1);
204 if (ret < 0) {
205 av_frame_free(&in);
206 return ret;
207 }
208
209 frame = av_frame_clone(in);
210 if (!frame)
211 return AVERROR(ENOMEM);
212
213 frame->width = s->w;
214 frame->height = s->h;
215
216 frame->data[0] += s->y * frame->linesize[0];
217 frame->data[0] += s->x * s->max_step[0];
218
219 for (int i = 1; i < 3; i ++) {
220 if (frame->data[i]) {
221 frame->data[i] += (s->y >> s->vsub) * frame->linesize[i];
222 frame->data[i] += (s->x >> s->hsub) * s->max_step[i];
223 }
224 }
225
226 if (frame->data[3]) {
227 frame->data[3] += s->y * frame->linesize[3];
228 frame->data[3] += s->x * s->max_step[3];
229 }
230
231 return ff_filter_frame(ctx->outputs[1], frame);
232 }
233 }
234
235 if (ff_inlink_acknowledge_status(ctx->inputs[0], &status, &pts)) {
236 ff_outlink_set_status(ctx->outputs[0], status, pts);
237 ff_outlink_set_status(ctx->outputs[1], status, pts);
238 return 0;
239 }
240
241 if (ff_inlink_acknowledge_status(ctx->inputs[1], &status, &pts)) {
242 ff_outlink_set_status(ctx->outputs[0], status, pts);
243 ff_outlink_set_status(ctx->outputs[1], status, pts);
244 return 0;
245 }
246
247 if (!s->feed || ctx->is_disabled) {
248 if (ff_outlink_frame_wanted(ctx->outputs[0])) {
249 ff_inlink_request_frame(ctx->inputs[0]);
250 if (!ctx->is_disabled)
251 ff_inlink_request_frame(ctx->inputs[1]);
252 return 0;
253 }
254 }
255
256 return FFERROR_NOT_READY;
257 }
258
259 static av_cold int init(AVFilterContext *ctx)
260 {
261 FeedbackContext *s = ctx->priv;
262
263 s->fifo = av_fifo_alloc2(8, sizeof(AVFrame *), AV_FIFO_FLAG_AUTO_GROW);
264 if (!s->fifo)
265 return AVERROR(ENOMEM);
266
267 return 0;
268 }
269
270 static av_cold void uninit(AVFilterContext *ctx)
271 {
272 FeedbackContext *s = ctx->priv;
273 if (s->fifo) {
274 size_t size = av_fifo_can_read(s->fifo);
275
276 for (size_t n = 0; n < size; n++) {
277 AVFrame *frame = NULL;
278
279 av_fifo_read(s->fifo, &frame, 1);
280
281 av_frame_free(&frame);
282 }
283
284 av_fifo_freep2(&s->fifo);
285 }
286 }
287
288 static const AVFilterPad inputs[] = {
289 {
290 .name = "default",
291 .type = AVMEDIA_TYPE_VIDEO,
292 .config_props = config_input,
293 },
294 {
295 .name = "feedin",
296 .type = AVMEDIA_TYPE_VIDEO,
297 .config_props = config_input,
298 },
299 };
300
301 static const AVFilterPad outputs[] = {
302 {
303 .name = "default",
304 .type = AVMEDIA_TYPE_VIDEO,
305 .config_props = config_output,
306 },
307 {
308 .name = "feedout",
309 .type = AVMEDIA_TYPE_VIDEO,
310 .config_props = config_output,
311 },
312 };
313
314 #define OFFSET(x) offsetof(FeedbackContext, x)
315 #define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
316 #define TFLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_RUNTIME_PARAM)
317
318 static const AVOption feedback_options[] = {
319 { "x", "set top left crop position", OFFSET(x), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, TFLAGS },
320 { "y", "set top left crop position", OFFSET(y), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, TFLAGS },
321 { "w", "set crop size", OFFSET(w), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS },
322 { "h", "set crop size", OFFSET(h), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS },
323 { NULL }
324 };
325
326 AVFILTER_DEFINE_CLASS(feedback);
327
328 const AVFilter ff_vf_feedback = {
329 .name = "feedback",
330 .description = NULL_IF_CONFIG_SMALL("Apply feedback video filter."),
331 .priv_class = &feedback_class,
332 .priv_size = sizeof(FeedbackContext),
333 .activate = activate,
334 .init = init,
335 .uninit = uninit,
336 FILTER_INPUTS(inputs),
337 FILTER_OUTPUTS(outputs),
338 FILTER_QUERY_FUNC2(query_formats),
339 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
340 .process_command = ff_filter_process_command,
341 };
342