mirror of
https://github.com/morgan9e/grd
synced 2026-04-14 00:14:18 +09:00
887 lines
28 KiB
C
887 lines
28 KiB
C
/*
|
||
* 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;
|
||
}
|