blob: 5e4c6f10f5dcf2a101d525be193fb7b5d7a9cade [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
hta@webrtc.org54536bb2012-05-03 14:07:23 +000013#include <assert.h>
hta@webrtc.org54536bb2012-05-03 14:07:23 +000014#include <string.h>
danilchapb8b6fbb2015-12-10 05:05:27 -080015
16#include <limits>
17
danilchapf6ff9712016-02-24 09:23:37 -080018#include "webrtc/base/checks.h"
pbos@webrtc.orga048d7c2013-05-29 14:27:38 +000019#include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_config.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000020
21namespace webrtc {
niklase@google.com470e71d2011-07-07 08:21:25 +000022void
pbos@webrtc.org2f446732013-04-08 11:08:41 +000023TMMBRSet::VerifyAndAllocateSet(uint32_t minimumSize)
niklase@google.com470e71d2011-07-07 08:21:25 +000024{
danilchapa4f31bd2016-02-29 05:26:01 -080025 clear();
26 reserve(minimumSize);
hta@webrtc.org54536bb2012-05-03 14:07:23 +000027}
28
29void
pbos@webrtc.org2f446732013-04-08 11:08:41 +000030TMMBRSet::VerifyAndAllocateSetKeepingData(uint32_t minimumSize)
hta@webrtc.org54536bb2012-05-03 14:07:23 +000031{
danilchapa4f31bd2016-02-29 05:26:01 -080032 reserve(minimumSize);
hta@webrtc.org54536bb2012-05-03 14:07:23 +000033}
34
35void TMMBRSet::SetEntry(unsigned int i,
pbos@webrtc.org2f446732013-04-08 11:08:41 +000036 uint32_t tmmbrSet,
37 uint32_t packetOHSet,
38 uint32_t ssrcSet) {
danilchapa4f31bd2016-02-29 05:26:01 -080039 RTC_DCHECK_LT(i, capacity());
40 if (i >= size()) {
41 resize(i+1);
hta@webrtc.org54536bb2012-05-03 14:07:23 +000042 }
danilchapa4f31bd2016-02-29 05:26:01 -080043 (*this)[i].set_bitrate_bps(tmmbrSet * 1000);
44 (*this)[i].set_packet_overhead(packetOHSet);
45 (*this)[i].set_ssrc(ssrcSet);
hta@webrtc.org54536bb2012-05-03 14:07:23 +000046}
47
pbos@webrtc.org2f446732013-04-08 11:08:41 +000048void TMMBRSet::AddEntry(uint32_t tmmbrSet,
49 uint32_t packetOHSet,
50 uint32_t ssrcSet) {
danilchapa4f31bd2016-02-29 05:26:01 -080051 RTC_DCHECK_LT(size(), capacity());
52 SetEntry(size(), tmmbrSet, packetOHSet, ssrcSet);
hta@webrtc.org54536bb2012-05-03 14:07:23 +000053}
54
pbos@webrtc.org2f446732013-04-08 11:08:41 +000055void TMMBRSet::RemoveEntry(uint32_t sourceIdx) {
danilchapa4f31bd2016-02-29 05:26:01 -080056 RTC_DCHECK_LT(sourceIdx, size());
57 erase(begin() + sourceIdx);
hta@webrtc.org54536bb2012-05-03 14:07:23 +000058}
59
pbos@webrtc.org2f446732013-04-08 11:08:41 +000060void TMMBRSet::SwapEntries(uint32_t i, uint32_t j) {
danilchapa4f31bd2016-02-29 05:26:01 -080061 using std::swap;
62 swap((*this)[i], (*this)[j]);
hta@webrtc.org54536bb2012-05-03 14:07:23 +000063}
64
pbos@webrtc.org2f446732013-04-08 11:08:41 +000065void TMMBRSet::ClearEntry(uint32_t idx) {
hta@webrtc.org54536bb2012-05-03 14:07:23 +000066 SetEntry(idx, 0, 0, 0);
niklase@google.com470e71d2011-07-07 08:21:25 +000067}
68
pwestin@webrtc.orgcac78782012-04-05 08:30:10 +000069TMMBRHelp::TMMBRHelp()
danilchap7c9426c2016-04-14 03:05:31 -070070 : _candidateSet(),
pwestin@webrtc.orgcac78782012-04-05 08:30:10 +000071 _boundingSet(),
pwestin@webrtc.orgcac78782012-04-05 08:30:10 +000072 _ptrIntersectionBoundingSet(NULL),
73 _ptrMaxPRBoundingSet(NULL) {
niklase@google.com470e71d2011-07-07 08:21:25 +000074}
75
pwestin@webrtc.orgcac78782012-04-05 08:30:10 +000076TMMBRHelp::~TMMBRHelp() {
77 delete [] _ptrIntersectionBoundingSet;
78 delete [] _ptrMaxPRBoundingSet;
79 _ptrIntersectionBoundingSet = 0;
80 _ptrMaxPRBoundingSet = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +000081}
82
83TMMBRSet*
pbos@webrtc.org2f446732013-04-08 11:08:41 +000084TMMBRHelp::VerifyAndAllocateBoundingSet(uint32_t minimumSize)
niklase@google.com470e71d2011-07-07 08:21:25 +000085{
danilchapa4f31bd2016-02-29 05:26:01 -080086 if(minimumSize > _boundingSet.capacity())
niklase@google.com470e71d2011-07-07 08:21:25 +000087 {
88 // make sure that our buffers are big enough
89 if(_ptrIntersectionBoundingSet)
90 {
91 delete [] _ptrIntersectionBoundingSet;
92 delete [] _ptrMaxPRBoundingSet;
93 }
94 _ptrIntersectionBoundingSet = new float[minimumSize];
95 _ptrMaxPRBoundingSet = new float[minimumSize];
96 }
97 _boundingSet.VerifyAndAllocateSet(minimumSize);
98 return &_boundingSet;
99}
100
pwestin@webrtc.orgcac78782012-04-05 08:30:10 +0000101TMMBRSet* TMMBRHelp::BoundingSet() {
102 return &_boundingSet;
niklase@google.com470e71d2011-07-07 08:21:25 +0000103}
104
niklase@google.com470e71d2011-07-07 08:21:25 +0000105TMMBRSet*
pbos@webrtc.org2f446732013-04-08 11:08:41 +0000106TMMBRHelp::VerifyAndAllocateCandidateSet(uint32_t minimumSize)
niklase@google.com470e71d2011-07-07 08:21:25 +0000107{
niklase@google.com470e71d2011-07-07 08:21:25 +0000108 _candidateSet.VerifyAndAllocateSet(minimumSize);
109 return &_candidateSet;
110}
111
112TMMBRSet*
113TMMBRHelp::CandidateSet()
114{
115 return &_candidateSet;
116}
117
pbos@webrtc.org2f446732013-04-08 11:08:41 +0000118int32_t
niklase@google.com470e71d2011-07-07 08:21:25 +0000119TMMBRHelp::FindTMMBRBoundingSet(TMMBRSet*& boundingSet)
120{
niklase@google.com470e71d2011-07-07 08:21:25 +0000121 // Work on local variable, will be modified
122 TMMBRSet candidateSet;
danilchapa4f31bd2016-02-29 05:26:01 -0800123 candidateSet.VerifyAndAllocateSet(_candidateSet.capacity());
niklase@google.com470e71d2011-07-07 08:21:25 +0000124
danilchapa4f31bd2016-02-29 05:26:01 -0800125 for (uint32_t i = 0; i < _candidateSet.size(); i++)
niklase@google.com470e71d2011-07-07 08:21:25 +0000126 {
hta@webrtc.org54536bb2012-05-03 14:07:23 +0000127 if(_candidateSet.Tmmbr(i))
niklase@google.com470e71d2011-07-07 08:21:25 +0000128 {
hta@webrtc.org54536bb2012-05-03 14:07:23 +0000129 candidateSet.AddEntry(_candidateSet.Tmmbr(i),
130 _candidateSet.PacketOH(i),
131 _candidateSet.Ssrc(i));
niklase@google.com470e71d2011-07-07 08:21:25 +0000132 }
133 else
134 {
135 // make sure this is zero if tmmbr = 0
hta@webrtc.org54536bb2012-05-03 14:07:23 +0000136 assert(_candidateSet.PacketOH(i) == 0);
137 // Old code:
138 // _candidateSet.ptrPacketOHSet[i] = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000139 }
140 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000141
hta@webrtc.org54536bb2012-05-03 14:07:23 +0000142 // Number of set candidates
pbos@webrtc.org2f446732013-04-08 11:08:41 +0000143 int32_t numSetCandidates = candidateSet.lengthOfSet();
niklase@google.com470e71d2011-07-07 08:21:25 +0000144 // Find bounding set
pbos@webrtc.org2f446732013-04-08 11:08:41 +0000145 uint32_t numBoundingSet = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000146 if (numSetCandidates > 0)
147 {
148 numBoundingSet = FindTMMBRBoundingSet(numSetCandidates, candidateSet);
danilchapa4f31bd2016-02-29 05:26:01 -0800149 if(numBoundingSet < 1 || (numBoundingSet > _candidateSet.size()))
niklase@google.com470e71d2011-07-07 08:21:25 +0000150 {
151 return -1;
152 }
153 boundingSet = &_boundingSet;
154 }
155 return numBoundingSet;
156}
157
158
pbos@webrtc.org2f446732013-04-08 11:08:41 +0000159int32_t
160TMMBRHelp::FindTMMBRBoundingSet(int32_t numCandidates, TMMBRSet& candidateSet)
niklase@google.com470e71d2011-07-07 08:21:25 +0000161{
pbos@webrtc.org2f446732013-04-08 11:08:41 +0000162 uint32_t numBoundingSet = 0;
danilchapa4f31bd2016-02-29 05:26:01 -0800163 VerifyAndAllocateBoundingSet(candidateSet.capacity());
niklase@google.com470e71d2011-07-07 08:21:25 +0000164
165 if (numCandidates == 1)
166 {
danilchapa4f31bd2016-02-29 05:26:01 -0800167 for (uint32_t i = 0; i < candidateSet.size(); i++)
niklase@google.com470e71d2011-07-07 08:21:25 +0000168 {
hta@webrtc.org54536bb2012-05-03 14:07:23 +0000169 if (candidateSet.Tmmbr(i) > 0)
niklase@google.com470e71d2011-07-07 08:21:25 +0000170 {
hta@webrtc.org54536bb2012-05-03 14:07:23 +0000171 _boundingSet.AddEntry(candidateSet.Tmmbr(i),
172 candidateSet.PacketOH(i),
173 candidateSet.Ssrc(i));
niklase@google.com470e71d2011-07-07 08:21:25 +0000174 numBoundingSet++;
175 }
176 }
tommi@webrtc.orgeec6ecd2014-07-11 19:09:59 +0000177 return (numBoundingSet == 1) ? 1 : -1;
niklase@google.com470e71d2011-07-07 08:21:25 +0000178 }
tommi@webrtc.orgeec6ecd2014-07-11 19:09:59 +0000179
180 // 1. Sort by increasing packetOH
danilchapa4f31bd2016-02-29 05:26:01 -0800181 for (int i = candidateSet.size() - 1; i >= 0; i--)
tommi@webrtc.orgeec6ecd2014-07-11 19:09:59 +0000182 {
183 for (int j = 1; j <= i; j++)
184 {
185 if (candidateSet.PacketOH(j-1) > candidateSet.PacketOH(j))
186 {
187 candidateSet.SwapEntries(j-1, j);
188 }
189 }
190 }
191 // 2. For tuples with same OH, keep the one w/ the lowest bitrate
danilchapa4f31bd2016-02-29 05:26:01 -0800192 for (uint32_t i = 0; i < candidateSet.size(); i++)
tommi@webrtc.orgeec6ecd2014-07-11 19:09:59 +0000193 {
194 if (candidateSet.Tmmbr(i) > 0)
195 {
196 // get min bitrate for packets w/ same OH
197 uint32_t currentPacketOH = candidateSet.PacketOH(i);
198 uint32_t currentMinTMMBR = candidateSet.Tmmbr(i);
199 uint32_t currentMinIndexTMMBR = i;
danilchapa4f31bd2016-02-29 05:26:01 -0800200 for (uint32_t j = i+1; j < candidateSet.size(); j++)
tommi@webrtc.orgeec6ecd2014-07-11 19:09:59 +0000201 {
202 if(candidateSet.PacketOH(j) == currentPacketOH)
203 {
204 if(candidateSet.Tmmbr(j) < currentMinTMMBR)
205 {
206 currentMinTMMBR = candidateSet.Tmmbr(j);
207 currentMinIndexTMMBR = j;
208 }
209 }
210 }
211 // keep lowest bitrate
danilchapa4f31bd2016-02-29 05:26:01 -0800212 for (uint32_t j = 0; j < candidateSet.size(); j++)
tommi@webrtc.orgeec6ecd2014-07-11 19:09:59 +0000213 {
214 if(candidateSet.PacketOH(j) == currentPacketOH
215 && j != currentMinIndexTMMBR)
216 {
217 candidateSet.ClearEntry(j);
danilchapf6ff9712016-02-24 09:23:37 -0800218 numCandidates--;
tommi@webrtc.orgeec6ecd2014-07-11 19:09:59 +0000219 }
220 }
221 }
222 }
223 // 3. Select and remove tuple w/ lowest tmmbr.
224 // (If more than 1, choose the one w/ highest OH).
225 uint32_t minTMMBR = 0;
226 uint32_t minIndexTMMBR = 0;
danilchapa4f31bd2016-02-29 05:26:01 -0800227 for (uint32_t i = 0; i < candidateSet.size(); i++)
tommi@webrtc.orgeec6ecd2014-07-11 19:09:59 +0000228 {
229 if (candidateSet.Tmmbr(i) > 0)
230 {
231 minTMMBR = candidateSet.Tmmbr(i);
232 minIndexTMMBR = i;
233 break;
234 }
235 }
236
danilchapa4f31bd2016-02-29 05:26:01 -0800237 for (uint32_t i = 0; i < candidateSet.size(); i++)
tommi@webrtc.orgeec6ecd2014-07-11 19:09:59 +0000238 {
239 if (candidateSet.Tmmbr(i) > 0 && candidateSet.Tmmbr(i) <= minTMMBR)
240 {
241 // get min bitrate
242 minTMMBR = candidateSet.Tmmbr(i);
243 minIndexTMMBR = i;
244 }
245 }
246 // first member of selected list
247 _boundingSet.SetEntry(numBoundingSet,
248 candidateSet.Tmmbr(minIndexTMMBR),
249 candidateSet.PacketOH(minIndexTMMBR),
250 candidateSet.Ssrc(minIndexTMMBR));
251
252 // set intersection value
253 _ptrIntersectionBoundingSet[numBoundingSet] = 0;
254 // calculate its maximum packet rate (where its line crosses x-axis)
danilchapf6ff9712016-02-24 09:23:37 -0800255 uint32_t packet_overhead_bits = 8 * _boundingSet.PacketOH(numBoundingSet);
256 if (packet_overhead_bits == 0) {
257 // Avoid division by zero.
258 _ptrMaxPRBoundingSet[numBoundingSet] = std::numeric_limits<float>::max();
259 } else {
260 _ptrMaxPRBoundingSet[numBoundingSet] =
261 _boundingSet.Tmmbr(numBoundingSet) * 1000 /
262 static_cast<float>(packet_overhead_bits);
263 }
tommi@webrtc.orgeec6ecd2014-07-11 19:09:59 +0000264 numBoundingSet++;
265 // remove from candidate list
266 candidateSet.ClearEntry(minIndexTMMBR);
267 numCandidates--;
268
269 // 4. Discard from candidate list all tuple w/ lower OH
270 // (next tuple must be steeper)
danilchapa4f31bd2016-02-29 05:26:01 -0800271 for (uint32_t i = 0; i < candidateSet.size(); i++)
tommi@webrtc.orgeec6ecd2014-07-11 19:09:59 +0000272 {
273 if(candidateSet.Tmmbr(i) > 0
274 && candidateSet.PacketOH(i) < _boundingSet.PacketOH(0))
275 {
276 candidateSet.ClearEntry(i);
277 numCandidates--;
278 }
279 }
280
281 if (numCandidates == 0)
282 {
283 // Should be true already:_boundingSet.lengthOfSet = numBoundingSet;
284 assert(_boundingSet.lengthOfSet() == numBoundingSet);
285 return numBoundingSet;
286 }
287
288 bool getNewCandidate = true;
danilchapf6ff9712016-02-24 09:23:37 -0800289 uint32_t curCandidateTMMBR = 0;
290 size_t curCandidateIndex = 0;
291 uint32_t curCandidatePacketOH = 0;
292 uint32_t curCandidateSSRC = 0;
tommi@webrtc.orgeec6ecd2014-07-11 19:09:59 +0000293 do
294 {
295 if (getNewCandidate)
296 {
297 // 5. Remove first remaining tuple from candidate list
danilchapa4f31bd2016-02-29 05:26:01 -0800298 for (uint32_t i = 0; i < candidateSet.size(); i++)
tommi@webrtc.orgeec6ecd2014-07-11 19:09:59 +0000299 {
300 if (candidateSet.Tmmbr(i) > 0)
301 {
302 curCandidateTMMBR = candidateSet.Tmmbr(i);
303 curCandidatePacketOH = candidateSet.PacketOH(i);
304 curCandidateSSRC = candidateSet.Ssrc(i);
305 curCandidateIndex = i;
306 candidateSet.ClearEntry(curCandidateIndex);
307 break;
308 }
309 }
310 }
311
312 // 6. Calculate packet rate and intersection of the current
313 // line with line of last tuple in selected list
danilchapf6ff9712016-02-24 09:23:37 -0800314 RTC_DCHECK_NE(curCandidatePacketOH,
315 _boundingSet.PacketOH(numBoundingSet - 1));
tommi@webrtc.orgeec6ecd2014-07-11 19:09:59 +0000316 float packetRate
317 = float(curCandidateTMMBR
318 - _boundingSet.Tmmbr(numBoundingSet-1))*1000
319 / (8*(curCandidatePacketOH
320 - _boundingSet.PacketOH(numBoundingSet-1)));
321
322 // 7. If the packet rate is equal or lower than intersection of
323 // last tuple in selected list,
324 // remove last tuple in selected list & go back to step 6
325 if(packetRate <= _ptrIntersectionBoundingSet[numBoundingSet-1])
326 {
327 // remove last tuple and goto step 6
328 numBoundingSet--;
329 _boundingSet.ClearEntry(numBoundingSet);
330 _ptrIntersectionBoundingSet[numBoundingSet] = 0;
331 _ptrMaxPRBoundingSet[numBoundingSet] = 0;
332 getNewCandidate = false;
333 } else
334 {
335 // 8. If packet rate is lower than maximum packet rate of
336 // last tuple in selected list, add current tuple to selected
337 // list
338 if (packetRate < _ptrMaxPRBoundingSet[numBoundingSet-1])
339 {
340 _boundingSet.SetEntry(numBoundingSet,
341 curCandidateTMMBR,
342 curCandidatePacketOH,
343 curCandidateSSRC);
344 _ptrIntersectionBoundingSet[numBoundingSet] = packetRate;
danilchapf6ff9712016-02-24 09:23:37 -0800345 float packet_overhead_bits =
346 8 * _boundingSet.PacketOH(numBoundingSet);
347 RTC_DCHECK_NE(packet_overhead_bits, 0.0f);
348 _ptrMaxPRBoundingSet[numBoundingSet] =
349 _boundingSet.Tmmbr(numBoundingSet) * 1000 /
350 packet_overhead_bits;
tommi@webrtc.orgeec6ecd2014-07-11 19:09:59 +0000351 numBoundingSet++;
352 }
353 numCandidates--;
354 getNewCandidate = true;
355 }
356
357 // 9. Go back to step 5 if any tuple remains in candidate list
358 } while (numCandidates > 0);
359
niklase@google.com470e71d2011-07-07 08:21:25 +0000360 return numBoundingSet;
361}
362
pbos@webrtc.org2f446732013-04-08 11:08:41 +0000363bool TMMBRHelp::IsOwner(const uint32_t ssrc,
364 const uint32_t length) const {
pwestin@webrtc.orgcac78782012-04-05 08:30:10 +0000365 if (length == 0) {
366 // Empty bounding set.
henrike@webrtc.org0ad51862012-03-30 16:54:13 +0000367 return false;
pwestin@webrtc.orgcac78782012-04-05 08:30:10 +0000368 }
pbos@webrtc.org2f446732013-04-08 11:08:41 +0000369 for(uint32_t i = 0;
danilchapa4f31bd2016-02-29 05:26:01 -0800370 (i < length) && (i < _boundingSet.size()); ++i) {
hta@webrtc.org54536bb2012-05-03 14:07:23 +0000371 if(_boundingSet.Ssrc(i) == ssrc) {
pwestin@webrtc.orgcac78782012-04-05 08:30:10 +0000372 return true;
373 }
374 }
375 return false;
niklase@google.com470e71d2011-07-07 08:21:25 +0000376}
377
pbos@webrtc.org2f446732013-04-08 11:08:41 +0000378bool TMMBRHelp::CalcMinBitRate( uint32_t* minBitrateKbit) const {
danilchapa4f31bd2016-02-29 05:26:01 -0800379 if (_candidateSet.size() == 0) {
pwestin@webrtc.orgcac78782012-04-05 08:30:10 +0000380 // Empty bounding set.
381 return false;
382 }
383 *minBitrateKbit = std::numeric_limits<uint32_t>::max();
384
pbos@webrtc.org2f446732013-04-08 11:08:41 +0000385 for (uint32_t i = 0; i < _candidateSet.lengthOfSet(); ++i) {
386 uint32_t curNetBitRateKbit = _candidateSet.Tmmbr(i);
pwestin@webrtc.orgcac78782012-04-05 08:30:10 +0000387 if (curNetBitRateKbit < MIN_VIDEO_BW_MANAGEMENT_BITRATE) {
388 curNetBitRateKbit = MIN_VIDEO_BW_MANAGEMENT_BITRATE;
niklase@google.com470e71d2011-07-07 08:21:25 +0000389 }
pwestin@webrtc.orgcac78782012-04-05 08:30:10 +0000390 *minBitrateKbit = curNetBitRateKbit < *minBitrateKbit ?
391 curNetBitRateKbit : *minBitrateKbit;
392 }
393 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000394}
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +0000395} // namespace webrtc