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