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