From patchwork Tue Dec 5 14:45:17 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nuo Mi X-Patchwork-Id: 44930 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:9153:b0:181:818d:5e7f with SMTP id x19csp379207pzc; Tue, 5 Dec 2023 06:48:27 -0800 (PST) X-Google-Smtp-Source: AGHT+IFK+1Ab90nk7dMcY+i2rNv9fW7wiJAf/VmKdlekzZMFnCUTJ5yJJ4c5kxGm7SsAoQy9UAff X-Received: by 2002:a17:906:608f:b0:9cf:7c60:47b9 with SMTP id t15-20020a170906608f00b009cf7c6047b9mr22475911ejj.1.1701787706651; Tue, 05 Dec 2023 06:48:26 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id m19-20020a1709060d9300b00a1051de2ad1si5416476eji.421.2023.12.05.06.48.26; Tue, 05 Dec 2023 06:48:26 -0800 (PST) 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=@outlook.com header.s=selector1 header.b=gdSZkeXX; arc=fail (body hash mismatch); 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; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 376D268CED9; Tue, 5 Dec 2023 16:46:20 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from APC01-PSA-obe.outbound.protection.outlook.com (mail-psaapc01olkn2037.outbound.protection.outlook.com [40.92.52.37]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 19AD368CEEC for ; Tue, 5 Dec 2023 16:46:11 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=bO5lU6kA15zM39XLsoveEsTnmjaRWdMXdHv9XxE/Ft1A+0h9BYb8N8eENQYrPCPLewosIjSxLhwzr//NILPHzqJqHEixv2TlWmmFU7tsJIlgk2ffHTZZYYuG6jZvDvxk2vpCTQ6dRoeOsR4qgXcyxVmlaRvkdp2v6uz2VCS8nmSAAguDeUHJ8uO85qNYiFQ6WYc6W947nfm010yHD5WTDAc4xN4MvTEMueH9ytLqxy+Nj24bmLwA/Ju6B63UGKxVHVxBvyGRZyHPh5xKhJr2ml7oMkcqPgIS9dTDfZNOakU+4cfs24lUx6XoNx2YS/f40v8s/UqMg9yTG/4ptLww9w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=D2K7wfcPjqeZ4n+RndJezPsxtbWoU2eG+966T2o2O38=; b=L3t9xn9fzkAsQ7byKUmr9nkRuEt3DNpMaGTE7Sz21I44D45PTwpjxPXdj6nR4NviA9kq4xOXdoJwBtrzQaLV0WDQxS7XDCyyydxb+Pl+bmj/xR092TQOCeGvu5rK1sppOAvbZcYuQfjoLStBbdCeOeddc9Cy8G4UzH19p34/MtPXXZ0KjVInm8TDnxs8/nYuICwEEEjRn0vmf//psuUUguH8wEUCxvFc/t3vmFmawZ3zIyaoySpLFUgg+uW8LNOUJgQbvjoRJUUryHB7Y9uGMpLUa3TmDRHe/33QBXMky0tCztOM109j59qbIzdqxXJoRRs0HUJc36o1H3sw4gcOhg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=outlook.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=D2K7wfcPjqeZ4n+RndJezPsxtbWoU2eG+966T2o2O38=; b=gdSZkeXX5Co1rOrD8erxhqkprDWWXGqPcfLzAmKucYeUPv/Ju75A9G/hjRd/EDAmBwjNU4mabvbJ8U1ovVXZ5EisjF8cFwRNG9us0PuNbJkaozkFx8T4Dw8tHUwupjToMpigDvs4zTfSQLYuu0bdDvhOcUl/nC1dLEVQzzxLeiRFrBBATE50tiTW6ABbBRPgWFHip8WEu0knA8d04w0t1f9elVJfqQeXqhznl5QZHKz1X7n9C+VKOoct01OxboZMnK5adxNQIcUTAt/yk16khckAIMLz/PqSfYlkPcS+toyokvTt7rYHj7kZ3JpxODt2HcUD3q5gPm1InkGBN4/k+A== Received: from TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) by JH0PR06MB6294.apcprd06.prod.outlook.com (2603:1096:990:14::7) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7046.33; Tue, 5 Dec 2023 14:45:57 +0000 Received: from TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f]) by TYSPR06MB6433.apcprd06.prod.outlook.com ([fe80::80b3:be24:949c:e49f%4]) with mapi id 15.20.7046.034; Tue, 5 Dec 2023 14:45:57 +0000 From: Nuo Mi To: ffmpeg-devel@ffmpeg.org Date: Tue, 5 Dec 2023 22:45:17 +0800 Message-ID: X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231205144519.7334-1-nuomi2021@gmail.com> References: <20231205144519.7334-1-nuomi2021@gmail.com> X-TMN: [0xCBVPzN46TPD78icMJQ6DIS1r/1QdtS] X-ClientProxiedBy: SG3P274CA0015.SGPP274.PROD.OUTLOOK.COM (2603:1096:4:be::27) To TYSPR06MB6433.apcprd06.prod.outlook.com (2603:1096:400:47a::6) X-Microsoft-Original-Message-ID: <20231205144519.7334-12-nuomi2021@gmail.com> MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 2 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: TYSPR06MB6433:EE_|JH0PR06MB6294:EE_ X-MS-Office365-Filtering-Correlation-Id: fabd356b-fcd0-4bf3-15a8-08dbf5a0e3ba X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: oUKOPotl8SlFQAGBZa7R7LysfU4hCu69XdgZ6u/mFwf+C14Adz3OMETiJuUTnB96Toj4F/HIPtWmZtsz2Z5vLkpA8MHwW+nCMFF6BtwYp6GaAcyOlb20xo4s1SpRY3yWn+3Ncy4WfMaW7pSox9ilT/gUKLU9uRQSnX9NXX4jUAlkJI2nGaJPxS5lzFHHR03B/t8NF7N9pZohRJZfaExSJO24Mw0EYSA6OVoSnJPSBjyvSll/E1lTObklhlGgzOOtj7KyOvmjgCRxlg27a8z27QgybL2qXO+davDe6YHcUBiT8kX0gQzulEwc1LjoGi1Xdn2xfg4NJKpLYe1JEX6cW5GRr5rxiHO2IuXf42ZFG7Fp2blsx4ZQMKeWXSI953hUxtb1jE6vk5jM2pwAkiM1YCaXVV6y49aGdNHAyTk85rIaeLC4FwYw3343cXEV74KJRpf7SnKo+oDqHOkANqGlEmvopGfHojUbqpqQj0WlvkcWDT6eoNxNStozF5hYjX5jXu10IahkkuljampLMQ9mEWDcVv/IzlCE106t0dYyDZd0U2K/y1l8St4dpD5ywi3SFunyXfCgtH90KEYwQayz/ObXfgxZojMjEDWAGlLKcuWUdYYkEFe0N0bjBw/xD0PQ X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: TbX6INtCLls+sEwewRrmAoMsih9Xy9rTXDuBI2h0KK1Y6cSPD+N904MXeI3xM8+c8u6oA5Zfj9myAN8DpnUGZ47FlHEEvos5GECMKF3gVIcKIQsvs5vX3ouobUU0EVXwpp4eC/mbceM/TPbA2uQSyIRxy15ZrqWzvs63/AZhASF/HR+f/LPr64Bo9dMgOBjaT/PJtgCSJ9Nkkf7J3Dbvfci+/Pv7z0YyubKhLoyOFex1b+TnHaEmxLSbTdOddloLtza1QtvE4tIwK08hNcKxPETy6KtlnD1CQiI7FiIra3WPBGt0/eR/PvtqG2wXR1tjPjmsFYRLYT6deKarW52QAcroczhDFrVAZ0MwxKQnbsjFOUqQ5nfI11FRvV60ITQzTadMo9oK3/ThbKl41SIdx8tQpfEgFS4buEIOe5MMlbdg4b7uPCYNNnqPRk4CU9RhdTzxu1nO5ijtufV2jtCRN828+5PwZ6FyBneXottPqAewbnuTTdI6EwYaXxQUmZ0Vkr0HkxKB+f4nuNnLhhI7jHQnlIDQueBecAdvyQ5RQzrSCKjFya/org39EYcWznU7FHSY/ngaKQj3WyV8GWshmyoUSGRQwmUL7AOLnw1DdGxBCREcRloKc+t8Kq5Vehg5O+EcZJ0Go56K9Y0nOcbGzeZA0eTUtKAmZtcF4EKorjksc1XQZw2t84BfrNhvtULH8g6XgtIQ8j87xaQOBq5Bz8Qts54Eh81oYHQ12coHWaBiaGhhEhvoci+Nwo5UgItQEboLXADFZX45rUamuhhzfgxIUxveR/nUye0hXKoNPkXhcBoW9Y+40WD8Mwyww4cve84mI9JgDekZZweR5oLf6XxHdmun4LGpPzjbYn/ntILutGCkXoGLjGaXt5K/LqNTIQcYcIeXJcAdXSQz4VK5Cy5B9k0CngYKhNHkFzslNnqrZ6sgCr9AeybmR9JMEI2sLBumX8NhmIm0Hh4NgxpNvv/1I5mG4WVGzmGSgKJb0b8Jm/VWmiD85DgHo3Skm7DqV+t+0WXoidSWJAv8Ac+2x1DuZKQXuqFeMxLURg0Z+chiPK+45J7xuwRhb2fL9UnFYjipXQ+uWoc3qerKFp2WebgffHj7GxAbNSLjbqhLgxzK0OH3RzDL1qCd263/BegCGJp1AbhtX0YAPPTXuFcZ+PyEdbPGmVYa6oBrJgDeairgHfsXvfxusDsXjzhMmXIUBA+F2/0zIODbOXMAxvJuwUl9VOY4CmnLOhD395BxE9suYTKXDgGID9hddjhwY4cz X-OriginatorOrg: outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: fabd356b-fcd0-4bf3-15a8-08dbf5a0e3ba X-MS-Exchange-CrossTenant-AuthSource: TYSPR06MB6433.apcprd06.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 05 Dec 2023 14:45:57.0200 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: JH0PR06MB6294 Subject: [FFmpeg-devel] [PATCH v6 12/14] vvcdec: add CTU parser 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: Nuo Mi Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: hm3SxM6JMJbY --- libavcodec/vvc/vvc_ctu.c | 2372 ++++++++++++++++++++++++++++++++++++++ libavcodec/vvc/vvc_ctu.h | 11 + 2 files changed, 2383 insertions(+) diff --git a/libavcodec/vvc/vvc_ctu.c b/libavcodec/vvc/vvc_ctu.c index 6138d2fc9f..de2bc9f931 100644 --- a/libavcodec/vvc/vvc_ctu.c +++ b/libavcodec/vvc/vvc_ctu.c @@ -24,8 +24,2372 @@ #include "vvc_cabac.h" #include "vvc_ctu.h" +#include "vvc_inter.h" #include "vvc_mvs.h" +#define PROF_TEMP_SIZE (PROF_BLOCK_SIZE) * sizeof(int16_t) + +#define TAB_MSM(fc, depth, x, y) fc->tab.msm[(depth)][((y) >> 5) * fc->ps.pps->width32 + ((x) >> 5)] +#define TAB_ISPMF(fc, x, y) fc->tab.ispmf[((y) >> 6) * fc->ps.pps->width64 + ((x) >> 6)] + +typedef enum VVCModeType { + MODE_TYPE_ALL, + MODE_TYPE_INTER, + MODE_TYPE_INTRA, +} VVCModeType; + +static void set_tb_pos(const VVCFrameContext *fc, const TransformBlock *tb) +{ + const int x_tb = tb->x0 >> MIN_TU_LOG2; + const int y_tb = tb->y0 >> MIN_TU_LOG2; + const int hs = fc->ps.sps->hshift[tb->c_idx]; + const int vs = fc->ps.sps->vshift[tb->c_idx]; + const int is_chroma = tb->c_idx != 0; + const int width = FFMAX(1, tb->tb_width >> (MIN_TU_LOG2 - hs)); + const int end = y_tb + FFMAX(1, tb->tb_height >> (MIN_TU_LOG2 - vs)); + + for (int y = y_tb; y < end; y++) { + const int off = y * fc->ps.pps->min_tu_width + x_tb; + for (int i = 0; i < width; i++) { + fc->tab.tb_pos_x0[is_chroma][off + i] = tb->x0; + fc->tab.tb_pos_y0[is_chroma][off + i] = tb->y0; + } + memset(fc->tab.tb_width [is_chroma] + off, tb->tb_width, width); + memset(fc->tab.tb_height[is_chroma] + off, tb->tb_height, width); + } +} + +static void set_tb_tab(uint8_t *tab, uint8_t v, const VVCFrameContext *fc, + const TransformBlock *tb) +{ + const int width = tb->tb_width << fc->ps.sps->hshift[tb->c_idx]; + const int height = tb->tb_height << fc->ps.sps->vshift[tb->c_idx]; + + for (int h = 0; h < height; h += MIN_TU_SIZE) { + const int y = (tb->y0 + h) >> MIN_TU_LOG2; + const int off = y * fc->ps.pps->min_tu_width + (tb->x0 >> MIN_TU_LOG2); + const int w = FFMAX(1, width >> MIN_TU_LOG2); + memset(tab + off, v, w); + } +} + +// 8.7.1 Derivation process for quantization parameters +static int get_qp_y_pred(const VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const CodingUnit *cu = lc->cu; + const int ctb_log2_size = sps->ctb_log2_size_y; + const int ctb_size_mask = (1 << ctb_log2_size) - 1; + const int xQg = lc->parse.cu_qg_top_left_x; + const int yQg = lc->parse.cu_qg_top_left_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int x_cb = cu->x0 >> sps->min_cb_log2_size_y; + const int y_cb = cu->y0 >> sps->min_cb_log2_size_y; + const int x_ctb = cu->x0 >> ctb_log2_size; + const int y_ctb = cu->y0 >> ctb_log2_size; + const int in_same_ctb_a = ((xQg - 1) >> ctb_log2_size) == x_ctb && (yQg >> ctb_log2_size) == y_ctb; + const int in_same_ctb_b = (xQg >> ctb_log2_size) == x_ctb && ((yQg - 1) >> ctb_log2_size) == y_ctb; + int qPy_pred, qPy_a, qPy_b; + + if (lc->na.cand_up) { + const int first_qg_in_ctu = !(xQg & ctb_size_mask) && !(yQg & ctb_size_mask); + const int qPy_up = fc->tab.qp[LUMA][x_cb + (y_cb - 1) * min_cb_width]; + if (first_qg_in_ctu && pps->ctb_to_col_bd[xQg >> ctb_log2_size] == xQg) + return qPy_up; + } + + // qPy_pred + qPy_pred = lc->ep->is_first_qg ? lc->sc->sh.slice_qp_y : lc->ep->qp_y; + + // qPy_b + if (!lc->na.cand_up || !in_same_ctb_b) + qPy_b = qPy_pred; + else + qPy_b = fc->tab.qp[LUMA][x_cb + (y_cb - 1) * min_cb_width]; + + // qPy_a + if (!lc->na.cand_left || !in_same_ctb_a) + qPy_a = qPy_pred; + else + qPy_a = fc->tab.qp[LUMA][(x_cb - 1) + y_cb * min_cb_width]; + + av_assert2(qPy_a >= -fc->ps.sps->qp_bd_offset && qPy_a < 63); + av_assert2(qPy_b >= -fc->ps.sps->qp_bd_offset && qPy_b < 63); + + return (qPy_a + qPy_b + 1) >> 1; +} + +static void set_cb_tab(const VVCLocalContext *lc, uint8_t *tab, const uint8_t v) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPPS *pps = fc->ps.pps; + const CodingUnit *cu = lc->cu; + const int log2_min_cb_size = fc->ps.sps->min_cb_log2_size_y; + const int x_cb = cu->x0 >> log2_min_cb_size; + const int y_cb = cu->y0 >> log2_min_cb_size; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + int x = y_cb * pps->min_cb_width + x_cb; + + for (int y = 0; y < (cb_height >> log2_min_cb_size); y++) { + const int width = cb_width >> log2_min_cb_size; + + memset(&tab[x], v, width); + x += pps->min_cb_width; + } +} + +static int set_qp_y(VVCLocalContext *lc, const int x0, const int y0, const int has_qp_delta) +{ + const VVCSPS *sps = lc->fc->ps.sps; + EntryPoint *ep = lc->ep; + CodingUnit *cu = lc->cu; + int cu_qp_delta = 0; + + if (!lc->fc->ps.pps->r->pps_cu_qp_delta_enabled_flag) { + ep->qp_y = lc->sc->sh.slice_qp_y; + } else if (ep->is_first_qg || (lc->parse.cu_qg_top_left_x == x0 && lc->parse.cu_qg_top_left_y == y0)) { + ep->qp_y = get_qp_y_pred(lc); + ep->is_first_qg = 0; + } + + if (has_qp_delta) { + const int cu_qp_delta_abs = ff_vvc_cu_qp_delta_abs(lc); + + if (cu_qp_delta_abs) + cu_qp_delta = ff_vvc_cu_qp_delta_sign_flag(lc) ? -cu_qp_delta_abs : cu_qp_delta_abs; + if (cu_qp_delta > (31 + sps->qp_bd_offset / 2) || cu_qp_delta < -(32 + sps->qp_bd_offset / 2)) + return AVERROR_INVALIDDATA; + lc->parse.is_cu_qp_delta_coded = 1; + + if (cu_qp_delta) { + int off = sps->qp_bd_offset; + ep->qp_y = FFUMOD(ep->qp_y + cu_qp_delta + 64 + 2 * off, 64 + off) - off; + } + } + + set_cb_tab(lc, lc->fc->tab.qp[LUMA], ep->qp_y); + cu->qp[LUMA] = ep->qp_y; + + return 0; +} + +static void set_qp_c_tab(const VVCLocalContext *lc, const TransformUnit *tu, const TransformBlock *tb) +{ + const int is_jcbcr = tu->joint_cbcr_residual_flag && tu->coded_flag[CB] && tu->coded_flag[CR]; + const int idx = is_jcbcr ? JCBCR : tb->c_idx; + + set_tb_tab(lc->fc->tab.qp[tb->c_idx], lc->cu->qp[idx], lc->fc, tb); +} + +static void set_qp_c(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + CodingUnit *cu = lc->cu; + const int x_center = cu->x0 + cu->cb_width / 2; + const int y_center = cu->y0 + cu->cb_height / 2; + const int single_tree = cu->tree_type == SINGLE_TREE; + const int qp_luma = (single_tree ? lc->ep->qp_y : ff_vvc_get_qPy(fc, x_center, y_center)) + sps->qp_bd_offset; + const int qp_chroma = av_clip(qp_luma, 0, MAX_QP + sps->qp_bd_offset); + const int sh_chroma_qp_offset[] = { + rsh->sh_cb_qp_offset, + rsh->sh_cr_qp_offset, + rsh->sh_joint_cbcr_qp_offset, + }; + int qp; + + for (int i = CB - 1; i < CR + sps->r->sps_joint_cbcr_enabled_flag; i++) { + qp = sps->chroma_qp_table[i][qp_chroma]; + qp = qp + pps->chroma_qp_offset[i] + sh_chroma_qp_offset[i] + lc->parse.chroma_qp_offset[i]; + qp = av_clip(qp, -sps->qp_bd_offset, MAX_QP) + sps->qp_bd_offset; + cu->qp[i + 1] = qp; + } +} + +static TransformUnit* alloc_tu(VVCFrameContext *fc, CodingUnit *cu) +{ + TransformUnit *tu = ff_refstruct_pool_get(fc->tu_pool); + if (!tu) + return NULL; + + tu->next = NULL; + + if (cu->tus.tail) + cu->tus.tail->next = tu; + else + cu->tus.head = tu; + cu->tus.tail = tu; + + return tu; +} + +static TransformUnit* add_tu(VVCFrameContext *fc, CodingUnit *cu, const int x0, const int y0, const int tu_width, const int tu_height) +{ + TransformUnit *tu = alloc_tu(fc, cu); + + if (!tu) + return NULL; + + tu->x0 = x0; + tu->y0 = y0; + tu->width = tu_width; + tu->height = tu_height; + tu->joint_cbcr_residual_flag = 0; + memset(tu->coded_flag, 0, sizeof(tu->coded_flag)); + tu->nb_tbs = 0; + + return tu; +} + +static TransformBlock* add_tb(TransformUnit *tu, VVCLocalContext *lc, + const int x0, const int y0, const int tb_width, const int tb_height, const int c_idx) +{ + TransformBlock *tb; + + tb = &tu->tbs[tu->nb_tbs++]; + tb->has_coeffs = 0; + tb->x0 = x0; + tb->y0 = y0; + tb->tb_width = tb_width; + tb->tb_height = tb_height; + tb->log2_tb_width = log2(tb_width); + tb->log2_tb_height = log2(tb_height); + + tb->max_scan_x = tb->max_scan_y = 0; + tb->min_scan_x = tb->min_scan_y = 0; + + tb->c_idx = c_idx; + tb->ts = 0; + tb->coeffs = lc->coeffs; + lc->coeffs += tb_width * tb_height; + return tb; +} + +static uint8_t tu_y_coded_flag_decode(VVCLocalContext *lc, const int is_sbt_not_coded, + const int sub_tu_index, const int is_isp, const int is_chroma_coded) +{ + uint8_t tu_y_coded_flag = 0; + const VVCSPS *sps = lc->fc->ps.sps; + CodingUnit *cu = lc->cu; + + if (!is_sbt_not_coded) { + int has_y_coded_flag = sub_tu_index < cu->num_intra_subpartitions - 1 || !lc->parse.infer_tu_cbf_luma; + if (!is_isp) { + const int is_large = cu->cb_width > sps->max_tb_size_y || cu->cb_height > sps->max_tb_size_y; + has_y_coded_flag = (cu->pred_mode == MODE_INTRA && !cu->act_enabled_flag) || is_chroma_coded || is_large; + } + tu_y_coded_flag = has_y_coded_flag ? ff_vvc_tu_y_coded_flag(lc) : 1; + } + if (is_isp) + lc->parse.infer_tu_cbf_luma = lc->parse.infer_tu_cbf_luma && !tu_y_coded_flag; + return tu_y_coded_flag; +} + +static void chroma_qp_offset_decode(VVCLocalContext *lc, const int is_128, const int is_chroma_coded) +{ + const VVCPPS *pps = lc->fc->ps.pps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + + if ((is_128 || is_chroma_coded) && + rsh->sh_cu_chroma_qp_offset_enabled_flag && !lc->parse.is_cu_chroma_qp_offset_coded) { + const int cu_chroma_qp_offset_flag = ff_vvc_cu_chroma_qp_offset_flag(lc); + if (cu_chroma_qp_offset_flag) { + int cu_chroma_qp_offset_idx = 0; + if (pps->r->pps_chroma_qp_offset_list_len_minus1 > 0) + cu_chroma_qp_offset_idx = ff_vvc_cu_chroma_qp_offset_idx(lc); + for (int i = CB - 1; i < JCBCR; i++) + lc->parse.chroma_qp_offset[i] = pps->chroma_qp_offset_list[cu_chroma_qp_offset_idx][i]; + } else { + memset(lc->parse.chroma_qp_offset, 0, sizeof(lc->parse.chroma_qp_offset)); + } + lc->parse.is_cu_chroma_qp_offset_coded = 1; + } +} + +static int hls_transform_unit(VVCLocalContext *lc, int x0, int y0,int tu_width, int tu_height, int sub_tu_index, int ch_type) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + CodingUnit *cu = lc->cu; + TransformUnit *tu = add_tu(fc, cu, x0, y0, tu_width, tu_height); + const int min_cb_width = pps->min_cb_width; + const VVCTreeType tree_type = cu->tree_type; + const int is_128 = cu->cb_width > 64 || cu->cb_height > 64; + const int is_isp = cu->isp_split_type != ISP_NO_SPLIT; + const int is_isp_last_tu = is_isp && (sub_tu_index == cu->num_intra_subpartitions - 1); + const int is_sbt_not_coded = cu->sbt_flag && + ((sub_tu_index == 0 && cu->sbt_pos_flag) || (sub_tu_index == 1 && !cu->sbt_pos_flag)); + const int chroma_available = tree_type != DUAL_TREE_LUMA && sps->r->sps_chroma_format_idc && + (!is_isp || is_isp_last_tu); + int ret, xc, yc, wc, hc, is_chroma_coded; + + if (!tu) + return AVERROR_INVALIDDATA; + + if (tree_type == SINGLE_TREE && is_isp_last_tu) { + const int x_cu = x0 >> fc->ps.sps->min_cb_log2_size_y; + const int y_cu = y0 >> fc->ps.sps->min_cb_log2_size_y; + xc = SAMPLE_CTB(fc->tab.cb_pos_x[ch_type], x_cu, y_cu); + yc = SAMPLE_CTB(fc->tab.cb_pos_y[ch_type], x_cu, y_cu); + wc = SAMPLE_CTB(fc->tab.cb_width[ch_type], x_cu, y_cu); + hc = SAMPLE_CTB(fc->tab.cb_height[ch_type], x_cu, y_cu); + } else { + xc = x0, yc = y0, wc = tu_width, hc = tu_height; + } + + if (chroma_available && !is_sbt_not_coded) { + tu->coded_flag[CB] = ff_vvc_tu_cb_coded_flag(lc); + tu->coded_flag[CR] = ff_vvc_tu_cr_coded_flag(lc, tu->coded_flag[CB]); + } + + is_chroma_coded = chroma_available && (tu->coded_flag[CB] || tu->coded_flag[CR]); + + if (tree_type != DUAL_TREE_CHROMA) { + int has_qp_delta; + tu->coded_flag[LUMA] = tu_y_coded_flag_decode(lc, is_sbt_not_coded, sub_tu_index, is_isp, is_chroma_coded); + has_qp_delta = (is_128 || tu->coded_flag[LUMA] || is_chroma_coded) && + pps->r->pps_cu_qp_delta_enabled_flag && !lc->parse.is_cu_qp_delta_coded; + ret = set_qp_y(lc, x0, y0, has_qp_delta); + if (ret < 0) + return ret; + add_tb(tu, lc, x0, y0, tu_width, tu_height, LUMA); + } + if (tree_type != DUAL_TREE_LUMA) { + chroma_qp_offset_decode(lc, is_128, is_chroma_coded); + if (chroma_available) { + const int hs = sps->hshift[CHROMA]; + const int vs = sps->vshift[CHROMA]; + add_tb(tu, lc, xc, yc, wc >> hs, hc >> vs, CB); + add_tb(tu, lc, xc, yc, wc >> hs, hc >> vs, CR); + } + } + if (sps->r->sps_joint_cbcr_enabled_flag && ((cu->pred_mode == MODE_INTRA && + (tu->coded_flag[CB] || tu->coded_flag[CR])) || + (tu->coded_flag[CB] && tu->coded_flag[CR])) && + chroma_available) { + tu->joint_cbcr_residual_flag = ff_vvc_tu_joint_cbcr_residual_flag(lc, tu->coded_flag[1], tu->coded_flag[2]); + } + + for (int i = 0; i < tu->nb_tbs; i++) { + TransformBlock *tb = &tu->tbs[i]; + const int is_chroma = tb->c_idx != LUMA; + tb->has_coeffs = tu->coded_flag[tb->c_idx]; + if (tb->has_coeffs && is_chroma) + tb->has_coeffs = tb->c_idx == CB ? 1 : !(tu->coded_flag[CB] && tu->joint_cbcr_residual_flag); + if (tb->has_coeffs) { + tb->ts = cu->bdpcm_flag[tb->c_idx]; + if (sps->r->sps_transform_skip_enabled_flag && !cu->bdpcm_flag[tb->c_idx] && + tb->tb_width <= sps->max_ts_size && tb->tb_height <= sps->max_ts_size && + !cu->sbt_flag && (is_chroma || !is_isp)) { + tb->ts = ff_vvc_transform_skip_flag(lc, is_chroma); + } + ret = ff_vvc_residual_coding(lc, tb); + if (ret < 0) + return ret; + set_tb_tab(fc->tab.tu_coded_flag[tb->c_idx], tu->coded_flag[tb->c_idx], fc, tb); + } + if (tb->c_idx != CR) + set_tb_pos(fc, tb); + if (tb->c_idx == CB) + set_tb_tab(fc->tab.tu_joint_cbcr_residual_flag, tu->joint_cbcr_residual_flag, fc, tb); + } + + return 0; +} + +static int hls_transform_tree(VVCLocalContext *lc, int x0, int y0,int tu_width, int tu_height, int ch_type) +{ + const CodingUnit *cu = lc->cu; + const VVCSPS *sps = lc->fc->ps.sps; + int ret; + + lc->parse.infer_tu_cbf_luma = 1; + if (cu->isp_split_type == ISP_NO_SPLIT && !cu->sbt_flag) { + if (tu_width > sps->max_tb_size_y || tu_height > sps->max_tb_size_y) { + const int ver_split_first = tu_width > sps->max_tb_size_y && tu_width > tu_height; + const int trafo_width = ver_split_first ? (tu_width / 2) : tu_width; + const int trafo_height = !ver_split_first ? (tu_height / 2) : tu_height; + + #define TRANSFORM_TREE(x, y) do { \ + ret = hls_transform_tree(lc, x, y, trafo_width, trafo_height, ch_type); \ + if (ret < 0) \ + return ret; \ + } while (0) + + TRANSFORM_TREE(x0, y0); + if (ver_split_first) + TRANSFORM_TREE(x0 + trafo_width, y0); + else + TRANSFORM_TREE(x0, y0 + trafo_height); + + } else { + ret = hls_transform_unit(lc, x0, y0, tu_width, tu_height, 0, ch_type); + if (ret < 0) + return ret; + + } + } else if (cu->sbt_flag) { + if (!cu->sbt_horizontal_flag) { + #define TRANSFORM_UNIT(x, width, idx) do { \ + ret = hls_transform_unit(lc, x, y0, width, tu_height, idx, ch_type); \ + if (ret < 0) \ + return ret; \ + } while (0) + + const int trafo_width = tu_width * lc->parse.sbt_num_fourths_tb0 / 4; + TRANSFORM_UNIT(x0, trafo_width, 0); + TRANSFORM_UNIT(x0 + trafo_width, tu_width - trafo_width, 1); + + #undef TRANSFORM_UNIT + } else { + #define TRANSFORM_UNIT(y, height, idx) do { \ + ret = hls_transform_unit(lc, x0, y, tu_width, height, idx, ch_type); \ + if (ret < 0) \ + return ret; \ + } while (0) + + const int trafo_height = tu_height * lc->parse.sbt_num_fourths_tb0 / 4; + TRANSFORM_UNIT(y0, trafo_height, 0); + TRANSFORM_UNIT(y0 + trafo_height, tu_height - trafo_height, 1); + + #undef TRANSFORM_UNIT + } + } else if (cu->isp_split_type == ISP_HOR_SPLIT) { + const int trafo_height = tu_height / cu->num_intra_subpartitions; + for (int i = 0; i < cu->num_intra_subpartitions; i++) { + ret = hls_transform_unit(lc, x0, y0 + trafo_height * i, tu_width, trafo_height, i, 0); + if (ret < 0) + return ret; + } + } else if (cu->isp_split_type == ISP_VER_SPLIT) { + const int trafo_width = tu_width / cu->num_intra_subpartitions; + for (int i = 0; i < cu->num_intra_subpartitions; i++) { + ret = hls_transform_unit(lc, x0 + trafo_width * i , y0, trafo_width, tu_height, i, 0); + if (ret < 0) + return ret; + } + } + + return 0; +} + +static int skipped_transform_tree(VVCLocalContext *lc, int x0, int y0,int tu_width, int tu_height) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + + if (tu_width > sps->max_tb_size_y || tu_height > sps->max_tb_size_y) { + const int ver_split_first = tu_width > sps->max_tb_size_y && tu_width > tu_height; + const int trafo_width = ver_split_first ? (tu_width / 2) : tu_width; + const int trafo_height = !ver_split_first ? (tu_height / 2) : tu_height; + + #define SKIPPED_TRANSFORM_TREE(x, y) do { \ + int ret = skipped_transform_tree(lc, x, y, trafo_width, trafo_height); \ + if (ret < 0) \ + return ret; \ + } while (0) + + SKIPPED_TRANSFORM_TREE(x0, y0); + if (ver_split_first) + SKIPPED_TRANSFORM_TREE(x0 + trafo_width, y0); + else + SKIPPED_TRANSFORM_TREE(x0, y0 + trafo_height); + } else { + TransformUnit *tu = add_tu(fc, lc->cu, x0, y0, tu_width, tu_height); + const int c_end = sps->r->sps_chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : (LUMA + 1); + if (!tu) + return AVERROR_INVALIDDATA; + for (int i = LUMA; i < c_end; i++) { + TransformBlock *tb = add_tb(tu, lc, x0, y0, tu_width >> sps->hshift[i], tu_height >> sps->vshift[i], i); + if (i != CR) + set_tb_pos(fc, tb); + } + } + + return 0; +} + +//6.4.1 Allowed quad split process +//6.4.2 Allowed binary split process +//6.4.3 Allowed ternary split process +static void can_split(const VVCLocalContext *lc, int x0, int y0,int cb_width, int cb_height, + int mtt_depth, int depth_offset, int part_idx, VVCSplitMode last_split_mode, + VVCTreeType tree_type, VVCModeType mode_type, VVCAllowedSplit* split) +{ + int min_qt_size, max_bt_size, max_tt_size, max_mtt_depth; + const VVCFrameContext *fc = lc->fc; + const VVCSH *sh = &lc->sc->sh; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int chroma = tree_type == DUAL_TREE_CHROMA; + int min_cb_size_y = sps->min_cb_size_y; + int *qt = &split->qt; + int *btv = &split->btv; + int *bth = &split->bth; + int *ttv = &split->ttv; + int *tth = &split->tth; + + *qt = *bth = *btv = *tth = *ttv = 1; + + if (mtt_depth) + *qt = 0; + + min_qt_size = sh->min_qt_size[chroma]; + if (cb_width <= min_qt_size) + *qt = 0; + + if (chroma) { + int chroma_area = (cb_width >> sps->hshift[1]) * (cb_height >> sps->vshift[1]); + int chroma_width = cb_width >> sps->hshift[1]; + + if (chroma_width == 8) + *ttv = 0; + else if (chroma_width <= 4) { + if (chroma_width == 4) + *btv = 0; + *qt = 0; + } + if (mode_type == MODE_TYPE_INTRA) + *qt = *btv = *bth = *ttv = *tth = 0; + if (chroma_area <= 32) { + *ttv = *tth = 0; + if (chroma_area <= 16) + *btv = *bth = 0; + } + } + max_bt_size = sh->max_bt_size[chroma]; + max_tt_size = sh->max_tt_size[chroma]; + max_mtt_depth = sh->max_mtt_depth[chroma] + depth_offset; + + if (mode_type == MODE_TYPE_INTER) { + int area = cb_width * cb_height; + if (area == 32) + *btv = *bth = 0; + else if (area == 64) + *ttv = *tth = 0; + } + if (cb_width <= 2 * min_cb_size_y) { + *ttv = 0; + if (cb_width <= min_cb_size_y) + *btv = 0; + } + if (cb_height <= 2 * min_cb_size_y) { + *tth = 0; + if (cb_height <= min_cb_size_y) + *bth = 0; + } + if (cb_width > max_bt_size || cb_height > max_bt_size) + *btv = *bth = 0; + max_tt_size = FFMIN(64, max_tt_size); + if (cb_width > max_tt_size || cb_height > max_tt_size) + *ttv = *tth = 0; + if (mtt_depth >= max_mtt_depth) + *btv = *bth = *ttv = *tth = 0; + if (x0 + cb_width > pps->width) { + *ttv = *tth = 0; + if (cb_height > 64) + *btv = 0; + if (y0 + cb_height <= pps->height) + *bth = 0; + else if (cb_width > min_qt_size) + *btv = *bth = 0; + } + if (y0 + cb_height > pps->height) { + *btv = *ttv = *tth = 0; + if (cb_width > 64) + *bth = 0; + } + if (mtt_depth > 0 && part_idx == 1) { + if (last_split_mode == SPLIT_TT_VER) + *btv = 0; + else if (last_split_mode == SPLIT_TT_HOR) + *bth = 0; + } + if (cb_width <= 64 && cb_height > 64) + *btv = 0; + if (cb_width > 64 && cb_height <= 64) + *bth = 0; +} + +static int get_num_intra_subpartitions(enum IspType isp_split_type, int cb_width, int cb_height) +{ + if (isp_split_type == ISP_NO_SPLIT) + return 1; + if ((cb_width == 4 && cb_height == 8) || (cb_width == 8 && cb_height == 4)) + return 2; + return 4; +} + +static int get_cclm_enabled(const VVCLocalContext *lc, const int x0, const int y0) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + int enabled = 0; + + if (!sps->r->sps_cclm_enabled_flag) + return 0; + if (!sps->r->sps_qtbtt_dual_tree_intra_flag || !IS_I(lc->sc->sh.r) || sps->ctb_log2_size_y < 6) + return 1; + else { + const int x64 = x0 >> 6 << 6; + const int y64 = y0 >> 6 << 6; + const int y32 = y0 >> 5 << 5; + const int x64_cu = x64 >> fc->ps.sps->min_cb_log2_size_y; + const int y64_cu = y64 >> fc->ps.sps->min_cb_log2_size_y; + const int y32_cu = y32 >> fc->ps.sps->min_cb_log2_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int depth = SAMPLE_CTB(fc->tab.cqt_depth[1], x64_cu, y64_cu); + const int min_depth = fc->ps.sps->ctb_log2_size_y - 6; + const VVCSplitMode msm64 = (VVCSplitMode)TAB_MSM(fc, 0, x64, y64); + const VVCSplitMode msm32 = (VVCSplitMode)TAB_MSM(fc, 1, x64, y32); + + enabled = SAMPLE_CTB(fc->tab.cb_width[1], x64_cu, y64_cu) == 64 && + SAMPLE_CTB(fc->tab.cb_height[1], x64_cu, y64_cu) == 64; + enabled |= depth == min_depth && msm64 == SPLIT_BT_HOR && + SAMPLE_CTB(fc->tab.cb_width[1], x64_cu, y32_cu) == 64 && + SAMPLE_CTB(fc->tab.cb_height[1], x64_cu, y32_cu) == 32; + enabled |= depth > min_depth; + enabled |= depth == min_depth && msm64 == SPLIT_BT_HOR && msm32 == SPLIT_BT_VER; + + if (enabled) { + const int w = SAMPLE_CTB(fc->tab.cb_width[0], x64_cu, y64_cu); + const int h = SAMPLE_CTB(fc->tab.cb_height[0], x64_cu, y64_cu); + const int depth0 = SAMPLE_CTB(fc->tab.cqt_depth[0], x64_cu, y64_cu); + if ((w == 64 && h == 64 && TAB_ISPMF(fc, x64, y64)) || + ((w < 64 || h < 64) && depth0 == min_depth)) + return 0; + } + + } + + return enabled; +} + +static int less(const void *a, const void *b) +{ + return *(const int*)a - *(const int*)b; +} + +//8.4.2 Derivation process for luma intra prediction mode +static enum IntraPredMode luma_intra_pred_mode(VVCLocalContext* lc, const int intra_subpartitions_mode_flag) +{ + VVCFrameContext *fc = lc->fc; + CodingUnit *cu = lc->cu; + const int x0 = cu->x0; + const int y0 = cu->y0; + enum IntraPredMode pred; + int intra_luma_not_planar_flag = 1; + int intra_luma_mpm_remainder = 0; + int intra_luma_mpm_flag = 1; + int intra_luma_mpm_idx = 0; + + if (!cu->intra_luma_ref_idx) + intra_luma_mpm_flag = ff_vvc_intra_luma_mpm_flag(lc); + if (intra_luma_mpm_flag) { + if (!cu->intra_luma_ref_idx) + intra_luma_not_planar_flag = ff_vvc_intra_luma_not_planar_flag(lc, intra_subpartitions_mode_flag); + if (intra_luma_not_planar_flag) + intra_luma_mpm_idx = ff_vvc_intra_luma_mpm_idx(lc); + } else { + intra_luma_mpm_remainder = ff_vvc_intra_luma_mpm_remainder(lc); + } + + if (!intra_luma_not_planar_flag) { + pred = INTRA_PLANAR; + } else { + const VVCSPS *sps = fc->ps.sps; + const int x_a = (x0 - 1) >> sps->min_cb_log2_size_y; + const int y_a = (y0 + cu->cb_height - 1) >> sps->min_cb_log2_size_y; + const int x_b = (x0 + cu->cb_width - 1) >> sps->min_cb_log2_size_y; + const int y_b = (y0 - 1) >> sps->min_cb_log2_size_y; + int min_cb_width = fc->ps.pps->min_cb_width; + int x0b = av_mod_uintp2(x0, sps->ctb_log2_size_y); + int y0b = av_mod_uintp2(y0, sps->ctb_log2_size_y); + const int available_l = lc->ctb_left_flag || x0b; + const int available_u = lc->ctb_up_flag || y0b; + + int a, b, cand[5]; + + if (!available_l || (SAMPLE_CTB(fc->tab.cpm[0], x_a, y_a) != MODE_INTRA) || + SAMPLE_CTB(fc->tab.imf, x_a, y_a)) { + a = INTRA_PLANAR; + } else { + a = SAMPLE_CTB(fc->tab.ipm, x_a, y_a); + } + + if (!available_u || (SAMPLE_CTB(fc->tab.cpm[0], x_b, y_b) != MODE_INTRA) || + SAMPLE_CTB(fc->tab.imf, x_b, y_b) || !y0b) { + b = INTRA_PLANAR; + } else { + b = SAMPLE_CTB(fc->tab.ipm, x_b, y_b); + } + + if (a == b && a > INTRA_DC) { + cand[0] = a; + cand[1] = 2 + ((a + 61) % 64); + cand[2] = 2 + ((a - 1) % 64); + cand[3] = 2 + ((a + 60) % 64); + cand[4] = 2 + (a % 64); + } else { + const int minab = FFMIN(a, b); + const int maxab = FFMAX(a, b); + if (a > INTRA_DC && b > INTRA_DC) { + const int diff = maxab - minab; + cand[0] = a; + cand[1] = b; + if (diff == 1) { + cand[2] = 2 + ((minab + 61) % 64); + cand[3] = 2 + ((maxab - 1) % 64); + cand[4] = 2 + ((minab + 60) % 64); + } else if (diff >= 62) { + cand[2] = 2 + ((minab - 1) % 64); + cand[3] = 2 + ((maxab + 61) % 64); + cand[4] = 2 + (minab % 64); + } else if (diff == 2) { + cand[2] = 2 + ((minab - 1) % 64); + cand[3] = 2 + ((minab + 61) % 64); + cand[4] = 2 + ((maxab - 1) % 64); + } else { + cand[2] = 2 + ((minab + 61) % 64); + cand[3] = 2 + ((minab - 1) % 64); + cand[4] = 2 + ((maxab + 61) % 64); + } + } else if (a > INTRA_DC || b > INTRA_DC) { + cand[0] = maxab; + cand[1] = 2 + ((maxab + 61 ) % 64); + cand[2] = 2 + ((maxab - 1) % 64); + cand[3] = 2 + ((maxab + 60 ) % 64); + cand[4] = 2 + (maxab % 64); + } else { + cand[0] = INTRA_DC; + cand[1] = INTRA_VERT; + cand[2] = INTRA_HORZ; + cand[3] = INTRA_VERT - 4; + cand[4] = INTRA_VERT + 4; + } + } + if (intra_luma_mpm_flag) { + pred = cand[intra_luma_mpm_idx]; + } else { + qsort(cand, FF_ARRAY_ELEMS(cand), sizeof(cand[0]), less); + pred = intra_luma_mpm_remainder + 1; + for (int i = 0; i < FF_ARRAY_ELEMS(cand); i++) { + if (pred >= cand[i]) + pred++; + } + } + } + return pred; +} + +static int lfnst_idx_decode(VVCLocalContext *lc) +{ + CodingUnit *cu = lc->cu; + const VVCTreeType tree_type = cu->tree_type; + const VVCSPS *sps = lc->fc->ps.sps; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + const TransformUnit *tu = cu->tus.head; + int lfnst_width, lfnst_height, min_lfnst; + int lfnst_idx = 0; + + memset(cu->apply_lfnst_flag, 0, sizeof(cu->apply_lfnst_flag)); + + if (!sps->r->sps_lfnst_enabled_flag || cu->pred_mode != MODE_INTRA || FFMAX(cb_width, cb_height) > sps->max_tb_size_y) + return 0; + + while (tu) { + for (int j = 0; j < tu->nb_tbs; j++) { + const TransformBlock *tb = tu->tbs + j; + if (tu->coded_flag[tb->c_idx] && tb->ts) + return 0; + } + tu = tu->next; + } + + if (tree_type == DUAL_TREE_CHROMA) { + lfnst_width = cb_width >> sps->hshift[1]; + lfnst_height = cb_height >> sps->vshift[1]; + } else { + const int vs = cu->isp_split_type == ISP_VER_SPLIT; + const int hs = cu->isp_split_type == ISP_HOR_SPLIT; + lfnst_width = vs ? cb_width / cu->num_intra_subpartitions : cb_width; + lfnst_height = hs ? cb_height / cu->num_intra_subpartitions : cb_height; + } + min_lfnst = FFMIN(lfnst_width, lfnst_height); + if (tree_type != DUAL_TREE_CHROMA && cu->intra_mip_flag && min_lfnst < 16) + return 0; + + if (min_lfnst >= 4) { + if ((cu->isp_split_type != ISP_NO_SPLIT || !lc->parse.lfnst_dc_only) && lc->parse.lfnst_zero_out_sig_coeff_flag) + lfnst_idx = ff_vvc_lfnst_idx(lc, tree_type != SINGLE_TREE); + } + + if (lfnst_idx) { + cu->apply_lfnst_flag[LUMA] = tree_type != DUAL_TREE_CHROMA; + cu->apply_lfnst_flag[CB] = cu->apply_lfnst_flag[CR] = tree_type == DUAL_TREE_CHROMA; + } + + return lfnst_idx; +} + +static MtsIdx mts_idx_decode(VVCLocalContext *lc) +{ + const CodingUnit *cu = lc->cu; + const VVCSPS *sps = lc->fc->ps.sps; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + const uint8_t transform_skip_flag = cu->tus.head->tbs[0].ts; //fix me + int mts_idx = MTS_DCT2_DCT2; + if (cu->tree_type != DUAL_TREE_CHROMA && !cu->lfnst_idx && + !transform_skip_flag && FFMAX(cb_width, cb_height) <= 32 && + cu->isp_split_type == ISP_NO_SPLIT && !cu->sbt_flag && + lc->parse.mts_zero_out_sig_coeff_flag && !lc->parse.mts_dc_only) { + if ((cu->pred_mode == MODE_INTER && sps->r->sps_explicit_mts_inter_enabled_flag) || + (cu->pred_mode == MODE_INTRA && sps->r->sps_explicit_mts_intra_enabled_flag)) { + mts_idx = ff_vvc_mts_idx(lc); + } + } + + return mts_idx; +} + +static enum IntraPredMode derive_center_luma_intra_pred_mode(const VVCFrameContext *fc, const VVCSPS *sps, const VVCPPS *pps, const CodingUnit *cu) +{ + const int x_center = (cu->x0 + cu->cb_width / 2) >> sps->min_cb_log2_size_y; + const int y_center = (cu->y0 + cu->cb_height / 2) >> sps->min_cb_log2_size_y; + const int min_cb_width = pps->min_cb_width; + const int intra_mip_flag = SAMPLE_CTB(fc->tab.imf, x_center, y_center); + const int cu_pred_mode = SAMPLE_CTB(fc->tab.cpm[0], x_center, y_center); + const int intra_pred_mode_y = SAMPLE_CTB(fc->tab.ipm, x_center, y_center); + + if (intra_mip_flag) { + if (cu->tree_type == SINGLE_TREE && sps->r->sps_chroma_format_idc == CHROMA_FORMAT_444) + return INTRA_INVALID; + return INTRA_PLANAR; + } + if (cu_pred_mode == MODE_IBC || cu_pred_mode == MODE_PLT) + return INTRA_DC; + return intra_pred_mode_y; +} + +static void derive_chroma_intra_pred_mode(VVCLocalContext *lc, + const int cclm_mode_flag, const int cclm_mode_idx, const int intra_chroma_pred_mode) +{ + const VVCFrameContext *fc = lc->fc; + CodingUnit *cu = lc->cu; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int x_cb = cu->x0 >> sps->min_cb_log2_size_y; + const int y_cb = cu->y0 >> sps->min_cb_log2_size_y; + const int min_cb_width = pps->min_cb_width; + const int intra_mip_flag = SAMPLE_CTB(fc->tab.imf, x_cb, y_cb); + enum IntraPredMode luma_intra_pred_mode = SAMPLE_CTB(fc->tab.ipm, x_cb, y_cb); + + if (cu->tree_type == SINGLE_TREE && sps->r->sps_chroma_format_idc == CHROMA_FORMAT_444 && + intra_chroma_pred_mode == 4 && intra_mip_flag) { + cu->mip_chroma_direct_flag = 1; + cu->intra_pred_mode_c = luma_intra_pred_mode; + return; + } + luma_intra_pred_mode = derive_center_luma_intra_pred_mode(fc, sps, pps, cu); + + if (cu->act_enabled_flag) { + cu->intra_pred_mode_c = luma_intra_pred_mode; + return; + } + if (cclm_mode_flag) { + cu->intra_pred_mode_c = INTRA_LT_CCLM + cclm_mode_idx; + } else if (intra_chroma_pred_mode == 4){ + cu->intra_pred_mode_c = luma_intra_pred_mode; + } else { + const static IntraPredMode pred_mode_c[][4 + 1] = { + {INTRA_VDIAG, INTRA_PLANAR, INTRA_PLANAR, INTRA_PLANAR, INTRA_PLANAR}, + {INTRA_VERT, INTRA_VDIAG, INTRA_VERT, INTRA_VERT, INTRA_VERT}, + {INTRA_HORZ, INTRA_HORZ, INTRA_VDIAG, INTRA_HORZ, INTRA_HORZ}, + {INTRA_DC, INTRA_DC, INTRA_DC, INTRA_VDIAG, INTRA_DC}, + }; + const int modes[4] = {INTRA_PLANAR, INTRA_VERT, INTRA_HORZ, INTRA_DC}; + int idx; + + // This workaround is necessary to have 4:4:4 video decode correctly + // See VVC ticket https://jvet.hhi.fraunhofer.de/trac/vvc/ticket/1602 + // and VTM source https://vcgit.hhi.fraunhofer.de/jvet/VVCSoftware_VTM/-/blob/master/source/Lib/CommonLib/UnitTools.cpp#L736 + if (cu->tree_type == SINGLE_TREE && sps->r->sps_chroma_format_idc == CHROMA_FORMAT_444 && intra_mip_flag) { + idx = 4; + } else { + for (idx = 0; idx < FF_ARRAY_ELEMS(modes); idx++) { + if (modes[idx] == luma_intra_pred_mode) + break; + } + } + + cu->intra_pred_mode_c = pred_mode_c[intra_chroma_pred_mode][idx]; + } + if (sps->r->sps_chroma_format_idc == CHROMA_FORMAT_422 && cu->intra_pred_mode_c <= INTRA_VDIAG) { + const static int mode_map_422[INTRA_VDIAG + 1] = { + 0, 1, 61, 62, 63, 64, 65, 66, 2, 3, 5, 6, 8, 10, 12, 13, + 14, 16, 18, 20, 22, 23, 24, 26, 28, 30, 31, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 41, 42, 43, 43, 44, 44, 45, 45, 46, 47, 48, 48, + 49, 49, 50, 51, 51, 52, 52, 53, 54, 55, 55, 56, 56, 57, 57, 58, + 59, 59, 60, + }; + cu->intra_pred_mode_c = mode_map_422[cu->intra_pred_mode_c]; + } +} + +static void intra_luma_pred_modes(VVCLocalContext *lc) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + CodingUnit *cu = lc->cu; + const int log2_min_cb_size = sps->min_cb_log2_size_y; + const int x0 = cu->x0; + const int y0 = cu->y0; + const int x_cb = x0 >> log2_min_cb_size; + const int y_cb = y0 >> log2_min_cb_size; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + + cu->intra_luma_ref_idx = 0; + if (sps->r->sps_bdpcm_enabled_flag && cb_width <= sps->max_ts_size && cb_height <= sps->max_ts_size) + cu->bdpcm_flag[LUMA] = ff_vvc_intra_bdpcm_luma_flag(lc); + if (cu->bdpcm_flag[LUMA]) { + cu->intra_pred_mode_y = ff_vvc_intra_bdpcm_luma_dir_flag(lc) ? INTRA_VERT : INTRA_HORZ; + } else { + if (sps->r->sps_mip_enabled_flag) + cu->intra_mip_flag = ff_vvc_intra_mip_flag(lc, fc->tab.imf); + if (cu->intra_mip_flag) { + int intra_mip_transposed_flag = ff_vvc_intra_mip_transposed_flag(lc); + int intra_mip_mode = ff_vvc_intra_mip_mode(lc); + int x = y_cb * pps->min_cb_width + x_cb; + for (int y = 0; y < (cb_height>>log2_min_cb_size); y++) { + int width = cb_width>>log2_min_cb_size; + memset(&fc->tab.imf[x], cu->intra_mip_flag, width); + fc->tab.imtf[x] = intra_mip_transposed_flag; + fc->tab.imm[x] = intra_mip_mode; + x += pps->min_cb_width; + } + cu->intra_pred_mode_y = intra_mip_mode; + } else { + int intra_subpartitions_mode_flag = 0; + if (sps->r->sps_mrl_enabled_flag && ((y0 % sps->ctb_size_y) > 0)) + cu->intra_luma_ref_idx = ff_vvc_intra_luma_ref_idx(lc); + if (sps->r->sps_isp_enabled_flag && !cu->intra_luma_ref_idx && + (cb_width <= sps->max_tb_size_y && cb_height <= sps->max_tb_size_y) && + (cb_width * cb_height > MIN_TU_SIZE * MIN_TU_SIZE) && + !cu->act_enabled_flag) + intra_subpartitions_mode_flag = ff_vvc_intra_subpartitions_mode_flag(lc); + if (!(x0 & 63) && !(y0 & 63)) + TAB_ISPMF(fc, x0, y0) = intra_subpartitions_mode_flag; + cu->isp_split_type = ff_vvc_isp_split_type(lc, intra_subpartitions_mode_flag); + cu->num_intra_subpartitions = get_num_intra_subpartitions(cu->isp_split_type, cb_width, cb_height); + cu->intra_pred_mode_y = luma_intra_pred_mode(lc, intra_subpartitions_mode_flag); + } + } + set_cb_tab(lc, fc->tab.ipm, cu->intra_pred_mode_y); +} + +static void intra_chroma_pred_modes(VVCLocalContext *lc) +{ + const VVCSPS *sps = lc->fc->ps.sps; + CodingUnit *cu = lc->cu; + const int hs = sps->hshift[CHROMA]; + const int vs = sps->vshift[CHROMA]; + + cu->mip_chroma_direct_flag = 0; + if (sps->r->sps_bdpcm_enabled_flag && + (cu->cb_width >> hs) <= sps->max_ts_size && + (cu->cb_height >> vs) <= sps->max_ts_size) { + cu->bdpcm_flag[CB] = cu->bdpcm_flag[CR] = ff_vvc_intra_bdpcm_chroma_flag(lc); + } + if (cu->bdpcm_flag[CHROMA]) { + cu->intra_pred_mode_c = ff_vvc_intra_bdpcm_chroma_dir_flag(lc) ? INTRA_VERT : INTRA_HORZ; + } else { + const int cclm_enabled = get_cclm_enabled(lc, cu->x0, cu->y0); + int cclm_mode_flag = 0; + int cclm_mode_idx = 0; + int intra_chroma_pred_mode = 0; + + if (cclm_enabled) + cclm_mode_flag = ff_vvc_cclm_mode_flag(lc); + + if (cclm_mode_flag) + cclm_mode_idx = ff_vvc_cclm_mode_idx(lc); + else + intra_chroma_pred_mode = ff_vvc_intra_chroma_pred_mode(lc); + derive_chroma_intra_pred_mode(lc, cclm_mode_flag, cclm_mode_idx, intra_chroma_pred_mode); + } +} + +static PredMode pred_mode_decode(VVCLocalContext *lc, + const VVCTreeType tree_type, + const VVCModeType mode_type) +{ + const VVCFrameContext *fc = lc->fc; + CodingUnit *cu = lc->cu; + const VVCSPS *sps = fc->ps.sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const int ch_type = tree_type == DUAL_TREE_CHROMA ? 1 : 0; + const int is_4x4 = cu->cb_width == 4 && cu->cb_height == 4; + int pred_mode_flag; + int pred_mode_ibc_flag; + PredMode pred_mode; + + cu->skip_flag = 0; + if (!IS_I(rsh) || sps->r->sps_ibc_enabled_flag) { + const int is_128 = cu->cb_width == 128 || cu->cb_height == 128; + if (tree_type != DUAL_TREE_CHROMA && + ((!is_4x4 && mode_type != MODE_TYPE_INTRA) || + (sps->r->sps_ibc_enabled_flag && !is_128))) { + cu->skip_flag = ff_vvc_cu_skip_flag(lc, fc->tab.skip); + } + + if (is_4x4 || mode_type == MODE_TYPE_INTRA || IS_I(rsh)) { + pred_mode_flag = 1; + } else if (mode_type == MODE_TYPE_INTER || cu->skip_flag) { + pred_mode_flag = 0; + } else { + pred_mode_flag = ff_vvc_pred_mode_flag(lc, ch_type); + } + pred_mode = pred_mode_flag ? MODE_INTRA : MODE_INTER; + + if (((IS_I(rsh) && !cu->skip_flag) || + (!IS_I(rsh) && (pred_mode != MODE_INTRA || + ((is_4x4 || mode_type == MODE_TYPE_INTRA) && !cu->skip_flag)))) && + !is_128 && mode_type != MODE_TYPE_INTER && sps->r->sps_ibc_enabled_flag && + tree_type != DUAL_TREE_CHROMA) { + pred_mode_ibc_flag = ff_vvc_pred_mode_ibc_flag(lc, ch_type); + } else if (cu->skip_flag && (is_4x4 || mode_type == MODE_TYPE_INTRA)) { + pred_mode_ibc_flag = 1; + } else if (is_128 || mode_type == MODE_TYPE_INTER || tree_type == DUAL_TREE_CHROMA) { + pred_mode_ibc_flag = 0; + } else { + pred_mode_ibc_flag = (IS_I(rsh)) ? sps->r->sps_ibc_enabled_flag : 0; + } + if (pred_mode_ibc_flag) + pred_mode = MODE_IBC; + } else { + pred_mode_flag = is_4x4 || mode_type == MODE_TYPE_INTRA || + mode_type != MODE_TYPE_INTER || IS_I(rsh); + pred_mode = pred_mode_flag ? MODE_INTRA : MODE_INTER; + } + return pred_mode; +} + +static void sbt_info(VVCLocalContext *lc, const VVCSPS *sps) +{ + CodingUnit *cu = lc->cu; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + + if (cu->pred_mode == MODE_INTER && sps->r->sps_sbt_enabled_flag && !cu->ciip_flag + && cb_width <= sps->max_tb_size_y && cb_height <= sps->max_tb_size_y) { + const int sbt_ver_h = cb_width >= 8; + const int sbt_hor_h = cb_height >= 8; + cu->sbt_flag = 0; + if (sbt_ver_h || sbt_hor_h) + cu->sbt_flag = ff_vvc_sbt_flag(lc); + if (cu->sbt_flag) { + const int sbt_ver_q = cb_width >= 16; + const int sbt_hor_q = cb_height >= 16; + int cu_sbt_quad_flag = 0; + + if ((sbt_ver_h || sbt_hor_h) && (sbt_ver_q || sbt_hor_q)) + cu_sbt_quad_flag = ff_vvc_sbt_quad_flag(lc); + if (cu_sbt_quad_flag) { + cu->sbt_horizontal_flag = sbt_hor_q; + if (sbt_ver_q && sbt_hor_q) + cu->sbt_horizontal_flag = ff_vvc_sbt_horizontal_flag(lc); + } else { + cu->sbt_horizontal_flag = sbt_hor_h; + if (sbt_ver_h && sbt_hor_h) + cu->sbt_horizontal_flag = ff_vvc_sbt_horizontal_flag(lc); + } + cu->sbt_pos_flag = ff_vvc_sbt_pos_flag(lc); + + { + const int sbt_min = cu_sbt_quad_flag ? 1 : 2; + lc->parse.sbt_num_fourths_tb0 = cu->sbt_pos_flag ? (4 - sbt_min) : sbt_min; + } + } + } +} + +static int skipped_transform_tree_unit(VVCLocalContext *lc) +{ + const CodingUnit *cu = lc->cu; + int ret; + + set_qp_y(lc, cu->x0, cu->y0, 0); + set_qp_c(lc); + ret = skipped_transform_tree(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + if (ret < 0) + return ret; + return 0; +} + +static void set_cb_pos(const VVCFrameContext *fc, const CodingUnit *cu) +{ + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int log2_min_cb_size = sps->min_cb_log2_size_y; + const int x_cb = cu->x0 >> log2_min_cb_size; + const int y_cb = cu->y0 >> log2_min_cb_size; + const int ch_type = cu->ch_type; + int x, y; + + x = y_cb * pps->min_cb_width + x_cb; + for (y = 0; y < (cu->cb_height >> log2_min_cb_size); y++) { + const int width = cu->cb_width >> log2_min_cb_size; + + for (int i = 0; i < width; i++) { + fc->tab.cb_pos_x[ch_type][x + i] = cu->x0; + fc->tab.cb_pos_y[ch_type][x + i] = cu->y0; + } + memset(&fc->tab.cb_width[ch_type][x], cu->cb_width, width); + memset(&fc->tab.cb_height[ch_type][x], cu->cb_height, width); + memset(&fc->tab.cqt_depth[ch_type][x], cu->cqt_depth, width); + + x += pps->min_cb_width; + } +} + +static CodingUnit* alloc_cu(VVCLocalContext *lc, const int x0, const int y0) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int rx = x0 >> sps->ctb_log2_size_y; + const int ry = y0 >> sps->ctb_log2_size_y; + CTU *ctu = fc->tab.ctus + ry * pps->ctb_width + rx; + CodingUnit *cu = ff_refstruct_pool_get(fc->cu_pool); + + if (!cu) + return NULL; + cu->next = NULL; + + if (lc->cu) + lc->cu->next = cu; + else + ctu->cus = cu; + lc->cu = cu; + + return cu; +} + +static CodingUnit* add_cu(VVCLocalContext *lc, const int x0, const int y0, + const int cb_width, const int cb_height, const int cqt_depth, const VVCTreeType tree_type) +{ + VVCFrameContext *fc = lc->fc; + const int ch_type = tree_type == DUAL_TREE_CHROMA ? 1 : 0; + CodingUnit *cu = alloc_cu(lc, x0, y0); + + if (!cu) + return NULL; + + memset(&cu->pu, 0, sizeof(cu->pu)); + + lc->parse.prev_tu_cbf_y = 0; + + cu->sbt_flag = 0; + cu->act_enabled_flag = 0; + + cu->tree_type = tree_type; + cu->x0 = x0; + cu->y0 = y0; + cu->cb_width = cb_width; + cu->cb_height = cb_height; + cu->ch_type = ch_type; + cu->cqt_depth = cqt_depth; + cu->tus.head = cu->tus.tail = NULL; + cu->bdpcm_flag[LUMA] = cu->bdpcm_flag[CB] = cu->bdpcm_flag[CR] = 0; + cu->isp_split_type = ISP_NO_SPLIT; + cu->intra_mip_flag = 0; + cu->ciip_flag = 0; + cu->coded_flag = 1; + cu->num_intra_subpartitions = 1; + + set_cb_pos(fc, cu); + return cu; +} + +static void set_cu_tabs(const VVCLocalContext *lc, const CodingUnit *cu) +{ + const VVCFrameContext *fc = lc->fc; + const TransformUnit *tu = cu->tus.head; + + set_cb_tab(lc, fc->tab.cpm[cu->ch_type], cu->pred_mode); + if (cu->tree_type != DUAL_TREE_CHROMA) + set_cb_tab(lc, fc->tab.skip, cu->skip_flag); + + while (tu) { + for (int j = 0; j < tu->nb_tbs; j++) { + const TransformBlock *tb = tu->tbs + j; + if (tb->c_idx != LUMA) + set_qp_c_tab(lc, tu, tb); + if (tb->c_idx != CR && cu->bdpcm_flag[tb->c_idx]) + set_tb_tab(fc->tab.pcmf[tb->c_idx], 1, fc, tb); + } + tu = tu->next; + } +} + +//8.5.2.7 Derivation process for merge motion vector difference +static void derive_mmvd(const VVCLocalContext *lc, MvField *mvf, const Mv *mmvd_offset) +{ + const SliceContext *sc = lc->sc; + Mv mmvd[2]; + + if (mvf->pred_flag == PF_BI) { + const RefPicList *rpl = sc->rpl; + const int poc = lc->fc->ps.ph.poc; + const int diff[] = { + poc - rpl[0].list[mvf->ref_idx[0]], + poc - rpl[1].list[mvf->ref_idx[1]] + }; + const int sign = FFSIGN(diff[0]) != FFSIGN(diff[1]); + + if (diff[0] == diff[1]) { + mmvd[1] = mmvd[0] = *mmvd_offset; + } + else { + const int i = FFABS(diff[0]) < FFABS(diff[1]); + const int o = !i; + mmvd[i] = *mmvd_offset; + if (!rpl[0].isLongTerm[mvf->ref_idx[0]] && !rpl[1].isLongTerm[mvf->ref_idx[1]]) { + ff_vvc_mv_scale(&mmvd[o], mmvd_offset, diff[i], diff[o]); + } + else { + mmvd[o].x = sign ? -mmvd[i].x : mmvd[i].x; + mmvd[o].y = sign ? -mmvd[i].y : mmvd[i].y; + } + } + mvf->mv[0].x += mmvd[0].x; + mvf->mv[0].y += mmvd[0].y; + mvf->mv[1].x += mmvd[1].x; + mvf->mv[1].y += mmvd[1].y; + } else { + const int idx = mvf->pred_flag - PF_L0; + mvf->mv[idx].x += mmvd_offset->x; + mvf->mv[idx].y += mmvd_offset->y; + } + +} + +static void mvf_to_mi(const MvField *mvf, MotionInfo *mi) +{ + mi->pred_flag = mvf->pred_flag; + mi->bcw_idx = mvf->bcw_idx; + mi->hpel_if_idx = mvf->hpel_if_idx; + for (int i = 0; i < 2; i++) { + const PredFlag mask = i + 1; + if (mvf->pred_flag & mask) { + mi->mv[i][0] = mvf->mv[i]; + mi->ref_idx[i] = mvf->ref_idx[i]; + } + } +} + +static void mv_merge_refine_pred_flag(MvField *mvf, const int width, const int height) +{ + if (mvf->pred_flag == PF_BI && (width + height) == 12) { + mvf->pred_flag = PF_L0; + mvf->bcw_idx = 0; + } +} + +// subblock-based inter prediction data +static void merge_data_subblock(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPH *ph = &fc->ps.ph; + CodingUnit* cu = lc->cu; + PredictionUnit *pu = &cu->pu; + int merge_subblock_idx = 0; + + set_cb_tab(lc, fc->tab.msf, pu->merge_subblock_flag); + if (ph->max_num_subblock_merge_cand > 1) { + merge_subblock_idx = ff_vvc_merge_subblock_idx(lc, ph->max_num_subblock_merge_cand); + } + ff_vvc_sb_mv_merge_mode(lc, merge_subblock_idx, pu); +} + +static void merge_data_regular(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPH *ph = &fc->ps.ph; + const CodingUnit* cu = lc->cu; + PredictionUnit *pu = &lc->cu->pu; + int merge_idx = 0; + Mv mmvd_offset; + MvField mvf; + + if (sps->r->sps_mmvd_enabled_flag) + pu->mmvd_merge_flag = ff_vvc_mmvd_merge_flag(lc); + if (pu->mmvd_merge_flag) { + int mmvd_cand_flag = 0; + if (sps->max_num_merge_cand > 1) + mmvd_cand_flag = ff_vvc_mmvd_cand_flag(lc); + ff_vvc_mmvd_offset_coding(lc, &mmvd_offset, ph->r->ph_mmvd_fullpel_only_flag); + merge_idx = mmvd_cand_flag; + } else if (sps->max_num_merge_cand > 1) { + merge_idx = ff_vvc_merge_idx(lc); + } + ff_vvc_luma_mv_merge_mode(lc, merge_idx, 0, &mvf); + if (pu->mmvd_merge_flag) + derive_mmvd(lc, &mvf, &mmvd_offset); + mv_merge_refine_pred_flag(&mvf, cu->cb_width, cu->cb_height); + ff_vvc_store_mvf(lc, &mvf); + mvf_to_mi(&mvf, &pu->mi); +} + +static int ciip_flag_decode(VVCLocalContext *lc, const int ciip_avaiable, const int gpm_avaiable, const int is_128) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const CodingUnit *cu = lc->cu; + + if (ciip_avaiable && gpm_avaiable) + return ff_vvc_ciip_flag(lc); + return sps->r->sps_ciip_enabled_flag && !cu->skip_flag && + !is_128 && (cu->cb_width * cu->cb_height >= 64); +} + +static void merge_data_gpm(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + PredictionUnit *pu = &lc->cu->pu; + int merge_gpm_idx[2]; + + pu->merge_gpm_flag = 1; + pu->gpm_partition_idx = ff_vvc_merge_gpm_partition_idx(lc); + merge_gpm_idx[0] = ff_vvc_merge_gpm_idx(lc, 0); + merge_gpm_idx[1] = 0; + if (sps->max_num_gpm_merge_cand > 2) + merge_gpm_idx[1] = ff_vvc_merge_gpm_idx(lc, 1); + + ff_vvc_luma_mv_merge_gpm(lc, merge_gpm_idx, pu->gpm_mv); + ff_vvc_store_gpm_mvf(lc, pu); +} + +static void merge_data_ciip(VVCLocalContext *lc) +{ + const VVCFrameContext* fc = lc->fc; + const VVCSPS* sps = fc->ps.sps; + CodingUnit *cu = lc->cu; + MotionInfo *mi = &cu->pu.mi; + int merge_idx = 0; + MvField mvf; + + if (sps->max_num_merge_cand > 1) + merge_idx = ff_vvc_merge_idx(lc); + ff_vvc_luma_mv_merge_mode(lc, merge_idx, 1, &mvf); + mv_merge_refine_pred_flag(&mvf, cu->cb_width, cu->cb_height); + ff_vvc_store_mvf(lc, &mvf); + mvf_to_mi(&mvf, mi); + cu->intra_pred_mode_y = cu->intra_pred_mode_c = INTRA_PLANAR; + cu->intra_luma_ref_idx = 0; + cu->intra_mip_flag = 0; +} + +// block-based inter prediction data +static void merge_data_block(VVCLocalContext *lc) +{ + const VVCFrameContext* fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + CodingUnit *cu = lc->cu; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + const int is_128 = cb_width == 128 || cb_height == 128; + const int ciip_avaiable = sps->r->sps_ciip_enabled_flag && + !cu->skip_flag && (cb_width * cb_height >= 64); + const int gpm_avaiable = sps->r->sps_gpm_enabled_flag && IS_B(rsh) && + (cb_width >= 8) && (cb_height >=8) && + (cb_width < 8 * cb_height) && (cb_height < 8 *cb_width); + + int regular_merge_flag = 1; + + if (!is_128 && (ciip_avaiable || gpm_avaiable)) + regular_merge_flag = ff_vvc_regular_merge_flag(lc, cu->skip_flag); + if (regular_merge_flag) { + merge_data_regular(lc); + } else { + cu->ciip_flag = ciip_flag_decode(lc, ciip_avaiable, gpm_avaiable, is_128); + if (cu->ciip_flag) + merge_data_ciip(lc); + else + merge_data_gpm(lc); + } +} + +static int hls_merge_data(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPH *ph = &fc->ps.ph; + const CodingUnit *cu = lc->cu; + PredictionUnit *pu = &lc->cu->pu; + + pu->merge_gpm_flag = 0; + pu->mi.num_sb_x = pu->mi.num_sb_y = 1; + if (cu->pred_mode == MODE_IBC) { + avpriv_report_missing_feature(lc->fc->avctx, "Intra Block Copy"); + return AVERROR_PATCHWELCOME; + } else { + if (ph->max_num_subblock_merge_cand > 0 && cu->cb_width >= 8 && cu->cb_height >= 8) + pu->merge_subblock_flag = ff_vvc_merge_subblock_flag(lc); + if (pu->merge_subblock_flag) + merge_data_subblock(lc); + else + merge_data_block(lc); + } + return 0; +} + +static void hls_mvd_coding(VVCLocalContext *lc, Mv* mvd) +{ + int16_t mv[2]; + int i; + + for (i = 0; i < 2; i++) { + mv[i] = ff_vvc_abs_mvd_greater0_flag(lc); + } + for (i = 0; i < 2; i++) { + if (mv[i]) + mv[i] += ff_vvc_abs_mvd_greater1_flag(lc); + } + for (i = 0; i < 2; i++) { + if (mv[i] > 0) { + if (mv[i] == 2) + mv[i] += ff_vvc_abs_mvd_minus2(lc); + mv[i] = (1 - 2 * ff_vvc_mvd_sign_flag(lc)) * mv[i]; + } + } + mvd->x = mv[0]; + mvd->y = mv[1]; +} + +static int bcw_idx_decode(VVCLocalContext *lc, const MotionInfo *mi, const int cb_width, const int cb_height) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const VVCPH *ph = &fc->ps.ph; + const VVCSH *sh = &lc->sc->sh; + const PredWeightTable *w = pps->r->pps_wp_info_in_ph_flag ? &ph->pwt : &sh->pwt; + int bcw_idx = 0; + + if (sps->r->sps_bcw_enabled_flag && mi->pred_flag == PF_BI && + !w->weight_flag[L0][LUMA][mi->ref_idx[0]] && + !w->weight_flag[L1][LUMA][mi->ref_idx[1]] && + !w->weight_flag[L0][CHROMA][mi->ref_idx[0]] && + !w->weight_flag[L1][CHROMA][mi->ref_idx[1]] && + cb_width * cb_height >= 256) { + bcw_idx = ff_vvc_bcw_idx(lc, ff_vvc_no_backward_pred_flag(lc)); + } + return bcw_idx; +} + +static int8_t ref_idx_decode(VVCLocalContext *lc, const VVCSH *sh, const int sym_mvd_flag, const int lx) +{ + const H266RawSliceHeader *rsh = sh->r; + int ref_idx = 0; + + if (rsh->num_ref_idx_active[lx] > 1 && !sym_mvd_flag) + ref_idx = ff_vvc_ref_idx_lx(lc, rsh->num_ref_idx_active[lx]); + else if (sym_mvd_flag) + ref_idx = sh->ref_idx_sym[lx]; + return ref_idx; +} + +static int mvds_decode(VVCLocalContext *lc, Mv mvds[2][MAX_CONTROL_POINTS], + const int num_cp_mv, const int lx) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPH *ph = &fc->ps.ph; + const PredictionUnit *pu = &lc->cu->pu; + const MotionInfo *mi = &pu->mi; + int has_no_zero_mvd = 0; + + if (lx == L1 && ph->r->ph_mvd_l1_zero_flag && mi->pred_flag == PF_BI) { + for (int j = 0; j < num_cp_mv; j++) + AV_ZERO64(&mvds[lx][j]); + } else { + Mv *mvd0 = &mvds[lx][0]; + if (lx == L1 && pu->sym_mvd_flag) { + mvd0->x = -mvds[L0][0].x; + mvd0->y = -mvds[L0][0].y; + } else { + hls_mvd_coding(lc, mvd0); + } + has_no_zero_mvd |= (mvd0->x || mvd0->y); + for (int j = 1; j < num_cp_mv; j++) { + Mv *mvd = &mvds[lx][j]; + hls_mvd_coding(lc, mvd); + mvd->x += mvd0->x; + mvd->y += mvd0->y; + has_no_zero_mvd |= (mvd->x || mvd->y); + } + } + return has_no_zero_mvd; +} + +static void mvp_add_difference(MotionInfo *mi, const int num_cp_mv, + const Mv mvds[2][MAX_CONTROL_POINTS], const int amvr_shift) +{ + for (int i = 0; i < 2; i++) { + const PredFlag mask = i + PF_L0; + if (mi->pred_flag & mask) { + for (int j = 0; j < num_cp_mv; j++) { + const Mv *mvd = &mvds[i][j]; + mi->mv[i][j].x += mvd->x << amvr_shift; + mi->mv[i][j].y += mvd->y << amvr_shift; + } + } + } +} + +static int mvp_data(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + PredictionUnit *pu = &lc->cu->pu; + const VVCSPS *sps = fc->ps.sps; + const VVCPH *ph = &fc->ps.ph; + const VVCSH *sh = &lc->sc->sh; + const H266RawSliceHeader *rsh = sh->r; + MotionInfo *mi = &pu->mi; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + + int mvp_lx_flag[2] = {0}; + int cu_affine_type_flag = 0; + int num_cp_mv; + int amvr_enabled, has_no_zero_mvd = 0, amvr_shift; + Mv mvds[2][MAX_CONTROL_POINTS]; + + mi->pred_flag = ff_vvc_pred_flag(lc, IS_B(rsh)); + if (sps->r->sps_affine_enabled_flag && cb_width >= 16 && cb_height >= 16) { + pu->inter_affine_flag = ff_vvc_inter_affine_flag(lc); + set_cb_tab(lc, fc->tab.iaf, pu->inter_affine_flag); + if (sps->r->sps_6param_affine_enabled_flag && pu->inter_affine_flag) + cu_affine_type_flag = ff_vvc_cu_affine_type_flag(lc); + } + mi->motion_model_idc = pu->inter_affine_flag + cu_affine_type_flag; + num_cp_mv = mi->motion_model_idc + 1; + + if (sps->r->sps_smvd_enabled_flag && !ph->r->ph_mvd_l1_zero_flag && + mi->pred_flag == PF_BI && !pu->inter_affine_flag && + sh->ref_idx_sym[0] > -1 && sh->ref_idx_sym[1] > -1) + pu->sym_mvd_flag = ff_vvc_sym_mvd_flag(lc); + + for (int i = L0; i <= L1; i++) { + const PredFlag pred_flag = PF_L0 + !i; + if (mi->pred_flag != pred_flag) { + mi->ref_idx[i] = ref_idx_decode(lc, sh, pu->sym_mvd_flag, i); + has_no_zero_mvd |= mvds_decode(lc, mvds, num_cp_mv, i); + mvp_lx_flag[i] = ff_vvc_mvp_lx_flag(lc); + } + } + + amvr_enabled = mi->motion_model_idc == MOTION_TRANSLATION ? + sps->r->sps_amvr_enabled_flag : sps->r->sps_affine_amvr_enabled_flag; + amvr_enabled &= has_no_zero_mvd; + + amvr_shift = ff_vvc_amvr_shift(lc, pu->inter_affine_flag, cu->pred_mode, amvr_enabled); + + mi->hpel_if_idx = amvr_shift == 3; + mi->bcw_idx = bcw_idx_decode(lc, mi, cb_width, cb_height); + + if (mi->motion_model_idc) + ff_vvc_affine_mvp(lc, mvp_lx_flag, amvr_shift, mi); + else + ff_vvc_mvp(lc, mvp_lx_flag, amvr_shift, mi); + + mvp_add_difference(mi, num_cp_mv, mvds, amvr_shift); + + if (mi->motion_model_idc) + ff_vvc_store_sb_mvs(lc, pu); + else + ff_vvc_store_mv(lc, &pu->mi); + + return 0; +} + +// derive bdofFlag from 8.5.6 Decoding process for inter blocks +// derive dmvr from 8.5.1 General decoding process for coding units coded in inter prediction mode +static void derive_dmvr_bdof_flag(const VVCLocalContext *lc, PredictionUnit *pu) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPPS *pps = fc->ps.pps; + const VVCPH *ph = &fc->ps.ph; + const VVCSH *sh = &lc->sc->sh; + const int poc = ph->poc; + const RefPicList *rpl0 = lc->sc->rpl + L0; + const RefPicList *rpl1 = lc->sc->rpl + L1; + const int8_t *ref_idx = pu->mi.ref_idx; + const MotionInfo *mi = &pu->mi; + const CodingUnit *cu = lc->cu; + const PredWeightTable *w = pps->r->pps_wp_info_in_ph_flag ? &fc->ps.ph.pwt : &sh->pwt; + + pu->dmvr_flag = 0; + pu->bdof_flag = 0; + + if (mi->pred_flag == PF_BI && + (poc - rpl0->list[ref_idx[L0]] == rpl1->list[ref_idx[L1]] - poc) && + !rpl0->isLongTerm[ref_idx[L0]] && !rpl1->isLongTerm[ref_idx[L1]] && + !cu->ciip_flag && + !mi->bcw_idx && + !w->weight_flag[L0][LUMA][mi->ref_idx[L0]] && !w->weight_flag[L1][LUMA][mi->ref_idx[L1]] && + !w->weight_flag[L0][CHROMA][mi->ref_idx[L0]] && !w->weight_flag[L1][CHROMA][mi->ref_idx[L1]] && + cu->cb_width >= 8 && cu->cb_height >= 8 && + (cu->cb_width * cu->cb_height >= 128)) { + // fixme: for RprConstraintsActiveFlag + if (!ph->r->ph_bdof_disabled_flag && + mi->motion_model_idc == MOTION_TRANSLATION && + !pu->merge_subblock_flag && + !pu->sym_mvd_flag) + pu->bdof_flag = 1; + if (!ph->r->ph_dmvr_disabled_flag && + pu->general_merge_flag && + !pu->mmvd_merge_flag) + pu->dmvr_flag = 1; + } +} + +// part of 8.5.1 General decoding process for coding units coded in inter prediction mode +static void refine_regular_subblock(const VVCLocalContext *lc) +{ + const CodingUnit *cu = lc->cu; + PredictionUnit *pu = &lc->cu->pu; + + derive_dmvr_bdof_flag(lc, pu); + if (pu->dmvr_flag || pu->bdof_flag) { + pu->mi.num_sb_x = (cu->cb_width > 16) ? (cu->cb_width >> 4) : 1; + pu->mi.num_sb_y = (cu->cb_height > 16) ? (cu->cb_height >> 4) : 1; + } +} + +static int vvc_inter_data(VVCLocalContext *lc) +{ + const CodingUnit *cu = lc->cu; + PredictionUnit *pu = &lc->cu->pu; + const MotionInfo *mi = &pu->mi; + int ret = 0; + + pu->general_merge_flag = 1; + if (!cu->skip_flag) + pu->general_merge_flag = ff_vvc_general_merge_flag(lc); + + if (pu->general_merge_flag) { + hls_merge_data(lc); + } else if (cu->pred_mode == MODE_IBC){ + avpriv_report_missing_feature(lc->fc->avctx, "Intra Block Copy"); + return AVERROR_PATCHWELCOME; + } else { + ret = mvp_data(lc); + } + if (!pu->merge_gpm_flag && !pu->inter_affine_flag && !pu->merge_subblock_flag) { + refine_regular_subblock(lc); + ff_vvc_update_hmvp(lc, mi); + } + return ret; +} + +static int hls_coding_unit(VVCLocalContext *lc, int x0, int y0, int cb_width, int cb_height, + int cqt_depth, const VVCTreeType tree_type, VVCModeType mode_type) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const int hs = sps->hshift[CHROMA]; + const int vs = sps->vshift[CHROMA]; + const int is_128 = cb_width > 64 || cb_height > 64; + int pred_mode_plt_flag = 0; + int ret; + + CodingUnit *cu = add_cu(lc, x0, y0, cb_width, cb_height, cqt_depth, tree_type); + + if (!cu) + return AVERROR(ENOMEM); + + ff_vvc_set_neighbour_available(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + + if (IS_I(rsh) && is_128) + mode_type = MODE_TYPE_INTRA; + cu->pred_mode = pred_mode_decode(lc, tree_type, mode_type); + + if (cu->pred_mode == MODE_INTRA && sps->r->sps_palette_enabled_flag && !is_128 && !cu->skip_flag && + mode_type != MODE_TYPE_INTER && ((cb_width * cb_height) > + (tree_type != DUAL_TREE_CHROMA ? 16 : (16 << hs << vs))) && + (mode_type != MODE_TYPE_INTRA || tree_type != DUAL_TREE_CHROMA)) { + pred_mode_plt_flag = ff_vvc_pred_mode_plt_flag(lc); + if (pred_mode_plt_flag) { + avpriv_report_missing_feature(lc->fc->avctx, "Palette"); + return AVERROR_PATCHWELCOME; + } + } + if (cu->pred_mode == MODE_INTRA && sps->r->sps_act_enabled_flag && tree_type == SINGLE_TREE) { + avpriv_report_missing_feature(fc->avctx, "Adaptive Color Transform"); + return AVERROR_PATCHWELCOME; + } + if (cu->pred_mode == MODE_INTRA || cu->pred_mode == MODE_PLT) { + if (tree_type == SINGLE_TREE || tree_type == DUAL_TREE_LUMA) { + if (pred_mode_plt_flag) { + avpriv_report_missing_feature(lc->fc->avctx, "Palette"); + return AVERROR_PATCHWELCOME; + } else { + intra_luma_pred_modes(lc); + } + ff_vvc_set_intra_mvf(lc); + } + if ((tree_type == SINGLE_TREE || tree_type == DUAL_TREE_CHROMA) && sps->r->sps_chroma_format_idc) { + if (pred_mode_plt_flag && tree_type == DUAL_TREE_CHROMA) { + avpriv_report_missing_feature(lc->fc->avctx, "Palette"); + return AVERROR_PATCHWELCOME; + } else if (!pred_mode_plt_flag) { + if (!cu->act_enabled_flag) + intra_chroma_pred_modes(lc); + } + } + } else if (tree_type != DUAL_TREE_CHROMA) { /* MODE_INTER or MODE_IBC */ + if ((ret = vvc_inter_data(lc)) < 0) + return ret; + } + if (cu->pred_mode != MODE_INTRA && !pred_mode_plt_flag && !lc->cu->pu.general_merge_flag) + cu->coded_flag = ff_vvc_cu_coded_flag(lc); + else + cu->coded_flag = !(cu->skip_flag || pred_mode_plt_flag); + + if (cu->coded_flag) { + sbt_info(lc, sps); + if (sps->r->sps_act_enabled_flag && cu->pred_mode != MODE_INTRA && tree_type == SINGLE_TREE) { + avpriv_report_missing_feature(fc->avctx, "Adaptive Color Transform"); + return AVERROR_PATCHWELCOME; + } + lc->parse.lfnst_dc_only = 1; + lc->parse.lfnst_zero_out_sig_coeff_flag = 1; + lc->parse.mts_dc_only = 1; + lc->parse.mts_zero_out_sig_coeff_flag = 1; + ret = hls_transform_tree(lc, x0, y0, cb_width, cb_height, cu->ch_type); + if (ret < 0) + return ret; + cu->lfnst_idx = lfnst_idx_decode(lc); + cu->mts_idx = mts_idx_decode(lc); + set_qp_c(lc); + if (ret < 0) + return ret; + } else { + av_assert0(tree_type == SINGLE_TREE); + ret = skipped_transform_tree_unit(lc); + if (ret < 0) + return ret; + } + set_cu_tabs(lc, cu); + + return 0; +} + +static int derive_mode_type_condition(const VVCLocalContext *lc, + const VVCSplitMode split, const int cb_width, const int cb_height, const VVCModeType mode_type_curr) +{ + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const VVCSPS *sps = lc->fc->ps.sps; + const int area = cb_width * cb_height; + + if ((IS_I(rsh) && sps->r->sps_qtbtt_dual_tree_intra_flag) || + mode_type_curr != MODE_TYPE_ALL || !sps->r->sps_chroma_format_idc || + sps->r->sps_chroma_format_idc == CHROMA_FORMAT_444) + return 0; + if ((area == 64 && (split == SPLIT_QT || split == SPLIT_TT_HOR || split == SPLIT_TT_VER)) || + (area == 32 && (split == SPLIT_BT_HOR || split == SPLIT_BT_VER))) + return 1; + if ((area == 64 && (split == SPLIT_BT_HOR || split == SPLIT_BT_VER) && sps->r->sps_chroma_format_idc == CHROMA_FORMAT_420) || + (area == 128 && (split == SPLIT_TT_HOR || split == SPLIT_TT_VER) && sps->r->sps_chroma_format_idc == CHROMA_FORMAT_420) || + (cb_width == 8 && split == SPLIT_BT_VER) || (cb_width == 16 && split == SPLIT_TT_VER)) + return 1 + !IS_I(rsh); + + return 0; +} + +static VVCModeType mode_type_decode(VVCLocalContext *lc, const int x0, const int y0, + const int cb_width, const int cb_height, const VVCSplitMode split, const int ch_type, + const VVCModeType mode_type_curr) +{ + VVCModeType mode_type; + const int mode_type_condition = derive_mode_type_condition(lc, split, cb_width, cb_height, mode_type_curr); + + if (mode_type_condition == 1) + mode_type = MODE_TYPE_INTRA; + else if (mode_type_condition == 2) { + mode_type = ff_vvc_non_inter_flag(lc, x0, y0, ch_type) ? MODE_TYPE_INTRA : MODE_TYPE_INTER; + } else { + mode_type = mode_type_curr; + } + + return mode_type; +} + +static int hls_coding_tree(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, int part_idx, + VVCSplitMode last_split_mode, VVCTreeType tree_type_curr, VVCModeType mode_type_curr); + +static int coding_tree_btv(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, + VVCTreeType tree_type, VVCModeType mode_type) +{ +#define CODING_TREE(x, idx) do { \ + ret = hls_coding_tree(lc, x, y0, cb_width / 2, cb_height, \ + qg_on_y, qg_on_c, cb_sub_div + 1, cqt_depth, mtt_depth + 1, \ + depth_offset, idx, SPLIT_BT_VER, tree_type, mode_type); \ + if (ret < 0) \ + return ret; \ +} while (0); + + const VVCPPS *pps = lc->fc->ps.pps; + const int x1 = x0 + cb_width / 2; + int ret = 0; + + depth_offset += (x0 + cb_width > pps->width) ? 1 : 0; + CODING_TREE(x0, 0); + if (x1 < pps->width) + CODING_TREE(x1, 1); + + return 0; + +#undef CODING_TREE +} + +static int coding_tree_bth(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, + VVCTreeType tree_type, VVCModeType mode_type) +{ +#define CODING_TREE(y, idx) do { \ + ret = hls_coding_tree(lc, x0, y, cb_width , cb_height / 2, \ + qg_on_y, qg_on_c, cb_sub_div + 1, cqt_depth, mtt_depth + 1, \ + depth_offset, idx, SPLIT_BT_HOR, tree_type, mode_type); \ + if (ret < 0) \ + return ret; \ + } while (0); + + const VVCPPS *pps = lc->fc->ps.pps; + const int y1 = y0 + (cb_height / 2); + int ret = 0; + + depth_offset += (y0 + cb_height > pps->height) ? 1 : 0; + CODING_TREE(y0, 0); + if (y1 < pps->height) + CODING_TREE(y1, 1); + + return 0; + +#undef CODING_TREE +} + +static int coding_tree_ttv(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, + VVCTreeType tree_type, VVCModeType mode_type) +{ +#define CODING_TREE(x, w, sub_div, idx) do { \ + ret = hls_coding_tree(lc, x, y0, w, cb_height, \ + qg_on_y, qg_on_c, sub_div, cqt_depth, mtt_depth + 1, \ + depth_offset, idx, SPLIT_TT_VER, tree_type, mode_type); \ + if (ret < 0) \ + return ret; \ + } while (0); + + const VVCSH *sh = &lc->sc->sh; + const int x1 = x0 + cb_width / 4; + const int x2 = x0 + cb_width * 3 / 4; + int ret; + + qg_on_y = qg_on_y && (cb_sub_div + 2 <= sh->cu_qp_delta_subdiv); + qg_on_c = qg_on_c && (cb_sub_div + 2 <= sh->cu_chroma_qp_offset_subdiv); + + CODING_TREE(x0, cb_width / 4, cb_sub_div + 2, 0); + CODING_TREE(x1, cb_width / 2, cb_sub_div + 1, 1); + CODING_TREE(x2, cb_width / 4, cb_sub_div + 2, 2); + + return 0; + +#undef CODING_TREE +} + +static int coding_tree_tth(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, + VVCTreeType tree_type, VVCModeType mode_type) +{ +#define CODING_TREE(y, h, sub_div, idx) do { \ + ret = hls_coding_tree(lc, x0, y, cb_width, h, \ + qg_on_y, qg_on_c, sub_div, cqt_depth, mtt_depth + 1, \ + depth_offset, idx, SPLIT_TT_HOR, tree_type, mode_type); \ + if (ret < 0) \ + return ret; \ + } while (0); + + const VVCSH *sh = &lc->sc->sh; + const int y1 = y0 + (cb_height / 4); + const int y2 = y0 + (3 * cb_height / 4); + int ret; + + qg_on_y = qg_on_y && (cb_sub_div + 2 <= sh->cu_qp_delta_subdiv); + qg_on_c = qg_on_c && (cb_sub_div + 2 <= sh->cu_chroma_qp_offset_subdiv); + + CODING_TREE(y0, cb_height / 4, cb_sub_div + 2, 0); + CODING_TREE(y1, cb_height / 2, cb_sub_div + 1, 1); + CODING_TREE(y2, cb_height / 4, cb_sub_div + 2, 2); + + return 0; + +#undef CODING_TREE +} + +static int coding_tree_qt(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, + VVCTreeType tree_type, VVCModeType mode_type) +{ +#define CODING_TREE(x, y, idx) do { \ + ret = hls_coding_tree(lc, x, y, cb_width / 2, cb_height / 2, \ + qg_on_y, qg_on_c, cb_sub_div + 2, cqt_depth + 1, 0, 0, \ + idx, SPLIT_QT, tree_type, mode_type); \ + if (ret < 0) \ + return ret; \ + } while (0); + + const VVCPPS *pps = lc->fc->ps.pps; + const int x1 = x0 + cb_width / 2; + const int y1 = y0 + cb_height / 2; + int ret = 0; + + CODING_TREE(x0, y0, 0); + if (x1 < pps->width) + CODING_TREE(x1, y0, 1); + if (y1 < pps->height) + CODING_TREE(x0, y1, 2); + if (x1 < pps->width && + y1 < pps->height) + CODING_TREE(x1, y1, 3); + + return 0; + +#undef CODING_TREE +} + +typedef int (*coding_tree_fn)(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, + VVCTreeType tree_type, VVCModeType mode_type); + +const static coding_tree_fn coding_tree[] = { + coding_tree_tth, + coding_tree_bth, + coding_tree_ttv, + coding_tree_btv, + coding_tree_qt, +}; + +static int hls_coding_tree(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, int part_idx, + VVCSplitMode last_split_mode, VVCTreeType tree_type_curr, VVCModeType mode_type_curr) +{ + VVCFrameContext *fc = lc->fc; + const VVCPPS *pps = fc->ps.pps; + const VVCSH *sh = &lc->sc->sh; + const H266RawSliceHeader *rsh = sh->r; + const int ch_type = tree_type_curr == DUAL_TREE_CHROMA; + int ret; + VVCAllowedSplit allowed; + + if (pps->r->pps_cu_qp_delta_enabled_flag && qg_on_y && cb_sub_div <= sh->cu_qp_delta_subdiv) { + lc->parse.is_cu_qp_delta_coded = 0; + lc->parse.cu_qg_top_left_x = x0; + lc->parse.cu_qg_top_left_y = y0; + } + if (rsh->sh_cu_chroma_qp_offset_enabled_flag && qg_on_c && + cb_sub_div <= sh->cu_chroma_qp_offset_subdiv) { + lc->parse.is_cu_chroma_qp_offset_coded = 0; + memset(lc->parse.chroma_qp_offset, 0, sizeof(lc->parse.chroma_qp_offset)); + } + + can_split(lc, x0, y0, cb_width, cb_height, mtt_depth, depth_offset, part_idx, + last_split_mode, tree_type_curr, mode_type_curr, &allowed); + if (ff_vvc_split_cu_flag(lc, x0, y0, cb_width, cb_height, ch_type, &allowed)) { + VVCSplitMode split = ff_vvc_split_mode(lc, x0, y0, cb_width, cb_height, cqt_depth, mtt_depth, ch_type, &allowed); + VVCModeType mode_type = mode_type_decode(lc, x0, y0, cb_width, cb_height, split, ch_type, mode_type_curr); + + VVCTreeType tree_type = (mode_type == MODE_TYPE_INTRA) ? DUAL_TREE_LUMA : tree_type_curr; + + if (split != SPLIT_QT) { + if (!(x0 & 31) && !(y0 & 31) && mtt_depth <= 1) + TAB_MSM(fc, mtt_depth, x0, y0) = split; + } + ret = coding_tree[split - 1](lc, x0, y0, cb_width, cb_height, qg_on_y, qg_on_c, + cb_sub_div, cqt_depth, mtt_depth, depth_offset, tree_type, mode_type); + if (ret < 0) + return ret; + if (mode_type_curr == MODE_TYPE_ALL && mode_type == MODE_TYPE_INTRA) { + ret = hls_coding_tree(lc, x0, y0, cb_width, cb_height, 0, qg_on_c, cb_sub_div, + cqt_depth, mtt_depth, 0, 0, split, DUAL_TREE_CHROMA, mode_type); + if (ret < 0) + return ret; + } + } else { + ret = hls_coding_unit(lc, x0, y0, cb_width, cb_height, cqt_depth, tree_type_curr, mode_type_curr); + if (ret < 0) + return ret; + } + + return 0; +} + +static int dual_tree_implicit_qt_split(VVCLocalContext *lc, + const int x0, const int y0, const int cb_size, const int cqt_depth) +{ + const VVCSH *sh = &lc->sc->sh; + const H266RawSliceHeader *rsh = sh->r; + const VVCPPS *pps = lc->fc->ps.pps; + const int cb_subdiv = 2 * cqt_depth; + int ret; + + if (cb_size > 64) { + #define DUAL_TREE(x, y) do { \ + ret = dual_tree_implicit_qt_split(lc, x, y, cb_size / 2, cqt_depth + 1); \ + if (ret < 0) \ + return ret; \ + } while (0) + + const int x1 = x0 + (cb_size / 2); + const int y1 = y0 + (cb_size / 2); + if (pps->r->pps_cu_qp_delta_enabled_flag && cb_subdiv <= sh->cu_qp_delta_subdiv) { + lc->parse.is_cu_qp_delta_coded = 0; + lc->parse.cu_qg_top_left_x = x0; + lc->parse.cu_qg_top_left_y = y0; + } + if (rsh->sh_cu_chroma_qp_offset_enabled_flag && cb_subdiv <= sh->cu_chroma_qp_offset_subdiv) { + lc->parse.is_cu_chroma_qp_offset_coded = 0; + memset(lc->parse.chroma_qp_offset, 0, sizeof(lc->parse.chroma_qp_offset)); + } + DUAL_TREE(x0, y0); + if (x1 < pps->width) + DUAL_TREE(x1, y0); + if (y1 < pps->height) + DUAL_TREE(x0, y1); + if (x1 < pps->width && y1 < pps->height) + DUAL_TREE(x1, y1); + #undef DUAL_TREE + } else { + #define CODING_TREE(tree_type) do { \ + const int qg_on_y = tree_type == DUAL_TREE_LUMA; \ + ret = hls_coding_tree(lc, x0, y0, cb_size, cb_size, qg_on_y, !qg_on_y, \ + cb_subdiv, cqt_depth, 0, 0, 0, SPLIT_NONE, tree_type, MODE_TYPE_ALL); \ + if (ret < 0) \ + return ret; \ + } while (0) + CODING_TREE(DUAL_TREE_LUMA); + CODING_TREE(DUAL_TREE_CHROMA); + #undef CODING_TREE + } + return 0; +} + +#define SET_SAO(elem, value) \ +do { \ + if (!sao_merge_up_flag && !sao_merge_left_flag) \ + sao->elem = value; \ + else if (sao_merge_left_flag) \ + sao->elem = CTB(fc->tab.sao, rx-1, ry).elem; \ + else if (sao_merge_up_flag) \ + sao->elem = CTB(fc->tab.sao, rx, ry-1).elem; \ + else \ + sao->elem = 0; \ +} while (0) + +static void hls_sao(VVCLocalContext *lc, const int rx, const int ry) +{ + VVCFrameContext *fc = lc->fc; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + int sao_merge_left_flag = 0; + int sao_merge_up_flag = 0; + SAOParams *sao = &CTB(fc->tab.sao, rx, ry); + int c_idx, i; + + if (rsh->sh_sao_luma_used_flag || rsh->sh_sao_chroma_used_flag) { + if (rx > 0) { + if (lc->ctb_left_flag) + sao_merge_left_flag = ff_vvc_sao_merge_flag_decode(lc); + } + if (ry > 0 && !sao_merge_left_flag) { + if (lc->ctb_up_flag) + sao_merge_up_flag = ff_vvc_sao_merge_flag_decode(lc); + } + } + + for (c_idx = 0; c_idx < (fc->ps.sps->r->sps_chroma_format_idc ? 3 : 1); c_idx++) { + const int sao_used_flag = !c_idx ? rsh->sh_sao_luma_used_flag : rsh->sh_sao_chroma_used_flag; + if (!sao_used_flag) { + sao->type_idx[c_idx] = SAO_NOT_APPLIED; + continue; + } + + if (c_idx == 2) { + sao->type_idx[2] = sao->type_idx[1]; + sao->eo_class[2] = sao->eo_class[1]; + } else { + SET_SAO(type_idx[c_idx], ff_vvc_sao_type_idx_decode(lc)); + } + + if (sao->type_idx[c_idx] == SAO_NOT_APPLIED) + continue; + + for (i = 0; i < 4; i++) + SET_SAO(offset_abs[c_idx][i], ff_vvc_sao_offset_abs_decode(lc)); + + if (sao->type_idx[c_idx] == SAO_BAND) { + for (i = 0; i < 4; i++) { + if (sao->offset_abs[c_idx][i]) { + SET_SAO(offset_sign[c_idx][i], + ff_vvc_sao_offset_sign_decode(lc)); + } else { + sao->offset_sign[c_idx][i] = 0; + } + } + SET_SAO(band_position[c_idx], ff_vvc_sao_band_position_decode(lc)); + } else if (c_idx != 2) { + SET_SAO(eo_class[c_idx], ff_vvc_sao_eo_class_decode(lc)); + } + + // Inferred parameters + sao->offset_val[c_idx][0] = 0; + for (i = 0; i < 4; i++) { + sao->offset_val[c_idx][i + 1] = sao->offset_abs[c_idx][i]; + if (sao->type_idx[c_idx] == SAO_EDGE) { + if (i > 1) + sao->offset_val[c_idx][i + 1] = -sao->offset_val[c_idx][i + 1]; + } else if (sao->offset_sign[c_idx][i]) { + sao->offset_val[c_idx][i + 1] = -sao->offset_val[c_idx][i + 1]; + } + sao->offset_val[c_idx][i + 1] *= 1 << (fc->ps.sps->bit_depth - FFMIN(10, fc->ps.sps->bit_depth)); + } + } +} + +static void alf_params(VVCLocalContext *lc, const int rx, const int ry) +{ + const VVCFrameContext *fc = lc->fc; + const H266RawSliceHeader *sh = lc->sc->sh.r; + ALFParams *alf = &CTB(fc->tab.alf, rx, ry); + + alf->ctb_flag[LUMA] = alf->ctb_flag[CB] = alf->ctb_flag[CR] = 0; + if (sh->sh_alf_enabled_flag) { + alf->ctb_flag[LUMA] = ff_vvc_alf_ctb_flag(lc, rx, ry, LUMA); + if (alf->ctb_flag[LUMA]) { + uint8_t alf_use_aps_flag = 0; + if (sh->sh_num_alf_aps_ids_luma > 0) + alf_use_aps_flag = ff_vvc_alf_use_aps_flag(lc); + if (alf_use_aps_flag) { + alf->ctb_filt_set_idx_y = 16; + if (sh->sh_num_alf_aps_ids_luma > 1) + alf->ctb_filt_set_idx_y += ff_vvc_alf_luma_prev_filter_idx(lc); + } else { + alf->ctb_filt_set_idx_y = ff_vvc_alf_luma_fixed_filter_idx(lc); + } + } + for (int c_idx = CB; c_idx <= CR; c_idx++) { + const uint8_t alf_enabled_flag = + c_idx == CB ? sh->sh_alf_cb_enabled_flag : sh->sh_alf_cr_enabled_flag; + if (alf_enabled_flag) { + const VVCALF *aps = fc->ps.alf_list[sh->sh_alf_aps_id_chroma]; + alf->ctb_flag[c_idx] = ff_vvc_alf_ctb_flag(lc, rx, ry, c_idx); + alf->alf_ctb_filter_alt_idx[c_idx - 1] = 0; + if (alf->ctb_flag[c_idx] && aps->num_chroma_filters > 1) + alf->alf_ctb_filter_alt_idx[c_idx - 1] = ff_vvc_alf_ctb_filter_alt_idx(lc, c_idx, aps->num_chroma_filters); + } + } + } + if (fc->ps.sps->r->sps_ccalf_enabled_flag) { + const uint8_t cc_enabled[] = { sh->sh_alf_cc_cb_enabled_flag, sh->sh_alf_cc_cr_enabled_flag }; + const uint8_t cc_aps_id[] = { sh->sh_alf_cc_cb_aps_id, sh->sh_alf_cc_cr_aps_id }; + for (int i = 0; i < 2; i++) { + alf->ctb_cc_idc[i] = 0; + if (cc_enabled[i]) { + const VVCALF *aps = fc->ps.alf_list[cc_aps_id[i]]; + alf->ctb_cc_idc[i] = ff_vvc_alf_ctb_cc_idc(lc, rx, ry, i, aps->num_cc_filters[i]); + } + } + } +} + +static void deblock_params(VVCLocalContext *lc, const int rx, const int ry) +{ + VVCFrameContext *fc = lc->fc; + const VVCSH *sh = &lc->sc->sh; + CTB(fc->tab.deblock, rx, ry) = sh->deblock; +} + +static int hls_coding_tree_unit(VVCLocalContext *lc, + const int x0, const int y0, const int ctu_idx, const int rx, const int ry) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const VVCSH *sh = &lc->sc->sh; + const H266RawSliceHeader *rsh = sh->r; + const unsigned int ctb_size = sps->ctb_size_y; + int ret = 0; + + memset(lc->parse.chroma_qp_offset, 0, sizeof(lc->parse.chroma_qp_offset)); + + hls_sao(lc, x0 >> sps->ctb_log2_size_y, y0 >> sps->ctb_log2_size_y); + alf_params(lc, x0 >> sps->ctb_log2_size_y, y0 >> sps->ctb_log2_size_y); + deblock_params(lc, x0 >> sps->ctb_log2_size_y, y0 >> sps->ctb_log2_size_y); + + if (IS_I(rsh) && sps->r->sps_qtbtt_dual_tree_intra_flag) + ret = dual_tree_implicit_qt_split(lc, x0, y0, ctb_size, 0); + else + ret = hls_coding_tree(lc, x0, y0, ctb_size, ctb_size, + 1, 1, 0, 0, 0, 0, 0, SPLIT_NONE, SINGLE_TREE, MODE_TYPE_ALL); + if (ret < 0) + return ret; + + if (rx == pps->ctb_to_col_bd[rx + 1] - 1) { + if (ctu_idx == sh->num_ctus_in_curr_slice - 1) { + const int end_of_slice_one_bit = ff_vvc_end_of_slice_flag_decode(lc); + if (!end_of_slice_one_bit) + return AVERROR_INVALIDDATA; + } else { + if (ry == pps->ctb_to_row_bd[ry + 1] - 1) { + const int end_of_tile_one_bit = ff_vvc_end_of_tile_one_bit(lc); + if (!end_of_tile_one_bit) + return AVERROR_INVALIDDATA; + } else { + if (fc->ps.sps->r->sps_entropy_coding_sync_enabled_flag) { + const int end_of_subset_one_bit = ff_vvc_end_of_subset_one_bit(lc); + if (!end_of_subset_one_bit) + return AVERROR_INVALIDDATA; + } + } + } + } + + return 0; +} + +static int has_inter_luma(const CodingUnit *cu) +{ + return cu->pred_mode != MODE_INTRA && cu->pred_mode != MODE_PLT && cu->tree_type != DUAL_TREE_CHROMA; +} + +static int pred_get_y(const int y0, const Mv *mv, const int height) +{ + return FFMAX(0, y0 + (mv->y >> 4) + height); +} + +static void cu_get_max_y(const CodingUnit *cu, int max_y[2][VVC_MAX_REF_ENTRIES], const VVCFrameContext *fc) +{ + const PredictionUnit *pu = &cu->pu; + + if (pu->merge_gpm_flag) { + for (int i = 0; i < FF_ARRAY_ELEMS(pu->gpm_mv); i++) { + const MvField *mvf = pu->gpm_mv + i; + const int lx = mvf->pred_flag - PF_L0; + const int idx = mvf->ref_idx[lx]; + const int y = pred_get_y(cu->y0, mvf->mv + lx, cu->cb_height); + + max_y[lx][idx] = FFMAX(max_y[lx][idx], y); + } + } else { + const MotionInfo *mi = &pu->mi; + const int max_dmvr_off = (!pu->inter_affine_flag && pu->dmvr_flag) ? 2 : 0; + const int sbw = cu->cb_width / mi->num_sb_x; + const int sbh = cu->cb_height / mi->num_sb_y; + for (int sby = 0; sby < mi->num_sb_y; sby++) { + for (int sbx = 0; sbx < mi->num_sb_x; sbx++) { + const int x0 = cu->x0 + sbx * sbw; + const int y0 = cu->y0 + sby * sbh; + const MvField *mvf = ff_vvc_get_mvf(fc, x0, y0); + for (int lx = 0; lx < 2; lx++) { + const PredFlag mask = 1 << lx; + if (mvf->pred_flag & mask) { + const int idx = mvf->ref_idx[lx]; + const int y = pred_get_y(y0, mvf->mv + lx, sbh); + + max_y[lx][idx] = FFMAX(max_y[lx][idx], y + max_dmvr_off); + } + } + } + } + } +} + +static void pred_get_max_y(VVCLocalContext *lc, const int rs) +{ + const VVCFrameContext *fc = lc->fc; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + CTU *ctu = fc->tab.ctus + rs; + const CodingUnit *cu = ctu->cus; + + if (IS_I(rsh)) + return; + + for (int lx = 0; lx < 2; lx++) + memset(ctu->max_y[lx], -1, sizeof(ctu->max_y[0][0]) * rsh->num_ref_idx_active[lx]); + + while (cu) { + if (has_inter_luma(cu)) + cu_get_max_y(cu, ctu->max_y, fc); + cu = cu->next; + } + ctu->max_y_idx[0] = ctu->max_y_idx[1] = 0; +} + +int ff_vvc_coding_tree_unit(VVCLocalContext *lc, + const int ctu_idx, const int rs, const int rx, const int ry) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int x_ctb = rx << sps->ctb_log2_size_y; + const int y_ctb = ry << sps->ctb_log2_size_y; + const int ctb_size = 1 << sps->ctb_log2_size_y << sps->ctb_log2_size_y; + EntryPoint* ep = lc->ep; + int ret; + + if (rx == pps->ctb_to_col_bd[rx]) { + //fix me for ibc + ep->num_hmvp = 0; + ep->is_first_qg = ry == pps->ctb_to_row_bd[ry] || !ctu_idx; + } + + lc->coeffs = fc->tab.coeffs + rs * ctb_size * VVC_MAX_SAMPLE_ARRAYS; + lc->cu = NULL; + + ff_vvc_cabac_init(lc, ctu_idx, rx, ry); + ff_vvc_decode_neighbour(lc, x_ctb, y_ctb, rx, ry, rs); + ret = hls_coding_tree_unit(lc, x_ctb, y_ctb, ctu_idx, rx, ry); + if (ret < 0) + return ret; + pred_get_max_y(lc, rs); + + return 0; +} + void ff_vvc_decode_neighbour(VVCLocalContext *lc, const int x_ctb, const int y_ctb, const int rx, const int ry, const int rs) { @@ -90,6 +2454,14 @@ void ff_vvc_ctu_free_cus(CTU *ctu) } } +int ff_vvc_get_qPy(const VVCFrameContext *fc, const int xc, const int yc) +{ + const int min_cb_log2_size_y = fc->ps.sps->min_cb_log2_size_y; + const int x = xc >> min_cb_log2_size_y; + const int y = yc >> min_cb_log2_size_y; + return fc->tab.qp[LUMA][x + y * fc->ps.pps->min_cb_width]; +} + void ff_vvc_ep_init_stat_coeff(EntryPoint *ep, const int bit_depth, const int persistent_rice_adaptation_enabled_flag) { diff --git a/libavcodec/vvc/vvc_ctu.h b/libavcodec/vvc/vvc_ctu.h index e9b157795c..f6563599ae 100644 --- a/libavcodec/vvc/vvc_ctu.h +++ b/libavcodec/vvc/vvc_ctu.h @@ -458,6 +458,17 @@ typedef struct ALFParams { uint8_t applied[3]; } ALFParams; +/** + * parse a CTU + * @param lc local context for CTU + * @param ctb_addr CTB(CTU) address in the current slice + * @param rs raster order for the CTU. + * @param rx raster order x for the CTU. + * @param ry raster order y for the CTU. + * @return AVERROR + */ +int ff_vvc_coding_tree_unit(VVCLocalContext *lc, int ctu_idx, int rs, int rx, int ry); + //utils void ff_vvc_set_neighbour_available(VVCLocalContext *lc, int x0, int y0, int w, int h); void ff_vvc_decode_neighbour(VVCLocalContext *lc, int x_ctb, int y_ctb, int rx, int ry, int rs);