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