xref: /aosp_15_r20/external/webrtc/media/base/sdp_video_format_utils.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
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>
15 #include <utility>
16 
17 #include "api/video_codecs/h264_profile_level_id.h"
18 #include "rtc_base/checks.h"
19 #include "rtc_base/string_to_number.h"
20 
21 namespace webrtc {
22 namespace {
23 const char kProfileLevelId[] = "profile-level-id";
24 const char kH264LevelAsymmetryAllowed[] = "level-asymmetry-allowed";
25 // Max frame rate for VP8 and VP9 video.
26 const char kVPxFmtpMaxFrameRate[] = "max-fr";
27 // Max frame size for VP8 and VP9 video.
28 const char kVPxFmtpMaxFrameSize[] = "max-fs";
29 const int kVPxFmtpFrameSizeSubBlockPixels = 256;
30 
IsH264LevelAsymmetryAllowed(const SdpVideoFormat::Parameters & params)31 bool IsH264LevelAsymmetryAllowed(const SdpVideoFormat::Parameters& params) {
32   const auto it = params.find(kH264LevelAsymmetryAllowed);
33   return it != params.end() && strcmp(it->second.c_str(), "1") == 0;
34 }
35 
36 // Compare H264 levels and handle the level 1b case.
H264LevelIsLess(H264Level a,H264Level b)37 bool H264LevelIsLess(H264Level a, H264Level b) {
38   if (a == H264Level::kLevel1_b)
39     return b != H264Level::kLevel1 && b != H264Level::kLevel1_b;
40   if (b == H264Level::kLevel1_b)
41     return a == H264Level::kLevel1;
42   return a < b;
43 }
44 
H264LevelMin(H264Level a,H264Level b)45 H264Level H264LevelMin(H264Level a, H264Level b) {
46   return H264LevelIsLess(a, b) ? a : b;
47 }
48 
ParsePositiveNumberFromParams(const SdpVideoFormat::Parameters & params,const char * parameter_name)49 absl::optional<int> ParsePositiveNumberFromParams(
50     const SdpVideoFormat::Parameters& params,
51     const char* parameter_name) {
52   const auto max_frame_rate_it = params.find(parameter_name);
53   if (max_frame_rate_it == params.end())
54     return absl::nullopt;
55 
56   const absl::optional<int> i =
57       rtc::StringToNumber<int>(max_frame_rate_it->second);
58   if (!i.has_value() || i.value() <= 0)
59     return absl::nullopt;
60   return i;
61 }
62 
63 }  // namespace
64 
65 // Set level according to https://tools.ietf.org/html/rfc6184#section-8.2.2.
H264GenerateProfileLevelIdForAnswer(const SdpVideoFormat::Parameters & local_supported_params,const SdpVideoFormat::Parameters & remote_offered_params,SdpVideoFormat::Parameters * answer_params)66 void H264GenerateProfileLevelIdForAnswer(
67     const SdpVideoFormat::Parameters& local_supported_params,
68     const SdpVideoFormat::Parameters& remote_offered_params,
69     SdpVideoFormat::Parameters* answer_params) {
70   // If both local and remote haven't set profile-level-id, they are both using
71   // the default profile. In this case, don't set profile-level-id in answer
72   // either.
73   if (!local_supported_params.count(kProfileLevelId) &&
74       !remote_offered_params.count(kProfileLevelId)) {
75     return;
76   }
77 
78   // Parse profile-level-ids.
79   const absl::optional<H264ProfileLevelId> local_profile_level_id =
80       ParseSdpForH264ProfileLevelId(local_supported_params);
81   const absl::optional<H264ProfileLevelId> remote_profile_level_id =
82       ParseSdpForH264ProfileLevelId(remote_offered_params);
83   // The local and remote codec must have valid and equal H264 Profiles.
84   RTC_DCHECK(local_profile_level_id);
85   RTC_DCHECK(remote_profile_level_id);
86   RTC_DCHECK_EQ(local_profile_level_id->profile,
87                 remote_profile_level_id->profile);
88 
89   // Parse level information.
90   const bool level_asymmetry_allowed =
91       IsH264LevelAsymmetryAllowed(local_supported_params) &&
92       IsH264LevelAsymmetryAllowed(remote_offered_params);
93   const H264Level local_level = local_profile_level_id->level;
94   const H264Level remote_level = remote_profile_level_id->level;
95   const H264Level min_level = H264LevelMin(local_level, remote_level);
96 
97   // Determine answer level. When level asymmetry is not allowed, level upgrade
98   // is not allowed, i.e., the level in the answer must be equal to or lower
99   // than the level in the offer.
100   const H264Level answer_level =
101       level_asymmetry_allowed ? local_level : min_level;
102 
103   // Set the resulting profile-level-id in the answer parameters.
104   (*answer_params)[kProfileLevelId] = *H264ProfileLevelIdToString(
105       H264ProfileLevelId(local_profile_level_id->profile, answer_level));
106 }
107 
ParseSdpForVPxMaxFrameRate(const SdpVideoFormat::Parameters & params)108 absl::optional<int> ParseSdpForVPxMaxFrameRate(
109     const SdpVideoFormat::Parameters& params) {
110   return ParsePositiveNumberFromParams(params, kVPxFmtpMaxFrameRate);
111 }
112 
ParseSdpForVPxMaxFrameSize(const SdpVideoFormat::Parameters & params)113 absl::optional<int> ParseSdpForVPxMaxFrameSize(
114     const SdpVideoFormat::Parameters& params) {
115   const absl::optional<int> i =
116       ParsePositiveNumberFromParams(params, kVPxFmtpMaxFrameSize);
117   return i ? absl::make_optional(i.value() * kVPxFmtpFrameSizeSubBlockPixels)
118            : absl::nullopt;
119 }
120 
121 }  // namespace webrtc
122