diff --git a/h265.c b/h265.c index 31a02e3..4e1dd06 100644 --- a/h265.c +++ b/h265.c @@ -17,7 +17,7 @@ cFemonH265::cFemonH265(cFemonVideoIf *videoHandlerP) formatM(VIDEO_FORMAT_INVALID), frameRateM(0), bitRateM(0), - scanM(VIDEO_SCAN_INVALID) + scanM(VIDEO_SCAN_PROGRESSIVE) { reset(); } @@ -28,7 +28,8 @@ cFemonH265::~cFemonH265() bool cFemonH265::processVideo(const uint8_t *bufP, int lenP) { - bool aud_found = false; + uint8_t nal_data[lenP]; + bool aud_found = false, sps_found = false; const uint8_t *buf = bufP; const uint8_t *start = buf; const uint8_t *end = start + lenP; @@ -54,8 +55,18 @@ bool cFemonH265::processVideo(const uint8_t *bufP, int lenP) switch ((buf[3] >> 1) & 0x3F) { case NAL_AUD: if (!aud_found) { - aud_found = true; debug2("%s Found NAL AUD at offset %d/%d", __PRETTY_FUNCTION__, int(buf - start), lenP); + aud_found = true; + } + break; + + 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)); + consumed = parseSPS(nal_data, nal_len); + if (consumed > 0) + sps_found = true; } break; @@ -63,23 +74,26 @@ bool cFemonH265::processVideo(const uint8_t *bufP, int lenP) break; } - if (aud_found) + if (aud_found && sps_found) break; buf += consumed + 4; } - if (aud_found) { - videoHandlerM->SetVideoCodec(VIDEO_CODEC_H265); - //videoHandlerM->SetVideoFormat(formatM); - //videoHandlerM->SetVideoSize(widthM, heightM); - //videoHandlerM->SetVideoAspectRatio(aspectRatioM); - //videoHandlerM->SetVideoBitrate(bitRateM); - //videoHandlerM->SetVideoScan(scanM); - //videoHandlerM->SetVideoFramerate((scanM == VIDEO_SCAN_PROGRESSIVE) ? (frameRateM / 2) : frameRateM); - } + 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); + videoHandlerM->SetVideoSize(widthM, heightM); + videoHandlerM->SetVideoScan(scanM); + //videoHandlerM->SetVideoFormat(formatM); + //videoHandlerM->SetVideoAspectRatio(aspectRatioM); + //videoHandlerM->SetVideoBitrate(bitRateM); + //videoHandlerM->SetVideoFramerate(frameRateM); + } + } - return aud_found; + return sps_found; } void cFemonH265::reset() @@ -116,3 +130,74 @@ int cFemonH265::nalUnescape(uint8_t *dstP, const uint8_t *srcP, int lenP) return d; } + +int cFemonH265::parseSPS(const uint8_t *bufP, int lenP) +{ + cFemonBitStream bs(bufP, 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]; + + 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 + // 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.SkipBits(8); // general_level_idc + 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] + } + 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] + } + 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] + // the number of bits in this syntax structure is not affected by this condition + 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] + } + if (sub_layer_level_present_flag[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 + 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 + + widthM = width; + heightM = height; + + return (bs.Index() / 8); +} diff --git a/h265.h b/h265.h index 30d8dbf..f07247e 100644 --- a/h265.h +++ b/h265.h @@ -33,6 +33,7 @@ private: 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); public: cFemonH265(cFemonVideoIf *videoHandlerP); diff --git a/osd.c b/osd.c index dab3fab..e7eddaa 100644 --- a/osd.c +++ b/osd.c @@ -307,7 +307,14 @@ void cFemonOsd::DrawStatusWindow(void) OSDDRAWSTATUSBM(OSDSPACING); } if (receiverM) { - if (IS_OSDRESOLUTION(receiverM->VideoVerticalSize(), 1080)) { + if (IS_OSDRESOLUTION(receiverM->VideoVerticalSize(), 2160)) { + switch (receiverM->VideoScan()) { + case VIDEO_SCAN_INTERLACED: bm = &OSDSYMBOL(SYMBOL_FORMAT_2160i); break; + case VIDEO_SCAN_PROGRESSIVE: bm = &OSDSYMBOL(SYMBOL_FORMAT_2160p); break; + default: bm = &OSDSYMBOL(SYMBOL_FORMAT_2160); break; + } + } + else if (IS_OSDRESOLUTION(receiverM->VideoVerticalSize(), 1080)) { switch (receiverM->VideoScan()) { case VIDEO_SCAN_INTERLACED: bm = &OSDSYMBOL(SYMBOL_FORMAT_1080i); break; case VIDEO_SCAN_PROGRESSIVE: bm = &OSDSYMBOL(SYMBOL_FORMAT_1080p); break; diff --git a/symbol.c b/symbol.c index 6dc6084..24e8ded 100644 --- a/symbol.c +++ b/symbol.c @@ -43,6 +43,9 @@ #include "symbols/six.xpm" #include "symbols/seven.xpm" #include "symbols/eight.xpm" +#include "symbols/format2160.xpm" +#include "symbols/format2160i.xpm" +#include "symbols/format2160p.xpm" #include "symbols/format1080.xpm" #include "symbols/format1080i.xpm" #include "symbols/format1080p.xpm" @@ -89,6 +92,9 @@ static cBitmap bmFive(five_xpm); static cBitmap bmSix(six_xpm); static cBitmap bmSeven(seven_xpm); static cBitmap bmEight(eight_xpm); +static cBitmap bmFormat2160(format2160_xpm); +static cBitmap bmFormat2160i(format2160i_xpm); +static cBitmap bmFormat2160p(format2160p_xpm); static cBitmap bmFormat1080(format1080_xpm); static cBitmap bmFormat1080i(format1080i_xpm); static cBitmap bmFormat1080p(format1080p_xpm); @@ -172,6 +178,9 @@ bool cFemonSymbolCache::Populate(void) cacheM.Append(bmSix.Scaled(yFactorM, yFactorM, antiAliasM)); // SYMBOL_SIX cacheM.Append(bmSeven.Scaled(yFactorM, yFactorM, antiAliasM)); // SYMBOL_SEVEN cacheM.Append(bmEight.Scaled(yFactorM, yFactorM, antiAliasM)); // SYMBOL_EIGHT + cacheM.Append(bmFormat2160.Scaled(yFactorM, yFactorM, antiAliasM)); // SYMBOL_FORMAT_2160 + cacheM.Append(bmFormat2160i.Scaled(yFactorM, yFactorM, antiAliasM)); // SYMBOL_FORMAT_2160i + cacheM.Append(bmFormat2160p.Scaled(yFactorM, yFactorM, antiAliasM)); // SYMBOL_FORMAT_2160p cacheM.Append(bmFormat1080.Scaled(yFactorM, yFactorM, antiAliasM)); // SYMBOL_FORMAT_1080 cacheM.Append(bmFormat1080i.Scaled(yFactorM, yFactorM, antiAliasM)); // SYMBOL_FORMAT_1080i cacheM.Append(bmFormat1080p.Scaled(yFactorM, yFactorM, antiAliasM)); // SYMBOL_FORMAT_1080p diff --git a/symbol.h b/symbol.h index fbfed60..bd69c54 100644 --- a/symbol.h +++ b/symbol.h @@ -45,6 +45,9 @@ enum eSymbols { SYMBOL_SIX, SYMBOL_SEVEN, SYMBOL_EIGHT, + SYMBOL_FORMAT_2160, + SYMBOL_FORMAT_2160i, + SYMBOL_FORMAT_2160p, SYMBOL_FORMAT_1080, SYMBOL_FORMAT_1080i, SYMBOL_FORMAT_1080p, diff --git a/symbols/format2160.xpm b/symbols/format2160.xpm new file mode 100644 index 0000000..da975ae --- /dev/null +++ b/symbols/format2160.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static const char *const format2160_xpm[] = { +"40 18 2 1", +". c #FFFFFF", +"+ c #000000", +"++++++++++++++++++++++++++++++++++++++++", +"+......................................+", +"+.....++++......++..++++++....++++.....+", +"+...+++++++..+++++.++++++++..++++++....+", +"+...++....++.+++++.+++...++..++..++....+", +"+.........++....++.++.......++....++...+", +"+.........++....++.++.......++....++...+", +"+.........++....++.++.+++...++....++...+", +"+.......+++.....++.+++++++..++....++...+", +"+......+++......++.+++..+++.++....++...+", +"+.....+++.......++.++....++.++....++...+", +"+.....+++.......++.++....++.++....++...+", +"+....+++........++.++....++.++....++...+", +"+....++.........++.+++..+++..++..++....+", +"+....++++++++...++..+++++++..++++++....+", +"+....++++++++...++...+++++....++++.....+", +"+......................................+", +"++++++++++++++++++++++++++++++++++++++++"}; diff --git a/symbols/format2160i.xpm b/symbols/format2160i.xpm new file mode 100644 index 0000000..94e9dd1 --- /dev/null +++ b/symbols/format2160i.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static const char *const format2160i_xpm[] = { +"43 18 2 1", +". c #FFFFFF", +"+ c #000000", +"+++++++++++++++++++++++++++++++++++++++++++", +"+.........................................+", +"+.....++++......++..++++++....++++........+", +"+...+++++++..+++++.++++++++..++++++.......+", +"+...++....++.+++++.+++...++..++..++.......+", +"+.........++....++.++.......++....++......+", +"+.........++....++.++.......++....++.++...+", +"+.........++....++.++.+++...++....++.++...+", +"+.......+++.....++.+++++++..++....++......+", +"+......+++......++.+++..+++.++....++.++...+", +"+.....+++.......++.++....++.++....++.++...+", +"+.....+++.......++.++....++.++....++.++...+", +"+....+++........++.++....++.++....++.++...+", +"+....++.........++.+++..+++..++..++..++...+", +"+....++++++++...++..+++++++..++++++..++...+", +"+....++++++++...++...+++++....++++...++...+", +"+.........................................+", +"+++++++++++++++++++++++++++++++++++++++++++"}; diff --git a/symbols/format2160p.xpm b/symbols/format2160p.xpm new file mode 100644 index 0000000..65daa7d --- /dev/null +++ b/symbols/format2160p.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static const char *const format2160p_xpm[] = { +"47 18 2 1", +". c #FFFFFF", +"+ c #000000", +"+++++++++++++++++++++++++++++++++++++++++++++++", +"+.............................................+", +"+.....++++......++..++++++....++++............+", +"+...+++++++..+++++.++++++++..++++++...........+", +"+...++....++.+++++.+++...++..++..++...........+", +"+.........++....++.++.......++....++..........+", +"+.........++....++.++.......++....++..........+", +"+.........++....++.++.+++...++....++.++++.....+", +"+.......+++.....++.+++++++..++....++.+++++....+", +"+......+++......++.+++..+++.++....++.++..++...+", +"+.....+++.......++.++....++.++....++.++..++...+", +"+.....+++.......++.++....++.++....++.+++++....+", +"+....+++........++.++....++.++....++.++++.....+", +"+....++.........++.+++..+++..++..++..++.......+", +"+....++++++++...++..+++++++..++++++..++.......+", +"+....++++++++...++...+++++....++++...++.......+", +"+.............................................+", +"+++++++++++++++++++++++++++++++++++++++++++++++"};