[FFmpeg-devel] [PATCH 2/2] avfilter: add mcfps filter
Michael Niedermayer
michaelni at gmx.at
Sun Aug 30 04:23:59 CEST 2015
From: Michael Niedermayer <michael at niedermayer.cc>
Works well with some scenes, works really not well with others
if you can improve it, i would not be unhappy
this should not be optimized yet except trivial things, first the code
should work well then it should be made to work fast
Signed-off-by: Michael Niedermayer <michael at niedermayer.cc>
---
libavfilter/Makefile | 1 +
libavfilter/allfilters.c | 1 +
libavfilter/vf_mcfps.c | 788 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 790 insertions(+)
create mode 100644 libavfilter/vf_mcfps.c
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index a2af794..b13d4e4 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -168,6 +168,7 @@ OBJS-$(CONFIG_LUT_FILTER) += vf_lut.o
OBJS-$(CONFIG_LUTRGB_FILTER) += vf_lut.o
OBJS-$(CONFIG_LUTYUV_FILTER) += vf_lut.o
OBJS-$(CONFIG_MCDEINT_FILTER) += vf_mcdeint.o
+OBJS-$(CONFIG_MCFPS_FILTER) += vf_mcfps.o
OBJS-$(CONFIG_MERGEPLANES_FILTER) += vf_mergeplanes.o framesync.o
OBJS-$(CONFIG_MPDECIMATE_FILTER) += vf_mpdecimate.o
OBJS-$(CONFIG_NEGATE_FILTER) += vf_lut.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index b7b3807..ddf58a0 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -184,6 +184,7 @@ void avfilter_register_all(void)
REGISTER_FILTER(LUTRGB, lutrgb, vf);
REGISTER_FILTER(LUTYUV, lutyuv, vf);
REGISTER_FILTER(MCDEINT, mcdeint, vf);
+ REGISTER_FILTER(MCFPS, mcfps, vf);
REGISTER_FILTER(MERGEPLANES, mergeplanes, vf);
REGISTER_FILTER(MPDECIMATE, mpdecimate, vf);
REGISTER_FILTER(NEGATE, negate, vf);
diff --git a/libavfilter/vf_mcfps.c b/libavfilter/vf_mcfps.c
new file mode 100644
index 0000000..f5a3c82
--- /dev/null
+++ b/libavfilter/vf_mcfps.c
@@ -0,0 +1,788 @@
+/*
+ * Copyright (c) 2014-2015 Michael Niedermayer <michaelni at gmx.at>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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/avassert.h"
+#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/rational.h"
+
+#include "libavcodec/avme.h"
+
+#include "avfilter.h"
+#include "internal.h"
+
+// TODO, clone last frame at EOF so no frames are lost
+// TODO >8bit support
+// TODO use SIMD for MC
+// TODO use some linesize instead of w where SIMD is used
+
+#define NB_INPUT_FRAMES 4
+#define ALPHA_MAX 1024
+#define LOG2_MB_SIZE 4
+
+enum MCFPSMode {
+ MCFPS_MODE_NN = 0,
+ MCFPS_MODE_LINEAR_IPOL = 1,
+ MCFPS_MODE_GMC = 2,
+ MCFPS_MODE_OBMC = 3,
+};
+
+#define NB_MVS 20
+typedef struct Pixel {
+ int16_t mv[NB_MVS][2];
+ uint32_t weight[NB_MVS];
+ int8_t ref[NB_MVS];
+ uint8_t layer[NB_MVS];
+ int nb;
+} Pixel;
+
+typedef struct InputFrame {
+ AVFrame *f;
+ uint8_t *halfpel[4][4];
+ int halfpel_linesize[4];
+ int16_t (*mv[2])[2];
+ int8_t *ref[2];
+} InputFrame;
+
+typedef struct MCFPSContext {
+ const AVClass *class;
+
+ AVRational frame_rate;
+ enum MCFPSMode mode;
+
+ InputFrame input[NB_INPUT_FRAMES];
+
+ int64_t out_pts;
+
+ int chroma_h_shift;
+ int chroma_v_shift;
+ int planes;
+
+ struct AVMEContext *avme[2];
+ int b_width, b_height;
+ int log2_mv_precision;
+
+ Pixel *pixel;
+} MCFPSContext;
+
+static const uint8_t obmc32[1024]={
+ 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0,
+ 0, 4, 4, 4, 8, 8, 8, 12, 12, 16, 16, 16, 20, 20, 20, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 8, 8, 8, 4, 4, 4, 0,
+ 0, 4, 8, 8, 12, 12, 16, 20, 20, 24, 28, 28, 32, 32, 36, 40, 40, 36, 32, 32, 28, 28, 24, 20, 20, 16, 12, 12, 8, 8, 4, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28, 28, 32, 36, 40, 44, 48, 52, 56, 56, 52, 48, 44, 40, 36, 32, 28, 28, 24, 20, 16, 12, 8, 4, 0,
+ 4, 8, 12, 16, 20, 24, 28, 32, 40, 44, 48, 52, 56, 60, 64, 68, 68, 64, 60, 56, 52, 48, 44, 40, 32, 28, 24, 20, 16, 12, 8, 4,
+ 4, 8, 12, 20, 24, 32, 36, 40, 48, 52, 56, 64, 68, 76, 80, 84, 84, 80, 76, 68, 64, 56, 52, 48, 40, 36, 32, 24, 20, 12, 8, 4,
+ 4, 8, 16, 24, 28, 36, 44, 48, 56, 60, 68, 76, 80, 88, 96,100,100, 96, 88, 80, 76, 68, 60, 56, 48, 44, 36, 28, 24, 16, 8, 4,
+ 4, 12, 20, 28, 32, 40, 48, 56, 64, 72, 80, 88, 92,100,108,116,116,108,100, 92, 88, 80, 72, 64, 56, 48, 40, 32, 28, 20, 12, 4,
+ 4, 12, 20, 28, 40, 48, 56, 64, 72, 80, 88, 96,108,116,124,132,132,124,116,108, 96, 88, 80, 72, 64, 56, 48, 40, 28, 20, 12, 4,
+ 4, 16, 24, 32, 44, 52, 60, 72, 80, 92,100,108,120,128,136,148,148,136,128,120,108,100, 92, 80, 72, 60, 52, 44, 32, 24, 16, 4,
+ 4, 16, 28, 36, 48, 56, 68, 80, 88,100,112,120,132,140,152,164,164,152,140,132,120,112,100, 88, 80, 68, 56, 48, 36, 28, 16, 4,
+ 4, 16, 28, 40, 52, 64, 76, 88, 96,108,120,132,144,156,168,180,180,168,156,144,132,120,108, 96, 88, 76, 64, 52, 40, 28, 16, 4,
+ 8, 20, 32, 44, 56, 68, 80, 92,108,120,132,144,156,168,180,192,192,180,168,156,144,132,120,108, 92, 80, 68, 56, 44, 32, 20, 8,
+ 8, 20, 32, 48, 60, 76, 88,100,116,128,140,156,168,184,196,208,208,196,184,168,156,140,128,116,100, 88, 76, 60, 48, 32, 20, 8,
+ 8, 20, 36, 52, 64, 80, 96,108,124,136,152,168,180,196,212,224,224,212,196,180,168,152,136,124,108, 96, 80, 64, 52, 36, 20, 8,
+ 8, 24, 40, 56, 68, 84,100,116,132,148,164,180,192,208,224,240,240,224,208,192,180,164,148,132,116,100, 84, 68, 56, 40, 24, 8,
+ 8, 24, 40, 56, 68, 84,100,116,132,148,164,180,192,208,224,240,240,224,208,192,180,164,148,132,116,100, 84, 68, 56, 40, 24, 8,
+ 8, 20, 36, 52, 64, 80, 96,108,124,136,152,168,180,196,212,224,224,212,196,180,168,152,136,124,108, 96, 80, 64, 52, 36, 20, 8,
+ 8, 20, 32, 48, 60, 76, 88,100,116,128,140,156,168,184,196,208,208,196,184,168,156,140,128,116,100, 88, 76, 60, 48, 32, 20, 8,
+ 8, 20, 32, 44, 56, 68, 80, 92,108,120,132,144,156,168,180,192,192,180,168,156,144,132,120,108, 92, 80, 68, 56, 44, 32, 20, 8,
+ 4, 16, 28, 40, 52, 64, 76, 88, 96,108,120,132,144,156,168,180,180,168,156,144,132,120,108, 96, 88, 76, 64, 52, 40, 28, 16, 4,
+ 4, 16, 28, 36, 48, 56, 68, 80, 88,100,112,120,132,140,152,164,164,152,140,132,120,112,100, 88, 80, 68, 56, 48, 36, 28, 16, 4,
+ 4, 16, 24, 32, 44, 52, 60, 72, 80, 92,100,108,120,128,136,148,148,136,128,120,108,100, 92, 80, 72, 60, 52, 44, 32, 24, 16, 4,
+ 4, 12, 20, 28, 40, 48, 56, 64, 72, 80, 88, 96,108,116,124,132,132,124,116,108, 96, 88, 80, 72, 64, 56, 48, 40, 28, 20, 12, 4,
+ 4, 12, 20, 28, 32, 40, 48, 56, 64, 72, 80, 88, 92,100,108,116,116,108,100, 92, 88, 80, 72, 64, 56, 48, 40, 32, 28, 20, 12, 4,
+ 4, 8, 16, 24, 28, 36, 44, 48, 56, 60, 68, 76, 80, 88, 96,100,100, 96, 88, 80, 76, 68, 60, 56, 48, 44, 36, 28, 24, 16, 8, 4,
+ 4, 8, 12, 20, 24, 32, 36, 40, 48, 52, 56, 64, 68, 76, 80, 84, 84, 80, 76, 68, 64, 56, 52, 48, 40, 36, 32, 24, 20, 12, 8, 4,
+ 4, 8, 12, 16, 20, 24, 28, 32, 40, 44, 48, 52, 56, 60, 64, 68, 68, 64, 60, 56, 52, 48, 44, 40, 32, 28, 24, 20, 16, 12, 8, 4,
+ 0, 4, 8, 12, 16, 20, 24, 28, 28, 32, 36, 40, 44, 48, 52, 56, 56, 52, 48, 44, 40, 36, 32, 28, 28, 24, 20, 16, 12, 8, 4, 0,
+ 0, 4, 8, 8, 12, 12, 16, 20, 20, 24, 28, 28, 32, 32, 36, 40, 40, 36, 32, 32, 28, 28, 24, 20, 20, 16, 12, 12, 8, 8, 4, 0,
+ 0, 4, 4, 4, 8, 8, 8, 12, 12, 16, 16, 16, 20, 20, 20, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 8, 8, 8, 4, 4, 4, 0,
+ 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0,
+};
+
+static const uint8_t obmc16[256]={
+ 0, 4, 4, 8, 8, 12, 12, 16, 16, 12, 12, 8, 8, 4, 4, 0,
+ 4, 8, 16, 20, 28, 32, 40, 44, 44, 40, 32, 28, 20, 16, 8, 4,
+ 4, 16, 24, 36, 44, 56, 64, 76, 76, 64, 56, 44, 36, 24, 16, 4,
+ 8, 20, 36, 48, 64, 76, 92,104,104, 92, 76, 64, 48, 36, 20, 8,
+ 8, 28, 44, 64, 80,100,116,136,136,116,100, 80, 64, 44, 28, 8,
+ 12, 32, 56, 76,100,120,144,164,164,144,120,100, 76, 56, 32, 12,
+ 12, 40, 64, 92,116,144,168,196,196,168,144,116, 92, 64, 40, 12,
+ 16, 44, 76,104,136,164,196,224,224,196,164,136,104, 76, 44, 16,
+ 16, 44, 76,104,136,164,196,224,224,196,164,136,104, 76, 44, 16,
+ 12, 40, 64, 92,116,144,168,196,196,168,144,116, 92, 64, 40, 12,
+ 12, 32, 56, 76,100,120,144,164,164,144,120,100, 76, 56, 32, 12,
+ 8, 28, 44, 64, 80,100,116,136,136,116,100, 80, 64, 44, 28, 8,
+ 8, 20, 36, 48, 64, 76, 92,104,104, 92, 76, 64, 48, 36, 20, 8,
+ 4, 16, 24, 36, 44, 56, 64, 76, 76, 64, 56, 44, 36, 24, 16, 4,
+ 4, 8, 16, 20, 28, 32, 40, 44, 44, 40, 32, 28, 20, 16, 8, 4,
+ 0, 4, 4, 8, 8, 12, 12, 16, 16, 12, 12, 8, 8, 4, 4, 0,
+};
+
+static const uint8_t obmc8[64]={
+ 4, 12, 20, 28, 28, 20, 12, 4,
+ 12, 36, 60, 84, 84, 60, 36, 12,
+ 20, 60,100,140,140,100, 60, 20,
+ 28, 84,140,196,196,140, 84, 28,
+ 28, 84,140,196,196,140, 84, 28,
+ 20, 60,100,140,140,100, 60, 20,
+ 12, 36, 60, 84, 84, 60, 36, 12,
+ 4, 12, 20, 28, 28, 20, 12, 4,
+};
+
+static const uint8_t obmc4[16]={
+ 16, 48, 48, 16,
+ 48,144,144, 48,
+ 48,144,144, 48,
+ 16, 48, 48, 16,
+};
+
+static const uint8_t * const obmc_tab[4]= {
+ obmc32, obmc16, obmc8, obmc4
+};
+
+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_YUV411P,
+ AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV440P,
+ AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ422P,
+ AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ440P,
+ AV_PIX_FMT_NONE
+ };
+ ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
+ return 0;
+}
+
+av_cold static void uninit(AVFilterContext *ctx)
+{
+ MCFPSContext *mcfps = ctx->priv;
+ int i, j, p;
+
+ for (i=0; i<NB_INPUT_FRAMES; i++) {
+ InputFrame *inf = &mcfps->input[i];
+ av_freep(&inf->mv[0]);
+ av_freep(&inf->mv[1]);
+ av_freep(&inf->ref[0]);
+ av_freep(&inf->ref[1]);
+ av_frame_free(&inf->f);
+ for (p = 0; p<mcfps->planes; p++)
+ for (j=0; j<4; j++)
+ av_freep(&inf->halfpel[p][j]);
+ }
+
+ for (i = 0; i<2; i++) {
+ av_me_free(&mcfps->avme[i]);
+ }
+
+ av_freep(&mcfps->pixel);
+}
+
+av_cold static int config_output(AVFilterLink *outlink)
+{
+ AVFilterContext *ctx = outlink->src;
+ AVFilterLink *inlink = ctx->inputs[0];
+ MCFPSContext *mcfps = ctx->priv;
+ const int height = inlink->h;
+ const int width = inlink->w;
+ int i;
+ int ret;
+
+ mcfps->log2_mv_precision = 2;
+
+ outlink->flags |= FF_LINK_FLAG_REQUEST_LOOP;
+ outlink->frame_rate = mcfps->frame_rate;
+ outlink->time_base = av_inv_q(mcfps->frame_rate);
+
+ mcfps->b_width = FF_CEIL_RSHIFT(width, LOG2_MB_SIZE);
+ mcfps->b_height = FF_CEIL_RSHIFT(height, LOG2_MB_SIZE);
+
+ for (i = 0; i < 2; i++) {
+ AVDictionary *opts = NULL;
+
+ if (i)
+ av_dict_set(&opts, "gop", "2", 0);
+ if (mcfps->log2_mv_precision > 1)
+ av_dict_set(&opts, "qpel", "1", 0);
+
+ if (!(mcfps->avme[i] = av_me_init(width, height, inlink->format, opts))) {
+ ret = AVERROR(ENOMEM);
+ av_dict_free(&opts);
+ goto fail;
+ }
+ av_dict_free(&opts);
+ }
+
+ if (!(mcfps->pixel = av_mallocz_array(width*height, sizeof(*mcfps->pixel)))) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ return 0;
+fail:
+
+ uninit(ctx);
+
+ return ret;
+}
+
+static int get_temporal_mv_difference(MCFPSContext *mcfps, int dir, int mbx, int mby, int *nomv) {
+ int x, y;
+ int ref0 = mcfps->input[2-dir].ref[dir][mby*mcfps->b_width + mbx];
+ int mvx0 = mcfps->input[2-dir].mv[dir][mby*mcfps->b_width + mbx][0];
+ int mvy0 = mcfps->input[2-dir].mv[dir][mby*mcfps->b_width + mbx][1];
+ int roughness = INT_MAX;
+ int ref, mvx, mvy;
+ int div = 1<<(mcfps->log2_mv_precision + LOG2_MB_SIZE);
+ int dir1 = 1-dir;
+
+ x = mbx + (mvx0 / div);
+ y = mby + (mvy0 / div);
+ x = av_clip(x, 0, mcfps->input[0].f->width - 1);
+ y = av_clip(y, 0, mcfps->input[0].f->height - 1);
+
+ ref = mcfps->input[2-dir1].ref[dir1][y*mcfps->b_width + x];
+ if (ref < 0 || ref0 < 0) {
+ (*nomv) ++;
+ return 0;
+ }
+
+ av_assert1(ref0 == 0);
+
+ mvx = -mcfps->input[2-dir1].mv[dir1][y*mcfps->b_width + x][0];
+ mvy = -mcfps->input[2-dir1].mv[dir1][y*mcfps->b_width + x][1];
+
+ return FFABS(mvx0 - mvx) + FFABS(mvy0 - mvy);
+}
+
+static int get_roughness(MCFPSContext *mcfps, int dir, int mbx, int mby) {
+ int x, y;
+ int ref0 = mcfps->input[2-dir].ref[dir][mby*mcfps->b_width + mbx];
+ int mvx0 = mcfps->input[2-dir].mv[dir][mby*mcfps->b_width + mbx][0];
+ int mvy0 = mcfps->input[2-dir].mv[dir][mby*mcfps->b_width + mbx][1];
+ int roughness = INT_MAX;
+
+ av_assert1(ref0 == 0);
+
+ for (y = FFMAX(mby-1, 0); y < FFMIN(mby+2, mcfps->b_height); y++) {
+ for (x = FFMAX(mbx-1, 0); x < FFMIN(mbx+2, mcfps->b_width); x++) {
+ int d, ref, mvx, mvy;
+ int dir1 = dir;
+ if (x == mbx && y == mby) {
+ dir1 = 1-dir;
+ }
+ ref = mcfps->input[2-dir].ref[dir1][y*mcfps->b_width + x];
+ if (ref < 0)
+ continue;
+ mvx = mcfps->input[2-dir].mv[dir1][y*mcfps->b_width + x][0];
+ mvy = mcfps->input[2-dir].mv[dir1][y*mcfps->b_width + x][1];
+ if (dir != dir1) {
+ mvx = -mvx;
+ mvy = -mvy;
+ }
+ d = FFABS(mvx0 - mvx) + FFABS(mvy0 - mvy);
+ roughness = FFMIN(roughness, d);
+ }
+ }
+ return roughness;
+}
+
+static void fill_pixels(MCFPSContext *mcfps, int alpha)
+{
+ int x, y;
+ int w = mcfps->input[0].f->width;
+ int h = mcfps->input[0].f->height;
+ int mby, mbx;
+ int dir;
+ int64_t temporal_diff = 0;
+ int nomv = 0;
+
+ for (y=0; y<h; y++){
+ int ymax = (h-y-1)<<mcfps->log2_mv_precision;
+ for (x=0; x<w; x++){
+ int xmax = (w-x-1)<<mcfps->log2_mv_precision;
+ Pixel *p = &mcfps->pixel[x + y*w];
+ p->weight[0] = ALPHA_MAX-alpha;
+ p->ref[0] = 1;
+ p->mv[0][0] = 0;
+ p->mv[0][1] = 0;
+ p->weight[1] = alpha;
+ p->ref[1] = 2;
+ p->mv[1][0] = 0;
+ p->mv[1][1] = 0;
+ p->nb = 2;
+// p->nb = 0;
+ }
+ }
+
+//FIXME remove outlier MVs ?
+//FIXME fill areas which have no MVs
+//FIXME change MVs t 1/16 earlier to improve precisiion
+
+ for (dir = 0; dir<2; dir++) {
+ for (mby=0; mby<mcfps->b_height; mby++) {
+ for (mbx=0; mbx<mcfps->b_width; mbx++) {
+ temporal_diff += get_temporal_mv_difference(mcfps, dir, mbx, mby, &nomv);
+ }
+ }
+ }
+
+ if (mcfps->b_height * mcfps->b_width * 5 / 8 > nomv)
+ for (dir = 0; dir<2; dir++) {
+ int a = dir ? alpha : (ALPHA_MAX-alpha);
+
+ for (mby=0; mby<mcfps->b_height; mby++) {
+ for (mbx=0; mbx<mcfps->b_width; mbx++) {
+ int ref = mcfps->input[2-dir].ref[dir][mby*mcfps->b_width + mbx];
+ int mvx = mcfps->input[2-dir].mv[dir][mby*mcfps->b_width + mbx][0];
+ int mvy = mcfps->input[2-dir].mv[dir][mby*mcfps->b_width + mbx][1];
+ int startx, starty, endx, endy;
+
+ if(ref < 0)
+ continue;
+
+ if (get_roughness(mcfps, dir, mbx, mby) > 32)
+ continue;
+
+ av_assert0(ref == 0);
+ startx = (mbx<<LOG2_MB_SIZE) - (1<<LOG2_MB_SIZE)/2 + (mvx)*a/(ALPHA_MAX<<mcfps->log2_mv_precision);
+ starty = (mby<<LOG2_MB_SIZE) - (1<<LOG2_MB_SIZE)/2 + (mvy)*a/(ALPHA_MAX<<mcfps->log2_mv_precision);
+ endx = startx + (2<<LOG2_MB_SIZE);
+ endy = starty + (2<<LOG2_MB_SIZE);
+
+ startx = av_clip(startx, 0, w-1);
+ starty = av_clip(starty, 0, h-1);
+ endx = av_clip(endx, 0, w-1);
+ endy = av_clip(endy, 0, h-1);
+
+ if (dir) {
+ mvx = -mvx;
+ mvy = -mvy;
+ }
+
+ for (y = starty; y<endy; y++) {
+ int ymin = -y <<mcfps->log2_mv_precision;
+ int ymax = (h-y-1)<<mcfps->log2_mv_precision;
+ for (x = startx; x<endx; x++) {
+ int xmin = -x <<mcfps->log2_mv_precision;
+ int xmax = (w-x-1)<<mcfps->log2_mv_precision;
+ int obmc_weight = obmc_tab[4-LOG2_MB_SIZE][(x-startx) + ((y-starty)<<(1+LOG2_MB_SIZE))];
+ Pixel *p = &mcfps->pixel[x + y*w];
+ if (p->nb + 1 >= NB_MVS) //FIXME discrad the vector of lowest weight
+ continue;
+ p->ref[p->nb] = 1;
+ p->weight[p->nb] = obmc_weight * (ALPHA_MAX-alpha);
+ p->mv[p->nb][0] = av_clip((mvx * alpha) / ALPHA_MAX, xmin, xmax);
+ p->mv[p->nb][1] = av_clip((mvy * alpha) / ALPHA_MAX, ymin, ymax);
+ p->nb ++;
+
+ p->ref[p->nb] = 2;
+ p->weight[p->nb] = obmc_weight * alpha;
+ p->mv[p->nb][0] = av_clip(-(mvx * (ALPHA_MAX-alpha)) / ALPHA_MAX, xmin, xmax);
+ p->mv[p->nb][1] = av_clip(-(mvy * (ALPHA_MAX-alpha)) / ALPHA_MAX, ymin, ymax);
+ p->nb ++;
+ }
+ }
+ }
+ }
+ }
+}
+
+// this should be optimized but do not do premature optims, first find out what is best
+static int mc_sample(MCFPSContext *mcfps, Pixel *pixel, int plane, int x, int y, int i)
+{
+ int ref = pixel->ref[i];
+ int is_chroma = plane == 1 || plane == 2;
+ int mvx = pixel->mv[i][0] << (4 - mcfps->log2_mv_precision - is_chroma); // 1/16pel precision
+ int mvy = pixel->mv[i][1] << (4 - mcfps->log2_mv_precision - is_chroma); // 1/16pel precision
+ int linesize;
+ uint8_t *p0, *p1, *p2, *p3;
+ int mvxfull, mvyfull, mvxsub, mvysub;
+ InputFrame *inpf;
+ av_assert0(ref >= 0 && ref <4);
+ inpf = &mcfps->input[ref];
+
+ linesize = inpf->halfpel_linesize[plane];
+
+ mvxfull = x + (mvx >> 4);
+ mvyfull = y + (mvy >> 4);
+ mvxsub = mvx & 7;
+ mvysub = mvy & 7;
+
+ p0 = inpf->halfpel[plane][0] + mvxfull + mvyfull*linesize;
+ p1 = inpf->halfpel[plane][1] + mvxfull + mvyfull*linesize;
+ p2 = inpf->halfpel[plane][2] + mvxfull + mvyfull*linesize;
+ p3 = inpf->halfpel[plane][3] + mvxfull + mvyfull*linesize;
+
+ if (mvx & 8) {
+ p0 += 1;
+ p2 += 1;
+ mvxsub = 8-mvxsub;
+ }
+ if (mvy & 8) {
+ p0 += linesize;
+ p1 += linesize;
+ mvysub = 8-mvysub;
+ }
+
+ return ( (8-mvysub)*((8-mvxsub)*p0[0] + (mvxsub)*p1[0])
+ + (mvysub)*((8-mvxsub)*p2[0] + (mvxsub)*p3[0]) + 32) >> 6;
+}
+
+static void interpolate_pixels(MCFPSContext *mcfps, AVFrame *out)
+{
+ int x, y, plane;
+
+ for (plane=0; plane<mcfps->planes; plane++){
+ int w = out->width;
+ int h = out->height;
+
+ if (plane == 1 || plane == 2) {
+ w = FF_CEIL_RSHIFT(w, mcfps->chroma_h_shift);
+ h = FF_CEIL_RSHIFT(h, mcfps->chroma_v_shift);
+ }
+
+ for (y=0; y<h; y++) {
+ for (x=0; x<w; x++) {
+ int i;
+ int weight_sum = 0;
+ int v = 0;
+ Pixel *pixel;
+ if (plane == 1 || plane == 2) //FIXME optimize
+ pixel = &mcfps->pixel[(x<<mcfps->chroma_h_shift) + (y<<mcfps->chroma_v_shift)*out->width];
+ else
+ pixel = &mcfps->pixel[x + y*out->width];
+
+ for(i=0; i<pixel->nb; i++) {
+ weight_sum += pixel->weight[i];
+ }
+ if(!weight_sum)
+ weight_sum = 1;
+ for(i=0; i<pixel->nb; i++) {
+ int t = mc_sample(mcfps, pixel, plane, x, y, i);
+ v += t * pixel->weight[i];
+ }
+ out->data[plane][ x + y*out->linesize[plane] ] =
+ ROUNDED_DIV(v, weight_sum);
+ }
+ }
+ }
+}
+
+static void interpolate(AVFilterContext *ctx, AVFrame *out)
+{
+ AVFilterLink *inlink = ctx->inputs[0];
+ AVFilterLink *outlink = ctx->outputs[0];
+ MCFPSContext *mcfps = ctx->priv;
+ int64_t pts;
+ AVFrame *frame;
+ int plane, alpha;
+ pts = av_rescale(out->pts,
+ outlink->time_base.num * (int64_t)ALPHA_MAX * inlink->time_base.den,
+ outlink->time_base.den * (int64_t) inlink->time_base.num
+ );
+ alpha = (pts - mcfps->input[1].f->pts*ALPHA_MAX)/ (mcfps->input[2].f->pts - mcfps->input[1].f->pts);
+ alpha = av_clip(alpha, 0, ALPHA_MAX);
+
+ switch(mcfps->mode) {
+ case MCFPS_MODE_NN:
+ pts = av_rescale_q(out->pts, outlink->time_base, inlink->time_base);
+ if (FFABS(pts - mcfps->input[1].f->pts) < FFABS(pts - mcfps->input[2].f->pts)) {
+ frame = mcfps->input[1].f;
+ } else
+ frame = mcfps->input[2].f;
+
+ av_frame_copy(out, frame);
+
+ break;
+ case MCFPS_MODE_LINEAR_IPOL:
+ for (plane=0; plane < mcfps->planes; plane++) {
+ int x, y;
+ int w = out->width;
+ int h = out->height;
+
+ if (plane == 1 || plane == 2) {
+ w = FF_CEIL_RSHIFT(w, mcfps->chroma_h_shift);
+ h = FF_CEIL_RSHIFT(h, mcfps->chroma_v_shift);
+ }
+
+ for (y=0; y<h; y++) {
+ for (x=0; x<w; x++) {
+ out->data[plane][ x + y*out->linesize[plane] ] =
+ ((ALPHA_MAX - alpha)*mcfps->input[1].f->data[plane][ x + y*mcfps->input[1].f->linesize[plane] ] +
+ alpha *mcfps->input[2].f->data[plane][ x + y*mcfps->input[2].f->linesize[plane] ] + 512) >> 10;
+ }
+ }
+ }
+
+ break;
+ case MCFPS_MODE_OBMC:
+ fill_pixels(mcfps, alpha);
+ interpolate_pixels(mcfps, out);
+ break;
+ }
+}
+
+static int fill_halfpel(MCFPSContext *mcfps, InputFrame *inpf)
+{
+ int x, y, p;
+ int j;
+
+ for (p=0; p<mcfps->planes; p++) {
+ int w = inpf->f->width;
+ int h = inpf->f->height;
+ int linesize = inpf->f->linesize[p];
+ uint8_t *data = inpf->f->data[p];
+ int hlinesize;
+
+ if (p == 1 || p == 2) {
+ w = FF_CEIL_RSHIFT(w, mcfps->chroma_h_shift);
+ h = FF_CEIL_RSHIFT(h, mcfps->chroma_v_shift);
+ }
+
+ if (!inpf->halfpel_linesize[p])
+ inpf->halfpel_linesize[p] = FFALIGN(w+1, 16);
+ hlinesize = inpf->halfpel_linesize[p];
+
+ for (j=0; j<4; j++)
+ if (!inpf->halfpel[p][j]) {
+ if (!(inpf->halfpel[p][j] = av_malloc(hlinesize * (h+1)))) {
+ return AVERROR(ENOMEM);
+ }
+ }
+// 1 -5 20
+ for (x=0; x<w+1; x++) {
+ int x1 = x < w ? x : (w-1);
+ int a = data[x1 + 1*linesize];
+ int b = data[x1 + 0*linesize];
+ int c = data[x1 + 0*linesize];
+ int d = data[x1 + 1*linesize];
+ int e = data[x1 + 2*linesize];
+ int f;
+ for (y=0; y<h+1; y++) {
+ int y1 = y + 3;
+ if (y1 >= h)
+ y1 = 2*h - y1 - 1;
+ f = data[y1*linesize + x1];
+ a = (20*(c+d) - 5*(b+e) + (a+f) + 16) >> 5;
+ if (a & ~255)
+ a = ~(a>>31);
+ inpf->halfpel[p][2][x + y*hlinesize] = a;
+ a=b; b=c; c=d; d=e; e=f;
+ }
+ }
+
+ for (y=0; y<h+1; y++) {
+ int y1 = y < h ? y : (h-1);
+ int a = data[y1*linesize + 1];
+ int b = data[y1*linesize + 0];
+ int c = data[y1*linesize + 0];
+ int d = data[y1*linesize + 1];
+ int e = data[y1*linesize + 2];
+ int f;
+ memcpy(inpf->halfpel[p][0] + y*hlinesize, data + y1*linesize, w);
+ for (x=0; x<w+1; x++) {
+ int x1 = x + 3;
+ if (x1 >= w)
+ x1 = 2*w - x1 - 1;
+ f = data[y1*linesize + x1];
+ a = (20*(c+d) - 5*(b+e) + (a+f) + 16) >> 5;
+ if (a & ~255)
+ a = ~(a>>31);
+ inpf->halfpel[p][1][x + y*hlinesize] = a;
+ a=b; b=c; c=d; d=e; e=f;
+ }
+ a = inpf->halfpel[p][2][y*hlinesize + 1];
+ b = inpf->halfpel[p][2][y*hlinesize + 0];
+ c = inpf->halfpel[p][2][y*hlinesize + 0];
+ d = inpf->halfpel[p][2][y*hlinesize + 1];
+ e = inpf->halfpel[p][2][y*hlinesize + 2];
+ for (x=0; x<w+1; x++) {
+ int x1 = x + 3;
+ if (x1 >= w)
+ x1 = 2*w - x1 - 1;
+ f = inpf->halfpel[p][2][y*hlinesize + x1];
+ a = (20*(c+d) - 5*(b+e) + (a+f) + 16) >> 5;
+ if (a & ~255)
+ a = ~(a>>31);
+ inpf->halfpel[p][3][x + y*hlinesize] = a;
+ a=b; b=c; c=d; d=e; e=f;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int extract_mvs(MCFPSContext *mcfps, InputFrame *f, int dir)
+{
+ if (!f->mv[dir])
+ f->mv[dir] = av_malloc(mcfps->b_width * mcfps->b_height * sizeof(*f->mv[0]));
+ if (!f->ref[dir])
+ f->ref[dir] = av_malloc(mcfps->b_width * mcfps->b_height * sizeof(*f->ref[0]));
+ if (!f->mv[dir] || !f->ref[0])
+ return AVERROR(ENOMEM);
+
+ return av_me_get_mvs(mcfps->avme[dir],
+ f->mv[dir],
+ f->ref[dir],
+ mcfps->b_width,
+ mcfps->b_height);
+}
+
+static int inject_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+ AVFilterContext *ctx = inlink->dst;
+ MCFPSContext *mcfps = ctx->priv;
+ int ret;
+ InputFrame tmp;
+
+ av_frame_free(&mcfps->input[0].f);
+ tmp = mcfps->input[0];
+ memmove(&mcfps->input[0], &mcfps->input[1], sizeof(mcfps->input[0]) * (NB_INPUT_FRAMES-1));
+ mcfps->input[NB_INPUT_FRAMES-1] = tmp;
+ mcfps->input[NB_INPUT_FRAMES-1].f = frame;
+
+ if (mcfps->mode > MCFPS_MODE_LINEAR_IPOL) {
+ ret = fill_halfpel(mcfps, &mcfps->input[NB_INPUT_FRAMES-1]);
+ if (ret < 0)
+ return ret;
+
+ frame->quality = 2 * FF_QP2LAMBDA; //FIXME adjust
+ // init per MB qscale stuff FIXME
+
+ av_me_add_frame(mcfps->avme[0], frame, 0);
+
+ if (mcfps->input[NB_INPUT_FRAMES-2].f) {
+ av_me_add_frame(mcfps->avme[1], frame, 1);
+ av_me_add_frame(mcfps->avme[1], mcfps->input[NB_INPUT_FRAMES-2].f, 0);
+
+ ret = extract_mvs(mcfps, &mcfps->input[NB_INPUT_FRAMES-2], 1);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = extract_mvs(mcfps, &mcfps->input[NB_INPUT_FRAMES-1], 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+ AVFilterContext *ctx = inlink->dst;
+ AVFilterLink *outlink = ctx->outputs[0];
+ MCFPSContext *mcfps = ctx->priv;
+ int ret;
+
+ mcfps->planes = av_pix_fmt_count_planes(frame->format);
+ avcodec_get_chroma_sub_sample(frame->format, &mcfps->chroma_h_shift, &mcfps->chroma_v_shift);
+
+ av_assert0(frame->pts != AV_NOPTS_VALUE); //FIXME
+
+ if (!mcfps->input[NB_INPUT_FRAMES-1].f ||
+ frame->pts < mcfps->input[NB_INPUT_FRAMES-1].f->pts) {
+ av_log(ctx, AV_LOG_VERBOSE, "Initializing outpts from input pts %"PRId64"\n",
+ frame->pts);
+ mcfps->out_pts = av_rescale_q(frame->pts, inlink->time_base, outlink->time_base);
+ }
+
+ if (!mcfps->input[NB_INPUT_FRAMES-1].f)
+ inject_frame(inlink, av_frame_clone(frame));
+ inject_frame(inlink, frame);
+
+ if (!mcfps->input[0].f)
+ return 0;
+
+ for (;;) {
+ AVFrame *out;
+
+ if (av_compare_ts(mcfps->input[NB_INPUT_FRAMES/2].f->pts, inlink->time_base,
+ mcfps->out_pts, outlink->time_base) < 0)
+ break;
+
+ out = ff_get_video_buffer(ctx->outputs[0], inlink->w, inlink->h);
+ if (!out)
+ return AVERROR(ENOMEM);
+ av_frame_copy_props(out, mcfps->input[NB_INPUT_FRAMES/2].f);
+ out->pts = mcfps->out_pts;
+ mcfps->out_pts ++;
+
+ interpolate(ctx, out);
+
+ ret = ff_filter_frame(ctx->outputs[0], out);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+#define OFFSET(x) offsetof(MCFPSContext, x)
+#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
+
+#define CONST(name, help, val, unit) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, INT_MIN, INT_MAX, FLAGS, unit }
+
+static const AVOption mcfps_options[] = {
+ { "mode", "specify the interpolation mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = MCFPS_MODE_NN}, 0, 3, FLAGS, "mode"},
+ CONST("nn", "", MCFPS_MODE_NN, "mode"),
+ CONST("linear", "", MCFPS_MODE_LINEAR_IPOL, "mode"),
+ CONST("gmc", "", MCFPS_MODE_GMC, "mode"),
+ CONST("obmc", "", MCFPS_MODE_OBMC, "mode"),
+
+ { "fps", "specify the frame rate", OFFSET(frame_rate), AV_OPT_TYPE_RATIONAL, {.dbl = 25}, 0, INT_MAX, FLAGS},
+
+
+ { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(mcfps);
+
+static const AVFilterPad avfilter_vf_mcfps_inputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .filter_frame = filter_frame,
+ },
+ { NULL }
+};
+
+static const AVFilterPad avfilter_vf_mcfps_outputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .config_props = config_output,
+ },
+ { NULL }
+};
+
+AVFilter ff_vf_mcfps = {
+ .name = "mcfps",
+ .description = NULL_IF_CONFIG_SMALL("Frame rate changing with motion compensated interpolation"),
+ .priv_size = sizeof(MCFPSContext),
+ .priv_class = &mcfps_class,
+ .uninit = uninit,
+ .query_formats = query_formats,
+ .inputs = avfilter_vf_mcfps_inputs,
+ .outputs = avfilter_vf_mcfps_outputs,
+ .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
+};
--
1.7.9.5
More information about the ffmpeg-devel
mailing list