H265 library improvements
This commit is contained in:
parent
5126928d0d
commit
5dce70e933
5 changed files with 543 additions and 4 deletions
503
lib/h265.cpp
503
lib/h265.cpp
|
@ -3,6 +3,46 @@
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
|
|
||||||
namespace h265 {
|
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<nalu::nalData> analysePackets(const char * data, unsigned long len){
|
std::deque<nalu::nalData> analysePackets(const char * data, unsigned long len){
|
||||||
std::deque<nalu::nalData> res;
|
std::deque<nalu::nalData> res;
|
||||||
|
|
||||||
|
@ -19,6 +59,36 @@ namespace h265 {
|
||||||
|
|
||||||
initData::initData() {}
|
initData::initData() {}
|
||||||
|
|
||||||
|
initData::initData(const std::string& hvccData) {
|
||||||
|
MP4::HVCC hvccBox;
|
||||||
|
hvccBox.setPayload(hvccData);
|
||||||
|
std::deque<MP4::HVCCArrayEntry> arrays = hvccBox.getArrays();
|
||||||
|
for (std::deque<MP4::HVCCArrayEntry>::iterator it = arrays.begin(); it != arrays.end(); it++){
|
||||||
|
for (std::deque<std::string>::iterator nalIt = it->nalUnits.begin(); nalIt != it->nalUnits.end(); nalIt++){
|
||||||
|
nalUnits[it->nalUnitType].insert(*nalIt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::set<std::string> & initData::getVPS() const{
|
||||||
|
static std::set<std::string> empty;
|
||||||
|
if (!nalUnits.count(32)){return empty;}
|
||||||
|
return nalUnits.at(32);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::set<std::string> & initData::getSPS() const{
|
||||||
|
static std::set<std::string> empty;
|
||||||
|
if (!nalUnits.count(33)){return empty;}
|
||||||
|
return nalUnits.at(33);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::set<std::string> & initData::getPPS() const{
|
||||||
|
static std::set<std::string> empty;
|
||||||
|
if (!nalUnits.count(34)){return empty;}
|
||||||
|
return nalUnits.at(34);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void initData::addUnit(char * data) {
|
void initData::addUnit(char * data) {
|
||||||
unsigned long nalSize = Bit::btohl(data);
|
unsigned long nalSize = Bit::btohl(data);
|
||||||
unsigned long nalType = (data[4] & 0x7E) >> 1;
|
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() {
|
bool initData::haveRequired() {
|
||||||
return (nalUnits.count(32) && nalUnits.count(33) && nalUnits.count(34));
|
return (nalUnits.count(32) && nalUnits.count(33) && nalUnits.count(34));
|
||||||
}
|
}
|
||||||
|
@ -72,6 +158,94 @@ namespace h265 {
|
||||||
return std::string(hvccBox.payload(), hvccBox.payloadSize());
|
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<bool> profilePresent;
|
||||||
|
std::deque<bool> 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<bool> profilePresent;
|
||||||
|
std::deque<bool> 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){
|
void updateProfileTierLevel(Utils::bitstream & bs, MP4::HVCC & hvccBox, unsigned int maxSubLayersMinus1){
|
||||||
hvccBox.setGeneralProfileSpace(bs.get(2));
|
hvccBox.setGeneralProfileSpace(bs.get(2));
|
||||||
|
|
||||||
|
@ -135,10 +309,339 @@ namespace h265 {
|
||||||
updateProfileTierLevel(bs, hvccBox, maxSubLayers - 1);
|
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){
|
spsUnit::spsUnit(const std::string & _data){
|
||||||
data = nalu::removeEmulationPrevention(_data);
|
data = nalu::removeEmulationPrevention(_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void skipShortTermRefPicSet(Utils::bitstream & bs, unsigned int idx, size_t count){
|
||||||
|
static std::map<int,int> negativePics;
|
||||||
|
static std::map<int,int> 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) {
|
void spsUnit::updateHVCC(MP4::HVCC & hvccBox) {
|
||||||
Utils::bitstream bs;
|
Utils::bitstream bs;
|
||||||
bs.append(data);
|
bs.append(data);
|
||||||
|
|
19
lib/h265.h
19
lib/h265.h
|
@ -9,15 +9,31 @@
|
||||||
|
|
||||||
namespace h265 {
|
namespace h265 {
|
||||||
std::deque<nalu::nalData> analysePackets(const char * data, unsigned long len);
|
std::deque<nalu::nalData> 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);
|
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 {
|
class initData {
|
||||||
public:
|
public:
|
||||||
initData();
|
initData();
|
||||||
|
initData(const std::string & hvccData);
|
||||||
void addUnit(char * data);
|
void addUnit(char * data);
|
||||||
|
void addUnit(const std::string & data);
|
||||||
bool haveRequired();
|
bool haveRequired();
|
||||||
std::string generateHVCC();
|
std::string generateHVCC();
|
||||||
|
metaInfo getMeta();
|
||||||
|
const std::set<std::string> & getVPS() const;
|
||||||
|
const std::set<std::string> & getSPS() const;
|
||||||
|
const std::set<std::string> & getPPS() const;
|
||||||
protected:
|
protected:
|
||||||
std::map<unsigned int, std::set<std::string> > nalUnits;
|
std::map<unsigned int, std::set<std::string> > nalUnits;
|
||||||
};
|
};
|
||||||
|
@ -26,6 +42,7 @@ namespace h265 {
|
||||||
public:
|
public:
|
||||||
vpsUnit(const std::string & _data);
|
vpsUnit(const std::string & _data);
|
||||||
void updateHVCC(MP4::HVCC & hvccBox);
|
void updateHVCC(MP4::HVCC & hvccBox);
|
||||||
|
std::string toPrettyString(size_t indent);
|
||||||
private:
|
private:
|
||||||
std::string data;
|
std::string data;
|
||||||
};
|
};
|
||||||
|
@ -34,6 +51,8 @@ namespace h265 {
|
||||||
public:
|
public:
|
||||||
spsUnit(const std::string & _data);
|
spsUnit(const std::string & _data);
|
||||||
void updateHVCC(MP4::HVCC & hvccBox);
|
void updateHVCC(MP4::HVCC & hvccBox);
|
||||||
|
std::string toPrettyString(size_t indent = 0);
|
||||||
|
void getMeta(metaInfo & res);
|
||||||
private:
|
private:
|
||||||
std::string data;
|
std::string data;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "mp4_generic.h"
|
#include "mp4_generic.h"
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
|
#include "h265.h"
|
||||||
|
|
||||||
namespace MP4 {
|
namespace MP4 {
|
||||||
MFHD::MFHD() {
|
MFHD::MFHD() {
|
||||||
|
@ -866,7 +867,7 @@ namespace MP4 {
|
||||||
r << std::string(indent + 1, ' ') << "Arrays:" << std::endl;
|
r << std::string(indent + 1, ' ') << "Arrays:" << std::endl;
|
||||||
std::deque<HVCCArrayEntry> arrays = getArrays();
|
std::deque<HVCCArrayEntry> arrays = getArrays();
|
||||||
for (unsigned int i = 0; i < arrays.size(); i++){
|
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++){
|
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;
|
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();
|
return r.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h265::metaInfo HVCC::getMetaInfo(){
|
||||||
|
h265::initData init(std::string(payload(), payloadSize()));
|
||||||
|
return init.getMeta();
|
||||||
|
}
|
||||||
|
|
||||||
void HVCC::setPayload(std::string newPayload) {
|
void HVCC::setPayload(std::string newPayload) {
|
||||||
if (!reserve(0, payloadSize(), newPayload.size())) {
|
if (!reserve(0, payloadSize(), newPayload.size())) {
|
||||||
ERROR_MSG("Cannot allocate enough memory for payload");
|
ERROR_MSG("Cannot allocate enough memory for payload");
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "mp4.h"
|
#include "mp4.h"
|
||||||
|
|
||||||
|
namespace h265 {
|
||||||
|
class metaInfo;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MP4 {
|
namespace MP4 {
|
||||||
class MFHD: public Box {
|
class MFHD: public Box {
|
||||||
public:
|
public:
|
||||||
|
@ -174,6 +178,7 @@ namespace MP4 {
|
||||||
std::string asAnnexB();
|
std::string asAnnexB();
|
||||||
void setPayload(std::string newPayload);
|
void setPayload(std::string newPayload);
|
||||||
std::string toPrettyString(uint32_t indent = 0);
|
std::string toPrettyString(uint32_t indent = 0);
|
||||||
|
h265::metaInfo getMetaInfo();
|
||||||
};
|
};
|
||||||
|
|
||||||
class Descriptor{
|
class Descriptor{
|
||||||
|
|
|
@ -192,7 +192,8 @@ namespace TS{
|
||||||
metaInit[pid] = std::string(entry.getESInfo(), entry.getESInfoLength());
|
metaInit[pid] = std::string(entry.getESInfo(), entry.getESInfoLength());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default: break;
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
entry.advance();
|
entry.advance();
|
||||||
}
|
}
|
||||||
|
@ -660,9 +661,12 @@ namespace TS{
|
||||||
char typeNal;
|
char typeNal;
|
||||||
|
|
||||||
isKeyFrame = false;
|
isKeyFrame = false;
|
||||||
typeNal = pesPayload[0] & 0x1F;
|
if (pidToCodec[tid] == MPEG2){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (pidToCodec[tid] == H264){
|
if (pidToCodec[tid] == H264){
|
||||||
|
typeNal = pesPayload[0] & 0x1F;
|
||||||
switch (typeNal){
|
switch (typeNal){
|
||||||
case 0x01:{
|
case 0x01:{
|
||||||
if (firstSlice){
|
if (firstSlice){
|
||||||
|
@ -702,6 +706,7 @@ namespace TS{
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}else if (pidToCodec[tid] == H265){
|
}else if (pidToCodec[tid] == H265){
|
||||||
|
typeNal = (((pesPayload[0] & 0x7E) >> 1) & 0xFF);
|
||||||
switch (typeNal){
|
switch (typeNal){
|
||||||
case 2:
|
case 2:
|
||||||
case 3: // TSA Picture
|
case 3: // TSA Picture
|
||||||
|
@ -723,7 +728,8 @@ namespace TS{
|
||||||
case 32:
|
case 32:
|
||||||
case 33:
|
case 33:
|
||||||
case 34:{
|
case 34:{
|
||||||
hevcInfo[tid].addUnit((char *)pesPayload); // may i convert to (char *)?
|
tthread::lock_guard<tthread::recursive_mutex> guard(tMutex);
|
||||||
|
hevcInfo[tid].addUnit(std::string(pesPayload, nextPtr - pesPayload)); // may i convert to (char *)?
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: break;
|
default: break;
|
||||||
|
|
Loading…
Add table
Reference in a new issue