#ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include "bitfields.h" #include "bitstream.h" #include "defines.h" #include "h264.h" #include #include #include #include namespace h264{ /// Helper function to determine if a H264 NAL unit is a keyframe or not bool isKeyframe(const char *data, uint32_t len){ uint8_t nalType = (data[0] & 0x1F); if (nalType == 0x05){return true;} if (nalType != 0x01){return false;} Utils::bitstream bs; for (size_t i = 1; i < 10 && i < len; ++i){ if (i + 2 < len && (memcmp(data + i, "\000\000\003", 3) == 0)){// Emulation prevention bytes bs.append(data + i, 2); i += 2; }else{ bs.append(data + i, 1); } } bs.getExpGolomb(); // Discard first_mb_in_slice uint64_t sliceType = bs.getUExpGolomb(); // Slice types: // 0: P - Predictive slice (at most 1 reference) // 1: B - Bi-predictive slice (at most 2 references) // 2: I - Intra slice (no external references) // 3: SP - Switching predictive slice (at most 1 reference) // 4: SI - Switching intra slice (no external references) // 5-9: 0-4, but all in picture of same type if (sliceType == 2 || sliceType == 4 || sliceType == 7 || sliceType == 9){return true;} return false; } std::deque analysePackets(const char *data, size_t len){ std::deque res; size_t offset = 0; // Make sure entire packet is within len while (offset + 5 < len && Bit::btohl(data + offset) + offset + 4 <= len){ nalu::nalData entry; entry.nalSize = Bit::btohl(data + offset); entry.nalType = (data + offset)[4] & 0x1F; res.push_back(entry); offset += entry.nalSize + 4; } return res; } 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){ data = dtscInit.data() + 8; dataLen = Bit::btohs(dtscInit.data() + 6); } void skipScalingList(Utils::bitstream &bs, size_t listSize){ size_t lastScale = 8; size_t nextScale = 8; for (size_t i = 0; i < listSize; i++){ if (nextScale){ uint64_t deltaScale = bs.getExpGolomb(); nextScale = (lastScale + deltaScale + 256) % 256; } lastScale = (nextScale ? nextScale : lastScale); } } bool sequenceParameterSet::validate() const{ Utils::bitstream bs; for (size_t i = 1; i < dataLen; i++){ if (i + 2 < dataLen && (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); } } if (bs.size() < 24){return false;}//static size data char profileIdc = bs.get(8); bs.skip(16); bs.getUExpGolomb();//ID if (profileIdc == 100 || profileIdc == 110 || profileIdc == 122 || profileIdc == 244 || profileIdc == 44 || profileIdc == 83 || profileIdc == 86 || profileIdc == 118 || profileIdc == 128){ // chroma format idc char chromaFormatIdc = bs.getUExpGolomb(); if (chromaFormatIdc == 3){bs.get(1);} bs.getUExpGolomb(); // luma bs.getUExpGolomb(); // chroma bs.skip(1); // transform bypass if (bs.get(1)){// Scaling matrix is present char listSize = (chromaFormatIdc == 3 ? 12 : 8); for (size_t i = 0; i < listSize; i++){ bool thisListPresent = bs.get(1); if (thisListPresent){ if (i < 6){ skipScalingList(bs, 16); }else{ skipScalingList(bs, 64); } } } } } bs.getUExpGolomb(); size_t cnt_type = bs.getUExpGolomb(); if (!cnt_type){ bs.getUExpGolomb(); }else if (cnt_type == 1){ ERROR_MSG("This part of the implementation is incomplete(2), to be continued. If this " "message is shown, contact developers immediately."); } if (!bs.size()){return false;} bs.getUExpGolomb(); // max_num_ref_frames bs.get(1); bs.getUExpGolomb(); bs.getUExpGolomb(); if (!bs.size()){return false;} bool mbs_only = (bs.get(1) == 1); // Gets used in height calculation if (!mbs_only){bs.skip(1);} bs.skip(1); // cropping flag if (bs.get(1)){ bs.getUExpGolomb(); // leftOffset bs.getUExpGolomb(); // rightOffset bs.getUExpGolomb(); // topOffset bs.getUExpGolomb(); // bottomOffset } if (!bs.size()){return false;} if (bs.get(1)){ if (bs.get(1)){bs.skip(8);} if (bs.get(1)){bs.skip(1);} if (bs.get(1)){ bs.skip(4); if (bs.get(1)){bs.skip(24);} } if (bs.get(1)){ bs.getUExpGolomb(); bs.getUExpGolomb(); } // Decode timing info if (!bs.size()){return false;} if (bs.get(1)){ return (bs.size() >= 65); } } return true; } SPSMeta sequenceParameterSet::getCharacteristics() const{ SPSMeta result; result.sep_col_plane = false; // For calculating width uint32_t widthInMbs = 0; uint32_t cropHorizontal = 0; // For calculating height uint32_t heightInMapUnits = 0; uint32_t cropVertical = 0; uint32_t sar_width = 0; uint32_t sar_height = 0; // Fill the bitstream Utils::bitstream bs; for (size_t i = 1; i < dataLen; i++){ if (i + 2 < dataLen && (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); } } char profileIdc = bs.get(8); result.profile = profileIdc; // Start skipping unused data bs.skip(8); result.level = bs.get(8); bs.getUExpGolomb(); if (profileIdc == 100 || profileIdc == 110 || profileIdc == 122 || profileIdc == 244 || profileIdc == 44 || profileIdc == 83 || profileIdc == 86 || profileIdc == 118 || profileIdc == 128){ // chroma format idc char chromaFormatIdc = bs.getUExpGolomb(); if (chromaFormatIdc == 3){result.sep_col_plane = (bs.get(1) == 1);} bs.getUExpGolomb(); // luma bs.getUExpGolomb(); // chroma bs.skip(1); // transform bypass if (bs.get(1)){// Scaling matrix is present char listSize = (chromaFormatIdc == 3 ? 12 : 8); for (size_t i = 0; i < listSize; i++){ bool thisListPresent = bs.get(1); if (thisListPresent){ if (i < 6){ skipScalingList(bs, 16); }else{ skipScalingList(bs, 64); } } } } } result.log2_max_frame_num = bs.getUExpGolomb() + 4; result.cnt_type = bs.getUExpGolomb(); if (!result.cnt_type){ result.log2_max_order_cnt = bs.getUExpGolomb() + 4; }else if (result.cnt_type == 1){ ERROR_MSG("This part of the implementation is incomplete(2), to be continued. If this " "message is shown, contact developers immediately."); } result.max_ref_frames = bs.getUExpGolomb(); // max_num_ref_frames result.gaps = (bs.get(1) == 1); // gaps in frame num allowed // Stop skipping data and start doing useful stuff widthInMbs = bs.getUExpGolomb() + 1; heightInMapUnits = bs.getUExpGolomb() + 1; result.mbs_only = (bs.get(1) == 1); // Gets used in height calculation if (!result.mbs_only){bs.skip(1);} bs.skip(1); // cropping flag if (bs.get(1)){ cropHorizontal = bs.getUExpGolomb(); // leftOffset cropHorizontal += bs.getUExpGolomb(); // rightOffset cropVertical = bs.getUExpGolomb(); // topOffset cropVertical += bs.getUExpGolomb(); // bottomOffset } // vuiParameters result.fps = 0; if (bs.get(1)){ // Skipping all the paramters we dont use if (bs.get(1)){ uint8_t aspect_ratio_idc = bs.get(8); switch (aspect_ratio_idc){ case 255: sar_width = bs.get(16); sar_height = bs.get(16); break; case 2: sar_width = 12; sar_height = 11; break; case 3: sar_width = 10; sar_height = 11; break; case 4: sar_width = 16; sar_height = 11; break; case 5: sar_width = 40; sar_height = 33; break; case 6: sar_width = 24; sar_height = 11; break; case 7: sar_width = 20; sar_height = 11; break; case 8: sar_width = 32; sar_height = 11; break; case 9: sar_width = 80; sar_height = 33; break; case 10: sar_width = 18; sar_height = 11; break; case 11: sar_width = 15; sar_height = 11; break; case 12: sar_width = 64; sar_height = 33; break; case 13: sar_width = 160; sar_height = 99; break; case 14: sar_width = 4; sar_height = 3; break; case 15: sar_width = 3; sar_height = 2; break; case 16: sar_width = 2; sar_height = 1; break; default: break; } } if (bs.get(1)){bs.skip(1);} if (bs.get(1)){ bs.skip(4); if (bs.get(1)){bs.skip(24);} } if (bs.get(1)){ bs.getUExpGolomb(); bs.getUExpGolomb(); } // Decode timing info if (bs.get(1)){ uint32_t unitsInTick = bs.get(32); uint32_t timeScale = bs.get(32); result.fps = (double)timeScale / (2 * unitsInTick); bs.skip(1); } } result.width = (widthInMbs * 16) - (cropHorizontal * 2); result.height = ((result.mbs_only ? 1 : 2) * heightInMapUnits * 16) - (cropVertical * 2); if (sar_width != sar_height){ if (sar_width > sar_height){ result.width = ((result.width * sar_width) / sar_height); }else{ result.height = ((result.height * sar_height) / sar_width); } } return result; } void spsUnit::scalingList(uint64_t *scalingList, size_t sizeOfScalingList, bool &useDefaultScalingMatrixFlag, Utils::bitstream &bs){ int32_t lastScale = 8; int32_t 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); } const char* spsUnit::profile(){ if (profileIdc == 66){ if (constraintSet1Flag){return "Constrained baseline";} return "Baseline"; } if (profileIdc == 77){return "Main";} if (profileIdc == 88){return "Extended";} if (profileIdc == 100){return "High";} if (profileIdc == 110){ if (constraintSet3Flag){return "High-10 Intra";} return "High-10"; } if (profileIdc == 122){ if (constraintSet3Flag){return "High-4:2:2 Intra";} return "High-4:2:2"; } if (profileIdc == 244){ if (constraintSet3Flag){return "High-4:4:4 Intra";} return "High-4:4:4"; } if (profileIdc == 44){return "CAVLC 4:4:4 Intra";} return "Unknown"; } const char* spsUnit::level(){ if (levelIdc == 9){return "1b";} if (levelIdc == 10){return "1";} if (levelIdc == 11){ if (constraintSet3Flag){return "1b";} return "1.1"; } if (levelIdc == 12){return "1.2";} if (levelIdc == 13){return "1.3";} if (levelIdc == 20){return "2";} if (levelIdc == 21){return "2.1";} if (levelIdc == 21){return "2.2";} if (levelIdc == 30){return "3";} if (levelIdc == 31){return "3.1";} if (levelIdc == 31){return "3.2";} if (levelIdc == 40){return "4";} if (levelIdc == 41){return "4.1";} if (levelIdc == 41){return "4.2";} if (levelIdc == 50){return "5";} if (levelIdc == 51){return "5.1";} return "Unknown"; } 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 << ") = " << profile() << 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)levelIdc << ") = " << level() << 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){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]; } } bool ppsValidate(const char *data, size_t 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); } } bs.getUExpGolomb(); bs.getUExpGolomb(); bs.get(2); if (bs.getUExpGolomb() > 0){ WARN_MSG("num_slice_groups_minus1 > 0, unimplemented structure"); return false; } bs.getUExpGolomb(); bs.getUExpGolomb(); bs.get(3); bs.getExpGolomb(); bs.getExpGolomb(); bs.getExpGolomb(); bs.get(2); if (!bs.size()){return false;} bs.get(1); if (!more_rbsp_data(bs)){return true;} bs.get(1); if (bs.get(1)){ //tricky scaling stuff, assume we're good. /// \TODO Maybe implement someday? Do we care? Doubt. return true; } return bs.size(); } 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(); } void ppsUnit::setPPSNumber(size_t newNumber){ // for now, can only convert from 0 to 16 picParameterSetId = newNumber; } 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.append(0x08, 8); 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); } 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; } 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: %zu bytes and %zu bits", byteOffset, bitOffset); size_t firstBitmask = ((1 << bitOffset) - 1) << (8 - bitOffset); size_t secondBitmask = (1 << (8 - bitOffset)) - 1; INFO_MSG("Bitmasks: %.2zX, %.2zX", 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 %.2X to %.2X %.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; payloadOffset = 1; 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); ++payloadOffset; payloadType = 0; while (tmp == 0xFF){ payloadType += tmp; tmp = bs.get(8); ++payloadOffset; } payloadType += tmp; tmp = bs.get(8); ++payloadOffset; payloadSize = 0; while (tmp == 0xFF){ payloadSize += tmp; tmp = bs.get(8); ++payloadOffset; } 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; switch (payloadType){ case 5:{// User data, unregistered out << " Type 5: User data, unregistered." << std::endl; std::stringstream uuid; for (uint32_t i = payloadOffset; i < payloadOffset + 16; ++i){ uuid << std::setw(2) << std::setfill('0') << std::hex << (int)(payload.data()[i]); } if (uuid.str() == "dc45e9bde6d948b7962cd820d923eeef"){ uuid.str("x264 encoder configuration"); } out << " UUID: " << uuid.str() << std::endl; out << " Payload: " << std::string(payload.data() + payloadOffset + 16, payloadSize - 17) << std::endl; }break; default: out << " Message of type " << payloadType << ", " << payloadSize << " bytes long" << std::endl; } } nalUnit *nalFactory(const char *_data, size_t _len, size_t &offset, bool annexb){ if (annexb){ // check if we have a start marker at the beginning, if so, move the offset over if (_len > offset && !_data[offset]){ for (size_t i = offset + 1; i < _len; ++i){ if (_data[i] > 1){ FAIL_MSG("Encountered bullshit AnnexB data..?"); return 0; } if (_data[i] == 1){ offset = i + 1; break; } } } // now we know we're starting at real data. Yay! } if (_len < offset + 4){ WARN_MSG("Not at least 4 bytes available - cancelling"); return 0; } uint32_t pktLen = 0; if (!annexb){ // read the 4b size in front pktLen = Bit::btohl(_data + offset); if (_len - offset < 4 + pktLen){ WARN_MSG("Not at least 4+%" PRIu32 " bytes available - cancelling", pktLen); return 0; } offset += 4; } const char *data = _data + offset; size_t len = _len - offset; if (annexb){ // search for the next start marker for (size_t i = 1; i < len - 2; ++i){ if (data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1){ while (i && !data[i]){--i;} pktLen = i + 1; offset += pktLen; break; } } }else{ offset += pktLen; } if (!pktLen){ WARN_MSG("Cannot determine packet length - cancelling"); return 0; } switch (data[0] & 0x1F){ case 1: case 5: case 19: return new codedSliceUnit(data, pktLen); case 6: return new seiUnit(data, pktLen); case 7: return new spsUnit(data, pktLen); case 8: return new ppsUnit(data, pktLen); default: return new nalUnit(data, pktLen); } } 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: case 19: 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); derived_fps = 0.0; if (timingInfoPresentFlag){ numUnitsInTick = bs.get(32); timeScale = bs.get(32); fixedFrameRateFlag = bs.get(1); derived_fps = (double)timeScale / (2 * numUnitsInTick); } 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; } } }// namespace h264