基本结构(SPS)
H.264 中的 SPS(Sequence Parameter Set)是一种参数集,用于描述视频序列的特征和配置信息。SPS 是在 H.264 视频流中的一个元数据单元,它包含了视频编码器的设置和视频序列的特性。
SPS 包含了以下重要的信息:
- Profile 和 Level:指定视频编码的配置和兼容性级别。
- 图像尺寸和宽高比:描述视频图像的尺寸和宽高比。
- 帧率和比特率:指定视频的帧率和比特率,影响视频的流畅度和压缩效率。
- 帧间预测和帧内预测设置:描述视频编码中的预测模式和帧类型。
- 量化参数:控制视频质量和压缩比例的参数。
- 熵编码模式:指定熵编码的方式,影响编码的复杂度和压缩效率。
- 参考帧设置:指定参考帧的配置和使用方式,用于帧间预测。
seq_parameter_set_rbsp()
封装结构
// SPS
typedef struct {
uint32_t profile_idc; //!< 当前H.264的编码配置, baseline(66) | main(77) | extended(88)
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 reserved_zero_2bits;
uint32_t level_idc; //!< 编码等级 0~51
uint32_t seq_parameter_set_id;
// if(profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 144) {
uint32_t chroma_format_idc;
// if( chroma_format_idc = = 3 )
uint32_t residual_colour_transform_flag;
// }
uint32_t bit_depth_luma_minus8;
uint32_t bit_depth_chroma_minus8;
uint32_t qpprime_y_zero_transform_bypass_flag;
uint32_t seq_scaling_matrix_present_flag;
// if (seq_scaling_matrix_present_flag)
// for(i = 0; i < 8; i++) {
uint32_t seq_scaling_list_present_flag[12];
// }
// }
// }
uint32_t log2_max_frame_num_minus4;
uint32_t pic_order_cnt_type;
// if(pic_order_cnt_type == 0) {
uint32_t log2_max_pic_order_cnt_lsb_minus4;
// }
// else if(pic_order_cnt_type == 1) {
uint32_t delta_pic_order_always_zero_flag;
int offset_for_non_ref_pic;
int offset_for_top_to_bottom_field;
uint32_t num_ref_frames_in_pic_order_cnt_cycle;
int offset_for_ref_frame[256];
// }
uint32_t max_num_ref_frames; //!< 参考帧的最大数目
uint32_t gaps_in_frame_num_value_allowed_flag;
uint32_t pic_width_in_mbs_minus1; //!< 用于计算图像的宽度,单位为宏块个数
uint32_t pic_height_in_map_units_minus1; //!< 度量视频中一帧图像的高度
uint32_t frame_mbs_only_flag;
// if (!frame_mbs_only_flag) {
uint32_t mb_adaptive_frame_field_flag;
// }
uint32_t direct_8x8_inference_flag;
uint32_t frame_cropping_flag;
// if (frame_cropping_flag) {
uint32_t frame_crop_left_offset;
uint32_t frame_crop_right_offset;
uint32_t frame_crop_top_offset;
uint32_t frame_crop_bottom_offset;
// }
uint32_t vui_parameters_present_flag;
// if (vui_parameters_present_flag) {
NaluSpsVuiParameters vui_parameters;
// }
} NaluSPS;
解析过程
/// 解析SPS头信息
bool parse_nalu_sps(uint8_t *sps_data, uint32_t sps_size, NaluSPS &sps) {
if (sps_size < 4)
return false;
/// 找到 SPS NAL 单元的起始位置
int start_code_length = 0;
if (sps_data[0] == 0 && sps_data[1] == 0 && sps_data[2] == 1) { // TODO: 00 00 01
start_code_length = 3;
} else if (sps_data[0] == 0 && sps_data[1] == 0 && sps_data[2] == 0 && sps_data[3] == 1) { // TODO: 00 00 00 01
start_code_length = 4;
} else if ((sps_data[0] & 0x1f) == 7) { // TODO: 继StartCode后第一个字节以 0x67 开头
start_code_length = 0;
} else {
return false;
}
sps_data += start_code_length;
sps_size -= start_code_length;
/// 标准文档里注明: SPS、PPS、SEI的解析,基于RBSP语法
/// ITU-T Advanced Video Coding For Generic Audiovisual Services
std::vector<uint8_t> rbsp = EBSP2RBSP(sps_data, (int)sps_size);
bs_t *b = bs_new(rbsp.data(), rbsp.size());
/// Nalu Header
uint32_t forbidden_zero_bit = bs_read_u(b, 1);
uint32_t nal_ref_idc = bs_read_u(b, 2);
uint32_t nal_unit_type = bs_read_u(b, 5);
NaluHeader nalu_header;
nalu_header.nal_ref_idc = nal_ref_idc;
nalu_header.nal_unit_type = nal_unit_type;
/// 序列参数集(SPS)-RBSP语法
if (H264_NAL_SPS == (NAL_TYPE)nal_unit_type) {
memset(&sps, 0, sizeof(NaluSPS));
sps.profile_idc = bs_read_u8(b); // TODO: 当前H.264的编码配置, baseline(66) | main(77) | extended(88)
sps.constraint_set0_flag = bs_read_u(b, 1);
sps.constraint_set1_flag = bs_read_u(b, 1);
sps.constraint_set2_flag = bs_read_u(b, 1);
sps.constraint_set3_flag = bs_read_u(b, 1);
sps.constraint_set4_flag = bs_read_u(b, 1);
sps.constraint_set5_flag = bs_read_u(b, 1);
sps.reserved_zero_2bits = bs_read_u(b, 2);
sps.level_idc = bs_read_u8(b);
sps.seq_parameter_set_id = bs_read_ue(b);
if (sps.profile_idc == 100 || sps.profile_idc == 110 || sps.profile_idc == 122 || sps.profile_idc == 244 || sps.profile_idc == 44 ||
sps.profile_idc == 83 || sps.profile_idc == 86 || sps.profile_idc == 118 || sps.profile_idc == 128 || sps.profile_idc == 138) {
sps.chroma_format_idc = bs_read_ue(b);
if (sps.chroma_format_idc == 3) {
sps.residual_colour_transform_flag = bs_read_u(b, 1);
}
sps.bit_depth_luma_minus8 = bs_read_ue(b);
sps.bit_depth_chroma_minus8 = bs_read_ue(b);
sps.qpprime_y_zero_transform_bypass_flag = bs_read_u(b, 1);
sps.seq_scaling_matrix_present_flag = bs_read_u(b, 1);
if (sps.seq_scaling_matrix_present_flag) {
uint32_t *ScalingList4x4[12];
uint32_t UseDefaultScalingMatrix4x4Flag[12];
uint32_t *ScalingList8x8[12];
uint32_t UseDefaultScalingMatrix8x8Flag[12];
for (int i = 0; i < ((sps.chroma_format_idc != 3) ? 8 : 12); i++) {
sps.seq_scaling_list_present_flag[i] = bs_read_u(b, 1);
if (sps.seq_scaling_list_present_flag[i]) {
if (i < 6) {
scaling_list(b, ScalingList4x4[i], 16, UseDefaultScalingMatrix4x4Flag[i]);
} else {
scaling_list(b, ScalingList8x8[i - 6], 64, UseDefaultScalingMatrix8x8Flag[i - 6]);
}
}
}
}
}
sps.log2_max_frame_num_minus4 = bs_read_ue(b);
sps.pic_order_cnt_type = bs_read_ue(b);
if (sps.pic_order_cnt_type == 0) {
sps.log2_max_pic_order_cnt_lsb_minus4 = bs_read_ue(b);
} else if (sps.pic_order_cnt_type == 1) {
sps.delta_pic_order_always_zero_flag = bs_read_u(b, 1);
sps.offset_for_non_ref_pic = bs_read_se(b);
sps.offset_for_top_to_bottom_field = bs_read_se(b);
sps.num_ref_frames_in_pic_order_cnt_cycle = bs_read_ue(b);
for (int i = 0; i < sps.num_ref_frames_in_pic_order_cnt_cycle; i++) {
sps.offset_for_ref_frame[i] = bs_read_se(b);
}
}
sps.max_num_ref_frames = bs_read_ue(b); // TODO: 参考帧的最大数目
sps.gaps_in_frame_num_value_allowed_flag = bs_read_u(b, 1);
sps.pic_width_in_mbs_minus1 = bs_read_ue(b); // TODO: 用于计算图像的宽度,单位为宏块个数
sps.pic_height_in_map_units_minus1 = bs_read_ue(b); // TODO: 度量视频中一帧图像的高度
uint32_t width = (sps.pic_width_in_mbs_minus1 + 1) * 16;
uint32_t height = (sps.pic_height_in_map_units_minus1 + 1) * 16;
sps.frame_mbs_only_flag = bs_read_u(b, 1); // TODO: 宏块的编码方式
if (!sps.frame_mbs_only_flag) {
sps.mb_adaptive_frame_field_flag = bs_read_u(b, 1);
}
sps.direct_8x8_inference_flag = bs_read_u(b, 1);
sps.frame_cropping_flag = bs_read_u(b, 1);
if (sps.frame_cropping_flag) {
sps.frame_crop_left_offset = bs_read_ue(b);
sps.frame_crop_right_offset = bs_read_ue(b);
sps.frame_crop_top_offset = bs_read_ue(b);
sps.frame_crop_bottom_offset = bs_read_ue(b);
}
sps.vui_parameters_present_flag = bs_read_u(b, 1); // TODO: SPS中是否存在VUI信息?
if (sps.vui_parameters_present_flag) {
vui_parameters(b, sps);
}
rbsp_trailing_bits(b);
}
bs_free(b);
return true;
}
视频使用者信息(VUI)
vui_parameters 是视频编码中的一种参数,用于描述视频序列的附加信息,即视频使用者信息 (Video Usability Information)。vui_parameters 包含了与视频序列的使用和显示相关的配置信息,以提供更好的用户体验和兼容性。
vui_parameters 主要包括以下信息:
- 宽高比 (aspect_ratio_info):视频的显示宽高比,用于正确显示视频的宽高比例。
- 颜色参数 (color_primaries、transfer_characteristics、matrix_coefficients):视频的颜色空间信息,用于正确解释和显示视频的颜色。
- 时间相关信息 (timing_info):视频的时间参数,包括帧率和时间码等,用于正确解码和播放视频。
- 视频信号范围 (video_signal_type_present_flag):视频信号的范围,例如全范围或标准范围,用于正确显示视频的亮度和对比度。
- 音频同步信息 (nal_hrd_parameters、vcl_hrd_parameters):视频和音频之间的同步信息,确保音视频同步播放。
vui_parameters()
封装结构
// Vui Parameters
typedef struct {
uint32_t aspect_ratio_info_present_flag;
// if(aspect_ratio_info_present_flag) {
uint32_t aspect_ratio_idc;
// if (aspect_ratio_idc == Extended_SAR) {
uint32_t sar_width;
uint32_t sar_height;
// }
// }
uint32_t overscan_info_present_flag;
// if (overscan_info_present_flag) {
uint32_t overscan_appropriate_flag;
// }
uint32_t video_signal_type_present_flag;
// if (video_signal_type_present_flag) {
uint32_t video_format;
uint32_t video_full_range_flag;
uint32_t colour_description_present_flag;
// if (colour_description_present_flag) {
uint32_t colour_primaries;
uint32_t transfer_characteristics;
uint32_t matrix_coefficients;
// }
// }
uint32_t chroma_loc_info_present_flag;
// if (chroma_loc_info_present_flag) {
uint32_t chroma_sample_loc_type_top_field;
uint32_t chroma_sample_loc_type_bottom_field;
// }
uint32_t timing_info_present_flag;
// if (timing_info_present_flag) {
uint32_t num_units_in_tick;
uint32_t time_scale;
uint32_t fixed_frame_rate_flag;
// }
uint32_t nal_hrd_parameters_present_flag;
// if (nal_hrd_parameters_present_flag) {
NaluSpsHrdParameters nal_hrd_parameters_present;
// }
uint32_t vcl_hrd_parameters_present_flag;
// if (vcl_hrd_parameters_present_flag) {
NaluSpsHrdParameters vcl_hrd_parameters_present;
// }
// if (nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag) {
uint32_t low_delay_hrd_flag;
// }
uint32_t pic_struct_present_flag;
uint32_t bitstream_restriction_flag;
// 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 num_reorder_frames;
uint32_t max_dec_frame_buffering;
// }
} NaluSpsVuiParameters;
解析过程
void vui_parameters(bs_t *b, NaluSPS &sps) {
NaluSpsVuiParameters vui;
memset(&vui, 0, sizeof(NaluSpsVuiParameters));
vui.aspect_ratio_info_present_flag = bs_read_u1(b);
if (vui.aspect_ratio_info_present_flag) {
vui.aspect_ratio_idc = bs_read_u8(b);
if (vui.aspect_ratio_idc == 255) {
vui.sar_width = bs_read_u(b, 16);
vui.sar_height = bs_read_u(b, 16);
}
}
vui.overscan_info_present_flag = bs_read_u1(b);
if (vui.overscan_info_present_flag) {
vui.overscan_appropriate_flag = bs_read_u1(b);
}
vui.video_signal_type_present_flag = bs_read_u1(b);
if (vui.video_signal_type_present_flag) {
vui.video_format = bs_read_u(b, 3);
vui.video_full_range_flag = bs_read_u1(b);
vui.colour_description_present_flag = bs_read_u1(b);
if (vui.colour_description_present_flag) {
vui.colour_primaries = bs_read_u8(b);
vui.transfer_characteristics = bs_read_u8(b);
vui.matrix_coefficients = bs_read_u8(b);
}
}
vui.chroma_loc_info_present_flag = bs_read_u1(b);
if (vui.chroma_loc_info_present_flag) {
vui.chroma_sample_loc_type_top_field = bs_read_ue(b);
vui.chroma_sample_loc_type_bottom_field = bs_read_ue(b);
}
vui.timing_info_present_flag = bs_read_u1(b);
if (vui.timing_info_present_flag) {
vui.num_units_in_tick = bs_read_u(b, 32);
vui.time_scale = bs_read_u(b, 32);
vui.fixed_frame_rate_flag = bs_read_u1(b);
}
vui.nal_hrd_parameters_present_flag = bs_read_u1(b);
if (vui.nal_hrd_parameters_present_flag) {
hrd_parameters(b);
}
vui.vcl_hrd_parameters_present_flag = bs_read_u1(b);
if (vui.vcl_hrd_parameters_present_flag) {
hrd_parameters(b);
}
if (vui.nal_hrd_parameters_present_flag || vui.vcl_hrd_parameters_present_flag) {
vui.low_delay_hrd_flag = bs_read_u1(b);
}
vui.pic_struct_present_flag = bs_read_u1(b);
vui.bitstream_restriction_flag = bs_read_u1(b);
if (vui.bitstream_restriction_flag) {
vui.motion_vectors_over_pic_boundaries_flag = bs_read_u1(b);
vui.max_bytes_per_pic_denom = bs_read_ue(b);
vui.max_bits_per_mb_denom = bs_read_ue(b);
vui.log2_max_mv_length_horizontal = bs_read_ue(b);
vui.log2_max_mv_length_vertical = bs_read_ue(b);
vui.num_reorder_frames = bs_read_ue(b);
vui.max_dec_frame_buffering = bs_read_ue(b);
}
sps.vui_parameters = vui;
}
假定参考解码器(HRD)
hrd_parameters 是视频编码中的一种参数,用于描述视频编码的恢复点检测相关的信息。HRD 是 Hypothetical Reference Decoder(假设参考解码器)的缩写,它定义了视频编码的比特率、缓冲区大小和相关的时间参数。
hrd_parameters 包含了视频编码过程中的控制和管理参数,用于确保编码后的视频流在解码时能够正确恢复,并保持一定的稳定性和适应性。
hrd_parameters 主要包括以下信息:
- 比特率 (bit_rate):视频编码的平均比特率。
- 缓冲区大小 (cpb_size):解码器的缓冲区大小,用于存储已解码和待解码的视频数据。
- 初始缓冲区满度 (initial_cpb_fullness):初始时解码器缓冲区的填充级别,通常以百分比表示。
- 约束参数 (cpb_cnt_minus1、bit_rate_scale、cpb_size_scale):用于计算实际的缓冲区大小和填充级别。
hrd_parameters()
封装结构
typedef struct {
uint32_t cpb_cnt_minus1;
uint32_t bit_rate_scale;
uint32_t cpb_size_scale;
// for( SchedSelIdx = 0; SchedSelIdx <= cpb_cnt_minus1; SchedSelIdx++ ) {
uint32_t bit_rate_value_minus1[5];
uint32_t cpb_size_value_minus1[5];
uint32_t cbr_flag[5];
// }
uint32_t initial_cpb_removal_delay_length_minus1;
uint32_t cpb_removal_delay_length_minus1;
uint32_t dpb_output_delay_length_minus1;
uint32_t time_offset_length;
} NaluSpsHrdParameters;
解析过程
void hrd_parameters(bs_t *b, NaluSpsHrdParameters &out_hrd) {
NaluSpsHrdParameters hrd;
memset(&hrd, 0, sizeof(NaluSpsHrdParameters));
hrd.cpb_cnt_minus1 = bs_read_ue(b);
hrd.bit_rate_scale = bs_read_u(b, 4);
hrd.cpb_size_scale = bs_read_u(b, 4);
for (int SchedSelIdx = 0; SchedSelIdx <= hrd.cpb_cnt_minus1; SchedSelIdx++) {
hrd.bit_rate_value_minus1[SchedSelIdx] = bs_read_ue(b);
hrd.cpb_size_value_minus1[SchedSelIdx] = bs_read_ue(b);
hrd.cbr_flag[SchedSelIdx] = bs_read_u1(b);
}
hrd.initial_cpb_removal_delay_length_minus1 = bs_read_u(b, 5);
hrd.cpb_removal_delay_length_minus1 = bs_read_u(b, 5);
hrd.dpb_output_delay_length_minus1 = bs_read_u(b, 5);
hrd.time_offset_length = bs_read_u(b, 5);
}
其他附录
scaling_list()
/// 缩放比例列表
void scaling_list(bs_t *b, uint32_t *scalingList, int sizeOfScalingList, uint32_t useDefaultScalingMatrixFlag) {
int lastScale = 8;
int nextScale = 8;
for (int j = 0; j < sizeOfScalingList; j++) {
if (nextScale != 0) {
//!< delta_scale
int32_t delta_scale = bs_read_se(b);
nextScale = (lastScale + delta_scale + 256) % 256;
useDefaultScalingMatrixFlag = (j == 0 && nextScale == 0);
}
scalingList[j] = (nextScale == 0) ? lastScale : nextScale;
lastScale = (int)scalingList[j];
}
}
版权声明:本文为davidsguo008原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。