FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/vf_weave.c
Date: 2024-11-20 23:03:26
Exec Total Coverage
Lines: 79 90 87.8%
Functions: 5 6 83.3%
Branches: 15 30 50.0%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2013 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/imgutils.h"
22 #include "libavutil/opt.h"
23 #include "libavutil/pixdesc.h"
24 #include "avfilter.h"
25 #include "filters.h"
26 #include "formats.h"
27 #include "video.h"
28
29 typedef struct WeaveContext {
30 const AVClass *class;
31 int first_field;
32 int double_weave;
33 int nb_planes;
34 int planeheight[4];
35 int outheight[4];
36 int linesize[4];
37
38 AVFrame *prev;
39 } WeaveContext;
40
41 #define OFFSET(x) offsetof(WeaveContext, x)
42 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
43
44 static const AVOption weave_options[] = {
45 { "first_field", "set first field", OFFSET(first_field), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, .unit = "field"},
46 { "top", "set top field first", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "field"},
47 { "t", "set top field first", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "field"},
48 { "bottom", "set bottom field first", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "field"},
49 { "b", "set bottom field first", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "field"},
50 { NULL }
51 };
52
53 AVFILTER_DEFINE_CLASS_EXT(weave, "(double)weave", weave_options);
54
55 1 static int query_formats(const AVFilterContext *ctx,
56 AVFilterFormatsConfig **cfg_in,
57 AVFilterFormatsConfig **cfg_out)
58 {
59 1 int reject_flags = AV_PIX_FMT_FLAG_PAL | AV_PIX_FMT_FLAG_HWACCEL;
60
61 1 return ff_set_common_formats2(ctx, cfg_in, cfg_out,
62 ff_formats_pixdesc_filter(0, reject_flags));
63 }
64
65 1 static int config_props_output(AVFilterLink *outlink)
66 {
67 1 AVFilterContext *ctx = outlink->src;
68 1 WeaveContext *s = ctx->priv;
69 1 AVFilterLink *inlink = ctx->inputs[0];
70 1 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
71 int ret;
72
73
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (!s->double_weave) {
74 1 FilterLink *il = ff_filter_link(inlink);
75 1 FilterLink *ol = ff_filter_link(outlink);
76 1 outlink->time_base.num = inlink->time_base.num * 2;
77 1 outlink->time_base.den = inlink->time_base.den;
78 1 ol->frame_rate.num = il->frame_rate.num;
79 1 ol->frame_rate.den = il->frame_rate.den * 2;
80 }
81 1 outlink->w = inlink->w;
82 1 outlink->h = inlink->h * 2;
83
84
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if ((ret = av_image_fill_linesizes(s->linesize, inlink->format, inlink->w)) < 0)
85 return ret;
86
87 1 s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
88 1 s->planeheight[0] = s->planeheight[3] = inlink->h;
89
90 1 s->outheight[1] = s->outheight[2] = AV_CEIL_RSHIFT(2*inlink->h, desc->log2_chroma_h);
91 1 s->outheight[0] = s->outheight[3] = 2*inlink->h;
92
93 1 s->nb_planes = av_pix_fmt_count_planes(inlink->format);
94
95 1 return 0;
96 }
97
98 typedef struct ThreadData {
99 AVFrame *in, *out;
100 } ThreadData;
101
102 225 static int weave_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
103 {
104 225 AVFilterLink *inlink = ctx->inputs[0];
105 225 FilterLink *inl = ff_filter_link(inlink);
106 225 WeaveContext *s = ctx->priv;
107 225 ThreadData *td = arg;
108 225 AVFrame *in = td->in;
109 225 AVFrame *out = td->out;
110
111
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 225 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
225 const int weave = (s->double_weave && !(inl->frame_count_out & 1));
112
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 225 times.
225 const int field1 = weave ? s->first_field : (!s->first_field);
113
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 225 times.
225 const int field2 = weave ? (!s->first_field) : s->first_field;
114
115
2/2
✓ Branch 0 taken 675 times.
✓ Branch 1 taken 225 times.
900 for (int i = 0; i < s->nb_planes; i++) {
116 675 const int height = s->planeheight[i];
117 675 const int start = (height * jobnr) / nb_jobs;
118 675 const int end = (height * (jobnr+1)) / nb_jobs;
119 675 const int compensation = 2*end > s->outheight[i];
120
121 675 av_image_copy_plane(out->data[i] + out->linesize[i] * field1 +
122 675 out->linesize[i] * start * 2,
123 675 out->linesize[i] * 2,
124 675 in->data[i] + start * in->linesize[i],
125 in->linesize[i],
126 675 s->linesize[i], end - start - compensation * field1);
127 675 av_image_copy_plane(out->data[i] + out->linesize[i] * field2 +
128 675 out->linesize[i] * start * 2,
129 675 out->linesize[i] * 2,
130 675 s->prev->data[i] + start * s->prev->linesize[i],
131 675 s->prev->linesize[i],
132 675 s->linesize[i], end - start - compensation * field2);
133 }
134
135 225 return 0;
136 }
137
138 50 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
139 {
140 50 AVFilterContext *ctx = inlink->dst;
141 50 WeaveContext *s = ctx->priv;
142 50 AVFilterLink *outlink = ctx->outputs[0];
143 ThreadData td;
144 AVFrame *out;
145
146
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 25 times.
50 if (!s->prev) {
147 25 s->prev = in;
148 25 return 0;
149 }
150
151 25 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
152
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 if (!out) {
153 av_frame_free(&in);
154 av_frame_free(&s->prev);
155 return AVERROR(ENOMEM);
156 }
157 25 av_frame_copy_props(out, in);
158
159 25 td.out = out, td.in = in;
160 25 ff_filter_execute(ctx, weave_slice, &td, NULL,
161
1/2
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
25 FFMIN(s->planeheight[1], ff_filter_get_nb_threads(ctx)));
162
163
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 out->pts = s->double_weave ? s->prev->pts : in->pts / 2;
164 #if FF_API_INTERLACED_FRAME
165 FF_DISABLE_DEPRECATION_WARNINGS
166 25 out->interlaced_frame = 1;
167 25 out->top_field_first = !s->first_field;
168 FF_ENABLE_DEPRECATION_WARNINGS
169 #endif
170 25 out->flags |= AV_FRAME_FLAG_INTERLACED;
171
1/2
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
25 if (s->first_field)
172 25 out->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST;
173 else
174 out->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST;
175
176
1/2
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
25 if (!s->double_weave)
177 25 av_frame_free(&in);
178 25 av_frame_free(&s->prev);
179
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 if (s->double_weave)
180 s->prev = in;
181 25 return ff_filter_frame(outlink, out);
182 }
183
184 2 static av_cold void uninit(AVFilterContext *ctx)
185 {
186 2 WeaveContext *s = ctx->priv;
187
188 2 av_frame_free(&s->prev);
189 2 }
190
191 static const AVFilterPad weave_inputs[] = {
192 {
193 .name = "default",
194 .type = AVMEDIA_TYPE_VIDEO,
195 .filter_frame = filter_frame,
196 },
197 };
198
199 static const AVFilterPad weave_outputs[] = {
200 {
201 .name = "default",
202 .type = AVMEDIA_TYPE_VIDEO,
203 .config_props = config_props_output,
204 },
205 };
206
207 const AVFilter ff_vf_weave = {
208 .name = "weave",
209 .description = NULL_IF_CONFIG_SMALL("Weave input video fields into frames."),
210 .priv_size = sizeof(WeaveContext),
211 .priv_class = &weave_class,
212 .uninit = uninit,
213 FILTER_INPUTS(weave_inputs),
214 FILTER_OUTPUTS(weave_outputs),
215 FILTER_QUERY_FUNC2(query_formats),
216 .flags = AVFILTER_FLAG_SLICE_THREADS,
217 };
218
219 static av_cold int init(AVFilterContext *ctx)
220 {
221 WeaveContext *s = ctx->priv;
222
223 if (!strcmp(ctx->filter->name, "doubleweave"))
224 s->double_weave = 1;
225
226 return 0;
227 }
228
229 const AVFilter ff_vf_doubleweave = {
230 .name = "doubleweave",
231 .description = NULL_IF_CONFIG_SMALL("Weave input video fields into double number of frames."),
232 .priv_class = &weave_class,
233 .priv_size = sizeof(WeaveContext),
234 .init = init,
235 .uninit = uninit,
236 FILTER_INPUTS(weave_inputs),
237 FILTER_OUTPUTS(weave_outputs),
238 FILTER_QUERY_FUNC2(query_formats),
239 .flags = AVFILTER_FLAG_SLICE_THREADS,
240 };
241