From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: <ffmpeg-devel-bounces@ffmpeg.org> Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTPS id 456364DE56 for <ffmpegdev@gitmailbox.com>; Wed, 30 Apr 2025 21:09:41 +0000 (UTC) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id D9555687A8E; Thu, 1 May 2025 00:09:36 +0300 (EEST) Received: from mail-wm1-f53.google.com (mail-wm1-f53.google.com [209.85.128.53]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id A8D94687A8E for <ffmpeg-devel@ffmpeg.org>; Thu, 1 May 2025 00:09:29 +0300 (EEST) Received: by mail-wm1-f53.google.com with SMTP id 5b1f17b1804b1-43cf628cb14so11181805e9.1 for <ffmpeg-devel@ffmpeg.org>; Wed, 30 Apr 2025 14:09:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=jkqxz-net.20230601.gappssmtp.com; s=20230601; t=1746047369; x=1746652169; darn=ffmpeg.org; h=content-transfer-encoding:mime-version:message-id:date:subject:to :from:from:to:cc:subject:date:message-id:reply-to; bh=m9V4uP2Knpz3M4YN+ubHBqe84FXXu/Ap9xL++bEYViE=; b=CuQ9u3eT/j2AjD0Hap0lA7TPSQ1oMFjuU+7A88HePKAAxo5pQZlX9i3tqammt6e0xx Yj9wBuWIUF47sjuVOPXhCPd7tGp3VwYETbEmKJsbaXl+uPjROtmljd1xD0IFCiSyBOyQ yu9Ra4Qk4UDyiXgtm8UEzkDDL+sgLUsh8hUEfZ7OtEfMiKNydFwFABK04AP8gWBFOq4L R1liYhcHpNrPdPGc+RIh8By/Nm8k87ncsG2gTSvch/03ib5AgQiz4OSHoiX5o2sa+EGh Mk4xCeuPfKtPLdz8q8KytNns+wcsFga+YoZkbMBQoKuFGX2C2bXNLDnvRQkhJjTqycwB o7AQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1746047369; x=1746652169; h=content-transfer-encoding:mime-version:message-id:date:subject:to :from:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=m9V4uP2Knpz3M4YN+ubHBqe84FXXu/Ap9xL++bEYViE=; b=e0id4TMT0jBEh1kO88hrwHevfNzymPMDQjwKK4IZayel1p4f824nGJk486bMxvZghF 6ZvUuJZohZ4Zb729ImonC7Sd4BFtV8FauXiqgkr5gc1U0bOzJJlPgt/5T5wWWRo8N5Hc vREK+bBzwgbePTG0bXIWvOzRVByCJUB2fBnIQ2TnMwCPdzG0GT+AvKSZbH438nbM3Kl9 uic6MxZlsEX39UqwVQyBvesyiRp5RZyBjNLPmnP0pfflt0VmOdZBLYfzj4pm+Zml0FYZ YmN3pjBPQb0SzDCXf6P5WE9pi+4rXvWbq7pNmXCLDGYKZp+xw+GtQ9LfhqJ/rxrZx5qm AUkQ== X-Gm-Message-State: AOJu0Yz0MxSSejbfIB4YzSCpO5IpM16Z2F8bubuJk8MmZWK7HOrnma7a OU99GPjdOtwUrPKVQdvvB/McsKNURMa/ilDbvg9VbutkV4IB3TgwuztFJmVKuKIbIPKginvOtWU 1JuREwQ== X-Gm-Gg: ASbGncuObDD9Y8ZUuW0unQ/rRrmkcO52sHc14yR9n77y2vkBC3JCcDUPF3J5BY1uyPS ds83+BZu3fMzGiIGfQnuvt8gNLQz9UAXMTxWJf0JDDacXT/xUN2BBvZKxKpcjB4uLlUpnDPZcZ0 lNifCGH0LaLoyxaNGBCyor0n3Dn5+KFBMchx+4+cluk5oy7ccwG9nGoPkb2+djmKA3bHN+R5NdP Znb9paI0af6iHCcMgAZTAkws4AY9p7MIwYmPZpRJZcR+I4lDg49EjN8dNM/J5BN/iK7DG3gum68 pX/W9VGSN1yncZRH1yPNQ/mieXoObcSFsPQ/bsn5TQ2+Esu/kG6QuG+ZW3gb0pKkIaOBhShrTcc CFck5cTKi7D/mluGvfwww6Ek= X-Google-Smtp-Source: AGHT+IF/5sIOJ/9+LqAt/+aZBzBXKZSYkblDJFWVtHlLBDhaGNqE2VqAUiWMykEboZDzNtN31FR6Pg== X-Received: by 2002:a05:600c:3c82:b0:43c:f680:5c2e with SMTP id 5b1f17b1804b1-441b5cb9ef1mr8824145e9.13.1746047368645; Wed, 30 Apr 2025 14:09:28 -0700 (PDT) Received: from localhost.localdomain (cpc92320-cmbg19-2-0-cust719.5-4.cable.virginm.net. [82.13.66.208]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-441b2b97288sm37582405e9.6.2025.04.30.14.09.28 for <ffmpeg-devel@ffmpeg.org> (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Apr 2025 14:09:28 -0700 (PDT) From: Mark Thompson <sw@jkqxz.net> To: ffmpeg-devel@ffmpeg.org Date: Wed, 30 Apr 2025 22:09:11 +0100 Message-ID: <20250430210917.729056-1-sw@jkqxz.net> X-Mailer: git-send-email 2.47.2 MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH] lavc: Add unit test for APV entropy decode X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches <ffmpeg-devel.ffmpeg.org> List-Unsubscribe: <https://ffmpeg.org/mailman/options/ffmpeg-devel>, <mailto:ffmpeg-devel-request@ffmpeg.org?subject=unsubscribe> List-Archive: <https://ffmpeg.org/pipermail/ffmpeg-devel> List-Post: <mailto:ffmpeg-devel@ffmpeg.org> List-Help: <mailto:ffmpeg-devel-request@ffmpeg.org?subject=help> List-Subscribe: <https://ffmpeg.org/mailman/listinfo/ffmpeg-devel>, <mailto:ffmpeg-devel-request@ffmpeg.org?subject=subscribe> Reply-To: FFmpeg development discussions and patches <ffmpeg-devel@ffmpeg.org> Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" <ffmpeg-devel-bounces@ffmpeg.org> Archived-At: <https://master.gitmailbox.com/ffmpegdev/20250430210917.729056-1-sw@jkqxz.net/> List-Archive: <https://master.gitmailbox.com/ffmpegdev/> List-Post: <mailto:ffmpegdev@gitmailbox.com> --- Cleaned up a bit from use earlier. This program allows testing of changes to entropy decode in a form much easier to debug than a whole stream. I had a more complete randomised test of transform here as well along with test code for checking intermediates, though it seems like the checkasm only might be preferred. Can add it if useful. Thanks, - Mark libavcodec/Makefile | 1 + libavcodec/tests/apv.c | 306 ++++++++++++++++++++++++++++++++++++++ tests/fate/libavcodec.mak | 5 + 3 files changed, 312 insertions(+) create mode 100644 libavcodec/tests/apv.c diff --git a/libavcodec/Makefile b/libavcodec/Makefile index e674671460..a98621aac4 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -1327,6 +1327,7 @@ TESTPROGS = avcodec \ jpeg2000dwt \ mathops \ +TESTPROGS-$(CONFIG_APV_DECODER) += apv TESTPROGS-$(CONFIG_AV1_VAAPI_ENCODER) += av1_levels TESTPROGS-$(CONFIG_CABAC) += cabac TESTPROGS-$(CONFIG_GOLOMB) += golomb diff --git a/libavcodec/tests/apv.c b/libavcodec/tests/apv.c new file mode 100644 index 0000000000..6d37014f58 --- /dev/null +++ b/libavcodec/tests/apv.c @@ -0,0 +1,306 @@ +/* + * 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 + */ + +#include "libavutil/lfg.h" +#include "libavutil/random_seed.h" + +#include "libavcodec/apv_decode.h" +#include "libavcodec/apv_dsp.h" +#include "libavcodec/put_bits.h" + + +// As defined in 7.1.4, for testing. +// Adds a check to limit loop after reading 16 zero bits to avoid +// getting stuck reading a stream of zeroes forever (this matches +// the behaviour of the faster version). + +static unsigned int apv_read_vlc_spec(GetBitContext *gbc, int k_param) +{ + unsigned int symbol_value = 0; + int parse_exp_golomb = 1; + int k = k_param; + int stop_loop = 0; + + if(get_bits1(gbc) == 1) { + parse_exp_golomb = 0; + } else { + if (get_bits1(gbc) == 0) { + symbol_value += (1 << k); + parse_exp_golomb = 0; + } else { + symbol_value += (2 << k); + parse_exp_golomb = 1; + } + } + if (parse_exp_golomb) { + int read_limit = 0; + do { + if (get_bits1(gbc) == 1) { + stop_loop = 1; + } else { + if (++read_limit == 16) + break; + symbol_value += (1 << k); + k++; + } + } while (!stop_loop); + } + if (k > 0) + symbol_value += get_bits(gbc, k); + + return symbol_value; +} + +// As defined in 7.2.4, for testing. + +static void apv_write_vlc_spec(PutBitContext *pbc, + unsigned int symbol_val, int k_param) +{ + int prefix_vlc_table[3][2] = {{1, 0}, {0, 0}, {0, 1}}; + + unsigned int symbol_value = symbol_val; + int val_prefix_vlc = av_clip(symbol_val >> k_param, 0, 2); + int bit_count = 0; + int k = k_param; + + while (symbol_value >= (1 << k)) { + symbol_value -= (1 << k); + if (bit_count < 2) + put_bits(pbc, 1, prefix_vlc_table[val_prefix_vlc][bit_count]); + else + put_bits(pbc, 1, 0); + if (bit_count >= 2) + ++k; + ++bit_count; + } + + if(bit_count < 2) + put_bits(pbc, 1, prefix_vlc_table[val_prefix_vlc][bit_count]); + else + put_bits(pbc, 1, 1); + + if(k > 0) + put_bits(pbc, k, symbol_value); +} + +static void binary(char *buf, uint32_t value, int bits) +{ + for (int i = 0; i < bits; i++) + buf[i] = (value >> (bits - i - 1) & 1) ? '1' : '0'; + buf[bits] = '\0'; +} + +static int test_apv_read_vlc(void) +{ + APVVLCLUT lut; + int err = 0; + + ff_apv_entropy_build_decode_lut(&lut); + + // Generate all possible 20 bit sequences (padded with zeroes), then + // verify that spec and improved parsing functions get the same result + // and consume the same number of bits for each possible k_param. + + for (int k = 0; k <= 5; k++) { + for (uint32_t b = 0; b < (1 << 20); b++) { + uint8_t buf[8] = { + b >> 12, + b >> 4, + b << 4, + 0, 0, 0, 0, 0 + }; + + GetBitContext gbc_test, gbc_spec; + unsigned int res_test, res_spec; + int con_test, con_spec; + + init_get_bits8(&gbc_test, buf, 8); + init_get_bits8(&gbc_spec, buf, 8); + + res_test = ff_apv_read_vlc (&gbc_test, k, &lut); + res_spec = apv_read_vlc_spec(&gbc_spec, k); + + con_test = get_bits_count(&gbc_test); + con_spec = get_bits_count(&gbc_spec); + + if (res_test != res_spec || + con_test != con_spec) { + char str[21]; + binary(str, b, 20); + av_log(NULL, AV_LOG_ERROR, + "Mismatch reading %s (%d) with k=%d:\n", str, b, k); + av_log(NULL, AV_LOG_ERROR, + "Test function result %d consumed %d bits.\n", + res_test, con_test); + av_log(NULL, AV_LOG_ERROR, + "Spec function result %d consumed %d bits.\n", + res_spec, con_spec); + ++err; + if (err > 10) + return err; + } + } + } + + return err; +} + +static int random_coeff(AVLFG *lfg) +{ + // Geometric distribution of code lengths (1-14 bits), + // uniform distribution within codes of the length, + // equal probability of either sign. + int length = (av_lfg_get(lfg) / (UINT_MAX / 14 + 1)); + int random = av_lfg_get(lfg); + int value = (1 << length) + (random & (1 << length) - 1); + if (random & (1 << length)) + return value; + else + return -value; +} + +static int random_run(AVLFG *lfg) +{ + // Expoenential distrbution of run lengths. + unsigned int random = av_lfg_get(lfg); + for (int len = 0;; len++) { + if (random & (1 << len)) + return len; + } + // You rolled zero on a 2^32 sided die; well done! + return 64; +} + +static int test_apv_entropy_decode_block(void) +{ + // Generate random entropy blocks, code them, then ensure they + // decode to the same block. Coefficients are weighted to have + // roughly equal numbers of each code length. + + APVVLCLUT decode_lut; + AVLFG lfg; + unsigned int seed = av_get_random_seed(); + av_lfg_init(&lfg, seed); + + av_log(NULL, AV_LOG_INFO, "seed = %u\n", seed); + + ff_apv_entropy_build_decode_lut(&decode_lut); + + for (int t = 0; t < 100; t++) { + APVEntropyState state; + int16_t block[64]; + int16_t block_test[64]; + uint8_t buffer[1024]; + PutBitContext pbc; + GetBitContext gbc; + int bits_written; + int pos, run, coeff, level, err; + int k_dc, k_run, k_level; + + memset(block, 0, sizeof(block)); + memset(buffer, 0, sizeof(buffer)); + init_put_bits(&pbc, buffer, sizeof(buffer)); + + // Randomly-constructed state. + state.prev_dc = random_coeff(&lfg); + state.prev_dc_diff = random_coeff(&lfg); + state.prev_1st_ac_level = random_coeff(&lfg); + + state.decode_lut = &decode_lut; + + k_dc = av_clip(state.prev_dc_diff >> 1, 0, 5); + k_run = 0; + k_level = av_clip(state.prev_1st_ac_level >> 2, 0, 4); + + coeff = random_coeff(&lfg) / 2; + block[ff_zigzag_direct[0]] = state.prev_dc + coeff; + apv_write_vlc_spec(&pbc, FFABS(coeff), k_dc); + if (coeff != 0) + put_bits(&pbc, 1, coeff < 0); + + pos = 1; + while (pos < 64) { + run = random_run(&lfg); + if (pos + run > 64) + run = 64 - pos; + apv_write_vlc_spec(&pbc, run, k_run); + k_run = av_clip(run >> 2, 0, 2); + pos += run; + if (pos < 64) { + coeff = random_coeff(&lfg); + level = FFABS(coeff) - 1; + block[ff_zigzag_direct[pos]] = coeff; + apv_write_vlc_spec(&pbc, level, k_level); + put_bits(&pbc, 1, coeff < 0); + k_level = av_clip((level + 1) >> 2, 0, 4); + ++pos; + } + } + bits_written = put_bits_count(&pbc); + flush_put_bits(&pbc); + + // Fill output blocm with a distinctive error value. + for (int i = 0; i < 64; i++) + block_test[i] = -9999; + init_get_bits8(&gbc, buffer, sizeof(buffer)); + + err = ff_apv_entropy_decode_block(block_test, &gbc, &state); + if (err < 0) { + av_log(NULL, AV_LOG_ERROR, "Entropy decode returned error.\n"); + return 1; + } else { + int bits_read = get_bits_count(&gbc); + if (bits_written != bits_read) { + av_log(NULL, AV_LOG_ERROR, "Wrote %d bits but read %d.\n", + bits_written, bits_read); + return 1; + } else { + err = 0; + for (int i = 0; i < 64; i++) { + if (block[i] != block_test[i]) + ++err; + } + if (err > 0) { + av_log(NULL, AV_LOG_ERROR, "%d mismatches in output block.\n", err); + return err; + } + } + } + } + + return 0; +} + +int main(void) +{ + int err; + + err = test_apv_read_vlc(); + if (err) { + av_log(NULL, AV_LOG_ERROR, "Read VLC test failed.\n"); + return err; + } + + err = test_apv_entropy_decode_block(); + if (err) { + av_log(NULL, AV_LOG_ERROR, "Entropy decode block test failed.\n"); + return err; + } + + return 0; +} diff --git a/tests/fate/libavcodec.mak b/tests/fate/libavcodec.mak index ef6e6ec40e..5f7ec97ff3 100644 --- a/tests/fate/libavcodec.mak +++ b/tests/fate/libavcodec.mak @@ -3,6 +3,11 @@ fate-av1-levels: libavcodec/tests/av1_levels$(EXESUF) fate-av1-levels: CMD = run libavcodec/tests/av1_levels$(EXESUF) fate-av1-levels: REF = /dev/null +FATE_LIBAVCODEC-$(CONFIG_APV_DECODER) += fate-apv +fate-apv: libavcodec/tests/apv$(EXESUF) +fate-apv: CMD = run libavcodec/tests/apv$(EXESUF) +fate-apv: REF = /dev/null + FATE_LIBAVCODEC-yes += fate-avpacket fate-avpacket: libavcodec/tests/avpacket$(EXESUF) fate-avpacket: CMD = run libavcodec/tests/avpacket$(EXESUF) -- 2.47.2 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".