xref: /aosp_15_r20/external/webrtc/pc/used_ids.h (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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