/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: hxsm.cpp,v 1.17.2.1 2004/07/09 02:05:30 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

/*
 *  
 *  ASM Manager for all streams in all Players.
 *
 *  This class will manage the bandwidth requirements
 *  of multiple streams.
 */

#include "hlxclib/stdio.h"      /* printf */
#include "hlxclib/stdlib.h"     /* atoi on Mac */
#include "hxtypes.h"    /* Basic Types */
#include "hxcom.h"      /* IUnknown */
#include "hxslist.h"
#include "hxerror.h"
#include "hxsmbw.h"
#include "hxsm.h"      /* HXSM */
#include "hxsmutil.h"
#include "hxpref.h"
#include "ihxpckts.h"
#include "hxcore.h"
#include "asmrulep.h"
#include "hxbuffer.h"
#include "chxpckts.h"
#include "hxbsrc.h"
#include "hxsrc.h"
#include "errdbg.h"
#include "rtspif.h"
#include "hxurl.h"
#include "hxtick.h"
#include "hxstrutl.h"
#include "hxbufctl.h"

#include "hxheap.h"

#ifdef _DEBUG
#undef HX_THIS_FILE
static const char HX_THIS_FILE[] = __FILE__;
#endif

#ifndef MIN
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#endif
#ifndef MAX
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#endif

HXSM::HXSM()
    : m_State(HX_NONE)
    , m_lRefCount(0)
    , m_pSubscriptionVariables(0)
    , m_ulOriginalHighestBandwidthAvail(0)
    , m_ulHighestBandwidthAvail(0)
    , m_ulPeakUsedBandwidth(0)
    , m_ulUpShiftRingPos(0)
    , m_ulUpShiftBandwidthAvail(0)
    , m_ulNumSources(0)
    , m_ulMaxAccelBitRate(0xffffffff)
    , m_ulNumReportsSinceUpShift(2)
    , m_ulLastStableBandwidth(0)
    , m_ulUpShiftTestPointScaleFactor(4000)
    , m_ulOfferToRecalc(0)
    , m_ulNextPacketWindow(0)
    , m_lPacketCounter(0)
    , m_ulUpShiftPastResistanceCount(0)
    , m_lLoss(0)
    , m_bInitialHighBwAvail(TRUE)
    , m_bPipeFull(FALSE)
    , m_bUpShiftInfoReady(FALSE)
    , m_bDidOfferUpShiftToRecalc(FALSE)
    , m_bLoadTest(FALSE)
    , m_bDoAccel(TRUE)
    , m_bDoDecel(TRUE)
    , m_bDisableBothAccelDecel(FALSE)
    , m_fAccelerationFactor(4.0)
    , m_bEnableSDB(TRUE)
#ifndef GOLD
    , m_pEM(0)
#endif
{
    m_pASMSourceInfo = new CHXSimpleList;
    m_pASMStreamInfo = new CHXSimpleList;
}

HXSM::~HXSM()
{
#ifndef GOLD
    HX_RELEASE(m_pEM);
#endif
    CHXSimpleList::Iterator     i;
    ASMSourceInfo*                 pASMSourceInfo;
    ASMStreamInfo*                 pASMStreamInfo;

    for (i = m_pASMSourceInfo->Begin(); i != m_pASMSourceInfo->End(); ++i)
    {
	pASMSourceInfo = (ASMSourceInfo*)(*i);
	pASMSourceInfo->Release();
    }

    for (i = m_pASMStreamInfo->Begin(); i != m_pASMStreamInfo->End(); ++i)
    {
	pASMStreamInfo = (ASMStreamInfo*)(*i);
	delete pASMStreamInfo;
    }

    delete m_pASMSourceInfo;
    delete m_pASMStreamInfo;
    HX_RELEASE(m_pSubscriptionVariables);
}

STDMETHODIMP_(UINT32)
HXSM::AddRef(void)
{
    return InterlockedIncrement(&m_lRefCount);
}

STDMETHODIMP_(UINT32)
HXSM::Release(void)
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
	return m_lRefCount;
    }

    delete this;
    return 0;
}

STDMETHODIMP
HXSM::QueryInterface
(
    REFIID interfaceID,
    void** ppInterfaceObj
)
{
    QInterfaceList qiList[] =
        {
            { GET_IIDHANDLE(IID_IUnknown), (IUnknown*)this },
            { GET_IIDHANDLE(IID_IHXBandwidthManager), (IHXBandwidthManager*)this },
        };
    
    return ::QIFind(qiList, QILISTSIZE(qiList), interfaceID, ppInterfaceObj);
}

STDMETHODIMP
HXSM::RegisterSource(HXSource* pSource, IUnknown* pUnknown)
{
    IHXSourceBandwidthInfo* pSBI;
    IHXPreferences* pPreferences = NULL;
    ASMSourceInfo* pASMSourceInfo = NULL;
    UINT16 i;

#ifndef GOLD
    HX_RELEASE(m_pEM);
    pUnknown->QueryInterface(IID_IHXErrorMessages, (void **)&m_pEM);
#endif

    DEBUG_OUT(m_pEM, DOL_BWMGR, (s,	"Register Source %p %s", pSource, pSource->GetURL()));

    if (HXR_OK == pSource->QueryInterface(IID_IHXSourceBandwidthInfo, 
	    (void **)&pSBI))
    {
	pASMSourceInfo = new ASMSourceInfo(pSource, this);
	pASMSourceInfo->AddRef();

	pASMSourceInfo->m_pSBI = pSBI;
	/* Local source is always considered in perfect play mode UNLESS we are
	 * in simulated network playback mode (used Preview mode in the Encoder)
	 */
	pASMSourceInfo->m_bPerfectPlay = pSource->IsPerfectPlay() && !pSource->IsSimulatedNetworkPlayback();

	m_ulNumSources++;
	m_pASMSourceInfo->AddTail((void *)pASMSourceInfo);
	pSBI->InitBw(pASMSourceInfo);
    }
    else
    {
	return HXR_OK;
    }

    /*
     * This variable tells us the highest amount of bandwidth that we
     * believe is useable.  If we stream thin, then the highest value
     * that the pipe can support would be the aggregate bandwidth of
     * the sources.  Barring any thinning, this would be the bandwidth
     * from prefs (unless we can determine it through fast buffering or
     * another transport feature).  This value is used when starting sources
     * as a initial guess for bandwidth (to set subscriptions).
     */
    if (m_ulHighestBandwidthAvail == 0)
    {
	pUnknown->QueryInterface(IID_IHXPreferences, (void **)&pPreferences);

        UINT32 ulTemp = 0;
	/* Get initial bandwidth guess from Prefs */
        if (HXR_OK == ReadPrefINT32(pPreferences, "Bandwidth", ulTemp))
        {
	    m_bInitialHighBwAvail = TRUE;

	    /* Translate the bandwidth from prefs into a starting point */
	         if (ulTemp == 14400)    m_ulHighestBandwidthAvail = 11000;
	    else if (ulTemp == 19200)    m_ulHighestBandwidthAvail = 14400;
	    else if (ulTemp == 28800)    m_ulHighestBandwidthAvail = 21600;
	    else if (ulTemp == 33600)    m_ulHighestBandwidthAvail = 25000;
	    else if (ulTemp == 34400)    m_ulHighestBandwidthAvail = 34400;
	    else if (ulTemp == 57600)    m_ulHighestBandwidthAvail = 50000;
	    else if (ulTemp == 65536)	 m_ulHighestBandwidthAvail = 56360;
	    else if (ulTemp == 115200)   m_ulHighestBandwidthAvail = 100000;
	    else if (ulTemp >  150000)   m_ulHighestBandwidthAvail = (UINT32)(ulTemp * 0.90);
	    else
	    {
		m_ulHighestBandwidthAvail = (UINT32)(ulTemp * 0.85);
	    }

	    /* Translate the bandwidth from prefs into a starting point */
	         if (ulTemp == 14400)    m_ulPeakUsedBandwidth = 12240;
	    else if (ulTemp == 19200)    m_ulPeakUsedBandwidth = 16320;
	    else if (ulTemp == 28800)    m_ulPeakUsedBandwidth = 24480;
	    else if (ulTemp == 33600)    m_ulPeakUsedBandwidth = 28560;
	    else if (ulTemp == 34400)    m_ulPeakUsedBandwidth = 34400;
	    else if (ulTemp == 57600)    m_ulPeakUsedBandwidth = 51840;
	    else if (ulTemp == 65536)	 m_ulPeakUsedBandwidth = 58980;
	    else if (ulTemp == 115200)   m_ulPeakUsedBandwidth = 104000;
	    else if (ulTemp >  150000)   m_ulPeakUsedBandwidth = (UINT32)(ulTemp * 0.91);
	    else
	    {
		m_ulPeakUsedBandwidth = (UINT32)(ulTemp * 0.90);
	    }

	    /*
	     * Figure out the resistance bitrate for upshifting.
	     * Modems get 65k.
	     * DSL / Low BW LANs get their pref value.
	     * High bandwidth devices cap at 600k
	     *    (unless the presentation is more)
	     */
	    if (ulTemp < 65000)
	    {
		m_ulResistanceBitRate = 65000;
	    }
	    else if (ulTemp < 600000)
	    {
		m_ulResistanceBitRate = m_ulPeakUsedBandwidth;
	    }
	    else
	    {
		m_ulResistanceBitRate = 600000;
	    }

	    m_ulOriginalResistanceBitRate = m_ulResistanceBitRate;
	}
	else
	{
	    /* Wild Guess */
	    m_ulHighestBandwidthAvail = 40000;
	    m_ulPeakUsedBandwidth = 40000;
	}



	//	       LOAD TEST ONLY OPTIONS
	// Read in the preferences for enabling/disabling Accel and 
	// Decel for load tests.
	//
        ReadPrefBOOL(pPreferences, "LoadTest", m_bLoadTest);
        ReadPrefBOOL(pPreferences, "DoAccel", m_bDoAccel);
        ReadPrefBOOL(pPreferences, "DoDecel", m_bDoDecel);
        ReadPrefBOOL(pPreferences, "DisableBothAccelDecel", m_bDisableBothAccelDecel);
        ReadPrefFLOAT(pPreferences, "AccelerationFactor", m_fAccelerationFactor);

	// DisableBothAccelDecel override all other preferences.
	// If it is true, set both DoAccel and DoDecel to false.
	// If DisableBothAccelDecel is FALSE do nothing at all.
	if (TRUE == m_bDisableBothAccelDecel)
	{
	    m_bDoAccel = FALSE;
	    m_bDoDecel = FALSE;
	}
	
	/////////////// END LOAD TEST ONLY SECTION

	/* Get MaxBandwidth from Prefs */
        ReadPrefINT32(pPreferences, "MaxBandwidth", m_ulMaxAccelBitRate); 

	HX_RELEASE(pPreferences);
	m_ulOriginalHighestBandwidthAvail = m_ulHighestBandwidthAvail;
    }

    /* This is ONLY used for load testing */
    if (m_bLoadTest)
    {
	const char* pSourceURL	       = NULL;
	CHXURL*	    pURL	       = NULL;
	UINT32	    ulTemp	       = 0;
	IHXValues*  pOptions	       = NULL;

	pSourceURL = pSource->GetURL();
	HX_ASSERT(pSourceURL);
	pURL = new CHXURL(pSourceURL);
	HX_ASSERT(pURL);
	pOptions = pURL->GetOptions();
	
	// Check for the DoAccel and DoDecel URL options. If they are
	// there set the sources properties to reflect them.  Also,
	// each option is overriden, in the 'off' state by the global
	// preferences DoAccel and DoDecel. Remember, both the
	// preferences values can be made false by setting the global
	// preference DisableBothAccelDecel to TRUE. Truth Table:
	//
	// Resulting ASM functionality = Option ^ Preference
	//
	// or,
	//
	// m_bSourceAccelAllowed = m_bSourceAccelAllowed^m_bDoAccel
	// m_bSourceDecelAllowed = m_bSourceDecelAllowed^m_bDoDecel
	//
	//                                           Resulting
	//   Preferences     URL Option String   ASM Functionality
	// DoAccel  DoDecel   DoAccel  DoDecel    Accel   Decel
	//   0        0         0        0          0       0
	//   0        0         0        1          0       0
	//   0        0         1        0          0       0
	//   0        0         1        1          0       0
	//   ...      ...       ...      ...        ...     ...
	//   0        1         0        0          0       0
	//   1        1         1        1          1       1
	// You get the idea. :-)
	
	//First, get any option strings. They default to FALSE in
	//the constructors.
	if( NULL != pOptions )
	{
	    if( pOptions->GetPropertyULONG32("DoAccel", ulTemp) == HXR_OK)
	    {
		pASMSourceInfo->m_bSourceAccelAllowed = (ulTemp == 1);
	    }
	    if( pOptions->GetPropertyULONG32("DoDecel", ulTemp) == HXR_OK)
	    {
		pASMSourceInfo->m_bSourceDecelAllowed = (ulTemp == 1);
	    }
	}//NULL != pOptions

	//Now, do the global preference overrides.
	pASMSourceInfo->m_bSourceAccelAllowed =
	    pASMSourceInfo->m_bSourceAccelAllowed && m_bDoAccel;
	pASMSourceInfo->m_bSourceDecelAllowed =
	    pASMSourceInfo->m_bSourceDecelAllowed && m_bDoDecel;


	HX_RELEASE(pOptions);
	HX_DELETE(pURL);


    }//m_bLoadTest

    //Report status of load test vars.
//      DEBUG_OUT( m_pEM, DOL_ASM, (s, "LoadTest %d", m_bLoadTest));
//      DEBUG_OUT( m_pEM, DOL_ASM, (s, "    DoAccel %u",  (UINT16)m_bDoAccel));
//      DEBUG_OUT( m_pEM, DOL_ASM, (s, "    DoDecel %u", (UINT16)m_bDoDecel));
//      DEBUG_OUT( m_pEM, DOL_ASM, (s, "    DisableBothAccelDecel %d",
//  				m_bDisableBothAccelDecel));
//      DEBUG_OUT( m_pEM, DOL_ASM, (s, "    m_bSourceAccelAllowed %d",
//  		    pASMSourceInfo->m_bSourceAccelAllowed));
//      DEBUG_OUT( m_pEM, DOL_ASM, (s, "    m_bSourceDecelAllowed %d",
//  		pASMSourceInfo->m_bSourceDecelAllowed));
    

    UINT32 unStreamCount = pSource->GetStreamCount();     
    pASMSourceInfo->m_pStreams = new ASMStreamInfo*[unStreamCount];;

    BOOL bEnableSDB = FALSE;
    bEnableSDB = (unStreamCount == 0);

    pASMSourceInfo->m_ulLowestBandwidthBeforeTimeStamp = 0;
    for (i = 0; i < unStreamCount; i++)
    {
	IUnknown* pStream = 0;
        IHXStream* pHXStream = NULL;
	ASMStreamInfo* pInfo = new ASMStreamInfo;

	HX_VERIFY(HXR_OK == pSource->GetStream(i, pStream));
	HX_VERIFY(HXR_OK == pStream->QueryInterface
	    (IID_IHXStreamBandwidthNegotiator,
	    (void **)&pInfo->m_pNegotiator));
	HX_VERIFY(HXR_OK == pStream->QueryInterface
	    (IID_IHXStreamBandwidthBias,
	    (void **)&pInfo->m_pBias));
	HX_VERIFY(HXR_OK == pStream->QueryInterface
	    (IID_IHXAtomicRuleGather,
	    (void **)&pInfo->m_pRuleGather));

        pStream->QueryInterface(IID_IHXStream, (void**)&pHXStream);

        if (pHXStream)
        {
            pInfo->m_ulStreamNumber = pHXStream->GetStreamNumber();
            pHXStream->Release();
        }

	pInfo->m_pNegotiator->GetFixedBandwidth(pInfo->m_ulFixedBandwidth);

        if (pInfo->m_ulFixedBandwidth != 1)
        {
            bEnableSDB = TRUE;
        }

	pInfo->m_pASMSourceInfo = pASMSourceInfo;
	pASMSourceInfo->m_pStreams[i] = pInfo;
	m_pASMStreamInfo->AddTail((void *)pInfo);

	UINT32 ulLowestBandwidthBeforeTimeStamp = 0;
	if (pInfo->m_ulFixedBandwidth != 0)
	{
	    ulLowestBandwidthBeforeTimeStamp = pInfo->m_ulFixedBandwidth;
	}
	else
	{
	    // XXXNH: 6/7/99
	    // We make this threshold array once and create it to be as large
	    // as we will ever possibly need.  Each subsequent call to
	    // GetThresholdInfo() should never need more than GetNumThresholds
	    // returns.  UNLESS the ASMStreamInfo's m_pNegotiator were to 
	    // change, but I don't think that's a feature we support.

	    UINT32 ulNumThresholds = 
		pInfo->m_pNegotiator->GetNumThresholds();
	    pInfo->m_pThreshold = new float[ulNumThresholds];
	    pInfo->m_pNegotiator->GetThresholdInfo(
		(float*)pInfo->m_pThreshold, 
		pInfo->m_ulNumThresholds);
	    pInfo->m_ulMaxEffectiveThreshold = pInfo->m_ulNumThresholds - 1;
	    ulNumThresholds = pInfo->m_ulNumThresholds;

	    ulLowestBandwidthBeforeTimeStamp = (UINT32) pInfo->m_pThreshold[ulNumThresholds-1];
	    for (UINT32 i = ulNumThresholds-1; i > 0 ; i--)
	    {
		UINT32 ulCurBand = (UINT32) pInfo->m_pThreshold[i];
		// used for timestamp rules
		if (ulCurBand == 0 || ulCurBand == 1)
		{
		    break;
		}
		else 
		{
		    ulLowestBandwidthBeforeTimeStamp = ulCurBand;
		}
	    }
	}

	HX_ASSERT(ulLowestBandwidthBeforeTimeStamp != 0);

	pASMSourceInfo->m_ulLowestBandwidthBeforeTimeStamp += ulLowestBandwidthBeforeTimeStamp;

	HX_RELEASE(pStream);
    }

    // Rule for determining if SetDeliveryBandwidth is used.
    //
    // HXASMStream::bFixed   bEnabledSDB  m_bEnableSDB
    // ------------------------------------------
    //     FALSE             FALSE       TRUE  // no fbw means bw > 1
    //     FALSE             TRUE        TRUE  // at least one rule is fbw==1
    //     TRUE              FALSE       FALSE // all bw are fbw==1
    //     TRUE              TRUE        TRUE  // no streamcount, SDB true


    m_bEnableSDB = bEnableSDB;




    return HXR_OK;
}

STDMETHODIMP
HXSM::RegisterSourcesDone()
{
    if (m_pASMSourceInfo->GetCount() > 0 &&
	m_pASMStreamInfo->GetCount() > 0)
    {
        CHXSimpleList::Iterator     i;
        ASMSourceInfo*		    pASMSourceInfo;

        for (i = m_pASMSourceInfo->Begin(); i != m_pASMSourceInfo->End(); ++i)
        {
	    pASMSourceInfo = (ASMSourceInfo*)(*i);

            if (pASMSourceInfo && pASMSourceInfo->m_pSource)
            {
	        DEBUG_OUT(m_pEM, DOL_TRANSPORT, (s, "(%p)RegisterSourcesDone AccelFactor:%f", pASMSourceInfo->m_pSource, m_fAccelerationFactor));

                if (pASMSourceInfo->m_pSource->m_bFastStart)
                {
                     pASMSourceInfo->m_ulLastSetDelivery = 0;
                }
            }
        }

	m_State = INIT;
	RecalcAccel();
    }

    return HXR_OK;    
}


BOOL
HXSM::NotEnoughBandwidth()
{
    CHXSimpleList::Iterator i;
    ASMSourceInfo*          pASMSourceInfo;
    UINT32		    ulTotal	    = 0;
    BOOL		    bIsLive	    = FALSE;

    for (i = m_pASMSourceInfo->Begin(); i != m_pASMSourceInfo->End(); ++i)
    {
	pASMSourceInfo = (ASMSourceInfo*)(*i);

	ulTotal += pASMSourceInfo->m_ulSubscribedBw;

	if (pASMSourceInfo->m_pSource &&
	    pASMSourceInfo->m_pSource->IsLive())
	{
	    bIsLive = TRUE;
	}
    }

    if (bIsLive && ulTotal > m_ulHighestBandwidthAvail)
    {
	return TRUE;
    }
    else
    {
	return FALSE;
    }
}

STDMETHODIMP
HXSM::UnRegisterSource(HXSource* pSource)
{
    LISTPOSITION		lPos;
    ASMSourceInfo*                 pASMSourceInfo = 0;
    ASMStreamInfo*                 pASMStreamInfo;
    BOOL			bFound = FALSE;

    lPos = m_pASMSourceInfo->GetHeadPosition();

    DEBUG_OUT(m_pEM, DOL_BWMGR, (s,	"UnRegister Source %p %s", pSource, pSource->GetURL()));

    while (lPos)
    {
	pASMSourceInfo = (ASMSourceInfo *)m_pASMSourceInfo->GetAt(lPos);
	if (pASMSourceInfo->m_pSource == pSource)
	{
	    m_pASMSourceInfo->RemoveAt(lPos);
	    pASMSourceInfo->Done();
	    bFound  = TRUE;
	    break;
	}
	m_pASMSourceInfo->GetNext(lPos);
    }
    
    if (!bFound)
    {
	return HXR_OK;
    }

    lPos = m_pASMStreamInfo->GetHeadPosition();

    while (lPos)
    {
	pASMStreamInfo = (ASMStreamInfo*) m_pASMStreamInfo->GetAt(lPos);
	if (pASMStreamInfo->m_pASMSourceInfo == pASMSourceInfo)
	{
	    /* RemoveAt returns the next position in the list.
	     * DO NOT use GetNext if you remove a node.
	     */
	    lPos = m_pASMStreamInfo->RemoveAt(lPos);

	    if (pASMStreamInfo->m_pNegotiator)
	    {
		pASMStreamInfo->m_pNegotiator->UnRegister();
	    }

	    delete pASMStreamInfo;
	}
	else
	{
	    m_pASMStreamInfo->GetNext(lPos);
	}
    }
    
    HX_RELEASE(pASMSourceInfo);
    
    m_ulNumSources--;

    if (m_ulNumSources > 0)
    {
	m_State = REDIST;
	RecalcAccel();
    }

    return HXR_OK;
}


/* Called by HXPlayer at end of each presentation */
STDMETHODIMP
HXSM::PresentationDone(void)
{
    if (m_ulNumSources == 0)
    {
	m_ulHighestBandwidthAvail = 0;
	m_ulPeakUsedBandwidth = 0;
	m_bInitialHighBwAvail = TRUE;
	m_bPipeFull = FALSE;
	m_bUpShiftInfoReady = FALSE;
	m_ulUpShiftRingPos = 0;
	m_ulUpShiftBandwidthAvail = 0;
	m_ulNumReportsSinceUpShift = 2;
	m_ulOfferToRecalc = 0;
	m_State = HX_NONE;
	m_bDidOfferUpShiftToRecalc = FALSE;
	m_lLoss = 0;
	m_ulNextPacketWindow = 0;
	m_lPacketCounter = 0;
	m_ulUpShiftPastResistanceCount = 0;
	m_ulUpShiftTestPointScaleFactor = 4000;
    }

    return HXR_OK;
}

/* If the source has enough data, it may tell the bandwidth
 * manager to cut down on accelerated buffering.
 */
STDMETHODIMP
HXSM::ChangeAccelerationStatus(HXSource* pSource,
			        BOOL	   bMayBeAccelerated,
				BOOL	   bUseAccelerationFactor,
				UINT32	   ulAccelerationFactor)
{
    LISTPOSITION		lPos;
    ASMSourceInfo*              pASMSourceInfo = 0;
    BOOL			bFound = FALSE;

    lPos = m_pASMSourceInfo->GetHeadPosition();

    while (lPos)
    {
	pASMSourceInfo = (ASMSourceInfo *)m_pASMSourceInfo->GetAt(lPos);
	if (pASMSourceInfo->m_pSource == pSource)
	{
	    bFound  = TRUE;
	    break;
	}
	m_pASMSourceInfo->GetNext(lPos);
    }

    /* This assert may happen if you are in PNA and are in the debugger
     * accumulating tons of data at the networking layer
     */
//    HX_ASSERT(bFound);

    if (bFound)
    {
	pASMSourceInfo->ChangeAccelerationStatus(bMayBeAccelerated,
	    bUseAccelerationFactor, ulAccelerationFactor);
    }
    else
    {
	/* Hmmm... ASM cannot help us here */
	/* Must be PNA. This may happen in case of TCP where the server sends 
	 * data 300% faster than the content bandwidth.
	 * Do the old style flow control by Pausing/Resuming the server
	 */
	if (!bMayBeAccelerated)
	{
	    pSource->DoPause();
	}
	else
	{
	    pSource->DoResume();
	}
    }

    return HXR_OK;
}

//ChangeBW() allows setting the maximum bandwidth limit on a source. Called by 
//RTSPProtocol::HandleSetParameterRequest()

STDMETHODIMP
HXSM::ChangeBW(UINT32 newBW, HXSource* pSource)
{
    CHXSimpleList::Iterator     ii;
    ASMSourceInfo*                 pASMSourceInfo;  
    BOOL bSourceFound = FALSE;     
    DEBUG_OUT(m_pEM, DOL_BWMGR, (s,"(%p)Request to change BW to %ld", pSource, newBW));
    for(ii = m_pASMSourceInfo->Begin(); ii != m_pASMSourceInfo->End(); ++ii)
    {
	pASMSourceInfo = (ASMSourceInfo*)(*ii);
	if (pASMSourceInfo->m_pSource == pSource) //Comparing Pointers
	{
	    bSourceFound = TRUE;
	    ASMRuleBook* pRuleBook = pASMSourceInfo->m_pMasterRuleBook;
	    BOOL bDownShift = FALSE;
	    if (pRuleBook)
	    {
		UINT32 ulNumStreamsForThisSource = 
		    pASMSourceInfo->m_pSource->GetStreamCount();

		BOOL* pCurrentSubInfo = new BOOL[pRuleBook->GetNumRules()];

		HXSMUpdateSubscriptionVars(m_pSubscriptionVariables, 
					   newBW, FALSE, 0);

		HX_RESULT lResult =
		    pRuleBook->GetSubscription(pCurrentSubInfo, m_pSubscriptionVariables);
		HX_ASSERT(lResult == HXR_OK);
		for (UINT16 idxRule = 0; idxRule < pRuleBook->GetNumRules(); 
		idxRule++)
		{	
		    if (pCurrentSubInfo[idxRule])
		    {
			IHXValues* pProps = 0;
			// Set Distribution
			pRuleBook->GetProperties(idxRule, pProps);
			for (UINT32 j = 0; j < ulNumStreamsForThisSource; j++)
			{		    
			    UINT8 pTemp[128];
			    IHXBuffer* pBw = NULL;

			    // Don't assume that streamid == index in pASMSourceInfo->m_pStreams[j]
			    SafeSprintf((char *)pTemp, 128, "Stream%ldBandwidth", pASMSourceInfo->m_pStreams[j]->m_ulStreamNumber); /* Flawfinder: ignore */

			    /*
			    * if this tripps either there's a bug in here or
			    * the content is messed up
			    *
			    */ 
			    HX_VERIFY(HXR_OK==
				pProps->GetPropertyCString((char*)pTemp, pBw));
			    if (pBw)
			    {
				BOOL bFoundMax=FALSE;
				UINT32 newMaxBW = (UINT32) 
				    (atoi((char*)pBw->GetBuffer()));
				for(int cnt =0; 
				cnt < (int)pASMSourceInfo->m_pStreams[j]->
				    m_ulNumThresholds; cnt++)
				{
				    if (newMaxBW == 
					pASMSourceInfo->m_pStreams[j]->m_pThreshold[cnt])
				    {
					bFoundMax = TRUE;
					pASMSourceInfo->m_pStreams[j]->
					    m_ulMaxEffectiveThreshold = cnt;
					// We are downshifting!!
					if ((int)pASMSourceInfo->m_pStreams[j]->
					    m_ulThresholdPosition > cnt) 
					{
					    bDownShift = TRUE;
					    pASMSourceInfo->m_pStreams[j]->
						m_ulThresholdPosition = cnt;
					}
					// We will try to upshift
					else if ((int)pASMSourceInfo->m_pStreams[j]->
					    m_ulThresholdPosition < cnt)  
					    pASMSourceInfo->m_bTryToUpShift = TRUE;
					break;
				    }
				}
				HX_ASSERT(bFoundMax);
				if (bDownShift || pASMSourceInfo->m_bTryToUpShift)
				    pASMSourceInfo->m_bAdjustBandwidth = TRUE;
				pBw->Release();
				pBw = NULL;
			    } 
			} 
			HX_RELEASE(pProps);
		    } 
		} 
	    }
	    //Assume a single stream source, or a fixedbw source .. 
	    //for live, we have more than 1 streams ??
	    else 	    
	    {
		BOOL bFoundMax = FALSE;
		UINT32 numStreams = pASMSourceInfo->m_pSource->GetStreamCount();
		//for(int j = 0; j < numStreams; j++)
		//	{
		for(int cnt =0; 
		cnt < (int)pASMSourceInfo->m_pStreams[0]->m_ulNumThresholds; cnt++)
		{
		    if (newBW == pASMSourceInfo->m_pStreams[0]->m_pThreshold[cnt])
		    {
			bFoundMax = TRUE;
			// We are downshifting!!
			if ((int)pASMSourceInfo->m_pStreams[0]->m_ulThresholdPosition > cnt) 
			{
			    bDownShift = TRUE;
			    pASMSourceInfo->m_pStreams[0]->m_ulThresholdPosition = cnt;
			}
			// We are upshifting
			else if ((int)pASMSourceInfo->m_pStreams[0]->m_ulThresholdPosition < cnt) 
			    pASMSourceInfo->m_bTryToUpShift	= TRUE;
			pASMSourceInfo->m_pStreams[0]->m_ulMaxEffectiveThreshold = cnt;
			break;
		    }
		}
		if (bDownShift || pASMSourceInfo->m_bTryToUpShift)
		    pASMSourceInfo->m_bAdjustBandwidth = TRUE;
	    }
	    m_State = REDIST;
	    RecalcAccel();
	} 
    } 
    HX_ASSERT(bSourceFound);

    return HXR_OK; 
}


void
HXSM::Recalc()
{
    CHXSimpleList::Iterator     i, j;
    ASMSourceInfo*                 pASMSourceInfo;
    ASMStreamInfo*                 pASMStreamInfo;
    INT32			lAggregateBandwidthUsage = 0;
    INT32			lAggregateBandwidthSent = 0;
    UINT32			ulSourceCount;
    UINT32			ulStreamCount;
    float			fBiasMean = (float) 0.;

    ulSourceCount = m_pASMSourceInfo->GetCount();
    ulStreamCount = m_pASMStreamInfo->GetCount();

    for (i = m_pASMSourceInfo->Begin(); i != m_pASMSourceInfo->End(); ++i)
    {
	pASMSourceInfo = (ASMSourceInfo*)(*i);

        /* Init these for later */
	pASMSourceInfo->m_ulMasterOffer = 0;
	pASMSourceInfo->m_bTimeStampDelivery = FALSE;
    }

    lAggregateBandwidthUsage = m_ulOfferToRecalc;

    INT32 lCorrectAggregateBandwidthUsage = lAggregateBandwidthUsage;

    /*
     * For each stream that is at a fixed bitrate, remove that bitrate
     * from our available bandwidth.
     */
    for (j = m_pASMStreamInfo->Begin(); j != m_pASMStreamInfo->End(); ++j)
    {
        INT32 lBias;

	pASMStreamInfo = (ASMStreamInfo*)(*j);

	HX_VERIFY(HXR_OK == pASMStreamInfo->m_pBias->GetBiasFactor(lBias));
	fBiasMean += lBias;

	if (pASMStreamInfo->m_ulFixedBandwidth)
	{
	    lAggregateBandwidthUsage -= pASMStreamInfo->m_ulFixedBandwidth;
	    ulStreamCount--;
	}

	/* Init this for later */
	pASMStreamInfo->m_ulMasterRuleBookSetOffer = 0;
    }

    /* fBiasMean is not needed if everything has a fixed bandwidth */
    if (ulStreamCount != 0)
    {
	fBiasMean /= (float)ulStreamCount;
    }

    /*
     * Calculate the offer for each source that has a master rulebook
     * defining it's bandwidth division.
     */
    INT32 lNewAggregateBandwidthUsage = lAggregateBandwidthUsage;

    for (j = m_pASMStreamInfo->Begin(); j != m_pASMStreamInfo->End(); ++j)
    {
	INT32 lBias;

	pASMStreamInfo = (ASMStreamInfo*)(*j);

	HX_VERIFY(HXR_OK == pASMStreamInfo->m_pBias->GetBiasFactor(lBias));

	if (pASMStreamInfo->m_pASMSourceInfo->m_pMasterRuleBook)
	{
	    UINT32 ulOffer = 
		(UINT32)(lAggregateBandwidthUsage / (float)ulStreamCount);

	    ulOffer += 
		(UINT32)(((float)lBias - fBiasMean) *
		((float)lAggregateBandwidthUsage / 100.0) *
		(2.0 / (float)ulStreamCount));

	    pASMStreamInfo->m_pASMSourceInfo->m_ulMasterOffer += ulOffer;
	    lNewAggregateBandwidthUsage -= ulOffer;
	}
    }

    lAggregateBandwidthUsage = lNewAggregateBandwidthUsage;

    /*
     * For each source that has a master rule book, evaluate it to find
     * out how much to distribute to each stream.
     */
    for (i = m_pASMSourceInfo->Begin(); i != m_pASMSourceInfo->End(); ++i)
    {
	pASMSourceInfo = (ASMSourceInfo*)(*i);
	ASMRuleBook* pRuleBook = pASMSourceInfo->m_pMasterRuleBook;

	if (pRuleBook)
	{
	    UINT32 ulNumStreamsForThisSource = 
		pASMSourceInfo->m_pSource->GetStreamCount();

	    BOOL* pCurrentSubInfo = new BOOL[pRuleBook->GetNumRules()];

	    HXSMUpdateSubscriptionVars(m_pSubscriptionVariables, 
				       pASMSourceInfo->m_ulMasterOffer,
				       FALSE, 0);

	    HX_RESULT lResult =
		pRuleBook->GetSubscription(pCurrentSubInfo, m_pSubscriptionVariables);
	    HX_ASSERT(lResult == HXR_OK);

	    for (UINT16 idxRule = 0; idxRule < pRuleBook->GetNumRules();
		idxRule++)
	    {
		if (pCurrentSubInfo[idxRule])
		{
		    IHXValues* pProps = 0;
		    // Set Distribution
		    pRuleBook->GetProperties(idxRule, pProps);

		    for (UINT32 j = 0; j < ulNumStreamsForThisSource; j++)
		    {
			UINT8       pTemp[128];
			IHXBuffer* pBw = NULL;
			HX_RESULT   hxResult;
			
			// Don't assume that streamid == index in pASMSourceInfo->m_pStreams[j]
                        SafeSprintf((char *)pTemp, 128, "Stream%ldBandwidth", pASMSourceInfo->m_pStreams[j]->m_ulStreamNumber); /* Flawfinder: ignore */

			/*
			 * if this tripps either there's a bug in here or
			 * the content is messed up
			 *
			 */
			//If the Stream?Bandwidth property isn't found for
			//all streams don't worry about it. Not all streams
			//may have rule books.
			hxResult = pProps->GetPropertyCString((char *)pTemp, pBw);
			if(HXR_OK==hxResult && pBw)
			{
			    pASMSourceInfo->m_pStreams[j]->
				m_ulMasterRuleBookSetOffer =
				atoi((char*)pBw->GetBuffer());

			    pBw->Release();
			    pBw = NULL;
			}
			else
			{
			    pASMSourceInfo
				->m_pStreams[j]
				->m_ulMasterRuleBookSetOffer = 0;
			}//HXR_OK==hxResult && pBw
		    }

		    HX_RELEASE(pProps);
		    break;
		}
	    }

	    delete [] pCurrentSubInfo;
	}
    }

    /*
     *  Now go through each of the streams that are not at a
     *  fixed bitrate and try to distribute the rest of the bandwidth.
     */
    UINT32 ulTakenBandwidth = 0;

    for (j = m_pASMStreamInfo->Begin(); j != m_pASMStreamInfo->End(); ++j)
    {
	INT32 lBias;

	pASMStreamInfo = (ASMStreamInfo*)(*j);

	if (pASMStreamInfo->m_ulFixedBandwidth != 0)
	{
	    ulTakenBandwidth += pASMStreamInfo->m_ulFixedBandwidth;
	    pASMStreamInfo->m_ulResistanceToLower = 0xffffffff;
	    continue;
	}

	HX_VERIFY(HXR_OK == pASMStreamInfo->m_pBias->GetBiasFactor(lBias));

	HX_ASSERT(pASMStreamInfo->m_pThreshold != NULL);

	UINT32 ulOffer = 0;

	if (pASMStreamInfo->m_pASMSourceInfo->m_bPerfectPlay)
	{
	    UINT32 i = pASMStreamInfo->m_ulMaxEffectiveThreshold;
	    ulTakenBandwidth += (UINT32)pASMStreamInfo->m_pThreshold[i];
	    pASMStreamInfo->m_ulResistanceToLower = 0xffffffff;
	    pASMStreamInfo->m_ulOffer = (UINT32)pASMStreamInfo->m_pThreshold[i];
	    pASMStreamInfo->m_ulThresholdPosition = i;

	    /*
	     * If we are in perfect play mode, just select the highest bandwidth rule
	     * and don't negotiate any further.
	     */
	    continue;
	}

	if (pASMStreamInfo->m_ulMasterRuleBookSetOffer)
	{
	    ulOffer = pASMStreamInfo->m_ulMasterRuleBookSetOffer - 1;
	}
	else
	{
	    ulOffer =
		(UINT32)(lAggregateBandwidthUsage / (float)ulStreamCount);

	    ulOffer += 
		(UINT32)(((float)lBias - fBiasMean) *
		((float)lAggregateBandwidthUsage / 100.0) *
		(2.0 / (float)ulStreamCount));
	}

	HX_ASSERT(pASMStreamInfo->m_ulMaxEffectiveThreshold >= 0);
	if (pASMStreamInfo->m_ulMaxEffectiveThreshold == 0)
	{
	    HX_ASSERT(pASMStreamInfo->m_pThreshold[0] == 0);

	    ulTakenBandwidth += (UINT32)pASMStreamInfo->m_pThreshold[0];
	    pASMStreamInfo->m_ulResistanceToLower = 0xffffffff;
	}
	else
	{
	    for (UINT32 i = 1; i <= pASMStreamInfo->m_ulMaxEffectiveThreshold; i++)
	    {
		if ((ulOffer <= pASMStreamInfo->m_pThreshold[i]) ||
			(i == (pASMStreamInfo->m_ulMaxEffectiveThreshold)))
		{
		    ulTakenBandwidth += (UINT32)pASMStreamInfo->m_pThreshold[i];

		    if (i == 1)
		    {
			pASMStreamInfo->m_ulResistanceToLower = 0xffffffff;
		    }
		    else
		    {
			pASMStreamInfo->m_ulResistanceToLower = (ulOffer -
			    (UINT32)pASMStreamInfo->m_pThreshold[i - 1]) * ulOffer;
		    }

		    pASMStreamInfo->
			m_ulOffer = ulOffer;
		    pASMStreamInfo->m_ulThresholdPosition = i;

		    break;
		}
	    }
	}
    }

    lAggregateBandwidthUsage = lCorrectAggregateBandwidthUsage;

tryagain:

    if (lAggregateBandwidthUsage < (INT32)ulTakenBandwidth)
    {
	/* Resistance is Futile.  You will be Real(tm)lyAssimilated */
	UINT32 ulLowestResistance = 0xffffffff;
	ASMStreamInfo* pLowestResistanceStream  = 0;

	for (j = m_pASMStreamInfo->Begin(); j != m_pASMStreamInfo->End(); ++j)
	{
	    pASMStreamInfo = (ASMStreamInfo*)(*j);

	    if (pASMStreamInfo->m_ulResistanceToLower < ulLowestResistance)
	    {
		ulLowestResistance = pASMStreamInfo->m_ulResistanceToLower;
		pLowestResistanceStream = pASMStreamInfo;
	    }
	}

	if (ulLowestResistance == 0xffffffff)
	{
	}
	else
	{
	    ulTakenBandwidth -= (UINT32)
		pLowestResistanceStream->m_pThreshold[pLowestResistanceStream->m_ulThresholdPosition];

	    pLowestResistanceStream->m_ulThresholdPosition--;

	    ulTakenBandwidth += (UINT32)
		pLowestResistanceStream->m_pThreshold[pLowestResistanceStream->m_ulThresholdPosition];

	    if (pLowestResistanceStream->m_ulThresholdPosition == 1)
	    {
		pLowestResistanceStream->m_ulResistanceToLower = 0xffffffff;
	    }
	    else
	    {
		pLowestResistanceStream->m_ulResistanceToLower = (pLowestResistanceStream->m_ulOffer -
		    (UINT32)pLowestResistanceStream->m_pThreshold[
		    pLowestResistanceStream->m_ulThresholdPosition - 1]) *
		    pLowestResistanceStream->m_ulOffer;
	    }

	    goto tryagain;
	}
    }

    UINT32 ulLeftOverForDropByN = lAggregateBandwidthUsage - ulTakenBandwidth;
    BOOL bForce = FALSE;

    for (j = m_pASMStreamInfo->Begin(); j != m_pASMStreamInfo->End(); ++j)
    {
	pASMStreamInfo = (ASMStreamInfo*)(*j);

	UINT32 ulBw = 1;

	if (pASMStreamInfo->m_ulFixedBandwidth)
	{
	    ulBw = pASMStreamInfo->m_ulFixedBandwidth;
	}
	else
	{
	    ulBw = (UINT32)
		    pASMStreamInfo->m_pThreshold[pASMStreamInfo->m_ulThresholdPosition];
	}

	UINT32 ulBwOffered = ulBw;

	if (ulBw == 1)
	{
	    // Hack Alert for DropByN. XXXSMP
	    ulBwOffered = ulBw = ulLeftOverForDropByN;
	}

	if ((ulBw != pASMStreamInfo->m_ulLastBandwidth) &&
	    (!pASMStreamInfo->m_ulFixedBandwidth))
	{
	    bForce = TRUE;
	}
	pASMStreamInfo->m_pRuleGather->
	    RuleGather(&pASMStreamInfo->m_pASMSourceInfo->
	    m_SubscriptionChanges);

	pASMStreamInfo->SetLastBandwidth(ulBw);
	HX_ASSERT(ulBw == ulBwOffered);

	//update the HXASMStream with our new bandwidth
	pASMStreamInfo->NotifyNewBandwidth();

	//update the source's knowledge of tsd
	pASMStreamInfo->NotifyTimeStampDelivery();

	//if the stream is behind, tell the server to chill
	pASMStreamInfo->NotifyLimitBandwidth(ulBwOffered);

	pASMStreamInfo->m_pRuleGather->RuleGather(0);
    }

    for (i = m_pASMSourceInfo->Begin(); i != m_pASMSourceInfo->End(); ++i)
    {
	pASMSourceInfo = (ASMSourceInfo*)(*i);

	if (!pASMSourceInfo->m_SubscriptionChanges.IsEmpty())
	{
	    pASMSourceInfo->m_pStreams[0]->m_pRuleGather->
		RuleFlush(&pASMSourceInfo->m_SubscriptionChanges);

	    for (j = pASMSourceInfo->m_SubscriptionChanges.Begin();
		 j != pASMSourceInfo->m_SubscriptionChanges.End(); ++j)
	    {
		RTSPSubscription* pSub = (RTSPSubscription*)(*j);
		delete pSub;
	    }
	    pASMSourceInfo->m_SubscriptionChanges.RemoveAll();
	}
    }

    if (m_State == REDO_ACCEL)
    {
	RecalcAccel();
	return;
    }
    if (m_State == CONGESTION)
    {
	RecalcAccel();
	return;
    }
    if (m_State == INIT)
    {
	m_State = INIT_REDIST;
	RecalcAccel();
	return;
    }
    if (bForce)
    {
	m_State = REDO_ACCEL;
	RecalcAccel();
	return;
    }
}

void
HXSM::RecalcAccel()
{
    CHXSimpleList::Iterator     i, j;
    ASMSourceInfo*                 pASMSourceInfo;
    ASMStreamInfo*                 pASMStreamInfo;
    UINT32 ulAggregateUsed = 0;
    UINT32 ulTotalMaxSubscribedBw = 0;

    if (m_State == INIT)
    {
	m_ulOfferToRecalc = m_ulHighestBandwidthAvail;

	if (m_ulOfferToRecalc > m_ulMaxAccelBitRate)
	{
	    m_ulOfferToRecalc = m_ulMaxAccelBitRate;
	}
#ifdef MOREDEBUG
	DEBUG_OUT(m_pEM, DOL_TRANSPORT, (s,
			"INIT Offer to Recalc() %d", m_ulOfferToRecalc));
#endif

	Recalc();
	return;
    }

    for (i = m_pASMSourceInfo->Begin(); i != m_pASMSourceInfo->End(); ++i)
    {
	pASMSourceInfo = (ASMSourceInfo*)(*i);

	pASMSourceInfo->m_ulSubscribedBw = 0;
	pASMSourceInfo->m_ulMaxSubscribedBw = 0;
    }

    for (j = m_pASMStreamInfo->Begin(); j != m_pASMStreamInfo->End(); ++j)
    {
	pASMStreamInfo = (ASMStreamInfo*)(*j);

	if (pASMStreamInfo->m_ulFixedBandwidth)
	{
	    pASMStreamInfo->m_pASMSourceInfo->m_ulSubscribedBw += 
		pASMStreamInfo->m_ulFixedBandwidth;
	    pASMStreamInfo->m_pASMSourceInfo->m_ulMaxSubscribedBw += 
		pASMStreamInfo->m_ulFixedBandwidth;
	    ulTotalMaxSubscribedBw += pASMStreamInfo->m_ulFixedBandwidth;
	    ulAggregateUsed += pASMStreamInfo->m_ulFixedBandwidth;
	}
	else
	{
	    pASMStreamInfo->m_pASMSourceInfo->m_ulSubscribedBw += (UINT32)
		pASMStreamInfo->m_pThreshold[
		    pASMStreamInfo->m_ulThresholdPosition];
	    HX_ASSERT(pASMStreamInfo->m_pASMSourceInfo->m_ulSubscribedBw < 0x7fffffff);

	    pASMStreamInfo->m_pASMSourceInfo->m_ulMaxSubscribedBw += (UINT32)
		pASMStreamInfo->m_pThreshold[
		    pASMStreamInfo->m_ulMaxEffectiveThreshold];

	    ulTotalMaxSubscribedBw += (UINT32)pASMStreamInfo->m_pThreshold[
		    pASMStreamInfo->m_ulMaxEffectiveThreshold];

	    ulAggregateUsed += (UINT32)
		pASMStreamInfo->m_pThreshold[
		    pASMStreamInfo->m_ulThresholdPosition];
	}
    }

    UINT32 ulNumBehindSources		= 0;
    UINT32 ulNumSlightlyBehindSources	= 0;
    INT32  lAggregateBandwidthUsage	= 0;
    INT32  ulMaxNeededBW                = 0;
    BOOL   bAllZeroBw			= TRUE;
    BOOL   bFastStart                   = FALSE;
    
    for (i = m_pASMSourceInfo->Begin(); i != m_pASMSourceInfo->End(); ++i)
    {
	pASMSourceInfo = (ASMSourceInfo*)(*i);

	if (pASMSourceInfo->m_ulLastSetDelivery == 0xffffffff)
	{
	    pASMSourceInfo->m_ulLastSetDelivery = pASMSourceInfo->m_ulSubscribedBw;
	}

        // if ANY of the sources are fast start then we are in 
        // fast start mode. We may wish to re-visit this decision!
        // this comment is for you Rahul!

        if (pASMSourceInfo && pASMSourceInfo->m_pSource)
        {
            bFastStart = bFastStart | pASMSourceInfo->m_pSource->m_bFastStart;
        }

	ulNumBehindSources += pASMSourceInfo->m_bBehind ? 1 : 0;
	ulNumSlightlyBehindSources += pASMSourceInfo->m_bSlightlyBehind ? 1 : 0;

	lAggregateBandwidthUsage += pASMSourceInfo->GetBandwidth();
	if (!pASMSourceInfo->GetBandwidth())
	    lAggregateBandwidthUsage += pASMSourceInfo->m_ulSubscribedBw;
	else
	    bAllZeroBw = FALSE;

        ulMaxNeededBW += pASMSourceInfo->m_ulMaxSubscribedBw;
    }

    // so if we are in fast start, let's check to see if we don't want to be in
    // fast start!
    double maxPossibleAccelRatio = 0.;
    if (ulMaxNeededBW > 0.)
    {
        maxPossibleAccelRatio = (double) m_ulOriginalHighestBandwidthAvail / (double) ulMaxNeededBW;
    }

    for (i = m_pASMSourceInfo->Begin(); i != m_pASMSourceInfo->End(); ++i)
    {
	pASMSourceInfo = (ASMSourceInfo*)(*i);

        // I could check here for pASMSourceInfo->m_pSource->m_bFastStart, but really
        // does it matter?

        if (pASMSourceInfo && pASMSourceInfo->m_pSource)
        {
            pASMSourceInfo->m_pSource->SetMaxPossibleAccelRatio(maxPossibleAccelRatio);
        }

        // turn this stuff of if we are on modem. *sigh* It probably sort of works on 
        // modems too. This value makes it work on ISDN, that's ok by me, but if people
        // don't like it, I can change it.         
        if (maxPossibleAccelRatio < 1.5 || 
            (m_ulOriginalHighestBandwidthAvail < 110000)) // it now works on DSL or higher
        {
	    bFastStart = FALSE;
            if (pASMSourceInfo->m_pSource->m_bFastStart)
	    {
                DEBUG_OUT(m_pEM, DOL_TRANSPORT, (s,"(%p)ASM %d - Leaving TurboPlay", pASMSourceInfo->m_pSource, __LINE__));
		pASMSourceInfo->m_pSource->LeaveFastStart(TP_OFF_BY_NOTENOUGHBW);
	    }
        }

        // Are we currently asking for less than max? Is so Disable!
	//
	// XXX HP I am not sure the following logic is valid, it's possible 
	//	  pASMSourceInfo->m_ulSubscribedBw < pASMSourceInfo->m_ulMaxSubscribedBw because the
	//	  master rulebook defines so even though we have plenty of bandwidth(i.e. maxPossibleAccelRatio > 1.5)
	//	  If it's really the case that the source doesn't subscribe to its max. bandwidth because
	//	  we don't have enough bandwidth, then maxPossibleAccelRatio < 1.5 and we should left faststart
	//	  already
	/*
        if (pASMSourceInfo && pASMSourceInfo->GetBandwidth() && pASMSourceInfo->m_ulMaxSubscribedBw != pASMSourceInfo->m_ulSubscribedBw
            &&  pASMSourceInfo->m_pSource->m_bFastStart)
        {
            DEBUG_OUT(m_pEM, DOL_TRANSPORT, (s,"ASM %d - Leaving TurboPlay", __LINE__));
            DEBUG_OUT(m_pEM, DOL_TRANSPORT, (s,"%d %d %d", pASMSourceInfo->m_ulMaxSubscribedBw, pASMSourceInfo->GetBandwidth(),pASMSourceInfo->m_ulSubscribedBw));
            pASMSourceInfo->m_pSource->LeaveFastStart();
        }
	*/
    }

    if (m_bPipeFull == TRUE)
    {
	/*
	 * Adjust the highest available bandwidth because we have found
	 * the maximum bandwidth that the pipe can handle.  We do this
	 * so that sources that get added in the future will have
	 * some information about the max. bandwidth that exists.  This
	 * value is aggresive because a source will want to consume everything
	 * it can and fall back if it goes over the top.
	 */
	HX_ASSERT(lAggregateBandwidthUsage >= 100);
	if (lAggregateBandwidthUsage < 100)
	{
	    /*
	     * Please have at *least* 100bps before attempting to
	     * run the RealPlayer.
	     */
	    lAggregateBandwidthUsage = 100;
	}
	m_ulHighestBandwidthAvail = (UINT32)(lAggregateBandwidthUsage);
	m_ulPeakUsedBandwidth = (UINT32)(lAggregateBandwidthUsage);
	m_bInitialHighBwAvail = FALSE;
	m_bPipeFull = FALSE;
    }

    if (m_State == REDIST)
    {
        m_State = REDO_ACCEL;
        m_ulOfferToRecalc = m_ulHighestBandwidthAvail;
        Recalc();
	return;
    }

    if ((m_State == REDO_ACCEL) || (m_State == INIT_REDIST))
    {
	UINT32 ulBigValue;

	if ((m_bInitialHighBwAvail) || (m_State == REDO_ACCEL))
	{
	    ulBigValue = m_ulPeakUsedBandwidth;

	    if (bFastStart && (m_State == INIT_REDIST))
	    {
		ulBigValue = m_ulMaxAccelBitRate;
	    } 
	    else if ((INT32)ulBigValue > lAggregateBandwidthUsage)
	    {
		ulBigValue = lAggregateBandwidthUsage;
	    }
	}
	else
	{
	    /*
	     * If your state is REDO_ACCEL & we have an imperical value
	     * then maybe we should use this (ABU) instead of the above,
	     * but this value may not be completely up to date?
	     */
	    ulBigValue = lAggregateBandwidthUsage;
	}

	HX_ASSERT(ulBigValue > 0); // Rahul's Crazy, This won't ever happen!

	for (i = m_pASMSourceInfo->Begin(); i != m_pASMSourceInfo->End(); ++i)
	{
	    pASMSourceInfo = (ASMSourceInfo*)(*i);

	    IHXThinnableSource* pThin = 0;
	    UINT32 ulSourceBandwidth = pASMSourceInfo->m_ulSubscribedBw;

	    UINT32 ulNewValue = 
		    (UINT32)(
		    ((float)ulSourceBandwidth /
		    (float)ulAggregateUsed) *
		    (float)(ulBigValue));

	    if ((HXR_OK == pASMSourceInfo->m_pSource->
		QueryInterface(IID_IHXThinnableSource, (void **)&pThin)))
	    {
		if ((ulNewValue > (pASMSourceInfo->m_ulLastSetDelivery * 1.02)) ||
		    (ulNewValue < (pASMSourceInfo->m_ulLastSetDelivery * 0.98)) ||
		    (pASMSourceInfo->m_bAdjustBandwidth && pASMSourceInfo->m_pSource->IsLive()))
		{
                    BOOL bFastStart = FALSE;
                    
                    if (pASMSourceInfo && pASMSourceInfo->m_pSource)
                    {
                        bFastStart = pASMSourceInfo->m_pSource->m_bFastStart;
                    }

                    if (bFastStart)
                    {
		        if (ulNewValue > (pASMSourceInfo->m_ulMaxSubscribedBw * m_fAccelerationFactor))
		        {
			    ulNewValue = (UINT32) 
			        (pASMSourceInfo->m_ulMaxSubscribedBw * m_fAccelerationFactor);
		        }
                
                        // if the server says to cap the value, we cap the value!
                        if (pASMSourceInfo && pASMSourceInfo->m_pSource && ulNewValue  > pASMSourceInfo->m_pSource->m_ulMaxBandwidth * 1000 )
                        {
                            ulNewValue = pASMSourceInfo->m_pSource->m_ulMaxBandwidth * 1000;
                        }
                        // if the value is greater is than the pipe set it to a 
                        // little less than the pipe!
                        if (ulNewValue  > m_ulOriginalHighestBandwidthAvail)
                        {
                            ulNewValue = m_ulOriginalHighestBandwidthAvail;
                        }
                    }
                    else
                    {
		        if (ulNewValue > (pASMSourceInfo->m_ulMaxSubscribedBw * 4))
		        {
			    ulNewValue = (UINT32) 
			        (pASMSourceInfo->m_ulMaxSubscribedBw * 4);
		        }

			/*
			 * Live streams get capped at 107% of Max to prevent 
			 * unneeded bandwidht modulation.
			 */
			if ((pASMSourceInfo->m_pSource->IsLive()) &&
			   (ulNewValue >
				(pASMSourceInfo->m_ulMaxSubscribedBw * 1.07)))
			{
			    ulNewValue = (UINT32)
				(pASMSourceInfo->m_ulMaxSubscribedBw * 1.07);
			}
		    }
		    pASMSourceInfo->m_bAdjustBandwidth = FALSE;

		    if ((ulNewValue > (pASMSourceInfo->m_ulMaxSubscribedBw)) &&
			(ulNewValue > (ulSourceBandwidth * 3)) &&
			(pASMSourceInfo->m_ulLastSetDelivery >
			    pASMSourceInfo->m_ulMaxSubscribedBw))
		    {
			/*
			 * If we are already accelerating 3x subscribed bandwidth
			 * and we are about to upshift beyond the max possible
			 * bandwidth, then let's stop and take a breather just
			 * above the max subscription.  This prevents us from
			 * buffering huge amounts of the crappy low bw stream.
			 */
			UINT32 ulTemp;
			ulTemp = (UINT32) 
			    (pASMSourceInfo->m_ulMaxSubscribedBw * 1.10);
			ulNewValue = MIN(ulTemp, ulNewValue);
		    }

		    if ((ulNewValue < ulSourceBandwidth) && (ulSourceBandwidth > 10))
		    {
			pASMSourceInfo->m_pSource->EnterBufferedPlay();
		    } 
    		    if (ulNewValue >= ulSourceBandwidth)
		    {
			pASMSourceInfo->m_pSource->LeaveBufferedPlay();
		    }

		    if ((ulNewValue >= pASMSourceInfo->m_ulLastSetDelivery) &&
			(!pASMSourceInfo->m_bMayBeAccelerated) && 
			(!pASMSourceInfo->m_bTryToUpShift))
		    {
			goto dont_actually_set_the_rate;
		    }

		    if (ulNewValue > m_ulResistanceBitRate && !bFastStart)
		    {
			UINT32 ulActualResistanceBitRate = m_ulResistanceBitRate;

			if (ulActualResistanceBitRate < ulAggregateUsed)
			{
			    ulActualResistanceBitRate = (UINT32)
			        (ulAggregateUsed * 1.05);
			}

			if (ulNewValue > ulActualResistanceBitRate)
			{
			    ulNewValue = ulActualResistanceBitRate;
			}
		    }
 
		    if(pASMSourceInfo->m_bTryToUpShift)
		    {
		    	pASMSourceInfo->m_bTryToUpShift=FALSE;

			//XXXRA why not check for ulNewValue < ((UINT32)(ulSourceBandwidth * 1.15)
			// before assignment.  
			ulNewValue = (UINT32)(ulSourceBandwidth * 1.15);
		    }
		    
		    pASMSourceInfo->m_ulLastSetDelivery = ulNewValue;

		    UINT32 ulActualRate = ulNewValue;

		    /*
		     * Always keep TCP traffic faster then needed
		     * (but keep it quiet so the rest of the algorithm doesn't
		     * find out :-)
		     */
		    if (pASMSourceInfo->m_TransportType == TNG_TCP)
		    {
			ulActualRate = MAX(ulActualRate,
			    (UINT32)(ulSourceBandwidth * 1.10));
		    }

		    DEBUG_OUT(m_pEM, DOL_TRANSPORT, (s, "(%p)Redist: Tranmission Rate to %d", pASMSourceInfo->m_pSource, ulActualRate));


                    if (m_bEnableSDB)
                    {
                        pThin->SetDeliveryBandwidth(ulActualRate, 0);
                    }

    		    if (bFastStart)
		    {
			pASMSourceInfo->m_pSource->m_turboPlayStats.ulAcceleratedBW = ulActualRate;
		    }
		}
	    }
dont_actually_set_the_rate:
	    HX_RELEASE(pThin);
	}
	m_State = HX_NONE;
	return;
    }

    if (m_State == CHILL_BUFFERING)
    {
	m_ulOfferToRecalc = lAggregateBandwidthUsage;
#ifdef MOREDEBUG
	DEBUG_OUT(m_pEM, DOL_TRANSPORT, (s,
			"CHILL to Recalc() %d", m_ulOfferToRecalc));
#endif
	m_State = HX_NONE;

	Recalc();
    }

    if ((ulNumBehindSources) ||
	((lAggregateBandwidthUsage > ((INT32)m_ulMaxAccelBitRate + 100)) &&
	    (bAllZeroBw)))
    {
	if ((lAggregateBandwidthUsage > (INT32)m_ulMaxAccelBitRate) &&
	    (bAllZeroBw))
	{
	    lAggregateBandwidthUsage = m_ulMaxAccelBitRate;
	}

	// XXXRA change m_ulNumReportsSinceUpShift to m_lNumReportsSinceUpShift
	m_ulNumReportsSinceUpShift = -2;
	UINT32 ulLow  = (UINT32)(m_ulLastStableBandwidth * 0.90);
	UINT32 ulHigh = (UINT32)(m_ulLastStableBandwidth * 1.10);
	/*
	 * XXXSMP Maybe we don't want to use stable point when the aggregate
	 * detected is more then the stable point?
	 */
	if ((lAggregateBandwidthUsage > (INT32)ulLow) && 
	    (lAggregateBandwidthUsage < (INT32)ulHigh))
	{
	    /*
	     * If we are close to the last stable bandwidth, then let's
	     * try that one again.
	     */
	    lAggregateBandwidthUsage = m_ulLastStableBandwidth;
#ifdef MOREDEBUG
	    DEBUG_OUT(m_pEM, DOL_TRANSPORT, (s,
			"Used Stable Point %d", m_ulLastStableBandwidth));
#endif

	    m_ulLastStableBandwidth = 0;

	    m_ulUpShiftTestPointScaleFactor = MAX(1500, 
		(UINT32)(m_ulUpShiftTestPointScaleFactor * 0.85));
	}
	if (m_State != CONGESTION)
	{
	    for (i = m_pASMSourceInfo->Begin(); i != m_pASMSourceInfo->End(); ++i)
	    {
		pASMSourceInfo = (ASMSourceInfo*)(*i);

		UINT32 ulSourceBandwidth = pASMSourceInfo->m_ulSubscribedBw;
		UINT32 ulNewValue = 
			(UINT32)(
			(float)ulSourceBandwidth /
			(float)ulAggregateUsed *
			(float)lAggregateBandwidthUsage * 0.97);

		if (ulNewValue < (pASMSourceInfo->m_ulSubscribedBw))
		{
		    // Attempt ASM Switching to reduce bandwidth usage.
		    m_State = CONGESTION;
		    m_ulOfferToRecalc = (UINT32)
		        (lAggregateBandwidthUsage * 0.97);

#ifdef MOREDEBUG
		    DEBUG_OUT(m_pEM, DOL_TRANSPORT, (s,
			"CONGESTION to Recalc() %d", m_ulOfferToRecalc));
#endif

                    // once again, if we are in start start mode we need to 
                    // turn it off. It may be off already, but I just want to 
                    // make sure.

                    if (pASMSourceInfo->m_pSource->m_bFastStart)
                    {
                        DEBUG_OUT(m_pEM, DOL_TRANSPORT, (s,"(%p)ASM %d - Leaving TurboPlay", pASMSourceInfo->m_pSource, __LINE__));
                        pASMSourceInfo->m_pSource->LeaveFastStart(TP_OFF_BY_NETCONGESTION);
                    }

		    Recalc();
		    return;
		}
	    }
	    m_State = CONGESTION;
	    RecalcAccel();
	    return;
	} //m_State != CONGESTION
	else
	{
	    BOOL bLossBehind = FALSE;
	    m_State = HX_NONE;
	    UINT32 ulTotalBandwidth = 0;
	    for (i = m_pASMSourceInfo->Begin(); i != m_pASMSourceInfo->End(); ++i)
	    {
		pASMSourceInfo = (ASMSourceInfo*)(*i);

		IHXThinnableSource* pThin = 0;
		UINT32 ulSourceBandwidth = pASMSourceInfo->m_ulSubscribedBw;

		UINT32 ulNewValue = 
			(UINT32)(
			(float)ulSourceBandwidth /
			(float)ulAggregateUsed *
			(float)lAggregateBandwidthUsage * 0.70);

		if (ulNewValue < ulSourceBandwidth)
		{
		    /* Can't be conservative, so go use what we need */
		    ulNewValue = 
			    (UINT32)(
			    (float)ulSourceBandwidth /
			    (float)ulAggregateUsed *
			    (float)lAggregateBandwidthUsage * 0.97);

                    // once again, if we are in start start mode we need to 
                    // turn it off. It may be off already, but I just want to 
                    // make sure.

                    if (pASMSourceInfo->m_pSource->m_bFastStart)
                    {
                        DEBUG_OUT(m_pEM, DOL_TRANSPORT, (s,"(%p)ASM %d - Leaving TurboPlay", pASMSourceInfo->m_pSource, __LINE__));
                        pASMSourceInfo->m_pSource->LeaveFastStart(TP_OFF_BY_NETCONGESTION);
                    }
		}

		if (pASMSourceInfo->m_bLossBehind &&
			(ulNewValue < ulSourceBandwidth))
		{
		    DEBUG_OUT(m_pEM, DOL_TRANSPORT, (s,
			    "(%p)No Loss Reduce: Will Force BP", pASMSourceInfo->m_pSource));
		    ulNewValue = ulSourceBandwidth;
		}

		if ((HXR_OK == pASMSourceInfo->m_pSource->
		    QueryInterface(IID_IHXThinnableSource, (void **)&pThin)))
		{
		    if (ulNewValue < (pASMSourceInfo->m_ulLastSetDelivery))
		    {
			DEBUG_OUT(m_pEM, DOL_TRANSPORT, (s,
			    "(%p)Congestion: Slow Tranmission Rate to %d %p", pASMSourceInfo->m_pSource,
			    ulNewValue, pASMSourceInfo));

			if (pASMSourceInfo->m_bLossBehind)
			{
			    pASMSourceInfo->m_ulIncomingBandwidth = ulNewValue;
			    bLossBehind = TRUE;
			}

			if (pASMSourceInfo->m_ulRateBeforeDeAccel)
			{
			    pASMSourceInfo->m_ulRateBeforeDeAccel = ulNewValue;
			    /*
			     * Reset the core's acceleration status. 
			     */

			    IHXWatermarkBufferControl* pWMBufCtl = NULL;
			    
			    pASMSourceInfo->m_pSource->
				QueryInterface(IID_IHXWatermarkBufferControl,
					       (void**)&pWMBufCtl);

			    if (pWMBufCtl)
			    {
				pWMBufCtl->ClearChillState();
				pWMBufCtl->Release();
				pWMBufCtl = NULL;
			    }
			    pASMSourceInfo->m_bMayBeAccelerated = TRUE;
			    pASMSourceInfo->m_bPendingChill	= FALSE;
			}

			if ((ulNewValue < ulSourceBandwidth) && (ulSourceBandwidth > 10))
			{
			    pASMSourceInfo->m_pSource->EnterBufferedPlay();
			}

			pASMSourceInfo->m_ulLastSetDelivery = ulNewValue;
		        ulTotalBandwidth += ulNewValue;
			UINT32 ulActualRate = ulNewValue;

			/*
			 * Always keep TCP traffic faster then needed
			 * (but keep it quiet so the rest of the algorithm doesn't
			 * find out :-)
			 */
			if (pASMSourceInfo->m_TransportType == TNG_TCP)
			{
			    ulActualRate = MAX(ulActualRate,
				(UINT32)(ulSourceBandwidth * 1.10));
			}
                        // In low heap mode, do not change the delivery bw.
			// NOTE: There is concern that this is not 
			// satisfactory as a truly long term solution since
			// bw rate control is regarded by some as critical
			// for limited resource platforms.

#if !defined(HELIX_CONFIG_LOW_HEAP_STREAMING)
			pThin->SetDeliveryBandwidth(ulActualRate, 0);
#endif
		    }
		    else
		    {
		        ulTotalBandwidth +=
			    pASMSourceInfo->m_ulLastSetDelivery;
		    }
		}
		HX_RELEASE(pThin);
	    }
	    if (bLossBehind)
	    {
		m_ulNumReportsSinceUpShift = -10;
		m_ulResistanceBitRate = MAX(15000, ulTotalBandwidth);
		DEBUG_OUT(m_pEM, DOL_TRANSPORT, 
		    (s, "Resistance Move %d", m_ulResistanceBitRate));
	    }
	}
    }
    else if (!ulNumSlightlyBehindSources)
    {
	INT32 lAccelTestPoint;
	double dRFactor = 1.05;

	if ((INT32)m_ulUpShiftBandwidthAvail > lAggregateBandwidthUsage)
	{
	    lAccelTestPoint = (INT32) 
		((m_ulUpShiftBandwidthAvail - lAggregateBandwidthUsage)
		* ((float)m_ulUpShiftTestPointScaleFactor / 10000.0)
		    + lAggregateBandwidthUsage);
	}
	else
	{
	    lAccelTestPoint = lAggregateBandwidthUsage;
	}

	UINT32 ulAccelTestPoint =
	    (lAccelTestPoint > 0) ? (UINT32)lAccelTestPoint : 0;
	BOOL bResistanceLimited = FALSE;
	BOOL bWentHigherThanResistanceRate = FALSE;

	if (ulAccelTestPoint > m_ulMaxAccelBitRate)
	{
	    ulAccelTestPoint = m_ulMaxAccelBitRate;
	}
	
	if (ulAccelTestPoint > m_ulResistanceBitRate)
	{
	    UINT32 ulActualResistanceBitRate = m_ulResistanceBitRate;

	    if (ulActualResistanceBitRate < ulAggregateUsed)
	    {
		ulActualResistanceBitRate = (UINT32)(ulAggregateUsed * 1.05);
	    }

	    UINT32 ulOldAccelTestPoint = ulAccelTestPoint;
	    if (ulAccelTestPoint > ulActualResistanceBitRate)
	    {
		UINT32 bHowManyReports =
		    (ulActualResistanceBitRate <
		      ulTotalMaxSubscribedBw) ? 5 : 10;

		if (m_ulUpShiftPastResistanceCount > bHowManyReports)
		{
		    bWentHigherThanResistanceRate = TRUE;

		    if ((m_ulOriginalResistanceBitRate >
		        ulActualResistanceBitRate) &&
			(ulActualResistanceBitRate < ulTotalMaxSubscribedBw))
		    {
			dRFactor = (ulActualResistanceBitRate +
			    ((m_ulOriginalResistanceBitRate -
			    ulActualResistanceBitRate) * 0.10)) /
			    (double)ulActualResistanceBitRate;
			if (dRFactor < 1.01)
			{
			    dRFactor = 1.01;
			}
#ifdef MOREDEBUG
		        DEBUG_OUT(m_pEM, DOL_TRANSPORT, (s,
			    "Resistance Accel Factor %0.2f", dRFactor));
#endif
		    }
		    else
		    {
			dRFactor = 1.01;
		    }
 		    ulAccelTestPoint = (UINT32)(ulActualResistanceBitRate *
		        dRFactor);
		}
		else
		{
		    ulAccelTestPoint = ulActualResistanceBitRate;
		}
		if (ulOldAccelTestPoint < ulAccelTestPoint)
		{
		    ulAccelTestPoint = ulOldAccelTestPoint;
		}
		else
		{
		    bResistanceLimited = TRUE;
		}
	    }
	}

	if ((m_ulNumReportsSinceUpShift >= 2) &&
		((INT32)m_ulHighestBandwidthAvail < lAggregateBandwidthUsage))
	{
	    m_ulHighestBandwidthAvail = lAggregateBandwidthUsage;
	    m_ulPeakUsedBandwidth = lAggregateBandwidthUsage;
	    m_bInitialHighBwAvail = FALSE;
	}

	DEBUG_OUT(m_pEM, DOL_TRANSPORT_EXTENDED,
	    (s, "UP Bw Report: Num=%d, Avail=%d, CurrentBw=%d, TestPoint=%d", 
	    m_ulNumReportsSinceUpShift, m_ulUpShiftBandwidthAvail,
	    lAggregateBandwidthUsage, ulAccelTestPoint));

	BOOL bDidChange = FALSE;
	BOOL bBrokeMax = FALSE;
	if ((m_ulNumReportsSinceUpShift >= 2) &&
	    (INT32) (ulAccelTestPoint) > lAggregateBandwidthUsage)
	{
	    m_ulLastStableBandwidth = lAggregateBandwidthUsage;
	    for (i = m_pASMSourceInfo->Begin(); i != m_pASMSourceInfo->End(); ++i)
	    {
		pASMSourceInfo = (ASMSourceInfo*)(*i);

		IHXThinnableSource* pThin = 0;
		UINT32 ulSourceBandwidth = pASMSourceInfo->m_ulSubscribedBw;

		UINT32 ulNewValue = 
			(UINT32)(
			(float)ulSourceBandwidth /
			(float)ulAggregateUsed *
			(float)ulAccelTestPoint);

		if (ulNewValue > (pASMSourceInfo->m_ulMaxSubscribedBw * 4))
		{
		    ulNewValue = (UINT32) 
			(pASMSourceInfo->m_ulMaxSubscribedBw * 4);
		}

		/*
		 * Live streams get capped at 107% of Max to prevent 
		 * unneeded bandwidht modulation.
		 */
		if ((pASMSourceInfo->m_pSource->IsLive()) &&
		   (ulNewValue >
			(pASMSourceInfo->m_ulMaxSubscribedBw * 1.07)))
		{
		    ulNewValue = (UINT32)
			(pASMSourceInfo->m_ulMaxSubscribedBw * 1.07);
		}

		if ((ulNewValue > (pASMSourceInfo->m_ulMaxSubscribedBw)) &&
		    (ulNewValue > (ulSourceBandwidth * 3)) &&
		    (pASMSourceInfo->m_ulLastSetDelivery <
			pASMSourceInfo->m_ulMaxSubscribedBw))
		{
		    /*
		     * If we are already accelerating 3x subscribed bandwidth
		     * and we are about to upshift beyond the max possible
		     * bandwidth, then let's stop and take a breather just
		     * above the max subscription.  This prevents us from
		     * buffering huge amounts of the crappy low bw stream.
		     */
		    UINT32 ulTemp;
		    ulTemp = (UINT32) 
			(pASMSourceInfo->m_ulMaxSubscribedBw * 1.10);
		    ulNewValue = MIN(ulTemp, ulNewValue);
		}

		if (pASMSourceInfo->m_ulLastSetDelivery >
			pASMSourceInfo->m_ulMaxSubscribedBw)
		{
		    bBrokeMax = TRUE;
		}

		if (pASMSourceInfo->m_bMayBeAccelerated &&
		   (HXR_OK == pASMSourceInfo->m_pSource->
		    QueryInterface(IID_IHXThinnableSource, (void **)&pThin)))
		{
		    double Factor;

		    if (bResistanceLimited)
		    {
			Factor = 1.005;
		    }
		    else if (pASMSourceInfo->m_pSource->IsLive())
		    {
			Factor = 1.01;
		    }
		    else
		    {
			Factor = 1.05;
		    }

		    if ((ulNewValue > (pASMSourceInfo->m_ulLastSetDelivery * Factor)) && 
			pASMSourceInfo->m_bSourceAccelAllowed)
		    {
			DEBUG_OUT(m_pEM, DOL_TRANSPORT, 
			(s, "(%p)Accelerating: NewTransmissionRate=%d", pASMSourceInfo->m_pSource, ulNewValue));

			m_ulUpShiftPastResistanceCount = 0;

			if (ulNewValue > ulSourceBandwidth)
			{
			    pASMSourceInfo->m_pSource->LeaveBufferedPlay();
			}

			bDidChange = TRUE;

			/*
			 * WHOA!  If this is a timestamp delivered source
			 * i.e. 5.0 thinning, then the imperical Getbandwidth()
			 * will never reach the rate that we set, so we help
			 * it along a bit (just a bit of a nudge, eh?).
			 */
			if (pASMSourceInfo->m_bTimeStampDelivery)
			{
			    pASMSourceInfo->m_ulIncomingBandwidth = ulNewValue;
			}
			m_ulNumReportsSinceUpShift = 0;
			pASMSourceInfo->m_bInvalidUpReport = TRUE;
			m_bDidOfferUpShiftToRecalc = FALSE;
			pThin->SetDeliveryBandwidth(
			    (pASMSourceInfo->m_ulLastSetDelivery = ulNewValue), 0);
		    }
		}
		HX_RELEASE(pThin);
	    }
	}
	if ((bDidChange == TRUE) && (bWentHigherThanResistanceRate))
	{
	    m_ulResistanceBitRate = (UINT32)(m_ulResistanceBitRate * dRFactor);
	    DEBUG_OUT(m_pEM, DOL_TRANSPORT, (s,
	        "Went over ResistanceBitRate %d", m_ulResistanceBitRate));
	}
	if (((bDidChange == FALSE) &&
	    (m_ulNumReportsSinceUpShift > NUM_REPORTS_NEEDED_TO_UPSHIFT) ||
	    (bBrokeMax == TRUE)) && (m_bDidOfferUpShiftToRecalc == FALSE))
	{
	    m_State = HX_NONE;
	    m_bDidOfferUpShiftToRecalc = TRUE;
	    m_ulOfferToRecalc = lAggregateBandwidthUsage;

#ifdef MOREDEBUG
	    DEBUG_OUT(m_pEM, DOL_TRANSPORT, (s,
			"Upshift Offer to Recalc() %d", m_ulOfferToRecalc));
#endif


	    Recalc();
	    return;
	}
    }
}

void
ASMStreamInfo::NotifyNewBandwidth()
{
    m_pNegotiator->SetBandwidthUsage(m_ulLastBandwidth,
	m_bTimeStampDelivery);
}

void
ASMStreamInfo::NotifyTimeStampDelivery()
{
    if(m_bTimeStampDelivery)
    {
	m_pASMSourceInfo->m_bTimeStampDelivery = TRUE;
    }
}

UINT32
ASMSourceInfo::GetBandwidth()
{
    return m_ulIncomingBandwidth;
}


void
ASMStreamInfo::SetLastBandwidth(UINT32 ulOffer)
{
    m_ulLastBandwidth = ulOffer;
}

void
ASMStreamInfo::NotifyLimitBandwidth(UINT32 ulRecv)
{
    if(m_pASMSourceInfo->m_bBehind)
    {
	m_pNegotiator->HandleSlowSource(ulRecv);
    }
}

ASMSourceInfo::ASMSourceInfo(HXSource* pSource, HXSM* pHXASM)
    : m_ulLastReportTime(0)
    , m_ulIncomingBandwidth(0)
    , m_ulRateBeforeDeAccel(0)
    , m_lTimeDiffBase(0)
    , m_ulBytesBehind(0)
    , m_lLastBehindTime(0)
    , m_ulLastSetDelivery(0xffffffff)
    , m_ulSubscribedBw(0)
    , m_ulMaxSubscribedBw(0)
    , m_bBehind(FALSE)
    , m_bLossBehind(FALSE)
    , m_bSlightlyBehind(FALSE)
    , m_bTimeStampDelivery(FALSE)
    , m_bPendingChill(FALSE)
    , m_bInvalidUpReport(FALSE)
    , m_bPerfectPlay(FALSE)
    , m_bIsDone(FALSE)
    , m_bMayBeAccelerated(TRUE)
    , m_bTryToUpShift(FALSE)
    , m_bAdjustBandwidth(FALSE)
    , m_ulLowestBandwidthBeforeTimeStamp(0)
    , m_bSourceAccelAllowed(TRUE)
    , m_bSourceDecelAllowed (TRUE)
    , m_bSlidingBwWindowReady(FALSE)
    , m_pMasterRuleBook(0)
    , m_pStreams(0)
    , m_TransportType(TNG_UDP)
    , m_pSource(pSource)
    , m_pSBI(0)
    , THRESHOLD(1000)
    , m_lRefCount(0)
    , m_ulBwDetectionDataCount(0)
    , m_ulBwDetectionDataLen(0)
    , m_ulSlidingWindowLocation(0)
    , m_pBwDetectionData(NULL)
    , m_pHXASM(pHXASM)
{
    m_lOuterThreshold = THRESHOLD;
    IHXValues* pHeader = 0;

    pSource->AddRef();

    if (pSource->m_pFileHeader)
    {
	IHXBuffer* pMasterRuleBook = NULL;
	pSource->m_pFileHeader->
	    GetPropertyCString("ASMRuleBook", pMasterRuleBook);

	if (pMasterRuleBook)
	{
	    m_pMasterRuleBook = new ASMRuleBook
		((const char *)pMasterRuleBook->GetBuffer());
	}

	HX_RELEASE(pMasterRuleBook);
    }
}

ASMSourceInfo::~ASMSourceInfo()
{
    HX_VECTOR_DELETE(m_pBwDetectionData);
    delete[] m_pStreams;
    delete m_pMasterRuleBook;
}


BOOL
ASMSourceInfo::AllocBWDetectionData(UINT32 ulReqSize)
{
    BOOL bOk = TRUE;

    // Our current array is too small
    if (ulReqSize > m_ulBwDetectionDataLen)
    {
	BwDetectionData* pTemp = new BwDetectionData[ulReqSize];
	if (!pTemp)
	{
	    bOk = FALSE;
	}
	else
	{
	    if (m_pBwDetectionData)
	    {
		memcpy(pTemp, m_pBwDetectionData, m_ulBwDetectionDataLen * sizeof(BwDetectionData)); /* Flawfinder: ignore */
		HX_VECTOR_DELETE(m_pBwDetectionData);
	    }
	    m_pBwDetectionData = pTemp;
	    m_ulBwDetectionDataLen = ulReqSize;
	}
    }

    return bOk;
}


void
ASMSourceInfo::Done()
{
    HX_RELEASE(m_pSBI);
    HX_RELEASE(m_pSource);
    m_bIsDone	= TRUE;
}

STDMETHODIMP_(UINT32)
ASMSourceInfo::AddRef(void)
{
    return InterlockedIncrement(&m_lRefCount);
}

STDMETHODIMP_(UINT32)
ASMSourceInfo::Release(void)
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
	return m_lRefCount;
    }

    delete this;
    return 0;
}

STDMETHODIMP
ASMSourceInfo::QueryInterface
(
    REFIID interfaceID,
    void** ppInterfaceObj
)
{
    QInterfaceList qiList[] =
        {
            { GET_IIDHANDLE(IID_IUnknown), (IUnknown*)this },
            { GET_IIDHANDLE(IID_IHXBandwidthManagerInput), (IHXBandwidthManagerInput*)this },
        };
    
    return ::QIFind(qiList, QILISTSIZE(qiList), interfaceID, ppInterfaceObj);
}


STDMETHODIMP
ASMSourceInfo::ReportDataPacket
(
    UINT32 ulTimeStamp,
    UINT32 ulArrivedTimeStamp,
    UINT32 ulSize
)
{
    if (m_bIsDone)
    {
	return HXR_OK;
    }

    UINT32 ulCurrentTime = HX_GET_TICKCOUNT();
    if (!m_pHXASM->m_ulNextPacketWindow)
    {
	m_pHXASM->m_ulNextPacketWindow = ulCurrentTime;
    }

    if (ulSize == REPORT_DATA_PACKET_LOST)
    {
        ULONG32 ulNumLost = ulArrivedTimeStamp-ulTimeStamp+1;
	//XXXgfw Don't allow Decel if this is a load test.....
	if (m_bSourceDecelAllowed)
	{
            m_pHXASM->m_lLoss += ulNumLost;    
	}
        m_pHXASM->m_lPacketCounter += ulNumLost;
	return HXR_OK;
    }

    m_pHXASM->m_lPacketCounter++;

    // Make sure we have a buffer for bandwidth detection!
    if (!m_pBwDetectionData)
    {
	//XXXNH: SMP pulled these numbers out of his hat, so let's
	// stick with them for now.
	UINT32 uEstSize = m_TransportType == TNG_TCP ? 300 : 20;
	
	if (!AllocBWDetectionData(uEstSize))
	    return HXR_OUTOFMEMORY;
    }

    if (CALCULATE_ELAPSED_TICKS(m_pHXASM->m_ulNextPacketWindow,
    	ulCurrentTime) > 3000)
    {
	m_pHXASM->m_ulNextPacketWindow = ulCurrentTime;

	UINT32 ulLossPercentage = (UINT32)(
	    100 * (float)m_pHXASM->m_lLoss / (float)m_pHXASM->m_lPacketCounter);

#ifdef MOREDEBUG
	DEBUG_OUT(m_pHXASM->m_pEM, DOL_TRANSPORT, (s, "(%p)Loss %d", m_pSource, ulLossPercentage));
#endif

	/* Don't do anything unless we have 4% Loss */
	BOOL bDeAccel = (ulLossPercentage >= 4);

	/* Cut down bandwidth usage ONLY if it won't cause a downshift */
	bDeAccel = bDeAccel &&
		(m_ulLastSetDelivery * 0.95 > m_ulSubscribedBw);

	/* Don't throttle back if we're in chill acc buffering */
	bDeAccel = bDeAccel && (m_ulRateBeforeDeAccel == 0);

	/* If Loss is over 20%, always throttle back */
	bDeAccel = bDeAccel || (ulLossPercentage >= 20);

	if (bDeAccel)
	{
	    if (ulLossPercentage >= 20)
	    {
		/* 
		 * make sure to never go into timestamp delivery/keyframe mode
		 * due to loss
		 */
	        m_ulIncomingBandwidth = (UINT32) MAX(m_ulLastSetDelivery * 0.85, 
				m_ulLowestBandwidthBeforeTimeStamp * 1.05);
	    }
	    else
	    {
		if (ulLossPercentage > 15)
		{
		    ulLossPercentage = 15;
		}
	        
		// do not change subscription due to "less than 20% loss"
		m_ulIncomingBandwidth = (UINT32) MAX(m_ulLastSetDelivery *
		    (1 - (ulLossPercentage / (float)100)),
		    m_ulSubscribedBw * 1.05);
	    }

            if (m_pSource->m_bFastStart)
            {
                DEBUG_OUT(m_pHXASM->m_pEM, DOL_TRANSPORT, (s,"(%p)ASM %d - Leaving TurboPlay", m_pSource, __LINE__));
                m_pSource->LeaveFastStart(TP_OFF_BY_NETCONGESTION);
                if (m_ulIncomingBandwidth > m_ulMaxSubscribedBw * 1.05)
                {
                    m_ulIncomingBandwidth = m_ulMaxSubscribedBw * 1.05;
                }
            }

	    m_bLossBehind = TRUE;
	    DEBUG_OUT(m_pHXASM->m_pEM, DOL_TRANSPORT, (s, "(%p)Lower Loss %d", m_pSource, m_ulIncomingBandwidth));
	    /* Signal Recalc to adjust the highest available bandwidth */
	    m_pHXASM->m_bPipeFull = TRUE;
	    m_bBehind = TRUE;

	    m_pHXASM->RecalcAccel();
	    m_bBehind = FALSE;
	    m_bLossBehind = FALSE;
	}
	else if (ulLossPercentage <= 1)
	{
	    m_pHXASM->m_ulUpShiftPastResistanceCount++;
	}

	if (ulLossPercentage > 2)
	{
	    m_pHXASM->m_ulUpShiftPastResistanceCount = 0;
	}

	m_pHXASM->m_lLoss = 0;
	m_pHXASM->m_lPacketCounter = 0;
    }

    if (m_bSlidingBwWindowReady)
    {
	//INT32 i;
	UINT32 ulPrevLocation = m_ulSlidingWindowLocation;

	/*
	 *  Set the data for the current packet
	 */
	m_pBwDetectionData[m_ulSlidingWindowLocation].
	    m_ulSize	    = ulSize;
	m_pBwDetectionData[m_ulSlidingWindowLocation].
	    m_ulTimeStamp	    = ulTimeStamp;
	m_pBwDetectionData[m_ulSlidingWindowLocation].
	    m_ulATime	    = ulArrivedTimeStamp;

	m_ulSlidingWindowLocation++;

	/*
	 * wrap around if at the end
	 */
	if (m_ulSlidingWindowLocation == m_ulBwDetectionDataCount)
	{
	    m_ulSlidingWindowLocation = 0;
	}

	/*
	 * count up all data for the whole window
	 */
	//UINT32 ulWindowSize = 0;
	//for (i = 0; i < m_ulBwDetectionDataCount; i++)
	//{
	//    ulWindowSize += m_pBwDetectionData[i].m_ulSize;
	//}

	/*
	 *  m_ulSlidingWindowLocation now points to the very first
	 *  info in the window.  Take the diff in time from the data
	 *  that we just entered (ulPrevLocation) and the time of the
	 *  first data.
	 */
	//UINT32 ulWindowRealTime = m_pBwDetectionData[ulPrevLocation].m_ulATime
	//    - m_pBwDetectionData[m_ulSlidingWindowLocation].m_ulATime;

	/*
	 *  Calculate bandwidth for the window in bytes * 8 / millis.
	 *  I have no idea why it is bytes * 8. //XXXPM?
	 *  If we are in constant bitrate mode, then this calculated
	 *  bitrate will be compared to the constant bitrate.  If we
	 *  are not in constant bitrate mode, then this calculated
	 *  bitrate will be compared to the bitrate coming from the
	 *  bandwidth reports.
	 */
	//m_ulLastBandwidthReport = (UINT32) (ulWindowSize * 8 /
	//    ((ulWindowRealTime) / 1000.0));

	// This is the short-term bandwidth count (sans buffering fudge)
    }
    else
    {
	m_pBwDetectionData[m_ulBwDetectionDataCount].
	    m_ulSize	    = ulSize;
	m_pBwDetectionData[m_ulBwDetectionDataCount].
	    m_ulTimeStamp	    = ulTimeStamp;
	m_pBwDetectionData[m_ulBwDetectionDataCount].
	    m_ulATime	    = ulArrivedTimeStamp;

	if(m_TransportType == TNG_TCP)
	{
	    if(((m_pBwDetectionData[m_ulBwDetectionDataCount].m_ulTimeStamp -
		m_pBwDetectionData[0].m_ulTimeStamp > 30000) &&
		m_ulBwDetectionDataCount > 300))
	    {
		m_bSlidingBwWindowReady = TRUE;
	    }
	}
	else if(m_TransportType == TNG_UDP)
	{
	    if (((((m_pBwDetectionData[m_ulBwDetectionDataCount].m_ulTimeStamp -
	        m_pBwDetectionData[0].m_ulTimeStamp) > 3000) &&
	        (m_ulBwDetectionDataCount > 20)) ||
	       (m_pBwDetectionData[m_ulBwDetectionDataCount].m_ulATime -
	        m_pBwDetectionData[0].m_ulATime > 3000)))
	    // XXXSMP Re-examine this formula later.
	    {
	        m_bSlidingBwWindowReady = TRUE;
	    }
	}
	if(!m_bSlidingBwWindowReady)	
	{
	    m_ulBwDetectionDataCount++;

	    // do we not have a big enough buffer for calculating bandwidth?
	    if (m_ulBwDetectionDataCount >= m_ulBwDetectionDataLen)
	    {
		// if we already have at least 1024 data points or we are 
		// out of memory then we'll just have to make do
		UINT32 ulNewSize = m_ulBwDetectionDataLen * 2;
		UINT32 ulMaxSize = min(BW_DETECTION_DATA_POINTS, ulNewSize);
		if (m_ulBwDetectionDataLen >= BW_DETECTION_DATA_POINTS ||
		    !AllocBWDetectionData(ulMaxSize))
		{
		    m_ulBwDetectionDataCount--;
		    m_bSlidingBwWindowReady = TRUE;
		}
	    }
	}
    }

    return HXR_OK;
}

UINT32
ASMSourceInfo::GetBandwidthSince(UINT32 ulTime,
				     UINT32 ulNow)
{
    if (m_bIsDone)
    {
	return HXR_OK;
    }

    /*
     *  If we have not seen enough yet to get an idea.
     */
    if(!m_bSlidingBwWindowReady)
    {
	//XXXPM Hack this in to make TCP do small window calcs
	if(m_pBwDetectionData && m_TransportType == TNG_TCP &&
	(m_pBwDetectionData[m_ulBwDetectionDataCount].m_ulTimeStamp -
	m_pBwDetectionData[0].m_ulTimeStamp > 1000))
	{
	    /* XXXPM, Disgusting */
	    goto tcphackedin;
	}
	return 0;
    }
tcphackedin:

    UINT32 i;
    UINT32 sane = m_ulSlidingWindowLocation;
    UINT32 ulCount = 0;

    if(m_ulSlidingWindowLocation == 0)
    {
	i = m_ulBwDetectionDataCount - 1;
    }
    else
    {
	i = m_ulSlidingWindowLocation - 1;
    }

    UINT32 ulLastTime = ulNow;
    /*
     * add up the data size for all packets since ulTime
     */
    while(m_pBwDetectionData[i].m_ulATime > ulTime)
    {	
	ulCount += m_pBwDetectionData[i].m_ulSize;
	/*
	 * remember the time of this packet so that we can calculate duration
	 */
	ulLastTime = m_pBwDetectionData[i].m_ulATime; 
	if(i == 0)
	{
	    i = m_ulBwDetectionDataCount - 1;
	}
	else
	{
	    i--;
	}
	if(i == sane)
	{
	    //XXXPM we have travelled all the way around the window
	    //and still don't have enough.
	    break;
	}
    }
    if (ulNow == ulLastTime)
    {
	return 0;
    }
    return (UINT32)((ulCount * 8) / ((ulNow - ulLastTime) / 1000.0));
}


STDMETHODIMP
ASMSourceInfo::ReportUpshiftInfo(UINT32 ulTimeStamp,
				     UINT32 ulSize)
{
    // In low heap mode, skip this because it causes the server to alter the
    // transmission rate. NOTE: technically this is the similar change in
    // ReportLatency should disable surestream stream switching, but in
    // testing this is not born out.
#if defined(HELIX_CONFIG_LOW_HEAP_STREAMING)
    return HXR_OK;
#endif
    if (m_bIsDone)
    {
	return HXR_OK;
    }

    if (m_TransportType == TNG_TCP)
	return HXR_OK;

    m_pHXASM->UpShiftInfo(ulTimeStamp, ulSize);

    return HXR_OK;
}

void
HXSM::UpShiftInfo(UINT32 ulTimeStamp, UINT32 ulSize)
{
    UINT32 ulT = 1;
    UINT32 ulS = 0;

    m_ulUpShiftTimes[m_ulUpShiftRingPos] = ulTimeStamp;
    m_ulUpShiftSizes[m_ulUpShiftRingPos] = ulSize;
    m_ulUpShiftRingPos++;

    if (m_ulUpShiftRingPos == 5)
    {
	m_ulUpShiftRingPos = 0;
	m_bUpShiftInfoReady = TRUE;
    }

    if (m_bUpShiftInfoReady)
    {
	for (int i = 0; i < 5; i++)
	{
	    /*
	     * XXXSMP We should:
	     * Weight each BackToBack timing by the size of the packet
	     * it timed, because timing larger packets is more accurate.
	     */
	    ulT += m_ulUpShiftTimes[i];
	    ulS += m_ulUpShiftSizes[i];
	}
    }

    if (ulT < 50)
    {
	ulT = (UINT32) (ulT * 1.2); // The clock is not very accurate.

	if (ulT > 50)
	    ulT = 50;
    }

    if (m_bUpShiftInfoReady)
    {
	/* Use 5ms padding for good luck */
	m_ulUpShiftBandwidthAvail = ulS * 8000 / (ulT + 5);
    }
    else
    {
	/* Use 40-60ms padding if we are counting on one info item only */
	if (ulSize > 500)
	    m_ulUpShiftBandwidthAvail = ulSize * 8000 / (ulTimeStamp + 40);
	else
	    m_ulUpShiftBandwidthAvail = ulSize * 8000 / (ulTimeStamp + 60);
    }

    RecalcAccel();
}


HX_RESULT
ASMSourceInfo::ReportLatency(UINT32 ulServerTime,
			     UINT32 ulClientTime)
{
    // In low heap mode, skip this because it causes the server to alter the
    // transmission rate. NOTE: technically this is the similar change in
    // ReportUpShiftInfo should disable surestream stream switching, but in
    // testing this is not born out.
#if defined(HELIX_CONFIG_LOW_HEAP_STREAMING)
    return HXR_OK;
#endif
    if (m_bIsDone)
    {
	return HXR_OK;
    }

    INT32 lBackedUp;

    lBackedUp = CalcBackup(ulServerTime, ulClientTime);
    INT32 lDetectedBandwidth;

    if(m_TransportType == TNG_UDP)
    {
	UINT32 ulTemp = MAX(800, 4000 - m_lOuterThreshold);
	UINT32 ulStartWindow = ulClientTime - ulTemp;

	lDetectedBandwidth = GetBandwidthSince(ulStartWindow, ulClientTime);
    }
    else
    {
	lDetectedBandwidth = GetBandwidthSince(ulClientTime -
				TCP_BANDWIDTH_WINDOW, ulClientTime);
    }

    /*
     *  The THRESHOLD window is from THRESHOLD to m_lOuterThreshold.
     *  Once we go over threshold, we recalc the bandwidth and will do
     *  something to slow things down in m_pHXASM->Recalc.  We don't
     *  want to recalc the bandwidth again until we catch up to THRESHOLD
     *  or get slower again.  This is so that when we send one LimitBandwidth
     *  message we don't send another just when we are catching up (because
     *  sometimes during this time we can receive less and less data.)
     */

    //XXXgfw Don't allow Decel if this is a load test.....
    if ((lBackedUp >= (INT32)THRESHOLD) && (m_bSourceDecelAllowed))
    {
	/*
	 *  If we are backed up over our threshold window, then resize the
	 *  window and recalc our bandwidth.
	 */
	if ((lBackedUp > m_lOuterThreshold) &&
	    ((lDetectedBandwidth < (INT32)m_ulIncomingBandwidth) || 
		(!m_ulIncomingBandwidth)))
	{
	    m_ulIncomingBandwidth = lDetectedBandwidth;
	    /* Signal Recalc to adjust the highest available bandwidth */
	    m_pHXASM->m_bPipeFull = TRUE;
	    m_lOuterThreshold = lBackedUp + 500;
	}
	/*
	 * This can either grow or shrink the threshold window.
	 */
	m_bBehind = TRUE;
	m_bSlightlyBehind = TRUE;
    }
    else 
    {
	m_lOuterThreshold = THRESHOLD;
	m_bBehind = FALSE;

	UINT ulNew = 0;

	//XXXgfw Don't allow Decel if this is a load test.....
	if ((lBackedUp > 400) && (m_bSourceDecelAllowed))
	    m_bSlightlyBehind = TRUE;
	else
	{
	    m_bSlightlyBehind = FALSE;
	    if (m_TransportType == TNG_TCP)
	    {
		DEBUG_OUT(m_pHXASM->m_pEM, DOL_TRANSPORT,
		    (s, "(%p)TCP Shift up = %d", m_pSource,
		    m_pHXASM->m_ulUpShiftBandwidthAvail));
		m_pHXASM->m_ulUpShiftBandwidthAvail = MAX(
		    m_pHXASM->m_ulUpShiftBandwidthAvail,
		    (UINT32)(lDetectedBandwidth * 1.5));
	    }
	}

	if (m_bInvalidUpReport)
	{
	    m_bInvalidUpReport = FALSE;
	    m_ulLastReportTime = ulClientTime;
	    return HXR_OK;
	}

	UINT32 ulTemp = MAX(m_ulRateBeforeDeAccel, m_ulLastSetDelivery);
	if ((lDetectedBandwidth > (INT32)ulTemp) ||
	    (!m_ulIncomingBandwidth))
	    ulNew = ulTemp + 1;
	else
	    ulNew = lDetectedBandwidth;

	if (ulNew > m_ulIncomingBandwidth)
	{
	    m_ulIncomingBandwidth = ulNew;
	}
    }
    m_ulLastReportTime = ulClientTime;
    m_pHXASM->m_ulNumReportsSinceUpShift++;

    if (m_bPendingChill)
    {
	m_bPendingChill = FALSE;
	m_pHXASM->m_State = HXSM::CHILL_BUFFERING;
	m_pHXASM->RecalcAccel();

	IHXThinnableSource* pThin = NULL;

	if ((HXR_OK == m_pSource->
	    QueryInterface(IID_IHXThinnableSource, (void **)&pThin)))
	{
	    HX_ASSERT(m_ulSubscribedBw > 0);

	    m_ulRateBeforeDeAccel = m_ulLastSetDelivery;

	    UINT32 ulSet;

	    if (m_ulIncomingBandwidth > m_ulMaxSubscribedBw)
	    {
		ulSet = m_ulMaxSubscribedBw;
	    }
	    else
	    {
		ulSet = (UINT32)(m_ulSubscribedBw * 0.75);
	    }

	    DEBUG_OUT(m_pHXASM->m_pEM, DOL_TRANSPORT, 
		(s, "(%p)Acceleration Buffer Full: NewTransmissionRate=%d %p", m_pSource,
		    ulSet, this));

	    pThin->SetDeliveryBandwidth((m_ulLastSetDelivery = ulSet), 0);
	}
	HX_RELEASE(pThin);
    }
    else
	m_pHXASM->RecalcAccel();

    return HXR_OK;
}

INT32
ASMSourceInfo::CalcBackup(UINT32 ulServerTime, UINT32 ulClientTime)
{

    INT32 lNewDiff = ulClientTime - ulServerTime;

    /* 
     *  If this is the first time we will have no idea of the base
     *  time diff.
     */
    if(!m_lTimeDiffBase)
    {		
	m_lTimeDiffBase = lNewDiff;
	return 0;
    }

    /*
     * If our difference got shorter than before then this is the new base
     * and our backup is 0
     */
    if(lNewDiff < m_lTimeDiffBase)
    {
	m_lTimeDiffBase = lNewDiff;
	return 0;
    }

    if (m_lLastBehindTime)
    {
	if (((INT32)m_ulBytesBehind +
	    ((lNewDiff - m_lTimeDiffBase) - m_lLastBehindTime) * 
	    (INT32)m_ulLastSetDelivery / 8000) > 0)
	{
	    m_ulBytesBehind +=
	    ((lNewDiff - m_lTimeDiffBase) - m_lLastBehindTime) * 
	    (INT32)m_ulLastSetDelivery / 8000;
	}
	else
	{
	    m_ulBytesBehind = 0;
	}
    }
    m_lLastBehindTime = lNewDiff - m_lTimeDiffBase;

    DEBUG_OUT(m_pHXASM->m_pEM, DOL_TRANSPORT_EXTENDED, 
	    (s, "(%p)Terminal Buffer Report: Behind by %dms (%d bytes)", m_pSource,
	    lNewDiff - m_lTimeDiffBase, m_ulBytesBehind));

    return lNewDiff - m_lTimeDiffBase;
}

STDMETHODIMP
ASMSourceInfo::SetCongestionFactor(UINT32 ulFactor)
{
    if (m_bIsDone)
    {
	return HXR_OK;
    }

    return HXR_OK;
}


void    
ASMSourceInfo::ChangeAccelerationStatus(BOOL	   bMayBeAccelerated,
					BOOL	   bUseAccelerationFactor,
					UINT32	   ulAccelerationFactor)
{
    if (m_bMayBeAccelerated == FALSE && bMayBeAccelerated == FALSE)
    {
	DEBUG_OUT(m_pHXASM->m_pEM, DOL_TRANSPORT, 
		(s, "(%p)Acceleration Buffer Way Full: Factor=%d", m_pSource, ulAccelerationFactor));
	HX_ASSERT(bUseAccelerationFactor);

	UINT32 ulNewRate = m_ulSubscribedBw * ulAccelerationFactor / 100;

	IHXThinnableSource* pThin = NULL;

	if (HXR_OK == m_pSource->
	    QueryInterface(IID_IHXThinnableSource, (void **)&pThin))
	{
	    pThin->SetDeliveryBandwidth((m_ulLastSetDelivery = ulNewRate), 0);
	}
	HX_RELEASE(pThin);

	return;
    }

    m_bMayBeAccelerated = bMayBeAccelerated;

    if (!m_bMayBeAccelerated && (m_ulLastSetDelivery > m_ulSubscribedBw))
    {
	m_bPendingChill = TRUE;
    }
    else if (m_bMayBeAccelerated && m_ulRateBeforeDeAccel)
    {
	IHXThinnableSource* pThin = NULL;

	if (HXR_OK == m_pSource->
	    QueryInterface(IID_IHXThinnableSource, (void **)&pThin))
	{
	    /* Only attempt conservative restart on LBR sources */
	    if (m_ulRateBeforeDeAccel < 150000)
	    {
		/* Can we be conservative when switching back up? */
		if ((m_ulRateBeforeDeAccel * 0.50) > m_ulSubscribedBw)
		{
		    /* Yes, be really conservative */
		    m_ulRateBeforeDeAccel = (UINT32)
		        (m_ulRateBeforeDeAccel * 0.70);
		}
		else if ((m_ulRateBeforeDeAccel * 0.70) > m_ulSubscribedBw)
		{
		    /* Be somewhat conservative */
		    m_ulRateBeforeDeAccel = (UINT32)
		        (m_ulRateBeforeDeAccel * 0.85);
		}
	    }

	    DEBUG_OUT(m_pHXASM->m_pEM, DOL_TRANSPORT, 
		(s, "(%p)Acceleration Buffer at 50%: NewTransmissionRate=%d", m_pSource, m_ulRateBeforeDeAccel));

	    pThin->SetDeliveryBandwidth(
		(m_ulLastSetDelivery = (UINT32) (m_ulRateBeforeDeAccel)), 0);
	    m_ulRateBeforeDeAccel = 0;
	}
	HX_RELEASE(pThin);
    }
}

STDMETHODIMP
ASMSourceInfo::SetTransportType(TRANSPORT_TYPE type)
{
    if (m_bIsDone)
    {
	return HXR_OK;
    }

    m_TransportType = type;

    if (m_TransportType == TNG_TCP)
    {
	THRESHOLD = 5000;
	m_lOuterThreshold = 5000;
    }

    return HXR_OK;
}




ASMStreamInfo::ASMStreamInfo() :
    m_pNegotiator(0),
    m_pBias(0),
    m_pRuleGather(0),
    m_ulFixedBandwidth(0),
    m_ulLastBandwidth(0),
    m_bTimeStampDelivery(FALSE),
    m_ulNumThresholds(0),
    m_ulThresholdPosition(0),
    m_ulResistanceToLower(0),
    m_ulOffer(0),
    m_pThreshold(NULL),
    m_ulMaxEffectiveThreshold(-1),
    m_ulStreamNumber(0)
{
}

ASMStreamInfo::~ASMStreamInfo()
{
    HX_VECTOR_DELETE(m_pThreshold);
    HX_RELEASE(m_pNegotiator);
    HX_RELEASE(m_pBias);
    HX_RELEASE(m_pRuleGather);
}
