webrtc/api/video_codecs/h265_profile_tier_level.cc
Florent Castelli 8037fc6ffa Migrate absl::optional to std::optional
Bug: webrtc:342905193
No-Try: True
Change-Id: Icc968be43b8830038ea9a1f5f604307220457807
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/361021
Auto-Submit: Florent Castelli <orphis@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Florent Castelli <orphis@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#42911}
2024-09-02 12:16:47 +00:00

309 lines
9.4 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2023 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "api/video_codecs/h265_profile_tier_level.h"
#include <optional>
#include <string>
#include "api/rtp_parameters.h"
#include "api/video/resolution.h"
#include "rtc_base/arraysize.h"
#include "rtc_base/string_to_number.h"
namespace webrtc {
namespace {
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.
std::optional<H265Profile> StringToH265Profile(const std::string& profile) {
std::optional<int> i = rtc::StringToNumber<int>(profile);
if (!i.has_value()) {
return std::nullopt;
}
switch (i.value()) {
case 1:
return H265Profile::kProfileMain;
case 2:
return H265Profile::kProfileMain10;
case 3:
return H265Profile::kProfileMainStill;
case 4:
return H265Profile::kProfileRangeExtensions;
case 5:
return H265Profile::kProfileHighThroughput;
case 6:
return H265Profile::kProfileMultiviewMain;
case 7:
return H265Profile::kProfileScalableMain;
case 8:
return H265Profile::kProfile3dMain;
case 9:
return H265Profile::kProfileScreenContentCoding;
case 10:
return H265Profile::kProfileScalableRangeExtensions;
case 11:
return H265Profile::kProfileHighThroughputScreenContentCoding;
default:
return std::nullopt;
}
}
// Annex A of https://www.itu.int/rec/T-REC-H.265 (08/21), section A.4,
// tiers and levels.
std::optional<H265Tier> StringToH265Tier(const std::string& tier) {
std::optional<int> i = rtc::StringToNumber<int>(tier);
if (!i.has_value()) {
return std::nullopt;
}
switch (i.value()) {
case 0:
return H265Tier::kTier0;
case 1:
return H265Tier::kTier1;
default:
return std::nullopt;
}
}
std::optional<H265Level> StringToH265Level(const std::string& level) {
const std::optional<int> i = rtc::StringToNumber<int>(level);
if (!i.has_value())
return std::nullopt;
switch (i.value()) {
case 30:
return H265Level::kLevel1;
case 60:
return H265Level::kLevel2;
case 63:
return H265Level::kLevel2_1;
case 90:
return H265Level::kLevel3;
case 93:
return H265Level::kLevel3_1;
case 120:
return H265Level::kLevel4;
case 123:
return H265Level::kLevel4_1;
case 150:
return H265Level::kLevel5;
case 153:
return H265Level::kLevel5_1;
case 156:
return H265Level::kLevel5_2;
case 180:
return H265Level::kLevel6;
case 183:
return H265Level::kLevel6_1;
case 186:
return H265Level::kLevel6_2;
default:
return std::nullopt;
}
}
std::string H265ProfileToString(H265Profile profile) {
switch (profile) {
case H265Profile::kProfileMain:
return "1";
case H265Profile::kProfileMain10:
return "2";
case H265Profile::kProfileMainStill:
return "3";
case H265Profile::kProfileRangeExtensions:
return "4";
case H265Profile::kProfileHighThroughput:
return "5";
case H265Profile::kProfileMultiviewMain:
return "6";
case H265Profile::kProfileScalableMain:
return "7";
case H265Profile::kProfile3dMain:
return "8";
case H265Profile::kProfileScreenContentCoding:
return "9";
case H265Profile::kProfileScalableRangeExtensions:
return "10";
case H265Profile::kProfileHighThroughputScreenContentCoding:
return "11";
}
}
std::string H265TierToString(H265Tier tier) {
switch (tier) {
case H265Tier::kTier0:
return "0";
case H265Tier::kTier1:
return "1";
}
}
std::string H265LevelToString(H265Level level) {
switch (level) {
case H265Level::kLevel1:
return "30";
case H265Level::kLevel2:
return "60";
case H265Level::kLevel2_1:
return "63";
case H265Level::kLevel3:
return "90";
case H265Level::kLevel3_1:
return "93";
case H265Level::kLevel4:
return "120";
case H265Level::kLevel4_1:
return "123";
case H265Level::kLevel5:
return "150";
case H265Level::kLevel5_1:
return "153";
case H265Level::kLevel5_2:
return "156";
case H265Level::kLevel6:
return "180";
case H265Level::kLevel6_1:
return "183";
case H265Level::kLevel6_2:
return "186";
}
}
std::optional<H265ProfileTierLevel> ParseSdpForH265ProfileTierLevel(
const CodecParameterMap& params) {
static const H265ProfileTierLevel kDefaultProfileTierLevel(
H265Profile::kProfileMain, H265Tier::kTier0, H265Level::kLevel3_1);
bool profile_tier_level_specified = false;
std::optional<H265Profile> profile;
const auto profile_it = params.find(kH265FmtpProfile);
if (profile_it != params.end()) {
profile_tier_level_specified = true;
const std::string& profile_str = profile_it->second;
profile = StringToH265Profile(profile_str);
if (!profile) {
return std::nullopt;
}
} else {
profile = H265Profile::kProfileMain;
}
std::optional<H265Tier> tier;
const auto tier_it = params.find(kH265FmtpTier);
if (tier_it != params.end()) {
profile_tier_level_specified = true;
const std::string& tier_str = tier_it->second;
tier = StringToH265Tier(tier_str);
if (!tier) {
return std::nullopt;
}
} else {
tier = H265Tier::kTier0;
}
std::optional<H265Level> level;
const auto level_it = params.find(kH265FmtpLevel);
if (level_it != params.end()) {
profile_tier_level_specified = true;
const std::string& level_str = level_it->second;
level = StringToH265Level(level_str);
if (!level) {
return std::nullopt;
}
} else {
level = H265Level::kLevel3_1;
}
// Spec Table A.9, level 1 to level 3.1 does not allow high tiers.
if (level <= H265Level::kLevel3_1 && tier == H265Tier::kTier1) {
return std::nullopt;
}
return !profile_tier_level_specified
? kDefaultProfileTierLevel
: H265ProfileTierLevel(profile.value(), tier.value(),
level.value());
}
bool H265IsSameProfileTierLevel(const CodecParameterMap& params1,
const CodecParameterMap& params2) {
const std::optional<H265ProfileTierLevel> ptl1 =
ParseSdpForH265ProfileTierLevel(params1);
const std::optional<H265ProfileTierLevel> ptl2 =
ParseSdpForH265ProfileTierLevel(params2);
return ptl1 && ptl2 && ptl1->profile == ptl2->profile &&
ptl1->tier == ptl2->tier && ptl1->level == ptl2->level;
}
std::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 std::nullopt;
}
} // namespace webrtc