From 11fa5d122c0cb8de6e4a49e11fc1333c44880a4c Mon Sep 17 00:00:00 2001 From: Heikki Tampio Date: Tue, 22 Aug 2023 13:54:22 +0300 Subject: [PATCH] v3c: Reconstruct the V3C file after receiving all NAL units --- examples/v3c_receiver.cc | 462 +++++++++++++++++++++++++++++------ examples/v3c_sender.cc | 160 ++++++++---- include/uvgrtp/v3c_parser.hh | 65 +++-- src/v3c_parser.cc | 170 ++++++++----- 4 files changed, 659 insertions(+), 198 deletions(-) diff --git a/examples/v3c_receiver.cc b/examples/v3c_receiver.cc index 9c5d4ca..fc2b599 100644 --- a/examples/v3c_receiver.cc +++ b/examples/v3c_receiver.cc @@ -28,95 +28,421 @@ constexpr uint16_t LOCAL_PORT = 8890; constexpr char LOCAL_ADDRESS[] = "127.0.0.1"; // This example runs for 5 seconds -constexpr auto RECEIVE_TIME_S = std::chrono::seconds(10); +constexpr auto RECEIVE_TIME_S = std::chrono::seconds(5); -void v3c_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame); -void avc_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame); -void cleanup(uvgrtp::context& ctx, uvgrtp::session* sess, uvgrtp::media_stream* receiver); -uint64_t vbytes_received; -uint64_t abytes_received; +void vps_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame); +void ad_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame); +void ovd_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame); +void gvd_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame); +void avd_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame); +void pvd_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame); +void cad_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame); + +bool is_gop_ready(uint64_t index, v3c_file_map &mmap); +void copy_rtp_payload(uint64_t max_size, v3c_unit_info& unit, uvgrtp::frame::rtp_frame* frame); +void create_v3c_unit(v3c_unit_info& current_unit, char* buf, uint64_t& ptr, uint64_t v3c_precision, uint32_t nal_precision); +uint64_t reconstruct_v3c_gop(bool hdr_byte, char* buf, uint64_t &ptr, v3c_file_map &mmap, uint64_t index); +uint64_t vps_count; + +constexpr int VPS_NALS = 1; +constexpr int AD_NALS = 35; +constexpr int OVD_NALS = 35; +constexpr int GVD_NALS = 131; +constexpr int AVD_NALS = 131; +std::string PATH = "C:\\Users\\ngheta\\Documents\\v3c_test_seq_2.vpcc"; int main(void) { std::cout << "Starting uvgRTP RTP receive hook example" << std::endl; + /* Fetch the original file and its size */ + uint64_t len = get_size(PATH); + std::cout << "File size " << len << std::endl; + + char* original_buf = nullptr; + original_buf = get_cmem(PATH, len); + uvgrtp::context ctx; uvgrtp::session* sess = ctx.create_session(LOCAL_ADDRESS, LOCAL_ADDRESS); int flags = RCE_RECEIVE_ONLY | RCE_NO_H26X_PREPEND_SC; - uvgrtp::media_stream* v3c = sess->create_stream(7790, 8890, RTP_FORMAT_V3C, flags); - uvgrtp::media_stream* avc = sess->create_stream(7792, 8892, RTP_FORMAT_H265, flags); - vbytes_received = 0; - abytes_received = 0; - /* Receive hook can be installed and uvgRTP will call this hook when an RTP frame is received - * - * This is a non-blocking operation - * - * If necessary, receive hook can be given an argument and this argument is supplied to - * the receive hook every time the hook is called. This argument could a pointer to application- - * specfic object if the application needs to be called inside the hook - * - * If it's not needed, it should be set to nullptr */ - if (!v3c || v3c->install_receive_hook(nullptr, v3c_receive_hook) != RTP_OK) - { - std::cerr << "Failed to install RTP reception hook"; - cleanup(ctx, sess, v3c); - return EXIT_FAILURE; - } - if (!avc || avc->install_receive_hook(nullptr, avc_receive_hook) != RTP_OK) - { - std::cerr << "Failed to install RTP reception hook"; - cleanup(ctx, sess, avc); - return EXIT_FAILURE; - } + + // Create the uvgRTP media streams with the correct RTP format + uvgrtp::media_stream* vps = sess->create_stream(8891, 8890, RTP_FORMAT_GENERIC, flags); + uvgrtp::media_stream* ad = sess->create_stream(8893, 8892, RTP_FORMAT_V3C, flags); + uvgrtp::media_stream* ovd = sess->create_stream(8895, 8894, RTP_FORMAT_H265, flags); + uvgrtp::media_stream* gvd = sess->create_stream(8897, 8896, RTP_FORMAT_H265, flags); + uvgrtp::media_stream* avd = sess->create_stream(8899, 8898, RTP_FORMAT_H265, flags); + uvgrtp::media_stream* pvd = sess->create_stream(9001, 9000, RTP_FORMAT_H265, flags); + uvgrtp::media_stream* cad = sess->create_stream(9003, 9002, RTP_FORMAT_V3C, flags); + avd->configure_ctx(RCC_RING_BUFFER_SIZE, 40*1000*1000); + + char* out_buf = new char[len]; + v3c_file_map mmap = {}; + + + v3c_unit_header hdr = { V3C_AD }; + hdr.ad = { 0, 0 }; + v3c_unit_info unit = { hdr, {}, new char[40 * 1000 * 1000], 0, false }; + mmap.ad_units.push_back(unit); + + hdr = { V3C_OVD }; + hdr.ovd = { 0, 0 }; + unit = { hdr, {}, new char[40 * 1000 * 1000], 0, false }; + mmap.ovd_units.push_back(unit); + + hdr = { V3C_GVD }; + hdr.gvd = { 0, 0 }; + unit = { hdr, {}, new char[40 * 1000 * 1000], 0, false }; + mmap.gvd_units.push_back(unit); + + hdr = { V3C_AVD }; + hdr.avd = { 0, 0 }; + unit = { hdr, {}, new char[40 * 1000 * 1000], 0, false }; + mmap.avd_units.push_back(unit); + + + vps->install_receive_hook(&mmap.vps_units, vps_receive_hook); + ad->install_receive_hook(&mmap.ad_units, ad_receive_hook); + ovd->install_receive_hook(&mmap.ovd_units, ovd_receive_hook); + gvd->install_receive_hook(&mmap.gvd_units, gvd_receive_hook); + avd->install_receive_hook(&mmap.avd_units, avd_receive_hook); + pvd->install_receive_hook(nullptr, pvd_receive_hook); + cad->install_receive_hook(nullptr, cad_receive_hook); + std::cout << "Waiting incoming packets for " << RECEIVE_TIME_S.count() << " s" << std::endl; + uint64_t ngops = 1; + uint64_t bytes = 0; + uint64_t ptr = 0; + bool hdb = true; + while (ngops <= 1) { + if (is_gop_ready(ngops, mmap)) { + std::cout << "Full GoP received, num: " << ngops << std::endl; + bytes += reconstruct_v3c_gop(hdb, out_buf, ptr, mmap, ngops - 1); + std::cout << "File size " << bytes << std::endl; + + ngops++; + hdb = false; + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } std::this_thread::sleep_for(RECEIVE_TIME_S); // lets this example run for some time - std::cout << "V3C Total bytes received " << vbytes_received << std::endl; - std::cout << "Video Total bytes received " << abytes_received << std::endl; - std::cout << "All Total bytes received " << abytes_received + vbytes_received << std::endl; - cleanup(ctx, sess, v3c); - cleanup(ctx, sess, avc); + sess->destroy_stream(vps); + sess->destroy_stream(ad); + sess->destroy_stream(ovd); + sess->destroy_stream(gvd); + sess->destroy_stream(avd); + sess->destroy_stream(pvd); + sess->destroy_stream(cad); + + ctx.destroy_session(sess); + + // compare files + for (auto i = 0; i < len; ++i) { + if (original_buf[i] != out_buf[i]) { + std::cout << "Difference at " << i << std::endl; + break; + } + } + + std::cout << "DONE " << std::endl; return EXIT_SUCCESS; } -void v3c_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame) +uint64_t reconstruct_v3c_gop(bool hdr_byte, char* buf, uint64_t &ptr, v3c_file_map& mmap, uint64_t index) { - std::cout << "Received V3C frame, size: " << frame->payload_len << " bytes" << std::endl; - vbytes_received += frame->payload_len; - /* Now we own the frame. Here you could give the frame to the application - * if f.ex "arg" was some application-specific pointer - * - * arg->copy_frame(frame) or whatever - * - * When we're done with the frame, it must be deallocated manually */ - (void)uvgrtp::frame::dealloc_frame(frame); -} -void avc_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame) -{ - std::cout << "Received video frame, size: " << frame->payload_len << " bytes" << std::endl; - abytes_received += frame->payload_len; - /* Now we own the frame. Here you could give the frame to the application - * if f.ex "arg" was some application-specific pointer - * - * arg->copy_frame(frame) or whatever - * - * When we're done with the frame, it must be deallocated manually */ - (void)uvgrtp::frame::dealloc_frame(frame); -} + /* Calculate GoP size and intiialize the output buffer + * + + 1 byte of Sample Stream Precision + +----------------------------------------------------------------+ + + 4 bytes of V3C Unit size + + x1 bytes of whole V3C VPS unit (incl. header) + +----------------------------------------------------------------+ + Atlas V3C unit + + 4 bytes for V3C Unit size + + 4 bytes of V3C header + + 1 byte of NAL Unit Size Precision (x1) + + NALs count (x1 bytes of NAL Unit Size + + x2 bytes of NAL unit payload) + +----------------------------------------------------------------+ + Video V3C unit + + 4 bytes for V3C Unit size + + 4 bytes of V3C header + + NALs count (4 bytes of NAL Unit Size + + x2 bytes of NAL unit payload) + +----------------------------------------------------------------+ + . + . + . + +----------------------------------------------------------------+ + Video V3C unit + + 4 bytes for V3C Unit size + + 4 bytes of V3C header + + NALs count (4 bytes of NAL Unit Size + + x2 bytes of NAL unit payload) + +----------------------------------------------------------------+ */ + uint8_t ATLAS_NAL_SIZE_PRECISION = 2; + uint8_t VIDEO_NAL_SIZE_PRECISION = 4; + uint64_t gop_size = 0; + if (hdr_byte) { + gop_size++; // Sample Stream Precision + } + uint64_t vps_size = mmap.vps_units.at(index).nal_infos.at(0).size; // incl. header + uint64_t ad_size = 4+4+1 + mmap.ad_units.at(index).ptr + mmap.ad_units.at(index).nal_infos.size() * ATLAS_NAL_SIZE_PRECISION; + uint64_t ovd_size = 8 + mmap.ovd_units.at(index).ptr + mmap.ovd_units.at(index).nal_infos.size() * VIDEO_NAL_SIZE_PRECISION; + uint64_t gvd_size = 8 + mmap.gvd_units.at(index).ptr + mmap.gvd_units.at(index).nal_infos.size() * VIDEO_NAL_SIZE_PRECISION; + uint64_t avd_size = 8 + mmap.avd_units.at(index).ptr + mmap.avd_units.at(index).nal_infos.size() * VIDEO_NAL_SIZE_PRECISION; + gop_size += vps_size + ad_size + ovd_size + gvd_size + avd_size; + std::cout << "Initializing GoP buffer of " << gop_size << " bytes" << std::endl; + //buf = new char[gop_size]; + std::cout << "start ptr is " << ptr << std::endl; -void cleanup(uvgrtp::context& ctx, uvgrtp::session* sess, uvgrtp::media_stream* receiver) -{ - if (receiver) - { - sess->destroy_stream(receiver); + // V3C Sample stream header + if (hdr_byte) { + std::cout << "Adding Sample Stream header byte" << std::endl; + uint8_t first_byte = 64; + buf[ptr] = first_byte; + ptr++; } - if (sess) - { - /* Session must be destroyed manually */ - ctx.destroy_session(sess); + uint8_t v3c_unit_size_precision = 3; + + uint8_t* v3c_size_arr = new uint8_t[v3c_unit_size_precision]; + + v3c_unit_info current_unit = mmap.vps_units.at(index); // Now processing VPS unit + uint32_t v3c_size_int = (uint32_t)current_unit.nal_infos.at(0).size; + + // Write the V3C VPS unit size to the output buffer + convert_size_big_endian(v3c_size_int, v3c_size_arr, v3c_unit_size_precision); + memcpy(&buf[ptr], v3c_size_arr, v3c_unit_size_precision); + ptr += v3c_unit_size_precision; + + // Write the V3C VPS unit payload to the output buffer + memcpy(&buf[ptr], current_unit.buf, v3c_size_int); + ptr += v3c_size_int; + + // Write out V3C AD unit + current_unit = mmap.ad_units.at(index); + create_v3c_unit(current_unit, buf, ptr, v3c_unit_size_precision, ATLAS_NAL_SIZE_PRECISION); + + // Write out V3C OVD unit + current_unit = mmap.ovd_units.at(index); + create_v3c_unit(current_unit, buf, ptr, v3c_unit_size_precision, VIDEO_NAL_SIZE_PRECISION); + + // Write out V3C GVD unit + current_unit = mmap.gvd_units.at(index); + create_v3c_unit(current_unit, buf, ptr, v3c_unit_size_precision, VIDEO_NAL_SIZE_PRECISION); + + // Write out V3C AVD unit + current_unit = mmap.avd_units.at(index); + create_v3c_unit(current_unit, buf, ptr, v3c_unit_size_precision, VIDEO_NAL_SIZE_PRECISION); + std::cout << "end ptr is " << ptr << std::endl; + + return gop_size; +} + +void create_v3c_unit(v3c_unit_info ¤t_unit, char* buf, uint64_t &ptr, uint64_t v3c_precision, uint32_t nal_precision) +{ + uint8_t v3c_type = current_unit.header.vuh_unit_type; + + // V3C unit size + uint8_t* v3c_size_arr = new uint8_t[v3c_precision]; + uint32_t v3c_size_int = 4 + current_unit.ptr + (uint32_t)current_unit.nal_infos.size() * nal_precision; + if (v3c_type == V3C_AD ||v3c_type == V3C_CAD) { + v3c_size_int++; // NAL size precision for Atlas V3C units } + convert_size_big_endian(v3c_size_int, v3c_size_arr, v3c_precision); + memcpy(&buf[ptr], v3c_size_arr, v3c_precision); + ptr += v3c_precision; + + // Next up create the V3C unit header + uint8_t v3c_header[4] = {0, 0, 0, 0}; + + // All V3C unit types have parameter_set_id in header + uint8_t param_set_id = current_unit.header.ad.vuh_v3c_parameter_set_id; + std::cout << "VUH typ: " << (uint32_t)v3c_type << " param set id " << (uint32_t)param_set_id << std::endl; + v3c_header[0] = v3c_type << 3 | ((param_set_id & 0b1110) >> 1); + v3c_header[1] = ((param_set_id & 0b1) << 7); + + // Only CAD does NOT have atlas_id + if (v3c_type != V3C_CAD) { + uint8_t atlas_id = current_unit.header.ad.vuh_atlas_id; + v3c_header[1] = v3c_header[1] | ((atlas_id & 0b111111) << 1); + } + // GVD has map_index and aux_video_flag, then zeroes + if (v3c_type == V3C_GVD) { + uint8_t map_index = current_unit.header.gvd.vuh_map_index; + bool auxiliary_video_flag = current_unit.header.gvd.vuh_auxiliary_video_flag; + v3c_header[1] = v3c_header[1] | ((map_index & 0b1000) >> 3); + v3c_header[2] = ((map_index & 0b111) << 5) | (auxiliary_video_flag << 4); + } + if (v3c_type == V3C_AVD) { + uint8_t vuh_attribute_index = current_unit.header.avd.vuh_attribute_index; + uint8_t vuh_attribute_partition_index = current_unit.header.avd.vuh_attribute_partition_index; + uint8_t vuh_map_index = current_unit.header.avd.vuh_map_index; + bool vuh_auxiliary_video_flag = current_unit.header.avd.vuh_auxiliary_video_flag; + + v3c_header[1] = v3c_header[1] | ((vuh_attribute_index & 0b1000000) >> 7); + v3c_header[2] = ((vuh_attribute_index & 0b111111) << 2) | ((vuh_attribute_partition_index & 0b11000) >> 3); + v3c_header[3] = ((vuh_attribute_partition_index & 0b111) << 5) | (vuh_map_index << 1) | (int)vuh_auxiliary_video_flag; + } + + // Copy V3C header to outbut buffer + memcpy(&buf[ptr], v3c_header, 4); + ptr += 4; + + // For Atlas V3C units, set one byte for NAL size precision + if (v3c_type == V3C_AD || v3c_type == V3C_CAD) { + uint8_t nal_size_precision_arr = uint8_t((nal_precision - 1) << 5); + memcpy(&buf[ptr], &nal_size_precision_arr, 1); + ptr++; + } + // For Video V3C units, NAL size precision is always 4 bytes + + // Copy V3C unit NAL sizes and NAL units to output buffer + for (auto& p : current_unit.nal_infos) { + + // Copy size + uint8_t* nal_size_arr = new uint8_t[nal_precision]; + convert_size_big_endian(uint32_t(p.size), nal_size_arr, nal_precision); + memcpy(&buf[ptr], nal_size_arr, nal_precision); + ptr += nal_precision; + + // Copy NAL unit + memcpy(&buf[ptr], ¤t_unit.buf[p.location], p.size); + ptr += p.size; + } + +} + +bool is_gop_ready(uint64_t index, v3c_file_map& mmap) +{ + if (mmap.vps_units.size() < index) + return false; + if (mmap.ad_units.size() < index || !mmap.ad_units.at(index-1).ready) + return false; + if (mmap.ovd_units.size() < index || !mmap.ovd_units.at(index-1).ready) + return false; + if (mmap.gvd_units.size() < index || !mmap.gvd_units.at(index-1).ready) + return false; + if (mmap.avd_units.size() < index || !mmap.avd_units.at(index-1).ready) + return false; + + return true; +} + + +void copy_rtp_payload(uint64_t max_size, v3c_unit_info &unit, uvgrtp::frame::rtp_frame *frame) +{ + if (unit.nal_infos.size() <= max_size) { + //std::cout << "Copy info " << std::endl; + memcpy(&unit.buf[unit.ptr], frame->payload, frame->payload_len); + + unit.nal_infos.push_back({ unit.ptr, frame->payload_len }); + + unit.ptr += frame->payload_len; + } + if (unit.nal_infos.size() == max_size) { + unit.ready = true; + } +} + +void vps_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame) +{ + std::cout << "Received VPS frame, size: " << frame->payload_len << " bytes" << std::endl; + + std::vector* vec = (std::vector*)arg; + + char* cbuf = new char[frame->payload_len]; + memcpy(cbuf, frame->payload, frame->payload_len); + v3c_unit_info vps = { {}, {{0, frame->payload_len}}, cbuf }; + vec->push_back(vps); + + (void)uvgrtp::frame::dealloc_frame(frame); +} +void ad_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame) +{ + std::vector* vec = (std::vector*)arg; + + + if ((vec->end()-1)->nal_infos.size() == AD_NALS) { + std::cout << "AD size == 35, adding new v3c_unit " << std::endl; + v3c_unit_header hdr = { V3C_AD }; + hdr.ad = { (uint8_t)vec->size(), 0 }; + v3c_unit_info info = { hdr, {}, new char[400 * 1000], 0, false}; + vec->push_back(info); + } + auto current = vec->end()-1; + // GET THIS NUMBER 35 DYNAMICALLY + copy_rtp_payload(AD_NALS, *current, frame); + + //std::cout << "Done with AD frame, num: " << current->nal_infos.size() << std::endl; + + (void)uvgrtp::frame::dealloc_frame(frame); +} +void ovd_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame) +{ + //std::cout << "Received OVD frame, size: " << frame->payload_len << " bytes" << std::endl; + std::vector* vec = (std::vector*)arg; + + if ((vec->end() - 1)->nal_infos.size() == OVD_NALS) { + std::cout << "OVD size == 35, adding new v3c_unit " << std::endl; + v3c_unit_header hdr = { V3C_OVD}; + hdr.ovd = {(uint8_t)vec->size(), 0}; + v3c_unit_info info = { hdr, {}, new char[400 * 1000], 0, false }; + vec->push_back(info); + } + auto current = vec->end() - 1; + // GET THIS NUMBER 35 DYNAMICALLY + copy_rtp_payload(OVD_NALS, *current, frame); + (void)uvgrtp::frame::dealloc_frame(frame); +} +void gvd_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame) +{ + //std::cout << "Received GVD frame, size: " << frame->payload_len << " bytes" << std::endl; + std::vector* vec = (std::vector*)arg; + + if ((vec->end() - 1)->nal_infos.size() == GVD_NALS) { + std::cout << "GVD size == 131, adding new v3c_unit " << std::endl; + v3c_unit_header hdr = { V3C_GVD }; + hdr.gvd = { (uint8_t)vec->size(), 0, 0, 0 }; + v3c_unit_info info = { hdr, {}, new char[400 * 1000], 0, false }; + vec->push_back(info); + } + auto current = vec->end() - 1; + // GET THIS NUMBER 35 DYNAMICALLY + copy_rtp_payload(GVD_NALS, *current, frame); + (void)uvgrtp::frame::dealloc_frame(frame); +} +void avd_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame) +{ + //std::cout << "Received AVD frame, size: " << frame->payload_len << " bytes" << std::endl; + std::vector* vec = (std::vector*)arg; + + if ((vec->end() - 1)->nal_infos.size() == AVD_NALS) { + std::cout << "AVD size == 131, adding new v3c_unit " << std::endl; + v3c_unit_header hdr = { V3C_AVD }; + hdr.avd = { (uint8_t)vec->size(), 0 }; + v3c_unit_info info = { hdr, {}, new char[40 * 1000 * 1000], 0, false }; + vec->push_back(info); + } + auto current = vec->end() - 1; + // GET THIS NUMBER 35 DYNAMICALLY + copy_rtp_payload(AVD_NALS, *current, frame); + (void)uvgrtp::frame::dealloc_frame(frame); +} +void pvd_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame) +{ + //std::cout << "Received PVD frame, size: " << frame->payload_len << " bytes" << std::endl; + + (void)uvgrtp::frame::dealloc_frame(frame); +} +void cad_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame) +{ + //std::cout << "Received CAD frame, size: " << frame->payload_len << " bytes" << std::endl; + + (void)uvgrtp::frame::dealloc_frame(frame); } \ No newline at end of file diff --git a/examples/v3c_sender.cc b/examples/v3c_sender.cc index 9f1333a..9526258 100644 --- a/examples/v3c_sender.cc +++ b/examples/v3c_sender.cc @@ -14,8 +14,11 @@ constexpr size_t PAYLOAD_LEN = 100; constexpr int AMOUNT_OF_TEST_PACKETS = 100; constexpr auto END_WAIT = std::chrono::seconds(5); -std::string PATH = "C:\\Users\\ngheta\\Documents\\TMIV_A3_C_QP3.bit"; -//std::string PATH = "C:\\Users\\ngheta\\Documents\\test_seq3.vpcc"; +//std::string PATH = "C:\\Users\\ngheta\\Documents\\TMIV_A3_C_QP3.bit"; +std::string PATH = "C:\\Users\\ngheta\\Documents\\v3c_test_seq_2.vpcc"; + +void sender_func(uvgrtp::media_stream* stream, const char* cbuf, const std::vector &units, rtp_flags_t flags, int fmt); + int main(void) { @@ -29,8 +32,7 @@ int main(void) Note: Use RTP_NO_H26X_SCL when sending video frames, as there is no start codes in the video sub-streams */ - std::vector nal_map = {}; - std::vector vps_map = {}; + v3c_file_map mmap; /* Fetch the file and its size */ uint64_t len = get_size(PATH); @@ -39,65 +41,89 @@ int main(void) cbuf = get_cmem(PATH, len); /* Map the locations and sizes of Atlas and video NAL units with the mmap_v3c_file function */ - mmap_v3c_file(cbuf, len, nal_map, vps_map); + mmap_v3c_file(cbuf, len, mmap); std::cout << "Sending Atlas NAL units via uvgRTP" << std::endl; /* Create the necessary uvgRTP media streams */ uvgrtp::context ctx; uvgrtp::session* sess = ctx.create_session(REMOTE_ADDRESS, REMOTE_ADDRESS); - int flags = RCE_SEND_ONLY; - uvgrtp::media_stream* v3c = sess->create_stream(8890, 7790, RTP_FORMAT_V3C, flags); - rtp_format_t video_format = RTP_FORMAT_GENERIC; + int flags = RCE_NO_FLAGS; - // Create the uvgRTP media stream with the correct RTP format - if (vps_map.begin()->ptl.ptl_profile_codec_group_idc == CODEC_AVC) { - std::cout << "Video codec: AVC Progressive High" << std::endl; - video_format = RTP_FORMAT_H264; - } - else if (vps_map.begin()->ptl.ptl_profile_codec_group_idc == CODEC_HEVC_MAIN10) { - std::cout << "Video codec: HEVC Main10" << std::endl; - video_format = RTP_FORMAT_H265; - } - else if (vps_map.begin()->ptl.ptl_profile_codec_group_idc == CODEC_HEVC444) { - std::cout << "Video codec: HEVC444" << std::endl; - video_format = RTP_FORMAT_H265; - } - else if (vps_map.begin()->ptl.ptl_profile_codec_group_idc == CODEC_VVC_MAIN10) { - std::cout << "Video codec: VVC Main10" << std::endl; - video_format = RTP_FORMAT_H266; - } - uvgrtp::media_stream* vid = sess->create_stream(8892, 7792, video_format, flags); + // Create the uvgRTP media streams with the correct RTP format + uvgrtp::media_stream* vps = sess->create_stream(8890, 8891, RTP_FORMAT_GENERIC, flags); + uvgrtp::media_stream* ad = sess->create_stream(8892, 8893, RTP_FORMAT_V3C, flags); + uvgrtp::media_stream* ovd = sess->create_stream(8894, 8895, RTP_FORMAT_H265, flags); + uvgrtp::media_stream* gvd = sess->create_stream(8896, 8897, RTP_FORMAT_H265, flags); + uvgrtp::media_stream* avd = sess->create_stream(8898, 8899, RTP_FORMAT_H265, flags); + uvgrtp::media_stream* pvd = sess->create_stream(9000, 9001, RTP_FORMAT_H265, flags); + uvgrtp::media_stream* cad = sess->create_stream(9002, 9003, RTP_FORMAT_V3C, flags); - uint64_t bytes_sent = 0; uint64_t send_ptr = 0; /* Start sending data */ - for (auto p : nal_map) - { - rtp_error_t ret = RTP_OK; - if (p.format == V3C_AD || p.format == V3C_CAD) { - std::cout << "Sending V3C NAL unit in location " << p.location << " with size " << p.size << std::endl; - ret = v3c->push_frame((uint8_t*)cbuf + p.location, p.size, RTP_NO_FLAGS); + std::unique_ptr vps_thread = + std::unique_ptr(new std::thread(sender_func, vps, cbuf, mmap.vps_units, RTP_NO_FLAGS, V3C_VPS)); - } - else { - std::cout << "Sending video NAL unit in location " << p.location << " with size " << p.size << std::endl; - ret = vid->push_frame((uint8_t*)cbuf + p.location, p.size, RTP_NO_H26X_SCL); - } - if (ret == RTP_OK) { - bytes_sent += p.size; - } - else { - std::cout << "Failed to send RTP frame!" << std::endl; - } + std::unique_ptr ad_thread = + std::unique_ptr(new std::thread(sender_func, ad, cbuf, mmap.ad_units, RTP_NO_FLAGS, V3C_AD)); + + std::unique_ptr ovd_thread = + std::unique_ptr(new std::thread(sender_func, ovd, cbuf, mmap.ovd_units, RTP_NO_H26X_SCL, V3C_OVD)); + + std::unique_ptr gvd_thread = + std::unique_ptr(new std::thread(sender_func, gvd, cbuf, mmap.gvd_units, RTP_NO_H26X_SCL, V3C_GVD)); + + std::unique_ptr avd_thread = + std::unique_ptr(new std::thread(sender_func, avd, cbuf, mmap.avd_units, RTP_NO_H26X_SCL, V3C_AVD)); + + std::unique_ptr pvd_thread = + std::unique_ptr(new std::thread(sender_func, pvd, cbuf, mmap.pvd_units, RTP_NO_H26X_SCL, V3C_PVD)); + + std::unique_ptr cad_thread = + std::unique_ptr(new std::thread(sender_func, cad, cbuf, mmap.cad_units, RTP_NO_FLAGS, V3C_CAD)); + + if (vps_thread && vps_thread->joinable()) + { + vps_thread->join(); + } + if (ad_thread && ad_thread->joinable()) + { + ad_thread->join(); + } + if (ovd_thread && ovd_thread->joinable()) + { + ovd_thread->join(); + } + if (gvd_thread && gvd_thread->joinable()) + { + gvd_thread->join(); + } + if (avd_thread && avd_thread->joinable()) + { + avd_thread->join(); + } + if (pvd_thread && pvd_thread->joinable()) + { + pvd_thread->join(); + } + if (cad_thread && cad_thread->joinable()) + { + cad_thread->join(); } - std::cout << "Sending finished. Total bytes sent " << bytes_sent << std::endl; + sess->destroy_stream(vps); + sess->destroy_stream(ad); + sess->destroy_stream(ovd); + sess->destroy_stream(gvd); + sess->destroy_stream(avd); + sess->destroy_stream(pvd); + sess->destroy_stream(cad); + + + std::cout << "Sending finished" << std::endl; - sess->destroy_stream(v3c); - sess->destroy_stream(vid); if (sess) { // Session must be destroyed manually @@ -105,3 +131,43 @@ int main(void) } return EXIT_SUCCESS; } + +void sender_func(uvgrtp::media_stream* stream, const char* cbuf, const std::vector &units, rtp_flags_t flags, int fmt) +{ + std::string filename = "results_" + std::to_string(fmt) + ".csv"; + //std::ofstream myfile; + //myfile.open(filename); + //myfile << "Stream " + std::to_string(fmt) + ";;;Send time (microseconds) \n"; + //myfile << "NAL number;NAL size(bytes);Cumulative bytes sent; Send time at 100 MBps;1 GBps; 10 GBps \n"; + int index = 0; + uint64_t bytes_sent = 0; + + for (auto& p : units) { + for (auto i : p.nal_infos) { + rtp_error_t ret = RTP_OK; + //std::cout << "Sending NAL unit in location " << i.location << " with size " << i.size << std::endl; + ret = stream->push_frame((uint8_t*)cbuf + i.location, i.size, flags); + if (ret == RTP_OK) { + index++; + bytes_sent += i.size; + double time_100m = bytes_sent / 100; // us so dont multiply *1000 * 1000; + double time_1g = bytes_sent / (1 * 1000); // us so dont multiply *1000 * 1000; + double time_10g = bytes_sent / (10 * 1000); // us so dont multiply *1000 * 1000; + std::cout << "bytes sent " << bytes_sent << std::endl; + std::string line = std::to_string(index) + ";" + std::to_string(i.size) + ";" + std::to_string(bytes_sent) + ";" + + std::to_string(time_100m) + ";" + + std::to_string(time_1g) + ";" + + std::to_string(time_10g) + ";" + "\n"; + + //myfile << line; + std::cout << line << std::endl; + } + else { + std::cout << "Failed to send RTP frame!" << std::endl; + } + } + } + //myfile.close(); + + +} diff --git a/include/uvgrtp/v3c_parser.hh b/include/uvgrtp/v3c_parser.hh index 1d65930..e8acbc8 100644 --- a/include/uvgrtp/v3c_parser.hh +++ b/include/uvgrtp/v3c_parser.hh @@ -7,18 +7,22 @@ #include // vuh_unit_type definitions: -constexpr int V3C_VPS = 0; // V3C parameter set -constexpr int V3C_AD = 1; // Atlas data -constexpr int V3C_OVD = 2; // Occupancy video data -constexpr int V3C_GVD = 3; // Geometry video data -constexpr int V3C_AVD = 4; // Attribute video data -constexpr int V3C_PVD = 5; // Packed video data -constexpr int V3C_CAD = 6; // Common atlas data +enum V3C_UNIT_TYPE { + V3C_VPS = 0, // V3C parameter set + V3C_AD = 1, // Atlas data + V3C_OVD = 2, // Occupancy video data + V3C_GVD = 3, // Geometry video data + V3C_AVD = 4, // Attribute video data + V3C_PVD = 5, // Packed video data + V3C_CAD = 6 // Common atlas data +}; -constexpr int CODEC_AVC = 0; // AVC Progressive High -constexpr int CODEC_HEVC_MAIN10 = 1; // HEVC Main10 -constexpr int CODEC_HEVC444 = 2; // HEVC444 -constexpr int CODEC_VVC_MAIN10 = 3; // VVC Main10 +enum CODEC { + CODEC_AVC = 0, // AVC Progressive High + CODEC_HEVC_MAIN10 = 1, // HEVC Main10 + CODEC_HEVC444 = 2, // HEVC444 + CODEC_VVC_MAIN10 = 3 // VVC Main10 +}; constexpr int V3C_HDR_LEN = 4; // 32 bits for v3c unit header @@ -33,11 +37,13 @@ struct profile_tier_level { uint8_t ptl_level_idc = 0; uint8_t ptl_num_sub_profiles = 0; bool ptl_extended_sub_profile_flag = 0; - std::vector ptl_sub_profile_idc; + std::vector ptl_sub_profile_idc = {}; bool ptl_toolset_constraints_present_flag = 0; }; -struct vuh_vps { +struct parameter_set { profile_tier_level ptl; + uint8_t vps_v3c_parameter_set_id = 0; + uint8_t vps_atlas_count_minus1 = 0; }; struct vuh_ad { uint8_t vuh_v3c_parameter_set_id = 0; @@ -72,7 +78,6 @@ struct vuh_cad { struct v3c_unit_header { uint8_t vuh_unit_type = 0; union { - vuh_vps vps; vuh_ad ad; vuh_ovd ovd; vuh_gvd gvd; @@ -80,25 +85,43 @@ struct v3c_unit_header { vuh_pvd pvd; vuh_cad cad; }; - ~v3c_unit_header() { - vps.~vuh_vps(); - } - }; struct nal_info { - uint8_t format = 0; // RTP format of this NAL unit (V3C, H264, H265, H266) uint64_t location = 0; // Start position of the NAL unit uint64_t size = 0; // Sie of the NAL unit }; +struct v3c_unit_info { + v3c_unit_header header; + std::vector nal_infos = {}; + char* buf; + uint64_t ptr = 0; + bool ready = false; +}; + +struct v3c_file_map { + std::vector vps_units = {}; + std::vector ad_units = {}; + std::vector ovd_units = {}; + std::vector gvd_units = {}; + std::vector avd_units = {}; + std::vector pvd_units = {}; + std::vector cad_units = {}; +}; + uint32_t combineBytes(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4); uint32_t combineBytes(uint8_t byte1, uint8_t byte2, uint8_t byte3); uint32_t combineBytes(uint8_t byte1, uint8_t byte2); +void convert_size_little_endian(uint32_t in, uint8_t* out, size_t output_size); +void convert_size_big_endian(uint32_t in, uint8_t* out, size_t output_size); uint64_t get_size(std::string filename); char* get_cmem(std::string filename, const size_t& len); // ad is for AD and CAD substreams, vd is for all VD substreams -bool mmap_v3c_file(char* cbuf, uint64_t len, std::vector& nal_map, std::vector &vps_map); -void parse_v3c_header(v3c_unit_header &hdr, char* buf, uint64_t ptr); \ No newline at end of file +bool mmap_v3c_file(char* cbuf, uint64_t len, v3c_file_map &mmap); +void parse_v3c_header(v3c_unit_header &hdr, char* buf, uint64_t ptr); + +void parse_vps_ptl(profile_tier_level &ptl, char* buf, uint64_t ptr); + diff --git a/src/v3c_parser.cc b/src/v3c_parser.cc index 4aa9f61..5ff224a 100644 --- a/src/v3c_parser.cc +++ b/src/v3c_parser.cc @@ -18,7 +18,27 @@ uint32_t combineBytes(uint8_t byte1, uint8_t byte2) { (static_cast(byte2)); } -bool mmap_v3c_file(char* cbuf, uint64_t len, std::vector& nal_map, std::vector& vps_map) +void convert_size_little_endian(uint32_t in, uint8_t* out, size_t output_size) { + // Make sure the output size is not larger than the size of uint32_t + if (output_size > sizeof(uint32_t)) { + output_size = sizeof(uint32_t); + } + + // Convert the uint32_t input into the output array + // The exact way to store the value depends on the endianness of the system + // This example assumes little-endian + for (size_t i = 0; i < output_size; ++i) { + out[i] = (in >> (8 * i)) & 0xFF; + } +} + +void convert_size_big_endian(uint32_t in, uint8_t* out, size_t output_size) { + for (size_t i = 0; i < output_size; ++i) { + out[output_size - i - 1] = static_cast(in >> (8 * i)); + } +} + +bool mmap_v3c_file(char* cbuf, uint64_t len, v3c_file_map &mmap) { uint64_t ptr = 0; @@ -60,20 +80,19 @@ bool mmap_v3c_file(char* cbuf, uint64_t len, std::vector& nal_map, std parse_v3c_header(v3c_hdr, cbuf, v3c_ptr); uint8_t vuh_t = v3c_hdr.vuh_unit_type; std::cout << "-- vuh_unit_type: " << (uint32_t)vuh_t << std::endl; + v3c_unit_info unit = { v3c_hdr, {}, nullptr}; if (vuh_t == V3C_VPS) { // Parameter set contains no NAL units, skip over std::cout << "-- Parameter set V3C unit" << std::endl; + unit.nal_infos.push_back({ ptr, combined_v3c_size }); + mmap.vps_units.push_back(unit); ptr += combined_v3c_size; - vps_map.push_back(v3c_hdr.vps); std::cout << std::endl; continue; } - else if (vuh_t == V3C_OVD || vuh_t == V3C_GVD || vuh_t == V3C_AVD || vuh_t == V3C_PVD) { - std::cout << "-- Video data V3C unit, " << std::endl; - } - // Rest of the function goes inside the V3C unit payload and parses NAL it into NAL units + // Rest of the function goes inside the V3C unit payload and parses it into NAL units v3c_ptr += V3C_HDR_LEN; // Jump over 4 bytes of V3C unit header if (vuh_t == V3C_AD || vuh_t == V3C_CAD) { uint8_t v3cu_first_byte = cbuf[v3c_ptr]; // Next up is 1 byte of NAL unit size precision @@ -85,12 +104,13 @@ bool mmap_v3c_file(char* cbuf, uint64_t len, std::vector& nal_map, std nal_size_precision = 4; std::cout << " -- Video NAL Sample stream, using NAL unit size precision of: " << (uint32_t)nal_size_precision << std::endl; } - + uint64_t amount_of_nal_units = 0; // Now start to parse the NAL sample stream while (true) { if (v3c_ptr >= (ptr + combined_v3c_size)) { break; } + amount_of_nal_units++; uint32_t combined_nal_size = 0; if (nal_size_precision == 2) { combined_nal_size = combineBytes(cbuf[v3c_ptr], cbuf[v3c_ptr + 1]); @@ -109,19 +129,41 @@ bool mmap_v3c_file(char* cbuf, uint64_t len, std::vector& nal_map, std switch (vuh_t) { case V3C_AD: case V3C_CAD: - std::cout << " -- v3c_ptr: " << v3c_ptr << ", NALU size : " << combined_nal_size << std::endl; + std::cout << " -- v3c_ptr: " << v3c_ptr << ", NALU size: " << combined_nal_size << std::endl; break; case V3C_OVD: case V3C_GVD: case V3C_AVD: case V3C_PVD: uint8_t h265_nalu_t = (cbuf[v3c_ptr] & 0b01111110) >> 1; - std::cout << " -- v3c_ptr: " << v3c_ptr << ", NALU size : " << combined_nal_size << ", HEVC NALU type: " << (uint32_t)h265_nalu_t << std::endl; + std::cout << " -- v3c_ptr: " << v3c_ptr << ", NALU size: " << combined_nal_size << ", HEVC NALU type: " << (uint32_t)h265_nalu_t << std::endl; } - nal_map.push_back({vuh_t, v3c_ptr, combined_nal_size}); + unit.nal_infos.push_back({ v3c_ptr, combined_nal_size }); v3c_ptr += combined_nal_size; - } + } + std::cout << " -- Amount of NAL units in v3c unit: " << amount_of_nal_units << std::endl; + + switch (vuh_t) { + case V3C_AD: + mmap.ad_units.push_back(unit); + break; + case V3C_CAD: + mmap.cad_units.push_back(unit); + break; + case V3C_OVD: + mmap.ovd_units.push_back(unit); + break; + case V3C_GVD: + mmap.gvd_units.push_back(unit); + break; + case V3C_AVD: + mmap.avd_units.push_back(unit); + break; + case V3C_PVD: + mmap.pvd_units.push_back(unit); + break; + } std::cout << std::endl; ptr += combined_v3c_size; } @@ -136,7 +178,7 @@ void parse_v3c_header(v3c_unit_header &hdr, char* buf, uint64_t ptr) uint8_t vuh_v3c_parameter_set_id = 0;; uint8_t vuh_atlas_id = 0; - + if (vuh_unit_type == V3C_AVD || vuh_unit_type == V3C_GVD || vuh_unit_type == V3C_OVD || vuh_unit_type == V3C_AD || vuh_unit_type == V3C_CAD || vuh_unit_type == V3C_PVD) { @@ -156,56 +198,6 @@ void parse_v3c_header(v3c_unit_header &hdr, char* buf, uint64_t ptr) switch (hdr.vuh_unit_type) { case V3C_VPS: { - hdr.vps = {}; - // first bit of first byte - hdr.vps.ptl.ptl_tier_flag = buf[ptr + 4] >> 7; - std::cout << "-- ptl_tier_flag: " << (uint32_t)hdr.vps.ptl.ptl_tier_flag << std::endl; - // 7 bits after - hdr.vps.ptl.ptl_profile_codec_group_idc = buf[ptr + 4] & 0b01111111; - std::cout << "-- ptl_profile_codec_group_idc: " << (uint32_t)hdr.vps.ptl.ptl_profile_codec_group_idc << std::endl; - // 8 bits after - hdr.vps.ptl.ptl_profile_toolset_idc = buf[ptr + 5]; - std::cout << "-- ptl_profile_toolset_idc: " << (uint32_t)hdr.vps.ptl.ptl_profile_toolset_idc << std::endl; - // 8 bits after - hdr.vps.ptl.ptl_profile_reconstruction_idc = buf[ptr + 6]; - std::cout << "-- ptl_profile_reconstruction_idc: " << (uint32_t)hdr.vps.ptl.ptl_profile_reconstruction_idc << std::endl; - // 16 reserved bits - //4 bits after - hdr.vps.ptl.ptl_max_decodes_idc = buf[ptr + 9] >> 4; - std::cout << "-- ptl_max_decodes_idc: " << (uint32_t)hdr.vps.ptl.ptl_max_decodes_idc << "+1 = " << - (uint32_t)hdr.vps.ptl.ptl_max_decodes_idc + 1 << std::endl; - // 12 reserved bits - // 8 bits after - hdr.vps.ptl.ptl_level_idc = buf[ptr + 11]; - std::cout << "-- ptl_level_idc: " << (uint32_t)hdr.vps.ptl.ptl_level_idc << "/30 = " << - (double)hdr.vps.ptl.ptl_level_idc / 30 << std::endl; - // 6 bits after - hdr.vps.ptl.ptl_num_sub_profiles = buf[ptr + 12] >> 2; - std::cout << "-- ptl_num_sub_profiles: " << (uint32_t)hdr.vps.ptl.ptl_num_sub_profiles << std::endl; - // 1 bit after - hdr.vps.ptl.ptl_extended_sub_profile_flag = (buf[ptr + 12] & 0b10) >> 1; - std::cout << "-- ptl_extended_sub_profile_flag: " << (uint32_t)hdr.vps.ptl.ptl_extended_sub_profile_flag << std::endl; - // next up are the sub-profile IDs. They can be either 32 or 64 bits long, indicated by ptl_extended_sub_profile_flag - // Note: This has not been tested. But it should work - ptr += 12; - uint64_t first_full_byte = ptr + 13; - if (hdr.vps.ptl.ptl_extended_sub_profile_flag == 1) { - for (int i = 0; i < hdr.vps.ptl.ptl_num_sub_profiles; i++) { - uint64_t sub_profile_id = (buf[first_full_byte] >> 1) | ((buf[first_full_byte - 1] & 0b1) << 63); - hdr.vps.ptl.ptl_sub_profile_idc.push_back(sub_profile_id); - first_full_byte += 8; - } - } - else { - for (int i = 0; i < hdr.vps.ptl.ptl_num_sub_profiles; i++) { - uint32_t sub_profile_id = (buf[first_full_byte] >> 1) | ((buf[first_full_byte - 1] & 0b1) << 31); - hdr.vps.ptl.ptl_sub_profile_idc.push_back((uint64_t)sub_profile_id); - first_full_byte += 4; - } - } - // 1 bit after - hdr.vps.ptl.ptl_toolset_constraints_present_flag = (buf[ptr] & 0b1); - std::cout << "-- ptl_toolset_constraints_present_flag: " << (uint32_t)hdr.vps.ptl.ptl_toolset_constraints_present_flag << std::endl; break; } case V3C_AD: @@ -267,6 +259,60 @@ void parse_v3c_header(v3c_unit_header &hdr, char* buf, uint64_t ptr) return; } +void parse_vps_ptl(profile_tier_level &ptl, char* buf, uint64_t ptr) +{ + // first bit of first byte + ptl.ptl_tier_flag = buf[ptr + 4] >> 7; + std::cout << "-- ptl_tier_flag: " << (uint32_t)ptl.ptl_tier_flag << std::endl; + // 7 bits after + ptl.ptl_profile_codec_group_idc = buf[ptr + 4] & 0b01111111; + std::cout << "-- ptl_profile_codec_group_idc: " << (uint32_t)ptl.ptl_profile_codec_group_idc << std::endl; + // 8 bits after + ptl.ptl_profile_toolset_idc = buf[ptr + 5]; + std::cout << "-- ptl_profile_toolset_idc: " << (uint32_t)ptl.ptl_profile_toolset_idc << std::endl; + // 8 bits after + ptl.ptl_profile_reconstruction_idc = buf[ptr + 6]; + std::cout << "-- ptl_profile_reconstruction_idc: " << (uint32_t)ptl.ptl_profile_reconstruction_idc << std::endl; + // 16 reserved bits + //4 bits after + ptl.ptl_max_decodes_idc = buf[ptr + 9] >> 4; + std::cout << "-- ptl_max_decodes_idc: " << (uint32_t)ptl.ptl_max_decodes_idc << "+1 = " << + (uint32_t)ptl.ptl_max_decodes_idc + 1 << std::endl; + // 12 reserved bits + // 8 bits after + ptl.ptl_level_idc = buf[ptr + 11]; + std::cout << "-- ptl_level_idc: " << (uint32_t)ptl.ptl_level_idc << "/30 = " << + (double)ptl.ptl_level_idc / 30 << std::endl; + // 6 bits after + ptl.ptl_num_sub_profiles = buf[ptr + 12] >> 2; + std::cout << "-- ptl_num_sub_profiles: " << (uint32_t)ptl.ptl_num_sub_profiles << std::endl; + // 1 bit after + ptl.ptl_extended_sub_profile_flag = (buf[ptr + 12] & 0b10) >> 1; + std::cout << "-- ptl_extended_sub_profile_flag: " << (uint32_t)ptl.ptl_extended_sub_profile_flag << std::endl; + // next up are the sub-profile IDs. They can be either 32 or 64 bits long, indicated by ptl_extended_sub_profile_flag + // Note: This has not been tested. But it should work + ptr += 12; + uint64_t first_full_byte = ptr + 13; + if (ptl.ptl_extended_sub_profile_flag == 1) { + for (int i = 0; i < ptl.ptl_num_sub_profiles; i++) { + // TODO this isnt right... + uint64_t sub_profile_id = (buf[first_full_byte] >> 1) | ((buf[first_full_byte - 1] & 0b1) << 63); + ptl.ptl_sub_profile_idc.push_back(sub_profile_id); + first_full_byte += 8; + } + } + else { + for (int i = 0; i < ptl.ptl_num_sub_profiles; i++) { + uint32_t sub_profile_id = (buf[first_full_byte] >> 1) | ((buf[first_full_byte - 1] & 0b1) << 31); + ptl.ptl_sub_profile_idc.push_back((uint64_t)sub_profile_id); + first_full_byte += 4; + } + } + // 1 bit after + ptl.ptl_toolset_constraints_present_flag = (buf[ptr] & 0b1); + std::cout << "-- ptl_toolset_constraints_present_flag: " << (uint32_t)ptl.ptl_toolset_constraints_present_flag << std::endl; +} + uint64_t get_size(std::string filename) { std::ifstream infile(filename, std::ios_base::binary);