v3c: Find H26x NAL unit boundaries from the V3C video substreams

This commit is contained in:
Heikki Tampio 2023-08-16 10:55:41 +03:00
parent a3598cd6c4
commit 4bb1062c99
4 changed files with 46 additions and 25 deletions

View File

@ -73,7 +73,7 @@ int main(void)
std::this_thread::sleep_for(RECEIVE_TIME_S); // lets this example run for some time 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 << "V3C Total bytes received " << vbytes_received << std::endl;
std::cout << "AVC Total bytes received " << abytes_received << std::endl; std::cout << "Video Total bytes received " << abytes_received << std::endl;
cleanup(ctx, sess, v3c); cleanup(ctx, sess, v3c);
cleanup(ctx, sess, avc); cleanup(ctx, sess, avc);
@ -95,7 +95,7 @@ void v3c_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame)
} }
void avc_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame) void avc_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame)
{ {
std::cout << "Received AVC frame, size: " << frame->payload_len << " bytes" << std::endl; std::cout << "Received video frame, size: " << frame->payload_len << " bytes" << std::endl;
abytes_received += frame->payload_len; abytes_received += frame->payload_len;
/* Now we own the frame. Here you could give the frame to the application /* Now we own the frame. Here you could give the frame to the application
* if f.ex "arg" was some application-specific pointer * if f.ex "arg" was some application-specific pointer

View File

@ -15,7 +15,7 @@ constexpr int AMOUNT_OF_TEST_PACKETS = 100;
constexpr auto END_WAIT = std::chrono::seconds(5); 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\\TMIV_A3_C_QP3.bit";
std::string PATH = "C:\\Users\\ngheta\\Documents\\test_seq3.vpcc"; std::string PATH = "C:\\Users\\ngheta\\Documents\\v3c_test_seq_2.vpcc";
int main(void) int main(void)
{ {
@ -23,51 +23,57 @@ int main(void)
/* A V3C Sample stream is divided into 6 types of 'sub-bitstreams' + parameters. /* A V3C Sample stream is divided into 6 types of 'sub-bitstreams' + parameters.
- The ad_map vector holds the locations and sizes of Atlas NAL units in the file (both AD and CAD) - The ad_map vector holds the locations and sizes of Atlas NAL units in the file (both AD and CAD)
- The vd_map holds the locations and sizes of all video V3C units (OVD, GVD, AVD, PVD) - The vd_map holds the locations and sizes of all video NAL units (OVD, GVD, AVD, PVD)
- First uint64_t is the start position of the unit, second is the size - First uint64_t is the start position of the unit, second is the size
- With this info you can send the data via different uvgRTP media streams */ - With this info you can send the data via different uvgRTP media streams */
std::vector<std::pair<uint64_t, uint64_t>> ad_map = {}; std::vector<std::pair<uint64_t, uint64_t>> ad_map = {};
std::vector<std::pair<uint64_t, uint64_t>> vd_map = {}; std::vector<std::pair<uint64_t, uint64_t>> vd_map = {};
/* Fetch the file and its size */
uint64_t len = get_size(PATH); uint64_t len = get_size(PATH);
uint64_t ptr = 0; uint64_t ptr = 0;
char* cbuf = nullptr; char* cbuf = nullptr;
cbuf = get_cmem(PATH, len); cbuf = get_cmem(PATH, len);
/* Map the locations and sizes of Atlas and video NAL units with the mmap_v3c_file function */
vuh_vps parameters = {}; vuh_vps parameters = {};
mmap_v3c_file(cbuf, len, parameters, ad_map, vd_map); mmap_v3c_file(cbuf, len, parameters, ad_map, vd_map);
std::cout << "Sending Atlas NAL units via uvgRTP" << std::endl; std::cout << "Sending Atlas NAL units via uvgRTP" << std::endl;
/* Create the necessary uvgRTP media streams */
uvgrtp::context ctx; uvgrtp::context ctx;
uvgrtp::session* sess = ctx.create_session(REMOTE_ADDRESS, REMOTE_ADDRESS); uvgrtp::session* sess = ctx.create_session(REMOTE_ADDRESS, REMOTE_ADDRESS);
int flags = RCE_SEND_ONLY; int flags = RCE_SEND_ONLY;
uvgrtp::media_stream* v3c = sess->create_stream(8890, 7790, RTP_FORMAT_V3C, flags); uvgrtp::media_stream* v3c = sess->create_stream(8890, 7790, RTP_FORMAT_V3C, flags);
uvgrtp::media_stream* vid = nullptr; rtp_format_t video_format = RTP_FORMAT_GENERIC;
// Create the uvgRTP media stream with the correct RTP format // Create the uvgRTP media stream with the correct RTP format
if (parameters.ptl.ptl_profile_codec_group_idc == CODEC_AVC) { if (parameters.ptl.ptl_profile_codec_group_idc == CODEC_AVC) {
std::cout << "Video codec: AVC Progressive High" << std::endl; std::cout << "Video codec: AVC Progressive High" << std::endl;
vid = sess->create_stream(8892, 7792, RTP_FORMAT_H264, flags); video_format = RTP_FORMAT_H264;
} }
else if (parameters.ptl.ptl_profile_codec_group_idc == CODEC_HEVC_MAIN10) { else if (parameters.ptl.ptl_profile_codec_group_idc == CODEC_HEVC_MAIN10) {
std::cout << "Video codec: HEVC Main10" << std::endl; std::cout << "Video codec: HEVC Main10" << std::endl;
vid = sess->create_stream(8892, 7792, RTP_FORMAT_H265, flags); video_format = RTP_FORMAT_H265;
} }
else if (parameters.ptl.ptl_profile_codec_group_idc == CODEC_HEVC444) { else if (parameters.ptl.ptl_profile_codec_group_idc == CODEC_HEVC444) {
std::cout << "Video codec: HEVC444" << std::endl; std::cout << "Video codec: HEVC444" << std::endl;
vid = sess->create_stream(8892, 7792, RTP_FORMAT_H265, flags); video_format = RTP_FORMAT_H265;
} }
else if (parameters.ptl.ptl_profile_codec_group_idc == CODEC_VVC_MAIN10) { else if (parameters.ptl.ptl_profile_codec_group_idc == CODEC_VVC_MAIN10) {
std::cout << "Video codec: VVC Main10" << std::endl; std::cout << "Video codec: VVC Main10" << std::endl;
vid = sess->create_stream(8892, 7792, RTP_FORMAT_H266, flags); video_format = RTP_FORMAT_H266;
} }
uvgrtp::media_stream* vid = sess->create_stream(8892, 7792, video_format, flags);
uint64_t v3c_bytes_sent = 0; uint64_t v3c_bytes_sent = 0;
uint64_t avc_bytes_sent = 0; uint64_t avc_bytes_sent = 0;
uint64_t send_ptr = 0; uint64_t send_ptr = 0;
/* Start sending data. First is all the Atlas NAL units, then all the video NAL units */
for (auto p : ad_map) for (auto p : ad_map)
{ {
std::cout << "Sending frame in location " << p.first << " with size " << p.second << std::endl; std::cout << "Sending frame in location " << p.first << " with size " << p.second << std::endl;
@ -84,7 +90,7 @@ int main(void)
{ {
std::cout << "Sending frame in location " << p.first << " with size " << p.second << std::endl; std::cout << "Sending frame in location " << p.first << " with size " << p.second << std::endl;
if (vid->push_frame((uint8_t*)cbuf + p.first, p.second, RTP_NO_FLAGS) != RTP_OK) if (vid->push_frame((uint8_t*)cbuf + p.first, p.second, RTP_NO_H26X_SCL) != RTP_OK)
{ {
std::cout << "Failed to send RTP frame!" << std::endl; std::cout << "Failed to send RTP frame!" << std::endl;
} }
@ -94,7 +100,7 @@ int main(void)
} }
std::cout << "V3C Sending finished. Total bytes sent " << v3c_bytes_sent << std::endl; std::cout << "V3C Sending finished. Total bytes sent " << v3c_bytes_sent << std::endl;
std::cout << "AVC Sending finished. Total bytes sent " << avc_bytes_sent << std::endl; std::cout << "Video Sending finished. Total bytes sent " << avc_bytes_sent << std::endl;
sess->destroy_stream(v3c); sess->destroy_stream(v3c);
sess->destroy_stream(vid); sess->destroy_stream(vid);

View File

@ -8,7 +8,7 @@
// vuh_unit_type definitions: // vuh_unit_type definitions:
constexpr int V3C_VPS = 0; // V3C parameter set constexpr int V3C_VPS = 0; // V3C parameter set
constexpr int V3C_AD = 1; // Atlas data constexpr int V3C_AD = 1; // Atlas data
constexpr int V3C_OVD = 2; // Occupancy video data constexpr int V3C_OVD = 2; // Occupancy video data
constexpr int V3C_GVD = 3; // Geometry video data constexpr int V3C_GVD = 3; // Geometry video data
constexpr int V3C_AVD = 4; // Attribute video data constexpr int V3C_AVD = 4; // Attribute video data

View File

@ -34,6 +34,7 @@ bool mmap_v3c_file(char* cbuf, uint64_t len, vuh_vps& param, std::vector<std::pa
uint8_t* v3c_size = new uint8_t[v3c_size_precision]; uint8_t* v3c_size = new uint8_t[v3c_size_precision];
std::vector<uint32_t> sizes = {}; std::vector<uint32_t> sizes = {};
uint8_t nal_size_precision = 0;
while (true) { while (true) {
if (ptr >= len) { if (ptr >= len) {
break; break;
@ -53,7 +54,7 @@ bool mmap_v3c_file(char* cbuf, uint64_t len, vuh_vps& param, std::vector<std::pa
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// Inside v3c unit now // Inside v3c unit now
std::cout << "Current V3C unit size " << combined_v3c_size << std::endl; std::cout << "Current V3C unit location " << ptr << ", size " << combined_v3c_size << std::endl;
uint64_t v3c_ptr = ptr; uint64_t v3c_ptr = ptr;
// Next 4 bytes are the V3C unit header // Next 4 bytes are the V3C unit header
@ -74,19 +75,24 @@ bool mmap_v3c_file(char* cbuf, uint64_t len, vuh_vps& param, std::vector<std::pa
else if (vuh_t == V3C_OVD || vuh_t == V3C_GVD || vuh_t == V3C_AVD || vuh_t == V3C_PVD) { else if (vuh_t == V3C_OVD || vuh_t == V3C_GVD || vuh_t == V3C_AVD || vuh_t == V3C_PVD) {
// Video data, map start and size // Video data, map start and size
std::cout << "-- Video data V3C unit, " << std::endl; std::cout << "-- Video data V3C unit, " << std::endl;
vd.push_back({ ptr + V3C_HDR_LEN, combined_v3c_size }); //vd.push_back({ ptr + V3C_HDR_LEN, combined_v3c_size });
ptr += combined_v3c_size; //ptr += combined_v3c_size;
std::cout << std::endl; //std::cout << std::endl;
continue; //continue;
} }
// 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 NAL it into NAL units
v3c_ptr += V3C_HDR_LEN; // Jump over 4 bytes of V3C unit header 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 uint8_t v3cu_first_byte = cbuf[v3c_ptr]; // Next up is 1 byte of NAL unit size precision
uint8_t nal_size_precision = (v3cu_first_byte >> 5) + 1; nal_size_precision = (v3cu_first_byte >> 5) + 1;
std::cout << " -- NAL Sample stream, 1 byte for NAL unit size precision: " << (uint32_t)nal_size_precision << std::endl; std::cout << " -- Atlas NAL Sample stream, 1 byte for NAL unit size precision: " << (uint32_t)nal_size_precision << std::endl;
++v3c_ptr; ++v3c_ptr;
}
else {
nal_size_precision = 4;
std::cout << " -- Video NAL Sample stream, using NAL unit size precision of: " << (uint32_t)nal_size_precision << std::endl;
}
// Now start to parse the NAL sample stream // Now start to parse the NAL sample stream
while (true) { while (true) {
@ -100,18 +106,27 @@ bool mmap_v3c_file(char* cbuf, uint64_t len, vuh_vps& param, std::vector<std::pa
else if (nal_size_precision == 3) { else if (nal_size_precision == 3) {
combined_nal_size = combineBytes(cbuf[v3c_ptr], cbuf[v3c_ptr + 1], cbuf[v3c_ptr + 2]); combined_nal_size = combineBytes(cbuf[v3c_ptr], cbuf[v3c_ptr + 1], cbuf[v3c_ptr + 2]);
} }
else if (nal_size_precision == 4) {
combined_nal_size = combineBytes(cbuf[v3c_ptr], cbuf[v3c_ptr + 1], cbuf[v3c_ptr + 2], cbuf[v3c_ptr + 3]);
}
else { else {
std::cout << " -- Error, invalid NAL size " << std::endl; std::cout << " -- Error, invalid NAL size " << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
v3c_ptr += nal_size_precision; v3c_ptr += nal_size_precision;
std::cout << " -- NALU size: " << combined_nal_size << std::endl;
switch (vuh_t) { switch (vuh_t) {
case V3C_AD: case V3C_AD:
case V3C_CAD: case V3C_CAD:
std::cout << " -- v3c_ptr: " << v3c_ptr << ", NALU size : " << combined_nal_size << std::endl;
ad.push_back({ v3c_ptr, combined_nal_size }); ad.push_back({ v3c_ptr, combined_nal_size });
break; 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;
vd.push_back({ v3c_ptr, combined_nal_size });
} }
v3c_ptr += combined_nal_size; v3c_ptr += combined_nal_size;
} }