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 RTC_LOG(LS_WARNING) << "Duplicate id found. Reassigning from " 56 << original_id << " to " << new_id; 57 idstruct->id = new_id; 58 } 59 SetIdUsed(new_id); 60 } 61 62 protected: IsIdUsed(int new_id)63 bool IsIdUsed(int new_id) { return id_set_.find(new_id) != id_set_.end(); } 64 const int min_allowed_id_; 65 const int max_allowed_id_; 66 67 private: 68 // Returns the first unused id in reverse order. 69 // This hopefully reduces the risk of more collisions. We want to change the 70 // default ids as little as possible. This function is virtual and can be 71 // overriden if the search for unused IDs should follow a specific pattern. FindUnusedId()72 virtual int FindUnusedId() { 73 while (IsIdUsed(next_id_) && next_id_ >= min_allowed_id_) { 74 --next_id_; 75 } 76 RTC_DCHECK(next_id_ >= min_allowed_id_); 77 return next_id_; 78 } 79 SetIdUsed(int new_id)80 void SetIdUsed(int new_id) { 81 RTC_DCHECK(new_id >= min_allowed_id_); 82 RTC_DCHECK(new_id <= max_allowed_id_); 83 RTC_DCHECK(!IsIdUsed(new_id)); 84 id_set_.insert(new_id); 85 } 86 int next_id_; 87 std::set<int> id_set_; 88 }; 89 90 // Helper class used for finding duplicate RTP payload types among audio, video 91 // and data codecs. When bundle is used the payload types may not collide. 92 class UsedPayloadTypes : public UsedIds<Codec> { 93 public: UsedPayloadTypes()94 UsedPayloadTypes() 95 : UsedIds<Codec>(kDynamicPayloadTypeMin, kDynamicPayloadTypeMax) {} 96 97 private: 98 static const int kDynamicPayloadTypeMin = 96; 99 static const int kDynamicPayloadTypeMax = 127; 100 }; 101 102 // Helper class used for finding duplicate RTP Header extension ids among 103 // audio and video extensions. 104 class UsedRtpHeaderExtensionIds : public UsedIds<webrtc::RtpExtension> { 105 public: 106 enum class IdDomain { 107 // Only allocate IDs that fit in one-byte header extensions. 108 kOneByteOnly, 109 // Prefer to allocate one-byte header extension IDs, but overflow to 110 // two-byte if none are left. 111 kTwoByteAllowed, 112 }; 113 UsedRtpHeaderExtensionIds(IdDomain id_domain)114 explicit UsedRtpHeaderExtensionIds(IdDomain id_domain) 115 : UsedIds<webrtc::RtpExtension>( 116 webrtc::RtpExtension::kMinId, 117 id_domain == IdDomain::kTwoByteAllowed 118 ? webrtc::RtpExtension::kMaxId 119 : webrtc::RtpExtension::kOneByteHeaderExtensionMaxId), 120 id_domain_(id_domain), 121 next_extension_id_(webrtc::RtpExtension::kOneByteHeaderExtensionMaxId) { 122 } 123 124 private: 125 // Returns the first unused id in reverse order from the max id of one byte 126 // header extensions. This hopefully reduce the risk of more collisions. We 127 // want to change the default ids as little as possible. If no unused id is 128 // found and two byte header extensions are enabled (i.e., 129 // |extmap_allow_mixed_| is true), search for unused ids from 15 to 255. FindUnusedId()130 int FindUnusedId() override { 131 if (next_extension_id_ <= 132 webrtc::RtpExtension::kOneByteHeaderExtensionMaxId) { 133 // First search in reverse order from the max id of one byte header 134 // extensions. 135 while (IsIdUsed(next_extension_id_) && 136 next_extension_id_ >= min_allowed_id_) { 137 --next_extension_id_; 138 } 139 } 140 141 if (id_domain_ == IdDomain::kTwoByteAllowed) { 142 if (next_extension_id_ < min_allowed_id_) { 143 // We have searched among all one-byte IDs without finding an unused ID, 144 // continue at the first two-byte ID. 145 next_extension_id_ = 146 webrtc::RtpExtension::kOneByteHeaderExtensionMaxId + 1; 147 } 148 149 if (next_extension_id_ > 150 webrtc::RtpExtension::kOneByteHeaderExtensionMaxId) { 151 while (IsIdUsed(next_extension_id_) && 152 next_extension_id_ <= max_allowed_id_) { 153 ++next_extension_id_; 154 } 155 } 156 } 157 RTC_DCHECK(next_extension_id_ >= min_allowed_id_); 158 RTC_DCHECK(next_extension_id_ <= max_allowed_id_); 159 return next_extension_id_; 160 } 161 162 const IdDomain id_domain_; 163 int next_extension_id_; 164 }; 165 166 } // namespace cricket 167 168 #endif // PC_USED_IDS_H_ 169