mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-25 11:11:08 +00:00
2488 lines
72 KiB
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;
|
|
}
|