/* GStreamer
 *  Copyright (C) 2022 Intel Corporation
 *     Author: He Junyan <junyan.he@intel.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the0
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

/**
 * SECTION:gstav1bitwriter
 * @title: GstAV1BitWriter
 * @short_description: Functions for writing bitstream headers
 *
 * Since: 1.28
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "gstav1bitwriter.h"
#include <gst/base/gstbitwriter.h>

#ifndef GST_DISABLE_GST_DEBUG
#define GST_CAT_DEFAULT gst_av1_debug_category_get()
static GstDebugCategory *
gst_av1_debug_category_get (void)
{
  static gsize cat_gonce = 0;

  if (g_once_init_enter (&cat_gonce)) {
    GstDebugCategory *cat = NULL;

    GST_DEBUG_CATEGORY_INIT (cat, "bitwriter_av1", 0, "av1 bitwriter library");

    g_once_init_leave (&cat_gonce, (gsize) cat);
  }

  return (GstDebugCategory *) cat_gonce;
}
#endif /* GST_DISABLE_GST_DEBUG */

#define WRITE_BITS_UNCHECK(bw, val, nbits)                                    \
  (nbits <= 8 ? gst_bit_writer_put_bits_uint8 (bw, val, nbits) :              \
   (nbits <= 16 ? gst_bit_writer_put_bits_uint16 (bw, val, nbits) :           \
    (nbits <= 32 ? gst_bit_writer_put_bits_uint32 (bw, val, nbits) :          \
     FALSE)))

#define WRITE_BITS(bw, val, nbits)                                            \
  if (!WRITE_BITS_UNCHECK (bw, val, nbits)) {                                 \
    g_warning ("Unsupported bit size: %u", nbits);                            \
    have_space = FALSE;                                                       \
    goto error;                                                               \
  }

static guint
_av1_uleb_size_in_bytes (guint64 value)
{
  guint size = 0;

  do {
    ++size;
  } while ((value >>= 7) != 0);
  return size;
}

static gboolean
_av1_encode_uleb (guint64 value, guint available,
    guint8 * coded_value, guint coded_size)
{
  const guint kMaximumLeb128Size = 8;
  const guint leb_size = _av1_uleb_size_in_bytes (value);
  const guint64 kMaximumLeb128Value = 0xffffffff /* 4294967295U */ ;
  guint i;

  g_assert (coded_size >= leb_size);

  if (value > kMaximumLeb128Value || coded_size > kMaximumLeb128Size ||
      coded_size > available || !coded_value) {
    return FALSE;
  }

  if (leb_size == coded_size) {
    for (i = 0; i < leb_size; i++) {
      guint8 byte = value & 0x7f;
      value >>= 7;

      if (value != 0)
        byte |= 0x80;           // Signal that more bytes follow.

      *(coded_value + i) = byte;
    }
  } else {
    memset (coded_value, 0, coded_size);

    i = 0;
    do {
      *(coded_value + i) = value & 0x7fU;
      value >>= 7;
      i++;
    } while (value);

    for (i = 0; i < coded_size - 1; i++)
      *(coded_value + i) |= 0x80;
  }

  return TRUE;
}

/* 4.10.3
 *
 * Variable length unsigned n-bit number appearing directly in the
 * bitstream */
static gboolean
_av1_write_uvlc (GstBitWriter * bw, guint32 value, gboolean * space)
{
  gboolean have_space = TRUE;
  gint64 shift_val = value;
  gint32 leading_zeroes = 1;

  value++;

  g_assert (shift_val > 0);

  while (shift_val >>= 1)
    leading_zeroes += 2;

  WRITE_BITS (bw, 0, leading_zeroes >> 1);
  WRITE_BITS (bw, value, (leading_zeroes + 1) >> 1);

  *space = TRUE;
  return TRUE;

error:
  *space = have_space;
  return FALSE;
}

static gint
_av1_helper_msb (guint n)
{
  int log = 0;
  guint value = n;
  int i;

  g_assert (n != 0);

  for (i = 4; i >= 0; --i) {
    const gint shift = (1 << i);
    const guint x = value >> shift;

    if (x != 0) {
      value = x;
      log += shift;
    }
  }
  return log;
}

static gboolean
_av1_write_uniform (GstBitWriter * bw, guint32 max_value, guint32 value,
    gboolean * space)
{
  gboolean have_space = TRUE;
  const gint l = max_value ? _av1_helper_msb (max_value) + 1 : 0;
  const gint m = (1 << l) - max_value;

  if (l == 0)
    goto success;

  if (value < m) {
    WRITE_BITS (bw, value, l - 1);
  } else {
    WRITE_BITS (bw, m + ((value - m) >> 1), l - 1);
    WRITE_BITS (bw, (value - m) & 1, 1);
  }

success:
  *space = TRUE;
  return TRUE;

error:
  *space = have_space;
  return FALSE;
}

/* 5.9.13
 *
 * Delta quantizer */
static gboolean
_av1_write_delta_q (GstBitWriter * bw, gint32 delta_q, gboolean * space)
{
  gboolean have_space = TRUE;

  if (delta_q != 0) {
    WRITE_BITS (bw, 1, 1);
    WRITE_BITS (bw, delta_q, 7);
  } else {
    WRITE_BITS (bw, 0, 1);
  }

  *space = TRUE;
  return TRUE;

error:
  *space = have_space;
  return FALSE;
}

/* 4.10.6
 *
 * su(n) */
static gboolean
_av1_write_su (GstBitWriter * bw, gint32 val, guint n, gboolean * space)
{
  gboolean have_space = TRUE;

  g_assert (n < 31);
  WRITE_BITS (bw, val, n + 1);

  *space = TRUE;
  return TRUE;

error:
  *space = have_space;
  return FALSE;
}

/* 5.9.16 Tile size calculation
 *
 * returns the smallest value for k such that blkSize << k is greater
 * than or equal to target */
static gint
_av1_helper_tile_log2 (gint blkSize, gint target)
{
  gint k;

  for (k = 0; (blkSize << k) < target; k++);

  return k;
}

static gboolean
_av1_write_primitive_quniform (GstBitWriter * bw, guint16 n, guint16 v,
    gboolean * space)
{
  gboolean have_space = TRUE;
  const gint l = _av1_helper_msb (n) + 1;
  const gint m = (1 << l) - n;

  if (n <= 1)
    goto success;

  if (v < m) {
    WRITE_BITS (bw, v, l - 1);
  } else {
    WRITE_BITS (bw, m + ((v - m) >> 1), l - 1);
    WRITE_BITS (bw, (v - m) & 1, 1);
  }

success:
  *space = TRUE;
  return TRUE;

error:
  *space = have_space;
  return FALSE;
}

static gboolean
_av1_write_primitive_subexpfin (GstBitWriter * bw, guint16 n, guint16 k,
    guint16 v, gboolean * space)
{
  gboolean have_space = TRUE;
  gint i = 0;
  gint mk = 0;

  while (1) {
    gint b = (i ? k + i - 1 : k);
    gint a = (1 << b);

    if (n <= mk + 3 * a) {
      if (!_av1_write_primitive_quniform (bw, n - mk, v - mk, space))
        goto error;

      break;
    } else {
      gint t = (v >= mk + a);
      WRITE_BITS (bw, t, 1);
      if (t) {
        i = i + 1;
        mk += a;
      } else {
        WRITE_BITS (bw, v - mk, b);
        break;
      }
    }
  }

  *space = TRUE;
  return TRUE;

error:
  *space = have_space;
  return FALSE;
}

/* Recenters a non-negative literal v around a reference r */
static guint16
_av1_helper_recenter_nonneg (guint16 r, guint16 v)
{
  if (v > (r << 1))
    return v;
  else if (v >= r)
    return ((v - r) << 1);
  else
    return ((r - v) << 1) - 1;
}

/* Recenters a non-negative literal v in [0, n-1] around
   a reference r also in [0, n-1] */
static guint16
_av1_helper_recenter_finite_nonneg (guint16 n, guint16 r, guint16 v)
{
  if ((r << 1) <= n) {
    return _av1_helper_recenter_nonneg (r, v);
  } else {
    return _av1_helper_recenter_nonneg (n - 1 - r, n - 1 - v);
  }
}

static gboolean
_av1_write_primitive_refsubexpfin (GstBitWriter * bw,
    guint16 n, guint16 k, guint16 ref, guint16 v, gboolean * space)
{
  gboolean have_space = TRUE;

  if (!_av1_write_primitive_subexpfin (bw, n, k,
          _av1_helper_recenter_finite_nonneg (n, ref, v), space))
    goto error;

  *space = TRUE;
  return TRUE;

error:
  *space = have_space;
  return FALSE;
}

static gboolean
_av1_write_signed_primitive_refsubexpfin (GstBitWriter * bw,
    guint16 n, guint16 k, gint16 ref, gint16 v, gboolean * space)
{
  gboolean have_space = TRUE;
  const guint16 scaled_n = (n << 1) - 1;
  ref += n - 1;
  v += n - 1;

  if (!_av1_write_primitive_refsubexpfin (bw, scaled_n, k, ref, v, space))
    goto error;

  *space = TRUE;
  return TRUE;

error:
  *space = have_space;
  return FALSE;
}

static gboolean
_av1_seq_level_idx_is_valid (GstAV1SeqLevels seq_level_idx)
{
  return seq_level_idx == GST_AV1_SEQ_LEVEL_MAX
      || (seq_level_idx < GST_AV1_SEQ_LEVELS
      /* The following levels are currently undefined. */
      && seq_level_idx != GST_AV1_SEQ_LEVEL_2_2
      && seq_level_idx != GST_AV1_SEQ_LEVEL_2_3
      && seq_level_idx != GST_AV1_SEQ_LEVEL_3_2
      && seq_level_idx != GST_AV1_SEQ_LEVEL_3_3
      && seq_level_idx != GST_AV1_SEQ_LEVEL_4_2
      && seq_level_idx != GST_AV1_SEQ_LEVEL_4_3
      && seq_level_idx != GST_AV1_SEQ_LEVEL_7_0
      && seq_level_idx != GST_AV1_SEQ_LEVEL_7_1
      && seq_level_idx != GST_AV1_SEQ_LEVEL_7_2
      && seq_level_idx != GST_AV1_SEQ_LEVEL_7_3);
}

static gboolean
_av1_bit_writer_timing_info (const GstAV1TimingInfo * timing_info,
    GstBitWriter * bw, gboolean * space)
{
  gboolean have_space = TRUE;

  GST_DEBUG ("writing timing info");

  if (timing_info->num_units_in_display_tick == 0 ||
      timing_info->time_scale == 0)
    goto error;

  WRITE_BITS (bw, timing_info->num_units_in_display_tick, 32);
  WRITE_BITS (bw, timing_info->time_scale, 32);

  WRITE_BITS (bw, timing_info->equal_picture_interval, 1);
  if (timing_info->equal_picture_interval) {
    if (timing_info->num_ticks_per_picture_minus_1 == G_MAXUINT)
      goto error;

    if (!_av1_write_uvlc (bw, timing_info->num_ticks_per_picture_minus_1,
            &have_space))
      goto error;
  }

  *space = TRUE;
  return TRUE;

error:
  GST_WARNING ("failed to write timing info");
  *space = have_space;
  return FALSE;
}

/* 5.5.4 */
static gboolean
_av1_bit_writer_decoder_model_info (const GstAV1DecoderModelInfo *
    decoder_model_info, GstBitWriter * bw, gboolean * space)
{
  gboolean have_space = TRUE;

  GST_DEBUG ("writing decoder model info");

  WRITE_BITS (bw, decoder_model_info->buffer_delay_length_minus_1, 5);
  WRITE_BITS (bw, decoder_model_info->num_units_in_decoding_tick, 32);
  WRITE_BITS (bw, decoder_model_info->buffer_removal_time_length_minus_1, 5);
  WRITE_BITS (bw, decoder_model_info->frame_presentation_time_length_minus_1,
      5);

  *space = TRUE;
  return TRUE;
error:
  GST_WARNING ("failed to write decoder model info");
  *space = have_space;
  return FALSE;
}

/* 5.5.5 */
static gboolean
_av1_bit_writer_operating_parameters_info (const GstAV1SequenceHeaderOBU *
    seq_header, const GstAV1OperatingPoint * op_point, GstBitWriter * bw,
    gboolean * space)
{
  gboolean have_space = TRUE;
  guint32 n = seq_header->decoder_model_info.buffer_delay_length_minus_1 + 1;

  GST_DEBUG ("writing operating parameters info");

  if (n > 32)
    goto error;

  WRITE_BITS (bw, op_point->decoder_buffer_delay, n);
  WRITE_BITS (bw, op_point->encoder_buffer_delay, n);
  WRITE_BITS (bw, op_point->low_delay_mode_flag, 1);

  *space = TRUE;
  return TRUE;

error:
  GST_WARNING ("failed to write operating parameters info");
  *space = have_space;
  return FALSE;
}

/* 5.5.2 */
static gboolean
_av1_bit_writer_color_config (const GstAV1SequenceHeaderOBU * seq_header,
    const GstAV1ColorConfig * color_config, GstBitWriter * bw, gboolean * space)
{
  gboolean have_space = TRUE;

  GST_DEBUG ("writing color config");

  WRITE_BITS (bw, color_config->high_bitdepth, 1);

  if (seq_header->seq_profile == GST_AV1_PROFILE_2
      && color_config->high_bitdepth) {
    WRITE_BITS (bw, color_config->twelve_bit, 1);
  } else if (seq_header->seq_profile > GST_AV1_PROFILE_2) {
    GST_WARNING ("Unsupported profile/bit-depth combination");
    goto error;
  }

  if (seq_header->seq_profile != GST_AV1_PROFILE_1)
    WRITE_BITS (bw, color_config->mono_chrome, 1);

  if (seq_header->num_planes != 1 && seq_header->num_planes != 3) {
    GST_WARNING ("num_planes is not correct");
    goto error;
  }
  if (!color_config->mono_chrome && seq_header->num_planes != 3) {
    GST_WARNING ("num_planes is not correct");
    goto error;
  }

  WRITE_BITS (bw, color_config->color_description_present_flag, 1);

  if (color_config->color_description_present_flag) {
    WRITE_BITS (bw, color_config->color_primaries, 8);
    WRITE_BITS (bw, color_config->transfer_characteristics, 8);
    WRITE_BITS (bw, color_config->matrix_coefficients, 8);
  }

  if (color_config->mono_chrome) {
    if (color_config->subsampling_x != 1 || color_config->subsampling_y != 1) {
      GST_WARNING ("set subsampling_x or subsampling_y wrong value");
      goto error;
    }

    WRITE_BITS (bw, color_config->color_range, 1);
    goto success;
  } else if (color_config->color_primaries == GST_AV1_CP_BT_709 &&
      color_config->transfer_characteristics == GST_AV1_TC_SRGB &&
      color_config->matrix_coefficients == GST_AV1_MC_IDENTITY) {

    if (color_config->color_range != 1) {
      GST_WARNING ("set color_range wrong value");
      goto error;
    }

    if (color_config->subsampling_x != 0 || color_config->subsampling_y != 0) {
      GST_WARNING ("set subsampling_x or subsampling_y wrong value");
      goto error;
    }

    if (!(seq_header->seq_profile == GST_AV1_PROFILE_1 ||
            (seq_header->seq_profile == GST_AV1_PROFILE_2
                && seq_header->bit_depth == 12))) {
      GST_WARNING ("sRGB colorspace not compatible with specified profile");
      goto error;
    }
  } else {
    WRITE_BITS (bw, color_config->color_range, 1);

    if (seq_header->seq_profile == GST_AV1_PROFILE_0) {
      if (color_config->subsampling_x != 1 || color_config->subsampling_y != 1) {
        GST_WARNING ("set subsampling_x or subsampling_y wrong value");
        goto error;
      }
    } else if (seq_header->seq_profile == GST_AV1_PROFILE_1) {
      if (color_config->subsampling_x != 0 || color_config->subsampling_y != 0) {
        GST_WARNING ("set subsampling_x or subsampling_y wrong value");
        goto error;
      }
    } else if (seq_header->seq_profile == GST_AV1_PROFILE_2) {
      if (seq_header->bit_depth == 12) {
        WRITE_BITS (bw, color_config->subsampling_x, 1);

        if (color_config->subsampling_x) {
          /* 422 or 420 */
          WRITE_BITS (bw, color_config->subsampling_y, 1);
        }
      }
    }

    if (color_config->subsampling_x && color_config->subsampling_y)
      WRITE_BITS (bw, color_config->chroma_sample_position, 2);
  }

  WRITE_BITS (bw, color_config->separate_uv_delta_q, 1);

  if (!(color_config->subsampling_x == 0 && color_config->subsampling_y == 0) &&
      !(color_config->subsampling_x == 1 && color_config->subsampling_y == 1) &&
      !(color_config->subsampling_x == 1 && color_config->subsampling_y == 0)) {
    GST_WARNING ("Only 4:4:4, 4:2:2 and 4:2:0 are currently supported, "
        "%d %d subsampling is not supported.\n",
        color_config->subsampling_x, color_config->subsampling_y);
    goto error;
  }

success:
  *space = TRUE;
  return TRUE;

error:
  GST_WARNING ("failed to write color config");
  *space = have_space;
  return FALSE;
}

static gboolean
_av1_bit_writer_sequence_header (const GstAV1SequenceHeaderOBU * seq_header,
    GstBitWriter * bw, gboolean * space)
{
  gboolean have_space = TRUE;
  gint i;

  GST_DEBUG ("writing sequence header");

  if (seq_header->seq_profile > GST_AV1_PROFILE_2) {
    GST_WARNING ("Unsupported profile %d", seq_header->seq_profile);
    goto error;
  }
  WRITE_BITS (bw, seq_header->seq_profile, 3);

  WRITE_BITS (bw, seq_header->still_picture, 1);

  if (!seq_header->still_picture && seq_header->reduced_still_picture_header) {
    GST_WARNING (" If reduced_still_picture_header is equal to 1, it is a"
        " requirement of bitstream conformance that still_picture is equal"
        " to 1. ");
    goto error;
  }
  WRITE_BITS (bw, seq_header->reduced_still_picture_header, 1);

  if (seq_header->reduced_still_picture_header) {
    if (!_av1_seq_level_idx_is_valid
        (seq_header->operating_points[0].seq_level_idx)) {
      GST_WARNING ("The seq_level_idx is unsupported");
      goto error;
    }
    WRITE_BITS (bw, seq_header->operating_points[0].seq_level_idx, 5);
  } else {
    WRITE_BITS (bw, seq_header->timing_info_present_flag, 1);
    if (seq_header->timing_info_present_flag) {
      if (!_av1_bit_writer_timing_info (&seq_header->timing_info, bw,
              &have_space))
        goto error;

      WRITE_BITS (bw, seq_header->decoder_model_info_present_flag, 1);
      if (seq_header->decoder_model_info_present_flag) {
        if (!_av1_bit_writer_decoder_model_info
            (&seq_header->decoder_model_info, bw, &have_space))
          goto error;
      }
    }

    WRITE_BITS (bw, seq_header->initial_display_delay_present_flag, 1);

    if (seq_header->operating_points_cnt_minus_1 + 1 >
        GST_AV1_MAX_OPERATING_POINTS) {
      GST_WARNING ("The operating points number %d is too big",
          seq_header->operating_points_cnt_minus_1 + 1);
      goto error;
    }
    WRITE_BITS (bw, seq_header->operating_points_cnt_minus_1, 5);

    for (i = 0; i < seq_header->operating_points_cnt_minus_1 + 1; i++) {
      const GstAV1OperatingPoint *op = &seq_header->operating_points[i];

      WRITE_BITS (bw, op->idc, 12);

      if (!_av1_seq_level_idx_is_valid (op->seq_level_idx)) {
        GST_WARNING ("The seq_level_idx is unsupported");
        goto error;
      }
      WRITE_BITS (bw, op->seq_level_idx, 5);

      if (op->seq_level_idx > GST_AV1_SEQ_LEVEL_3_3)
        WRITE_BITS (bw, op->seq_tier, 1);

      if (seq_header->decoder_model_info_present_flag) {
        WRITE_BITS (bw, op->decoder_model_present_for_this_op, 1);
        if (op->decoder_model_present_for_this_op) {
          if (!_av1_bit_writer_operating_parameters_info (seq_header,
                  op, bw, &have_space))
            goto error;
        }
      }

      if (seq_header->initial_display_delay_present_flag) {
        WRITE_BITS (bw, op->initial_display_delay_present_for_this_op, 1);

        if (op->initial_display_delay_present_for_this_op) {
          if (op->initial_display_delay_minus_1 + 1 > 10) {
            GST_INFO ("AV1 does not support more than 10 decoded frames delay");
            goto error;
          }

          WRITE_BITS (bw, op->initial_display_delay_minus_1, 4);
        }
      }
    }
  }

  WRITE_BITS (bw, seq_header->frame_width_bits_minus_1, 4);
  WRITE_BITS (bw, seq_header->frame_height_bits_minus_1, 4);
  WRITE_BITS (bw, seq_header->max_frame_width_minus_1,
      seq_header->frame_width_bits_minus_1 + 1);
  WRITE_BITS (bw, seq_header->max_frame_height_minus_1,
      seq_header->frame_height_bits_minus_1 + 1);

  if (!seq_header->reduced_still_picture_header)
    WRITE_BITS (bw, seq_header->frame_id_numbers_present_flag, 1);

  if (seq_header->frame_id_numbers_present_flag) {
    if (seq_header->additional_frame_id_length_minus_1 + 1 +
        seq_header->delta_frame_id_length_minus_2 + 2 > 16) {
      GST_WARNING ("Invalid frame_id_length");
      goto error;
    }
    WRITE_BITS (bw, seq_header->delta_frame_id_length_minus_2, 4);
    WRITE_BITS (bw, seq_header->additional_frame_id_length_minus_1, 3);
  }

  WRITE_BITS (bw, seq_header->use_128x128_superblock, 1);
  WRITE_BITS (bw, seq_header->enable_filter_intra, 1);
  WRITE_BITS (bw, seq_header->enable_intra_edge_filter, 1);

  if (!seq_header->reduced_still_picture_header) {
    WRITE_BITS (bw, seq_header->enable_interintra_compound, 1);
    WRITE_BITS (bw, seq_header->enable_masked_compound, 1);
    WRITE_BITS (bw, seq_header->enable_warped_motion, 1);
    WRITE_BITS (bw, seq_header->enable_dual_filter, 1);
    WRITE_BITS (bw, seq_header->enable_order_hint, 1);
    if (seq_header->enable_order_hint) {
      WRITE_BITS (bw, seq_header->enable_jnt_comp, 1);
      WRITE_BITS (bw, seq_header->enable_ref_frame_mvs, 1);
    }

    WRITE_BITS (bw, seq_header->seq_choose_screen_content_tools, 1);
    if (!seq_header->seq_choose_screen_content_tools)
      WRITE_BITS (bw, seq_header->seq_force_screen_content_tools, 1);

    if (seq_header->seq_force_screen_content_tools > 0) {
      WRITE_BITS (bw, seq_header->seq_choose_integer_mv, 1);
      if (!seq_header->seq_choose_integer_mv)
        WRITE_BITS (bw, seq_header->seq_force_integer_mv, 1);
    }

    if (seq_header->enable_order_hint)
      WRITE_BITS (bw, seq_header->order_hint_bits_minus_1, 3);
  }

  WRITE_BITS (bw, seq_header->enable_superres, 1);
  WRITE_BITS (bw, seq_header->enable_cdef, 1);
  WRITE_BITS (bw, seq_header->enable_restoration, 1);

  if (!_av1_bit_writer_color_config (seq_header, &seq_header->color_config,
          bw, &have_space))
    goto error;

  WRITE_BITS (bw, seq_header->film_grain_params_present, 1);

  *space = TRUE;
  return TRUE;

error:
  GST_WARNING ("failed to write sequence header");
  *space = have_space;
  return FALSE;
}

/* Return the actual size field size. */
static guint
_av1_bit_writer_add_size_field (guint8 * data, guint * size, guint header_size,
    guint payload_size, guint size_field_size, gboolean * space)
{
  guint size_field_sz;

  size_field_sz = _av1_uleb_size_in_bytes (payload_size);
  g_assert (size_field_sz > 0);

  if (size_field_size > 0) {
    if (size_field_sz > size_field_size) {
      GST_WARNING ("the fixed size field size is too small");
      goto error;
    }

    size_field_sz = size_field_size;
  }

  /* Move and write the data size field */
  if (header_size + payload_size + size_field_sz > *size) {
    *space = FALSE;
    goto error;
  }
  *space = TRUE;

  memmove (data + header_size + size_field_sz,
      data + header_size, payload_size);

  if (!_av1_encode_uleb (payload_size, sizeof (payload_size),
          data + header_size, size_field_sz))
    goto error;

  *size = header_size + payload_size + size_field_sz;
  return size_field_sz;

error:
  GST_WARNING ("failed to write the size field");
  return 0;
}

/**
 * gst_av1_bit_writer_sequence_header_obu:
 * @seq_hdr: the sequence header of #GstAV1SequenceHeaderOBU to write
 * @size_field: whether the header contain size feild
 * @data: (out): the bit stream generated by the sequence header
 * @size: (inout): the size in bytes of the input and output
 *
 * Generating the according AV1 bit stream OBU by providing the sequence header.
 *
 * Returns: a #GstAV1BitWriterResult
 *
 * Since: 1.22
 **/
GstAV1BitWriterResult
gst_av1_bit_writer_sequence_header_obu (const GstAV1SequenceHeaderOBU *
    seq_hdr, gboolean size_field, guint8 * data, guint * size)
{
  gboolean have_space = TRUE;
  GstBitWriter bw;
  guint header_size;
  guint payload_size;

  g_return_val_if_fail (seq_hdr != NULL, GST_AV1_BIT_WRITER_ERROR);
  g_return_val_if_fail (data != NULL, GST_AV1_BIT_WRITER_ERROR);
  g_return_val_if_fail (size != NULL, GST_AV1_BIT_WRITER_ERROR);
  g_return_val_if_fail (*size > 0, GST_AV1_BIT_WRITER_ERROR);

  gst_bit_writer_init_with_data (&bw, data, *size, FALSE);

  /* obu_forbidden_bit */
  WRITE_BITS (&bw, 0, 1);
  /* obu_type */
  WRITE_BITS (&bw, GST_AV1_OBU_SEQUENCE_HEADER, 4);
  /* obu_extention_flag */
  WRITE_BITS (&bw, 0, 1);
  /* obu_has_size_field */
  WRITE_BITS (&bw, 1, 1);
  /* obu_reserved_1bit */
  WRITE_BITS (&bw, 0, 1);

  header_size = gst_bit_writer_get_size (&bw);
  g_assert (header_size % 8 == 0);
  header_size /= 8;

  if (!_av1_bit_writer_sequence_header (seq_hdr, &bw, &have_space))
    goto error;

  /* Add trailings. */
  WRITE_BITS (&bw, 1, 1);
  if (!gst_bit_writer_align_bytes (&bw, 0)) {
    have_space = FALSE;
    goto error;
  }

  payload_size = gst_bit_writer_get_size (&bw);
  g_assert (payload_size % 8 == 0);
  payload_size /= 8;
  payload_size -= header_size;

  gst_bit_writer_reset (&bw);

  if (size_field) {
    if (!_av1_bit_writer_add_size_field (data, size,
            header_size, payload_size, 0, &have_space))
      goto error;
  } else {
    *size = header_size + payload_size;
  }

  return GST_AV1_BIT_WRITER_OK;

error:
  gst_bit_writer_reset (&bw);
  *size = 0;
  return have_space ? GST_AV1_BIT_WRITER_INVALID_DATA :
      GST_AV1_BIT_WRITER_NO_MORE_SPACE;
}

/* 5.9.5 */
static gboolean
_av1_bit_writer_superres_params (const GstAV1FrameHeaderOBU * frame_header,
    const GstAV1SequenceHeaderOBU * seq_header, GstBitWriter * bw,
    gboolean * space)
{
  gboolean have_space = TRUE;

  GST_DEBUG ("writing superres param");

  if (seq_header->enable_superres)
    WRITE_BITS (bw, frame_header->use_superres, 1);

  if (frame_header->use_superres) {
    guint8 coded_denom;

    if (frame_header->superres_denom < GST_AV1_SUPERRES_DENOM_MIN)
      goto error;

    coded_denom = frame_header->superres_denom - GST_AV1_SUPERRES_DENOM_MIN;
    if (coded_denom > (1 << GST_AV1_SUPERRES_DENOM_BITS) - 1)
      goto error;

    WRITE_BITS (bw, coded_denom, GST_AV1_SUPERRES_DENOM_BITS);
  }

  *space = TRUE;
  return TRUE;

error:
  GST_WARNING ("failed to write superres param");
  *space = have_space;
  return FALSE;
}

/* 5.9.5 */
static gboolean
_av1_bit_writer_frame_size (const GstAV1FrameHeaderOBU * frame_header,
    const GstAV1SequenceHeaderOBU * seq_header, GstBitWriter * bw,
    gboolean * space)
{
  gboolean have_space = TRUE;

  GST_DEBUG ("writing frame size");

  if (frame_header->frame_size_override_flag) {
    guint16 frame_width_minus_1;
    guint16 frame_height_minus_1;

    frame_width_minus_1 = frame_header->frame_width - 1;
    WRITE_BITS (bw, frame_width_minus_1,
        seq_header->frame_width_bits_minus_1 + 1);
    frame_height_minus_1 = frame_header->frame_height - 1;
    WRITE_BITS (bw, frame_height_minus_1,
        seq_header->frame_height_bits_minus_1 + 1);
  }

  if (!_av1_bit_writer_superres_params (frame_header, seq_header, bw, space))
    goto error;

  *space = TRUE;
  return TRUE;

error:
  GST_WARNING ("failed to write frame size");
  *space = have_space;
  return FALSE;
}

/* 5.9.6 */
static gboolean
_av1_bit_writer_render_size (const GstAV1FrameHeaderOBU * frame_header,
    GstBitWriter * bw, gboolean * space)
{
  gboolean have_space = TRUE;

  GST_DEBUG ("writing render size");

  WRITE_BITS (bw, frame_header->render_and_frame_size_different, 1);

  if (frame_header->render_and_frame_size_different) {
    guint16 render_width_minus_1 = frame_header->render_width - 1;
    guint16 render_height_minus_1 = frame_header->render_height - 1;

    WRITE_BITS (bw, render_width_minus_1, 16);
    WRITE_BITS (bw, render_height_minus_1, 16);
  }

  *space = TRUE;
  return TRUE;

error:
  GST_WARNING ("failed to write render size");
  *space = have_space;
  return FALSE;
}

/* 5.9.15 */
static gboolean
_av1_bit_writer_tile_info (const GstAV1FrameHeaderOBU * frame_header,
    const GstAV1SequenceHeaderOBU * seq_header, GstBitWriter * bw,
    gboolean * space)
{
  gboolean have_space = TRUE;
  const GstAV1TileInfo *tile_info;
  guint32 mi_cols /* MiCols */ ;
  guint32 mi_rows /* MiRows */ ;
  gint sb_cols /* sbCols */ ;
  gint sb_rows /* sbRows */ ;
  gint sb_shift /*sbShift */ ;
  gint sb_size /* sbSize */ ;
  gint max_tile_width_sb /* maxTileWidthSb */ ;
  gint max_tile_height_sb /* maxTileHeightSb */ ;
  gint max_tile_area_sb /* maxTileAreaSb */ ;
  gint min_log2_tile_cols /* minLog2TileCols */ ;
  gint max_log2_tile_cols /* maxLog2TileCols */ ;
  gint min_log2_tile_rows /* minLog2TileRows */ ;
  gint max_log2_tile_rows /* maxLog2TileRows */ ;
  gint min_log2_tiles /* minLog2Tiles */ ;
  gint size_sb /* sizeSb */ ;
  gint widest_tile_sb /* widestTileSb */ ;

  tile_info = &frame_header->tile_info;

  GST_DEBUG ("writing tile info");

  /* User must specify the frame resolution. */
  if (frame_header->frame_width == 0 || frame_header->frame_height == 0) {
    GST_WARNING ("unknown frame_width or frame_height");
    goto error;
  }

  mi_cols = 2 * ((frame_header->frame_width + 7) >> 3);
  mi_rows = 2 * ((frame_header->frame_height + 7) >> 3);

  sb_cols = seq_header->use_128x128_superblock ?
      ((mi_cols + 31) >> 5) : ((mi_cols + 15) >> 4);
  sb_rows = seq_header->use_128x128_superblock ?
      ((mi_rows + 31) >> 5) : ((mi_rows + 15) >> 4);
  sb_shift = seq_header->use_128x128_superblock ? 5 : 4;
  sb_size = sb_shift + 2;

  max_tile_width_sb = GST_AV1_MAX_TILE_WIDTH >> sb_size;
  max_tile_area_sb = GST_AV1_MAX_TILE_AREA >> (2 * sb_size);
  min_log2_tile_cols = _av1_helper_tile_log2 (max_tile_width_sb, sb_cols);
  max_log2_tile_cols = _av1_helper_tile_log2 (1, MIN (sb_cols,
          GST_AV1_MAX_TILE_COLS));
  max_log2_tile_rows = _av1_helper_tile_log2 (1, MIN (sb_rows,
          GST_AV1_MAX_TILE_ROWS));
  min_log2_tiles = MAX (min_log2_tile_cols,
      _av1_helper_tile_log2 (max_tile_area_sb, sb_rows * sb_cols));

  WRITE_BITS (bw, tile_info->uniform_tile_spacing_flag, 1);
  if (tile_info->uniform_tile_spacing_flag) {
    /* columns */
    gint ones = tile_info->tile_cols_log2 - min_log2_tile_cols;
    if (ones < 0)
      goto error;

    while (ones--)
      WRITE_BITS (bw, 1, 1);
    if (tile_info->tile_cols_log2 < max_log2_tile_cols)
      WRITE_BITS (bw, 0, 1);

    /* rows */
    min_log2_tile_rows = MAX (min_log2_tiles - tile_info->tile_cols_log2, 0);
    ones = tile_info->tile_rows_log2 - min_log2_tile_rows;
    if (ones < 0)
      goto error;

    while (ones--)
      WRITE_BITS (bw, 1, 1);
    if (tile_info->tile_rows_log2 < max_log2_tile_rows)
      WRITE_BITS (bw, 0, 1);
  } else {
    /* Explicit tiles with configurable tile widths and heights */
    guint i;
    guint width_sb;
    guint height_sb;

    widest_tile_sb = 0;

    /* columns */
    width_sb = sb_cols;
    for (i = 0; i < tile_info->tile_cols; i++) {
      size_sb = tile_info->mi_col_starts[i + 1] - tile_info->mi_col_starts[i];
      size_sb = size_sb >> sb_shift;
      widest_tile_sb = MAX (size_sb, widest_tile_sb);
      if (_av1_write_uniform (bw, MIN (width_sb, max_tile_width_sb),
              size_sb - 1, &have_space))
        goto error;

      width_sb -= size_sb;
    }

    if (width_sb != 0)
      goto error;

    // rows
    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 = MAX (max_tile_area_sb / widest_tile_sb, 1);

    height_sb = sb_rows;
    for (i = 0; i < tile_info->tile_rows; i++) {
      size_sb = tile_info->mi_row_starts[i + 1] - tile_info->mi_row_starts[i];
      size_sb = size_sb >> sb_shift;
      if (_av1_write_uniform (bw, MIN (height_sb, max_tile_height_sb),
              size_sb - 1, &have_space))
        goto error;

      height_sb -= size_sb;
    }

    if (height_sb != 0)
      goto error;
  }

  if (tile_info->tile_cols_log2 > 0 || tile_info->tile_rows_log2 > 0) {
    WRITE_BITS (bw, tile_info->context_update_tile_id,
        tile_info->tile_cols_log2 + tile_info->tile_rows_log2);

    WRITE_BITS (bw, tile_info->tile_size_bytes_minus_1, 2);
  }

  *space = TRUE;
  return TRUE;

error:
  GST_WARNING ("failed to write tile info");
  *space = have_space;
  return FALSE;
}

/* 5.9.12 */
static gboolean
_av1_bit_writer_quantization_params (const GstAV1FrameHeaderOBU * frame_header,
    const GstAV1SequenceHeaderOBU * seq_header, guint * qindex_offset,
    GstBitWriter * bw, gboolean * space)
{
  gboolean have_space = TRUE;
  const GstAV1QuantizationParams *quant_params =
      &(frame_header->quantization_params);

  GST_DEBUG ("writing quantization params");

  if (qindex_offset)
    *qindex_offset = gst_bit_writer_get_size (bw);

  WRITE_BITS (bw, quant_params->base_q_idx, 8);

  if (!_av1_write_delta_q (bw,
          frame_header->quantization_params.delta_q_y_dc, &have_space))
    goto error;

  if (seq_header->num_planes > 1) {
    if (seq_header->color_config.separate_uv_delta_q)
      WRITE_BITS (bw, quant_params->diff_uv_delta, 1);

    if (!_av1_write_delta_q (bw,
            frame_header->quantization_params.delta_q_u_dc, &have_space))
      goto error;

    if (!_av1_write_delta_q (bw,
            frame_header->quantization_params.delta_q_u_ac, &have_space))
      goto error;

    if (quant_params->diff_uv_delta) {
      if (!_av1_write_delta_q (bw,
              frame_header->quantization_params.delta_q_v_dc, &have_space))
        goto error;

      if (!_av1_write_delta_q (bw,
              frame_header->quantization_params.delta_q_v_ac, &have_space))
        goto error;
    }
  }

  WRITE_BITS (bw, quant_params->using_qmatrix, 1);
  if (quant_params->using_qmatrix) {
    WRITE_BITS (bw, quant_params->qm_y, 4);
    WRITE_BITS (bw, quant_params->qm_u, 4);

    if (seq_header->color_config.separate_uv_delta_q)
      WRITE_BITS (bw, quant_params->qm_v, 4);
  }

  *space = TRUE;
  return TRUE;

error:
  GST_WARNING ("failed to write quantization params");
  *space = have_space;
  return FALSE;
}

/* 5.9.14 */
static gboolean
_av1_bit_writer_segmentation_params (const GstAV1FrameHeaderOBU * frame_header,
    const GstAV1SequenceHeaderOBU * seq_header, guint * segmentation_offset,
    GstBitWriter * bw, gboolean * space)
{
  gboolean have_space = TRUE;

  GST_DEBUG ("writing segmentation params");

  if (segmentation_offset)
    *segmentation_offset = gst_bit_writer_get_size (bw);

  /* TODO: segmentation support. */
  if (frame_header->segmentation_params.segmentation_enabled) {
    GST_WARNING ("segmentation is not supported now");
    goto error;
  }

  WRITE_BITS (bw, frame_header->segmentation_params.segmentation_enabled, 1);

  *space = TRUE;
  return TRUE;

error:
  GST_WARNING ("failed to write segmentation params");
  *space = have_space;
  return FALSE;
}

/* 5.9.17 */
static gboolean
_av1_bit_writer_delta_q_params (const GstAV1FrameHeaderOBU * frame_header,
    const GstAV1SequenceHeaderOBU * seq_header, GstBitWriter * bw,
    gboolean * space)
{
  gboolean have_space = TRUE;

  GST_DEBUG ("writing delta q params");

  if (frame_header->quantization_params.base_q_idx > 0)
    WRITE_BITS (bw, frame_header->quantization_params.delta_q_present, 1);

  if (frame_header->quantization_params.delta_q_present)
    WRITE_BITS (bw, frame_header->quantization_params.delta_q_res, 2);

  *space = TRUE;
  return TRUE;

error:
  GST_WARNING ("failed to write delta q params");
  *space = have_space;
  return FALSE;
}

/* 5.9.18 */
static gboolean
_av1_bit_writer_delta_lf_params (const GstAV1FrameHeaderOBU * frame_header,
    const GstAV1SequenceHeaderOBU * seq_header, GstBitWriter * bw,
    gboolean * space)
{
  gboolean have_space = TRUE;

  GST_DEBUG ("writing delta lf params");

  if (frame_header->quantization_params.delta_q_present) {
    if (!frame_header->allow_intrabc)
      WRITE_BITS (bw, frame_header->loop_filter_params.delta_lf_present, 1);

    if (frame_header->loop_filter_params.delta_lf_present) {
      WRITE_BITS (bw, frame_header->loop_filter_params.delta_lf_res, 2);
      WRITE_BITS (bw, frame_header->loop_filter_params.delta_lf_multi, 1);
    }
  }

  *space = TRUE;
  return TRUE;

error:
  GST_WARNING ("failed to write delta lf params");
  *space = have_space;
  return FALSE;
}

static gboolean
_av1_bit_writer_loop_filter_params (const GstAV1FrameHeaderOBU * frame_header,
    const GstAV1SequenceHeaderOBU * seq_header, guint * loopfilter_offset,
    GstBitWriter * bw, gboolean * space)
{
  gboolean have_space = TRUE;
  const GstAV1LoopFilterParams *lf_params = &frame_header->loop_filter_params;

  GST_DEBUG ("writing loop filter params");

  if (frame_header->coded_lossless || frame_header->allow_intrabc)
    goto success;

  if (loopfilter_offset)
    *loopfilter_offset = gst_bit_writer_get_size (bw);

  WRITE_BITS (bw, lf_params->loop_filter_level[0], 6);
  WRITE_BITS (bw, lf_params->loop_filter_level[1], 6);
  if (seq_header->num_planes > 1) {
    if (lf_params->loop_filter_level[0] || lf_params->loop_filter_level[1]) {
      WRITE_BITS (bw, lf_params->loop_filter_level[2], 6);
      WRITE_BITS (bw, lf_params->loop_filter_level[3], 6);
    }
  }

  WRITE_BITS (bw, lf_params->loop_filter_sharpness, 3);

  WRITE_BITS (bw, lf_params->loop_filter_delta_enabled, 1);
  if (lf_params->loop_filter_delta_enabled) {
    guint i;

    WRITE_BITS (bw, lf_params->loop_filter_delta_update, 1);

    if (lf_params->loop_filter_delta_update) {
      const gint8 default_loop_filter_ref_deltas[] =
          { 1, 0, 0, 0, -1, 0, -1, -1 };
      gboolean update_ref_deltas;

      for (i = 0; i < GST_AV1_TOTAL_REFS_PER_FRAME; i++) {
        /* If loop_filter_ref_deltas[i] is different from default
           value, we update it. */
        update_ref_deltas = (lf_params->loop_filter_ref_deltas[i] !=
            default_loop_filter_ref_deltas[i]);

        WRITE_BITS (bw, update_ref_deltas, 1);

        if (update_ref_deltas) {
          if (!_av1_write_su (bw, lf_params->loop_filter_ref_deltas[i],
                  6, space))
            goto error;
        }
      }

      for (i = 0; i < 2; i++) {
        /* If loop_filter_mode_deltas[i] is different from default
           value, we update it. */
        update_ref_deltas = (lf_params->loop_filter_mode_deltas[i] != 0);

        WRITE_BITS (bw, update_ref_deltas, 1);

        if (update_ref_deltas) {
          if (!_av1_write_su (bw, lf_params->loop_filter_mode_deltas[i],
                  6, space))
            goto error;
        }
      }
    }
  }

success:
  *space = TRUE;
  return TRUE;

error:
  GST_WARNING ("failed to write loop filter params");
  *space = have_space;
  return FALSE;
}

static gboolean
_av1_bit_writer_cdef_params (const GstAV1FrameHeaderOBU * frame_header,
    const GstAV1SequenceHeaderOBU * seq_header, guint * cdef_offset,
    guint * cdef_size, GstBitWriter * bw, gboolean * space)
{
  gboolean have_space = TRUE;
  const GstAV1CDEFParams *cdef_params = &frame_header->cdef_params;
  guint cdef_start;
  guint i;

  GST_DEBUG ("writing cdef params");

  if (frame_header->coded_lossless || frame_header->allow_intrabc
      || !seq_header->enable_cdef)
    goto success;

  cdef_start = gst_bit_writer_get_size (bw);
  if (cdef_offset)
    *cdef_offset = cdef_start;

  WRITE_BITS (bw, cdef_params->cdef_damping - 3, 2);
  WRITE_BITS (bw, cdef_params->cdef_bits, 2);

  for (i = 0; i < (1 << cdef_params->cdef_bits); i++) {
    guint8 cdef_y_sec_strength;

    WRITE_BITS (bw, cdef_params->cdef_y_pri_strength[i], 4);

    cdef_y_sec_strength = cdef_params->cdef_y_sec_strength[i];

    if (cdef_y_sec_strength >= 4) {
      GST_WARNING ("cdef_y_sec_strength is not valid");
      goto error;
    }
    WRITE_BITS (bw, cdef_y_sec_strength, 2);

    if (seq_header->num_planes > 1) {
      guint8 cdef_uv_sec_strength;

      WRITE_BITS (bw, cdef_params->cdef_uv_pri_strength[i], 4);

      cdef_uv_sec_strength = cdef_params->cdef_uv_sec_strength[i];

      if (cdef_uv_sec_strength >= 4) {
        GST_WARNING ("cdef_uv_sec_strength is not valid");
        goto error;
      }
      WRITE_BITS (bw, cdef_uv_sec_strength, 2);
    }
  }

  if (cdef_size)
    *cdef_size = gst_bit_writer_get_size (bw) - cdef_start;

success:
  *space = TRUE;
  return TRUE;

error:
  GST_WARNING ("failed to write cdef params");
  *space = have_space;
  return FALSE;
}

static gboolean
_av1_bit_writer_loop_restoration_params (const GstAV1FrameHeaderOBU *
    frame_header, const GstAV1SequenceHeaderOBU * seq_header,
    GstBitWriter * bw, gboolean * space)
{
  gboolean have_space = TRUE;
  guint i, j;
  guint8 use_chroma_lr = 0 /* useChromaLr */ ;
  const GstAV1LoopRestorationParams *lr_params =
      &frame_header->loop_restoration_params;
  const GstAV1FrameRestorationType remap_lr_type /* Remap_Lr_Type */ [4] = {
    GST_AV1_FRAME_RESTORE_NONE,
    GST_AV1_FRAME_RESTORE_SWITCHABLE,
    GST_AV1_FRAME_RESTORE_WIENER, GST_AV1_FRAME_RESTORE_SGRPROJ
  };

  GST_DEBUG ("writing loop restoration params");

  if (frame_header->all_lossless || frame_header->allow_intrabc
      || !seq_header->enable_restoration)
    goto success;

  for (i = 0; i < seq_header->num_planes; i++) {
    for (j = 0; j < 4; j++) {
      if (lr_params->frame_restoration_type[i] == remap_lr_type[j])
        break;
    }
    if (j == 4)
      goto error;

    if (lr_params->frame_restoration_type[i] != GST_AV1_FRAME_RESTORE_NONE) {
      if (!lr_params->uses_lr) {
        GST_WARNING ("uses_lr set to wrong value");
        goto error;
      }

      if (i > 1)
        use_chroma_lr = 1;
    }

    WRITE_BITS (bw, j, 2);
  }

  if (lr_params->uses_lr) {
    if (lr_params->lr_unit_shift > 2)
      goto error;

    if (seq_header->use_128x128_superblock) {
      WRITE_BITS (bw, lr_params->lr_unit_shift - 1, 1);
    } else {
      WRITE_BITS (bw, 1, 1);

      if (lr_params->lr_unit_shift > 1)
        /* lr_unit_extra_shift */
        WRITE_BITS (bw, 1, 1);
    }

    if (seq_header->color_config.subsampling_x
        && seq_header->color_config.subsampling_y && use_chroma_lr)
      WRITE_BITS (bw, lr_params->lr_uv_shift, 1);
  }

success:
  *space = TRUE;
  return TRUE;

error:
  GST_WARNING ("failed to write loop restoration params");
  *space = have_space;
  return FALSE;
}

/* 5.9.22 */
static gboolean
_av1_bit_writer_skip_mode_params (const GstAV1FrameHeaderOBU * frame_header,
    const GstAV1SequenceHeaderOBU * seq_header, GstBitWriter * bw,
    gboolean * space)
{
  gboolean have_space = TRUE;

  GST_DEBUG ("writing skip mode params");

  /* if skipModeAllowed is true */
  if (frame_header->skip_mode_frame[0] > 0
      || frame_header->skip_mode_frame[1] > 0)
    WRITE_BITS (bw, frame_header->skip_mode_present, 1);

  *space = TRUE;
  return TRUE;

error:
  GST_WARNING ("failed to write skip mode params");
  *space = have_space;
  return FALSE;
}

/* 5.9.24 */
static gboolean
_av1_bit_writer_global_motion_params (const GstAV1FrameHeaderOBU * frame_header,
    const GstAV1SequenceHeaderOBU * seq_header, GstBitWriter * bw,
    gboolean * space)
{
  gboolean have_space = TRUE;
  gint ref, i;
  const GstAV1GlobalMotionParams *gm_params =
      &(frame_header->global_motion_params);
  gint32 prev_gm_params[GST_AV1_NUM_REF_FRAMES][6] /* PrevGmParams */ ;

  GST_DEBUG ("writing global motion params");

  if (frame_header->frame_is_intra)
    goto success;

  if (frame_header->primary_ref_frame != GST_AV1_PRIMARY_REF_NONE) {
    memcpy (prev_gm_params, &frame_header->ref_global_motion_params,
        sizeof (gint32) * GST_AV1_NUM_REF_FRAMES * 6);
  } else {
    for (ref = GST_AV1_REF_INTRA_FRAME; ref < GST_AV1_NUM_REF_FRAMES; ref++)
      for (i = 0; i < 6; i++)
        prev_gm_params[ref][i] =
            ((i % 3 == 2) ? 1 << GST_AV1_WARPEDMODEL_PREC_BITS : 0);
  }

  for (ref = GST_AV1_REF_LAST_FRAME; ref <= GST_AV1_REF_ALTREF_FRAME; ref++) {
    WRITE_BITS (bw, gm_params->gm_type[ref] != GST_AV1_WARP_MODEL_IDENTITY, 1);

    if (gm_params->gm_type[ref] != GST_AV1_WARP_MODEL_IDENTITY) {
      WRITE_BITS (bw, gm_params->gm_type[ref] == GST_AV1_WARP_MODEL_ROTZOOM, 1);

      if (gm_params->gm_type[ref] != GST_AV1_WARP_MODEL_ROTZOOM)
        WRITE_BITS (bw,
            gm_params->gm_type[ref] == GST_AV1_WARP_MODEL_TRANSLATION, 1);
    }

    if (gm_params->gm_type[ref] >= GST_AV1_WARP_MODEL_ROTZOOM) {
      if (!_av1_write_signed_primitive_refsubexpfin (bw,
              (1 << GST_AV1_GM_ABS_ALPHA_BITS) + 1, 3,
              (prev_gm_params[ref][2] >> (GST_AV1_WARPEDMODEL_PREC_BITS -
                      GST_AV1_GM_ALPHA_PREC_BITS)) -
              (1 << GST_AV1_GM_ALPHA_PREC_BITS),
              (gm_params->gm_params[ref][2] >> (GST_AV1_WARPEDMODEL_PREC_BITS -
                      GST_AV1_GM_ALPHA_PREC_BITS)) -
              (1 << GST_AV1_GM_ALPHA_PREC_BITS), space))
        goto error;

      if (!_av1_write_signed_primitive_refsubexpfin (bw,
              (1 << GST_AV1_GM_ABS_ALPHA_BITS) + 1, 3,
              (prev_gm_params[ref][3] >> (GST_AV1_WARPEDMODEL_PREC_BITS -
                      GST_AV1_GM_ALPHA_PREC_BITS)),
              (gm_params->gm_params[ref][3] >> (GST_AV1_WARPEDMODEL_PREC_BITS -
                      GST_AV1_GM_ALPHA_PREC_BITS)), space))
        goto error;
    }

    if (gm_params->gm_type[ref] >= GST_AV1_WARP_MODEL_AFFINE) {
      if (!_av1_write_signed_primitive_refsubexpfin (bw,
              (1 << GST_AV1_GM_ABS_ALPHA_BITS) + 1, 3,
              (prev_gm_params[ref][4] >> (GST_AV1_WARPEDMODEL_PREC_BITS -
                      GST_AV1_GM_ALPHA_PREC_BITS)),
              (gm_params->gm_params[ref][4] >> (GST_AV1_WARPEDMODEL_PREC_BITS -
                      GST_AV1_GM_ALPHA_PREC_BITS)), space))
        goto error;

      if (!_av1_write_signed_primitive_refsubexpfin (bw,
              (1 << GST_AV1_GM_ABS_ALPHA_BITS) + 1, 3,
              (prev_gm_params[ref][5] >> (GST_AV1_WARPEDMODEL_PREC_BITS -
                      GST_AV1_GM_ALPHA_PREC_BITS)) -
              (1 << GST_AV1_GM_ALPHA_PREC_BITS),
              (gm_params->gm_params[ref][5] >> (GST_AV1_WARPEDMODEL_PREC_BITS -
                      GST_AV1_GM_ALPHA_PREC_BITS)) -
              (1 << GST_AV1_GM_ALPHA_PREC_BITS), space))
        goto error;
    }

    if (gm_params->gm_type[ref] >= GST_AV1_WARP_MODEL_TRANSLATION) {
      const gint trans_bits =
          (gm_params->gm_type[ref] == GST_AV1_WARP_MODEL_TRANSLATION) ?
          GST_AV1_GM_ABS_TRANS_ONLY_BITS -
          !frame_header->allow_high_precision_mv : GST_AV1_GM_ABS_TRANS_BITS;
      const gint trans_prec_diff =
          (gm_params->gm_type[ref] == GST_AV1_WARP_MODEL_TRANSLATION) ?
          GST_AV1_WARPEDMODEL_PREC_BITS - 3 +
          !frame_header->allow_high_precision_mv :
          (GST_AV1_WARPEDMODEL_PREC_BITS - GST_AV1_GM_TRANS_PREC_BITS);

      if (!_av1_write_signed_primitive_refsubexpfin (bw, (1 << trans_bits) + 1,
              3, (prev_gm_params[ref][0] >> trans_prec_diff),
              (gm_params->gm_params[ref][0] >> trans_prec_diff), space))
        goto error;

      if (!_av1_write_signed_primitive_refsubexpfin (bw, (1 << trans_bits) + 1,
              3, (prev_gm_params[ref][1] >> trans_prec_diff),
              (gm_params->gm_params[ref][1] >> trans_prec_diff), space))
        goto error;
    }
  }

success:
  *space = TRUE;
  return TRUE;

error:
  GST_WARNING ("failed to write global motion params");
  *space = have_space;
  return FALSE;
}

/* 5.9.30 */
static gboolean
_av1_bit_writer_film_grain_params (const GstAV1FrameHeaderOBU * frame_header,
    const GstAV1SequenceHeaderOBU * seq_header, GstBitWriter * bw,
    gboolean * space)
{
  gboolean have_space = TRUE;
  guint i;
  gint num_pos_chroma /* numPosChroma */ , num_pos_luma /* numPosLuma */ ;
  const GstAV1FilmGrainParams *fg_params = &frame_header->film_grain_params;

  GST_DEBUG ("writing film grain params");

  fg_params = &frame_header->film_grain_params;
  if (!seq_header->film_grain_params_present || (!frame_header->show_frame
          && !frame_header->showable_frame))
    goto success;

  WRITE_BITS (bw, fg_params->apply_grain, 1);
  if (!fg_params->apply_grain)
    goto success;

  WRITE_BITS (bw, fg_params->grain_seed, 16);

  if (frame_header->frame_type == GST_AV1_INTER_FRAME)
    WRITE_BITS (bw, fg_params->update_grain, 1);

  if (!fg_params->update_grain) {
    WRITE_BITS (bw, fg_params->film_grain_params_ref_idx, 3);
    goto success;
  }

  WRITE_BITS (bw, fg_params->num_y_points, 4);

  for (i = 0; i < fg_params->num_y_points; i++) {
    WRITE_BITS (bw, fg_params->point_y_value[i], 8);
    WRITE_BITS (bw, fg_params->point_y_scaling[i], 8);
  }

  if (!seq_header->color_config.mono_chrome)
    WRITE_BITS (bw, fg_params->chroma_scaling_from_luma, 1);

  if (!(seq_header->color_config.mono_chrome
          || fg_params->chroma_scaling_from_luma
          || (seq_header->color_config.subsampling_x == 1
              && seq_header->color_config.subsampling_y == 1
              && fg_params->num_y_points == 0))) {
    WRITE_BITS (bw, fg_params->num_cb_points, 4);

    for (i = 0; i < fg_params->num_cb_points; i++) {
      WRITE_BITS (bw, fg_params->point_cb_value[i], 8);
      WRITE_BITS (bw, fg_params->point_cb_scaling[i], 8);
    }

    WRITE_BITS (bw, fg_params->num_cr_points, 4);
    for (i = 0; i < fg_params->num_cr_points; i++) {
      WRITE_BITS (bw, fg_params->point_cr_value[i], 8);
      WRITE_BITS (bw, fg_params->point_cr_scaling[i], 8);
    }
  }

  WRITE_BITS (bw, fg_params->grain_scaling_minus_8, 2);

  WRITE_BITS (bw, fg_params->ar_coeff_lag, 2);

  num_pos_luma = 2 * fg_params->ar_coeff_lag * (fg_params->ar_coeff_lag + 1);
  if (fg_params->num_y_points) {
    num_pos_chroma = num_pos_luma + 1;
    for (i = 0; i < num_pos_luma; i++)
      WRITE_BITS (bw, fg_params->ar_coeffs_y_plus_128[i], 8);
  } else {
    num_pos_chroma = num_pos_luma;
  }

  if (fg_params->chroma_scaling_from_luma || fg_params->num_cb_points) {
    for (i = 0; i < num_pos_chroma; i++)
      WRITE_BITS (bw, fg_params->ar_coeffs_cb_plus_128[i], 8);
  }

  if (fg_params->chroma_scaling_from_luma || fg_params->num_cr_points) {
    for (i = 0; i < num_pos_chroma; i++)
      WRITE_BITS (bw, fg_params->ar_coeffs_cr_plus_128[i], 8);
  }

  WRITE_BITS (bw, fg_params->ar_coeff_shift_minus_6, 2);

  WRITE_BITS (bw, fg_params->grain_scale_shift, 2);

  if (fg_params->num_cb_points) {
    WRITE_BITS (bw, fg_params->cb_mult, 8);
    WRITE_BITS (bw, fg_params->cb_luma_mult, 8);
    WRITE_BITS (bw, fg_params->cb_offset, 9);
  }

  if (fg_params->num_cr_points) {
    WRITE_BITS (bw, fg_params->cr_mult, 8);
    WRITE_BITS (bw, fg_params->cr_luma_mult, 8);
    WRITE_BITS (bw, fg_params->cr_offset, 9);
  }

  WRITE_BITS (bw, fg_params->overlap_flag, 1);
  WRITE_BITS (bw, fg_params->clip_to_restricted_range, 1);

success:
  *space = TRUE;
  return TRUE;

error:
  GST_WARNING ("failed to write film grain params");
  *space = have_space;
  return FALSE;
}

static gboolean
_av1_bit_writer_uncompressed_frame_header (const GstAV1FrameHeaderOBU *
    frame_header, const GstAV1SequenceHeaderOBU * seq_header,
    guint8 temporal_id, guint8 spatial_id, guint * qindex_offset,
    guint * segmentation_offset, guint * loopfilter_offset,
    guint * cdef_offset, guint * cdef_size, GstBitWriter * bw, gboolean * space)
{
  gboolean have_space = TRUE;
  guint i;
  gint id_len /* idLen */  = 0;
  const GstAV1DecoderModelInfo *cmi = &seq_header->decoder_model_info;

  GST_DEBUG ("writing frame header");

  if (seq_header->frame_id_numbers_present_flag)
    id_len = seq_header->additional_frame_id_length_minus_1 + 1 +
        seq_header->delta_frame_id_length_minus_2 + 2;

  if (!seq_header->reduced_still_picture_header) {
    WRITE_BITS (bw, frame_header->show_existing_frame, 1);
    if (frame_header->show_existing_frame) {
      WRITE_BITS (bw, frame_header->frame_to_show_map_idx, 3);

      if (seq_header->decoder_model_info_present_flag
          && !seq_header->timing_info.equal_picture_interval)
        WRITE_BITS (bw, frame_header->frame_presentation_time,
            cmi->frame_presentation_time_length_minus_1 + 1);

      if (seq_header->frame_id_numbers_present_flag) {
        if (id_len <= 0)
          goto error;

        WRITE_BITS (bw, frame_header->display_frame_id, id_len);
      }

      goto success;
    }

    if (seq_header->still_picture &&
        (frame_header->frame_type != GST_AV1_KEY_FRAME
            || !frame_header->show_frame)) {
      GST_INFO ("Still pictures must be coded as shown keyframes");
      goto error;
    }
    WRITE_BITS (bw, frame_header->frame_type, 2);
    WRITE_BITS (bw, frame_header->show_frame, 1);

    if (frame_header->show_frame
        && seq_header->decoder_model_info_present_flag
        && !seq_header->timing_info.equal_picture_interval)
      WRITE_BITS (bw, frame_header->frame_presentation_time,
          cmi->frame_presentation_time_length_minus_1 + 1);

    if (!frame_header->show_frame)
      WRITE_BITS (bw, frame_header->showable_frame, 1);

    if (!(frame_header->frame_type == GST_AV1_SWITCH_FRAME
            || (frame_header->frame_type == GST_AV1_KEY_FRAME
                && frame_header->show_frame)))
      WRITE_BITS (bw, frame_header->error_resilient_mode, 1);
  }

  WRITE_BITS (bw, frame_header->disable_cdf_update, 1);

  if (seq_header->seq_force_screen_content_tools ==
      GST_AV1_SELECT_SCREEN_CONTENT_TOOLS)
    WRITE_BITS (bw, frame_header->allow_screen_content_tools, 1);

  if (frame_header->allow_screen_content_tools) {
    if (seq_header->seq_force_integer_mv == GST_AV1_SELECT_INTEGER_MV)
      WRITE_BITS (bw, frame_header->force_integer_mv, 1);
  }

  if (seq_header->frame_id_numbers_present_flag) {
    if (id_len <= 0)
      goto error;

    WRITE_BITS (bw, frame_header->current_frame_id, id_len);
  }

  if (frame_header->frame_type != GST_AV1_SWITCH_FRAME
      && !seq_header->reduced_still_picture_header)
    WRITE_BITS (bw, frame_header->frame_size_override_flag, 1);

  WRITE_BITS (bw, frame_header->order_hint,
      seq_header->order_hint_bits_minus_1 + 1);

  if (frame_header->frame_is_intra || frame_header->error_resilient_mode) {
    if (frame_header->primary_ref_frame != GST_AV1_PRIMARY_REF_NONE) {
      GST_WARNING ("primary_ref_frame is not none.");
      goto error;
    }
  } else {
    WRITE_BITS (bw, frame_header->primary_ref_frame, 3);
  }

  if (seq_header->decoder_model_info_present_flag) {
    if (frame_header->buffer_removal_time_present_flag) {
      guint op_num;
      const GstAV1OperatingPoint *operating_points;

      for (op_num = 0; op_num <= seq_header->operating_points_cnt_minus_1;
          op_num++) {
        operating_points = &seq_header->operating_points[op_num];

        if (operating_points->decoder_model_present_for_this_op) {
          gint op_pt_idc = operating_points->idc;
          gint in_temporal_layer = (op_pt_idc >> temporal_id) & 1;
          gint in_spatial_layer = (op_pt_idc >> (spatial_id + 8)) & 1;

          if (op_pt_idc == 0 || (in_temporal_layer && in_spatial_layer))
            WRITE_BITS (bw, frame_header->buffer_removal_time[op_num],
                cmi->buffer_removal_time_length_minus_1 + 1);
        }
      }
    }
  }

  if (frame_header->frame_type == GST_AV1_INTRA_ONLY_FRAME) {
    if (frame_header->refresh_frame_flags == 0xFF) {
      GST_INFO ("Intra only frames cannot have refresh flags 0xFF");
      goto error;
    }
  }
  if (!(frame_header->frame_type == GST_AV1_SWITCH_FRAME ||
          (frame_header->frame_type == GST_AV1_KEY_FRAME
              && frame_header->show_frame)))
    WRITE_BITS (bw, frame_header->refresh_frame_flags, 8);

  if (!frame_header->frame_is_intra
      || frame_header->refresh_frame_flags !=
      (1 << GST_AV1_NUM_REF_FRAMES) - 1) {
    if (frame_header->error_resilient_mode && seq_header->enable_order_hint) {
      for (i = 0; i < GST_AV1_NUM_REF_FRAMES; i++)
        WRITE_BITS (bw, frame_header->ref_order_hint[i],
            seq_header->order_hint_bits_minus_1 + 1);
    }
  }

  if (frame_header->frame_is_intra) {
    if (!_av1_bit_writer_frame_size (frame_header, seq_header, bw, space))
      goto error;

    if (!_av1_bit_writer_render_size (frame_header, bw, space))
      goto error;

    if (frame_header->allow_screen_content_tools
        && frame_header->upscaled_width == frame_header->frame_width)
      WRITE_BITS (bw, frame_header->allow_intrabc, 1);
  } else {
    if (seq_header->enable_order_hint) {
      WRITE_BITS (bw, frame_header->frame_refs_short_signaling, 1);

      if (frame_header->frame_refs_short_signaling) {
        WRITE_BITS (bw, frame_header->last_frame_idx, 3);
        WRITE_BITS (bw, frame_header->gold_frame_idx, 3);
      }
    }

    for (i = 0; i < GST_AV1_REFS_PER_FRAME; i++) {
      if (!frame_header->frame_refs_short_signaling)
        WRITE_BITS (bw, frame_header->ref_frame_idx[i], 3);

      if (seq_header->frame_id_numbers_present_flag) {
        gint32 delta_frame_id /* DeltaFrameId */ ;

        if (id_len <= 0)
          goto error;

        delta_frame_id =
            frame_header->current_frame_id - frame_header->expected_frame_id[i];
        delta_frame_id = delta_frame_id + (1 << id_len);
        delta_frame_id = delta_frame_id % (1 << id_len);
        WRITE_BITS (bw, delta_frame_id - 1,
            seq_header->delta_frame_id_length_minus_2 + 2);
      }
    }

    if (frame_header->frame_size_override_flag
        && !frame_header->error_resilient_mode) {
      /* 5.9.7 */
      /* TODO: reuse reference frame width/height. Just disable now. */
      for (i = 0; i < GST_AV1_REFS_PER_FRAME; i++)
        WRITE_BITS (bw, 0, 1);
    }

    if (!_av1_bit_writer_frame_size (frame_header, seq_header, bw, space))
      goto error;

    if (!_av1_bit_writer_render_size (frame_header, bw, space))
      goto error;

    if (!frame_header->force_integer_mv)
      WRITE_BITS (bw, frame_header->allow_high_precision_mv, 1);

    WRITE_BITS (bw, frame_header->is_filter_switchable, 1);
    if (!frame_header->is_filter_switchable)
      WRITE_BITS (bw, frame_header->interpolation_filter, 2);

    WRITE_BITS (bw, frame_header->is_motion_mode_switchable, 1);

    if (!(frame_header->error_resilient_mode
            || !seq_header->enable_ref_frame_mvs))
      WRITE_BITS (bw, frame_header->use_ref_frame_mvs, 1);
  }

  if (!(seq_header->reduced_still_picture_header
          || frame_header->disable_cdf_update))
    WRITE_BITS (bw, frame_header->disable_frame_end_update_cdf, 1);

  if (!_av1_bit_writer_tile_info (frame_header, seq_header, bw, space))
    goto error;

  if (!_av1_bit_writer_quantization_params (frame_header, seq_header,
          qindex_offset, bw, space))
    goto error;

  if (!_av1_bit_writer_segmentation_params (frame_header, seq_header,
          segmentation_offset, bw, space))
    goto error;

  if (!_av1_bit_writer_delta_q_params (frame_header, seq_header, bw, space))
    goto error;

  if (!_av1_bit_writer_delta_lf_params (frame_header, seq_header, bw, space))
    goto error;

  if (!_av1_bit_writer_loop_filter_params (frame_header, seq_header,
          loopfilter_offset, bw, space))
    goto error;

  if (!_av1_bit_writer_cdef_params (frame_header, seq_header,
          cdef_offset, cdef_size, bw, space))
    goto error;

  if (!_av1_bit_writer_loop_restoration_params (frame_header, seq_header,
          bw, space))
    goto error;

  /* 5.9.21 tx_mode() */
  if (frame_header->coded_lossless != 1) {
    /* Write the tx_mode_select bit. */
    if (frame_header->tx_mode == GST_AV1_TX_MODE_SELECT) {
      WRITE_BITS (bw, 1, 1);
    } else {
      WRITE_BITS (bw, 0, 1);
    }
  }

  /* 5.9.23 inlined frame_reference_mode () */
  if (!frame_header->frame_is_intra)
    WRITE_BITS (bw, frame_header->reference_select, 1);

  if (!_av1_bit_writer_skip_mode_params (frame_header, seq_header, bw, space))
    goto error;

  if (!(frame_header->frame_is_intra || frame_header->error_resilient_mode ||
          !seq_header->enable_warped_motion))
    WRITE_BITS (bw, frame_header->allow_warped_motion, 1);

  WRITE_BITS (bw, frame_header->reduced_tx_set, 1);

  if (!_av1_bit_writer_global_motion_params (frame_header,
          seq_header, bw, space))
    goto error;

  if (!_av1_bit_writer_film_grain_params (frame_header, seq_header, bw, space))
    goto error;

success:
  *space = TRUE;
  return TRUE;

error:
  GST_WARNING ("failed to write frame header");
  *space = have_space;
  return FALSE;
}

/**
 * gst_av1_bit_writer_frame_header_obu:
 * @frame_hdr: the frame header of #GstAV1FrameHeaderOBU to write
 * @seq_hdr: the sequence header of #GstAV1SequenceHeaderOBU to refer
 * @temporal_id: specifies the temporal level of the data contained in the OBU.
 * @spatial_id: specifies the spatial level of the data contained in the OBU.
 * @size_field: whether the OBU header contains the OBU size.
 * @data: (out): the bit stream generated by the frame header
 * @size: (inout): the size in bytes of the input and output
 *
 * Generating the according AV1 bit stream OBU by providing the frame header.
 *
 * Returns: a #GstAV1BitWriterResult
 *
 * Since: 1.22
 **/
GstAV1BitWriterResult
gst_av1_bit_writer_frame_header_obu (const GstAV1FrameHeaderOBU * frame_hdr,
    const GstAV1SequenceHeaderOBU * seq_hdr, guint8 temporal_id,
    guint8 spatial_id, gboolean size_field, guint8 * data, guint * size)
{
  return gst_av1_bit_writer_frame_header_obu_with_offsets (frame_hdr, seq_hdr,
      temporal_id, spatial_id, size_field, 0, NULL, NULL, NULL, NULL, NULL,
      data, size);
}

/**
 * gst_av1_bit_writer_frame_header_obu_with_offsets:
 * @frame_hdr: the frame header of #GstAV1FrameHeaderOBU to write
 * @seq_hdr: the sequence header of #GstAV1SequenceHeaderOBU to refer
 * @temporal_id: specifies the temporal level of the data contained in the OBU.
 * @spatial_id: specifies the spatial level of the data contained in the OBU.
 * @size_field: whether the OBU header contains the OBU size.
 * @size_field_size: >0 means a fixed OBU header size field.
 * @qindex_offset: (out): return the qindex fields offset in bits.
 * @segmentation_offset: (out): return the segmentation fields offset in bits.
 * @lf_offset: (out): return the loopfilter fields offset in bits.
 * @cdef_offset: (out): return the cdef fields offset in bits.
 * @cdef_size: (out): return the cdef fields size in bits.
 * @data: (out): the bit stream generated by the frame header
 * @size: (inout): the size in bytes of the input and output
 *
 * While Generating the according AV1 bit stream OBU by providing the frame
 * header, this function also return bit offsets of qindex, segmentation and
 * cdef, etc. These offsets can help to change the content of these fields
 * later. This function is useful if the encoder may change the content of
 * the frame header after generating it. For example, some HW needs user to
 * provide a frame header before the real encoding job, and it will change
 * the according fields in the frame header during the real encoding job.
 *
 * Returns: a #GstAV1BitWriterResult
 *
 * Since: 1.22
 **/
GstAV1BitWriterResult
gst_av1_bit_writer_frame_header_obu_with_offsets (const GstAV1FrameHeaderOBU *
    frame_hdr, const GstAV1SequenceHeaderOBU * seq_hdr, guint8 temporal_id,
    guint8 spatial_id, gboolean size_field, guint size_field_size,
    guint * qindex_offset, guint * segmentation_offset, guint * lf_offset,
    guint * cdef_offset, guint * cdef_size, guint8 * data, guint * size)
{
  gboolean have_space = TRUE;
  GstBitWriter bw;
  guint payload_size;
  guint header_size;

  g_return_val_if_fail (frame_hdr != NULL, GST_AV1_BIT_WRITER_ERROR);
  g_return_val_if_fail (seq_hdr != NULL, GST_AV1_BIT_WRITER_ERROR);
  g_return_val_if_fail (temporal_id < GST_AV1_MAX_NUM_TEMPORAL_LAYERS,
      GST_AV1_BIT_WRITER_ERROR);
  g_return_val_if_fail (spatial_id < GST_AV1_MAX_NUM_SPATIAL_LAYERS,
      GST_AV1_BIT_WRITER_ERROR);
  g_return_val_if_fail (data != NULL, GST_AV1_BIT_WRITER_ERROR);
  g_return_val_if_fail (size != NULL, GST_AV1_BIT_WRITER_ERROR);
  g_return_val_if_fail (*size > 0, GST_AV1_BIT_WRITER_ERROR);

  gst_bit_writer_init_with_data (&bw, data, *size, FALSE);

  WRITE_BITS (&bw, 0, 1);
  /* obu_type */
  WRITE_BITS (&bw, GST_AV1_OBU_FRAME_HEADER, 4);
  /* obu_extention_flag */
  if (temporal_id > 0 || spatial_id > 0) {
    WRITE_BITS (&bw, 1, 1);
  } else {
    WRITE_BITS (&bw, 0, 1);
  }

  /* obu_has_size_field */
  if (size_field > 0) {
    WRITE_BITS (&bw, 1, 1);
  } else {
    WRITE_BITS (&bw, 0, 1);
  }
  /* obu_reserved_1bit */
  WRITE_BITS (&bw, 0, 1);

  header_size = 1;

  /* 5.3.3 OBU extension header */
  if (temporal_id > 0 || spatial_id > 0) {
    WRITE_BITS (&bw, temporal_id, 3);
    WRITE_BITS (&bw, spatial_id, 2);
    /* obu_extension_header_reserved_3bits */
    WRITE_BITS (&bw, 0, 3);
    header_size = 2;
  }

  /* Write size field later */

  if (!_av1_bit_writer_uncompressed_frame_header (frame_hdr, seq_hdr,
          temporal_id, spatial_id, qindex_offset, segmentation_offset,
          lf_offset, cdef_offset, cdef_size, &bw, &have_space))
    goto error;

  /* Add trailings. */
  WRITE_BITS (&bw, 1, 1);
  if (!gst_bit_writer_align_bytes (&bw, 0)) {
    have_space = FALSE;
    goto error;
  }

  payload_size = gst_bit_writer_get_size (&bw);
  g_assert (payload_size % 8 == 0);
  payload_size /= 8;
  payload_size -= header_size;

  gst_bit_writer_reset (&bw);

  if (size_field) {
    size_field_size = _av1_bit_writer_add_size_field (data, size,
        header_size, payload_size, size_field_size, &have_space);
    if (!size_field_size)
      goto error;
  } else {
    *size = header_size + payload_size;
    size_field_size = 0;
  }

  if (qindex_offset)
    *qindex_offset = *qindex_offset + size_field_size * 8;
  if (segmentation_offset)
    *segmentation_offset = *segmentation_offset + size_field_size * 8;
  if (lf_offset)
    *lf_offset = *lf_offset + size_field_size * 8;
  if (cdef_offset)
    *cdef_offset = *cdef_offset + size_field_size * 8;

  return GST_AV1_BIT_WRITER_OK;

error:
  gst_bit_writer_reset (&bw);
  *size = 0;
  return have_space ? GST_AV1_BIT_WRITER_INVALID_DATA :
      GST_AV1_BIT_WRITER_NO_MORE_SPACE;
}

/**
 * gst_av1_bit_writer_temporal_delimiter_obu:
 * @size_field: whether the header contain size feild
 * @data: (out): the bit stream generated
 * @size: (inout): the size in bytes of the input and output
 *
 * Generating the according temporal delimiter AV1 bit stream OBU.
 *
 * Returns: a #GstAV1BitWriterResult
 *
 * Since: 1.22
 **/
GstAV1BitWriterResult
gst_av1_bit_writer_temporal_delimiter_obu (gboolean size_field,
    guint8 * data, guint * size)
{
  gboolean have_space = TRUE;
  GstBitWriter bw;

  g_return_val_if_fail (data != NULL, GST_AV1_BIT_WRITER_ERROR);
  g_return_val_if_fail (size != NULL, GST_AV1_BIT_WRITER_ERROR);
  g_return_val_if_fail (*size > 0, GST_AV1_BIT_WRITER_ERROR);

  gst_bit_writer_init_with_data (&bw, data, *size, FALSE);

  /* obu_forbidden_bit */
  WRITE_BITS (&bw, 0, 1);
  /* obu_type */
  WRITE_BITS (&bw, GST_AV1_OBU_TEMPORAL_DELIMITER, 4);
  /* obu_extention_flag */
  WRITE_BITS (&bw, 0, 1);
  /* obu_has_size_field */
  if (size_field) {
    WRITE_BITS (&bw, 1, 1);
  } else {
    WRITE_BITS (&bw, 0, 1);
  }
  /* obu_reserved_1bit */
  WRITE_BITS (&bw, 0, 1);

  /* No need to add trailings */

  /* header_size is 1 and payload_size is 0 */
  if (size_field) {
    if (!_av1_bit_writer_add_size_field (data, size, 1, 0, 0, &have_space))
      goto error;
  } else {
    *size = 1 + 0;
  }

  return GST_AV1_BIT_WRITER_OK;

error:
  gst_bit_writer_reset (&bw);
  *size = 0;
  return have_space ? GST_AV1_BIT_WRITER_INVALID_DATA :
      GST_AV1_BIT_WRITER_NO_MORE_SPACE;
}

/* 5.8.2 */
static gboolean
_av1_bit_writer_metadata_itut_t35 (const GstAV1MetadataITUT_T35 * itut_t35,
    GstBitWriter * bw, gboolean * space)
{
  gboolean have_space = TRUE;

  GST_DEBUG ("writing metadata itut t35");

  WRITE_BITS (bw, itut_t35->itu_t_t35_country_code, 8);

  if (itut_t35->itu_t_t35_country_code == 0xFF)
    WRITE_BITS (bw, itut_t35->itu_t_t35_country_code_extention_byte, 8);

  *space = TRUE;
  return TRUE;

error:
  GST_WARNING ("failed to write metadata itut t35");
  *space = have_space;
  return FALSE;
}

/* 5.8.3 */
static gboolean
_av1_bit_writer_metadata_hdr_cll (const GstAV1MetadataHdrCll * hdr_cll,
    GstBitWriter * bw, gboolean * space)
{
  gboolean have_space = TRUE;

  GST_DEBUG ("writing metadata hdr cll");

  WRITE_BITS (bw, hdr_cll->max_cll, 16);
  WRITE_BITS (bw, hdr_cll->max_fall, 16);

  *space = TRUE;
  return TRUE;

error:
  GST_WARNING ("failed to write metadata hdr cll");
  *space = have_space;
  return FALSE;
}

/* 5.8.4 */
static gboolean
_av1_bit_writer_metadata_hdr_mdcv (const GstAV1MetadataHdrMdcv * hdr_mdcv,
    GstBitWriter * bw, gboolean * space)
{
  gboolean have_space = TRUE;
  gint i;

  GST_DEBUG ("writing metadata hdr mdcv");

  for (i = 0; i < 3; i++) {
    WRITE_BITS (bw, hdr_mdcv->primary_chromaticity_x[i], 16);
    WRITE_BITS (bw, hdr_mdcv->primary_chromaticity_y[i], 16);
  }

  WRITE_BITS (bw, hdr_mdcv->white_point_chromaticity_x, 16);
  WRITE_BITS (bw, hdr_mdcv->white_point_chromaticity_y, 16);

  WRITE_BITS (bw, hdr_mdcv->luminance_max, 32);
  WRITE_BITS (bw, hdr_mdcv->luminance_min, 32);

  *space = TRUE;
  return TRUE;

error:
  GST_WARNING ("failed to write metadata hdr mdcv");
  *space = have_space;
  return FALSE;
}

/* 5.8.5 */
static gboolean
_av1_bit_writer_metadata_scalability (const GstAV1MetadataScalability *
    scalability, GstBitWriter * bw, gboolean * space)
{
  gboolean have_space = TRUE;
  guint i, j;

  GST_DEBUG ("writing metadata scalability");

  WRITE_BITS (bw, scalability->scalability_mode_idc, 8);

  if (scalability->scalability_mode_idc != GST_AV1_SCALABILITY_SS)
    goto success;

  /* 5.8.6 */
  WRITE_BITS (bw, scalability->spatial_layers_cnt_minus_1, 2);
  WRITE_BITS (bw, scalability->spatial_layer_dimensions_present_flag, 1);
  WRITE_BITS (bw, scalability->spatial_layer_description_present_flag, 1);
  WRITE_BITS (bw, scalability->temporal_group_description_present_flag, 1);
  /* scalability_structure_reserved_3bits */
  WRITE_BITS (bw, 0, 3);

  if (scalability->spatial_layer_dimensions_present_flag) {
    for (i = 0; i <= scalability->spatial_layers_cnt_minus_1; i++) {
      WRITE_BITS (bw, scalability->spatial_layer_max_width[i], 16);
      WRITE_BITS (bw, scalability->spatial_layer_max_height[i], 16);
    }
  }

  if (scalability->spatial_layer_description_present_flag) {
    for (i = 0; i <= scalability->spatial_layers_cnt_minus_1; i++)
      WRITE_BITS (bw, scalability->spatial_layer_ref_id[i], 8);
  }

  if (scalability->temporal_group_description_present_flag) {
    WRITE_BITS (bw, scalability->temporal_group_size, 8);

    for (i = 0; i < scalability->temporal_group_size; i++) {
      WRITE_BITS (bw, scalability->temporal_group_temporal_id[i], 3);
      WRITE_BITS (bw,
          scalability->temporal_group_temporal_switching_up_point_flag[i], 1);
      WRITE_BITS (bw,
          scalability->temporal_group_spatial_switching_up_point_flag[i], 1);
      WRITE_BITS (bw, scalability->temporal_group_ref_cnt[i], 3);
      for (j = 0; j < scalability->temporal_group_ref_cnt[i]; j++)
        WRITE_BITS (bw, scalability->temporal_group_ref_pic_diff[i][j], 8);
    }
  }

success:
  *space = TRUE;
  return TRUE;

error:
  GST_WARNING ("failed to write metadata scalability");
  *space = have_space;
  return FALSE;
}

/**
 * gst_av1_bit_writer_metadata_obu:
 * @metadata: the meta data of #GstAV1MetadataOBU to write
 * @temporal_id: specifies the temporal level of the data contained in the OBU.
 * @spatial_id: specifies the spatial level of the data contained in the OBU.
 * @size_field: whether the header contain size feild
 * @data: (out): the bit stream generated by the meta data
 * @size: (inout): the size in bytes of the input and output
 *
 * Generating the according AV1 bit stream OBU by providing the meta data.
 *
 * Returns: a #GstAV1BitWriterResult
 *
 * Since: 1.22
 **/
GstAV1BitWriterResult
gst_av1_bit_writer_metadata_obu (const GstAV1MetadataOBU * metadata,
    guint8 temporal_id, guint8 spatial_id, gboolean size_field,
    guint8 * data, guint * size)
{
  gboolean have_space = TRUE;
  GstBitWriter bw;
  guint header_size;
  guint payload_size;
  guint8 metadata_type_data[4];
  guint metadata_size;
  guint i;

  g_return_val_if_fail (metadata != NULL, GST_AV1_BIT_WRITER_ERROR);
  g_return_val_if_fail (temporal_id < GST_AV1_MAX_NUM_TEMPORAL_LAYERS,
      GST_AV1_BIT_WRITER_ERROR);
  g_return_val_if_fail (spatial_id < GST_AV1_MAX_NUM_SPATIAL_LAYERS,
      GST_AV1_BIT_WRITER_ERROR);
  g_return_val_if_fail (data != NULL, GST_AV1_BIT_WRITER_ERROR);
  g_return_val_if_fail (size != NULL, GST_AV1_BIT_WRITER_ERROR);
  g_return_val_if_fail (*size > 0, GST_AV1_BIT_WRITER_ERROR);

  gst_bit_writer_init_with_data (&bw, data, *size, FALSE);

  /* obu_forbidden_bit */
  WRITE_BITS (&bw, 0, 1);
  /* obu_type */
  WRITE_BITS (&bw, GST_AV1_OBU_METADATA, 4);
  /* obu_extention_flag */
  if (temporal_id > 0 || spatial_id > 0) {
    WRITE_BITS (&bw, 1, 1);
  } else {
    WRITE_BITS (&bw, 0, 1);
  }
  /* obu_has_size_field */
  if (size_field) {
    WRITE_BITS (&bw, 1, 1);
  } else {
    WRITE_BITS (&bw, 0, 1);
  }
  /* obu_reserved_1bit */
  WRITE_BITS (&bw, 0, 1);

  header_size = 1;
  /* 5.3.3 OBU extension header */
  if (temporal_id > 0 || spatial_id > 0) {
    WRITE_BITS (&bw, temporal_id, 3);
    WRITE_BITS (&bw, spatial_id, 2);
    /* obu_extension_header_reserved_3bits */
    WRITE_BITS (&bw, 0, 3);
    header_size = 2;
  }

  /* Generate the metadata_type first */
  metadata_size = _av1_uleb_size_in_bytes (metadata->metadata_type);
  if (metadata_size > 4) {
    GST_WARNING ("Invalid metadata_type");
    goto error;
  }
  if (!_av1_encode_uleb (metadata->metadata_type,
          sizeof (metadata->metadata_type),
          metadata_type_data, metadata_size)) {
    GST_WARNING ("Failed to write metadata_type");
    goto error;
  }
  for (i = 0; i < metadata_size; i++)
    WRITE_BITS (&bw, metadata_type_data[i], 8);

  switch (metadata->metadata_type) {
    case GST_AV1_METADATA_TYPE_ITUT_T35:
      if (!_av1_bit_writer_metadata_itut_t35 (&metadata->itut_t35,
              &bw, &have_space))
        goto error;
      break;
    case GST_AV1_METADATA_TYPE_HDR_CLL:
      if (!_av1_bit_writer_metadata_hdr_cll (&metadata->hdr_cll,
              &bw, &have_space))
        goto error;
      break;
    case GST_AV1_METADATA_TYPE_HDR_MDCV:
      if (!_av1_bit_writer_metadata_hdr_mdcv (&metadata->hdr_mdcv,
              &bw, &have_space))
        goto error;
      break;
    case GST_AV1_METADATA_TYPE_SCALABILITY:
      if (!_av1_bit_writer_metadata_scalability (&metadata->scalability,
              &bw, &have_space))
        goto error;
      break;
    default:
      GST_WARNING ("Unsupported metadata_type");
      goto error;
  }

  /* Add trailings. */
  WRITE_BITS (&bw, 1, 1);
  if (!gst_bit_writer_align_bytes (&bw, 0)) {
    have_space = FALSE;
    goto error;
  }

  payload_size = gst_bit_writer_get_size (&bw);
  g_assert (payload_size % 8 == 0);
  payload_size /= 8;
  payload_size -= header_size;

  gst_bit_writer_reset (&bw);

  if (size_field) {
    if (!_av1_bit_writer_add_size_field (data, size,
            header_size, payload_size, 0, &have_space))
      goto error;
  } else {
    *size = header_size + payload_size;
  }

  return GST_AV1_BIT_WRITER_OK;

error:
  gst_bit_writer_reset (&bw);
  *size = 0;
  return have_space ? GST_AV1_BIT_WRITER_INVALID_DATA :
      GST_AV1_BIT_WRITER_NO_MORE_SPACE;
}
