diff mbox

[FFmpeg-devel] avfilter: add VMAF filter

Message ID 1499099892-14026-1-git-send-email-ashk43712@gmail.com
State Superseded
Headers show

Commit Message

Ashish Singh July 3, 2017, 4:38 p.m. UTC
Added vmaf section in doc/filters.texi and Changelog.

---
 Changelog                |   1 +
 configure                |   5 +
 doc/filters.texi         |  33 ++++
 libavfilter/Makefile     |   1 +
 libavfilter/allfilters.c |   1 +
 libavfilter/vf_vmaf.c    | 402 +++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 443 insertions(+)
 create mode 100644 libavfilter/vf_vmaf.c

Comments

Moritz Barsnick July 4, 2017, 10:02 a.m. UTC | #1
On Mon, Jul 03, 2017 at 22:08:12 +0530, Ashish Singh wrote:
> Added vmaf section in doc/filters.texi and Changelog.

Thanks for that.

> +This filter takes in input two input videos, the first input is

Input input? ;-) "This filter takes two input videos" should suffice.

> +Currently it requires Netflix's vmaf library (libvmaf) as a pre-requisite.
> +It can be enabled using --enable-libvmaf at ./configure ..

Extra dot there. Perhaps you also want to wrap "--enable-libvmaf" and
"./configure" in something like %code{}.

> +If no model path is not specified it uses default model.

Double negation. I think you missed to delete some word(s) when
rearranging the sentence.

> +On this example the input file @file{main.mpg} being processed is compared with the

"In this example". (Shouldn't the explanation come before the example?)

> +static const AVOption vmaf_options[] = {
> +    {"model_path",  "Set the model to be used for computing vmaf.",            OFFSET(model_path), AV_OPT_TYPE_STRING, {.str="/usr/local/share/model/vmaf_v0.6.1.pkl"}, 0, 1, FLAGS},
> +    {"log_path",  "Set the file path to be used to store logs.",            OFFSET(log_path), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS},
> +    {"log_fmt",  "Set the format of the log (xml or json).",            OFFSET(log_fmt), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS},
> +    {"disable_clip",  "Disables clip for computing vmaf.",            OFFSET(disable_clip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
> +    {"disable avx",  "Disables avx for computing vmaf.",            OFFSET(disable_avx), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
> +    {"enable_transform",  "Enables transform for computing vmaf.",            OFFSET(enable_transform), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
> +    {"phone_model",  "Invokes the phone model that will generate higher VMAF scores.",            OFFSET(phone_model), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
> +    {"psnr",  "Enables computing psnr along with vmaf.",            OFFSET(psnr), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
> +    {"ssim",  "Enables computing ssim along with vmaf.",            OFFSET(ssim), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
> +    {"ms_ssim",  "Enables computing ms-ssim along with vmaf.",            OFFSET(ms_ssim), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
> +    {"pool",  "Set the pool method to be used for computing vmaf.",            OFFSET(pool), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS},
> +    { NULL }
> +};

You may want to document the options as well. (Yes, I am aware that
they are a bit self-documenting, but most users aren't aware.)

I realize "log_fmt" and "pool" are passed directly to libvmaf. I hope
that library reports errors in those correctly. Otherwise, enums would
be preferable (but would restrict ffmpeg to those it knows from
libvmaf).

You could also use that big load of whitespace before OFFSET() to
align the columns a bit. But that's cosmetic.

> +static int read_frame_8bit(float *ref_data, float *main_data, float *temp_data,
> +                           int stride, double *score, void *ctx)

This and its 10 bit variant are so very similar, they should be
macro-fied.

Moritz
Derek Buitenhuis July 4, 2017, 3:21 p.m. UTC | #2
On 7/3/2017 5:38 PM, Ashish Singh wrote:
> +enabled libvmaf           && { check_lib libvmaf "libvmaf.h" "compute_vmaf" -lvmaf -lstdc++ -lpthread -lm ||
> +                               die "ERROR: libvmaf must be installed"; }

This won't work on, for example, OS X, which uses libc++ instead of libstdc++.
It'll also add extra unneeded libs to the linker flags when libvmaf is built
as a hared library,

The correct solution is for libvmaf to provide a proper pkg-config file.

> +    {"disable avx",  "Disables avx for computing vmaf.",            OFFSET(disable_avx), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},

Erm, why is this a user option?

[...]

At a glace am I correct in thinking that all this locking around calls is because
libvmaf is not thread-safe?

- Derek
Ronald S. Bultje July 4, 2017, 9:13 p.m. UTC | #3
Hi,

On Tue, Jul 4, 2017 at 11:21 AM, Derek Buitenhuis <
derek.buitenhuis@gmail.com> wrote:

> At a glace am I correct in thinking that all this locking around calls is
> because
> libvmaf is not thread-safe?


vmaf's interface is pull-based.

Ronald
James Almer July 4, 2017, 9:29 p.m. UTC | #4
On 7/3/2017 1:38 PM, Ashish Singh wrote:
> Added vmaf section in doc/filters.texi and Changelog.
> 
> ---
>  Changelog                |   1 +
>  configure                |   5 +
>  doc/filters.texi         |  33 ++++
>  libavfilter/Makefile     |   1 +
>  libavfilter/allfilters.c |   1 +
>  libavfilter/vf_vmaf.c    | 402 +++++++++++++++++++++++++++++++++++++++++++++++
>  6 files changed, 443 insertions(+)
>  create mode 100644 libavfilter/vf_vmaf.c
> 
> diff --git a/Changelog b/Changelog
> index 1778980..311b55b 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -10,6 +10,7 @@ version <next>:
>  - config.log and other configuration files moved into ffbuild/ directory
>  - update cuvid/nvenc headers to Video Codec SDK 8.0.14
>  - afir audio filter
> +- vmaf video filter
>  
>  version 3.3:
>  - CrystalHD decoder moved to new decode API
> diff --git a/configure b/configure
> index 5ae5227..bafcd07 100755
> --- a/configure
> +++ b/configure
> @@ -259,6 +259,7 @@ External library support:
>    --enable-libtwolame      enable MP2 encoding via libtwolame [no]
>    --enable-libv4l2         enable libv4l2/v4l-utils [no]
>    --enable-libvidstab      enable video stabilization using vid.stab [no]
> +  --enable-libvmaf         enable vmaf filter via libvmaf [no]
>    --enable-libvo-amrwbenc  enable AMR-WB encoding via libvo-amrwbenc [no]
>    --enable-libvorbis       enable Vorbis en/decoding via libvorbis,
>                             native implementation exists [no]
> @@ -1569,6 +1570,7 @@ EXTERNAL_LIBRARY_LIST="
>      libtheora
>      libtwolame
>      libv4l2
> +    libvmaf
>      libvorbis
>      libvpx
>      libwavpack
> @@ -3172,6 +3174,7 @@ uspp_filter_deps="gpl avcodec"
>  vaguedenoiser_filter_deps="gpl"
>  vidstabdetect_filter_deps="libvidstab"
>  vidstabtransform_filter_deps="libvidstab"
> +vmaf_filter_deps="libvmaf"
>  zmq_filter_deps="libzmq"
>  zoompan_filter_deps="swscale"
>  zscale_filter_deps="libzimg"
> @@ -5845,6 +5848,8 @@ enabled libtwolame        && require libtwolame twolame.h twolame_init -ltwolame
>                                 die "ERROR: libtwolame must be installed and version must be >= 0.3.10"; }
>  enabled libv4l2           && require_pkg_config libv4l2 libv4l2.h v4l2_ioctl
>  enabled libvidstab        && require_pkg_config "vidstab >= 0.98" vid.stab/libvidstab.h vsMotionDetectInit
> +enabled libvmaf           && { check_lib libvmaf "libvmaf.h" "compute_vmaf" -lvmaf -lstdc++ -lpthread -lm ||
> +                               die "ERROR: libvmaf must be installed"; }
>  enabled libvo_amrwbenc    && require libvo_amrwbenc vo-amrwbenc/enc_if.h E_IF_init -lvo-amrwbenc
>  enabled libvorbis         && require libvorbis vorbis/vorbisenc.h vorbis_info_init -lvorbisenc -lvorbis -logg
>  
> diff --git a/doc/filters.texi b/doc/filters.texi
> index 5985db6..792a11c 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -14701,6 +14701,39 @@ vignette='PI/4+random(1)*PI/50':eval=frame
>  
>  @end itemize
>  
> +@section vmaf
> +
> +Obtain the average VMAF (Video Multi-Method Assessment Fusion)
> +score between two input videos.
> +
> +This filter takes in input two input videos, the first input is
> +considered the "main" source and is passed unchanged to the
> +output. The second input is used as a "reference" video for computing
> +the VMAF score.
> +
> +Both video inputs must have the same resolution and pixel format for
> +this filter to work correctly. Also it assumes that both inputs
> +have the same number of frames, which are compared one by one.
> +
> +The obtained average VMAF score is printed through the logging system.
> +
> +Currently it requires Netflix's vmaf library (libvmaf) as a pre-requisite.
> +It can be enabled using --enable-libvmaf at ./configure ..
> +If no model path is not specified it uses default model.
> +
> +For example:
> +@example
> +ffmpeg -i main.mpg -i ref.mpg -lavfi vmaf -f null -
> +@end example
> +
> +Example with options:
> +@example
> +ffmpeg -i main.mpg -i ref.mpg -lavfi vmaf="psnr=1:enable-transform=1" -f null -
> +@end example
> +
> +On this example the input file @file{main.mpg} being processed is compared with the
> +reference file @file{ref.mpg}.
> +
>  @section vstack
>  Stack input videos vertically.
>  
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index f7dfe8a..1c4bd56 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -314,6 +314,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_VMAF_FILTER)                   += vf_vmaf.o dualinput.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 cd35ae4..6894a6f 100644
> --- a/libavfilter/allfilters.c
> +++ b/libavfilter/allfilters.c
> @@ -325,6 +325,7 @@ static void register_all(void)
>      REGISTER_FILTER(VIDSTABDETECT,  vidstabdetect,  vf);
>      REGISTER_FILTER(VIDSTABTRANSFORM, vidstabtransform, vf);
>      REGISTER_FILTER(VIGNETTE,       vignette,       vf);
> +    REGISTER_FILTER(VMAF,           vmaf,           vf);
>      REGISTER_FILTER(VSTACK,         vstack,         vf);
>      REGISTER_FILTER(W3FDIF,         w3fdif,         vf);
>      REGISTER_FILTER(WAVEFORM,       waveform,       vf);
> diff --git a/libavfilter/vf_vmaf.c b/libavfilter/vf_vmaf.c
> new file mode 100644
> index 0000000..ce966e4
> --- /dev/null
> +++ b/libavfilter/vf_vmaf.c
> @@ -0,0 +1,402 @@
> +/*
> + * Copyright (c) 2017 Ronald S. Bultje <rsbultje@gmail.com>
> + * Copyright (c) 2017 Ashish Pratap Singh <ashk43712@gmail.com>
> + *
> + * 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 the VMAF between two input videos.
> + */
> +
> +#include <inttypes.h>
> +#include <pthread.h>
> +#include <string.h>
> +#include <libvmaf.h>
> +#include "libavutil/avstring.h"
> +#include "libavutil/opt.h"
> +#include "libavutil/pixdesc.h"
> +#include "avfilter.h"
> +#include "dualinput.h"
> +#include "drawutils.h"
> +#include "formats.h"
> +#include "internal.h"
> +#include "video.h"
> +
> +typedef struct VMAFContext {
> +    const AVClass *class;
> +    FFDualInputContext dinput;
> +    char *format;
> +    int width;
> +    int height;
> +    double vmaf_score;
> +    pthread_t vmaf_thread;
> +    pthread_mutex_t lock;
> +    pthread_cond_t cond;
> +    int eof;
> +    AVFrame *gmain;
> +    AVFrame *gref;
> +    int frame_set;
> +    char *model_path;
> +    char *log_path;
> +    char *log_fmt;
> +    int disable_clip;
> +    int disable_avx;
> +    int enable_transform;
> +    int phone_model;
> +    int psnr;
> +    int ssim;
> +    int ms_ssim;
> +    char *pool;
> +} VMAFContext;
> +
> +#define OFFSET(x) offsetof(VMAFContext, x)
> +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
> +
> +static const AVOption vmaf_options[] = {
> +    {"model_path",  "Set the model to be used for computing vmaf.",            OFFSET(model_path), AV_OPT_TYPE_STRING, {.str="/usr/local/share/model/vmaf_v0.6.1.pkl"}, 0, 1, FLAGS},
> +    {"log_path",  "Set the file path to be used to store logs.",            OFFSET(log_path), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS},
> +    {"log_fmt",  "Set the format of the log (xml or json).",            OFFSET(log_fmt), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS},
> +    {"disable_clip",  "Disables clip for computing vmaf.",            OFFSET(disable_clip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
> +    {"disable avx",  "Disables avx for computing vmaf.",            OFFSET(disable_avx), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},

Remove this option, and check for the AVX cpuflag instead.

That is, instead of passing s->disable_avx to compute_vmaf(), pass
!(av_get_cpu_flags() & AV_CPU_FLAG_AVX)
Ronald S. Bultje July 4, 2017, 9:41 p.m. UTC | #5
Hi,

On Tue, Jul 4, 2017 at 5:29 PM, James Almer <jamrial@gmail.com> wrote:

> On 7/3/2017 1:38 PM, Ashish Singh wrote:
> > +    {"disable avx",  "Disables avx for computing vmaf.",
> OFFSET(disable_avx), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
>
> Remove this option, and check for the AVX cpuflag instead.
>
> That is, instead of passing s->disable_avx to compute_vmaf(), pass
> !(av_get_cpu_flags() & AV_CPU_FLAG_AVX)


I _think_ disable_avx is semantically more identical to
disable_optimizations. That is, it forces C when enabled.

Ronald
James Almer July 4, 2017, 10:07 p.m. UTC | #6
On 7/4/2017 6:41 PM, Ronald S. Bultje wrote:
> Hi,
> 
> On Tue, Jul 4, 2017 at 5:29 PM, James Almer <jamrial@gmail.com> wrote:
> 
>> On 7/3/2017 1:38 PM, Ashish Singh wrote:
>>> +    {"disable avx",  "Disables avx for computing vmaf.",
>> OFFSET(disable_avx), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
>>
>> Remove this option, and check for the AVX cpuflag instead.
>>
>> That is, instead of passing s->disable_avx to compute_vmaf(), pass
>> !(av_get_cpu_flags() & AV_CPU_FLAG_AVX)
> 
> 
> I _think_ disable_avx is semantically more identical to
> disable_optimizations. That is, it forces C when enabled.

--disable-optimizations just removes -O3 and such from CFLAGS. What
you're thinking about is --disable-asm, which disables inline_asm,
x86asm (yasm/nasm) and probably similar options from other architectures.

In any case, on second thought checking for the AVX flag outside of the
arch specific folders is not correct, as is checking for inline_asm or
x86asm, so i think perhaps the best choice here would be to just call
compute_vmaf() with this parameters set to 0.

> Ronald
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
Ronald S. Bultje July 4, 2017, 11:58 p.m. UTC | #7
Hi,

On Tue, Jul 4, 2017 at 6:07 PM, James Almer <jamrial@gmail.com> wrote:

> On 7/4/2017 6:41 PM, Ronald S. Bultje wrote:
> > Hi,
> >
> > On Tue, Jul 4, 2017 at 5:29 PM, James Almer <jamrial@gmail.com> wrote:
> >
> >> On 7/3/2017 1:38 PM, Ashish Singh wrote:
> >>> +    {"disable avx",  "Disables avx for computing vmaf.",
> >> OFFSET(disable_avx), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
> >>
> >> Remove this option, and check for the AVX cpuflag instead.
> >>
> >> That is, instead of passing s->disable_avx to compute_vmaf(), pass
> >> !(av_get_cpu_flags() & AV_CPU_FLAG_AVX)
> >
> >
> > I _think_ disable_avx is semantically more identical to
> > disable_optimizations. That is, it forces C when enabled.
>
> --disable-optimizations just removes -O3 and such from CFLAGS. What
> you're thinking about is --disable-asm, which disables inline_asm,
> x86asm (yasm/nasm) and probably similar options from other architectures.
>
> In any case, on second thought checking for the AVX flag outside of the
> arch specific folders is not correct, as is checking for inline_asm or
> x86asm, so i think perhaps the best choice here would be to just call
> compute_vmaf() with this parameters set to 0.


I probably agree, this is almost certainly meant for debugging. We should
not be debugging libvmaf from within ffmpeg. ;-).

Ronald
diff mbox

Patch

diff --git a/Changelog b/Changelog
index 1778980..311b55b 100644
--- a/Changelog
+++ b/Changelog
@@ -10,6 +10,7 @@  version <next>:
 - config.log and other configuration files moved into ffbuild/ directory
 - update cuvid/nvenc headers to Video Codec SDK 8.0.14
 - afir audio filter
+- vmaf video filter
 
 version 3.3:
 - CrystalHD decoder moved to new decode API
diff --git a/configure b/configure
index 5ae5227..bafcd07 100755
--- a/configure
+++ b/configure
@@ -259,6 +259,7 @@  External library support:
   --enable-libtwolame      enable MP2 encoding via libtwolame [no]
   --enable-libv4l2         enable libv4l2/v4l-utils [no]
   --enable-libvidstab      enable video stabilization using vid.stab [no]
+  --enable-libvmaf         enable vmaf filter via libvmaf [no]
   --enable-libvo-amrwbenc  enable AMR-WB encoding via libvo-amrwbenc [no]
   --enable-libvorbis       enable Vorbis en/decoding via libvorbis,
                            native implementation exists [no]
@@ -1569,6 +1570,7 @@  EXTERNAL_LIBRARY_LIST="
     libtheora
     libtwolame
     libv4l2
+    libvmaf
     libvorbis
     libvpx
     libwavpack
@@ -3172,6 +3174,7 @@  uspp_filter_deps="gpl avcodec"
 vaguedenoiser_filter_deps="gpl"
 vidstabdetect_filter_deps="libvidstab"
 vidstabtransform_filter_deps="libvidstab"
+vmaf_filter_deps="libvmaf"
 zmq_filter_deps="libzmq"
 zoompan_filter_deps="swscale"
 zscale_filter_deps="libzimg"
@@ -5845,6 +5848,8 @@  enabled libtwolame        && require libtwolame twolame.h twolame_init -ltwolame
                                die "ERROR: libtwolame must be installed and version must be >= 0.3.10"; }
 enabled libv4l2           && require_pkg_config libv4l2 libv4l2.h v4l2_ioctl
 enabled libvidstab        && require_pkg_config "vidstab >= 0.98" vid.stab/libvidstab.h vsMotionDetectInit
+enabled libvmaf           && { check_lib libvmaf "libvmaf.h" "compute_vmaf" -lvmaf -lstdc++ -lpthread -lm ||
+                               die "ERROR: libvmaf must be installed"; }
 enabled libvo_amrwbenc    && require libvo_amrwbenc vo-amrwbenc/enc_if.h E_IF_init -lvo-amrwbenc
 enabled libvorbis         && require libvorbis vorbis/vorbisenc.h vorbis_info_init -lvorbisenc -lvorbis -logg
 
diff --git a/doc/filters.texi b/doc/filters.texi
index 5985db6..792a11c 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -14701,6 +14701,39 @@  vignette='PI/4+random(1)*PI/50':eval=frame
 
 @end itemize
 
+@section vmaf
+
+Obtain the average VMAF (Video Multi-Method Assessment Fusion)
+score between two input videos.
+
+This filter takes in input two input videos, the first input is
+considered the "main" source and is passed unchanged to the
+output. The second input is used as a "reference" video for computing
+the VMAF score.
+
+Both video inputs must have the same resolution and pixel format for
+this filter to work correctly. Also it assumes that both inputs
+have the same number of frames, which are compared one by one.
+
+The obtained average VMAF score is printed through the logging system.
+
+Currently it requires Netflix's vmaf library (libvmaf) as a pre-requisite.
+It can be enabled using --enable-libvmaf at ./configure ..
+If no model path is not specified it uses default model.
+
+For example:
+@example
+ffmpeg -i main.mpg -i ref.mpg -lavfi vmaf -f null -
+@end example
+
+Example with options:
+@example
+ffmpeg -i main.mpg -i ref.mpg -lavfi vmaf="psnr=1:enable-transform=1" -f null -
+@end example
+
+On this example the input file @file{main.mpg} being processed is compared with the
+reference file @file{ref.mpg}.
+
 @section vstack
 Stack input videos vertically.
 
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index f7dfe8a..1c4bd56 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -314,6 +314,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_VMAF_FILTER)                   += vf_vmaf.o dualinput.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 cd35ae4..6894a6f 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -325,6 +325,7 @@  static void register_all(void)
     REGISTER_FILTER(VIDSTABDETECT,  vidstabdetect,  vf);
     REGISTER_FILTER(VIDSTABTRANSFORM, vidstabtransform, vf);
     REGISTER_FILTER(VIGNETTE,       vignette,       vf);
+    REGISTER_FILTER(VMAF,           vmaf,           vf);
     REGISTER_FILTER(VSTACK,         vstack,         vf);
     REGISTER_FILTER(W3FDIF,         w3fdif,         vf);
     REGISTER_FILTER(WAVEFORM,       waveform,       vf);
diff --git a/libavfilter/vf_vmaf.c b/libavfilter/vf_vmaf.c
new file mode 100644
index 0000000..ce966e4
--- /dev/null
+++ b/libavfilter/vf_vmaf.c
@@ -0,0 +1,402 @@ 
+/*
+ * Copyright (c) 2017 Ronald S. Bultje <rsbultje@gmail.com>
+ * Copyright (c) 2017 Ashish Pratap Singh <ashk43712@gmail.com>
+ *
+ * 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 the VMAF between two input videos.
+ */
+
+#include <inttypes.h>
+#include <pthread.h>
+#include <string.h>
+#include <libvmaf.h>
+#include "libavutil/avstring.h"
+#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
+#include "avfilter.h"
+#include "dualinput.h"
+#include "drawutils.h"
+#include "formats.h"
+#include "internal.h"
+#include "video.h"
+
+typedef struct VMAFContext {
+    const AVClass *class;
+    FFDualInputContext dinput;
+    char *format;
+    int width;
+    int height;
+    double vmaf_score;
+    pthread_t vmaf_thread;
+    pthread_mutex_t lock;
+    pthread_cond_t cond;
+    int eof;
+    AVFrame *gmain;
+    AVFrame *gref;
+    int frame_set;
+    char *model_path;
+    char *log_path;
+    char *log_fmt;
+    int disable_clip;
+    int disable_avx;
+    int enable_transform;
+    int phone_model;
+    int psnr;
+    int ssim;
+    int ms_ssim;
+    char *pool;
+} VMAFContext;
+
+#define OFFSET(x) offsetof(VMAFContext, x)
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+
+static const AVOption vmaf_options[] = {
+    {"model_path",  "Set the model to be used for computing vmaf.",            OFFSET(model_path), AV_OPT_TYPE_STRING, {.str="/usr/local/share/model/vmaf_v0.6.1.pkl"}, 0, 1, FLAGS},
+    {"log_path",  "Set the file path to be used to store logs.",            OFFSET(log_path), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS},
+    {"log_fmt",  "Set the format of the log (xml or json).",            OFFSET(log_fmt), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS},
+    {"disable_clip",  "Disables clip for computing vmaf.",            OFFSET(disable_clip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"disable avx",  "Disables avx for computing vmaf.",            OFFSET(disable_avx), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"enable_transform",  "Enables transform for computing vmaf.",            OFFSET(enable_transform), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"phone_model",  "Invokes the phone model that will generate higher VMAF scores.",            OFFSET(phone_model), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"psnr",  "Enables computing psnr along with vmaf.",            OFFSET(psnr), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"ssim",  "Enables computing ssim along with vmaf.",            OFFSET(ssim), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"ms_ssim",  "Enables computing ms-ssim along with vmaf.",            OFFSET(ms_ssim), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"pool",  "Set the pool method to be used for computing vmaf.",            OFFSET(pool), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS},
+    { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(vmaf);
+
+static int read_frame_8bit(float *ref_data, float *main_data, float *temp_data,
+                           int stride, double *score, void *ctx)
+{
+    VMAFContext *s = (VMAFContext *) ctx;
+    int ret;
+
+    pthread_mutex_lock(&s->lock);
+
+    while (!s->frame_set && !s->eof) {
+        pthread_cond_wait(&s->cond, &s->lock);
+    }
+
+    if (s->frame_set) {
+        int ref_stride = s->gref->linesize[0];
+        int main_stride = s->gmain->linesize[0];
+
+        uint8_t *ref_ptr = s->gref->data[0];
+        uint8_t *main_ptr = s->gmain->data[0];
+
+        float *ptr = ref_data;
+
+        int h = s->height;
+        int w = s->width;
+
+        int i,j;
+
+        for (i = 0; i < h; i++) {
+            for ( j = 0; j < w; j++) {
+                ptr[j] = (float)ref_ptr[j];
+            }
+            ref_ptr += ref_stride / sizeof(*ref_ptr);
+            ptr += stride / sizeof(*ptr);
+        }
+
+        ptr = main_data;
+
+        for (i = 0; i < h; i++) {
+            for (j = 0; j < w; j++) {
+                ptr[j] = (float)main_ptr[j];
+            }
+            main_ptr += main_stride / sizeof(*main_ptr);
+            ptr += stride / sizeof(*ptr);
+        }
+    }
+
+    ret = !s->frame_set;
+
+    s->frame_set = 0;
+
+    pthread_cond_signal(&s->cond);
+    pthread_mutex_unlock(&s->lock);
+
+    if (ret) {
+        return 2;
+    }
+
+    return 0;
+}
+
+static int read_frame_10bit(float *ref_data, float *main_data, float *temp_data,
+                            int stride, double *score, void *ctx)
+{
+    VMAFContext *s = (VMAFContext *) ctx;
+    int ret;
+
+    pthread_mutex_lock(&s->lock);
+
+    while (!s->frame_set && !s->eof) {
+        pthread_cond_wait(&s->cond, &s->lock);
+    }
+
+    if (s->frame_set) {
+        int ref_stride = s->gref->linesize[0];
+        int main_stride = s->gmain->linesize[0];
+
+        uint16_t *ref_ptr = (uint16_t *) s->gref->data[0];
+        uint16_t *main_ptr = (uint16_t *) s->gmain->data[0];
+
+        float *ptr = ref_data;
+
+        int h = s->height;
+        int w = s->width;
+
+        int i,j;
+
+        for (i = 0; i < h; i++) {
+            for ( j = 0; j < w; j++) {
+                ptr[j] = (float)ref_ptr[j];
+            }
+            ref_ptr += ref_stride / sizeof(*ref_ptr);
+            ptr += stride / sizeof(*ptr);
+        }
+
+        ptr = main_data;
+
+        for (i = 0; i < h; i++) {
+            for (j = 0; j < w; j++) {
+                ptr[j] = (float)main_ptr[j];
+            }
+            main_ptr += main_stride / sizeof(*main_ptr);
+            ptr += stride / sizeof(*ptr);
+        }
+    }
+
+    ret = !s->frame_set;
+
+    s->frame_set = 0;
+
+    pthread_cond_signal(&s->cond);
+    pthread_mutex_unlock(&s->lock);
+
+    if (ret) {
+        return 2;
+    }
+
+    return 0;
+}
+
+static void compute_vmaf_score(VMAFContext *s)
+{
+    int (*read_frame)(float *ref_data, float *main_data, float *temp_data,
+                      int stride, double *score, void *ctx);
+
+    if (!strcmp(s->format, "yuv420p") || !strcmp(s->format, "yuv422p") ||
+        !strcmp(s->format, "yuv444p")) {
+        read_frame = read_frame_8bit;
+    } else {
+        read_frame = read_frame_10bit;
+    }
+
+    s->vmaf_score = compute_vmaf(s->format, s->width, s->height, read_frame, s,
+                                 s->model_path, s->log_path, s->log_fmt,
+                                 s->disable_clip, s->disable_avx,
+                                 s->enable_transform, s->phone_model,
+                                 s->psnr, s->ssim, s->ms_ssim, s->pool);
+}
+
+static void *call_vmaf(void *ctx)
+{
+    VMAFContext *s = (VMAFContext *) ctx;
+    compute_vmaf_score(s);
+    pthread_exit(NULL);
+}
+
+static AVFrame *do_vmaf(AVFilterContext *ctx, AVFrame *main, const AVFrame *ref)
+{
+    VMAFContext *s = ctx->priv;
+
+    pthread_mutex_lock(&s->lock);
+
+    while (s->frame_set != 0) {
+        pthread_cond_wait(&s->cond, &s->lock);
+    }
+
+    av_frame_ref(s->gref, ref);
+    av_frame_ref(s->gmain, main);
+
+    s->frame_set = 1;
+
+    pthread_cond_signal(&s->cond);
+    pthread_mutex_unlock(&s->lock);
+
+    return main;
+}
+
+static av_cold int init(AVFilterContext *ctx)
+{
+    VMAFContext *s = ctx->priv;
+
+    s->gref = av_frame_alloc();
+    s->gmain = av_frame_alloc();
+
+    pthread_mutex_init(&s->lock, NULL);
+    pthread_cond_init (&s->cond, NULL);
+
+    s->dinput.process = do_vmaf;
+    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_YUV444P10LE, AV_PIX_FMT_YUV422P10LE, AV_PIX_FMT_YUV420P10LE,
+        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;
+    VMAFContext *s = ctx->priv;
+    int th;
+
+    if (ctx->inputs[0]->w != ctx->inputs[1]->w ||
+        ctx->inputs[0]->h != ctx->inputs[1]->h) {
+        av_log(ctx, AV_LOG_ERROR, "Width and height of input videos must be same.\n");
+        return AVERROR(EINVAL);
+    }
+    if (ctx->inputs[0]->format != ctx->inputs[1]->format) {
+        av_log(ctx, AV_LOG_ERROR, "Inputs must be of same pixel format.\n");
+        return AVERROR(EINVAL);
+    }
+    if (!(s->model_path)) {
+        av_log(ctx, AV_LOG_ERROR, "No model specified.\n");
+        return AVERROR(EINVAL);
+    }
+
+    s->format = av_get_pix_fmt_name(ctx->inputs[0]->format);
+    s->width = ctx->inputs[0]->w;
+    s->height = ctx->inputs[0]->h;
+
+    th = pthread_create(&s->vmaf_thread, NULL, call_vmaf, (void *) s);
+    if (th) {
+        av_log(ctx, AV_LOG_ERROR, "Thread creation failed.\n");
+        return AVERROR(EINVAL);
+    }
+
+    return 0;
+}
+
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    VMAFContext *s = ctx->priv;
+    AVFilterLink *mainlink = ctx->inputs[0];
+    int ret;
+
+    outlink->w = mainlink->w;
+    outlink->h = mainlink->h;
+    outlink->time_base = mainlink->time_base;
+    outlink->sample_aspect_ratio = mainlink->sample_aspect_ratio;
+    outlink->frame_rate = mainlink->frame_rate;
+    if ((ret = ff_dualinput_init(ctx, &s->dinput)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref)
+{
+    VMAFContext *s = inlink->dst->priv;
+    return ff_dualinput_filter_frame(&s->dinput, inlink, inpicref);
+}
+
+static int request_frame(AVFilterLink *outlink)
+{
+    VMAFContext *s = outlink->src->priv;
+    return ff_dualinput_request_frame(&s->dinput, outlink);
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+    VMAFContext *s = ctx->priv;
+
+    ff_dualinput_uninit(&s->dinput);
+
+    pthread_mutex_lock(&s->lock);
+    s->eof = 1;
+    pthread_cond_signal(&s->cond);
+    pthread_mutex_unlock(&s->lock);
+
+    pthread_join(s->vmaf_thread, NULL);
+
+    av_frame_free(&s->gref);
+    av_frame_free(&s->gmain);
+
+    pthread_mutex_destroy(&s->lock);
+    pthread_cond_destroy(&s->cond);
+
+    av_log(ctx, AV_LOG_INFO, "VMAF score: %f\n",s->vmaf_score);
+}
+
+static const AVFilterPad vmaf_inputs[] = {
+    {
+        .name         = "main",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .filter_frame = filter_frame,
+    },{
+        .name         = "reference",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .filter_frame = filter_frame,
+        .config_props = config_input_ref,
+    },
+    { NULL }
+};
+
+static const AVFilterPad vmaf_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .config_props  = config_output,
+        .request_frame = request_frame,
+    },
+    { NULL }
+};
+
+AVFilter ff_vf_vmaf = {
+    .name          = "vmaf",
+    .description   = NULL_IF_CONFIG_SMALL("Calculate the VMAF between two video streams."),
+    .init          = init,
+    .uninit        = uninit,
+    .query_formats = query_formats,
+    .priv_size     = sizeof(VMAFContext),
+    .priv_class    = &vmaf_class,
+    .inputs        = vmaf_inputs,
+    .outputs       = vmaf_outputs,
+};