diff --git a/webrtc/common_video/h264/profile_level_id.cc b/webrtc/common_video/h264/profile_level_id.cc index c7b7f2c2ff..1b3841d2ac 100644 --- a/webrtc/common_video/h264/profile_level_id.cc +++ b/webrtc/common_video/h264/profile_level_id.cc @@ -14,6 +14,8 @@ #include #include +#include "webrtc/base/arraysize.h" + namespace { // For level_idc=11 and profile_idc=0x42, 0x4D, or 0x58, the constraint set3 @@ -68,6 +70,33 @@ constexpr ProfilePattern kProfilePatterns[] = { {0x64, BitPattern("00000000"), webrtc::H264::kProfileHigh}, {0x64, BitPattern("00001100"), webrtc::H264::kProfileConstrainedHigh}}; +struct LevelConstraint { + const int max_macroblocks_per_second; + const int max_macroblock_frame_size; + const webrtc::H264::Level level; +}; + +// This is from ITU-T H.264 (02/2016) Table A-1 – Level limits. +static constexpr LevelConstraint kLevelConstraints[] = { + {1485, 99, webrtc::H264::kLevel1}, + {1485, 99, webrtc::H264::kLevel1_b}, + {3000, 396, webrtc::H264::kLevel1_1}, + {6000, 396, webrtc::H264::kLevel1_2}, + {11880, 396, webrtc::H264::kLevel1_3}, + {11880, 396, webrtc::H264::kLevel2}, + {19800, 792, webrtc::H264::kLevel2_1}, + {20250, 1620, webrtc::H264::kLevel2_2}, + {40500, 1620, webrtc::H264::kLevel3}, + {108000, 3600, webrtc::H264::kLevel3_1}, + {216000, 5120, webrtc::H264::kLevel3_2}, + {245760, 8192, webrtc::H264::kLevel4}, + {245760, 8192, webrtc::H264::kLevel4_1}, + {522240, 8704, webrtc::H264::kLevel4_2}, + {589824, 22080, webrtc::H264::kLevel5}, + {983040, 3684, webrtc::H264::kLevel5_1}, + {2073600, 3684, webrtc::H264::kLevel5_2}, +}; + } // anonymous namespace namespace webrtc { @@ -129,6 +158,23 @@ rtc::Optional ParseProfileLevelId(const char* str) { return rtc::Optional(); } +rtc::Optional SupportedLevel(int max_frame_pixel_count, float max_fps) { + static const int kPixelsPerMacroblock = 16 * 16; + + for (int i = arraysize(kLevelConstraints) - 1; i >= 0; --i) { + const LevelConstraint& level_constraint = kLevelConstraints[i]; + if (level_constraint.max_macroblock_frame_size * kPixelsPerMacroblock <= + max_frame_pixel_count && + level_constraint.max_macroblocks_per_second <= + max_fps * level_constraint.max_macroblock_frame_size) { + return rtc::Optional(level_constraint.level); + } + } + + // No level supported. + return rtc::Optional(); +} + rtc::Optional ProfileLevelIdToString( const ProfileLevelId& profile_level_id) { // Handle special case level == 1b. diff --git a/webrtc/common_video/h264/profile_level_id.h b/webrtc/common_video/h264/profile_level_id.h index 44292631cb..f69b7f6cdc 100644 --- a/webrtc/common_video/h264/profile_level_id.h +++ b/webrtc/common_video/h264/profile_level_id.h @@ -60,6 +60,12 @@ struct ProfileLevelId { // profile level id. rtc::Optional ParseProfileLevelId(const char* str); +// Given that a decoder supports up to a given frame size (in pixels) at up to a +// given number of frames per second, return the highest H.264 level where it +// can guarantee that it will be able to support all valid encoded streams that +// are within that level. +rtc::Optional SupportedLevel(int max_frame_pixel_count, float max_fps); + // Returns canonical string representation as three hex bytes of the profile // level id, or returns nothing for invalid profile level ids. rtc::Optional ProfileLevelIdToString( diff --git a/webrtc/common_video/h264/profile_level_id_unittest.cc b/webrtc/common_video/h264/profile_level_id_unittest.cc index 491fda14aa..1b3c51b75a 100644 --- a/webrtc/common_video/h264/profile_level_id_unittest.cc +++ b/webrtc/common_video/h264/profile_level_id_unittest.cc @@ -70,6 +70,21 @@ TEST(H264ProfileLevelId, TestParsingConstrainedHigh) { EXPECT_EQ(kProfileConstrainedHigh, ParseProfileLevelId("640c1f")->profile); } +TEST(H264ProfileLevelId, TestSupportedLevel) { + EXPECT_EQ(kLevel2_1, *SupportedLevel(640 * 480, 25)); + EXPECT_EQ(kLevel3_1, *SupportedLevel(1280 * 720, 30)); + EXPECT_EQ(kLevel4_2, *SupportedLevel(1920 * 1280, 60)); +} + +// Test supported level below level 1 requirements. +TEST(H264ProfileLevelId, TestSupportedLevelInvalid) { + EXPECT_FALSE(SupportedLevel(0, 0)); + // All levels support fps > 5. + EXPECT_FALSE(SupportedLevel(1280 * 720, 5)); + // All levels support frame sizes > 183 * 137. + EXPECT_FALSE(SupportedLevel(183 * 137, 30)); +} + TEST(H264ProfileLevelId, TestToString) { EXPECT_EQ("42e01f", *ProfileLevelIdToString(ProfileLevelId( kProfileConstrainedBaseline, kLevel3_1)));