| 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 | 4 | s->sad = ff_scene_sad_get_fn(s->bitdepth); | |
| 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 FFFilter ff_vf_framerate = { | ||
| 436 | .p.name = "framerate", | ||
| 437 | .p.description = NULL_IF_CONFIG_SMALL("Upsamples or downsamples progressive source between specified frame rates."), | ||
| 438 | .p.priv_class = &framerate_class, | ||
| 439 | .p.flags = AVFILTER_FLAG_SLICE_THREADS, | ||
| 440 | .priv_size = sizeof(FrameRateContext), | ||
| 441 | .init = init, | ||
| 442 | .uninit = uninit, | ||
| 443 | FILTER_INPUTS(framerate_inputs), | ||
| 444 | FILTER_OUTPUTS(framerate_outputs), | ||
| 445 | FILTER_PIXFMTS_ARRAY(pix_fmts), | ||
| 446 | .activate = activate, | ||
| 447 | }; | ||
| 448 |