|  | /* | 
|  | *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. | 
|  | * | 
|  | *  Use of this source code is governed by a BSD-style license | 
|  | *  that can be found in the LICENSE file in the root of the source | 
|  | *  tree. An additional intellectual property rights grant can be found | 
|  | *  in the file PATENTS.  All contributing project authors may | 
|  | *  be found in the AUTHORS file in the root of the source tree. | 
|  | */ | 
|  |  | 
|  | #include "webrtc/modules/rtp_rtcp/source/tmmbr_help.h" | 
|  |  | 
|  | #include <assert.h> | 
|  | #include <limits> | 
|  | #include <string.h> | 
|  | #include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_config.h" | 
|  |  | 
|  | namespace webrtc { | 
|  | TMMBRSet::TMMBRSet() : | 
|  | _sizeOfSet(0), | 
|  | _lengthOfSet(0) | 
|  | { | 
|  | } | 
|  |  | 
|  | TMMBRSet::~TMMBRSet() | 
|  | { | 
|  | _sizeOfSet = 0; | 
|  | _lengthOfSet = 0; | 
|  | } | 
|  |  | 
|  | void | 
|  | TMMBRSet::VerifyAndAllocateSet(uint32_t minimumSize) | 
|  | { | 
|  | if(minimumSize > _sizeOfSet) | 
|  | { | 
|  | // make sure that our buffers are big enough | 
|  | _data.resize(minimumSize); | 
|  | _sizeOfSet = minimumSize; | 
|  | } | 
|  | // reset memory | 
|  | for(uint32_t i = 0; i < _sizeOfSet; i++) | 
|  | { | 
|  | _data.at(i).tmmbr = 0; | 
|  | _data.at(i).packet_oh = 0; | 
|  | _data.at(i).ssrc = 0; | 
|  | } | 
|  | _lengthOfSet = 0; | 
|  | } | 
|  |  | 
|  | void | 
|  | TMMBRSet::VerifyAndAllocateSetKeepingData(uint32_t minimumSize) | 
|  | { | 
|  | if(minimumSize > _sizeOfSet) | 
|  | { | 
|  | { | 
|  | _data.resize(minimumSize); | 
|  | } | 
|  | _sizeOfSet = minimumSize; | 
|  | } | 
|  | } | 
|  |  | 
|  | void TMMBRSet::SetEntry(unsigned int i, | 
|  | uint32_t tmmbrSet, | 
|  | uint32_t packetOHSet, | 
|  | uint32_t ssrcSet) { | 
|  | assert(i < _sizeOfSet); | 
|  | _data.at(i).tmmbr = tmmbrSet; | 
|  | _data.at(i).packet_oh = packetOHSet; | 
|  | _data.at(i).ssrc = ssrcSet; | 
|  | if (i >= _lengthOfSet) { | 
|  | _lengthOfSet = i + 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | void TMMBRSet::AddEntry(uint32_t tmmbrSet, | 
|  | uint32_t packetOHSet, | 
|  | uint32_t ssrcSet) { | 
|  | assert(_lengthOfSet < _sizeOfSet); | 
|  | SetEntry(_lengthOfSet, tmmbrSet, packetOHSet, ssrcSet); | 
|  | } | 
|  |  | 
|  | void TMMBRSet::RemoveEntry(uint32_t sourceIdx) { | 
|  | assert(sourceIdx < _lengthOfSet); | 
|  | _data.erase(_data.begin() + sourceIdx); | 
|  | _lengthOfSet--; | 
|  | _data.resize(_sizeOfSet);  // Ensure that size remains the same. | 
|  | } | 
|  |  | 
|  | void TMMBRSet::SwapEntries(uint32_t i, uint32_t j) { | 
|  | SetElement temp; | 
|  | temp = _data[i]; | 
|  | _data[i] = _data[j]; | 
|  | _data[j] = temp; | 
|  | } | 
|  |  | 
|  | void TMMBRSet::ClearEntry(uint32_t idx) { | 
|  | SetEntry(idx, 0, 0, 0); | 
|  | } | 
|  |  | 
|  | TMMBRHelp::TMMBRHelp() | 
|  | : _criticalSection(CriticalSectionWrapper::CreateCriticalSection()), | 
|  | _candidateSet(), | 
|  | _boundingSet(), | 
|  | _boundingSetToSend(), | 
|  | _ptrIntersectionBoundingSet(NULL), | 
|  | _ptrMaxPRBoundingSet(NULL) { | 
|  | } | 
|  |  | 
|  | TMMBRHelp::~TMMBRHelp() { | 
|  | delete [] _ptrIntersectionBoundingSet; | 
|  | delete [] _ptrMaxPRBoundingSet; | 
|  | _ptrIntersectionBoundingSet = 0; | 
|  | _ptrMaxPRBoundingSet = 0; | 
|  | delete _criticalSection; | 
|  | } | 
|  |  | 
|  | TMMBRSet* | 
|  | TMMBRHelp::VerifyAndAllocateBoundingSet(uint32_t minimumSize) | 
|  | { | 
|  | CriticalSectionScoped lock(_criticalSection); | 
|  |  | 
|  | if(minimumSize > _boundingSet.sizeOfSet()) | 
|  | { | 
|  | // make sure that our buffers are big enough | 
|  | if(_ptrIntersectionBoundingSet) | 
|  | { | 
|  | delete [] _ptrIntersectionBoundingSet; | 
|  | delete [] _ptrMaxPRBoundingSet; | 
|  | } | 
|  | _ptrIntersectionBoundingSet = new float[minimumSize]; | 
|  | _ptrMaxPRBoundingSet = new float[minimumSize]; | 
|  | } | 
|  | _boundingSet.VerifyAndAllocateSet(minimumSize); | 
|  | return &_boundingSet; | 
|  | } | 
|  |  | 
|  | TMMBRSet* TMMBRHelp::BoundingSet() { | 
|  | return &_boundingSet; | 
|  | } | 
|  |  | 
|  | int32_t | 
|  | TMMBRHelp::SetTMMBRBoundingSetToSend(const TMMBRSet* boundingSetToSend, | 
|  | const uint32_t maxBitrateKbit) | 
|  | { | 
|  | CriticalSectionScoped lock(_criticalSection); | 
|  |  | 
|  | if (boundingSetToSend == NULL) | 
|  | { | 
|  | _boundingSetToSend.clearSet(); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | VerifyAndAllocateBoundingSetToSend(boundingSetToSend->lengthOfSet()); | 
|  | _boundingSetToSend.clearSet(); | 
|  | for (uint32_t i = 0; i < boundingSetToSend->lengthOfSet(); i++) | 
|  | { | 
|  | // cap at our configured max bitrate | 
|  | uint32_t bitrate = boundingSetToSend->Tmmbr(i); | 
|  | if(maxBitrateKbit) | 
|  | { | 
|  | // do we have a configured max bitrate? | 
|  | if(bitrate > maxBitrateKbit) | 
|  | { | 
|  | bitrate = maxBitrateKbit; | 
|  | } | 
|  | } | 
|  | _boundingSetToSend.SetEntry(i, bitrate, | 
|  | boundingSetToSend->PacketOH(i), | 
|  | boundingSetToSend->Ssrc(i)); | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int32_t | 
|  | TMMBRHelp::VerifyAndAllocateBoundingSetToSend(uint32_t minimumSize) | 
|  | { | 
|  | CriticalSectionScoped lock(_criticalSection); | 
|  |  | 
|  | _boundingSetToSend.VerifyAndAllocateSet(minimumSize); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | TMMBRSet* | 
|  | TMMBRHelp::VerifyAndAllocateCandidateSet(uint32_t minimumSize) | 
|  | { | 
|  | CriticalSectionScoped lock(_criticalSection); | 
|  |  | 
|  | _candidateSet.VerifyAndAllocateSet(minimumSize); | 
|  | return &_candidateSet; | 
|  | } | 
|  |  | 
|  | TMMBRSet* | 
|  | TMMBRHelp::CandidateSet() | 
|  | { | 
|  | return &_candidateSet; | 
|  | } | 
|  |  | 
|  | TMMBRSet* | 
|  | TMMBRHelp::BoundingSetToSend() | 
|  | { | 
|  | return &_boundingSetToSend; | 
|  | } | 
|  |  | 
|  | int32_t | 
|  | TMMBRHelp::FindTMMBRBoundingSet(TMMBRSet*& boundingSet) | 
|  | { | 
|  | CriticalSectionScoped lock(_criticalSection); | 
|  |  | 
|  | // Work on local variable, will be modified | 
|  | TMMBRSet    candidateSet; | 
|  | candidateSet.VerifyAndAllocateSet(_candidateSet.sizeOfSet()); | 
|  |  | 
|  | // TODO(hta) Figure out if this should be lengthOfSet instead. | 
|  | for (uint32_t i = 0; i < _candidateSet.sizeOfSet(); i++) | 
|  | { | 
|  | if(_candidateSet.Tmmbr(i)) | 
|  | { | 
|  | candidateSet.AddEntry(_candidateSet.Tmmbr(i), | 
|  | _candidateSet.PacketOH(i), | 
|  | _candidateSet.Ssrc(i)); | 
|  | } | 
|  | else | 
|  | { | 
|  | // make sure this is zero if tmmbr = 0 | 
|  | assert(_candidateSet.PacketOH(i) == 0); | 
|  | // Old code: | 
|  | // _candidateSet.ptrPacketOHSet[i] = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Number of set candidates | 
|  | int32_t numSetCandidates = candidateSet.lengthOfSet(); | 
|  | // Find bounding set | 
|  | uint32_t numBoundingSet = 0; | 
|  | if (numSetCandidates > 0) | 
|  | { | 
|  | numBoundingSet =  FindTMMBRBoundingSet(numSetCandidates, candidateSet); | 
|  | if(numBoundingSet < 1 || (numBoundingSet > _candidateSet.sizeOfSet())) | 
|  | { | 
|  | return -1; | 
|  | } | 
|  | boundingSet = &_boundingSet; | 
|  | } | 
|  | return numBoundingSet; | 
|  | } | 
|  |  | 
|  |  | 
|  | int32_t | 
|  | TMMBRHelp::FindTMMBRBoundingSet(int32_t numCandidates, TMMBRSet& candidateSet) | 
|  | { | 
|  | CriticalSectionScoped lock(_criticalSection); | 
|  |  | 
|  | uint32_t numBoundingSet = 0; | 
|  | VerifyAndAllocateBoundingSet(candidateSet.sizeOfSet()); | 
|  |  | 
|  | if (numCandidates == 1) | 
|  | { | 
|  | // TODO(hta): lengthOfSet instead of sizeOfSet? | 
|  | for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++) | 
|  | { | 
|  | if (candidateSet.Tmmbr(i) > 0) | 
|  | { | 
|  | _boundingSet.AddEntry(candidateSet.Tmmbr(i), | 
|  | candidateSet.PacketOH(i), | 
|  | candidateSet.Ssrc(i)); | 
|  | numBoundingSet++; | 
|  | } | 
|  | } | 
|  | return (numBoundingSet == 1) ? 1 : -1; | 
|  | } | 
|  |  | 
|  | // 1. Sort by increasing packetOH | 
|  | for (int i = candidateSet.sizeOfSet() - 1; i >= 0; i--) | 
|  | { | 
|  | for (int j = 1; j <= i; j++) | 
|  | { | 
|  | if (candidateSet.PacketOH(j-1) > candidateSet.PacketOH(j)) | 
|  | { | 
|  | candidateSet.SwapEntries(j-1, j); | 
|  | } | 
|  | } | 
|  | } | 
|  | // 2. For tuples with same OH, keep the one w/ the lowest bitrate | 
|  | for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++) | 
|  | { | 
|  | if (candidateSet.Tmmbr(i) > 0) | 
|  | { | 
|  | // get min bitrate for packets w/ same OH | 
|  | uint32_t currentPacketOH = candidateSet.PacketOH(i); | 
|  | uint32_t currentMinTMMBR = candidateSet.Tmmbr(i); | 
|  | uint32_t currentMinIndexTMMBR = i; | 
|  | for (uint32_t j = i+1; j < candidateSet.sizeOfSet(); j++) | 
|  | { | 
|  | if(candidateSet.PacketOH(j) == currentPacketOH) | 
|  | { | 
|  | if(candidateSet.Tmmbr(j) < currentMinTMMBR) | 
|  | { | 
|  | currentMinTMMBR = candidateSet.Tmmbr(j); | 
|  | currentMinIndexTMMBR = j; | 
|  | } | 
|  | } | 
|  | } | 
|  | // keep lowest bitrate | 
|  | for (uint32_t j = 0; j < candidateSet.sizeOfSet(); j++) | 
|  | { | 
|  | if(candidateSet.PacketOH(j) == currentPacketOH | 
|  | && j != currentMinIndexTMMBR) | 
|  | { | 
|  | candidateSet.ClearEntry(j); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | // 3. Select and remove tuple w/ lowest tmmbr. | 
|  | // (If more than 1, choose the one w/ highest OH). | 
|  | uint32_t minTMMBR = 0; | 
|  | uint32_t minIndexTMMBR = 0; | 
|  | for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++) | 
|  | { | 
|  | if (candidateSet.Tmmbr(i) > 0) | 
|  | { | 
|  | minTMMBR = candidateSet.Tmmbr(i); | 
|  | minIndexTMMBR = i; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++) | 
|  | { | 
|  | if (candidateSet.Tmmbr(i) > 0 && candidateSet.Tmmbr(i) <= minTMMBR) | 
|  | { | 
|  | // get min bitrate | 
|  | minTMMBR = candidateSet.Tmmbr(i); | 
|  | minIndexTMMBR = i; | 
|  | } | 
|  | } | 
|  | // first member of selected list | 
|  | _boundingSet.SetEntry(numBoundingSet, | 
|  | candidateSet.Tmmbr(minIndexTMMBR), | 
|  | candidateSet.PacketOH(minIndexTMMBR), | 
|  | candidateSet.Ssrc(minIndexTMMBR)); | 
|  |  | 
|  | // set intersection value | 
|  | _ptrIntersectionBoundingSet[numBoundingSet] = 0; | 
|  | // calculate its maximum packet rate (where its line crosses x-axis) | 
|  | _ptrMaxPRBoundingSet[numBoundingSet] | 
|  | = _boundingSet.Tmmbr(numBoundingSet) * 1000 | 
|  | / float(8 * _boundingSet.PacketOH(numBoundingSet)); | 
|  | numBoundingSet++; | 
|  | // remove from candidate list | 
|  | candidateSet.ClearEntry(minIndexTMMBR); | 
|  | numCandidates--; | 
|  |  | 
|  | // 4. Discard from candidate list all tuple w/ lower OH | 
|  | // (next tuple must be steeper) | 
|  | for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++) | 
|  | { | 
|  | if(candidateSet.Tmmbr(i) > 0 | 
|  | && candidateSet.PacketOH(i) < _boundingSet.PacketOH(0)) | 
|  | { | 
|  | candidateSet.ClearEntry(i); | 
|  | numCandidates--; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (numCandidates == 0) | 
|  | { | 
|  | // Should be true already:_boundingSet.lengthOfSet = numBoundingSet; | 
|  | assert(_boundingSet.lengthOfSet() == numBoundingSet); | 
|  | return numBoundingSet; | 
|  | } | 
|  |  | 
|  | bool getNewCandidate = true; | 
|  | int curCandidateTMMBR = 0; | 
|  | int curCandidateIndex = 0; | 
|  | int curCandidatePacketOH = 0; | 
|  | int curCandidateSSRC = 0; | 
|  | do | 
|  | { | 
|  | if (getNewCandidate) | 
|  | { | 
|  | // 5. Remove first remaining tuple from candidate list | 
|  | for (uint32_t i = 0; i < candidateSet.sizeOfSet(); i++) | 
|  | { | 
|  | if (candidateSet.Tmmbr(i) > 0) | 
|  | { | 
|  | curCandidateTMMBR    = candidateSet.Tmmbr(i); | 
|  | curCandidatePacketOH = candidateSet.PacketOH(i); | 
|  | curCandidateSSRC     = candidateSet.Ssrc(i); | 
|  | curCandidateIndex    = i; | 
|  | candidateSet.ClearEntry(curCandidateIndex); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // 6. Calculate packet rate and intersection of the current | 
|  | // line with line of last tuple in selected list | 
|  | float packetRate | 
|  | = float(curCandidateTMMBR | 
|  | - _boundingSet.Tmmbr(numBoundingSet-1))*1000 | 
|  | / (8*(curCandidatePacketOH | 
|  | - _boundingSet.PacketOH(numBoundingSet-1))); | 
|  |  | 
|  | // 7. If the packet rate is equal or lower than intersection of | 
|  | //    last tuple in selected list, | 
|  | //    remove last tuple in selected list & go back to step 6 | 
|  | if(packetRate <= _ptrIntersectionBoundingSet[numBoundingSet-1]) | 
|  | { | 
|  | // remove last tuple and goto step 6 | 
|  | numBoundingSet--; | 
|  | _boundingSet.ClearEntry(numBoundingSet); | 
|  | _ptrIntersectionBoundingSet[numBoundingSet] = 0; | 
|  | _ptrMaxPRBoundingSet[numBoundingSet]        = 0; | 
|  | getNewCandidate = false; | 
|  | } else | 
|  | { | 
|  | // 8. If packet rate is lower than maximum packet rate of | 
|  | // last tuple in selected list, add current tuple to selected | 
|  | // list | 
|  | if (packetRate < _ptrMaxPRBoundingSet[numBoundingSet-1]) | 
|  | { | 
|  | _boundingSet.SetEntry(numBoundingSet, | 
|  | curCandidateTMMBR, | 
|  | curCandidatePacketOH, | 
|  | curCandidateSSRC); | 
|  | _ptrIntersectionBoundingSet[numBoundingSet] = packetRate; | 
|  | _ptrMaxPRBoundingSet[numBoundingSet] | 
|  | = _boundingSet.Tmmbr(numBoundingSet)*1000 | 
|  | / float(8*_boundingSet.PacketOH(numBoundingSet)); | 
|  | numBoundingSet++; | 
|  | } | 
|  | numCandidates--; | 
|  | getNewCandidate = true; | 
|  | } | 
|  |  | 
|  | // 9. Go back to step 5 if any tuple remains in candidate list | 
|  | } while (numCandidates > 0); | 
|  |  | 
|  | return numBoundingSet; | 
|  | } | 
|  |  | 
|  | bool TMMBRHelp::IsOwner(const uint32_t ssrc, | 
|  | const uint32_t length) const { | 
|  | CriticalSectionScoped lock(_criticalSection); | 
|  |  | 
|  | if (length == 0) { | 
|  | // Empty bounding set. | 
|  | return false; | 
|  | } | 
|  | for(uint32_t i = 0; | 
|  | (i < length) && (i < _boundingSet.sizeOfSet()); ++i) { | 
|  | if(_boundingSet.Ssrc(i) == ssrc) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool TMMBRHelp::CalcMinBitRate( uint32_t* minBitrateKbit) const { | 
|  | CriticalSectionScoped lock(_criticalSection); | 
|  |  | 
|  | if (_candidateSet.sizeOfSet() == 0) { | 
|  | // Empty bounding set. | 
|  | return false; | 
|  | } | 
|  | *minBitrateKbit = std::numeric_limits<uint32_t>::max(); | 
|  |  | 
|  | for (uint32_t i = 0; i < _candidateSet.lengthOfSet(); ++i) { | 
|  | uint32_t curNetBitRateKbit = _candidateSet.Tmmbr(i); | 
|  | if (curNetBitRateKbit < MIN_VIDEO_BW_MANAGEMENT_BITRATE) { | 
|  | curNetBitRateKbit = MIN_VIDEO_BW_MANAGEMENT_BITRATE; | 
|  | } | 
|  | *minBitrateKbit = curNetBitRateKbit < *minBitrateKbit ? | 
|  | curNetBitRateKbit : *minBitrateKbit; | 
|  | } | 
|  | return true; | 
|  | } | 
|  | }  // namespace webrtc |