Skip to content

Commit

Permalink
lavfi: port decimate libmpcodecs filter
Browse files Browse the repository at this point in the history
This filter is based on the MPlayer decimate filter by Rich Felker.
  • Loading branch information
saste committed Aug 31, 2012
1 parent 6c01078 commit 9dd3d6c
Show file tree
Hide file tree
Showing 7 changed files with 312 additions and 2 deletions.
1 change: 1 addition & 0 deletions Changelog
Expand Up @@ -58,6 +58,7 @@ version next:
instead of AV_CODEC_ID_TEXT instead of AV_CODEC_ID_TEXT
- smartblur filter ported from MPlayer - smartblur filter ported from MPlayer
- CPiA decoder - CPiA decoder
- decimate filter ported from MPlayer




version 0.11: version 0.11:
Expand Down
1 change: 1 addition & 0 deletions configure
Expand Up @@ -1889,6 +1889,7 @@ blackframe_filter_deps="gpl"
boxblur_filter_deps="gpl" boxblur_filter_deps="gpl"
colormatrix_filter_deps="gpl" colormatrix_filter_deps="gpl"
cropdetect_filter_deps="gpl" cropdetect_filter_deps="gpl"
decimate_filter_deps="gpl"
delogo_filter_deps="gpl" delogo_filter_deps="gpl"
deshake_filter_deps="avcodec" deshake_filter_deps="avcodec"
drawtext_filter_deps="libfreetype" drawtext_filter_deps="libfreetype"
Expand Down
37 changes: 37 additions & 0 deletions doc/filters.texi
Expand Up @@ -1539,6 +1539,43 @@ indicates never reset and return the largest area encountered during
playback. playback.
@end table @end table


@section decimate

This filter drops frames that do not differ greatly from the previous
frame in order to reduce framerate. The main use of this filter is
for very-low-bitrate encoding (e.g. streaming over dialup modem), but
it could in theory be used for fixing movies that were
inverse-telecined incorrectly.

It accepts the following parameters:
@var{max}:@var{hi}:@var{lo}:@var{frac}.

@table @option

@item max
Set the maximum number of consecutive frames which can be dropped (if
positive), or the minimum interval between dropped frames (if
negative). If the value is 0, the frame is dropped unregarding the
number of previous sequentially dropped frames.

Default value is 0.

@item hi, lo, frac
Set the dropping threshold values.

Values for @var{hi} and @var{lo} are for 8x8 pixel blocks and
represent actual pixel value differences, so a threshold of 64
corresponds to 1 unit of difference for each pixel, or the same spread
out differently over the block.

A frame is a candidate for dropping if no 8x8 blocks differ by more
than a threshold of @var{hi}, and if no more than @var{frac} blocks (1
meaning the whole image) differ by more than a threshold of @var{lo}.

Default value for @var{hi} is 64*12, default value for @var{lo} is
64*5, and default value for @var{frac} is 0.33.
@end table

@section delogo @section delogo


Suppress a TV station logo by a simple interpolation of the surrounding Suppress a TV station logo by a simple interpolation of the surrounding
Expand Down
2 changes: 2 additions & 0 deletions libavfilter/Makefile
Expand Up @@ -10,6 +10,7 @@ FFLIBS-$(CONFIG_ACONVERT_FILTER) += swresample
FFLIBS-$(CONFIG_AMOVIE_FILTER) += avformat avcodec FFLIBS-$(CONFIG_AMOVIE_FILTER) += avformat avcodec
FFLIBS-$(CONFIG_ARESAMPLE_FILTER) += swresample FFLIBS-$(CONFIG_ARESAMPLE_FILTER) += swresample
FFLIBS-$(CONFIG_ATEMPO_FILTER) += avcodec FFLIBS-$(CONFIG_ATEMPO_FILTER) += avcodec
FFLIBS-$(CONFIG_DECIMATE_FILTER) += avcodec
FFLIBS-$(CONFIG_MOVIE_FILTER) += avformat avcodec FFLIBS-$(CONFIG_MOVIE_FILTER) += avformat avcodec
FFLIBS-$(CONFIG_PAN_FILTER) += swresample FFLIBS-$(CONFIG_PAN_FILTER) += swresample
FFLIBS-$(CONFIG_REMOVELOGO_FILTER) += avformat avcodec FFLIBS-$(CONFIG_REMOVELOGO_FILTER) += avformat avcodec
Expand Down Expand Up @@ -88,6 +89,7 @@ OBJS-$(CONFIG_COLORMATRIX_FILTER) += vf_colormatrix.o
OBJS-$(CONFIG_COPY_FILTER) += vf_copy.o OBJS-$(CONFIG_COPY_FILTER) += vf_copy.o
OBJS-$(CONFIG_CROP_FILTER) += vf_crop.o OBJS-$(CONFIG_CROP_FILTER) += vf_crop.o
OBJS-$(CONFIG_CROPDETECT_FILTER) += vf_cropdetect.o OBJS-$(CONFIG_CROPDETECT_FILTER) += vf_cropdetect.o
OBJS-$(CONFIG_DECIMATE_FILTER) += vf_decimate.o
OBJS-$(CONFIG_DELOGO_FILTER) += vf_delogo.o OBJS-$(CONFIG_DELOGO_FILTER) += vf_delogo.o
OBJS-$(CONFIG_DESHAKE_FILTER) += vf_deshake.o OBJS-$(CONFIG_DESHAKE_FILTER) += vf_deshake.o
OBJS-$(CONFIG_DRAWBOX_FILTER) += vf_drawbox.o OBJS-$(CONFIG_DRAWBOX_FILTER) += vf_drawbox.o
Expand Down
1 change: 1 addition & 0 deletions libavfilter/allfilters.c
Expand Up @@ -78,6 +78,7 @@ void avfilter_register_all(void)
REGISTER_FILTER (COPY, copy, vf); REGISTER_FILTER (COPY, copy, vf);
REGISTER_FILTER (CROP, crop, vf); REGISTER_FILTER (CROP, crop, vf);
REGISTER_FILTER (CROPDETECT, cropdetect, vf); REGISTER_FILTER (CROPDETECT, cropdetect, vf);
REGISTER_FILTER (DECIMATE, decimate, vf);
REGISTER_FILTER (DELOGO, delogo, vf); REGISTER_FILTER (DELOGO, delogo, vf);
REGISTER_FILTER (DESHAKE, deshake, vf); REGISTER_FILTER (DESHAKE, deshake, vf);
REGISTER_FILTER (DRAWBOX, drawbox, vf); REGISTER_FILTER (DRAWBOX, drawbox, vf);
Expand Down
4 changes: 2 additions & 2 deletions libavfilter/version.h
Expand Up @@ -29,8 +29,8 @@
#include "libavutil/avutil.h" #include "libavutil/avutil.h"


#define LIBAVFILTER_VERSION_MAJOR 3 #define LIBAVFILTER_VERSION_MAJOR 3
#define LIBAVFILTER_VERSION_MINOR 14 #define LIBAVFILTER_VERSION_MINOR 15
#define LIBAVFILTER_VERSION_MICRO 101 #define LIBAVFILTER_VERSION_MICRO 100


#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
LIBAVFILTER_VERSION_MINOR, \ LIBAVFILTER_VERSION_MINOR, \
Expand Down
268 changes: 268 additions & 0 deletions libavfilter/vf_decimate.c
@@ -0,0 +1,268 @@
/*
* Copyright (c) 2003 Rich Felker
* Copyright (c) 2012 Stefano Sabatini
*
* 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.
*/

/**
* @file decimate filter, ported from libmpcodecs/vf_decimate.c by
* Rich Felker.
*/

#include "libavutil/pixdesc.h"
#include "libavutil/timestamp.h"
#include "libavcodec/dsputil.h"
#include "avfilter.h"
#include "internal.h"
#include "formats.h"
#include "video.h"

typedef struct {
int lo, hi; ///< lower and higher threshold number of differences
///< values for 8x8 blocks

float frac; ///< threshold of changed pixels over the total fraction

int max_drop_count; ///< if positive: maximum number of sequential frames to drop
///< if negative: minimum number of frames between two drops

int drop_count; ///< if positive: number of frames sequentially dropped
///< if negative: number of sequential frames which were not dropped

int hsub, vsub; ///< chroma subsampling values
AVFilterBufferRef *ref; ///< reference picture
DSPContext dspctx; ///< context providing optimized diff routines
AVCodecContext *avctx; ///< codec context required for the DSPContext
} DecimateContext;

/**
* Return 1 if the two planes are different, 0 otherwise.
*/
static int diff_planes(AVFilterContext *ctx,
uint8_t *cur, uint8_t *ref, int linesize,
int w, int h)
{
DecimateContext *decimate = ctx->priv;
DSPContext *dspctx = &decimate->dspctx;

int x, y;
int d, c = 0;
int t = (w/16)*(h/16)*decimate->frac;
DCTELEM block[8*8];

/* compute difference for blocks of 8x8 bytes */
for (y = 0; y < h-7; y += 4) {
for (x = 8; x < w-7; x += 4) {
dspctx->diff_pixels(block,
cur+x+y*linesize,
ref+x+y*linesize, linesize);
d = dspctx->sum_abs_dctelem(block);
if (d > decimate->hi)
return 1;
if (d > decimate->lo) {
c++;
if (c > t)
return 1;
}
}
}
return 0;
}

/**
* Tell if the frame should be decimated, for example if it is no much
* different with respect to the reference frame ref.
*/
static int decimate_frame(AVFilterContext *ctx,
AVFilterBufferRef *cur, AVFilterBufferRef *ref)
{
DecimateContext *decimate = ctx->priv;
int plane;

if (decimate->max_drop_count > 0 &&
decimate->drop_count >= decimate->max_drop_count)
return 0;
if (decimate->max_drop_count < 0 &&
(decimate->drop_count-1) > decimate->max_drop_count)
return 0;

for (plane = 0; ref->data[plane] && ref->linesize[plane]; plane++) {
int vsub = plane == 1 || plane == 2 ? decimate->vsub : 0;
int hsub = plane == 1 || plane == 2 ? decimate->hsub : 0;
if (diff_planes(ctx,
cur->data[plane], ref->data[plane], ref->linesize[plane],
ref->video->w>>hsub, ref->video->h>>vsub))
return 0;
}

return 1;
}

static av_cold int init(AVFilterContext *ctx, const char *args)
{
DecimateContext *decimate = ctx->priv;

/* set default values */
decimate->drop_count = decimate->max_drop_count = 0;
decimate->lo = 64*5;
decimate->hi = 64*12;
decimate->frac = 0.33;

if (args) {
char c1, c2, c3, c4;
int n = sscanf(args, "%d%c%d%c%d%c%f%c",
&decimate->max_drop_count, &c1,
&decimate->hi, &c2, &decimate->lo, &c3,
&decimate->frac, &c4);
if (n != 1 &&
(n != 3 || c1 != ':') &&
(n != 5 || c1 != ':' || c2 != ':') &&
(n != 7 || c1 != ':' || c2 != ':' || c3 != ':')) {
av_log(ctx, AV_LOG_ERROR,
"Invalid syntax for argument '%s': "
"must be in the form 'max:hi:lo:frac'\n", args);
return AVERROR(EINVAL);
}
}

av_log(ctx, AV_LOG_VERBOSE, "max_drop_count:%d hi:%d lo:%d frac:%f\n",
decimate->max_drop_count, decimate->hi, decimate->lo, decimate->frac);

decimate->avctx = avcodec_alloc_context3(NULL);
if (!decimate->avctx)
return AVERROR(ENOMEM);
dsputil_init(&decimate->dspctx, decimate->avctx);

return 0;
}

static av_cold void uninit(AVFilterContext *ctx)
{
DecimateContext *decimate = ctx->priv;
avfilter_unref_bufferp(&decimate->ref);
avcodec_close(decimate->avctx);
av_freep(&decimate->avctx);
}

static int query_formats(AVFilterContext *ctx)
{
static const enum PixelFormat pix_fmts[] = {
PIX_FMT_YUV444P, PIX_FMT_YUV422P,
PIX_FMT_YUV420P, PIX_FMT_YUV411P,
PIX_FMT_YUV410P, PIX_FMT_YUV440P,
PIX_FMT_YUVJ444P, PIX_FMT_YUVJ422P,
PIX_FMT_YUVJ420P, PIX_FMT_YUVJ440P,
PIX_FMT_YUVA420P,
PIX_FMT_NONE
};

ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));

return 0;
}

static int config_input(AVFilterLink *inlink)
{
AVFilterContext *ctx = inlink->dst;
DecimateContext *decimate = ctx->priv;
const AVPixFmtDescriptor *pix_desc = &av_pix_fmt_descriptors[inlink->format];
decimate->hsub = pix_desc->log2_chroma_w;
decimate->vsub = pix_desc->log2_chroma_h;

return 0;
}

static int start_frame(AVFilterLink *inlink, AVFilterBufferRef *picref) { return 0; }

static int draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir) { return 0; }

static int end_frame(AVFilterLink *inlink)
{
DecimateContext *decimate = inlink->dst->priv;
AVFilterBufferRef *cur = inlink->cur_buf;
AVFilterLink *outlink = inlink->dst->outputs[0];
int ret;

if (decimate->ref && decimate_frame(inlink->dst, cur, decimate->ref)) {
decimate->drop_count = FFMAX(1, decimate->drop_count+1);
} else {
avfilter_unref_buffer(decimate->ref);
decimate->ref = cur;
inlink->cur_buf = NULL;
decimate->drop_count = FFMIN(-1, decimate->drop_count-1);

if ((ret = ff_start_frame(outlink,
avfilter_ref_buffer(cur, ~AV_PERM_WRITE)) < 0) ||
(ret = ff_draw_slice(outlink, 0, inlink->h, 1)) < 0 ||
(ret = ff_end_frame(outlink)) < 0)
return ret;
}

av_log(inlink->dst, AV_LOG_DEBUG,
"%s pts:%s pts_time:%s drop_count:%d\n",
decimate->drop_count > 0 ? "drop" : "keep",
av_ts2str(cur->pts), av_ts2timestr(cur->pts, &inlink->time_base),
decimate->drop_count);

return 0;
}

static int request_frame(AVFilterLink *outlink)
{
DecimateContext *decimate = outlink->src->priv;
AVFilterLink *inlink = outlink->src->inputs[0];
int ret;

do {
ret = ff_request_frame(inlink);
} while (decimate->drop_count > 0 && ret >= 0);

return ret;
}

AVFilter avfilter_vf_decimate = {
.name = "decimate",
.description = NULL_IF_CONFIG_SMALL("Remove near-duplicate frames."),
.init = init,
.uninit = uninit,

.priv_size = sizeof(DecimateContext),
.query_formats = query_formats,

.inputs = (const AVFilterPad[]) {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.get_video_buffer = ff_null_get_video_buffer,
.config_props = config_input,
.start_frame = start_frame,
.draw_slice = draw_slice,
.end_frame = end_frame,
.min_perms = AV_PERM_READ | AV_PERM_PRESERVE,
},
{ .name = NULL }
},
.outputs = (const AVFilterPad[]) {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.request_frame = request_frame,
},
{ .name = NULL }
},
};

0 comments on commit 9dd3d6c

Please sign in to comment.