180 lines
6.7 KiB
C++
180 lines
6.7 KiB
C++
|
/*
|
||
|
*
|
||
|
* @APPLE_LICENSE_HEADER_START@
|
||
|
*
|
||
|
* Copyright (c) 1999-2008 Apple Inc. All Rights Reserved.
|
||
|
*
|
||
|
* This file contains Original Code and/or Modifications of Original Code
|
||
|
* as defined in and that are subject to the Apple Public Source License
|
||
|
* Version 2.0 (the 'License'). You may not use this file except in
|
||
|
* compliance with the License. Please obtain a copy of the License at
|
||
|
* http://www.opensource.apple.com/apsl/ and read it before using this
|
||
|
* file.
|
||
|
*
|
||
|
* The Original Code and all software distributed under the License are
|
||
|
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||
|
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||
|
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||
|
* Please see the License for the specific language governing rights and
|
||
|
* limitations under the License.
|
||
|
*
|
||
|
* @APPLE_LICENSE_HEADER_END@
|
||
|
*
|
||
|
*/
|
||
|
/*
|
||
|
File: RTPOverbufferWindow.cpp
|
||
|
|
||
|
Contains: Implementation of the class
|
||
|
|
||
|
Written By: Denis Serenyi
|
||
|
|
||
|
*/
|
||
|
|
||
|
#include "RTPOverbufferWindow.h"
|
||
|
#include "OSMemory.h"
|
||
|
#include "MyAssert.h"
|
||
|
|
||
|
RTPOverbufferWindow::RTPOverbufferWindow(UInt32 inSendInterval, UInt32 inInitialWindowSize, UInt32 inMaxSendAheadTimeInSec,
|
||
|
Float32 inOverbufferRate)
|
||
|
: fWindowSize(inInitialWindowSize),
|
||
|
fBytesSentSinceLastReport(0),
|
||
|
fSendInterval(inSendInterval),
|
||
|
fBytesDuringLastSecond(0),
|
||
|
fLastSecondStart(-1),
|
||
|
fBytesDuringPreviousSecond(0),
|
||
|
fPreviousSecondStart(-1),
|
||
|
fBytesDuringBucket(0),
|
||
|
fBucketBegin(0),
|
||
|
fBucketTimeAhead(0),
|
||
|
fPreviousBucketTimeAhead(0),
|
||
|
fMaxSendAheadTime(inMaxSendAheadTimeInSec * 1000),
|
||
|
fWriteBurstBeginning(false),
|
||
|
fOverbufferingEnabled(true),
|
||
|
fOverbufferRate(inOverbufferRate),
|
||
|
fSendAheadDurationInMsec(1000),
|
||
|
fOverbufferWindowBegin(-1)
|
||
|
{
|
||
|
if (fSendInterval == 0)
|
||
|
{
|
||
|
fOverbufferingEnabled = false;
|
||
|
fSendInterval = 200;
|
||
|
}
|
||
|
|
||
|
if(fOverbufferRate < 1.0)
|
||
|
fOverbufferRate = 1.0;
|
||
|
|
||
|
}
|
||
|
|
||
|
SInt64 RTPOverbufferWindow::CheckTransmitTime(const SInt64& inTransmitTime, const SInt64& inCurrentTime, SInt32 inPacketSize)
|
||
|
{
|
||
|
// if this is the beginning of a bucket interval, roll over figures from last time.
|
||
|
// accumulate statistics over the period of a second
|
||
|
if (inCurrentTime - fBucketBegin > fSendInterval)
|
||
|
{
|
||
|
fPreviousBucketBegin = fBucketBegin;
|
||
|
fBucketBegin = inCurrentTime;
|
||
|
if (fPreviousBucketBegin == 0)
|
||
|
fPreviousBucketBegin = fBucketBegin - fSendInterval;
|
||
|
fBytesDuringBucket = 0;
|
||
|
if (inCurrentTime - fLastSecondStart > 1000)
|
||
|
{
|
||
|
fBytesDuringPreviousSecond = fBytesDuringLastSecond;
|
||
|
fBytesDuringLastSecond = 0;
|
||
|
fPreviousSecondStart = fLastSecondStart;
|
||
|
fLastSecondStart = inCurrentTime;
|
||
|
}
|
||
|
|
||
|
fPreviousBucketTimeAhead = fBucketTimeAhead;
|
||
|
}
|
||
|
|
||
|
if (fOverbufferWindowBegin == -1)
|
||
|
fOverbufferWindowBegin = inCurrentTime;
|
||
|
|
||
|
if ((inTransmitTime <= inCurrentTime + fSendInterval) ||
|
||
|
(fOverbufferingEnabled && (inTransmitTime <= inCurrentTime + fSendInterval + fSendAheadDurationInMsec)))
|
||
|
{
|
||
|
//
|
||
|
// If this happens, this packet needs to be sent regardless of overbuffering
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (!fOverbufferingEnabled || (fWindowSize == 0))
|
||
|
return inTransmitTime;
|
||
|
|
||
|
// if the client is running low on memory, wait a while for it to be freed up
|
||
|
// there's nothing magic bout these numbers, we're just trying to be conservative
|
||
|
if ((fWindowSize != -1) && (inPacketSize * 5 > fWindowSize - fBytesSentSinceLastReport))
|
||
|
{
|
||
|
return inCurrentTime + (fSendInterval * 5); // client reports don't come that often
|
||
|
}
|
||
|
|
||
|
// if we're far enough ahead, then wait until it's time to send more packets
|
||
|
if (inTransmitTime - inCurrentTime > fMaxSendAheadTime)
|
||
|
return inTransmitTime - fMaxSendAheadTime + fSendInterval;
|
||
|
|
||
|
// during the first second just send packets normally
|
||
|
// if (fPreviousSecondStart == -1)
|
||
|
// return inCurrentTime + fSendInterval;
|
||
|
|
||
|
// now figure if we want to send this packet during this bucket. We have two limitations.
|
||
|
// First we scale up bitrate slowly, so we should only try and send a little more than we
|
||
|
// sent recently (averaged over a second or two). However, we always try and send at
|
||
|
// least the current bitrate and never more than double.
|
||
|
// SInt32 currentBitRate = fBytesDuringBucket * 1000 / (inCurrentTime - fPreviousBucketBegin);
|
||
|
// SInt32 averageBitRate = (fBytesDuringPreviousSecond + fBytesDuringLastSecond) * 1000 / (inCurrentTime - fPreviousSecondStart);
|
||
|
// SInt32 averageBitRate = fBytesDuringPreviousSecond * 1000 / (fLastSecondStart - fPreviousSecondStart);
|
||
|
fBucketTimeAhead = inTransmitTime - inCurrentTime;
|
||
|
// printf("Current br = %d, average br = %d (cta = %qd, pta = %qd)\n", currentBitRate, averageBitRate, currentTimeAhead, fPreviousBucketTimeAhead);
|
||
|
|
||
|
// always try and stay as far ahead as we were before
|
||
|
if (fBucketTimeAhead < fPreviousBucketTimeAhead)
|
||
|
return -1;
|
||
|
|
||
|
// but don't send at more that double the bitrate (for any given time we should only get further
|
||
|
// ahead by that amount of time)
|
||
|
//printf("cta - pta = %qd, ct - pbb = %qd\n", fBucketTimeAhead - fPreviousBucketTimeAhead, SInt64((inCurrentTime - fPreviousBucketBegin) * (fOverbufferRate - 1.0)));
|
||
|
if (fBucketTimeAhead - fPreviousBucketTimeAhead > ((inCurrentTime - fPreviousBucketBegin) * (fOverbufferRate - 1.0)))
|
||
|
{
|
||
|
fBucketTimeAhead = fPreviousBucketTimeAhead + SInt64((inCurrentTime - fPreviousBucketBegin) * (fOverbufferRate - 1.0));
|
||
|
return inCurrentTime + fSendInterval; // this will get us to the next bucket
|
||
|
}
|
||
|
|
||
|
// don't send more than 10% over the average bitrate for the previous second
|
||
|
// if (currentBitRate > averageBitRate * 11 / 10)
|
||
|
// return inCurrentTime + fSendInterval; // this will get us to the next bucket
|
||
|
|
||
|
return -1; // send this packet
|
||
|
}
|
||
|
|
||
|
void RTPOverbufferWindow::ResetOverBufferWindow()
|
||
|
{
|
||
|
fBytesDuringLastSecond = 0;
|
||
|
fLastSecondStart = -1;
|
||
|
fBytesDuringPreviousSecond = 0;
|
||
|
fPreviousSecondStart = -1;
|
||
|
fBytesDuringBucket = 0;
|
||
|
fBucketBegin = 0;
|
||
|
fBucketTimeAhead = 0;
|
||
|
fPreviousBucketTimeAhead = 0;
|
||
|
fOverbufferWindowBegin = -1;
|
||
|
}
|
||
|
|
||
|
void RTPOverbufferWindow::AddPacketToWindow(SInt32 inPacketSize)
|
||
|
{
|
||
|
fBytesDuringBucket += inPacketSize;
|
||
|
fBytesDuringLastSecond += inPacketSize;
|
||
|
fBytesSentSinceLastReport += inPacketSize;
|
||
|
}
|
||
|
|
||
|
void RTPOverbufferWindow::EmptyOutWindow(const SInt64& inCurrentTime)
|
||
|
{
|
||
|
// no longer needed
|
||
|
}
|
||
|
|
||
|
void RTPOverbufferWindow::SetWindowSize(UInt32 inWindowSizeInBytes)
|
||
|
{
|
||
|
fWindowSize = inWindowSizeInBytes;
|
||
|
fBytesSentSinceLastReport = 0;
|
||
|
}
|