v3c: Reconstruct the V3C file after receiving all NAL units
This commit is contained in:
parent
0e8c52aa77
commit
11fa5d122c
|
@ -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<v3c_unit_info>* vec = (std::vector<v3c_unit_info>*)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<v3c_unit_info>* vec = (std::vector<v3c_unit_info>*)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<v3c_unit_info>* vec = (std::vector<v3c_unit_info>*)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<v3c_unit_info>* vec = (std::vector<v3c_unit_info>*)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<v3c_unit_info>* vec = (std::vector<v3c_unit_info>*)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);
|
||||
}
|
|
@ -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<v3c_unit_info> &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_info> nal_map = {};
|
||||
std::vector<vuh_vps> 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<std::thread> vps_thread =
|
||||
std::unique_ptr<std::thread>(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<std::thread> ad_thread =
|
||||
std::unique_ptr<std::thread>(new std::thread(sender_func, ad, cbuf, mmap.ad_units, RTP_NO_FLAGS, V3C_AD));
|
||||
|
||||
std::unique_ptr<std::thread> ovd_thread =
|
||||
std::unique_ptr<std::thread>(new std::thread(sender_func, ovd, cbuf, mmap.ovd_units, RTP_NO_H26X_SCL, V3C_OVD));
|
||||
|
||||
std::unique_ptr<std::thread> gvd_thread =
|
||||
std::unique_ptr<std::thread>(new std::thread(sender_func, gvd, cbuf, mmap.gvd_units, RTP_NO_H26X_SCL, V3C_GVD));
|
||||
|
||||
std::unique_ptr<std::thread> avd_thread =
|
||||
std::unique_ptr<std::thread>(new std::thread(sender_func, avd, cbuf, mmap.avd_units, RTP_NO_H26X_SCL, V3C_AVD));
|
||||
|
||||
std::unique_ptr<std::thread> pvd_thread =
|
||||
std::unique_ptr<std::thread>(new std::thread(sender_func, pvd, cbuf, mmap.pvd_units, RTP_NO_H26X_SCL, V3C_PVD));
|
||||
|
||||
std::unique_ptr<std::thread> cad_thread =
|
||||
std::unique_ptr<std::thread>(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<v3c_unit_info> &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();
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -7,18 +7,22 @@
|
|||
#include <string>
|
||||
|
||||
// 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<uint64_t> ptl_sub_profile_idc;
|
||||
std::vector<uint64_t> 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_info> nal_infos = {};
|
||||
char* buf;
|
||||
uint64_t ptr = 0;
|
||||
bool ready = false;
|
||||
};
|
||||
|
||||
struct v3c_file_map {
|
||||
std::vector<v3c_unit_info> vps_units = {};
|
||||
std::vector<v3c_unit_info> ad_units = {};
|
||||
std::vector<v3c_unit_info> ovd_units = {};
|
||||
std::vector<v3c_unit_info> gvd_units = {};
|
||||
std::vector<v3c_unit_info> avd_units = {};
|
||||
std::vector<v3c_unit_info> pvd_units = {};
|
||||
std::vector<v3c_unit_info> 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_info>& nal_map, std::vector<vuh_vps> &vps_map);
|
||||
void parse_v3c_header(v3c_unit_header &hdr, char* buf, uint64_t ptr);
|
||||
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);
|
||||
|
||||
|
|
|
@ -18,7 +18,27 @@ uint32_t combineBytes(uint8_t byte1, uint8_t byte2) {
|
|||
(static_cast<uint32_t>(byte2));
|
||||
}
|
||||
|
||||
bool mmap_v3c_file(char* cbuf, uint64_t len, std::vector<nal_info>& nal_map, std::vector<vuh_vps>& 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<uint8_t>(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_info>& 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_info>& 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_info>& 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);
|
||||
|
|
Loading…
Reference in New Issue