blob: fb1ed625ed83b324275e2cdfc9a387e335a08291 [file] [log] [blame]
/*
* 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