1 /*
2 * Copyright (c) 2012 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
11 #include "webrtc/modules/rtp_rtcp/source/tmmbr_help.h"
12
13 #include <assert.h>
14 #include <string.h>
15
16 #include <limits>
17
18 #include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_config.h"
19
20 namespace webrtc {
TMMBRSet()21 TMMBRSet::TMMBRSet() :
22 _sizeOfSet(0),
23 _lengthOfSet(0)
24 {
25 }
26
~TMMBRSet()27 TMMBRSet::~TMMBRSet()
28 {
29 _sizeOfSet = 0;
30 _lengthOfSet = 0;
31 }
32
33 void
VerifyAndAllocateSet(uint32_t minimumSize)34 TMMBRSet::VerifyAndAllocateSet(uint32_t minimumSize)
35 {
36 if(minimumSize > _sizeOfSet)
37 {
38 // make sure that our buffers are big enough
39 _data.resize(minimumSize);
40 _sizeOfSet = minimumSize;
41 }
42 // reset memory
43 for(uint32_t i = 0; i < _sizeOfSet; i++)
44 {
45 _data.at(i).tmmbr = 0;
46 _data.at(i).packet_oh = 0;
47 _data.at(i).ssrc = 0;
48 }
49 _lengthOfSet = 0;
50 }
51
52 void
VerifyAndAllocateSetKeepingData(uint32_t minimumSize)53 TMMBRSet::VerifyAndAllocateSetKeepingData(uint32_t minimumSize)
54 {
55 if(minimumSize > _sizeOfSet)
56 {
57 {
58 _data.resize(minimumSize);
59 }
60 _sizeOfSet = minimumSize;
61 }
62 }
63
SetEntry(unsigned int i,uint32_t tmmbrSet,uint32_t packetOHSet,uint32_t ssrcSet)64 void TMMBRSet::SetEntry(unsigned int i,
65 uint32_t tmmbrSet,
66 uint32_t packetOHSet,
67 uint32_t ssrcSet) {
68 assert(i < _sizeOfSet);
69 _data.at(i).tmmbr = tmmbrSet;
70 _data.at(i).packet_oh = packetOHSet;
71 _data.at(i).ssrc = ssrcSet;
72 if (i >= _lengthOfSet) {
73 _lengthOfSet = i + 1;
74 }
75 }
76
AddEntry(uint32_t tmmbrSet,uint32_t packetOHSet,uint32_t ssrcSet)77 void TMMBRSet::AddEntry(uint32_t tmmbrSet,
78 uint32_t packetOHSet,
79 uint32_t ssrcSet) {
80 assert(_lengthOfSet < _sizeOfSet);
81 SetEntry(_lengthOfSet, tmmbrSet, packetOHSet, ssrcSet);
82 }
83
RemoveEntry(uint32_t sourceIdx)84 void TMMBRSet::RemoveEntry(uint32_t sourceIdx) {
85 assert(sourceIdx < _lengthOfSet);
86 _data.erase(_data.begin() + sourceIdx);
87 _lengthOfSet--;
88 _data.resize(_sizeOfSet); // Ensure that size remains the same.
89 }
90
SwapEntries(uint32_t i,uint32_t j)91 void TMMBRSet::SwapEntries(uint32_t i, uint32_t j) {
92 SetElement temp;
93 temp = _data[i];
94 _data[i] = _data[j];
95 _data[j] = temp;
96 }
97
ClearEntry(uint32_t idx)98 void TMMBRSet::ClearEntry(uint32_t idx) {
99 SetEntry(idx, 0, 0, 0);
100 }
101
TMMBRHelp()102 TMMBRHelp::TMMBRHelp()
103 : _criticalSection(CriticalSectionWrapper::CreateCriticalSection()),
104 _candidateSet(),
105 _boundingSet(),
106 _boundingSetToSend(),
107 _ptrIntersectionBoundingSet(NULL),
108 _ptrMaxPRBoundingSet(NULL) {
109 }
110
~TMMBRHelp()111 TMMBRHelp::~TMMBRHelp() {
112 delete [] _ptrIntersectionBoundingSet;
113 delete [] _ptrMaxPRBoundingSet;
114 _ptrIntersectionBoundingSet = 0;
115 _ptrMaxPRBoundingSet = 0;
116 delete _criticalSection;
117 }
118
119 TMMBRSet*
VerifyAndAllocateBoundingSet(uint32_t minimumSize)120 TMMBRHelp::VerifyAndAllocateBoundingSet(uint32_t minimumSize)
121 {
122 CriticalSectionScoped lock(_criticalSection);
123
124 if(minimumSize > _boundingSet.sizeOfSet())
125 {
126 // make sure that our buffers are big enough
127 if(_ptrIntersectionBoundingSet)
128 {
129 delete [] _ptrIntersectionBoundingSet;
130 delete [] _ptrMaxPRBoundingSet;
131 }
132 _ptrIntersectionBoundingSet = new float[minimumSize];
133 _ptrMaxPRBoundingSet = new float[minimumSize];
134 }
135 _boundingSet.VerifyAndAllocateSet(minimumSize);
136 return &_boundingSet;
137 }
138
BoundingSet()139 TMMBRSet* TMMBRHelp::BoundingSet() {
140 return &_boundingSet;
141 }
142
143 int32_t
SetTMMBRBoundingSetToSend(const TMMBRSet * boundingSetToSend,const uint32_t maxBitrateKbit)144 TMMBRHelp::SetTMMBRBoundingSetToSend(const TMMBRSet* boundingSetToSend,
145 const uint32_t maxBitrateKbit)
146 {
147 CriticalSectionScoped lock(_criticalSection);
148
149 if (boundingSetToSend == NULL)
150 {
151 _boundingSetToSend.clearSet();
152 return 0;
153 }
154
155 VerifyAndAllocateBoundingSetToSend(boundingSetToSend->lengthOfSet());
156 _boundingSetToSend.clearSet();
157 for (uint32_t i = 0; i < boundingSetToSend->lengthOfSet(); i++)
158 {
159 // cap at our configured max bitrate
160 uint32_t bitrate = boundingSetToSend->Tmmbr(i);
161 if(maxBitrateKbit)
162 {
163 // do we have a configured max bitrate?
164 if(bitrate > maxBitrateKbit)
165 {
166 bitrate = maxBitrateKbit;
167 }
168 }
169 _boundingSetToSend.SetEntry(i, bitrate,
170 boundingSetToSend->PacketOH(i),
171 boundingSetToSend->Ssrc(i));
172 }
173 return 0;
174 }
175
176 int32_t
VerifyAndAllocateBoundingSetToSend(uint32_t minimumSize)177 TMMBRHelp::VerifyAndAllocateBoundingSetToSend(uint32_t minimumSize)
178 {
179 CriticalSectionScoped lock(_criticalSection);
180
181 _boundingSetToSend.VerifyAndAllocateSet(minimumSize);
182 return 0;
183 }
184
185 TMMBRSet*
VerifyAndAllocateCandidateSet(uint32_t minimumSize)186 TMMBRHelp::VerifyAndAllocateCandidateSet(uint32_t minimumSize)
187 {
188 CriticalSectionScoped lock(_criticalSection);
189
190 _candidateSet.VerifyAndAllocateSet(minimumSize);
191 return &_candidateSet;
192 }
193
194 TMMBRSet*
CandidateSet()195 TMMBRHelp::CandidateSet()
196 {
197 return &_candidateSet;
198 }
199
200 TMMBRSet*
BoundingSetToSend()201 TMMBRHelp::BoundingSetToSend()
202 {
203 return &_boundingSetToSend;
204 }
205
206 int32_t
FindTMMBRBoundingSet(TMMBRSet * & boundingSet)207 TMMBRHelp::FindTMMBRBoundingSet(TMMBRSet*& boundingSet)
208 {
209 CriticalSectionScoped lock(_criticalSection);
210
211 // Work on local variable, will be modified
212 TMMBRSet candidateSet;
213 candidateSet.VerifyAndAllocateSet(_candidateSet.sizeOfSet());
214
215 // TODO(hta) Figure out if this should be lengthOfSet instead.
216 for (uint32_t i = 0; i < _candidateSet.sizeOfSet(); i++)
217 {
218 if(_candidateSet.Tmmbr(i))
219 {
220 candidateSet.AddEntry(_candidateSet.Tmmbr(i),
221 _candidateSet.PacketOH(i),
222 _candidateSet.Ssrc(i));
223 }
224 else
225 {
226 // make sure this is zero if tmmbr = 0
227 assert(_candidateSet.PacketOH(i) == 0);
228 // Old code:
229 // _candidateSet.ptrPacketOHSet[i] = 0;
230 }
231 }
232
233 // Number of set candidates
234 int32_t numSetCandidates = candidateSet.lengthOfSet();
235 // Find bounding set
236 uint32_t numBoundingSet = 0;
237 if (numSetCandidates > 0)
238 {
239 numBoundingSet = FindTMMBRBoundingSet(numSetCandidates, candidateSet);
240 if(numBoundingSet < 1 || (numBoundingSet > _candidateSet.sizeOfSet()))
241 {
242 return -1;
243 }
244 boundingSet = &_boundingSet;
245 }
246 return numBoundingSet;
247 }
248
249
250 int32_t
FindTMMBRBoundingSet(int32_t numCandidates,TMMBRSet & candidateSet)251 TMMBRHelp::FindTMMBRBoundingSet(int32_t numCandidates, TMMBRSet& candidateSet)
252 {
253 CriticalSectionScoped lock(_criticalSection);
254
255 uint32_t numBoundingSet = 0;
256 VerifyAndAllocateBoundingSet(candidateSet.sizeOfSet());
257
258 if (numCandidates == 1)
259 {
260 // TODO(hta): lengthOfSet instead of sizeOfSet?
261 for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++)
262 {
263 if (candidateSet.Tmmbr(i) > 0)
264 {
265 _boundingSet.AddEntry(candidateSet.Tmmbr(i),
266 candidateSet.PacketOH(i),
267 candidateSet.Ssrc(i));
268 numBoundingSet++;
269 }
270 }
271 return (numBoundingSet == 1) ? 1 : -1;
272 }
273
274 // 1. Sort by increasing packetOH
275 for (int i = candidateSet.sizeOfSet() - 1; i >= 0; i--)
276 {
277 for (int j = 1; j <= i; j++)
278 {
279 if (candidateSet.PacketOH(j-1) > candidateSet.PacketOH(j))
280 {
281 candidateSet.SwapEntries(j-1, j);
282 }
283 }
284 }
285 // 2. For tuples with same OH, keep the one w/ the lowest bitrate
286 for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++)
287 {
288 if (candidateSet.Tmmbr(i) > 0)
289 {
290 // get min bitrate for packets w/ same OH
291 uint32_t currentPacketOH = candidateSet.PacketOH(i);
292 uint32_t currentMinTMMBR = candidateSet.Tmmbr(i);
293 uint32_t currentMinIndexTMMBR = i;
294 for (uint32_t j = i+1; j < candidateSet.sizeOfSet(); j++)
295 {
296 if(candidateSet.PacketOH(j) == currentPacketOH)
297 {
298 if(candidateSet.Tmmbr(j) < currentMinTMMBR)
299 {
300 currentMinTMMBR = candidateSet.Tmmbr(j);
301 currentMinIndexTMMBR = j;
302 }
303 }
304 }
305 // keep lowest bitrate
306 for (uint32_t j = 0; j < candidateSet.sizeOfSet(); j++)
307 {
308 if(candidateSet.PacketOH(j) == currentPacketOH
309 && j != currentMinIndexTMMBR)
310 {
311 candidateSet.ClearEntry(j);
312 }
313 }
314 }
315 }
316 // 3. Select and remove tuple w/ lowest tmmbr.
317 // (If more than 1, choose the one w/ highest OH).
318 uint32_t minTMMBR = 0;
319 uint32_t minIndexTMMBR = 0;
320 for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++)
321 {
322 if (candidateSet.Tmmbr(i) > 0)
323 {
324 minTMMBR = candidateSet.Tmmbr(i);
325 minIndexTMMBR = i;
326 break;
327 }
328 }
329
330 for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++)
331 {
332 if (candidateSet.Tmmbr(i) > 0 && candidateSet.Tmmbr(i) <= minTMMBR)
333 {
334 // get min bitrate
335 minTMMBR = candidateSet.Tmmbr(i);
336 minIndexTMMBR = i;
337 }
338 }
339 // first member of selected list
340 _boundingSet.SetEntry(numBoundingSet,
341 candidateSet.Tmmbr(minIndexTMMBR),
342 candidateSet.PacketOH(minIndexTMMBR),
343 candidateSet.Ssrc(minIndexTMMBR));
344
345 // set intersection value
346 _ptrIntersectionBoundingSet[numBoundingSet] = 0;
347 // calculate its maximum packet rate (where its line crosses x-axis)
348 _ptrMaxPRBoundingSet[numBoundingSet]
349 = _boundingSet.Tmmbr(numBoundingSet) * 1000
350 / float(8 * _boundingSet.PacketOH(numBoundingSet));
351 numBoundingSet++;
352 // remove from candidate list
353 candidateSet.ClearEntry(minIndexTMMBR);
354 numCandidates--;
355
356 // 4. Discard from candidate list all tuple w/ lower OH
357 // (next tuple must be steeper)
358 for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++)
359 {
360 if(candidateSet.Tmmbr(i) > 0
361 && candidateSet.PacketOH(i) < _boundingSet.PacketOH(0))
362 {
363 candidateSet.ClearEntry(i);
364 numCandidates--;
365 }
366 }
367
368 if (numCandidates == 0)
369 {
370 // Should be true already:_boundingSet.lengthOfSet = numBoundingSet;
371 assert(_boundingSet.lengthOfSet() == numBoundingSet);
372 return numBoundingSet;
373 }
374
375 bool getNewCandidate = true;
376 int curCandidateTMMBR = 0;
377 int curCandidateIndex = 0;
378 int curCandidatePacketOH = 0;
379 int curCandidateSSRC = 0;
380 do
381 {
382 if (getNewCandidate)
383 {
384 // 5. Remove first remaining tuple from candidate list
385 for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++)
386 {
387 if (candidateSet.Tmmbr(i) > 0)
388 {
389 curCandidateTMMBR = candidateSet.Tmmbr(i);
390 curCandidatePacketOH = candidateSet.PacketOH(i);
391 curCandidateSSRC = candidateSet.Ssrc(i);
392 curCandidateIndex = i;
393 candidateSet.ClearEntry(curCandidateIndex);
394 break;
395 }
396 }
397 }
398
399 // 6. Calculate packet rate and intersection of the current
400 // line with line of last tuple in selected list
401 float packetRate
402 = float(curCandidateTMMBR
403 - _boundingSet.Tmmbr(numBoundingSet-1))*1000
404 / (8*(curCandidatePacketOH
405 - _boundingSet.PacketOH(numBoundingSet-1)));
406
407 // 7. If the packet rate is equal or lower than intersection of
408 // last tuple in selected list,
409 // remove last tuple in selected list & go back to step 6
410 if(packetRate <= _ptrIntersectionBoundingSet[numBoundingSet-1])
411 {
412 // remove last tuple and goto step 6
413 numBoundingSet--;
414 _boundingSet.ClearEntry(numBoundingSet);
415 _ptrIntersectionBoundingSet[numBoundingSet] = 0;
416 _ptrMaxPRBoundingSet[numBoundingSet] = 0;
417 getNewCandidate = false;
418 } else
419 {
420 // 8. If packet rate is lower than maximum packet rate of
421 // last tuple in selected list, add current tuple to selected
422 // list
423 if (packetRate < _ptrMaxPRBoundingSet[numBoundingSet-1])
424 {
425 _boundingSet.SetEntry(numBoundingSet,
426 curCandidateTMMBR,
427 curCandidatePacketOH,
428 curCandidateSSRC);
429 _ptrIntersectionBoundingSet[numBoundingSet] = packetRate;
430 _ptrMaxPRBoundingSet[numBoundingSet]
431 = _boundingSet.Tmmbr(numBoundingSet)*1000
432 / float(8*_boundingSet.PacketOH(numBoundingSet));
433 numBoundingSet++;
434 }
435 numCandidates--;
436 getNewCandidate = true;
437 }
438
439 // 9. Go back to step 5 if any tuple remains in candidate list
440 } while (numCandidates > 0);
441
442 return numBoundingSet;
443 }
444
IsOwner(const uint32_t ssrc,const uint32_t length) const445 bool TMMBRHelp::IsOwner(const uint32_t ssrc,
446 const uint32_t length) const {
447 CriticalSectionScoped lock(_criticalSection);
448
449 if (length == 0) {
450 // Empty bounding set.
451 return false;
452 }
453 for(uint32_t i = 0;
454 (i < length) && (i < _boundingSet.sizeOfSet()); ++i) {
455 if(_boundingSet.Ssrc(i) == ssrc) {
456 return true;
457 }
458 }
459 return false;
460 }
461
CalcMinBitRate(uint32_t * minBitrateKbit) const462 bool TMMBRHelp::CalcMinBitRate( uint32_t* minBitrateKbit) const {
463 CriticalSectionScoped lock(_criticalSection);
464
465 if (_candidateSet.sizeOfSet() == 0) {
466 // Empty bounding set.
467 return false;
468 }
469 *minBitrateKbit = std::numeric_limits<uint32_t>::max();
470
471 for (uint32_t i = 0; i < _candidateSet.lengthOfSet(); ++i) {
472 uint32_t curNetBitRateKbit = _candidateSet.Tmmbr(i);
473 if (curNetBitRateKbit < MIN_VIDEO_BW_MANAGEMENT_BITRATE) {
474 curNetBitRateKbit = MIN_VIDEO_BW_MANAGEMENT_BITRATE;
475 }
476 *minBitrateKbit = curNetBitRateKbit < *minBitrateKbit ?
477 curNetBitRateKbit : *minBitrateKbit;
478 }
479 return true;
480 }
481 } // namespace webrtc
482