v3c: Add comments

This commit is contained in:
Heikki Tampio 2023-08-23 12:03:34 +03:00
parent 3efbd098a6
commit d681961c4c
4 changed files with 72 additions and 119 deletions

View File

@ -1,34 +1,13 @@
#include <uvgrtp/lib.hh>
#include <thread>
#include <iostream>
#include <fstream>
/* 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<v3c_unit_info>* vec = (std::vector<v3c_unit_info>*)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<v3c_unit_info>* vec = (std::vector<v3c_unit_info>*)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<v3c_unit_info>* vec = (std::vector<v3c_unit_info>*)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<v3c_unit_info>* vec = (std::vector<v3c_unit_info>*)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<v3c_unit_info>* vec = (std::vector<v3c_unit_info>*)arg;
copy_rtp_payload(*vec, AVD_NALS, frame);
(void)uvgrtp::frame::dealloc_frame(frame);
}

View File

@ -5,32 +5,35 @@
#include <cstring>
#include <vector>
#include <string>
#include <atomic>
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<v3c_unit_info> &units, rtp_flags_t flags, int fmt);
std::atomic<uint64_t> 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;
}
}
}

View File

@ -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<v3c_unit_info>& 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);

View File

@ -18,20 +18,6 @@ uint32_t combineBytes(uint8_t byte1, uint8_t byte2) {
(static_cast<uint32_t>(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<uint8_t>(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<v3c_unit_info>& 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(&current->buf[current->ptr], frame->payload, frame->payload_len);