diff --git a/h264.c b/h264.c index 0ed474d..44f75c4 100644 --- a/h264.c +++ b/h264.c @@ -203,21 +203,19 @@ int cFemonH264::nalUnescape(uint8_t *dstP, const uint8_t *srcP, int lenP) { int s = 0, d = 0; - while (s < lenP) { - if (!srcP[s] && !srcP[s + 1]) { - // hit 00 00 xx - dstP[d] = dstP[d + 1] = 0; - s += 2; - d += 2; - if (srcP[s] == 3) { - s++; // 00 00 03 xx --> 00 00 xx - if (s >= lenP) - return d; - } + while (s < lenP - 3) { + if (!srcP[s] && !srcP[s + 1] && srcP[s + 2] == 3) { + dstP[d++] = srcP[s++]; + dstP[d++] = srcP[s++]; + s++; // skip emulation_prevention_three_byte } - dstP[d++] = srcP[s++]; + else + dstP[d++] = srcP[s++]; } + while (s < lenP) + dstP[d++] = srcP[s++]; + return d; } @@ -553,7 +551,7 @@ int cFemonH264::parseSPS(const uint8_t *bufP, int lenP) if (bs.GetBit()) { // video_signal_type_present_flag uint32_t video_format; video_format = bs.GetBits(3); // video_format - if (video_format < sizeof(videoFormatS) / sizeof(videoFormatS[0])) { + if (video_format < ELEMENTS(videoFormatS)) { format = videoFormatS[video_format]; debug2("%s video_format=%d", __PRETTY_FUNCTION__, format); } @@ -646,7 +644,7 @@ int cFemonH264::parseSEI(const uint8_t *bufP, int lenP) eVideoScan scan = scanM; - while ((bs.Index() * 8 + 16) < lenP) { // sei_message + while ((bs.Index() * 8 + 16) < lenP) { // sei_message int lastByte, payloadSize = 0, payloadType = 0; do { @@ -661,14 +659,14 @@ int cFemonH264::parseSEI(const uint8_t *bufP, int lenP) switch (payloadType) { // sei_payload case 1: // pic_timing - if (cpbDpbDelaysPresentFlagM) { // cpb_dpb_delays_present_flag + if (cpbDpbDelaysPresentFlagM) { // cpb_dpb_delays_present_flag bs.SkipUeGolomb(); // cpb_removal_delay bs.SkipUeGolomb(); // dpb_output_delay } - if (picStructPresentFlagM) { // pic_struct_present_flag + if (picStructPresentFlagM) { // pic_struct_present_flag uint32_t pic_struct, ct_type = 0, i = 0; pic_struct = bs.GetBits(4); // pic_struct - if (pic_struct >= (sizeof(seiNumClockTsTableS)) / sizeof(seiNumClockTsTableS[0])) + if (pic_struct >= ELEMENTS(seiNumClockTsTableS)) return 0; if (frameMbsOnlyFlagM && !mbAdaptiveFrameFieldFlagM) scan = VIDEO_SCAN_PROGRESSIVE; @@ -720,7 +718,7 @@ int cFemonH264::parseSEI(const uint8_t *bufP, int lenP) } } if (timeOffsetLengthM > 0) - bs.SkipBits(timeOffsetLengthM); // time_offset + bs.SkipBits(timeOffsetLengthM); // time_offset } } if (i > 0) diff --git a/h264.h b/h264.h index 3e7589f..b7a8570 100644 --- a/h264.h +++ b/h264.h @@ -49,10 +49,10 @@ private: int parseSPS(const uint8_t *buf, int len); int parseSEI(const uint8_t *buf, int len); - static const t_SAR sarS[]; - static const t_DAR darS[]; - static const eVideoFormat videoFormatS[]; - static const uint8_t seiNumClockTsTableS[9]; + static const t_SAR sarS[]; + static const t_DAR darS[]; + static const eVideoFormat videoFormatS[]; + static const uint8_t seiNumClockTsTableS[9]; public: cFemonH264(cFemonVideoIf *videoHandlerP); diff --git a/h265.c b/h265.c index 4e1dd06..9b67759 100644 --- a/h265.c +++ b/h265.c @@ -5,10 +5,67 @@ * */ +#include + #include "log.h" #include "tools.h" #include "h265.h" +const cFemonH265::t_DAR cFemonH265::darS[] = +{ + { VIDEO_ASPECT_RATIO_1_1, 100 }, + { VIDEO_ASPECT_RATIO_4_3, 133 }, + { VIDEO_ASPECT_RATIO_16_9, 177 }, + { VIDEO_ASPECT_RATIO_2_21_1, 221 }, + { VIDEO_ASPECT_RATIO_12_11, 109 }, + { VIDEO_ASPECT_RATIO_10_11, 90 }, + { VIDEO_ASPECT_RATIO_16_11, 145 }, + { VIDEO_ASPECT_RATIO_40_33, 121 }, + { VIDEO_ASPECT_RATIO_24_11, 218 }, + { VIDEO_ASPECT_RATIO_20_11, 181 }, + { VIDEO_ASPECT_RATIO_32_11, 290 }, + { VIDEO_ASPECT_RATIO_80_33, 242 }, + { VIDEO_ASPECT_RATIO_18_11, 163 }, + { VIDEO_ASPECT_RATIO_15_11, 136 }, + { VIDEO_ASPECT_RATIO_64_33, 193 }, + { VIDEO_ASPECT_RATIO_160_99, 161 }, + { VIDEO_ASPECT_RATIO_3_2, 150 }, + { VIDEO_ASPECT_RATIO_2_1, 200 } +}; + +const cFemonH265::t_SAR cFemonH265::sarS[] = +{ + { 0, 0 }, // VIDEO_ASPECT_RATIO_INVALID + { 1, 1 }, // VIDEO_ASPECT_RATIO_1_1 + { 12, 11 }, // VIDEO_ASPECT_RATIO_12_11 + { 10, 11 }, // VIDEO_ASPECT_RATIO_10_11 + { 16, 11 }, // VIDEO_ASPECT_RATIO_16_11 + { 40, 33 }, // VIDEO_ASPECT_RATIO_40_33 + { 24, 11 }, // VIDEO_ASPECT_RATIO_24_11 + { 20, 11 }, // VIDEO_ASPECT_RATIO_20_11 + { 32, 11 }, // VIDEO_ASPECT_RATIO_32_11 + { 80, 33 }, // VIDEO_ASPECT_RATIO_80_33 + { 18, 11 }, // VIDEO_ASPECT_RATIO_18_11 + { 15, 11 }, // VIDEO_ASPECT_RATIO_15_11 + { 64, 33 }, // VIDEO_ASPECT_RATIO_64_33 + { 160, 99 }, // VIDEO_ASPECT_RATIO_160_99 + { 4, 3 }, // VIDEO_ASPECT_RATIO_4_3 + { 3, 2 }, // VIDEO_ASPECT_RATIO_3_2 + { 2, 1 } // VIDEO_ASPECT_RATIO_2_1 +}; + +const eVideoFormat cFemonH265::videoFormatS[] = +{ + VIDEO_FORMAT_COMPONENT, + VIDEO_FORMAT_PAL, + VIDEO_FORMAT_NTSC, + VIDEO_FORMAT_SECAM, + VIDEO_FORMAT_MAC, + VIDEO_FORMAT_UNKNOWN, + VIDEO_FORMAT_RESERVED, + VIDEO_FORMAT_RESERVED +}; + cFemonH265::cFemonH265(cFemonVideoIf *videoHandlerP) : videoHandlerM(videoHandlerP), widthM(0), @@ -17,7 +74,8 @@ cFemonH265::cFemonH265(cFemonVideoIf *videoHandlerP) formatM(VIDEO_FORMAT_INVALID), frameRateM(0), bitRateM(0), - scanM(VIDEO_SCAN_PROGRESSIVE) + scanM(VIDEO_SCAN_INVALID), + frameFieldInfoPresentFlagM(false) { reset(); } @@ -29,7 +87,7 @@ cFemonH265::~cFemonH265() bool cFemonH265::processVideo(const uint8_t *bufP, int lenP) { uint8_t nal_data[lenP]; - bool aud_found = false, sps_found = false; + bool aud_found = false, sps_found = false, sei_found = true; // SEI temporarily disabled! const uint8_t *buf = bufP; const uint8_t *start = buf; const uint8_t *end = start + lenP; @@ -63,18 +121,28 @@ bool cFemonH265::processVideo(const uint8_t *bufP, int lenP) case NAL_SPS: if (!sps_found) { debug2("%s Found NAL SPS at offset %d/%d", __PRETTY_FUNCTION__, int(buf - start), lenP); - int nal_len = nalUnescape(nal_data, buf + 4, int(end - buf - 4)); + int nal_len = nalUnescape(nal_data, buf + 5, int(end - buf - 5)); consumed = parseSPS(nal_data, nal_len); if (consumed > 0) sps_found = true; } break; + case NAL_SEI: + if (!sei_found) { + debug2("%s Found NAL SEI at offset %d/%d", __PRETTY_FUNCTION__, int(buf - start), lenP); + int nal_len = nalUnescape(nal_data, buf + 5, int(end - buf - 5)); + consumed = parseSEI(nal_data, nal_len); + if (consumed > 0) + sei_found = true; + } + break; + default: break; } - if (aud_found && sps_found) + if (aud_found && sps_found && sei_found) break; buf += consumed + 4; @@ -83,13 +151,16 @@ bool cFemonH265::processVideo(const uint8_t *bufP, int lenP) if (aud_found) { videoHandlerM->SetVideoCodec(VIDEO_CODEC_H265); if (sps_found) { - debug2("%s width=%d height=%d, aspect=%d format=%d bitrate=%.0f scan=%d framerate=%.2f", __PRETTY_FUNCTION__, widthM, heightM, aspectRatioM, formatM, bitRateM, scanM, frameRateM); + debug2("%s width=%d height=%d, aspect=%d format=%d bitrate=%.0f", __PRETTY_FUNCTION__, widthM, heightM, aspectRatioM, formatM, bitRateM); videoHandlerM->SetVideoSize(widthM, heightM); + videoHandlerM->SetVideoFormat(formatM); + videoHandlerM->SetVideoAspectRatio(aspectRatioM); + videoHandlerM->SetVideoBitrate(bitRateM); + } + if (sps_found || sei_found) { + debug2("%s scan=%d framerate=%.2f", __PRETTY_FUNCTION__, scanM, frameRateM); videoHandlerM->SetVideoScan(scanM); - //videoHandlerM->SetVideoFormat(formatM); - //videoHandlerM->SetVideoAspectRatio(aspectRatioM); - //videoHandlerM->SetVideoBitrate(bitRateM); - //videoHandlerM->SetVideoFramerate(frameRateM); + videoHandlerM->SetVideoFramerate(frameRateM); } } @@ -98,6 +169,7 @@ bool cFemonH265::processVideo(const uint8_t *bufP, int lenP) void cFemonH265::reset() { + frameFieldInfoPresentFlagM = false; } const uint8_t *cFemonH265::nextStartCode(const uint8_t *startP, const uint8_t *endP) @@ -113,21 +185,19 @@ int cFemonH265::nalUnescape(uint8_t *dstP, const uint8_t *srcP, int lenP) { int s = 0, d = 0; - while (s < lenP) { - if (!srcP[s] && !srcP[s + 1]) { - // hit 00 00 xx - dstP[d] = dstP[d + 1] = 0; - s += 2; - d += 2; - if (srcP[s] == 3) { - s++; // 00 00 03 xx --> 00 00 xx - if (s >= lenP) - return d; - } + while (s < lenP - 3) { + if (!srcP[s] && !srcP[s + 1] && srcP[s + 2] == 3) { + dstP[d++] = srcP[s++]; + dstP[d++] = srcP[s++]; + s++; // skip emulation_prevention_three_byte } - dstP[d++] = srcP[s++]; + else + dstP[d++] = srcP[s++]; } + while (s < lenP) + dstP[d++] = srcP[s++]; + return d; } @@ -137,67 +207,509 @@ int cFemonH265::parseSPS(const uint8_t *bufP, int lenP) uint32_t width = widthM; uint32_t height = heightM; - bool profilePresentFlag = true; - unsigned int chroma_format_idc; - uint8_t sps_max_sub_layers_minus1; - uint8_t sub_layer_profile_present_flag[8]; - uint8_t sub_layer_level_present_flag[8]; + eVideoFormat format = formatM; + eVideoAspectRatio aspect_ratio = aspectRatioM; + eVideoScan scan = scanM; + double frame_rate = frameRateM; + double bit_rate = bitRateM; + bool frame_field_info_present_flag = frameFieldInfoPresentFlagM; - bs.SkipBits(4); // sps_video_parameter_set_id - sps_max_sub_layers_minus1 = bs.GetBits(3); // sps_max_sub_layers_minus1 - bs.SkipBit(); // sps_temporal_id_nesting_flag + const char *profile_name = NULL; + bool general_tier_flag, conformance_window_flag, sps_sub_layer_ordering_info_present_flag, profilePresentFlag = true; + bool general_max_12bit_constraint_flag = false, general_max_10bit_constraint_flag = false, general_max_8bit_constraint_flag = false; + bool general_max_422chroma_constraint_flag = false, general_max_420chroma_constraint_flag = false, general_max_monochrome_constraint_flag = false; + bool general_intra_constraint_flag = false, general_one_picture_only_constraint_flag = false, general_lower_bit_rate_constraint_flag = false; + bool general_progressive_source_flag, general_interlaced_source_flag, general_profile_compatibility_flag[32]; + uint32_t chroma_format_idc, log2_max_pic_order_cnt_lsb_minus4, num_short_term_ref_pic_sets, num_long_term_ref_pics_sps; + uint8_t sps_max_sub_layers_minus1, sub_layer_profile_present_flag[8], sub_layer_level_present_flag[8], general_profile_idc, general_level_idc; + + bs.SkipBits(4); // sps_video_parameter_set_id + sps_max_sub_layers_minus1 = bs.GetBits(3); // sps_max_sub_layers_minus1 + bs.SkipBit(); // sps_temporal_id_nesting_flag // start of profile_tier_level(1, sps_max_sub_layers_minus1) if (profilePresentFlag) { - bs.SkipBits(2); // general_profile_space - bs.SkipBit(); // general_profile_space - bs.SkipBits(5); // general_profile_idc - bs.SkipBits(32); // general_profile_compatibility_flag[0-31] - bs.SkipBit(); // general_progressive_source_flag - bs.SkipBit(); // general_interlaced_source_flag - bs.SkipBit(); // general_non_packed_constraint_flag - bs.SkipBit(); // general_frame_only_constraint_flag + bs.SkipBits(2); // general_profile_space + general_tier_flag = bs.GetBit(); // general_tier_flag + general_profile_idc = bs.GetBits(5); // general_profile_idc + for (int i = 0; i < 32; ++i) { + general_profile_compatibility_flag[i] = bs.GetBit(); // general_profile_compatibility_flag[i] + } + general_progressive_source_flag = bs.GetBit(); // general_progressive_source_flag + general_interlaced_source_flag = bs.GetBit(); // general_interlaced_source_flag + if (general_progressive_source_flag && !general_interlaced_source_flag) + scan = VIDEO_SCAN_PROGRESSIVE; + else if (!general_progressive_source_flag && general_interlaced_source_flag) + scan = VIDEO_SCAN_INTERLACED; + else if (!general_progressive_source_flag && !general_interlaced_source_flag) + scan = VIDEO_SCAN_UNKNOWN; + else + scan = VIDEO_SCAN_INVALID; + debug2("%s general_progressive_source_flag=%d general_interlaced_source_flag=%d scan_type=%d", __PRETTY_FUNCTION__, general_progressive_source_flag, general_interlaced_source_flag, scan); + bs.SkipBit(); // general_non_packed_constraint_flag + bs.SkipBit(); // general_frame_only_constraint_flag + if (general_profile_idc == 4 || general_profile_compatibility_flag[4] || + general_profile_idc == 5 || general_profile_compatibility_flag[5] || + general_profile_idc == 6 || general_profile_compatibility_flag[6] || + general_profile_idc == 7 || general_profile_compatibility_flag[7]) { + // the number of bits in this syntax structure is not affected by this condition + general_max_12bit_constraint_flag = bs.GetBit(); // general_max_12bit_constraint_flag + general_max_10bit_constraint_flag = bs.GetBit(); // general_max_10bit_constraint_flag + general_max_8bit_constraint_flag = bs.GetBit(); // general_max_8bit_constraint_flag + debug2("%s general_max_12bit_constraint_flag=%d general_max_10bit_constraint_flag=%d general_max_8bit_constraint_flag=%d", __PRETTY_FUNCTION__, general_max_12bit_constraint_flag, general_max_10bit_constraint_flag, general_max_8bit_constraint_flag); + general_max_422chroma_constraint_flag = bs.GetBit(); // general_max_422chroma_constraint_flag + general_max_420chroma_constraint_flag = bs.GetBit(); // general_max_420chroma_constraint_flag + general_max_monochrome_constraint_flag = bs.GetBit(); // general_max_monochrome_constraint_flag + debug2("%s general_max_422chroma_constraint_flag=%d general_max_420chroma_constraint_flag=%d general_max_monochrome_constraint_flag=%d", __PRETTY_FUNCTION__, general_max_422chroma_constraint_flag, general_max_420chroma_constraint_flag, general_max_monochrome_constraint_flag); + general_intra_constraint_flag = bs.GetBit(); // general_intra_constraint_flag + general_one_picture_only_constraint_flag = bs.GetBit(); // general_one_picture_only_constraint_flag + general_lower_bit_rate_constraint_flag = bs.GetBit(); // general_lower_bit_rate_constraint_flag + debug2("%s general_intra_constraint_flag=%d general_one_picture_only_constraint_flag=%d general_lower_bit_rate_constraint_flag=%d", __PRETTY_FUNCTION__, general_intra_constraint_flag, general_one_picture_only_constraint_flag, general_lower_bit_rate_constraint_flag); + bs.SkipBits(34); // general_reserved_zero_34bits + } + else + bs.SkipBits(43); // general_reserved_zero_43bits // the number of bits in this syntax structure is not affected by this condition - bs.SkipBits(43); // general_reserved_zero_43bits - // the number of bits in this syntax structure is not affected by this condition - bs.SkipBit(); // general_reserved_zero_bit + bs.SkipBit(); // general_reserved_zero_bit } - bs.SkipBits(8); // general_level_idc + general_level_idc = bs.GetBits(8); // general_level_idc + debug2("%s general_profile_idc=%d general_tier_flag=%d general_level_idc=%d", __PRETTY_FUNCTION__, general_profile_idc, general_tier_flag, general_level_idc); + switch (general_profile_idc) { + default: + case 0: + profile_name = "None"; + break; + case 1: + profile_name = "Main"; + break; + case 2: + profile_name = "Main 10"; + break; + case 3: + profile_name = "Main Still Picture"; + break; + case 4: + profile_name = "Format Range Extensions"; + break; + case 5: + profile_name = "Format Range Extensions High Throughput"; + break; + } + if (general_profile_idc == 1 || general_profile_idc == 2) + switch (general_level_idc) { + case 30: // level 1 + bit_rate = general_tier_flag ? 0 : 128000; + break; + case 60: // level 2 + bit_rate = general_tier_flag ? 0 : 1500000; + break; + case 63: // level 2.1 + bit_rate = general_tier_flag ? 0 : 3000000; + break; + case 90: // level 3 + bit_rate = general_tier_flag ? 0 : 6000000; + break; + case 93: // level 3 + bit_rate = general_tier_flag ? 0 : 10000000; + break; + case 120: // level 4 + bit_rate = general_tier_flag ? 30000000 : 12000000; + break; + case 123: // level 4.1 + bit_rate = general_tier_flag ? 50000000 : 20000000; + break; + case 150: // level 5 + bit_rate = general_tier_flag ? 100000000 : 25000000; + break; + case 153: // level 5.1 + bit_rate = general_tier_flag ? 160000000 : 40000000; + break; + case 156: // level 5.2 + bit_rate = general_tier_flag ? 240000000 : 60000000; + break; + case 180: // level 6 + bit_rate = general_tier_flag ? 240000000 : 60000000; + break; + case 183: // level 6.1 + bit_rate = general_tier_flag ? 480000000 : 120000000; + break; + case 186: // level 6.2 + bit_rate = general_tier_flag ? 800000000 : 240000000; + break; + default: + bit_rate = 0; + break; + } + else + bit_rate = 0; + debug2("%s profile=\"%s@L%.1f@%s\" bit_rate=%.f", __PRETTY_FUNCTION__, profile_name, (double)general_level_idc / 30, general_tier_flag ? "High" : "Main", bit_rate); for (int i = 0; i < sps_max_sub_layers_minus1; ++i) { - sub_layer_profile_present_flag[i] = bs.GetBit(); // sub_layer_profile_present_flag[i] - sub_layer_level_present_flag[i] = bs.GetBit(); // sub_layer_level_present_flag[i] + sub_layer_profile_present_flag[i] = bs.GetBit(); // sub_layer_profile_present_flag[i] + sub_layer_level_present_flag[i] = bs.GetBit(); // sub_layer_level_present_flag[i] } if (sps_max_sub_layers_minus1 > 0) { for (int i = sps_max_sub_layers_minus1; i < 8; ++i) - bs.SkipBits(2); // reserved_zero_2bits[i] + bs.SkipBits(2); // reserved_zero_2bits[i] } for (int i = 0; i < sps_max_sub_layers_minus1; ++i) { if (sub_layer_profile_present_flag[i]) { - bs.SkipBits(2); // sub_layer_profile_space[i] - bs.SkipBit(); // sub_layer_tier_flag[i] - bs.SkipBits(5); // sub_layer_profile_idc[i] - bs.SkipBits(32); // sub_layer_profile_compatibility_flag[i][0-31] - bs.SkipBit(); // sub_layer_progressive_source_flag[i] - bs.SkipBit(); // sub_layer_interlaced_source_flag[i] - bs.SkipBit(); // sub_layer_non_packed_constraint_flag[i] - bs.SkipBit(); // sub_layer_frame_only_constraint_flag[i] + bs.SkipBits(2); // sub_layer_profile_space[i] + bs.SkipBit(); // sub_layer_tier_flag[i] + bs.SkipBits(5); // sub_layer_profile_idc[i] + bs.SkipBits(32); // sub_layer_profile_compatibility_flag[i][0-31] + bs.SkipBit(); // sub_layer_progressive_source_flag[i] + bs.SkipBit(); // sub_layer_interlaced_source_flag[i] + bs.SkipBit(); // sub_layer_non_packed_constraint_flag[i] + bs.SkipBit(); // sub_layer_frame_only_constraint_flag[i] // the number of bits in this syntax structure is not affected by this condition - bs.SkipBits(43); // sub_layer_reserved_zero_43bits[i] + bs.SkipBits(43); // sub_layer_reserved_zero_43bits[i] // the number of bits in this syntax structure is not affected by this condition - bs.SkipBit(); // sub_layer_reserved_zero_bit[i] + bs.SkipBit(); // sub_layer_reserved_zero_bit[i] } if (sub_layer_level_present_flag[i]) - bs.SkipBits(8); // sub_layer_level_idc[i] + bs.SkipBits(8); // sub_layer_level_idc[i] } // end of profile_tier_level - bs.SkipUeGolomb(); // sps_seq_parameter_set_id - chroma_format_idc = bs.GetUeGolomb(); // chroma_format_idc + bs.SkipUeGolomb(); // sps_seq_parameter_set_id + chroma_format_idc = bs.GetUeGolomb(); // chroma_format_idc if (chroma_format_idc == 3) - bs.SkipBit(); // separate_colour_plane_flag - width = bs.GetUeGolomb(); // pic_width_in_luma_samples - height = bs.GetUeGolomb(); // pic_height_in_luma_samples + bs.SkipBit(); // separate_colour_plane_flag + width = bs.GetUeGolomb(); // pic_width_in_luma_samples + height = bs.GetUeGolomb(); // pic_height_in_luma_samples + + conformance_window_flag = bs.GetBit(); // conformance_window_flag + if (conformance_window_flag) { + bs.SkipUeGolomb(); // conf_win_left_offset + bs.SkipUeGolomb(); // conf_win_right_offset + bs.SkipUeGolomb(); // conf_win_top_offset + bs.SkipUeGolomb(); // conf_win_bottom_offset + } + bs.SkipUeGolomb(); // bit_depth_luma_minus8 + bs.SkipUeGolomb(); // bit_depth_chroma_minus8 + log2_max_pic_order_cnt_lsb_minus4 = bs.GetUeGolomb(); // log2_max_pic_order_cnt_lsb_minus4 + sps_sub_layer_ordering_info_present_flag = bs.GetBit(); // sps_sub_layer_ordering_info_present_flag + for (int i = (sps_sub_layer_ordering_info_present_flag ? 0 : sps_max_sub_layers_minus1); i <= sps_max_sub_layers_minus1; ++i) { + bs.SkipUeGolomb(); // sps_max_dec_pic_buffering_minus1[i] + bs.SkipUeGolomb(); // sps_max_num_reorder_pics[i] + bs.SkipUeGolomb(); // sps_max_latency_increase_plus1[i] + } + bs.SkipUeGolomb(); // log2_min_luma_coding_block_size_minus3 + bs.SkipUeGolomb(); // log2_diff_max_min_luma_coding_block_size + bs.SkipUeGolomb(); // log2_min_luma_transform_block_size_minus2 + bs.SkipUeGolomb(); // log2_diff_max_min_luma_transform_block_size + bs.SkipUeGolomb(); // max_transform_hierarchy_depth_inter + bs.SkipUeGolomb(); // max_transform_hierarchy_depth_intra + if (bs.GetBit()) { // scaling_list_enabled_flag + if (bs.GetBit()) { // sps_scaling_list_data_present_flag + // start of scaling_list_data() + for (int sizeId = 0; sizeId < 4; ++sizeId) { + for (int matrixId = 0; matrixId < 6; matrixId += (sizeId == 3) ? 3 : 1) { + if (!bs.GetBit()) // scaling_list_pred_mode_flag[sizeId][matrixId] + bs.SkipUeGolomb(); // scaling_list_pred_matrix_id_delta[sizeId][matrixId] + else { + int coefNum = min(64, (1 << (4 + (sizeId << 1)))); + if (sizeId > 1) + bs.SkipSeGolomb(); // scaling_list_dc_coef_minus8[sizeId−2][matrixId] + for (int i = 0; i < coefNum; ++i) + bs.SkipSeGolomb(); // scaling_list_delta_coef + } + } + } + // end of scaling_list_data() + } + } + bs.SkipBit(); // amp_enabled_flag + bs.SkipBit(); // sample_adaptive_offset_enabled_flag + if (bs.GetBit()) { // pcm_enabled_flag + bs.SkipBits(4); // pcm_sample_bit_depth_luma_minus1 + bs.SkipBits(4); // pcm_sample_bit_depth_chroma_minus1 + bs.SkipUeGolomb(); // log2_min_pcm_luma_coding_block_size_minus3 + bs.SkipUeGolomb(); // log2_diff_max_min_pcm_luma_coding_block_size + bs.SkipBit(); // pcm_loop_filter_disabled_flag + } + num_short_term_ref_pic_sets = bs.GetUeGolomb(); // num_short_term_ref_pic_sets + uint32_t NumDeltaPocs[num_short_term_ref_pic_sets]; + for (uint32_t stRpsIdx = 0; stRpsIdx < num_short_term_ref_pic_sets; ++stRpsIdx) { + // start of st_ref_pic_set(stRpsIdx) + bool inter_ref_pic_set_prediction_flag = false; + if (stRpsIdx != 0) + inter_ref_pic_set_prediction_flag = bs.GetBit(); // inter_ref_pic_set_prediction_flag + if (inter_ref_pic_set_prediction_flag) { + uint32_t RefRpsIdx, delta_idx_minus1 = 0; + if (stRpsIdx == num_short_term_ref_pic_sets) + delta_idx_minus1 = bs.GetUeGolomb(); // delta_idx_minus1 + bs.SkipBit(); // delta_rps_sign + bs.SkipUeGolomb(); // abs_delta_rps_minus1 + RefRpsIdx = stRpsIdx - (delta_idx_minus1 + 1); + NumDeltaPocs[stRpsIdx] = 0; + for (uint32_t j = 0; j <= NumDeltaPocs[RefRpsIdx]; ++j) { + if (!bs.GetBit()) { // used_by_curr_pic_flag[j] + if (bs.GetBit()) // use_delta_flag[j] + NumDeltaPocs[stRpsIdx]++; + } + else + NumDeltaPocs[stRpsIdx]++; + } + } + else { + uint32_t num_negative_pics = bs.GetUeGolomb(); // num_negative_pics + uint32_t num_positive_pics = bs.GetUeGolomb(); // num_positive_pics + for (uint32_t j = 0; j < num_negative_pics; ++j) { + bs.SkipUeGolomb(); // delta_poc_s0_minus1[i] + bs.SkipBit(); // used_by_curr_pic_s0_flag[i] + } + for (uint32_t j = 0; j < num_positive_pics; ++j) { + bs.SkipUeGolomb(); // delta_poc_s1_minus1[i] + bs.SkipBit(); // delta_poc_s1_minus1[i] + } + NumDeltaPocs[stRpsIdx] = num_negative_pics + num_positive_pics; + } + // end of st_ref_pic_set(stRpsIdx) + } + if (bs.GetBit()) { // long_term_ref_pics_present_flag + num_long_term_ref_pics_sps = bs.GetUeGolomb(); // num_long_term_ref_pics_sps + for (uint32_t i = 0; i < num_long_term_ref_pics_sps; ++i) { + bs.SkipBits(log2_max_pic_order_cnt_lsb_minus4 + 4); // lt_ref_pic_poc_lsb_sps[i] + bs.SkipBit(); // used_by_curr_pic_lt_sps_flag[i] + } + } + bs.SkipBit(); // sps_temporal_mvp_enabled_flag + bs.SkipBit(); // strong_intra_smoothing_enabled_flag + if (bs.GetBit()) { // vui_parameters_present_flag + // start of vui_parameters() + if (bs.GetBit()) { // aspect_ratio_info_present_flag + uint32_t sar_width = 0, sar_height = 0; + uint8_t aspect_ratio_idc = bs.GetBits(8); // aspect_ratio_idc + debug2("%s aspect_ratio_idc=%d", __PRETTY_FUNCTION__, aspect_ratio_idc); + if (aspect_ratio_idc == 255) { // EXTENDED_SAR + sar_width = bs.GetBits(16); // sar_width + sar_height = bs.GetBits(16); // sar_height + } + else if (aspect_ratio_idc < ELEMENTS(sarS)) { + sar_width = sarS[aspect_ratio_idc].w; + sar_height = sarS[aspect_ratio_idc].h; + } + if (sar_width && sar_height) { + int index = -1, ratio = int(100.0L * sar_width * width / sar_height / height); + for (unsigned int i = 0; i < ELEMENTS(darS); ++i) { + if (darS[i].ratio == ratio) { + index = i; + break; + } + } + if (index < 0) { + if (aspect_ratio_idc == 255) + aspect_ratio = VIDEO_ASPECT_RATIO_EXTENDED; + else + aspect_ratio = VIDEO_ASPECT_RATIO_INVALID; + } + else + aspect_ratio = darS[index].dar; + debug2("%s sar_width=%d sar_height=%d aspect_ratio=%d", __PRETTY_FUNCTION__, sar_width, sar_height, aspect_ratio); + } + } + if (bs.GetBit()) // overscan_info_present_flag + bs.SkipBit(); // overscan_appropriate_flag + if (bs.GetBit()) { // video_signal_type_present_flag + uint32_t video_format = bs.GetBits(3); // video_format + if (video_format < ELEMENTS(videoFormatS)) { + format = videoFormatS[video_format]; + debug2("%s video_format=%d format=%d", __PRETTY_FUNCTION__, video_format, format); + } + bs.SkipBit(); // video_full_range_flag + if (bs.GetBit()) { // colour_description_present_flag + bs.SkipBits(8); // colour_primaries + bs.SkipBits(8); // transfer_characteristics + bs.SkipBits(8); // matrix_coeffs + } + } + if (bs.GetBit()) { // chroma_loc_info_present_flag + bs.SkipUeGolomb(); // chroma_sample_loc_type_top_field + bs.SkipUeGolomb(); // chroma_sample_loc_type_bottom_field + } + bs.SkipBit(); // neutral_chroma_indication_flag + bs.SkipBit(); // field_seq_flag + frame_field_info_present_flag = bs.GetBit(); // frame_field_info_present_flag + debug2("%s frame_field_info_present_flag=%d", __PRETTY_FUNCTION__, frame_field_info_present_flag); + if (bs.GetBit()) { // default_display_window_flag + bs.SkipUeGolomb(); // def_disp_win_left_offset + bs.SkipUeGolomb(); // def_disp_win_right_offset + bs.SkipUeGolomb(); // def_disp_win_top_offset + bs.SkipUeGolomb(); // def_disp_win_bottom_offset + } + if (bs.GetBit()) { // vui_timing_info_present_flag + uint32_t vui_num_units_in_tick = bs.GetBits(32); // vui_num_units_in_tick + uint32_t vui_time_scale = bs.GetBits(32); // vui_time_scale + if (vui_num_units_in_tick > 0) { + frame_rate = (double)vui_time_scale / vui_num_units_in_tick; + debug2("%s frame_rate = vui_time_scale(%d) / vui_num_units_in_tick(%d) = %.f", __PRETTY_FUNCTION__, vui_time_scale, vui_num_units_in_tick, frame_rate); + } + if (bs.GetBit()) // vui_poc_proportional_to_timing_flag + bs.SkipUeGolomb(); // vui_num_ticks_poc_diff_one_minus1 + if (bs.GetBit()) { // vui_hrd_parameters_present_flag + // start of hrd_parameters(1, sps_max_sub_layers_minus1) + uint32_t bit_rate_scale = 0; + bool commonInfPresentFlag = true, nal_hrd_parameters_present_flag = false, vcl_hrd_parameters_present_flag = false, sub_pic_hrd_params_present_flag = false; + if (commonInfPresentFlag) { + nal_hrd_parameters_present_flag = bs.GetBit(); // nal_hrd_parameters_present_flag + vcl_hrd_parameters_present_flag = bs.GetBit(); // vcl_hrd_parameters_present_flag + if (nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag) { + sub_pic_hrd_params_present_flag = bs.GetBit(); // sub_pic_hrd_params_present_flag + if (sub_pic_hrd_params_present_flag) { + bs.SkipBits(8); // tick_divisor_minus2 + bs.SkipBits(5); // du_cpb_removal_delay_increment_length_minus1 + bs.SkipBit(); // sub_pic_cpb_params_in_pic_timing_sei_flag + bs.SkipBits(5); // dpb_output_delay_du_length_minus1 + } + bit_rate_scale = bs.GetBits(4); // bit_rate_scale + bs.SkipBits(4); // cpb_size_scale + if (sub_pic_hrd_params_present_flag) + bs.SkipBits(4); // cpb_size_du_scale + bs.SkipBits(5); // initial_cpb_removal_delay_length_minus1 + bs.SkipBits(5); // au_cpb_removal_delay_length_minus1 + bs.SkipBits(5); // dpb_output_delay_length_minus1 + } + } + for (uint32_t i = 0; i <= sps_max_sub_layers_minus1; ++i) { + bool fixed_pic_rate_within_cvs_flag = false, low_delay_hrd_flag = false; + uint32_t cpb_cnt_minus1 = 0; + if (!bs.GetBit()) // fixed_pic_rate_general_flag[i] + fixed_pic_rate_within_cvs_flag = bs.GetBit(); // fixed_pic_rate_within_cvs_flag[i] + if (fixed_pic_rate_within_cvs_flag) + bs.SkipUeGolomb(); // elemental_duration_in_tc_minus1[i] + else + low_delay_hrd_flag = bs.GetBit(); // low_delay_hrd_flag[i] + if (!low_delay_hrd_flag) + cpb_cnt_minus1 = bs.GetUeGolomb(); // cpb_cnt_minus1[i] + if (nal_hrd_parameters_present_flag) { + // start of sub_layer_hrd_parameters(i) + for (uint32_t i = 0; i <= cpb_cnt_minus1; ++i) { + uint32_t bit_rate_value_minus1; + bit_rate_value_minus1 = bs.GetUeGolomb(); // bit_rate_value_minus1[i] + bs.SkipUeGolomb(); // cpb_size_value_minus1[i] + if (sub_pic_hrd_params_present_flag) { + bs.SkipUeGolomb(); // cpb_size_du_value_minus1[i] + bs.SkipUeGolomb(); // bit_rate_du_value_minus1[i] + } + else { + double bitrate = (double)((bit_rate_value_minus1 + 1) * pow(2.0, 6 + bit_rate_scale)); + debug2("%s bit_rate_value_minus1(%u) + 1 * 2 ^ (6 + bit_rate_scale(%u)) = %.f", __PRETTY_FUNCTION__, bit_rate_value_minus1, bit_rate_scale, bitrate); + } + bs.SkipBit(); // cbr_flag[i] + } + // end of sub_layer_hrd_parameters(i) + } + if (vcl_hrd_parameters_present_flag) { + // start of sub_layer_hrd_parameters(i) + for (uint32_t i = 0; i <= cpb_cnt_minus1; ++i) { + bs.SkipUeGolomb(); // bit_rate_value_minus1[i] + bs.SkipUeGolomb(); // cpb_size_value_minus1[i] + if (sub_pic_hrd_params_present_flag) { + bs.SkipUeGolomb(); // cpb_size_du_value_minus1[i] + bs.SkipUeGolomb(); // bit_rate_du_value_minus1[i] + } + bs.SkipBit(); // cbr_flag[i] + } + // end of sub_layer_hrd_parameters(i) + } + } + // end of hrd_parameters(1, sps_max_sub_layers_minus1) + } + } + if (bs.GetBit()) { // bitstream_restriction_flag + bs.SkipBit(); // tiles_fixed_structure_flag + bs.SkipBit(); // motion_vectors_over_pic_boundaries_flag + bs.SkipBit(); // restricted_ref_pic_lists_flag + bs.SkipUeGolomb(); // min_spatial_segmentation_idc + bs.SkipUeGolomb(); // max_bytes_per_pic_denom + bs.SkipUeGolomb(); // max_bits_per_min_cu_denom + bs.SkipUeGolomb(); // log2_max_mv_length_horizontal + bs.SkipUeGolomb(); // log2_max_mv_length_vertical + } + // end of vui_parameters() + } + if (bs.GetBit()) { // sps_extension_present_flag + bs.SkipBit(); // sps_range_extension_flag + bs.SkipBit(); // sps_multilayer_extension_flag + bs.SkipBit(); // sps_3d_extension_flag + bs.SkipBits(5); // sps_extension_5bits + } widthM = width; heightM = height; + formatM = format; + aspectRatioM = aspect_ratio; + scanM = scan; + frameRateM = frame_rate; + bitRateM = bit_rate; + frameFieldInfoPresentFlagM = frame_field_info_present_flag; + + return (bs.Index() / 8); +} + +int cFemonH265::parseSEI(const uint8_t *bufP, int lenP) +{ + cFemonBitStream bs(bufP, lenP); + + eVideoScan scan = scanM; + + while ((bs.Index() * 8 + 16) < lenP) { // sei_message + int len, lastByte, payloadSize = 0, payloadType = 0; + + do { + lastByte = bs.GetBits(8) & 0xFF; + payloadType += lastByte; + } while (lastByte == 0xFF); // last_payload_type_byte + + do { + lastByte = bs.GetBits(8) & 0xFF; + payloadSize += lastByte; + } while (lastByte == 0xFF); // last_payload_size_byte + + switch (payloadType) { // sei_payload + case 1: // pic_timing + len = payloadSize * 8; + if (frameFieldInfoPresentFlagM) { + uint8_t pic_struct = bs.GetBits(4); // pic_struct + switch (pic_struct) { + case 0: // frame + case 7: // frame doubling + case 8: // frame tripling + scan = VIDEO_SCAN_PROGRESSIVE; + break; + case 1: // top + case 2: // bottom + case 3: // top bottom + case 4: // bottom top + case 5: // top bottom top + case 6: // bottom top bottom + case 9: // top paired with previous bottom + case 10: // bottom paired with previous top + case 11: // top paired with next bottom + case 12: // bottom paired with next top + scan = VIDEO_SCAN_INTERLACED; + break; + default: + scan = VIDEO_SCAN_RESERVED; + break; + } + debug2("%s pic_struct=%d scan_type=%d", __PRETTY_FUNCTION__, pic_struct, scan); + bs.SkipBits(2); // source_scan_type + bs.SkipBit(); // duplicate_flag + len -= 7; + } + bs.SkipBits(len); + break; + + default: + bs.SkipBits(payloadSize * 8); + break; + } + + // force byte align + bs.ByteAlign(); + } + + scanM = scan; return (bs.Index() / 8); } diff --git a/h265.h b/h265.h index f07247e..0a93483 100644 --- a/h265.h +++ b/h265.h @@ -19,8 +19,19 @@ private: NAL_AUD = 35, // Access Unit Delimiter NAL_EOS = 36, // End of Sequence NAL_EOB = 37, // End of Bitstream + NAL_SEI = 39, // Prefix Supplemental Enchancement Information }; + typedef struct DAR { + eVideoAspectRatio dar; + int ratio; + } t_DAR; + + typedef struct SAR { + int w; + int h; + } t_SAR; + cFemonVideoIf *videoHandlerM; uint32_t widthM; uint32_t heightM; @@ -29,11 +40,17 @@ private: double frameRateM; double bitRateM; eVideoScan scanM; + bool frameFieldInfoPresentFlagM; void reset(); const uint8_t *nextStartCode(const uint8_t *start, const uint8_t *end); int nalUnescape(uint8_t *dst, const uint8_t *src, int len); int parseSPS(const uint8_t *buf, int len); + int parseSEI(const uint8_t *buf, int len); + + static const t_SAR sarS[]; + static const t_DAR darS[]; + static const eVideoFormat videoFormatS[]; public: cFemonH265(cFemonVideoIf *videoHandlerP);