H.264 序列参数集(SPS)

基本结构(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];
	}
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

毕加索解锁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值