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