mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-12 21:30:45 +01:00
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:
parent
b065f1bbd8
commit
9c95a4f704
5 changed files with 120 additions and 1 deletions
|
@ -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") {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue