/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: hxbufstate.cpp,v 1.5.4.4 2004/07/09 02:05:57 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 ***** */
#include "hxbufstate.h"

#include "hxassert.h"

class HXBufferedPktInfo
{
public:
    HXBufferedPktInfo(INT64 llTimestamp, UINT32 ulSize);

    INT64 Timestamp() const { return m_llTimestamp;}
    UINT32 Size() const { return m_ulSize;}

private:
    INT64 m_llTimestamp;
    UINT32 m_ulSize;
};

inline
HXBufferedPktInfo::HXBufferedPktInfo(INT64 llTimestamp, 
				     UINT32 ulSize) :
    m_llTimestamp(llTimestamp),
    m_ulSize(ulSize)
{}


HXBufferingState::HXBufferingState() :
    m_ulPreroll(0)
    , m_ulPredata(0)
    , m_ulMinimumPrerollInMs(0)
    , m_ulMinimumPreroll(0)
    , m_ulMinimumBufferingInMs(0)
    , m_ulMinimumBuffering(0)
    , m_ulRemainingToBufferInMs(0)
    , m_ulRemainingToBuffer(0)
    , m_ulCurrentBufferingInMs(0)
    , m_ulCurrentBuffering(0)
    , m_bIsFirstPacket(TRUE)
    , m_preDataAtStart(FALSE)
    , m_prerollAtStart(FALSE)
    , m_preDataAfterSeek(FALSE)
    , m_prerollAfterSeek(FALSE)
    , m_llLowestTimeStamp(0)
    , m_llHighestTimeStamp(0)
    , m_llLowestTimestampAtTransport(0)
    , m_llHighestTimestampAtTransport(0)
    , m_ulNumBytesAtTransport(0)
    , m_bDoneAtTransport(FALSE)
    , m_ulTSRollOver(0)
    , m_ulLastPacketTimeStamp(0)
    , m_ulAvgBandwidth(0)
    , m_pASMProps(NULL)
    , m_bCurrentTimeSet(FALSE)
    , m_llCurrentPlaybackTime(0)
    , m_ulBufferedData(0)
    , m_ulLastTimeSync(0)
    , m_llFirstLivePacketTimestamp(0)
    , m_ulTimeSyncRollOver(0)
{}

HXBufferingState::~HXBufferingState()
{
    HX_RELEASE(m_pASMProps);

    ClearPktInfo();
}

void HXBufferingState::OnStreamHeader(UINT32 ulPreroll,
				      UINT32 ulPredata,
				      BOOL preDataAtStart,
				      BOOL preDataAfterSeek,
				      BOOL prerollAtStart,
				      BOOL prerollAfterSeek,
				      ULONG32 ulAvgBitRate)
{
    m_ulPreroll = ulPreroll;
    m_ulPredata = ulPredata;
    m_preDataAtStart = preDataAtStart;
    m_preDataAfterSeek = preDataAfterSeek;
    m_prerollAtStart = prerollAtStart;
    m_prerollAfterSeek = prerollAfterSeek;
    m_ulAvgBandwidth = ulAvgBitRate;
}

void HXBufferingState::OnStream(IUnknown* pStream)
{
    HX_RELEASE(m_pASMProps);

    if (pStream)
    {
	pStream->QueryInterface(IID_IHXASMProps, (void**) &m_pASMProps);
    }
}

void HXBufferingState::Init(ULONG32 ulPerfectPlayTime)
{
    SetMinPrerollInMs(m_ulPreroll, m_ulPreroll + ulPerfectPlayTime);
    SetMinPreroll();
}


void HXBufferingState::SetMinimumPreroll(UINT32 ulSourcePreroll, 
					 UINT32 ulInitialAudioPreroll,
					 UINT32 ulPerfectPlayTime,
					 BOOL   bIsRebuffering)
{
    UINT32 ulMinimumPreroll = m_ulPreroll;
    
    ulMinimumPreroll += ulInitialAudioPreroll;
    
    if (ulMinimumPreroll < ulSourcePreroll)
    {
	ulMinimumPreroll = ulSourcePreroll;
    }
    
    SetMinPrerollInMs(ulMinimumPreroll, 
		      ulMinimumPreroll + ulPerfectPlayTime);
    m_ulCurrentBufferingInMs = 0;

    /* If we have received at lest one packet for this stream,
     * mark the lowest timestamp to be the
     * last packet timstamp to reset buffering calculations
     */
    if (bIsRebuffering && !m_bIsFirstPacket)
    {
	m_bIsFirstPacket = TRUE;
	m_llLowestTimeStamp = 
	    CAST_TO_INT64 (m_ulTSRollOver) * CAST_TO_INT64 MAX_UINT32 + CAST_TO_INT64 (m_ulLastPacketTimeStamp);
    }
    
    UpdateMinPredata();
}

void HXBufferingState::Stop()
{
    m_ulRemainingToBufferInMs = 0;
    m_ulRemainingToBuffer = 0;
}

void HXBufferingState::Reset(BOOL bIsSeeking, UINT32 ulSeekTime)
{
    m_ulRemainingToBufferInMs = m_ulMinimumBufferingInMs;
    m_ulRemainingToBuffer = m_ulMinimumBuffering;
    m_ulCurrentBufferingInMs = 0;
    m_ulCurrentBuffering = 0;
    m_ulTSRollOver = 0;
    m_ulLastPacketTimeStamp = 0;
    m_bIsFirstPacket = TRUE;

    ClearPktInfo();
    m_bCurrentTimeSet = FALSE;

    if (bIsSeeking)
    {
	m_llLowestTimeStamp = CAST_TO_INT64 ulSeekTime;
	m_llHighestTimeStamp = CAST_TO_INT64 ulSeekTime;
    }
}

void HXBufferingState::GetRemainToBuffer(REF(UINT32) ulRemainToBufferInMs,
					 REF(UINT32) ulRemainToBuffer)
{
    ulRemainToBufferInMs = m_ulRemainingToBufferInMs;
    ulRemainToBuffer = m_ulRemainingToBuffer;
}

UINT16 HXBufferingState::GetPercentDone(BOOL bIsSeekPerformed)
{
    UINT16  uTotalPercentDone = 100;
    UINT16  uPreDataPercentDone = 100;
    UINT16  uPrerollPercentDone = 100;

    BOOL bHasPreroll;
    BOOL bHasPredata;
    BOOL bNeedsData = FALSE;

    if (!bIsSeekPerformed)
    {
	// start/rebuffer mode
	bHasPreroll = m_prerollAtStart;
	bHasPredata = m_preDataAtStart;
    }
    else
    {
	// seek mode
	bHasPreroll = m_prerollAfterSeek;
	bHasPredata = m_preDataAfterSeek;
    }


    // satisfy the preroll (by default + pre-set)
    if (!bHasPredata || bHasPreroll)
    {
	// percent done on preroll
	if (m_ulMinimumBufferingInMs)
	{
	    uPrerollPercentDone =
		((m_ulMinimumBufferingInMs-m_ulRemainingToBufferInMs ) * 100) /
				m_ulMinimumBufferingInMs;
	}
	
	uTotalPercentDone = uPrerollPercentDone;

	bNeedsData = (m_ulRemainingToBufferInMs > 0);
    }

    // satisfy the predata
    if (bHasPredata)
    {
	// percent done on predata
	if (m_ulMinimumBuffering)
	{
	    uPreDataPercentDone = 
		((m_ulMinimumBuffering - m_ulRemainingToBuffer ) * 100) /
				m_ulMinimumBuffering;
	}
	
	uTotalPercentDone = uPreDataPercentDone;
	bNeedsData = (m_ulRemainingToBuffer > 0);
    }

    if (bHasPredata && bHasPreroll)
    {
	uTotalPercentDone = (uPrerollPercentDone + uPreDataPercentDone) / 2;
    }
    else if ((bIsSeekPerformed) &&
	     (uTotalPercentDone == 100) &&
	     (bNeedsData))
    {
	uTotalPercentDone = 99;
    }

    return uTotalPercentDone;
}

void HXBufferingState::UpdatePreroll(ULONG32 ulPreroll)
{
    UINT32 ulExtraPrerollInMs   = m_ulMinimumPrerollInMs - m_ulPreroll;
    UINT32 ulExtraBufferingInMs = m_ulMinimumBufferingInMs - m_ulPreroll;
    
    SetPreroll(ulPreroll);
    SetMinPrerollInMs(ulPreroll + ulExtraPrerollInMs,
		      ulPreroll + ulExtraBufferingInMs);
    SetMinPreroll();

    // Notice that we don't call ClearCurrentBufferingInMs()
    // or ClearCurrentBuffering(). This allows us to count
    // any data we have already received as part of the
    // new preroll

    CalcRemainingToBufferInMs();
    CalcRemainingToBuffer();
}

INT64 HXBufferingState::CreateINT64Timestamp(UINT32 ulTime)
{
    return CAST_TO_INT64 (m_ulTSRollOver) * CAST_TO_INT64 MAX_UINT32 + CAST_TO_INT64 ulTime;
}

INT64 HXBufferingState::CreateINT64Timesync(UINT32 ulTime)
{
    return CAST_TO_INT64 (m_ulTimeSyncRollOver) * CAST_TO_INT64 MAX_UINT32 + CAST_TO_INT64 ulTime;
}

void HXBufferingState::OnPacket(UINT32 ulTimestamp, UINT32 ulPacketSize,
				UINT32 ulElapsedTime,
				BOOL bIsLive, BOOL bIsBufferedPlayMode)
{
    //  0xFA .. 0xFF [roll over] (0x01)
    if (m_ulLastPacketTimeStamp > ulTimestamp &&
	((m_ulLastPacketTimeStamp - ulTimestamp) > MAX_TIMESTAMP_GAP))
    {
	m_ulTSRollOver++;
    }
    
    INT64 llActualTimeStamp = CreateINT64Timestamp(ulTimestamp);
    m_ulLastPacketTimeStamp = ulTimestamp;

    if (m_bIsFirstPacket)
    {
	/* Only if we are live,  store the first packet timestemp
	 * as the lowest timestamp. In any other case, we consider
	 * the lowest ts to be 0 OR the Seek time.
	 * This is to fix the post-seek buffering where we should not
	 * account any packets < seek ts towards buffering completion.
	 */
	if (bIsLive)
	{
	    m_llLowestTimeStamp = CAST_TO_INT64 ulTimestamp;
            m_llFirstLivePacketTimestamp = m_llLowestTimeStamp;
        }

	m_llHighestTimeStamp = CAST_TO_INT64 ulTimestamp;
	m_bIsFirstPacket = FALSE;
    }

    // Add this packet to our packet info statistics
    AddPktInfo(llActualTimeStamp, ulPacketSize);

    // data based preroll
    if (DataBasedPreroll())
    {
	m_ulCurrentBuffering += ulPacketSize;
    
	if (bIsBufferedPlayMode)
	{
	    /* 
	     * We wait to have at least 1 second worth of data
	     * before doing any calculations. This may need some
	     * tweaking.
	     */
	    if ((m_ulRemainingToBuffer != 0) &&
		(m_ulCurrentBuffering >= m_ulAvgBandwidth / 8))
	    {
		/* 
		 * Highly unlikely, but may happen from a server on the 
		 * local machine.
		 */
		if (ulElapsedTime == 0)
		{
		    if (m_ulCurrentBuffering >= m_ulMinimumPreroll)
		    {
			m_ulRemainingToBuffer = 0;
		    }
		}
		else
		{
		    UINT32 ulDenom = ulElapsedTime * m_ulAvgBandwidth / 8000;
		    
		    /* Sanity check - may be 0 only when bandwidth
		     * is really low 
		     */
		    ulDenom = ulDenom > 0 ? ulDenom : 1;
		    
		    CalcRemainingToBuffer(ulDenom);
		}
	    }
	}
	else
	{
	    /* bIsBufferedPlayMode == FALSE */
	    if (m_ulRemainingToBuffer)
	    {
		CalcRemainingToBuffer();
	    }
	}
    }

    if (llActualTimeStamp >= m_llHighestTimeStamp)
    {
	m_llHighestTimeStamp = llActualTimeStamp;
    } 
}

void HXBufferingState::UpdateBufferingInMs(INT64 llRefLowTimestamp,
					   INT64 llHighTimestamp, 
					   BOOL bIsBufferedPlayMode,
					   BOOL bIsTimestampDelivery,
					   UINT32 ulElapsedTime)
{
    UpdateCurrentBufferingInMs(llRefLowTimestamp, llHighTimestamp);

    if (bIsBufferedPlayMode)
    {
	/* We handle time stamp delivered streams differently
	 * in case of BUffered/PerfectPlay. This is because
	 * server sends timestamp delivered stream based
	 * on the timestamps on the packet and pretty
	 * much streams in realtime. 
	 * So even if we are on LAN and streaming a RealText
	 * file, we will get packets in realtime even though
	 * we have enough bandwidth available
	 * 
	 * For timestamp delivered streams, we just fulfill 
	 * preroll.
	 */
	if (bIsTimestampDelivery &&
	    m_ulCurrentBufferingInMs > m_ulMinimumPrerollInMs)
	{
	    m_ulRemainingToBufferInMs = 0;
	}
	else if ((m_ulRemainingToBufferInMs != 0) &&
		 (m_ulCurrentBufferingInMs >= 1000))
	{
	    /* 
	     * Highly unlikely, but may happen from a 
	     * server on the local machine.
	     */
	    if (ulElapsedTime == 0)
	    {
		if (m_ulCurrentBufferingInMs >= m_ulMinimumPrerollInMs)
		{
		    m_ulRemainingToBufferInMs = 0;
		}
	    }
	    else
	    {
		CalcRemainingToBufferInMs(ulElapsedTime);
	    }
	}
    }
    else
    {
	/* bIsBufferedPlayMode == FALSE */
	if (m_ulRemainingToBufferInMs)
	{
	    CalcRemainingToBufferInMs();
	}
    }
}

void HXBufferingState::UpdateTransportStats(INT64 llLowTSAtTransport,
					    INT64 llHighTSAtTransport,
					    ULONG32 ulBytesAtTransport,
					    BOOL bDoneAtTransport)
{
    m_llLowestTimestampAtTransport = llLowTSAtTransport; 
    m_llHighestTimestampAtTransport = llHighTSAtTransport; 
    m_ulNumBytesAtTransport = ulBytesAtTransport;
    m_bDoneAtTransport = bDoneAtTransport;
}

HX_RESULT HXBufferingState::GetBufferingStats(REF(INT64) llLowTimestamp, 
					      REF(INT64) llHighTimestamp,
					      REF(UINT32) ulBytesBuffered,
					      BOOL bUseTransportStats)
{
    HX_RESULT res = HXR_NO_DATA;

    if (!m_bIsFirstPacket)
    {
        // Note: GetBufferedData() MUST be called first because
        //       it is the one responsible for pruning the list
        ulBytesBuffered = GetBufferedData();

	if (!m_pktInfo.IsEmpty())
	{
	    HXBufferedPktInfo* pPktInfo = 
		(HXBufferedPktInfo*)m_pktInfo.GetHead();

	    llLowTimestamp = pPktInfo->Timestamp();
	}
	else
	{
	    llLowTimestamp = m_llHighestTimeStamp;
	}

	llHighTimestamp = m_llHighestTimeStamp;

	if (bUseTransportStats)
	{
	    if (m_llHighestTimestampAtTransport > llHighTimestamp)
	    {
		llHighTimestamp = m_llHighestTimestampAtTransport;
	    }

	    ulBytesBuffered += m_ulNumBytesAtTransport;
	}

	res = HXR_OK;
    }

    return res;
}

void HXBufferingState::GetExcessBufferInfo(INT64 llTheLowestTS,
					   INT64 llTheHighestTS,
					   BOOL bIsSeekPerformed,
					   REF(UINT32) ulRemainToBufferInMs,
					   REF(UINT32) ulRemainToBuffer,
					   REF(UINT32) ulExcessBufferInMs,
					   REF(UINT32) ulExcessBuffer,
					   REF(UINT32) ulExcessForThisStreamInMs,
					   REF(UINT32) ulExcessForThisStream)
{
    UINT32 ulCurBufInMs		    = 0;
    UINT32 ulRemainForThisStreamInMs = 0;
    UINT32 ulRemainForThisStream     = 0;
    
    ulExcessForThisStreamInMs = 0;
    ulExcessForThisStream     = 0;	
    
    if (m_ulNumBytesAtTransport > 0)
    {
	HX_ASSERT((llTheHighestTS - m_llLowestTimestampAtTransport) < MAX_TIMESTAMP_GAP);
	
	ulCurBufInMs = INT64_TO_UINT32(llTheHighestTS - 
				       m_llLowestTimestampAtTransport);
    }
    else
    {
	HX_ASSERT((llTheHighestTS - llTheLowestTS) < MAX_TIMESTAMP_GAP);
	
	ulCurBufInMs = INT64_TO_UINT32(llTheHighestTS - llTheLowestTS);
    }

    if (ulCurBufInMs <= m_ulMinimumBufferingInMs)
    {
	ulRemainForThisStreamInMs = m_ulMinimumBufferingInMs - ulCurBufInMs;
    }
    else
    {
	ulExcessForThisStreamInMs = ulCurBufInMs - m_ulMinimumBufferingInMs;
    }

    if (m_ulNumBytesAtTransport <= m_ulMinimumBuffering)
    {
	ulRemainForThisStream = 
	    m_ulMinimumBuffering - m_ulNumBytesAtTransport;
    }
    else
    {
	ulExcessForThisStream = m_ulNumBytesAtTransport - m_ulMinimumBuffering;
    }

    
    BOOL bHasPreroll = HasPreroll(bIsSeekPerformed);
    BOOL bHasPredata = HasPredata(bIsSeekPerformed);
	
    // satisfy the preroll (by default + pre-set)
    if (!bHasPredata || bHasPreroll)
    {
	if (ulRemainToBufferInMs < ulRemainForThisStreamInMs)
	{
	    ulRemainToBufferInMs = ulRemainForThisStreamInMs;
	}
	
	if (ulExcessBufferInMs < ulExcessForThisStreamInMs)
	{
	    ulExcessBufferInMs = ulExcessForThisStreamInMs;
	}
    }

    // satisfy the predata
    if (bHasPredata)
    {
	ulRemainToBuffer += ulRemainForThisStream;
	ulExcessBuffer   += ulExcessForThisStream;
    }

}

void HXBufferingState::OnTimeSync(UINT32 ulCurrentTime)
{
    //  0xFA .. 0xFF [roll over] (0x01)
    if (m_ulLastTimeSync > ulCurrentTime &&
       ((m_ulLastTimeSync - ulCurrentTime) > MAX_TIMESTAMP_GAP))
    {
       m_ulTimeSyncRollOver++;
    }
    m_ulLastTimeSync = ulCurrentTime;

    m_llCurrentPlaybackTime = CreateINT64Timesync(ulCurrentTime);

    // Add back the first live packet timestamp.
    // If not live, then m_llFirstLivePacketTimeStamp is 0.
    m_llCurrentPlaybackTime += m_llFirstLivePacketTimestamp;
    m_bCurrentTimeSet = TRUE;
}

void HXBufferingState::SetMinPrerollInMs(ULONG32 ulMinPrerollInMs, 
					 ULONG32 ulMinBufferingInMs)
{
    m_ulMinimumPrerollInMs    = ulMinPrerollInMs;
    m_ulMinimumBufferingInMs  = ulMinBufferingInMs;
    m_ulRemainingToBufferInMs = m_ulMinimumBufferingInMs;
}

void HXBufferingState::SetMinPreroll()
{
    SetMinPreroll(MsToBytes(m_ulMinimumPrerollInMs,
			    m_ulAvgBandwidth),
		  m_ulAvgBandwidth);
}

void HXBufferingState::SetMinPreroll(UINT32 ulMinimumPredata,
				     UINT32 ulBandwidth)
{
    HX_ASSERT(m_ulMinimumPrerollInMs >= m_ulPreroll);

    UINT32 ulExtraBufferingInMs = m_ulMinimumPrerollInMs - m_ulPreroll;

    /* Get current bandiwidth from asm */
    m_ulMinimumPreroll	= (ulMinimumPredata + 
			   MsToBytes(ulExtraBufferingInMs, 
				     ulBandwidth));

    // There is no buffered or perfect play with MIN_HEAP on.
#if defined(HELIX_CONFIG_MIN_PCM_PUSHDOWN_BYTES)
    m_ulMinimumBuffering  = m_ulMinimumPreroll;
#else
    ulExtraBufferingInMs = m_ulMinimumBufferingInMs - m_ulPreroll;

    m_ulMinimumBuffering  = (ulMinimumPredata + 
			     MsToBytes(ulExtraBufferingInMs, 
				       ulBandwidth));
#endif

    m_ulRemainingToBuffer  = m_ulMinimumBuffering;
    m_ulCurrentBuffering   = 0;
}

void HXBufferingState::UpdateMinPredata()
{
    UINT32	    ulMinimumPredata = m_ulPredata;
    UINT32	    ulBandwidth	     = m_ulAvgBandwidth;
    
#if defined(HELIX_FEATURE_ASM)
    // no ASMProps in AutoConfig
    HX_ASSERT(m_pASMProps);
    
    if (m_pASMProps)
    {
	UINT32 ulTmp;
	if (HXR_OK == m_pASMProps->GetPreData(ulTmp))
	{
	    ulMinimumPredata = ulTmp;
	}

	if (HXR_OK == m_pASMProps->GetBandwidth(ulTmp))
	{
	    ulBandwidth = ulTmp;
	}
    }
#endif /* HELIX_FEATURE_ASM */
    
    SetMinPreroll(ulMinimumPredata, ulBandwidth);
}

void HXBufferingState::UpdateCurrentBufferingInMs(INT64 llLowestTimeStamp, 
						  INT64 llHighestTimeStamp)
{
    INT64 llLowTimestamp = m_llLowestTimeStamp;
    
    if (m_bIsFirstPacket)
    {
	/* use the reference stream's lowest timestamp for calculation */
	llLowTimestamp = llLowestTimeStamp;
    }

    if (llHighestTimeStamp > llLowTimestamp)
    {
	// if the stream has been continuesly playing for 49 days
	// we will set m_ulCurrentBufferingInMs to MAX_UINT32
	if (llHighestTimeStamp - llLowTimestamp > MAX_UINT32)
	{
	    m_ulCurrentBufferingInMs = MAX_UINT32;
	}
	else
	{
	    m_ulCurrentBufferingInMs = 
		INT64_TO_UINT32(llHighestTimeStamp - llLowTimestamp);
	}
    }
}

ULONG32 HXBufferingState::MsToBytes(ULONG32 ulValueInMs, ULONG32 ulBw) const
{
    return (ulValueInMs * (ulBw / 8)) / 1000;
}

void HXBufferingState::CalcRemainingToBufferInMs()
{
    m_ulRemainingToBufferInMs = CalcRemaining(m_ulMinimumBufferingInMs,
					      m_ulCurrentBufferingInMs,
					      0,
					      0);
}

void HXBufferingState::CalcRemainingToBufferInMs(ULONG32 ulElapsedTime)
{
    m_ulRemainingToBufferInMs = CalcRemaining(m_ulMinimumBufferingInMs,
					      m_ulCurrentBufferingInMs,
					      m_ulMinimumPrerollInMs,
					      ulElapsedTime);
}

void HXBufferingState::CalcRemainingToBuffer()
{
    m_ulRemainingToBuffer = CalcRemaining(m_ulMinimumBuffering,
					  m_ulCurrentBuffering,
					  0,
					  0);
}

void HXBufferingState::CalcRemainingToBuffer(ULONG32 ulDenom)
{
    m_ulRemainingToBuffer = CalcRemaining(m_ulMinimumBuffering,
					  m_ulCurrentBuffering,
					  m_ulMinimumPreroll,
					  ulDenom);
}

UINT32 HXBufferingState::CalcRemaining(UINT32 ulMinimumBuffering,
				       UINT32 ulCurrentBuffering,
				       UINT32 ulMinimumPreroll,
				       UINT32 ulDenom) const
{
    UINT32 ulRemainingToBuffer = 0;

    if (ulMinimumBuffering > ulCurrentBuffering)
    {
	ulRemainingToBuffer = ulMinimumBuffering - ulCurrentBuffering;
    }

    if (ulDenom)
    {
	UINT32 ulBufferWhilePlaying = ulRemainingToBuffer;

	/*
	 *	Buffers during playback = X*X/Y + X*(X/Y)^2 + X*(X/Y)^3 + ....
	 *	    = X*X/Y (1 + X/Y + (X/Y)^2 + ....)
	 *
	 *      = X*X/Y ( 1 / (1 - X/Y))  IFF X/Y < 1 i.e. X < Y
	 *
	 *	      ELSE We have enough data... Satisfy preroll 
	 */
	
	/* Add a fudge factor of 10% */
	ulDenom = (UINT32) (ulDenom * 1.10);
	
	if (ulCurrentBuffering < ulDenom)
	{
	    double x = ulCurrentBuffering;
	    double y = ulDenom;
	    ulBufferWhilePlaying = (UINT32) (((x * x) / y) * (1. / (1. - x / y)));
	}
	
	if (ulBufferWhilePlaying < ulRemainingToBuffer)
	{
	    ulRemainingToBuffer -= ulBufferWhilePlaying;
	}
	else if (ulCurrentBuffering >= ulMinimumPreroll)
	{
	    ulRemainingToBuffer = 0;
	}
    }

    return ulRemainingToBuffer;
}

void HXBufferingState::SetAvgBWToASMBw()
{
    UINT32 ulBandwidth = 0;
    if (m_pASMProps &&
	m_pASMProps->GetBandwidth(ulBandwidth) == HXR_OK)
    {
	m_ulAvgBandwidth = ulBandwidth;
    }
}

void HXBufferingState::ClearPktInfo()
{
    m_ulBufferedData = 0;
    while(!m_pktInfo.IsEmpty())
    {
	HXBufferedPktInfo* pInfo = (HXBufferedPktInfo*)m_pktInfo.RemoveHead();
	delete pInfo;
    }
}

void HXBufferingState::AddPktInfo(INT64 llTimestamp, UINT32 ulPacketSize)
{
    // Create the HXBufferedPktInfo class
    HXBufferedPktInfo* pPktInfo = new HXBufferedPktInfo(llTimestamp, 
							ulPacketSize);
    if (pPktInfo)
    {
	m_pktInfo.AddTail(pPktInfo);
	m_ulBufferedData += ulPacketSize;
    }

    // purge the old packet info data
    //  this keeps the list short and prevents the Symbian
    //  allocator from freaking out
    (void)GetBufferedData();
}

UINT32 HXBufferingState::GetBufferedData()
{
    BOOL bDone = m_pktInfo.IsEmpty() || !m_bCurrentTimeSet;
    
    // Remove all packet info that is past due
    // and subtract their sizes from our buffering total
    while(!bDone)
    {
	HXBufferedPktInfo* pPktInfo = (HXBufferedPktInfo*)m_pktInfo.GetHead();
	
	if (pPktInfo->Timestamp() <= m_llCurrentPlaybackTime)
	{
	    m_ulBufferedData -= pPktInfo->Size();
	    
	    m_pktInfo.RemoveHead();
	    delete pPktInfo;
	    
	    bDone = m_pktInfo.IsEmpty();
	}
	else
	{
	    bDone = TRUE;
	}
    }

    return m_ulBufferedData;
}
