h264-sps-parser
Version:
Parse H.264 SPS NAL units
206 lines (183 loc) • 6.49 kB
text/typescript
import { Bitstream } from "./bitstream";
import { getVUIParams, VUIParams } from "./vui";
export type FrameCropping = {
left: number;
right: number;
top: number;
bottom: number;
};
export { VUIParams, Bitstream };
export type SPSInfo = {
sps_id: number;
profile_idc: number;
level_idc: number;
profile_compatibility: number;
frame_mbs_only_flag: 0|1;
pic_width_in_mbs: number;
pic_height_in_map_units: number;
frame_cropping_flag: 0|1;
frame_cropping: FrameCropping;
chroma_format_idc: number;
bit_depth_luma: number;
bit_depth_chroma: number;
color_plane_flag: 0|1;
qpprime_y_zero_transform_bypass_flag: 0|1;
seq_scaling_matrix_present_flag: 0|1;
seq_scaling_matrix: number[][];
log2_max_frame_num: number;
pic_order_cnt_type: number;
delta_pic_order_always_zero_flag: 0|1;
offset_for_non_ref_pic: number;
offset_for_top_to_bottom_field: number;
offset_for_ref_frame: number[];
log2_max_pic_order_cnt_lsb: number;
max_num_ref_frames: number;
gaps_in_frame_num_value_allowed_flag: 0|1;
mb_adaptive_frame_field_flag: 0|1;
direct_8x8_inference_flag: 0|1;
vui_parameters_present_flag: 0|1;
vui_parameters: VUIParams;
};
function scaling_list(stream: Bitstream, sizeOfScalingList: number, use_default: number[], i: number): number[] {
let lastScale = 8;
let nextScale = 8;
const scalingList = [];
for (let j = 0; j < sizeOfScalingList; j++) {
if (nextScale !== 0) {
const deltaScale = stream.SignedExpGolomb();
nextScale = (lastScale + deltaScale + 256) % 256;
use_default[i] = +(j === 0 && nextScale === 0);
}
if (nextScale) { lastScale = nextScale; }
scalingList[j] = lastScale;
}
return scalingList;
}
function getFrameCropping(flag: 0|1, stream: Bitstream): FrameCropping {
if (!flag) return { left: 0, right: 0, top: 0, bottom: 0 };
const left = stream.ExpGolomb();
const right = stream.ExpGolomb();
const top = stream.ExpGolomb();
const bottom = stream.ExpGolomb();
return { left, right, top, bottom };
}
export function parse(nalu: Uint8Array): SPSInfo {
if ((nalu[0] & 0x1F) !== 7) throw new Error("Not an SPS unit");
const stream = new Bitstream(new DataView(nalu.buffer, nalu.byteOffset + 4));
const profile_idc = nalu[1];
const profile_compatibility = nalu[2];
const level_idc = nalu[3];
const sps_id = stream.ExpGolomb();
let chroma_format_idc = 1;
let bit_depth_luma = 0;
let bit_depth_chroma = 0;
let color_plane_flag: 0|1 = 0;
let qpprime_y_zero_transform_bypass_flag: 0|1 = 0;
let seq_scaling_matrix_present_flag: 0|1 = 0;
const scaling_list_4x4: number[][] = [];
const scaling_list_8x8: number[][] = [];
const use_default_scaling_matrix_4x4_flag: number[] = [];
const use_default_scaling_matrix_8x8_flag: number[] = [];
if (profile_idc === 100 || profile_idc === 110 ||
profile_idc === 122 || profile_idc === 244 || profile_idc === 44 ||
profile_idc === 83 || profile_idc === 86 || profile_idc === 118 ||
profile_idc === 128 || profile_idc === 138 || profile_idc === 139 ||
profile_idc === 134 || profile_idc === 135) {
chroma_format_idc = stream.ExpGolomb();
let limit = 8;
if (chroma_format_idc === 3) {
limit = 12;
color_plane_flag = stream.readBit();
}
bit_depth_luma = stream.ExpGolomb() + 8;
bit_depth_chroma = stream.ExpGolomb() + 8;
qpprime_y_zero_transform_bypass_flag = stream.readBit();
seq_scaling_matrix_present_flag = stream.readBit();
if (seq_scaling_matrix_present_flag) {
let i = 0;
for (; i < 6; i++) {
if (stream.readBit()) { //seq_scaling_list_present_flag
scaling_list_4x4.push(
scaling_list(
stream, 16,
use_default_scaling_matrix_4x4_flag, i,
)
);
}
}
for (; i < limit; i++) {
if (stream.readBit()) { //seq_scaling_list_present_flag
scaling_list_8x8.push(
scaling_list(
stream, 64,
use_default_scaling_matrix_8x8_flag, i - 6,
)
);
}
}
}
}
const log2_max_frame_num = stream.ExpGolomb() + 4;
const pic_order_cnt_type = stream.ExpGolomb();
let delta_pic_order_always_zero_flag: 0|1 = 0;
let offset_for_non_ref_pic = 0;
let offset_for_top_to_bottom_field = 0;
const offset_for_ref_frame: number[] = [];
let log2_max_pic_order_cnt_lsb = 0;
if (pic_order_cnt_type === 0) {
log2_max_pic_order_cnt_lsb = stream.ExpGolomb() + 4;
} else if (pic_order_cnt_type === 1) {
delta_pic_order_always_zero_flag = stream.readBit();
offset_for_non_ref_pic = stream.SignedExpGolomb();
offset_for_top_to_bottom_field = stream.SignedExpGolomb();
const num_ref_frames_in_pic_order_cnt_cycle = stream.ExpGolomb();
for (let i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) {
offset_for_ref_frame.push(stream.SignedExpGolomb());
}
}
const max_num_ref_frames = stream.ExpGolomb();
const gaps_in_frame_num_value_allowed_flag = stream.readBit();
const pic_width_in_mbs = stream.ExpGolomb() + 1;
const pic_height_in_map_units = stream.ExpGolomb() + 1;
const frame_mbs_only_flag = stream.readBit();
let mb_adaptive_frame_field_flag: 0|1 = 0;
if (!frame_mbs_only_flag) {
mb_adaptive_frame_field_flag = stream.readBit();
}
const direct_8x8_inference_flag = stream.readBit();
const frame_cropping_flag = stream.readBit();
const frame_cropping = getFrameCropping(frame_cropping_flag, stream);
const vui_parameters_present_flag = stream.readBit();
const vui_parameters = getVUIParams(vui_parameters_present_flag, stream);
return {
sps_id,
profile_compatibility,
profile_idc,
level_idc,
chroma_format_idc,
bit_depth_luma,
bit_depth_chroma,
color_plane_flag,
qpprime_y_zero_transform_bypass_flag,
seq_scaling_matrix_present_flag,
seq_scaling_matrix: scaling_list_4x4,
log2_max_frame_num,
pic_order_cnt_type,
delta_pic_order_always_zero_flag,
offset_for_non_ref_pic,
offset_for_top_to_bottom_field,
offset_for_ref_frame,
log2_max_pic_order_cnt_lsb,
max_num_ref_frames,
gaps_in_frame_num_value_allowed_flag,
pic_width_in_mbs,
pic_height_in_map_units,
frame_mbs_only_flag,
mb_adaptive_frame_field_flag,
direct_8x8_inference_flag,
frame_cropping_flag,
frame_cropping,
vui_parameters_present_flag,
vui_parameters,
};
}