/* * VVC video decoder * * Copyright (C) 2021 Nuo Mi * Copyright (C) 2022 Xu Mu * * 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 "libavcodec/codec_internal.h"#include "libavcodec/decode.h"#include "libavcodec/hwaccel_internal.h"#include "libavcodec/hwconfig.h"#include "libavcodec/profiles.h"#include "libavutil/refstruct.h"#include "libavutil/cpu.h"#include "libavutil/mem.h"#include "libavutil/thread.h" #include "dec.h"#include "ctu.h"#include "data.h"#include "refs.h"#include "thread.h"#include "config_components.h" #define TAB_MAX 32 typedef struct Tab { void **tab; size_t size;} Tab; typedef struct TabList { Tab tabs[TAB_MAX]; int nb_tabs; int zero; int realloc;} TabList; #define TL_ADD(t, s) do { \ av_assert0(l->nb_tabs < TAB_MAX); \ l->tabs[l->nb_tabs].tab = (void**)&fc->tab.t; \ l->tabs[l->nb_tabs].size = sizeof(*fc->tab.t) * (s); \ l->nb_tabs++; \} while (0) static void tl_init(TabList *l, const int zero, const int realloc){ l->nb_tabs = 0; l->zero = zero; l->realloc = realloc;} static int tl_free(TabList *l){ for (int i = 0; i < l->nb_tabs; i++) av_freep(l->tabs[i].tab); return 0;} static int tl_create(TabList *l){ if (l->realloc) { tl_free(l); for (int i = 0; i < l->nb_tabs; i++) { Tab *t = l->tabs + i; *t->tab = l->zero ? av_mallocz(t->size) : av_malloc(t->size); if (!*t->tab) return AVERROR(ENOMEM); } } return 0;} static int tl_zero(TabList *l){ if (l->zero) { for (int i = 0; i < l->nb_tabs; i++) { Tab *t = l->tabs + i; memset(*t->tab, 0, t->size); } } return 0;} static void ctu_nz_tl_init(TabList *l, VVCFrameContext *fc){ const VVCSPS *sps = fc->ps.sps; const VVCPPS *pps = fc->ps.pps; const int ctu_size = sps ? (1 << sps->ctb_log2_size_y << sps->ctb_log2_size_y) : 0; const int ctu_count = pps ? pps->ctb_count : 0; const int changed = fc->tab.sz.ctu_count != ctu_count || fc->tab.sz.ctu_size != ctu_size; tl_init(l, 0, changed); TL_ADD(cus, ctu_count); TL_ADD(ctus, ctu_count); TL_ADD(deblock, ctu_count); TL_ADD(sao, ctu_count); TL_ADD(alf, ctu_count); TL_ADD(slice_idx, ctu_count); TL_ADD(coeffs, ctu_count * ctu_size * VVC_MAX_SAMPLE_ARRAYS);} static void min_cb_tl_init(TabList *l, VVCFrameContext *fc){ const VVCPPS *pps = fc->ps.pps; const int pic_size_in_min_cb = pps ? pps->min_cb_width * pps->min_cb_height : 0; const int changed = fc->tab.sz.pic_size_in_min_cb != pic_size_in_min_cb; tl_init(l, 1, changed); TL_ADD(imf, pic_size_in_min_cb); for (int i = LUMA; i <= CHROMA; i++) TL_ADD(cb_width[i], pic_size_in_min_cb); //is_a0_available requires this} static void min_cb_nz_tl_init(TabList *l, VVCFrameContext *fc){ const VVCPPS *pps = fc->ps.pps; const int pic_size_in_min_cb = pps ? pps->min_cb_width * pps->min_cb_height : 0; const int changed = fc->tab.sz.pic_size_in_min_cb != pic_size_in_min_cb; tl_init(l, 0, changed); TL_ADD(skip, pic_size_in_min_cb); TL_ADD(ipm, pic_size_in_min_cb); for (int i = LUMA; i <= CHROMA; i++) { TL_ADD(cqt_depth[i], pic_size_in_min_cb); TL_ADD(cb_pos_x[i], pic_size_in_min_cb); TL_ADD(cb_pos_y[i], pic_size_in_min_cb); TL_ADD(cb_height[i], pic_size_in_min_cb); TL_ADD(cp_mv[i], pic_size_in_min_cb * MAX_CONTROL_POINTS); TL_ADD(cpm[i], pic_size_in_min_cb); TL_ADD(pcmf[i], pic_size_in_min_cb); } // For luma, qp can only change at the CU level, so the qp tab size is related to the CU. TL_ADD(qp[LUMA], pic_size_in_min_cb);} static void min_pu_tl_init(TabList *l, VVCFrameContext *fc){ const VVCPPS *pps = fc->ps.pps; const int pic_size_in_min_pu = pps ? pps->min_pu_width * pps->min_pu_height : 0; const int changed = fc->tab.sz.pic_size_in_min_pu != pic_size_in_min_pu; tl_init(l, 1, changed); TL_ADD(iaf, pic_size_in_min_pu);} static void min_pu_nz_tl_init(TabList *l, VVCFrameContext *fc){ const VVCPPS *pps = fc->ps.pps; const int pic_size_in_min_pu = pps ? pps->min_pu_width * pps->min_pu_height : 0; const int changed = fc->tab.sz.pic_size_in_min_pu != pic_size_in_min_pu; tl_init(l, 0, changed); TL_ADD(msf, pic_size_in_min_pu); TL_ADD(mmi, pic_size_in_min_pu); TL_ADD(mvf, pic_size_in_min_pu);} static void min_tu_tl_init(TabList *l, VVCFrameContext *fc){ const VVCPPS *pps = fc->ps.pps; const int pic_size_in_min_tu = pps ? pps->min_tu_width * pps->min_tu_height : 0; const int changed = fc->tab.sz.pic_size_in_min_tu != pic_size_in_min_tu; tl_init(l, 1, changed); TL_ADD(tu_joint_cbcr_residual_flag, pic_size_in_min_tu); for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS; i++) { TL_ADD(tu_coded_flag[i], pic_size_in_min_tu); for (int vertical = 0; vertical < 2; vertical++) TL_ADD(bs[vertical][i], pic_size_in_min_tu); }} static void min_tu_nz_tl_init(TabList *l, VVCFrameContext *fc){ const VVCPPS *pps = fc->ps.pps; const int pic_size_in_min_tu = pps ? pps->min_tu_width * pps->min_tu_height : 0; const int changed = fc->tab.sz.pic_size_in_min_tu != pic_size_in_min_tu; tl_init(l, 0, changed); for (int i = LUMA; i <= CHROMA; i++) { TL_ADD(tb_width[i], pic_size_in_min_tu); TL_ADD(tb_height[i], pic_size_in_min_tu); } for (int vertical = 0; vertical < 2; vertical++) { TL_ADD(max_len_p[vertical], pic_size_in_min_tu); TL_ADD(max_len_q[vertical], pic_size_in_min_tu); } // For chroma, considering the joint CbCr, the QP tab size is related to the TU. for (int i = CB; i < VVC_MAX_SAMPLE_ARRAYS; i++) TL_ADD(qp[i], pic_size_in_min_tu);} static void pixel_buffer_nz_tl_init(TabList *l, VVCFrameContext *fc){ const VVCSPS *sps = fc->ps.sps; const VVCPPS *pps = fc->ps.pps; const int width = pps ? pps->width : 0; const int height = pps ? pps->height : 0; const int ctu_width = pps ? pps->ctb_width : 0; const int ctu_height = pps ? pps->ctb_height : 0; const int chroma_idc = sps ? sps->r->sps_chroma_format_idc : 0; const int ps = sps ? sps->pixel_shift : 0; const int c_end = chroma_idc ? VVC_MAX_SAMPLE_ARRAYS : 1; const int changed = fc->tab.sz.chroma_format_idc != chroma_idc || fc->tab.sz.width != width || fc->tab.sz.height != height || fc->tab.sz.ctu_width != ctu_width || fc->tab.sz.ctu_height != ctu_height || fc->tab.sz.pixel_shift != ps; tl_init(l, 0, changed); for (int c_idx = 0; c_idx < c_end; c_idx++) { const int w = width >> (sps ? sps->hshift[c_idx] : 0); const int h = height >> (sps ? sps->vshift[c_idx] : 0); TL_ADD(sao_pixel_buffer_h[c_idx], (w * 2 * ctu_height) << ps); TL_ADD(sao_pixel_buffer_v[c_idx], (h * 2 * ctu_width) << ps); } for (int c_idx = 0; c_idx < c_end; c_idx++) { const int w = width >> (sps ? sps->hshift[c_idx] : 0); const int h = height >> (sps ? sps->vshift[c_idx] : 0); const int border_pixels = c_idx ? ALF_BORDER_CHROMA : ALF_BORDER_LUMA; for (int i = 0; i < 2; i++) { TL_ADD(alf_pixel_buffer_h[c_idx][i], (w * border_pixels * ctu_height) << ps); TL_ADD(alf_pixel_buffer_v[c_idx][i], h * ALF_PADDING_SIZE * ctu_width); } }} static void msm_tl_init(TabList *l, VVCFrameContext *fc){ const VVCPPS *pps = fc->ps.pps; const int w32 = pps ? AV_CEIL_RSHIFT(pps->width, 5) : 0; const int h32 = pps ? AV_CEIL_RSHIFT(pps->height, 5) : 0; const int changed = AV_CEIL_RSHIFT(fc->tab.sz.width, 5) != w32 || AV_CEIL_RSHIFT(fc->tab.sz.height, 5) != h32; tl_init(l, 1, changed); for (int i = LUMA; i <= CHROMA; i++) TL_ADD(msm[i], w32 * h32);} static void ispmf_tl_init(TabList *l, VVCFrameContext *fc){ const VVCPPS *pps = fc->ps.pps; const int w64 = pps ? AV_CEIL_RSHIFT(pps->width, 6) : 0; const int h64 = pps ? AV_CEIL_RSHIFT(pps->height, 6) : 0; const int changed = AV_CEIL_RSHIFT(fc->tab.sz.width, 6) != w64 || AV_CEIL_RSHIFT(fc->tab.sz.height, 6) != h64; tl_init(l, 1, changed); TL_ADD(ispmf, w64 * h64);} static void ibc_tl_init(TabList *l, VVCFrameContext *fc){ const VVCSPS *sps = fc->ps.sps; const VVCPPS *pps = fc->ps.pps; const int ctu_height = pps ? pps->ctb_height : 0; const int ctu_size = sps ? sps->ctb_size_y : 0; const int ps = sps ? sps->pixel_shift : 0; const int chroma_idc = sps ? sps->r->sps_chroma_format_idc : 0; const int has_ibc = sps ? sps->r->sps_ibc_enabled_flag : 0; const int changed = fc->tab.sz.chroma_format_idc != chroma_idc || fc->tab.sz.ctu_height != ctu_height || fc->tab.sz.ctu_size != ctu_size || fc->tab.sz.pixel_shift != ps; fc->tab.sz.ibc_buffer_width = ctu_size ? 2 * MAX_CTU_SIZE * MAX_CTU_SIZE / ctu_size : 0; tl_init(l, has_ibc, changed); for (int i = LUMA; i < VVC_MAX_SAMPLE_ARRAYS; i++) { const int hs = sps ? sps->hshift[i] : 0; const int vs = sps ? sps->vshift[i] : 0; TL_ADD(ibc_vir_buf[i], fc->tab.sz.ibc_buffer_width * ctu_size * ctu_height << ps >> hs >> vs); }} typedef void (*tl_init_fn)(TabList *l, VVCFrameContext *fc); static int frame_context_for_each_tl(VVCFrameContext *fc, int (*unary_fn)(TabList *l)){ const tl_init_fn init[] = { ctu_nz_tl_init, min_cb_tl_init, min_cb_nz_tl_init, min_pu_tl_init, min_pu_nz_tl_init, min_tu_tl_init, min_tu_nz_tl_init, pixel_buffer_nz_tl_init, msm_tl_init, ispmf_tl_init, ibc_tl_init, }; for (int i = 0; i < FF_ARRAY_ELEMS(init); i++) { TabList l; int ret; init[i](&l, fc); ret = unary_fn(&l); if (ret < 0) return ret; } return 0;} static void free_cus(VVCFrameContext *fc){ if (fc->tab.cus) { for (int i = 0; i < fc->tab.sz.ctu_count; i++) ff_vvc_ctu_free_cus(fc->tab.cus + i); }} static void pic_arrays_free(VVCFrameContext *fc){ free_cus(fc); frame_context_for_each_tl(fc, tl_free); av_refstruct_pool_uninit(&fc->rpl_tab_pool); av_refstruct_pool_uninit(&fc->tab_dmvr_mvf_pool); memset(&fc->tab.sz, 0, sizeof(fc->tab.sz));} static int pic_arrays_init(VVCContext *s, VVCFrameContext *fc){ const VVCSPS *sps = fc->ps.sps; const VVCPPS *pps = fc->ps.pps; const int ctu_count = pps->ctb_count; const int pic_size_in_min_pu = pps->min_pu_width * pps->min_pu_height; int ret; free_cus(fc); ret = frame_context_for_each_tl(fc, tl_create); if (ret < 0) return ret; // for error handling case, we may call free_cus before VVC_TASK_STAGE_INIT, so we need to set cus to 0 here memset(fc->tab.cus, 0, sizeof(*fc->tab.cus) * ctu_count); memset(fc->tab.slice_idx, -1, sizeof(*fc->tab.slice_idx) * ctu_count); if (fc->tab.sz.ctu_count != ctu_count) { av_refstruct_pool_uninit(&fc->rpl_tab_pool); fc->rpl_tab_pool = av_refstruct_pool_alloc(ctu_count * sizeof(RefPicListTab), 0); if (!fc->rpl_tab_pool) return AVERROR(ENOMEM); } if (fc->tab.sz.pic_size_in_min_pu != pic_size_in_min_pu) { av_refstruct_pool_uninit(&fc->tab_dmvr_mvf_pool); fc->tab_dmvr_mvf_pool = av_refstruct_pool_alloc( pic_size_in_min_pu * sizeof(MvField), AV_REFSTRUCT_POOL_FLAG_ZERO_EVERY_TIME); if (!fc->tab_dmvr_mvf_pool) return AVERROR(ENOMEM); } fc->tab.sz.ctu_count = pps->ctb_count; fc->tab.sz.ctu_size = 1 << sps->ctb_log2_size_y << sps->ctb_log2_size_y; fc->tab.sz.pic_size_in_min_cb = pps->min_cb_width * pps->min_cb_height; fc->tab.sz.pic_size_in_min_pu = pic_size_in_min_pu; fc->tab.sz.pic_size_in_min_tu = pps->min_tu_width * pps->min_tu_height; fc->tab.sz.width = pps->width; fc->tab.sz.height = pps->height; fc->tab.sz.ctu_width = pps->ctb_width; fc->tab.sz.ctu_height = pps->ctb_height; fc->tab.sz.chroma_format_idc = sps->r->sps_chroma_format_idc; fc->tab.sz.pixel_shift = sps->pixel_shift; return 0;} int ff_vvc_per_frame_init(VVCFrameContext *fc){ return frame_context_for_each_tl(fc, tl_zero);} static int min_positive(const int idx, const int diff, const int min_diff){ return diff > 0 && (idx < 0 || diff < min_diff);} static int max_negtive(const int idx, const int diff, const int max_diff){ return diff < 0 && (idx < 0 || diff > max_diff);} typedef int (*smvd_find_fxn)(const int idx, const int diff, const int old_diff); static int8_t smvd_find(const VVCFrameContext *fc, const SliceContext *sc, int lx, smvd_find_fxn find){ const H266RawSliceHeader *rsh = sc->sh.r; const RefPicList *rpl = sc->rpl + lx; const int poc = fc->ref->poc; int8_t idx = -1; int old_diff = -1; for (int i = 0; i < rsh->num_ref_idx_active[lx]; i++) { if (!rpl->refs[i].is_lt) { int diff = poc - rpl->refs[i].poc; if (find(idx, diff, old_diff)) { idx = i; old_diff = diff; } } } return idx;} static void smvd_ref_idx(const VVCFrameContext *fc, SliceContext *sc){ VVCSH *sh = &sc->sh; if (IS_B(sh->r)) { sh->ref_idx_sym[0] = smvd_find(fc, sc, 0, min_positive); sh->ref_idx_sym[1] = smvd_find(fc, sc, 1, max_negtive); if (sh->ref_idx_sym[0] == -1 || sh->ref_idx_sym[1] == -1) { sh->ref_idx_sym[0] = smvd_find(fc, sc, 0, max_negtive); sh->ref_idx_sym[1] = smvd_find(fc, sc, 1, min_positive); } }} static void eps_free(SliceContext *slice){ av_freep(&slice->eps); slice->nb_eps = 0;} static void slices_free(VVCFrameContext *fc){ if (fc->slices) { for (int i = 0; i < fc->nb_slices_allocated; i++) { SliceContext *slice = fc->slices[i]; if (slice) { av_refstruct_unref(&slice->ref); av_refstruct_unref(&slice->sh.r); eps_free(slice); av_free(slice); } } av_freep(&fc->slices); } fc->nb_slices_allocated = 0; fc->nb_slices = 0;} static int slices_realloc(VVCFrameContext *fc){ void *p; const int size = (fc->nb_slices_allocated + 1) * 3 / 2; if (fc->nb_slices < fc->nb_slices_allocated) return 0; p = av_realloc_array(fc->slices, size, sizeof(*fc->slices)); if (!p) return AVERROR(ENOMEM); fc->slices = p; for (int i = fc->nb_slices_allocated; i < size; i++) { fc->slices[i] = av_mallocz(sizeof(*fc->slices[0])); if (!fc->slices[i]) { fc->nb_slices_allocated = i; return AVERROR(ENOMEM);Lines 1–500 of 1161. Load more
/* * VVC video decoder * * Copyright (C) 2021 Nuo Mi * Copyright (C) 2022 Xu Mu * * 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 "libavcodec/codec_internal.h" #include "libavcodec/decode.h" #include "libavcodec/hwaccel_internal.h" #include "libavcodec/hwconfig.h" #include "libavcodec/profiles.h" #include "libavutil/refstruct.h" #include "libavutil/cpu.h" #include "libavutil/mem.h" #include "libavutil/thread.h" #include "dec.h" #include "ctu.h" #include "data.h" #include "refs.h" #include "thread.h" #include "config_components.h" #define TAB_MAX 32 typedef struct Tab { void **tab; size_t size; } Tab; typedef struct TabList { Tab tabs[TAB_MAX]; int nb_tabs; int zero; int realloc; } TabList; #define TL_ADD(t, s) do { \ av_assert0(l->nb_tabs < TAB_MAX); \ l->tabs[l->nb_tabs].tab = (void**)&fc->tab.t; \ l->tabs[l->nb_tabs].size = sizeof(*fc->tab.t) * (s); \ l->nb_tabs++; \ } while (0) static void tl_init(TabList *l, const int zero, const int realloc) { l->nb_tabs = 0; l->zero = zero; l->realloc = realloc; } static int tl_free(TabList *l) { for (int i = 0; i < l->nb_tabs; i++) av_freep(l->tabs[i].tab); return 0; } static int tl_create(TabList *l) { if (l->realloc) { tl_free(l); for (int i = 0; i < l->nb_tabs; i++) { Tab *t = l->tabs + i; *t->tab = l->zero ? av_mallocz(t->size) : av_malloc(t->size); if (!*t->tab) return AVERROR(ENOMEM); } } return 0; } static int tl_zero(TabList *l) { if (l->zero) { for (int i = 0; i < l->nb_tabs; i++) { Tab *t = l->tabs + i; memset(*t->tab, 0, t->size); } } return 0; } static void ctu_nz_tl_init(TabList *l, VVCFrameContext *fc) { const VVCSPS *sps = fc->ps.sps; const VVCPPS *pps = fc->ps.pps; const int ctu_size = sps ? (1 << sps->ctb_log2_size_y << sps->ctb_log2_size_y) : 0; const int ctu_count = pps ? pps->ctb_count : 0; const int changed = fc->tab.sz.ctu_count != ctu_count || fc->tab.sz.ctu_size != ctu_size; tl_init(l, 0, changed); TL_ADD(cus, ctu_count); TL_ADD(ctus, ctu_count); TL_ADD(deblock, ctu_count); TL_ADD(sao, ctu_count); TL_ADD(alf, ctu_count); TL_ADD(slice_idx, ctu_count); TL_ADD(coeffs, ctu_count * ctu_size * VVC_MAX_SAMPLE_ARRAYS); } static void min_cb_tl_init(TabList *l, VVCFrameContext *fc) { const VVCPPS *pps = fc->ps.pps; const int pic_size_in_min_cb = pps ? pps->min_cb_width * pps->min_cb_height : 0; const int changed = fc->tab.sz.pic_size_in_min_cb != pic_size_in_min_cb; tl_init(l, 1, changed); TL_ADD(imf, pic_size_in_min_cb); for (int i = LUMA; i <= CHROMA; i++) TL_ADD(cb_width[i], pic_size_in_min_cb); //is_a0_available requires this } static void min_cb_nz_tl_init(TabList *l, VVCFrameContext *fc) { const VVCPPS *pps = fc->ps.pps; const int pic_size_in_min_cb = pps ? pps->min_cb_width * pps->min_cb_height : 0; const int changed = fc->tab.sz.pic_size_in_min_cb != pic_size_in_min_cb; tl_init(l, 0, changed); TL_ADD(skip, pic_size_in_min_cb); TL_ADD(ipm, pic_size_in_min_cb); for (int i = LUMA; i <= CHROMA; i++) { TL_ADD(cqt_depth[i], pic_size_in_min_cb); TL_ADD(cb_pos_x[i], pic_size_in_min_cb); TL_ADD(cb_pos_y[i], pic_size_in_min_cb); TL_ADD(cb_height[i], pic_size_in_min_cb); TL_ADD(cp_mv[i], pic_size_in_min_cb * MAX_CONTROL_POINTS); TL_ADD(cpm[i], pic_size_in_min_cb); TL_ADD(pcmf[i], pic_size_in_min_cb); } // For luma, qp can only change at the CU level, so the qp tab size is related to the CU. TL_ADD(qp[LUMA], pic_size_in_min_cb); } static void min_pu_tl_init(TabList *l, VVCFrameContext *fc) { const VVCPPS *pps = fc->ps.pps; const int pic_size_in_min_pu = pps ? pps->min_pu_width * pps->min_pu_height : 0; const int changed = fc->tab.sz.pic_size_in_min_pu != pic_size_in_min_pu; tl_init(l, 1, changed); TL_ADD(iaf, pic_size_in_min_pu); } static void min_pu_nz_tl_init(TabList *l, VVCFrameContext *fc) { const VVCPPS *pps = fc->ps.pps; const int pic_size_in_min_pu = pps ? pps->min_pu_width * pps->min_pu_height : 0; const int changed = fc->tab.sz.pic_size_in_min_pu != pic_size_in_min_pu; tl_init(l, 0, changed); TL_ADD(msf, pic_size_in_min_pu); TL_ADD(mmi, pic_size_in_min_pu); TL_ADD(mvf, pic_size_in_min_pu); } static void min_tu_tl_init(TabList *l, VVCFrameContext *fc) { const VVCPPS *pps = fc->ps.pps; const int pic_size_in_min_tu = pps ? pps->min_tu_width * pps->min_tu_height : 0; const int changed = fc->tab.sz.pic_size_in_min_tu != pic_size_in_min_tu; tl_init(l, 1, changed); TL_ADD(tu_joint_cbcr_residual_flag, pic_size_in_min_tu); for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS; i++) { TL_ADD(tu_coded_flag[i], pic_size_in_min_tu); for (int vertical = 0; vertical < 2; vertical++) TL_ADD(bs[vertical][i], pic_size_in_min_tu); } } static void min_tu_nz_tl_init(TabList *l, VVCFrameContext *fc) { const VVCPPS *pps = fc->ps.pps; const int pic_size_in_min_tu = pps ? pps->min_tu_width * pps->min_tu_height : 0; const int changed = fc->tab.sz.pic_size_in_min_tu != pic_size_in_min_tu; tl_init(l, 0, changed); for (int i = LUMA; i <= CHROMA; i++) { TL_ADD(tb_width[i], pic_size_in_min_tu); TL_ADD(tb_height[i], pic_size_in_min_tu); } for (int vertical = 0; vertical < 2; vertical++) { TL_ADD(max_len_p[vertical], pic_size_in_min_tu); TL_ADD(max_len_q[vertical], pic_size_in_min_tu); } // For chroma, considering the joint CbCr, the QP tab size is related to the TU. for (int i = CB; i < VVC_MAX_SAMPLE_ARRAYS; i++) TL_ADD(qp[i], pic_size_in_min_tu); } static void pixel_buffer_nz_tl_init(TabList *l, VVCFrameContext *fc) { const VVCSPS *sps = fc->ps.sps; const VVCPPS *pps = fc->ps.pps; const int width = pps ? pps->width : 0; const int height = pps ? pps->height : 0; const int ctu_width = pps ? pps->ctb_width : 0; const int ctu_height = pps ? pps->ctb_height : 0; const int chroma_idc = sps ? sps->r->sps_chroma_format_idc : 0; const int ps = sps ? sps->pixel_shift : 0; const int c_end = chroma_idc ? VVC_MAX_SAMPLE_ARRAYS : 1; const int changed = fc->tab.sz.chroma_format_idc != chroma_idc || fc->tab.sz.width != width || fc->tab.sz.height != height || fc->tab.sz.ctu_width != ctu_width || fc->tab.sz.ctu_height != ctu_height || fc->tab.sz.pixel_shift != ps; tl_init(l, 0, changed); for (int c_idx = 0; c_idx < c_end; c_idx++) { const int w = width >> (sps ? sps->hshift[c_idx] : 0); const int h = height >> (sps ? sps->vshift[c_idx] : 0); TL_ADD(sao_pixel_buffer_h[c_idx], (w * 2 * ctu_height) << ps); TL_ADD(sao_pixel_buffer_v[c_idx], (h * 2 * ctu_width) << ps); } for (int c_idx = 0; c_idx < c_end; c_idx++) { const int w = width >> (sps ? sps->hshift[c_idx] : 0); const int h = height >> (sps ? sps->vshift[c_idx] : 0); const int border_pixels = c_idx ? ALF_BORDER_CHROMA : ALF_BORDER_LUMA; for (int i = 0; i < 2; i++) { TL_ADD(alf_pixel_buffer_h[c_idx][i], (w * border_pixels * ctu_height) << ps); TL_ADD(alf_pixel_buffer_v[c_idx][i], h * ALF_PADDING_SIZE * ctu_width); } } } static void msm_tl_init(TabList *l, VVCFrameContext *fc) { const VVCPPS *pps = fc->ps.pps; const int w32 = pps ? AV_CEIL_RSHIFT(pps->width, 5) : 0; const int h32 = pps ? AV_CEIL_RSHIFT(pps->height, 5) : 0; const int changed = AV_CEIL_RSHIFT(fc->tab.sz.width, 5) != w32 || AV_CEIL_RSHIFT(fc->tab.sz.height, 5) != h32; tl_init(l, 1, changed); for (int i = LUMA; i <= CHROMA; i++) TL_ADD(msm[i], w32 * h32); } static void ispmf_tl_init(TabList *l, VVCFrameContext *fc) { const VVCPPS *pps = fc->ps.pps; const int w64 = pps ? AV_CEIL_RSHIFT(pps->width, 6) : 0; const int h64 = pps ? AV_CEIL_RSHIFT(pps->height, 6) : 0; const int changed = AV_CEIL_RSHIFT(fc->tab.sz.width, 6) != w64 || AV_CEIL_RSHIFT(fc->tab.sz.height, 6) != h64; tl_init(l, 1, changed); TL_ADD(ispmf, w64 * h64); } static void ibc_tl_init(TabList *l, VVCFrameContext *fc) { const VVCSPS *sps = fc->ps.sps; const VVCPPS *pps = fc->ps.pps; const int ctu_height = pps ? pps->ctb_height : 0; const int ctu_size = sps ? sps->ctb_size_y : 0; const int ps = sps ? sps->pixel_shift : 0; const int chroma_idc = sps ? sps->r->sps_chroma_format_idc : 0; const int has_ibc = sps ? sps->r->sps_ibc_enabled_flag : 0; const int changed = fc->tab.sz.chroma_format_idc != chroma_idc || fc->tab.sz.ctu_height != ctu_height || fc->tab.sz.ctu_size != ctu_size || fc->tab.sz.pixel_shift != ps; fc->tab.sz.ibc_buffer_width = ctu_size ? 2 * MAX_CTU_SIZE * MAX_CTU_SIZE / ctu_size : 0; tl_init(l, has_ibc, changed); for (int i = LUMA; i < VVC_MAX_SAMPLE_ARRAYS; i++) { const int hs = sps ? sps->hshift[i] : 0; const int vs = sps ? sps->vshift[i] : 0; TL_ADD(ibc_vir_buf[i], fc->tab.sz.ibc_buffer_width * ctu_size * ctu_height << ps >> hs >> vs); } } typedef void (*tl_init_fn)(TabList *l, VVCFrameContext *fc); static int frame_context_for_each_tl(VVCFrameContext *fc, int (*unary_fn)(TabList *l)) { const tl_init_fn init[] = { ctu_nz_tl_init, min_cb_tl_init, min_cb_nz_tl_init, min_pu_tl_init, min_pu_nz_tl_init, min_tu_tl_init, min_tu_nz_tl_init, pixel_buffer_nz_tl_init, msm_tl_init, ispmf_tl_init, ibc_tl_init, }; for (int i = 0; i < FF_ARRAY_ELEMS(init); i++) { TabList l; int ret; init[i](&l, fc); ret = unary_fn(&l); if (ret < 0) return ret; } return 0; } static void free_cus(VVCFrameContext *fc) { if (fc->tab.cus) { for (int i = 0; i < fc->tab.sz.ctu_count; i++) ff_vvc_ctu_free_cus(fc->tab.cus + i); } } static void pic_arrays_free(VVCFrameContext *fc) { free_cus(fc); frame_context_for_each_tl(fc, tl_free); av_refstruct_pool_uninit(&fc->rpl_tab_pool); av_refstruct_pool_uninit(&fc->tab_dmvr_mvf_pool); memset(&fc->tab.sz, 0, sizeof(fc->tab.sz)); } static int pic_arrays_init(VVCContext *s, VVCFrameContext *fc) { const VVCSPS *sps = fc->ps.sps; const VVCPPS *pps = fc->ps.pps; const int ctu_count = pps->ctb_count; const int pic_size_in_min_pu = pps->min_pu_width * pps->min_pu_height; int ret; free_cus(fc); ret = frame_context_for_each_tl(fc, tl_create); if (ret < 0) return ret; // for error handling case, we may call free_cus before VVC_TASK_STAGE_INIT, so we need to set cus to 0 here memset(fc->tab.cus, 0, sizeof(*fc->tab.cus) * ctu_count); memset(fc->tab.slice_idx, -1, sizeof(*fc->tab.slice_idx) * ctu_count); if (fc->tab.sz.ctu_count != ctu_count) { av_refstruct_pool_uninit(&fc->rpl_tab_pool); fc->rpl_tab_pool = av_refstruct_pool_alloc(ctu_count * sizeof(RefPicListTab), 0); if (!fc->rpl_tab_pool) return AVERROR(ENOMEM); } if (fc->tab.sz.pic_size_in_min_pu != pic_size_in_min_pu) { av_refstruct_pool_uninit(&fc->tab_dmvr_mvf_pool); fc->tab_dmvr_mvf_pool = av_refstruct_pool_alloc( pic_size_in_min_pu * sizeof(MvField), AV_REFSTRUCT_POOL_FLAG_ZERO_EVERY_TIME); if (!fc->tab_dmvr_mvf_pool) return AVERROR(ENOMEM); } fc->tab.sz.ctu_count = pps->ctb_count; fc->tab.sz.ctu_size = 1 << sps->ctb_log2_size_y << sps->ctb_log2_size_y; fc->tab.sz.pic_size_in_min_cb = pps->min_cb_width * pps->min_cb_height; fc->tab.sz.pic_size_in_min_pu = pic_size_in_min_pu; fc->tab.sz.pic_size_in_min_tu = pps->min_tu_width * pps->min_tu_height; fc->tab.sz.width = pps->width; fc->tab.sz.height = pps->height; fc->tab.sz.ctu_width = pps->ctb_width; fc->tab.sz.ctu_height = pps->ctb_height; fc->tab.sz.chroma_format_idc = sps->r->sps_chroma_format_idc; fc->tab.sz.pixel_shift = sps->pixel_shift; return 0; } int ff_vvc_per_frame_init(VVCFrameContext *fc) { return frame_context_for_each_tl(fc, tl_zero); } static int min_positive(const int idx, const int diff, const int min_diff) { return diff > 0 && (idx < 0 || diff < min_diff); } static int max_negtive(const int idx, const int diff, const int max_diff) { return diff < 0 && (idx < 0 || diff > max_diff); } typedef int (*smvd_find_fxn)(const int idx, const int diff, const int old_diff); static int8_t smvd_find(const VVCFrameContext *fc, const SliceContext *sc, int lx, smvd_find_fxn find) { const H266RawSliceHeader *rsh = sc->sh.r; const RefPicList *rpl = sc->rpl + lx; const int poc = fc->ref->poc; int8_t idx = -1; int old_diff = -1; for (int i = 0; i < rsh->num_ref_idx_active[lx]; i++) { if (!rpl->refs[i].is_lt) { int diff = poc - rpl->refs[i].poc; if (find(idx, diff, old_diff)) { idx = i; old_diff = diff; } } } return idx; } static void smvd_ref_idx(const VVCFrameContext *fc, SliceContext *sc) { VVCSH *sh = &sc->sh; if (IS_B(sh->r)) { sh->ref_idx_sym[0] = smvd_find(fc, sc, 0, min_positive); sh->ref_idx_sym[1] = smvd_find(fc, sc, 1, max_negtive); if (sh->ref_idx_sym[0] == -1 || sh->ref_idx_sym[1] == -1) { sh->ref_idx_sym[0] = smvd_find(fc, sc, 0, max_negtive); sh->ref_idx_sym[1] = smvd_find(fc, sc, 1, min_positive); } } } static void eps_free(SliceContext *slice) { av_freep(&slice->eps); slice->nb_eps = 0; } static void slices_free(VVCFrameContext *fc) { if (fc->slices) { for (int i = 0; i < fc->nb_slices_allocated; i++) { SliceContext *slice = fc->slices[i]; if (slice) { av_refstruct_unref(&slice->ref); av_refstruct_unref(&slice->sh.r); eps_free(slice); av_free(slice); } } av_freep(&fc->slices); } fc->nb_slices_allocated = 0; fc->nb_slices = 0; } static int slices_realloc(VVCFrameContext *fc) { void *p; const int size = (fc->nb_slices_allocated + 1) * 3 / 2; if (fc->nb_slices < fc->nb_slices_allocated) return 0; p = av_realloc_array(fc->slices, size, sizeof(*fc->slices)); if (!p) return AVERROR(ENOMEM); fc->slices = p; for (int i = fc->nb_slices_allocated; i < size; i++) { fc->slices[i] = av_mallocz(sizeof(*fc->slices[0])); if (!fc->slices[i]) { fc->nb_slices_allocated = i; return AVERROR(ENOMEM);