From patchwork Sun Sep 9 22:08:11 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Thompson X-Patchwork-Id: 10285 Delivered-To: ffmpegpatchwork@gmail.com Received: by 2002:a02:12c4:0:0:0:0:0 with SMTP id 65-v6csp1824015jap; Sun, 9 Sep 2018 15:08:45 -0700 (PDT) X-Google-Smtp-Source: ANB0VdbR6dgsQNRBHXJqueyaM+UvYt2QK7vwSzf+z9gsR0vpBghKGdmsSpEeAoJIvoohGskmZR4q X-Received: by 2002:a1c:d98a:: with SMTP id q132-v6mr11724266wmg.78.1536530925799; Sun, 09 Sep 2018 15:08:45 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1536530925; cv=none; d=google.com; s=arc-20160816; b=kxXVvFcnU45co8XH/GBepElTwtdWJn0zvSAfxEKy/aaGcenvAgiebf8xizNyRALyuW VTzDtRjv8joVrgsvn7QX3mCQiTF0dYfyFR3GqMmGt/MbrmHvWJy/C912pyPy9kYHB1yi +ywqoxGhjQjoRCSn+HsFOBaJd21EOICAqSwNLyUIVShzLHSqlfzr259hcVO9jLWR3Lua ALDsZBENuj2BqvuAQvTXk4A0yR7rVpCFzD9UdlhdCdFnXTLueLwWMScKmlScdD1ykDtX 1VeD5Ceoi2kIkIW8T27QWz+pqV0qThdSCkqEQ0qUO1LwGZqJ+wwPx475vNE3cCf4PsbV yNnA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:references:in-reply-to:message-id:date :to:from:dkim-signature:delivered-to; bh=RJgJImGDe/PLBSai4PXCSwt925m10h9IBa3DSZyCouI=; b=m7wsD/PG4OxbpG5K7Bncd4LH6aQJl3T+g86I7HCG4R5fEpibE4+ReGVMwhdLDcDK5J 4++zu7UelEHtPA3kdAN+HqMCNijhLMeY6PO/+6kofMj5G+3H/xgqiXuyf+q8eP6lFNDS 4c9fSLQy1CigWqO7Oi92uAZCABUPIPs3L3us3TKtC2QfhSJAljzmvYinWJK0hLLqjMdn bR/+oojd5bFpRD8dnxEDVWosNmnH6YVVd8fnlyWbeyx9jto9zzimXfnIEX15dSltxDpp OjyREqmMftrSeoTB98F6iNszPJE4NgGYxvVDC9FxeZXWtTOQP2Nn57z04VL/MRNWi+2K KXnw== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@jkqxz-net.20150623.gappssmtp.com header.s=20150623 header.b=webGisl8; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id i1-v6si13521921wrq.11.2018.09.09.15.08.45; Sun, 09 Sep 2018 15:08:45 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@jkqxz-net.20150623.gappssmtp.com header.s=20150623 header.b=webGisl8; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 5A38968A331; Mon, 10 Sep 2018 01:08:18 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wm0-f44.google.com (mail-wm0-f44.google.com [74.125.82.44]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id AA09268A26C for ; Mon, 10 Sep 2018 01:08:11 +0300 (EEST) Received: by mail-wm0-f44.google.com with SMTP id b19-v6so19588064wme.3 for ; Sun, 09 Sep 2018 15:08:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=jkqxz-net.20150623.gappssmtp.com; s=20150623; h=from:to:subject:date:message-id:in-reply-to:references; bh=6f4WncIwVTxzatDKrhDo/Xkoz5okWz7fL4P58vzDTpg=; b=webGisl8sKRq1uuv1S8b7nzi+qUWRjRInYLIjsI0nqf6ZOKLhUeEIbRuDcsKFp4dxS 3CftmGuRpkqvz1R7z+emToqwDD6nqqPNOEwRDK0PnvVynIi/2WljNkcoOL4w+v+7kBnh +jq1ErCM+YH+yerPiz/3fZzB1UIWJ4n0HG6qmHfPIsNEfmCnCj31F8YFTN9WN582ggjp QAKbh84e9JiwP+tX3d3RdhL6zIxoXM+4alrO31i+tnUcKQgrhNfiD0dYwELE8mg3lQh2 iRz+M+eeYGlEZT8mWWHCnJ2gDj8DssZ3V720upsI/gGTAvyfBv4oLnrhcL27qMKKF+6w FJbQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=6f4WncIwVTxzatDKrhDo/Xkoz5okWz7fL4P58vzDTpg=; b=r7DjqxWJ6+BFXqJygC7rh4rx44sK/RqvGFLr79PrqXr85lJBuO9vkG6387mKS+WV2K PmLCqcAHnO1buaJSFRVFRNNwBLpfLPvuP+1xwWT4vU+k8ex/KS6MrR5ABSPNlPtRJtWO sZzo2M/IRwAZ6BYH5QRs5WiYEhIP+CII17rf1LHQtXiFaiViDIiU61ncklDsL3m/+5Cl /WlRARRVPUg4lRCE1tfw4cpRwdHkfiZNYL9NlgMRig8KYxJUJ5Zo26vtaBCfaDNDWtKF TKcAO8jb84kC2sgafLYOOYbFTjq98/zQXKCQVnb0B/AAY0i4jGEM1/id2HiPaSljmB4D anKA== X-Gm-Message-State: APzg51DIE7j4RuOvlLxZ/QkpldKHijX4XQSsQLlqtvPYAbPVw1qxdtXC XeePJWsbUG+LhQVq1i2J/X9P3pMdslY= X-Received: by 2002:a1c:d785:: with SMTP id o127-v6mr10950774wmg.67.1536530898347; Sun, 09 Sep 2018 15:08:18 -0700 (PDT) Received: from rywe.jkqxz.net (cpc91242-cmbg18-2-0-cust650.5-4.cable.virginm.net. [82.8.130.139]) by smtp.gmail.com with ESMTPSA id 75-v6sm29406215wml.21.2018.09.09.15.08.17 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 09 Sep 2018 15:08:17 -0700 (PDT) From: Mark Thompson To: ffmpeg-devel@ffmpeg.org Date: Sun, 9 Sep 2018 23:08:11 +0100 Message-Id: <20180909220812.16171-2-sw@jkqxz.net> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20180909220812.16171-1-sw@jkqxz.net> References: <20180909220812.16171-1-sw@jkqxz.net> Subject: [FFmpeg-devel] [PATCH 2/3] lavc: Add coded bitstream read/write support for AV1 X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 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 MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" --- Against versions which people might have seen before: * Removed all of the vestigial annex B support (not useful, we are going to ensure that AVPackets in FFmpeg never hold annex B data). * gm_params subexp parsing made sensible - it doesn't compute the actual gm_params values any more, but the code actually makes sense rather than being opaque pasta from the standard. * Metadata support added. * Tile-group / tile-info / frame-with-tiles OBUs are combined in a hopefully better way. * Miscellaneous small fixes. Thanks to James Almer for testing and various fixes incorporated into this. configure | 2 + libavcodec/Makefile | 1 + libavcodec/av1.h | 88 ++ libavcodec/cbs.c | 6 + libavcodec/cbs_av1.c | 1293 ++++++++++++++++++++ libavcodec/cbs_av1.h | 429 +++++++ libavcodec/cbs_av1_syntax_template.c | 1676 ++++++++++++++++++++++++++ libavcodec/cbs_internal.h | 1 + 8 files changed, 3496 insertions(+) create mode 100644 libavcodec/cbs_av1.c create mode 100644 libavcodec/cbs_av1.h create mode 100644 libavcodec/cbs_av1_syntax_template.c diff --git a/configure b/configure index 595be65f2c..62a96c7b32 100755 --- a/configure +++ b/configure @@ -2267,6 +2267,7 @@ CONFIG_EXTRA=" bswapdsp cabac cbs + cbs_av1 cbs_h264 cbs_h265 cbs_mpeg2 @@ -2530,6 +2531,7 @@ w32threads_deps="atomics_native" threads_if_any="$THREADS_LIST" # subsystems +cbs_av1_select="cbs" cbs_h264_select="cbs golomb" cbs_h265_select="cbs golomb" cbs_mpeg2_select="cbs" diff --git a/libavcodec/Makefile b/libavcodec/Makefile index ceec1df972..9c2aab3fe3 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -63,6 +63,7 @@ OBJS-$(CONFIG_BLOCKDSP) += blockdsp.o OBJS-$(CONFIG_BSWAPDSP) += bswapdsp.o OBJS-$(CONFIG_CABAC) += cabac.o OBJS-$(CONFIG_CBS) += cbs.o +OBJS-$(CONFIG_CBS_AV1) += cbs_av1.o OBJS-$(CONFIG_CBS_H264) += cbs_h2645.o h2645_parse.o OBJS-$(CONFIG_CBS_H265) += cbs_h2645.o h2645_parse.o OBJS-$(CONFIG_CBS_MPEG2) += cbs_mpeg2.o diff --git a/libavcodec/av1.h b/libavcodec/av1.h index c989b69974..f2ec39c86b 100644 --- a/libavcodec/av1.h +++ b/libavcodec/av1.h @@ -39,4 +39,92 @@ typedef enum { AV1_OBU_PADDING = 15, } AV1_OBU_Type; +// Metadata types (section 6.7.1). +enum { + AV1_METADATA_TYPE_HDR_CLL = 1, + AV1_METADATA_TYPE_HDR_MDCV = 2, + AV1_METADATA_TYPE_SCALABILITY = 3, + AV1_METADATA_TYPE_ITUT_T35 = 4, + AV1_METADATA_TYPE_TIMECODE = 5, +}; + +// Frame types (section 6.8.2). +enum { + AV1_FRAME_KEY = 0, + AV1_FRAME_INTER = 1, + AV1_FRAME_INTRA_ONLY = 2, + AV1_FRAME_SWITCH = 3, +}; + +// Reference frames (section 6.10.24). +enum { + AV1_REF_FRAME_INTRA = 0, + AV1_REF_FRAME_LAST = 1, + AV1_REF_FRAME_LAST2 = 2, + AV1_REF_FRAME_LAST3 = 3, + AV1_REF_FRAME_GOLDEN = 4, + AV1_REF_FRAME_BWDREF = 5, + AV1_REF_FRAME_ALTREF2 = 6, + AV1_REF_FRAME_ALTREF = 7, +}; + +// Constants (section 3). +enum { + AV1_MAX_OPERATING_POINTS = 32, + + AV1_MAX_SB_SIZE = 128, + AV1_MI_SIZE = 4, + + AV1_MAX_TILE_WIDTH = 4096, + AV1_MAX_TILE_AREA = 4096 * 2304, + AV1_MAX_TILE_ROWS = 64, + AV1_MAX_TILE_COLS = 64, + + AV1_NUM_REF_FRAMES = 8, + AV1_REFS_PER_FRAME = 7, + AV1_TOTAL_REFS_PER_FRAME = 8, + AV1_PRIMARY_REF_NONE = 7, + + AV1_MAX_SEGMENTS = 8, + AV1_SEG_LVL_MAX = 8, + + AV1_SEG_LVL_ALT_Q = 0, + AV1_SEG_LVL_ALT_LF_Y_V = 1, + AV1_SEG_LVL_REF_FRAME = 5, + AV1_SEG_LVL_SKIP = 6, + AV1_SEG_LVL_GLOBAL_MV = 7, + + AV1_SELECT_SCREEN_CONTENT_TOOLS = 2, + AV1_SELECT_INTEGER_MV = 2, + + AV1_SUPERRES_NUM = 8, + AV1_SUPERRES_DENOM_MIN = 9, + + AV1_INTERPOLATION_FILTER_SWITCHABLE = 4, + + AV1_GM_ABS_ALPHA_BITS = 12, + AV1_GM_ALPHA_PREC_BITS = 15, + AV1_GM_ABS_TRANS_ONLY_BITS = 9, + AV1_GM_TRANS_ONLY_PREC_BITS = 3, + AV1_GM_ABS_TRANS_BITS = 12, + AV1_GM_TRANS_PREC_BITS = 6, + AV1_WARPEDMODEL_PREC_BITS = 16, + + AV1_WARP_MODEL_IDENTITY = 0, + AV1_WARP_MODEL_TRANSLATION = 1, + AV1_WARP_MODEL_ROTZOOM = 2, + AV1_WARP_MODEL_AFFINE = 3, +}; + + +// The main colour configuration information uses the same ISO/IEC 23001-8 +// (H.273) enums as FFmpeg does, so separate definitions are not required. + +// Chroma sample position. +enum { + AV1_CSP_UNKNOWN = 0, + AV1_CSP_VERTICAL = 1, // -> AVCHROMA_LOC_LEFT. + AV1_CSP_COLOCATED = 2, // -> AVCHROMA_LOC_TOPLEFT. +}; + #endif /* AVCODEC_AV1_H */ diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c index be6c043b58..f0b1b7de7c 100644 --- a/libavcodec/cbs.c +++ b/libavcodec/cbs.c @@ -29,6 +29,9 @@ static const CodedBitstreamType *cbs_type_table[] = { +#if CONFIG_CBS_AV1 + &ff_cbs_type_av1, +#endif #if CONFIG_CBS_H264 &ff_cbs_type_h264, #endif @@ -44,6 +47,9 @@ static const CodedBitstreamType *cbs_type_table[] = { }; const enum AVCodecID ff_cbs_all_codec_ids[] = { +#if CONFIG_CBS_AV1 + AV_CODEC_ID_AV1, +#endif #if CONFIG_CBS_H264 AV_CODEC_ID_H264, #endif diff --git a/libavcodec/cbs_av1.c b/libavcodec/cbs_av1.c new file mode 100644 index 0000000000..ba963eafc7 --- /dev/null +++ b/libavcodec/cbs_av1.c @@ -0,0 +1,1293 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avassert.h" +#include "libavutil/pixfmt.h" + +#include "cbs.h" +#include "cbs_internal.h" +#include "cbs_av1.h" +#include "internal.h" + + +static int cbs_av1_read_uvlc(CodedBitstreamContext *ctx, GetBitContext *gbc, + const char *name, uint32_t *write_to, + uint32_t range_min, uint32_t range_max) +{ + uint32_t value; + int position, zeroes, i, j; + char bits[65]; + + if (ctx->trace_enable) + position = get_bits_count(gbc); + + zeroes = i = 0; + while (1) { + if (get_bits_left(gbc) < zeroes + 1) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid uvlc code at " + "%s: bitstream ended.\n", name); + return AVERROR_INVALIDDATA; + } + + if (get_bits1(gbc)) { + bits[i++] = '1'; + break; + } else { + bits[i++] = '0'; + ++zeroes; + } + } + + if (zeroes >= 32) { + value = MAX_UINT_BITS(32); + } else { + value = get_bits_long(gbc, zeroes); + + for (j = 0; j < zeroes; j++) + bits[i++] = (value >> (zeroes - j - 1) & 1) ? '1' : '0'; + + value += (1 << zeroes) - 1; + } + + if (ctx->trace_enable) { + bits[i] = 0; + ff_cbs_trace_syntax_element(ctx, position, name, NULL, + bits, value); + } + + if (value < range_min || value > range_max) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " + "%"PRIu32", but must be in [%"PRIu32",%"PRIu32"].\n", + name, value, range_min, range_max); + return AVERROR_INVALIDDATA; + } + + *write_to = value; + return 0; +} + +static int cbs_av1_write_uvlc(CodedBitstreamContext *ctx, PutBitContext *pbc, + const char *name, uint32_t value, + uint32_t range_min, uint32_t range_max) +{ + uint32_t v; + int position, zeroes; + + if (value < range_min || value > range_max) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " + "%"PRIu32", but must be in [%"PRIu32",%"PRIu32"].\n", + name, value, range_min, range_max); + return AVERROR_INVALIDDATA; + } + + if (ctx->trace_enable) + position = put_bits_count(pbc); + + if (value == 0) { + zeroes = 0; + put_bits(pbc, 1, 1); + } else { + zeroes = av_log2(value + 1); + v = value - (1 << zeroes) + 1; + put_bits(pbc, zeroes + 1, 1); + put_bits(pbc, zeroes, v); + } + + if (ctx->trace_enable) { + char bits[65]; + int i, j; + i = 0; + for (j = 0; j < zeroes; j++) + bits[i++] = '0'; + bits[i++] = '1'; + for (j = 0; j < zeroes; j++) + bits[i++] = (v >> (zeroes - j - 1) & 1) ? '1' : '0'; + bits[i++] = 0; + ff_cbs_trace_syntax_element(ctx, position, name, NULL, + bits, value); + } + + return 0; +} + +static int cbs_av1_read_leb128(CodedBitstreamContext *ctx, GetBitContext *gbc, + const char *name, uint64_t *write_to) +{ + uint64_t value; + int position, err, i; + + if (ctx->trace_enable) + position = get_bits_count(gbc); + + value = 0; + for (i = 0; i < 8; i++) { + int subscript[2] = { 1, i }; + uint32_t byte; + err = ff_cbs_read_unsigned(ctx, gbc, 8, "leb128_byte[i]", subscript, + &byte, 0x00, 0xff); + if (err < 0) + return err; + + value |= (uint64_t)(byte & 0x7f) << (i * 7); + if (!(byte & 0x80)) + break; + } + + if (ctx->trace_enable) + ff_cbs_trace_syntax_element(ctx, position, name, NULL, "", value); + + *write_to = value; + return 0; +} + +static int cbs_av1_write_leb128(CodedBitstreamContext *ctx, PutBitContext *pbc, + const char *name, uint64_t value) +{ + int position, err, len, i; + uint8_t byte; + + len = (av_log2(value) + 7) / 7; + + if (ctx->trace_enable) + position = put_bits_count(pbc); + + for (i = 0; i < len; i++) { + int subscript[2] = { 1, i }; + + byte = value >> (7 * i) & 0x7f; + if (i < len - 1) + byte |= 0x80; + + err = ff_cbs_write_unsigned(ctx, pbc, 8, "leb128_byte[i]", subscript, + byte, 0x00, 0xff); + if (err < 0) + return err; + } + + if (ctx->trace_enable) + ff_cbs_trace_syntax_element(ctx, position, name, NULL, "", value); + + return 0; +} + +static int cbs_av1_read_su(CodedBitstreamContext *ctx, GetBitContext *gbc, + int width, const char *name, + const int *subscripts, int32_t *write_to) +{ + uint32_t magnitude; + int position, sign; + int32_t value; + + if (ctx->trace_enable) + position = get_bits_count(gbc); + + if (get_bits_left(gbc) < width + 1) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid signed value at " + "%s: bitstream ended.\n", name); + return AVERROR_INVALIDDATA; + } + + magnitude = get_bits(gbc, width); + sign = get_bits1(gbc); + value = sign ? -(int32_t)magnitude : magnitude; + + if (ctx->trace_enable) { + char bits[33]; + int i; + for (i = 0; i < width; i++) + bits[i] = magnitude >> (width - i - 1) & 1 ? '1' : '0'; + bits[i] = sign ? '1' : '0'; + bits[i + 1] = 0; + + ff_cbs_trace_syntax_element(ctx, position, + name, subscripts, bits, value); + } + + *write_to = value; + return 0; +} + +static int cbs_av1_write_su(CodedBitstreamContext *ctx, PutBitContext *pbc, + int width, const char *name, + const int *subscripts, int32_t value) +{ + uint32_t magnitude; + int sign; + + if (put_bits_left(pbc) < width + 1) + return AVERROR(ENOSPC); + + sign = value < 0; + magnitude = sign ? -value : value; + + if (ctx->trace_enable) { + char bits[33]; + int i; + for (i = 0; i < width; i++) + bits[i] = magnitude >> (width - i - 1) & 1 ? '1' : '0'; + bits[i] = sign ? '1' : '0'; + bits[i + 1] = 0; + + ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc), + name, subscripts, bits, value); + } + + put_bits(pbc, width, magnitude); + put_bits(pbc, 1, sign); + + return 0; +} + +static int cbs_av1_read_ns(CodedBitstreamContext *ctx, GetBitContext *gbc, + uint32_t n, const char *name, + const int *subscripts, uint32_t *write_to) +{ + uint32_t w, m, v, extra_bit, value; + int position; + + av_assert0(n > 0); + + if (ctx->trace_enable) + position = get_bits_count(gbc); + + w = av_log2(n) + 1; + m = (1 << w) - n; + + if (get_bits_left(gbc) < w) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid non-symmetric value at " + "%s: bitstream ended.\n", name); + return AVERROR_INVALIDDATA; + } + + if (w - 1 > 0) + v = get_bits(gbc, w - 1); + else + v = 0; + + if (v < m) { + value = v; + } else { + extra_bit = get_bits1(gbc); + value = (v << 1) - m + extra_bit; + } + + if (ctx->trace_enable) { + char bits[33]; + int i; + for (i = 0; i < w - 1; i++) + bits[i] = (v >> i & 1) ? '1' : '0'; + if (v >= m) + bits[i++] = extra_bit ? '1' : '0'; + bits[i] = 0; + + ff_cbs_trace_syntax_element(ctx, position, + name, subscripts, bits, value); + } + + *write_to = value; + return 0; +} + +static int cbs_av1_write_ns(CodedBitstreamContext *ctx, PutBitContext *pbc, + uint32_t n, const char *name, + const int *subscripts, uint32_t value) +{ + uint32_t w, m, v, extra_bit; + int position; + + if (value > n) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " + "%"PRIu32", but must be in [0,%"PRIu32"].\n", + name, value, n); + return AVERROR_INVALIDDATA; + } + + if (ctx->trace_enable) + position = put_bits_count(pbc); + + w = av_log2(n) + 1; + m = (1 << w) - n; + + if (put_bits_left(pbc) < w) + return AVERROR(ENOSPC); + + if (value < m) { + v = value; + put_bits(pbc, w - 1, v); + } else { + v = m + ((value - m) >> 1); + extra_bit = (value - m) & 1; + put_bits(pbc, w - 1, v); + put_bits(pbc, 1, extra_bit); + } + + if (ctx->trace_enable) { + char bits[33]; + int i; + for (i = 0; i < w - 1; i++) + bits[i] = (v >> i & 1) ? '1' : '0'; + if (value >= m) + bits[i++] = extra_bit ? '1' : '0'; + bits[i] = 0; + + ff_cbs_trace_syntax_element(ctx, position, + name, subscripts, bits, value); + } + + return 0; +} + +static int cbs_av1_read_increment(CodedBitstreamContext *ctx, GetBitContext *gbc, + uint32_t range_min, uint32_t range_max, + const char *name, uint32_t *write_to) +{ + uint32_t value; + int position, i; + char bits[33]; + + av_assert0(range_min <= range_max && range_max - range_min < sizeof(bits) - 1); + if (ctx->trace_enable) + position = get_bits_count(gbc); + + for (i = 0, value = range_min; value < range_max;) { + if (get_bits_left(gbc) < 1) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid increment value at " + "%s: bitstream ended.\n", name); + return AVERROR_INVALIDDATA; + } + if (get_bits1(gbc)) { + bits[i++] = '1'; + ++value; + } else { + bits[i++] = '0'; + break; + } + } + + if (ctx->trace_enable) { + bits[i] = 0; + ff_cbs_trace_syntax_element(ctx, position, + name, NULL, bits, value); + } + + *write_to = value; + return 0; +} + +static int cbs_av1_write_increment(CodedBitstreamContext *ctx, PutBitContext *pbc, + uint32_t range_min, uint32_t range_max, + const char *name, uint32_t value) +{ + int len; + + av_assert0(range_min <= range_max && range_max - range_min < 32); + if (value < range_min || value > range_max) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " + "%"PRIu32", but must be in [%"PRIu32",%"PRIu32"].\n", + name, value, range_min, range_max); + return AVERROR_INVALIDDATA; + } + + if (value == range_max) + len = range_max - range_min; + else + len = value - range_min + 1; + if (put_bits_left(pbc) < len) + return AVERROR(ENOSPC); + + if (ctx->trace_enable) { + char bits[33]; + int i; + for (i = 0; i < len; i++) { + if (range_min + i == value) + bits[i] = '0'; + else + bits[i] = '1'; + } + bits[i] = 0; + ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc), + name, NULL, bits, value); + } + + if (len > 0) + put_bits(pbc, len, (1 << len) - 1 - (value != range_max)); + + return 0; +} + +static int cbs_av1_read_subexp(CodedBitstreamContext *ctx, GetBitContext *gbc, + uint32_t range_max, const char *name, + const int *subscripts, uint32_t *write_to) +{ + uint32_t value; + int position, err; + uint32_t max_len, len, range_offset, range_bits; + + if (ctx->trace_enable) + position = get_bits_count(gbc); + + av_assert0(range_max > 0); + max_len = av_log2(range_max - 1) - 3; + + err = cbs_av1_read_increment(ctx, gbc, 0, max_len, + "subexp_more_bits", &len); + if (err < 0) + return err; + + if (len) { + range_bits = 2 + len; + range_offset = 1 << range_bits; + } else { + range_bits = 3; + range_offset = 0; + } + + if (len < max_len) { + err = ff_cbs_read_unsigned(ctx, gbc, range_bits, + "subexp_bits", NULL, &value, + 0, MAX_UINT_BITS(range_bits)); + if (err < 0) + return err; + + } else { + err = cbs_av1_read_ns(ctx, gbc, range_max - range_offset, + "subexp_final_bits", NULL, &value); + if (err < 0) + return err; + } + value += range_offset; + + if (ctx->trace_enable) + ff_cbs_trace_syntax_element(ctx, position, + name, subscripts, "", value); + + *write_to = value; + return err; +} + +static int cbs_av1_write_subexp(CodedBitstreamContext *ctx, PutBitContext *pbc, + uint32_t range_max, const char *name, + const int *subscripts, uint32_t value) +{ + int position, err; + uint32_t max_len, len, range_offset, range_bits; + + if (value > range_max) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " + "%"PRIu32", but must be in [0,%"PRIu32"].\n", + name, value, range_max); + return AVERROR_INVALIDDATA; + } + + if (ctx->trace_enable) + position = put_bits_count(pbc); + + av_assert0(range_max > 0); + max_len = av_log2(range_max - 1) - 3; + + if (value < 8) { + range_bits = 3; + range_offset = 0; + len = 0; + } else { + range_bits = av_log2(value); + len = range_bits - 2; + if (len > max_len) { + // The top bin is combined with the one below it. + av_assert0(len == max_len + 1); + --range_bits; + len = max_len; + } + range_offset = 1 << range_bits; + } + + err = cbs_av1_write_increment(ctx, pbc, 0, max_len, + "subexp_more_bits", len); + if (err < 0) + return err; + + if (len < max_len) { + err = ff_cbs_write_unsigned(ctx, pbc, range_bits, + "subexp_bits", NULL, + value - range_offset, + 0, MAX_UINT_BITS(range_bits)); + if (err < 0) + return err; + + } else { + err = cbs_av1_write_ns(ctx, pbc, range_max - range_offset, + "subexp_final_bits", NULL, + value - range_offset); + if (err < 0) + return err; + } + + if (ctx->trace_enable) + ff_cbs_trace_syntax_element(ctx, position, + name, subscripts, "", value); + + return err; +} + + +static int cbs_av1_tile_log2(int blksize, int target) +{ + int k; + for (k = 0; (blksize << k) < target; k++); + return k; +} + +static int cbs_av1_get_relative_dist(const AV1RawSequenceHeader *seq, + unsigned int a, unsigned int b) +{ + unsigned int diff, m; + if (!seq->enable_order_hint) + return 0; + diff = a - b; + m = 1 << seq->order_hint_bits_minus_1; + diff = (diff & (m - 1)) - (diff & m); + return diff; +} + + +#define HEADER(name) do { \ + ff_cbs_trace_header(ctx, name); \ + } while (0) + +#define CHECK(call) do { \ + err = (call); \ + if (err < 0) \ + return err; \ + } while (0) + +#define FUNC_NAME(rw, codec, name) cbs_ ## codec ## _ ## rw ## _ ## name +#define FUNC_AV1(rw, name) FUNC_NAME(rw, av1, name) +#define FUNC(name) FUNC_AV1(READWRITE, name) + +#define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]){ subs, __VA_ARGS__ }) : NULL) + +#define fb(width, name) \ + xf(width, name, current->name, 0, MAX_UINT_BITS(width), 0) +#define fc(width, name, range_min, range_max) \ + xf(width, name, current->name, range_min, range_max, 0) +#define flag(name) fb(1, name) +#define su(width, name) \ + xsu(width, name, current->name, 0) + +#define fbs(width, name, subs, ...) \ + xf(width, name, current->name, 0, MAX_UINT_BITS(width), subs, __VA_ARGS__) +#define fcs(width, name, range_min, range_max, subs, ...) \ + xf(width, name, current->name, range_min, range_max, subs, __VA_ARGS__) +#define flags(name, subs, ...) \ + xf(1, name, current->name, 0, 1, subs, __VA_ARGS__) +#define sus(width, name, subs, ...) \ + xsu(width, name, current->name, subs, __VA_ARGS__) + +#define fixed(width, name, value) do { \ + av_unused uint32_t fixed_value = value; \ + xf(width, name, fixed_value, value, value, 0); \ + } while (0) + + +#define READ +#define READWRITE read +#define RWContext GetBitContext + +#define xf(width, name, var, range_min, range_max, subs, ...) do { \ + uint32_t value = range_min; \ + CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + &value, range_min, range_max)); \ + var = value; \ + } while (0) + +#define xsu(width, name, var, subs, ...) do { \ + int32_t value = 0; \ + CHECK(cbs_av1_read_su(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), &value)); \ + var = value; \ + } while (0) + +#define uvlc(name, range_min, range_max) do { \ + uint32_t value = range_min; \ + CHECK(cbs_av1_read_uvlc(ctx, rw, #name, \ + &value, range_min, range_max)); \ + current->name = value; \ + } while (0) + +#define ns(max_value, name, subs, ...) do { \ + uint32_t value = 0; \ + CHECK(cbs_av1_read_ns(ctx, rw, max_value, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), &value)); \ + current->name = value; \ + } while (0) + +#define increment(name, min, max) do { \ + uint32_t value = 0; \ + CHECK(cbs_av1_read_increment(ctx, rw, min, max, #name, &value)); \ + current->name = value; \ + } while (0) + +#define subexp(name, max, subs, ...) do { \ + uint32_t value = 0; \ + CHECK(cbs_av1_read_subexp(ctx, rw, max, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), &value)); \ + current->name = value; \ + } while (0) + +#define delta_q(name) do { \ + uint8_t delta_coded; \ + int8_t delta_q; \ + xf(1, name.delta_coded, delta_coded, 0, 1, 0); \ + if (delta_coded) \ + xsu(1 + 6, name.delta_q, delta_q, 0); \ + else \ + delta_q = 0; \ + current->name = delta_q; \ + } while (0) + +#define leb128(name) do { \ + uint64_t value = 0; \ + CHECK(cbs_av1_read_leb128(ctx, rw, #name, &value)); \ + current->name = value; \ + } while (0) + +#define infer(name, value) do { \ + current->name = value; \ + } while (0) + +#define byte_alignment(rw) (get_bits_count(rw) % 8) + +#include "cbs_av1_syntax_template.c" + +#undef READ +#undef READWRITE +#undef RWContext +#undef xf +#undef xsu +#undef uvlc +#undef leb128 +#undef ns +#undef increment +#undef subexp +#undef delta_q +#undef leb128 +#undef infer +#undef byte_alignment + + +#define WRITE +#define READWRITE write +#define RWContext PutBitContext + +#define xf(width, name, var, range_min, range_max, subs, ...) do { \ + CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + var, range_min, range_max)); \ + } while (0) + +#define xsu(width, name, var, subs, ...) do { \ + CHECK(cbs_av1_write_su(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), var)); \ + } while (0) + +#define uvlc(name, range_min, range_max) do { \ + CHECK(cbs_av1_write_uvlc(ctx, rw, #name, current->name, \ + range_min, range_max)); \ + } while (0) + +#define ns(max_value, name, subs, ...) do { \ + CHECK(cbs_av1_write_ns(ctx, rw, max_value, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + current->name)); \ + } while (0) + +#define increment(name, min, max) do { \ + CHECK(cbs_av1_write_increment(ctx, rw, min, max, #name, \ + current->name)); \ + } while (0) + +#define subexp(name, max, subs, ...) do { \ + CHECK(cbs_av1_write_subexp(ctx, rw, max, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + current->name)); \ + } while (0) + +#define delta_q(name) do { \ + xf(1, name.delta_coded, current->name != 0, 0, 1, 0); \ + if (current->name) \ + xsu(1 + 6, name.delta_q, current->name, 0); \ + } while (0) + +#define leb128(name) do { \ + CHECK(cbs_av1_write_leb128(ctx, rw, #name, current->name)); \ + } while (0) + +#define infer(name, value) do { \ + if (current->name != (value)) { \ + av_log(ctx->log_ctx, AV_LOG_WARNING, "Warning: " \ + "%s does not match inferred value: " \ + "%"PRId64", but should be %"PRId64".\n", \ + #name, (int64_t)current->name, (int64_t)(value)); \ + } \ + } while (0) + +#define byte_alignment(rw) (put_bits_count(rw) % 8) + +#include "cbs_av1_syntax_template.c" + +#undef READ +#undef READWRITE +#undef RWContext +#undef xf +#undef xsu +#undef uvlc +#undef leb128 +#undef ns +#undef increment +#undef subexp +#undef delta_q +#undef infer +#undef byte_alignment + + +static int cbs_av1_split_fragment(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + int header) +{ + GetBitContext gbc; + uint8_t *data; + size_t size; + uint64_t obu_length; + int pos, err; + + data = frag->data; + size = frag->data_size; + + while (size > 0) { + AV1RawOBUHeader header; + uint64_t obu_size; + + init_get_bits(&gbc, data, 8 * size); + + err = cbs_av1_read_obu_header(ctx, &gbc, &header); + if (err < 0) + return err; + + if (!header.obu_has_size_field) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid OBU for raw " + "stream: size field must be present.\n"); + return AVERROR_INVALIDDATA; + } + + if (get_bits_left(&gbc) < 8) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid OBU: fragment " + "too short (%zu bytes).\n", size); + return AVERROR_INVALIDDATA; + } + + err = cbs_av1_read_leb128(ctx, &gbc, "obu_size", &obu_size); + if (err < 0) + return err; + + pos = get_bits_count(&gbc); + av_assert0(pos % 8 == 0 && pos / 8 <= size); + + obu_length = pos / 8 + obu_size; + + if (size < obu_length) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid OBU length: " + "%"PRIu64", but only %zu bytes remaining in fragment.\n", + obu_length, size); + return AVERROR_INVALIDDATA; + } + + err = ff_cbs_insert_unit_data(ctx, frag, -1, header.obu_type, + data, obu_length, frag->data_ref); + if (err < 0) + return err; + + data += obu_length; + size -= obu_length; + } + + return 0; +} + +static void cbs_av1_free_tile_data(AV1RawTileData *td) +{ + av_buffer_unref(&td->data_ref); +} + +static void cbs_av1_free_metadata(AV1RawMetadata *md) +{ + switch (md->metadata_type) { + case AV1_METADATA_TYPE_ITUT_T35: + av_buffer_unref(&md->itut_t35.payload_ref); + break; + } +} + +static void cbs_av1_free_obu(void *unit, uint8_t *content) +{ + AV1RawOBU *obu = (AV1RawOBU*)content; + + switch (obu->header.obu_type) { + case AV1_OBU_TILE_GROUP: + cbs_av1_free_tile_data(&obu->tile_group.tile_data); + break; + case AV1_OBU_FRAME: + cbs_av1_free_tile_data(&obu->frame.tile_group.tile_data); + break; + case AV1_OBU_TILE_LIST: + cbs_av1_free_tile_data(&obu->tile_list.tile_data); + break; + case AV1_OBU_METADATA: + cbs_av1_free_metadata(&obu->metadata); + break; + } + + av_freep(&obu); +} + +static int cbs_av1_ref_tile_data(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit, + GetBitContext *gbc, + AV1RawTileData *td) +{ + int pos; + + pos = get_bits_count(gbc); + if (pos >= 8 * unit->data_size) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Bitstream ended before " + "any data in tile group (%d bits read).\n", pos); + return AVERROR_INVALIDDATA; + } + // Must be byte-aligned at this point. + av_assert0(pos % 8 == 0); + + td->data_ref = av_buffer_ref(unit->data_ref); + if (!td->data_ref) + return AVERROR(ENOMEM); + + td->data = unit->data + pos / 8; + td->data_size = unit->data_size - pos / 8; + + return 0; +} + +static int cbs_av1_read_unit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit) +{ + CodedBitstreamAV1Context *priv = ctx->priv_data; + AV1RawOBU *obu; + GetBitContext gbc; + int err, start_pos, end_pos; + + err = ff_cbs_alloc_unit_content(ctx, unit, sizeof(*obu), + &cbs_av1_free_obu); + if (err < 0) + return err; + obu = unit->content; + + err = init_get_bits(&gbc, unit->data, 8 * unit->data_size); + if (err < 0) + return err; + + err = cbs_av1_read_obu_header(ctx, &gbc, &obu->header); + if (err < 0) + return err; + av_assert0(obu->header.obu_type == unit->type); + + if (obu->header.obu_has_size_field) { + uint64_t obu_size; + err = cbs_av1_read_leb128(ctx, &gbc, "obu_size", &obu_size); + if (err < 0) + return err; + obu->obu_size = obu_size; + } else { + if (unit->data_size < 1 + obu->header.obu_extension_flag) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid OBU length: " + "unit too short (%zu).\n", unit->data_size); + return AVERROR_INVALIDDATA; + } + obu->obu_size = unit->data_size - 1 - obu->header.obu_extension_flag; + } + + start_pos = get_bits_count(&gbc); + + if (obu->header.obu_extension_flag) { + priv->temporal_id = obu->header.temporal_id; + priv->spatial_id = obu->header.temporal_id; + + if (obu->header.obu_type != AV1_OBU_SEQUENCE_HEADER && + obu->header.obu_type != AV1_OBU_TEMPORAL_DELIMITER && + priv->operating_point_idc) { + int in_temporal_layer = + (priv->operating_point_idc >> priv->temporal_id ) & 1; + int in_spatial_layer = + (priv->operating_point_idc >> (priv->spatial_id + 8)) & 1; + if (!in_temporal_layer || !in_spatial_layer) { + // Decoding will drop this OBU at this operating point. + } + } + } else { + priv->temporal_id = 0; + priv->spatial_id = 0; + } + + switch (obu->header.obu_type) { + case AV1_OBU_SEQUENCE_HEADER: + { + err = cbs_av1_read_sequence_header_obu(ctx, &gbc, &obu->sequence_header); + if (err < 0) + return err; + + av_buffer_unref(&priv->sequence_header_ref); + priv->sequence_header = NULL; + + priv->sequence_header_ref = av_buffer_ref(unit->content_ref); + if (!priv->sequence_header_ref) + return AVERROR(ENOMEM); + priv->sequence_header = &obu->sequence_header; + } + break; + case AV1_OBU_TEMPORAL_DELIMITER: + { + err = cbs_av1_read_temporal_delimiter_obu(ctx, &gbc); + if (err < 0) + return err; + } + break; + case AV1_OBU_FRAME_HEADER: + case AV1_OBU_REDUNDANT_FRAME_HEADER: + { + err = cbs_av1_read_frame_header_obu(ctx, &gbc, &obu->frame_header); + if (err < 0) + return err; + } + break; + case AV1_OBU_TILE_GROUP: + { + err = cbs_av1_read_tile_group_obu(ctx, &gbc, &obu->tile_group); + if (err < 0) + return err; + + err = cbs_av1_ref_tile_data(ctx, unit, &gbc, &obu->tile_group.tile_data); + if (err < 0) + return err; + } + break; + case AV1_OBU_FRAME: + { + err = cbs_av1_read_frame_obu(ctx, &gbc, &obu->frame); + if (err < 0) + return err; + + err = cbs_av1_ref_tile_data(ctx, unit, &gbc, &obu->frame.tile_group.tile_data); + if (err < 0) + return err; + } + break; + case AV1_OBU_TILE_LIST: + { + err = cbs_av1_read_tile_list_obu(ctx, &gbc, &obu->tile_list); + if (err < 0) + return err; + + err = cbs_av1_ref_tile_data(ctx, unit, &gbc, &obu->tile_list.tile_data); + if (err < 0) + return err; + } + break; + case AV1_OBU_METADATA: + { + err = cbs_av1_read_metadata_obu(ctx, &gbc, &obu->metadata); + if (err < 0) + return err; + } + break; + case AV1_OBU_PADDING: + default: + return AVERROR(ENOSYS); + } + + end_pos = get_bits_count(&gbc); + av_assert0(end_pos <= unit->data_size * 8); + + if (obu->obu_size > 0 && + obu->header.obu_type != AV1_OBU_TILE_GROUP && + obu->header.obu_type != AV1_OBU_FRAME) { + err = cbs_av1_read_trailing_bits(ctx, &gbc, + obu->obu_size * 8 + start_pos - end_pos); + if (err < 0) + return err; + } + + return 0; +} + +static int cbs_av1_write_obu(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit, + PutBitContext *pbc) +{ + CodedBitstreamAV1Context *priv = ctx->priv_data; + AV1RawOBU *obu = unit->content; + PutBitContext pbc_tmp; + AV1RawTileData *td; + size_t header_size; + int err, start_pos, end_pos, data_pos; + + // OBUs in the normal bitstream format must contain a size field + // in every OBU (in annex B it is optional, but we don't support + // writing that). + obu->header.obu_has_size_field = 1; + + err = cbs_av1_write_obu_header(ctx, pbc, &obu->header); + if (err < 0) + return err; + + if (obu->header.obu_has_size_field) { + pbc_tmp = *pbc; + // Add space for the size field to fill later. + put_bits32(pbc, 0); + put_bits32(pbc, 0); + } + + td = NULL; + start_pos = put_bits_count(pbc); + + switch (obu->header.obu_type) { + case AV1_OBU_SEQUENCE_HEADER: + { + err = cbs_av1_write_sequence_header_obu(ctx, pbc, &obu->sequence_header); + if (err < 0) + return err; + + av_buffer_unref(&priv->sequence_header_ref); + priv->sequence_header = NULL; + + priv->sequence_header_ref = av_buffer_ref(unit->content_ref); + if (!priv->sequence_header_ref) + return AVERROR(ENOMEM); + priv->sequence_header = &obu->sequence_header; + } + break; + case AV1_OBU_TEMPORAL_DELIMITER: + { + err = cbs_av1_write_temporal_delimiter_obu(ctx, pbc); + if (err < 0) + return err; + } + break; + case AV1_OBU_FRAME_HEADER: + case AV1_OBU_REDUNDANT_FRAME_HEADER: + { + err = cbs_av1_write_frame_header_obu(ctx, pbc, &obu->frame_header); + if (err < 0) + return err; + } + break; + case AV1_OBU_TILE_GROUP: + { + err = cbs_av1_write_tile_group_obu(ctx, pbc, &obu->tile_group); + if (err < 0) + return err; + + td = &obu->tile_group.tile_data; + } + break; + case AV1_OBU_FRAME: + { + err = cbs_av1_write_frame_obu(ctx, pbc, &obu->frame); + if (err < 0) + return err; + + td = &obu->frame.tile_group.tile_data; + } + break; + case AV1_OBU_TILE_LIST: + { + err = cbs_av1_write_tile_list_obu(ctx, pbc, &obu->tile_list); + if (err < 0) + return err; + + td = &obu->tile_list.tile_data; + } + break; + case AV1_OBU_METADATA: + { + err = cbs_av1_write_metadata_obu(ctx, pbc, &obu->metadata); + if (err < 0) + return err; + } + break; + case AV1_OBU_PADDING: + default: + return AVERROR(ENOSYS); + } + + end_pos = put_bits_count(pbc); + header_size = (end_pos - start_pos + 7) / 8; + if (td) { + obu->obu_size = header_size + td->data_size; + } else if (header_size > 0) { + // Add trailing bits and recalculate. + err = cbs_av1_write_trailing_bits(ctx, pbc, 8 - end_pos % 8); + if (err < 0) + return err; + end_pos = put_bits_count(pbc); + obu->obu_size = (end_pos - start_pos + 7) / 8; + } else { + // Empty OBU. + obu->obu_size = 0; + } + + end_pos = put_bits_count(pbc); + // Must now be byte-aligned. + av_assert0(end_pos % 8 == 0); + flush_put_bits(pbc); + start_pos /= 8; + end_pos /= 8; + + *pbc = pbc_tmp; + err = cbs_av1_write_leb128(ctx, pbc, "obu_size", obu->obu_size); + if (err < 0) + return err; + + data_pos = put_bits_count(pbc) / 8; + flush_put_bits(pbc); + av_assert0(data_pos <= start_pos); + + if (8 * obu->obu_size > put_bits_left(pbc)) + return AVERROR(ENOSPC); + + if (obu->obu_size > 0) { + memmove(priv->write_buffer + data_pos, + priv->write_buffer + start_pos, header_size); + skip_put_bytes(pbc, header_size); + + if (td) { + memcpy(priv->write_buffer + data_pos + header_size, + td->data, td->data_size); + skip_put_bytes(pbc, td->data_size); + } + } + + return 0; +} + +static int cbs_av1_write_unit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit) +{ + CodedBitstreamAV1Context *priv = ctx->priv_data; + PutBitContext pbc; + int err; + + if (!priv->write_buffer) { + // Initial write buffer size is 1MB. + priv->write_buffer_size = 1024 * 1024; + + reallocate_and_try_again: + err = av_reallocp(&priv->write_buffer, priv->write_buffer_size); + if (err < 0) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Unable to allocate a " + "sufficiently large write buffer (last attempt " + "%zu bytes).\n", priv->write_buffer_size); + return err; + } + } + + init_put_bits(&pbc, priv->write_buffer, priv->write_buffer_size); + + err = cbs_av1_write_obu(ctx, unit, &pbc); + if (err == AVERROR(ENOSPC)) { + // Overflow. + priv->write_buffer_size *= 2; + goto reallocate_and_try_again; + } + if (err < 0) + return err; + + // Overflow but we didn't notice. + av_assert0(put_bits_count(&pbc) <= 8 * priv->write_buffer_size); + + // OBU data must be byte-aligned. + av_assert0(put_bits_count(&pbc) % 8 == 0); + + unit->data_size = put_bits_count(&pbc) / 8; + flush_put_bits(&pbc); + + err = ff_cbs_alloc_unit_data(ctx, unit, unit->data_size); + if (err < 0) + return err; + + memcpy(unit->data, priv->write_buffer, unit->data_size); + + return 0; +} + +static int cbs_av1_assemble_fragment(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag) +{ + size_t size, pos; + int i; + + size = 0; + for (i = 0; i < frag->nb_units; i++) + size += frag->units[i].data_size; + + frag->data_ref = av_buffer_alloc(size + AV_INPUT_BUFFER_PADDING_SIZE); + if (!frag->data_ref) + return AVERROR(ENOMEM); + frag->data = frag->data_ref->data; + memset(frag->data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE); + + pos = 0; + for (i = 0; i < frag->nb_units; i++) { + memcpy(frag->data + pos, frag->units[i].data, + frag->units[i].data_size); + pos += frag->units[i].data_size; + } + av_assert0(pos == size); + frag->data_size = size; + + return 0; +} + +static void cbs_av1_close(CodedBitstreamContext *ctx) +{ + CodedBitstreamAV1Context *priv = ctx->priv_data; + + av_buffer_unref(&priv->sequence_header_ref); + + av_freep(&priv->write_buffer); +} + +const CodedBitstreamType ff_cbs_type_av1 = { + .codec_id = AV_CODEC_ID_AV1, + + .priv_data_size = sizeof(CodedBitstreamAV1Context), + + .split_fragment = &cbs_av1_split_fragment, + .read_unit = &cbs_av1_read_unit, + .write_unit = &cbs_av1_write_unit, + .assemble_fragment = &cbs_av1_assemble_fragment, + + .close = &cbs_av1_close, +}; diff --git a/libavcodec/cbs_av1.h b/libavcodec/cbs_av1.h new file mode 100644 index 0000000000..5c72c1b46c --- /dev/null +++ b/libavcodec/cbs_av1.h @@ -0,0 +1,429 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_CBS_AV1_H +#define AVCODEC_CBS_AV1_H + +#include +#include + +#include "av1.h" +#include "cbs.h" + + +typedef struct AV1RawOBUHeader { + uint8_t obu_forbidden_bit; + uint8_t obu_type; + uint8_t obu_extension_flag; + uint8_t obu_has_size_field; + uint8_t obu_reserved_1bit; + + uint8_t temporal_id; + uint8_t spatial_id; + uint8_t extension_header_reserved_3bits; +} AV1RawOBUHeader; + +typedef struct AV1RawColorConfig { + uint8_t high_bitdepth; + uint8_t twelve_bit; + uint8_t mono_chrome; + + uint8_t color_description_present_flag; + uint8_t color_primaries; + uint8_t transfer_characteristics; + uint8_t matrix_coefficients; + + uint8_t color_range; + uint8_t subsampling_x; + uint8_t subsampling_y; + uint8_t chroma_sample_position; + uint8_t separate_uv_delta_q; +} AV1RawColorConfig; + +typedef struct AV1RawTimingInfo { + uint32_t num_units_in_display_tick; + uint32_t time_scale; + + uint8_t equal_picture_interval; + uint32_t num_ticks_per_picture_minus_1; +} AV1RawTimingInfo; + +typedef struct AV1RawDecoderModelInfo { + uint8_t buffer_delay_length_minus_1; + uint32_t num_units_in_decoding_tick; + uint8_t buffer_removal_time_length_minus_1; + uint8_t frame_presentation_time_length_minus_1; +} AV1RawDecoderModelInfo; + +typedef struct AV1RawSequenceHeader { + uint8_t seq_profile; + uint8_t still_picture; + uint8_t reduced_still_picture_header; + + uint8_t timing_info_present_flag; + uint8_t decoder_model_info_present_flag; + uint8_t initial_display_delay_present_flag; + uint8_t operating_points_cnt_minus_1; + + AV1RawTimingInfo timing_info; + AV1RawDecoderModelInfo decoder_model_info; + + uint16_t operating_point_idc[AV1_MAX_OPERATING_POINTS]; + uint8_t seq_level_idx[AV1_MAX_OPERATING_POINTS]; + uint8_t seq_tier[AV1_MAX_OPERATING_POINTS]; + uint8_t decoder_model_present_for_this_op[AV1_MAX_OPERATING_POINTS]; + uint8_t decoder_buffer_delay[AV1_MAX_OPERATING_POINTS]; + uint8_t encoder_buffer_delay[AV1_MAX_OPERATING_POINTS]; + uint8_t low_delay_mode_flag[AV1_MAX_OPERATING_POINTS]; + uint8_t initial_display_delay_present_for_this_op[AV1_MAX_OPERATING_POINTS]; + uint8_t initial_display_delay_minus_1[AV1_MAX_OPERATING_POINTS]; + + uint8_t frame_width_bits_minus_1; + uint8_t frame_height_bits_minus_1; + uint16_t max_frame_width_minus_1; + uint16_t max_frame_height_minus_1; + + uint8_t frame_id_numbers_present_flag; + uint8_t delta_frame_id_length_minus_2; + uint8_t additional_frame_id_length_minus_1; + + uint8_t use_128x128_superblock; + uint8_t enable_filter_intra; + uint8_t enable_intra_edge_filter; + uint8_t enable_intraintra_compound; + uint8_t enable_masked_compound; + uint8_t enable_warped_motion; + uint8_t enable_dual_filter; + + uint8_t enable_order_hint; + uint8_t enable_jnt_comp; + uint8_t enable_ref_frame_mvs; + + uint8_t seq_choose_screen_content_tools; + uint8_t seq_force_screen_content_tools; + uint8_t seq_choose_integer_mv; + uint8_t seq_force_integer_mv; + + uint8_t order_hint_bits_minus_1; + + uint8_t enable_superres; + uint8_t enable_cdef; + uint8_t enable_restoration; + + AV1RawColorConfig color_config; + + uint8_t film_grain_params_present; +} AV1RawSequenceHeader; + +typedef struct AV1RawFrameHeader { + uint8_t show_existing_frame; + uint8_t frame_to_show_map_idx; + uint32_t frame_presentation_time; + uint32_t display_frame_id; + + uint8_t frame_type; + uint8_t show_frame; + uint8_t showable_frame; + + uint8_t error_resilient_mode; + uint8_t disable_cdf_update; + uint8_t allow_screen_content_tools; + uint8_t force_integer_mv; + + uint32_t current_frame_id; + uint8_t frame_size_override_flag; + uint8_t order_hint; + + uint8_t buffer_removal_time_present_flag; + uint32_t buffer_removal_time[AV1_MAX_OPERATING_POINTS]; + + uint8_t primary_ref_frame; + uint16_t frame_width_minus_1; + uint16_t frame_height_minus_1; + uint8_t use_superres; + uint8_t coded_denom; + uint8_t render_and_frame_size_different; + uint8_t render_width_minus_1; + uint8_t render_height_minus_1; + + uint8_t found_ref; + + uint8_t refresh_frame_flags; + uint8_t allow_intrabc; + uint8_t ref_order_hint[AV1_NUM_REF_FRAMES]; + uint8_t frame_refs_short_signaling; + uint8_t last_frame_idx; + uint8_t golden_frame_idx; + int8_t ref_frame_idx[AV1_REFS_PER_FRAME]; + uint8_t delta_frame_id_minus1; + + uint8_t allow_high_precision_mv; + uint8_t is_filter_switchable; + uint8_t interpolation_filter; + uint8_t is_motion_mode_switchable; + uint8_t use_ref_frame_mvs; + + uint8_t disable_frame_end_update_cdf; + + uint8_t uniform_tile_spacing_flag; + uint8_t tile_cols_log2; + uint8_t tile_rows_log2; + uint8_t width_in_sbs_minus_1[AV1_MAX_TILE_COLS]; + uint8_t height_in_sbs_minus_1[AV1_MAX_TILE_ROWS]; + uint16_t context_update_tile_id; + uint8_t tile_size_bytes_minus1; + + // These are derived values, but it's very unhelpful to have to + // recalculate them all the time so we store them here. + uint16_t tile_cols; + uint16_t tile_rows; + + uint8_t base_q_idx; + int8_t delta_q_y_dc; + uint8_t diff_uv_delta; + int8_t delta_q_u_dc; + int8_t delta_q_u_ac; + int8_t delta_q_v_dc; + int8_t delta_q_v_ac; + uint8_t using_qmatrix; + uint8_t qm_y; + uint8_t qm_u; + uint8_t qm_v; + + uint8_t segmentation_enabled; + uint8_t segmentation_update_map; + uint8_t segmentation_temporal_update; + uint8_t segmentation_update_data; + uint8_t feature_enabled[AV1_MAX_SEGMENTS][AV1_SEG_LVL_MAX]; + uint8_t feature_value[AV1_MAX_SEGMENTS][AV1_SEG_LVL_MAX]; + + uint8_t delta_q_present; + uint8_t delta_q_res; + uint8_t delta_lf_present; + uint8_t delta_lf_res; + uint8_t delta_lf_multi; + + uint8_t loop_filter_level[4]; + uint8_t loop_filter_sharpness; + uint8_t loop_filter_delta_enabled; + uint8_t loop_filter_delta_update; + uint8_t update_ref_delta[AV1_TOTAL_REFS_PER_FRAME]; + int8_t loop_filter_ref_deltas[AV1_TOTAL_REFS_PER_FRAME]; + uint8_t update_mode_delta[2]; + int8_t loop_filter_mode_deltas[2]; + + uint8_t cdef_damping_minus_3; + uint8_t cdef_bits; + uint8_t cdef_y_pri_strength[8]; + uint8_t cdef_y_sec_strength[8]; + uint8_t cdef_uv_pri_strength[8]; + uint8_t cdef_uv_sec_strength[8]; + + uint8_t lr_type[3]; + uint8_t lr_unit_shift; + uint8_t lr_uv_shift; + + uint8_t tx_mode; + uint8_t reference_select; + uint8_t skip_mode_present; + + uint8_t allow_warped_motion; + uint8_t reduced_tx_set; + + uint8_t is_global[AV1_TOTAL_REFS_PER_FRAME]; + uint8_t is_rot_zoom[AV1_TOTAL_REFS_PER_FRAME]; + uint8_t is_translation[AV1_TOTAL_REFS_PER_FRAME]; + //AV1RawSubexp gm_params[AV1_TOTAL_REFS_PER_FRAME][6]; + uint32_t gm_params[AV1_TOTAL_REFS_PER_FRAME][6]; + + uint8_t apply_grain; + uint16_t grain_seed; + uint8_t update_grain; + uint8_t film_grain_params_ref_idx; + uint8_t num_y_points; + uint8_t point_y_value[16]; + uint8_t point_y_scaling[16]; + uint8_t chroma_scaling_from_luma; + uint8_t num_cb_points; + uint8_t point_cb_value[16]; + uint8_t point_cb_scaling[16]; + uint8_t num_cr_points; + uint8_t point_cr_value[16]; + uint8_t point_cr_scaling[16]; + uint8_t grain_scaling_minus_8; + uint8_t ar_coeff_lag; + uint8_t ar_coeffs_y_plus_128[24]; + uint8_t ar_coeffs_cb_plus_128[24]; + uint8_t ar_coeffs_cr_plus_128[24]; + uint8_t ar_coeff_shift_minus_6; + uint8_t grain_scale_shift; + uint8_t cb_mult; + uint8_t cb_luma_mult; + uint16_t cb_offset; + uint8_t cr_mult; + uint8_t cr_luma_mult; + uint16_t cr_offset; + uint8_t overlap_flag; + uint8_t clip_to_restricted_range; +} AV1RawFrameHeader; + +typedef struct AV1RawTileData { + uint8_t *data; + size_t data_size; + AVBufferRef *data_ref; +} AV1RawTileData; + +typedef struct AV1RawTileGroup { + uint8_t tile_start_and_end_present_flag; + uint16_t tg_start; + uint16_t tg_end; + + AV1RawTileData tile_data; +} AV1RawTileGroup; + +typedef struct AV1RawFrame { + AV1RawFrameHeader header; + AV1RawTileGroup tile_group; +} AV1RawFrame; + +typedef struct AV1RawTileList { + uint8_t output_frame_width_in_tiles_minus_1; + uint8_t output_frame_height_in_tiles_minus_1; + uint16_t tile_count_minus_1; + + AV1RawTileData tile_data; +} AV1RawTileList; + +typedef struct AV1RawMetadataHDRCLL { + uint16_t max_cll; + uint16_t max_fall; +} AV1RawMetadataHDRCLL; + +typedef struct AV1RawMetadataHDRMDCV { + uint16_t primary_chromaticity_x[3]; + uint16_t primary_chromaticity_y[3]; + uint16_t white_point_chromaticity_x; + uint16_t white_point_chromaticity_y; + uint32_t luminance_max; + uint32_t luminance_min; +} AV1RawMetadataHDRMDCV; + +typedef struct AV1RawMetadataScalability { + uint8_t scalability_mode_idc; + // TODO: more stuff. +} AV1RawMetadataScalability; + +typedef struct AV1RawMetadataITUTT35 { + uint8_t itu_t_t35_country_code; + uint8_t itu_t_t35_country_code_extension_byte; + + uint8_t *payload; + size_t payload_size; + AVBufferRef *payload_ref; +} AV1RawMetadataITUTT35; + +typedef struct AV1RawMetadataTimecode { + uint8_t counting_type; + uint8_t full_timestamp_flag; + uint8_t discontinuity_flag; + uint8_t cnt_dropped_flag; + uint16_t n_frames; + uint8_t seconds_value; + uint8_t minutes_value; + uint8_t hours_value; + uint8_t seconds_flag; + uint8_t minutes_flag; + uint8_t hours_flag; + uint8_t time_offset_length; + uint32_t time_offset_value; +} AV1RawMetadataTimecode; + +typedef struct AV1RawMetadata { + uint64_t metadata_type; + union { + AV1RawMetadataHDRCLL hdr_cll; + AV1RawMetadataHDRMDCV hdr_mdcv; + AV1RawMetadataScalability scalability; + AV1RawMetadataITUTT35 itut_t35; + AV1RawMetadataTimecode timecode; + }; +} AV1RawMetadata; + + +typedef struct AV1RawOBU { + AV1RawOBUHeader header; + + size_t obu_size; + + union { + AV1RawSequenceHeader sequence_header; + AV1RawFrameHeader frame_header; + AV1RawFrame frame; + AV1RawTileGroup tile_group; + AV1RawTileList tile_list; + AV1RawMetadata metadata; + }; +} AV1RawOBU; + +typedef struct AV1ReferenceFrameState { + int valid; + int frame_type; + int upscaled_width; + int frame_width; + int frame_height; + int render_width; + int render_height; + int order_hint; + int sign_bias; + int subsampling_x; + int subsampling_y; + int bit_depth; +} AV1ReferenceFrameState; + +typedef struct CodedBitstreamAV1Context { + AV1RawSequenceHeader *sequence_header; + AVBufferRef *sequence_header_ref; + + int seen_frame_header; + + int temporal_id; + int spatial_id; + int operating_point_idc; + + int bit_depth; + int frame_width; + int frame_height; + int upscaled_width; + int render_width; + int render_height; + + int num_planes; + int coded_lossless; + int all_lossless; + int tile_cols; + int tile_rows; + + AV1ReferenceFrameState ref[AV1_NUM_REF_FRAMES]; + + // Write buffer. + uint8_t *write_buffer; + size_t write_buffer_size; +} CodedBitstreamAV1Context; + + +#endif /* AVCODEC_CBS_AV1_H */ diff --git a/libavcodec/cbs_av1_syntax_template.c b/libavcodec/cbs_av1_syntax_template.c new file mode 100644 index 0000000000..ec91c70e6d --- /dev/null +++ b/libavcodec/cbs_av1_syntax_template.c @@ -0,0 +1,1676 @@ +/* + * 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 + */ + +static int FUNC(obu_header)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawOBUHeader *current) +{ + int err; + av_unused int zero = 0; + + HEADER("OBU header"); + + fc(1, obu_forbidden_bit, 0, 0); + + fc(4, obu_type, 0, AV1_OBU_PADDING); + flag(obu_extension_flag); + flag(obu_has_size_field); + + fc(1, obu_reserved_1bit, 0, 0); + + if (current->obu_extension_flag) { + fb(3, temporal_id); + fb(2, spatial_id); + fc(3, extension_header_reserved_3bits, 0, 0); + } + + return 0; +} + +static int FUNC(trailing_bits)(CodedBitstreamContext *ctx, RWContext *rw, int nb_bits) +{ + int err; + + av_assert0(nb_bits > 0); + + fixed(1, trailing_one_bit, 1); + --nb_bits; + + while (nb_bits > 0) { + fixed(1, trailing_zero_bit, 0); + --nb_bits; + } + + return 0; +} + +static int FUNC(byte_alignment)(CodedBitstreamContext *ctx, RWContext *rw) +{ + int err; + + while (byte_alignment(rw) != 0) + fixed(1, zero_bit, 0); + + return 0; +} + +static int FUNC(color_config)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawColorConfig *current, int seq_profile) +{ + CodedBitstreamAV1Context *priv = ctx->priv_data; + int err; + + flag(high_bitdepth); + + if (seq_profile == FF_PROFILE_AV1_PROFESSIONAL && + current->high_bitdepth) { + flag(twelve_bit); + priv->bit_depth = current->twelve_bit ? 12 : 10; + } else { + priv->bit_depth = current->high_bitdepth ? 10 : 8; + } + + if (seq_profile == FF_PROFILE_AV1_HIGH) + infer(mono_chrome, 0); + else + flag(mono_chrome); + priv->num_planes = current->mono_chrome ? 1 : 3; + + flag(color_description_present_flag); + if (current->color_description_present_flag) { + fb(8, color_primaries); + fb(8, transfer_characteristics); + fb(8, matrix_coefficients); + } else { + infer(color_primaries, AVCOL_PRI_UNSPECIFIED); + infer(transfer_characteristics, AVCOL_TRC_UNSPECIFIED); + infer(matrix_coefficients, AVCOL_SPC_UNSPECIFIED); + } + + if (current->mono_chrome) { + infer(color_range, 1); + infer(subsampling_x, 1); + infer(subsampling_y, 1); + infer(chroma_sample_position, AV1_CSP_UNKNOWN); + infer(separate_uv_delta_q, 0); + + } else if (current->color_primaries == AVCOL_PRI_BT709 && + current->transfer_characteristics == AVCOL_TRC_IEC61966_2_1 && + current->matrix_coefficients == AVCOL_SPC_RGB) { + infer(color_range, 1); + infer(subsampling_x, 0); + infer(subsampling_y, 0); + flag(separate_uv_delta_q); + + } else { + flag(color_range); + + if (seq_profile == FF_PROFILE_AV1_MAIN) { + infer(subsampling_x, 1); + infer(subsampling_y, 1); + } else if (seq_profile == FF_PROFILE_AV1_HIGH) { + infer(subsampling_x, 0); + infer(subsampling_y, 0); + } else { + if (priv->bit_depth == 12) { + fb(1, subsampling_x); + if (current->subsampling_x) + fb(1, subsampling_y); + else + infer(subsampling_y, 0); + } else { + infer(subsampling_x, 1); + infer(subsampling_y, 0); + } + } + if (current->subsampling_x && current->subsampling_y) { + fc(2, chroma_sample_position, AV1_CSP_UNKNOWN, + AV1_CSP_COLOCATED); + } + + flag(separate_uv_delta_q); + } + + return 0; +} + +static int FUNC(timing_info)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawTimingInfo *current) +{ + int err; + + fc(32, num_units_in_display_tick, 1, MAX_UINT_BITS(32)); + fc(32, time_scale, 1, MAX_UINT_BITS(32)); + + flag(equal_picture_interval); + if (current->equal_picture_interval) + uvlc(num_ticks_per_picture_minus_1, 0, MAX_UINT_BITS(32) - 1); + + return 0; +} + +static int FUNC(decoder_model_info)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawDecoderModelInfo *current) +{ + int err; + + fb(5, buffer_delay_length_minus_1); + fb(32, num_units_in_decoding_tick); + fb(5, buffer_removal_time_length_minus_1); + fb(5, frame_presentation_time_length_minus_1); + + return 0; +} + +static int FUNC(sequence_header_obu)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawSequenceHeader *current) +{ + int i, err; + + HEADER("Sequence Header"); + + fc(3, seq_profile, FF_PROFILE_AV1_MAIN, + FF_PROFILE_AV1_PROFESSIONAL); + flag(still_picture); + flag(reduced_still_picture_header); + + if (current->reduced_still_picture_header) { + infer(timing_info_present_flag, 0); + infer(decoder_model_info_present_flag, 0); + infer(initial_display_delay_present_flag, 0); + infer(operating_points_cnt_minus_1, 0); + infer(operating_point_idc[0], 0); + + fb(5, seq_level_idx[0]); + + infer(seq_tier[0], 0); + infer(decoder_model_present_for_this_op[0], 0); + infer(initial_display_delay_present_for_this_op[0], 0); + + } else { + flag(timing_info_present_flag); + if (current->timing_info_present_flag) { + CHECK(FUNC(timing_info)(ctx, rw, ¤t->timing_info)); + + flag(decoder_model_info_present_flag); + if (current->decoder_model_info_present_flag) { + CHECK(FUNC(decoder_model_info) + (ctx, rw, ¤t->decoder_model_info)); + } + } else { + infer(decoder_model_info_present_flag, 0); + } + + flag(initial_display_delay_present_flag); + + fb(5, operating_points_cnt_minus_1); + for (i = 0; i <= current->operating_points_cnt_minus_1; i++) { + fbs(12, operating_point_idc[i], 1, i); + fbs(5, seq_level_idx[i], 1, i); + + if (current->seq_level_idx[i] > 7) + flags(seq_tier[i], 1, i); + else + infer(seq_tier[i], 0); + + if (current->decoder_model_info_present_flag) { + flags(decoder_model_present_for_this_op[i], 1, i); + if (current->decoder_model_present_for_this_op[i]) { + int n = current->decoder_model_info.buffer_delay_length_minus_1 + 1; + fbs(n, decoder_buffer_delay[i], 1, i); + fbs(n, encoder_buffer_delay[i], 1, i); + flags(low_delay_mode_flag[i], 1, i); + } + } else { + infer(decoder_model_present_for_this_op[i], 0); + } + + if (current->initial_display_delay_present_flag) { + flags(initial_display_delay_present_for_this_op[i], 1, i); + if (current->initial_display_delay_present_for_this_op[i]) + fbs(4, initial_display_delay_minus_1[i], 1, i); + } + } + } + + fb(4, frame_width_bits_minus_1); + fb(4, frame_height_bits_minus_1); + + fb(current->frame_width_bits_minus_1 + 1, max_frame_width_minus_1); + fb(current->frame_height_bits_minus_1 + 1, max_frame_height_minus_1); + + if (current->reduced_still_picture_header) + infer(frame_id_numbers_present_flag, 0); + else + flag(frame_id_numbers_present_flag); + if (current->frame_id_numbers_present_flag) { + fb(4, delta_frame_id_length_minus_2); + fb(3, additional_frame_id_length_minus_1); + } + + flag(use_128x128_superblock); + flag(enable_filter_intra); + flag(enable_intra_edge_filter); + + if (current->reduced_still_picture_header) { + infer(enable_intraintra_compound, 0); + infer(enable_masked_compound, 0); + infer(enable_warped_motion, 0); + infer(enable_dual_filter, 0); + infer(enable_order_hint, 0); + infer(enable_jnt_comp, 0); + infer(enable_ref_frame_mvs, 0); + + infer(seq_force_screen_content_tools, + AV1_SELECT_SCREEN_CONTENT_TOOLS); + infer(seq_force_integer_mv, + AV1_SELECT_INTEGER_MV); + } else { + flag(enable_intraintra_compound); + flag(enable_masked_compound); + flag(enable_warped_motion); + flag(enable_dual_filter); + + flag(enable_order_hint); + if (current->enable_order_hint) { + flag(enable_jnt_comp); + flag(enable_ref_frame_mvs); + } else { + infer(enable_jnt_comp, 0); + infer(enable_ref_frame_mvs, 0); + } + + flag(seq_choose_screen_content_tools); + if (current->seq_choose_screen_content_tools) + infer(seq_force_screen_content_tools, + AV1_SELECT_SCREEN_CONTENT_TOOLS); + else + fb(1, seq_force_screen_content_tools); + if (current->seq_force_screen_content_tools > 0) { + flag(seq_choose_integer_mv); + if (current->seq_choose_integer_mv) + infer(seq_force_integer_mv, + AV1_SELECT_INTEGER_MV); + else + fb(1, seq_force_integer_mv); + } else { + infer(seq_force_integer_mv, AV1_SELECT_INTEGER_MV); + } + + if (current->enable_order_hint) + fb(3, order_hint_bits_minus_1); + } + + flag(enable_superres); + flag(enable_cdef); + flag(enable_restoration); + + CHECK(FUNC(color_config)(ctx, rw, ¤t->color_config, + current->seq_profile)); + + flag(film_grain_params_present); + + return 0; +} + +static int FUNC(temporal_delimiter_obu)(CodedBitstreamContext *ctx, RWContext *rw) +{ + CodedBitstreamAV1Context *priv = ctx->priv_data; + + HEADER("Temporal Delimiter"); + + priv->seen_frame_header = 0; + + return 0; +} + +static int FUNC(superres_params)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) +{ + CodedBitstreamAV1Context *priv = ctx->priv_data; + const AV1RawSequenceHeader *seq = priv->sequence_header; + int denom, err; + + if (seq->enable_superres) + flag(use_superres); + else + infer(use_superres, 0); + + if (current->use_superres) { + fb(3, coded_denom); + denom = current->coded_denom + AV1_SUPERRES_DENOM_MIN; + } else { + denom = AV1_SUPERRES_NUM; + } + + priv->upscaled_width = priv->frame_width; + priv->frame_width = (priv->upscaled_width * AV1_SUPERRES_NUM + + denom / 2) / denom; + + return 0; +} + +static int FUNC(frame_size)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) +{ + CodedBitstreamAV1Context *priv = ctx->priv_data; + const AV1RawSequenceHeader *seq = priv->sequence_header; + int err; + + if (current->frame_size_override_flag) { + fb(seq->frame_width_bits_minus_1 + 1, frame_width_minus_1); + fb(seq->frame_height_bits_minus_1 + 1, frame_height_minus_1); + + priv->frame_width = current->frame_width_minus_1 + 1; + priv->frame_height = current->frame_height_minus_1 + 1; + } else { + priv->frame_width = seq->max_frame_width_minus_1 + 1; + priv->frame_height = seq->max_frame_height_minus_1 + 1; + } + + CHECK(FUNC(superres_params)(ctx, rw, current)); + + return 0; +} + +static int FUNC(render_size)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) +{ + CodedBitstreamAV1Context *priv = ctx->priv_data; + int err; + + flag(render_and_frame_size_different); + + if (current->render_and_frame_size_different) { + fb(16, render_width_minus_1); + fb(16, render_height_minus_1); + + priv->render_width = current->render_width_minus_1 + 1; + priv->render_height = current->render_height_minus_1 + 1; + } else { + priv->render_width = priv->upscaled_width; + priv->render_height = priv->frame_height; + } + + return 0; +} + +static int FUNC(frame_size_with_refs)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) +{ + CodedBitstreamAV1Context *priv = ctx->priv_data; + int i, err; + + for (i = 0; i < AV1_REFS_PER_FRAME; i++) { + flag(found_ref); + if (current->found_ref) { + AV1ReferenceFrameState *ref = + &priv->ref[current->ref_frame_idx[i]]; + + if (!ref->valid) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Missing reference frame needed for frame size " + "(ref = %d, ref_frame_idx = %d).\n", + i, current->ref_frame_idx[i]); + return AVERROR_INVALIDDATA; + } + + priv->upscaled_width = ref->upscaled_width; + priv->frame_width = ref->frame_width; + priv->frame_height = ref->frame_height; + priv->render_width = ref->render_width; + priv->render_height = ref->render_height; + break; + } + } + + if (current->found_ref == 0) { + CHECK(FUNC(frame_size)(ctx, rw, current)); + CHECK(FUNC(render_size)(ctx, rw, current)); + } else { + CHECK(FUNC(superres_params)(ctx, rw, current)); + } + + return 0; +} + +static int FUNC(interpolation_filter)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) +{ + int err; + + flag(is_filter_switchable); + if (current->is_filter_switchable) + infer(interpolation_filter, + AV1_INTERPOLATION_FILTER_SWITCHABLE); + else + fb(2, interpolation_filter); + + return 0; +} + +static int FUNC(tile_info)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) +{ + CodedBitstreamAV1Context *priv = ctx->priv_data; + const AV1RawSequenceHeader *seq = priv->sequence_header; + int mi_cols, mi_rows, sb_cols, sb_rows, sb_shift, sb_size; + int max_tile_width_sb, max_tile_height_sb, max_tile_area_sb; + int min_log2_tile_cols, max_log2_tile_cols, max_log2_tile_rows; + int min_log2_tiles, min_log2_tile_rows; + int i, err; + + mi_cols = 2 * ((priv->frame_width + 7) >> 3); + mi_rows = 2 * ((priv->frame_height + 7) >> 3); + + sb_cols = seq->use_128x128_superblock ? ((mi_cols + 31) >> 5) + : ((mi_cols + 15) >> 4); + sb_rows = seq->use_128x128_superblock ? ((mi_rows + 31) >> 5) + : ((mi_rows + 15) >> 4); + + sb_shift = seq->use_128x128_superblock ? 5 : 4; + sb_size = sb_shift + 2; + + max_tile_width_sb = AV1_MAX_TILE_WIDTH >> sb_size; + max_tile_area_sb = AV1_MAX_TILE_AREA >> (2 * sb_size); + + min_log2_tile_cols = cbs_av1_tile_log2(max_tile_width_sb, sb_cols); + max_log2_tile_cols = cbs_av1_tile_log2(1, FFMIN(sb_cols, AV1_MAX_TILE_COLS)); + max_log2_tile_rows = cbs_av1_tile_log2(1, FFMIN(sb_rows, AV1_MAX_TILE_ROWS)); + min_log2_tiles = FFMAX(min_log2_tile_cols, + cbs_av1_tile_log2(max_tile_area_sb, sb_rows * sb_cols)); + + flag(uniform_tile_spacing_flag); + + if (current->uniform_tile_spacing_flag) { + int tile_width_sb, tile_height_sb; + + increment(tile_cols_log2, min_log2_tile_cols, max_log2_tile_cols); + + tile_width_sb = (sb_cols + (1 << current->tile_cols_log2) - 1) >> + current->tile_cols_log2; + current->tile_cols = (sb_cols + tile_width_sb - 1) / tile_width_sb; + + min_log2_tile_rows = FFMAX(min_log2_tiles - current->tile_cols_log2, 0); + + increment(tile_rows_log2, min_log2_tile_rows, max_log2_tile_rows); + + tile_height_sb = (sb_rows + (1 << current->tile_rows_log2) - 1) >> + current->tile_rows_log2; + current->tile_rows = (sb_rows + tile_height_sb - 1) / tile_height_sb; + + } else { + int widest_tile_sb, start_sb, size_sb, max_width, max_height; + + widest_tile_sb = 0; + + start_sb = 0; + for (i = 0; start_sb < sb_cols && i < AV1_MAX_TILE_COLS; i++) { + max_width = FFMIN(sb_cols - start_sb, max_tile_width_sb); + ns(max_width, width_in_sbs_minus_1[i], 1, i); + size_sb = current->width_in_sbs_minus_1[i] + 1; + widest_tile_sb = FFMAX(size_sb, widest_tile_sb); + start_sb += size_sb; + } + current->tile_cols_log2 = cbs_av1_tile_log2(1, i); + current->tile_cols = i; + + if (min_log2_tiles > 0) + max_tile_area_sb = (sb_rows * sb_cols) >> (min_log2_tiles + 1); + else + max_tile_area_sb = sb_rows * sb_cols; + max_tile_height_sb = FFMAX(max_tile_area_sb / widest_tile_sb, 1); + + start_sb = 0; + for (i = 0; start_sb < sb_rows && i < AV1_MAX_TILE_ROWS; i++) { + max_height = FFMIN(sb_rows - start_sb, max_tile_height_sb); + ns(max_height, height_in_sbs_minus_1[i], 1, i); + size_sb = current->height_in_sbs_minus_1[i] + 1; + start_sb += size_sb; + } + current->tile_rows_log2 = cbs_av1_tile_log2(1, i); + current->tile_rows = i; + } + + if (current->tile_cols_log2 > 0 || + current->tile_rows_log2 > 0) { + fb(current->tile_cols_log2 + current->tile_rows_log2, + context_update_tile_id); + fb(2, tile_size_bytes_minus1); + } else { + infer(context_update_tile_id, 0); + } + + priv->tile_cols = current->tile_cols; + priv->tile_rows = current->tile_rows; + + return 0; +} + +static int FUNC(quantization_params)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) +{ + CodedBitstreamAV1Context *priv = ctx->priv_data; + const AV1RawSequenceHeader *seq = priv->sequence_header; + int err; + + fb(8, base_q_idx); + + delta_q(delta_q_y_dc); + + if (priv->num_planes > 1) { + if (seq->color_config.separate_uv_delta_q) + flag(diff_uv_delta); + else + infer(diff_uv_delta, 0); + + delta_q(delta_q_u_dc); + delta_q(delta_q_u_ac); + + if (current->diff_uv_delta) { + delta_q(delta_q_v_dc); + delta_q(delta_q_v_ac); + } else { + infer(delta_q_v_dc, current->delta_q_u_dc); + infer(delta_q_v_ac, current->delta_q_u_ac); + } + } else { + infer(delta_q_u_dc, 0); + infer(delta_q_u_ac, 0); + infer(delta_q_v_dc, 0); + infer(delta_q_v_ac, 0); + } + + flag(using_qmatrix); + if (current->using_qmatrix) { + fb(4, qm_y); + fb(4, qm_u); + if (seq->color_config.separate_uv_delta_q) + fb(4, qm_v); + else + infer(qm_v, current->qm_u); + } + + return 0; +} + +static int FUNC(segmentation_params)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) +{ + static const uint8_t bits[AV1_SEG_LVL_MAX] = { 8, 6, 6, 6, 6, 3, 0, 0 }; + static const uint8_t sign[AV1_SEG_LVL_MAX] = { 1, 1, 1, 1, 1, 0, 0, 0 }; + int i, j, err; + + flag(segmentation_enabled); + + if (current->segmentation_enabled) { + if (current->primary_ref_frame == AV1_PRIMARY_REF_NONE) { + infer(segmentation_update_map, 1); + infer(segmentation_temporal_update, 0); + infer(segmentation_update_data, 1); + } else { + flag(segmentation_update_map); + if (current->segmentation_update_map) + flag(segmentation_temporal_update); + else + infer(segmentation_temporal_update, 0); + flag(segmentation_update_data); + } + + if (current->segmentation_update_data) { + for (i = 0; i < AV1_MAX_SEGMENTS; i++) { + for (j = 0; j < AV1_SEG_LVL_MAX; j++) { + flags(feature_enabled[i][j], 2, i, j); + + if (current->feature_enabled[i][j] && bits[j] > 0) { + if (sign[j]) + sus(1 + bits[j], feature_value[i][j], 2, i, j); + else + fbs(bits[j], feature_value[i][j], 2, i, j); + } else { + infer(feature_value[i][j], 0); + } + } + } + } + } else { + for (i = 0; i < AV1_MAX_SEGMENTS; i++) { + for (j = 0; j < AV1_SEG_LVL_MAX; j++) { + infer(feature_enabled[i][j], 0); + infer(feature_value[i][j], 0); + } + } + } + + return 0; +} + +static int FUNC(delta_q_params)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) +{ + int err; + + if (current->base_q_idx > 0) + flag(delta_q_present); + else + infer(delta_q_present, 0); + + if (current->delta_q_present) + fb(2, delta_q_res); + + return 0; +} + +static int FUNC(delta_lf_params)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) +{ + int err; + + if (current->delta_q_present) { + if (!current->allow_intrabc) + flag(delta_lf_present); + else + infer(delta_lf_present, 0); + if (current->delta_lf_present) { + fb(2, delta_lf_res); + flag(delta_lf_multi); + } else { + infer(delta_lf_res, 0); + infer(delta_lf_multi, 0); + } + } else { + infer(delta_lf_present, 0); + infer(delta_lf_res, 0); + infer(delta_lf_multi, 0); + } + + return 0; +} + +static int FUNC(loop_filter_params)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) +{ + CodedBitstreamAV1Context *priv = ctx->priv_data; + int i, err; + + if (priv->coded_lossless || current->allow_intrabc) { + infer(loop_filter_level[0], 0); + infer(loop_filter_level[1], 0); + infer(loop_filter_ref_deltas[AV1_REF_FRAME_INTRA], 1); + infer(loop_filter_ref_deltas[AV1_REF_FRAME_LAST], 0); + infer(loop_filter_ref_deltas[AV1_REF_FRAME_LAST2], 0); + infer(loop_filter_ref_deltas[AV1_REF_FRAME_LAST3], 0); + infer(loop_filter_ref_deltas[AV1_REF_FRAME_BWDREF], 0); + infer(loop_filter_ref_deltas[AV1_REF_FRAME_GOLDEN], -1); + infer(loop_filter_ref_deltas[AV1_REF_FRAME_ALTREF], -1); + infer(loop_filter_ref_deltas[AV1_REF_FRAME_ALTREF2], -1); + for (i = 0; i < 2; i++) + infer(loop_filter_mode_deltas[i], 0); + return 0; + } + + fb(6, loop_filter_level[0]); + fb(6, loop_filter_level[1]); + + if (priv->num_planes > 1) { + if (current->loop_filter_level[0] || + current->loop_filter_level[1]) { + fb(6, loop_filter_level[2]); + fb(6, loop_filter_level[3]); + } + } + + fb(3, loop_filter_sharpness); + + flag(loop_filter_delta_enabled); + if (current->loop_filter_delta_enabled) { + flag(loop_filter_delta_update); + if (current->loop_filter_delta_update) { + for (i = 0; i < AV1_TOTAL_REFS_PER_FRAME; i++) { + flags(update_ref_delta[i], 1, i); + if (current->update_ref_delta[i]) + sus(1 + 6, loop_filter_ref_deltas[i], 1, i); + } + for (i = 0; i < 2; i++) { + flags(update_mode_delta[i], 1, i); + if (current->update_mode_delta[i]) + sus(1 + 6, loop_filter_mode_deltas[i], 1, i); + } + } + } + + return 0; +} + +static int FUNC(cdef_params)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) +{ + CodedBitstreamAV1Context *priv = ctx->priv_data; + const AV1RawSequenceHeader *seq = priv->sequence_header; + int i, err; + + if (priv->coded_lossless || current->allow_intrabc || + !seq->enable_cdef) { + infer(cdef_damping_minus_3, 0); + infer(cdef_bits, 0); + infer(cdef_y_pri_strength[0], 0); + infer(cdef_y_sec_strength[0], 0); + infer(cdef_uv_pri_strength[0], 0); + infer(cdef_uv_sec_strength[0], 0); + + return 0; + } + + fb(2, cdef_damping_minus_3); + fb(2, cdef_bits); + + for (i = 0; i < (1 << current->cdef_bits); i++) { + fbs(4, cdef_y_pri_strength[i], 1, i); + fbs(2, cdef_y_sec_strength[i], 1, i); + + if (priv->num_planes > 1) { + fbs(4, cdef_uv_pri_strength[i], 1, i); + fbs(2, cdef_uv_sec_strength[i], 1, i); + } + } + + return 0; +} + +static int FUNC(lr_params)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) +{ + CodedBitstreamAV1Context *priv = ctx->priv_data; + const AV1RawSequenceHeader *seq = priv->sequence_header; + int uses_lr, uses_chroma_lr; + int i, err; + + if (priv->all_lossless || current->allow_intrabc || + !seq->enable_restoration) { + return 0; + } + + uses_lr = uses_chroma_lr = 0; + for (i = 0; i < priv->num_planes; i++) { + fbs(2, lr_type[i], 1, i); + + if (current->lr_type[i] != 0) { + uses_lr = 1; + if (i > 0) + uses_chroma_lr = 1; + } + } + + if (uses_lr) { + if (seq->use_128x128_superblock) + increment(lr_unit_shift, 1, 2); + else + increment(lr_unit_shift, 0, 2); + + if(seq->color_config.subsampling_x && + seq->color_config.subsampling_y && uses_chroma_lr) { + fb(1, lr_uv_shift); + } else { + infer(lr_uv_shift, 0); + } + } + + return 0; +} + +static int FUNC(read_tx_mode)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) +{ + CodedBitstreamAV1Context *priv = ctx->priv_data; + int err; + + if (priv->coded_lossless) + infer(tx_mode, 0); + else + increment(tx_mode, 1, 2); + + return 0; +} + +static int FUNC(frame_reference_mode)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) +{ + int err; + + if (current->frame_type == AV1_FRAME_INTRA_ONLY || + current->frame_type == AV1_FRAME_KEY) + infer(reference_select, 0); + else + flag(reference_select); + + return 0; +} + +static int FUNC(skip_mode_params)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) +{ + CodedBitstreamAV1Context *priv = ctx->priv_data; + const AV1RawSequenceHeader *seq = priv->sequence_header; + int skip_mode_allowed; + int err; + + if (current->frame_type == AV1_FRAME_KEY || + current->frame_type == AV1_FRAME_INTRA_ONLY || + !current->reference_select || !seq->enable_order_hint) { + skip_mode_allowed = 0; + } else { + int forward_idx, backward_idx; + int forward_hint, backward_hint; + int ref_hint, dist, i; + + forward_idx = -1; + backward_idx = -1; + for (i = 0; i < AV1_REFS_PER_FRAME; i++) { + ref_hint = priv->ref[i].order_hint; + dist = cbs_av1_get_relative_dist(seq, ref_hint, + current->order_hint); + if (dist < 0) { + if (forward_idx < 0 || + cbs_av1_get_relative_dist(seq, ref_hint, + forward_hint) > 0) { + forward_idx = i; + forward_hint = ref_hint; + } + } else if (dist > 0) { + if (backward_idx < 0 || + cbs_av1_get_relative_dist(seq, ref_hint, + backward_hint) < 0) { + backward_idx = i; + backward_hint = ref_hint; + } + } + } + + if (forward_idx < 0) { + skip_mode_allowed = 0; + } else if (backward_idx >= 0) { + skip_mode_allowed = 1; + // Frames for skip mode are forward_idx and backward_idx. + } else { + int second_forward_idx; + int second_forward_hint; + + second_forward_idx = -1; + for (i = 0; i < AV1_REFS_PER_FRAME; i++) { + ref_hint = priv->ref[i].order_hint; + if (cbs_av1_get_relative_dist(seq, ref_hint, + forward_hint) < 0) { + if (second_forward_idx < 0 || + cbs_av1_get_relative_dist(seq, ref_hint, + second_forward_hint) > 0) { + second_forward_idx = i; + second_forward_hint = ref_hint; + } + } + } + + if (second_forward_idx < 0) { + skip_mode_allowed = 0; + } else { + skip_mode_allowed = 1; + // Frames for skip mode are forward_idx and second_forward_idx. + } + } + } + + if (skip_mode_allowed) + flag(skip_mode_present); + else + infer(skip_mode_present, 0); + + return 0; +} + +static int FUNC(global_motion_param)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current, + int type, int ref, int idx) +{ + uint32_t abs_bits, prec_bits, num_syms; + int err; + + if (idx < 2) { + if (type == AV1_WARP_MODEL_TRANSLATION) { + abs_bits = AV1_GM_ABS_TRANS_ONLY_BITS - !current->allow_high_precision_mv; + prec_bits = AV1_GM_TRANS_ONLY_PREC_BITS - !current->allow_high_precision_mv; + } else { + abs_bits = AV1_GM_ABS_TRANS_BITS; + prec_bits = AV1_GM_TRANS_PREC_BITS; + } + } else { + abs_bits = AV1_GM_ABS_ALPHA_BITS; + prec_bits = AV1_GM_ALPHA_PREC_BITS; + } + + num_syms = 2 * (1 << abs_bits) + 1; + subexp(gm_params[ref][idx], num_syms, 2, ref, idx); + + // Actual gm_params value is not reconstructed here. + (void)prec_bits; + + return 0; +} + +static int FUNC(global_motion_params)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) +{ + int ref, type; + int err; + + if (current->frame_type == AV1_FRAME_KEY || + current->frame_type == AV1_FRAME_INTRA_ONLY) + return 0; + + for (ref = AV1_REF_FRAME_LAST; ref <= AV1_REF_FRAME_ALTREF; ref++) { + flags(is_global[ref], 1, ref); + if (current->is_global[ref]) { + flags(is_rot_zoom[ref], 1, ref); + if (current->is_rot_zoom[ref]) { + type = AV1_WARP_MODEL_ROTZOOM; + } else { + flags(is_translation[ref], 1, ref); + type = current->is_translation[ref] ? AV1_WARP_MODEL_TRANSLATION + : AV1_WARP_MODEL_AFFINE; + } + } else { + type = AV1_WARP_MODEL_IDENTITY; + } + + if (type >= AV1_WARP_MODEL_ROTZOOM) { + CHECK(FUNC(global_motion_param)(ctx, rw, current, type, ref, 2)); + CHECK(FUNC(global_motion_param)(ctx, rw, current, type, ref, 3)); + if (type == AV1_WARP_MODEL_AFFINE) { + CHECK(FUNC(global_motion_param)(ctx, rw, current, type, ref, 4)); + CHECK(FUNC(global_motion_param)(ctx, rw, current, type, ref, 5)); + } else { + // gm_params[ref][4] = -gm_params[ref][3] + // gm_params[ref][5] = gm_params[ref][2] + } + } + if (type >= AV1_WARP_MODEL_TRANSLATION) { + CHECK(FUNC(global_motion_param)(ctx, rw, current, type, ref, 0)); + CHECK(FUNC(global_motion_param)(ctx, rw, current, type, ref, 1)); + } + } + + return 0; +} + +static int FUNC(film_grain_params)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) +{ + CodedBitstreamAV1Context *priv = ctx->priv_data; + const AV1RawSequenceHeader *seq = priv->sequence_header; + int num_pos_luma, num_pos_chroma; + int i, err; + + if (!seq->film_grain_params_present || + (!current->show_frame && !current->showable_frame)) + return 0; + + flag(apply_grain); + + if (!current->apply_grain) + return 0; + + fb(16, grain_seed); + + if (current->frame_type == AV1_FRAME_INTER) + flag(update_grain); + else + infer(update_grain, 1); + + if (!current->update_grain) { + fb(3, film_grain_params_ref_idx); + return 0; + } + + fb(4, num_y_points); + for (i = 0; i < current->num_y_points; i++) { + fbs(8, point_y_value[i], 1, i); + fbs(8, point_y_scaling[i], 1, i); + } + + if (seq->color_config.mono_chrome) + infer(chroma_scaling_from_luma, 0); + else + flag(chroma_scaling_from_luma); + + if (seq->color_config.mono_chrome || + current->chroma_scaling_from_luma || + (seq->color_config.subsampling_x == 1 && + seq->color_config.subsampling_y == 1 && + current->num_y_points == 0)) { + infer(num_cb_points, 0); + infer(num_cr_points, 0); + } else { + fb(4, num_cb_points); + for (i = 0; i < current->num_cb_points; i++) { + fbs(8, point_cb_value[i], 1, i); + fbs(8, point_cb_scaling[i], 1, i); + } + fb(4, num_cr_points); + for (i = 0; i < current->num_cr_points; i++) { + fbs(8, point_cr_value[i], 1, i); + fbs(8, point_cr_scaling[i], 1, i); + } + } + + fb(2, grain_scaling_minus_8); + fb(2, ar_coeff_lag); + num_pos_luma = 2 * current->ar_coeff_lag * (current->ar_coeff_lag + 1); + if (current->num_y_points) { + num_pos_chroma = num_pos_luma + 1; + for (i = 0; i < num_pos_luma; i++) + fbs(8, ar_coeffs_y_plus_128[i], 1, i); + } else { + num_pos_chroma = num_pos_luma; + } + if (current->chroma_scaling_from_luma || current->num_cb_points) { + for (i = 0; i < num_pos_chroma; i++) + fbs(8, ar_coeffs_cb_plus_128[i], 1, i); + } + if (current->chroma_scaling_from_luma || current->num_cr_points) { + for (i = 0; i < num_pos_chroma; i++) + fbs(8, ar_coeffs_cr_plus_128[i], 1, i); + } + fb(2, ar_coeff_shift_minus_6); + fb(2, grain_scale_shift); + if (current->num_cb_points) { + fb(8, cb_mult); + fb(8, cb_luma_mult); + fb(9, cb_offset); + } + if (current->num_cr_points) { + fb(8, cr_mult); + fb(8, cr_luma_mult); + fb(9, cr_offset); + } + + flag(overlap_flag); + flag(clip_to_restricted_range); + + return 0; +} + +static int FUNC(uncompressed_header)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) +{ + CodedBitstreamAV1Context *priv = ctx->priv_data; + const AV1RawSequenceHeader *seq; + int id_len, all_frames, frame_is_intra, order_hint_bits; + int i, err; + + if (!priv->sequence_header) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "No sequence header available: " + "unable to decode frame header.\n"); + return AVERROR_INVALIDDATA; + } + seq = priv->sequence_header; + + id_len = seq->additional_frame_id_length_minus_1 + + seq->delta_frame_id_length_minus_2 + 3; + all_frames = (1 << AV1_NUM_REF_FRAMES) - 1; + + if (seq->reduced_still_picture_header) { + infer(show_existing_frame, 0); + infer(frame_type, AV1_FRAME_KEY); + infer(show_frame, 1); + infer(showable_frame, 0); + frame_is_intra = 1; + + } else { + flag(show_existing_frame); + + if (current->show_existing_frame) { + AV1ReferenceFrameState *frame; + + fb(3, frame_to_show_map_idx); + frame = &priv->ref[current->frame_to_show_map_idx]; + + if (seq->decoder_model_info_present_flag && + !seq->timing_info.equal_picture_interval) { + fb(seq->decoder_model_info.frame_presentation_time_length_minus_1 + 1, + frame_presentation_time); + } + + if (seq->frame_id_numbers_present_flag) + fb(id_len, display_frame_id); + + if (frame->frame_type == AV1_FRAME_KEY) + infer(refresh_frame_flags, all_frames); + else + infer(refresh_frame_flags, 0); + + return 0; + } + + fb(2, frame_type); + frame_is_intra = (current->frame_type == AV1_FRAME_INTRA_ONLY || + current->frame_type == AV1_FRAME_KEY); + + flag(show_frame); + if (current->show_frame && + seq->decoder_model_info_present_flag && + !seq->timing_info.equal_picture_interval) { + fb(seq->decoder_model_info.frame_presentation_time_length_minus_1 + 1, + frame_presentation_time); + } + if (current->show_frame) + infer(showable_frame, current->frame_type != AV1_FRAME_KEY); + else + flag(showable_frame); + + if (current->frame_type == AV1_FRAME_SWITCH || + (current->frame_type == AV1_FRAME_KEY && current->show_frame)) + infer(error_resilient_mode, 1); + else + flag(error_resilient_mode); + } + + if (current->frame_type == AV1_FRAME_KEY && current->show_frame) { + for (i = 0; i < AV1_NUM_REF_FRAMES; i++) { + priv->ref[i].valid = 0; + priv->ref[i].order_hint = 0; + } + } + + flag(disable_cdf_update); + + if (seq->seq_force_screen_content_tools == + AV1_SELECT_SCREEN_CONTENT_TOOLS) { + flag(allow_screen_content_tools); + } else { + infer(allow_screen_content_tools, + seq->seq_force_screen_content_tools); + } + if (current->allow_screen_content_tools) { + if (seq->seq_force_integer_mv == AV1_SELECT_INTEGER_MV) + flag(force_integer_mv); + else + infer(force_integer_mv, seq->seq_force_integer_mv); + } else { + infer(force_integer_mv, 0); + } + + if (seq->frame_id_numbers_present_flag) + fb(id_len, current_frame_id); + else + infer(current_frame_id, 0); + + if (current->frame_type == AV1_FRAME_SWITCH) + infer(frame_size_override_flag, 1); + else if(seq->reduced_still_picture_header) + infer(frame_size_override_flag, 0); + else + flag(frame_size_override_flag); + + order_hint_bits = + seq->enable_order_hint ? seq->order_hint_bits_minus_1 + 1 : 0; + if (order_hint_bits > 0) + fb(order_hint_bits, order_hint); + else + infer(order_hint, 0); + + if (frame_is_intra || current->error_resilient_mode) + infer(primary_ref_frame, AV1_PRIMARY_REF_NONE); + else + fb(3, primary_ref_frame); + + if (seq->decoder_model_info_present_flag) { + flag(buffer_removal_time_present_flag); + if (current->buffer_removal_time_present_flag) { + for (i = 0; i <= seq->operating_points_cnt_minus_1; i++) { + if (seq->decoder_model_present_for_this_op[i]) { + int op_pt_idc = seq->operating_point_idc[i]; + int in_temporal_layer = (op_pt_idc >> priv->temporal_id ) & 1; + int in_spatial_layer = (op_pt_idc >> (priv->spatial_id + 8)) & 1; + if (seq->operating_point_idc[i] == 0 || + in_temporal_layer || in_spatial_layer) { + fbs(seq->decoder_model_info.buffer_removal_time_length_minus_1 + 1, + buffer_removal_time[i], 1, i); + } + } + } + } + } + + if (current->frame_type == AV1_FRAME_SWITCH || + (current->frame_type == AV1_FRAME_KEY && current->show_frame)) + infer(refresh_frame_flags, all_frames); + else + fb(8, refresh_frame_flags); + + if (!frame_is_intra || current->refresh_frame_flags != all_frames) { + if (current->error_resilient_mode && seq->enable_order_hint) { + for (i = 0; i < AV1_NUM_REF_FRAMES; i++) { + fbs(order_hint_bits, ref_order_hint[i], 1, i); + if (current->ref_order_hint[i] != priv->ref[i].order_hint) + priv->ref[i].valid = 0; + } + } + } + + if (current->frame_type == AV1_FRAME_KEY || + current->frame_type == AV1_FRAME_INTRA_ONLY) { + CHECK(FUNC(frame_size)(ctx, rw, current)); + CHECK(FUNC(render_size)(ctx, rw, current)); + + if (current->allow_screen_content_tools && + priv->upscaled_width == priv->frame_width) + flag(allow_intrabc); + else + infer(allow_intrabc, 0); + + } else { + if (!seq->enable_order_hint) { + infer(frame_refs_short_signaling, 0); + } else { + flag(frame_refs_short_signaling); + if (current->frame_refs_short_signaling) { + fb(3, last_frame_idx); + fb(3, golden_frame_idx); + + for (i = 0; i < AV1_REFS_PER_FRAME; i++) { + if (i == 0) + infer(ref_frame_idx[i], current->last_frame_idx); + else if (i == AV1_REF_FRAME_GOLDEN - + AV1_REF_FRAME_LAST) + infer(ref_frame_idx[i], current->golden_frame_idx); + else + infer(ref_frame_idx[i], -1); + } + } + } + + for (i = 0; i < AV1_REFS_PER_FRAME; i++) { + if (!current->frame_refs_short_signaling) + fbs(3, ref_frame_idx[i], 1, i); + if (seq->frame_id_numbers_present_flag) { + fb(seq->delta_frame_id_length_minus_2 + 2, + delta_frame_id_minus1); + } + } + + if (current->frame_size_override_flag && + !current->error_resilient_mode) { + CHECK(FUNC(frame_size_with_refs)(ctx, rw, current)); + } else { + CHECK(FUNC(frame_size)(ctx, rw, current)); + CHECK(FUNC(render_size)(ctx, rw, current)); + } + + if (current->force_integer_mv) + infer(allow_high_precision_mv, 0); + else + flag(allow_high_precision_mv); + + CHECK(FUNC(interpolation_filter)(ctx, rw, current)); + + flag(is_motion_mode_switchable); + + if (current->error_resilient_mode || + !seq->enable_ref_frame_mvs) + infer(use_ref_frame_mvs, 0); + else + flag(use_ref_frame_mvs); + + infer(allow_intrabc, 0); + } + + if (!frame_is_intra) { + for (i = 0; i < AV1_REFS_PER_FRAME; i++) { + int ref_frame, ref_hint; + ref_frame = AV1_REF_FRAME_LAST + i; + ref_hint = priv->ref[current->ref_frame_idx[i]].order_hint; + if (seq->enable_order_hint) { + priv->ref[ref_frame].sign_bias = 0; + } else { + priv->ref[ref_frame].sign_bias = + cbs_av1_get_relative_dist(seq, ref_hint, + current->order_hint) > 0; + } + } + } + + if (seq->reduced_still_picture_header || current->disable_cdf_update) + infer(disable_frame_end_update_cdf, 1); + else + flag(disable_frame_end_update_cdf); + + // Init non-coeff CDFs / load previous CDFs. + + CHECK(FUNC(tile_info)(ctx, rw, current)); + + CHECK(FUNC(quantization_params)(ctx, rw, current)); + + CHECK(FUNC(segmentation_params)(ctx, rw, current)); + + CHECK(FUNC(delta_q_params)(ctx, rw, current)); + + CHECK(FUNC(delta_lf_params)(ctx, rw, current)); + + // Init coeff CDFs / load previous segments. + + priv->coded_lossless = 1; + for (i = 0; i < AV1_MAX_SEGMENTS; i++) { + int qindex; + if (current->feature_enabled[i][AV1_SEG_LVL_ALT_Q]) { + qindex = (current->base_q_idx + + current->feature_value[i][AV1_SEG_LVL_ALT_Q]); + } else { + qindex = current->base_q_idx; + } + qindex = av_clip_uintp2(qindex, 8); + + if (qindex || current->delta_q_y_dc || + current->delta_q_u_ac || current->delta_q_u_dc || + current->delta_q_v_ac || current->delta_q_v_dc) { + priv->coded_lossless = 0; + } + } + priv->all_lossless = priv->coded_lossless && + priv->frame_width == priv->upscaled_width; + + CHECK(FUNC(loop_filter_params)(ctx, rw, current)); + + CHECK(FUNC(cdef_params)(ctx, rw, current)); + + CHECK(FUNC(lr_params)(ctx, rw, current)); + + CHECK(FUNC(read_tx_mode)(ctx, rw, current)); + + CHECK(FUNC(frame_reference_mode)(ctx, rw, current)); + + CHECK(FUNC(skip_mode_params)(ctx, rw, current)); + + if (frame_is_intra || current->error_resilient_mode || + !seq->enable_warped_motion) + infer(allow_warped_motion, 0); + else + flag(allow_warped_motion); + + flag(reduced_tx_set); + + CHECK(FUNC(global_motion_params)(ctx, rw, current)); + + CHECK(FUNC(film_grain_params)(ctx, rw, current)); + + for (i = 0; i < AV1_NUM_REF_FRAMES; i++) { + if (current->refresh_frame_flags & (1 << i)) { + priv->ref[i] = (AV1ReferenceFrameState) { + .valid = 1, + .frame_type = current->frame_type, + .upscaled_width = priv->upscaled_width, + .frame_width = priv->frame_width, + .frame_height = priv->frame_height, + .render_width = priv->render_width, + .render_height = priv->render_height, + .order_hint = current->order_hint, + .subsampling_x = seq->color_config.subsampling_x, + .subsampling_y = seq->color_config.subsampling_y, + .bit_depth = priv->bit_depth, + }; + } + } + + av_log(ctx->log_ctx, AV_LOG_DEBUG, "Frame %d: Size %dx%d " + "Upscaled %d Render %dx%d Subsample %dx%d " + "BitDepth %d Tiles %dx%d.\n", current->order_hint, + priv->frame_width, priv->frame_height, priv->upscaled_width, + priv->render_width, priv->render_height, + seq->color_config.subsampling_x + 1, + seq->color_config.subsampling_y + 1, priv->bit_depth, + priv->tile_rows, priv->tile_cols); + + return 0; +} + +static int FUNC(frame_header_obu)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) +{ + CodedBitstreamAV1Context *priv = ctx->priv_data; + int err; + + HEADER("Frame Header"); + + if (priv->seen_frame_header) { + // Nothing to do. + } else { + priv->seen_frame_header = 1; + + CHECK(FUNC(uncompressed_header)(ctx, rw, current)); + + if (current->show_existing_frame) { + priv->seen_frame_header = 0; + } else { + priv->seen_frame_header = 1; + } + } + + return 0; +} + +static int FUNC(tile_group_obu)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawTileGroup *current) +{ + CodedBitstreamAV1Context *priv = ctx->priv_data; + int num_tiles, tile_bits; + int err; + + HEADER("Tile Group"); + + num_tiles = priv->tile_cols * priv->tile_rows; + if (num_tiles > 1) + flag(tile_start_and_end_present_flag); + else + infer(tile_start_and_end_present_flag, 0); + + if (num_tiles == 1 || !current->tile_start_and_end_present_flag) { + infer(tg_start, 0); + infer(tg_end, num_tiles - 1); + } else { + tile_bits = cbs_av1_tile_log2(1, priv->tile_cols) + + cbs_av1_tile_log2(1, priv->tile_rows); + fb(tile_bits, tg_start); + fb(tile_bits, tg_end); + } + + CHECK(FUNC(byte_alignment)(ctx, rw)); + + // Reset header for next frame. + if (current->tg_end == num_tiles - 1) + priv->seen_frame_header = 0; + + // Tile data follows. + + return 0; +} + +static int FUNC(frame_obu)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrame *current) +{ + int err; + + CHECK(FUNC(frame_header_obu)(ctx, rw, ¤t->header)); + + CHECK(FUNC(byte_alignment)(ctx, rw)); + + CHECK(FUNC(tile_group_obu)(ctx, rw, ¤t->tile_group)); + + return 0; +} + +static int FUNC(tile_list_obu)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawTileList *current) +{ + int err; + + fb(8, output_frame_width_in_tiles_minus_1); + fb(8, output_frame_height_in_tiles_minus_1); + + fb(16, tile_count_minus_1); + + // Tile data follows. + + return 0; +} + +static int FUNC(metadata_hdr_cll)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawMetadataHDRCLL *current) +{ + int err; + + fb(16, max_cll); + fb(16, max_fall); + + return 0; +} + +static int FUNC(metadata_hdr_mdcv)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawMetadataHDRMDCV *current) +{ + int err, i; + + for (i = 0; i < 3; i++) { + fcs(16, primary_chromaticity_x[i], 0, 50000, 1, i); + fcs(16, primary_chromaticity_y[i], 0, 50000, 1, i); + } + + fc(16, white_point_chromaticity_x, 0, 50000); + fc(16, white_point_chromaticity_y, 0, 50000); + + fc(32, luminance_max, 1, MAX_UINT_BITS(32)); + fc(32, luminance_min, 0, current->luminance_max >> 6); + + return 0; +} + +static int FUNC(metadata_scalability)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawMetadataScalability *current) +{ + // TODO: scalability metadata. + + return AVERROR_PATCHWELCOME; +} + +static int FUNC(metadata_itut_t35)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawMetadataITUTT35 *current) +{ + int err; + size_t i; + + fb(8, itu_t_t35_country_code); + if (current->itu_t_t35_country_code == 0xff) + fb(8, itu_t_t35_country_code_extension_byte); + +#ifdef READ + // The payload runs up to the start of the trailing bits, but there might + // be arbitrarily many trailing zeroes so we need to read through twice. + { + GetBitContext tmp = *rw; + current->payload_size = 0; + for (i = 0; get_bits_left(rw) >= 8; i++) { + if (get_bits(rw, 8)) + current->payload_size = i; + } + *rw = tmp; + } + + current->payload_ref = av_buffer_alloc(current->payload_size); + if (!current->payload_ref) + return AVERROR(ENOMEM); + current->payload = current->payload_ref->data; +#endif + + for (i = 0; i < current->payload_size; i++) + xf(8, itu_t_t35_payload_bytes[i], current->payload[i], + 0x00, 0xff, 1, i); + + return 0; +} + +static int FUNC(metadata_timecode)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawMetadataTimecode *current) +{ + int err; + + fb(5, counting_type); + flag(full_timestamp_flag); + flag(discontinuity_flag); + flag(cnt_dropped_flag); + fb(9, n_frames); + + if (current->full_timestamp_flag) { + fb(6, seconds_value); + fb(6, minutes_value); + fb(5, hours_value); + } else { + flag(seconds_flag); + if (current->seconds_flag) { + fb(6, seconds_value); + flag(minutes_flag); + if (current->minutes_flag) { + fb(6, minutes_value); + flag(hours_flag); + if (current->hours_flag) + fb(5, hours_value); + } + } + } + + fb(5, time_offset_length); + if (current->time_offset_length > 0) + fb(current->time_offset_length, time_offset_value); + + return 0; +} + +static int FUNC(metadata_obu)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawMetadata *current) +{ + int err; + + leb128(metadata_type); + + switch (current->metadata_type) { + case AV1_METADATA_TYPE_HDR_CLL: + CHECK(FUNC(metadata_hdr_cll)(ctx, rw, ¤t->hdr_cll)); + break; + case AV1_METADATA_TYPE_HDR_MDCV: + CHECK(FUNC(metadata_hdr_mdcv)(ctx, rw, ¤t->hdr_mdcv)); + break; + case AV1_METADATA_TYPE_SCALABILITY: + CHECK(FUNC(metadata_scalability)(ctx, rw, ¤t->scalability)); + break; + case AV1_METADATA_TYPE_ITUT_T35: + CHECK(FUNC(metadata_itut_t35)(ctx, rw, ¤t->itut_t35)); + break; + case AV1_METADATA_TYPE_TIMECODE: + CHECK(FUNC(metadata_timecode)(ctx, rw, ¤t->timecode)); + break; + default: + // Unknown metadata type. + return AVERROR_PATCHWELCOME; + } + + return 0; +} diff --git a/libavcodec/cbs_internal.h b/libavcodec/cbs_internal.h index 172b8a2515..f074a4affc 100644 --- a/libavcodec/cbs_internal.h +++ b/libavcodec/cbs_internal.h @@ -86,6 +86,7 @@ int ff_cbs_write_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc, #define MAX_UINT_BITS(length) ((UINT64_C(1) << (length)) - 1) +extern const CodedBitstreamType ff_cbs_type_av1; extern const CodedBitstreamType ff_cbs_type_h264; extern const CodedBitstreamType ff_cbs_type_h265; extern const CodedBitstreamType ff_cbs_type_mpeg2;