Darwin-Streaming-Server/Server.tproj/RTPOverbufferWindow.cpp

180 lines
6.7 KiB
C++
Raw Normal View History

/*
*
* @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;
}