#include "mpeg4-vvc.h" #include "mpeg4-bits.h" #include #include #include #include #define H266_OPI 12 #define H266_DCI 13 #define H266_VPS 14 #define H266_SPS 15 #define H266_PPS 16 #define H266_AUD 20 #define H266_PREFIX_SEI 23 #define H266_SUFFIX_SEI 24 /* * ISO/IEC 14496-15:2021 11.2.4.2.2 Syntax (p156) aligned(8) class VvcPTLRecord(num_sublayers) { bit(2) reserved = 0; unsigned int(6) num_bytes_constraint_info; unsigned int(7) general_profile_idc; unsigned int(1) general_tier_flag; unsigned int(8) general_level_idc; unsigned int(1) ptl_frame_only_constraint_flag; unsigned int(1) ptl_multi_layer_enabled_flag; unsigned int(8*num_bytes_constraint_info - 2) general_constraint_info; for (i=num_sublayers - 2; i >= 0; i--) unsigned int(1) ptl_sublayer_level_present_flag[i]; for (j=num_sublayers; j<=8 && num_sublayers > 1; j++) bit(1) ptl_reserved_zero_bit = 0; for (i=num_sublayers-2; i >= 0; i--) if (ptl_sublayer_level_present_flag[i]) unsigned int(8) sublayer_level_idc[i]; unsigned int(8) ptl_num_sub_profiles; for (j=0; j < ptl_num_sub_profiles; j++) unsigned int(32) general_sub_profile_idc[j]; } */ static int mpeg4_vvc_ptl_record_load(struct mpeg4_bits_t* bits, struct mpeg4_vvc_t* vvc) { int i; mpeg4_bits_read_n(bits, 2); // reserved vvc->native_ptl.num_bytes_constraint_info = mpeg4_bits_read_uint32(bits, 6); vvc->native_ptl.general_profile_idc = mpeg4_bits_read_uint32(bits, 7); vvc->native_ptl.general_tier_flag = mpeg4_bits_read_uint32(bits, 1); vvc->native_ptl.general_level_idc = mpeg4_bits_read_uint32(bits, 8); for (i = 0; i < (int)vvc->native_ptl.num_bytes_constraint_info && i < sizeof(vvc->native_ptl.general_constraint_info)/sizeof(vvc->native_ptl.general_constraint_info[0]); i++) { vvc->native_ptl.general_constraint_info[i] = mpeg4_bits_read_uint8(bits, 8); } vvc->native_ptl.ptl_frame_only_constraint_flag = (vvc->native_ptl.general_constraint_info[0] & 0x80) ? 1 : 0; vvc->native_ptl.ptl_multi_layer_enabled_flag = (vvc->native_ptl.general_constraint_info[0] & 0x40) ? 1 : 0; vvc->native_ptl.ptl_sublayer_level_present_flag = 0; assert(vvc->num_sublayers >= 0 && vvc->num_sublayers <= 8); for (i = (int)vvc->num_sublayers - 2; i >= 0; i-=8) vvc->native_ptl.ptl_sublayer_level_present_flag = mpeg4_bits_read_uint8(bits, 8); for (i = (int)vvc->num_sublayers - 2; i >= 0 && i < sizeof(vvc->native_ptl.sublayer_level_idc)/sizeof(vvc->native_ptl.sublayer_level_idc[0]); i--) { if(vvc->native_ptl.ptl_sublayer_level_present_flag & (1 << i)) vvc->native_ptl.sublayer_level_idc[i] = mpeg4_bits_read_uint8(bits, 8); } vvc->native_ptl.ptl_num_sub_profiles = mpeg4_bits_read_uint8(bits, 8); vvc->native_ptl.general_sub_profile_idc = (uint32_t*)(vvc->data + vvc->off); vvc->off += 4 * vvc->native_ptl.ptl_num_sub_profiles; for (i = 0; i < vvc->native_ptl.ptl_num_sub_profiles; i++) { vvc->native_ptl.general_sub_profile_idc[i] = mpeg4_bits_read_uint32(bits, 32); } return mpeg4_bits_error(bits); } static int mpeg4_vvc_ptl_record_save(struct mpeg4_bits_t* bits, const struct mpeg4_vvc_t* vvc) { int i; mpeg4_bits_write_n(bits, 0, 2); // reserved mpeg4_bits_write_n(bits, vvc->native_ptl.num_bytes_constraint_info, 6); mpeg4_bits_write_n(bits, vvc->native_ptl.general_profile_idc, 7); mpeg4_bits_write_n(bits, vvc->native_ptl.general_tier_flag, 1); mpeg4_bits_write_n(bits, vvc->native_ptl.general_level_idc, 8); mpeg4_bits_write_n(bits, vvc->native_ptl.ptl_frame_only_constraint_flag, 1); mpeg4_bits_write_n(bits, vvc->native_ptl.ptl_multi_layer_enabled_flag, 1); for (i = 0; i < (int)vvc->native_ptl.num_bytes_constraint_info; i++) { mpeg4_bits_write_n(bits, vvc->native_ptl.general_constraint_info[i], i + 1 < (int)vvc->native_ptl.num_bytes_constraint_info ? 8 : 6); } assert(vvc->num_sublayers >= 0 && vvc->num_sublayers <= 8); for (i = (int)vvc->num_sublayers - 2; i >= 0; i -= 8) mpeg4_bits_write_n(bits, vvc->native_ptl.ptl_sublayer_level_present_flag, 8); for (i = (int)vvc->num_sublayers - 2; i >= 0 && i < sizeof(vvc->native_ptl.sublayer_level_idc) / sizeof(vvc->native_ptl.sublayer_level_idc[0]); i--) { if (vvc->native_ptl.ptl_sublayer_level_present_flag & (1 << i)) mpeg4_bits_write_uint8(bits, vvc->native_ptl.sublayer_level_idc[i], 8); } mpeg4_bits_write_uint8(bits, vvc->native_ptl.ptl_num_sub_profiles, 8); for (i = 0; i < vvc->native_ptl.ptl_num_sub_profiles; i++) { mpeg4_bits_write_uint32(bits, vvc->native_ptl.general_sub_profile_idc[i], 32); } return mpeg4_bits_error(bits); } /* * ISO/IEC 14496-15:2021 11.2.4.2.2 Syntax (p156) aligned(8) class VvcDecoderConfigurationRecord { bit(5) reserved = '11111'b; unsigned int(2) LengthSizeMinusOne; unsigned int(1) ptl_present_flag; if (ptl_present_flag) { unsigned int(9) ols_idx; unsigned int(3) num_sublayers; unsigned int(2) constant_frame_rate; unsigned int(2) chroma_format_idc; unsigned int(3) bit_depth_minus8; bit(5) reserved = '11111'b; VvcPTLRecord(num_sublayers) native_ptl; unsigned_int(16) max_picture_width; unsigned_int(16) max_picture_height; unsigned int(16) avg_frame_rate; } unsigned int(8) num_of_arrays; for (j=0; j < num_of_arrays; j++) { unsigned int(1) array_completeness; bit(2) reserved = 0; unsigned int(5) NAL_unit_type; if (NAL_unit_type != DCI_NUT && NAL_unit_type != OPI_NUT) unsigned int(16) num_nalus; for (i=0; i< num_nalus; i++) { unsigned int(16) nal_unit_length; bit(8*nal_unit_length) nal_unit; } } } */ int mpeg4_vvc_decoder_configuration_record_load(const uint8_t* data, size_t bytes, struct mpeg4_vvc_t* vvc) { struct mpeg4_bits_t bits; uint8_t nalutype; uint16_t i, j, k, n, numOfArrays; uint8_t* dst; vvc->off = 0; // clear mpeg4_bits_init(&bits, (void*)data, bytes); mpeg4_bits_read_n(&bits, 5); // reserved '11111'b vvc->lengthSizeMinusOne = mpeg4_bits_read_uint32(&bits, 2); vvc->ptl_present_flag = mpeg4_bits_read(&bits); if (vvc->ptl_present_flag) { vvc->ols_idx = mpeg4_bits_read_uint32(&bits, 9); vvc->num_sublayers = mpeg4_bits_read_uint32(&bits, 3); vvc->constant_frame_rate = mpeg4_bits_read_uint32(&bits, 2); vvc->chroma_format_idc = mpeg4_bits_read_uint32(&bits, 2); vvc->bit_depth_minus8 = mpeg4_bits_read_uint32(&bits, 3); mpeg4_bits_read_n(&bits, 5); // reserved '11111'b mpeg4_vvc_ptl_record_load(&bits, vvc); vvc->max_picture_width = mpeg4_bits_read_uint16(&bits, 16); vvc->max_picture_height = mpeg4_bits_read_uint16(&bits, 16); vvc->avg_frame_rate = mpeg4_bits_read_uint16(&bits, 16); } if (0 != mpeg4_bits_error(&bits)) { assert(0); return -1; } assert(0 == bits.bits % 8); dst = vvc->data + vvc->off; numOfArrays = mpeg4_bits_read_uint8(&bits, 8); for (i = 0; i < numOfArrays && 0 == mpeg4_bits_error(&bits); i++) { nalutype = mpeg4_bits_read_uint8(&bits, 8); n = 1; if ((nalutype & 0x1f) != H266_DCI && (nalutype & 0x1f) != H266_OPI) n = mpeg4_bits_read_uint16(&bits, 16); for (j = 0; j < n; j++) { if (vvc->numOfArrays >= sizeof(vvc->nalu) / sizeof(vvc->nalu[0])) { assert(0); return -E2BIG; // too many nalu(s) } k = mpeg4_bits_read_uint16(&bits, 16); vvc->nalu[vvc->numOfArrays].array_completeness = (nalutype >> 7) & 0x01; vvc->nalu[vvc->numOfArrays].type = nalutype & 0x1F; vvc->nalu[vvc->numOfArrays].bytes = k; vvc->nalu[vvc->numOfArrays].data = dst; memcpy(vvc->nalu[vvc->numOfArrays].data, data + bits.bits / 8, k); vvc->numOfArrays++; mpeg4_bits_skip(&bits, (uint64_t)k * 8); dst += k; } } vvc->off = (int)(dst - vvc->data); return mpeg4_bits_error(&bits) ? -1 : (int)(bits.bits / 8); } int mpeg4_vvc_decoder_configuration_record_save(const struct mpeg4_vvc_t* vvc, uint8_t* data, size_t bytes) { uint16_t n; uint8_t i, j, k; uint8_t* ptr, * end; uint8_t* p; uint8_t array_completeness = 1; struct mpeg4_bits_t bits; const uint8_t nalu[] = { H266_OPI, H266_DCI, H266_VPS, H266_SPS, H266_PPS, H266_PREFIX_SEI, H266_SUFFIX_SEI }; assert(vvc->lengthSizeMinusOne <= 3); memset(data, 0, bytes); mpeg4_bits_init(&bits, (void*)data, bytes); mpeg4_bits_write_n(&bits, 0x1F, 5); mpeg4_bits_write_n(&bits, vvc->lengthSizeMinusOne, 2); mpeg4_bits_write_n(&bits, vvc->ptl_present_flag, 1); if (vvc->ptl_present_flag) { mpeg4_bits_write_n(&bits, vvc->ols_idx, 9); mpeg4_bits_write_n(&bits, vvc->num_sublayers, 3); mpeg4_bits_write_n(&bits, vvc->constant_frame_rate, 2); mpeg4_bits_write_n(&bits, vvc->chroma_format_idc, 2); mpeg4_bits_write_n(&bits, vvc->bit_depth_minus8, 3); mpeg4_bits_write_n(&bits, 0x1F, 5); mpeg4_vvc_ptl_record_save(&bits, vvc); mpeg4_bits_write_uint16(&bits, vvc->max_picture_width, 16); mpeg4_bits_write_uint16(&bits, vvc->max_picture_height, 16); mpeg4_bits_write_uint16(&bits, vvc->avg_frame_rate, 16); } if (0 != mpeg4_bits_error(&bits) || 0 != bits.bits % 8) { assert(0); return -1; } //mpeg4_bits_write_uint8(&bits, vvc->numOfArrays, 8); p = data + bits.bits / 8 + 1 /*num_of_arrays*/; end = data + bytes; for (k = i = 0; i < sizeof(nalu) / sizeof(nalu[0]) && p + 5 <= end; i++) { ptr = p + 3; for (n = j = 0; j < vvc->numOfArrays; j++) { if (nalu[i] != vvc->nalu[j].type) continue; if (ptr + 2 + vvc->nalu[j].bytes > end) return 0; // don't have enough memory array_completeness = vvc->nalu[j].array_completeness; assert(vvc->nalu[i].data + vvc->nalu[j].bytes <= vvc->data + sizeof(vvc->data)); ptr[0] = (vvc->nalu[j].bytes >> 8) & 0xFF; ptr[1] = vvc->nalu[j].bytes & 0xFF; memcpy(ptr + 2, vvc->nalu[j].data, vvc->nalu[j].bytes); ptr += 2 + vvc->nalu[j].bytes; n++; } if (n > 0) { // array_completeness + NAL_unit_type p[0] = (array_completeness << 7) | (nalu[i] & 0x1F); p[1] = (n >> 8) & 0xFF; p[2] = n & 0xFF; p = ptr; k++; } } data[bits.bits / 8] = k; // num_of_arrays return mpeg4_bits_error(&bits) ? -1 : (int)(p - data); } int mpeg4_vvc_to_nalu(const struct mpeg4_vvc_t* vvc, uint8_t* data, size_t bytes) { uint8_t i; uint8_t* p, * end; const uint8_t startcode[] = { 0, 0, 0, 1 }; p = data; end = p + bytes; for (i = 0; i < vvc->numOfArrays; i++) { if (p + vvc->nalu[i].bytes + 4 > end || vvc->nalu[i].bytes < 2) return -1; memcpy(p, startcode, 4); memcpy(p + 4, vvc->nalu[i].data, vvc->nalu[i].bytes); assert(vvc->nalu[i].type == ((vvc->nalu[i].data[1] >> 3) & 0x1F)); p += 4 + vvc->nalu[i].bytes; } return (int)(p - data); } // RFC4648 static size_t base32_encode(char* target, const void* source, size_t bytes) { size_t i, j; const uint8_t* ptr = (const uint8_t*)source; static const char* s_base32_enc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; for (j = i = 0; i < bytes / 5 * 5; i += 5) { target[j++] = s_base32_enc[(ptr[i] >> 3) & 0x1F]; /* c1 */ target[j++] = s_base32_enc[((ptr[i] & 0x07) << 2) | ((ptr[i + 1] >> 6) & 0x03)]; /*c2*/ target[j++] = s_base32_enc[(ptr[i + 1] >> 1) & 0x1F];/*c3*/ target[j++] = s_base32_enc[((ptr[i + 1] & 0x01) << 4) | ((ptr[i + 2] >> 4) & 0x0F)]; /*c4*/ target[j++] = s_base32_enc[((ptr[i + 2] & 0x0F) << 1) | ((ptr[i + 3] >> 7) & 0x01)]; /*c5*/ target[j++] = s_base32_enc[(ptr[i + 3] >> 2) & 0x1F];/*c6*/ target[j++] = s_base32_enc[((ptr[i + 3] & 0x03) << 3) | ((ptr[i + 4] >> 5) & 0x07)]; /*c7*/ target[j++] = s_base32_enc[ptr[i + 4] & 0x1F]; /* c8 */ } if (i + 1 == bytes) { target[j++] = s_base32_enc[(ptr[i] >> 3) & 0x1F]; /* c1 */ target[j++] = s_base32_enc[((ptr[i] & 0x07) << 2)]; /*c2*/ } else if (i + 2 == bytes) { target[j++] = s_base32_enc[(ptr[i] >> 3) & 0x1F]; /* c1 */ target[j++] = s_base32_enc[((ptr[i] & 0x07) << 2) | ((ptr[i + 1] >> 6) & 0x03)]; /*c2*/ target[j++] = s_base32_enc[(ptr[i + 1] >> 1) & 0x1F];/*c3*/ target[j++] = s_base32_enc[((ptr[i + 1] & 0x01) << 4)]; /*c4*/ } else if (i + 3 == bytes) { target[j++] = s_base32_enc[(ptr[i] >> 3) & 0x1F]; /* c1 */ target[j++] = s_base32_enc[((ptr[i] & 0x07) << 2) | ((ptr[i + 1] >> 6) & 0x03)]; /*c2*/ target[j++] = s_base32_enc[(ptr[i + 1] >> 1) & 0x1F];/*c3*/ target[j++] = s_base32_enc[((ptr[i + 1] & 0x01) << 4) | ((ptr[i + 2] >> 4) & 0x0F)]; /*c4*/ target[j++] = s_base32_enc[((ptr[i + 2] & 0x0F) << 1)]; /*c5*/ } else if (i + 4 == bytes) { target[j++] = s_base32_enc[(ptr[i] >> 3) & 0x1F]; /* c1 */ target[j++] = s_base32_enc[((ptr[i] & 0x07) << 2) | ((ptr[i + 1] >> 6) & 0x03)]; /*c2*/ target[j++] = s_base32_enc[(ptr[i + 1] >> 1) & 0x1F];/*c3*/ target[j++] = s_base32_enc[((ptr[i + 1] & 0x01) << 4) | ((ptr[i + 2] >> 4) & 0x0F)]; /*c4*/ target[j++] = s_base32_enc[((ptr[i + 2] & 0x0F) << 1) | ((ptr[i + 3] >> 7) & 0x01)]; /*c5*/ target[j++] = s_base32_enc[(ptr[i + 3] >> 2) & 0x1F];/*c6*/ target[j++] = s_base32_enc[((ptr[i + 3] & 0x03) << 3)]; /*c7*/ } while (0 != (j % 8)) { target[j++] = '='; } return j; } int mpeg4_vvc_codecs(const struct mpeg4_vvc_t* vvc, char* codecs, size_t bytes) { // ISO/IEC 14496-15:2021 // Annex E Sub-parameters of the MIME type "codecs" parameter (p276) // 'vvc1.' or 'vvi1.' prefix (5 chars) int i, n; char buffer[129]; // 1. trailing zero bits of the general_constraint_info() syntax structure may be omitted from the input bits to base32 encoding n = (int)vvc->native_ptl.num_bytes_constraint_info; for (i = (int)vvc->native_ptl.num_bytes_constraint_info - 1; i >= 0 && n > 1; i--) { if (0 == vvc->native_ptl.general_constraint_info[i]) n--; else break; } i = base32_encode(buffer, vvc->native_ptl.general_constraint_info, n); //2, the trailing padding with the "=" character may be omitted from the base32 string; while (i > 0 && buffer[i - 1] == '=') i--; return snprintf(codecs, bytes, "vvc1.%u.%c%u.C%.*s", (unsigned int)vvc->native_ptl.general_profile_idc, vvc->native_ptl.general_tier_flag ? 'H' : 'L', vvc->native_ptl.general_level_idc, i, buffer); } #if defined(_DEBUG) || defined(DEBUG) void vvc_annexbtomp4_test(void); static void mpeg4_vvc_codecs_test(struct mpeg4_vvc_t* vvc) { int r; char buffer[129]; //const char* s = "vvc1.1.L51.CQA.O1+3"; //const char* s1 = "vvc1.17.L83.CYA.O1+3"; //const char* s2 = "vvc1.17.L83.CYA.O1+3"; const char* s3 = "vvc1.1.L105.CAA"; r = mpeg4_vvc_codecs(vvc, buffer, sizeof(buffer)); assert(r == strlen(s3) && 0 == memcmp(buffer, s3, r)); } void mpeg4_vvc_test(void) { const uint8_t data[] = { 0xff, 0x00, 0x11, 0x1f, 0x01, 0x02, 0x69, 0x00, 0x00, 0x02, 0xd0, 0x05, 0x00, 0x00, 0x00, 0x02, 0x8f, 0x00, 0x01, 0x00, 0x2a, 0x00, 0x79, 0x00, 0x0b, 0x02, 0x69, 0x00, 0x00, 0x03, 0x00, 0x16, 0x88, 0x01, 0x40, 0x48, 0x80, 0x2b, 0x49, 0xff, 0x45, 0x19, 0x18, 0xe0, 0x0c, 0x42, 0x55, 0x5a, 0xab, 0xd5, 0xeb, 0x33, 0x25, 0x5a, 0x12, 0xe4, 0x72, 0xd4, 0x56, 0x5a, 0x32, 0x30, 0x40, 0x90, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x81, 0x00, 0x00, 0x0b, 0x44, 0x00, 0xa0, 0x22, 0x24, 0x18, 0x20 }; uint8_t buffer[sizeof(data)]; struct mpeg4_vvc_t vvc; memset(&vvc, 0, sizeof(vvc)); assert(sizeof(data) == mpeg4_vvc_decoder_configuration_record_load(data, sizeof(data), &vvc)); assert(3 == vvc.lengthSizeMinusOne && 1 == vvc.ptl_present_flag && 1 == vvc.num_sublayers); assert(1 == vvc.chroma_format_idc && 0 == vvc.bit_depth_minus8); assert(720 == vvc.max_picture_width && 1280 == vvc.max_picture_height && 0 == vvc.avg_frame_rate); assert(1 == vvc.native_ptl.num_bytes_constraint_info && 1 == vvc.native_ptl.general_profile_idc && 0 == vvc.native_ptl.general_tier_flag && 0x69 == vvc.native_ptl.general_level_idc); assert(2 == vvc.numOfArrays && H266_SPS == vvc.nalu[0].type && 0x2a == vvc.nalu[0].bytes && H266_PPS == vvc.nalu[1].type && 0x0c == vvc.nalu[1].bytes); assert(sizeof(data) == mpeg4_vvc_decoder_configuration_record_save(&vvc, buffer, sizeof(buffer)) && 0 == memcmp(buffer, data, sizeof(data))); mpeg4_vvc_codecs_test(&vvc); } #endif