gstreamer/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gstav1bitwriter.c

2488 lines
72 KiB
C

/* 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.
*/
#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;
}