FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/vf_fieldhint.c
Date: 2022-11-26 13:19:19
Exec Total Coverage
Lines: 0 154 0.0%
Branches: 0 72 0.0%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2016 Paul B Mahol
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg 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 * 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 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 FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include "libavutil/avassert.h"
22 #include "libavutil/file_open.h"
23 #include "libavutil/imgutils.h"
24 #include "libavutil/internal.h"
25 #include "libavutil/opt.h"
26 #include "libavutil/pixdesc.h"
27 #include "avfilter.h"
28 #include "internal.h"
29 #include "video.h"
30
31 enum HintModes {
32 ABSOLUTE_HINT,
33 RELATIVE_HINT,
34 PATTERN_HINT,
35 NB_HINTS,
36 };
37
38 typedef struct FieldHintContext {
39 const AVClass *class;
40
41 char *hint_file_str;
42 FILE *hint;
43 int mode;
44
45 AVFrame *frame[3];
46
47 int64_t line;
48 int nb_planes;
49 int eof;
50 int planewidth[4];
51 int planeheight[4];
52 } FieldHintContext;
53
54 #define OFFSET(x) offsetof(FieldHintContext, x)
55 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
56
57 static const AVOption fieldhint_options[] = {
58 { "hint", "set hint file", OFFSET(hint_file_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
59 { "mode", "set hint mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_HINTS-1, FLAGS, "mode" },
60 { "absolute", 0, 0, AV_OPT_TYPE_CONST, {.i64=ABSOLUTE_HINT}, 0, 0, FLAGS, "mode" },
61 { "relative", 0, 0, AV_OPT_TYPE_CONST, {.i64=RELATIVE_HINT}, 0, 0, FLAGS, "mode" },
62 { "pattern", 0, 0, AV_OPT_TYPE_CONST, {.i64=PATTERN_HINT}, 0, 0, FLAGS, "mode" },
63 { NULL }
64 };
65
66 AVFILTER_DEFINE_CLASS(fieldhint);
67
68 static av_cold int init(AVFilterContext *ctx)
69 {
70 FieldHintContext *s = ctx->priv;
71 int ret;
72
73 if (!s->hint_file_str) {
74 av_log(ctx, AV_LOG_ERROR, "Hint file must be set.\n");
75 return AVERROR(EINVAL);
76 }
77 s->hint = avpriv_fopen_utf8(s->hint_file_str, "r");
78 if (!s->hint) {
79 ret = AVERROR(errno);
80 av_log(ctx, AV_LOG_ERROR, "%s: %s\n", s->hint_file_str, av_err2str(ret));
81 return ret;
82 }
83
84 return 0;
85 }
86
87 static int query_formats(AVFilterContext *ctx)
88 {
89 int reject_flags = AV_PIX_FMT_FLAG_BITSTREAM |
90 AV_PIX_FMT_FLAG_HWACCEL |
91 AV_PIX_FMT_FLAG_PAL;
92
93 return ff_set_common_formats(ctx, ff_formats_pixdesc_filter(0, reject_flags));
94 }
95
96 static int config_input(AVFilterLink *inlink)
97 {
98 FieldHintContext *s = inlink->dst->priv;
99 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
100 int ret;
101
102 if ((ret = av_image_fill_linesizes(s->planewidth, inlink->format, inlink->w)) < 0)
103 return ret;
104
105 s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
106 s->planeheight[0] = s->planeheight[3] = inlink->h;
107
108 s->nb_planes = av_pix_fmt_count_planes(inlink->format);
109
110 return 0;
111 }
112
113 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
114 {
115 AVFilterContext *ctx = inlink->dst;
116 AVFilterLink *outlink = ctx->outputs[0];
117 FieldHintContext *s = ctx->priv;
118 AVFrame *out, *top, *bottom;
119 char buf[1024] = { 0 };
120 int64_t tf, bf;
121 int tfactor = 0, bfactor = 1;
122 char hint = '=', field = '=';
123 int p;
124
125 av_frame_free(&s->frame[0]);
126 s->frame[0] = s->frame[1];
127 s->frame[1] = s->frame[2];
128 s->frame[2] = in;
129 if (!s->frame[1])
130 return 0;
131 else if (!s->frame[0]) {
132 s->frame[0] = av_frame_clone(s->frame[1]);
133 if (!s->frame[0])
134 return AVERROR(ENOMEM);
135 }
136
137 while (1) {
138 if (fgets(buf, sizeof(buf)-1, s->hint)) {
139 s->line++;
140 if (buf[0] == '#' || buf[0] == ';') {
141 continue;
142 } else if (sscanf(buf, "%"PRId64",%"PRId64" %c %c", &tf, &bf, &hint, &field) == 4) {
143 ;
144 } else if (sscanf(buf, "%"PRId64",%"PRId64" %c", &tf, &bf, &hint) == 3) {
145 ;
146 } else if (sscanf(buf, "%"PRId64",%"PRId64"", &tf, &bf) == 2) {
147 ;
148 } else {
149 av_log(ctx, AV_LOG_ERROR, "Invalid entry at line %"PRId64".\n", s->line);
150 return AVERROR_INVALIDDATA;
151 }
152 switch (s->mode) {
153 case ABSOLUTE_HINT:
154 if (tf > outlink->frame_count_in + 1 || tf < FFMAX(0, outlink->frame_count_in - 1) ||
155 bf > outlink->frame_count_in + 1 || bf < FFMAX(0, outlink->frame_count_in - 1)) {
156 av_log(ctx, AV_LOG_ERROR, "Out of range frames %"PRId64" and/or %"PRId64" on line %"PRId64" for %"PRId64". input frame.\n", tf, bf, s->line, inlink->frame_count_out);
157 return AVERROR_INVALIDDATA;
158 }
159 break;
160 case PATTERN_HINT:
161 case RELATIVE_HINT:
162 if (tf > 1 || tf < -1 ||
163 bf > 1 || bf < -1) {
164 av_log(ctx, AV_LOG_ERROR, "Out of range %"PRId64" and/or %"PRId64" on line %"PRId64" for %"PRId64". input frame.\n", tf, bf, s->line, inlink->frame_count_out);
165 return AVERROR_INVALIDDATA;
166 }
167 break;
168 default:
169 return AVERROR_BUG;
170 };
171 break;
172 } else {
173 if (s->mode == PATTERN_HINT) {
174 fseek(s->hint, 0, SEEK_SET);
175 continue;
176 }
177 av_log(ctx, AV_LOG_ERROR, "Missing entry for %"PRId64". input frame.\n", inlink->frame_count_out);
178 return AVERROR_INVALIDDATA;
179 }
180 }
181
182 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
183 if (!out)
184 return AVERROR(ENOMEM);
185 av_frame_copy_props(out, s->frame[1]);
186
187 switch (s->mode) {
188 case ABSOLUTE_HINT:
189 top = s->frame[tf - outlink->frame_count_in + 1];
190 bottom = s->frame[bf - outlink->frame_count_in + 1];
191 break;
192 case PATTERN_HINT:
193 case RELATIVE_HINT:
194 top = s->frame[1 + tf];
195 bottom = s->frame[1 + bf];
196 break;
197 default:
198 av_assert0(0);
199 }
200
201 switch (field) {
202 case 'b':
203 tfactor = 1;
204 top = bottom;
205 break;
206 case 't':
207 bfactor = 0;
208 bottom = top;
209 break;
210 case '=':
211 break;
212 default:
213 av_log(ctx, AV_LOG_ERROR, "Invalid field: %c.\n", field);
214 av_frame_free(&out);
215 return AVERROR(EINVAL);
216 }
217
218 switch (hint) {
219 case '+':
220 out->interlaced_frame = 1;
221 break;
222 case '-':
223 out->interlaced_frame = 0;
224 break;
225 case '=':
226 break;
227 case 'b':
228 tfactor = 1;
229 top = bottom;
230 break;
231 case 't':
232 bfactor = 0;
233 bottom = top;
234 break;
235 default:
236 av_log(ctx, AV_LOG_ERROR, "Invalid hint: %c.\n", hint);
237 av_frame_free(&out);
238 return AVERROR(EINVAL);
239 }
240
241 for (p = 0; p < s->nb_planes; p++) {
242 av_image_copy_plane(out->data[p],
243 out->linesize[p] * 2,
244 top->data[p] + tfactor * top->linesize[p],
245 top->linesize[p] * 2,
246 s->planewidth[p],
247 (s->planeheight[p] + 1) / 2);
248 av_image_copy_plane(out->data[p] + out->linesize[p],
249 out->linesize[p] * 2,
250 bottom->data[p] + bfactor * bottom->linesize[p],
251 bottom->linesize[p] * 2,
252 s->planewidth[p],
253 (s->planeheight[p] + 1) / 2);
254 }
255
256 return ff_filter_frame(outlink, out);
257 }
258
259 static int request_frame(AVFilterLink *outlink)
260 {
261 AVFilterContext *ctx = outlink->src;
262 FieldHintContext *s = ctx->priv;
263 int ret;
264
265 if (s->eof)
266 return AVERROR_EOF;
267
268 ret = ff_request_frame(ctx->inputs[0]);
269 if (ret == AVERROR_EOF && s->frame[2]) {
270 AVFrame *next = av_frame_clone(s->frame[2]);
271 if (!next)
272 return AVERROR(ENOMEM);
273 ret = filter_frame(ctx->inputs[0], next);
274 s->eof = 1;
275 }
276
277 return ret;
278 }
279
280 static av_cold void uninit(AVFilterContext *ctx)
281 {
282 FieldHintContext *s = ctx->priv;
283
284 if (s->hint)
285 fclose(s->hint);
286 s->hint = NULL;
287
288 av_frame_free(&s->frame[0]);
289 av_frame_free(&s->frame[1]);
290 av_frame_free(&s->frame[2]);
291 }
292
293 static const AVFilterPad inputs[] = {
294 {
295 .name = "default",
296 .type = AVMEDIA_TYPE_VIDEO,
297 .config_props = config_input,
298 .filter_frame = filter_frame,
299 },
300 };
301
302 static const AVFilterPad outputs[] = {
303 {
304 .name = "default",
305 .type = AVMEDIA_TYPE_VIDEO,
306 .request_frame = request_frame,
307 },
308 };
309
310 const AVFilter ff_vf_fieldhint = {
311 .name = "fieldhint",
312 .description = NULL_IF_CONFIG_SMALL("Field matching using hints."),
313 .priv_size = sizeof(FieldHintContext),
314 .priv_class = &fieldhint_class,
315 .init = init,
316 .uninit = uninit,
317 FILTER_INPUTS(inputs),
318 FILTER_OUTPUTS(outputs),
319 FILTER_QUERY_FUNC(query_formats),
320 };
321