Helper API for codec factories to calculate supported H.265 levels.

This expose a new GetSupportedH265Level API for WebRTC external
factories to calculate H.265 levels to be use for SDP negotation.

Bug: webrtc:13485
Change-Id: Ib420da2b9b1b7af00129294be5b3efec172e8faf
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/345544
Commit-Queue: Sergey Silkin <ssilkin@webrtc.org>
Reviewed-by: Philip Eliasson <philipel@webrtc.org>
Reviewed-by: Sergey Silkin <ssilkin@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#42079}
This commit is contained in:
Qiu Jianlin 2024-04-09 10:26:05 +08:00 committed by WebRTC LUCI CQ
parent b065f1bbd8
commit 9c95a4f704
5 changed files with 120 additions and 1 deletions

View file

@ -139,6 +139,7 @@ rtc_source_set("render_resolution") {
rtc_source_set("resolution") {
visibility = [ "*" ]
public = [ "resolution.h" ]
deps = [ "../../rtc_base/system:rtc_export" ]
}
rtc_library("encoded_image") {

View file

@ -13,10 +13,12 @@
#include <utility>
#include "rtc_base/system/rtc_export.h"
namespace webrtc {
// A struct representing a video resolution in pixels.
struct Resolution {
struct RTC_EXPORT Resolution {
int width = 0;
int height = 0;

View file

@ -12,6 +12,7 @@
#include <string>
#include "rtc_base/arraysize.h"
#include "rtc_base/string_to_number.h"
namespace webrtc {
@ -22,6 +23,42 @@ const char kH265FmtpProfile[] = "profile-id";
const char kH265FmtpTier[] = "tier-flag";
const char kH265FmtpLevel[] = "level-id";
// Used to align frame width and height for luma picture size calculation.
// Use the maximum value allowed by spec to get upper bound of luma picture
// size for given resolution.
static constexpr int kMinCbSizeYMax = 64;
struct LevelConstraint {
const int max_luma_picture_size;
const double max_luma_sample_rate;
const int max_pic_width_or_height_in_pixels;
const H265Level level;
};
// This is from ITU-T H.265 (09/2023) Table A.8, A.9 & A.11 Level limits.
// The max_pic_width_or_height_in_luma_samples is pre-calculated following
// ITU-T H.265 section A.4.1, that is, find the largest integer value that
// is multiple of minimal MinCbSizeY(8 according to equation 7-10 and 7-12), is
// less than sqrt(max_luma_picture_size * 8). For example, at level 1,
// max_luma_picture_size is 36864, so pic_width_in_luma_samples <= sqrt(36864 *
// 8) = 543.06. The largest integer that is multiple of 8 and less than 543.06
// is 536.
static constexpr LevelConstraint kLevelConstraints[] = {
{36864, 552960, 536, H265Level::kLevel1},
{122880, 3686400, 984, H265Level::kLevel2},
{245760, 7372800, 1400, H265Level::kLevel2_1},
{552960, 16588800, 2096, H265Level::kLevel3},
{983040, 33177600, 2800, H265Level::kLevel3_1},
{2228224, 66846720, 4216, H265Level::kLevel4},
{2228224, 133693400, 4216, H265Level::kLevel4_1},
{8912896, 267386880, 8440, H265Level::kLevel5},
{8912896, 534773760, 8440, H265Level::kLevel5_1},
{8912896, 1069547520, 8440, H265Level::kLevel5_2},
{35651584, 1069547520, 16888, H265Level::kLevel6},
{35651584, 2139095040, 16888, H265Level::kLevel6_1},
{35651584, 4278190080, 16888, H265Level::kLevel6_2},
};
} // anonymous namespace
// Annex A of https://www.itu.int/rec/T-REC-H.265 (08/21), section A.3.
@ -245,4 +282,25 @@ bool H265IsSameProfileTierLevel(const CodecParameterMap& params1,
ptl1->tier == ptl2->tier && ptl1->level == ptl2->level;
}
absl::optional<H265Level> GetSupportedH265Level(const Resolution& resolution,
float max_fps) {
int aligned_width =
(resolution.width + kMinCbSizeYMax - 1) & ~(kMinCbSizeYMax - 1);
int aligned_height =
(resolution.height + kMinCbSizeYMax - 1) & ~(kMinCbSizeYMax - 1);
for (int i = arraysize(kLevelConstraints) - 1; i >= 0; --i) {
const LevelConstraint& level_constraint = kLevelConstraints[i];
if (level_constraint.max_luma_picture_size <=
aligned_width * aligned_height &&
level_constraint.max_luma_sample_rate <=
aligned_width * aligned_height * max_fps &&
level_constraint.max_pic_width_or_height_in_pixels >= aligned_width &&
level_constraint.max_pic_width_or_height_in_pixels >= aligned_height) {
return level_constraint.level;
}
}
return absl::nullopt;
}
} // namespace webrtc

View file

@ -14,6 +14,7 @@
#include <string>
#include "absl/types/optional.h"
#include "api/video/resolution.h"
#include "api/video_codecs/sdp_video_format.h"
#include "rtc_base/system/rtc_export.h"
@ -89,6 +90,14 @@ RTC_EXPORT absl::optional<H265Tier> StringToH265Tier(const std::string& tier);
RTC_EXPORT absl::optional<H265Level> StringToH265Level(
const std::string& level);
// Given that a decoder supports up to a give frame size(in pixels) at up to a
// given number of frames per second, return the highest H.265 level where it
// can guranatee that it will be able to support all valid encoded streams that
// are within that level.
RTC_EXPORT absl::optional<H265Level> GetSupportedH265Level(
const Resolution& resolution,
float max_fps);
// Parses an SDP key-value map of format parameters to retrive an H265
// profile/tier/level. Returns an H265ProfileTierlevel by setting its
// members. profile defaults to `kProfileMain` if no profile-id is specified.

View file

@ -245,4 +245,53 @@ TEST(H265ProfileTierLevel, TestProfileTierLevelCompare) {
EXPECT_FALSE(H265IsSameProfileTierLevel(params1, params2));
}
TEST(H265ProfileTierLevel, TestGetSupportedH265Level) {
// Test with 720p at 30fps
Resolution r{.width = 1280, .height = 720};
EXPECT_EQ(GetSupportedH265Level(r, 30).value_or(H265Level::kLevel1),
H265Level::kLevel3);
// Test with QCIF at 15fps
r.width = 176;
r.height = 144;
EXPECT_EQ(GetSupportedH265Level(r, 15).value_or(H265Level::kLevel2),
H265Level::kLevel1);
// Test with 1080p at 30fps
r.width = 1920;
r.height = 1080;
EXPECT_EQ(GetSupportedH265Level(r, 30).value_or(H265Level::kLevel1),
H265Level::kLevel3_1);
// Test with 1080p at 60fps
EXPECT_EQ(GetSupportedH265Level(r, 60).value_or(H265Level::kLevel1),
H265Level::kLevel3_1);
// Test with 4K at 30fps
r.width = 3840;
r.height = 2160;
EXPECT_EQ(GetSupportedH265Level(r, 30).value_or(H265Level::kLevel1),
H265Level::kLevel4_1);
// Test with 4K at 60fps
EXPECT_EQ(GetSupportedH265Level(r, 60).value_or(H265Level::kLevel1),
H265Level::kLevel4_1);
// Test with 8K at 30fps
r.width = 8192;
r.height = 4320;
EXPECT_EQ(GetSupportedH265Level(r, 30).value_or(H265Level::kLevel1),
H265Level::kLevel6);
// Test with 64x64 at 30fps
r.width = 64;
r.height = 64;
EXPECT_EQ(GetSupportedH265Level(r, 30), absl::nullopt);
// Test with extremly large width or height at 15fps
r.width = 16928;
r.height = 64;
EXPECT_EQ(GetSupportedH265Level(r, 15), absl::nullopt);
}
} // namespace webrtc