/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: hxsrc.cpp,v 1.35.2.1 2004/07/09 02:05:58 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 "hxtypes.h"
#include "hxcom.h"
#ifdef _WINDOWS
#include <windows.h>
#endif

#include "hlxclib/stdio.h"
#include "hlxclib/stdlib.h"
#include "prefdefs.h"
#include "plprefk.h"
#include "hxcomm.h"
#include "ihxpckts.h"
#include "hxfiles.h"
#include "hxengin.h"
#include "hxcore.h"
#include "hxprefs.h"
#include "hxpref.h"
#include "hxausvc.h"
#include "hxmon.h"
#include "hxclreg.h"
#include "hxgroup.h"
#include "hxsmbw.h"
#include "hxstrm.h"
#include "hxwin.h"


#include "hxcore.h"
#include "hxhyper.h"
#include "playhpnv.h"
#include "hxplugn.h"
#include "hxrendr.h"

#include "chxeven.h"
#include "chxelst.h"
#include "hxmap.h"
#include "hxrquest.h"
#include "hxmangle.h"
#include "hxtick.h"
#include "dbcs.h"

#include "hxstrutl.h"
#include "strminfo.h"
#include "timeval.h"
#include "statsmgr.h"
#include "hxbsrc.h"
#include "hxsrc.h"
#include "srcinfo.h"
#include "corshare.h"
#include "upgrdcol.h"
#include "hxrasyn.h"
#include "hxaudstr.h"
#include "hxplugn.h"
#include "hxrendr.h"
#include "errdbg.h"

// will be taken out once flags are defined in a separate file
#include "rmfftype.h"	
#include "hxplay.h"
#include "hxcleng.h"
#include "hxsrc.h"

#include "hxheap.h"
#ifdef _DEBUG
#undef HX_THIS_FILE		
static const char HX_THIS_FILE[] = __FILE__;
#endif

#if defined(HELIX_CONFIG_MIN_PCM_PUSHDOWN_BYTES)
// TurboPlay should never be turned on in low heap mode, but just in case ...
#define TURBO_AUDIO_PUSHDOWN 100
#else
#define TURBO_AUDIO_PUSHDOWN 2000
#endif

HXSource::HXSource() :
	  m_pPlayer (0)
	, m_lRefCount(0)
	, m_ulStreamIndex(0)
	, m_bInitialized (FALSE)
	, m_bIsPreBufferingDone(FALSE)
	, m_bClipTimeAdjusted(FALSE)
	, mLastError (HXR_OK)
	, m_ulPerfectPlayTime (0)
	, m_ulBufferedPlayTime(0)
        , m_ulStreamHeadersExpected(0)
	, m_bPerfectPlayEntireClip(FALSE)
	, m_bCannotBufferEntireClip(FALSE)
	, m_uNumStreams (0)
	, mFlags (0)
	, m_bPerfectPlayAllowed (FALSE)
	, mSaveAsAllowed (FALSE)
	, mLiveStream (FALSE)
	, m_bRestrictedLiveStream (FALSE)
	, m_bSourceEnd (FALSE)
	, m_bForcedSourceEnd(FALSE)
	, m_ulPreRollInMs (0)
	, m_ulPreRoll (0)
	, m_ulAvgBandwidth (0)
	, m_ulDuration (0)
	, m_bReceivedData (FALSE)
	, m_bReceivedHeader(FALSE)
	, m_bNonSeekable(FALSE)

	, m_nSeeking (0)
	, m_bPaused (FALSE)  
	, m_bFirstResume(TRUE)
	, m_bResumePending(FALSE)
	, m_bIsActive(FALSE)

	, m_uActiveStreams (0)

	, m_pszURL(NULL)
	, m_pURL(NULL)
	, m_bAltURL(TRUE)

	, m_ulStartTime (0)
	, m_ulEndTime (0)
	, m_ulDelay (0)
	, m_ulOriginalDelay (0)
	, m_ulPrefetchDelay (0)
	, m_llLastExpectedPacketTime(0)
	, m_ulRestrictedDuration(0)
	, m_bDelayed(FALSE)

	, m_ulSourceStartTime(0)
	, m_pPreferences (0)
	, m_pRegistry (0)
	, m_pScheduler (0)
#if defined(HELIX_FEATURE_STATS) && defined(HELIX_FEATURE_REGISTRY)
	, m_pStats (0)
	, m_pStatsManager(NULL)
#endif /* HELIX_FEATURE_STATS && HELIX_FEATURE_REGISTRY */
	, m_bLocked (FALSE)
	, m_pEngine (0)
	, m_bRebufferingRequired(FALSE)
	, m_bInitialBuffering(TRUE)
	, m_bPartOfNextGroup(FALSE)
	, m_bPartOfPrefetchGroup(FALSE)
	, m_pBufferManager(NULL)
	, m_pFileHeader(0)
	, m_bPerfectPlay(FALSE)
	, m_bBufferedPlay(FALSE)
	, m_ulLossHack(0)
	, m_ulNumFakeLostPackets(0)
	, m_ulLastBufferingCalcTime(0)
	, m_pSourceInfo(NULL)
	, m_pRequest(NULL)
	, m_pASMSource(NULL)
	, m_pBackChannel(NULL)
	, m_bDefaultAltURL(FALSE)
	, m_bCustomEndTime(FALSE)	
	, m_bCustomDuration(FALSE)
	, m_bIsPreBufferingStarted(FALSE)
	, m_ulOriginalDuration(0)
	, m_bReSetup(FALSE)
#if defined(HELIX_FEATURE_AUTOUPGRADE)
	, m_pUpgradeCollection(NULL)
#endif /* HELIX_FEATURE_AUTOUPGRADE */
	, m_bRTSPRuleFlagWorkAround(FALSE)
        , m_bContinueWithHeaders(FALSE)
	, m_bPrefetch(FALSE)
	, m_prefetchType(PrefetchUnknown)
	, m_ulPrefetchValue(0)
	, m_ulFirstPacketTime(0)
	, m_bIsMeta(FALSE)
	, m_bFastStart(FALSE)
	, m_serverTurboPlay(TURBO_PLAY_UNKNOWN)
	, m_ulMaxBandwidth(4000)	// 4000kbps
	, m_pAudioStreamList(NULL)
        , m_maxPossibleAccelRatio(4.0)	// I donno what's a good value?
        , m_ulTurboPushDown(TURBO_AUDIO_PUSHDOWN)
        , m_bSureStreamClip(FALSE)
        , m_ulTurboStartActiveTime(0)
	, m_srcEndCode(END_UNKNOWN)
	, m_pRecordControl(NULL)
	, m_bPlayFromRecordControl(FALSE)
        , m_pRedirectURL(NULL)
        , m_bRedirectPending(FALSE)
{
    mStreamInfoTable = new CHXMapLongToObj;
}

HXSource::~HXSource()
{
    // XXX moved this to the distuctor because we want to keep arround
    // the request until the renderers are close at which point we should
    // be released and deleted.
    HX_RELEASE(m_pRequest);
    HX_VECTOR_DELETE(m_pszURL);
    HX_DELETE(mStreamInfoTable);
}

void
HXSource::Stop()
{
    m_pSourceInfo = NULL;    

    HX_DELETE (m_pURL);
    HX_DELETE (m_pBufferManager);

    if ( FAILED(mLastError) )
    {
	HX_RELEASE(m_pRequest);
    }

#if defined(HELIX_FEATURE_STATS) && defined(HELIX_FEATURE_REGISTRY)
    if (m_pStatsManager)
    {
	m_pStatsManager->DoCleanup();
	HX_RELEASE(m_pStatsManager);
    }        
    HX_DELETE(m_pStats);    
#endif /* HELIX_FEATURE_STATS && HELIX_FEATURE_REGISTRY */

    DeleteAllEvents(); 

    HX_RELEASE(m_pRegistry);
    HX_RELEASE(m_pPreferences);
    HX_RELEASE(m_pScheduler);
    HX_RELEASE(m_pEngine);
}    

HX_RESULT HXSource::DoCleanup(EndCode endCode)
{
    m_ulDuration = 0;    
    m_ulPreRollInMs = 0;
    m_ulPreRoll = 0;
    m_ulAvgBandwidth = 0;
    m_ulLastBufferingCalcTime = 0;

    m_ulMaxBandwidth = 0;
    m_serverTurboPlay = TURBO_PLAY_UNKNOWN;

    m_bAltURL = FALSE;    
    m_bPaused = FALSE;
    m_bFirstResume = TRUE;
    m_bIsActive = FALSE;
    m_bResumePending = FALSE;
    m_bIsPreBufferingStarted = FALSE;
    m_bIsPreBufferingDone = FALSE;    
    m_bClipTimeAdjusted = FALSE;    
    m_bInitialBuffering = TRUE;
    m_bRebufferingRequired = FALSE;    
    m_bReceivedData = FALSE;
    m_bReceivedHeader = FALSE;

    ReleaseAudioStreams(m_pAudioStreamList);
    HX_DELETE(m_pAudioStreamList);

    DeleteStreamTable();

    CHXSimpleList::Iterator lIter = m_HXStreamList.Begin();
    for (; lIter != m_HXStreamList.End(); ++lIter)
    {
	HXStream* pStream = (HXStream*) (*lIter);
	pStream->Release();
    }

    m_HXStreamList.RemoveAll();    

    HX_RELEASE(m_pFileHeader);    
    HX_RELEASE(m_pASMSource);
    HX_RELEASE(m_pBackChannel);
#if defined(HELIX_FEATURE_AUTOUPGRADE)
    HX_RELEASE(m_pUpgradeCollection);
#endif /* HELIX_FEATURE_AUTOUPGRADE */

    m_bForcedSourceEnd	= FALSE;
    m_bSourceEnd	= FALSE;

    HX_DELETE(m_pRedirectURL);
    m_bRedirectPending  = FALSE;

#if defined(HELIX_FEATURE_RECORDCONTROL)
    if(m_pRecordControl)
    {
	m_pRecordControl->Cleanup();
    }
#endif /* HELIX_FEATURE_RECORDCONTROL */
    HX_RELEASE(m_pRecordControl);

    return HXR_OK;
}

/////////////////////////////////////////////////////////////////////////
//	Method:
//		IUnknown::QueryInterface
//	Purpose:
//		Implement this to export the interfaces supported by your 
//		object.
//
STDMETHODIMP HXSource::QueryInterface(REFIID riid, void** ppvObj)
{
    QInterfaceList qiList[] =
        {
            { GET_IIDHANDLE(IID_IHXStreamSource), (IHXStreamSource*)this },
            { GET_IIDHANDLE(IID_IHXPendingStatus), (IHXPendingStatus*)this },
            { GET_IIDHANDLE(IID_IHXInfoLogger), (IHXInfoLogger*)this },
            { GET_IIDHANDLE(IID_IHXPrivateStreamSource), (IHXPrivateStreamSource*)this },
            { GET_IIDHANDLE(IID_IHXSourceBufferingStats), (IHXSourceBufferingStats*)this },
            { GET_IIDHANDLE(IID_IHXSourceBufferingStats2), (IHXSourceBufferingStats2*)this },
#if defined(HELIX_FEATURE_HYPER_NAVIGATE)
            { GET_IIDHANDLE(IID_IHXHyperNavigate), (IHXHyperNavigate*)this },
            { GET_IIDHANDLE(IID_IHXHyperNavigate2), (IHXHyperNavigate2*)this },
#endif /* defined(HELIX_FEATURE_HYPER_NAVIGATE) */
            { GET_IIDHANDLE(IID_IUnknown), (IUnknown*)(IHXStreamSource*)this },
        };
    
    HX_RESULT res = ::QIFind(qiList, QILISTSIZE(qiList), riid, ppvObj);
    
    // if successful, return immediately...
    if (SUCCEEDED(res))
    {
        return res;
    }
    // ... otherwise proceed onward
    
    if (IsEqualIID(riid, IID_IHXBackChannel))
    {
	if (m_pBackChannel)
	{
	    AddRef();
	    *ppvObj = (IHXBackChannel*)this;
	    return HXR_OK;
	}
	else
	{
	    *ppvObj = NULL;
	    return HXR_NOINTERFACE;
	}
    }
    else if (IsEqualIID(riid, IID_IHXASMSource))
    {
	if (m_pASMSource)
	{
	    AddRef();
	    *ppvObj = (IHXASMSource*)this;
	    return HXR_OK;
	}
	else
	{
	    *ppvObj = NULL;
	    return HXR_NOINTERFACE;
	}
    }

#if defined(HELIX_FEATURE_AUTOUPGRADE)
    else if (IsEqualIID(riid, IID_IHXUpgradeCollection))
    {
	if (!m_pUpgradeCollection)
	{
	    m_pUpgradeCollection = new HXUpgradeCollection;
	    m_pUpgradeCollection->AddRef();
	}

	return m_pUpgradeCollection->QueryInterface(riid, ppvObj);
    }
#endif /* HELIX_FEATURE_AUTOUPGRADE */
    else if (m_pRequest &&
	m_pRequest->QueryInterface(riid, ppvObj) == HXR_OK)
    {
	return HXR_OK;
    }
    else if (m_pFileHeader &&
	m_pFileHeader->QueryInterface(riid, ppvObj) == HXR_OK)
    {
	return HXR_OK;
    }

    *ppvObj = NULL;
    return HXR_NOINTERFACE;
}

/////////////////////////////////////////////////////////////////////////
//	Method:
//		IUnknown::AddRef
//	Purpose:
//		Everyone usually implements this the same... feel free to use
//		this implementation.
//
STDMETHODIMP_(ULONG32) HXSource::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}

/////////////////////////////////////////////////////////////////////////
//	Method:
//		IUnknown::Release
//	Purpose:
//		Everyone usually implements this the same... feel free to use
//		this implementation.
//
STDMETHODIMP_(ULONG32) HXSource::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
	return m_lRefCount;
    }

    delete this;
    return 0;
}

// *** IHXStreamSource methods ***

/************************************************************************
 *	Method:
 *		IHXStreamSource::IsLive
 *	Purpose:
 *		Ask the source whether it is live
 *
 */
STDMETHODIMP_ (BOOL) HXSource::IsLive(void)
{
    return mLiveStream;
}

/************************************************************************
 *	Method:
 *	    IHXStreamSource::GetPlayer
 *	Purpose:
 *	    Get the interface to the player object of which the source is
 *	    a part of.
 *
 */
STDMETHODIMP HXSource::GetPlayer(IHXPlayer* &pPlayer)
{
    pPlayer = m_pPlayer;
    if (pPlayer)
    {
	pPlayer->AddRef();
    }

    return HXR_OK;
}

/************************************************************************
 *	Method:
 *	    IHXStreamSource::GetContext
 *	Purpose:
 *	    Get the interface to the context object of which the source is
 *	    a part of.
 *
 */
STDMETHODIMP HXSource::GetContext(IUnknown* &pContext)
{
    pContext = ((IUnknown*)(IHXClientEngine*) m_pEngine);
    if (pContext)
    {
	pContext->AddRef();
    }

    return HXR_OK;
}

/************************************************************************
 *	Method:
 *	    IHXStreamSource::GetURL
 *	Purpose:
 *	    Get the URL for this source. NOTE: The returned string is
 *	    assumed to be valid for the life of the IHXStreamSource from which it
 *	    was returned.
 *
 */
STDMETHODIMP_(const char*) HXSource::GetURL(void)
{
    const char* pURL = NULL;

    /* 
     * Make sure that the we have not been re-directed
     * to a new URL.
     */ 
    if (m_pRequest && 
	m_pRequest->GetURL(pURL) == HXR_OK &&
	pURL)
    {
	if (!m_pszURL ||
	    ::strcasecmp(pURL, m_pszURL) != 0)
	{
	    HX_VECTOR_DELETE(m_pszURL);
	    m_pszURL = new char[strlen(pURL) + 1];
	    strcpy(m_pszURL, pURL); /* Flawfinder: ignore */
	}
    }

    // append RAM mimeType as URL option to notify the
    // caller(i.e. ViewSource)
    if (m_bIsMeta)
    {
	char* pszTemp = NULL;

	pszTemp = ::HXFindChar(m_pszURL, '?');
	if (pszTemp)
	{
		int lenTemp = strlen(m_pszURL) + strlen("&mimeType=") + strlen(RAM_MIMETYPE) + 1;
	    pszTemp = new char[lenTemp];
	    SafeSprintf(pszTemp, lenTemp, "%s%s%s", m_pszURL, "&mimeType=", RAM_MIMETYPE); /* Flawfinder: ignore */
	}
	else
	{
		int lenTemp = strlen(m_pszURL) + strlen("?mimeType=") + strlen(RAM_MIMETYPE) + 1;
	    pszTemp = new char[lenTemp];
	    SafeSprintf(pszTemp, lenTemp, "%s%s%s", m_pszURL, "?mimeType=", RAM_MIMETYPE); /* Flawfinder: ignore */
	}

	HX_VECTOR_DELETE(m_pszURL);
	m_pszURL = pszTemp;
    }

    return m_pszURL;
}


/************************************************************************
 *	Method:
 *	    IHXStreamSource::GetStreamCount
 *	Purpose:
 *	    Get the number of streams in this source
 *
 */
STDMETHODIMP_(UINT16)
HXSource::GetStreamCount()
{
    return m_HXStreamList.GetCount();
}


/************************************************************************
 *	Method:
 *	    IHXStreamSource::GetStream
 *	Purpose:
 *	    Get the stream with the given index.
 *	Note: 
 *	    Since stream numbers may not be zero based, stream index is not
 *	    equal to the stream number. Actual stream number may be determined 
 *	    from IHXStream::GetStreamNumber()
 *
 */
STDMETHODIMP
HXSource::GetStream
(
    UINT16 nIndex,
    REF(IUnknown*) pUnknown
)
{
    LISTPOSITION lPosition = m_HXStreamList.FindIndex((int) nIndex);
    if (!lPosition)
    {
	pUnknown = NULL;
	return HXR_INVALID_PARAMETER;
    }

    HXStream* pStream = (HXStream*) m_HXStreamList.GetAt(lPosition);
    HX_ASSERT(pStream);

    return pStream->QueryInterface(IID_IUnknown,(void**)&pUnknown);
}


// IHXPrivateStreamSource methods 
STDMETHODIMP_(BOOL) HXSource::IsSaveAllowed()
{
    return mSaveAsAllowed;
}


/*
 * IHXBackChannel method
 */

/************************************************************************
 *	Method:
 *	    IHXBackChannel::PacketReady
 *	Purpose:
 *      A back channel packet sent from Renderer to FileFormat plugin.
 */
STDMETHODIMP
HXSource::PacketReady(IHXPacket* pPacket)
{
    HX_ASSERT(m_pBackChannel);

    if (m_pBackChannel)
    {
	return m_pBackChannel->PacketReady(pPacket);
    }

    return HXR_FAIL;
}


/*
 * IHXASMSource methods
 */

/************************************************************************
 *	Method:
 *	    IHXASMSource::Subscribe
 *	Purpose:
 *      Called to inform a file format that a subscription has occurred,
 *	    to rule number uRuleNumber, for stream uStreamNumber.
 */
STDMETHODIMP
HXSource::Subscribe(UINT16	uStreamNumber,
		     UINT16	uRuleNumber)
{
    HX_ASSERT(m_pASMSource);

    if (m_pASMSource)
    {
	return m_pASMSource->Subscribe(uStreamNumber, uRuleNumber);
    }

    return HXR_FAIL;
}

/************************************************************************
 *	Method:
 *	    IHXASMSource::Unsubscribe
 *	Purpose:
 *      Called to inform a file format that a unsubscription has occurred,
 *	    to rule number uRuleNumber, for stream uStreamNumber.
 */
STDMETHODIMP
HXSource::Unsubscribe(UINT16	uStreamNumber,
		       UINT16	uRuleNumber)
{
    HX_ASSERT(m_pASMSource);

    if (m_pASMSource)
    {
	return m_pASMSource->Unsubscribe(uStreamNumber, uRuleNumber);
    }

    return HXR_FAIL;
}

void	
HXSource::AddHXStream(HXStream* pStream)
{
    pStream->AddRef();
    m_HXStreamList.AddTail(pStream);

    UINT16 uStreamNumber = pStream->GetStreamNumber();
    STREAM_INFO* pStreamInfo;
    if (mStreamInfoTable->Lookup((LONG32) uStreamNumber, (void*& )pStreamInfo))
    {
	pStreamInfo->m_pStream = pStream;
	pStreamInfo->m_pStream->AddRef();

	pStreamInfo->BufferingState().OnStream((IHXStream*)pStream);
    }
}

HX_RESULT
HXSource::CollectAudioStreams(CHXSimpleList*& pAudioStreamList)
{
#if defined(HELIX_FEATURE_ADVANCEDGROUPMGR)
    HX_RESULT	    rc = HXR_OK;
    UINT16	    i = 0;
    UINT16	    uNumAudioStreams = 0;
    CHXAudioPlayer* pAudioPlayer = NULL;
    CHXAudioStream* pCHXAudioStream = NULL;
    IHXValues*	    pHeader = NULL;

    pAudioStreamList = NULL;

    if (!m_pPlayer)
    {
	goto cleanup;
    }

    pAudioPlayer = m_pPlayer->GetAudioPlayer();
    if (!pAudioPlayer)
    {
	goto cleanup;
    }

    uNumAudioStreams = pAudioPlayer->GetAudioStreamCount();
    for (i = 0; i < uNumAudioStreams; i++)
    {
	pCHXAudioStream = pAudioPlayer->GetCHXAudioStream(i);
	pCHXAudioStream->AddRef();

	pHeader = ((IHXAudioStream*)pCHXAudioStream)->GetStreamInfo();

	if (pHeader &&
	    IsAudioStreamFromThisSource(pHeader))
	{
	    if (!pAudioStreamList)
	    {
		pAudioStreamList = new CHXSimpleList();
	    }

	    pAudioStreamList->AddTail(pCHXAudioStream);
	}
	else
	{
	    HX_RELEASE(pCHXAudioStream);
	}
	HX_RELEASE(pHeader);	
    }

cleanup:

    if (!pAudioStreamList)
    {	 
	rc = HXR_FAILED;
    }

    return rc;
#else
    return HXR_NOTIMPL;
#endif /* HELIX_FEATURE_ADVANCEDGROUPMGR */
}

void		
HXSource::RemoveAudioStreams(void)
{    
#if defined(HELIX_FEATURE_ADVANCEDGROUPMGR)
    CHXAudioPlayer* pAudioPlayer = NULL;
    CHXSimpleList*  pAudioStreamList = NULL;

    if (!m_pPlayer)
    {
	goto cleanup;
    }

    pAudioPlayer = m_pPlayer->GetAudioPlayer();
    if (!pAudioPlayer)
    {
	goto cleanup;
    }

    pAudioPlayer->AddRef();

    if (HXR_OK == CollectAudioStreams(pAudioStreamList) && pAudioStreamList)
    {
	pAudioPlayer->ManageAudioStreams(pAudioStreamList, CHXAudioPlayer::STR_REMOVE);

	ReleaseAudioStreams(pAudioStreamList);
	HX_DELETE(pAudioStreamList);
    }

    HX_RELEASE(pAudioPlayer);

cleanup:
#endif /* HELIX_FEATURE_ADVANCEDGROUPMGR */

    return;
}

void
HXSource::PauseAudioStreams(void)
{
#if defined(HELIX_FEATURE_ADVANCEDGROUPMGR)
    CHXAudioPlayer* pAudioPlayer = NULL;
    CHXSimpleList*  pAudioStreamList = NULL;

    if (!m_pPlayer)
    {
	goto cleanup;
    }

    pAudioPlayer = m_pPlayer->GetAudioPlayer();
    if (!pAudioPlayer)
    {
	goto cleanup;
    }

    pAudioPlayer->AddRef();

    if (HXR_OK == CollectAudioStreams(pAudioStreamList) && pAudioStreamList)
    {
	pAudioPlayer->ManageAudioStreams(pAudioStreamList, CHXAudioPlayer::STR_PAUSE);

	ReleaseAudioStreams(pAudioStreamList);
	HX_DELETE(pAudioStreamList);
    }

    HX_RELEASE(pAudioPlayer);

cleanup:
#endif /* HELIX_FEATURE_ADVANCEDGROUPMGR */

    return;
}

void
HXSource::ResumeAudioStreams(void)
{
#if defined(HELIX_FEATURE_ADVANCEDGROUPMGR)
    CHXAudioPlayer* pAudioPlayer = NULL;
    CHXSimpleList*  pAudioStreamList = NULL;

    if (!m_pPlayer)
    {
	goto cleanup;
    }

    pAudioPlayer = m_pPlayer->GetAudioPlayer();
    if (!pAudioPlayer)
    {
	goto cleanup;
    }

    pAudioPlayer->AddRef();

    if (HXR_OK == CollectAudioStreams(pAudioStreamList) && pAudioStreamList)
    {
	pAudioPlayer->ManageAudioStreams(pAudioStreamList, CHXAudioPlayer::STR_RESUME);

	ReleaseAudioStreams(pAudioStreamList);
	HX_DELETE(pAudioStreamList);
    }

    HX_RELEASE(pAudioPlayer);

cleanup:
#endif /* HELIX_FEATURE_ADVANCEDGROUPMGR */

    return;
}

void
HXSource::ReleaseAudioStreams(CHXSimpleList* pAudioStreamList)
{
#if defined(HELIX_FEATURE_ADVANCEDGROUPMGR)
    if (pAudioStreamList && !pAudioStreamList->IsEmpty())
    {
	CHXSimpleList::Iterator lIter = pAudioStreamList->Begin();
	for (; lIter != pAudioStreamList->End(); ++lIter)
	{
	    CHXAudioStream* pAudioStream = (CHXAudioStream*) (*lIter);
	    HX_RELEASE(pAudioStream);
	}
    }
#endif /* HELIX_FEATURE_ADVANCEDGROUPMGR */

    return;
}

HX_RESULT HXSource::Init(HXPlayer * pPlayer, UINT32 unRegistryID)
{
    HX_RESULT	theErr = HXR_OK;
   
    m_pPlayer = pPlayer;
    if (m_pPlayer->QueryInterface(IID_IHXPreferences, (void **) &m_pPreferences) != HXR_OK)
    {
	theErr = HXR_INVALID_PARAMETER;	//HX_INVALID_INTERFACE;
    }

    m_pBufferManager = new CBufferManager(this);
    if(!m_pBufferManager)
    {
        theErr = HXR_OUTOFMEMORY;
    }

    // create registry entries
    if (HXR_OK != m_pPlayer->QueryInterface(IID_IHXRegistry, (void**)&m_pRegistry))
    {
	m_pRegistry = NULL;
    }
    else
    {
	m_ulRegistryID = unRegistryID;
    }

    if (!theErr &&
	m_pPlayer->QueryInterface(IID_IHXScheduler, (void **) &m_pScheduler) != HXR_OK)
    {
	theErr = HXR_INVALID_PARAMETER;	//HX_INVALID_INTERFACE;
    }

    IHXPreferences* pPref = NULL;

    m_pPlayer->QueryInterface(IID_IHXPreferences, (void**)&pPref);
    if (HXR_OK == ReadPrefINT32(pPref, "TurboPushdown", m_ulTurboPushDown))
    {
	m_ulTurboPushDown = (m_ulTurboPushDown / 100) * 100;  // round to multiple of 100
    }
    HX_RELEASE(pPref);

    m_pEngine = m_pPlayer->m_pEngine;
    m_pEngine->AddRef();

    return theErr;
}

HX_RESULT HXSource::SetupRegistry()
{
    HX_RESULT	theErr = HXR_OK;
#if defined(HELIX_FEATURE_STATS) && defined(HELIX_FEATURE_REGISTRY)
    UINT32	ulRepeatedRegistryID = 0;
    IHXBuffer*	pRepeatRegName = NULL;
    char        szSourceName[MAX_DISPLAY_NAME] = {0}; /* Flawfinder: ignore */

    HX_ASSERT(m_pSourceInfo);    

    if (m_pSourceInfo->m_bLeadingSource &&
	!m_pSourceInfo->m_pRepeatList)
    {
	m_pStats = new SOURCE_STATS(m_pRegistry, m_ulRegistryID);
    }
    else if (m_pRegistry && 
	    HXR_OK == m_pRegistry->GetPropName(m_pPlayer->m_ulRepeatedRegistryID, pRepeatRegName))
    {
	SafeSprintf(szSourceName, MAX_DISPLAY_NAME, "%s.%ld%ld%ld", 
		pRepeatRegName->GetBuffer(),
		m_pSourceInfo->m_uGroupID,
		m_pSourceInfo->m_uTrackID,
		(int)m_pSourceInfo->m_bLeadingSource);
    
	ulRepeatedRegistryID = m_pRegistry->GetId(szSourceName);
	if (!ulRepeatedRegistryID)
	{
	    ulRepeatedRegistryID = m_pRegistry->AddComp(szSourceName);
	}

	m_pStatsManager = new StatsManager(m_pRegistry, m_ulRegistryID, ulRepeatedRegistryID);
	m_pStatsManager->AddRef();

	m_pStats = new SOURCE_STATS(m_pRegistry, ulRepeatedRegistryID);
        if( !m_pStats )
        {
            theErr = HXR_OUTOFMEMORY;
        }
    }
    else
    {
	// why stats' creation failed??
	HX_ASSERT(FALSE);
    }

    HX_RELEASE(pRepeatRegName);
#endif /* HELIX_FEATURE_STATS && HELIX_FEATURE_REGISTRY */

    return theErr;
}

HX_RESULT HXSource::ReadPreferences(void)
{
    UINT32      un16Temp = 10;

    ReadPrefINT32(m_pPreferences, "PerfectPlayTime", un16Temp); 
    m_ulPerfectPlayTime = un16Temp;

    ReadPrefINT32(m_pPreferences, "BufferedPlayTime", un16Temp);
    m_ulBufferedPlayTime = un16Temp;

    ReadPrefBOOL(m_pPreferences, "PerfPlayEntireClip", m_bPerfectPlayEntireClip); 

    return HXR_OK;
}


HX_RESULT HXSource::DeleteStreamTable(void)
{
    HX_RESULT theErr = HXR_OK;

    STREAM_INFO	* sInfo = NULL;
	
    for (CHXMapLongToObj::Iterator i = mStreamInfoTable->Begin();
         i != mStreamInfoTable->End(); ++i) 
    {    
	sInfo = (STREAM_INFO*) (*i);
	
	if (sInfo)
	{
	    HX_DELETE (sInfo);
	}
    }

    mStreamInfoTable->RemoveAll();
    m_ulStreamIndex = 0;
    m_uNumStreams = 0;
    
    return theErr;
}	


IHXValues* HXSource::GetHeaderInfo(UINT16 stream_number)
{
    STREAM_INFO	* pStreamInfo;
    if (mStreamInfoTable->Lookup((LONG32) stream_number, (void*& )pStreamInfo))
    {
	return pStreamInfo->m_pHeader;
    }
    else
    {
	return NULL;
    }
}

HX_RESULT	
HXSource::GetStreamHeaderInfo(UINT16 index, IHXValues*& hdr)
{
    HX_TRACE("HXSource::GetStreamHeaderInfo");
    
    // sanity check
    if (index >= m_uNumStreams)
    {
	return HXR_INVALID_PARAMETER; // HX_INVALID_INDEX;
    }
    
    CHXMapLongToObj::Iterator i = mStreamInfoTable->Begin();
    for (UINT16 j=0; j < index; j++)
    {
	++i;
    }

    STREAM_INFO* pStreamInfo = (STREAM_INFO*) *i;

    hdr = pStreamInfo->m_pHeader;
    if (hdr)
    {
	hdr->AddRef();
    }

    return HXR_OK;
}


void	HXSource::SetFlags(UINT16 flags)
{
    mFlags = flags;

    if (mFlags & HX_PERFECT_PLAY_ENABLED)
    {
	m_bPerfectPlayAllowed = TRUE;
    }
    else
    {
	m_bPerfectPlayAllowed = FALSE;
    }

    if (mFlags & HX_SAVE_ENABLED)
    {
	mSaveAsAllowed = TRUE;
    }
    else
    {
	mSaveAsAllowed = FALSE;
    }
}

#if defined(HELIX_FEATURE_STATS) && defined(HELIX_FEATURE_REGISTRY)
HX_RESULT HXSource::CopyMetaDataToRegistry(IHXValues* pHeader)
{
    // get Request Object
    char pszServerMetaData[256] = {0}; /* Flawfinder: ignore */
    char pszMetaData[256] = {0}; /* Flawfinder: ignore */

    IHXValues* pReqHeaders = NULL;
    IHXRequest* pRequest = NULL;
    IHXBuffer* pBuffer = NULL;
    char szRegKeyName[MAX_DISPLAY_NAME] = {0}; /* Flawfinder: ignore */
    char buff[MAX_DISPLAY_NAME] = {0}; /* Flawfinder: ignore */
    char szSMDKey[256]; /* Flawfinder: ignore */
    ULONG32 ulValue = 0;
    IHXBuffer* pParentName = NULL;
    UINT32 regid = 0;
    const char szServerMetaData[] = {"ServerMetaData"};


    if (HXR_OK == m_pRegistry->GetPropName(m_pStats->m_ulRegistryID, pParentName))
    {
	SafeStrCpy(buff, (const char*) pParentName->GetBuffer(), MAX_DISPLAY_NAME);
	char* pDot = strrchr(buff, '.');
	if (pDot)
	{
	    *pDot = '\0';
	}
	SafeStrCpy(szSMDKey, buff, 256);

	if (HXR_OK == GetRequest(pRequest))
	{
	    // get request headers
	    if (HXR_OK == pRequest->GetRequestHeaders(pReqHeaders))
	    {
		// look for the meta data properties
		if (HXR_OK == pReqHeaders->GetPropertyCString("AcceptMetaInfo", pBuffer))
		{
		    SafeStrCpy(pszMetaData, (char*) pBuffer->GetBuffer(), 256);
		    HX_RELEASE(pBuffer);

		    // look for comma delimited entries
		    const char* pCharEntryStart = pszMetaData;
		    const char* pCharEntryEnd = pCharEntryStart;

		    while (pCharEntryEnd && *pCharEntryEnd)
		    {
			++pCharEntryEnd;
			if (*pCharEntryEnd == ',' || !*pCharEntryEnd)
			{
			    // copy next prop request into buffer 
			    strncpy(buff, pCharEntryStart, (UINT32)pCharEntryEnd - (UINT32)pCharEntryStart); /* Flawfinder: ignore */
			    *(buff+(UINT32)pCharEntryEnd - (UINT32)pCharEntryStart) = '\0';

			    // see if this prop is in file header (it should be!)
			    if (HXR_OK == pHeader->GetPropertyCString(buff, pBuffer))
			    {
				// create new registry entry
				SafeSprintf(szRegKeyName, MAX_DISPLAY_NAME, "%s.%s", pParentName->GetBuffer(), buff);
				regid = m_pRegistry->GetId(szRegKeyName);
				if (!regid)
				{
				    m_pRegistry->AddStr(szRegKeyName, pBuffer);
				}
				else
				{
				    // set new value
				    m_pRegistry->SetStrByName(szRegKeyName, pBuffer);
				}

				HX_RELEASE(pBuffer);
			    }
			    else if (HXR_OK == pHeader->GetPropertyULONG32(buff, ulValue))
			    {
				// create new registry entry
				SafeSprintf(szRegKeyName, MAX_DISPLAY_NAME, "%s.%s", pParentName->GetBuffer(), buff);

				regid = m_pRegistry->GetId(szRegKeyName);
				if (!regid)
				{
				    m_pRegistry->AddInt(szRegKeyName, ulValue);
				}
				else
				{
				    // set new value
				    m_pRegistry->SetIntByName(szRegKeyName, ulValue);
				}
			    }

			    pCharEntryStart = pCharEntryEnd + 1;
			}
		    }
		}

		// look for the meta data properties
		if (HXR_OK == pReqHeaders->GetPropertyCString("AcceptServerMetaData", pBuffer))
		{
		    SafeStrCpy(pszServerMetaData, (char*) pBuffer->GetBuffer(), 256);
		    HX_RELEASE(pBuffer);

		    // first make sure we have a composit key for "statistics.Player0.ServerMetaData"
		    SafeSprintf(buff, MAX_DISPLAY_NAME, "%s.%s", szSMDKey, szServerMetaData);
		    regid = m_pRegistry->GetId(buff);
		    if (!regid)
		    {
			m_pRegistry->AddComp(buff);
		    }
		    SafeStrCpy(szSMDKey, buff, 256);

		    // look for comma delimited entries
		    const char* pCharEntryStart = pszServerMetaData;
		    const char* pCharEntryEnd = pCharEntryStart;

		    while (pCharEntryEnd && *pCharEntryEnd)
		    {
			++pCharEntryEnd;
			if (*pCharEntryEnd == ',' || !*pCharEntryEnd)
			{
			    // copy next prop request into buffer 
			    strncpy(buff, pCharEntryStart, (UINT32)pCharEntryEnd - (UINT32)pCharEntryStart); /* Flawfinder: ignore */
			    *(buff+(UINT32)pCharEntryEnd - (UINT32)pCharEntryStart) = '\0';

			    // see if this prop is in file header (it should be!)
			    if (HXR_OK == pHeader->GetPropertyCString(buff, pBuffer))
			    {
				// create new registry entry (if one does not exist)
				SafeSprintf(szRegKeyName, MAX_DISPLAY_NAME, "%s.%s", szSMDKey, buff);
				regid = m_pRegistry->GetId(szRegKeyName);
				if (!regid)
				{
				    m_pRegistry->AddStr(szRegKeyName, pBuffer);
				}
				else
				{
				    // set new value
				    m_pRegistry->SetStrByName(szRegKeyName, pBuffer);
				}

				HX_RELEASE(pBuffer);
			    }
			    else if (HXR_OK == pHeader->GetPropertyULONG32(buff, ulValue))
			    {
				// create new registry entry
				SafeSprintf(szRegKeyName, MAX_DISPLAY_NAME, "%s.%s", szSMDKey, buff);
				regid = m_pRegistry->GetId(szRegKeyName);
				if (!regid)
				{
				    m_pRegistry->AddInt(szRegKeyName, ulValue);
				}
				else
				{
				    // set new value
				    m_pRegistry->SetIntByName(szRegKeyName, ulValue);
				}
			    }

			    pCharEntryStart = pCharEntryEnd + 1;
			}
		    }
		}

		HX_RELEASE(pReqHeaders);
	    }

	    HX_RELEASE(pRequest);
	}

	HX_RELEASE(pParentName);
    }

    return HXR_OK;
}
#endif /* HELIX_FEATURE_STATS && HELIX_FEATURE_REGISTRY */

HX_RESULT HXSource::SetPlayTimes(UINT32 ulStartTime, 
				  UINT32 ulEndTime, 
				  UINT32 ulDelay, 
				  UINT32 ulDuration)
{
    m_ulStartTime	    = ulStartTime;
    m_ulEndTime		    = ulEndTime;
    /* We do not handle Delay field for now */
    m_ulDelay		    = ulDelay;
    m_ulRestrictedDuration  = ulDuration;

    if (m_ulEndTime > 0)
    {
	m_bCustomEndTime = TRUE;
    }
    else
    {
    	m_bCustomEndTime = FALSE;
    }

    return HXR_OK;
}

HX_RESULT
HXSource::UpdatePlayTimes(IHXValues* pValues)
{
    HX_RESULT	rc = HXR_OK;
    char	szStart[] = "Start";
    char	szEnd[] = "End";
    char	szDelay[] = "Delay";
    char	szDuration[] = "Duration";
    UINT32	ulStartTime = 0;
    UINT32	ulEndTime = 0;
    UINT32	ulDelay = 0;
    UINT32	ulRestrictedDuration = 0;

    pValues->GetPropertyULONG32(szStart, ulStartTime);
    pValues->GetPropertyULONG32(szEnd, ulEndTime);
    pValues->GetPropertyULONG32(szDelay, ulDelay);
    pValues->GetPropertyULONG32(szDuration, ulRestrictedDuration);

    if (ulStartTime		!= m_ulStartTime    ||
	ulEndTime		!= m_ulEndTime	    ||
	ulDelay			!= m_ulDelay	    ||
	ulRestrictedDuration	!= m_ulRestrictedDuration)
    {
	SetPlayTimes(ulStartTime, ulEndTime, ulDelay, ulRestrictedDuration);
	rc = AdjustClipTime();
    }

    return rc;
}

ULONG32 
HXSource::GetPerfectPlayTime(void)
{
    ULONG32 ulCurrentPlayTime = m_pPlayer ? m_pPlayer->GetCurrentPlayTime() : 0;
    ULONG32 result	      = m_bPerfectPlay ? m_ulPerfectPlayTime : m_ulBufferedPlayTime;

    if (m_ulDuration != 0)
    {
	// mPlaybackLength is in Milliseconds, we want to calculate
	// playback in seconds...
	ULONG32 playbackTimeLeftInClip = 
	    ((m_ulDuration > ulCurrentPlayTime ? m_ulDuration - ulCurrentPlayTime : 0) /1000)+1;

	/* Perfect Play entire clip ONLY if user has chosen PerfectPlay specifically
	 * If we internally go in buffered play mode, always use perfect play time
	 * setting.
	 */
	if (m_bPerfectPlay)
	{
	    if (m_bPerfectPlayEntireClip)
	    {
		result = playbackTimeLeftInClip;
	    }
	    else
	    {
		result = min(m_ulPerfectPlayTime, playbackTimeLeftInClip);
	    }
	}
	else // if (m_bBufferedPlay)
	{
	    result = min(m_ulBufferedPlayTime, playbackTimeLeftInClip);
	}
    }
    
    // check if enough memory is available to handle the result
    if(result > 0)
    {
	// get 50% of the available memory
	ULONG32 maxMemAvail = GetAvailableMemory() / 2;
	ULONG32 bytesPerSecond = m_ulAvgBandwidth/8;
	ULONG32 maxTimeAvail = maxMemAvail/(bytesPerSecond > 0 ? bytesPerSecond : 1);

	// if this is true then we can't buffer the entire clip
	if (maxTimeAvail < result)
		m_bCannotBufferEntireClip = (m_bPerfectPlayEntireClip) ? TRUE : FALSE;
	else
		m_bCannotBufferEntireClip = FALSE;

	result = min(result,maxTimeAvail);
    }

    /* Value now returned in ms. */
    return 1000*(max(result, PERFECTPLAY_MIN_TIME));
}

STDMETHODIMP 
HXSource::ReportRebufferStatus(UINT16 uStreamNumber,
			        UINT8 unNeeded, 
			        UINT8 unAvailable) 
{
    STREAM_INFO* pStreamInfo = 0;

//    if (m_bDelayed ||
//	(m_ulDelay > 0 && m_ulDelay > m_pPlayer->GetCurrentPlayTime()))

    if (m_bDelayed)
    {
	return HXR_OK;
    }

    DEBUG_OUT(m_pPlayer, DOL_TRANSPORT, (s, "(%p)ReportRebufferStatus %lu %lu %lu", this, uStreamNumber, unNeeded, unAvailable));

    if (mStreamInfoTable->Lookup((LONG32) uStreamNumber, (void*& )pStreamInfo))
    {
	pStreamInfo->m_unNeeded	    = unNeeded;
	pStreamInfo->m_unAvailable  = unAvailable;
	if (unNeeded > unAvailable)
	{
//	    dfprintf("buff", "reportrebufferstatus: %lu\n", m_pPlayer->GetInternalCurrentPlayTime());	    
//	    g_bRahulLog = TRUE;

	    if (m_bFastStart)
	    {
                DEBUG_OUT(m_pPlayer, DOL_TRANSPORT, (s, "(%p)ALMOST Turbo OFF ReportRebufferStatus", this));
                //with surestream audio only clips, rarender may report buffering immediately after resume
                if (m_bSureStreamClip)
		{
		    if (CALCULATE_ELAPSED_TICKS(m_ulTurboStartActiveTime, HX_GET_TICKCOUNT()) > 1000)
		    {
			LeaveFastStart(TP_OFF_BY_REBUFFER);
			DEBUG_OUT(m_pPlayer, DOL_TRANSPORT, (s, "(%p)Turbo OFF ReportRebufferStatus", this));
		    }
		}
		else
		{
                    LeaveFastStart(TP_OFF_BY_REBUFFER);
                    DEBUG_OUT(m_pPlayer, DOL_TRANSPORT, (s, "(%p)Turbo OFF ReportRebufferStatus", this));
		}
	    }

	    // log rebuffer action
	    LogInformation("BUFBEG", NULL);
	    
	    DoRebuffer();
 	}
	else
	{
	    m_bRebufferingRequired = IsRebufferRequired();
	    if (!m_bRebufferingRequired)
	    {
		// log rebuffer action
		LogInformation("BUFEND", NULL);
	    }
	}
	
        return HXR_OK;
    }

    return HXR_UNEXPECTED;
}

STDMETHODIMP 
HXSource::SetGranularity
(
    UINT16 uStreamNumber,
    ULONG32 ulGranularity
) 
{
    STREAM_INFO* pStreamInfo = 0;

    if (mStreamInfoTable->Lookup((LONG32) uStreamNumber, (void*& )pStreamInfo))
    {
	m_pPlayer->SetGranularity(this, uStreamNumber, ulGranularity);
	return HXR_OK;
    }
    else
    {
	return HXR_OK;
    }

    return HXR_UNEXPECTED;
}

BOOL
HXSource::TryResume(void)
{
    BOOL    bResume = FALSE;

    // resume the persistent source ASAP
    if (m_pSourceInfo && m_pSourceInfo->m_bIsPersistentSource)
    {
        m_bDelayed = FALSE;
        bResume = TRUE;
    }
    else if (m_bPaused && m_bDelayed && m_pPlayer &&
             m_pPlayer->CanBeStarted(this, m_pSourceInfo, m_bPartOfNextGroup))
    {
        UINT32 ulCurrentTime = m_pPlayer->GetInternalCurrentPlayTime();
        UINT32 ulStartTime = 0;

        if (m_ulDelay > m_ulPreRollInMs + NETWORK_FUDGE_FACTOR)
        {
            ulStartTime = m_ulDelay - (m_ulPreRollInMs + NETWORK_FUDGE_FACTOR);
        }

        if (ulCurrentTime >= ulStartTime)
        {
            m_bDelayed = FALSE;
            bResume = TRUE;
        }

        if (!m_bIsPreBufferingStarted)
        {
            m_bIsPreBufferingStarted = TRUE;
            bResume = TRUE;
        }
    }
    else if (m_bPrefetch)
    {
        bResume = TRUE;
    }

    if (bResume && CanBeResumed())
    {
        if (m_pSourceInfo)
        {
            m_pSourceInfo->Register();
        }

        m_bResumePending = TRUE;
    }

    return bResume;
}

BOOL
HXSource::IsAnyAudioStream(void)
{
    IHXAudioPlayer* pAudioPlayer		= NULL;
    BOOL		bAtLeastOneAudioStream	= FALSE;

    if (!m_pPlayer)
    {
	return FALSE;
    }

    m_pPlayer->QueryInterface(IID_IHXAudioPlayer, (void **) &pAudioPlayer);
    UINT16 uNumAudioStreams = pAudioPlayer->GetAudioStreamCount();
    for (UINT16 i = 0; i < uNumAudioStreams && !bAtLeastOneAudioStream; i++)
    {
	IHXAudioStream* pAudioStream = pAudioPlayer->GetAudioStream(i);
	IHXValues* pHeader = pAudioStream->GetStreamInfo();
	pAudioStream->Release();
	if (!pHeader)
	{
	    continue;
	}
	
	if (IsAudioStreamFromThisSource(pHeader))
	{
	    bAtLeastOneAudioStream = TRUE;
	    pHeader->Release();
	    break;
	}

	pHeader->Release();
    }

    if (!bAtLeastOneAudioStream)
    {
        IHXValues* pHeader = NULL;
        int nStreamCnt = GetNumStreams();
        ULONG32     ulIsAudioStream = FALSE;

        for (int i=0; i<nStreamCnt && !bAtLeastOneAudioStream; i++)
        {
            GetStreamHeaderInfo(i, pHeader);

            pHeader->GetPropertyULONG32("IsAudioStream", ulIsAudioStream);
            if (ulIsAudioStream)
            {
                bAtLeastOneAudioStream = TRUE;
            }
	    HX_RELEASE(pHeader);
        }
    }

    pAudioPlayer->Release();

    return bAtLeastOneAudioStream;
}

void
HXSource::EventReady(CHXEvent* pEvent)
{
    if (m_pPlayer)
    {
	m_pPlayer->EventReady(this, pEvent);
    }
}

BOOL 
HXSource::IsAudioStreamFromThisSource(IHXValues* pAudioHeader)
{
    BOOL	bFound		= FALSE;

    CHXMapLongToObj::Iterator ndxStreamIterator = mStreamInfoTable->Begin();	
    for (; ndxStreamIterator != mStreamInfoTable->End(); ++ndxStreamIterator) 
    {
	STREAM_INFO* pStreamInfo = (STREAM_INFO*) (*ndxStreamIterator);
	if (pStreamInfo->m_pHeader == pAudioHeader)
	{
	    bFound = TRUE;
	    break;
	}
    }

    return bFound;
}

ULONG32
HXSource::GetAvailableMemory()
{
    ULONG32 memAvail = 1000000;

#if   defined (__MWERKS__)
//  XXX Hack! We can't call any of the Memory Manager calls at interrupt time so we will have 
//  to change this to ask our interrupt safe memory allocator how much memory is free
    memAvail = 1000000;
#elif defined(_WINCE) && !defined(_WIN32_WCE_EMULATION)
    STORE_INFORMATION stInfo;
    GetStoreInformation(&stInfo);
    memAvail = stInfo.dwFreeSize;
#elif (_WIN32)
    MEMORYSTATUS status;
    status.dwLength = sizeof(MEMORYSTATUS);
    GlobalMemoryStatus(&status);
    memAvail = status.dwAvailPageFile;
#elif defined( _WINDOWS)
    memAvail = GetFreeSpace(0);
#elif defined (_UNIX)
    // XXXX Need to get this to compile.
    memAvail = 1000000;
#endif

    return memAvail;
}

void
HXSource::ReportError(HX_RESULT theErr)
{
    if (m_pPlayer)
    {
	m_pPlayer->ReportError(this, theErr);
    }
}


HX_RESULT		
HXSource::AdjustClipTime(void)
{
    HX_RESULT theErr = HXR_OK;
    UINT32  ulOriginalDuration = m_ulDuration;        
    UINT32  ulTrackEndTime = 0;
    CHXMapLongToObj::Iterator i;
    BOOL    bCustomEndTimeSet = FALSE;

    if (m_bPartOfPrefetchGroup)
    {
	if (m_ulDelay)
	{
	    m_ulPrefetchDelay = m_ulDelay;
	    m_bDelayed = TRUE;
	    AdjustClipBandwidthStats(FALSE);
	    DoPause();
	}

	m_ulEndTime = 0;
	m_bCustomEndTime = FALSE;
	goto cleanup;
    }
	
    /* For a live stream, the only qualifiers that make sense
     * are Duration and Delay. All other qualifiers should be made 0
     */
    if (mLiveStream)
    {
	m_ulStartTime = 0;

	if (!m_bRestrictedLiveStream)
	{
	    m_ulEndTime = 0;
	}
    }

    for (i = mStreamInfoTable->Begin(); i != mStreamInfoTable->End(); ++i) 
    {    
	STREAM_INFO*	pStreamInfo = (STREAM_INFO*) (*i);

	ulTrackEndTime = 0;

	HX_RESULT hrTemp = pStreamInfo->m_pHeader->GetPropertyULONG32("EndTime", ulTrackEndTime);

	if (HXR_OK == hrTemp && !m_bCustomEndTime)
	{	
	    if (m_ulEndTime < ulTrackEndTime)
	    {
		m_ulEndTime = ulTrackEndTime;
	    }
	    pStreamInfo->m_bCustomEndTime = TRUE;
	}
	else if (m_bCustomEndTime)
	{
	    ulTrackEndTime = m_ulEndTime;
	    pStreamInfo->m_bCustomEndTime = TRUE;
	}

	if (ulTrackEndTime > 0 && !mLiveStream)
	{
	    pStreamInfo->m_pHeader->SetPropertyULONG32("TrackEndTime",
							ulTrackEndTime);
	    bCustomEndTimeSet = TRUE;
	}
    }
    
    // if max. duration is set on this source
    if (m_pSourceInfo && 
	m_pSourceInfo->m_ulMaxDuration)
    {
	if (m_ulRestrictedDuration)
	{	    
	    if (m_ulRestrictedDuration > m_pSourceInfo->m_ulMaxDuration)
	    {
		m_ulRestrictedDuration = m_pSourceInfo->m_ulMaxDuration;
	    }
	}
	else if (m_ulDuration)
	{
	    if (m_ulDuration > m_pSourceInfo->m_ulMaxDuration)
	    {
		m_ulRestrictedDuration = m_pSourceInfo->m_ulMaxDuration;
	    }
	}
    }

    // By default, we always set the end time to be the duration of the clip,
    // but do not set end time if it was manually specified.
    if (!bCustomEndTimeSet && !mLiveStream)
    {
	m_ulEndTime = m_ulDuration;
    }

    /* Check if there is any end time */
    if ((m_ulEndTime < m_ulDuration) || m_bRestrictedLiveStream)
    {
	m_ulDuration = m_ulEndTime;
    }
    
    /* Is "Delay" specified too? */
    if (m_ulDelay > 0) 
    {
	/* Increase duration of this clip */
	m_ulDuration += m_ulDelay;
	UINT32 ulStartTime = 0;

	if (m_ulDelay > m_ulPreRollInMs + NETWORK_FUDGE_FACTOR)
	{
	    ulStartTime = m_ulDelay - (m_ulPreRollInMs + NETWORK_FUDGE_FACTOR);
	}

	// no need to pause delayed persistent component since Pause() will be called
	// on actual tracks' AdjustClipTime() if necessary
	if (m_pSourceInfo && !m_pSourceInfo->m_bIsPersistentSource)
	{
	    m_bDelayed = TRUE;
	    AdjustClipBandwidthStats(FALSE);
	    DoPause();
	}
    }

    if (m_ulStartTime > 0) /* reduce duration by start time amount */
    {
    	if (m_ulDuration > m_ulStartTime)
    	{
	    m_ulDuration -= m_ulStartTime;
	}
	else
	{
	    /* This is bad case. We consider it invalid */
	    m_ulDuration = 0;
	}
    }

    /* We now allow to increase the default duration of the clip */
    if (m_ulRestrictedDuration > 0)
    {
	m_ulDuration = m_ulRestrictedDuration + m_ulDelay;

	if (mLiveStream && !m_bRestrictedLiveStream)
	{
	    m_bRestrictedLiveStream = TRUE;
	    m_ulEndTime = m_ulRestrictedDuration + m_ulStartTime;
	}
	
	if (m_ulEndTime > m_ulRestrictedDuration + m_ulStartTime)
	{
	    m_ulEndTime = m_ulRestrictedDuration + m_ulStartTime;
	}
    }

    // orig duration is active duration for this source -- time for which
    // this source lasts.
    if (m_ulDuration > m_ulDelay)
    {
	m_ulOriginalDuration = m_ulDuration - m_ulDelay;
    }
    else
    {
	m_ulOriginalDuration = 0;
    }

    if (m_pURL					    &&
	rtspProtocol == m_pURL->GetProtocol()	    &&
	m_llLastExpectedPacketTime != m_ulEndTime   &&
	!m_bFirstResume)
    {
	m_bRTSPRuleFlagWorkAround = TRUE;
    }
    /* If we receive a packet after this stream, we consider the stream to be done */
    m_llLastExpectedPacketTime  = CAST_TO_INT64 m_ulEndTime;

    // Seek to the starting position only if the source
    // has not been resumed yet
    if (m_ulStartTime > 0 && m_bFirstResume) 
    {
	/* We will add m_ulStartTime in DoSeek() call*/
    	theErr = DoSeek(0);
    }

    /* Update stream durations if required */
    for (i = mStreamInfoTable->Begin(); i != mStreamInfoTable->End(); ++i) 
    {    
	STREAM_INFO* pStreamInfo = (STREAM_INFO*) (*i);
	if (m_ulStartTime > 0)
	{
	    pStreamInfo->m_pHeader->SetPropertyULONG32("TrackStartTime",
					    m_ulStartTime);
	}

	
	if (m_ulEndTime > 0 && 
	    !mLiveStream    &&
	    HXR_OK != pStreamInfo->m_pHeader->GetPropertyULONG32("TrackEndTime",
								  ulTrackEndTime))
	{
	    pStreamInfo->m_pHeader->SetPropertyULONG32("TrackEndTime",
							m_ulEndTime);
	}

	if (ulOriginalDuration != m_ulDuration)
	{
	    pStreamInfo->m_ulDuration = m_ulDuration;
	    pStreamInfo->m_pHeader->SetPropertyULONG32("Duration", 
					pStreamInfo->m_ulDuration);
	}

	if (m_ulDelay > 0)
	{
	    pStreamInfo->m_pHeader->SetPropertyULONG32("Delay", m_ulDelay);
	}
    }

//{FILE* f1 = ::fopen("d:\\temp\\url.txt", "a+"); ::fprintf(f1, "%p %s %lu %lu\n", this, m_pszURL, m_ulDelay, m_ulDuration);::fclose(f1);}

cleanup:

    m_bClipTimeAdjusted = TRUE;

    return theErr;
}

void
HXSource::GenerateFakeLostPacket(CHXEvent*& theEvent)
{
    IHXPacket* pPacket = theEvent->GetPacket();
    CHXPacket* pLostPacket = new CHXPacket;
    pLostPacket->AddRef();
    pLostPacket->Set(0, 0, pPacket->GetStreamNumber(), 0, 0);
    pLostPacket->SetAsLost();

    /* Create a new event with lost packet */
    CHXEvent* pEvent = new CHXEvent(pLostPacket);
    pEvent->SetTimeStartPos(theEvent->GetTimeStartPos());
    pEvent->SetTimeOffset(theEvent->GetTimeOffset());
    pEvent->SetPreSeekEvent(theEvent->IsPreSeekEvent());
    pLostPacket->Release();

    delete theEvent;
    theEvent = pEvent;
}

char*
HXSource::GetAltURL(BOOL& bDefault)
{
#if defined(HELIX_FEATURE_ALT_URL)
    char*   pAltURL = NULL;

    if (!m_pURL || m_bInitialized)
    {
	goto cleanup;
    }

    pAltURL = m_pURL->GetAltURL(bDefault);

cleanup:

    return pAltURL;
#else
    return NULL;
#endif /* HELIX_FEATURE_ALT_URL */
}

HX_RESULT
HXSource::SetRequest(const CHXURL* pURL, BOOL bAltURL)
{
    HX_RESULT		hr = HXR_OK;
    IHXValues*		pValues = NULL;
    IHXValues*		pValuesInRequest = NULL;
    IHXGroup*		pGroup = NULL;
    IHXGroup2*		pGroup2 = NULL;
    IHXGroupManager*	pGroupManager = NULL;

    HX_RELEASE(m_pRequest);

    if (m_pPlayer)
    {
	m_pPlayer->GetActiveRequest(m_pRequest);

	if (m_pRequest)
	{
	    m_pPlayer->ResetActiveRequest();
	}
    }

#if defined(HELIX_FEATURE_ADVANCEDGROUPMGR)
    if (m_pPlayer && m_pSourceInfo)
    {
	if (HXR_OK == m_pPlayer->QueryInterface(IID_IHXGroupManager, (void**)&pGroupManager))
	{
	    if (HXR_OK == pGroupManager->GetGroup(m_pSourceInfo->m_uGroupID, pGroup))
	    {
		if (HXR_OK == pGroup->QueryInterface(IID_IHXGroup2, (void**)&pGroup2))
		{
		    pGroup2->GetTrack2(m_pSourceInfo->m_uTrackID, pValues, pValuesInRequest);
		    UINT32 ulValue = 0;
		    char   szDuration[128] = {0}; /* Flawfinder: ignore */
		    IHXBuffer* pBuffer = NULL;
		    if (pValues && HXR_OK == pValues->GetPropertyULONG32("Duration", ulValue))
		    {
			if (!pValuesInRequest)
			{
			    pValuesInRequest = new CHXHeader();
			    if( pValuesInRequest )
			    {
			        pValuesInRequest->AddRef();
			    }
			    else
			    {
			        hr = HXR_OUTOFMEMORY;
			    }
			}
			if (pValuesInRequest)
			{
			    SafeSprintf (szDuration, 128, "%lu", ulValue); /* Flawfinder: ignore */
			    pBuffer = new CHXBuffer();
			    pBuffer->AddRef();
    			    pBuffer->Set((UCHAR*)szDuration, strlen(szDuration) + 1);
			    pValuesInRequest->SetPropertyCString("Duration", pBuffer);
			    HX_RELEASE(pBuffer);
			}
		    }
		}
		HX_RELEASE(pGroup2);
	    }
	    HX_RELEASE(pGroup);
	}
	HX_RELEASE(pGroupManager);
    }
#endif /* HELIX_FEATURE_ADVANCEDGROUPMGR */

    IHXRegistry* pRegistry = NULL;   
    m_pEngine->QueryInterface(IID_IHXRegistry, (void**)&pRegistry);

    hr = ::SetRequest(pURL->GetEscapedURL(), bAltURL, m_pPreferences, pRegistry, pValuesInRequest, m_pRequest);

    HX_RELEASE(pValues);
    HX_RELEASE(pValuesInRequest);
    HX_RELEASE(pRegistry);

#if defined(HELIX_FEATURE_RECORDCONTROL)
    if(hr == HXR_OK && pURL->GetProtocol() != fileProtocol)
    {
	if(!m_pRecordControl)
	{
	    m_pRecordControl = new HXRecordControl((IHXPlayer*)m_pPlayer, (IHXStreamSource*)this);
	    if(m_pRecordControl)
	    {
		m_pRecordControl->AddRef();
		if(m_pRecordControl->IsValid())
                    m_bPlayFromRecordControl = m_pRecordControl->CanGetPackets();
                else
		    HX_RELEASE(m_pRecordControl);
	    }
	}

	if(m_pRecordControl)
	{
	    m_pRecordControl->SetSource((IHXStreamSource*)this);
	}
    }
#endif /* HELIX_FEATURE_RECORDCONTROL */

    return hr;
}

void
HXSource::UpdateDuration(UINT32 ulDuration)
{
    CHXSimpleList* pRepeatList = m_pSourceInfo->GetRepeatList();

    // ulDuration excludes the delay time
    if (pRepeatList && 
	ulDuration >= (m_ulDuration - m_ulDelay))
    {
	m_pSourceInfo->m_ulTotalTrackDuration = ulDuration + m_ulDelay;
	if (m_pSourceInfo->m_pPeerSourceInfo)
	{
	    m_pSourceInfo->m_pPeerSourceInfo->m_ulTotalTrackDuration = m_pSourceInfo->m_ulTotalTrackDuration;
	}
    }
    else
    {
	m_ulOriginalDuration = m_ulRestrictedDuration = ulDuration;
	AdjustClipTime();
	m_pSourceInfo->UpdateDuration(m_ulDuration);
    }
}

void
HXSource::UpdateDelay(UINT32 ulDelay)
{
    m_ulDelay = ulDelay;
    AdjustClipTime();
    m_pSourceInfo->UpdateDelay(m_ulDelay);
}

void
HXSource::InitialBufferingDone(void)
{
    m_bInitialBuffering = FALSE;

    // resume if we satisfy the initial preroll AND we have issued
    // rebuffer
    // note: IsRebufferRequired() should return FALSE!!
    if (m_bRebufferingRequired)
    {
	m_bRebufferingRequired = IsRebufferRequired();
	HX_ASSERT(!m_bRebufferingRequired);
    }

    return;
}

void
HXSource::DoRebuffer(void)
{
    m_bRebufferingRequired  = TRUE;

    if (m_pPlayer)
    {
	m_pPlayer->InternalPause();	    
        ReBuffer();
    }

    return;
}

BOOL		
HXSource::IsRebufferRequired(void)
{
    BOOL	    bResult = FALSE;
    STREAM_INFO*    pStreamInfo = NULL;

    // Check if all streams are doing OK
    for (CHXMapLongToObj::Iterator ndxStrm = mStreamInfoTable->Begin();
	 ndxStrm != mStreamInfoTable->End(); ++ndxStrm) 
    {    
	pStreamInfo = (STREAM_INFO*) (*ndxStrm);
	
	if (pStreamInfo->m_unNeeded > pStreamInfo->m_unAvailable)
	{
	    bResult = TRUE;
	    break;
	}
    }

    return bResult;
}

BOOL
HXSource::IsRebufferDone(void)
{
    BOOL    bResult = TRUE;

    if (m_pSourceInfo)
    {
	bResult = m_pSourceInfo->IsRebufferDone();
    }

    return bResult;
}

void		
HXSource::ScheduleProcessCallback()
{
    if (m_pSourceInfo)
    {
        m_pSourceInfo->ScheduleProcessCallback();
    }
}

#if defined(HELIX_FEATURE_HYPER_NAVIGATE)
/************************************************************************
 *	Method:
 *	    IHXHyperNavigate::GoToURL
 *	Purpose:
 *	    Acts as a proxy for actual hypernavigate interface.
 *	    Is used to convert any relative URLs to absolute URLs
 */
STDMETHODIMP 
HXSource::GoToURL(const char* pURL, const char* pTarget)
{
    return Execute(pURL, pTarget, NULL, NULL, NULL);
}

/************************************************************************
 *	Method:
 *	    IHXHyperNavigate2::Execute
 *	Purpose:
 *	    
 *	Parameters:
 *      pURL:	    URL (absolute or relative)
 *	    pTargetInstance:	
 *	    pTargetApplication: 
 *	    pTargetRegion:
 *	    pParams:
 */
STDMETHODIMP 
HXSource::Execute(const char* pURL,
		   const char* pTargetInstance,
		   const char* pTargetApplication,
		   const char* pTargetRegion,
		   IHXValues* pParams)
{
    HX_RESULT theErr = HXR_OK;
    CHXString newURL = pURL;

//    pURL = "rogers.rt";

//    if (pTargetInstance && 
//	stricmp(pTargetInstance, "_player") == 0 &&
    if (ShouldConvert(pTargetInstance) && 
	pURL  &&
	strnicmp(pURL, URL_COMMAND, sizeof(URL_COMMAND) - 1) != 0 )
    {
	CHXURL urlObj(pURL);
	IHXValues* pHeader = urlObj.GetProperties();
	IHXBuffer* pBuffer = NULL;

	if(pHeader && 
	   m_pszURL &&
	   HXR_OK != pHeader->GetPropertyBuffer(PROPERTY_SCHEME, pBuffer))
	{
	    // relative URL
	    // if it starts with '/', make it relative to the root of 
	    // the URL prefix

	    CHXString urlPrefix, urlRoot;
	    char* pURLFragment = NULL;
	    theErr = CHXURL::GeneratePrefixRootFragment(m_pszURL, urlPrefix, urlRoot, pURLFragment);
	    HX_VECTOR_DELETE(pURLFragment);

	    if (!theErr)
	    {
		if(*pURL == '/')
		{
		    newURL = urlRoot + pURL;
		}
		else
		{
		    newURL = urlPrefix + pURL;
		}	
	    }
	}
	HX_RELEASE(pBuffer);
	HX_RELEASE(pHeader);
    }
    
    AddRef();

    HX_ASSERT(m_pPlayer && m_pPlayer->m_pHyperNavigate);
    if (m_pPlayer && m_pPlayer->m_pHyperNavigate)
    {
	theErr = m_pPlayer->m_pHyperNavigate->ExecuteWithContext(newURL, 
			pTargetInstance, pTargetApplication, pTargetRegion, 
			pParams, (IUnknown*) (IHXStreamSource*) this);
    }

    Release();

    return theErr;
}
#endif /* defined(HELIX_FEATURE_HYPER_NAVIGATE) */

STDMETHODIMP
HXSource::GetTotalBuffering(UINT16  uStreamNumber,
			    REF(INT64)  llLowestTimestamp, 
			    REF(INT64)  llHighestTimestamp,
			    REF(UINT32) ulNumBytes,
			    REF(BOOL)   bDone)
{
    HX_RESULT res = HXR_NO_DATA;
    
    llLowestTimestamp = 0;
    llHighestTimestamp = 0;
    ulNumBytes = 0;
    bDone = FALSE;

    STREAM_INFO* pStreamInfo;
    if (mStreamInfoTable->Lookup((LONG32) uStreamNumber, (void*& )pStreamInfo))
    {
	HXBufferingState& bufState = pStreamInfo->BufferingState();
	BOOL   bUseTransportStats = FALSE;

	INT64  llTransportLowTS = 0;
	INT64  llTransportHighTS = 0;
	UINT32 ulTransportBytes = 0;
	BOOL   bTransportDone = FALSE;

	if (!IsLocalSource() &&
	    (HXR_OK == GetCurrentBuffering(uStreamNumber,
					   llTransportLowTS,
					   llTransportHighTS,
					   ulTransportBytes,
					   bTransportDone)))
	{
	    bufState.UpdateTransportStats(llTransportLowTS,
					  llTransportHighTS,
					  ulTransportBytes,
					  bTransportDone);

	    bUseTransportStats = TRUE;

	    // Update bDone with what the transport says.
	    bDone = bTransportDone;
	}

	res = bufState.GetBufferingStats(llLowestTimestamp,
					 llHighestTimestamp,
					 ulNumBytes,
					 bUseTransportStats);
    }


    return res;
}

/* 
 * All relative URLs are converted to absolute URLs unless the 
 * original request (ram/smil) passed in OpenRequest/OpenURL()
 * is a mem: URL AND the target is not _player.
 *
 * This fixes relative URLs being hurled to the browser using events
 * come from the same location as the .ram file. (Broadcase usage case)
 * PR 31352
 *
 * This also fixes content on CD-ROMs where relative URLs being hurled 
 * to the browser using events come from the same location as 
 * the .rm file in which they are merged. 
 * PR 23489
 */
BOOL HXSource::ShouldConvert(const char* pTargetInstance)
{
    if (pTargetInstance && 
	stricmp(pTargetInstance, "_player") == 0)
    {
	return TRUE;
    }
    
    const char* pPlayerURL = NULL;
    IHXRequest* pPlayerRequest = NULL;
    if (m_pPlayer)
    {
	m_pPlayer->GetRequest(pPlayerRequest);
	if (pPlayerRequest)
	{
	    pPlayerRequest->GetURL(pPlayerURL);
	}
    }
    HX_RELEASE(pPlayerRequest);

    if (pPlayerURL && ::strncasecmp(pPlayerURL, "mem:", 4) == 0)
    {
	return FALSE;	
    }

    return TRUE;
}

void
HXSource::MergeUpgradeRequest(BOOL bAddDefault /*= FALSE*/, char* pUpgradeString /* = NULL*/)
{
#if defined(HELIX_FEATURE_AUTOUPGRADE)
    if (m_pPlayer && 
	bAddDefault && 
	(!m_pUpgradeCollection || m_pUpgradeCollection->GetCount() == 0))
    {
	if (!m_pUpgradeCollection)
	{
	    m_pUpgradeCollection = new HXUpgradeCollection;
	}

	if (!pUpgradeString)
	{
	    pUpgradeString = "Missing Component";
	}

	IHXBuffer* pPluginID = (IHXBuffer*) new CHXBuffer;
	pPluginID->AddRef();
	pPluginID->Set((const UINT8*)pUpgradeString, strlen(pUpgradeString) + 1);
	m_pUpgradeCollection->Add(eUT_Required, pPluginID, 0, 0);
	pPluginID->Release();
    }

    if (m_pPlayer && m_pUpgradeCollection && m_pUpgradeCollection->GetCount() > 0)
    {
	UINT32 ulCount = m_pUpgradeCollection->GetCount();
	IHXUpgradeCollection* pPlayerUpgrade;
	m_pPlayer->QueryInterface(IID_IHXUpgradeCollection, (void**) &pPlayerUpgrade);
	for (UINT32 i = 0; i < ulCount; i++)
	{
	    HXUpgradeType upgradeType;
	    IHXBuffer* pPluginId = (IHXBuffer*) new CHXBuffer;
	    UINT32	majorVersion;
	    UINT32	minorVersion;
	    pPluginId->AddRef(); 
	    // GetAt is a non-COM like API. It expects pPluginID to be allocated by the user
	    // and does not perform an addref either!
	    m_pUpgradeCollection->GetAt(i, upgradeType, pPluginId, majorVersion, minorVersion);
	    pPlayerUpgrade->Add(upgradeType, pPluginId, majorVersion, minorVersion);
	    pPluginId->Release();
	}

	pPlayerUpgrade->Release();
	m_pUpgradeCollection->RemoveAll();
    }
#endif /* HELIX_FEATURE_AUTOUPGRADE */
}

void
HXSource::ClearUpgradeRequest()
{
#if defined(HELIX_FEATURE_AUTOUPGRADE)
    if (m_pUpgradeCollection)
    {
	m_pUpgradeCollection->RemoveAll();
    }
#endif /* HELIX_FEATURE_AUTOUPGRADE */
}

void
HXSource::EnterPrefetch(PrefetchType prefetchType, UINT32 ulPrefetchValue)
{
    m_bPrefetch = TRUE;
    m_prefetchType = prefetchType;
    m_ulPrefetchValue = ulPrefetchValue;

    return;
}

void
HXSource::LeavePrefetch(void)
{
    m_bPrefetch = FALSE;

    // send prefetch notification so that SMIL
    // renderer can resolve the duration on this prefetch track
    if (m_pSourceInfo)
    {
	m_pPlayer->PrefetchTrackDone(m_pSourceInfo->m_uGroupID, 
		    		     m_pSourceInfo->m_uTrackID, 
				     HXR_OK);
    }

    return;
}

void		
HXSource::SetSoundLevel(UINT16 uSoundLevel, BOOL bReflushAudioDevice)
{
#if defined(HELIX_FEATURE_SMIL_SOUNDLEVEL)
    CHXAudioPlayer* pAudioPlayer = NULL;
    CHXAudioStream* pCHXAudioStream = NULL;
    CHXSimpleList*  pAudioStreamList = NULL;

    if (!m_pPlayer)
    {
	goto cleanup;
    }

    pAudioPlayer = m_pPlayer->GetAudioPlayer();
    if (!pAudioPlayer)
    {
	goto cleanup;
    }

    pAudioPlayer->AddRef();

    if (HXR_OK == CollectAudioStreams(pAudioStreamList) && pAudioStreamList)
    {
	pAudioPlayer->SetSoundLevel(pAudioStreamList, uSoundLevel, bReflushAudioDevice);

	ReleaseAudioStreams(pAudioStreamList);
	HX_DELETE(pAudioStreamList);
    }

    HX_RELEASE(pAudioPlayer);

cleanup:
#endif /* HELIX_FEATURE_SMIL_SOUNDLEVEL */

    return;
}

void		
HXSource::SetAudioDeviceReflushHint(void)
{
#if defined(HELIX_FEATURE_SOUNDLEVEL)
    CHXAudioPlayer* pAudioPlayer = NULL;
    CHXSimpleList*  pAudioStreamList = NULL;

    if (!m_pPlayer)
    {
	goto cleanup;
    }

    pAudioPlayer = m_pPlayer->GetAudioPlayer();
    if (!pAudioPlayer)
    {
	goto cleanup;
    }

    pAudioPlayer->AddRef();

    if (HXR_OK == CollectAudioStreams(pAudioStreamList) && pAudioStreamList)
    {
	pAudioPlayer->ManageAudioStreams(pAudioStreamList, CHXAudioPlayer::STR_SETHINT);

	ReleaseAudioStreams(pAudioStreamList);
	HX_DELETE(pAudioStreamList);
    }

    HX_RELEASE(pAudioPlayer);

cleanup:
#endif /* HELIX_FEATURE_SOUNDLEVEL */

    return;
}

void
HXSource::LeaveFastStart(TurboPlayOffReason leftReason)
{
    m_turboPlayStats.tpOffReason = leftReason;
    m_bFastStart = FALSE;
}

void
HXSource::DeleteAllEvents()
{
    if (m_PacketBufferList.GetCount() > 0)
    {
	LISTPOSITION pos = m_PacketBufferList.GetHeadPosition();
	while (pos != NULL)
	{
	    CHXEvent* pTempEvent  = (CHXEvent*) m_PacketBufferList.GetNext(pos);
	    delete pTempEvent;
	}

	m_PacketBufferList.RemoveAll();
    }
}

void	
HXSource::SetMinimumPreroll(ULONG32 ulMinimumAudioPreroll, ULONG32 ulMinimumStartingPreroll)
{
    UINT32 ulTotalMinimumPreroll = 0;

    if (m_pPlayer)
    {
	// get the user-set minimum preroll
	m_pPlayer->GetMinimumPreroll(ulTotalMinimumPreroll);
    }

    if (IsAnyAudioStream())
    {
	m_ulPreRollInMs	+= ulMinimumAudioPreroll;
    }

    if (m_ulPreRollInMs < ulTotalMinimumPreroll)
    {
	m_ulPreRollInMs = ulTotalMinimumPreroll;
    }

    DEBUG_OUT(m_pPlayer, DOL_TRANSPORT, (s, "(%p)Preroll: %lu MinPreroll %lu MinAudioPreroll %lu", this, m_ulPreRollInMs, ulTotalMinimumPreroll, ulMinimumStartingPreroll));

    m_pBufferManager->SetMinimumPreroll(IsLocalSource() | m_bPerfectPlay,
					ulTotalMinimumPreroll,
					ulMinimumStartingPreroll);
}

HX_RESULT		
HXSource::SendHeaderToRecordControl(BOOL bFileHeader, IHXValues* pHeader)
{
#if defined(HELIX_FEATURE_RECORDCONTROL)
    HX_RESULT nResult = HXR_OK;

    if(m_pRecordControl && pHeader)
    {
	if(bFileHeader)
	    nResult = m_pRecordControl->OnFileHeader(pHeader);
	else
	    nResult = m_pRecordControl->OnStreamHeader(pHeader);

	if(nResult != HXR_OK)
	{
	    m_bPlayFromRecordControl = FALSE;
	    if(nResult != HXR_RECORD)
		HX_RELEASE(m_pRecordControl);
	}
    }
    return nResult;
#else
    return HXR_NOTIMPL;
#endif /* HELIX_FEATURE_RECORDCONTROL */
}

void
HXSource::ProcessFileHeader(void)
{
    UINT32      bNonSeekAble = 0;
    IHXBuffer*  pTitle = NULL;
    IHXBuffer*  pAuthor = NULL;
    IHXBuffer*  pCopyright = NULL;
    IHXBuffer*  pAbstract = NULL;
    IHXBuffer*  pDescription = NULL;
    IHXBuffer*  pKeywords = NULL;
    IHXValues*  pValues = NULL;

    if (m_pURL)
    {
	pValues = m_pURL->GetOptions();
    }

    // retrieve the TAC from the URL
    if (pValues)
    {
	pValues->GetPropertyBuffer("Title", pTitle);
        pValues->GetPropertyBuffer("Author", pAuthor);
        pValues->GetPropertyBuffer("Copyright", pCopyright);
        pValues->GetPropertyBuffer("Abstract", pAbstract);
        pValues->GetPropertyBuffer("Description", pDescription);
        pValues->GetPropertyBuffer("Keywords", pKeywords);

//#define LOSS_HACK
#ifdef LOSS_HACK
	UINT32 ulLoss = 0;
	if (HXR_OK == pValues->GetPropertyULONG32("Loss", ulLoss))
	{
	    m_ulLossHack = ulLoss;
	    /* Initialize random number generator */
	    ::srand((unsigned int) HX_GET_TICKCOUNT());
	}
#endif /*LOSS_HACK*/
    }

    if (m_pFileHeader)
    {
        if (!pTitle)	    m_pFileHeader->GetPropertyBuffer("Title", pTitle);
        if (!pAuthor)	    m_pFileHeader->GetPropertyBuffer("Author", pAuthor);
        if (!pCopyright)    m_pFileHeader->GetPropertyBuffer("Copyright", pCopyright);
        if (!pDescription)  m_pFileHeader->GetPropertyCString("Description", pDescription);
        if (!pAbstract)	    m_pFileHeader->GetPropertyCString("Abstract", pAbstract);
        if (!pKeywords)	    m_pFileHeader->GetPropertyCString("Keywords", pKeywords);

        m_pFileHeader->GetPropertyULONG32("NonSeekable", bNonSeekAble);
	m_bNonSeekable = bNonSeekAble ? TRUE : FALSE;
        
        m_pFileHeader->GetPropertyULONG32("StreamCount",m_ulStreamHeadersExpected);
            
        HX_ASSERT(mStreamInfoTable->IsEmpty() == TRUE);
        if (mStreamInfoTable->IsEmpty() && m_ulStreamHeadersExpected > 0 &&
            m_ulStreamHeadersExpected < mStreamInfoTable->GetHashTableSize())
        {
            mStreamInfoTable->InitHashTable(m_ulStreamHeadersExpected);
        }
    }

#if defined(HELIX_FEATURE_STATS) && defined(HELIX_FEATURE_REGISTRY)
    if (m_pStats)
    {
        if (pTitle && m_pStats->m_pTitle)
        {
	    m_pStats->m_pTitle->SetStr((char*)(pTitle->GetBuffer()));
        }

        if (pAuthor && m_pStats->m_pAuthor)
        {
	    m_pStats->m_pAuthor->SetStr((char*)(pAuthor->GetBuffer()));
        }

        if (pCopyright && m_pStats->m_pCopyright)
        {
	    m_pStats->m_pCopyright->SetStr((char*)(pCopyright->GetBuffer()));
        }

        if (pAbstract && m_pStats->m_pAbstract)
        {
	    m_pStats->m_pAbstract->SetStr((char*)(pAbstract->GetBuffer()));
        }
    
        if (pDescription && m_pStats->m_pDescription)
        {
            m_pStats->m_pDescription->SetStr((char*)(pDescription->GetBuffer()));
        }

        if (pKeywords && m_pStats->m_pKeywords)
        {
	    m_pStats->m_pKeywords->SetStr((char*)(pKeywords->GetBuffer()));
        }
    }

    CopyMetaDataToRegistry(m_pFileHeader);
#endif /* HELIX_FEATURE_STATS && HELIX_FEATURE_REGISTRY */

    m_bReceivedHeader = TRUE;

    HX_RELEASE(pTitle);
    HX_RELEASE(pAuthor);
    HX_RELEASE(pCopyright);
    HX_RELEASE(pAbstract);
    HX_RELEASE(pDescription);
    HX_RELEASE(pKeywords);
    HX_RELEASE(pValues);

    return;
}

static BOOL GetHeaderBOOL(IHXValues* pHeader, const char* pKey)
{
    BOOL bRet = FALSE;

    ULONG32 ulTemp = 0;
    if ((HXR_OK == pHeader->GetPropertyULONG32(pKey, ulTemp)) && ulTemp)
    {
	bRet = TRUE;
    }

    return bRet;
}

HX_RESULT
HXSource::ProcessStreamHeaders(IHXValues* pHeader, STREAM_INFO*& pStreamInfo)
{
    HX_RESULT       rc = HXR_OK;
    UINT32          ulStreamNumber = 0;
    UINT32          ulAvgBitRate = 0;
    UINT32          ulAvgPacketSize = 0;
    UINT32          ulPreroll = 0;
    ULONG32         ulPredata = 0;
    UINT32          ulDuration = 0;
    UINT32          ulTemp = 0;
    void*           lTmp = NULL;

    pStreamInfo = NULL;

    if (!pHeader)
    {
        rc = HX_INVALID_HEADER;
        goto cleanup;
    }

    // Use file header for default duration
    if (m_pFileHeader)
    {
        m_pFileHeader->GetPropertyULONG32("Duration", ulDuration);
        if (!m_ulDuration && ulDuration)
        {
            m_ulDuration = ulDuration;
            ulDuration = 0;
        }
    }

    pHeader->GetPropertyULONG32("StreamNumber",	    ulStreamNumber);
    pHeader->GetPropertyULONG32("AvgBitRate",	    ulAvgBitRate);
    pHeader->GetPropertyULONG32("AvgPacketSize",    ulAvgPacketSize);
    pHeader->GetPropertyULONG32("Preroll",	    ulPreroll);
    pHeader->GetPropertyULONG32("Predata",          ulPredata);
    pHeader->GetPropertyULONG32("Duration",	    ulDuration);

    if (mStreamInfoTable->Lookup((LONG32) ulStreamNumber, (void *&) lTmp))
    {
	// a header with this stream number already exists..
        rc = HX_INVALID_HEADER;
        goto cleanup;
    }

    // max preroll
    if (m_ulPreRollInMs < ulPreroll)
    {
	m_ulPreRollInMs = ulPreroll;
    }

    m_ulAvgBandwidth   += ulAvgBitRate;

    // max duration...
    if (m_ulDuration < ulDuration)
    {
	m_ulDuration = ulDuration;
    }

    pStreamInfo = new STREAM_INFO;
    if (!pStreamInfo)
    {
	rc = HXR_OUTOFMEMORY;
        goto cleanup;
    }

    pStreamInfo->m_pHeader                  = pHeader;
    pStreamInfo->m_pHeader->AddRef();

    pStreamInfo->m_bCustomEndTime           = FALSE;
    pStreamInfo->m_bSrcStreamDone           = FALSE;
    pStreamInfo->m_bSrcStreamFillingDone    = FALSE;
    pStreamInfo->m_bPacketRequested         = FALSE;
    
    pStreamInfo->m_uStreamNumber = (UINT16) ulStreamNumber;

    pStreamInfo->m_ulDuration = ulDuration;

    pStreamInfo->BufferingState().OnStreamHeader(
	ulPreroll,
	ulPredata,
	GetHeaderBOOL(pHeader, "PreDataAtStart"),
	GetHeaderBOOL(pHeader, "PreDataAfterSeek"),
	GetHeaderBOOL(pHeader, "PrerollAtStart"),
	GetHeaderBOOL(pHeader, "PrerollAfterSeek"),
	ulAvgBitRate);


    mStreamInfoTable->SetAt(ulStreamNumber, (void *) pStreamInfo);

cleanup:

    return rc;
}

HX_RESULT HXSource::OnTimeSync(ULONG32 ulCurrentTime)
{
    HX_RESULT res = HXR_OK;

    for (CHXMapLongToObj::Iterator i = mStreamInfoTable->Begin();
         i != mStreamInfoTable->End(); ++i) 
    {    
	STREAM_INFO* pStreamInfo = (STREAM_INFO*) (*i);

	pStreamInfo->BufferingState().OnTimeSync(ulCurrentTime);
    }

    return res;
}
