Last active
August 3, 2019 01:50
-
-
Save 0x09/5417ddeb1c80ad52acb69691aca3353a to your computer and use it in GitHub Desktop.
resdetect filter for FFmpeg
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/configure b/configure | |
index 5a4f507246..6cc110d7e7 100755 | |
--- a/configure | |
+++ b/configure | |
@@ -255,6 +255,7 @@ External library support: | |
--enable-libopus enable Opus de/encoding via libopus [no] | |
--enable-libpulse enable Pulseaudio input via libpulse [no] | |
--enable-librsvg enable SVG rasterization via librsvg [no] | |
+ --enable-libresdet enable resdetect filter [no] | |
--enable-librubberband enable rubberband needed for rubberband filter [no] | |
--enable-librtmp enable RTMP[E] support via librtmp [no] | |
--enable-libshine enable fixed-point MP3 encoding via libshine [no] | |
@@ -1779,6 +1780,7 @@ EXTERNAL_LIBRARY_LIST=" | |
libopenmpt | |
libopus | |
libpulse | |
+ libresdet | |
librsvg | |
librtmp | |
libshine | |
@@ -3548,6 +3550,7 @@ scale_vaapi_filter_deps="vaapi" | |
vpp_qsv_filter_deps="libmfx" | |
vpp_qsv_filter_select="qsvvpp" | |
yadif_cuda_filter_deps="ffnvcodec cuda_nvcc" | |
+resdetect_filter_deps="libresdet" | |
# examples | |
avio_dir_cmd_deps="avformat avutil" | |
@@ -6333,6 +6336,7 @@ enabled rkmpp && { require_pkg_config rkmpp rockchip_mpp rockchip/r | |
die "ERROR: rkmpp requires --enable-libdrm"; } | |
} | |
enabled vapoursynth && require_pkg_config vapoursynth "vapoursynth-script >= 42" VSScript.h vsscript_init | |
+enabled libresdet && require_pkg_config libresdet resdet resdet.h resdet_methods | |
if enabled gcrypt; then | |
diff --git a/libavfilter/Makefile b/libavfilter/Makefile | |
index efc7bbb153..53cfc0d181 100644 | |
--- a/libavfilter/Makefile | |
+++ b/libavfilter/Makefile | |
@@ -341,6 +341,7 @@ OBJS-$(CONFIG_REMAP_FILTER) += vf_remap.o framesync.o | |
OBJS-$(CONFIG_REMOVEGRAIN_FILTER) += vf_removegrain.o | |
OBJS-$(CONFIG_REMOVELOGO_FILTER) += bbox.o lswsutils.o lavfutils.o vf_removelogo.o | |
OBJS-$(CONFIG_REPEATFIELDS_FILTER) += vf_repeatfields.o | |
+OBJS-$(CONFIG_RESDETECT_FILTER) += vf_resdetect.o | |
OBJS-$(CONFIG_REVERSE_FILTER) += f_reverse.o | |
OBJS-$(CONFIG_RGBASHIFT_FILTER) += vf_chromashift.o | |
OBJS-$(CONFIG_ROBERTS_FILTER) += vf_convolution.o | |
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c | |
index abd726d616..beb6104609 100644 | |
--- a/libavfilter/allfilters.c | |
+++ b/libavfilter/allfilters.c | |
@@ -325,6 +325,7 @@ extern AVFilter ff_vf_remap; | |
extern AVFilter ff_vf_removegrain; | |
extern AVFilter ff_vf_removelogo; | |
extern AVFilter ff_vf_repeatfields; | |
+extern AVFilter ff_vf_resdetect; | |
extern AVFilter ff_vf_reverse; | |
extern AVFilter ff_vf_rgbashift; | |
extern AVFilter ff_vf_roberts; | |
diff --git a/libavfilter/vf_resdetect.c b/libavfilter/vf_resdetect.c | |
new file mode 100644 | |
index 0000000000..f2d63226c1 | |
--- /dev/null | |
+++ b/libavfilter/vf_resdetect.c | |
@@ -0,0 +1,177 @@ | |
+/* | |
+ * Copyright (c) 2019 0x09.net | |
+ * 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 | |
+ * resolution detection filter using resdet | |
+ */ | |
+ | |
+#include "libavutil/opt.h" | |
+#include "libavutil/imgutils.h" | |
+#include "avfilter.h" | |
+#include "internal.h" | |
+ | |
+#include <resdet.h> | |
+ | |
+typedef struct ResDetectContext { | |
+ const AVClass *class; | |
+ const char *method_name; | |
+ RDMethod *method; | |
+ size_t range; | |
+ float threshold; | |
+} ResDetectContext; | |
+ | |
+static int query_formats(AVFilterContext *ctx) | |
+{ | |
+ // Any planar 8-bit format where the first plane represents the primary intensity data will work | |
+ static const enum AVPixelFormat pix_fmts[] = { | |
+ AV_PIX_FMT_GRAY8, | |
+ AV_PIX_FMT_YUV420P, | |
+ AV_PIX_FMT_YUV422P, | |
+ AV_PIX_FMT_YUV444P, | |
+ AV_PIX_FMT_YUV410P, | |
+ AV_PIX_FMT_YUV411P, | |
+ AV_PIX_FMT_YUVJ420P, | |
+ AV_PIX_FMT_YUVJ422P, | |
+ AV_PIX_FMT_YUVJ444P, | |
+ AV_PIX_FMT_NV12, | |
+ 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 av_cold int init(AVFilterContext *ctx) | |
+{ | |
+ ResDetectContext *s = ctx->priv; | |
+ | |
+ s->method = resdet_get_method(s->method_name); | |
+ if(!s->method) | |
+ { | |
+ av_log(ctx, AV_LOG_ERROR, "Invalid resdetect method '%s'.\n", s->method_name); | |
+ return AVERROR(EINVAL); | |
+ } | |
+ | |
+ if(!s->range) | |
+ s->range = resdet_default_range(); | |
+ | |
+ if(s->threshold < 0) | |
+ s->threshold = s->method->threshold; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int sortres(const void *left, const void *right) | |
+{ | |
+ return ((const RDResolution*)right)->confidence*10000 - ((const RDResolution*)left)->confidence*10000; | |
+} | |
+ | |
+/* | |
+ * resdet expects a single non-aligned Y plane. If the frame data is already suitable for that (usually the case) | |
+ * then just return it, otherwise copy into a new buffer which can be freed by `free_plane_unaligned` | |
+ * some mild extensions to resdet's api could make this unnecessary as the actual internal detect functions | |
+ * do accept stride and dist parameters | |
+*/ | |
+static uint8_t *get_plane_unaligned(AVFrame *frame) | |
+{ | |
+ if(frame->linesize[0] == frame->width) | |
+ return frame->data[0]; | |
+ | |
+ uint8_t *buf = av_malloc(frame->width * frame->height); | |
+ if(buf) | |
+ av_image_copy_plane(buf, frame->width, frame->data[0], frame->linesize[0], 1, frame->height); | |
+ | |
+ return buf; | |
+} | |
+ | |
+static void free_plane_unaligned(AVFrame *frame, uint8_t *buf) | |
+{ | |
+ if(frame->linesize[0] != frame->width) | |
+ av_free(buf); | |
+} | |
+ | |
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame) | |
+{ | |
+ AVFilterContext *ctx = inlink->dst; | |
+ ResDetectContext *s = ctx->priv; | |
+ RDResolution *rw, *rh; | |
+ size_t cw, ch; | |
+ | |
+ uint8_t *buf = get_plane_unaligned(frame); | |
+ if(!buf) | |
+ return AVERROR(ENOMEM); | |
+ | |
+ resdetect_with_params(buf, 1, frame->width, frame->height, | |
+ &rw, &cw, &rh, &ch, | |
+ s->method, s->range, s->threshold); | |
+ | |
+ free_plane_unaligned(frame, buf); | |
+ | |
+ qsort(rw, cw, sizeof(*rw), sortres); | |
+ qsort(rh, ch, sizeof(*rh), sortres); | |
+ | |
+ av_log(ctx, AV_LOG_INFO, "w: %zu, h: %zu\n", rw[0].index, rh[0].index); | |
+ | |
+ return ff_filter_frame(inlink->dst->outputs[0], frame); | |
+} | |
+ | |
+#define OFFSET(x) offsetof(ResDetectContext, x) | |
+#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM | |
+ | |
+static const AVOption resdetect_options[] = { | |
+ { "method", "Detection method to use", OFFSET(method_name), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, FLAGS }, | |
+ { "range", "Number of neighboring values to search", OFFSET(range), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS }, | |
+ { "threshold", "Consider results above this confidence", OFFSET(threshold), AV_OPT_TYPE_FLOAT, { .dbl = -1 }, -1.0, 1.0, FLAGS }, | |
+ { NULL } | |
+}; | |
+ | |
+AVFILTER_DEFINE_CLASS(resdetect); | |
+ | |
+static const AVFilterPad avfilter_vf_resdetect_inputs[] = { | |
+ { | |
+ .name = "default", | |
+ .type = AVMEDIA_TYPE_VIDEO, | |
+ .filter_frame = filter_frame, | |
+ }, | |
+ { NULL } | |
+}; | |
+ | |
+static const AVFilterPad avfilter_vf_resdetect_outputs[] = { | |
+ { | |
+ .name = "default", | |
+ .type = AVMEDIA_TYPE_VIDEO | |
+ }, | |
+ { NULL } | |
+}; | |
+ | |
+AVFilter ff_vf_resdetect = { | |
+ .name = "resdetect", | |
+ .description = NULL_IF_CONFIG_SMALL("Detect source resolution of upscaled video."), | |
+ .priv_size = sizeof(ResDetectContext), | |
+ .priv_class = &resdetect_class, | |
+ .init = init, | |
+ .query_formats = query_formats, | |
+ .inputs = avfilter_vf_resdetect_inputs, | |
+ .outputs = avfilter_vf_resdetect_outputs, | |
+ .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, | |
+}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment