@tianfeng98/hls.js
Version:
HLS.js is a JavaScript library that supports playing MPEG-TS and HEVC encoded HLS streams in browsers with support for MSE.
539 lines (506 loc) • 18.3 kB
text/typescript
import ExpGolomb from './exp-golomb';
class H265Parser {
static _ebsp2rbsp(uint8array) {
const src = uint8array;
const src_length = src.byteLength;
const dst = new Uint8Array(src_length);
let dst_idx = 0;
for (let i = 0; i < src_length; i++) {
if (i >= 2) {
// Unescape: Skip 0x03 after 00 00
if (src[i] === 0x03 && src[i - 1] === 0x00 && src[i - 2] === 0x00) {
continue;
}
}
dst[dst_idx] = src[i];
dst_idx++;
}
return new Uint8Array(dst.buffer, 0, dst_idx);
}
static parseVPS(uint8array) {
const rbsp = H265Parser._ebsp2rbsp(uint8array);
const gb = new ExpGolomb(rbsp);
/* remove NALu Header */
gb.readUByte();
gb.readUByte();
// VPS
const video_parameter_set_id = gb.readBits(4);
gb.readBits(2);
const max_layers_minus1 = gb.readBits(6);
const max_sub_layers_minus1 = gb.readBits(3);
const temporal_id_nesting_flag = gb.readBoolean();
// and more ...
return {
num_temporal_layers: max_sub_layers_minus1 + 1,
temporal_id_nested: temporal_id_nesting_flag,
};
}
static parseSPS(uint8array) {
const rbsp = H265Parser._ebsp2rbsp(uint8array);
let gb = new ExpGolomb(rbsp);
/* remove NALu Header */
gb.readUByte();
gb.readUByte();
let left_offset = 0,
right_offset = 0,
top_offset = 0,
bottom_offset = 0;
// SPS
const video_paramter_set_id = gb.readBits(4);
const max_sub_layers_minus1 = gb.readBits(3);
const temporal_id_nesting_flag = gb.readBoolean();
// profile_tier_level begin
const general_profile_space = gb.readBits(2);
const general_tier_flag = gb.readBoolean();
const general_profile_idc = gb.readBits(5);
const general_profile_compatibility_flags_1 = gb.readUByte();
const general_profile_compatibility_flags_2 = gb.readUByte();
const general_profile_compatibility_flags_3 = gb.readUByte();
const general_profile_compatibility_flags_4 = gb.readUByte();
const general_constraint_indicator_flags_1 = gb.readUByte();
const general_constraint_indicator_flags_2 = gb.readUByte();
const general_constraint_indicator_flags_3 = gb.readUByte();
const general_constraint_indicator_flags_4 = gb.readUByte();
const general_constraint_indicator_flags_5 = gb.readUByte();
const general_constraint_indicator_flags_6 = gb.readUByte();
const general_level_idc = gb.readUByte();
const sub_layer_profile_present_flag = [] as any[];
const sub_layer_level_present_flag = [] as any[];
for (let i = 0; i < max_sub_layers_minus1; i++) {
sub_layer_profile_present_flag.push(gb.readBoolean());
sub_layer_level_present_flag.push(gb.readBoolean() as any);
}
if (max_sub_layers_minus1 > 0) {
for (let i = max_sub_layers_minus1; i < 8; i++) {
gb.readBits(2);
}
}
for (let i = 0; i < max_sub_layers_minus1; i++) {
if (sub_layer_profile_present_flag[i]) {
gb.readUByte(); // sub_layer_profile_space, sub_layer_tier_flag, sub_layer_profile_idc
gb.readUByte();
gb.readUByte();
gb.readUByte();
gb.readUByte(); // sub_layer_profile_compatibility_flag
gb.readUByte();
gb.readUByte();
gb.readUByte();
gb.readUByte();
gb.readUByte();
gb.readUByte();
}
if (sub_layer_level_present_flag[i]) {
gb.readUByte();
}
}
// profile_tier_level end
const seq_parameter_set_id = gb.readUEG();
const chroma_format_idc = gb.readUEG();
if (chroma_format_idc == 3) {
gb.readBits(1); // separate_colour_plane_flag
}
const pic_width_in_luma_samples = gb.readUEG();
const pic_height_in_luma_samples = gb.readUEG();
const conformance_window_flag = gb.readBoolean();
if (conformance_window_flag) {
left_offset += gb.readUEG();
right_offset += gb.readUEG();
top_offset += gb.readUEG();
bottom_offset += gb.readUEG();
}
const bit_depth_luma_minus8 = gb.readUEG();
const bit_depth_chroma_minus8 = gb.readUEG();
const log2_max_pic_order_cnt_lsb_minus4 = gb.readUEG();
const sub_layer_ordering_info_present_flag = gb.readBoolean();
for (
let i = sub_layer_ordering_info_present_flag ? 0 : max_sub_layers_minus1;
i <= max_sub_layers_minus1;
i++
) {
gb.readUEG(); // max_dec_pic_buffering_minus1[i]
gb.readUEG(); // max_num_reorder_pics[i]
gb.readUEG(); // max_latency_increase_plus1[i]
}
const log2_min_luma_coding_block_size_minus3 = gb.readUEG();
const log2_diff_max_min_luma_coding_block_size = gb.readUEG();
const log2_min_transform_block_size_minus2 = gb.readUEG();
const log2_diff_max_min_transform_block_size = gb.readUEG();
const max_transform_hierarchy_depth_inter = gb.readUEG();
const max_transform_hierarchy_depth_intra = gb.readUEG();
const scaling_list_enabled_flag = gb.readBoolean();
if (scaling_list_enabled_flag) {
const sps_scaling_list_data_present_flag = gb.readBoolean();
if (sps_scaling_list_data_present_flag) {
for (let sizeId = 0; sizeId < 4; sizeId++) {
for (
let matrixId = 0;
matrixId < (sizeId === 3 ? 2 : 6);
matrixId++
) {
const scaling_list_pred_mode_flag = gb.readBoolean();
if (!scaling_list_pred_mode_flag) {
gb.readUEG(); // scaling_list_pred_matrix_id_delta
} else {
const coefNum = Math.min(64, 1 << (4 + (sizeId << 1)));
if (sizeId > 1) {
gb.readEG();
}
for (let i = 0; i < coefNum; i++) {
gb.readEG();
}
}
}
}
}
}
const amp_enabled_flag = gb.readBoolean();
const sample_adaptive_offset_enabled_flag = gb.readBoolean();
const pcm_enabled_flag = gb.readBoolean();
if (pcm_enabled_flag) {
gb.readUByte();
gb.readUEG();
gb.readUEG();
gb.readBoolean();
}
const num_short_term_ref_pic_sets = gb.readUEG();
let num_delta_pocs = 0;
for (let i = 0; i < num_short_term_ref_pic_sets; i++) {
let inter_ref_pic_set_prediction_flag = false;
if (i !== 0) {
inter_ref_pic_set_prediction_flag = gb.readBoolean();
}
if (inter_ref_pic_set_prediction_flag) {
if (i === num_short_term_ref_pic_sets) {
gb.readUEG();
}
gb.readBoolean();
gb.readUEG();
let next_num_delta_pocs = 0;
for (let j = 0; j <= num_delta_pocs; j++) {
const used_by_curr_pic_flag = gb.readBoolean();
let use_delta_flag = false;
if (!used_by_curr_pic_flag) {
use_delta_flag = gb.readBoolean();
}
if (used_by_curr_pic_flag || use_delta_flag) {
next_num_delta_pocs++;
}
}
num_delta_pocs = next_num_delta_pocs;
} else {
const num_negative_pics = gb.readUEG();
const num_positive_pics = gb.readUEG();
num_delta_pocs = num_negative_pics + num_positive_pics;
for (let j = 0; j < num_negative_pics; j++) {
gb.readUEG();
gb.readBoolean();
}
for (let j = 0; j < num_positive_pics; j++) {
gb.readUEG();
gb.readBoolean();
}
}
}
const long_term_ref_pics_present_flag = gb.readBoolean();
if (long_term_ref_pics_present_flag) {
const num_long_term_ref_pics_sps = gb.readUEG();
for (let i = 0; i < num_long_term_ref_pics_sps; i++) {
for (let j = 0; j < log2_max_pic_order_cnt_lsb_minus4 + 4; j++) {
gb.readBits(1);
}
gb.readBits(1);
}
}
//*
let default_display_window_flag = false; // for calc offset
let min_spatial_segmentation_idc = 0; // for hvcC
let sar_width = 1,
sar_height = 1;
let fps_fixed = false,
fps_den = 1,
fps_num = 1;
//*/
const sps_temporal_mvp_enabled_flag = gb.readBoolean();
const strong_intra_smoothing_enabled_flag = gb.readBoolean();
const vui_parameters_present_flag = gb.readBoolean();
if (vui_parameters_present_flag) {
const aspect_ratio_info_present_flag = gb.readBoolean();
if (aspect_ratio_info_present_flag) {
const aspect_ratio_idc = gb.readUByte();
const sar_w_table = [
1, 12, 10, 16, 40, 24, 20, 32, 80, 18, 15, 64, 160, 4, 3, 2,
];
const sar_h_table = [
1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99, 3, 2, 1,
];
if (aspect_ratio_idc > 0 && aspect_ratio_idc <= 16) {
sar_width = sar_w_table[aspect_ratio_idc - 1];
sar_height = sar_h_table[aspect_ratio_idc - 1];
} else if (aspect_ratio_idc === 255) {
sar_width = gb.readBits(16);
sar_height = gb.readBits(16);
}
}
const overscan_info_present_flag = gb.readBoolean();
if (overscan_info_present_flag) {
gb.readBoolean();
}
const video_signal_type_present_flag = gb.readBoolean();
if (video_signal_type_present_flag) {
gb.readBits(3);
gb.readBoolean();
const colour_description_present_flag = gb.readBoolean();
if (colour_description_present_flag) {
gb.readUByte();
gb.readUByte();
gb.readUByte();
}
}
const chroma_loc_info_present_flag = gb.readBoolean();
if (chroma_loc_info_present_flag) {
gb.readUEG();
gb.readUEG();
}
const neutral_chroma_indication_flag = gb.readBoolean();
const field_seq_flag = gb.readBoolean();
const frame_field_info_present_flag = gb.readBoolean();
default_display_window_flag = gb.readBoolean();
if (default_display_window_flag) {
gb.readUEG();
gb.readUEG();
gb.readUEG();
gb.readUEG();
}
const vui_timing_info_present_flag = gb.readBoolean();
if (vui_timing_info_present_flag) {
fps_den = gb.readBits(32);
fps_num = gb.readBits(32);
const vui_poc_proportional_to_timing_flag = gb.readBoolean();
if (vui_poc_proportional_to_timing_flag) {
gb.readUEG();
}
const vui_hrd_parameters_present_flag = gb.readBoolean();
if (vui_hrd_parameters_present_flag) {
const commonInfPresentFlag = 1;
let nal_hrd_parameters_present_flag = false;
let vcl_hrd_parameters_present_flag = false;
let sub_pic_hrd_params_present_flag = false;
if (commonInfPresentFlag) {
nal_hrd_parameters_present_flag = gb.readBoolean();
vcl_hrd_parameters_present_flag = gb.readBoolean();
if (
nal_hrd_parameters_present_flag ||
vcl_hrd_parameters_present_flag
) {
sub_pic_hrd_params_present_flag = gb.readBoolean();
if (sub_pic_hrd_params_present_flag) {
gb.readUByte();
gb.readBits(5);
gb.readBoolean();
gb.readBits(5);
}
const bit_rate_scale = gb.readBits(4);
const cpb_size_scale = gb.readBits(4);
if (sub_pic_hrd_params_present_flag) {
gb.readBits(4);
}
gb.readBits(5);
gb.readBits(5);
gb.readBits(5);
}
}
for (let i = 0; i <= max_sub_layers_minus1; i++) {
const fixed_pic_rate_general_flag = gb.readBoolean();
fps_fixed = fixed_pic_rate_general_flag;
let fixed_pic_rate_within_cvs_flag = true;
let cpbCnt = 1;
if (!fixed_pic_rate_general_flag) {
fixed_pic_rate_within_cvs_flag = gb.readBoolean();
}
let low_delay_hrd_flag = false;
if (fixed_pic_rate_within_cvs_flag) {
gb.readUEG();
} else {
low_delay_hrd_flag = gb.readBoolean();
}
if (!low_delay_hrd_flag) {
cpbCnt = gb.readUEG() + 1;
}
if (nal_hrd_parameters_present_flag) {
for (let j = 0; j < cpbCnt; j++) {
gb.readUEG();
gb.readUEG();
if (sub_pic_hrd_params_present_flag) {
gb.readUEG();
gb.readUEG();
}
}
gb.readBoolean();
}
if (vcl_hrd_parameters_present_flag) {
for (let j = 0; j < cpbCnt; j++) {
gb.readUEG();
gb.readUEG();
if (sub_pic_hrd_params_present_flag) {
gb.readUEG();
gb.readUEG();
}
}
gb.readBoolean();
}
}
}
}
const bitstream_restriction_flag = gb.readBoolean();
if (bitstream_restriction_flag) {
const tiles_fixed_structure_flag = gb.readBoolean();
const motion_vectors_over_pic_boundaries_flag = gb.readBoolean();
const restricted_ref_pic_lists_flag = gb.readBoolean();
min_spatial_segmentation_idc = gb.readUEG();
const max_bytes_per_pic_denom = gb.readUEG();
const max_bits_per_min_cu_denom = gb.readUEG();
const log2_max_mv_length_horizontal = gb.readUEG();
const log2_max_mv_length_vertical = gb.readUEG();
}
}
const sps_extension_flag = gb.readBoolean(); // ignore...
// for meta data
const codec_mimetype = `hvc1.${general_profile_idc}.1.L${general_level_idc}.B0`;
const sub_wc = chroma_format_idc === 1 || chroma_format_idc === 2 ? 2 : 1;
const sub_hc = chroma_format_idc === 1 ? 2 : 1;
const codec_width =
pic_width_in_luma_samples - (left_offset + right_offset) * sub_wc;
const codec_height =
pic_height_in_luma_samples - (top_offset + bottom_offset) * sub_hc;
let sar_scale = 1;
if (sar_width !== 1 && sar_height !== 1) {
sar_scale = sar_width / sar_height;
}
gb.destroy();
gb = null as any;
return {
codec_mimetype,
level_string: H265Parser.getLevelString(general_level_idc),
profile_idc: general_profile_idc,
bit_depth: bit_depth_luma_minus8 + 8,
ref_frames: 1, // FIXME!!!
chroma_format: chroma_format_idc,
chroma_format_string: H265Parser.getChromaFormatString(chroma_format_idc),
general_level_idc,
general_profile_space,
general_tier_flag,
general_profile_idc,
general_profile_compatibility_flags_1,
general_profile_compatibility_flags_2,
general_profile_compatibility_flags_3,
general_profile_compatibility_flags_4,
general_constraint_indicator_flags_1,
general_constraint_indicator_flags_2,
general_constraint_indicator_flags_3,
general_constraint_indicator_flags_4,
general_constraint_indicator_flags_5,
general_constraint_indicator_flags_6,
min_spatial_segmentation_idc,
constant_frame_rate: 0 /* FIXME!! fps_fixed ? 1 : 0? */,
chroma_format_idc,
bit_depth_luma_minus8,
bit_depth_chroma_minus8,
frame_rate: {
fixed: fps_fixed,
fps: fps_num / fps_den,
fps_den: fps_den,
fps_num: fps_num,
},
sar_ratio: {
width: sar_width,
height: sar_height,
},
codec_size: {
width: codec_width,
height: codec_height,
},
present_size: {
width: codec_width * sar_scale,
height: codec_height,
},
};
}
static parsePPS(uint8array) {
const rbsp = H265Parser._ebsp2rbsp(uint8array);
const gb = new ExpGolomb(rbsp);
/* remove NALu Header */
gb.readUByte();
gb.readUByte();
const pic_parameter_set_id = gb.readUEG();
const seq_parameter_set_id = gb.readUEG();
const dependent_slice_segments_enabled_flag = gb.readBoolean();
const output_flag_present_flag = gb.readBoolean();
const num_extra_slice_header_bits = gb.readBits(3);
const sign_data_hiding_enabled_flag = gb.readBoolean();
const cabac_init_present_flag = gb.readBoolean();
const num_ref_idx_l0_default_active_minus1 = gb.readUEG();
const num_ref_idx_l1_default_active_minus1 = gb.readUEG();
const init_qp_minus26 = gb.readEG();
const constrained_intra_pred_flag = gb.readBoolean();
const transform_skip_enabled_flag = gb.readBoolean();
const cu_qp_delta_enabled_flag = gb.readBoolean();
if (cu_qp_delta_enabled_flag) {
const diff_cu_qp_delta_depth = gb.readUEG();
}
const cb_qp_offset = gb.readEG();
const cr_qp_offset = gb.readEG();
const pps_slice_chroma_qp_offsets_present_flag = gb.readBoolean();
const weighted_pred_flag = gb.readBoolean();
const weighted_bipred_flag = gb.readBoolean();
const transquant_bypass_enabled_flag = gb.readBoolean();
const tiles_enabled_flag = gb.readBoolean();
const entropy_coding_sync_enabled_flag = gb.readBoolean();
// and more ...
// needs hvcC
let parallelismType = 1; // slice-based parallel decoding
if (entropy_coding_sync_enabled_flag && tiles_enabled_flag) {
parallelismType = 0; // mixed-type parallel decoding
} else if (entropy_coding_sync_enabled_flag) {
parallelismType = 3; // wavefront-based parallel decoding
} else if (tiles_enabled_flag) {
parallelismType = 2; // tile-based parallel decoding
}
return {
parallelismType,
};
}
static getChromaFormatString(chroma_idc) {
switch (chroma_idc) {
case 0:
return '4:0:0';
case 1:
return '4:2:0';
case 2:
return '4:2:2';
case 3:
return '4:4:4';
default:
return 'Unknown';
}
}
static getProfileString(profile_idc) {
switch (profile_idc) {
case 1:
return 'Main';
case 2:
return 'Main10';
case 3:
return 'MainSP';
case 4:
return 'Rext';
case 9:
return 'SCC';
default:
return 'Unknown';
}
}
static getLevelString(level_idc) {
return (level_idc / 30).toFixed(1);
}
}
export default H265Parser;