Files
grd/grd-nal-writer.c

887 lines
28 KiB
C
Raw Normal View History

2026-02-13 13:06:50 +09:00
/*
* Copyright (C) 2022 Pascal Nowack
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include "config.h"
#include "grd-nal-writer.h"
#define H264_PROFILE_HIGH 100
/* See also E.2.1 VUI parameters semantics (Rec. ITU-T H.264 (08/2021)) */
#define H264_Extended_SAR 255
#define H264_NAL_REF_IDC_ZERO 0
#define H264_NAL_REF_IDC_MEDIUM 2
#define H264_NAL_REF_IDC_HIGH 3
/*
* See also Table 7-1 NAL unit type codes, syntax element categories,
* and NAL unit type classes (Rec. ITU-T H.264 (08/2021))
*/
#define H264_NAL_UNIT_TYPE_SLICE_NON_IDR 1
#define H264_NAL_UNIT_TYPE_SLICE_IDR 5
#define H264_NAL_UNIT_TYPE_SPS 7
#define H264_NAL_UNIT_TYPE_PPS 8
#define H264_NAL_UNIT_TYPE_AUD 9
/*
* See also Table 7-6 Name association to slice_type
* (Rec. ITU-T H.264 (08/2021))
*/
#define H264_SLICE_TYPE_P 0
#define H264_SLICE_TYPE_B 1
#define H264_SLICE_TYPE_I 2
#define BITSTREAM_ALLOCATION_STEP 4096
typedef struct
{
uint32_t *buffer;
uint32_t bit_offset;
uint32_t capacity;
} NalBitstream;
struct _GrdNalWriter
{
GObject parent;
NalBitstream *nal_bitstream;
};
G_DEFINE_TYPE (GrdNalWriter, grd_nal_writer, G_TYPE_OBJECT)
static void
start_bitstream (GrdNalWriter *nal_writer)
{
g_assert (!nal_writer->nal_bitstream);
nal_writer->nal_bitstream = g_new0 (NalBitstream, 1);
}
static uint32_t
swap_byte_order (uint32_t value)
{
uint8_t *ptr = (uint8_t *) &value;
return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
}
static uint8_t *
end_bitstream (GrdNalWriter *nal_writer,
uint32_t *bitstream_length)
{
NalBitstream *nal_bitstream = nal_writer->nal_bitstream;
uint32_t *buffer = nal_bitstream->buffer;
uint32_t offset_in_dword;
uint32_t byte_pos;
uint32_t bits_left;
byte_pos = nal_bitstream->bit_offset >> 5;
offset_in_dword = nal_bitstream->bit_offset & 0x1F;
bits_left = 32 - offset_in_dword;
if (bits_left)
buffer[byte_pos] = swap_byte_order (buffer[byte_pos] << bits_left);
*bitstream_length = nal_bitstream->bit_offset;
g_clear_pointer (&nal_writer->nal_bitstream, g_free);
return (uint8_t *) buffer;
}
static void
ensure_remaining_capacity (NalBitstream *nal_bitstream,
uint32_t required_capacity_in_bits)
{
uint32_t new_capacity;
if (required_capacity_in_bits <= nal_bitstream->capacity * 32)
return;
new_capacity = nal_bitstream->capacity + BITSTREAM_ALLOCATION_STEP;
g_assert (required_capacity_in_bits <= new_capacity * 32);
nal_bitstream->buffer = g_realloc (nal_bitstream->buffer,
new_capacity * sizeof (uint32_t));
nal_bitstream->capacity = new_capacity;
}
static void
write_u (GrdNalWriter *nal_writer,
uint32_t value,
uint32_t n_bits)
{
NalBitstream *nal_bitstream = nal_writer->nal_bitstream;
uint32_t offset_in_dword;
uint32_t byte_pos;
uint32_t bits_left;
if (n_bits == 0)
return;
ensure_remaining_capacity (nal_bitstream, nal_bitstream->bit_offset + n_bits);
byte_pos = nal_bitstream->bit_offset >> 5;
offset_in_dword = nal_bitstream->bit_offset & 0x1F;
bits_left = 32 - offset_in_dword;
if (!offset_in_dword)
nal_bitstream->buffer[byte_pos] = 0;
nal_bitstream->bit_offset += n_bits;
if (bits_left > n_bits)
{
nal_bitstream->buffer[byte_pos] <<= n_bits;
nal_bitstream->buffer[byte_pos] |= value;
return;
}
n_bits -= bits_left;
nal_bitstream->buffer[byte_pos] <<= bits_left;
nal_bitstream->buffer[byte_pos] |= value >> n_bits;
nal_bitstream->buffer[byte_pos] =
swap_byte_order (nal_bitstream->buffer[byte_pos]);
nal_bitstream->buffer[++byte_pos] = value;
}
/* Exponential Golomb coding (unsigned) */
static void
write_ue (GrdNalWriter *nal_writer,
uint32_t value)
{
uint32_t value_to_write;
uint32_t n_bits = 0;
uint32_t tmp;
/*
* Write down value + 1, but before that write down n - 1 zeros,
* where n represents the bits to be written for value + 1
*/
value_to_write = value + 1;
tmp = value_to_write;
while (tmp)
{
++n_bits;
tmp >>= 1;
}
if (n_bits > 1)
write_u (nal_writer, 0, n_bits - 1);
write_u (nal_writer, value_to_write, n_bits);
}
/* Exponential Golomb coding (signed) */
static void
write_se (GrdNalWriter *nal_writer,
int32_t value)
{
/*
* If the value is <= 0, map the value to -2 * value (even integer value),
* otherwise map it to 2 * value - 1 (odd integer value)
*/
if (value <= 0)
write_ue (nal_writer, -value << 1);
else
write_ue (nal_writer, (value << 1) - 1);
}
static void
write_nal_start_code_prefix (GrdNalWriter *nal_writer)
{
write_u (nal_writer, 0x00000001, 32);
}
static void
write_nal_header (GrdNalWriter *nal_writer,
uint8_t nal_ref_idc,
uint8_t nal_unit_type)
{
/* See also 7.3.1 NAL unit syntax (Rec. ITU-T H.264 (08/2021)) */
/* forbidden_zero_bit */
write_u (nal_writer, 0, 1);
/* nal_ref_idc */
write_u (nal_writer, nal_ref_idc, 2);
/* nal_unit_type */
write_u (nal_writer, nal_unit_type, 5);
}
static void
byte_align_bitstream (GrdNalWriter *nal_writer)
{
NalBitstream *nal_bitstream = nal_writer->nal_bitstream;
uint32_t offset_in_byte;
uint32_t bits_left;
offset_in_byte = nal_bitstream->bit_offset & 0x7;
bits_left = 8 - offset_in_byte;
if (!bits_left)
return;
/* rbsp_alignment_zero_bit */
write_u (nal_writer, 0, bits_left);
}
static void
write_trailing_bits (GrdNalWriter *nal_writer)
{
/* rbsp_stop_one_bit */
write_u (nal_writer, 1, 1);
byte_align_bitstream (nal_writer);
}
static void
write_access_unit_delimiter (GrdNalWriter *nal_writer)
{
uint32_t primary_pic_type;
primary_pic_type = 1;
write_u (nal_writer, primary_pic_type, 3);
}
uint8_t *
grd_nal_writer_get_aud_bitstream (GrdNalWriter *nal_writer,
uint32_t *bitstream_length)
{
uint8_t *bitstream;
start_bitstream (nal_writer);
write_nal_start_code_prefix (nal_writer);
write_nal_header (nal_writer, H264_NAL_REF_IDC_ZERO, H264_NAL_UNIT_TYPE_AUD);
write_access_unit_delimiter (nal_writer);
write_trailing_bits (nal_writer);
bitstream = end_bitstream (nal_writer, bitstream_length);
g_assert (*bitstream_length % 8 == 0);
return bitstream;
}
static void
write_vui_parameters (GrdNalWriter *nal_writer,
const VAEncSequenceParameterBufferH264 *sequence_param)
{
uint32_t aspect_ratio_info_present_flag;
uint32_t overscan_info_present_flag;
uint32_t video_signal_type_present_flag;
uint32_t chroma_loc_info_present_flag;
uint32_t timing_info_present_flag;
uint32_t nal_hrd_parameters_present_flag;
uint32_t vcl_hrd_parameters_present_flag;
uint32_t pic_struct_present_flag;
uint32_t bitstream_restriction_flag;
g_assert (sequence_param->vui_parameters_present_flag);
aspect_ratio_info_present_flag =
sequence_param->vui_fields.bits.aspect_ratio_info_present_flag;
timing_info_present_flag =
sequence_param->vui_fields.bits.timing_info_present_flag;
bitstream_restriction_flag =
sequence_param->vui_fields.bits.bitstream_restriction_flag;
overscan_info_present_flag = 0;
video_signal_type_present_flag = 0;
chroma_loc_info_present_flag = 0;
nal_hrd_parameters_present_flag = 0;
vcl_hrd_parameters_present_flag = 0;
pic_struct_present_flag = 0;
/*
* See also E.1.1 VUI parameters syntax (Rec. ITU-T H.264 (08/2021))
*
* Not all paths are covered, only the ones relevant for GNOME Remote Desktop.
* Unhandled branches are preceded with an assertion.
*/
/* aspect_ratio_info_present_flag */
write_u (nal_writer, aspect_ratio_info_present_flag, 1);
if (aspect_ratio_info_present_flag)
{
/* aspect_ratio_idc */
write_u (nal_writer, sequence_param->aspect_ratio_idc, 8);
if (sequence_param->aspect_ratio_idc == H264_Extended_SAR)
{
/* sar_width */
write_u (nal_writer, sequence_param->sar_width, 16);
/* sar_height */
write_u (nal_writer, sequence_param->sar_height, 16);
}
}
/* overscan_info_present_flag */
write_u (nal_writer, overscan_info_present_flag, 1);
g_assert (!overscan_info_present_flag);
/* video_signal_type_present_flag */
write_u (nal_writer, video_signal_type_present_flag, 1);
g_assert (!video_signal_type_present_flag);
/* chroma_loc_info_present_flag */
write_u (nal_writer, chroma_loc_info_present_flag, 1);
g_assert (!chroma_loc_info_present_flag);
/* timing_info_present_flag */
write_u (nal_writer, timing_info_present_flag, 1);
if (timing_info_present_flag)
{
uint32_t fixed_frame_rate_flag =
sequence_param->vui_fields.bits.fixed_frame_rate_flag;
/* num_units_in_tick */
write_u (nal_writer, sequence_param->num_units_in_tick, 32);
/* time_scale */
write_u (nal_writer, sequence_param->time_scale, 32);
/* fixed_frame_rate_flag */
write_u (nal_writer, fixed_frame_rate_flag, 1);
}
/* nal_hrd_parameters_present_flag */
write_u (nal_writer, nal_hrd_parameters_present_flag, 1);
g_assert (!nal_hrd_parameters_present_flag);
/* vcl_hrd_parameters_present_flag */
write_u (nal_writer, vcl_hrd_parameters_present_flag, 1);
g_assert (!vcl_hrd_parameters_present_flag);
g_assert (!nal_hrd_parameters_present_flag &&
!vcl_hrd_parameters_present_flag);
/* pic_struct_present_flag */
write_u (nal_writer, pic_struct_present_flag, 1);
/* bitstream_restriction_flag */
write_u (nal_writer, bitstream_restriction_flag, 1);
if (bitstream_restriction_flag)
{
uint32_t motion_vectors_over_pic_boundaries_flag;
uint32_t max_bytes_per_pic_denom;
uint32_t max_bits_per_mb_denom;
uint32_t log2_max_mv_length_horizontal;
uint32_t log2_max_mv_length_vertical;
uint32_t max_num_reorder_frames;
uint32_t max_dec_frame_buffering;
log2_max_mv_length_horizontal =
sequence_param->vui_fields.bits.log2_max_mv_length_horizontal;
log2_max_mv_length_vertical =
sequence_param->vui_fields.bits.log2_max_mv_length_vertical;
motion_vectors_over_pic_boundaries_flag = 1;
max_bytes_per_pic_denom = 0;
max_bits_per_mb_denom = 0;
max_num_reorder_frames = 0;
max_dec_frame_buffering = 1;
/* motion_vectors_over_pic_boundaries_flag */
write_u (nal_writer, motion_vectors_over_pic_boundaries_flag, 1);
/* max_bytes_per_pic_denom */
write_ue (nal_writer, max_bytes_per_pic_denom);
/* max_bits_per_mb_denom */
write_ue (nal_writer, max_bits_per_mb_denom);
/* log2_max_mv_length_horizontal */
write_ue (nal_writer, log2_max_mv_length_horizontal);
/* log2_max_mv_length_vertical */
write_ue (nal_writer, log2_max_mv_length_vertical);
/* max_num_reorder_frames */
write_ue (nal_writer, max_num_reorder_frames);
/* max_dec_frame_buffering */
write_ue (nal_writer, max_dec_frame_buffering);
}
}
static void
write_sps_data (GrdNalWriter *nal_writer,
const VAEncSequenceParameterBufferH264 *sequence_param)
{
uint32_t profile_idc;
uint32_t constraint_set0_flag;
uint32_t constraint_set1_flag;
uint32_t constraint_set2_flag;
uint32_t constraint_set3_flag;
uint32_t constraint_set4_flag;
uint32_t constraint_set5_flag;
uint32_t chroma_format_idc;
uint32_t qpprime_y_zero_transform_bypass_flag;
uint32_t seq_scaling_matrix_present_flag;
uint32_t log2_max_frame_num_minus4;
uint32_t pic_order_cnt_type;
uint32_t gaps_in_frame_num_value_allowed_flag;
uint32_t pic_height_in_map_units;
uint32_t frame_mbs_only_flag;
uint32_t direct_8x8_inference_flag;
g_assert (sequence_param->picture_width_in_mbs > 0);
frame_mbs_only_flag = sequence_param->seq_fields.bits.frame_mbs_only_flag;
/*
* See also 7.4.2.1.1 Sequence parameter set data semantics
* (Rec. ITU-T H.264 (08/2021))
*/
profile_idc = H264_PROFILE_HIGH;
constraint_set0_flag = 0;
constraint_set1_flag = 0;
constraint_set2_flag = 0;
constraint_set3_flag = 0;
g_assert (profile_idc == H264_PROFILE_HIGH);
g_assert (frame_mbs_only_flag == 1);
/* frame_mbs_only_flag is equal to 1 */
constraint_set4_flag = 1;
g_assert (profile_idc == H264_PROFILE_HIGH);
/* No B-slices are present in the coded video sequence */
constraint_set5_flag = 1;
chroma_format_idc = sequence_param->seq_fields.bits.chroma_format_idc;
seq_scaling_matrix_present_flag =
sequence_param->seq_fields.bits.seq_scaling_matrix_present_flag;
log2_max_frame_num_minus4 =
sequence_param->seq_fields.bits.log2_max_frame_num_minus4;
pic_order_cnt_type = sequence_param->seq_fields.bits.pic_order_cnt_type;
direct_8x8_inference_flag =
sequence_param->seq_fields.bits.direct_8x8_inference_flag;
qpprime_y_zero_transform_bypass_flag = 0;
gaps_in_frame_num_value_allowed_flag = 0;
g_assert (frame_mbs_only_flag);
pic_height_in_map_units = sequence_param->picture_height_in_mbs;
g_assert (pic_height_in_map_units > 0);
/*
* See also 7.3.2.1.1 Sequence parameter set data syntax
* (Rec. ITU-T H.264 (08/2021))
*
* Not all paths are covered, only the ones relevant for GNOME Remote Desktop.
* Unhandled branches are preceded with an assertion.
*/
/* profile_idc */
write_u (nal_writer, profile_idc, 8);
/* constraint_set0_flag */
write_u (nal_writer, constraint_set0_flag, 1);
/* constraint_set1_flag */
write_u (nal_writer, constraint_set1_flag, 1);
/* constraint_set2_flag */
write_u (nal_writer, constraint_set2_flag, 1);
/* constraint_set3_flag */
write_u (nal_writer, constraint_set3_flag, 1);
/* constraint_set4_flag */
write_u (nal_writer, constraint_set4_flag, 1);
/* constraint_set5_flag */
write_u (nal_writer, constraint_set5_flag, 1);
/* reserved_zero_2bits */
write_u (nal_writer, 0, 2);
/* level_idc */
write_u (nal_writer, sequence_param->level_idc, 8);
/* seq_parameter_set_id */
write_ue (nal_writer, sequence_param->seq_parameter_set_id);
g_assert (profile_idc == H264_PROFILE_HIGH);
/* chroma_format_idc */
write_ue (nal_writer, chroma_format_idc);
g_assert (chroma_format_idc != 3);
/* bit_depth_luma_minus8 */
write_ue (nal_writer, sequence_param->bit_depth_luma_minus8);
/* bit_depth_chroma_minus8 */
write_ue (nal_writer, sequence_param->bit_depth_chroma_minus8);
/* qpprime_y_zero_transform_bypass_flag */
write_u (nal_writer, qpprime_y_zero_transform_bypass_flag, 1);
/* seq_scaling_matrix_present_flag */
write_u (nal_writer, seq_scaling_matrix_present_flag, 1);
g_assert (!seq_scaling_matrix_present_flag);
/* log2_max_frame_num_minus4 */
write_ue (nal_writer, log2_max_frame_num_minus4);
/* pic_order_cnt_type */
write_ue (nal_writer, pic_order_cnt_type);
if (pic_order_cnt_type == 0)
g_assert_not_reached ();
else if (pic_order_cnt_type == 1)
g_assert_not_reached ();
/* max_num_ref_frames */
write_ue (nal_writer, sequence_param->max_num_ref_frames);
/* gaps_in_frame_num_value_allowed_flag */
write_u (nal_writer, gaps_in_frame_num_value_allowed_flag, 1);
/* pic_width_in_mbs_minus1 */
write_ue (nal_writer, sequence_param->picture_width_in_mbs - 1);
/* pic_height_in_map_units_minus1 */
write_ue (nal_writer, pic_height_in_map_units - 1);
/* frame_mbs_only_flag */
write_u (nal_writer, frame_mbs_only_flag, 1);
g_assert (frame_mbs_only_flag);
/* direct_8x8_inference_flag */
write_u (nal_writer, direct_8x8_inference_flag, 1);
/* frame_cropping_flag */
write_u (nal_writer, sequence_param->frame_cropping_flag, 1);
g_assert (!sequence_param->frame_cropping_flag);
/* vui_parameters_present_flag */
write_u (nal_writer, sequence_param->vui_parameters_present_flag, 1);
if (sequence_param->vui_parameters_present_flag)
write_vui_parameters (nal_writer, sequence_param);
}
uint8_t *
grd_nal_writer_get_sps_bitstream (GrdNalWriter *nal_writer,
const VAEncSequenceParameterBufferH264 *sequence_param,
uint32_t *bitstream_length)
{
uint8_t *bitstream;
start_bitstream (nal_writer);
write_nal_start_code_prefix (nal_writer);
write_nal_header (nal_writer, H264_NAL_REF_IDC_HIGH, H264_NAL_UNIT_TYPE_SPS);
write_sps_data (nal_writer, sequence_param);
write_trailing_bits (nal_writer);
bitstream = end_bitstream (nal_writer, bitstream_length);
g_assert (*bitstream_length % 8 == 0);
return bitstream;
}
static void
write_pps_data (GrdNalWriter *nal_writer,
const VAEncPictureParameterBufferH264 *picture_param)
{
uint32_t entropy_coding_mode_flag;
uint32_t bottom_field_pic_order_in_frame_present_flag;
uint32_t num_slice_groups_minus1;
uint32_t weighted_pred_flag;
uint32_t weighted_bipred_idc;
uint32_t pic_init_qs_minus26;
uint32_t deblocking_filter_control_present_flag;
uint32_t constrained_intra_pred_flag;
uint32_t redundant_pic_cnt_present_flag;
uint32_t transform_8x8_mode_flag;
uint32_t pic_scaling_matrix_present_flag;
entropy_coding_mode_flag =
picture_param->pic_fields.bits.entropy_coding_mode_flag;
bottom_field_pic_order_in_frame_present_flag =
picture_param->pic_fields.bits.pic_order_present_flag;
weighted_pred_flag = picture_param->pic_fields.bits.weighted_pred_flag;
weighted_bipred_idc = picture_param->pic_fields.bits.weighted_bipred_idc;
deblocking_filter_control_present_flag =
picture_param->pic_fields.bits.deblocking_filter_control_present_flag;
constrained_intra_pred_flag =
picture_param->pic_fields.bits.constrained_intra_pred_flag;
redundant_pic_cnt_present_flag =
picture_param->pic_fields.bits.redundant_pic_cnt_present_flag;
transform_8x8_mode_flag =
picture_param->pic_fields.bits.transform_8x8_mode_flag;
pic_scaling_matrix_present_flag =
picture_param->pic_fields.bits.pic_scaling_matrix_present_flag;
num_slice_groups_minus1 = 0;
pic_init_qs_minus26 = 0;
/*
* See also 7.3.2.2 Picture parameter set RBSP syntax
* (Rec. ITU-T H.264 (08/2021))
*
* Not all paths are covered, only the ones relevant for GNOME Remote Desktop.
* Unhandled branches are preceded with an assertion.
*/
/* pic_parameter_set_id */
write_ue (nal_writer, picture_param->pic_parameter_set_id);
/* seq_parameter_set_id */
write_ue (nal_writer, picture_param->seq_parameter_set_id);
/* entropy_coding_mode_flag */
write_u (nal_writer, entropy_coding_mode_flag, 1);
/* bottom_field_pic_order_in_frame_present_flag */
write_u (nal_writer, bottom_field_pic_order_in_frame_present_flag, 1);
/* num_slice_groups_minus1 */
write_ue (nal_writer, num_slice_groups_minus1);
g_assert (num_slice_groups_minus1 == 0);
/* num_ref_idx_l0_default_active_minus1 */
write_ue (nal_writer, picture_param->num_ref_idx_l0_active_minus1);
/* num_ref_idx_l1_default_active_minus1 */
write_ue (nal_writer, picture_param->num_ref_idx_l1_active_minus1);
/* weighted_pred_flag */
write_u (nal_writer, weighted_pred_flag, 1);
/* weighted_bipred_idc */
write_u (nal_writer, weighted_bipred_idc, 2);
/* pic_init_qp_minus26 */
write_se (nal_writer, picture_param->pic_init_qp - 26);
/* pic_init_qs_minus26 */
write_se (nal_writer, pic_init_qs_minus26);
/* chroma_qp_index_offset */
write_se (nal_writer, picture_param->chroma_qp_index_offset);
/* deblocking_filter_control_present_flag */
write_u (nal_writer, deblocking_filter_control_present_flag, 1);
/* constrained_intra_pred_flag */
write_u (nal_writer, constrained_intra_pred_flag, 1);
/* redundant_pic_cnt_present_flag */
write_u (nal_writer, redundant_pic_cnt_present_flag, 1);
/* more_rbsp_data */
/* transform_8x8_mode_flag */
write_u (nal_writer, transform_8x8_mode_flag, 1);
/* pic_scaling_matrix_present_flag */
write_u (nal_writer, pic_scaling_matrix_present_flag, 1);
g_assert (!pic_scaling_matrix_present_flag);
/* second_chroma_qp_index_offset */
write_se (nal_writer, picture_param->second_chroma_qp_index_offset);
}
uint8_t *
grd_nal_writer_get_pps_bitstream (GrdNalWriter *nal_writer,
const VAEncPictureParameterBufferH264 *picture_param,
uint32_t *bitstream_length)
{
uint8_t *bitstream;
start_bitstream (nal_writer);
write_nal_start_code_prefix (nal_writer);
write_nal_header (nal_writer, H264_NAL_REF_IDC_HIGH, H264_NAL_UNIT_TYPE_PPS);
write_pps_data (nal_writer, picture_param);
write_trailing_bits (nal_writer);
bitstream = end_bitstream (nal_writer, bitstream_length);
g_assert (*bitstream_length % 8 == 0);
return bitstream;
}
static void
write_ref_pic_list_modification (GrdNalWriter *nal_writer,
const VAEncSliceParameterBufferH264 *slice_param)
{
if (slice_param->slice_type != H264_SLICE_TYPE_I)
{
uint32_t ref_pic_list_modification_flag_l0;
ref_pic_list_modification_flag_l0 = 0;
/* ref_pic_list_modification_flag_l0 */
write_u (nal_writer, ref_pic_list_modification_flag_l0, 1);
g_assert (!ref_pic_list_modification_flag_l0);
}
g_assert (slice_param->slice_type != H264_SLICE_TYPE_B);
}
static void
write_dec_ref_pic_marking (GrdNalWriter *nal_writer,
const VAEncPictureParameterBufferH264 *picture_param)
{
if (picture_param->pic_fields.bits.idr_pic_flag)
{
uint32_t no_output_of_prior_pics_flag;
uint32_t long_term_reference_flag;
no_output_of_prior_pics_flag = 0;
long_term_reference_flag = 0;
/* no_output_of_prior_pics_flag */
write_u (nal_writer, no_output_of_prior_pics_flag, 1);
/* long_term_reference_flag */
write_u (nal_writer, long_term_reference_flag, 1);
}
else
{
uint32_t adaptive_ref_pic_marking_mode_flag;
adaptive_ref_pic_marking_mode_flag = 0;
/* adaptive_ref_pic_marking_mode_flag */
write_u (nal_writer, adaptive_ref_pic_marking_mode_flag, 1);
g_assert (!adaptive_ref_pic_marking_mode_flag);
}
}
static void
write_slice_header (GrdNalWriter *nal_writer,
const VAEncSliceParameterBufferH264 *slice_param,
const VAEncSequenceParameterBufferH264 *sequence_param,
const VAEncPictureParameterBufferH264 *picture_param,
const uint8_t nal_ref_idc)
{
uint32_t separate_colour_plane_flag;
uint32_t log2_max_frame_num;
uint16_t frame_num;
uint32_t frame_mbs_only_flag;
uint32_t pic_order_cnt_type;
uint32_t redundant_pic_cnt_present_flag;
uint32_t weighted_pred_flag;
uint32_t weighted_bipred_idc;
uint32_t entropy_coding_mode_flag;
uint32_t deblocking_filter_control_present_flag;
uint32_t num_slice_groups_minus1;
frame_num = picture_param->frame_num;
log2_max_frame_num =
sequence_param->seq_fields.bits.log2_max_frame_num_minus4 + 4;
frame_mbs_only_flag = sequence_param->seq_fields.bits.frame_mbs_only_flag;
pic_order_cnt_type = sequence_param->seq_fields.bits.pic_order_cnt_type;
redundant_pic_cnt_present_flag =
picture_param->pic_fields.bits.redundant_pic_cnt_present_flag;
weighted_pred_flag = picture_param->pic_fields.bits.weighted_pred_flag;
weighted_bipred_idc = picture_param->pic_fields.bits.weighted_bipred_idc;
entropy_coding_mode_flag =
picture_param->pic_fields.bits.entropy_coding_mode_flag;
deblocking_filter_control_present_flag =
picture_param->pic_fields.bits.deblocking_filter_control_present_flag;
separate_colour_plane_flag = 0;
num_slice_groups_minus1 = 0;
/*
* See also 7.3.3 Slice header syntax (Rec. ITU-T H.264 (08/2021))
*
* Not all paths are covered, only the ones relevant for GNOME Remote Desktop.
* Unhandled branches are preceded with an assertion.
*/
/* first_mb_in_slice */
write_ue (nal_writer, slice_param->macroblock_address);
/* slice_type */
write_ue (nal_writer, slice_param->slice_type);
/* pic_parameter_set_id */
write_ue (nal_writer, slice_param->pic_parameter_set_id);
g_assert (!separate_colour_plane_flag);
/* frame_num */
write_u (nal_writer, frame_num, log2_max_frame_num);
g_assert (frame_mbs_only_flag);
if (picture_param->pic_fields.bits.idr_pic_flag)
{
/* idr_pic_id */
write_ue (nal_writer, slice_param->idr_pic_id);
}
if (pic_order_cnt_type == 0)
g_assert_not_reached ();
if (pic_order_cnt_type == 1)
g_assert_not_reached ();
g_assert (!redundant_pic_cnt_present_flag);
g_assert (slice_param->slice_type != H264_SLICE_TYPE_B);
if (slice_param->slice_type == H264_SLICE_TYPE_P)
{
/* num_ref_idx_active_override_flag */
write_u (nal_writer, slice_param->num_ref_idx_active_override_flag, 1);
g_assert (!slice_param->num_ref_idx_active_override_flag);
}
write_ref_pic_list_modification (nal_writer, slice_param);
g_assert (!weighted_pred_flag && !weighted_bipred_idc);
if (nal_ref_idc)
write_dec_ref_pic_marking (nal_writer, picture_param);
if (entropy_coding_mode_flag && slice_param->slice_type != H264_SLICE_TYPE_I)
{
/* cabac_init_idc */
write_ue (nal_writer, slice_param->cabac_init_idc);
}
/* slice_qp_delta */
write_se (nal_writer, slice_param->slice_qp_delta);
g_assert (slice_param->slice_type == H264_SLICE_TYPE_I ||
slice_param->slice_type == H264_SLICE_TYPE_P);
g_assert (!deblocking_filter_control_present_flag);
g_assert (num_slice_groups_minus1 == 0);
}
static void
get_nal_header_parameters (const VAEncSliceParameterBufferH264 *slice_param,
const VAEncPictureParameterBufferH264 *picture_param,
uint8_t *nal_ref_idc,
uint8_t *nal_unit_type)
{
switch (slice_param->slice_type)
{
case H264_SLICE_TYPE_I:
*nal_ref_idc = H264_NAL_REF_IDC_HIGH;
if (picture_param->pic_fields.bits.idr_pic_flag)
*nal_unit_type = H264_NAL_UNIT_TYPE_SLICE_IDR;
else
*nal_unit_type = H264_NAL_UNIT_TYPE_SLICE_NON_IDR;
break;
case H264_SLICE_TYPE_P:
*nal_ref_idc = H264_NAL_REF_IDC_MEDIUM;
*nal_unit_type = H264_NAL_UNIT_TYPE_SLICE_NON_IDR;
break;
default:
g_assert_not_reached ();
}
}
uint8_t *
grd_nal_writer_get_slice_header_bitstream (GrdNalWriter *nal_writer,
const VAEncSliceParameterBufferH264 *slice_param,
const VAEncSequenceParameterBufferH264 *sequence_param,
const VAEncPictureParameterBufferH264 *picture_param,
uint32_t *bitstream_length)
{
uint8_t nal_ref_idc;
uint8_t nal_unit_type;
get_nal_header_parameters (slice_param, picture_param,
&nal_ref_idc, &nal_unit_type);
start_bitstream (nal_writer);
write_nal_start_code_prefix (nal_writer);
write_nal_header (nal_writer, nal_ref_idc, nal_unit_type);
write_slice_header (nal_writer, slice_param, sequence_param, picture_param,
nal_ref_idc);
return end_bitstream (nal_writer, bitstream_length);
}
GrdNalWriter *
grd_nal_writer_new (void)
{
return g_object_new (GRD_TYPE_NAL_WRITER, NULL);
}
static void
grd_nal_writer_dispose (GObject *object)
{
GrdNalWriter *nal_writer = GRD_NAL_WRITER (object);
g_assert (!nal_writer->nal_bitstream);
G_OBJECT_CLASS (grd_nal_writer_parent_class)->dispose (object);
}
static void
grd_nal_writer_init (GrdNalWriter *nal_writer)
{
}
static void
grd_nal_writer_class_init (GrdNalWriterClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = grd_nal_writer_dispose;
}