From 983a7e16eada19bfeb423ae64e2de7dbadd45497 Mon Sep 17 00:00:00 2001 From: Matthias Reichl Date: Sat, 28 Mar 2026 12:25:00 +0100 Subject: [PATCH] add sourcepluin-libpostproc from michaelni Rebased version of https://github.com/michaelni/FFmpeg/tree/sourceplugin-libpostproc-8.0 Signed-off-by: Matthias Reichl --- LICENSE.md | 2 + MAINTAINERS | 2 + Makefile | 2 + configure | 13 +- doc/filters.texi | 175 ++ fftools/ffprobe.c | 3 + fftools/opt_common.c | 3 + libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/vf_pp.c | 191 ++ libavutil/avutil.h | 1 + libpostproc/Makefile | 23 + libpostproc/libpostproc.v | 7 + libpostproc/postprocess.c | 992 ++++++ libpostproc/postprocess.h | 109 + libpostproc/postprocess_altivec_template.c | 1214 ++++++++ libpostproc/postprocess_internal.h | 185 ++ libpostproc/postprocess_template.c | 3293 ++++++++++++++++++++ libpostproc/postprocres.rc | 55 + libpostproc/tests/.gitignore | 3 + libpostproc/tests/blocktest.c | 113 + libpostproc/tests/stripetest.c | 115 + libpostproc/tests/temptest.c | 106 + libpostproc/tests/test_utils.c | 38 + libpostproc/tests/test_utils.h | 27 + libpostproc/version.c | 45 + libpostproc/version.h | 48 + libpostproc/version_major.h | 33 + tests/Makefile | 1 + tests/fate/filter-video.mak | 16 + tests/fate/libpostproc.mak | 14 + tests/ref/fate/blocktest | 1800 +++++++++++ tests/ref/fate/filter-pp | 10 + tests/ref/fate/filter-pp1 | 1 + tests/ref/fate/filter-pp2 | 1 + tests/ref/fate/filter-pp3 | 1 + tests/ref/fate/filter-pp4 | 1 + tests/ref/fate/filter-pp5 | 1 + tests/ref/fate/filter-pp6 | 1 + tests/ref/fate/filter-qp | 1 + tests/ref/fate/stripetest | 360 +++ tests/ref/fate/temptest | 336 ++ tools/gen-rc | 1 + 43 files changed, 9344 insertions(+), 1 deletion(-) create mode 100644 libavfilter/vf_pp.c create mode 100644 libpostproc/Makefile create mode 100644 libpostproc/libpostproc.v create mode 100644 libpostproc/postprocess.c create mode 100644 libpostproc/postprocess.h create mode 100644 libpostproc/postprocess_altivec_template.c create mode 100644 libpostproc/postprocess_internal.h create mode 100644 libpostproc/postprocess_template.c create mode 100644 libpostproc/postprocres.rc create mode 100644 libpostproc/tests/.gitignore create mode 100644 libpostproc/tests/blocktest.c create mode 100644 libpostproc/tests/stripetest.c create mode 100644 libpostproc/tests/temptest.c create mode 100644 libpostproc/tests/test_utils.c create mode 100644 libpostproc/tests/test_utils.h create mode 100644 libpostproc/version.c create mode 100644 libpostproc/version.h create mode 100644 libpostproc/version_major.h create mode 100644 tests/fate/libpostproc.mak create mode 100644 tests/ref/fate/blocktest create mode 100644 tests/ref/fate/filter-pp create mode 100644 tests/ref/fate/filter-pp1 create mode 100644 tests/ref/fate/filter-pp2 create mode 100644 tests/ref/fate/filter-pp3 create mode 100644 tests/ref/fate/filter-pp4 create mode 100644 tests/ref/fate/filter-pp5 create mode 100644 tests/ref/fate/filter-pp6 create mode 100644 tests/ref/fate/filter-qp create mode 100644 tests/ref/fate/stripetest create mode 100644 tests/ref/fate/temptest --- a/LICENSE.md +++ b/LICENSE.md @@ -12,6 +12,7 @@ configure to activate them. In this case Specifically, the GPL parts of FFmpeg are: +- libpostproc - optional x86 optimization in the files - `libavcodec/x86/flac_dsp_gpl.asm` - `libavcodec/x86/idct_mmx.c` @@ -44,6 +45,7 @@ Specifically, the GPL parts of FFmpeg ar - `vf_owdenoise.c` - `vf_perspective.c` - `vf_phase.c` + - `vf_pp.c` - `vf_pp7.c` - `vf_pullup.c` - `vf_repeatfields.c` --- a/MAINTAINERS +++ b/MAINTAINERS @@ -139,6 +139,8 @@ Generic Parts: ratecontrol.c [2] Michael Niedermayer simple IDCT: simple_idct.c, simple_idct.h [2] Michael Niedermayer + postprocessing: + libpostproc/* [2] Michael Niedermayer table generation: tableprint.c, tableprint.h Reimar Doeffinger fixed point FFT: --- a/Makefile +++ b/Makefile @@ -27,6 +27,7 @@ ALLFFLIBS = \ avfilter \ avformat \ avutil \ + postproc \ swscale \ swresample \ @@ -35,6 +36,7 @@ FFLIBS-$(CONFIG_AVDEVICE) += avdevice FFLIBS-$(CONFIG_AVFILTER) += avfilter FFLIBS-$(CONFIG_AVFORMAT) += avformat FFLIBS-$(CONFIG_AVCODEC) += avcodec +FFLIBS-$(CONFIG_POSTPROC) += postproc FFLIBS-$(CONFIG_SWRESAMPLE) += swresample FFLIBS-$(CONFIG_SWSCALE) += swscale --- a/configure +++ b/configure @@ -132,6 +132,7 @@ Component options: --disable-avformat disable libavformat build --disable-swresample disable libswresample build --disable-swscale disable libswscale build + --disable-postproc disable libpostproc build --disable-avfilter disable libavfilter build --disable-pthreads disable pthreads [autodetect] --disable-w32threads disable Win32 threads [autodetect] @@ -2229,6 +2230,7 @@ LIBRARY_LIST=" avdevice avfilter swscale + postproc avformat avcodec swresample @@ -4242,6 +4244,7 @@ pan_filter_deps="swresample" perspective_filter_deps="gpl" phase_filter_deps="gpl" pp7_filter_deps="gpl" +pp_filter_deps="gpl postproc" prewitt_opencl_filter_deps="opencl" procamp_vaapi_filter_deps="vaapi" program_opencl_filter_deps="opencl" @@ -4363,6 +4366,8 @@ avfilter_suggest="libm stdatomic zlib" avformat_deps="avcodec avutil" avformat_suggest="libm network zlib stdatomic" avutil_suggest="amf clock_gettime ffnvcodec gcrypt libm zlib libdrm libmfx opencl openssl user32 vaapi vulkan videotoolbox corefoundation corevideo coremedia bcrypt stdatomic" +postproc_deps="avutil gpl" +postproc_suggest="libm stdatomic" swresample_deps="avutil" swresample_suggest="libm libsoxr stdatomic" swscale_deps="avutil" @@ -7992,7 +7997,7 @@ void (^block)(void); EOF # add some linker flags -check_ldflags -Wl,-rpath-link=:libswresample:libswscale:libavfilter:libavdevice:libavformat:libavcodec:libavutil +check_ldflags -Wl,-rpath-link=:libpostproc:libswresample:libswscale:libavfilter:libavdevice:libavformat:libavcodec:libavutil enabled rpath && add_ldexeflags -Wl,-rpath,$libdir && add_ldsoflags -Wl,-rpath,$libdir test_ldflags -Wl,-Bsymbolic && append SHFLAGS -Wl,-Bsymbolic @@ -8374,6 +8379,7 @@ enabled fsync_filter && prepend a enabled mcdeint_filter && prepend avfilter_deps "avcodec" enabled movie_filter && prepend avfilter_deps "avformat avcodec" enabled pan_filter && prepend avfilter_deps "swresample" +enabled pp_filter && prepend avfilter_deps "postproc" enabled qrencode_filter && prepend avfilter_deps "swscale" enabled qrencodesrc_filter && prepend avfilter_deps "swscale" enabled removelogo_filter && prepend avfilter_deps "avformat avcodec swscale" @@ -8425,6 +8431,9 @@ expand_deps(){ reorder_by ${1}_deps LIBRARY_LIST # linking order is expected later } +#we have to remove gpl from the deps here as some code assumes all lib deps are libs +postproc_deps="$(filter_out 'gpl' $postproc_deps)" + map 'expand_deps $v' $LIBRARY_LIST if test "$quiet" != "yes"; then @@ -8522,6 +8531,7 @@ echo "optimize for size ${small- echo "optimizations ${optimizations-no}" echo "static ${static-no}" echo "shared ${shared-no}" +echo "postprocessing support ${postproc-no}" echo "network support ${network-no}" echo "threading support ${thread_type-no}" echo "safe bitstream reader ${safe_bitstream_reader-no}" @@ -8986,6 +8996,7 @@ extralibs_avcodec="$avcodec_extralibs" extralibs_avformat="$avformat_extralibs" extralibs_avdevice="$avdevice_extralibs" extralibs_avfilter="$avfilter_extralibs" +extralibs_postproc="$postproc_extralibs" extralibs_swscale="$swscale_extralibs" extralibs_swresample="$swresample_extralibs" EOF --- a/doc/filters.texi +++ b/doc/filters.texi @@ -20089,6 +20089,181 @@ Set window Y position, relative offset o This filter supports same @ref{commands} as options. +@section pp + +Enable the specified chain of postprocessing subfilters using libpostproc. This +library should be automatically selected with a GPL build (@code{--enable-gpl}). +Subfilters must be separated by '/' and can be disabled by prepending a '-'. +Each subfilter and some options have a short and a long name that can be used +interchangeably, i.e. dr/dering are the same. + +The filters accept the following options: + +@table @option +@item subfilters +Set postprocessing subfilters string. +@end table + +All subfilters share common options to determine their scope: + +@table @option +@item a/autoq +Honor the quality commands for this subfilter. + +@item c/chrom +Do chrominance filtering, too (default). + +@item y/nochrom +Do luma filtering only (no chrominance). + +@item n/noluma +Do chrominance filtering only (no luma). +@end table + +These options can be appended after the subfilter name, separated by a '|'. + +Available subfilters are: + +@table @option +@item hb/hdeblock[|difference[|flatness]] +Horizontal deblocking filter +@table @option +@item difference +Difference factor where higher values mean more deblocking (default: @code{32}). +@item flatness +Flatness threshold where lower values mean more deblocking (default: @code{39}). +@end table + +@item vb/vdeblock[|difference[|flatness]] +Vertical deblocking filter +@table @option +@item difference +Difference factor where higher values mean more deblocking (default: @code{32}). +@item flatness +Flatness threshold where lower values mean more deblocking (default: @code{39}). +@end table + +@item ha/hadeblock[|difference[|flatness]] +Accurate horizontal deblocking filter +@table @option +@item difference +Difference factor where higher values mean more deblocking (default: @code{32}). +@item flatness +Flatness threshold where lower values mean more deblocking (default: @code{39}). +@end table + +@item va/vadeblock[|difference[|flatness]] +Accurate vertical deblocking filter +@table @option +@item difference +Difference factor where higher values mean more deblocking (default: @code{32}). +@item flatness +Flatness threshold where lower values mean more deblocking (default: @code{39}). +@end table +@end table + +The horizontal and vertical deblocking filters share the difference and +flatness values so you cannot set different horizontal and vertical +thresholds. + +@table @option +@item h1/x1hdeblock +Experimental horizontal deblocking filter + +@item v1/x1vdeblock +Experimental vertical deblocking filter + +@item dr/dering +Deringing filter + +@item tn/tmpnoise[|threshold1[|threshold2[|threshold3]]], temporal noise reducer +@table @option +@item threshold1 +larger -> stronger filtering +@item threshold2 +larger -> stronger filtering +@item threshold3 +larger -> stronger filtering +@end table + +@item al/autolevels[:f/fullyrange], automatic brightness / contrast correction +@table @option +@item f/fullyrange +Stretch luma to @code{0-255}. +@end table + +@item lb/linblenddeint +Linear blend deinterlacing filter that deinterlaces the given block by +filtering all lines with a @code{(1 2 1)} filter. + +@item li/linipoldeint +Linear interpolating deinterlacing filter that deinterlaces the given block by +linearly interpolating every second line. + +@item ci/cubicipoldeint +Cubic interpolating deinterlacing filter deinterlaces the given block by +cubically interpolating every second line. + +@item md/mediandeint +Median deinterlacing filter that deinterlaces the given block by applying a +median filter to every second line. + +@item fd/ffmpegdeint +FFmpeg deinterlacing filter that deinterlaces the given block by filtering every +second line with a @code{(-1 4 2 4 -1)} filter. + +@item l5/lowpass5 +Vertically applied FIR lowpass deinterlacing filter that deinterlaces the given +block by filtering all lines with a @code{(-1 2 6 2 -1)} filter. + +@item fq/forceQuant[|quantizer] +Overrides the quantizer table from the input with the constant quantizer you +specify. +@table @option +@item quantizer +Quantizer to use +@end table + +@item de/default +Default pp filter combination (@code{hb|a,vb|a,dr|a}) + +@item fa/fast +Fast pp filter combination (@code{h1|a,v1|a,dr|a}) + +@item ac +High quality pp filter combination (@code{ha|a|128|7,va|a,dr|a}) +@end table + +@subsection Examples + +@itemize +@item +Apply horizontal and vertical deblocking, deringing and automatic +brightness/contrast: +@example +pp=hb/vb/dr/al +@end example + +@item +Apply default filters without brightness/contrast correction: +@example +pp=de/-al +@end example + +@item +Apply default filters and temporal denoiser: +@example +pp=default/tmpnoise|1|2|3 +@end example + +@item +Apply deblocking on luma only, and switch vertical deblocking on or off +automatically depending on available CPU time: +@example +pp=hb|y/vb|a +@end example +@end itemize + @section pp7 Apply Postprocessing filter 7. It is variant of the @ref{spp} filter, similar to spp = 6 with 7 point DCT, where only the center sample is --- a/fftools/ffprobe.c +++ b/fftools/ffprobe.c @@ -64,6 +64,8 @@ #include "libswscale/version.h" #include "libswresample/swresample.h" #include "libswresample/version.h" +#include "libpostproc/postprocess.h" +#include "libpostproc/version.h" #include "libavfilter/version.h" #include "textformat/avtextformat.h" #include "cmdutils.h" @@ -2830,6 +2832,7 @@ static void ffprobe_show_library_version SHOW_LIB_VERSION(avfilter, AVFILTER); SHOW_LIB_VERSION(swscale, SWSCALE); SHOW_LIB_VERSION(swresample, SWRESAMPLE); + SHOW_LIB_VERSION(postproc, POSTPROC); avtext_print_section_footer(tfc); } --- a/fftools/opt_common.c +++ b/fftools/opt_common.c @@ -61,6 +61,8 @@ #include "libswresample/swresample.h" #include "libswresample/version.h" +#include "libpostproc/postprocess.h" +#include "libpostproc/version.h" enum show_muxdemuxers { SHOW_DEFAULT, @@ -195,6 +197,7 @@ static void print_all_libs_info(int flag PRINT_LIB_INFO(avfilter, AVFILTER, flags, level); PRINT_LIB_INFO(swscale, SWSCALE, flags, level); PRINT_LIB_INFO(swresample, SWRESAMPLE, flags, level); + PRINT_LIB_INFO(postproc, POSTPROC, flags, level); } static void print_program_info(int flags, int level) --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -440,6 +440,7 @@ OBJS-$(CONFIG_PHOTOSENSITIVITY_FILTER) OBJS-$(CONFIG_PIXDESCTEST_FILTER) += vf_pixdesctest.o OBJS-$(CONFIG_PIXELIZE_FILTER) += vf_pixelize.o OBJS-$(CONFIG_PIXSCOPE_FILTER) += vf_datascope.o +OBJS-$(CONFIG_PP_FILTER) += vf_pp.o qp_table.o OBJS-$(CONFIG_PP7_FILTER) += vf_pp7.o qp_table.o OBJS-$(CONFIG_PREMULTIPLY_DYNAMIC_FILTER) += vf_premultiply.o framesync.o OBJS-$(CONFIG_PREMULTIPLY_FILTER) += vf_premultiply.o framesync.o --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -412,6 +412,7 @@ extern const FFFilter ff_vf_photosensiti extern const FFFilter ff_vf_pixdesctest; extern const FFFilter ff_vf_pixelize; extern const FFFilter ff_vf_pixscope; +extern const FFFilter ff_vf_pp; extern const FFFilter ff_vf_pp7; extern const FFFilter ff_vf_premultiply; extern const FFFilter ff_vf_premultiply_dynamic; --- /dev/null +++ b/libavfilter/vf_pp.c @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2002 A'rpi + * Copyright (C) 2012 Clément Bœsch + * + * 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 + * libpostproc filter, ported from MPlayer. + */ + +#include "libavutil/avassert.h" +#include "libavutil/mem.h" +#include "libavutil/opt.h" + +#include "filters.h" +#include "qp_table.h" +#include "video.h" + +#include "libpostproc/postprocess.h" + +typedef struct PPFilterContext { + const AVClass *class; + char *subfilters; + int mode_id; + pp_mode *modes[PP_QUALITY_MAX + 1]; + void *pp_ctx; +} PPFilterContext; + +#define OFFSET(x) offsetof(PPFilterContext, x) +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM +static const AVOption pp_options[] = { + { "subfilters", "set postprocess subfilters", OFFSET(subfilters), AV_OPT_TYPE_STRING, {.str="de"}, .flags = FLAGS }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(pp); + +static av_cold int pp_init(AVFilterContext *ctx) +{ + int i; + PPFilterContext *pp = ctx->priv; + + for (i = 0; i <= PP_QUALITY_MAX; i++) { + pp->modes[i] = pp_get_mode_by_name_and_quality(pp->subfilters, i); + if (!pp->modes[i]) + return AVERROR_EXTERNAL; + } + pp->mode_id = PP_QUALITY_MAX; + return 0; +} + +static int pp_process_command(AVFilterContext *ctx, const char *cmd, const char *args, + char *res, int res_len, int flags) +{ + PPFilterContext *pp = ctx->priv; + + if (!strcmp(cmd, "quality")) { + pp->mode_id = av_clip(strtol(args, NULL, 10), 0, PP_QUALITY_MAX); + return 0; + } + return AVERROR(ENOSYS); +} + +static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVJ420P, + AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVJ422P, + AV_PIX_FMT_YUV411P, + AV_PIX_FMT_GBRP, + AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P, + AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUVJ440P, + AV_PIX_FMT_GRAY8, + AV_PIX_FMT_NONE +}; + +static int pp_config_props(AVFilterLink *inlink) +{ + int flags = PP_CPU_CAPS_AUTO; + PPFilterContext *pp = inlink->dst->priv; + + switch (inlink->format) { + case AV_PIX_FMT_GRAY8: + case AV_PIX_FMT_YUVJ420P: + case AV_PIX_FMT_YUV420P: flags |= PP_FORMAT_420; break; + case AV_PIX_FMT_YUVJ422P: + case AV_PIX_FMT_YUV422P: flags |= PP_FORMAT_422; break; + case AV_PIX_FMT_YUV411P: flags |= PP_FORMAT_411; break; + case AV_PIX_FMT_GBRP: + case AV_PIX_FMT_YUVJ444P: + case AV_PIX_FMT_YUV444P: flags |= PP_FORMAT_444; break; + case AV_PIX_FMT_YUVJ440P: + case AV_PIX_FMT_YUV440P: flags |= PP_FORMAT_440; break; + default: av_assert0(0); + } + + pp->pp_ctx = pp_get_context(inlink->w, inlink->h, flags); + if (!pp->pp_ctx) + return AVERROR(ENOMEM); + return 0; +} + +static int pp_filter_frame(AVFilterLink *inlink, AVFrame *inbuf) +{ + AVFilterContext *ctx = inlink->dst; + PPFilterContext *pp = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + const int aligned_w = FFALIGN(outlink->w, 8); + const int aligned_h = FFALIGN(outlink->h, 8); + AVFrame *outbuf; + int qstride = 0; + int8_t *qp_table = NULL; + int ret; + + outbuf = ff_get_video_buffer(outlink, aligned_w, aligned_h); + if (!outbuf) { + av_frame_free(&inbuf); + return AVERROR(ENOMEM); + } + av_frame_copy_props(outbuf, inbuf); + outbuf->width = inbuf->width; + outbuf->height = inbuf->height; + + ret = ff_qp_table_extract(inbuf, &qp_table, &qstride, NULL, NULL); + if (ret < 0) { + av_frame_free(&inbuf); + av_frame_free(&outbuf); + return ret; + } + + pp_postprocess((const uint8_t **)inbuf->data, inbuf->linesize, + outbuf->data, outbuf->linesize, + aligned_w, outlink->h, + qp_table, + qstride, + pp->modes[pp->mode_id], + pp->pp_ctx, + outbuf->pict_type | (qp_table ? PP_PICT_TYPE_QP2 : 0)); + + av_frame_free(&inbuf); + av_freep(&qp_table); + return ff_filter_frame(outlink, outbuf); +} + +static av_cold void pp_uninit(AVFilterContext *ctx) +{ + int i; + PPFilterContext *pp = ctx->priv; + + for (i = 0; i <= PP_QUALITY_MAX; i++) + pp_free_mode(pp->modes[i]); + if (pp->pp_ctx) + pp_free_context(pp->pp_ctx); +} + +static const AVFilterPad pp_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = pp_config_props, + .filter_frame = pp_filter_frame, + }, +}; + +const FFFilter ff_vf_pp = { + .p.name = "pp", + .p.description = NULL_IF_CONFIG_SMALL("Filter video using libpostproc."), + .p.priv_class = &pp_class, + .p.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, + .priv_size = sizeof(PPFilterContext), + .init = pp_init, + .uninit = pp_uninit, + FILTER_INPUTS(pp_inputs), + FILTER_OUTPUTS(ff_video_default_filterpad), + FILTER_PIXFMTS_ARRAY(pix_fmts), + .process_command = pp_process_command, +}; --- a/libavutil/avutil.h +++ b/libavutil/avutil.h @@ -41,6 +41,7 @@ * @li @ref lavd "libavdevice" special devices muxing/demuxing library * @li @ref lavu "libavutil" common utility library * @li @ref lswr "libswresample" audio resampling, format conversion and mixing + * @li @ref lpp "libpostproc" post processing library * @li @ref libsws "libswscale" color conversion and scaling library * * @section ffmpeg_versioning Versioning and compatibility --- /dev/null +++ b/libpostproc/Makefile @@ -0,0 +1,23 @@ +NAME = postproc +DESC = FFmpeg postprocessing library +FFLIBS = avutil + +HEADERS = postprocess.h \ + version.h \ + version_major.h \ + +OBJS = postprocess.o \ + version.o \ + +TESTOBJS = tests/test_utils.o \ + +# Windows resource file +SHLIBOBJS-$(HAVE_GNU_WINDRES) += postprocres.o + +TESTPROGS = blocktest \ + stripetest \ + temptest \ + +$(SUBDIR)tests/blocktest$(EXESUF): $(SUBDIR)tests/test_utils.o +$(SUBDIR)tests/stripetest$(EXESUF): $(SUBDIR)tests/test_utils.o +$(SUBDIR)tests/temptest$(EXESUF): $(SUBDIR)tests/test_utils.o --- /dev/null +++ b/libpostproc/libpostproc.v @@ -0,0 +1,7 @@ +LIBPOSTPROC_MAJOR { + global: + postproc_*; + pp_*; + local: + *; +}; --- /dev/null +++ b/libpostproc/postprocess.c @@ -0,0 +1,992 @@ +/* + * Copyright (C) 2001-2003 Michael Niedermayer (michaelni@gmx.at) + * + * AltiVec optimizations (C) 2004 Romain Dolbeau + * + * 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 + * postprocessing. + */ + +/* + C MMX MMX2 AltiVec +isVertDC Ec Ec Ec +isVertMinMaxOk Ec Ec Ec +doVertLowPass E e Ec +doVertDefFilter Ec Ec e Ec +isHorizDC Ec Ec Ec +isHorizMinMaxOk a E Ec +doHorizLowPass E e Ec +doHorizDefFilter Ec Ec e Ec +do_a_deblock Ec E Ec +deRing E e Ecp +Vertical RKAlgo1 E a +Horizontal RKAlgo1 a +Vertical X1# a E +Horizontal X1# a E +LinIpolDeinterlace e E +CubicIpolDeinterlace a e +LinBlendDeinterlace e E +MedianDeinterlace# E Ec Ec +TempDeNoiser# E e Ec + +# more or less selfinvented filters so the exactness is not too meaningful +E = Exact implementation +e = almost exact implementation (slightly different rounding,...) +a = alternative / approximate impl +c = checked against the other implementations (-vo md5) +p = partially optimized, still some work to do +*/ + +/* +TODO: +reduce the time wasted on the mem transfer +unroll stuff if instructions depend too much on the prior one +move YScale thing to the end instead of fixing QP +write a faster and higher quality deblocking filter :) +make the mainloop more flexible (variable number of blocks at once + (the if/else stuff per block is slowing things down) +compare the quality & speed of all filters +split this huge file +optimize c versions +try to unroll inner for(x=0 ... loop to avoid these damn if(x ... checks +... +*/ + +//Changelog: use git log + +#include +#include +#include + +#include "config.h" +#include "libavutil/common.h" +#include "libavutil/cpu.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/mem.h" +//#undef HAVE_MMXEXT_INLINE +//#undef HAVE_MMX_INLINE +//#undef ARCH_X86 +//#define DEBUG_BRIGHTNESS +#include "postprocess.h" +#include "postprocess_internal.h" +#include "libavutil/avstring.h" + +#define GET_MODE_BUFFER_SIZE 500 +#define OPTIONS_ARRAY_SIZE 10 +#define BLOCK_SIZE 8 +#define TEMP_STRIDE 8 +//#define NUM_BLOCKS_AT_ONCE 16 //not used yet + +#define DERING_THRESHOLD 20 + +#if ARCH_X86 && HAVE_INLINE_ASM +DECLARE_ASM_CONST(8, uint64_t, w05)= 0x0005000500050005LL; +DECLARE_ASM_CONST(8, uint64_t, w04)= 0x0004000400040004LL; +DECLARE_ASM_CONST(8, uint64_t, w20)= 0x0020002000200020LL; +DECLARE_ASM_CONST(8, uint64_t, b00)= 0x0000000000000000LL; +DECLARE_ASM_CONST(8, uint64_t, b01)= 0x0101010101010101LL; +DECLARE_ASM_CONST(8, uint64_t, b08)= 0x0808080808080808LL; +DECLARE_ASM_CONST(8, uint64_t, b80)= 0x8080808080808080LL; +#endif + +static const struct PPFilter filters[]= +{ + {"hb", "hdeblock", 1, 1, 3, H_DEBLOCK}, + {"vb", "vdeblock", 1, 2, 4, V_DEBLOCK}, +/* {"hr", "rkhdeblock", 1, 1, 3, H_RK1_FILTER}, + {"vr", "rkvdeblock", 1, 2, 4, V_RK1_FILTER},*/ + {"h1", "x1hdeblock", 1, 1, 3, H_X1_FILTER}, + {"v1", "x1vdeblock", 1, 2, 4, V_X1_FILTER}, + {"ha", "ahdeblock", 1, 1, 3, H_A_DEBLOCK}, + {"va", "avdeblock", 1, 2, 4, V_A_DEBLOCK}, + {"dr", "dering", 1, 5, 6, DERING}, + {"al", "autolevels", 0, 1, 2, LEVEL_FIX}, + {"lb", "linblenddeint", 1, 1, 4, LINEAR_BLEND_DEINT_FILTER}, + {"li", "linipoldeint", 1, 1, 4, LINEAR_IPOL_DEINT_FILTER}, + {"ci", "cubicipoldeint", 1, 1, 4, CUBIC_IPOL_DEINT_FILTER}, + {"md", "mediandeint", 1, 1, 4, MEDIAN_DEINT_FILTER}, + {"fd", "ffmpegdeint", 1, 1, 4, FFMPEG_DEINT_FILTER}, + {"l5", "lowpass5", 1, 1, 4, LOWPASS5_DEINT_FILTER}, + {"tn", "tmpnoise", 1, 7, 8, TEMP_NOISE_FILTER}, + {"fq", "forcequant", 1, 0, 0, FORCE_QUANT}, + {"be", "bitexact", 1, 0, 0, BITEXACT}, + {"vi", "visualize", 1, 0, 0, VISUALIZE}, + {NULL, NULL,0,0,0,0} //End Marker +}; + +static const char * const replaceTable[]= +{ + "default", "hb:a,vb:a,dr:a", + "de", "hb:a,vb:a,dr:a", + "fast", "h1:a,v1:a,dr:a", + "fa", "h1:a,v1:a,dr:a", + "ac", "ha:a:128:7,va:a,dr:a", + NULL //End Marker +}; + +/* The horizontal functions exist only in C because the MMX + * code is faster with vertical filters and transposing. */ + +/** + * Check if the given 8x8 Block is mostly "flat" + */ +static inline int isHorizDC_C(const uint8_t src[], int stride, const PPContext *c) +{ + int numEq= 0; + int y; + const int dcOffset= ((c->nonBQP*c->ppMode.baseDcDiff)>>8) + 1; + const int dcThreshold= dcOffset*2 + 1; + + for(y=0; y c->ppMode.flatnessThreshold; +} + +/** + * Check if the middle 8x8 Block in the given 8x16 block is flat + */ +static inline int isVertDC_C(const uint8_t src[], int stride, const PPContext *c) +{ + int numEq= 0; + int y; + const int dcOffset= ((c->nonBQP*c->ppMode.baseDcDiff)>>8) + 1; + const int dcThreshold= dcOffset*2 + 1; + + src+= stride*4; // src points to begin of the 8x8 Block + for(y=0; y c->ppMode.flatnessThreshold; +} + +static inline int isHorizMinMaxOk_C(const uint8_t src[], int stride, int QP) +{ + int i; + for(i=0; i<2; i++){ + if((unsigned)(src[0] - src[5] + 2*QP) > 4*QP) return 0; + src += stride; + if((unsigned)(src[2] - src[7] + 2*QP) > 4*QP) return 0; + src += stride; + if((unsigned)(src[4] - src[1] + 2*QP) > 4*QP) return 0; + src += stride; + if((unsigned)(src[6] - src[3] + 2*QP) > 4*QP) return 0; + src += stride; + } + return 1; +} + +static inline int isVertMinMaxOk_C(const uint8_t src[], int stride, int QP) +{ + int x; + src+= stride*4; + for(x=0; x 4*QP) return 0; + if((unsigned)(src[1+x + 2*stride] - src[1+x + 7*stride] + 2*QP) > 4*QP) return 0; + if((unsigned)(src[2+x + 4*stride] - src[2+x + 1*stride] + 2*QP) > 4*QP) return 0; + if((unsigned)(src[3+x + 6*stride] - src[3+x + 3*stride] + 2*QP) > 4*QP) return 0; + } + return 1; +} + +static inline int horizClassify_C(const uint8_t src[], int stride, const PPContext *c) +{ + if( isHorizDC_C(src, stride, c) ){ + return isHorizMinMaxOk_C(src, stride, c->QP); + }else{ + return 2; + } +} + +static inline int vertClassify_C(const uint8_t src[], int stride, const PPContext *c) +{ + if( isVertDC_C(src, stride, c) ){ + return isVertMinMaxOk_C(src, stride, c->QP); + }else{ + return 2; + } +} + +static inline void doHorizDefFilter_C(uint8_t dst[], int stride, const PPContext *c) +{ + int y; + for(y=0; yQP){ + const int q=(dst[3] - dst[4])/2; + const int leftEnergy= 5*(dst[2] - dst[1]) + 2*(dst[0] - dst[3]); + const int rightEnergy= 5*(dst[6] - dst[5]) + 2*(dst[4] - dst[7]); + + int d= FFABS(middleEnergy) - FFMIN( FFABS(leftEnergy), FFABS(rightEnergy) ); + d= FFMAX(d, 0); + + d= (5*d + 32) >> 6; + d*= FFSIGN(-middleEnergy); + + if(q>0) + { + d = FFMAX(d, 0); + d = FFMIN(d, q); + } + else + { + d = FFMIN(d, 0); + d = FFMAX(d, q); + } + + dst[3]-= d; + dst[4]+= d; + } + dst+= stride; + } +} + +/** + * Do a horizontal low pass filter on the 10x8 block (dst points to middle 8x8 Block) + * using the 9-Tap Filter (1,1,2,2,4,2,2,1,1)/16 (C version) + */ +static inline void doHorizLowPass_C(uint8_t dst[], int stride, const PPContext *c) +{ + int y; + for(y=0; yQP ? dst[-1] : dst[0]; + const int last= FFABS(dst[8] - dst[7]) < c->QP ? dst[8] : dst[7]; + + int sums[10]; + sums[0] = 4*first + dst[0] + dst[1] + dst[2] + 4; + sums[1] = sums[0] - first + dst[3]; + sums[2] = sums[1] - first + dst[4]; + sums[3] = sums[2] - first + dst[5]; + sums[4] = sums[3] - first + dst[6]; + sums[5] = sums[4] - dst[0] + dst[7]; + sums[6] = sums[5] - dst[1] + last; + sums[7] = sums[6] - dst[2] + last; + sums[8] = sums[7] - dst[3] + last; + sums[9] = sums[8] - dst[4] + last; + + dst[0]= (sums[0] + sums[2] + 2*dst[0])>>4; + dst[1]= (sums[1] + sums[3] + 2*dst[1])>>4; + dst[2]= (sums[2] + sums[4] + 2*dst[2])>>4; + dst[3]= (sums[3] + sums[5] + 2*dst[3])>>4; + dst[4]= (sums[4] + sums[6] + 2*dst[4])>>4; + dst[5]= (sums[5] + sums[7] + 2*dst[5])>>4; + dst[6]= (sums[6] + sums[8] + 2*dst[6])>>4; + dst[7]= (sums[7] + sums[9] + 2*dst[7])>>4; + + dst+= stride; + } +} + +/** + * Experimental Filter 1 (Horizontal) + * will not damage linear gradients + * Flat blocks should look like they were passed through the (1,1,2,2,4,2,2,1,1) 9-Tap filter + * can only smooth blocks at the expected locations (it cannot smooth them if they did move) + * MMX2 version does correct clipping C version does not + * not identical with the vertical one + */ +static inline void horizX1Filter(uint8_t *src, int stride, int QP) +{ + int y; + static uint64_t lut[256]; + if(!lut[255]) + { + int i; + for(i=0; i<256; i++) + { + int v= i < 128 ? 2*i : 2*(i-256); +/* +//Simulate 112242211 9-Tap filter + uint64_t a= (v/16) & 0xFF; + uint64_t b= (v/8) & 0xFF; + uint64_t c= (v/4) & 0xFF; + uint64_t d= (3*v/8) & 0xFF; +*/ +//Simulate piecewise linear interpolation + uint64_t a= (v/16) & 0xFF; + uint64_t b= (v*3/16) & 0xFF; + uint64_t c= (v*5/16) & 0xFF; + uint64_t d= (7*v/16) & 0xFF; + uint64_t A= (0x100 - a)&0xFF; + uint64_t B= (0x100 - b)&0xFF; + uint64_t C= (0x100 - c)&0xFF; + uint64_t D= (0x100 - c)&0xFF; + + lut[i] = (a<<56) | (b<<48) | (c<<40) | (d<<32) | + (D<<24) | (C<<16) | (B<<8) | (A); + //lut[i] = (v<<32) | (v<<24); + } + } + + for(y=0; yQP; + const int dcOffset= ((c->nonBQP*c->ppMode.baseDcDiff)>>8) + 1; + const int dcThreshold= dcOffset*2 + 1; + + src+= step*4; // src points to begin of the 8x8 Block + for(y=0; y<8; y++){ + int numEq= 0; + + numEq += ((unsigned)(src[-1*step] - src[0*step] + dcOffset)) < dcThreshold; + numEq += ((unsigned)(src[ 0*step] - src[1*step] + dcOffset)) < dcThreshold; + numEq += ((unsigned)(src[ 1*step] - src[2*step] + dcOffset)) < dcThreshold; + numEq += ((unsigned)(src[ 2*step] - src[3*step] + dcOffset)) < dcThreshold; + numEq += ((unsigned)(src[ 3*step] - src[4*step] + dcOffset)) < dcThreshold; + numEq += ((unsigned)(src[ 4*step] - src[5*step] + dcOffset)) < dcThreshold; + numEq += ((unsigned)(src[ 5*step] - src[6*step] + dcOffset)) < dcThreshold; + numEq += ((unsigned)(src[ 6*step] - src[7*step] + dcOffset)) < dcThreshold; + numEq += ((unsigned)(src[ 7*step] - src[8*step] + dcOffset)) < dcThreshold; + if(numEq > c->ppMode.flatnessThreshold){ + int min, max, x; + + if(src[0] > src[step]){ + max= src[0]; + min= src[step]; + }else{ + max= src[step]; + min= src[0]; + } + for(x=2; x<8; x+=2){ + if(src[x*step] > src[(x+1)*step]){ + if(src[x *step] > max) max= src[ x *step]; + if(src[(x+1)*step] < min) min= src[(x+1)*step]; + }else{ + if(src[(x+1)*step] > max) max= src[(x+1)*step]; + if(src[ x *step] < min) min= src[ x *step]; + } + } + if(max-min < 2*QP){ + const int first= FFABS(src[-1*step] - src[0]) < QP ? src[-1*step] : src[0]; + const int last= FFABS(src[8*step] - src[7*step]) < QP ? src[8*step] : src[7*step]; + + int sums[10]; + sums[0] = 4*first + src[0*step] + src[1*step] + src[2*step] + 4; + sums[1] = sums[0] - first + src[3*step]; + sums[2] = sums[1] - first + src[4*step]; + sums[3] = sums[2] - first + src[5*step]; + sums[4] = sums[3] - first + src[6*step]; + sums[5] = sums[4] - src[0*step] + src[7*step]; + sums[6] = sums[5] - src[1*step] + last; + sums[7] = sums[6] - src[2*step] + last; + sums[8] = sums[7] - src[3*step] + last; + sums[9] = sums[8] - src[4*step] + last; + + if (mode & VISUALIZE) { + src[0*step] = + src[1*step] = + src[2*step] = + src[3*step] = + src[4*step] = + src[5*step] = + src[6*step] = + src[7*step] = 128; + } + src[0*step]= (sums[0] + sums[2] + 2*src[0*step])>>4; + src[1*step]= (sums[1] + sums[3] + 2*src[1*step])>>4; + src[2*step]= (sums[2] + sums[4] + 2*src[2*step])>>4; + src[3*step]= (sums[3] + sums[5] + 2*src[3*step])>>4; + src[4*step]= (sums[4] + sums[6] + 2*src[4*step])>>4; + src[5*step]= (sums[5] + sums[7] + 2*src[5*step])>>4; + src[6*step]= (sums[6] + sums[8] + 2*src[6*step])>>4; + src[7*step]= (sums[7] + sums[9] + 2*src[7*step])>>4; + } + }else{ + const int middleEnergy= 5*(src[4*step] - src[3*step]) + 2*(src[2*step] - src[5*step]); + + if(FFABS(middleEnergy) < 8*QP){ + const int q=(src[3*step] - src[4*step])/2; + const int leftEnergy= 5*(src[2*step] - src[1*step]) + 2*(src[0*step] - src[3*step]); + const int rightEnergy= 5*(src[6*step] - src[5*step]) + 2*(src[4*step] - src[7*step]); + + int d= FFABS(middleEnergy) - FFMIN( FFABS(leftEnergy), FFABS(rightEnergy) ); + d= FFMAX(d, 0); + + d= (5*d + 32) >> 6; + d*= FFSIGN(-middleEnergy); + + if(q>0){ + d = FFMAX(d, 0); + d = FFMIN(d, q); + }else{ + d = FFMIN(d, 0); + d = FFMAX(d, q); + } + + if ((mode & VISUALIZE) && d) { + d= (d < 0) ? 32 : -32; + src[3*step]= av_clip_uint8(src[3*step] - d); + src[4*step]= av_clip_uint8(src[4*step] + d); + d = 0; + } + + src[3*step]-= d; + src[4*step]+= d; + } + } + + src += stride; + } +} + +//Note: we have C and SSE2 version (which uses MMX(EXT) when advantageous) +//Plain C versions +//we always compile C for testing which needs bitexactness +#define TEMPLATE_PP_C 1 +#include "postprocess_template.c" + +#if HAVE_ALTIVEC +#include "libavutil/ppc/util_altivec.h" + +# define TEMPLATE_PP_ALTIVEC 1 +# include "postprocess_altivec_template.c" +# include "postprocess_template.c" +#endif + +#if ARCH_X86 && HAVE_INLINE_ASM +# if CONFIG_RUNTIME_CPUDETECT +# define TEMPLATE_PP_SSE2 1 +# include "postprocess_template.c" +# else +# if HAVE_SSE2_INLINE +# define TEMPLATE_PP_SSE2 1 +# include "postprocess_template.c" +# endif +# endif +#endif + +typedef void (*pp_fn)(const uint8_t src[], int srcStride, uint8_t dst[], int dstStride, int width, int height, + const int8_t QPs[], int QPStride, int isColor, PPContext *c2); + +static inline void postProcess(const uint8_t src[], int srcStride, uint8_t dst[], int dstStride, int width, int height, + const int8_t QPs[], int QPStride, int isColor, pp_mode *vm, pp_context *vc) +{ + pp_fn pp = postProcess_C; + PPContext *c= (PPContext *)vc; + PPMode *ppMode= (PPMode *)vm; + c->ppMode= *ppMode; //FIXME + + if (!(ppMode->lumMode & BITEXACT)) { +#if CONFIG_RUNTIME_CPUDETECT +#if ARCH_X86 && HAVE_INLINE_ASM + // ordered per speed fastest first + if (c->cpuCaps & AV_CPU_FLAG_SSE2) pp = postProcess_SSE2; +#elif HAVE_ALTIVEC + if (c->cpuCaps & AV_CPU_FLAG_ALTIVEC) pp = postProcess_altivec; +#endif +#else /* CONFIG_RUNTIME_CPUDETECT */ +#if HAVE_SSE2_INLINE + pp = postProcess_SSE2; +#elif HAVE_ALTIVEC + pp = postProcess_altivec; +#endif +#endif /* !CONFIG_RUNTIME_CPUDETECT */ + } + + pp(src, srcStride, dst, dstStride, width, height, QPs, QPStride, isColor, c); +} + +/* -pp Command line Help +*/ +const char pp_help[] = +"Available postprocessing filters:\n" +"Filters Options\n" +"short long name short long option Description\n" +"* * a autoq CPU power dependent enabler\n" +" c chrom chrominance filtering enabled\n" +" y nochrom chrominance filtering disabled\n" +" n noluma luma filtering disabled\n" +"hb hdeblock (2 threshold) horizontal deblocking filter\n" +" 1. difference factor: default=32, higher -> more deblocking\n" +" 2. flatness threshold: default=39, lower -> more deblocking\n" +" the h & v deblocking filters share these\n" +" so you can't set different thresholds for h / v\n" +"vb vdeblock (2 threshold) vertical deblocking filter\n" +"ha hadeblock (2 threshold) horizontal deblocking filter\n" +"va vadeblock (2 threshold) vertical deblocking filter\n" +"h1 x1hdeblock experimental h deblock filter 1\n" +"v1 x1vdeblock experimental v deblock filter 1\n" +"dr dering deringing filter\n" +"al autolevels automatic brightness / contrast\n" +" f fullyrange stretch luminance to (0..255)\n" +"lb linblenddeint linear blend deinterlacer\n" +"li linipoldeint linear interpolating deinterlace\n" +"ci cubicipoldeint cubic interpolating deinterlacer\n" +"md mediandeint median deinterlacer\n" +"fd ffmpegdeint ffmpeg deinterlacer\n" +"l5 lowpass5 FIR lowpass deinterlacer\n" +"de default hb:a,vb:a,dr:a\n" +"fa fast h1:a,v1:a,dr:a\n" +"ac ha:a:128:7,va:a,dr:a\n" +"tn tmpnoise (3 threshold) temporal noise reducer\n" +" 1. <= 2. <= 3. larger -> stronger filtering\n" +"fq forceQuant force quantizer\n" +"Usage:\n" +"[: