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 1783A4D98C
	for <ffmpegdev@gitmailbox.com>; Mon, 21 Apr 2025 15:25:48 +0000 (UTC)
Received: from [127.0.1.1] (localhost [127.0.0.1])
	by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id B8F6B688001;
	Mon, 21 Apr 2025 18:25:03 +0300 (EEST)
Received: from mail-wm1-f44.google.com (mail-wm1-f44.google.com
 [209.85.128.44])
 by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 526D0687DBB
 for <ffmpeg-devel@ffmpeg.org>; Mon, 21 Apr 2025 18:24:53 +0300 (EEST)
Received: by mail-wm1-f44.google.com with SMTP id
 5b1f17b1804b1-43ea40a6e98so37263795e9.1
 for <ffmpeg-devel@ffmpeg.org>; Mon, 21 Apr 2025 08:24:53 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
 d=jkqxz-net.20230601.gappssmtp.com; s=20230601; t=1745249093; x=1745853893;
 darn=ffmpeg.org; 
 h=content-transfer-encoding:mime-version:references:in-reply-to
 :message-id:date:subject:to:from:from:to:cc:subject:date:message-id
 :reply-to; bh=XSdS4Qat7YrdyKFCzyOsYil0TWvliH9plSa8VpegVC0=;
 b=s4AvJZUa6UjuQJ0XGWcPOZKs7iX8BIFe4eZWFmav9VBJ0Nt+SBcqRfgfX9/Wue5EwT
 6ve5XaRjXDqoZteyAP2rYsFmPDmIwfNKaqRUS1dOq25BOU3WNrST3En9AMG5d6KVZoz3
 D203n9WbuHvBtOieZK5YEdf2k2iXP8oBbLYa/s9z8fW28J+9lq3Qloxs889NAOUuzbHQ
 3Zqqd3HTFCsSBGeb/gz7soT6q0Jl6gHh3AWenr1EWgKJZHouZkMmVfOi9SS6QtVJJony
 4iq24zT4r0FGvvNHTl53zzAkCP8/Bf8c878KkNYj4DmiLj0C4870f7MYvIDGsWdEQHZ8
 TXxw==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
 d=1e100.net; s=20230601; t=1745249093; x=1745853893;
 h=content-transfer-encoding:mime-version:references:in-reply-to
 :message-id:date:subject:to:from:x-gm-message-state:from:to:cc
 :subject:date:message-id:reply-to;
 bh=XSdS4Qat7YrdyKFCzyOsYil0TWvliH9plSa8VpegVC0=;
 b=TgG/oOZUuyMiZ2ywgf4nbbiA7Quriz3P4AQsXKuqv+uCV7ICYiy5GxYQ5ruZZlirgT
 aTa9lQETldb1b312ZLp+lI2nQaRq2DR/8k5sviBClcU1eQGwsJo55fzyWTdGzPd/3ad5
 3JUOSlXpWVfVHomSoP1uEvFQ8hg/8Z75gRF74hrfb232XsI0gsv1YFhkX6eaEEGpaP9a
 tyCAK80nPaf8xFbZO95jRsNA41vcAfwDD1kW428Z/wwpcQXWG0aiSiXlZlMnQyDA1HZt
 noOXP5si4DeiZL2rcVD9cSu5KP5EoqZH4iBHXMCFGS5lJ8BM1LS6O4J4O+4bLzoYJEjr
 WM1g==
X-Gm-Message-State: AOJu0Yy81xmkqHmzRWgB1A1cxyQrd18OJ5XcQeM3hjmJ7w+2BPtH77XI
 WOft52H8+0u3d/TiFSjUgo6qBXD6666gY75LDhKOmI9uHdxqKstlhf5KkkyYSoyob5YQ0wWsS6y
 R
X-Gm-Gg: ASbGncse2vHZH7ycdowj9/1DYyLUuEa8bXYQ4FY00Z6mv2D85tSkZTUs/aOZyhF/IAJ
 lGstlMWw4HXf2aGtiz1V1gZWr1naRZcXNuhmk6WayUtPh1fStObyXwDGIfELqCfRwKLGF2w3Tpu
 75mMLSB7uONmcHaKnJIx6M3Cmvuy76GXC6Mn1MorWd6/FbHXXVu1HM4h0bAOo1Y/3neDwlWZO36
 OsyY11mOw2GUiZd2y4GMQ/oaLmPjV7qSVp9/YYo9VSn03OqIOdCjmbAbJeJinS1cWHCldc9xuG/
 VCXewb91WAdpyUI/JlabkSR2Y8IK625t+nFQjw9QAmDFBa70VHsLa3gtQE77OSPLP5kyF4rS3kz
 ijzG1lHBlR1yGeXUYTI3VOvs=
X-Google-Smtp-Source: AGHT+IFb3CpKT/0IkTgABm8TioyBow0GaA+ch4VBW/BySEbWlL48cXAUI/FHg8NOm3ZJ3f1UzVhAug==
X-Received: by 2002:a05:600c:3592:b0:439:9e13:2dd7 with SMTP id
 5b1f17b1804b1-4408188366cmr41889465e9.2.1745249092441; 
 Mon, 21 Apr 2025 08:24:52 -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-4406d5bbc07sm140657145e9.17.2025.04.21.08.24.52
 for <ffmpeg-devel@ffmpeg.org>
 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
 Mon, 21 Apr 2025 08:24:52 -0700 (PDT)
From: Mark Thompson <sw@jkqxz.net>
To: ffmpeg-devel@ffmpeg.org
Date: Mon, 21 Apr 2025 16:24:35 +0100
Message-ID: <20250421152445.2110045-5-sw@jkqxz.net>
X-Mailer: git-send-email 2.47.2
In-Reply-To: <20250421152445.2110045-1-sw@jkqxz.net>
References: <20250421152445.2110045-1-sw@jkqxz.net>
MIME-Version: 1.0
Subject: [FFmpeg-devel] [PATCH v2 4/6] lavc: APV decoder
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/20250421152445.2110045-5-sw@jkqxz.net/>
List-Archive: <https://master.gitmailbox.com/ffmpegdev/>
List-Post: <mailto:ffmpegdev@gitmailbox.com>

---
 configure                |   1 +
 libavcodec/Makefile      |   1 +
 libavcodec/allcodecs.c   |   1 +
 libavcodec/apv_decode.c  | 331 +++++++++++++++++++++++++++++++++++++++
 libavcodec/apv_decode.h  |  80 ++++++++++
 libavcodec/apv_dsp.c     | 136 ++++++++++++++++
 libavcodec/apv_dsp.h     |  37 +++++
 libavcodec/apv_entropy.c | 200 +++++++++++++++++++++++
 8 files changed, 787 insertions(+)
 create mode 100644 libavcodec/apv_decode.c
 create mode 100644 libavcodec/apv_decode.h
 create mode 100644 libavcodec/apv_dsp.c
 create mode 100644 libavcodec/apv_dsp.h
 create mode 100644 libavcodec/apv_entropy.c

diff --git a/configure b/configure
index ca404d2797..ee270b770c 100755
--- a/configure
+++ b/configure
@@ -2935,6 +2935,7 @@ apng_decoder_select="inflate_wrapper"
 apng_encoder_select="deflate_wrapper llvidencdsp"
 aptx_encoder_select="audio_frame_queue"
 aptx_hd_encoder_select="audio_frame_queue"
+apv_decoder_select="cbs_apv"
 asv1_decoder_select="blockdsp bswapdsp idctdsp"
 asv1_encoder_select="aandcttables bswapdsp fdctdsp pixblockdsp"
 asv2_decoder_select="blockdsp bswapdsp idctdsp"
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index a5f5c4e904..e674671460 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -244,6 +244,7 @@ OBJS-$(CONFIG_APTX_HD_DECODER)         += aptxdec.o aptx.o
 OBJS-$(CONFIG_APTX_HD_ENCODER)         += aptxenc.o aptx.o
 OBJS-$(CONFIG_APNG_DECODER)            += png.o pngdec.o pngdsp.o
 OBJS-$(CONFIG_APNG_ENCODER)            += png.o pngenc.o
+OBJS-$(CONFIG_APV_DECODER)             += apv_decode.o apv_entropy.o apv_dsp.o
 OBJS-$(CONFIG_ARBC_DECODER)            += arbc.o
 OBJS-$(CONFIG_ARGO_DECODER)            += argo.o
 OBJS-$(CONFIG_SSA_DECODER)             += assdec.o ass.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index f10519617e..09f06c71d6 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -47,6 +47,7 @@ extern const FFCodec ff_anm_decoder;
 extern const FFCodec ff_ansi_decoder;
 extern const FFCodec ff_apng_encoder;
 extern const FFCodec ff_apng_decoder;
+extern const FFCodec ff_apv_decoder;
 extern const FFCodec ff_arbc_decoder;
 extern const FFCodec ff_argo_decoder;
 extern const FFCodec ff_asv1_encoder;
diff --git a/libavcodec/apv_decode.c b/libavcodec/apv_decode.c
new file mode 100644
index 0000000000..db1ae9756d
--- /dev/null
+++ b/libavcodec/apv_decode.c
@@ -0,0 +1,331 @@
+/*
+ * 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/mem_internal.h"
+#include "libavutil/pixdesc.h"
+
+#include "apv.h"
+#include "apv_decode.h"
+#include "apv_dsp.h"
+#include "avcodec.h"
+#include "cbs.h"
+#include "cbs_apv.h"
+#include "codec_internal.h"
+#include "decode.h"
+#include "thread.h"
+
+
+typedef struct APVDecodeContext {
+    CodedBitstreamContext *cbc;
+    APVDSPContext dsp;
+
+    CodedBitstreamFragment au;
+    APVDerivedTileInfo tile_info;
+
+    APVVLCLUT decode_lut;
+
+    AVFrame *output_frame;
+} APVDecodeContext;
+
+static const enum AVPixelFormat apv_format_table[5][5] = {
+    { AV_PIX_FMT_GRAY8,    AV_PIX_FMT_GRAY10,     AV_PIX_FMT_GRAY12,     AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16 },
+    { 0 }, // 4:2:0 is not valid.
+    { AV_PIX_FMT_YUV422P,  AV_PIX_FMT_YUV422P10,  AV_PIX_FMT_YUV422P12,  AV_PIX_FMT_GRAY14, AV_PIX_FMT_YUV422P16 },
+    { AV_PIX_FMT_YUV444P,  AV_PIX_FMT_YUV444P10,  AV_PIX_FMT_YUV444P12,  AV_PIX_FMT_GRAY14, AV_PIX_FMT_YUV444P16 },
+    { AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_YUVA444P16 },
+};
+
+static int apv_decode_check_format(AVCodecContext *avctx,
+                                   const APVRawFrameHeader *header)
+{
+    int err, bit_depth;
+
+    avctx->profile = header->frame_info.profile_idc;
+    avctx->level   = header->frame_info.level_idc;
+
+    bit_depth = header->frame_info.bit_depth_minus8 + 8;
+    if (bit_depth < 8 || bit_depth > 16 || bit_depth % 2) {
+        avpriv_request_sample(avctx, "Bit depth %d", bit_depth);
+        return AVERROR_PATCHWELCOME;
+    }
+    avctx->pix_fmt =
+        apv_format_table[header->frame_info.chroma_format_idc][bit_depth - 4 >> 2];
+
+    err = ff_set_dimensions(avctx,
+                            FFALIGN(header->frame_info.frame_width,  16),
+                            FFALIGN(header->frame_info.frame_height, 16));
+    if (err < 0) {
+        // Unsupported frame size.
+        return err;
+    }
+    avctx->width  = header->frame_info.frame_width;
+    avctx->height = header->frame_info.frame_height;
+
+    avctx->sample_aspect_ratio = (AVRational){ 1, 1 };
+
+    avctx->color_primaries = header->color_primaries;
+    avctx->color_trc       = header->transfer_characteristics;
+    avctx->colorspace      = header->matrix_coefficients;
+    avctx->color_range     = header->full_range_flag ? AVCOL_RANGE_JPEG
+                                                     : AVCOL_RANGE_MPEG;
+    avctx->chroma_sample_location = AVCHROMA_LOC_TOPLEFT;
+
+    avctx->refs = 0;
+    avctx->has_b_frames = 0;
+
+    return 0;
+}
+
+static av_cold int apv_decode_init(AVCodecContext *avctx)
+{
+    APVDecodeContext *apv = avctx->priv_data;
+    int err;
+
+    err = ff_cbs_init(&apv->cbc, AV_CODEC_ID_APV, avctx);
+    if (err < 0)
+        return err;
+
+    ff_apv_entropy_build_decode_lut(&apv->decode_lut);
+
+    ff_apv_dsp_init(&apv->dsp);
+
+    if (avctx->extradata) {
+        av_log(avctx, AV_LOG_WARNING,
+               "APV does not support extradata.\n");
+    }
+
+    return 0;
+}
+
+static av_cold int apv_decode_close(AVCodecContext *avctx)
+{
+    APVDecodeContext *apv = avctx->priv_data;
+
+    ff_cbs_fragment_free(&apv->au);
+    ff_cbs_close(&apv->cbc);
+
+    return 0;
+}
+
+static int apv_decode_block(AVCodecContext *avctx,
+                            void *output,
+                            ptrdiff_t pitch,
+                            GetBitContext *gbc,
+                            APVEntropyState *entropy_state,
+                            int bit_depth,
+                            int qp_shift,
+                            const uint16_t *qmatrix)
+{
+    APVDecodeContext *apv = avctx->priv_data;
+    int err;
+
+    LOCAL_ALIGNED_32(int16_t, coeff, [64]);
+
+    err = ff_apv_entropy_decode_block(coeff, gbc, entropy_state);
+    if (err < 0)
+        return 0;
+
+    apv->dsp.decode_transquant(output, pitch,
+                               coeff, qmatrix,
+                               bit_depth, qp_shift);
+
+    return 0;
+}
+
+static int apv_decode_tile_component(AVCodecContext *avctx, void *data,
+                                     int job, int thread)
+{
+    APVRawFrame                      *input = data;
+    APVDecodeContext                   *apv = avctx->priv_data;
+    const CodedBitstreamAPVContext *apv_cbc = apv->cbc->priv_data;
+    const APVDerivedTileInfo     *tile_info = &apv_cbc->tile_info;
+
+    int tile_index = job / apv_cbc->num_comp;
+    int comp_index = job % apv_cbc->num_comp;
+
+    const AVPixFmtDescriptor *pix_fmt_desc =
+        av_pix_fmt_desc_get(avctx->pix_fmt);
+
+    int sub_w = comp_index == 0 ? 1 : pix_fmt_desc->log2_chroma_w + 1;
+    int sub_h = comp_index == 0 ? 1 : pix_fmt_desc->log2_chroma_h + 1;
+
+    APVRawTile *tile = &input->tile[tile_index];
+
+    int tile_y = tile_index / tile_info->tile_cols;
+    int tile_x = tile_index % tile_info->tile_cols;
+
+    int tile_start_x = tile_info->col_starts[tile_x];
+    int tile_start_y = tile_info->row_starts[tile_y];
+
+    int tile_width  = tile_info->col_starts[tile_x + 1] - tile_start_x;
+    int tile_height = tile_info->row_starts[tile_y + 1] - tile_start_y;
+
+    int tile_mb_width  = tile_width  / APV_MB_WIDTH;
+    int tile_mb_height = tile_height / APV_MB_HEIGHT;
+
+    int blk_mb_width  = 2 / sub_w;
+    int blk_mb_height = 2 / sub_h;
+
+    int bit_depth;
+    int qp_shift;
+    LOCAL_ALIGNED_32(uint16_t, qmatrix_scaled, [64]);
+
+    GetBitContext gbc;
+
+    APVEntropyState entropy_state = {
+        .log_ctx           = avctx,
+        .decode_lut        = &apv->decode_lut,
+        .prev_dc           = 0,
+        .prev_dc_diff      = 20,
+        .prev_1st_ac_level = 0,
+    };
+
+    init_get_bits8(&gbc, tile->tile_data[comp_index],
+                   tile->tile_header.tile_data_size[comp_index]);
+
+    // Combine the bitstream quantisation matrix with the qp scaling
+    // in advance.  (Including qp_shift as well would overflow 16 bits.)
+    // Fix the row ordering at the same time.
+    {
+        static const uint8_t apv_level_scale[6] = { 40, 45, 51, 57, 64, 71 };
+        int qp = tile->tile_header.tile_qp[comp_index];
+        int level_scale = apv_level_scale[qp % 6];
+
+        bit_depth = apv_cbc->bit_depth;
+        qp_shift  = qp / 6;
+
+        for (int y = 0; y < 8; y++) {
+            for (int x = 0; x < 8; x++)
+                qmatrix_scaled[y * 8 + x] = level_scale *
+                    input->frame_header.quantization_matrix.q_matrix[comp_index][x][y];
+        }
+    }
+
+    for (int mb_y = 0; mb_y < tile_mb_height; mb_y++) {
+        for (int mb_x = 0; mb_x < tile_mb_width; mb_x++) {
+            for (int blk_y = 0; blk_y < blk_mb_height; blk_y++) {
+                for (int blk_x = 0; blk_x < blk_mb_width; blk_x++) {
+                    int frame_y = (tile_start_y +
+                                   APV_MB_HEIGHT * mb_y +
+                                   APV_TR_SIZE * blk_y) / sub_h;
+                    int frame_x = (tile_start_x +
+                                   APV_MB_WIDTH * mb_x +
+                                   APV_TR_SIZE * blk_x) / sub_w;
+
+                    ptrdiff_t frame_pitch = apv->output_frame->linesize[comp_index];
+                    uint8_t  *block_start = apv->output_frame->data[comp_index] +
+                                            frame_y * frame_pitch + 2 * frame_x;
+
+                    apv_decode_block(avctx,
+                                     block_start, frame_pitch,
+                                     &gbc, &entropy_state,
+                                     bit_depth,
+                                     qp_shift,
+                                     qmatrix_scaled);
+                }
+            }
+        }
+    }
+
+    av_log(avctx, AV_LOG_DEBUG,
+           "Decoded tile %d component %d: %dx%d MBs starting at (%d,%d)\n",
+           tile_index, comp_index, tile_mb_width, tile_mb_height,
+           tile_start_x, tile_start_y);
+
+    return 0;
+}
+
+static int apv_decode(AVCodecContext *avctx, AVFrame *output,
+                      APVRawFrame *input)
+{
+    APVDecodeContext                   *apv = avctx->priv_data;
+    const CodedBitstreamAPVContext *apv_cbc = apv->cbc->priv_data;
+    const APVDerivedTileInfo     *tile_info = &apv_cbc->tile_info;
+    int err, job_count;
+
+    err = apv_decode_check_format(avctx, &input->frame_header);
+    if (err < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Unsupported format parameters.\n");
+        return err;
+    }
+
+    err = ff_thread_get_buffer(avctx, output, 0);
+    if (err) {
+        av_log(avctx, AV_LOG_ERROR, "No output frame supplied.\n");
+        return err;
+    }
+
+    apv->output_frame = output;
+
+    // Each component within a tile is independent of every other,
+    // so we can decode all in parallel.
+    job_count = tile_info->num_tiles * apv_cbc->num_comp;
+
+    avctx->execute2(avctx, apv_decode_tile_component,
+                    input, NULL, job_count);
+
+    return 0;
+}
+
+static int apv_decode_frame(AVCodecContext *avctx, AVFrame *frame,
+                            int *got_frame, AVPacket *packet)
+{
+    APVDecodeContext      *apv = avctx->priv_data;
+    CodedBitstreamFragment *au = &apv->au;
+    int err;
+
+    err = ff_cbs_read_packet(apv->cbc, au, packet);
+    if (err < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to read packet.\n");
+        return err;
+    }
+
+    for (int i = 0; i < au->nb_units; i++) {
+        CodedBitstreamUnit *pbu = &au->units[i];
+
+        switch (pbu->type) {
+        case APV_PBU_PRIMARY_FRAME:
+            err = apv_decode(avctx, frame, pbu->content);
+            if (err < 0)
+                return err;
+            *got_frame = 1;
+            break;
+        default:
+            av_log(avctx, AV_LOG_WARNING,
+                   "Ignoring unsupported PBU type %d.\n", pbu->type);
+        }
+    }
+
+    ff_cbs_fragment_reset(au);
+
+    return packet->size;
+}
+
+const FFCodec ff_apv_decoder = {
+    .p.name                = "apv",
+    CODEC_LONG_NAME("Advanced Professional Video"),
+    .p.type                = AVMEDIA_TYPE_VIDEO,
+    .p.id                  = AV_CODEC_ID_APV,
+    .priv_data_size        = sizeof(APVDecodeContext),
+    .init                  = apv_decode_init,
+    .close                 = apv_decode_close,
+    FF_CODEC_DECODE_CB(apv_decode_frame),
+    .p.capabilities        = AV_CODEC_CAP_DR1 |
+                             AV_CODEC_CAP_SLICE_THREADS |
+                             AV_CODEC_CAP_FRAME_THREADS,
+};
diff --git a/libavcodec/apv_decode.h b/libavcodec/apv_decode.h
new file mode 100644
index 0000000000..34c6176ea0
--- /dev/null
+++ b/libavcodec/apv_decode.h
@@ -0,0 +1,80 @@
+/*
+ * 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
+ */
+
+#ifndef AVCODEC_APV_DECODE_H
+#define AVCODEC_APV_DECODE_H
+
+#include <stdint.h>
+
+#include "apv.h"
+#include "avcodec.h"
+#include "get_bits.h"
+
+
+// Number of bits in the entropy look-up tables.
+// It may be desirable to tune this per-architecture, as a larger LUT
+// trades greater memory use for fewer instructions.
+// (N bits -> 24*2^N bytes of tables; 9 -> 12KB of tables.)
+#define APV_VLC_LUT_BITS 9
+#define APV_VLC_LUT_SIZE (1 << APV_VLC_LUT_BITS)
+
+typedef struct APVVLCLUTEntry {
+    uint16_t result;  // Return value if not reading more.
+    uint8_t  consume; // Number of bits to consume.
+    uint8_t  more;    // Whether to read additional bits.
+} APVVLCLUTEntry;
+
+typedef struct APVVLCLUT {
+    APVVLCLUTEntry lut[6][APV_VLC_LUT_SIZE];
+} APVVLCLUT;
+
+typedef struct APVEntropyState {
+    void *log_ctx;
+
+    const APVVLCLUT *decode_lut;
+
+    int16_t prev_dc;
+    int16_t prev_dc_diff;
+    int16_t prev_1st_ac_level;
+} APVEntropyState;
+
+
+/**
+ * Build the decoder VLC look-up table.
+ */
+void ff_apv_entropy_build_decode_lut(APVVLCLUT *decode_lut);
+
+/**
+ * Entropy decode a single 8x8 block to coefficients.
+ *
+ * Outputs in block order (dezigzag already applied).
+ */
+int ff_apv_entropy_decode_block(int16_t *coeff,
+                                GetBitContext *gbc,
+                                APVEntropyState *state);
+
+/**
+ * Read a single APV VLC code.
+ *
+ * This entrypoint is exposed for testing.
+ */
+unsigned int ff_apv_read_vlc(GetBitContext *gbc, int k_param,
+                             const APVVLCLUT *lut);
+
+
+#endif /* AVCODEC_APV_DECODE_H */
diff --git a/libavcodec/apv_dsp.c b/libavcodec/apv_dsp.c
new file mode 100644
index 0000000000..fe11cd6b94
--- /dev/null
+++ b/libavcodec/apv_dsp.c
@@ -0,0 +1,136 @@
+/*
+ * 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 <stdint.h>
+
+#include "config.h"
+#include "libavutil/attributes.h"
+#include "libavutil/common.h"
+
+#include "apv.h"
+#include "apv_dsp.h"
+
+
+static const int8_t apv_trans_matrix[8][8] = {
+    {  64,  64,  64,  64,  64,  64,  64,  64 },
+    {  89,  75,  50,  18, -18, -50, -75, -89 },
+    {  84,  35, -35, -84, -84, -35,  35,  84 },
+    {  75, -18, -89, -50,  50,  89,  18, -75 },
+    {  64, -64, -64,  64,  64, -64, -64,  64 },
+    {  50, -89,  18,  75, -75, -18,  89, -50 },
+    {  35, -84,  84, -35, -35,  84, -84,  35 },
+    {  18, -50,  75, -89,  89, -75,  50, -18 },
+};
+
+static void apv_decode_transquant_c(void *output,
+                                    ptrdiff_t pitch,
+                                    const int16_t *input_flat,
+                                    const int16_t *qmatrix_flat,
+                                    int bit_depth,
+                                    int qp_shift)
+{
+    const int16_t (*input)[8]   = (const int16_t(*)[8])input_flat;
+    const int16_t (*qmatrix)[8] = (const int16_t(*)[8])qmatrix_flat;
+
+    int16_t scaled_coeff[8][8];
+    int32_t recon_sample[8][8];
+
+    // Dequant.
+    {
+        // Note that level_scale was already combined into qmatrix
+        // before we got here.
+        int bd_shift = bit_depth + 3 - 5;
+
+        for (int y = 0; y < 8; y++) {
+            for (int x = 0; x < 8; x++) {
+                int coeff = (((input[y][x] * qmatrix[y][x]) << qp_shift) +
+                             (1 << (bd_shift - 1))) >> bd_shift;
+
+                scaled_coeff[y][x] =
+                    av_clip(coeff, APV_MIN_TRANS_COEFF,
+                                   APV_MAX_TRANS_COEFF);
+            }
+        }
+    }
+
+    // Transform.
+    {
+        int32_t tmp[8][8];
+
+        // Vertical transform of columns.
+        for (int x = 0; x < 8; x++) {
+            for (int i = 0; i < 8; i++) {
+                int sum = 0;
+                for (int j = 0; j < 8; j++)
+                    sum += apv_trans_matrix[j][i] * scaled_coeff[j][x];
+                tmp[i][x] = sum;
+            }
+        }
+
+        // Renormalise.
+        for (int x = 0; x < 8; x++) {
+            for (int y = 0; y < 8; y++)
+                tmp[y][x] = (tmp[y][x] + 64) >> 7;
+        }
+
+        // Horizontal transform of rows.
+        for (int y = 0; y < 8; y++) {
+            for (int i = 0; i < 8; i++) {
+                int sum = 0;
+                for (int j = 0; j < 8; j++)
+                    sum += apv_trans_matrix[j][i] * tmp[y][j];
+                recon_sample[y][i] = sum;
+            }
+        }
+    }
+
+    // Output.
+    if (bit_depth == 8) {
+        uint8_t *ptr = output;
+        int bd_shift = 20 - bit_depth;
+
+        for (int y = 0; y < 8; y++) {
+            for (int x = 0; x < 8; x++) {
+                int sample = ((recon_sample[y][x] +
+                               (1 << (bd_shift - 1))) >> bd_shift) +
+                    (1 << (bit_depth - 1));
+                ptr[x] = av_clip_uintp2(sample, bit_depth);
+            }
+            ptr += pitch;
+        }
+    } else {
+        uint16_t *ptr = output;
+        int bd_shift = 20 - bit_depth;
+        pitch /= 2; // Pitch was in bytes, 2 bytes per sample.
+
+        for (int y = 0; y < 8; y++) {
+            for (int x = 0; x < 8; x++) {
+                int sample = ((recon_sample[y][x] +
+                               (1 << (bd_shift - 1))) >> bd_shift) +
+                    (1 << (bit_depth - 1));
+                ptr[x] = av_clip_uintp2(sample, bit_depth);
+            }
+            ptr += pitch;
+        }
+    }
+}
+
+av_cold void ff_apv_dsp_init(APVDSPContext *dsp)
+{
+    dsp->decode_transquant = apv_decode_transquant_c;
+}
diff --git a/libavcodec/apv_dsp.h b/libavcodec/apv_dsp.h
new file mode 100644
index 0000000000..31645b8581
--- /dev/null
+++ b/libavcodec/apv_dsp.h
@@ -0,0 +1,37 @@
+/*
+ * 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
+ */
+
+#ifndef AVCODEC_APV_DSP_H
+#define AVCODEC_APV_DSP_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+
+typedef struct APVDSPContext {
+    void (*decode_transquant)(void *output,
+                              ptrdiff_t pitch,
+                              const int16_t *input,
+                              const int16_t *qmatrix,
+                              int bit_depth,
+                              int qp_shift);
+} APVDSPContext;
+
+void ff_apv_dsp_init(APVDSPContext *dsp);
+
+#endif /* AVCODEC_APV_DSP_H */
diff --git a/libavcodec/apv_entropy.c b/libavcodec/apv_entropy.c
new file mode 100644
index 0000000000..00e0b4fbdf
--- /dev/null
+++ b/libavcodec/apv_entropy.c
@@ -0,0 +1,200 @@
+/*
+ * 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 "apv.h"
+#include "apv_decode.h"
+
+
+void ff_apv_entropy_build_decode_lut(APVVLCLUT *decode_lut)
+{
+    const int code_len = APV_VLC_LUT_BITS;
+    const int lut_size = APV_VLC_LUT_SIZE;
+
+    for (int k = 0; k <= 5; k++) {
+        for (unsigned int code = 0; code < lut_size; code++) {
+            APVVLCLUTEntry *ent = &decode_lut->lut[k][code];
+            unsigned int first_bit      = code & (1 << code_len - 1);
+            unsigned int remaining_bits = code ^ first_bit;
+
+            if (first_bit) {
+                ent->consume = 1 + k;
+                ent->result  = remaining_bits >> (code_len - k - 1);
+                ent->more    = 0;
+            } else {
+                unsigned int second_bit = code & (1 << code_len - 2);
+                remaining_bits ^= second_bit;
+
+                if (second_bit) {
+                    unsigned int bits_left = code_len - 2;
+                    unsigned int first_set = bits_left - av_log2(remaining_bits);
+                    unsigned int last_bits = first_set - 1 + k;
+
+                    if (first_set + last_bits <= bits_left) {
+                        // Whole code fits here.
+                        ent->consume = 2 + first_set + last_bits;
+                        ent->result  = ((2 << k) +
+                                        (((1 << first_set - 1) - 1) << k) +
+                                        ((code >> bits_left - first_set - last_bits) & (1 << last_bits) - 1));
+                        ent->more    = 0;
+                    } else {
+                        // Need to read more, collapse to default.
+                        ent->consume = 2;
+                        ent->more    = 1;
+                    }
+                } else {
+                    ent->consume = 2 + k;
+                    ent->result  = (1 << k) + (remaining_bits >> (code_len - k - 2));
+                    ent->more    = 0;
+                }
+            }
+        }
+    }
+}
+
+av_always_inline
+static unsigned int apv_read_vlc(GetBitContext *gbc, int k_param,
+                                 const APVVLCLUT *lut)
+{
+    unsigned int next_bits;
+    const APVVLCLUTEntry *ent;
+
+    next_bits = show_bits(gbc, APV_VLC_LUT_BITS);
+    ent = &lut->lut[k_param][next_bits];
+
+    if (ent->more) {
+        unsigned int leading_zeroes;
+
+        skip_bits(gbc, ent->consume);
+
+        next_bits = show_bits(gbc, 16);
+        leading_zeroes = 15 - av_log2(next_bits);
+
+        skip_bits(gbc, leading_zeroes + 1);
+
+        return (2 << k_param) +
+            ((1 << leading_zeroes) - 1) * (1 << k_param) +
+            get_bits(gbc, leading_zeroes + k_param);
+    } else {
+        skip_bits(gbc, ent->consume);
+        return ent->result;
+    }
+}
+
+unsigned int ff_apv_read_vlc(GetBitContext *gbc, int k_param,
+                             const APVVLCLUT *lut)
+{
+    return apv_read_vlc(gbc, k_param, lut);
+}
+
+int ff_apv_entropy_decode_block(int16_t *coeff,
+                                GetBitContext *gbc,
+                                APVEntropyState *state)
+{
+    const APVVLCLUT *lut = state->decode_lut;
+    int k_param;
+
+    // DC coefficient.
+    {
+        int abs_dc_coeff_diff;
+        int sign_dc_coeff_diff;
+        int dc_coeff;
+
+        k_param = av_clip(state->prev_dc_diff >> 1, 0, 5);
+        abs_dc_coeff_diff = apv_read_vlc(gbc, k_param, lut);
+
+        if (abs_dc_coeff_diff > 0)
+            sign_dc_coeff_diff = get_bits1(gbc);
+        else
+            sign_dc_coeff_diff = 0;
+
+        if (sign_dc_coeff_diff)
+            dc_coeff = state->prev_dc - abs_dc_coeff_diff;
+        else
+            dc_coeff = state->prev_dc + abs_dc_coeff_diff;
+
+        if (dc_coeff < APV_MIN_TRANS_COEFF ||
+            dc_coeff > APV_MAX_TRANS_COEFF) {
+            av_log(state->log_ctx, AV_LOG_ERROR,
+                   "Out-of-range DC coefficient value: %d "
+                   "(from prev_dc %d abs_dc_coeff_diff %d sign_dc_coeff_diff %d)\n",
+                   dc_coeff, state->prev_dc, abs_dc_coeff_diff, sign_dc_coeff_diff);
+            return AVERROR_INVALIDDATA;
+        }
+
+        coeff[0] = dc_coeff;
+
+        state->prev_dc      = dc_coeff;
+        state->prev_dc_diff = abs_dc_coeff_diff;
+    }
+
+    // AC coefficients.
+    {
+        int scan_pos   = 1;
+        int first_ac   = 1;
+        int prev_level = state->prev_1st_ac_level;
+        int prev_run   = 0;
+
+        do {
+            int coeff_zero_run;
+
+            k_param = av_clip(prev_run >> 2, 0, 2);
+            coeff_zero_run = apv_read_vlc(gbc, k_param, lut);
+
+            if (coeff_zero_run > APV_BLK_COEFFS - scan_pos) {
+                av_log(state->log_ctx, AV_LOG_ERROR,
+                       "Out-of-range zero-run value: %d (at scan pos %d)\n",
+                       coeff_zero_run, scan_pos);
+                return AVERROR_INVALIDDATA;
+            }
+
+            for (int i = 0; i < coeff_zero_run; i++) {
+                coeff[ff_zigzag_direct[scan_pos]] = 0;
+                ++scan_pos;
+            }
+            prev_run = coeff_zero_run;
+
+            if (scan_pos < APV_BLK_COEFFS) {
+                int abs_ac_coeff_minus1;
+                int sign_ac_coeff;
+                int level;
+
+                k_param = av_clip(prev_level >> 2, 0, 4);
+                abs_ac_coeff_minus1 = apv_read_vlc(gbc, k_param, lut);
+                sign_ac_coeff = get_bits(gbc, 1);
+
+                if (sign_ac_coeff)
+                    level = -abs_ac_coeff_minus1 - 1;
+                else
+                    level = abs_ac_coeff_minus1 + 1;
+
+                coeff[ff_zigzag_direct[scan_pos]] = level;
+
+                prev_level = abs_ac_coeff_minus1 + 1;
+                if (first_ac) {
+                    state->prev_1st_ac_level = prev_level;
+                    first_ac = 0;
+                }
+
+                ++scan_pos;
+            }
+
+        } while (scan_pos < APV_BLK_COEFFS);
+    }
+
+    return 0;
+}
-- 
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".