v3c: Add comments
This commit is contained in:
parent
3efbd098a6
commit
d681961c4c
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(¤t->buf[current->ptr], frame->payload, frame->payload_len);
|
||||
|
|
Loading…
Reference in New Issue