Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * Copyright (C) 2012 Mark Himsley | ||
3 | * | ||
4 | * get_scene_score() Copyright (c) 2011 Stefano Sabatini | ||
5 | * taken from libavfilter/vf_select.c | ||
6 | * | ||
7 | * This file is part of FFmpeg. | ||
8 | * | ||
9 | * FFmpeg is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU Lesser General Public | ||
11 | * License as published by the Free Software Foundation; either | ||
12 | * version 2.1 of the License, or (at your option) any later version. | ||
13 | * | ||
14 | * FFmpeg is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * Lesser General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU Lesser General Public | ||
20 | * License along with FFmpeg; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
22 | */ | ||
23 | |||
24 | /** | ||
25 | * @file | ||
26 | * filter for upsampling or downsampling a progressive source | ||
27 | */ | ||
28 | |||
29 | #define DEBUG | ||
30 | |||
31 | #include "libavutil/avassert.h" | ||
32 | #include "libavutil/imgutils.h" | ||
33 | #include "libavutil/internal.h" | ||
34 | #include "libavutil/opt.h" | ||
35 | #include "libavutil/pixdesc.h" | ||
36 | |||
37 | #include "avfilter.h" | ||
38 | #include "video.h" | ||
39 | #include "filters.h" | ||
40 | #include "framerate.h" | ||
41 | #include "scene_sad.h" | ||
42 | |||
43 | #define OFFSET(x) offsetof(FrameRateContext, x) | ||
44 | #define V AV_OPT_FLAG_VIDEO_PARAM | ||
45 | #define F AV_OPT_FLAG_FILTERING_PARAM | ||
46 | #define FRAMERATE_FLAG_SCD 01 | ||
47 | |||
48 | static const AVOption framerate_options[] = { | ||
49 | {"fps", "required output frames per second rate", OFFSET(dest_frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="50"}, 0, INT_MAX, V|F }, | ||
50 | |||
51 | {"interp_start", "point to start linear interpolation", OFFSET(interp_start), AV_OPT_TYPE_INT, {.i64=15}, 0, 255, V|F }, | ||
52 | {"interp_end", "point to end linear interpolation", OFFSET(interp_end), AV_OPT_TYPE_INT, {.i64=240}, 0, 255, V|F }, | ||
53 | {"scene", "scene change level", OFFSET(scene_score), AV_OPT_TYPE_DOUBLE, {.dbl=8.2}, 0, 100., V|F }, | ||
54 | |||
55 | {"flags", "set flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64=1}, 0, INT_MAX, V|F, .unit = "flags" }, | ||
56 | {"scene_change_detect", "enable scene change detection", 0, AV_OPT_TYPE_CONST, {.i64=FRAMERATE_FLAG_SCD}, INT_MIN, INT_MAX, V|F, .unit = "flags" }, | ||
57 | {"scd", "enable scene change detection", 0, AV_OPT_TYPE_CONST, {.i64=FRAMERATE_FLAG_SCD}, INT_MIN, INT_MAX, V|F, .unit = "flags" }, | ||
58 | |||
59 | {NULL} | ||
60 | }; | ||
61 | |||
62 | AVFILTER_DEFINE_CLASS(framerate); | ||
63 | |||
64 | 91 | static double get_scene_score(AVFilterContext *ctx, AVFrame *crnt, AVFrame *next) | |
65 | { | ||
66 | 91 | FrameRateContext *s = ctx->priv; | |
67 | 91 | double ret = 0; | |
68 | |||
69 | 91 | ff_dlog(ctx, "get_scene_score()\n"); | |
70 | |||
71 |
1/2✓ Branch 0 taken 91 times.
✗ Branch 1 not taken.
|
91 | if (crnt->height == next->height && |
72 |
1/2✓ Branch 0 taken 91 times.
✗ Branch 1 not taken.
|
91 | crnt->width == next->width) { |
73 | uint64_t sad; | ||
74 | double mafd, diff; | ||
75 | |||
76 | 91 | ff_dlog(ctx, "get_scene_score() process\n"); | |
77 | 91 | s->sad(crnt->data[0], crnt->linesize[0], next->data[0], next->linesize[0], crnt->width, crnt->height, &sad); | |
78 | 91 | mafd = (double)sad * 100.0 / (crnt->width * crnt->height) / (1 << s->bitdepth); | |
79 | 91 | diff = fabs(mafd - s->prev_mafd); | |
80 |
2/2✓ Branch 0 taken 88 times.
✓ Branch 1 taken 3 times.
|
91 | ret = av_clipf(FFMIN(mafd, diff), 0, 100.0); |
81 | 91 | s->prev_mafd = mafd; | |
82 | } | ||
83 | 91 | ff_dlog(ctx, "get_scene_score() result is:%f\n", ret); | |
84 | 91 | return ret; | |
85 | } | ||
86 | |||
87 | typedef struct ThreadData { | ||
88 | AVFrame *copy_src1, *copy_src2; | ||
89 | uint16_t src1_factor, src2_factor; | ||
90 | } ThreadData; | ||
91 | |||
92 | 873 | static int filter_slice(AVFilterContext *ctx, void *arg, int job, int nb_jobs) | |
93 | { | ||
94 | 873 | FrameRateContext *s = ctx->priv; | |
95 | 873 | ThreadData *td = arg; | |
96 | 873 | AVFrame *work = s->work; | |
97 | 873 | AVFrame *src1 = td->copy_src1; | |
98 | 873 | AVFrame *src2 = td->copy_src2; | |
99 | 873 | uint16_t src1_factor = td->src1_factor; | |
100 | 873 | uint16_t src2_factor = td->src2_factor; | |
101 | int plane; | ||
102 | |||
103 |
4/6✓ Branch 0 taken 3492 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2619 times.
✓ Branch 3 taken 873 times.
✓ Branch 4 taken 2619 times.
✗ Branch 5 not taken.
|
3492 | for (plane = 0; plane < 4 && src1->data[plane] && src2->data[plane]; plane++) { |
104 | 2619 | const int start = (s->height[plane] * job ) / nb_jobs; | |
105 | 2619 | const int end = (s->height[plane] * (job+1)) / nb_jobs; | |
106 | 2619 | uint8_t *src1_data = src1->data[plane] + start * src1->linesize[plane]; | |
107 | 2619 | uint8_t *src2_data = src2->data[plane] + start * src2->linesize[plane]; | |
108 | 2619 | uint8_t *dst_data = work->data[plane] + start * work->linesize[plane]; | |
109 | |||
110 | 2619 | s->blend(src1_data, src1->linesize[plane], src2_data, src2->linesize[plane], | |
111 | 2619 | dst_data, work->linesize[plane], s->line_size[plane], end - start, | |
112 | 2619 | src1_factor, src2_factor, s->blend_factor_max >> 1); | |
113 | } | ||
114 | |||
115 | 873 | return 0; | |
116 | } | ||
117 | |||
118 | 97 | static int blend_frames(AVFilterContext *ctx, int interpolate) | |
119 | { | ||
120 | 97 | FrameRateContext *s = ctx->priv; | |
121 | 97 | AVFilterLink *outlink = ctx->outputs[0]; | |
122 | 97 | double interpolate_scene_score = 0; | |
123 | |||
124 |
1/2✓ Branch 0 taken 97 times.
✗ Branch 1 not taken.
|
97 | if ((s->flags & FRAMERATE_FLAG_SCD)) { |
125 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 91 times.
|
97 | if (s->score >= 0.0) |
126 | 6 | interpolate_scene_score = s->score; | |
127 | else | ||
128 | 91 | interpolate_scene_score = s->score = get_scene_score(ctx, s->f0, s->f1); | |
129 | 97 | ff_dlog(ctx, "blend_frames() interpolate scene score:%f\n", interpolate_scene_score); | |
130 | } | ||
131 | // decide if the shot-change detection allows us to blend two frames | ||
132 |
1/2✓ Branch 0 taken 97 times.
✗ Branch 1 not taken.
|
97 | if (interpolate_scene_score < s->scene_score) { |
133 | ThreadData td; | ||
134 | 97 | td.copy_src1 = s->f0; | |
135 | 97 | td.copy_src2 = s->f1; | |
136 | 97 | td.src2_factor = interpolate; | |
137 | 97 | td.src1_factor = s->blend_factor_max - td.src2_factor; | |
138 | |||
139 | // get work-space for output frame | ||
140 | 97 | s->work = ff_get_video_buffer(outlink, outlink->w, outlink->h); | |
141 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
|
97 | if (!s->work) |
142 | ✗ | return AVERROR(ENOMEM); | |
143 | |||
144 | 97 | av_frame_copy_props(s->work, s->f0); | |
145 | |||
146 | 97 | ff_dlog(ctx, "blend_frames() INTERPOLATE to create work frame\n"); | |
147 | 97 | ff_filter_execute(ctx, filter_slice, &td, NULL, | |
148 |
1/2✓ Branch 0 taken 97 times.
✗ Branch 1 not taken.
|
97 | FFMIN(FFMAX(1, outlink->h >> 2), ff_filter_get_nb_threads(ctx))); |
149 | 97 | return 1; | |
150 | } | ||
151 | ✗ | return 0; | |
152 | } | ||
153 | |||
154 | 365 | static int process_work_frame(AVFilterContext *ctx) | |
155 | { | ||
156 | 365 | FrameRateContext *s = ctx->priv; | |
157 | int64_t work_pts; | ||
158 | int64_t interpolate, interpolate8; | ||
159 | int ret; | ||
160 | |||
161 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 357 times.
|
365 | if (!s->f1) |
162 | 8 | return 0; | |
163 |
3/4✓ Branch 0 taken 8 times.
✓ Branch 1 taken 349 times.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
357 | if (!s->f0 && !s->flush) |
164 | 8 | return 0; | |
165 | |||
166 | 349 | work_pts = s->start_pts + av_rescale_q(s->n, av_inv_q(s->dest_frame_rate), s->dest_time_base); | |
167 | |||
168 |
4/4✓ Branch 0 taken 227 times.
✓ Branch 1 taken 122 times.
✓ Branch 2 taken 224 times.
✓ Branch 3 taken 3 times.
|
349 | if (work_pts >= s->pts1 && !s->flush) |
169 | 224 | return 0; | |
170 | |||
171 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 125 times.
|
125 | if (!s->f0) { |
172 | av_assert1(s->flush); | ||
173 | ✗ | s->work = s->f1; | |
174 | ✗ | s->f1 = NULL; | |
175 | } else { | ||
176 |
3/4✓ Branch 0 taken 2 times.
✓ Branch 1 taken 123 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
125 | if (work_pts >= s->pts1 + s->delta && s->flush) |
177 | 2 | return 0; | |
178 | |||
179 | 123 | interpolate = av_rescale(work_pts - s->pts0, s->blend_factor_max, s->delta); | |
180 | 123 | interpolate8 = av_rescale(work_pts - s->pts0, 256, s->delta); | |
181 | 123 | ff_dlog(ctx, "process_work_frame() interpolate: %"PRId64"/256\n", interpolate8); | |
182 |
3/4✓ Branch 0 taken 122 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 122 times.
|
123 | if (interpolate >= s->blend_factor_max || interpolate8 > s->interp_end) { |
183 | 1 | s->work = av_frame_clone(s->f1); | |
184 |
3/4✓ Branch 0 taken 97 times.
✓ Branch 1 taken 25 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 97 times.
|
122 | } else if (interpolate <= 0 || interpolate8 < s->interp_start) { |
185 | 25 | s->work = av_frame_clone(s->f0); | |
186 | } else { | ||
187 | 97 | ret = blend_frames(ctx, interpolate); | |
188 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
|
97 | if (ret < 0) |
189 | ✗ | return ret; | |
190 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
|
97 | if (ret == 0) |
191 | ✗ | s->work = av_frame_clone(interpolate > (s->blend_factor_max >> 1) ? s->f1 : s->f0); | |
192 | } | ||
193 | } | ||
194 | |||
195 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 123 times.
|
123 | if (!s->work) |
196 | ✗ | return AVERROR(ENOMEM); | |
197 | |||
198 | 123 | s->work->pts = work_pts; | |
199 | 123 | s->n++; | |
200 | |||
201 | 123 | return 1; | |
202 | } | ||
203 | |||
204 | 8 | static av_cold int init(AVFilterContext *ctx) | |
205 | { | ||
206 | 8 | FrameRateContext *s = ctx->priv; | |
207 | 8 | s->start_pts = AV_NOPTS_VALUE; | |
208 | 8 | return 0; | |
209 | } | ||
210 | |||
211 | 8 | static av_cold void uninit(AVFilterContext *ctx) | |
212 | { | ||
213 | 8 | FrameRateContext *s = ctx->priv; | |
214 | 8 | av_frame_free(&s->f0); | |
215 | 8 | av_frame_free(&s->f1); | |
216 | 8 | } | |
217 | |||
218 | static const enum AVPixelFormat pix_fmts[] = { | ||
219 | AV_PIX_FMT_YUV410P, | ||
220 | AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUVJ411P, | ||
221 | AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVJ420P, | ||
222 | AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVJ422P, | ||
223 | AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUVJ440P, | ||
224 | AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P, | ||
225 | AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV420P12, | ||
226 | AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV422P12, | ||
227 | AV_PIX_FMT_YUV444P9, AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV444P12, | ||
228 | AV_PIX_FMT_NONE | ||
229 | }; | ||
230 | |||
231 | #define BLEND_FRAME_FUNC(nbits) \ | ||
232 | static void blend_frames##nbits##_c(BLEND_FUNC_PARAMS) \ | ||
233 | { \ | ||
234 | int line, pixel; \ | ||
235 | uint##nbits##_t *dstw = (uint##nbits##_t *)dst; \ | ||
236 | uint##nbits##_t *src1w = (uint##nbits##_t *)src1; \ | ||
237 | uint##nbits##_t *src2w = (uint##nbits##_t *)src2; \ | ||
238 | int bytes = nbits / 8; \ | ||
239 | width /= bytes; \ | ||
240 | src1_linesize /= bytes; \ | ||
241 | src2_linesize /= bytes; \ | ||
242 | dst_linesize /= bytes; \ | ||
243 | for (line = 0; line < height; line++) { \ | ||
244 | for (pixel = 0; pixel < width; pixel++) \ | ||
245 | dstw[pixel] = ((src1w[pixel] * factor1) + \ | ||
246 | (src2w[pixel] * factor2) + half) \ | ||
247 | >> BLEND_FACTOR_DEPTH(nbits); \ | ||
248 | src1w += src1_linesize; \ | ||
249 | src2w += src2_linesize; \ | ||
250 | dstw += dst_linesize; \ | ||
251 | } \ | ||
252 | } | ||
253 |
4/4✓ Branch 0 taken 921600 times.
✓ Branch 1 taken 3840 times.
✓ Branch 2 taken 3840 times.
✓ Branch 3 taken 216 times.
|
925656 | BLEND_FRAME_FUNC(8) |
254 |
4/4✓ Branch 0 taken 13670400 times.
✓ Branch 1 taken 64080 times.
✓ Branch 2 taken 64080 times.
✓ Branch 3 taken 2403 times.
|
13736883 | BLEND_FRAME_FUNC(16) |
255 | |||
256 | 4 | void ff_framerate_init(FrameRateContext *s) | |
257 | { | ||
258 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
|
4 | if (s->bitdepth == 8) { |
259 | 2 | s->blend_factor_max = 1 << BLEND_FACTOR_DEPTH(8); | |
260 | 2 | s->blend = blend_frames8_c; | |
261 | } else { | ||
262 | 2 | s->blend_factor_max = 1 << BLEND_FACTOR_DEPTH(16); | |
263 | 2 | s->blend = blend_frames16_c; | |
264 | } | ||
265 | #if ARCH_X86 | ||
266 | 4 | ff_framerate_init_x86(s); | |
267 | #endif | ||
268 | 4 | } | |
269 | |||
270 | 4 | static int config_input(AVFilterLink *inlink) | |
271 | { | ||
272 | 4 | AVFilterContext *ctx = inlink->dst; | |
273 | 4 | FrameRateContext *s = ctx->priv; | |
274 | 4 | const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink->format); | |
275 | int plane; | ||
276 | |||
277 | 4 | s->vsub = pix_desc->log2_chroma_h; | |
278 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 4 times.
|
20 | for (plane = 0; plane < 4; plane++) { |
279 | 16 | s->line_size[plane] = av_image_get_linesize(inlink->format, inlink->w, plane); | |
280 |
4/4✓ Branch 0 taken 12 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 8 times.
|
16 | s->height[plane] = inlink->h >> ((plane == 1 || plane == 2) ? s->vsub : 0); |
281 | } | ||
282 | |||
283 | 4 | s->bitdepth = pix_desc->comp[0].depth; | |
284 | |||
285 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
|
4 | s->sad = ff_scene_sad_get_fn(s->bitdepth == 8 ? 8 : 16); |
286 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (!s->sad) |
287 | ✗ | return AVERROR(EINVAL); | |
288 | |||
289 | 4 | s->srce_time_base = inlink->time_base; | |
290 | |||
291 | 4 | ff_framerate_init(s); | |
292 | |||
293 | 4 | return 0; | |
294 | } | ||
295 | |||
296 | 245 | static int activate(AVFilterContext *ctx) | |
297 | { | ||
298 | int ret, status; | ||
299 | 245 | AVFilterLink *inlink = ctx->inputs[0]; | |
300 | 245 | AVFilterLink *outlink = ctx->outputs[0]; | |
301 | 245 | FrameRateContext *s = ctx->priv; | |
302 | AVFrame *inpicref; | ||
303 | int64_t pts; | ||
304 | |||
305 |
1/2✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
|
245 | FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); |
306 | |||
307 | 365 | retry: | |
308 | 365 | ret = process_work_frame(ctx); | |
309 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 365 times.
|
365 | if (ret < 0) |
310 | ✗ | return ret; | |
311 |
2/2✓ Branch 0 taken 123 times.
✓ Branch 1 taken 242 times.
|
365 | else if (ret == 1) |
312 | 123 | return ff_filter_frame(outlink, s->work); | |
313 | |||
314 | 242 | ret = ff_inlink_consume_frame(inlink, &inpicref); | |
315 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 242 times.
|
242 | if (ret < 0) |
316 | ✗ | return ret; | |
317 | |||
318 |
2/2✓ Branch 0 taken 118 times.
✓ Branch 1 taken 124 times.
|
242 | if (inpicref) { |
319 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 118 times.
|
118 | if (inpicref->flags & AV_FRAME_FLAG_INTERLACED) |
320 | ✗ | av_log(ctx, AV_LOG_WARNING, "Interlaced frame found - the output will not be correct.\n"); | |
321 | |||
322 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 118 times.
|
118 | if (inpicref->pts == AV_NOPTS_VALUE) { |
323 | ✗ | av_log(ctx, AV_LOG_WARNING, "Ignoring frame without PTS.\n"); | |
324 | ✗ | av_frame_free(&inpicref); | |
325 | } | ||
326 | } | ||
327 | |||
328 |
2/2✓ Branch 0 taken 118 times.
✓ Branch 1 taken 124 times.
|
242 | if (inpicref) { |
329 | 118 | pts = av_rescale_q(inpicref->pts, s->srce_time_base, s->dest_time_base); | |
330 | |||
331 |
3/4✓ Branch 0 taken 114 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 114 times.
|
118 | if (s->f1 && pts == s->pts1) { |
332 | ✗ | av_log(ctx, AV_LOG_WARNING, "Ignoring frame with same PTS.\n"); | |
333 | ✗ | av_frame_free(&inpicref); | |
334 | } | ||
335 | } | ||
336 | |||
337 |
2/2✓ Branch 0 taken 118 times.
✓ Branch 1 taken 124 times.
|
242 | if (inpicref) { |
338 | 118 | av_frame_free(&s->f0); | |
339 | 118 | s->f0 = s->f1; | |
340 | 118 | s->pts0 = s->pts1; | |
341 | 118 | s->f1 = inpicref; | |
342 | 118 | s->pts1 = pts; | |
343 | 118 | s->delta = s->pts1 - s->pts0; | |
344 | 118 | s->score = -1.0; | |
345 | |||
346 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 118 times.
|
118 | if (s->delta < 0) { |
347 | ✗ | av_log(ctx, AV_LOG_WARNING, "PTS discontinuity.\n"); | |
348 | ✗ | s->start_pts = s->pts1; | |
349 | ✗ | s->n = 0; | |
350 | ✗ | av_frame_free(&s->f0); | |
351 | } | ||
352 | |||
353 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 114 times.
|
118 | if (s->start_pts == AV_NOPTS_VALUE) |
354 | 4 | s->start_pts = s->pts1; | |
355 | |||
356 | 118 | goto retry; | |
357 | } | ||
358 | |||
359 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 120 times.
|
124 | if (ff_inlink_acknowledge_status(inlink, &status, &pts)) { |
360 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
|
4 | if (!s->flush) { |
361 | 2 | s->flush = 1; | |
362 | 2 | goto retry; | |
363 | } | ||
364 | 2 | ff_outlink_set_status(outlink, status, pts); | |
365 | 2 | return 0; | |
366 | } | ||
367 | |||
368 |
1/2✓ Branch 1 taken 120 times.
✗ Branch 2 not taken.
|
120 | FF_FILTER_FORWARD_WANTED(outlink, inlink); |
369 | |||
370 | ✗ | return FFERROR_NOT_READY; | |
371 | } | ||
372 | |||
373 | 4 | static int config_output(AVFilterLink *outlink) | |
374 | { | ||
375 | 4 | AVFilterContext *ctx = outlink->src; | |
376 | 4 | FilterLink *l = ff_filter_link(outlink); | |
377 | 4 | FrameRateContext *s = ctx->priv; | |
378 | int exact; | ||
379 | |||
380 | 4 | ff_dlog(ctx, "config_output()\n"); | |
381 | |||
382 | 4 | ff_dlog(ctx, | |
383 | "config_output() input time base:%u/%u (%f)\n", | ||
384 | ctx->inputs[0]->time_base.num,ctx->inputs[0]->time_base.den, | ||
385 | av_q2d(ctx->inputs[0]->time_base)); | ||
386 | |||
387 | // make sure timebase is small enough to hold the framerate | ||
388 | |||
389 | 4 | exact = av_reduce(&s->dest_time_base.num, &s->dest_time_base.den, | |
390 | 4 | av_gcd((int64_t)s->srce_time_base.num * s->dest_frame_rate.num, | |
391 | 4 | (int64_t)s->srce_time_base.den * s->dest_frame_rate.den ), | |
392 | 4 | (int64_t)s->srce_time_base.den * s->dest_frame_rate.num, INT_MAX); | |
393 | |||
394 | 4 | av_log(ctx, AV_LOG_INFO, | |
395 | "time base:%u/%u -> %u/%u exact:%d\n", | ||
396 | s->srce_time_base.num, s->srce_time_base.den, | ||
397 | s->dest_time_base.num, s->dest_time_base.den, exact); | ||
398 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (!exact) { |
399 | ✗ | av_log(ctx, AV_LOG_WARNING, "Timebase conversion is not exact\n"); | |
400 | } | ||
401 | |||
402 | 4 | l->frame_rate = s->dest_frame_rate; | |
403 | 4 | outlink->time_base = s->dest_time_base; | |
404 | |||
405 | 4 | ff_dlog(ctx, | |
406 | "config_output() output time base:%u/%u (%f) w:%d h:%d\n", | ||
407 | outlink->time_base.num, outlink->time_base.den, | ||
408 | av_q2d(outlink->time_base), | ||
409 | outlink->w, outlink->h); | ||
410 | |||
411 | |||
412 | 4 | av_log(ctx, AV_LOG_INFO, "fps -> fps:%u/%u scene score:%f interpolate start:%d end:%d\n", | |
413 | s->dest_frame_rate.num, s->dest_frame_rate.den, | ||
414 | s->scene_score, s->interp_start, s->interp_end); | ||
415 | |||
416 | 4 | return 0; | |
417 | } | ||
418 | |||
419 | static const AVFilterPad framerate_inputs[] = { | ||
420 | { | ||
421 | .name = "default", | ||
422 | .type = AVMEDIA_TYPE_VIDEO, | ||
423 | .config_props = config_input, | ||
424 | }, | ||
425 | }; | ||
426 | |||
427 | static const AVFilterPad framerate_outputs[] = { | ||
428 | { | ||
429 | .name = "default", | ||
430 | .type = AVMEDIA_TYPE_VIDEO, | ||
431 | .config_props = config_output, | ||
432 | }, | ||
433 | }; | ||
434 | |||
435 | const AVFilter ff_vf_framerate = { | ||
436 | .name = "framerate", | ||
437 | .description = NULL_IF_CONFIG_SMALL("Upsamples or downsamples progressive source between specified frame rates."), | ||
438 | .priv_size = sizeof(FrameRateContext), | ||
439 | .priv_class = &framerate_class, | ||
440 | .init = init, | ||
441 | .uninit = uninit, | ||
442 | FILTER_INPUTS(framerate_inputs), | ||
443 | FILTER_OUTPUTS(framerate_outputs), | ||
444 | FILTER_PIXFMTS_ARRAY(pix_fmts), | ||
445 | .flags = AVFILTER_FLAG_SLICE_THREADS, | ||
446 | .activate = activate, | ||
447 | }; | ||
448 |