From patchwork Fri Sep 15 20:47:58 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ashish Singh X-Patchwork-Id: 5163 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.2.36.26 with SMTP id f26csp1062944jaa; Fri, 15 Sep 2017 14:11:03 -0700 (PDT) X-Received: by 10.223.173.84 with SMTP id p78mr19133550wrc.277.1505509863699; Fri, 15 Sep 2017 14:11:03 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1505509863; cv=none; d=google.com; s=arc-20160816; b=ILSOJ0VWOJv5Fl+nzg86NbTOuBb1ZmCh1ayrz7z2KJ4LuS0U6FfCuyEKPX3qNJ0Z4N 1Njo78nokOB8elAdFONgsliugzMrgLWmQ9ywienGstfT3URcURZbLcQVT3Egw9kLFjYm D6XJYvFpmpXqwdbNTqTEG5Qk7/I1lhz6zbwdwfE8sxJCLh1+zcBfNqQ0g1hPWrni2oZw acEDxmirpezNIZvmBCkVIx80Wqu3ldBfi9S4c0p9Yvz+mSlSrtpqzQ3qXo1TpLDhcUHp OYyxxsTTXa0WGTJoOkzLDrmZhFCtNk8QoGOYYMGeFLyZTXgXzYKitr6aWBsZFIx8575s 89WA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:references:in-reply-to:message-id:date :to:from:dkim-signature:delivered-to:arc-authentication-results; bh=PEolIEpo/lGRCDdmjse4FS9cvQ0GTO3/XHTHQYEn31c=; b=igTqBwt0p27Zf+ai8MZ/xVA8QcjQexKFjn7M6ndnYxRhR9mZngj4txb4QMpxWQzL2k oEasQZt5HuCl05Y4266iXSmw7+wlgrZsbrqY3tMOBEOadMfXr+hut1E+vHyjELHxsW+S 7q11l20y2/OC0VXLNZlcKyEqWXnZ7AE0llmVUPjPKOgoBB8bLjT76t/xPs8z+o3Xuedd 58k5jdmjsYXlYWWwVpFxZFRy0fRMb05Mt3aTAUx65rdJ6IsN+u5QSqtNXSidlYF3q24L 7gXi8OCyjCUKl/IUvQjVuQdO8ZQ57xgDcbTW/qo0LWry0G0Z3PhXgW3XYTCfOFbOQb1V kcRA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20161025 header.b=GcsBsnCE; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=gmail.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id y202si1547802wmc.263.2017.09.15.14.11.03; Fri, 15 Sep 2017 14:11:03 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20161025 header.b=GcsBsnCE; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id B2A3968A097; Sat, 16 Sep 2017 00:10:54 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf0-f195.google.com (mail-pf0-f195.google.com [209.85.192.195]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id AA79468A057 for ; Sat, 16 Sep 2017 00:10:47 +0300 (EEST) Received: by mail-pf0-f195.google.com with SMTP id f84so1740515pfj.3 for ; Fri, 15 Sep 2017 14:10:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=L0Ss3agC0GFaSNcL1oGo0+AjVemf9NB/VAY78K8e1X8=; b=GcsBsnCE+/yhi/3OwVJeonfGEDFYg9rDuvNexlbSgq4TcWK0H+WgCvtSCMxzWglDEP REZLitWyHB1wnRY57PABbw65+tuTY4M5e1mHU/hIskcWZ8MoK3HTDkFc2QcVvJD98ptW iqBfEenJ/d0ojQde/iXek/LbrdyrrfWKo+7MjE9n/c9dLThOWQpN5bYqVW3kCq1+m3xr rHDzvjEtrtEGxzg+G+7r26SDZCK8j92F/lT4skMBv1Q3vjHuuFG7VlXxvQSmvZlMx+4C Fl84Yg1rNO4Nq0mTgjI2CrXG+jLBf9Orxjr/EVvBIVBtb08a4X5dIXNGSv7S+fHLh0FP j35Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=L0Ss3agC0GFaSNcL1oGo0+AjVemf9NB/VAY78K8e1X8=; b=f+CthAD+MWCtGemPvPQHFm9qJ9TCITtuY7nPNeUsVC5c0u2mKG5+7oyy0eEzSET4Ae W92XyNoiYPC2pWYzSSLl+bEYCmOmvCJlMT/zfuJ3oiL+fGhHeDKw/JH/T8XRhpUsEPxe N4KEs8VcDg/COmLZpmWly2B7idYI2nRz6+aES+hPv/mG2AMycH3zq5LohefF2Ns0vzYp PMbMF3wbn0r1J8rmuTlSvJkeOaaHT7fhPbX6qPeI1dOLMMx9/+sBmeWFrFx2MyCheoVD iYlZcfKf/sY9MEtsLhZRdDyuwbpKO43iKIRQbLHcdftqDSY8AVwmuq50/4fE3AmHcp85 J3Ng== X-Gm-Message-State: AHPjjUgK5JD0x6bLI+9w0LthG7VoRjg97BiQgwipbTN+hlEaFpxI5f6Z u7kjOCZpY9LmVm9t X-Google-Smtp-Source: ADKCNb67Jbe6pQjNHRwDy7JBODM0FG/KJ8P9uXS2xdUV9BIyA8dfGhJ3iZkO4LbLGyj1HMxvZ6KbOw== X-Received: by 10.99.121.194 with SMTP id u185mr25304004pgc.21.1505508488665; Fri, 15 Sep 2017 13:48:08 -0700 (PDT) Received: from localhost.localdomain ([27.124.62.83]) by smtp.gmail.com with ESMTPSA id p85sm3698367pfj.47.2017.09.15.13.48.06 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 15 Sep 2017 13:48:08 -0700 (PDT) From: Ashish Pratap Singh To: ffmpeg-devel@ffmpeg.org Date: Sat, 16 Sep 2017 02:17:58 +0530 Message-Id: <1505508478-17076-1-git-send-email-ashk43712@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1504728287-16766-1-git-send-email-ashk43712@gmail.com> References: <1504728287-16766-1-git-send-email-ashk43712@gmail.com> Subject: [FFmpeg-devel] [PATCH] avfilter: add vmafmotion filter X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Ashish Singh MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" From: Ashish Singh Hi, this patch addresses the previous issues and changes it to a single input filter. Signed-off-by: Ashish Singh --- Changelog | 1 + doc/filters.texi | 14 ++ libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/vf_vmafmotion.c | 325 ++++++++++++++++++++++++++++++++++++++++++++ libavfilter/vmaf_motion.h | 58 ++++++++ 6 files changed, 400 insertions(+) create mode 100644 libavfilter/vf_vmafmotion.c create mode 100644 libavfilter/vmaf_motion.h diff --git a/Changelog b/Changelog index ea48e81..574f46e 100644 --- a/Changelog +++ b/Changelog @@ -48,6 +48,7 @@ version : - convolve video filter - VP9 tile threading support - KMS screen grabber +- vmafmotion video filter version 3.3: - CrystalHD decoder moved to new decode API diff --git a/doc/filters.texi b/doc/filters.texi index 830de54..d996357 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -15570,6 +15570,20 @@ vignette='PI/4+random(1)*PI/50':eval=frame @end itemize +@section vmafmotion + +Obtain the average vmaf motion score of a video. +It is one of the component filters of VMAF. + +The obtained average motion score is printed through the logging system. + +In the below example the input file @file{ref.mpg} is being processed and score +is computed. + +@example +ffmpeg -i ref.mpg -lavfi vmafmotion -f null - +@end example + @section vstack Stack input videos vertically. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 8aa974e..4289ee0 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -330,6 +330,7 @@ OBJS-$(CONFIG_VFLIP_FILTER) += vf_vflip.o OBJS-$(CONFIG_VIDSTABDETECT_FILTER) += vidstabutils.o vf_vidstabdetect.o OBJS-$(CONFIG_VIDSTABTRANSFORM_FILTER) += vidstabutils.o vf_vidstabtransform.o OBJS-$(CONFIG_VIGNETTE_FILTER) += vf_vignette.o +OBJS-$(CONFIG_VMAFMOTION_FILTER) += vf_vmafmotion.o framesync.o OBJS-$(CONFIG_VSTACK_FILTER) += vf_stack.o framesync.o OBJS-$(CONFIG_W3FDIF_FILTER) += vf_w3fdif.o OBJS-$(CONFIG_WAVEFORM_FILTER) += vf_waveform.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 63e8672..8ec54be 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -341,6 +341,7 @@ static void register_all(void) REGISTER_FILTER(VIDSTABDETECT, vidstabdetect, vf); REGISTER_FILTER(VIDSTABTRANSFORM, vidstabtransform, vf); REGISTER_FILTER(VIGNETTE, vignette, vf); + REGISTER_FILTER(VMAFMOTION, vmafmotion, vf); REGISTER_FILTER(VSTACK, vstack, vf); REGISTER_FILTER(W3FDIF, w3fdif, vf); REGISTER_FILTER(WAVEFORM, waveform, vf); diff --git a/libavfilter/vf_vmafmotion.c b/libavfilter/vf_vmafmotion.c new file mode 100644 index 0000000..c31c37c --- /dev/null +++ b/libavfilter/vf_vmafmotion.c @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2017 Ronald S. Bultje + * Copyright (c) 2017 Ashish Pratap Singh + * + * 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 + */ + +/** + * @file + * Calculate VMAF Motion score. + */ + +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "avfilter.h" +#include "drawutils.h" +#include "formats.h" +#include "internal.h" +#include "vmaf_motion.h" + +#define vmafmotion_options NULL +#define BIT_SHIFT 10 + +static const float FILTER_5[5] = { + 0.054488685, + 0.244201342, + 0.402619947, + 0.244201342, + 0.054488685 +}; + +typedef struct VMAFMotionContext { + const AVClass *class; + VMAFMotionData data; +} VMAFMotionContext; + +AVFILTER_DEFINE_CLASS(vmafmotion); + +static uint64_t image_sad(const uint16_t *img1, const uint16_t *img2, int w, + int h, ptrdiff_t _img1_stride, ptrdiff_t _img2_stride) +{ + ptrdiff_t img1_stride = _img1_stride / sizeof(*img1); + ptrdiff_t img2_stride = _img2_stride / sizeof(*img2); + uint64_t sum = 0; + int i, j; + + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + sum += abs(img1[j] - img2[j]); + } + img1 += img1_stride; + img2 += img2_stride; + } + + return sum; +} + +static void convolution_x(const uint16_t *filter, int filt_w, const uint16_t *src, + uint16_t *dst, int w, int h, ptrdiff_t _src_stride, + ptrdiff_t _dst_stride) +{ + ptrdiff_t src_stride = _src_stride / sizeof(*src); + ptrdiff_t dst_stride = _dst_stride / sizeof(*dst); + int radius = filt_w / 2; + int borders_left = radius; + int borders_right = w - (filt_w - radius); + int i, j, k; + int sum = 0; + + for (i = 0; i < h; i++) { + for (j = 0; j < borders_left; j++) { + sum = 0; + for (k = 0; k < filt_w; k++) { + int j_tap = FFABS(j - radius + k); + if (j_tap >= w) { + j_tap = w - (j_tap - w + 1); + } + sum += filter[k] * src[i * src_stride + j_tap]; + } + dst[i * dst_stride + j] = sum >> BIT_SHIFT; + } + + for (j = borders_left; j < borders_right; j++) { + int sum = 0; + for (k = 0; k < filt_w; k++) { + sum += filter[k] * src[i * src_stride + j - radius + k]; + } + dst[i * dst_stride + j] = sum >> BIT_SHIFT; + } + + for (j = borders_right; j < w; j++) { + sum = 0; + for (k = 0; k < filt_w; k++) { + int j_tap = FFABS(j - radius + k); + if (j_tap >= w) { + j_tap = w - (j_tap - w + 1); + } + sum += filter[k] * src[i * src_stride + j_tap]; + } + dst[i * dst_stride + j] = sum >> BIT_SHIFT; + } + } +} + +#define conv_y_fn(type, bits) \ + static void convolution_y_##bits##bit(const uint16_t *filter, int filt_w, \ + const uint8_t *_src, uint16_t *dst, \ + int w, int h, ptrdiff_t _src_stride, \ + ptrdiff_t _dst_stride) \ +{ \ + const type *src = (const type *) _src; \ + ptrdiff_t src_stride = _src_stride / sizeof(*src); \ + ptrdiff_t dst_stride = _dst_stride / sizeof(*dst); \ + int radius = filt_w / 2; \ + int borders_top = radius; \ + int borders_bottom = h - (filt_w - radius); \ + int i, j, k; \ + int sum = 0; \ + \ + for (i = 0; i < borders_top; i++) { \ + for (j = 0; j < w; j++) { \ + sum = 0; \ + for (k = 0; k < filt_w; k++) { \ + int i_tap = FFABS(i - radius + k); \ + if (i_tap >= h) { \ + i_tap = h - (i_tap - h + 1); \ + } \ + sum += filter[k] * src[i_tap * src_stride + j]; \ + } \ + dst[i * dst_stride + j] = sum >> BIT_SHIFT; \ + } \ + } \ + for (i = borders_top; i < borders_bottom; i++) { \ + for (j = 0; j < w; j++) { \ + sum = 0; \ + for (k = 0; k < filt_w; k++) { \ + sum += filter[k] * src[(i - radius + k) * src_stride + j]; \ + } \ + dst[i * dst_stride + j] = sum >> BIT_SHIFT; \ + } \ + } \ + for (i = borders_bottom; i < h; i++) { \ + for (j = 0; j < w; j++) { \ + sum = 0; \ + for (k = 0; k < filt_w; k++) { \ + int i_tap = FFABS(i - radius + k); \ + if (i_tap >= h) { \ + i_tap = h - (i_tap - h + 1); \ + } \ + sum += filter[k] * src[i_tap * src_stride + j]; \ + } \ + dst[i * dst_stride + j] = sum >> BIT_SHIFT; \ + } \ + } \ +} + +conv_y_fn(uint8_t, 8); +conv_y_fn(uint16_t, 10); + +static void vmafmotiondsp_init(VMAFMotionDSPContext *dsp, int bpp) { + dsp->convolution_x = convolution_x; + dsp->convolution_y = bpp == 10 ? convolution_y_10bit : convolution_y_8bit; + dsp->sad = image_sad; +} + +double ff_vmafmotion_process(VMAFMotionData *s, AVFrame *ref) +{ + double score; + + s->vmafdsp.convolution_y(s->filter, 5, ref->data[0], s->temp_data, + s->width, s->height, ref->linesize[0], s->stride); + s->vmafdsp.convolution_x(s->filter, 5, s->temp_data, s->blur_data[0], + s->width, s->height, s->stride, s->stride); + + if (!s->nb_frames) { + score = 0.0; + } else { + uint64_t sad = image_sad(s->blur_data[1], s->blur_data[0], + s->width, s->height, s->stride, s->stride); + score = (double) (sad * 1.0 / (s->width * s->height)); + } + + FFSWAP(uint16_t *, s->blur_data[0], s->blur_data[1]); + s->nb_frames++; + s->motion_sum += score; + + return score; +} + +static void set_meta(AVDictionary **metadata, const char *key, float d) +{ + char value[128]; + snprintf(value, sizeof(value), "%0.2f", d); + av_dict_set(metadata, key, value, 0); +} + +static void do_vmafmotion(AVFilterContext *ctx, AVFrame *ref) +{ + VMAFMotionContext *s = ctx->priv; + + double score; + + score = ff_vmafmotion_process(&s->data, ref); + set_meta(&ref->metadata, "lavfi.vmafmotion.score", score); +} + + +int ff_vmafmotion_init(VMAFMotionData *s, + int w, int h, enum AVPixelFormat fmt) +{ + size_t data_sz; + int i; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt); + + s->width = w; + s->height = h; + s->stride = FFALIGN(w * sizeof(uint16_t), 32); + + data_sz = (size_t) s->stride * h; + if (!(s->blur_data[0] = av_malloc(data_sz)) || + !(s->blur_data[1] = av_malloc(data_sz)) || + !(s->temp_data = av_malloc(data_sz))) { + return AVERROR(ENOMEM); + } + + for (i = 0; i < 5; i++) { + s->filter[i] = lrint(FILTER_5[i] * (1 << BIT_SHIFT)); + } + + vmafmotiondsp_init(&s->vmafdsp, desc->comp[0].depth); + + return 0; +} + +static int query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV420P10, + AV_PIX_FMT_NONE + }; + + AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts); + if (!fmts_list) + return AVERROR(ENOMEM); + return ff_set_common_formats(ctx, fmts_list); +} + +static int config_input_ref(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + VMAFMotionContext *s = ctx->priv; + + return ff_vmafmotion_init(&s->data, ctx->inputs[0]->w, + ctx->inputs[0]->h, ctx->inputs[0]->format); +} + +double ff_vmafmotion_uninit(VMAFMotionData *s) +{ + av_free(s->blur_data[0]); + av_free(s->blur_data[1]); + av_free(s->temp_data); + + return s->nb_frames > 0 ? s->motion_sum / s->nb_frames : 0.0; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *ref) +{ + AVFilterContext *ctx = inlink->dst; + do_vmafmotion(ctx, ref); + return ff_filter_frame(ctx->outputs[0], ref); +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + VMAFMotionContext *s = ctx->priv; + double avg_motion = ff_vmafmotion_uninit(&s->data); + + if (s->data.nb_frames > 0) { + av_log(ctx, AV_LOG_INFO, "VMAF Motion avg: %.3f\n", avg_motion); + } +} + +static const AVFilterPad vmafmotion_inputs[] = { + { + .name = "reference", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = filter_frame, + .config_props = config_input_ref, + }, + { NULL } +}; + +static const AVFilterPad vmafmotion_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL } +}; + +AVFilter ff_vf_vmafmotion = { + .name = "vmafmotion", + .description = NULL_IF_CONFIG_SMALL("Calculate the VMAF Motion score."), + .uninit = uninit, + .query_formats = query_formats, + .priv_size = sizeof(VMAFMotionContext), + .priv_class = &vmafmotion_class, + .inputs = vmafmotion_inputs, + .outputs = vmafmotion_outputs, +}; diff --git a/libavfilter/vmaf_motion.h b/libavfilter/vmaf_motion.h new file mode 100644 index 0000000..0c71182 --- /dev/null +++ b/libavfilter/vmaf_motion.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017 Ronald S. Bultje + * Copyright (c) 2017 Ashish Pratap Singh + * + * 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 + */ + +#ifndef AVFILTER_VMAFMOTION_H +#define AVFILTER_VMAFMOTION_H + +#include +#include +#include "video.h" + +typedef struct VMAFMotionDSPContext { + uint64_t (*sad)(const uint16_t *img1, const uint16_t *img2, int w, int h, + ptrdiff_t img1_stride, ptrdiff_t img2_stride); + void (*convolution_x)(const uint16_t *filter, int filt_w, const uint16_t *src, + uint16_t *dst, int w, int h, ptrdiff_t src_stride, + ptrdiff_t dst_stride); + void (*convolution_y)(const uint16_t *filter, int filt_w, const uint8_t *src, + uint16_t *dst, int w, int h, ptrdiff_t src_stride, + ptrdiff_t dst_stride); +} VMAFMotionDSPContext; + +void ff_vmafmotion_init_x86(VMAFMotionDSPContext *dsp); + +typedef struct VMAFMotionData { + uint16_t filter[5]; + int width; + int height; + ptrdiff_t stride; + uint16_t *blur_data[2 /* cur, prev */]; + uint16_t *temp_data; + double motion_sum; + uint64_t nb_frames; + VMAFMotionDSPContext vmafdsp; +} VMAFMotionData; + +int ff_vmafmotion_init(VMAFMotionData *data, int w, int h, enum AVPixelFormat fmt); +double ff_vmafmotion_process(VMAFMotionData *data, AVFrame *frame); +double ff_vmafmotion_uninit(VMAFMotionData *data); + +#endif /* AVFILTER_VMAFMOTION_H */