From patchwork Sun Oct 20 20:05:25 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Niklas Haas X-Patchwork-Id: 52422 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:612c:143:b0:48e:c0f8:d0de with SMTP id h3csp2255700vqi; Sun, 20 Oct 2024 14:04:05 -0700 (PDT) X-Forwarded-Encrypted: i=2; AJvYcCVNfUw5SX+mpXHdPCdbkTImdCtk2AIcUhxQss7OkEoKRUgAepmNb0CFH4cvuvi8jg0WcE/yrGJGr6Enth24SxpS@gmail.com X-Google-Smtp-Source: AGHT+IFScdZaQICVdrFHBIhV8dCX7RW7KR94dmzeQEFQlRgzphnMA6pt/9nsyPzJsxNhU2hwHKUf X-Received: by 2002:a17:907:7f90:b0:a9a:76d:e86c with SMTP id a640c23a62f3a-a9a69c9b27dmr983127866b.49.1729458245076; Sun, 20 Oct 2024 14:04:05 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1729458245; cv=none; d=google.com; s=arc-20240605; b=YWO3Mu82zkpjUQvpygVaD5mGlRpr3GIsgKBqWk5R2z5V9ynqTqzEU3Tf1BxdqmvrpB j9cJOdWF7jmy14xRGRn8eswxbkwKVv0tQQDpE8gbLMjMoec0U9PDuEyqur/NdcRWlO2+ Nm+Hf5I3M7HUJjqdPrAsBPwglzC7dyYyWEKPDZ/QRQpdaA26KFnRZfb8S6+cMGK+INr0 i/iSRuWFBBk7OftbzKyKs4IvV6QL+iF6OcLiHAVQMk93o6jQje+w+dmRfyTU58GTo4tC Hh4ucPETDMB+23L475hfoWurlfTV23qW6ybXhZzGnT5AmPqPSmrKRucjZPBQstbP/2FS 9FTQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=Fva/aK+HKDczOstMrVilOD530N6JTAyAZas1FTYTUuA=; fh=xmAeKtysnShNOmkhiJmYkS30uw4Fu2hvBJ7qlIwukxQ=; b=IgYuIRUefsLn4cmd/aXVbNAhqtmJo+4rspOJoA3OjgMI2NQGblzCTO2tLNYfzamlnt 0TnoeUmOoT4jVIF4vZo8Qu99fZnNKi34soqHa7VG+Q2YfRnGJWT4T8ajnp+P6wTqy79f OT2/p4fS5/UmhOzjjeIfAwlc8NTc0Jnhoh/Y5EhuCFek3XE9TmjfWtF9rbCVFR4/iOMt GN5hCrUCB+DNas41kofo7CqvT9XnCsC3aCsqPra7x9BSuVR9BMCHYMjCE/1wCRJ5eoJI qGowQmpDDLTJVO4Da5kVYwJtiN3t1WzfzA/fKxevW4F5+WV7OFUQx4CpRDrg/W4Ti9o8 e0pg==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@haasn.xyz header.s=mail header.b=ihnI8bw9; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id a640c23a62f3a-a9a91333bc7si149405166b.390.2024.10.20.14.04.03; Sun, 20 Oct 2024 14:04:05 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@haasn.xyz header.s=mail header.b=ihnI8bw9; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id F320B68DB5D; Sun, 20 Oct 2024 23:09:11 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from haasn.dev (haasn.dev [78.46.187.166]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id C6AC568DAE3 for ; Sun, 20 Oct 2024 23:09:00 +0300 (EEST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=haasn.xyz; s=mail; t=1729454938; bh=VUsSZg00n3tkpTgsiWaQvNVYtGSoiGk6kvvJndy0Ujc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ihnI8bw9K2xMG/KcOJX77Jkh93OnbhK2osKxoreIrQR62X0nKR41vyALFpUFJp68B phx7oovzhRM0JhSSHkMlIyftsHXZdMWsZ9a3LHwrDJlYXwhCgVNdjNGjbAWlY0dV7Q aV+tCmY5sIlcyGTze5D6YRdoONitQIVYGz0ORTok= Received: from haasn.dev (unknown [10.30.0.2]) by haasn.dev (Postfix) with ESMTP id 703F84BE8F; Sun, 20 Oct 2024 22:08:58 +0200 (CEST) From: Niklas Haas To: ffmpeg-devel@ffmpeg.org Date: Sun, 20 Oct 2024 22:05:25 +0200 Message-ID: <20241020200851.1414766-17-ffmpeg@haasn.xyz> X-Mailer: git-send-email 2.46.1 In-Reply-To: <20241020200851.1414766-1-ffmpeg@haasn.xyz> References: <20241020200851.1414766-1-ffmpeg@haasn.xyz> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v3 16/18] tests/swscale: rewrite on top of new API X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Niklas Haas Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: j14UMNJvTiD8 From: Niklas Haas This rewrite cleans up the code to use AVFrames and the new swscale API. The log format has also been simplified and expanded to account for the new options. (Not yet implemented) The self testing code path has also been expanded to test the new swscale implementation against the old one, to serve as an unchanging reference. This does not accomplish much yet, but serves as a framework for future work. Sponsored-by: Sovereign Tech Fund Signed-off-by: Niklas Haas --- libswscale/tests/swscale.c | 665 ++++++++++++++++--------------------- 1 file changed, 284 insertions(+), 381 deletions(-) diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c index af8069f728..c11a46024e 100644 --- a/libswscale/tests/swscale.c +++ b/libswscale/tests/swscale.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2024 Nikles Haas * Copyright (C) 2003-2011 Michael Niedermayer * * This file is part of FFmpeg. @@ -26,424 +27,307 @@ #undef HAVE_AV_CONFIG_H #include "libavutil/cpu.h" -#include "libavutil/imgutils.h" -#include "libavutil/mem.h" -#include "libavutil/avutil.h" -#include "libavutil/crc.h" -#include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "libavutil/lfg.h" #include "libavutil/sfc64.h" +#include "libavutil/frame.h" +#include "libavutil/pixfmt.h" +#include "libavutil/avassert.h" +#include "libavutil/macros.h" #include "libswscale/swscale.h" -/* HACK Duplicated from swscale_internal.h. - * Should be removed when a cleaner pixel format system exists. */ -#define isGray(x) \ - ((x) == AV_PIX_FMT_GRAY8 || \ - (x) == AV_PIX_FMT_YA8 || \ - (x) == AV_PIX_FMT_GRAY16BE || \ - (x) == AV_PIX_FMT_GRAY16LE || \ - (x) == AV_PIX_FMT_YA16BE || \ - (x) == AV_PIX_FMT_YA16LE) -#define hasChroma(x) \ - (!(isGray(x) || \ - (x) == AV_PIX_FMT_MONOBLACK || \ - (x) == AV_PIX_FMT_MONOWHITE)) - -static av_always_inline int isALPHA(enum AVPixelFormat pix_fmt) -{ - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt); - return desc->flags & AV_PIX_FMT_FLAG_ALPHA; -} +enum { + WIDTH = 96, + HEIGHT = 96, +}; -static double prob = 1; -FFSFC64 prng_state; +struct options { + enum AVPixelFormat src_fmt; + enum AVPixelFormat dst_fmt; + double prob; +}; -static uint64_t getSSD(const uint8_t *src1, const uint8_t *src2, - int stride1, int stride2, int w, int h) -{ - int x, y; - uint64_t ssd = 0; +struct mode { + SwsFlags flags; + SwsDither dither; +}; - for (y = 0; y < h; y++) { - for (x = 0; x < w; x++) { - int d = src1[x + y * stride1] - src2[x + y * stride2]; - ssd += d * d; - } - } - return ssd; -} +const int dst_w[] = { WIDTH, WIDTH - WIDTH / 3, WIDTH + WIDTH / 3 }; +const int dst_h[] = { HEIGHT, HEIGHT - HEIGHT / 3, HEIGHT + HEIGHT / 3 }; + +const struct mode modes[] = { + { SWS_FAST_BILINEAR }, + { SWS_BILINEAR }, + { SWS_BICUBIC }, + { SWS_X | SWS_BITEXACT }, + { SWS_POINT }, + { SWS_AREA | SWS_ACCURATE_RND }, + { SWS_BICUBIC | SWS_FULL_CHR_H_INT | SWS_FULL_CHR_H_INP }, + {0}, // test defaults +}; -static uint64_t getSSD0(int ref, const uint8_t *src1, int stride1, - int w, int h) -{ - int x, y; - uint64_t ssd = 0; +static FFSFC64 prng_state; +static SwsContext *sws[3]; /* reused between tests for efficiency */ - for (y = 0; y < h; y++) { - for (x = 0; x < w; x++) { - int d = src1[x + y * stride1] - ref; - ssd += d * d; - } - } - return ssd; +static int fmt_comps(enum AVPixelFormat fmt) +{ + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt); + int comps = desc->nb_components >= 3 ? 0b111 : 0b1; + if (desc->flags & AV_PIX_FMT_FLAG_ALPHA) + comps |= 0b1000; + return comps; } -struct Results { - uint64_t ssdY; - uint64_t ssdU; - uint64_t ssdV; - uint64_t ssdA; - uint32_t crc; -}; - -// test by ref -> src -> dst -> out & compare out against ref -// ref & out are YV12 -static int doTest(const uint8_t * const ref[4], int refStride[4], int w, int h, - enum AVPixelFormat srcFormat, enum AVPixelFormat dstFormat, - int srcW, int srcH, int dstW, int dstH, int flags, - struct Results *r) +static void get_mse(int mse[4], const AVFrame *a, const AVFrame *b, int comps) { - const AVPixFmtDescriptor *desc_yuva420p = av_pix_fmt_desc_get(AV_PIX_FMT_YUVA420P); - const AVPixFmtDescriptor *desc_src = av_pix_fmt_desc_get(srcFormat); - const AVPixFmtDescriptor *desc_dst = av_pix_fmt_desc_get(dstFormat); - static enum AVPixelFormat cur_srcFormat; - static int cur_srcW, cur_srcH; - static const uint8_t *src[4]; - static int srcStride[4]; - uint8_t *dst[4] = { 0 }; - uint8_t *out[4] = { 0 }; - int dstStride[4] = {0}; - int i; - uint64_t ssdY, ssdU = 0, ssdV = 0, ssdA = 0; - SwsContext *dstContext = NULL, *outContext = NULL; - uint32_t crc = 0; - int res = 0; - - if (ff_sfc64_get(&prng_state) > UINT64_MAX * prob) - return 0; - - if (cur_srcFormat != srcFormat || cur_srcW != srcW || cur_srcH != srcH) { - SwsContext *srcContext = NULL; - int p; - - for (p = 0; p < 4; p++) - av_freep(&src[p]); - - res = av_image_fill_linesizes(srcStride, srcFormat, srcW); - if (res < 0) { - fprintf(stderr, "av_image_fill_linesizes failed\n"); - goto end; - } - for (p = 0; p < 4; p++) { - srcStride[p] = FFALIGN(srcStride[p], 16); - if (srcStride[p]) - src[p] = av_mallocz(srcStride[p] * srcH + 16); - if (srcStride[p] && !src[p]) { - perror("Malloc"); - res = -1; - goto end; + av_assert1(a->format == AV_PIX_FMT_YUVA420P); + av_assert1(b->format == a->format); + av_assert1(b->width == a->width && b->height == a->height); + + for (int p = 0; p < 4; p++) { + const int is_chroma = p == 1 || p == 2; + const int stride_a = a->linesize[p]; + const int stride_b = b->linesize[p]; + const int w = (a->width + is_chroma) >> is_chroma; + const int h = (a->height + is_chroma) >> is_chroma; + uint64_t sum = 0; + + if (comps & (1 << p)) { + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + int d = a->data[p][y * stride_a + x] - b->data[p][y * stride_b + x]; + sum += d * d; + } + } + } else { + const int ref = is_chroma ? 128 : 0xFF; + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + int d = a->data[p][y * stride_a + x] - ref; + sum += d * d; + } } } - srcContext = sws_getContext(w, h, AV_PIX_FMT_YUVA420P, srcW, srcH, - srcFormat, SWS_BILINEAR, NULL, NULL, NULL); - if (!srcContext) { - fprintf(stderr, "Failed to get %s ---> %s\n", - desc_yuva420p->name, - desc_src->name); - res = -1; - goto end; - } - sws_scale(srcContext, ref, refStride, 0, h, - (uint8_t * const *) src, srcStride); - sws_freeContext(srcContext); - cur_srcFormat = srcFormat; - cur_srcW = srcW; - cur_srcH = srcH; + mse[p] = sum / (w * h); } +} - res = av_image_fill_linesizes(dstStride, dstFormat, dstW); - if (res < 0) { - fprintf(stderr, "av_image_fill_linesizes failed\n"); - goto end; - } +static int scale_legacy(AVFrame *dst, const AVFrame *src, struct mode mode) +{ + SwsContext *sws_legacy; + int ret; - for (i = 0; i < 4; i++) { - /* Image buffers passed into libswscale can be allocated any way you - * prefer, as long as they're aligned enough for the architecture, and - * they're freed appropriately (such as using av_free for buffers - * allocated with av_malloc). */ - /* An extra 16 bytes is being allocated because some scalers may write - * out of bounds. */ - dstStride[i] = FFALIGN(dstStride[i], 16); - if (dstStride[i]) - dst[i] = av_mallocz(dstStride[i] * dstH + 16); - if (dstStride[i] && !dst[i]) { - perror("Malloc"); - res = -1; - - goto end; - } - } + sws_legacy = sws_alloc_context(); + if (!sws_legacy) + return -1; + + sws_legacy->src_w = src->width; + sws_legacy->src_h = src->height; + sws_legacy->src_format = src->format; + sws_legacy->dst_w = dst->width; + sws_legacy->dst_h = dst->height; + sws_legacy->dst_format = dst->format; + sws_legacy->flags = mode.flags; + sws_legacy->dither = mode.dither; + + ret = sws_init_context(sws_legacy, NULL, NULL); + if (!ret) + ret = sws_scale_frame(sws_legacy, dst, src); + + sws_freeContext(sws_legacy); + return ret; +} + +/* Runs a series of ref -> src -> dst -> out, and compares out vs ref */ +static int run_test(enum AVPixelFormat src_fmt, enum AVPixelFormat dst_fmt, + int dst_w, int dst_h, struct mode mode, const AVFrame *ref, + const int mse_ref[4]) +{ + AVFrame *src = NULL, *dst = NULL, *out = NULL; + int mse[4], mse_sws[4], ret = -1; + const int comps = fmt_comps(src_fmt) & fmt_comps(dst_fmt); + + src = av_frame_alloc(); + dst = av_frame_alloc(); + out = av_frame_alloc(); + if (!src || !dst || !out) + goto error; - dstContext = sws_alloc_context(); - if (!dstContext) { - fprintf(stderr, "Failed to alloc %s ---> %s\n", - desc_src->name, desc_dst->name); - res = -1; - goto end; + av_frame_copy_props(src, ref); + av_frame_copy_props(dst, ref); + av_frame_copy_props(out, ref); + src->width = out->width = ref->width; + src->height = out->height = ref->height; + out->format = ref->format; + src->format = src_fmt; + dst->format = dst_fmt; + dst->width = dst_w; + dst->height = dst_h; + + if (sws_scale_frame(sws[0], src, ref) < 0) { + fprintf(stderr, "Failed %s ---> %s\n", av_get_pix_fmt_name(ref->format), + av_get_pix_fmt_name(src->format)); + goto error; } - av_opt_set_int(dstContext, "sws_flags", flags, 0); - av_opt_set_int(dstContext, "srcw", srcW, 0); - av_opt_set_int(dstContext, "srch", srcH, 0); - av_opt_set_int(dstContext, "dstw", dstW, 0); - av_opt_set_int(dstContext, "dsth", dstH, 0); - av_opt_set_int(dstContext, "src_format", srcFormat, 0); - av_opt_set_int(dstContext, "dst_format", dstFormat, 0); - av_opt_set(dstContext, "alphablend", "none", 0); - - if (sws_init_context(dstContext, NULL, NULL) < 0) { - sws_freeContext(dstContext); - fprintf(stderr, "Failed to init %s ---> %s\n", - desc_src->name, desc_dst->name); - res = -1; - goto end; + sws[1]->flags = mode.flags; + sws[1]->dither = mode.dither; + if (sws_scale_frame(sws[1], dst, src) < 0) { + fprintf(stderr, "Failed %s ---> %s\n", av_get_pix_fmt_name(src->format), + av_get_pix_fmt_name(dst->format)); + goto error; } - printf(" %s %dx%d -> %s %3dx%3d flags=%2d", - desc_src->name, srcW, srcH, - desc_dst->name, dstW, dstH, - flags); - fflush(stdout); + if (sws_scale_frame(sws[2], out, dst) < 0) { + fprintf(stderr, "Failed %s ---> %s\n", av_get_pix_fmt_name(dst->format), + av_get_pix_fmt_name(out->format)); + goto error; + } - sws_scale(dstContext, (const uint8_t * const*)src, srcStride, 0, srcH, dst, dstStride); - - for (i = 0; i < 4 && dstStride[i]; i++) - crc = av_crc(av_crc_get_table(AV_CRC_32_IEEE), crc, dst[i], - dstStride[i] * dstH); - - if (r && crc == r->crc) { - ssdY = r->ssdY; - ssdU = r->ssdU; - ssdV = r->ssdV; - ssdA = r->ssdA; - } else { - for (i = 0; i < 4; i++) { - refStride[i] = FFALIGN(refStride[i], 16); - if (refStride[i]) - out[i] = av_mallocz(refStride[i] * h); - if (refStride[i] && !out[i]) { - perror("Malloc"); - res = -1; - goto end; - } - } - outContext = sws_getContext(dstW, dstH, dstFormat, w, h, - AV_PIX_FMT_YUVA420P, SWS_BILINEAR, - NULL, NULL, NULL); - if (!outContext) { - fprintf(stderr, "Failed to get %s ---> %s\n", - desc_dst->name, - desc_yuva420p->name); - res = -1; - goto end; - } - sws_scale(outContext, (const uint8_t * const *) dst, dstStride, 0, dstH, - out, refStride); - - ssdY = getSSD(ref[0], out[0], refStride[0], refStride[0], w, h); - if (hasChroma(srcFormat) && hasChroma(dstFormat)) { - //FIXME check that output is really gray - ssdU = getSSD(ref[1], out[1], refStride[1], refStride[1], - (w + 1) >> 1, (h + 1) >> 1); - ssdV = getSSD(ref[2], out[2], refStride[2], refStride[2], - (w + 1) >> 1, (h + 1) >> 1); - } else { - ssdU = getSSD0(128, out[1], refStride[1], - (w + 1) >> 1, (h + 1) >> 1); - ssdV = getSSD0(128, out[2], refStride[2], - (w + 1) >> 1, (h + 1) >> 1); - } - if (isALPHA(srcFormat) && isALPHA(dstFormat)) { - ssdA = getSSD(ref[3], out[3], refStride[3], refStride[3], w, h); - } else { - ssdA = getSSD0(0xFF, out[3], refStride[3], w, h); + get_mse(mse, out, ref, comps); + printf("%s %dx%d -> %s %3dx%3d, flags=%u dither=%u, " + "MSE={%5d %5d %5d %5d}\n", + av_get_pix_fmt_name(src->format), src->width, src->height, + av_get_pix_fmt_name(dst->format), dst->width, dst->height, + mode.flags, mode.dither, + mse[0], mse[1], mse[2], mse[3]); + + if (!mse_ref) { + /* Compare against the legacy swscale API as a reference */ + if (scale_legacy(dst, src, mode) < 0) { + fprintf(stderr, "Failed ref %s ---> %s\n", av_get_pix_fmt_name(src->format), + av_get_pix_fmt_name(dst->format)); + goto error; } - ssdY /= w * h; - ssdU /= w * h / 4; - ssdV /= w * h / 4; - ssdA /= w * h; - - sws_freeContext(outContext); + if (sws_scale_frame(sws[2], out, dst) < 0) + goto error; - for (i = 0; i < 4; i++) - if (refStride[i]) - av_free(out[i]); + get_mse(mse_sws, out, ref, comps); + mse_ref = mse_sws; } - if(r){ - if(ssdY>r->ssdY*1.02+1 || ssdU>r->ssdU*1.02+1 || ssdV>r->ssdV*1.02+1|| ssdA>r->ssdA*1.02+1) - printf("WORSE SSD=%5"PRId64",%5"PRId64",%5"PRId64",%5"PRId64"", - r->ssdY, r->ssdU, r->ssdV, r->ssdA); - else if(ssdY>r->ssdY || ssdU>r->ssdU || ssdV>r->ssdV|| ssdA>r->ssdA) - printf("worse SSD=%5"PRId64",%5"PRId64",%5"PRId64",%5"PRId64"", - r->ssdY, r->ssdU, r->ssdV, r->ssdA); + for (int i = 0; i < 4; i++) { + if (mse[i] > mse_ref[i]) { + int bad = mse[i] > mse_ref[i] * 1.02 + 1; + printf("\033[1;31m %s, ref MSE={%5d %5d %5d %5d}\033[0m\n", + bad ? "WORSE" : "worse", + mse_ref[0], mse_ref[1], mse_ref[2], mse_ref[3]); + if (bad) + goto error; + break; + } } - printf(" CRC=%08x SSD=%5"PRId64 ",%5"PRId64 ",%5"PRId64 ",%5"PRId64 "\n", - crc, ssdY, ssdU, ssdV, ssdA); - -end: - sws_freeContext(dstContext); - - for (i = 0; i < 4; i++) - if (dstStride[i]) - av_free(dst[i]); - - return !!res; + fflush(stdout); + ret = 0; /* fall through */ + error: + av_frame_free(&src); + av_frame_free(&dst); + av_frame_free(&out); + return ret; } -static void selfTest(const uint8_t * const ref[4], int refStride[4], - int w, int h, - enum AVPixelFormat srcFormat_in, - enum AVPixelFormat dstFormat_in) +static int run_self_tests(const AVFrame *ref, struct options opts) { - const int flags[] = { SWS_FAST_BILINEAR, - SWS_BILINEAR, SWS_BICUBIC, - SWS_X|SWS_BITEXACT , SWS_POINT , SWS_AREA|SWS_ACCURATE_RND, - SWS_BICUBIC|SWS_FULL_CHR_H_INT|SWS_FULL_CHR_H_INP, 0}; - const int srcW = w; - const int srcH = h; - const int dstW[] = { srcW - srcW / 3, srcW, srcW + srcW / 3, 0 }; - const int dstH[] = { srcH - srcH / 3, srcH, srcH + srcH / 3, 0 }; - enum AVPixelFormat srcFormat, dstFormat; - const AVPixFmtDescriptor *desc_src, *desc_dst; - - for (srcFormat = srcFormat_in != AV_PIX_FMT_NONE ? srcFormat_in : 0; - srcFormat < AV_PIX_FMT_NB; srcFormat++) { - if (!sws_isSupportedInput(srcFormat) || - !sws_isSupportedOutput(srcFormat)) + enum AVPixelFormat src_fmt, dst_fmt, + src_fmt_min = 0, + dst_fmt_min = 0, + src_fmt_max = AV_PIX_FMT_NB - 1, + dst_fmt_max = AV_PIX_FMT_NB - 1; + + if (opts.src_fmt != AV_PIX_FMT_NONE) + src_fmt_min = src_fmt_max = opts.src_fmt; + if (opts.dst_fmt != AV_PIX_FMT_NONE) + dst_fmt_min = dst_fmt_max = opts.dst_fmt; + + for (src_fmt = src_fmt_min; src_fmt <= src_fmt_max; src_fmt++) { + if (!sws_test_format(src_fmt, 0) || !sws_test_format(src_fmt, 1)) continue; - - desc_src = av_pix_fmt_desc_get(srcFormat); - - for (dstFormat = dstFormat_in != AV_PIX_FMT_NONE ? dstFormat_in : 0; - dstFormat < AV_PIX_FMT_NB; dstFormat++) { - int i, j, k; - int res = 0; - - if (!sws_isSupportedInput(dstFormat) || - !sws_isSupportedOutput(dstFormat)) + for (dst_fmt = dst_fmt_min; dst_fmt <= dst_fmt_max; dst_fmt++) { + if (!sws_test_format(dst_fmt, 0) || !sws_test_format(dst_fmt, 1)) continue; - - desc_dst = av_pix_fmt_desc_get(dstFormat); - - printf("%s -> %s\n", desc_src->name, desc_dst->name); - fflush(stdout); - - for (k = 0; flags[k] && !res; k++) - for (i = 0; dstW[i] && !res; i++) - for (j = 0; dstH[j] && !res; j++) - res = doTest(ref, refStride, w, h, - srcFormat, dstFormat, - srcW, srcH, dstW[i], dstH[j], flags[k], - NULL); - if (dstFormat_in != AV_PIX_FMT_NONE) - break; + for (int h = 0; h < FF_ARRAY_ELEMS(dst_h); h++) + for (int w = 0; w < FF_ARRAY_ELEMS(dst_w); w++) + for (int m = 0; m < FF_ARRAY_ELEMS(modes); m++) { + if (ff_sfc64_get(&prng_state) > UINT64_MAX * opts.prob) + continue; + if (run_test(src_fmt, dst_fmt, dst_w[w], dst_h[h], + modes[m], ref, NULL) < 0) + return -1; + } } - if (srcFormat_in != AV_PIX_FMT_NONE) - break; } + + return 0; } -static int fileTest(const uint8_t * const ref[4], int refStride[4], - int w, int h, FILE *fp, - enum AVPixelFormat srcFormat_in, - enum AVPixelFormat dstFormat_in) +static int run_file_tests(const AVFrame *ref, FILE *fp, struct options opts) { char buf[256]; + int ret; while (fgets(buf, sizeof(buf), fp)) { - struct Results r; - enum AVPixelFormat srcFormat; - char srcStr[21]; - int srcW = 0, srcH = 0; - enum AVPixelFormat dstFormat; - char dstStr[21]; - int dstW = 0, dstH = 0; - int flags; - int ret; + char src_fmt_str[20], dst_fmt_str[20]; + enum AVPixelFormat src_fmt; + enum AVPixelFormat dst_fmt; + int sw, sh, dw, dh, mse[4]; + struct mode mode; ret = sscanf(buf, - " %20s %dx%d -> %20s %dx%d flags=%d CRC=%x" - " SSD=%"SCNu64 ", %"SCNu64 ", %"SCNu64 ", %"SCNu64 "\n", - srcStr, &srcW, &srcH, dstStr, &dstW, &dstH, - &flags, &r.crc, &r.ssdY, &r.ssdU, &r.ssdV, &r.ssdA); - if (ret != 12) { - srcStr[0] = dstStr[0] = 0; - ret = sscanf(buf, "%20s -> %20s\n", srcStr, dstStr); + " %20s %dx%d -> %20s %dx%d, flags=%u dither=%u, " + "MSE={%d %d %d %d}\n", + src_fmt_str, &sw, &sh, dst_fmt_str, &dw, &dh, + &mode.flags, &mode.dither, + &mse[0], &mse[1], &mse[2], &mse[3]); + if (ret != 13) { + printf("%s", buf); + continue; } - srcFormat = av_get_pix_fmt(srcStr); - dstFormat = av_get_pix_fmt(dstStr); - - if (srcFormat == AV_PIX_FMT_NONE || dstFormat == AV_PIX_FMT_NONE || - srcW > 8192U || srcH > 8192U || dstW > 8192U || dstH > 8192U) { + src_fmt = av_get_pix_fmt(src_fmt_str); + dst_fmt = av_get_pix_fmt(dst_fmt_str); + if (src_fmt == AV_PIX_FMT_NONE || dst_fmt == AV_PIX_FMT_NONE || + sw != ref->width || sh != ref->height || dw > 8192 || dh > 8192 || + mode.dither >= SWS_DITHER_NB) { fprintf(stderr, "malformed input file\n"); return -1; } - if ((srcFormat_in != AV_PIX_FMT_NONE && srcFormat_in != srcFormat) || - (dstFormat_in != AV_PIX_FMT_NONE && dstFormat_in != dstFormat)) - continue; - if (ret != 12) { - printf("%s", buf); + + if (opts.src_fmt != AV_PIX_FMT_NONE && src_fmt != opts.src_fmt || + opts.dst_fmt != AV_PIX_FMT_NONE && dst_fmt != opts.dst_fmt) continue; - } - doTest(ref, refStride, w, h, - srcFormat, dstFormat, - srcW, srcH, dstW, dstH, flags, - &r); + if (run_test(src_fmt, dst_fmt, dw, dh, mode, ref, mse) < 0) + return -1; } return 0; } -#define W 96 -#define H 96 - int main(int argc, char **argv) { - enum AVPixelFormat srcFormat = AV_PIX_FMT_NONE; - enum AVPixelFormat dstFormat = AV_PIX_FMT_NONE; - uint8_t *rgb_data = av_malloc(W * H * 4); - const uint8_t * const rgb_src[4] = { rgb_data, NULL, NULL, NULL }; - int rgb_stride[4] = { 4 * W, 0, 0, 0 }; - uint8_t *data = av_malloc(4 * W * H); - const uint8_t * const src[4] = { data, data + W * H, data + W * H * 2, data + W * H * 3 }; - int stride[4] = { W, W, W, W }; - int x, y; - SwsContext *sws; - AVLFG rand; - int res = -1; - int i; - FILE *fp = NULL; + struct options opts = { + .src_fmt = AV_PIX_FMT_NONE, + .dst_fmt = AV_PIX_FMT_NONE, + .prob = 1.0, + }; - if (!rgb_data || !data) - return -1; + AVFrame *rgb = NULL, *ref = NULL; + FILE *fp = NULL; + AVLFG rand; + int ret = -1; - for (i = 1; i < argc; i += 2) { + for (int i = 1; i < argc; i += 2) { if (!strcmp(argv[i], "-help") || !strcmp(argv[i], "--help")) { fprintf(stderr, "swscale [options...]\n" " -help\n" " This text\n" " -ref \n" - " Uses file as reference to compae tests againsts. Tests that have become worse will contain the string worse or WORSE\n" + " Uses file as reference to compare tests againsts. Tests that have become worse will contain the string worse or WORSE\n" " -p \n" " The percentage of tests or comparisons to perform. Doing all tests will take long and generate over a hundred MB text output\n" " It is often convenient to perform a random subset\n" @@ -454,7 +338,7 @@ int main(int argc, char **argv) " -cpuflags \n" " Uses the specified cpuflags in the tests\n" ); - goto error; + return 0; } if (argv[i][0] != '-' || i + 1 == argc) goto bad_option; @@ -466,26 +350,26 @@ int main(int argc, char **argv) } } else if (!strcmp(argv[i], "-cpuflags")) { unsigned flags = av_get_cpu_flags(); - int ret = av_parse_cpu_caps(&flags, argv[i + 1]); - if (ret < 0) { + int res = av_parse_cpu_caps(&flags, argv[i + 1]); + if (res < 0) { fprintf(stderr, "invalid cpu flags %s\n", argv[i + 1]); - return ret; + goto error; } av_force_cpu_flags(flags); } else if (!strcmp(argv[i], "-src")) { - srcFormat = av_get_pix_fmt(argv[i + 1]); - if (srcFormat == AV_PIX_FMT_NONE) { + opts.src_fmt = av_get_pix_fmt(argv[i + 1]); + if (opts.src_fmt == AV_PIX_FMT_NONE) { fprintf(stderr, "invalid pixel format %s\n", argv[i + 1]); - return -1; + goto error; } } else if (!strcmp(argv[i], "-dst")) { - dstFormat = av_get_pix_fmt(argv[i + 1]); - if (dstFormat == AV_PIX_FMT_NONE) { + opts.dst_fmt = av_get_pix_fmt(argv[i + 1]); + if (opts.dst_fmt == AV_PIX_FMT_NONE) { fprintf(stderr, "invalid pixel format %s\n", argv[i + 1]); - return -1; + goto error; } } else if (!strcmp(argv[i], "-p")) { - prob = atof(argv[i + 1]); + opts.prob = atof(argv[i + 1]); } else { bad_option: fprintf(stderr, "bad option or argument missing (%s) see -help\n", argv[i]); @@ -494,32 +378,51 @@ bad_option: } ff_sfc64_init(&prng_state, 0, 0, 0, 12); - - sws = sws_getContext(W / 12, H / 12, AV_PIX_FMT_RGB32, W, H, - AV_PIX_FMT_YUVA420P, SWS_BILINEAR, NULL, NULL, NULL); - av_lfg_init(&rand, 1); - for (y = 0; y < H; y++) - for (x = 0; x < W * 4; x++) - rgb_data[ x + y * 4 * W] = av_lfg_get(&rand); - res = sws_scale(sws, rgb_src, rgb_stride, 0, H / 12, (uint8_t * const *) src, stride); - if (res < 0 || res != H) { - res = -1; - goto error; + for (int i = 0; i < 3; i++) { + sws[i] = sws_alloc_context(); + if (!sws[i]) + goto error; + sws[i]->flags = SWS_BILINEAR; } - sws_freeContext(sws); - av_free(rgb_data); - if(fp) { - res = fileTest(src, stride, W, H, fp, srcFormat, dstFormat); - fclose(fp); - } else { - selfTest(src, stride, W, H, srcFormat, dstFormat); - res = 0; + rgb = av_frame_alloc(); + if (!rgb) + goto error; + rgb->width = WIDTH / 12; + rgb->height = HEIGHT / 12; + rgb->format = AV_PIX_FMT_RGBA; + if (av_frame_get_buffer(rgb, 32) < 0) + goto error; + + for (int y = 0; y < rgb->height; y++) { + for (int x = 0; x < rgb->width; x++) { + for (int c = 0; c < 4; c++) + rgb->data[0][y * rgb->linesize[0] + x * 4 + c] = av_lfg_get(&rand); + } } -error: - av_free(data); - return res; + ref = av_frame_alloc(); + if (!ref) + goto error; + ref->width = WIDTH; + ref->height = HEIGHT; + ref->format = AV_PIX_FMT_YUVA420P; + + if (sws_scale_frame(sws[0], ref, rgb) < 0) + goto error; + + ret = fp ? run_file_tests(ref, fp, opts) + : run_self_tests(ref, opts); + + /* fall through */ +error: + for (int i = 0; i < 3; i++) + sws_free_context(&sws[i]); + av_frame_free(&rgb); + av_frame_free(&ref); + if (fp) + fclose(fp); + return ret; }