• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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         if (numBoundingSet != 1)
270         {
271             numBoundingSet = -1;
272         }
273     } else
274     {
275         // 1. Sort by increasing packetOH
276         for (int i = candidateSet.sizeOfSet() - 1; i >= 0; i--)
277         {
278             for (int j = 1; j <= i; j++)
279             {
280                 if (candidateSet.PacketOH(j-1) > candidateSet.PacketOH(j))
281                 {
282                     candidateSet.SwapEntries(j-1, j);
283                 }
284             }
285         }
286         // 2. For tuples with same OH, keep the one w/ the lowest bitrate
287         for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++)
288         {
289             if (candidateSet.Tmmbr(i) > 0)
290             {
291                 // get min bitrate for packets w/ same OH
292                 uint32_t currentPacketOH = candidateSet.PacketOH(i);
293                 uint32_t currentMinTMMBR = candidateSet.Tmmbr(i);
294                 uint32_t currentMinIndexTMMBR = i;
295                 for (uint32_t j = i+1; j < candidateSet.sizeOfSet(); j++)
296                 {
297                     if(candidateSet.PacketOH(j) == currentPacketOH)
298                     {
299                         if(candidateSet.Tmmbr(j) < currentMinTMMBR)
300                         {
301                             currentMinTMMBR = candidateSet.Tmmbr(j);
302                             currentMinIndexTMMBR = j;
303                         }
304                     }
305                 }
306                 // keep lowest bitrate
307                 for (uint32_t j = 0; j < candidateSet.sizeOfSet(); j++)
308                 {
309                   if(candidateSet.PacketOH(j) == currentPacketOH
310                      && j != currentMinIndexTMMBR)
311                     {
312                         candidateSet.ClearEntry(j);
313                     }
314                 }
315             }
316         }
317         // 3. Select and remove tuple w/ lowest tmmbr.
318         // (If more than 1, choose the one w/ highest OH).
319         uint32_t minTMMBR = 0;
320         uint32_t minIndexTMMBR = 0;
321         for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++)
322         {
323             if (candidateSet.Tmmbr(i) > 0)
324             {
325                 minTMMBR = candidateSet.Tmmbr(i);
326                 minIndexTMMBR = i;
327                 break;
328             }
329         }
330 
331         for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++)
332         {
333             if (candidateSet.Tmmbr(i) > 0 && candidateSet.Tmmbr(i) <= minTMMBR)
334             {
335                 // get min bitrate
336                 minTMMBR = candidateSet.Tmmbr(i);
337                 minIndexTMMBR = i;
338             }
339         }
340         // first member of selected list
341         _boundingSet.SetEntry(numBoundingSet,
342                               candidateSet.Tmmbr(minIndexTMMBR),
343                               candidateSet.PacketOH(minIndexTMMBR),
344                               candidateSet.Ssrc(minIndexTMMBR));
345 
346         // set intersection value
347         _ptrIntersectionBoundingSet[numBoundingSet] = 0;
348         // calculate its maximum packet rate (where its line crosses x-axis)
349         _ptrMaxPRBoundingSet[numBoundingSet]
350             = _boundingSet.Tmmbr(numBoundingSet) * 1000
351             / float(8 * _boundingSet.PacketOH(numBoundingSet));
352         numBoundingSet++;
353         // remove from candidate list
354         candidateSet.ClearEntry(minIndexTMMBR);
355         numCandidates--;
356 
357         // 4. Discard from candidate list all tuple w/ lower OH
358         // (next tuple must be steeper)
359         for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++)
360         {
361             if(candidateSet.Tmmbr(i) > 0
362                && candidateSet.PacketOH(i) < _boundingSet.PacketOH(0))
363             {
364                 candidateSet.ClearEntry(i);
365                 numCandidates--;
366             }
367         }
368 
369         if (numCandidates == 0)
370         {
371             // Should be true already:_boundingSet.lengthOfSet = numBoundingSet;
372             assert(_boundingSet.lengthOfSet() == numBoundingSet);
373             return numBoundingSet;
374         }
375 
376         bool getNewCandidate = true;
377         int curCandidateTMMBR = 0;
378         int curCandidateIndex = 0;
379         int curCandidatePacketOH = 0;
380         int curCandidateSSRC = 0;
381         do
382         {
383             if (getNewCandidate)
384             {
385                 // 5. Remove first remaining tuple from candidate list
386                 for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++)
387                 {
388                     if (candidateSet.Tmmbr(i) > 0)
389                     {
390                         curCandidateTMMBR    = candidateSet.Tmmbr(i);
391                         curCandidatePacketOH = candidateSet.PacketOH(i);
392                         curCandidateSSRC     = candidateSet.Ssrc(i);
393                         curCandidateIndex    = i;
394                         candidateSet.ClearEntry(curCandidateIndex);
395                         break;
396                     }
397                 }
398             }
399 
400             // 6. Calculate packet rate and intersection of the current
401             // line with line of last tuple in selected list
402             float packetRate
403                 = float(curCandidateTMMBR
404                         - _boundingSet.Tmmbr(numBoundingSet-1))*1000
405                 / (8*(curCandidatePacketOH
406                       - _boundingSet.PacketOH(numBoundingSet-1)));
407 
408             // 7. If the packet rate is equal or lower than intersection of
409             //    last tuple in selected list,
410             //    remove last tuple in selected list & go back to step 6
411             if(packetRate <= _ptrIntersectionBoundingSet[numBoundingSet-1])
412             {
413                 // remove last tuple and goto step 6
414                 numBoundingSet--;
415                 _boundingSet.ClearEntry(numBoundingSet);
416                 _ptrIntersectionBoundingSet[numBoundingSet] = 0;
417                 _ptrMaxPRBoundingSet[numBoundingSet]        = 0;
418                 getNewCandidate = false;
419             } else
420             {
421                 // 8. If packet rate is lower than maximum packet rate of
422                 // last tuple in selected list, add current tuple to selected
423                 // list
424                 if (packetRate < _ptrMaxPRBoundingSet[numBoundingSet-1])
425                 {
426                     _boundingSet.SetEntry(numBoundingSet,
427                                           curCandidateTMMBR,
428                                           curCandidatePacketOH,
429                                           curCandidateSSRC);
430                     _ptrIntersectionBoundingSet[numBoundingSet] = packetRate;
431                     _ptrMaxPRBoundingSet[numBoundingSet]
432                         = _boundingSet.Tmmbr(numBoundingSet)*1000
433                         / float(8*_boundingSet.PacketOH(numBoundingSet));
434                     numBoundingSet++;
435                 }
436                 numCandidates--;
437                 getNewCandidate = true;
438             }
439 
440             // 9. Go back to step 5 if any tuple remains in candidate list
441         } while (numCandidates > 0);
442     }
443     return numBoundingSet;
444 }
445 
IsOwner(const uint32_t ssrc,const uint32_t length) const446 bool TMMBRHelp::IsOwner(const uint32_t ssrc,
447                         const uint32_t length) const {
448   CriticalSectionScoped lock(_criticalSection);
449 
450   if (length == 0) {
451     // Empty bounding set.
452     return false;
453   }
454   for(uint32_t i = 0;
455       (i < length) && (i < _boundingSet.sizeOfSet()); ++i) {
456     if(_boundingSet.Ssrc(i) == ssrc) {
457       return true;
458     }
459   }
460   return false;
461 }
462 
CalcMinBitRate(uint32_t * minBitrateKbit) const463 bool TMMBRHelp::CalcMinBitRate( uint32_t* minBitrateKbit) const {
464   CriticalSectionScoped lock(_criticalSection);
465 
466   if (_candidateSet.sizeOfSet() == 0) {
467     // Empty bounding set.
468     return false;
469   }
470   *minBitrateKbit = std::numeric_limits<uint32_t>::max();
471 
472   for (uint32_t i = 0; i < _candidateSet.lengthOfSet(); ++i) {
473     uint32_t curNetBitRateKbit = _candidateSet.Tmmbr(i);
474     if (curNetBitRateKbit < MIN_VIDEO_BW_MANAGEMENT_BITRATE) {
475       curNetBitRateKbit = MIN_VIDEO_BW_MANAGEMENT_BITRATE;
476     }
477     *minBitrateKbit = curNetBitRateKbit < *minBitrateKbit ?
478         curNetBitRateKbit : *minBitrateKbit;
479   }
480   return true;
481 }
482 }  // namespace webrtc
483