| /* |
| * Copyright (C) 2017 Paul B Mahol |
| * |
| * This file is part of FFmpeg. |
| * |
| * FFmpeg is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * FFmpeg is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with FFmpeg; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include "libavutil/common.h" |
| #include "libavutil/opt.h" |
| #include "internal.h" |
| |
| typedef struct VFRDETContext { |
| const AVClass *class; |
| |
| int64_t prev_pts; |
| int64_t delta; |
| int64_t min_delta; |
| int64_t max_delta; |
| int64_t avg_delta; |
| |
| uint64_t vfr; |
| uint64_t cfr; |
| } VFRDETContext; |
| |
| static int filter_frame(AVFilterLink *inlink, AVFrame *in) |
| { |
| AVFilterContext *ctx = inlink->dst; |
| VFRDETContext *s = ctx->priv; |
| |
| if (s->prev_pts != AV_NOPTS_VALUE) { |
| int64_t delta = in->pts - s->prev_pts; |
| |
| if (s->delta == AV_NOPTS_VALUE) { |
| s->delta = delta; |
| s->min_delta = delta; |
| s->max_delta = delta; |
| } |
| |
| if (s->delta != delta) { |
| s->vfr++; |
| s->delta = delta; |
| s->min_delta = FFMIN(delta, s->min_delta); |
| s->max_delta = FFMAX(delta, s->max_delta); |
| s->avg_delta += delta; |
| } else { |
| s->cfr++; |
| } |
| } |
| |
| s->prev_pts = in->pts; |
| |
| return ff_filter_frame(ctx->outputs[0], in); |
| } |
| |
| static av_cold int init(AVFilterContext *ctx) |
| { |
| VFRDETContext *s = ctx->priv; |
| |
| s->prev_pts = AV_NOPTS_VALUE; |
| s->delta = AV_NOPTS_VALUE; |
| s->min_delta = INT64_MAX; |
| s->max_delta = INT64_MIN; |
| |
| return 0; |
| } |
| |
| static av_cold void uninit(AVFilterContext *ctx) |
| { |
| VFRDETContext *s = ctx->priv; |
| |
| av_log(ctx, AV_LOG_INFO, "VFR:%f (%"PRIu64"/%"PRIu64")", s->vfr / (float)(s->vfr + s->cfr), s->vfr, s->cfr); |
| if (s->vfr) |
| av_log(ctx, AV_LOG_INFO, " min: %"PRId64" max: %"PRId64" avg: %"PRId64, s->min_delta, s->max_delta, s->avg_delta / s->vfr); |
| av_log(ctx, AV_LOG_INFO, "\n"); |
| } |
| |
| static const AVFilterPad vfrdet_inputs[] = { |
| { |
| .name = "default", |
| .type = AVMEDIA_TYPE_VIDEO, |
| .filter_frame = filter_frame, |
| }, |
| { NULL } |
| }; |
| |
| static const AVFilterPad vfrdet_outputs[] = { |
| { |
| .name = "default", |
| .type = AVMEDIA_TYPE_VIDEO, |
| }, |
| { NULL } |
| }; |
| |
| AVFilter ff_vf_vfrdet = { |
| .name = "vfrdet", |
| .description = NULL_IF_CONFIG_SMALL("Variable frame rate detect filter."), |
| .priv_size = sizeof(VFRDETContext), |
| .init = init, |
| .uninit = uninit, |
| .inputs = vfrdet_inputs, |
| .outputs = vfrdet_outputs, |
| }; |