258 lines
9.1 KiB
C++
258 lines
9.1 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: RTPBandwidthTracker.cpp
|
|
|
|
Contains: Implementation of class decribed in .h file
|
|
|
|
*/
|
|
|
|
#include "RTPBandwidthTracker.h"
|
|
#include "MyAssert.h"
|
|
#include "OS.h"
|
|
|
|
void RTPBandwidthTracker::SetWindowSize( SInt32 clientWindowSize )
|
|
{
|
|
//
|
|
// Currently we only allow this info to be set once
|
|
if (fClientWindow > 0)
|
|
return;
|
|
|
|
// call SetWindowSize once the clients buffer size is known
|
|
// since this occurs before the stream starts to send
|
|
|
|
fClientWindow = clientWindowSize;
|
|
fLastCongestionAdjust = 0;
|
|
|
|
#if RTP_PACKET_RESENDER_DEBUGGING
|
|
//€ test to see what happens w/o slow start at beginning
|
|
//if ( initSlowStart )
|
|
// qtss_printf( "ack list initializing with slow start.\n" );
|
|
//else
|
|
// qtss_printf( "ack list initializing at full speed.\n" );
|
|
#endif
|
|
|
|
if ( fUseSlowStart )
|
|
{
|
|
fSlowStartThreshold = clientWindowSize * 3 / 4;
|
|
|
|
//
|
|
// This is a change to the standard TCP slow start algorithm. What
|
|
// we found was that on high bitrate high latency networks (a DSL connection, perhaps),
|
|
// it took just too long for the ACKs to come in and for the window size to
|
|
// grow enough. So we cheat a bit.
|
|
fCongestionWindow = clientWindowSize / 2;
|
|
//fCongestionWindow = kMaximumSegmentSize;
|
|
}
|
|
else
|
|
{
|
|
fSlowStartThreshold = clientWindowSize;
|
|
fCongestionWindow = clientWindowSize;
|
|
}
|
|
|
|
if ( fSlowStartThreshold < kMaximumSegmentSize )
|
|
fSlowStartThreshold = kMaximumSegmentSize;
|
|
}
|
|
|
|
void RTPBandwidthTracker::EmptyWindow( UInt32 bytesIncreased, Bool16 updateBytesInList )
|
|
{
|
|
if (bytesIncreased == 0)
|
|
return;
|
|
|
|
Assert(fClientWindow > 0 && fCongestionWindow > 0);
|
|
|
|
if(fBytesInList < bytesIncreased)
|
|
bytesIncreased = fBytesInList;
|
|
|
|
if (updateBytesInList)
|
|
fBytesInList -= bytesIncreased;
|
|
|
|
// this assert hits
|
|
Assert(fBytesInList < ((UInt32)fClientWindow + 2000)); //mainly just to catch fBytesInList wrapping below 0
|
|
|
|
// update the congestion window by the number of bytes just acknowledged.
|
|
|
|
if ( fCongestionWindow >= fSlowStartThreshold )
|
|
{
|
|
// when we hit the slow start threshold, only increase the
|
|
// window for each window full of acks.
|
|
fCongestionWindow += bytesIncreased * bytesIncreased / fCongestionWindow;
|
|
}
|
|
else
|
|
//
|
|
// This is a change to the standard TCP slow start algorithm. What
|
|
// we found was that on high bitrate high latency networks (a DSL connection, perhaps),
|
|
// it took just too long for the ACKs to come in and for the window size to
|
|
// grow enough. So we cheat a bit.
|
|
fCongestionWindow += bytesIncreased;
|
|
|
|
|
|
if ( fCongestionWindow > fClientWindow )
|
|
fCongestionWindow = fClientWindow;
|
|
|
|
// qtss_printf("Window = %d, %d left\n", fCongestionWindow, fCongestionWindow-fBytesInList);
|
|
}
|
|
|
|
void RTPBandwidthTracker::AdjustWindowForRetransmit()
|
|
{
|
|
// this assert hits
|
|
Assert(fBytesInList < ((UInt32)fClientWindow + 2000)); //mainly just to catch fBytesInList wrapping below 0
|
|
|
|
// slow start says that we should reduce the new ss threshold to 1/2
|
|
// of where started getting errors ( the current congestion window size )
|
|
|
|
// so, we get a burst of re-tx becuase our RTO was mis-estimated
|
|
// it doesn't seem like we should lower the threshold for each one.
|
|
// it seems like we should just lower it when we first enter
|
|
// the re-transmit "state"
|
|
// if ( !fIsRetransmitting )
|
|
// fSlowStartThreshold = fCongestionWindow/2;
|
|
|
|
// make sure that it is at least 1 packet
|
|
if ( fSlowStartThreshold < kMaximumSegmentSize )
|
|
fSlowStartThreshold = kMaximumSegmentSize;
|
|
|
|
// start the full window segemnt counter over again.
|
|
fSlowStartByteCount = 0;
|
|
|
|
// tcp resets to one (max segment size) mss, but i'm experimenting a bit
|
|
// with not being so brutal.
|
|
|
|
//curAckList->fCongestionWindow = kMaximumSegmentSize;
|
|
|
|
// fCongestionWindow = kMaximumSegmentSize;
|
|
// fCongestionWindow = fCongestionWindow / 2; // half the congestion window size
|
|
SInt64 theTime = OS::Milliseconds();
|
|
if (theTime - fLastCongestionAdjust > 250)
|
|
{
|
|
fSlowStartThreshold = fCongestionWindow * 3 / 4;
|
|
fCongestionWindow = fCongestionWindow / 2;
|
|
fLastCongestionAdjust = theTime;
|
|
}
|
|
|
|
/*
|
|
if ( fSlowStartThreshold < fCongestionWindow )
|
|
fCongestionWindow = fSlowStartThreshold/2;
|
|
else
|
|
fCongestionWindow = fCongestionWindow /2;
|
|
*/
|
|
|
|
if ( fCongestionWindow < kMaximumSegmentSize )
|
|
fCongestionWindow = kMaximumSegmentSize;
|
|
|
|
// qtss_printf("Congestion window now %d\n", fCongestionWindow);
|
|
fIsRetransmitting = true;
|
|
}
|
|
|
|
void RTPBandwidthTracker::AddToRTTEstimate( SInt32 rttSampleMSecs )
|
|
{
|
|
// qtss_printf("%d ", rttSampleMSecs);
|
|
// static int count = 0;
|
|
// if ((count++ % 10) == 0) qtss_printf("\n");
|
|
|
|
// this assert hits
|
|
Assert(fBytesInList < ((UInt32)fClientWindow + 2000)); //mainly just to catch fBytesInList wrapping below 0
|
|
|
|
if ( fRunningAverageMSecs == 0 )
|
|
fRunningAverageMSecs = rttSampleMSecs * 8; // init avg to cur sample, scaled by 2**3
|
|
|
|
SInt32 delta = rttSampleMSecs - fRunningAverageMSecs / 8; // scale average back to get cur delta from sample
|
|
|
|
// add 1/8 the delta back to the smooth running average
|
|
fRunningAverageMSecs = fRunningAverageMSecs + delta; // same as: rt avg = rt avg + delta / 8, but scaled
|
|
|
|
if ( delta < 0 )
|
|
delta = -1*delta; // absolute value
|
|
|
|
/*
|
|
|
|
fRunningMeanDevationMSecs is kept scaled by 4
|
|
|
|
|
|
so this is the same as
|
|
|
|
fRunningMeanDevationMSecs = fRunningMeanDevationMSecs + ( |delta| - fRunningMeanDevationMSecs ) /4;
|
|
*/
|
|
|
|
fRunningMeanDevationMSecs += delta - fRunningMeanDevationMSecs / 4;
|
|
|
|
|
|
fUnadjustedRTO = fCurRetransmitTimeout = fRunningAverageMSecs / 8 + fRunningMeanDevationMSecs;
|
|
|
|
// rto should not be too low..
|
|
if ( fCurRetransmitTimeout < kMinRetransmitIntervalMSecs )
|
|
fCurRetransmitTimeout = kMinRetransmitIntervalMSecs;
|
|
|
|
// or too high...
|
|
if ( fCurRetransmitTimeout > kMaxRetransmitIntervalMSecs )
|
|
fCurRetransmitTimeout = kMaxRetransmitIntervalMSecs;
|
|
// qtss_printf("CurTimeout == %d\n", fCurRetransmitTimeout);
|
|
}
|
|
|
|
void RTPBandwidthTracker::UpdateStats()
|
|
{
|
|
fNumStatsSamples++;
|
|
|
|
if (fMaxCongestionWindowSize < fCongestionWindow)
|
|
fMaxCongestionWindowSize = fCongestionWindow;
|
|
if (fMinCongestionWindowSize > fCongestionWindow)
|
|
fMinCongestionWindowSize = fCongestionWindow;
|
|
|
|
if (fMaxRTO < fUnadjustedRTO)
|
|
fMaxRTO = fUnadjustedRTO;
|
|
if (fMinRTO > fUnadjustedRTO)
|
|
fMinRTO = fUnadjustedRTO;
|
|
|
|
fTotalCongestionWindowSize += fCongestionWindow;
|
|
fTotalRTO += fUnadjustedRTO;
|
|
}
|
|
|
|
void RTPBandwidthTracker::UpdateAckTimeout(UInt32 bitsSentInInterval, SInt64 intervalLengthInMsec)
|
|
{
|
|
//
|
|
// First figure out how long it will take us to fill up our window, based on
|
|
// the movie's current bit rate
|
|
UInt32 unadjustedTimeout = 0;
|
|
if (bitsSentInInterval > 0)
|
|
unadjustedTimeout = (UInt32) ((intervalLengthInMsec * fCongestionWindow) / bitsSentInInterval);
|
|
|
|
//
|
|
// If we wait that long, that's too long because we need to actually wait for the ack to arrive.
|
|
// So, subtract 1/2 the rto - the last ack timeout
|
|
UInt32 rto = (UInt32)fUnadjustedRTO;
|
|
if (rto < fAckTimeout)
|
|
rto = fAckTimeout;
|
|
UInt32 adjustment = (rto - fAckTimeout) / 2;
|
|
//qtss_printf("UnadjustedTimeout = %"_U32BITARG_". rto: %"_S32BITARG_". Last ack timeout: %"_U32BITARG_". Adjustment = %"_U32BITARG_".", unadjustedTimeout, fUnadjustedRTO, fAckTimeout, adjustment);
|
|
if (adjustment > unadjustedTimeout)
|
|
adjustment = unadjustedTimeout;
|
|
fAckTimeout = unadjustedTimeout - adjustment;
|
|
|
|
//qtss_printf("AckTimeout: %"_U32BITARG_"\n",fAckTimeout);
|
|
if (fAckTimeout > kMaxAckTimeout)
|
|
fAckTimeout = kMaxAckTimeout;
|
|
else if (fAckTimeout < kMinAckTimeout)
|
|
fAckTimeout = kMinAckTimeout;
|
|
}
|