"use strict"; function H265SPSParser() { var vBitCount = 0; var spsMap = null; var pSPSBytes = null; function Constructor() { vBitCount = 0; spsMap = new Map(); } function get_bit(base, offset) { var vCurBytes = (vBitCount + offset) >> 3; offset = (vBitCount + offset) & 0x00000007; return (((base[(vCurBytes)])) >> (0x7 - (offset & 0x7))) & 0x1; } function read_bits(pBuf, vReadBits) { var vCurBytes = vBitCount / 8; var vCurBits = vBitCount % 8; var vOffset = 0; var vTmp = 0, vTmp2 = 0; if (vReadBits == 1) { vTmp = get_bit(pBuf, vOffset); } else { for (var i = 0; i < vReadBits; i++) { vTmp2 = get_bit(pBuf, i); vTmp = (vTmp << 1) + vTmp2; } } vBitCount += vReadBits; return vTmp; } function ue(base, offset) { var zeros = 0, vTmp = 0, vReturn = 0; var vIdx = offset; do { vTmp = get_bit(base, vIdx++); if (vTmp == 0) zeros++; } while (0 == vTmp); if (zeros == 0) { vBitCount += 1; return 0; } // insert first 1 bit vReturn = 1 << zeros; for (var i = zeros - 1; i >= 0; i-- , vIdx++) { vTmp = get_bit(base, vIdx); vReturn |= vTmp << i; } vBitCount += zeros * 2 + 1; return (vReturn - 1); } function se(base, offset) { var vReturn = ue(base, offset); if (vReturn & 0x1) { return (vReturn + 1) / 2; } else { return -vReturn / 2; } } function byte_aligned() { if ((vBitCount & 0x00000007) == 0) return 1; else return 0; } function profile_tier_level(profilePresentFlag, maxNumSubLayersMinus1) { if (profilePresentFlag) { spsMap.set("general_profile_space", read_bits(pSPSBytes, 2)); spsMap.set("general_tier_flag", read_bits(pSPSBytes, 1)); spsMap.set("general_profile_idc", read_bits(pSPSBytes, 5)); var generalProfileCompatibilityFlag = new Array(32); for (var j = 0; j < 32; j++) { generalProfileCompatibilityFlag[j] = read_bits(pSPSBytes, 1); } spsMap.set("general_progressive_source_flag", read_bits(pSPSBytes, 1)); spsMap.set("general_interlaced_source_flag", read_bits(pSPSBytes, 1)); spsMap.set("general_non_packed_constraint_flag", read_bits(pSPSBytes, 1)); spsMap.set("general_frame_only_constraint_flag", read_bits(pSPSBytes, 1)); var generalProfileIdc = spsMap.get("general_profile_idc"); if (generalProfileIdc === 4 || generalProfileCompatibilityFlag[4] || generalProfileIdc === 5 || generalProfileCompatibilityFlag[5] || generalProfileIdc === 6 || generalProfileCompatibilityFlag[6] || generalProfileIdc === 7 || generalProfileCompatibilityFlag[7]) { spsMap.set("general_max_12bit_constraint_flag", read_bits(pSPSBytes, 1)); spsMap.set("general_max_10bit_constraint_flag", read_bits(pSPSBytes, 1)); spsMap.set("general_max_8bit_constraint_flag", read_bits(pSPSBytes, 1)); spsMap.set("general_max_422chroma_constraint_flag", read_bits(pSPSBytes, 1)); spsMap.set("general_max_420chroma_constraint_flag", read_bits(pSPSBytes, 1)); spsMap.set("general_max_monochrome_constraint_flag", read_bits(pSPSBytes, 1)); spsMap.set("general_intra_constraint_flag", read_bits(pSPSBytes, 1)); spsMap.set("general_one_picture_only_constraint_flag", read_bits(pSPSBytes, 1)); spsMap.set("general_lower_bit_rate_constraint_flag", read_bits(pSPSBytes, 1)); spsMap.set("general_reserved_zero_34bits", read_bits(pSPSBytes, 34)); } else { spsMap.set("general_reserved_zero_43bits", read_bits(pSPSBytes, 43)); } if ((generalProfileIdc >= 1 && generalProfileIdc <= 5) || generalProfileCompatibilityFlag[1] || generalProfileCompatibilityFlag[2] || generalProfileCompatibilityFlag[3] || generalProfileCompatibilityFlag[4] || generalProfileCompatibilityFlag[5]) { /* The number of bits in this syntax structure is not affected by this condition */ spsMap.set("general_inbld_flag", read_bits(pSPSBytes, 1)); } else { spsMap.set("general_reserved_zero_bit", read_bits(pSPSBytes, 1)); } } spsMap.set("general_level_idc", read_bits(pSPSBytes, 8)); var subLayerProfilePresentFlag = new Array(maxNumSubLayersMinus1); var subLayerLevelPresentFlag = new Array(maxNumSubLayersMinus1); for (i = 0; i < maxNumSubLayersMinus1; i++) { subLayerProfilePresentFlag[i] = read_bits(pSPSBytes, 1); subLayerLevelPresentFlag[i] = read_bits(pSPSBytes, 1); } var reservedZero2bits = new Array(8); var subLayerProfileIdc = new Array(maxNumSubLayersMinus1); if (maxNumSubLayersMinus1 > 0) { for (var i = maxNumSubLayersMinus1; i < 8; i++) { reservedZero2bits[i] = read_bits(pSPSBytes, 2); } } for (var i = 0; i < maxNumSubLayersMinus1; i++) { if (subLayerProfilePresentFlag[i]) { spsMap.set("sub_layer_profile_space", read_bits(pSPSBytes, 2)); spsMap.set("sub_layer_tier_flag", read_bits(pSPSBytes, 1)); subLayerProfileIdc[i] = read_bits(pSPSBytes, 5); for (var j = 0; j < 32; j++) { subLayerProfileCompatibilityFlag[i][j] = read_bits(pSPSBytes, 1); } spsMap.set("sub_layer_progressive_source_flag", read_bits(pSPSBytes, 1)); spsMap.set("sub_layer_interlaced_source_flag", read_bits(pSPSBytes, 1)); spsMap.set("sub_layer_non_packed_constraint_flag", read_bits(pSPSBytes, 1)); spsMap.set("sub_layer_frame_only_constraint_flag", read_bits(pSPSBytes, 1)); if (subLayerProfileIdc[i] === 4 || subLayerProfileCompatibilityFlag[i][4] || subLayerProfileIdc[i] === 5 || subLayerProfileCompatibilityFlag[i][5] || subLayerProfileIdc[i] === 6 || subLayerProfileCompatibilityFlag[i][6] || subLayerProfileIdc[i] === 7 || subLayerProfileCompatibilityFlag[i][7]) { /* The number of bits in this syntax structure is not affected by this condition */ spsMap.set("sub_layer_max_12bit_constraint_flag", read_bits(pSPSBytes, 1)); spsMap.set("sub_layer_max_10bit_constraint_flag", read_bits(pSPSBytes, 1)); spsMap.set("sub_layer_max_8bit_constraint_flag", read_bits(pSPSBytes, 1)); spsMap.set("sub_layer_max_422chroma_constraint_flag", read_bits(pSPSBytes, 1)); spsMap.set("sub_layer_max_420chroma_constraint_flag", read_bits(pSPSBytes, 1)); spsMap.set("sub_layer_max_monochrome_constraint_flag", read_bits(pSPSBytes, 1)); spsMap.set("sub_layer_intra_constraint_flag", read_bits(pSPSBytes, 1)); spsMap.set("sub_layer_one_picture_only_constraint_flag", read_bits(pSPSBytes, 1)); spsMap.set("sub_layer_lower_bit_rate_constraint_flag", read_bits(pSPSBytes, 1)); spsMap.set("sub_layer_reserved_zero_34bits", read_bits(pSPSBytes, 34)); } else { spsMap.set("sub_layer_reserved_zero_43bits", read_bits(pSPSBytes, 43)); } if ((subLayerProfileIdc[i] >= 1 && subLayerProfileIdc[i] <= 5) || subLayerProfileCompatibilityFlag[1] || subLayerProfileCompatibilityFlag[2] || subLayerProfileCompatibilityFlag[3] || subLayerProfileCompatibilityFlag[4] || subLayerProfileCompatibilityFlag[5]) { /* The number of bits in this syntax structure is not affected by this condition */ spsMap.set("sub_layer_inbld_flag", read_bits(pSPSBytes, 1)); } else { spsMap.set("sub_layer_reserved_zero_bit", read_bits(pSPSBytes, 1)); } } if (subLayerLevelPresentFlag[i]) { spsMap.set("sub_layer_level_idc", read_bits(pSPSBytes, 8)); } } } Constructor.prototype = { parse: function (spsPayload) { pSPSBytes = spsPayload; //console.log("=========================SPS START========================="); vBitCount = 0; spsMap.clear(); spsMap.set("forbidden_zero_bit", read_bits(pSPSBytes, 1)); spsMap.set("nal_unit_type", read_bits(pSPSBytes, 6)); spsMap.set("nuh_layer_id", read_bits(pSPSBytes, 6)); spsMap.set("nuh_temporal_id_plus1", read_bits(pSPSBytes, 3)); spsMap.set("sps_video_parameter_set_id", read_bits(pSPSBytes, 4)); if (spsMap.get("nuh_layer_id") === 0) { spsMap.set("sps_max_sub_layers_minus1", read_bits(pSPSBytes, 3)); } else { spsMap.set("sps_ext_or_max_sub_layers_minus1", read_bits(pSPSBytes, 3)); } var MultiLayerExtSpsFlag = (spsMap.get("nuh_layer_id") !== 0 && spsMap.get("sps_ext_or_max_sub_layers_minus1") === 7); if (!MultiLayerExtSpsFlag) { spsMap.set("sps_max_sub_layers_minus1", read_bits(pSPSBytes, 1)); //profile_tier_level(1, spsMap.get("sps_max_sub_layers_minus1")); } read_bits(pSPSBytes, 84); spsMap.set("sps_seq_parameter_set_id", ue(pSPSBytes, 0)); if (MultiLayerExtSpsFlag) { spsMap.set("update_rep_format_flag", read_bits(pSPSBytes, 1)); if (spsMap.get("update_rep_format_flag")) { spsMap.set("sps_rep_format_idx", read_bits(pSPSBytes, 8)); } } else { spsMap.set("chroma_format_idc", ue(pSPSBytes, 0)); if (spsMap.get("chroma_format_idc") === 3) { spsMap.set("separate_colour_plane_flag", read_bits(pSPSBytes, 1)); } spsMap.set("pic_width_in_luma_samples", ue(pSPSBytes, 0)); spsMap.set("pic_height_in_luma_samples", ue(pSPSBytes, 0)); spsMap.set("conformance_window_flag", read_bits(pSPSBytes, 1)); if (spsMap.get("conformance_window_flag")) { spsMap.set("conf_win_left_offset", ue(pSPSBytes, 0)); spsMap.set("conf_win_right_offset", ue(pSPSBytes, 0)); spsMap.set("conf_win_top_offset", ue(pSPSBytes, 0)); spsMap.set("conf_win_bottom_offset", ue(pSPSBytes, 0)); } } //console.log("=========================SPS END========================="); return true; }, getSizeInfo: function () { var width = spsMap.get("pic_width_in_luma_samples"); var height = spsMap.get("pic_height_in_luma_samples"); if (spsMap.get("conformance_window_flag")) { var chromaFormatIdc = spsMap.get("chroma_format_idc"); var separateColourPlaneFlag = spsMap.get("separate_colour_plane_flag"); if (typeof separateColourPlaneFlag === "undefined") { separateColourPlaneFlag = 0; } var subWidthC = ((1 === chromaFormatIdc) || (2 === chromaFormatIdc)) && (0 === separateColourPlaneFlag) ? 2 : 1; var subHeightC = (1 === chromaFormatIdc) && (0 === separateColourPlaneFlag) ? 2 : 1; width -= (subWidthC * spsMap.get("conf_win_right_offset") + subWidthC * spsMap.get("conf_win_left_offset")); height -= (subHeightC * spsMap.get("conf_win_bottom_offset") + subHeightC * spsMap.get("conf_win_top_offset")); } var decodeSize = width * height; var sizeInfo = { 'width': width, 'height': height, 'decodeSize': decodeSize }; return sizeInfo; }, getSpsValue: function (key) { return spsMap.get(key); }, }; return new Constructor(); }