blob: c76afad8513fa741821f361da142c16e142bc3b6 [file] [log] [blame]
Johannes Kronc3fcee72021-04-19 07:09:261/*
2 * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "media/base/sdp_video_format_utils.h"
12
13#include <cstring>
14#include <map>
Philipp Hancke7c5f9cf2024-02-20 14:28:1415#include <string>
Johannes Kronc3fcee72021-04-19 07:09:2616#include <utility>
17
18#include "api/video_codecs/h264_profile_level_id.h"
Qiu Jianlinb3488d02023-12-07 00:12:1219#ifdef RTC_ENABLE_H265
20#include "api/video_codecs/h265_profile_tier_level.h"
21#endif
Philipp Hancke7c5f9cf2024-02-20 14:28:1422#include "absl/algorithm/container.h"
23#include "media/base/media_constants.h"
Johannes Kronc3fcee72021-04-19 07:09:2624#include "rtc_base/checks.h"
25#include "rtc_base/string_to_number.h"
26
27namespace webrtc {
28namespace {
29const char kProfileLevelId[] = "profile-level-id";
30const char kH264LevelAsymmetryAllowed[] = "level-asymmetry-allowed";
31// Max frame rate for VP8 and VP9 video.
32const char kVPxFmtpMaxFrameRate[] = "max-fr";
33// Max frame size for VP8 and VP9 video.
34const char kVPxFmtpMaxFrameSize[] = "max-fs";
35const int kVPxFmtpFrameSizeSubBlockPixels = 256;
Qiu Jianlinb3488d02023-12-07 00:12:1236#ifdef RTC_ENABLE_H265
37constexpr char kH265ProfileId[] = "profile-id";
38constexpr char kH265TierFlag[] = "tier-flag";
39constexpr char kH265LevelId[] = "level-id";
40#endif
Johannes Kronc3fcee72021-04-19 07:09:2641
Philipp Hanckede172522023-12-14 08:45:3942bool IsH264LevelAsymmetryAllowed(const CodecParameterMap& params) {
Johannes Kronc3fcee72021-04-19 07:09:2643 const auto it = params.find(kH264LevelAsymmetryAllowed);
44 return it != params.end() && strcmp(it->second.c_str(), "1") == 0;
45}
46
47// Compare H264 levels and handle the level 1b case.
48bool H264LevelIsLess(H264Level a, H264Level b) {
49 if (a == H264Level::kLevel1_b)
50 return b != H264Level::kLevel1 && b != H264Level::kLevel1_b;
51 if (b == H264Level::kLevel1_b)
52 return a == H264Level::kLevel1;
53 return a < b;
54}
55
56H264Level H264LevelMin(H264Level a, H264Level b) {
57 return H264LevelIsLess(a, b) ? a : b;
58}
59
Florent Castelli8037fc62024-08-29 13:00:4060std::optional<int> ParsePositiveNumberFromParams(
Philipp Hanckede172522023-12-14 08:45:3961 const CodecParameterMap& params,
Johannes Kronc3fcee72021-04-19 07:09:2662 const char* parameter_name) {
63 const auto max_frame_rate_it = params.find(parameter_name);
64 if (max_frame_rate_it == params.end())
Florent Castelli8037fc62024-08-29 13:00:4065 return std::nullopt;
Johannes Kronc3fcee72021-04-19 07:09:2666
Florent Castelli8037fc62024-08-29 13:00:4067 const std::optional<int> i =
Johannes Kronc3fcee72021-04-19 07:09:2668 rtc::StringToNumber<int>(max_frame_rate_it->second);
69 if (!i.has_value() || i.value() <= 0)
Florent Castelli8037fc62024-08-29 13:00:4070 return std::nullopt;
Johannes Kronc3fcee72021-04-19 07:09:2671 return i;
72}
73
Qiu Jianlinb3488d02023-12-07 00:12:1274#ifdef RTC_ENABLE_H265
75// Compares two H265Level and return the smaller.
76H265Level H265LevelMin(H265Level a, H265Level b) {
77 return a <= b ? a : b;
78}
79
80// Returns true if none of profile-id/tier-flag/level-id is specified
81// explicitly in the param.
Philipp Hanckede172522023-12-14 08:45:3982bool IsDefaultH265PTL(const CodecParameterMap& params) {
Qiu Jianlinb3488d02023-12-07 00:12:1283 return !params.count(kH265ProfileId) && !params.count(kH265TierFlag) &&
84 !params.count(kH265LevelId);
85}
86#endif
87
Johannes Kronc3fcee72021-04-19 07:09:2688} // namespace
89
Qiu Jianlinb3488d02023-12-07 00:12:1290#ifdef RTC_ENABLE_H265
91// Set level according to https://tools.ietf.org/html/rfc7798#section-7.1
92void H265GenerateProfileTierLevelForAnswer(
Philipp Hanckede172522023-12-14 08:45:3993 const CodecParameterMap& local_supported_params,
94 const CodecParameterMap& remote_offered_params,
95 CodecParameterMap* answer_params) {
Qiu Jianlinb3488d02023-12-07 00:12:1296 // If local and remote haven't set profile-id/tier-flag/level-id, they
97 // are both using the default PTL In this case, don't set PTL in answer
98 // either.
99 if (IsDefaultH265PTL(local_supported_params) &&
100 IsDefaultH265PTL(remote_offered_params)) {
101 return;
102 }
103
104 // Parse profile-tier-level.
Florent Castelli8037fc62024-08-29 13:00:40105 const std::optional<H265ProfileTierLevel> local_profile_tier_level =
Qiu Jianlinb3488d02023-12-07 00:12:12106 ParseSdpForH265ProfileTierLevel(local_supported_params);
Florent Castelli8037fc62024-08-29 13:00:40107 const std::optional<H265ProfileTierLevel> remote_profile_tier_level =
Qiu Jianlinb3488d02023-12-07 00:12:12108 ParseSdpForH265ProfileTierLevel(remote_offered_params);
109 // Profile and tier for local and remote codec must be valid and equal.
110 RTC_DCHECK(local_profile_tier_level);
111 RTC_DCHECK(remote_profile_tier_level);
112 RTC_DCHECK_EQ(local_profile_tier_level->profile,
113 remote_profile_tier_level->profile);
114 RTC_DCHECK_EQ(local_profile_tier_level->tier,
115 remote_profile_tier_level->tier);
116
117 const H265Level answer_level = H265LevelMin(local_profile_tier_level->level,
118 remote_profile_tier_level->level);
119
120 // Level-id in answer is changable as long as the highest level indicated by
121 // the answer is not higher than that indicated by the offer. See
122 // https://tools.ietf.org/html/rfc7798#section-7.2.2, sub-clause 2.
123 (*answer_params)[kH265LevelId] = H265LevelToString(answer_level);
124}
125#endif
126
Johannes Kronc3fcee72021-04-19 07:09:26127// Set level according to https://tools.ietf.org/html/rfc6184#section-8.2.2.
128void H264GenerateProfileLevelIdForAnswer(
Philipp Hanckede172522023-12-14 08:45:39129 const CodecParameterMap& local_supported_params,
130 const CodecParameterMap& remote_offered_params,
131 CodecParameterMap* answer_params) {
Johannes Kronc3fcee72021-04-19 07:09:26132 // If both local and remote haven't set profile-level-id, they are both using
133 // the default profile. In this case, don't set profile-level-id in answer
134 // either.
135 if (!local_supported_params.count(kProfileLevelId) &&
136 !remote_offered_params.count(kProfileLevelId)) {
137 return;
138 }
139
140 // Parse profile-level-ids.
Florent Castelli8037fc62024-08-29 13:00:40141 const std::optional<H264ProfileLevelId> local_profile_level_id =
Johannes Kronc3fcee72021-04-19 07:09:26142 ParseSdpForH264ProfileLevelId(local_supported_params);
Florent Castelli8037fc62024-08-29 13:00:40143 const std::optional<H264ProfileLevelId> remote_profile_level_id =
Johannes Kronc3fcee72021-04-19 07:09:26144 ParseSdpForH264ProfileLevelId(remote_offered_params);
145 // The local and remote codec must have valid and equal H264 Profiles.
146 RTC_DCHECK(local_profile_level_id);
147 RTC_DCHECK(remote_profile_level_id);
148 RTC_DCHECK_EQ(local_profile_level_id->profile,
149 remote_profile_level_id->profile);
150
151 // Parse level information.
152 const bool level_asymmetry_allowed =
153 IsH264LevelAsymmetryAllowed(local_supported_params) &&
154 IsH264LevelAsymmetryAllowed(remote_offered_params);
155 const H264Level local_level = local_profile_level_id->level;
156 const H264Level remote_level = remote_profile_level_id->level;
157 const H264Level min_level = H264LevelMin(local_level, remote_level);
158
159 // Determine answer level. When level asymmetry is not allowed, level upgrade
160 // is not allowed, i.e., the level in the answer must be equal to or lower
161 // than the level in the offer.
162 const H264Level answer_level =
163 level_asymmetry_allowed ? local_level : min_level;
164
165 // Set the resulting profile-level-id in the answer parameters.
166 (*answer_params)[kProfileLevelId] = *H264ProfileLevelIdToString(
167 H264ProfileLevelId(local_profile_level_id->profile, answer_level));
168}
169
Florent Castelli8037fc62024-08-29 13:00:40170std::optional<int> ParseSdpForVPxMaxFrameRate(const CodecParameterMap& params) {
Johannes Kronc3fcee72021-04-19 07:09:26171 return ParsePositiveNumberFromParams(params, kVPxFmtpMaxFrameRate);
172}
173
Florent Castelli8037fc62024-08-29 13:00:40174std::optional<int> ParseSdpForVPxMaxFrameSize(const CodecParameterMap& params) {
175 const std::optional<int> i =
Johannes Kronc3fcee72021-04-19 07:09:26176 ParsePositiveNumberFromParams(params, kVPxFmtpMaxFrameSize);
Florent Castelli8037fc62024-08-29 13:00:40177 return i ? std::make_optional(i.value() * kVPxFmtpFrameSizeSubBlockPixels)
178 : std::nullopt;
Johannes Kronc3fcee72021-04-19 07:09:26179}
180
Philipp Hancke7c5f9cf2024-02-20 14:28:14181bool SupportsPerLayerPictureLossIndication(const CodecParameterMap& params) {
182 return absl::c_find_if(
183 params, [](const std::pair<std::string, std::string>& kv) {
184 return kv.first ==
185 cricket::kCodecParamPerLayerPictureLossIndication &&
186 kv.second == "1";
187 }) != params.end();
188}
189
Johannes Kronc3fcee72021-04-19 07:09:26190} // namespace webrtc