diff --git a/lib/h265.cpp b/lib/h265.cpp index b987123b..0e57a589 100644 --- a/lib/h265.cpp +++ b/lib/h265.cpp @@ -3,6 +3,46 @@ #include "defines.h" namespace h265 { + const char * typeToStr(uint8_t type){ + switch (type){ + case 0: + case 1: return "Trailing slice"; + case 2: + case 3: return "TSA slice"; + case 4: + case 5: return "STSA slice"; + case 6: + case 7: return "Decodable leading slice"; + case 8: + case 9: return "Skipped leading slice"; + case 16: + case 17: + case 18: return "BLA slice"; + case 19: + case 20: return "IDR (keyframe) slice"; + case 21: return "CRA slice"; + case 32: return "VPS"; + case 33: return "SPS"; + case 34: return "PPS"; + case 35: return "Access unit delimiter"; + case 36: return "End of sequence"; + case 37: return "End of bitstream"; + case 38: return "Filler data"; + case 39: + case 40: return "SEI"; + case 48: return "RTP Aggregation Packet"; + case 49: return "RTP Fragmentation Unit"; + case 50: return "RTP PAyload Content Information (PACI)"; + default: return "UNKNOWN"; + } + } + + bool isKeyframe(const char * data, uint32_t len){ + if (!len){return false;} + uint8_t nalType = (data[0] & 0x7E) >> 1; + return (nalType >= 16 && nalType <= 21); + } + std::deque analysePackets(const char * data, unsigned long len){ std::deque res; @@ -19,6 +59,36 @@ namespace h265 { initData::initData() {} + initData::initData(const std::string& hvccData) { + MP4::HVCC hvccBox; + hvccBox.setPayload(hvccData); + std::deque arrays = hvccBox.getArrays(); + for (std::deque::iterator it = arrays.begin(); it != arrays.end(); it++){ + for (std::deque::iterator nalIt = it->nalUnits.begin(); nalIt != it->nalUnits.end(); nalIt++){ + nalUnits[it->nalUnitType].insert(*nalIt); + } + } + } + + const std::set & initData::getVPS() const{ + static std::set empty; + if (!nalUnits.count(32)){return empty;} + return nalUnits.at(32); + } + + const std::set & initData::getSPS() const{ + static std::set empty; + if (!nalUnits.count(33)){return empty;} + return nalUnits.at(33); + } + + const std::set & initData::getPPS() const{ + static std::set empty; + if (!nalUnits.count(34)){return empty;} + return nalUnits.at(34); + } + + void initData::addUnit(char * data) { unsigned long nalSize = Bit::btohl(data); unsigned long nalType = (data[4] & 0x7E) >> 1; @@ -30,6 +100,22 @@ namespace h265 { } } + void initData::addUnit(const std::string & data) { + if (data.size() <= 1){return;} + unsigned long nalType = (data[0] & 0x7E) >> 1; + switch (nalType) { + case 32: //vps + case 33: //sps + case 34: //pps + nalUnits[nalType].insert(data); + } + INFO_MSG("added nal of type %u" , nalType); + if (nalType == 32){ + vpsUnit vps(data); + std::cout << vps.toPrettyString(0); + } + } + bool initData::haveRequired() { return (nalUnits.count(32) && nalUnits.count(33) && nalUnits.count(34)); } @@ -72,6 +158,94 @@ namespace h265 { return std::string(hvccBox.payload(), hvccBox.payloadSize()); } + metaInfo initData::getMeta() { + metaInfo res; + if (!nalUnits.count(33)){ + return res; + } + spsUnit sps(*nalUnits[33].begin()); + sps.getMeta(res); + return res; + } + + void skipProfileTierLevel(Utils::bitstream & bs, unsigned int maxSubLayersMinus1){ + bs.skip(8); + bs.skip(32);//general_profile_flags + bs.skip(4); + bs.skip(44);//reserverd_zero + bs.skip(8); + std::deque profilePresent; + std::deque levelPresent; + for (size_t i = 0; i < maxSubLayersMinus1; i++){ + profilePresent.push_back(bs.get(1)); + levelPresent.push_back(bs.get(1)); + } + if (maxSubLayersMinus1){ + for (int i = maxSubLayersMinus1; i < 8; i++){ + bs.skip(2); + } + } + for (int i = 0; i < maxSubLayersMinus1; i++){ + if (profilePresent[i]){ + bs.skip(8); + bs.skip(32);//sub_layer_profile_flags + bs.skip(4); + bs.skip(44);//reserved_zero + } + if (levelPresent[i]){ + bs.skip(8); + } + } + } + + std::string printProfileTierLevel(Utils::bitstream & bs, unsigned int maxSubLayersMinus1, size_t indent){ + std::stringstream r; + r << std::string(indent, ' ') << "general_profile_space: " << bs.get(2) << std::endl; + r << std::string(indent, ' ') << "general_tier_flag: " << bs.get(1) << std::endl; + r << std::string(indent, ' ') << "general_profile_idc: " << bs.get(5) << std::endl; + r << std::string(indent, ' ') << "general_profile_compatibility_flags: 0x" << std::hex << bs.get(32) << std::dec << std::endl; + r << std::string(indent, ' ') << "general_progressive_source_flag: " << bs.get(1) << std::endl; + r << std::string(indent, ' ') << "general_interlaced_source_flag: " << bs.get(1) << std::endl; + r << std::string(indent, ' ') << "general_non_packed_constraint_flag: " << bs.get(1) << std::endl; + r << std::string(indent, ' ') << "general_frame_only_constraint_flag: " << bs.get(1) << std::endl; + r << std::string(indent, ' ') << "general_reserved_zero_44bits: " << bs.get(44) << std::endl; + r << std::string(indent, ' ') << "general_level_idc: " << bs.get(8) << std::endl; + std::deque profilePresent; + std::deque levelPresent; + for (size_t i = 0; i < maxSubLayersMinus1; i++){ + bool profile = bs.get(1); + bool level = bs.get(1); + profilePresent.push_back(profile); + levelPresent.push_back(level); + r << std::string(indent + 1, ' ') << "sub_layer_profile_present_flag[" << i << "]: " << (profile ? 1 : 0) << std::endl; + r << std::string(indent + 1, ' ') << "sub_layer_level_present_flag[" << i << "]: " << (level ? 1 : 0) << std::endl; + } + + if (maxSubLayersMinus1){ + for (int i = maxSubLayersMinus1; i < 8; i++){ + r << std::string(indent + 1, ' ') << "reserver_zero_2_bits[" << i << "]: " << bs.get(2) << std::endl; + } + } + for (int i = 0; i < maxSubLayersMinus1; i++){ + r << std::string(indent + 1, ' ') << "sub_layer[" << i << "]:" << std::endl; + if (profilePresent[i]){ + r << std::string(indent + 2, ' ') << "sub_layer_profile_space: " << bs.get(2) << std::endl; + r << std::string(indent + 2, ' ') << "sub_layer_tier_flag: " << bs.get(1) << std::endl; + r << std::string(indent + 2, ' ') << "sub_layer_profile_idc: " << bs.get(5) << std::endl; + r << std::string(indent + 2, ' ') << "sub_layer_profile_compatibility_flags: " << std::hex << bs.get(32) << std::dec << std::endl; + r << std::string(indent + 2, ' ') << "sub_layer_progressive_source_flag: " << bs.get(1) << std::endl; + r << std::string(indent + 2, ' ') << "sub_layer_interlaced_source_flag: " << bs.get(1) << std::endl; + r << std::string(indent + 2, ' ') << "sub_layer_non_packed_constraint_flag: " << bs.get(1) << std::endl; + r << std::string(indent + 2, ' ') << "sub_layer_frame_only_constraint_flag: " << bs.get(1) << std::endl; + r << std::string(indent + 2, ' ') << "sub_layer_reserved_zero_44bits: " << bs.get(44) << std::endl; + } + if (levelPresent[i]){ + r << std::string(indent + 2, ' ') << "sub_layer_level_idc: " << bs.get(8) << std::endl; + } + } + return r.str(); + } + void updateProfileTierLevel(Utils::bitstream & bs, MP4::HVCC & hvccBox, unsigned int maxSubLayersMinus1){ hvccBox.setGeneralProfileSpace(bs.get(2)); @@ -135,10 +309,339 @@ namespace h265 { updateProfileTierLevel(bs, hvccBox, maxSubLayers - 1); } + std::string vpsUnit::toPrettyString(size_t indent){ + Utils::bitstream bs; + bs.append(data); + bs.skip(16);//Nal Header + std::stringstream r; + r << std::string(indent, ' ') << "vps_video_parameter_set_id: " << bs.get(4) << std::endl; + r << std::string(indent, ' ') << "vps_reserved_three_2bits: " << bs.get(2) << std::endl; + r << std::string(indent, ' ') << "vps_max_layers_minus1: " << bs.get(6) << std::endl; + unsigned int maxSubLayersMinus1 = bs.get(3); + r << std::string(indent, ' ') << "vps_max_sub_layers_minus1: " << maxSubLayersMinus1 << std::endl; + r << std::string(indent, ' ') << "vps_temporal_id_nesting_flag: " << bs.get(1) << std::endl; + r << std::string(indent, ' ') << "vps_reserved_0xffff_16bits: " << std::hex << bs.get(16) << std::dec << std::endl; + r << std::string(indent, ' ') << "profile_tier_level(): " << std::endl << printProfileTierLevel(bs, maxSubLayersMinus1, indent + 1); + bool sub_layer_ordering_info = bs.get(1); + r << std::string(indent, ' ') << "vps_sub_layer_ordering_info_present_flag: " << sub_layer_ordering_info << std::endl; + for (int i = (sub_layer_ordering_info ? 0 : maxSubLayersMinus1); i <= maxSubLayersMinus1; i++){ + r << std::string(indent, ' ') << "vps_max_dec_pic_buffering_minus1[" << i << "]: " << bs.getUExpGolomb() << std::endl; + r << std::string(indent, ' ') << "vps_max_num_reorder_pics[" << i << "]: " << bs.getUExpGolomb() << std::endl; + r << std::string(indent, ' ') << "vps_max_latency_increase_plus1[" << i << "]: " << bs.getUExpGolomb() << std::endl; + } + unsigned int vps_max_layer_id = bs.get(6); + uint64_t vps_num_layer_sets_minus1 = bs.getUExpGolomb(); + r << std::string(indent, ' ') << "vps_max_layer_id: " << vps_max_layer_id << std::endl; + r << std::string(indent, ' ') << "vps_num_layer_sets_minus1: " << vps_num_layer_sets_minus1 << std::endl; + for (int i = 1; i <= vps_num_layer_sets_minus1; i++){ + for (int j = 0; j < vps_max_layer_id; j++){ + r << std::string(indent, ' ') << "layer_id_included_flag[" << i << "][" << j << "]: " << bs.get(1) << std::endl; + } + } + bool vps_timing_info = bs.get(1); + r << std::string(indent, ' ') << "vps_timing_info_present_flag: " << (vps_timing_info ? 1 : 0) << std::endl; + + return r.str(); + } + spsUnit::spsUnit(const std::string & _data){ data = nalu::removeEmulationPrevention(_data); } + void skipShortTermRefPicSet(Utils::bitstream & bs, unsigned int idx, size_t count){ + static std::map negativePics; + static std::map positivePics; + if (idx == 0){ + negativePics.clear(); + positivePics.clear(); + } + bool interPrediction = false; + if (idx != 0){ + interPrediction = bs.get(1); + } + if (interPrediction){ + uint64_t deltaIdxMinus1 = 0; + if (idx == count){ + deltaIdxMinus1 = bs.getUExpGolomb(); + } + bs.skip(1); + bs.getUExpGolomb(); + uint64_t refRpsIdx = idx - deltaIdxMinus1 - 1; + uint64_t deltaPocs = negativePics[refRpsIdx] + positivePics[refRpsIdx]; + for (int j = 0; j < deltaPocs; j++){ + bool usedByCurrPicFlag = bs.get(1); + if (!usedByCurrPicFlag){ + bs.skip(1); + } + } + }else{ + negativePics[idx] = bs.getUExpGolomb(); + positivePics[idx] = bs.getUExpGolomb(); + for (int i = 0; i < negativePics[idx]; i++){ + bs.getUExpGolomb(); + bs.skip(1); + } + for (int i = 0; i < positivePics[idx]; i++){ + bs.getUExpGolomb(); + bs.skip(1); + } + } + } + + std::string printShortTermRefPicSet(Utils::bitstream & bs, unsigned int idx, size_t indent){ + std::stringstream r; + bool interPrediction = false; + if (idx != 0){ + interPrediction = bs.get(1); + r << std::string(indent, ' ') << "inter_ref_pic_set_predicition_flag: " << (interPrediction ? 1 : 0) << std::endl; + } + if (interPrediction){ + WARN_MSG("interprediciton not yet handled"); + }else{ + uint64_t negativePics = bs.getUExpGolomb(); + uint64_t positivePics = bs.getUExpGolomb(); + r << std::string(indent, ' ') << "num_negative_pics: " << negativePics << std::endl; + r << std::string(indent, ' ') << "num_positive_pics: " << positivePics << std::endl; + for (int i = 0; i < negativePics; i++){ + r << std::string(indent + 1, ' ') << "delta_poc_s0_minus1[" << i << "]: " << bs.getUExpGolomb() << std::endl; + r << std::string(indent + 1, ' ') << "used_by_curr_pic_s0_flag[" << i << "]: " << bs.get(1) << std::endl; + } + for (int i = 0; i < positivePics; i++){ + r << std::string(indent + 1, ' ') << "delta_poc_s1_minus1[" << i << "]: " << bs.getUExpGolomb() << std::endl; + r << std::string(indent + 1, ' ') << "used_by_curr_pic_s1_flag[" << i << "]: " << bs.get(1) << std::endl; + } + } + return r.str(); + } + + void parseVuiParameters(Utils::bitstream & bs, metaInfo & res){ + bool aspectRatio = bs.get(1); + if (aspectRatio){ + uint16_t aspectRatioIdc = bs.get(8); + if (aspectRatioIdc == 255){ + bs.skip(32); + } + } + bool overscanInfo = bs.get(1); + if (overscanInfo){ + bs.skip(1); + } + bool videoSignalTypePresent = bs.get(1); + if (videoSignalTypePresent){ + bs.skip(4); + bool colourDescription = bs.get(1); + if (colourDescription){ + bs.skip(24); + } + } + bool chromaLocPresent = bs.get(1); + if (chromaLocPresent){ + bs.getUExpGolomb(); + bs.getUExpGolomb(); + } + bs.skip(3); + bool defaultDisplay = bs.get(1); + if (defaultDisplay){ + bs.getUExpGolomb(); + bs.getUExpGolomb(); + bs.getUExpGolomb(); + bs.getUExpGolomb(); + } + bool timingFlag = bs.get(1); + if (timingFlag){ + uint32_t unitsInTick = bs.get(32); + uint32_t timescale = bs.get(32); + res.fps = (double)timescale / unitsInTick; + } + } + + std::string printVuiParameters(Utils::bitstream & bs, size_t indent){ + std::stringstream r; + bool aspectRatio = bs.get(1); + r << std::string(indent, ' ') << "aspect_ratio_info_present_flag: " << (aspectRatio ? 1 : 0) << std::endl; + if (aspectRatio){ + uint16_t aspectRatioIdc = bs.get(8); + r << std::string(indent, ' ') << "aspect_ratio_idc: " << aspectRatioIdc << std::endl; + if (aspectRatioIdc == 255){ + r << std::string(indent, ' ') << "sar_width: " << bs.get(16) << std::endl; + r << std::string(indent, ' ') << "sar_height: " << bs.get(16) << std::endl; + } + } + return r.str(); + } + + void skipScalingList(Utils::bitstream & bs){ + for (int sizeId = 0; sizeId < 4; sizeId++){ + for (int matrixId = 0; matrixId < (sizeId == 3 ? 2 : 6); matrixId++){ + bool modeFlag = bs.get(1); + if (!modeFlag){ + bs.getUExpGolomb(); + }else{ + size_t coefNum = std::min(64, (1 << (4 + (sizeId << 1)))); + if (sizeId > 1){ + bs.getExpGolomb(); + } + for (int i = 0; i < coefNum; i++){ + bs.getExpGolomb(); + } + } + } + } + } + + void spsUnit::getMeta(metaInfo & res) { + Utils::bitstream bs; + bs.append(data); + bs.skip(16);//Nal Header + bs.skip(4); + unsigned int maxSubLayersMinus1 = bs.get(3); + bs.skip(1); + skipProfileTierLevel(bs, maxSubLayersMinus1); + bs.getUExpGolomb(); + uint64_t chromaFormatIdc = bs.getUExpGolomb(); + bool separateColorPlane = false; + if (chromaFormatIdc == 3){ + separateColorPlane = bs.get(1); + } + res.width = bs.getUExpGolomb(); + res.height = bs.getUExpGolomb(); + bool conformanceWindow = bs.get(1); + if (conformanceWindow){ + uint8_t subWidthC = ((chromaFormatIdc == 1 || chromaFormatIdc == 2) ? 2 : 1); + uint8_t subHeightC = (chromaFormatIdc == 1 ? 2 : 1); + uint64_t left = bs.getUExpGolomb(); + uint64_t right = bs.getUExpGolomb(); + uint64_t top = bs.getUExpGolomb(); + uint64_t bottom = bs.getUExpGolomb(); + res.width -= (subWidthC * right); + res.height -= (subHeightC * bottom); + } + bs.getUExpGolomb(); + bs.getUExpGolomb(); + uint64_t log2MaxPicOrderCntLsbMinus4 = bs.getUExpGolomb(); + bool subLayerOrdering = bs.get(1); + for (int i= (subLayerOrdering ? 0 : maxSubLayersMinus1); i <= maxSubLayersMinus1; i++){ + bs.getUExpGolomb(); + bs.getUExpGolomb(); + bs.getUExpGolomb(); + } + bs.getUExpGolomb(); + bs.getUExpGolomb(); + bs.getUExpGolomb(); + bs.getUExpGolomb(); + bs.getUExpGolomb(); + bs.getUExpGolomb(); + bool scalingListEnabled = bs.get(1); + if (scalingListEnabled){ + bool scalingListPresent = bs.get(1); + if (scalingListPresent){ + skipScalingList(bs); + } + } + bs.skip(2); + bool pcmEnabled = bs.get(1); + if (pcmEnabled){ + bs.skip(8); + bs.getUExpGolomb(); + bs.getUExpGolomb(); + bs.skip(1); + } + uint64_t shortTermPicSets = bs.getUExpGolomb(); + for (int i= 0; i < shortTermPicSets; i++){ + skipShortTermRefPicSet(bs, i, shortTermPicSets); + } + bool longTermRefPics = bs.get(1); + if (longTermRefPics){ + uint64_t numLongTermPics = bs.getUExpGolomb(); + for (int i = 0; i < numLongTermPics; i++){ + bs.skip(log2MaxPicOrderCntLsbMinus4 + 4); + bs.skip(1); + } + } + bs.skip(2); + bool vuiParams = bs.get(1); + if (vuiParams){ + parseVuiParameters(bs, res); + } + } + + std::string spsUnit::toPrettyString(size_t indent){ + Utils::bitstream bs; + bs.append(data); + bs.skip(16);//Nal Header + std::stringstream r; + r << std::string(indent, ' ') << "sps_video_parameter_set_id: " << bs.get(4) << std::endl; + unsigned int maxSubLayersMinus1 = bs.get(3); + r << std::string(indent, ' ') << "sps_max_sub_layers_minus1: " << maxSubLayersMinus1 << std::endl; + r << std::string(indent, ' ') << "sps_temporal_id_nesting_flag: " << bs.get(1) << std::endl; + r << std::string(indent, ' ') << "profile_tier_level(): " << std::endl << printProfileTierLevel(bs, maxSubLayersMinus1, indent + 1); + r << std::string(indent, ' ') << "sps_seq_parameter_set_id: " << bs.getUExpGolomb() << std::endl; + uint64_t chromaFormatIdc = bs.getUExpGolomb(); + r << std::string(indent, ' ') << "chroma_format_idc: " << chromaFormatIdc << std::endl; + if (chromaFormatIdc == 3){ + r << std::string(indent, ' ') << "separate_colour_plane_flag: " << bs.get(1) << std::endl; + } + r << std::string(indent, ' ') << "pic_width_in_luma_samples: " << bs.getUExpGolomb() << std::endl; + r << std::string(indent, ' ') << "pic_height_in_luma_samples: " << bs.getUExpGolomb() << std::endl; + bool conformance_window_flag = bs.get(1); + r << std::string(indent, ' ') << "conformance_window_flag: " << conformance_window_flag << std::endl; + if (conformance_window_flag){ + r << std::string(indent, ' ') << "conf_window_left_offset: " << bs.getUExpGolomb() << std::endl; + r << std::string(indent, ' ') << "conf_window_right_offset: " << bs.getUExpGolomb() << std::endl; + r << std::string(indent, ' ') << "conf_window_top_offset: " << bs.getUExpGolomb() << std::endl; + r << std::string(indent, ' ') << "conf_window_bottom_offset: " << bs.getUExpGolomb() << std::endl; + } + r << std::string(indent, ' ') << "bit_depth_luma_minus8: " << bs.getUExpGolomb() << std::endl; + r << std::string(indent, ' ') << "bit_depth_chroma_minus8: " << bs.getUExpGolomb() << std::endl; + r << std::string(indent, ' ') << "log2_max_pic_order_cnt_lsb_minus4: " << bs.getUExpGolomb() << std::endl; + bool subLayerOrdering = bs.get(1); + r << std::string(indent, ' ') << "sps_sub_layer_ordering_info_present_flag: " << subLayerOrdering << std::endl; + for (int i= (subLayerOrdering ? 0 : maxSubLayersMinus1); i <= maxSubLayersMinus1; i++){ + r << std::string(indent + 1, ' ') << "sps_max_dec_pic_buffering_minus1[" << i << "]: " << bs.getUExpGolomb() << std::endl; + r << std::string(indent + 1, ' ') << "sps_max_num_reorder_pics[" << i << "]: " << bs.getUExpGolomb() << std::endl; + r << std::string(indent + 1, ' ') << "sps_max_latency_increase_plus1[" << i << "]: " << bs.getUExpGolomb() << std::endl; + } + r << std::string(indent, ' ') << "log2_min_luma_coding_block_size_minus3: " << bs.getUExpGolomb() << std::endl; + r << std::string(indent, ' ') << "log2_diff_max_min_luma_coding_block_size: " << bs.getUExpGolomb() << std::endl; + r << std::string(indent, ' ') << "log2_min_transform_block_size_minus2: " << bs.getUExpGolomb() << std::endl; + r << std::string(indent, ' ') << "log2_diff_max_min_transform_block_size: " << bs.getUExpGolomb() << std::endl; + r << std::string(indent, ' ') << "max_transform_hierarchy_depth_inter: " << bs.getUExpGolomb() << std::endl; + r << std::string(indent, ' ') << "max_transform_hierarchy_depth_intra: " << bs.getUExpGolomb() << std::endl; + bool scalingListEnabled = bs.get(1); + r << std::string(indent, ' ') << "scaling_list_enabled_flag: " << scalingListEnabled << std::endl; + if (scalingListEnabled){ + WARN_MSG("Not implemented scaling list in HEVC sps"); + } + r << std::string(indent, ' ') << "amp_enabled_flag: " << bs.get(1) << std::endl; + r << std::string(indent, ' ') << "sample_adaptive_offset_enabled_flag: " << bs.get(1) << std::endl; + bool pcmEnabled = bs.get(1); + r << std::string(indent, ' ') << "pcm_enabled_flag: " << pcmEnabled << std::endl; + if (pcmEnabled){ + WARN_MSG("Not implemented pcm_enabled in HEVC sps"); + } + uint64_t shortTermPicSets = bs.getUExpGolomb(); + r << std::string(indent, ' ') << "num_short_term_ref_pic_sets: " << shortTermPicSets << std::endl; + for (int i= 0; i < shortTermPicSets; i++){ + r << std::string(indent, ' ') << "short_term_ref_pic_set(" << i << "):" << std::endl << printShortTermRefPicSet(bs, i, indent + 1); + + } + bool longTermRefPics = bs.get(1); + r << std::string(indent, ' ') << "long_term_ref_pics_present_flag: " << (longTermRefPics ? 1 : 0) << std::endl; + if (longTermRefPics){ + WARN_MSG("Implement longTermRefPics"); + } + r << std::string(indent, ' ') << "sps_temporal_mvp_enabled_flag: " << bs.get(1) << std::endl; + r << std::string(indent, ' ') << "strong_intra_smoothing_enabled_flag: " << bs.get(1) << std::endl; + + bool vuiParams = bs.get(1); + r << std::string(indent, ' ') << "vui_parameters_present_flag: " << (vuiParams ? 1 : 0) << std::endl; + if (vuiParams){ + r << std::string(indent, ' ') << "vui_parameters:" << std::endl << printVuiParameters(bs, indent + 1); + + } + return r.str(); + } + void spsUnit::updateHVCC(MP4::HVCC & hvccBox) { Utils::bitstream bs; bs.append(data); diff --git a/lib/h265.h b/lib/h265.h index bec6d8d5..bda1cc3e 100644 --- a/lib/h265.h +++ b/lib/h265.h @@ -9,15 +9,31 @@ namespace h265 { std::deque analysePackets(const char * data, unsigned long len); + + const char * typeToStr(uint8_t type); + bool isKeyframe(const char * data, uint32_t len); void updateProfileTierLevel(Utils::bitstream & bs, MP4::HVCC & hvccBox, unsigned long maxSubLayersMinus1); + std::string printProfileTierLevel(Utils::bitstream & bs, unsigned long maxSubLayersMinus1, size_t indent); + + struct metaInfo { + unsigned int width; + unsigned int height; + double fps; + }; class initData { public: initData(); + initData(const std::string & hvccData); void addUnit(char * data); + void addUnit(const std::string & data); bool haveRequired(); std::string generateHVCC(); + metaInfo getMeta(); + const std::set & getVPS() const; + const std::set & getSPS() const; + const std::set & getPPS() const; protected: std::map > nalUnits; }; @@ -26,6 +42,7 @@ namespace h265 { public: vpsUnit(const std::string & _data); void updateHVCC(MP4::HVCC & hvccBox); + std::string toPrettyString(size_t indent); private: std::string data; }; @@ -34,6 +51,8 @@ namespace h265 { public: spsUnit(const std::string & _data); void updateHVCC(MP4::HVCC & hvccBox); + std::string toPrettyString(size_t indent = 0); + void getMeta(metaInfo & res); private: std::string data; }; diff --git a/lib/mp4_generic.cpp b/lib/mp4_generic.cpp index 747d960f..fb5a682e 100644 --- a/lib/mp4_generic.cpp +++ b/lib/mp4_generic.cpp @@ -1,5 +1,6 @@ #include "mp4_generic.h" #include "defines.h" +#include "h265.h" namespace MP4 { MFHD::MFHD() { @@ -866,7 +867,7 @@ namespace MP4 { r << std::string(indent + 1, ' ') << "Arrays:" << std::endl; std::deque arrays = getArrays(); for (unsigned int i = 0; i < arrays.size(); i++){ - r << std::string(indent + 2, ' ') << "Array with type " << (int)arrays[i].nalUnitType << std::endl; + r << std::string(indent + 2, ' ') << "Array with type " << h265::typeToStr(arrays[i].nalUnitType) << std::endl; for (unsigned int j = 0; j < arrays[i].nalUnits.size(); j++){ r << std::string(indent + 3, ' ') << "Nal unit of " << arrays[i].nalUnits[j].size() << " bytes" << std::endl; } @@ -874,6 +875,11 @@ namespace MP4 { return r.str(); } + h265::metaInfo HVCC::getMetaInfo(){ + h265::initData init(std::string(payload(), payloadSize())); + return init.getMeta(); + } + void HVCC::setPayload(std::string newPayload) { if (!reserve(0, payloadSize(), newPayload.size())) { ERROR_MSG("Cannot allocate enough memory for payload"); diff --git a/lib/mp4_generic.h b/lib/mp4_generic.h index 959bb9b1..a8f2cc17 100644 --- a/lib/mp4_generic.h +++ b/lib/mp4_generic.h @@ -1,6 +1,10 @@ #pragma once #include "mp4.h" +namespace h265 { + class metaInfo; +} + namespace MP4 { class MFHD: public Box { public: @@ -174,6 +178,7 @@ namespace MP4 { std::string asAnnexB(); void setPayload(std::string newPayload); std::string toPrettyString(uint32_t indent = 0); + h265::metaInfo getMetaInfo(); }; class Descriptor{ diff --git a/lib/ts_stream.cpp b/lib/ts_stream.cpp index 2a59b4c1..bc729941 100644 --- a/lib/ts_stream.cpp +++ b/lib/ts_stream.cpp @@ -192,7 +192,8 @@ namespace TS{ metaInit[pid] = std::string(entry.getESInfo(), entry.getESInfoLength()); } break; - default: break; + default: + break; } entry.advance(); } @@ -660,9 +661,12 @@ namespace TS{ char typeNal; isKeyFrame = false; - typeNal = pesPayload[0] & 0x1F; + if (pidToCodec[tid] == MPEG2){ + return; + } if (pidToCodec[tid] == H264){ + typeNal = pesPayload[0] & 0x1F; switch (typeNal){ case 0x01:{ if (firstSlice){ @@ -702,6 +706,7 @@ namespace TS{ default: break; } }else if (pidToCodec[tid] == H265){ + typeNal = (((pesPayload[0] & 0x7E) >> 1) & 0xFF); switch (typeNal){ case 2: case 3: // TSA Picture @@ -723,7 +728,8 @@ namespace TS{ case 32: case 33: case 34:{ - hevcInfo[tid].addUnit((char *)pesPayload); // may i convert to (char *)? + tthread::lock_guard guard(tMutex); + hevcInfo[tid].addUnit(std::string(pesPayload, nextPtr - pesPayload)); // may i convert to (char *)? break; } default: break;