From faa696c6d1480c566af8bf84db39aef01cf52293 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 28 Feb 2017 16:24:55 +0100 Subject: [PATCH] Added H264 analyser (most code by Erik Zandvliet) --- CMakeLists.txt | 1 + lib/bitstream.cpp | 85 +++ lib/bitstream.h | 33 ++ lib/h264.cpp | 980 ++++++++++++++++++++++++++++++-- lib/h264.h | 230 ++++++++ src/analysers/h264_analyser.cpp | 30 + 6 files changed, 1297 insertions(+), 62 deletions(-) create mode 100644 src/analysers/h264_analyser.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 59facedb..95b684a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -273,6 +273,7 @@ makeAnalyser(RAX rax) #makeAnalyser(RTP rtp) #LTS makeAnalyser(RTSP rtsp_rtp) #LTS makeAnalyser(TS ts) #LTS +makeAnalyser(H264 h264) #LTS makeAnalyser(TSStream tsstream) #LTS makeAnalyser(Stats stats) #LTS diff --git a/lib/bitstream.cpp b/lib/bitstream.cpp index b7617814..5ed9b757 100644 --- a/lib/bitstream.cpp +++ b/lib/bitstream.cpp @@ -151,6 +151,91 @@ namespace Utils { return golombPeeker() - 1; } + + bitWriter::bitWriter(){ + dataBuffer = NULL; + bufferSize = 0; + reallocate(0); + dataSize = 0; + } + + bitWriter::~bitWriter(){ + if (dataBuffer != NULL){ + free(dataBuffer); + } + } + + void bitWriter::reallocate(size_t newSize){ + size_t sizeBefore = bufferSize / 8; + char * tmp; + if (dataBuffer != NULL){ + tmp = (char*)realloc(dataBuffer, (newSize / 8) + 1); + }else{ + tmp = (char*)malloc((newSize / 8) + 1); + } + if (tmp){ + dataBuffer = tmp; + bufferSize = ((newSize / 8) + 1) * 8; + memset(dataBuffer + sizeBefore, 0x00, (bufferSize / 8) - sizeBefore); + }else{ + FAIL_MSG("Could not reallocate!!"); + } + } + + size_t bitWriter::size() { + return dataSize; + } + + void bitWriter::append(uint64_t value, size_t bitLength){ + if (dataSize + bitLength > bufferSize){ + reallocate(dataSize + bitLength); + } + + int64_t fullShift = (bitLength / 8) * 8; + uint64_t firstMask = ((0x01ull << (bitLength % 8)) - 1) << fullShift; + + appendData( ((value & firstMask) >> fullShift), bitLength - fullShift); + while (fullShift > 0) { + fullShift -= 8; + uint64_t mask = (0xFFull) << fullShift; + appendData((value & mask) >> fullShift, 8); + } + } + + void bitWriter::appendData(uint8_t data, size_t len){ + size_t byteOffset = dataSize / 8; + size_t bitOffset = dataSize % 8; + if (len <= 8 - bitOffset){ + dataBuffer[byteOffset] |= (data << (8 - bitOffset - len)); + dataSize += len; + }else{ + size_t shift = (len - (8 - bitOffset)); + dataBuffer[byteOffset] |= (data >> shift); + dataSize += (len - shift); + appendData(data, shift); + } + } + + size_t bitWriter::UExpGolombEncodedSize(uint64_t value){ + value ++; + size_t res = 1; + size_t maxVal = 1; + while (value > maxVal){ + maxVal = (maxVal << 1 | 0x01); + res += 1; + } + return 2 * res - 1; + } + + void bitWriter::appendExpGolomb(uint64_t value){ + append(value + 1, UExpGolombEncodedSize(value)); + } + + void bitWriter::appendUExpGolomb(uint64_t value){ + append(value + 1, UExpGolombEncodedSize(value)); + } + + //Note: other bitstream here bitstreamLSBF::bitstreamLSBF() { readBufferOffset = 0; diff --git a/lib/bitstream.h b/lib/bitstream.h index bd623480..0119dc8a 100644 --- a/lib/bitstream.h +++ b/lib/bitstream.h @@ -1,5 +1,6 @@ #pragma once #include +#include "defines.h" namespace Utils { class bitstream { @@ -26,6 +27,17 @@ namespace Utils { long long unsigned int getUExpGolomb(); long long int peekExpGolomb(); long long unsigned int peekUExpGolomb(); + + static size_t bitSizeUExpGolomb(size_t value){ + size_t i = 1; + size_t power = 2; + while (power - 2 < value){ + i+= 2; + power *= 2; + } + return i; + } + private: bool checkBufferSize(unsigned int size); long long unsigned int golombGetter(); @@ -36,6 +48,27 @@ namespace Utils { size_t bufferSize; }; + class bitWriter { + public: + bitWriter(); + ~bitWriter(); + size_t size(); + void append(uint64_t value, size_t bitLength); + void appendExpGolomb(uint64_t value); + void appendUExpGolomb(uint64_t value); + static size_t UExpGolombEncodedSize(uint64_t value); + std::string str() { return std::string(dataBuffer, (dataSize / 8) + (dataSize % 8 ? 1 : 0)); } + protected: + void reallocate(size_t newSize); + void appendData(uint8_t data, size_t len); + + char * dataBuffer; + //NOTE: ALL SIZES IN BITS! + size_t bufferSize; + + size_t dataSize; + }; + class bitstreamLSBF { public: bitstreamLSBF(); diff --git a/lib/h264.cpp b/lib/h264.cpp index 602bd434..91203890 100644 --- a/lib/h264.cpp +++ b/lib/h264.cpp @@ -2,8 +2,9 @@ #define _GNU_SOURCE #endif #include "h264.h" -#include +#include #include +#include #include "bitfields.h" #include "bitstream.h" #include "defines.h" @@ -24,66 +25,7 @@ namespace h264 { return res; } - unsigned long toAnnexB(const char * data, unsigned long dataSize, char *& result){ - //toAnnexB keeps the same size. - if (!result){ - result = (char *)malloc(dataSize); - } - int offset = 0; - while (offset < dataSize){ - //Read unit size - unsigned long unitSize = Bit::btohl(data + offset); - //Write annex b header - memset(result + offset, 0x00, 3); - result[offset + 3] = 0x01; - //Copy the nal unit - memcpy(result + offset + 4, data + offset + 4, unitSize); - //Update the offset - offset += 4 + unitSize; - } - return dataSize; - } - - unsigned long fromAnnexB(const char * data, unsigned long dataSize, char *& result){ - const char * lastCheck = data + dataSize - 3; - if (!result){ - FAIL_MSG("No output buffer given to FromAnnexB"); - return 0; - } - int offset = 0; - int newOffset = 0; - while (offset < dataSize){ - const char * begin = data + offset; - while ( begin < lastCheck && !(!begin[0] && !begin[1] && begin[2] == 0x01)){ - begin++; - if (begin < lastCheck && begin[0]){ - begin++; - } - } - begin += 3;//Initialize begin after the first 0x000001 pattern. - if (begin > data + dataSize){ - offset = dataSize; - continue; - } - const char * end = (const char*)memmem(begin, dataSize - (begin - data), "\000\000\001", 3); - if (!end) { - end = data + dataSize; - } - //Check for 4-byte lead in's. Yes, we access -1 here - if (end > begin && end[-1] == 0x00){ - end--; - } - unsigned int nalSize = end - begin; - Bit::htobl(result + newOffset, nalSize); - memcpy(result + newOffset + 4, begin, nalSize); - - newOffset += 4 + nalSize; - offset = end - data; - } - return newOffset; - } - - sequenceParameterSet::sequenceParameterSet(const char * _data, unsigned long _dataLen) : data(_data), dataLen(_dataLen) {} + sequenceParameterSet::sequenceParameterSet(const char * _data, size_t _dataLen) : data(_data), dataLen(_dataLen) {} //DTSC Initdata is the payload for an avcc box. init[8+] is data, init[6-7] is a network-encoded length void sequenceParameterSet::fromDTSCInit(const std::string & dtscInit){ @@ -186,7 +128,6 @@ namespace h264 { } //vuiParameters - result.fps = 0;//default in case not given if (bs.get(1)) { //Skipping all the paramters we dont use if (bs.get(1)) { @@ -222,5 +163,920 @@ namespace h264 { return result; } + void spsUnit::scalingList(uint64_t * scalingList, size_t sizeOfScalingList, bool & useDefaultScalingMatrixFlag, Utils::bitstream & bs){ + int lastScale = 8; + int nextScale = 8; + for (int i = 0; i < sizeOfScalingList; i++){ + if (nextScale){ + int64_t deltaScale = bs.getExpGolomb(); + nextScale = (lastScale + deltaScale + 256) % 256; + useDefaultScalingMatrixFlag = (i == 0 && nextScale == 0); + } + scalingList[i] = (nextScale == 0 ? lastScale : nextScale); + lastScale = scalingList[i]; + } + } + + spsUnit::spsUnit(const char * data, size_t len) : nalUnit(data, len) { + scalingListPresentFlags = NULL; + scalingList4x4 = NULL; + useDefaultScalingMatrix4x4Flag = NULL; + scalingList8x8 = NULL; + useDefaultScalingMatrix8x8Flag = NULL; + derived_subWidthC = 0; + derived_subHeightC = 0; + derived_mbWidthC = 0; + derived_mbHeightC = 0; + derived_scalingList4x4Amount = 0; + derived_scalingList8x8Amount = 0; + + //Fill the bitstream + Utils::bitstream bs; + for (size_t i = 1; i < len; i++) { + if (i + 2 < len && (memcmp(data + i, "\000\000\003", 3) == 0)){//Emulation prevention bytes + //Yes, we increase i here + bs.append(data + i, 2); + i += 2; + } else { + //No we don't increase i here + bs.append(data + i, 1); + } + } + profileIdc = bs.get(8); + constraintSet0Flag = bs.get(1); + constraintSet1Flag = bs.get(1); + constraintSet2Flag = bs.get(1); + constraintSet3Flag = bs.get(1); + constraintSet4Flag = bs.get(1); + constraintSet5Flag = bs.get(1); + bs.skip(2); + levelIdc = bs.get(8); + seqParameterSetId = bs.getUExpGolomb(); + switch (profileIdc){ + case 100: + case 110: + case 122: + case 244: + case 44: + case 83: + case 86: + case 118: + case 128: + chromaFormatIdc = bs.getUExpGolomb(); + if (chromaFormatIdc == 3){ + separateColourPlaneFlag = bs.get(1); + } + if (chromaFormatIdc == 1){ + derived_subWidthC = 2; + derived_subHeightC = 2; + } + if (chromaFormatIdc == 2){ + derived_subWidthC = 2; + derived_subHeightC = 1; + } + if (chromaFormatIdc == 3 && !separateColourPlaneFlag){ + derived_subWidthC = 1; + derived_subHeightC = 1; + } + if (derived_subWidthC){ + derived_mbWidthC = 16 / derived_subWidthC; + derived_mbHeightC = 16 / derived_subHeightC; + } + + bitDepthLumaMinus8 = bs.getUExpGolomb(); + derived_bitDepth_Y = 8 + bitDepthLumaMinus8; + derived_qpBdOffset_Y = 6 * bitDepthLumaMinus8; + bitDepthChromaMinus8 = bs.getUExpGolomb(); + derived_bitDepth_C = 8 + bitDepthChromaMinus8; + derived_qpBdOffset_C = 6 * bitDepthChromaMinus8; + derived_rawMbBits = 256 * derived_bitDepth_Y + 2 * derived_mbWidthC * derived_mbHeightC * derived_bitDepth_C; + qpprimeYZeroTransformBypassFlag = bs.get(1); + seqScalingMatrixPresentFlag = bs.get(1); + if (seqScalingMatrixPresentFlag){ + derived_scalingListSize = (chromaFormatIdc == 3 ? 12 : 8); + scalingListPresentFlags = (uint8_t*)malloc(derived_scalingListSize * sizeof(uint8_t)); + + derived_scalingList4x4Amount = 6; + scalingList4x4 = (uint64_t **)malloc(derived_scalingList4x4Amount * sizeof(uint64_t*)); + useDefaultScalingMatrix4x4Flag = (bool*)malloc(derived_scalingList4x4Amount * sizeof(bool)); + for (int i = 0; i < derived_scalingList4x4Amount; i++){ + scalingList4x4[i] = NULL; + useDefaultScalingMatrix4x4Flag[i] = false; + } + + + derived_scalingList8x8Amount = derived_scalingListSize - 6; + scalingList8x8 = (uint64_t **)malloc(derived_scalingList8x8Amount * sizeof(uint64_t*)); + useDefaultScalingMatrix8x8Flag = (bool*)malloc(derived_scalingList8x8Amount * sizeof(bool)); + for (int i = 0; i < derived_scalingList8x8Amount; i++){ + scalingList8x8[i] = NULL; + useDefaultScalingMatrix8x8Flag[i] = false; + } + + for (size_t i = 0; i < derived_scalingListSize; i++){ + scalingListPresentFlags[i] = bs.get(1); + if (scalingListPresentFlags[i]){ + if (i < 6){ + scalingList4x4[i] = (uint64_t*)malloc(16 * sizeof(uint64_t)); + scalingList(scalingList4x4[i], 16, useDefaultScalingMatrix4x4Flag[i], bs); + }else{ + scalingList8x8[i-6] = (uint64_t*)malloc(64 * sizeof(uint64_t)); + scalingList(scalingList8x8[i-6], 64, useDefaultScalingMatrix8x8Flag[i-6], bs); + } + } + } + } + break; + default: + break; + } + log2MaxFrameNumMinus4 = bs.getUExpGolomb(); + derived_maxFrameNum = pow(2, log2MaxFrameNumMinus4 + 4); + picOrderCntType = bs.getUExpGolomb(); + if (!picOrderCntType){ + log2MaxPicOrderCntLsbMinus4 = bs.getUExpGolomb(); + derived_maxPicOrderCntLsb = pow(2, log2MaxPicOrderCntLsbMinus4 + 4); + }else if (picOrderCntType == 1){ + WARN_MSG("picOrderCntType == 1 encountered, which has not-yet implemented parameters"); + return; + } + maxNumRefFrames = bs.getUExpGolomb(); + gapsInFrameNumValueAllowedFlag = bs.get(1); + picWidthInMbsMinus1 = bs.getUExpGolomb(); + derived_picWidthInMbs = picWidthInMbsMinus1 + 1; + derived_picWidthInSamples_L = derived_picWidthInMbs * 16; + derived_picWidthInSamples_C = derived_picWidthInMbs * derived_mbWidthC; + picHeightInMapUnitsMinus1 = bs.getUExpGolomb(); + derived_picHeightInMapUnits = picHeightInMapUnitsMinus1 + 1; + derived_picSizeInMapUnits = derived_picWidthInMbs * derived_picHeightInMapUnits; + frameMbsOnlyFlag = bs.get(1); + derived_frameHeightInMbs = (2 - (frameMbsOnlyFlag ? 1 : 0)) * derived_picHeightInMapUnits; + if (!frameMbsOnlyFlag){ + mbAdaptiveFrameFieldFlag = bs.get(1); + } + direct8x8InferenceFlag = bs.get(1); + frameCroppingFlag = bs.get(1); + if (frameCroppingFlag){ + frameCropLeftOffset = bs.getUExpGolomb(); + frameCropRightOffset = bs.getUExpGolomb(); + frameCropTopOffset = bs.getUExpGolomb(); + frameCropBottomOffset = bs.getUExpGolomb(); + } + vuiParametersPresentFlag = bs.get(1); + if (vuiParametersPresentFlag){ + vuiParams = vui_parameters(bs); + } + } + + void spsUnit::setSPSNumber(size_t newNumber){ + //for now, can only convert from 0 to 16 + if (seqParameterSetId != 0){ + return; + } + seqParameterSetId = 16; + payload.insert(4, 1, 0x08); + } + + void spsUnit::toPrettyString(std::ostream & out) { + out << "Nal unit of type " << (((uint8_t)payload[0]) & 0x1F) << " [Sequence Parameter Set] , " << payload.size() << " bytes long" << std::endl; + out << " profile_idc: 0x" << std::setw(2) << std::setfill('0') << std::hex << (int)profileIdc << std::dec << " (" << (int)profileIdc << ")" << std::endl; + out << " contraints: " << (constraintSet0Flag ? "0 " : "") << (constraintSet1Flag ? "1 " : "") << (constraintSet2Flag ? "2 " : "") << (constraintSet3Flag ? "3 " : "") << (constraintSet4Flag ? "4 " : "") << (constraintSet5Flag ? "5" : "") << std::endl; + out << " level_idc: 0x" << std::setw(2) << std::setfill('0') << std::hex << (int)levelIdc << std::dec << " (" << (int)profileIdc << ")" << std::endl; + out << " seq_parameter_set_id: " << seqParameterSetId << (seqParameterSetId >= 32 ? " INVALID" : "") << std::endl; + switch (profileIdc){ + case 100: + case 110: + case 122: + case 244: + case 44: + case 83: + case 86: + case 118: + case 128: + out << " chroma_format_idc: " << chromaFormatIdc << (chromaFormatIdc >= 4 ? " INVALID" : "") << std::endl; + if (chromaFormatIdc == 3){ + out << " separate_colour_plane_flag: " << (separateColourPlaneFlag ? 1 : 0) << std::endl; + } + out << " bit_depth_luma_minus_8: " << bitDepthLumaMinus8 << (bitDepthLumaMinus8 >= 7 ? " INVALID": "") << std::endl; + out << " -> BitDepth_Y: " << derived_bitDepth_Y << std::endl; + out << " -> QpBdOffset_Y: " << derived_qpBdOffset_Y << std::endl; + out << " bit_depth_chroma_minus_8: " << bitDepthChromaMinus8 << (bitDepthChromaMinus8 >= 7 ? " INVALID": "") << std::endl; + out << " -> BitDepth_C: " << derived_bitDepth_C << std::endl; + out << " -> QpBdOffset_C: " << derived_qpBdOffset_C << std::endl; + out << " -> RawMbBits: " << derived_rawMbBits << std::endl; + out << " qpprime_y_zero-transform_bypass_flag: " << (qpprimeYZeroTransformBypassFlag ? 1 : 0) << std::endl; + out << " seq_scaling_matrix_present_flag: " << (seqScalingMatrixPresentFlag ? 1 : 0) << std::endl; + if (seqScalingMatrixPresentFlag){ + for (int i = 0; i < derived_scalingListSize; i++){ + out << " seq_scaling_list_present_flag[" << i << "]: " << (scalingListPresentFlags[i] ? 1 : 0) << std::endl; + if (scalingListPresentFlags[i]){ + if (i < 6){ + for (int j = 0; j < 16; j++){ + out << " scalingMatrix4x4[" << i << "][" << j << "]: " << scalingList4x4[i][j] << std::endl; + } + out << " useDefaultScalingMatrix4x4Flag[" << i << "]: " << (useDefaultScalingMatrix4x4Flag[i] ? 1 : 0) << std::endl; + }else{ + for (int j = 0; j < 64; j++){ + out << " scalingMatrix8x8[" << i-6 << "][" << j << "]: " << scalingList8x8[i-6][j] << std::endl; + } + out << " useDefaultScalingMatrix8x8Flag[" << i - 6 << "]: " << (useDefaultScalingMatrix8x8Flag[i - 6] ? 1 : 0) << std::endl; + } + } + } + + } + break; + default: + break; + } + out << " log2_max_frame_num_minus4: " << log2MaxFrameNumMinus4 << (log2MaxFrameNumMinus4 >= 13 ? " INVALID" : "") << std::endl; + out << " -> MaxFrameNum: " << derived_maxFrameNum << std::endl; + out << " pic_order_cnt_type: " << picOrderCntType << (picOrderCntType >= 3 ? " INVALID" : "") << std::endl; + if (!picOrderCntType){ + out << " log2_max_pic_order_cnt_lsb_minus4: " << log2MaxPicOrderCntLsbMinus4 << (log2MaxPicOrderCntLsbMinus4 >= 13 ? " INVALID" : "") << std::endl; + out << " -> MaxPicOrderCntLsb: " << derived_maxPicOrderCntLsb << std::endl; + } + out << " max_num_ref_frames: " << maxNumRefFrames << std::endl; + out << " gaps_in_frame_num_value_allowed_flag: " << (gapsInFrameNumValueAllowedFlag ? 1 : 0) << std::endl; + out << " pic_width_in_mbs_minus_1: " << picWidthInMbsMinus1 << std::endl; + out << " -> PicWidthInMbs: " << derived_picWidthInMbs << std::endl; + out << " -> PicWidthInSamples_L: " << derived_picWidthInSamples_L << std::endl; + out << " -> PicWidthInSamples_C: " << derived_picWidthInSamples_C << std::endl; + out << " pic_height_in_map_units_minus_1: " << picHeightInMapUnitsMinus1 << std::endl; + out << " -> PicHeightInMapUnits: " << derived_picHeightInMapUnits << std::endl; + out << " -> PicSizeInMapUnits: " << derived_picSizeInMapUnits << std::endl; + out << " frame_mbs_only_flag: " << frameMbsOnlyFlag << std::endl; + out << " -> FrameHeightInMbs: " << derived_frameHeightInMbs << std::endl; + if (!frameMbsOnlyFlag){ + out << " mb_adaptive_frame_field_flag: " << mbAdaptiveFrameFieldFlag << std::endl; + } + out << " direct_8x8_inference_flag: " << direct8x8InferenceFlag << std::endl; + out << " frame_cropping_flag: " << frameCroppingFlag << std::endl; + if (frameCroppingFlag){ + out << " frame_crop_left_offset: " << frameCropLeftOffset << std::endl; + out << " frame_crop_right_offset: " << frameCropRightOffset << std::endl; + out << " frame_crop_top_offset: " << frameCropTopOffset << std::endl; + out << " frame_crop_bottom_offset: " << frameCropBottomOffset << std::endl; + } + out << " vui_parameter_present_flag: " << vuiParametersPresentFlag << std::endl; + if (vuiParametersPresentFlag){ + vuiParams.toPrettyString(out, 2); + } + } + + std::string spsUnit::generate() { + Utils::bitWriter bw; + bw.append(0x07, 8); + bw.append(profileIdc, 8); + bw.append(constraintSet0Flag ? 1 : 0, 1); + bw.append(constraintSet1Flag ? 1 : 0, 1); + bw.append(constraintSet2Flag ? 1 : 0, 1); + bw.append(constraintSet3Flag ? 1 : 0, 1); + bw.append(constraintSet4Flag ? 1 : 0, 1); + bw.append(constraintSet5Flag ? 1 : 0, 1); + bw.append(0x00, 2); + bw.append(levelIdc, 8); + bw.appendUExpGolomb(seqParameterSetId); + switch (profileIdc){ + case 100: + case 110: + case 122: + case 244: + case 44: + case 83: + case 86: + case 118: + case 128: + bw.appendUExpGolomb(chromaFormatIdc); + if (chromaFormatIdc == 3){ + bw.append(separateColourPlaneFlag ? 1 : 0, 1); + } + bw.appendUExpGolomb(bitDepthLumaMinus8); + bw.appendUExpGolomb(bitDepthChromaMinus8); + bw.append(qpprimeYZeroTransformBypassFlag, 1); + bw.append(seqScalingMatrixPresentFlag, 1); + if (seqScalingMatrixPresentFlag){ + for (int i = 0; i < derived_scalingListSize; i++){ + bw.append(0, 1); + } + } + break; + default: + break; + } + bw.appendUExpGolomb(log2MaxFrameNumMinus4); + bw.appendUExpGolomb(picOrderCntType); + if (picOrderCntType == 0){ + bw.appendUExpGolomb(log2MaxPicOrderCntLsbMinus4); + } + bw.appendUExpGolomb(maxNumRefFrames); + bw.append(gapsInFrameNumValueAllowedFlag ? 1 : 0, 1); + bw.appendUExpGolomb(picWidthInMbsMinus1); + bw.appendUExpGolomb(picHeightInMapUnitsMinus1); + bw.append(frameMbsOnlyFlag ? 1 : 0, 1); + if (!frameMbsOnlyFlag){ + bw.append(mbAdaptiveFrameFieldFlag ? 1 : 0, 1); + } + bw.append(direct8x8InferenceFlag ? 1 : 0, 1); + bw.append(frameCroppingFlag ? 1 : 0, 1); + if (frameCroppingFlag){ + bw.appendUExpGolomb(frameCropLeftOffset); + bw.appendUExpGolomb(frameCropRightOffset); + bw.appendUExpGolomb(frameCropTopOffset); + bw.appendUExpGolomb(frameCropBottomOffset); + } + bw.append(vuiParametersPresentFlag ? 1 : 0, 1); + if (vuiParametersPresentFlag){ + vuiParams.generate(bw); + } + bw.append(1,1); + std::string tmp = bw.str(); + std::string res; + for (int i = 0; i < tmp.size(); i++){ + if (res.size() > 2 && res[res.size() - 1] == 0x00 && res[res.size() - 2] == 0x00){ + if (tmp[i] == 0x00 || tmp[i] == 0x01 || tmp[i] == 0x02 || tmp[i] == 0x03){ + res += (char)0x03; + } + } + res += tmp[i]; + } + return res; + } + + bool more_rbsp_data(Utils::bitstream & bs){ + if (bs.size() < 8){ + //todo IMPLEMENT + WARN_MSG("bs.size() < 8, not sure if return value is correct"); + return false; + } + return true; + } + + void ppsUnit::scalingList(uint64_t * scalingList, size_t sizeOfScalingList, bool & useDefaultScalingMatrixFlag, Utils::bitstream & bs){ + int lastScale = 8; + int nextScale = 8; + for (int i = 0; i < sizeOfScalingList; i++){ + if (nextScale){ + int64_t deltaScale = bs.getExpGolomb(); + nextScale = (lastScale + deltaScale + 256) % 256; + useDefaultScalingMatrixFlag = (i == 0 && nextScale == 0); + } + scalingList[i] = (nextScale == 0 ? lastScale : nextScale); + lastScale = scalingList[i]; + } + } + ppsUnit::ppsUnit(const char * data, size_t len, uint8_t chromaFormatIdc) : nalUnit(data, len) { + picScalingMatrixPresentFlags = NULL; + Utils::bitstream bs; + for (size_t i = 1; i < len; i++) { + if (i + 2 < len && (memcmp(data + i, "\000\000\003", 3) == 0)){//Emulation prevention bytes + //Yes, we increase i here + bs.append(data + i, 2); + i += 2; + } else { + //No we don't increase i here + bs.append(data + i, 1); + } + } + picParameterSetId = bs.getUExpGolomb(); + seqParameterSetId = bs.getUExpGolomb(); + entropyCodingModeFlag = bs.get(1); + bottomFieldPicOrderInFramePresentFlag = bs.get(1); + numSliceGroupsMinus1 = bs.getUExpGolomb(); + if (numSliceGroupsMinus1 > 0){ + WARN_MSG("num_slice_groups_minus1 > 0, unimplemented structure"); + return; + } + numrefIdx10DefaultActiveMinus1 = bs.getUExpGolomb(); + numrefIdx11DefaultActiveMinus1 = bs.getUExpGolomb(); + weightedPredFlag = bs.get(1); + weightedBipredIdc = bs.get(2); + picInitQpMinus26 = bs.getExpGolomb(); + picInitQsMinus26 = bs.getExpGolomb(); + chromaQpIndexOffset = bs.getExpGolomb(); + deblockingFilterControlPresentFlag = bs.get(1); + constrainedIntraPredFlag = bs.get(1); + redundantPicCntPresentFlag = bs.get(1); + if (!more_rbsp_data(bs)){ + status_moreRBSP = false; + return; + } + status_moreRBSP = true; + transform8x8ModeFlag = bs.get(1); + picScalingMatrixPresentFlag = bs.get(1); + if (picScalingMatrixPresentFlag){ + derived_scalingListSize = 6 + (chromaFormatIdc ? ((chromaFormatIdc == 3 ? 6 : 2) * transform8x8ModeFlag) : 0); + picScalingMatrixPresentFlags = (uint8_t*)malloc(derived_scalingListSize * sizeof(uint8_t)); + + + derived_scalingList4x4Amount = 6; + scalingList4x4 = (uint64_t **)malloc(derived_scalingList4x4Amount * sizeof(uint64_t*)); + useDefaultScalingMatrix4x4Flag = (bool*)malloc(derived_scalingList4x4Amount * sizeof(bool)); + for (int i = 0; i < derived_scalingList4x4Amount; i++){ + scalingList4x4[i] = NULL; + useDefaultScalingMatrix4x4Flag[i] = false; + } + + + derived_scalingList8x8Amount = derived_scalingListSize - 6; + scalingList8x8 = (uint64_t **)malloc(derived_scalingList8x8Amount * sizeof(uint64_t*)); + useDefaultScalingMatrix8x8Flag = (bool*)malloc(derived_scalingList8x8Amount * sizeof(bool)); + for (int i = 0; i < derived_scalingList8x8Amount; i++){ + scalingList8x8[i] = NULL; + useDefaultScalingMatrix8x8Flag[i] = false; + } + + for (size_t i = 0; i < derived_scalingListSize; i++){ + picScalingMatrixPresentFlags[i] = bs.get(1); + if (picScalingMatrixPresentFlags[i]){ + if (i < 6){ + scalingList4x4[i] = (uint64_t*)malloc(16 * sizeof(uint64_t)); + scalingList(scalingList4x4[i], 16, useDefaultScalingMatrix4x4Flag[i], bs); + }else{ + scalingList8x8[i-6] = (uint64_t*)malloc(64 * sizeof(uint64_t)); + scalingList(scalingList8x8[i-6], 64, useDefaultScalingMatrix8x8Flag[i-6], bs); + } + } + } + } + secondChromaQpIndexOffset = bs.getExpGolomb(); + + INFO_MSG("PPS Bits left: %llu", bs.size()); + } + + void ppsUnit::setPPSNumber(size_t newNumber){ + //for now, can only convert from 0 to 16 + if (seqParameterSetId != 0 || picParameterSetId != 0){ + return; + } + picParameterSetId = 16; + payload.insert(1, 1, 0x08); + } + + void ppsUnit::setSPSNumber(size_t newNumber){ + //for now, can only convert from 0 to 16 + if (seqParameterSetId != 0 || picParameterSetId != 16){ + return; + } + seqParameterSetId = 16; + payload.insert(2, 1, 0x84); + payload[3] &= 0x7F; + } + + void ppsUnit::toPrettyString(std::ostream & out) { + out << "Nal unit of type " << (((uint8_t)payload[0]) & 0x1F) << " [Picture Parameter Set] , " << payload.size() << " bytes long" << std::endl; + out << " pic_parameter_set_id: " << picParameterSetId << (picParameterSetId >= 256 ? " INVALID" : "") << std::endl; + out << " seq_parameter_set_id: " << seqParameterSetId << (seqParameterSetId >= 32 ? " INVALID" : "") << std::endl; + out << " entropy_coding_mode_flag: " << (entropyCodingModeFlag ? 1 : 0) << std::endl; + out << " bottom_field_pic_order_in_frame_present_flag: " << (bottomFieldPicOrderInFramePresentFlag ? 1 : 0) << std::endl; + out << " num_slice_groups_minus1: " << numSliceGroupsMinus1 << std::endl; + if (numSliceGroupsMinus1 > 0){ + return; + } + out << " num_ref_idx_10_default_active_minus1: " << numrefIdx10DefaultActiveMinus1 << std::endl; + out << " num_ref_idx_11_default_active_minus1: " << numrefIdx11DefaultActiveMinus1 << std::endl; + out << " weighted_pred_flag: " << (weightedPredFlag ? 1 : 0) << std::endl; + out << " weighted_bipred_idc: " << (uint32_t)weightedBipredIdc << std::endl; + out << " pic_init_qp_minus26: " << picInitQpMinus26 << std::endl; + out << " pic_init_qs_minus26: " << picInitQsMinus26 << std::endl; + out << " chroma_qp_index_offset: " << chromaQpIndexOffset << std::endl; + out << " deblocking_filter_control_present_flag: " << deblockingFilterControlPresentFlag << std::endl; + out << " constrained_intra_pred_flag: " << constrainedIntraPredFlag << std::endl; + out << " redundant_pic_cnt_present_flag: " << redundantPicCntPresentFlag << std::endl; + if (status_moreRBSP){ + out << " transform_8x8_mode_flag: " << transform8x8ModeFlag << std::endl; + out << " pic_scaling_matrix_present_flag: " << picScalingMatrixPresentFlag << std::endl; + if (picScalingMatrixPresentFlag){ + for (int i = 0; i < derived_scalingListSize; i++){ + out << " pic_scaling_matrix_present_flag[" << i << "]: " << (picScalingMatrixPresentFlags[i] ? 1 : 0) << std::endl; + if (picScalingMatrixPresentFlags[i]){ + if (i < 6){ + for (int j = 0; j < 16; j++){ + out << " scalingMatrix4x4[" << i << "][" << j << "]: " << scalingList4x4[i][j] << std::endl; + } + out << " useDefaultScalingMatrix4x4Flag[" << i << "]: " << (useDefaultScalingMatrix4x4Flag[i] ? 1 : 0) << std::endl; + }else{ + for (int j = 0; j < 64; j++){ + out << " scalingMatrix8x8[" << i-6 << "][" << j << "]: " << scalingList8x8[i-6][j] << std::endl; + } + out << " useDefaultScalingMatrix8x8Flag[" << i - 6 << "]: " << (useDefaultScalingMatrix8x8Flag[i - 6] ? 1 : 0) << std::endl; + } + } + } + } + out << " second_chroma_qp_index_offset: " << secondChromaQpIndexOffset << std::endl; + } + } + + std::string ppsUnit::generate() { + Utils::bitWriter bw; + bw.appendUExpGolomb(picParameterSetId); + bw.appendUExpGolomb(seqParameterSetId); + bw.append(entropyCodingModeFlag ? 1 : 0, 1); + bw.append(bottomFieldPicOrderInFramePresentFlag ? 1 : 0, 1); + if(numSliceGroupsMinus1 > 0){ + INFO_MSG("Forcing to numSliceGroupsMinus1 == 0"); + } + bw.appendUExpGolomb(0);//numSliceGroupsMinus1 + bw.appendUExpGolomb(numrefIdx10DefaultActiveMinus1); + bw.appendUExpGolomb(numrefIdx11DefaultActiveMinus1); + bw.append(weightedPredFlag ? 1 : 0, 1); + bw.append(weightedBipredIdc, 2); + bw.appendExpGolomb(picInitQpMinus26); + bw.appendExpGolomb(picInitQsMinus26); + bw.appendExpGolomb(chromaQpIndexOffset); + bw.append(deblockingFilterControlPresentFlag ? 1 : 0, 1); + bw.append(constrainedIntraPredFlag ? 1 : 0, 1); + bw.append(redundantPicCntPresentFlag ? 1 : 0, 1); + + if (status_moreRBSP){ + bw.append(transform8x8ModeFlag ? 1 : 0, 1); + bw.append(picScalingMatrixPresentFlag, 1); + if (picScalingMatrixPresentFlag){ + for (int i = 0; i < derived_scalingListSize; i++){ + bw.append(0, 1);//picScalingMatrixPresnetFlags[i] + } + } + bw.appendExpGolomb(secondChromaQpIndexOffset); + } + + std::string tmp = bw.str(); + std::string res; + for (int i = 0; i < tmp.size(); i++){ + if (res.size() > 2 && res[res.size() - 1] == 0x00 && res[res.size() - 2] == 0x00){ + if (tmp[i] == 0x00 || tmp[i] == 0x01 || tmp[i] == 0x02 || tmp[i] == 0x03){ + res += (char)0x03; + } + } + res += tmp[i]; + } + return res; + } + + codedSliceUnit::codedSliceUnit(const char * data, size_t len) : nalUnit(data, len) { + Utils::bitstream bs; + for (size_t i = 1; i < len; i++) { + if (i + 2 < len && (memcmp(data + i, "\000\000\003", 3) == 0)){//Emulation prevention bytes + //Yes, we increase i here + bs.append(data + i, 2); + i += 2; + } else { + //No we don't increase i here + bs.append(data + i, 1); + } + } + firstMbInSlice = bs.getUExpGolomb(); + sliceType = bs.getUExpGolomb(); + picParameterSetId = bs.getUExpGolomb(); + } + + void codedSliceUnit::setPPSNumber(size_t newNumber){ + //for now, can only convert from 0 to 16 + if (picParameterSetId != 0){ + return; + } + size_t bitOffset = 0; + bitOffset += Utils::bitstream::bitSizeUExpGolomb(firstMbInSlice); + bitOffset += Utils::bitstream::bitSizeUExpGolomb(sliceType); + size_t byteOffset = bitOffset / 8; + bitOffset -= (byteOffset * 8); + INFO_MSG("Offset for this value: %d bytes and %d bits", byteOffset, bitOffset); + size_t firstBitmask = ((1 << bitOffset) - 1) << (8-bitOffset); + size_t secondBitmask = (1 << (8-bitOffset)) -1; + INFO_MSG("Bitmasks: %0.2X, %0.2X", firstBitmask, secondBitmask); + char toCopy = payload[1+byteOffset]; + payload.insert(1+byteOffset, 1, toCopy); + payload[1+byteOffset] &= firstBitmask; + payload[1+byteOffset] |= (0x08 >> bitOffset); + payload[2+byteOffset] &= secondBitmask; + payload[2+byteOffset] |= (0x08 << (8-bitOffset)); + INFO_MSG("Translated %0.2X to %0.2X %0.2X", toCopy, payload[1+byteOffset], payload[2+byteOffset]); + } + + void codedSliceUnit::toPrettyString(std::ostream & out) { + std::string strSliceType = "Unknown"; + switch (sliceType){ + case 5: + case 0: strSliceType = "P - Predictive slice (at most 1 reference)"; break; + case 6: + case 1: strSliceType = " B - Bi-predictive slice (at most 2 references)"; break; + case 7: + case 2: strSliceType = " I - Intra slice (no external references)"; break; + case 8: + case 3: strSliceType = " SP - Switching predictive slice (at most 1 reference)"; break; + case 9: + case 4: strSliceType = " SI - Switching intra slice (no external references)"; break; + } + out << "Nal unit of type " << (((uint8_t)payload[0]) & 0x1F) << " [Coded Slice] , " << payload.size() << " bytes long" << std::endl; + out << " first_mb_in_slice: " << firstMbInSlice << std::endl; + out << " slice_type " << sliceType << ": " << strSliceType << std::endl; + out << " pic_parameter_set_id: " << picParameterSetId << (picParameterSetId >= 256 ? " INVALID" : "") << std::endl; + } + + + seiUnit::seiUnit(const char * data, size_t len) : nalUnit(data, len) { + Utils::bitstream bs; + for (size_t i = 1; i < len; i++) { + if (i + 2 < len && (memcmp(data + i, "\000\000\003", 3) == 0)){//Emulation prevention bytes + //Yes, we increase i here + bs.append(data + i, 2); + i += 2; + } else { + //No we don't increase i here + bs.append(data + i, 1); + } + } + uint8_t tmp = bs.get(8); + payloadType = 0; + while (tmp == 0xFF){ + payloadType += tmp; + tmp = bs.get(8); + } + payloadType += tmp; + + tmp = bs.get(8); + payloadSize = 0; + while (tmp == 0xFF){ + payloadSize += tmp; + tmp = bs.get(8); + } + payloadSize += tmp; + } + + void seiUnit::toPrettyString(std::ostream & out) { + out << "Nal unit of type " << (((uint8_t)payload[0]) & 0x1F) << " [Supplemental Enhancement Unit] , " << payload.size() << " bytes long" << std::endl; + out << " Message of type " << payloadType << ", " << payloadSize << " bytes long" << std::endl; + } + + nalUnit * nalFactory(char * _data, size_t _len, size_t & offset, bool annexb) { + char * data = _data + offset; + size_t len = _len - offset; + nalUnit * result = NULL; + if (len < 4){ + return result; + } + if (annexb){ + FAIL_MSG("Not supported in annexb mode yet"); + return result; + } + uint32_t pktLen = Bit::btohl(data); + if (len < 4 + pktLen){ + return result; + } + switch (data[5] & 0x1F){ + case 1: + case 5: + result = new codedSliceUnit(data + 4, pktLen); + break; + case 6: + result = new seiUnit(data + 4, pktLen); + break; + case 7: + result = new spsUnit(data + 4, pktLen); + break; + case 8: + result = new ppsUnit(data + 4, pktLen); + break; + default: + result = new nalUnit(data + 4, pktLen); + break; + } + offset += 4 + pktLen; + return result; + } + + nalUnit * nalFactory(FILE * in, bool annexb) { + nalUnit * result = NULL; + char size[4]; + if (fread(size, 4, 1, in)){ + if (annexb){ + size_t curPos = ftell(in); + if (size[2] == 0x01){ + curPos --; + } + fseek(in, curPos, SEEK_SET); + char * data= (char*)malloc(1024 * 1024 * sizeof(char));//allocate 1MB in size + size_t len = fread(data, 1, 1024 * 1024, in); + if (len){ + std::string str(data, len); + size_t nextPos = str.find("\000\000\001", 0, 3); + if (nextPos == std::string::npos && feof(in)){ + nextPos = len; + } + if (nextPos != std::string::npos){ + if (str[nextPos - 1] == 0x00){ + nextPos --; + } + switch (data[0] & 0x1F){ + case 1: + case 5: + result = new codedSliceUnit(data, nextPos); + break; + case 6: + result = new seiUnit(data, nextPos); + break; + case 7: + result = new spsUnit(data, nextPos); + break; + case 8: + result = new ppsUnit(data, nextPos); + break; + default: + result = new nalUnit(data, nextPos); + break; + } + fseek(in, curPos + nextPos, SEEK_SET); + }else{ + FAIL_MSG("NAL Unit of over 1MB, unexpected behaviour until next AnnexB boundary in file"); + } + } + free(data); + }else{ + uint32_t len = Bit::btohl(size); + char * data = (char*)malloc(len * sizeof(char)); + if (fread(data, len, 1, in)){ + switch (data[0] & 0x1F){ + case 7: + result = new spsUnit(data, len); + break; + default: + result = new nalUnit(data, len); + break; + } + } + free(data); + } + } + return result; + } + + vui_parameters::vui_parameters(Utils::bitstream & bs) { + aspectRatioInfoPresentFlag = bs.get(1); + if (aspectRatioInfoPresentFlag){ + aspectRatioIdc = bs.get(8); + if (aspectRatioIdc == 255){ + sarWidth = bs.get(16); + sarHeight = bs.get(16); + } + } + overscanInfoPresentFlag = bs.get(1); + if (overscanInfoPresentFlag){ + overscanAppropriateFlag = bs.get(1); + } + videoSignalTypePresentFlag = bs.get(1); + if (videoSignalTypePresentFlag){ + videoFormat = bs.get(3); + videoFullRangeFlag = bs.get(1);; + colourDescriptionPresentFlag = bs.get(1); + if (colourDescriptionPresentFlag){ + colourPrimaries = bs.get(8); + transferCharacteristics = bs.get(8); + matrixCoefficients = bs.get(8); + } + } + chromaLocInfoPresentFlag = bs.get(1); + if (chromaLocInfoPresentFlag){ + chromaSampleLocTypeTopField = bs.getUExpGolomb(); + chromaSampleLocTypeBottomField = bs.getUExpGolomb(); + } + timingInfoPresentFlag = bs.get(1); + if (timingInfoPresentFlag){ + numUnitsInTick = bs.get(32); + timeScale = bs.get(32); + fixedFrameRateFlag = bs.get(1); + } + nalHrdParametersPresentFlag = bs.get(1); +//hrd param nal + vclHrdParametersPresentFlag = bs.get(1); +//hrd param vcl + if (nalHrdParametersPresentFlag || vclHrdParametersPresentFlag){ + lowDelayHrdFlag = bs.get(1); + } + picStructPresentFlag = bs.get(1); + bitstreamRestrictionFlag = bs.get(1); + if (bitstreamRestrictionFlag){ + motionVectorsOverPicBoundariesFlag = bs.get(1); + maxBytesPerPicDenom = bs.getUExpGolomb(); + maxBitsPerMbDenom = bs.getUExpGolomb(); + log2MaxMvLengthHorizontal = bs.getUExpGolomb(); + log2MaxMvLengthVertical = bs.getUExpGolomb(); + numReorderFrames = bs.getUExpGolomb(); + maxDecFrameBuffering = bs.getUExpGolomb(); + } + } + + void vui_parameters::generate(Utils::bitWriter & bw){ + bw.append(aspectRatioInfoPresentFlag ? 1 : 0, 1); + if (aspectRatioInfoPresentFlag){ + bw.append(aspectRatioIdc, 8); + if (aspectRatioIdc == 0xFF){ + bw.append(sarWidth, 16); + bw.append(sarHeight, 16); + } + } + bw.append(overscanInfoPresentFlag ? 1 : 0, 1); + if (overscanInfoPresentFlag){ + bw.append(overscanAppropriateFlag ? 1 : 0, 1); + } + bw.append(videoSignalTypePresentFlag ? 1 : 0, 1); + if (videoSignalTypePresentFlag){ + bw.append(videoFormat, 3); + bw.append(videoFullRangeFlag, 1); + bw.append(colourDescriptionPresentFlag ? 1 : 0, 1); + if (colourDescriptionPresentFlag){ + bw.append(colourPrimaries, 8); + bw.append(transferCharacteristics, 8); + bw.append(matrixCoefficients, 8); + } + } + bw.append(chromaLocInfoPresentFlag ? 1 : 0, 1); + if (chromaLocInfoPresentFlag){ + bw.appendUExpGolomb(chromaSampleLocTypeTopField); + bw.appendUExpGolomb(chromaSampleLocTypeBottomField); + } + bw.append(timingInfoPresentFlag ? 1 : 0, 1); + if (timingInfoPresentFlag){ + bw.append(numUnitsInTick, 32); + bw.append(timeScale, 32); + bw.append(fixedFrameRateFlag ? 1 : 0, 1); + } + bw.append(nalHrdParametersPresentFlag ? 1 : 0, 1); + if (nalHrdParametersPresentFlag){ + + } + bw.append(vclHrdParametersPresentFlag ? 1 : 0, 1); + if (vclHrdParametersPresentFlag){ + + } + if (nalHrdParametersPresentFlag || vclHrdParametersPresentFlag){ + bw.append(lowDelayHrdFlag ? 1 : 0, 1); + } + bw.append(picStructPresentFlag ? 1 : 0, 1); + bw.append(bitstreamRestrictionFlag ? 1 : 0, 1); + if (bitstreamRestrictionFlag){ + bw.append(motionVectorsOverPicBoundariesFlag ? 1 : 0, 1); + bw.appendUExpGolomb(maxBytesPerPicDenom); + bw.appendUExpGolomb(maxBitsPerMbDenom); + bw.appendUExpGolomb(log2MaxMvLengthHorizontal); + bw.appendUExpGolomb(log2MaxMvLengthVertical); + bw.appendUExpGolomb(numReorderFrames); + bw.appendUExpGolomb(maxDecFrameBuffering); + } + } + + void vui_parameters::toPrettyString(std::ostream & out, size_t indent){ + out << std::string(indent, ' ') << "Vui parameters" << std::endl; + out << std::string(indent + 2, ' ') << "aspect_ratio_info_present_flag: " << aspectRatioInfoPresentFlag << std::endl; + if (aspectRatioInfoPresentFlag){ + out << std::string(indent + 2, ' ') << "aspect_ratio_idc: " << (int32_t)aspectRatioIdc << std::endl; + if (aspectRatioIdc == 255){ + out << std::string(indent + 2, ' ') << "sar_width: " << sarWidth << std::endl; + out << std::string(indent + 2, ' ') << "sar_height: " << sarHeight << std::endl; + } + } + out << std::string(indent + 2, ' ') << "overscan_info_present_flag: " << overscanInfoPresentFlag << std::endl; + if (overscanInfoPresentFlag){ + out << std::string(indent + 2, ' ') << "overscan_appropriate_present_flag: " << overscanAppropriateFlag << std::endl; + } + out << std::string(indent + 2, ' ') << "video_signal_type_present_flag: " << videoSignalTypePresentFlag << std::endl; + if (videoSignalTypePresentFlag){ + out << std::string(indent + 2, ' ') << "video_format" << videoFormat << std::endl; + out << std::string(indent + 2, ' ') << "video_full_range_flag" << videoFullRangeFlag << std::endl; + out << std::string(indent + 2, ' ') << "colour_description_present_flag" << colourDescriptionPresentFlag << std::endl; + if (colourDescriptionPresentFlag){ + out << std::string(indent + 2, ' ') << "colour_primaries" << colourPrimaries << std::endl; + out << std::string(indent + 2, ' ') << "transfer_characteristics" << transferCharacteristics << std::endl; + out << std::string(indent + 2, ' ') << "matrix_coefficients" << matrixCoefficients << std::endl; + } + } + out << std::string(indent + 2, ' ') << "chroma_loc_info_present_flag: " << chromaLocInfoPresentFlag << std::endl; + if (chromaLocInfoPresentFlag){ + out << std::string(indent + 2, ' ') << "chroma_sample_loc_type_top_field" << chromaSampleLocTypeTopField << std::endl; + out << std::string(indent + 2, ' ') << "chroma_sample_loc_type_bottom_field" << chromaSampleLocTypeBottomField << std::endl; + } + out << std::string(indent + 2, ' ') << "timing_info_present_flag: " << timingInfoPresentFlag << std::endl; + if (timingInfoPresentFlag){ + out << std::string(indent + 2, ' ') << "num_units_in_tick: " << numUnitsInTick << std::endl; + out << std::string(indent + 2, ' ') << "time_scale: " << timeScale << std::endl; + out << std::string(indent + 2, ' ') << "fixed_frame_rate_flag: " << fixedFrameRateFlag << std::endl; + } + out << std::string(indent + 2, ' ') << "nal_hrd_parameters_present_flag: " << nalHrdParametersPresentFlag << std::endl; + + out << std::string(indent + 2, ' ') << "vcl_hrd_parameters_present_flag: " << vclHrdParametersPresentFlag << std::endl; + if (nalHrdParametersPresentFlag || vclHrdParametersPresentFlag){ + out << std::string(indent + 2, ' ') << "low_delay_hrd_flag: " << lowDelayHrdFlag << std::endl; + } + out << std::string(indent + 2, ' ') << "pic_struct_present_flag: " << picStructPresentFlag << std::endl; + out << std::string(indent + 2, ' ') << "bitstream_restiction_flag: " << bitstreamRestrictionFlag << std::endl; + if (bitstreamRestrictionFlag){ + out << std::string(indent + 2, ' ') << "motion_vectors_over_pic_boundaries_flag: " << motionVectorsOverPicBoundariesFlag << std::endl; + out << std::string(indent + 2, ' ') << "max_bytes_per_pic_denom: " << maxBytesPerPicDenom << std::endl; + out << std::string(indent + 2, ' ') << "max_bits_per_mb_denom: " << maxBitsPerMbDenom << std::endl; + out << std::string(indent + 2, ' ') << "log2_max_mv_length_horizontal: " << log2MaxMvLengthHorizontal << std::endl; + out << std::string(indent + 2, ' ') << "log2_max_mv_length_vertical: " << log2MaxMvLengthVertical << std::endl; + out << std::string(indent + 2, ' ') << "num_reorder_frames: " << numReorderFrames << std::endl; + out << std::string(indent + 2, ' ') << "max_dec_frame_buffering: " << maxDecFrameBuffering << std::endl; + } + } } diff --git a/lib/h264.h b/lib/h264.h index 0afd0540..0a369d0f 100644 --- a/lib/h264.h +++ b/lib/h264.h @@ -1,7 +1,12 @@ +#pragma once #include #include +#include +#include #include "nal.h" +#include "bitfields.h" +#include "bitstream.h" namespace h264 { @@ -67,4 +72,229 @@ namespace h264 { unsigned long dataLen; }; + class nalUnit { + public: + nalUnit(const char * data, size_t len) : payload(data, len) {} + uint8_t getType() { return payload[0] & 0x1F; } + virtual void toPrettyString(std::ostream & out){ + out << "Nal unit of type " << (((uint8_t)payload[0]) & 0x1F) << ", " << payload.size() << " bytes long" << std::endl; + } + void write(std::ostream & out){ + //always writes in annex_b style + out.write("\000\000\000\001", 4); + out.write(payload.data(), payload.size()); + } + virtual std::string generate() { return ""; } + virtual void setSPSNumber(size_t newNumber) {} + virtual void setPPSNumber(size_t newNumber) {} + protected: + std::string payload; + }; + + class hrd_parameters { + public: + hrd_parameters() {} + hrd_parameters(Utils::bitstream & bs); + void toPrettyString(std::ostream & out, size_t indent = 0); + + uint64_t cpbCntMinus1; + uint8_t bitRateScale; + uint8_t cpbSizeScale; + }; + + class vui_parameters { + public: + vui_parameters() {}; + vui_parameters(Utils::bitstream & bs); + void generate(Utils::bitWriter & bw); + void toPrettyString(std::ostream & out, size_t indent = 0); + + bool aspectRatioInfoPresentFlag; + uint8_t aspectRatioIdc; + uint16_t sarWidth; + uint16_t sarHeight; + bool overscanInfoPresentFlag; + bool overscanAppropriateFlag; + bool videoSignalTypePresentFlag; + uint8_t videoFormat; + bool videoFullRangeFlag; + bool colourDescriptionPresentFlag; + uint8_t colourPrimaries; + uint8_t transferCharacteristics; + uint8_t matrixCoefficients; + bool chromaLocInfoPresentFlag; + uint64_t chromaSampleLocTypeTopField; + uint64_t chromaSampleLocTypeBottomField; + bool timingInfoPresentFlag; + uint32_t numUnitsInTick; + uint32_t timeScale; + bool fixedFrameRateFlag; + bool nalHrdParametersPresentFlag; + + bool vclHrdParametersPresentFlag; + + bool lowDelayHrdFlag; + bool picStructPresentFlag; + bool bitstreamRestrictionFlag; + bool motionVectorsOverPicBoundariesFlag; + uint64_t maxBytesPerPicDenom; + uint64_t maxBitsPerMbDenom; + uint64_t log2MaxMvLengthHorizontal; + uint64_t log2MaxMvLengthVertical; + uint64_t numReorderFrames; + uint64_t maxDecFrameBuffering; + }; + + class spsUnit : public nalUnit { + public: + spsUnit(const char * data, size_t len); + ~spsUnit(){ + if (scalingListPresentFlags != NULL){ + free(scalingListPresentFlags); + } + } + std::string generate(); + void toPrettyString(std::ostream & out); + void scalingList(uint64_t * scalingList, size_t sizeOfScalingList, bool & useDefaultScalingMatrixFlag, Utils::bitstream & bs); + void setSPSNumber(size_t newNumber); + uint8_t profileIdc; + bool constraintSet0Flag; + bool constraintSet1Flag; + bool constraintSet2Flag; + bool constraintSet3Flag; + bool constraintSet4Flag; + bool constraintSet5Flag; + uint8_t levelIdc; + uint64_t seqParameterSetId; + + uint64_t chromaFormatIdc; + bool separateColourPlaneFlag; + uint64_t bitDepthLumaMinus8; + uint64_t bitDepthChromaMinus8; + bool qpprimeYZeroTransformBypassFlag; + bool seqScalingMatrixPresentFlag; + //Here go scaling lists + uint8_t * scalingListPresentFlags; + + uint64_t ** scalingList4x4; + bool * useDefaultScalingMatrix4x4Flag; + uint64_t ** scalingList8x8; + bool * useDefaultScalingMatrix8x8Flag; + + + uint64_t log2MaxFrameNumMinus4; + uint64_t picOrderCntType; + uint64_t log2MaxPicOrderCntLsbMinus4; + + //Here go values for pic_order_cnt_type == 1 + + uint64_t maxNumRefFrames; + bool gapsInFrameNumValueAllowedFlag; + uint64_t picWidthInMbsMinus1; + uint64_t picHeightInMapUnitsMinus1; + bool frameMbsOnlyFlag; + bool mbAdaptiveFrameFieldFlag; + bool direct8x8InferenceFlag; + bool frameCroppingFlag; + uint64_t frameCropLeftOffset; + uint64_t frameCropRightOffset; + uint64_t frameCropTopOffset; + uint64_t frameCropBottomOffset; + bool vuiParametersPresentFlag; + + vui_parameters vuiParams; + + //DERIVATIVE VALUES + uint8_t derived_subWidthC; + uint8_t derived_subHeightC; + uint8_t derived_mbWidthC; + uint8_t derived_mbHeightC; + uint64_t derived_bitDepth_Y; + uint64_t derived_qpBdOffset_Y; + uint64_t derived_bitDepth_C; + uint64_t derived_qpBdOffset_C; + uint64_t derived_rawMbBits; + uint64_t derived_maxFrameNum; + uint64_t derived_maxPicOrderCntLsb; + uint64_t derived_picWidthInMbs; + uint64_t derived_picWidthInSamples_L; + uint64_t derived_picWidthInSamples_C; + uint64_t derived_picHeightInMapUnits; + uint64_t derived_picSizeInMapUnits; + uint64_t derived_frameHeightInMbs; + size_t derived_scalingListSize; + size_t derived_scalingList4x4Amount; + size_t derived_scalingList8x8Amount; + }; + class ppsUnit : public nalUnit { + public: + ppsUnit(const char * data, size_t len, uint8_t chromaFormatIdc = 0); + ~ppsUnit(){ + if (picScalingMatrixPresentFlags != NULL){ + free(picScalingMatrixPresentFlags); + } + } + void scalingList(uint64_t * scalingList, size_t sizeOfScalingList, bool & useDefaultScalingMatrixFlag, Utils::bitstream & bs); + void setPPSNumber(size_t newNumber); + void setSPSNumber(size_t newNumber); + void toPrettyString(std::ostream & out); + std::string generate(); + + uint64_t picParameterSetId; + uint64_t seqParameterSetId; + bool entropyCodingModeFlag; + bool bottomFieldPicOrderInFramePresentFlag; + uint64_t numSliceGroupsMinus1; + uint64_t numrefIdx10DefaultActiveMinus1; + uint64_t numrefIdx11DefaultActiveMinus1; + bool weightedPredFlag; + uint8_t weightedBipredIdc; + int64_t picInitQpMinus26; + int64_t picInitQsMinus26; + int64_t chromaQpIndexOffset; + bool deblockingFilterControlPresentFlag; + bool constrainedIntraPredFlag; + bool redundantPicCntPresentFlag; + bool transform8x8ModeFlag; + bool picScalingMatrixPresentFlag; + //Here go scaling lists + uint8_t * picScalingMatrixPresentFlags; + + uint64_t ** scalingList4x4; + bool * useDefaultScalingMatrix4x4Flag; + uint64_t ** scalingList8x8; + bool * useDefaultScalingMatrix8x8Flag; + + int64_t secondChromaQpIndexOffset; + + size_t derived_scalingListSize; + size_t derived_scalingList4x4Amount; + size_t derived_scalingList8x8Amount; + + bool status_moreRBSP; + }; + class codedSliceUnit : public nalUnit { + public: + codedSliceUnit(const char * data, size_t len); + void setPPSNumber(size_t newNumber); + void toPrettyString(std::ostream & out); + + uint64_t firstMbInSlice; + uint64_t sliceType; + uint64_t picParameterSetId; + }; + + class seiUnit : public nalUnit { + public: + seiUnit(const char * data, size_t len); + void toPrettyString(std::ostream & out); + + uint32_t payloadType; + uint32_t payloadSize; + }; + + + + nalUnit * nalFactory(FILE * in, bool annexb = true); + nalUnit * nalFactory(char * data, size_t len, size_t & offset, bool annexb = true); } diff --git a/src/analysers/h264_analyser.cpp b/src/analysers/h264_analyser.cpp new file mode 100644 index 00000000..1f308a5f --- /dev/null +++ b/src/analysers/h264_analyser.cpp @@ -0,0 +1,30 @@ +/// \file h264_analyser.cpp +/// Reads an H264 file and prints all readable data about it + +#include +#include +#include + +#include +#include +#include +#include +#include + +int main(int argc, char ** argv){ + Util::Config conf = Util::Config(argv[0]); + conf.addOption("filename", JSON::fromString("{\"arg_num\":1, \"arg\":\"string\", \"help\":\"Full path of the file to analyse.\"}")); + conf.parseArgs(argc, argv); + FILE * F = fopen(conf.getString("filename").c_str(), "r+b"); + if (!F){ + FAIL_MSG("No such file"); + } + + h264::nalUnit * nalPtr = h264::nalFactory(F); + while (nalPtr){ + nalPtr->toPrettyString(std::cout); + nalPtr = h264::nalFactory(F); + } + return 0; +} +