v3c: Add parsing of the profile_tier_level fields in V3C VPS units

This commit is contained in:
Heikki Tampio 2023-08-17 08:56:41 +03:00
parent 1d5d1fa1a9
commit 0e8c52aa77
4 changed files with 74 additions and 24 deletions

View File

@ -42,7 +42,7 @@ int main(void)
uvgrtp::context ctx;
uvgrtp::session* sess = ctx.create_session(LOCAL_ADDRESS, LOCAL_ADDRESS);
int flags = RCE_RECEIVE_ONLY;
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;
@ -74,6 +74,7 @@ int main(void)
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);

View File

@ -14,8 +14,8 @@ 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\\test_seq3.vpcc";
int main(void)
{
@ -23,11 +23,14 @@ int main(void)
/* A V3C Sample stream is divided into 6 types of 'sub-bitstreams' + parameters.
- The nal_map holds nal_info structs
- nal_info struct holds the format(V3C, H264, H265, H266), start position and size of the NAL unit
- 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_V3C for
Atlas NAL units and a second one for the video NAL units in the correct format*/
Atlas NAL units and a second one for the video NAL units in the correct format
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 = {};
/* Fetch the file and its size */
uint64_t len = get_size(PATH);
@ -36,8 +39,7 @@ 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 */
vuh_vps parameters = {};
mmap_v3c_file(cbuf, len, parameters, nal_map);
mmap_v3c_file(cbuf, len, nal_map, vps_map);
std::cout << "Sending Atlas NAL units via uvgRTP" << std::endl;
@ -49,19 +51,19 @@ int main(void)
rtp_format_t video_format = RTP_FORMAT_GENERIC;
// Create the uvgRTP media stream with the correct RTP format
if (parameters.ptl.ptl_profile_codec_group_idc == CODEC_AVC) {
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 (parameters.ptl.ptl_profile_codec_group_idc == CODEC_HEVC_MAIN10) {
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 (parameters.ptl.ptl_profile_codec_group_idc == CODEC_HEVC444) {
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 (parameters.ptl.ptl_profile_codec_group_idc == CODEC_VVC_MAIN10) {
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;
}

View File

@ -27,6 +27,14 @@ constexpr uint8_t VVC_SC = 0x00000001; // VVC Start Code
struct profile_tier_level {
uint8_t ptl_tier_flag = 0;
uint8_t ptl_profile_codec_group_idc = 0;
uint8_t ptl_profile_toolset_idc = 0;
uint8_t ptl_profile_reconstruction_idc = 0;
uint8_t ptl_max_decodes_idc = 0;
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;
bool ptl_toolset_constraints_present_flag = 0;
};
struct vuh_vps {
profile_tier_level ptl;
@ -72,6 +80,10 @@ struct v3c_unit_header {
vuh_pvd pvd;
vuh_cad cad;
};
~v3c_unit_header() {
vps.~vuh_vps();
}
};
struct nal_info {
@ -88,5 +100,5 @@ 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, vuh_vps& param, std::vector<nal_info>& nal_map);
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);

View File

@ -18,7 +18,7 @@ uint32_t combineBytes(uint8_t byte1, uint8_t byte2) {
(static_cast<uint32_t>(byte2));
}
bool mmap_v3c_file(char* cbuf, uint64_t len, vuh_vps& param, std::vector<nal_info>& nal_map)
bool mmap_v3c_file(char* cbuf, uint64_t len, std::vector<nal_info>& nal_map, std::vector<vuh_vps>& vps_map)
{
uint64_t ptr = 0;
@ -32,7 +32,6 @@ bool mmap_v3c_file(char* cbuf, uint64_t len, vuh_vps& param, std::vector<nal_inf
uint8_t* v3c_size = new uint8_t[v3c_size_precision];
std::vector<uint32_t> sizes = {};
uint8_t nal_size_precision = 0;
while (true) {
if (ptr >= len) {
@ -66,18 +65,12 @@ bool mmap_v3c_file(char* cbuf, uint64_t len, vuh_vps& param, std::vector<nal_inf
// Parameter set contains no NAL units, skip over
std::cout << "-- Parameter set V3C unit" << std::endl;
ptr += combined_v3c_size;
sizes.push_back(combined_v3c_size);
param = v3c_hdr.vps;
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) {
// Video data, map start and size
std::cout << "-- Video data V3C unit, " << std::endl;
//vd.push_back({ ptr + V3C_HDR_LEN, combined_v3c_size });
//ptr += combined_v3c_size;
//std::cout << std::endl;
//continue;
}
// Rest of the function goes inside the V3C unit payload and parses NAL it into NAL units
@ -131,7 +124,6 @@ bool mmap_v3c_file(char* cbuf, uint64_t len, vuh_vps& param, std::vector<nal_inf
std::cout << std::endl;
ptr += combined_v3c_size;
sizes.push_back(combined_v3c_size);
}
std::cout << "File parsed" << std::endl;
return true;
@ -163,7 +155,7 @@ void parse_v3c_header(v3c_unit_header &hdr, char* buf, uint64_t ptr)
}
switch (hdr.vuh_unit_type) {
case V3C_VPS:
case V3C_VPS: {
hdr.vps = {};
// first bit of first byte
hdr.vps.ptl.ptl_tier_flag = buf[ptr + 4] >> 7;
@ -171,8 +163,51 @@ void parse_v3c_header(v3c_unit_header &hdr, char* buf, uint64_t ptr)
// 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:
hdr.ad = {};
hdr.ad.vuh_v3c_parameter_set_id = vuh_v3c_parameter_set_id;