blob: c153181da9812d026f60e76c47cabb49153b253b [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
pwestin@webrtc.orgcac78782012-04-05 08:30:10 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:25 +00003 *
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
pbos@webrtc.orga048d7c2013-05-29 14:27:38 +000011#include "webrtc/modules/rtp_rtcp/source/tmmbr_help.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000012
danilchap262e4862016-08-05 03:37:38 -070013#include <algorithm>
danilchapb8b6fbb2015-12-10 05:05:27 -080014#include <limits>
15
danilchapf6ff9712016-02-24 09:23:37 -080016#include "webrtc/base/checks.h"
pbos@webrtc.orga048d7c2013-05-29 14:27:38 +000017#include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_config.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000018
19namespace webrtc {
danilchap262e4862016-08-05 03:37:38 -070020void TMMBRSet::VerifyAndAllocateSet(uint32_t minimumSize) {
danilchapa4f31bd2016-02-29 05:26:01 -080021 clear();
22 reserve(minimumSize);
hta@webrtc.org54536bb2012-05-03 14:07:23 +000023}
24
danilchap262e4862016-08-05 03:37:38 -070025void TMMBRSet::VerifyAndAllocateSetKeepingData(uint32_t minimumSize) {
danilchapa4f31bd2016-02-29 05:26:01 -080026 reserve(minimumSize);
hta@webrtc.org54536bb2012-05-03 14:07:23 +000027}
28
29void TMMBRSet::SetEntry(unsigned int i,
danilchap262e4862016-08-05 03:37:38 -070030 uint32_t tmmbrSet,
31 uint32_t packetOHSet,
32 uint32_t ssrcSet) {
danilchapa4f31bd2016-02-29 05:26:01 -080033 RTC_DCHECK_LT(i, capacity());
34 if (i >= size()) {
danilchap262e4862016-08-05 03:37:38 -070035 resize(i + 1);
hta@webrtc.org54536bb2012-05-03 14:07:23 +000036 }
danilchapa4f31bd2016-02-29 05:26:01 -080037 (*this)[i].set_bitrate_bps(tmmbrSet * 1000);
38 (*this)[i].set_packet_overhead(packetOHSet);
39 (*this)[i].set_ssrc(ssrcSet);
hta@webrtc.org54536bb2012-05-03 14:07:23 +000040}
41
pbos@webrtc.org2f446732013-04-08 11:08:41 +000042void TMMBRSet::AddEntry(uint32_t tmmbrSet,
43 uint32_t packetOHSet,
44 uint32_t ssrcSet) {
danilchapa4f31bd2016-02-29 05:26:01 -080045 RTC_DCHECK_LT(size(), capacity());
46 SetEntry(size(), tmmbrSet, packetOHSet, ssrcSet);
hta@webrtc.org54536bb2012-05-03 14:07:23 +000047}
48
pbos@webrtc.org2f446732013-04-08 11:08:41 +000049void TMMBRSet::RemoveEntry(uint32_t sourceIdx) {
danilchapa4f31bd2016-02-29 05:26:01 -080050 RTC_DCHECK_LT(sourceIdx, size());
51 erase(begin() + sourceIdx);
hta@webrtc.org54536bb2012-05-03 14:07:23 +000052}
53
danilchap262e4862016-08-05 03:37:38 -070054TMMBRSet* TMMBRHelp::VerifyAndAllocateCandidateSet(uint32_t minimumSize) {
55 _candidateSet.VerifyAndAllocateSet(minimumSize);
56 return &_candidateSet;
hta@webrtc.org54536bb2012-05-03 14:07:23 +000057}
58
danilchap262e4862016-08-05 03:37:38 -070059TMMBRSet* TMMBRHelp::CandidateSet() {
60 return &_candidateSet;
niklase@google.com470e71d2011-07-07 08:21:25 +000061}
62
danilchap262e4862016-08-05 03:37:38 -070063int32_t TMMBRHelp::FindTMMBRBoundingSet(TMMBRSet*& boundingSet) {
64 // Work on local variable, will be modified
65 TMMBRSet candidateSet;
66 candidateSet.VerifyAndAllocateSet(_candidateSet.capacity());
niklase@google.com470e71d2011-07-07 08:21:25 +000067
danilchap262e4862016-08-05 03:37:38 -070068 for (size_t i = 0; i < _candidateSet.size(); i++) {
69 if (_candidateSet.Tmmbr(i)) {
70 candidateSet.AddEntry(_candidateSet.Tmmbr(i), _candidateSet.PacketOH(i),
71 _candidateSet.Ssrc(i));
danilchapf6ff9712016-02-24 09:23:37 -080072 } else {
danilchap262e4862016-08-05 03:37:38 -070073 // make sure this is zero if tmmbr = 0
74 RTC_DCHECK_EQ(_candidateSet.PacketOH(i), 0u);
75 // Old code:
76 // _candidateSet.ptrPacketOHSet[i] = 0;
danilchapf6ff9712016-02-24 09:23:37 -080077 }
danilchap262e4862016-08-05 03:37:38 -070078 }
tommi@webrtc.orgeec6ecd2014-07-11 19:09:59 +000079
danilchap262e4862016-08-05 03:37:38 -070080 // Number of set candidates
81 int32_t numSetCandidates = candidateSet.lengthOfSet();
82 // Find bounding set
83 uint32_t numBoundingSet = 0;
84 if (numSetCandidates > 0) {
85 FindBoundingSet(std::move(candidateSet), &_boundingSet);
86 numBoundingSet = _boundingSet.size();
87 if (numBoundingSet < 1 || (numBoundingSet > _candidateSet.size())) {
88 return -1;
tommi@webrtc.orgeec6ecd2014-07-11 19:09:59 +000089 }
danilchap262e4862016-08-05 03:37:38 -070090 boundingSet = &_boundingSet;
91 }
92 return numBoundingSet;
niklase@google.com470e71d2011-07-07 08:21:25 +000093}
94
danilchap262e4862016-08-05 03:37:38 -070095void TMMBRHelp::FindBoundingSet(std::vector<rtcp::TmmbItem> candidates,
96 std::vector<rtcp::TmmbItem>* bounding_set) {
97 RTC_DCHECK(bounding_set);
98 RTC_DCHECK(!candidates.empty());
99 size_t num_candidates = candidates.size();
100
101 if (num_candidates == 1) {
102 RTC_DCHECK(candidates[0].bitrate_bps());
103 *bounding_set = std::move(candidates);
104 return;
105 }
106
107 // 1. Sort by increasing packet overhead.
108 std::sort(candidates.begin(), candidates.end(),
109 [](const rtcp::TmmbItem& lhs, const rtcp::TmmbItem& rhs) {
110 return lhs.packet_overhead() < rhs.packet_overhead();
111 });
112
113 // 2. For tuples with same overhead, keep the one with the lowest bitrate.
114 for (auto it = candidates.begin(); it != candidates.end();) {
115 RTC_DCHECK(it->bitrate_bps());
116 auto current_min = it;
117 auto next_it = it + 1;
118 // Use fact candidates are sorted by overhead, so candidates with same
119 // overhead are adjusted.
120 while (next_it != candidates.end() &&
121 next_it->packet_overhead() == current_min->packet_overhead()) {
122 if (next_it->bitrate_bps() < current_min->bitrate_bps()) {
123 current_min->set_bitrate_bps(0);
124 current_min = next_it;
125 } else {
126 next_it->set_bitrate_bps(0);
127 }
128 ++next_it;
129 --num_candidates;
130 }
131 it = next_it;
132 }
133
134 // 3. Select and remove tuple with lowest tmmbr.
135 // (If more than 1, choose the one with highest overhead).
136 auto min_bitrate_it = candidates.end();
137 for (auto it = candidates.begin(); it != candidates.end(); ++it) {
138 if (it->bitrate_bps()) {
139 min_bitrate_it = it;
140 break;
141 }
142 }
143
144 for (auto it = min_bitrate_it; it != candidates.end(); ++it) {
145 if (it->bitrate_bps() &&
146 it->bitrate_bps() <= min_bitrate_it->bitrate_bps()) {
147 // Get min bitrate.
148 min_bitrate_it = it;
149 }
150 }
151
152 bounding_set->clear();
153 bounding_set->reserve(num_candidates);
154 std::vector<float> intersection(num_candidates);
155 std::vector<float> max_packet_rate(num_candidates);
156
157 // First member of selected list.
158 bounding_set->push_back(*min_bitrate_it);
159 intersection[0] = 0;
160 // Calculate its maximum packet rate (where its line crosses x-axis).
161 uint16_t packet_overhead = bounding_set->back().packet_overhead();
162 if (packet_overhead == 0) {
163 // Avoid division by zero.
164 max_packet_rate[0] = std::numeric_limits<float>::max();
165 } else {
166 max_packet_rate[0] = bounding_set->back().bitrate_bps() /
167 static_cast<float>(packet_overhead);
168 }
169 // Remove from candidate list.
170 min_bitrate_it->set_bitrate_bps(0);
171 --num_candidates;
172
173 // 4. Discard from candidate list all tuple with lower overhead
174 // (next tuple must be steeper).
175 for (auto it = candidates.begin(); it != candidates.end(); ++it) {
176 if (it->bitrate_bps() &&
177 it->packet_overhead() < bounding_set->front().packet_overhead()) {
178 it->set_bitrate_bps(0);
179 --num_candidates;
180 }
181 }
182
183 bool get_new_candidate = true;
184 rtcp::TmmbItem cur_candidate;
185 while (num_candidates > 0) {
186 if (get_new_candidate) {
187 // 5. Remove first remaining tuple from candidate list.
188 for (auto it = candidates.begin(); it != candidates.end(); ++it) {
189 if (it->bitrate_bps()) {
190 cur_candidate = *it;
191 it->set_bitrate_bps(0);
192 break;
193 }
194 }
195 }
196
197 // 6. Calculate packet rate and intersection of the current
198 // line with line of last tuple in selected list.
199 RTC_DCHECK_NE(cur_candidate.packet_overhead(),
200 bounding_set->back().packet_overhead());
201 float packet_rate = static_cast<float>(cur_candidate.bitrate_bps() -
202 bounding_set->back().bitrate_bps()) /
203 (cur_candidate.packet_overhead() -
204 bounding_set->back().packet_overhead());
205
206 // 7. If the packet rate is equal or lower than intersection of
207 // last tuple in selected list,
208 // remove last tuple in selected list & go back to step 6.
209 if (packet_rate <= intersection[bounding_set->size() - 1]) {
210 // Remove last tuple and goto step 6.
211 bounding_set->pop_back();
212 get_new_candidate = false;
213 } else {
214 // 8. If packet rate is lower than maximum packet rate of
215 // last tuple in selected list, add current tuple to selected
216 // list.
217 if (packet_rate < max_packet_rate[bounding_set->size() - 1]) {
218 bounding_set->push_back(cur_candidate);
219 intersection[bounding_set->size() - 1] = packet_rate;
220 uint16_t packet_overhead = bounding_set->back().packet_overhead();
221 RTC_DCHECK_NE(packet_overhead, 0);
222 max_packet_rate[bounding_set->size() - 1] =
223 bounding_set->back().bitrate_bps() /
224 static_cast<float>(packet_overhead);
225 }
226 --num_candidates;
227 get_new_candidate = true;
228 }
229
230 // 9. Go back to step 5 if any tuple remains in candidate list.
231 }
232}
233
234bool TMMBRHelp::IsOwner(const uint32_t ssrc, const uint32_t length) const {
pwestin@webrtc.orgcac78782012-04-05 08:30:10 +0000235 if (length == 0) {
236 // Empty bounding set.
henrike@webrtc.org0ad51862012-03-30 16:54:13 +0000237 return false;
pwestin@webrtc.orgcac78782012-04-05 08:30:10 +0000238 }
danilchap262e4862016-08-05 03:37:38 -0700239 for (size_t i = 0; (i < length) && (i < _boundingSet.size()); ++i) {
240 if (_boundingSet.Ssrc(i) == ssrc) {
pwestin@webrtc.orgcac78782012-04-05 08:30:10 +0000241 return true;
242 }
243 }
244 return false;
niklase@google.com470e71d2011-07-07 08:21:25 +0000245}
246
danilchap262e4862016-08-05 03:37:38 -0700247bool TMMBRHelp::CalcMinBitRate(uint32_t* minBitrateKbit) const {
danilchapa4f31bd2016-02-29 05:26:01 -0800248 if (_candidateSet.size() == 0) {
pwestin@webrtc.orgcac78782012-04-05 08:30:10 +0000249 // Empty bounding set.
250 return false;
251 }
252 *minBitrateKbit = std::numeric_limits<uint32_t>::max();
253
danilchap262e4862016-08-05 03:37:38 -0700254 for (size_t i = 0; i < _candidateSet.lengthOfSet(); ++i) {
pbos@webrtc.org2f446732013-04-08 11:08:41 +0000255 uint32_t curNetBitRateKbit = _candidateSet.Tmmbr(i);
pwestin@webrtc.orgcac78782012-04-05 08:30:10 +0000256 if (curNetBitRateKbit < MIN_VIDEO_BW_MANAGEMENT_BITRATE) {
257 curNetBitRateKbit = MIN_VIDEO_BW_MANAGEMENT_BITRATE;
niklase@google.com470e71d2011-07-07 08:21:25 +0000258 }
danilchap262e4862016-08-05 03:37:38 -0700259 *minBitrateKbit = curNetBitRateKbit < *minBitrateKbit ? curNetBitRateKbit
260 : *minBitrateKbit;
pwestin@webrtc.orgcac78782012-04-05 08:30:10 +0000261 }
262 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000263}
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +0000264} // namespace webrtc