From d681961c4c6e6e460f86a4ae0071489a6edc5c09 Mon Sep 17 00:00:00 2001 From: Heikki Tampio Date: Wed, 23 Aug 2023 12:03:34 +0300 Subject: [PATCH] v3c: Add comments --- examples/v3c_receiver.cc | 44 +++------------ examples/v3c_sender.cc | 33 +++++------ include/uvgrtp/v3c_parser.hh | 10 +++- src/v3c_parser.cc | 104 +++++++++++++---------------------- 4 files changed, 72 insertions(+), 119 deletions(-) diff --git a/examples/v3c_receiver.cc b/examples/v3c_receiver.cc index 414ed119..807c7e6e 100644 --- a/examples/v3c_receiver.cc +++ b/examples/v3c_receiver.cc @@ -1,34 +1,13 @@ - #include #include #include #include -/* There are two main ways of getting received RTP frames from uvgRTP. - * This example demonstrates the usage of hook function to receive RTP frames. - * - * The advantage of using a hook function is minimal CPU usage and delay between - * uvgRTP receiving the frame and application processing the frame. When using - * the hook method, the application must take care that it is not using the hook - * function for heavy processing since this may block RTP frame reception. - * - * Hook based frame reception is generally recommended for most serious applications, - * but there can be situations where polling method is better, especially if performance - * is not a huge concern or if there needs to be tight control when the frame is - * received by the application. - * - * This example only implements the receiving, but it can be used together with the - * sending example to test the functionality. - */ - - // parameters for this test. You can change these to suit your network environment -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(5); +constexpr auto RECEIVE_TIME_S = std::chrono::seconds(10); void vps_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame); void ad_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame); @@ -43,6 +22,8 @@ constexpr int AD_NALS = 35; constexpr int OVD_NALS = 35; constexpr int GVD_NALS = 131; constexpr int AVD_NALS = 131; +constexpr int EXPECTED_GOPS = 1; + std::string PATH = "C:\\Users\\ngheta\\Documents\\v3c_test_seq_2.vpcc"; int main(void) @@ -77,16 +58,14 @@ int main(void) uint64_t bytes = 0; uint64_t ptr = 0; bool hdb = true; - char* out_buf = new char[len]; + char* out_buf = nullptr; - while (ngops <= 1) { + while (ngops <= EXPECTED_GOPS) { 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 << "GoP size " << bytes << std::endl; - + std::cout << "Full GoP received, num: " << ngops << std::endl; ngops++; - hdb = false; + hdb = false; // Only add the V3C Sample Stream header byte to only the first GoP } std::this_thread::sleep_for(std::chrono::milliseconds(10)); } @@ -108,16 +87,13 @@ int main(void) break; } } - - std::cout << "DONE " << std::endl; + std::cout << "Done " << std::endl; return EXIT_SUCCESS; } 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]; @@ -130,14 +106,12 @@ void vps_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame) void ad_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame) { std::vector* vec = (std::vector*)arg; - copy_rtp_payload(*vec, AD_NALS, frame); (void)uvgrtp::frame::dealloc_frame(frame); } void ovd_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame) { std::vector* vec = (std::vector*)arg; - copy_rtp_payload(*vec, OVD_NALS, frame); (void)uvgrtp::frame::dealloc_frame(frame); } @@ -145,13 +119,11 @@ void gvd_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame) { std::vector* vec = (std::vector*)arg; copy_rtp_payload(*vec, GVD_NALS, frame); - (void)uvgrtp::frame::dealloc_frame(frame); } void avd_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame) { std::vector* vec = (std::vector*)arg; - copy_rtp_payload(*vec, AVD_NALS, frame); (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 362f659d..728ec832 100644 --- a/examples/v3c_sender.cc +++ b/examples/v3c_sender.cc @@ -5,32 +5,35 @@ #include #include #include +#include constexpr char REMOTE_ADDRESS[] = "127.0.0.1"; -constexpr uint16_t REMOTE_PORT = 8890; -// the parameters of demostration -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\\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); +std::atomic bytes_sent; int main(void) { std::cout << "Parsing V3C file" << std::endl; - /* A V3C Sample stream is divided into 6 types of 'sub-bitstreams' + parameters. - - The nal_map holds nal_info structs + /* A V3C Sample stream is divided into 4 types of 'sub-bitstreams' + parameters. + - In v3c_file_map there are vectors of + - V3C unit infos + - v3c_unit_infos + - header + - nal_infos + - buf + - ptr + - ready + + - The nal_infos holds nal_info structs - nal_info struct holds the format(Atlas, H264, H265, H266), start position and size of the NAL unit - - With this info you can send the data via different uvgRTP media streams. Usually 2 streams, one in RTP_FORMAT_ATLAS for - Atlas NAL units and a second one for the video NAL units in the correct format + - With this info you can send the data via different uvgRTP media streams. Note: Use RTP_NO_H26X_SCL when sending video frames, as there is no start codes in the video sub-streams */ - + bytes_sent = 0; v3c_file_map mmap; /* Fetch the file and its size */ @@ -92,8 +95,7 @@ int main(void) sess->destroy_stream(streams.gvd); sess->destroy_stream(streams.avd); - - std::cout << "Sending finished" << std::endl; + std::cout << "Sending finished, " << bytes_sent << " bytes sent" << std::endl; if (sess) { @@ -113,8 +115,7 @@ void sender_func(uvgrtp::media_stream* stream, const char* cbuf, const std::vect if (ret != RTP_OK) { std::cout << "Failed to send RTP frame!" << std::endl; } + bytes_sent += i.size; } } - - } diff --git a/include/uvgrtp/v3c_parser.hh b/include/uvgrtp/v3c_parser.hh index d9395f13..58c21eca 100644 --- a/include/uvgrtp/v3c_parser.hh +++ b/include/uvgrtp/v3c_parser.hh @@ -28,6 +28,13 @@ enum CODEC { constexpr int V3C_HDR_LEN = 4; // 32 bits for v3c unit header +// These are signaled to the receiver one way or the other, for example SDP +constexpr uint8_t ATLAS_NAL_SIZE_PRECISION = 2; +constexpr uint8_t VIDEO_NAL_SIZE_PRECISION = 4; +constexpr uint8_t V3C_SIZE_PRECISION = 3; +constexpr int INPUT_BUFFER_SIZE = 40 * 1000 * 1000; // Received NAL units are copied to the input buffer + + struct vuh_ad { uint8_t vuh_v3c_parameter_set_id = 0; uint8_t vuh_atlas_id = 0; @@ -104,7 +111,6 @@ struct v3c_streams { 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); // Get size of a file in bytes @@ -132,7 +138,7 @@ void copy_rtp_payload(std::vector& units, uint64_t max_size, uvgr void create_v3c_unit(v3c_unit_info& current_unit, char* buf, uint64_t& ptr, uint64_t v3c_precision, uint32_t nal_precision); // Reconstruct a whole GoP from V3C Units -uint64_t reconstruct_v3c_gop(bool hdr_byte, char* buf, uint64_t& ptr, v3c_file_map& mmap, uint64_t index); +uint64_t reconstruct_v3c_gop(bool hdr_byte, char* &buf, uint64_t& ptr, v3c_file_map& mmap, uint64_t index); // Check if there is a complete GoP in the memory map bool is_gop_ready(uint64_t index, v3c_file_map& mmap); diff --git a/src/v3c_parser.cc b/src/v3c_parser.cc index ed2ce1ca..696f11c1 100644 --- a/src/v3c_parser.cc +++ b/src/v3c_parser.cc @@ -18,20 +18,6 @@ uint32_t combineBytes(uint8_t byte1, uint8_t byte2) { (static_cast(byte2)); } -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)); @@ -327,22 +313,22 @@ v3c_file_map init_mmap() v3c_unit_header hdr = { V3C_AD }; hdr.ad = { 0, 0 }; - v3c_unit_info unit = { hdr, {}, new char[40 * 1000 * 1000], 0, false }; + v3c_unit_info unit = { hdr, {}, new char[INPUT_BUFFER_SIZE], 0, false }; mmap.ad_units.push_back(unit); hdr = { V3C_OVD }; hdr.ovd = { 0, 0 }; - unit = { hdr, {}, new char[40 * 1000 * 1000], 0, false }; + unit = { hdr, {}, new char[INPUT_BUFFER_SIZE], 0, false }; mmap.ovd_units.push_back(unit); hdr = { V3C_GVD }; hdr.gvd = { 0, 0 }; - unit = { hdr, {}, new char[40 * 1000 * 1000], 0, false }; + unit = { hdr, {}, new char[INPUT_BUFFER_SIZE], 0, false }; mmap.gvd_units.push_back(unit); hdr = { V3C_AVD }; hdr.avd = { 0, 0 }; - unit = { hdr, {}, new char[40 * 1000 * 1000], 0, false }; + unit = { hdr, {}, new char[INPUT_BUFFER_SIZE], 0, false }; mmap.avd_units.push_back(unit); return mmap; } @@ -353,7 +339,7 @@ void create_v3c_unit(v3c_unit_info& current_unit, char* buf, uint64_t& ptr, uint // 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; + uint32_t v3c_size_int = 4 + (uint32_t)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 } @@ -421,7 +407,7 @@ void create_v3c_unit(v3c_unit_info& current_unit, char* buf, uint64_t& ptr, uint } -uint64_t reconstruct_v3c_gop(bool hdr_byte, char* buf, uint64_t& ptr, v3c_file_map& mmap, uint64_t index) +uint64_t reconstruct_v3c_gop(bool hdr_byte, char* &buf, uint64_t& ptr, v3c_file_map& mmap, uint64_t index) { /* Calculate GoP size and intiialize the output buffer * @@ -453,12 +439,10 @@ uint64_t reconstruct_v3c_gop(bool hdr_byte, char* buf, uint64_t& ptr, v3c_file_m + 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 - } + /*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; @@ -466,28 +450,25 @@ uint64_t reconstruct_v3c_gop(bool hdr_byte, char* buf, uint64_t& ptr, v3c_file_m 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; + + buf = new char[gop_size]; // 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++; } - uint8_t v3c_unit_size_precision = 3; - - uint8_t* v3c_size_arr = new uint8_t[v3c_unit_size_precision]; + uint8_t* v3c_size_arr = new uint8_t[V3C_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; + convert_size_big_endian(v3c_size_int, v3c_size_arr, V3C_SIZE_PRECISION); + memcpy(&buf[ptr], v3c_size_arr, V3C_SIZE_PRECISION); + ptr += V3C_SIZE_PRECISION; // Write the V3C VPS unit payload to the output buffer memcpy(&buf[ptr], current_unit.buf, v3c_size_int); @@ -495,20 +476,19 @@ uint64_t reconstruct_v3c_gop(bool hdr_byte, char* buf, uint64_t& ptr, v3c_file_m // 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); + create_v3c_unit(current_unit, buf, ptr, V3C_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); + create_v3c_unit(current_unit, buf, ptr, V3C_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); + create_v3c_unit(current_unit, buf, ptr, V3C_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; + create_v3c_unit(current_unit, buf, ptr, V3C_SIZE_PRECISION, VIDEO_NAL_SIZE_PRECISION); return gop_size; } @@ -532,36 +512,30 @@ bool is_gop_ready(uint64_t index, v3c_file_map& mmap) void copy_rtp_payload(std::vector& units, uint64_t max_size, uvgrtp::frame::rtp_frame* frame) { if ((units.end() - 1)->nal_infos.size() == max_size) { - std::cout << "AD size == 35, adding new v3c_unit " << std::endl; v3c_unit_header hdr = { (units.end() - 1)->header.vuh_unit_type }; + v3c_unit_info info = { {}, {}, new char[INPUT_BUFFER_SIZE], 0, false }; + switch ((units.end() - 1)->header.vuh_unit_type) { - case V3C_AD: { - hdr.ad = { (uint8_t)units.size(), 0 }; - v3c_unit_info info = { hdr, {}, new char[400 * 1000], 0, false }; - units.push_back(info); - break; - } - case V3C_OVD: { - hdr.ovd = { (uint8_t)units.size(), 0 }; - v3c_unit_info info = { hdr, {}, new char[400 * 1000], 0, false }; - units.push_back(info); - break; - } - case V3C_GVD: { - hdr.gvd = { (uint8_t)units.size(), 0, 0, 0 }; - v3c_unit_info info = { hdr, {}, new char[400 * 1000], 0, false }; - units.push_back(info); - break; - } - case V3C_AVD: { - hdr.avd = { (uint8_t)units.size(), 0 }; - v3c_unit_info info = { hdr, {}, new char[40 * 1000 * 1000], 0, false }; - units.push_back(info); - break; - } + case V3C_AD: { + info.header.ad = { (uint8_t)units.size(), 0 }; + break; + } + case V3C_OVD: { + info.header.ovd = { (uint8_t)units.size(), 0 }; + break; + } + case V3C_GVD: { + info.header.gvd = { (uint8_t)units.size(), 0, 0, 0 }; + break; + } + case V3C_AVD: { + info.header.avd = { (uint8_t)units.size(), 0 }; + break; + } } + units.push_back(info); } - auto& current = units.end() - 1; + auto current = units.end() - 1; if (current->nal_infos.size() <= max_size) { memcpy(¤t->buf[current->ptr], frame->payload, frame->payload_len);