| /* |
| * Copyright (c) 2011 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 "tmmbr_help.h" |
| |
| #include "rtp_rtcp_config.h" |
| |
| namespace webrtc { |
| TMMBRSet::TMMBRSet() : |
| ptrTmmbrSet(0), |
| ptrPacketOHSet(0), |
| ptrSsrcSet(0), |
| sizeOfSet(0), |
| lengthOfSet(0) |
| { |
| } |
| |
| TMMBRSet::~TMMBRSet() |
| { |
| delete [] ptrTmmbrSet; |
| delete [] ptrPacketOHSet; |
| delete [] ptrSsrcSet; |
| ptrTmmbrSet = 0; |
| ptrPacketOHSet = 0; |
| ptrSsrcSet = 0; |
| sizeOfSet = 0; |
| lengthOfSet = 0; |
| } |
| |
| void |
| TMMBRSet::VerifyAndAllocateSet(WebRtc_UWord32 minimumSize) |
| { |
| if(minimumSize > sizeOfSet) |
| { |
| // make sure that our buffers are big enough |
| if(ptrTmmbrSet) |
| { |
| delete [] ptrTmmbrSet; |
| delete [] ptrPacketOHSet; |
| delete [] ptrSsrcSet; |
| } |
| ptrTmmbrSet = new WebRtc_UWord32[minimumSize]; |
| ptrPacketOHSet = new WebRtc_UWord32[minimumSize]; |
| ptrSsrcSet = new WebRtc_UWord32[minimumSize]; |
| sizeOfSet = minimumSize; |
| } |
| // reset memory |
| for(WebRtc_UWord32 i = 0; i < sizeOfSet; i++) |
| { |
| ptrTmmbrSet[i] = 0; |
| ptrPacketOHSet[i] = 0; |
| ptrSsrcSet[i] = 0; |
| } |
| lengthOfSet = 0; |
| } |
| |
| TMMBRHelp::TMMBRHelp(const bool audio) : |
| _criticalSection(CriticalSectionWrapper::CreateCriticalSection()), |
| _audio(audio), |
| _candidateSet(), |
| _boundingSet(), |
| _boundingSetToSend(), |
| _ptrIntersectionBoundingSet(NULL), |
| _ptrMaxPRBoundingSet(NULL) |
| { |
| } |
| |
| TMMBRHelp::~TMMBRHelp() |
| { |
| delete [] _ptrIntersectionBoundingSet; |
| delete [] _ptrMaxPRBoundingSet; |
| _ptrIntersectionBoundingSet = 0; |
| _ptrMaxPRBoundingSet = 0; |
| delete _criticalSection; |
| } |
| |
| TMMBRSet* |
| TMMBRHelp::VerifyAndAllocateBoundingSet(WebRtc_UWord32 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; |
| } |
| |
| WebRtc_Word32 |
| TMMBRHelp::SetTMMBRBoundingSetToSend(const TMMBRSet* boundingSetToSend, |
| const WebRtc_UWord32 maxBitrateKbit) |
| { |
| CriticalSectionScoped lock(_criticalSection); |
| |
| if (boundingSetToSend == NULL) |
| { |
| _boundingSetToSend.lengthOfSet = 0; |
| return 0; |
| } |
| |
| VerifyAndAllocateBoundingSetToSend(boundingSetToSend->lengthOfSet); |
| |
| for (WebRtc_UWord32 i = 0; i < boundingSetToSend->lengthOfSet; i++) |
| { |
| // cap at our configured max bitrate |
| WebRtc_UWord32 bitrate = boundingSetToSend->ptrTmmbrSet[i]; |
| if(maxBitrateKbit) |
| { |
| // do we have a configured max bitrate? |
| if(bitrate > maxBitrateKbit) |
| { |
| bitrate = maxBitrateKbit; |
| } |
| } |
| |
| _boundingSetToSend.ptrTmmbrSet[i] = bitrate; |
| _boundingSetToSend.ptrPacketOHSet[i] = boundingSetToSend->ptrPacketOHSet[i]; |
| _boundingSetToSend.ptrSsrcSet[i] = boundingSetToSend->ptrSsrcSet[i]; |
| } |
| _boundingSetToSend.lengthOfSet = boundingSetToSend->lengthOfSet; |
| return 0; |
| } |
| |
| WebRtc_Word32 |
| TMMBRHelp::VerifyAndAllocateBoundingSetToSend(WebRtc_UWord32 minimumSize) |
| { |
| CriticalSectionScoped lock(_criticalSection); |
| |
| _boundingSetToSend.VerifyAndAllocateSet(minimumSize); |
| return 0; |
| } |
| |
| TMMBRSet* |
| TMMBRHelp::VerifyAndAllocateCandidateSet(WebRtc_UWord32 minimumSize) |
| { |
| CriticalSectionScoped lock(_criticalSection); |
| |
| _candidateSet.VerifyAndAllocateSet(minimumSize); |
| return &_candidateSet; |
| } |
| |
| TMMBRSet* |
| TMMBRHelp::CandidateSet() |
| { |
| return &_candidateSet; |
| } |
| |
| TMMBRSet* |
| TMMBRHelp::BoundingSetToSend() |
| { |
| return &_boundingSetToSend; |
| } |
| |
| WebRtc_Word32 |
| TMMBRHelp::FindTMMBRBoundingSet(TMMBRSet*& boundingSet) |
| { |
| CriticalSectionScoped lock(_criticalSection); |
| |
| // Work on local variable, will be modified |
| TMMBRSet candidateSet; |
| candidateSet.VerifyAndAllocateSet(_candidateSet.sizeOfSet); |
| |
| // Number of set candidates |
| WebRtc_Word32 numSetCandidates = 0; |
| for (WebRtc_UWord32 i = 0; i < _candidateSet.sizeOfSet; i++) |
| { |
| if(_candidateSet.ptrTmmbrSet[i]) |
| { |
| numSetCandidates++; |
| candidateSet.ptrTmmbrSet[i] = _candidateSet.ptrTmmbrSet[i]; |
| candidateSet.ptrPacketOHSet[i] = _candidateSet.ptrPacketOHSet[i]; |
| candidateSet.ptrSsrcSet[i] = _candidateSet.ptrSsrcSet[i]; |
| } |
| else |
| { |
| // make sure this is zero if tmmbr = 0 |
| _candidateSet.ptrPacketOHSet[i] = 0; |
| } |
| } |
| candidateSet.lengthOfSet = numSetCandidates; |
| |
| // Find bounding set |
| WebRtc_UWord32 numBoundingSet = 0; |
| if (numSetCandidates > 0) |
| { |
| numBoundingSet = FindTMMBRBoundingSet(numSetCandidates, candidateSet); |
| if(numBoundingSet < 1 || (numBoundingSet > _candidateSet.sizeOfSet)) |
| { |
| return -1; |
| } |
| boundingSet = &_boundingSet; |
| } |
| return numBoundingSet; |
| } |
| |
| |
| WebRtc_Word32 |
| TMMBRHelp::FindTMMBRBoundingSet(WebRtc_Word32 numCandidates, TMMBRSet& candidateSet) |
| { |
| CriticalSectionScoped lock(_criticalSection); |
| |
| WebRtc_UWord32 numBoundingSet = 0; |
| VerifyAndAllocateBoundingSet(candidateSet.sizeOfSet); |
| |
| if (numCandidates == 1) |
| { |
| for (WebRtc_UWord32 i = 0; i < candidateSet.sizeOfSet; i++) |
| { |
| if(candidateSet.ptrTmmbrSet[i] > 0) |
| { |
| _boundingSet.ptrTmmbrSet[numBoundingSet] = candidateSet.ptrTmmbrSet[i]; |
| _boundingSet.ptrPacketOHSet[numBoundingSet] = candidateSet.ptrPacketOHSet[i]; |
| _boundingSet.ptrSsrcSet[numBoundingSet] = candidateSet.ptrSsrcSet[i]; |
| numBoundingSet++; |
| } |
| } |
| if (numBoundingSet != 1) |
| { |
| numBoundingSet = -1; |
| } |
| } else |
| { |
| // 1. Sort by increasing packetOH |
| WebRtc_UWord32 temp; |
| for (int i = candidateSet.sizeOfSet - 1; i >= 0; i--) |
| { |
| for (int j = 1; j <= i; j++) |
| { |
| if (candidateSet.ptrPacketOHSet[j-1] > candidateSet.ptrPacketOHSet[j]) |
| { |
| temp = candidateSet.ptrPacketOHSet[j-1]; |
| candidateSet.ptrPacketOHSet[j-1] = candidateSet.ptrPacketOHSet[j]; |
| candidateSet.ptrPacketOHSet[j] = temp; |
| temp = candidateSet.ptrTmmbrSet[j-1]; |
| candidateSet.ptrTmmbrSet[j-1] = candidateSet.ptrTmmbrSet[j]; |
| candidateSet.ptrTmmbrSet[j] = temp; |
| temp = candidateSet.ptrSsrcSet[j-1]; |
| candidateSet.ptrSsrcSet[j-1] = candidateSet.ptrSsrcSet[j]; |
| candidateSet.ptrSsrcSet[j] = temp; |
| } |
| } |
| } |
| // 2. For tuples with same OH, keep the one w/ the lowest bitrate |
| for (WebRtc_UWord32 i = 0; i < candidateSet.sizeOfSet; i++) |
| { |
| if (candidateSet.ptrTmmbrSet[i] > 0) |
| { |
| // get min bitrate for packets w/ same OH |
| WebRtc_UWord32 currentPacketOH = candidateSet.ptrPacketOHSet[i]; |
| WebRtc_UWord32 currentMinTMMBR = candidateSet.ptrTmmbrSet[i]; |
| WebRtc_UWord32 currentMinIndexTMMBR = i; |
| for (WebRtc_UWord32 j = i+1; j < candidateSet.sizeOfSet; j++) |
| { |
| if(candidateSet.ptrPacketOHSet[j] == currentPacketOH) |
| { |
| if(candidateSet.ptrTmmbrSet[j] < currentMinTMMBR) |
| { |
| currentMinTMMBR = candidateSet.ptrTmmbrSet[j]; |
| currentMinIndexTMMBR = j; |
| } |
| } |
| } |
| // keep lowest bitrate |
| for (WebRtc_UWord32 j = 0; j < candidateSet.sizeOfSet; j++) |
| { |
| if(candidateSet.ptrPacketOHSet[j] == currentPacketOH && j != currentMinIndexTMMBR) |
| { |
| candidateSet.ptrTmmbrSet[j] = 0; |
| candidateSet.ptrPacketOHSet[j] = 0; |
| candidateSet.ptrSsrcSet[j] = 0; |
| numCandidates--; |
| } |
| } |
| } |
| } |
| // 3. Select and remove tuple w/ lowest tmmbr. (If more than 1, choose the one w/ highest OH). |
| WebRtc_UWord32 minTMMBR = 0; |
| WebRtc_UWord32 minIndexTMMBR = 0; |
| for (WebRtc_UWord32 i = 0; i < candidateSet.sizeOfSet; i++) |
| { |
| if (candidateSet.ptrTmmbrSet[i] > 0) |
| { |
| minTMMBR = candidateSet.ptrTmmbrSet[i]; |
| minIndexTMMBR = i; |
| break; |
| } |
| } |
| |
| for (WebRtc_UWord32 i = 0; i < candidateSet.sizeOfSet; i++) |
| { |
| if (candidateSet.ptrTmmbrSet[i] > 0 && candidateSet.ptrTmmbrSet[i] <= minTMMBR) |
| { |
| // get min bitrate |
| minTMMBR = candidateSet.ptrTmmbrSet[i]; |
| minIndexTMMBR = i; |
| } |
| } |
| // first member of selected list |
| _boundingSet.ptrTmmbrSet[numBoundingSet] = candidateSet.ptrTmmbrSet[minIndexTMMBR]; |
| _boundingSet.ptrPacketOHSet[numBoundingSet] = candidateSet.ptrPacketOHSet[minIndexTMMBR]; |
| _boundingSet.ptrSsrcSet[numBoundingSet] = candidateSet.ptrSsrcSet[minIndexTMMBR]; |
| // set intersection value |
| _ptrIntersectionBoundingSet[numBoundingSet] = 0; |
| // calculate its maximum packet rate (where its line crosses x-axis) |
| _ptrMaxPRBoundingSet[numBoundingSet] = _boundingSet.ptrTmmbrSet[numBoundingSet]*1000 / float(8*_boundingSet.ptrPacketOHSet[numBoundingSet]); |
| numBoundingSet++; |
| // remove from candidate list |
| candidateSet.ptrTmmbrSet[minIndexTMMBR] = 0; |
| candidateSet.ptrPacketOHSet[minIndexTMMBR] = 0; |
| candidateSet.ptrSsrcSet[minIndexTMMBR] = 0; |
| numCandidates--; |
| |
| // 4. Discard from candidate list all tuple w/ lower OH (next tuple must be steeper) |
| for (WebRtc_UWord32 i = 0; i < candidateSet.sizeOfSet; i++) |
| { |
| if(candidateSet.ptrTmmbrSet[i] > 0 && candidateSet.ptrPacketOHSet[i] < _boundingSet.ptrPacketOHSet[0]) |
| { |
| candidateSet.ptrTmmbrSet[i] = 0; |
| candidateSet.ptrPacketOHSet[i] = 0; |
| candidateSet.ptrSsrcSet[i] = 0; |
| numCandidates--; |
| } |
| } |
| |
| if (numCandidates == 0) |
| { |
| _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 (WebRtc_UWord32 i = 0; i < candidateSet.sizeOfSet; i++) |
| { |
| if (candidateSet.ptrTmmbrSet[i] > 0) |
| { |
| curCandidateTMMBR = candidateSet.ptrTmmbrSet[i]; |
| curCandidatePacketOH = candidateSet.ptrPacketOHSet[i]; |
| curCandidateSSRC = candidateSet.ptrSsrcSet[i]; |
| curCandidateIndex = i; |
| candidateSet.ptrTmmbrSet[curCandidateIndex] = 0; |
| candidateSet.ptrPacketOHSet[curCandidateIndex] = 0; |
| candidateSet.ptrSsrcSet[curCandidateIndex] = 0; |
| break; |
| } |
| } |
| } |
| |
| // 6. Calculate packet rate and intersection of the current line with line of last tuple in selected list |
| float packetRate = float(curCandidateTMMBR - _boundingSet.ptrTmmbrSet[numBoundingSet-1])*1000 / (8*(curCandidatePacketOH - _boundingSet.ptrPacketOHSet[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.ptrTmmbrSet[numBoundingSet] = 0; |
| _boundingSet.ptrPacketOHSet[numBoundingSet] = 0; |
| _boundingSet.ptrSsrcSet[numBoundingSet] = 0; |
| _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.ptrTmmbrSet[numBoundingSet] = curCandidateTMMBR; |
| _boundingSet.ptrPacketOHSet[numBoundingSet] = curCandidatePacketOH; |
| _boundingSet.ptrSsrcSet[numBoundingSet] = curCandidateSSRC; |
| _ptrIntersectionBoundingSet[numBoundingSet] = packetRate; |
| _ptrMaxPRBoundingSet[numBoundingSet] = _boundingSet.ptrTmmbrSet[numBoundingSet]*1000 / float(8*_boundingSet.ptrPacketOHSet[numBoundingSet]); |
| numBoundingSet++; |
| } |
| numCandidates--; |
| getNewCandidate = true; |
| } |
| |
| // 9. Go back to step 5 if any tuple remains in candidate list |
| } while (numCandidates > 0); |
| } |
| _boundingSet.lengthOfSet = numBoundingSet; |
| return numBoundingSet; |
| } |
| |
| bool |
| TMMBRHelp::IsOwner(const WebRtc_UWord32 ssrc, |
| const WebRtc_UWord32 length) const |
| { |
| CriticalSectionScoped lock(_criticalSection); |
| |
| if (length == 0) |
| { |
| // empty bounding set |
| return false; |
| } |
| |
| for(WebRtc_UWord32 i = 0; (i < length) && (i < _boundingSet.sizeOfSet); ++i) |
| { |
| if(_boundingSet.ptrSsrcSet[i] == ssrc) |
| { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| WebRtc_Word32 |
| TMMBRHelp::CalcMinMaxBitRate(const WebRtc_UWord32 totalPacketRate, |
| const WebRtc_UWord32 lengthOfBoundingSet, |
| WebRtc_UWord32& minBitrateKbit, |
| WebRtc_UWord32& maxBitrateKbit) const |
| { |
| CriticalSectionScoped lock(_criticalSection); |
| |
| if (lengthOfBoundingSet <= 0 || _candidateSet.sizeOfSet == 0) |
| { |
| // empty bounding set |
| return -1; |
| } |
| |
| minBitrateKbit = 0xFFFFFFFF; |
| maxBitrateKbit = 0; |
| |
| for (WebRtc_UWord32 i = 0; i < _candidateSet.sizeOfSet; ++i) |
| { |
| if (_candidateSet.ptrTmmbrSet[i]) |
| { |
| WebRtc_Word32 curNetBitRate = static_cast<WebRtc_Word32>((_candidateSet.ptrTmmbrSet[i]*1000.0 |
| - (totalPacketRate * (_candidateSet.ptrPacketOHSet[i] << 3)))/1000 + 0.5); |
| |
| if (curNetBitRate < 0) |
| { |
| // could be negative for a large packet rate |
| if(_audio) |
| { |
| curNetBitRate = MIN_AUDIO_BW_MANAGEMENT_BITRATE; |
| }else |
| { |
| curNetBitRate = MIN_VIDEO_BW_MANAGEMENT_BITRATE; |
| } |
| } |
| minBitrateKbit = (WebRtc_UWord32(curNetBitRate) < minBitrateKbit) ? curNetBitRate : minBitrateKbit; |
| } |
| } |
| maxBitrateKbit = minBitrateKbit; |
| |
| if (maxBitrateKbit == 0 || maxBitrateKbit < minBitrateKbit) |
| { |
| return -1; |
| } |
| |
| if(_audio) |
| { |
| if (minBitrateKbit < MIN_AUDIO_BW_MANAGEMENT_BITRATE) |
| { |
| minBitrateKbit = MIN_AUDIO_BW_MANAGEMENT_BITRATE; |
| } |
| if (maxBitrateKbit < MIN_AUDIO_BW_MANAGEMENT_BITRATE) |
| { |
| maxBitrateKbit = MIN_AUDIO_BW_MANAGEMENT_BITRATE; |
| } |
| }else |
| { |
| if (minBitrateKbit < MIN_VIDEO_BW_MANAGEMENT_BITRATE) |
| { |
| minBitrateKbit = MIN_VIDEO_BW_MANAGEMENT_BITRATE; |
| } |
| if (maxBitrateKbit < MIN_VIDEO_BW_MANAGEMENT_BITRATE) |
| { |
| maxBitrateKbit = MIN_VIDEO_BW_MANAGEMENT_BITRATE; |
| } |
| } |
| |
| return 0; |
| } |
| } // namespace webrtc |