1 /* 2 * Copyright 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 #ifndef PC_USED_IDS_H_ 11 #define PC_USED_IDS_H_ 12 13 #include <set> 14 #include <vector> 15 16 #include "api/rtp_parameters.h" 17 #include "media/base/codec.h" 18 #include "rtc_base/checks.h" 19 #include "rtc_base/logging.h" 20 21 namespace cricket { 22 template <typename IdStruct> 23 class UsedIds { 24 public: UsedIds(int min_allowed_id,int max_allowed_id)25 UsedIds(int min_allowed_id, int max_allowed_id) 26 : min_allowed_id_(min_allowed_id), 27 max_allowed_id_(max_allowed_id), 28 next_id_(max_allowed_id) {} ~UsedIds()29 virtual ~UsedIds() {} 30 31 // Loops through all Id in `ids` and changes its id if it is 32 // already in use by another IdStruct. Call this methods with all Id 33 // in a session description to make sure no duplicate ids exists. 34 // Note that typename Id must be a type of IdStruct. 35 template <typename Id> FindAndSetIdUsed(std::vector<Id> * ids)36 void FindAndSetIdUsed(std::vector<Id>* ids) { 37 for (const Id& id : *ids) { 38 FindAndSetIdUsed(&id); 39 } 40 } 41 42 // Finds and sets an unused id if the `idstruct` id is already in use. FindAndSetIdUsed(IdStruct * idstruct)43 void FindAndSetIdUsed(IdStruct* idstruct) { 44 const int original_id = idstruct->id; 45 int new_id = idstruct->id; 46 47 if (original_id > max_allowed_id_ || original_id < min_allowed_id_) { 48 // If the original id is not in range - this is an id that can't be 49 // dynamically changed. 50 return; 51 } 52 53 if (IsIdUsed(original_id)) { 54 new_id = FindUnusedId(); 55 // Duplicate id found. Reassign from the original id to the new. 56 idstruct->id = new_id; 57 } 58 SetIdUsed(new_id); 59 } 60 61 protected: IsIdUsed(int new_id)62 virtual bool IsIdUsed(int new_id) { 63 return id_set_.find(new_id) != id_set_.end(); 64 } 65 const int min_allowed_id_; 66 const int max_allowed_id_; 67 68 private: 69 // Returns the first unused id in reverse order. 70 // This hopefully reduces the risk of more collisions. We want to change the 71 // default ids as little as possible. This function is virtual and can be 72 // overriden if the search for unused IDs should follow a specific pattern. FindUnusedId()73 virtual int FindUnusedId() { 74 while (IsIdUsed(next_id_) && next_id_ >= min_allowed_id_) { 75 --next_id_; 76 } 77 RTC_DCHECK(next_id_ >= min_allowed_id_); 78 return next_id_; 79 } 80 SetIdUsed(int new_id)81 void SetIdUsed(int new_id) { 82 RTC_DCHECK(new_id >= min_allowed_id_); 83 RTC_DCHECK(new_id <= max_allowed_id_); 84 RTC_DCHECK(!IsIdUsed(new_id)); 85 id_set_.insert(new_id); 86 } 87 int next_id_; 88 std::set<int> id_set_; 89 }; 90 91 // Helper class used for finding duplicate RTP payload types among audio, video 92 // and data codecs. When bundle is used the payload types may not collide. 93 class UsedPayloadTypes : public UsedIds<Codec> { 94 public: UsedPayloadTypes()95 UsedPayloadTypes() 96 : UsedIds<Codec>(kFirstDynamicPayloadTypeLowerRange, 97 kLastDynamicPayloadTypeUpperRange) {} 98 99 protected: IsIdUsed(int new_id)100 bool IsIdUsed(int new_id) override { 101 // Range marked for RTCP avoidance is "used". 102 if (new_id > kLastDynamicPayloadTypeLowerRange && 103 new_id < kFirstDynamicPayloadTypeUpperRange) 104 return true; 105 return UsedIds<Codec>::IsIdUsed(new_id); 106 } 107 108 private: 109 static const int kFirstDynamicPayloadTypeLowerRange = 35; 110 static const int kLastDynamicPayloadTypeLowerRange = 63; 111 112 static const int kFirstDynamicPayloadTypeUpperRange = 96; 113 static const int kLastDynamicPayloadTypeUpperRange = 127; 114 }; 115 116 // Helper class used for finding duplicate RTP Header extension ids among 117 // audio and video extensions. 118 class UsedRtpHeaderExtensionIds : public UsedIds<webrtc::RtpExtension> { 119 public: 120 enum class IdDomain { 121 // Only allocate IDs that fit in one-byte header extensions. 122 kOneByteOnly, 123 // Prefer to allocate one-byte header extension IDs, but overflow to 124 // two-byte if none are left. 125 kTwoByteAllowed, 126 }; 127 UsedRtpHeaderExtensionIds(IdDomain id_domain)128 explicit UsedRtpHeaderExtensionIds(IdDomain id_domain) 129 : UsedIds<webrtc::RtpExtension>( 130 webrtc::RtpExtension::kMinId, 131 id_domain == IdDomain::kTwoByteAllowed 132 ? webrtc::RtpExtension::kMaxId 133 : webrtc::RtpExtension::kOneByteHeaderExtensionMaxId), 134 id_domain_(id_domain), 135 next_extension_id_(webrtc::RtpExtension::kOneByteHeaderExtensionMaxId) { 136 } 137 138 private: 139 // Returns the first unused id in reverse order from the max id of one byte 140 // header extensions. This hopefully reduce the risk of more collisions. We 141 // want to change the default ids as little as possible. If no unused id is 142 // found and two byte header extensions are enabled (i.e., 143 // `extmap_allow_mixed_` is true), search for unused ids from 15 to 255. FindUnusedId()144 int FindUnusedId() override { 145 if (next_extension_id_ <= 146 webrtc::RtpExtension::kOneByteHeaderExtensionMaxId) { 147 // First search in reverse order from the max id of one byte header 148 // extensions. 149 while (IsIdUsed(next_extension_id_) && 150 next_extension_id_ >= min_allowed_id_) { 151 --next_extension_id_; 152 } 153 } 154 155 if (id_domain_ == IdDomain::kTwoByteAllowed) { 156 if (next_extension_id_ < min_allowed_id_) { 157 // We have searched among all one-byte IDs without finding an unused ID, 158 // continue at the first two-byte ID. 159 next_extension_id_ = 160 webrtc::RtpExtension::kOneByteHeaderExtensionMaxId + 1; 161 } 162 163 if (next_extension_id_ > 164 webrtc::RtpExtension::kOneByteHeaderExtensionMaxId) { 165 while (IsIdUsed(next_extension_id_) && 166 next_extension_id_ <= max_allowed_id_) { 167 ++next_extension_id_; 168 } 169 } 170 } 171 RTC_DCHECK(next_extension_id_ >= min_allowed_id_); 172 RTC_DCHECK(next_extension_id_ <= max_allowed_id_); 173 return next_extension_id_; 174 } 175 176 const IdDomain id_domain_; 177 int next_extension_id_; 178 }; 179 180 } // namespace cricket 181 182 #endif // PC_USED_IDS_H_ 183