From 040042113e0534e68dfb98366d82132894a2c9ed Mon Sep 17 00:00:00 2001 From: He Junyan Date: Tue, 19 Jul 2022 17:33:13 +0800 Subject: [PATCH] codecparsers: Implement the AV1 bit code writer Part-of: --- .../gst/codecparsers/gstav1bitwriter.c | 2469 +++++++++++++++++ .../gst/codecparsers/gstav1bitwriter.h | 88 + .../gst-libs/gst/codecparsers/meson.build | 1 + 3 files changed, 2558 insertions(+) create mode 100644 subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gstav1bitwriter.c create mode 100644 subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gstav1bitwriter.h diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gstav1bitwriter.c b/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gstav1bitwriter.c new file mode 100644 index 0000000000..4fe133f44e --- /dev/null +++ b/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gstav1bitwriter.c @@ -0,0 +1,2469 @@ +/* GStreamer + * Copyright (C) 2022 Intel Corporation + * Author: He Junyan + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstav1bitwriter.h" +#include + +#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; +} diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gstav1bitwriter.h b/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gstav1bitwriter.h new file mode 100644 index 0000000000..b77eafd57a --- /dev/null +++ b/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gstav1bitwriter.h @@ -0,0 +1,88 @@ +/* GStreamer + * Copyright (C) 2022 Intel Corporation + * Author: He Junyan + * + * 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. + */ + +#ifndef __GST_AV1_BIT_WRITER_H__ +#define __GST_AV1_BIT_WRITER_H__ + +#include +#include + +G_BEGIN_DECLS + +/** + * GstAV1BitWriterResult: + * @GST_AV1_BIT_WRITER_OK: The writing succeeded + * @GST_AV1_BIT_WRITER_INVALID_DATA: The input data to write is invalid + * @GST_AV1_BIT_WRITER_NO_MORE_SPACE: The output does not have enough size + * @GST_AV1_BIT_WRITER_ERROR: An general error occurred when writing + * + * The result of writing AV1 data into bit stream. + * + * Since: 1.22 + */ +typedef enum +{ + GST_AV1_BIT_WRITER_OK, + GST_AV1_BIT_WRITER_INVALID_DATA, + GST_AV1_BIT_WRITER_NO_MORE_SPACE, + GST_AV1_BIT_WRITER_ERROR +} GstAV1BitWriterResult; + +GST_CODEC_PARSERS_API +GstAV1BitWriterResult gst_av1_bit_writer_sequence_header_obu (const GstAV1SequenceHeaderOBU * seq_hdr, + gboolean size_field, + guint8 * data, + guint * size); +GST_CODEC_PARSERS_API +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); +GST_CODEC_PARSERS_API +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); +GST_CODEC_PARSERS_API +GstAV1BitWriterResult gst_av1_bit_writer_temporal_delimiter_obu (gboolean size_field, + guint8 * data, + guint * size); +GST_CODEC_PARSERS_API +GstAV1BitWriterResult gst_av1_bit_writer_metadata_obu (const GstAV1MetadataOBU * metadata, + guint8 temporal_id, + guint8 spatial_id, + gboolean size_field, + guint8 * data, + guint * size); +G_END_DECLS + +#endif /* __GST_AV1_BIT_WRITER_H__ */ diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/meson.build b/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/meson.build index c06c2992f5..7e968354a8 100644 --- a/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/meson.build +++ b/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/meson.build @@ -18,6 +18,7 @@ codecparser_sources = files([ 'gstav1parser.c', 'gsth264bitwriter.c', 'gsth265bitwriter.c', + 'gstav1bitwriter.c', ]) codecparser_headers = [ 'codecparsers-prelude.h',