/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: srcinfo.cpp,v 1.26.2.3 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"
#include "hxresult.h"
#include "smiltype.h"
#include "hxcomm.h"		// IHXRegistryID
#include "hxengin.h"
#include "hxcore.h"
#include "hxupgrd.h"
#include "hxrendr.h"
#include "hxasm.h"
#include "hxsmbw.h"
#include "hxgroup.h"
#include "hxausvc.h"
#include "hxslist.h"
#include "hxmap.h"
#include "chxpckts.h"
#include "chxeven.h"
#include "chxelst.h"
#include "strminfo.h"

#include "timeval.h"
#include "hxbsrc.h"
#include "hxsrc.h"
#include "hxstrm.h"
#include "hxsmstr.h"
#include "hxaudply.h"
#include "basgroup.h"
#include "advgroup.h"
#include "hxthread.h"
#include "hxtick.h"
#include "hxstrutl.h"

#include "srcinfo.h"
#include "hxplay.h"
#include "hxcleng.h"
#include "plghand2.h"

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

#define TIME_SYNC_FUDGE_FACTOR      10
#define GOTOEXITONERROR(theErr, label)      if ((theErr) != 0) goto label

SourceInfo::SourceInfo(HXPlayer* pPlayer)
{
    m_pRendererMap = new CHXMapLongToObj;
    
    m_pPlayer = pPlayer;
    m_pPlayer->AddRef();
    m_pSource		    = NULL;
    m_bDone		    = FALSE;
    m_bStopped		    = FALSE;
    m_bInitialized	    = FALSE;
    m_pStatus		    = NULL;
    m_bAllPacketsReceived   = FALSE;
    m_bActive		    = TRUE;
    m_bIsPersistentSource   = FALSE;
    m_bIsRegisterSourceDone = FALSE;
    m_uTrackID		    = 0;
    m_uGroupID		    = 0;
    m_bToBeResumed	    = TRUE;
    m_bAreStreamsSetup	    = FALSE;
    m_bTrackStartedToBeSent = TRUE;
    m_bTrackStoppedToBeSent = TRUE;
    m_bPrefetch		    = FALSE;
    m_bLoadPluginAttempted  = FALSE;
    m_pCurrentScheduleList  = NULL;
    m_ulSourceDuration	    = 0;
    m_ulMaxDuration	    = 0;
    m_ulTrackDuration	    = 0;
    m_ulTotalTrackDuration  = 0;
    m_pDependNode	    = NULL;
    m_uNumDependencies	    = 0;
    m_bTobeInitializedBeforeBegin = FALSE;
    m_bAltURL		    = FALSE;
    m_lastErrorFromMainURL  = HXR_OK;
    m_lastError             = HXR_OK;
    m_bLocked		    = FALSE;
    m_bIsTrackDurationSet   = FALSE;
    m_bDurationTimeSyncScheduled = FALSE;
    m_bAudioDeviceReflushHint = FALSE;
    m_pProcessCallback = new CHXGenericCallback((void*)this, (fGenericCBFunc)ProcessCallback);
    m_pProcessCallback->AddRef();

    m_prefetchType = PrefetchUnknown;
    m_ulPrefetchValue = 0;
    m_uSoundLevel = 100;
    m_fillType = FillRemove;

    m_pPeerSourceInfo = NULL;   
    m_bSeekPending = FALSE;
    m_bIndefiniteDuration = FALSE;
    m_bRepeatIndefinite = FALSE;
    m_bSeekToLastFrame = FALSE;
    m_ulRepeatInterval = 0;
    m_ulRepeatDelayTimeOffset = 0;
    m_ulSeekTime = 0;
    m_ulPausedStartTime = 0;

    m_bLeadingSource = TRUE;
    m_bRepeatPending = FALSE;
    m_pRepeatList = NULL;
    m_curPosition = 0;
    
    m_ulPersistentComponentID = MAX_UINT32;
    m_ulPersistentComponentSelfID = MAX_UINT32;
    m_pRendererAdviseSink = NULL;

#ifdef THREADS_SUPPORTED
    HXMutex::MakeMutex(m_pMutex);
#else
    HXMutex::MakeStubMutex(m_pMutex);
#endif

    /*
     * The following members are needed for live sync support.
     */
    m_pWallClock = NULL;
    m_ulStreamStartTime = 0;
    m_llLatestPacketTime    = 0;
}

SourceInfo::~SourceInfo()
{
    if (m_pProcessCallback && m_pPlayer->m_pScheduler)
    {
	m_pPlayer->m_pScheduler->Remove(m_pProcessCallback->GetPendingCallback());
        m_pProcessCallback->CallbackCanceled();
    }

    if (m_pRepeatList)
    {
	while (m_pRepeatList->GetCount())
	{
	    RepeatInfo* pRepeatInfo = (RepeatInfo*)m_pRepeatList->RemoveHead();
	    HX_DELETE(pRepeatInfo);
	}

	HX_DELETE(m_pRepeatList);
    }

    HX_RELEASE(m_pStatus);
    HX_RELEASE(m_pProcessCallback);
    HX_RELEASE(m_pPlayer);
    HX_DELETE(m_pCurrentScheduleList);
    HX_VECTOR_DELETE(m_pDependNode);
    HX_DELETE(m_pMutex);

    HX_DELETE(m_pRendererMap);
    /*
     * For live sync support, we may have used a shared wall
     * clock, if so then we're done with it now, so we may need 
     * to do some cleanup work here.     
     */
    DoneWithWallClock();
};



HX_RESULT
SourceInfo::Begin()
{
    HX_RESULT theErr = HXR_OK;

    if(!m_pSource || !m_pSource->IsInitialized())
    {
	return HXR_OK;
    }

    if (m_pPlayer->m_bInitialized)
    {
	// handle the seek so that the server will be notified 
	// via the play request upon the first resume
	if (m_bSeekPending)
	{
	    m_bSeekPending = FALSE;
	
	    Pause();
	    Seek(m_ulSeekTime);
	    m_pSource->DoSeek(m_ulSeekTime);
	}

	theErr = m_pSource->DoResume();
    }

    if (!m_bInitialized)
    {
	return HXR_OK;
    }

    /* Only send this OnBegin()'s if not the first begin. In the case
     * of the first begin, these are actually sent after the header's
     * arrive...
     */
    if (!m_pPlayer->m_bIsFirstBegin && !m_pPlayer->m_bInternalPauseResume)
    {
	CHXMapLongToObj::Iterator ndxRend = m_pRendererMap->Begin();
	for (; !theErr &&
		ndxRend != m_pRendererMap->End(); ++ndxRend)
	{
	    RendererInfo* pRendInfo = (RendererInfo*) (*ndxRend);

	    if (!pRendInfo->m_bInitialBeginToBeSent)
	    {
		IHXRenderer* pRend  = (IHXRenderer*) pRendInfo->m_pRenderer;
		pRend->OnBegin(m_pPlayer->m_ulCurrentPlayTime);
	    }
	}
    }

    return theErr;
}


HX_RESULT
SourceInfo::Pause()
{
    HX_RESULT	theErr	= HXR_OK;

    if(!m_pSource)
    {
	return HXR_OK;
    }

    theErr = m_pSource->DoPause();

    /* Do not send OnPause to renderers if it is an internal Pause */
    if (m_pPlayer->m_bInternalPauseResume || !m_bInitialized)
    {
	return theErr;
    }

    CHXMapLongToObj::Iterator ndxRend = m_pRendererMap->Begin();
    for (; !theErr &&
	    ndxRend != m_pRendererMap->End(); ++ndxRend)
    {
	RendererInfo* pRendInfo = (RendererInfo*) (*ndxRend);
	IHXRenderer* pRend     = (IHXRenderer*) pRendInfo->m_pRenderer;

	m_pPlayer->m_pScheduler->Remove(
         pRendInfo->m_pTimeSyncCallback->GetPendingCallback());
        
        pRendInfo->m_pTimeSyncCallback->CallbackCanceled();
	pRendInfo->m_bIsFirstCallback   = TRUE;

	pRend->OnPause(m_pPlayer->m_ulCurrentPlayTime);
    }

    return theErr;
}


HX_RESULT
SourceInfo::Seek(UINT32 ulSeekTo)
{
    HX_RESULT			    rc = HXR_OK;
    INT64			    llLastExpectedPacketTime = 0;
    BOOL			    bSeekToLastFrame = FALSE;
    BOOL			    bDurationTimeSyncSent = TRUE;
    UINT32			    ulValue = 0;
    RendererInfo*		    pRendInfo = NULL;
    STREAM_INFO*		    pStreamInfo = NULL;
    IHXValues*			    pStatus = NULL;
    IHXPersistentRenderer*	    pPersRender = NULL;
    IHXPersistentComponent*	    pPersComp = NULL;
    HXPersistentComponentManager*  pPersCompMgr = NULL;
    CHXMapLongToObj::Iterator ndx;

    /* m_pSource should never be NULL */
    HX_ASSERT(m_pSource);
    if(!m_pSource || !m_bInitialized)
    {
	return HXR_OK;
    }

    m_bDone = FALSE;
    m_bActive = TRUE;
    m_bAllPacketsReceived = FALSE;
    llLastExpectedPacketTime = m_pSource->GetLastExpectedPacketTime();

    /* Are we seeking past the last expected packet time?
     * If so, verify the "show" attribute to see whether we need to
     * get the last video frame
     */    
    if (!m_pSource->IsLive() && ulSeekTo >= INT64_TO_UINT32(llLastExpectedPacketTime + m_pSource->GetDelay()))
    {
#if defined(HELIX_FEATURE_NESTEDMETA)
	pPersCompMgr = m_pPlayer->m_pPersistentComponentManager;
	if (FillFreeze == m_fillType && pPersCompMgr) 
	{
	    rc = pPersCompMgr->GetPersistentComponent(m_ulPersistentComponentID,
						      pPersComp);
	    HX_ASSERT(HXR_OK == rc);

	    if (pPersComp)
	    {
		rc = pPersComp->GetPersistentRenderer(pPersRender);
		HX_ASSERT(HXR_OK == rc);

		rc = pPersRender->GetElementStatus(m_uGroupID,
						   m_uTrackID,
						   ulSeekTo,
						   pStatus);
		HX_ASSERT(HXR_OK == rc);

		if (pStatus && HXR_OK == pStatus->GetPropertyULONG32("Show", ulValue))
		{
		    bSeekToLastFrame = (BOOL)ulValue;
		}

		HX_RELEASE(pStatus);
		HX_RELEASE(pPersRender);
		HX_RELEASE(pPersComp);
	    }
	}
	else if (FillHold == m_fillType)
	{
	    bSeekToLastFrame = TRUE;
	}
#else
	if (FillHold == m_fillType)
	{
	    bSeekToLastFrame = TRUE;
	}
#endif /* HELIX_FEATURE_NESTEDMETA */
    }

    // also check if we already sent OnTimeSync() upon its duration ends
    // which means the renderer got the last frame so we don't need to issue
    // last frame seek
    if (bSeekToLastFrame)
    {
	ndx = m_pRendererMap->Begin();
	for (; ndx != m_pRendererMap->End(); ++ndx)
	{
	    pRendInfo = (RendererInfo*)(*ndx);
	    if (!pRendInfo->m_bDurationTimeSyncSent)
	    {
		bDurationTimeSyncSent = FALSE;
	    }
	}

	if (bDurationTimeSyncSent)
	{
	    bSeekToLastFrame = FALSE;
	}
    }

    CHXMapLongToObj::Iterator ndxRend = m_pRendererMap->Begin();
    for (; ndxRend != m_pRendererMap->End(); ++ndxRend)
    {
	RendererInfo* pRendInfo     = (RendererInfo*)(*ndxRend);
	IHXRenderer* pRend         = (IHXRenderer*)pRendInfo->m_pRenderer;
	STREAM_INFO*  pStreamInfo   = pRendInfo->m_pStreamInfo;
	
	pStreamInfo->m_bSrcInfoStreamDone  = FALSE;

	pRendInfo->m_pStream->ResetASMRuleState();
	pStreamInfo->ResetPostEndTimeEventList();

	if (m_pSource->IsLive())
	{
#if defined(HELIX_FEATURE_RECORDCONTROL)
	    if(m_pSource->IsPlayingFromRecordControl())
	    {
		pStreamInfo->m_ulTimeBeforeSeek = m_pPlayer->m_ulTimeBeforeSeek;
		pStreamInfo->m_ulTimeAfterSeek = m_pPlayer->m_ulTimeAfterSeek;

		if (pRendInfo->m_bTimeDiffPositive)
		{
		    pStreamInfo->m_ulTimeBeforeSeek += pRendInfo->m_ulTimeDiff;
		    pStreamInfo->m_ulTimeAfterSeek += pRendInfo->m_ulTimeDiff;
		}
		else
		{
		    pStreamInfo->m_ulTimeBeforeSeek -= pRendInfo->m_ulTimeDiff;
		    pStreamInfo->m_ulTimeAfterSeek -= pRendInfo->m_ulTimeDiff;
		}
	    }
	    else
#endif /* HELIX_FEATURE_RECORDCONTROL */
	    {
		UINT32 ulLastTimeSync = (LONG32) m_pPlayer->m_ulLiveSeekTime;

		if (pRendInfo->m_bTimeDiffPositive)
		{
		    ulLastTimeSync += pRendInfo->m_ulTimeDiff;
		}
		else
		{
		    ulLastTimeSync -= pRendInfo->m_ulTimeDiff;
		}

		pStreamInfo->m_ulTimeBeforeSeek = ulLastTimeSync;
		pStreamInfo->m_ulTimeAfterSeek  = ulLastTimeSync + m_pPlayer->m_ulElapsedPauseTime;
	    }
	}
	else
	{
	    pStreamInfo->m_ulTimeBeforeSeek = m_pPlayer->m_ulTimeBeforeSeek;
	    if (bSeekToLastFrame)
	    {		
		HX_ASSERT(llLastExpectedPacketTime + m_pSource->GetDelay() >= 1);
		pStreamInfo->m_ulTimeAfterSeek = INT64_TO_UINT32(llLastExpectedPacketTime + m_pSource->GetDelay() - 1);
	    }
	    else
	    {
		pStreamInfo->m_ulTimeAfterSeek = m_pPlayer->m_ulTimeAfterSeek;
	    }
	}
	
	pRend->OnPreSeek(pStreamInfo->m_ulTimeBeforeSeek,
			 pStreamInfo->m_ulTimeAfterSeek);

	pStreamInfo->m_pStream->m_bPostSeekToBeSent = TRUE;

	// reset renderer info attributes 
	pRendInfo->m_ulLatestEventTime	    = 0;
	pRendInfo->m_bIsFirstCallback	    = TRUE;
	pRendInfo->m_ulLastSyncTime         = 0;
	pRendInfo->m_ulNextDueSyncTime      = m_pPlayer->m_ulCurrentPlayTime;

	/* Do we need to send a time sync for renderer duration? */
	if (bSeekToLastFrame || pRendInfo->m_ulNextDueSyncTime <= pRendInfo->m_ulDuration)
	{
	    pRendInfo->m_bDurationTimeSyncSent  = FALSE;
	    pRendInfo->m_bOnEndOfPacketSent = FALSE;
	}
	else
	{
	    pRendInfo->m_bDurationTimeSyncSent  = TRUE;
	}

	m_pPlayer->m_pScheduler->Remove(
         pRendInfo->m_pTimeSyncCallback->GetPendingCallback());
        
        pRendInfo->m_pTimeSyncCallback->CallbackCanceled();

	if (!pRendInfo->m_bInitialBeginToBeSent)
	{
	    pRendInfo->m_bInitialBeginToBeSent  = TRUE;
	    m_pPlayer->EnterToBeginList(pRendInfo);
	}

	if (m_pSource->IsLive())
	{
	    pRendInfo->m_BufferingReason    = BUFFERING_LIVE_PAUSE;
	}
	else
	{
	    pRendInfo->m_BufferingReason    = BUFFERING_SEEK;
	}
    }

    m_bSeekToLastFrame = bSeekToLastFrame;
    m_llLatestPacketTime = 0;

    return HXR_OK;
}

HX_RESULT
SourceInfo::BeginTrack(void)
{
#if defined(HELIX_FEATURE_ADVANCEDGROUPMGR)
    HX_RESULT	    rc = HXR_OK;
    UINT32	    ulDelay = 0;
    UINT32	    ulPausedTime = 0;
    RendererInfo*   pRendInfo = NULL;
    STREAM_INFO*    pStreamInfo = NULL;
    CHXAudioPlayer* pAudioPlayer = NULL;
    CHXMapLongToObj::Iterator ndxRend;

    if (!m_pSource)
    {
	rc = HXR_UNEXPECTED;
	goto cleanup;
    }

    if (m_pSource->IsPaused())
    {
	ulPausedTime = m_pPlayer->m_ulCurrentPlayTime - m_ulPausedStartTime;	
	ulDelay = m_pSource->GetDelay() + ulPausedTime;
	
	// update source's delay time which will call sourceinfo's
	// UpdateDelay()
	m_pSource->UpdateDelay(ulDelay);

	// resume
	m_pSource->ResumeAudioStreams();

	rc = Begin();
    }
    // track is stopped ahead of its duration
    else if (m_bStopped)
    {
	m_bStopped = FALSE;
	m_bSeekPending = TRUE;
	m_ulSeekTime = m_pPlayer->m_ulCurrentPlayTime;
	
	m_pSource->ReSetup();
    }
    // either track has not been played yet
    // or track is still playing
    else
    {
    	rc = HXR_FAILED;
	goto cleanup;
    }
   
cleanup:
    
    return rc;
#else
    return HXR_NOTIMPL;
#endif /* HELIX_FEATURE_ADVANCEDGROUPMGR */
}

HX_RESULT
SourceInfo::PauseTrack(void)
{
#if defined(HELIX_FEATURE_ADVANCEDGROUPMGR)
    HX_RESULT	rc = HXR_OK;

    // stop the source
    if (m_pSource)
    {
	m_pSource->PauseAudioStreams();
    }

    m_ulPausedStartTime = m_pPlayer->m_ulCurrentPlayTime;
    rc = Pause();

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

HX_RESULT
SourceInfo::SeekTrack(UINT32 ulSeekTime)
{
    // low priority since it is rarely used by the SMIL
    return HXR_NOTIMPL;
}

HX_RESULT
SourceInfo::StopTrack(void)
{
#if defined(HELIX_FEATURE_ADVANCEDGROUPMGR)
    HX_RESULT	rc = HXR_OK;

    m_bStopped = TRUE;

    // stop the source
    if (m_pSource)
    {
	m_pSource->RemoveAudioStreams();
    }

    Reset();

    m_bDone = TRUE;
    m_pPlayer->m_uNumSourcesActive--;

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

HX_RESULT
SourceInfo::SetSoundLevel(UINT16 uSoundLevel, BOOL bReflushAudioDevice)
{
    m_uSoundLevel = uSoundLevel;
    m_pSource->SetSoundLevel(uSoundLevel, bReflushAudioDevice);

    return HXR_OK;
}

void
SourceInfo::Reset()
{
    /* m_pSource should never be NULL */
    HX_ASSERT(m_pSource);

    if (!m_pSource)
    {
	return;
    }

#if defined(HELIX_FEATURE_ADVANCEDGROUPMGR)
    if(m_bIsPersistentSource)
    {
	m_bDone = FALSE;
	m_bAllPacketsReceived = FALSE;

	CHXMapLongToObj::Iterator ndxRend = m_pRendererMap->Begin();
	for (; ndxRend != m_pRendererMap->End(); ++ndxRend)
	{
	    RendererInfo* pRendInfo     = (RendererInfo*)(*ndxRend);
	    IHXRenderer* pRend         = (IHXRenderer*)pRendInfo->m_pRenderer;
	    STREAM_INFO*  pStreamInfo   = pRendInfo->m_pStreamInfo;
	    pStreamInfo->m_bSrcInfoStreamDone  = FALSE;

	    /* Reset the latest packet time */
	    pRendInfo->m_ulLatestEventTime	    = 0;

	    m_pPlayer->m_pScheduler->Remove(
             pRendInfo->m_pTimeSyncCallback->GetPendingCallback());
        
            pRendInfo->m_pTimeSyncCallback->CallbackCanceled();

	    pRendInfo->m_bIsFirstCallback	    = TRUE;
	    pRendInfo->m_ulLastSyncTime         = 0;
	    pRendInfo->m_ulNumberOfPacketsQueued= 0;
	    pRendInfo->m_ulNextDueSyncTime      = 0;
	    if (!pRendInfo->m_bInitialBeginToBeSent && pRendInfo->m_pRenderer)
	    {
		pRendInfo->m_bInitialBeginToBeSent  = TRUE;
		m_pPlayer->EnterToBeginList(pRendInfo);
	    }
	}
    }
    else
#endif /* HELIX_FEATURE_ADVANCEDGROUPMGR */
    {	
	DoCleanup();
	RenderersCleanup();
    }

    return;
}


void
SourceInfo::DoCleanup(EndCode endCode)
{
    if (m_pProcessCallback && m_pPlayer->m_pScheduler)
    {
	m_pPlayer->m_pScheduler->Remove(m_pProcessCallback->GetPendingCallback());
        m_pProcessCallback->CallbackCanceled();
    }

    if (m_bIsPersistentSource)
    {
	return;
    }

    m_bLocked = TRUE;
    m_pMutex->Lock();

    CHXMapLongToObj::Iterator ndxRend = m_pRendererMap->Begin();
    for (; ndxRend != m_pRendererMap->End(); ++ndxRend)
    {
	RendererInfo* pRendInfo = (RendererInfo*)(*ndxRend);

	/* Tell each renderer that we are done with the stream */
	if(pRendInfo)
	{
            if (pRendInfo->m_pStream &&
                pRendInfo->m_pStreamInfo && pRendInfo->m_pStreamInfo->m_pHeader)
            {
                IHXBuffer* pMimeTypeBuffer = NULL;
	        pRendInfo->m_pStreamInfo->m_pHeader->GetPropertyCString("MimeType", pMimeTypeBuffer);

	        if (pMimeTypeBuffer && pMimeTypeBuffer->GetBuffer() &&
                    ::strcasecmp((const char*) pMimeTypeBuffer->GetBuffer(), "application/vnd.rn-objectsstream") == 0 ||
                    ::strcasecmp((const char*) pMimeTypeBuffer->GetBuffer(), "application/x-rn-objects") == 0 ||
		    ::strcasecmp((const char*) pMimeTypeBuffer->GetBuffer(), "application/vnd.rn-objects") == 0)
	        {
	            m_pPlayer->m_pEngine->m_lROBActive--;
                    HX_ASSERT(m_pPlayer->m_pEngine->m_lROBActive >=0 );
	        }

                HX_RELEASE(pMimeTypeBuffer);
            }

	    if(pRendInfo->m_pRenderer)
		pRendInfo->m_pRenderer->EndStream();

	    if (pRendInfo->m_bInitialBeginToBeSent)
	    {
		m_pPlayer->RemoveFromPendingList(pRendInfo);
	    }

	    if (pRendInfo->m_pTimeSyncCallback)
	    {
//{FILE* f1 = ::fopen("c:\\temp\\ts.txt", "a+"); ::fprintf(f1, "%p RELEASING TimeSyncCallback: Pending: %d Handle: %lu\n", pRendInfo->m_pTimeSyncCallback, (int)pRendInfo->m_bIsCallbackPending, (UINT32) pRendInfo->m_PendingHandle);::fclose(f1);}

	        m_pPlayer->m_pScheduler->Remove(
                 pRendInfo->m_pTimeSyncCallback->GetPendingCallback());
        
                pRendInfo->m_pTimeSyncCallback->CallbackCanceled();
                HX_RELEASE(pRendInfo->m_pTimeSyncCallback);
	    }

	    HX_RELEASE(pRendInfo->m_pStream);
	}
    }

    // cleanup (i.e. registry)
    m_pSource->DoCleanup(endCode);

    UnRegister();

    m_lastError = HXR_OK;
    m_bAreStreamsSetup = FALSE;
    m_bDone = FALSE;
    m_bInitialized = FALSE;
    m_bSeekPending = FALSE;
    m_bSeekToLastFrame = FALSE;

    m_pMutex->Unlock();
    m_bLocked = FALSE;
}

void
SourceInfo::Stop(EndCode endCode)
{
    /* We have already been here once */
    if (m_pSource == NULL)
    {
	return;
    }

    DoCleanup(endCode);

    if (m_bIsPersistentSource)
    {
	return;
    }

    m_bLocked = TRUE;
    m_pMutex->Lock();

    m_pSource->Stop();

#if defined(HELIX_FEATURE_BASICGROUPMGR)
    if (m_bTrackStoppedToBeSent)
    {
	m_pPlayer->m_pGroupManager->TrackStopped(m_uGroupID, m_uTrackID);  
	m_bTrackStoppedToBeSent = FALSE;
	if (m_pPeerSourceInfo)
	{
	    m_pPeerSourceInfo->m_bTrackStoppedToBeSent = FALSE;
	}
    }
#endif /* HELIX_FEATURE_BASICGROUPMGR */

    m_pMutex->Unlock();
    m_bLocked = FALSE;
}

void
SourceInfo::Remove()
{
    // stop the source
    if (m_pSource)
    {
	m_pSource->RemoveAudioStreams();
    }

    m_bDone = TRUE;

    Stop();
    CloseRenderers();
}

/*
 * -- LIVE SYNC SUPPORT --
 *
 * SharedWallClock class is used for live sync support.
 * There is one of these objects per shared wall clock.
 *
 * This object will also be associated with an HXPlayer
 * since we won't ever share wall clocks between two players.
 * This is an intentional design choice, since it doesn't
 * reall make sense to do any sync-ing across multiple players.
 * 
 * This object will keep a list of source's that are sharing
 * it. This is mainly needed so that the wall clock can
 * reset the start time for all the sources in the event that
 * it gets a new start time that is earlier than it had
 * previously thought. Basically, the start time of the wall
 * clock is what does the syncing.      
 */
SharedWallClock::SharedWallClock
(
    const char* pName, 
    UINT32 ulStartTime,
    HXPlayer* pPlayer
)
    : m_ulStartTime(ulStartTime)
    , m_strName(pName)
    , m_pPlayer(pPlayer)
{
    HX_ASSERT(m_pPlayer);
    (*m_pPlayer->m_pSharedWallClocks)[pName] = this;
};

/*
 * -- LIVE SYNC SUPPORT --
 *
 * Obviously this is the method that handles resyncing the
 * start times for all the sources that share the wall clock.
 *
 * This method also returns the start time. The intended useage
 * is for the caller to pass the start time that it expects, and
 * then respect the start time that is returned. In the event that
 * the input start time is less than the previously known start
 * time, then all sources are reset.
 */
UINT32
SharedWallClock::ResetStartTime(UINT32 ulStartTime)
{
    if (ulStartTime < m_ulStartTime)
    {
	m_ulStartTime = ulStartTime;
	CHXSimpleList::Iterator indx = m_UserList.Begin();

	for (; indx != m_UserList.End(); ++indx)
	{
	    SourceInfo* pSourceInfo = (SourceInfo*)(*indx);
	    pSourceInfo->ResetStartTime(ulStartTime);
	}
    }
    return m_ulStartTime;
};

/*
 * -- LIVE SYNC SUPPORT --
 *
 * Nothing tricky here, we just track the incoming user on our
 * list of known users.
 */
void
SharedWallClock::AddUser(SourceInfo* pSourceInfo)
{
    m_UserList.AddTail(pSourceInfo);
};

/*
 * -- LIVE SYNC SUPPORT --
 *
 * This method removes users from the shared clock's user list
 * and also cleans up the shared clock when the last user is
 * removed. So, any caller of this method should take note to
 * NOT use the shared clock after calling this method.
 */
void
SharedWallClock::RemoveUser(SourceInfo* pSourceInfo)
{
    LISTPOSITION pos = m_UserList.Find(pSourceInfo);
    HX_ASSERT(pos);

    m_UserList.RemoveAt(pos);
    
    if (m_UserList.IsEmpty())
    {
	m_pPlayer->m_pSharedWallClocks->RemoveKey(m_strName);
	delete this;
    }
};

/*
 * -- LIVE SYNC SUPPORT --
 *
 * If the start time is changed for a source then all the
 * renderer's need to have their start time's reset. This
 * method just loops through the renderer info to do the work.
 */
void
SourceInfo::ResetStartTime(UINT32 ulStartTime)
{
    if (ulStartTime < m_ulStreamStartTime)
    {
	m_ulStreamStartTime = ulStartTime;
	CHXMapLongToObj::Iterator indx = m_pRendererMap->Begin();

	for (; indx != m_pRendererMap->End(); ++indx)
	{
	    RendererInfo* pRendererInfo = (RendererInfo*)(*indx);
	    pRendererInfo->m_ulStreamStartTime = ulStartTime;
	}
    }
};

/*
 * -- LIVE SYNC SUPPORT --
 *
 * This is the real work horse for live sync support. This
 * method actually determines if the URL for the source is
 * targeted at a shared wall clock by looking for the wallclock
 * option in the URL.
 *
 * If the wallclock name is found then we try to find the wall
 * clock object for this name and player. If we don't find it
 * then we create a new clock. If we do find it then we want
 * to determine if the start time should be reset to the lower
 * value. We also return the start time to be stored in the
 * renderers.
 */
UINT32
SourceInfo::CalculateLiveStartTime(IHXPacket* pFirstPacket)
{
    UINT32 ulStreamStartTime = pFirstPacket->GetTime();;

    /*
     * Find out if this live feed has a "wallclock" property.
     */
    CHXURL*	pURL = m_pSource->GetCHXURL();
    IHXValues*  pValues = pURL->GetOptions();
    IHXBuffer* pWallClockName = NULL;

    pValues->GetPropertyBuffer("wallclock", pWallClockName);

    if (pWallClockName)
    {
	m_strWallClockName = (const char*)pWallClockName->GetBuffer();

	/* We shouldn't already have a wall clock! */
	HX_ASSERT(m_pWallClock == NULL);

	/*
	 * Lookup a wall clock with this name, if it's not found then
	 * we are the first one, and we should create a shared wallclock
	 * object, add it to the list, and do the standard setup.
	 */
	void* pLookupResult = NULL;
	if (!m_pPlayer->FindSharedWallClocks(m_strWallClockName,pLookupResult))
	{
	    m_pWallClock = new SharedWallClock(m_strWallClockName,
					ulStreamStartTime,
					m_pPlayer);
	}
	else
	{
	    m_pWallClock = (SharedWallClock*)pLookupResult;
	    ulStreamStartTime = m_pWallClock->ResetStartTime(ulStreamStartTime);
	}
	m_pWallClock->AddUser(this);
    }

    HX_RELEASE(pValues);
    HX_RELEASE(pWallClockName);

    return ulStreamStartTime;
}

/*
 * -- LIVE SYNC SUPPORT --
 *
 * Basic clean up support for wall clock object. Notice that after
 * calling RemoveUser, you can use the wall clock object since it
 * may get deleted... so we always reset our wall clock object
 * pointer to NULL.
 */
void
SourceInfo::DoneWithWallClock()
{
    if (m_pWallClock)
    {
	m_pWallClock->RemoveUser(this);
	m_pWallClock = NULL;
    }
}

HX_RESULT
SourceInfo::ProcessIdle(BOOL	    bIsFirst,
		        ULONG32&    ulNumStreamsToBeFilled,
		        BOOL&       bIsBuffering,
		        UINT16&     uLowestBuffering,
		        BOOL	    bPersistent)
{
    HX_RESULT		theErr		    = HXR_OK;
    CHXEvent*          pEvent              = NULL;
    HXSource*          pSource             = NULL;
    IHXPendingStatus*  pStatus             = NULL;
    RendererInfo*       pRendInfo           = NULL;
    IHXValues*         pHeader             = NULL;
    HXStream*          pStream             = NULL;
    IHXRenderer*       pRenderer           = NULL;
    STREAM_INFO*        pStreamInfo         = NULL;
    IHXPacket*         pPacket             = NULL;
    UINT16              unStatusCode        = 0;
    UINT16              unPercentDone       = 0;
    UINT32		ulPacketTime	    = 0;
    INT64		llActualPacketTime  = 0;
    INT64		llLastExpectedPacketTime = 0;
    IHXBuffer*         pStatusDesc         = NULL;
    BOOL		bAtInterrupt	    = FALSE;

    bAtInterrupt = m_pPlayer->m_pEngine->AtInterruptTime();

    if (m_bStopped)
    {
	return HXR_OK;
    }

    /* Check if a source has not been initialized. This will happen
     * ONLY when we start a new track in the mid of a presentation
     */
    if (!m_bInitialized)
    {
	/* Do not initialize source at interrupt time */
	if (bAtInterrupt)
	{
	    ScheduleProcessCallback();
	    return HXR_OK;
	}

	theErr = InitializeAndSetupRendererSites();

	if (theErr || !m_bInitialized)
	{
	    return theErr;
	}
    }

    if (m_bSeekPending)
    {
	m_bSeekPending = FALSE;
	
	Pause();
	Seek(m_ulSeekTime);
	m_pSource->DoSeek(m_ulSeekTime);
	Begin();
    }

    pSource = m_pSource;
    pStatus = m_pStatus;

    // don't start getting events till the player setup has been
    // done   
    if (!m_pPlayer->m_bInitialized  || 
	m_pPlayer->m_bSetupToBeDone || 
	m_pPlayer->m_bPostSetupToBeDone)
    {
	return HXR_OK;
    }
    
    UINT16  uIndex	= 0;
    BOOL    bHandled	= TRUE;
    llLastExpectedPacketTime = m_pSource->GetLastExpectedPacketTime();

    LISTPOSITION posRend = m_pCurrentScheduleList->GetHeadPosition();
    for (; uIndex < m_pCurrentScheduleList->GetCount(); uIndex++)
    {
	BOOL	bSentMe = TRUE;
	BOOL	bEndMe = FALSE;	
	llActualPacketTime = 0;
	
	theErr = HXR_OK;

	if (uIndex > 0)
	{
	    if (bHandled)
	    {
		posRend = m_pCurrentScheduleList->RemoveAt(posRend);
		LISTPOSITION listRet = m_pCurrentScheduleList->AddTail(pRendInfo);
                // This check is critical for catching oom errors. XXXJHHB
                if( listRet == NULL )
                {
                    return HXR_OUTOFMEMORY;
                }
	    }
	    else
	    {
		m_pCurrentScheduleList->GetNext(posRend);
	    }
	}

	bHandled = TRUE;
	pRendInfo       = (RendererInfo*) 
			m_pCurrentScheduleList->GetAt(posRend);

	pStreamInfo     = pRendInfo->m_pStreamInfo;

	if (pStreamInfo->m_bSrcInfoStreamDone)
	{
	    // SPECIAL CASE:
	    // the player received all the packets(m_bSrcStreamDone is TRUE) and 
	    // EndOfPacket() hasn't been sent to the renderer yet, 
	    // BUT the renderer still calls ReportRebufferStatus()	
	    if (!IsRebufferDone())
	    {
		bIsBuffering = TRUE;
	    }
	    else
	    {
		CheckIfDone();
	    }
	    continue;
	}

	if (bIsFirst)
	{
	    pStreamInfo->m_bSrcInfoStreamFillingDone = FALSE;
	    ulNumStreamsToBeFilled++;
	}

	BOOL bThisSourceBuffering = FALSE;

	if (!bPersistent)
	{
	    // every HXSource has to implement IID_IHXPendingStatus
	    HX_ASSERT(pStatus);
	    HX_VERIFY(HXR_OK == pStatus->GetStatus(unStatusCode, pStatusDesc,
						   unPercentDone));
	    HX_RELEASE(pStatusDesc);

	    if ((HX_STATUS_BUFFERING == unStatusCode && unPercentDone < 100) ||
		HX_STATUS_CONTACTING == unStatusCode)
	    {
		bIsBuffering            = TRUE;
		bThisSourceBuffering    = TRUE;

		if (uLowestBuffering > unPercentDone)
		{
		    uLowestBuffering = unPercentDone;
		}
	    }
	}

	/* the event times are actual ts - preroll.. so we do not
	 * need to add preroll in calculations here...
	 */	  
	 
	UINT32 ulDeliveryTime = m_pPlayer->m_ulCurrentPlayTime + 
	    			   m_pPlayer->m_ulLowestGranularity;

#ifdef _MACINTOSH
#define ADDITIONAL_PREDELIVERY_TIME	4000
 	/* on Mac we try to be even farther ahead than the timeline in
 	 * packet delivery since the callbacks are not that smooth
 	 * and if we are really close to the wire, it results in
 	 * unnecccessary re-buffers. 
 	 */ 
	ulDeliveryTime += ADDITIONAL_PREDELIVERY_TIME;
#endif
	
	if (pSource)
	{
	    pSource->FillRecordControl();
	}

	if (bPersistent || bThisSourceBuffering ||
	    (pRendInfo->m_ulLatestEventTime <= ulDeliveryTime))
	{	  
    	    theErr = pSource->GetEvent(pStreamInfo->m_uStreamNumber, pEvent);

	    if (!theErr)
	    {
		pPacket = pEvent->GetPacket();

		if (pEvent->IsPreSeekEvent())
		{
		     // associate the packet with its renderer..
		     pEvent->m_pRendererInfo   = pRendInfo;		    
		     if (!bAtInterrupt || pRendInfo->m_bInterruptSafe)
		     {
			 theErr = m_pPlayer->SendPacket(pEvent);
			 delete pEvent;
		     }
		     else
		     {
			// insert event in the common packet/event list...
			theErr = m_pPlayer->m_EventList.InsertEvent(pEvent);
                        if( theErr == HXR_OUTOFMEMORY )
                        {
                            return HXR_OUTOFMEMORY;
                        }
			pRendInfo->m_ulNumberOfPacketsQueued++;
		     }

		     continue;
		}

		if (!pPacket->IsLost())
		{
		    ulPacketTime = pPacket->GetTime();

		    llActualPacketTime = 
			pRendInfo->m_pStreamInfo->BufferingState().CreateINT64Timestamp(ulPacketTime);

		    if (m_llLatestPacketTime < llActualPacketTime)
		    {
			m_llLatestPacketTime = llActualPacketTime;
		    }

		    pRendInfo->m_ulLatestEventTime  = pEvent->GetTimeStartPos();
		    
		    if (pSource->IsLive() && pRendInfo->m_bIsFirstPacket)
		    {
			pRendInfo->m_bIsFirstPacket     = FALSE;

			/*
			 * -- LIVE SYNC SUPPORT --
			 *
			 * We used to just set the stream start time to the 
			 * timestamp of this first packet. Now we use this
			 * helper function to implement support for sharing
			 * start times for sources off of the same wall clock.
			 * See other LIVE SYNC SUPPORT comments for more 
			 * details.
			 */
			UINT32 ulLiveStart = CalculateLiveStartTime(pPacket);
			pRendInfo->m_ulStreamStartTime = ulLiveStart;
			m_ulStreamStartTime = ulLiveStart;

			UINT32  ulLowestTime = 0;
			BOOL    bAtLeastOneLowestTime = FALSE;
			UINT16  uNumLowestKnown = 0;
			CHXMapLongToObj::Iterator tmpndxRend = m_pRendererMap->Begin();

			for (;!theErr && tmpndxRend != m_pRendererMap->End();
				++tmpndxRend)
			{
			    RendererInfo* pTmpRendInfo  = (RendererInfo*)(*tmpndxRend);
			    /* Have we received the lowest packet?*/
			    if (!pTmpRendInfo->m_bIsFirstPacket)
			    {
				uNumLowestKnown++;
				if (!bAtLeastOneLowestTime)
				{
				    bAtLeastOneLowestTime = TRUE;
				    ulLowestTime = pTmpRendInfo->m_ulStreamStartTime;
				    SetLiveSyncStartTime(pSource, pTmpRendInfo, ulLowestTime);
				}
				else
				{
				    if (ulLowestTime > pTmpRendInfo->m_ulStreamStartTime)
				    {
					ulLowestTime = pTmpRendInfo->m_ulStreamStartTime;
				    }
				}
			    }
			}

			if (uNumLowestKnown > 1)
			{
			    tmpndxRend = m_pRendererMap->Begin();

			    for (;!theErr && tmpndxRend != m_pRendererMap->End();
				    ++tmpndxRend)
			    {
				RendererInfo* pTmpRendInfo      = (RendererInfo*)(*tmpndxRend);
				/* Have we received the lowest packet?*/
				if (!pTmpRendInfo->m_bIsFirstPacket)
				{
				    pTmpRendInfo->m_ulStreamStartTime = ulLowestTime;
				    SetLiveSyncStartTime(pSource, pTmpRendInfo, ulLowestTime);
				}
			    }
			}
		    }

		    if ((!pSource->IsLive() || pSource->isRestrictedLiveStream()) &&
			llActualPacketTime > llLastExpectedPacketTime)
		    {
			// XXX HP
			// work around since the rule flag of all RTSP packets are set to
			// HX_ASM_SWITCH_ON only and this could cause the stream doesn't end 
			// properly if its endtime was changed after the first resume has been 
			// sent
			// THIS WILL BE FIXED IN THE NEXT SERVER RELEASE("dial-tone")  
			if (pSource->m_bRTSPRuleFlagWorkAround)
			{
			    bSentMe = FALSE;
			    bEndMe = TRUE;
			}
			else 
			{
			    pRendInfo->m_pStream->PostEndTimePacket(pPacket, bSentMe, bEndMe);
			}
		    }
		}		    

		if (bSentMe)
		{		    
		    // clear up the post events which were not sent
		    if (pStreamInfo->m_pPostEndTimeEventList)
		    {
			while (pStreamInfo->m_pPostEndTimeEventList->GetNumEvents())
			{
			    CHXEvent* pPostEndTimeEvent = pStreamInfo->m_pPostEndTimeEventList->RemoveHead();

			    // associate the packet with its renderer..
			    pPostEndTimeEvent->m_pRendererInfo   = pRendInfo;
			    // insert event in the common packet/event list...
			    theErr = m_pPlayer->m_EventList.InsertEvent(pPostEndTimeEvent);
                            if( theErr == HXR_OUTOFMEMORY )
                            {
                                return HXR_OUTOFMEMORY;
			    }
			    pRendInfo->m_ulNumberOfPacketsQueued++;
			}
		    }

		    // associate the packet with its renderer..
		    pEvent->m_pRendererInfo   = pRendInfo;
		    // insert event in the common packet/event list...
		    theErr = m_pPlayer->m_EventList.InsertEvent(pEvent);
		    if( theErr == HXR_OUTOFMEMORY )
                    {
                        return HXR_OUTOFMEMORY;
		    }
		    pRendInfo->m_ulNumberOfPacketsQueued++;
		}
		else
		{
		    if (!pStreamInfo->m_pPostEndTimeEventList)
		    {
			pStreamInfo->m_pPostEndTimeEventList = new CHXEventList();
		    }

		    if (pStreamInfo->m_pPostEndTimeEventList)
		    {
//{FILE* f1 = ::fopen("c:\\temp\\out.txt", "a+"); ::fprintf(f1, "bSentMe is FALSE PacketTime: %lu PacketRule: %u\n", pEvent->GetPacket()->GetTime(), pEvent->GetPacket()->GetASMRuleNumber());::fclose(f1);}			
			theErr = pStreamInfo->m_pPostEndTimeEventList->InsertEvent(pEvent);
			if( theErr == HXR_OUTOFMEMORY )
			{
			    return theErr;
			}
		    }
		}
	    }
	    else
	    {
		bHandled = FALSE;
	    }

	    if (theErr && !bPersistent)
	    {
		if (!pStreamInfo->m_bSrcInfoStreamFillingDone &&
		    ulNumStreamsToBeFilled > 0)
		{
		    pStreamInfo->m_bSrcInfoStreamFillingDone = TRUE;
		    ulNumStreamsToBeFilled--;
		}
	    }
	}
	else
	{
	    if (!pStreamInfo->m_bSrcInfoStreamFillingDone &&
		ulNumStreamsToBeFilled > 0)
	    {
		pStreamInfo->m_bSrcInfoStreamFillingDone = TRUE;
		ulNumStreamsToBeFilled--;
	    }
	}

	if (theErr == HXR_BUFFERING)
	{
	    if (bPersistent)
	    {
		bIsBuffering    = TRUE;
	    }
	}
	else if (theErr == HXR_AT_END)
	{
	    if (!pStreamInfo->m_bSrcInfoStreamDone)
	    {
		if (!bAtInterrupt || pRendInfo->m_bInterruptSafe)
		{
		    pStreamInfo->m_bSrcInfoStreamDone = TRUE;

		    pStreamInfo->ResetPostEndTimeEventList();

		    if (pRendInfo->m_ulNumberOfPacketsQueued == 0 &&
			pRendInfo->m_pRenderer)
		    {			
			m_pPlayer->SendPostSeekIfNecessary(pRendInfo);
			pRendInfo->m_pRenderer->OnEndofPackets();
			pRendInfo->m_bOnEndOfPacketSent = TRUE;	
		    }

		    CheckIfDone();
		}
		else
		{
		    ScheduleProcessCallback();
		    continue;
		}
	    }
	}
	else if (theErr == HXR_NO_DATA)
	{
	    /* mask this error */
	    theErr  = HXR_OK;
	}
	if (!llActualPacketTime)
	{
	    STREAM_INFO* pStrInfo = pRendInfo->m_pStreamInfo;
	    UINT32 ulPktTime = 
		pStrInfo->BufferingState().LastPacketTimestamp();
	    llActualPacketTime = 
		pStrInfo->BufferingState().CreateINT64Timestamp(ulPktTime);
	    
	    /* if this is a stream where we do not have to wait to receive 
	     * a packet >= stream's last expected packet time, 
	     * make the highest timestamp to be the HIGHEST timestamp
	     * across ALL streams for this source.
	     *
	     * This is to fix end tag support on sources with sparse streams
	     * e.g. audio/video with an event stream where we may not really 
	     * have a packet till way down in the future.  
	     */ 
	    if (pRendInfo->m_pStreamInfo->m_bCanBeStoppedAnyTime &&
		!pStreamInfo->m_bSrcInfoStreamDone &&
		m_llLatestPacketTime > llLastExpectedPacketTime)
	    {
		/* check if ALL other streams have ended */
		if (AllOtherStreamsHaveEnded(pStreamInfo))
		{
                    /*
                     * The logic above was designed to handle endTime's
                     * on a/v streams, so that once we received a/v packets
                     * past the end time (even though more packets are 
                     * still coming in), we can go ahead and end the
                     * event stream. However, this was causing a bug
                     * where syncmm event streams were getting terminated
                     * even though there was a packet waiting for it
                     * at the transport. This additional test imposes
                     * that we don't terminate the stream unless we
                     * have no packets waiting at the transport AND
                     * the stream has said it's done.
                     */
                    if (pStreamInfo->BufferingState().DoneAtTransport())
                    {
    		        bEndMe		= TRUE;
		        llActualPacketTime  = m_llLatestPacketTime;
                    }
		}
	    }
	}

	if (!bPersistent && 
	    (!pSource->IsLive() || pSource->isRestrictedLiveStream()) && 
	    !pStreamInfo->m_bSrcInfoStreamDone &&
	    llActualPacketTime > llLastExpectedPacketTime &&
	    bEndMe)
	{
	    if (!bAtInterrupt || pRendInfo->m_bInterruptSafe)
	    {
		pStreamInfo->m_bSrcInfoStreamDone = TRUE;

		pStreamInfo->ResetPostEndTimeEventList();

		if (!pStreamInfo->m_bSrcInfoStreamFillingDone &&
		    ulNumStreamsToBeFilled > 0)
		{
		    pStreamInfo->m_bSrcInfoStreamFillingDone = TRUE;
		    ulNumStreamsToBeFilled--;
		}

		if (pRendInfo->m_ulNumberOfPacketsQueued == 0 &&
		    pRendInfo->m_pRenderer)
		{
		    m_pPlayer->SendPostSeekIfNecessary(pRendInfo);
		    pRendInfo->m_pRenderer->OnEndofPackets();
		    pRendInfo->m_bOnEndOfPacketSent = TRUE;	
		}

		if (!pSource->isRestrictedLiveStream())
		{
		    CheckIfDone();
		}
	    }
	    else
	    {
		ScheduleProcessCallback();
		continue;
	    }
	}
    }

    if (!bPersistent && 
	m_bInitialized &&
	m_ulSourceDuration < m_pPlayer->m_ulCurrentPlayTime && 
	m_pSource && 
	!m_pSource->IsLive() &&
	m_pSource->IsActive())
    {
	/* Remove bandwidth usage for this source */
	m_pSource->AdjustClipBandwidthStats(FALSE);
    }
    else if (m_bInitialized &&
	    m_pSource &&
	    m_pPlayer->m_ulCurrentPlayTime > m_pSource->GetDelay() &&
	    m_pPlayer->m_ulCurrentPlayTime <= m_ulSourceDuration && 
	    !m_pSource->IsActive())
    {
	/* Add bandwidth usage for this source */
	m_pSource->AdjustClipBandwidthStats(TRUE);
    }

    return HXR_OK;
}

HX_RESULT
SourceInfo::Register()
{
    HX_RESULT theErr = HXR_OK;
    if (!m_bIsRegisterSourceDone)
    {
	m_bIsRegisterSourceDone = TRUE;

	if (m_pSource->m_bSureStreamClip)
	{
	    m_pPlayer->SureStreamSourceRegistered(this);
	}
	
	m_pSource->CanBeFastStarted();

#if defined(HELIX_FEATURE_ASM)
	/* Register Source with ASM Bandwidth Manager */
	IHXBandwidthManager* pMgr = 0;

	HX_VERIFY(HXR_OK == m_pPlayer->QueryInterface(
	    IID_IHXBandwidthManager, (void **)&pMgr));

	theErr = pMgr->RegisterSource(m_pSource, (IUnknown*) (IHXPlayer*) m_pPlayer);
	pMgr->Release();
#endif /* HELIX_FEATURE_ASM */
    }

    return theErr;
}

HX_RESULT
SourceInfo::UnRegister()
{
    HX_RESULT theErr = HXR_OK;
    if (m_bIsRegisterSourceDone)
    {
	m_bIsRegisterSourceDone = FALSE;

	if (m_pSource->m_bSureStreamClip)
	{
	    m_pPlayer->SureStreamSourceUnRegistered(this);
	}

#if defined(HELIX_FEATURE_ASM)
	/* Register Source with ASM Bandwidth Manager */
	IHXBandwidthManager* pMgr = 0;

	HX_VERIFY(HXR_OK == m_pPlayer->QueryInterface(
	    IID_IHXBandwidthManager, (void **)&pMgr));

	HX_ASSERT(pMgr) ;
	if (pMgr)
	{
		theErr = pMgr->UnRegisterSource(m_pSource);
		pMgr->Release();
	}

        CheckIfDone();
#endif /* HELIX_FEATURE_ASM */
    }

    return theErr;
}

void		
SourceInfo::ChangeAccelerationStatus(BOOL bMayBeAccelerated,
				     BOOL bUseAccelerationFactor,
				     UINT32 ulAccelerationFactor)
{
    IHXBandwidthManager* pMgr = 0;

    HX_VERIFY(HXR_OK == m_pPlayer->QueryInterface(
	IID_IHXBandwidthManager, (void **)&pMgr));

    if (pMgr)
    {
	pMgr->ChangeAccelerationStatus(m_pSource, bMayBeAccelerated,
				       bUseAccelerationFactor, ulAccelerationFactor);
	pMgr->Release();
    }
}

void SourceInfo::CheckIfDone()
{
    BOOL bIsDone = TRUE;
    CHXMapLongToObj::Iterator   ndxRend;
    RendererInfo*   pRendInfo   = NULL;
    STREAM_INFO*    pStreamInfo = NULL;

    // keep the source active throughout the duration
    // of source
    if (m_bDone					&&
	m_bActive				&& 
	m_pPlayer->m_uNumSourcesActive > 0	&&
	!KeepSourceActive())
    {	  
	m_bActive = FALSE;
	m_pPlayer->m_uNumSourcesActive--;
    }

    if (!m_bDone)
    {
	ndxRend = m_pRendererMap->Begin();

	for (;ndxRend != m_pRendererMap->End(); ++ndxRend)
	{
	    pRendInfo       = (RendererInfo*)(*ndxRend);
	    pStreamInfo     = pRendInfo->m_pStreamInfo;

	    if (!pStreamInfo->m_bSrcInfoStreamDone)
	    {
		bIsDone = FALSE;
		break;
	    }
	}

	if (bIsDone || m_pSource->IsSourceDone())
	{
	    m_bDone = TRUE;	

	    if (!m_bAllPacketsReceived)
	    {
		m_bAllPacketsReceived = TRUE;
		// If we are done receiving all the packets, release bandwidth
		UnRegister();
	    }

	    if (!m_pSource->IsSourceDone())
	    {
		m_pSource->SetEndOfClip(TRUE);
	    }

	    if (m_pPlayer->m_uNumCurrentSourceNotDone > 0)
	    {
		m_pPlayer->m_uNumCurrentSourceNotDone--;
	    }
	}
    }

#if defined(HELIX_FEATURE_BASICGROUPMGR)
    if (!m_bActive && 
	m_bTrackStoppedToBeSent &&
	!m_pPlayer->m_pEngine->AtInterruptTime())
    {
	// send TrackStopped at the end of its active duration
	m_bTrackStoppedToBeSent = FALSE;
	m_pPlayer->m_pGroupManager->TrackStopped(m_uGroupID, m_uTrackID);  
	if (m_pPeerSourceInfo)
	{
	    m_pPeerSourceInfo->m_bTrackStoppedToBeSent = FALSE;
	}
    }
#endif /* HELIX_FEATURE_BASICGROUPMGR */
}

void
SourceInfo::SetupRendererSites(BOOL bIsPersistent)
{
    CHXMapLongToObj::Iterator ndxRend = m_pRendererMap->Begin();
    for (; ndxRend != m_pRendererMap->End(); ++ndxRend)
    {
	RendererInfo* pRendInfo   = (RendererInfo*) (*ndxRend);
	IHXRenderer* pRenderer   = (IHXRenderer*) pRendInfo->m_pRenderer;

	HX_DISPLAY_TYPE    ulFlags;
	IHXBuffer*         pInfoBuffer = NULL;

	/*
	 * Find out if the renderer is a display type renderer.
	 */
	if (HXR_OK == pRenderer->GetDisplayType(ulFlags,pInfoBuffer))
	{
	    HX_RELEASE(pInfoBuffer);
	    if (HX_DISPLAY_WINDOW == (HX_DISPLAY_WINDOW & ulFlags))
	    {
		STREAM_INFO*  pStreamInfo = pRendInfo->m_pStreamInfo;
		IHXValues*   pProps      = pStreamInfo->m_pStreamProps;
		/*
		 * Call the helper function that handles setup of a
		 * renderer site. This function will do the
		 * whole process of finding out if a site exists by
		 * PlayToFrom; if not, it calls the site supplier; then it
		 * it actually hooks up the SiteUserSupplier with all
		 * the appropriate sites, etc.
		 */
		m_pPlayer->SetupRendererSite(pRenderer,pProps,bIsPersistent);
	    }
	}
    }
}


HX_RESULT
SourceInfo::InitializeRenderers(BOOL& bSourceInitialized)
{
    HX_RESULT   theErr              = HXR_OK;
    HX_RESULT	theFinalErr	    = HXR_OK;
    IHXValues* pHeader             = NULL;
    HXStream*  pStream             = NULL;
    IHXRenderer*   pRenderer       = NULL;
    IHXBuffer* pMimeTypeBuffer     = NULL;
    IUnknown*   pUnkRenderer        = NULL;
    STREAM_INFO*    pStreamInfo     = NULL;
    BOOL	bAddDefaultUpgrade  = FALSE;

    bSourceInitialized = FALSE;

    if (m_pPlayer->m_pEngine->AtInterruptTime()) //for Mac
    {
	return HXR_OK;
    }

    if (m_pSource->GetLastError() != HXR_OK)
    {
	/* Since the source has an error, mark plugin load attempt
	 * to TRUE so that the player object can request upgrade
	 * if it needs to
	 */
	m_bLoadPluginAttempted = TRUE;
	return HXR_OK;
    }

    if (!m_pSource->IsInitialized())
    {
	// atleast one source is not yet initialized
	return HXR_OK;
    }

    bSourceInitialized = TRUE;

#if defined(HELIX_FEATURE_NESTEDMETA)
    IHXPersistentComponent* pPersistentComponent = NULL;

    if (HXR_OK == m_pPlayer->m_pPersistentComponentManager->GetPersistentComponent(m_ulPersistentComponentID,
										   pPersistentComponent))
    {
	// no need to AddRef() since it's maintained by PersistentManager
	m_pRendererAdviseSink = ((HXPersistentComponent*)pPersistentComponent)->m_pRendererAdviseSink;
	m_pRendererAdviseSink->AddRef();
    }

    HX_RELEASE(pPersistentComponent);
#endif /* HELIX_FEATURE_NESTEDMETA */

    theErr = SetupStreams();

    if (m_bIndefiniteDuration	||
	m_pSource->IsLive()	|| 
	m_pPlayer->IsLive())
    {
	m_pPlayer->m_bIsLive = TRUE;
	
	m_pPlayer->m_pAudioPlayer->SetLive(m_pPlayer->m_bIsLive);

	if (m_pSource->isRestrictedLiveStream())
	{
	    m_pPlayer->SetPresentationTime(max(m_pPlayer->m_ulPresentationDuration, GetActiveDuration()));
	}
	else
	{
	    m_pPlayer->SetPresentationTime(0);
	}
    }
    else
    {
	m_pPlayer->SetPresentationTime(max(m_pPlayer->m_ulPresentationDuration, GetActiveDuration()));
    }

    // attemp to load all the plugins
    m_bLoadPluginAttempted = TRUE;

    UINT16 uNumStreams = m_pSource->GetNumStreams();
    CHXMapLongToObj::Iterator ndxRend = m_pRendererMap->Begin();
    for (UINT16 i = 0; ndxRend != m_pRendererMap->End(); ++ndxRend, i++)
    {
	RendererInfo*	pRendInfo = (RendererInfo*)(*ndxRend);

	pStreamInfo	= pRendInfo->m_pStreamInfo;
	pHeader         = pStreamInfo->m_pHeader;
	pStream         = pRendInfo->m_pStream;
	
	HX_ASSERT(pHeader);
	HX_ASSERT(pStream);

	pRenderer       = NULL;
	pUnkRenderer    = NULL;
	bAddDefaultUpgrade  = FALSE;
	HX_RELEASE(pMimeTypeBuffer);

	pHeader->GetPropertyCString("MimeType", pMimeTypeBuffer);

	HX_ASSERT(pMimeTypeBuffer && pMimeTypeBuffer->GetBuffer());
	if (!pMimeTypeBuffer || !pMimeTypeBuffer->GetBuffer())
	{
	    GOTOEXITONERROR(theErr = HXR_NOT_INITIALIZED, exit);
	}

	/* 
	 * Check for "CanBeStoppedAnyTime" property. For streams with this 
	 * property, we do not have to wait to receive a 
	 * packet with timestamp >= lastPacketTime. 
	 * We can instead use the highest timestamp received across
	 * ALL the streams in a given source 
	 */
	UINT32 ulCanBeStoppedAnyTime = 0;
	if (pHeader->GetPropertyULONG32("CanBeStoppedAnyTime", ulCanBeStoppedAnyTime) == HXR_OK)	    
	{   
	    pStreamInfo->m_bCanBeStoppedAnyTime = (ulCanBeStoppedAnyTime == 1);
	}
	/* temporary hack till we fix rmff fileformat to correctly set this
	 * property AND the servers areupdated to have this fixed fileformat.
	 */
	else if (::strcasecmp((const char*) pMimeTypeBuffer->GetBuffer(), "application/x-pn-realevent") == 0 ||
		 ::strcasecmp((const char*) pMimeTypeBuffer->GetBuffer(), "syncMM/x-pn-realvideo") == 0)
	{
	    pStreamInfo->m_bCanBeStoppedAnyTime = TRUE;
	}
	else if (::strcasecmp((const char*) pMimeTypeBuffer->GetBuffer(), "application/vnd.rn-objectsstream") == 0 ||
                ::strcasecmp((const char*) pMimeTypeBuffer->GetBuffer(), "application/x-rn-objects") == 0 ||
		 ::strcasecmp((const char*) pMimeTypeBuffer->GetBuffer(), "application/vnd.rn-objects") == 0)
	{
	    m_pPlayer->m_pEngine->m_lROBActive++;
	}

	IHXPluginHandler3* pPlugin2Handler3 = NULL;
	HX_VERIFY(HXR_OK ==
	    m_pPlayer->m_pPlugin2Handler->QueryInterface(IID_IHXPluginHandler3, (void**) &pPlugin2Handler3));

	if (!(HXR_OK == pPlugin2Handler3->FindGroupOfPluginsUsingStrings(PLUGIN_CLASS, PLUGIN_RENDERER_TYPE, 
	    PLUGIN_RENDERER_MIME, (char*)pMimeTypeBuffer->GetBuffer(), NULL, NULL, pRendInfo->m_pRendererEnumerator) &&
	    pRendInfo->m_pRendererEnumerator &&
	    (HXR_OK == pRendInfo->m_pRendererEnumerator->GetNextPlugin(pUnkRenderer, NULL)) &&
	    pUnkRenderer))
	{
#if defined(HELIX_FEATURE_AUTOUPGRADE)
	    if(m_pPlayer->m_pUpgradeCollection)
		m_pPlayer->m_pUpgradeCollection->Add(eUT_Required, pMimeTypeBuffer, 0, 0);
#endif /* HELIX_FEATURE_AUTOUPGRADE */

	    theErr = HXR_NO_RENDERER;
	}

	HX_RELEASE(pPlugin2Handler3);

	GOTOEXITONERROR(theErr, nextrend);

tryNextRendererForSameMimeType:
	HX_ASSERT(pUnkRenderer);
	pUnkRenderer->QueryInterface(IID_IHXRenderer, (void**) &pRenderer);
	theErr = pStream->SetRenderer(pUnkRenderer);

	GOTOEXITONERROR(theErr, nextrend);
	HX_RELEASE(pUnkRenderer);

        // now get the TAC info for this track/source
        UINT32 sourceID;
	sourceID	    = 0;
        if (HXR_OK == m_pSource->GetID(sourceID))
        {
            m_pPlayer->CheckTrackAndSourceOnTrackStarted(m_uGroupID, m_uTrackID, sourceID);
        }

	if (!pStreamInfo->m_pStreamProps)
	{
	    // Create an IHXValues for storing stream properties related
	    // to metafile initilization and layout hookup.
	    pStreamInfo->m_pStreamProps = new CHXHeader();
	    pStreamInfo->m_pStreamProps->AddRef();
	}

	IHXBuffer* pStreamPlayTo;
	pStreamPlayTo = NULL;
	// If the stream doesn't have a playto property, than
	// check if the parent source has a playto property
	if ((HXR_OK != pStreamInfo->m_pStreamProps->GetPropertyCString("playto",pStreamPlayTo)) ||
	    !pStreamPlayTo)
	{
	    /* Set region name to playto property */
	    pStreamInfo->m_pStreamProps->GetPropertyCString("region",pStreamPlayTo);
	    
	    /* create a unique value */
	    if (!pStreamPlayTo)
	    {
		char szBuffer[14]; /* Flawfinder: ignore */
		ULONG32 length = SafeSprintf(szBuffer,14, "%#010lx",(ULONG32)(void*)pStreamInfo); /* Flawfinder: ignore */

		pStreamPlayTo = (IHXBuffer*) new CHXBuffer();
		pStreamPlayTo->AddRef();

		pStreamPlayTo->Set((UCHAR*)szBuffer,length+1);
	    }

	    pStreamInfo->m_pStreamProps->SetPropertyCString("playto",pStreamPlayTo);
	}

	HX_RELEASE(pStreamPlayTo);

	theErr = SetupRenderer(pRendInfo, pRenderer, pStreamInfo, pStream);

nextrend:
	if (theErr)
	{
	    if (theErr == HXR_OUTOFMEMORY)
	    {
		m_pPlayer->Report( HXLOG_ERR, theErr, 0, NULL, NULL );
	    }

	    if (theErr == HXR_REQUEST_UPGRADE)
	    {
		bAddDefaultUpgrade = TRUE;
	    }

	    HX_RELEASE(pUnkRenderer);
	    if (pRendInfo->m_pRendererEnumerator &&
		HXR_OK == pRendInfo->m_pRendererEnumerator->GetNextPlugin(pUnkRenderer, NULL) &&
		pUnkRenderer)
	    {
		theErr = HXR_OK;
		HX_RELEASE(pRendInfo->m_pRenderer);
		goto tryNextRendererForSameMimeType;
	    }
	    else
	    {
		if (!theFinalErr)
		{
		    bSourceInitialized = FALSE;
		    theFinalErr = theErr;
		}	    

		// merge any upgrade requests for this source to the player
		m_pSource->MergeUpgradeRequest(bAddDefaultUpgrade, pMimeTypeBuffer ? (char*) pMimeTypeBuffer->GetBuffer() : (char*)NULL);
		theErr = HXR_OK;
	    }
	}
    }

exit:

    if (theErr && !theFinalErr)
    {
	bSourceInitialized = FALSE;
	theFinalErr = theErr;
    }

#if defined(HELIX_FEATURE_BASICGROUPMGR)
    if(!theFinalErr && 
       m_bTrackStartedToBeSent)
    {    
	m_pPlayer->m_pGroupManager->TrackStarted(m_uGroupID, m_uTrackID);  
	m_bTrackStartedToBeSent = FALSE;
	if (m_pPeerSourceInfo)
	{
	    m_pPeerSourceInfo->m_bTrackStartedToBeSent = FALSE;
	}
    }
#endif /* HELIX_FEATURE_BASICGROUPMGR */

    HX_RELEASE(pMimeTypeBuffer);
    HX_RELEASE(pUnkRenderer);

    if (!theFinalErr)
    {
	theFinalErr = InitializeRenderersExt(bSourceInitialized);
    }

    if (!theFinalErr && bSourceInitialized)
    {
	m_bInitialized = TRUE;
	  
	// renderers initialized...clear any pending upgrade requests for this source!
	m_pSource->ClearUpgradeRequest();

	if (m_bAudioDeviceReflushHint)
	{
	    m_pSource->SetAudioDeviceReflushHint();
	}

	// set the soundLevel != 100 by default
	if (m_uSoundLevel != 100)
	{
	    m_pSource->SetSoundLevel(m_uSoundLevel, FALSE);
	}

	/* Enter the order of the stream numbers in which they will be 
	 * scheduled for GetEvent() calls
	 */
	if (NULL == m_pCurrentScheduleList)
	{
	    m_pCurrentScheduleList = new CHXSimpleList;
	}

	CHXMapLongToObj::Iterator ndxRend = m_pRendererMap->Begin();
	for (; ndxRend != m_pRendererMap->End(); ++ndxRend)
	{
	    RendererInfo* pRendInfo = (RendererInfo*)(*ndxRend);
	    m_pCurrentScheduleList->AddTail((void*) pRendInfo);
	}
    }

    return theFinalErr;
}

HX_RESULT
SourceInfo::InitializeRenderersExt(BOOL& bSourceInitialized)
{
    return HXR_OK;
}

HX_RESULT
SourceInfo::SetupStreams()
{
    if (m_bAreStreamsSetup)
    {
	return HXR_OK;
    }

    HX_RESULT	    theErr          = HXR_OK;
    IHXValues*	    pHeader         = NULL;
    HXStream*	    pStream         = NULL;
    STREAM_INFO*    pStreamInfo     = NULL;
    RendererInfo*   pRendInfo       = NULL;
    HXSource*	    pSource	    = m_pSource;

    if (!pSource->IsInitialized())
    {
	HX_ASSERT(FALSE);
	return HXR_UNEXPECTED;
    }

    UINT16 uNumStreams = pSource->GetNumStreams();

    HX_ASSERT(m_pRendererMap->IsEmpty() == TRUE);
    if (m_pRendererMap->IsEmpty() && uNumStreams > 0 &&
	(UINT32) uNumStreams < m_pRendererMap->GetHashTableSize())
    {
	m_pRendererMap->InitHashTable((UINT32) uNumStreams);
    }
    
    for (UINT16 i=0; i < uNumStreams; i++)
    {
	UINT32	ulStreamNumber = 0;
	pHeader         = NULL;
	pStream         = NULL;

	pSource->GetStreamHeaderInfo(i, pHeader);

	if (!pHeader)
	{
	    GOTOEXITONERROR(theErr = HXR_FAILED, exit);	    
	}

	pStream = new HXStream;
	if (!pStream)
	{
	    theErr = HXR_OUTOFMEMORY;
	    goto exit;
	}

	pStream->AddRef();
	theErr = pStream->Init(m_pPlayer, pSource, pHeader);
	
	GOTOEXITONERROR(theErr, exit);

	pHeader->GetPropertyULONG32("StreamNumber", ulStreamNumber);

	if (HXR_OK != pSource->GetStreamInfo(ulStreamNumber, pStreamInfo) || !pStreamInfo)
	{
	    GOTOEXITONERROR(theErr = HXR_FAILED, exit);
	}

	HX_RELEASE(pHeader);

	pRendInfo = NewRendererInfo();
	if (!pRendInfo)
	{
	    theErr = HXR_OUTOFMEMORY;
	    GOTOEXITONERROR(theErr, exit);
	}
	// get properties of the header
	pRendInfo->m_pRenderer          = NULL;
	pRendInfo->m_pStreamInfo        = pStreamInfo;
	pRendInfo->m_ulGranularity      = 0;
	pRendInfo->m_ulDuration         = pStreamInfo->m_ulDuration;
	pRendInfo->m_BufferingReason    = BUFFERING_START_UP;
        
        struct timeSyncParamStruct obj = {this, pRendInfo};
        pRendInfo->m_pTimeSyncCallback  = new CTimeSyncCallback((void*)&obj, (fGenericCBFunc)TimeSyncCallback);
	pRendInfo->m_pStream		= pStream;
	pRendInfo->m_pStream->AddRef();

	if (m_ulSourceDuration < pStreamInfo->m_ulDuration)
	{
	    m_ulSourceDuration = pStreamInfo->m_ulDuration;
	}
    
	if (pRendInfo->m_pTimeSyncCallback)
	{
	    pRendInfo->m_pTimeSyncCallback->AddRef();
	}
	else
	{
	    theErr = HXR_OUTOFMEMORY;
	    GOTOEXITONERROR(theErr, exit);
	}

	m_pRendererMap->SetAt(pStreamInfo->m_uStreamNumber, (void*) pRendInfo);

	if (pStream)
	{
	    if (pStream->IsSureStream())
	    {
		pSource->m_bSureStreamClip = TRUE;
	    }
	    pSource->AddHXStream(pStream);
	    HX_RELEASE(pStream);
	}
    }

exit:
    HX_RELEASE(pStream);
    HX_RELEASE(pHeader);

    if (!theErr)
    {
	m_bAreStreamsSetup = TRUE;
    }

    return theErr;
}

RendererInfo* 
SourceInfo::NewRendererInfo()
{
    return (new RendererInfo());
}

HX_RESULT
SourceInfo::SetupRenderer(RendererInfo*& pRendInfo, IHXRenderer*& pRenderer, 
			 STREAM_INFO*& pStreamInfo, HXStream*& pStream)
{
    HX_RESULT       theErr              = HXR_OK;
    ULONG32         ulSyncGranularity   = DEFAULT_TIMESYNC_GRANULARITY;
    BOOL	    bLiveSource		= FALSE;
    HXSource*      pSource             = m_pSource;
    IHXPlugin*     pPlugin             = NULL;
    STREAM_INFO*    pSrcStreamInfo	= NULL;
    IHXStatistics* pStatistics		= NULL;
    const char**    ppTmpMimeType = 0;

    pRendInfo->m_pRenderer          = pRenderer;

    if (HXR_OK != pRenderer->QueryInterface(IID_IHXPlugin,(void**)&pPlugin))
    {
	theErr = HXR_NOT_INITIALIZED;
    }
    else
    {
	/* Initialize the plugin for use */
	if (HXR_OK != pPlugin->InitPlugin((IUnknown*) (IHXStreamSource*) m_pSource))
	{
	    theErr = HXR_NOT_INITIALIZED;
	}
	pPlugin->Release();
    }

    GOTOEXITONERROR(theErr, exit);

#if defined(HELIX_FEATURE_STATS) && defined(HELIX_FEATURE_REGISTRY)
    if (HXR_OK == pRenderer->QueryInterface(IID_IHXStatistics, (void**) &pStatistics))
    {
	if (HXR_OK == pSource->GetStreamInfo(pRendInfo->m_pStreamInfo->m_uStreamNumber, pSrcStreamInfo) &&
	    pSrcStreamInfo && pSrcStreamInfo->m_pStats)
	{
	    pStatistics->InitializeStatistics(pSrcStreamInfo->m_pStats->m_pRenderer->m_ulRegistryID);
	}

	HX_RELEASE (pStatistics);
    }
#endif /* HELIX_FEATURE_STATS && HELIX_FEATURE_REGISTRY */

    theErr = pRenderer->StartStream(pStream, m_pPlayer);
    GOTOEXITONERROR(theErr, exit);

    theErr = pRenderer->OnHeader(pStreamInfo->m_pHeader);
    GOTOEXITONERROR(theErr, exit);

    // start the stream
    /* get the minimum granularity */
    pRenderer->GetRendererInfo(ppTmpMimeType, ulSyncGranularity);
    /* sanity check */
    if (ulSyncGranularity < MINIMUM_TIMESYNC_GRANULARITY)
    {
	ulSyncGranularity = MINIMUM_TIMESYNC_GRANULARITY;
    }

    if (ulSyncGranularity < m_pPlayer->m_ulLowestGranularity)
    {
	m_pPlayer->m_ulLowestGranularity = ulSyncGranularity;
    }

    // get properties of the header
    pStreamInfo->m_ulDelay  	    = m_pSource->GetDelay();
    pRendInfo->m_ulGranularity      = ulSyncGranularity;
    pRendInfo->m_ulDuration         = pStreamInfo->m_ulDuration;
    // OnHeader may have overriddden the m_ulPreroll from the stream header
    // if it was too big or too small or not set.

    ULONG32 ulPreroll;
    if (HXR_OK == pStreamInfo->m_pHeader->GetPropertyULONG32("Preroll", ulPreroll))
    {
	pStreamInfo->BufferingState().SetPreroll(ulPreroll);
    }

    // check if renderer is interrupt safe
    IHXInterruptSafe* pInterruptSafe;
    if (HXR_OK == pRenderer->QueryInterface(IID_IHXInterruptSafe,(void**)&pInterruptSafe))
    {
	HX_ASSERT(pInterruptSafe) ;
	if (pInterruptSafe)
	{
	    pRendInfo->m_bInterruptSafe = pInterruptSafe->IsInterruptSafe();
	    pInterruptSafe->Release();
	}
    }    
    
    m_pPlayer->m_bResumeOnlyAtSystemTime |= (!pRendInfo->m_bInterruptSafe);
    
    /* Enter to the begin list so that we can call Begin at the right time */
    m_pPlayer->EnterToBeginList(pRendInfo);

    // notify the persistent renderer(source) who implements
    // IHXRendererAdviseSink to monitor the status of its tracks
    if (m_pRendererAdviseSink && pStream && !m_bIsPersistentSource)
    {
	bLiveSource = pSource->IsLive();

	if (m_bIndefiniteDuration)
	{
	    m_ulTrackDuration = MAX_UINT32;
	}
	else	
	{	
	    m_ulTrackDuration = m_pSource->GetDuration();
	}

	if (!m_bIsTrackDurationSet)
	{	    
	    m_bIsTrackDurationSet = TRUE;

	    m_pRendererAdviseSink->TrackDurationSet(m_uGroupID,
						    m_uTrackID,
						    m_ulTrackDuration,
						    pStreamInfo->m_ulDelay,
						    bLiveSource);
	}
    }	

    if (m_pRendererAdviseSink && pStream)
    {
	IUnknown* pUnk = 0;
	if(HXR_OK == pStream->QueryInterface(IID_IUnknown, (void**)&pUnk))
	{
	    /* 
	     * construct the IHXValues info for the RendererInitialized call
	     */
	    IHXValues* pValues = new CHXHeader;
	    pValues->AddRef();

	    pValues->SetPropertyULONG32("GroupIndex", m_uGroupID);
	    pValues->SetPropertyULONG32("TrackIndex", m_uTrackID);
	    pValues->SetPropertyULONG32("Delay", pStreamInfo->m_ulDelay);
	    
	    /* We should really fix SMIL renderer to look at MAX duration
	     * For now, we will pass max duration for all the renderers.
	     * The only case where the duration is different is with image maps 
	     * in video/audio stream.
	     */
	    pValues->SetPropertyULONG32("Duration", m_ulTrackDuration);	    
	    pValues->SetPropertyULONG32("LiveSource", bLiveSource);

	    if (!m_id.IsEmpty())
	    {
    		IHXBuffer* pBuffer = new CHXBuffer();
		pBuffer->AddRef();
    
		pBuffer->Set((const UCHAR*)(const char*)m_id, m_id.GetLength()+1);
		pValues->SetPropertyCString("id", pBuffer);

		HX_RELEASE(pBuffer);
	    }

	    HX_RESULT rc = m_pRendererAdviseSink->RendererInitialized(pRenderer, pUnk, pValues);

	    pValues->Release();
	    pUnk->Release();
	}
    }

    // XXX HP we need to re-examine how the SMIL renderer's layout site 
    // setup work!!
    if (m_bIsPersistentSource)
    {
	m_pPlayer->m_bSetupLayoutSiteGroup = FALSE;
    }

exit:
    return theErr;
}


HX_RESULT
SourceInfo::InitializeAndSetupRendererSites()
{
    HX_RESULT theErr = HXR_OK;
    BOOL bInitialized = FALSE;

    if (HXR_OK != m_lastError)
    {
        return m_lastError;
    }

    if (m_bLocked)
    {
	return HXR_OK;
    }

    m_bLocked = TRUE;
    m_pMutex->Lock();

    theErr = InitializeRenderers(bInitialized);
    if (!theErr && m_bInitialized)
    {
	/* This source may have audio streams */
	if (!m_pPlayer->m_ulMinimumAudioPreroll)
	{
	    m_pPlayer->m_ulMinimumAudioPreroll = 
		m_pPlayer->m_pAudioPlayer->GetInitialPushdown();
	}

	UINT32 ulMinimumStartingPreroll = m_pPlayer->m_pAudioPlayer->GetInitialPushdown(TRUE);

	m_pSource->SetMinimumPreroll(m_pPlayer->m_ulMinimumAudioPreroll, ulMinimumStartingPreroll);

#if defined(HELIX_FEATURE_VIDEO)
	/* Set all the renderer sites */
	SetupRendererSites(!m_pPlayer->m_bSetupLayoutSiteGroup);

	/* Did we have to call BeginChangeLayout? */
	if (m_pPlayer->m_pSiteSupplier			&& 
	    !m_pPlayer->m_bBeginChangeLayoutTobeCalled	&&
	    // XXX HP we will not call DoneChangeLayout on persistent source until
	    //	      the actual source has been initialized. this fixed the resizing
	    //	      problem during stop/play on SMIL in RAM
	    //	      the other DoneChangeLayout in HXPlayer won't be called either
	    //	      since we set m_bSetupLayoutSiteGroup=FALSE in SetupRenderer() of
	    //	      the persistent source
	    !m_bIsPersistentSource)
	{
	    /*
	     * At this point all renderers are layed out!
	     */
	    m_pPlayer->m_bBeginChangeLayoutTobeCalled	= TRUE;
	    m_pPlayer->m_pSiteSupplier->DoneChangeLayout();
	}
#endif /* HELIX_FEATURE_VIDEO */

	if (m_pSource->TryResume())
	{
	    m_pPlayer->RegisterSourcesDone();
	}

	Begin();
    }

    if (HXR_OK != theErr && HXR_OK == m_lastError)
    {
        m_lastError = theErr;
    }

    m_pMutex->Unlock();
    m_bLocked = FALSE;

    return theErr;
}

void
SourceInfo::RenderersCleanup()
{
    CHXMapLongToObj::Iterator ndxRend = m_pRendererMap->Begin();
    for (; ndxRend != m_pRendererMap->End(); ++ndxRend)
    {
	RendererInfo* pRendInfo = (RendererInfo*)(*ndxRend);
	LONG32	      lStreamNumber = ndxRend.get_key();

	if(m_pRendererAdviseSink && pRendInfo->m_pRenderer)
	{
	    IHXValues* pValues = new CHXHeader;
	    pValues->AddRef();
	    pValues->SetPropertyULONG32("GroupIndex", m_uGroupID);
	    pValues->SetPropertyULONG32("TrackIndex", m_uTrackID);
	    pValues->SetPropertyULONG32("StreamNumber", lStreamNumber);

	    if (!m_id.IsEmpty())
	    {
    		IHXBuffer* pBuffer = new CHXBuffer();
		pBuffer->AddRef();
    
		pBuffer->Set((const UCHAR*)(const char*)m_id, m_id.GetLength()+1);
		pValues->SetPropertyCString("id", pBuffer);

		HX_RELEASE(pBuffer);
	    }

	    m_pRendererAdviseSink->RendererClosed(pRendInfo->m_pRenderer, pValues);
	    pValues->Release();
	}

	// remove all the packets associated with this renderer
	LISTPOSITION lPos = m_pPlayer->m_EventList.GetHeadPosition();
	while(lPos && m_pPlayer->m_EventList.GetCount())
	{	    
	    CHXEvent* pEvent = (CHXEvent*)m_pPlayer->m_EventList.GetAt(lPos);
	    
	    if (pEvent->m_pRendererInfo == pRendInfo)
	    {
		HX_DELETE(pEvent);
		lPos = m_pPlayer->m_EventList.RemoveAt(lPos);
	    }
	    else
	    {
		m_pPlayer->m_EventList.GetNext(lPos);
	    }
	}

	RenderersCleanupExt(pRendInfo);

	HX_RELEASE(pRendInfo->m_pRenderer);
	HX_RELEASE(pRendInfo->m_pRendererEnumerator);
	HX_DELETE(pRendInfo);
    }

    if (m_pCurrentScheduleList)
    {
	m_pCurrentScheduleList->RemoveAll();
    }

    m_pRendererMap->RemoveAll();

    return;
}

void
SourceInfo::RenderersCleanupExt(RendererInfo* pRendInfo)
{
    return;
}

HX_RESULT
SourceInfo::CloseRenderers()
{
    if (m_bActive)
    {
	m_bActive = FALSE;
	m_pPlayer->m_uNumSourcesActive--;
    }

    // persistent source will be closed by the persistent manager
    if (m_bIsPersistentSource)
    {
#if defined(HELIX_FEATURE_STATS) && defined(HELIX_FEATURE_REGISTRY)
        //Move this reg tree to a different place.
        m_pPlayer->UpdatePersistentSrcInfo(this,
                                           m_pPlayer->m_pStats->m_ulRegistryID,
                                           m_uTrackID);
#endif        
	return HXR_FAIL;
    }

    // This way the source will still be around until the
    // renderers are closed.
    HX_RELEASE(m_pSource);

    RenderersCleanup();

    HX_RELEASE(m_pRendererAdviseSink);

    return HXR_OK;
}

HX_RESULT 
SourceInfo::OnTimeSync(ULONG32 ulCurrentTime)
{
    HX_RESULT theErr = HXR_OK;

//{FILE* f1 = ::fopen("d:\\temp\\audio.txt", "a+"); ::fprintf(f1, "SourceInfo::OnTimeSync: %lu\n", ulCurrentTime);::fclose(f1);}

    CHXMapLongToObj::Iterator ndxRend =	m_pRendererMap->Begin();

    for (; !theErr && ndxRend != m_pRendererMap->End(); ++ndxRend)
    {
	RendererInfo* pRendInfo = (RendererInfo*)(*ndxRend);

	/* It is OK to send time sync within +TIME_SYNC_FUDGE_FACTOR */
	if (!pRendInfo->m_bInitialBeginToBeSent &&
	    pRendInfo->m_ulNextDueSyncTime <=
	    ulCurrentTime + TIME_SYNC_FUDGE_FACTOR &&
	    (m_pSource->IsLive() || ulCurrentTime <= pRendInfo->m_ulDuration ||
	    !pRendInfo->m_bDurationTimeSyncSent ||
	     (m_bIsPersistentSource && !m_bActive)))
	{
	    m_pPlayer->m_pScheduler->Remove(
             pRendInfo->m_pTimeSyncCallback->GetPendingCallback());
        
            pRendInfo->m_pTimeSyncCallback->CallbackCanceled();
	    
            theErr = OnTimeSync(pRendInfo, TRUE);

	    if(m_pPlayer->m_bCurrentPresentationClosed)
	    {
		goto exit;
	    }
	}
    }

exit:
    return theErr;
}

HX_RESULT    
SourceInfo::OnTimeSync(RendererInfo* pRendInfo, BOOL bUseCurrentTime)
{
    HX_RESULT theErr = HXR_OK;

    if (m_bLocked)
    {
	return HXR_OK;
    }

    m_bLocked = TRUE;
    m_pMutex->Lock();

    // no OnTimeSync() when the source(track) is paused
    if (!m_pSource || m_pSource->IsPaused())
    {
	m_pMutex->Unlock();
	m_bLocked = FALSE;
	return HXR_OK;
    }

    if (!m_pPlayer->m_bInitialized)
    {
	m_pMutex->Unlock();
	m_bLocked = FALSE;
	return HXR_NOT_INITIALIZED;
    }

    m_pPlayer->m_bCurrentPresentationClosed = FALSE;

    /* Do we query for the current time?
     * This routine may be called as a callback (where we want to find
     * out the current time) OR can be called for all DUE renderers
     * whenevr we get a timesync (here we do not want to call getcurrenttime()
     * again)
     *
     * Changed to: (on 09/05/1998 --RA) 
     *
     * Always ask the audio services what the current time is. This is 
     * because we do not make system call in this function anymore to get the 
     * audio device time. Instead, we use realtime to adjust the
     * last audio time returned by the device.
     */

    m_pPlayer->m_ulCurrentPlayTime = 
	m_pPlayer->m_pAudioPlayer->GetCurrentPlayBackTime();

    if (m_pPlayer->m_bIsFirstTimeSync)
    {
	m_pPlayer->m_bIsFirstTimeSync  = FALSE;
	m_pPlayer->m_ulFirstTimeSync   = m_pPlayer->m_ulCurrentPlayTime;
    }

    /*
     * Call ProcessCurrentEvents ONLY if we are here form a scheduled
     * timesync callback. If we are here from HXPlayer::OnTimeSync, we
     * should have already called ProcessCurrentEvents in HXPlayer.
     */
    if (!bUseCurrentTime)
    {
	theErr  = m_pPlayer->ProcessCurrentEvents();
    }

    /* Duration check */
    ULONG32 ulCurrentPlayTime = m_pPlayer->m_ulCurrentPlayTime;

//{FILE* f1 = ::fopen("c:\\temp\\sync.txt", "a+"); ::fprintf(f1, "SourceInfo::OnTimeSync: Renderer: %p %lu\n", pRendInfo->m_pRenderer, ulCurrentPlayTime);::fclose(f1);}

    IHXRenderer* lRend         = (IHXRenderer*) pRendInfo->m_pRenderer;
    STREAM_INFO*  pStreamInfo   = pRendInfo->m_pStreamInfo;
    
    if (m_pSource->isRestrictedLiveStream() && 
	ulCurrentPlayTime > pRendInfo->m_ulDuration)
    {
	// mark all the streams done if one of the streams is done
	if (!pStreamInfo->m_bSrcInfoStreamDone)
	{
	    pStreamInfo->m_bSrcInfoStreamDone = TRUE;

	    pStreamInfo->ResetPostEndTimeEventList();

	    if (!pStreamInfo->m_bSrcInfoStreamFillingDone)
	    {
		pStreamInfo->m_bSrcInfoStreamFillingDone = TRUE;
	    }
	}
	
	CheckIfDone();

	goto exit;
    }

    if (m_pSource)
    {
	theErr = m_pSource->OnTimeSync(ulCurrentPlayTime);
    }

    /* if we were in a seek state before, we need to send a postseekmessage
     * before sending timesync...
     */

    /* We should probably not do this since there may be Pre-Seek Packets for
     * some renderers that have not been sent yet since they are not
     * interrup safe.
     */
    /*
    if (pRendInfo->m_BufferingReason == BUFFERING_SEEK || 
	pRendInfo->m_BufferingReason == BUFFERING_LIVE_PAUSE)
    {
	pRendInfo->m_BufferingReason   = BUFFERING_CONGESTION;
	lRend->OnPostSeek(pStreamInfo->m_ulTimeBeforeSeek,
			  pStreamInfo->m_ulTimeAfterSeek);
    }
    */

    /* We only want to send time sync if it is greater than the last time
     * sync sent to the renderer
     */
//{FILE* f1 = ::fopen("c:\\temp\\sync.txt", "a+"); ::fprintf(f1, "SourceInfo::OnTimeSync: lastsynctime: %lu currentplaytime: %lu\n", pRendInfo->m_ulLastSyncTime, ulCurrentPlayTime );::fclose(f1);}

    if (pRendInfo->m_bIsFirstCallback ||
	pRendInfo->m_ulLastSyncTime < ulCurrentPlayTime)
    {
	/* tell the renderer what their time should be (not the player's time)
	 * taking into account the source's start time, delay, loop count.
	 */
	UINT32 ulRealtime = ulCurrentPlayTime;

	if (m_pSource->IsLive())
	{
	    /* First TimeSync and received at least one packet */
	    if (pRendInfo->m_bIsFirstTimeSync && !pRendInfo->m_bIsFirstPacket)
	    {
		pRendInfo->m_bIsFirstTimeSync = FALSE;
		pRendInfo->m_bTimeDiffPositive = 
		    (pRendInfo->m_ulStreamStartTime >= 
		    m_pPlayer->m_ulFirstTimeSync);

		// CALCULATE_ELAPSED_TICKS assumes 0 is > 0xf??????? and 
		// since we know it's not in this case we special case it
		if (m_pPlayer->m_ulFirstTimeSync != 0)
		{
		    if (pRendInfo->m_bTimeDiffPositive)
		    {
			pRendInfo->m_ulTimeDiff        = 
					CALCULATE_ELAPSED_TICKS(m_pPlayer->m_ulFirstTimeSync,
					pRendInfo->m_ulStreamStartTime);
		    }
		    else
		    {
			pRendInfo->m_ulTimeDiff        = 
					CALCULATE_ELAPSED_TICKS(pRendInfo->m_ulStreamStartTime,
					m_pPlayer->m_ulFirstTimeSync);
		    }
		}
		else
		{
		    pRendInfo->m_ulTimeDiff        = 
			pRendInfo->m_ulStreamStartTime;
		}
	    }

	    if (pRendInfo->m_bTimeDiffPositive)
	    {
	        ulRealtime += pRendInfo->m_ulTimeDiff;
	    }
	    else
	    {
	        ulRealtime -= pRendInfo->m_ulTimeDiff;
	    }
	}

	BOOL bAtInterrupt = m_pPlayer->m_pEngine->AtInterruptTime();
	if (!bAtInterrupt || pRendInfo->m_bInterruptSafe)
	{
	    if (!m_bIsPersistentSource	&& 
		!m_pSource->IsLive()	&&
		ulRealtime >= pRendInfo->m_ulDuration)
	    {
		ulRealtime = pRendInfo->m_ulDuration;
	    }

	    theErr = lRend->OnTimeSync(ulRealtime);	    
	    if( theErr == HXR_OUTOFMEMORY )
	    {
	        m_pPlayer->Report( HXLOG_ERR, theErr, 0, NULL, NULL );
	    }
	    if( theErr != HXR_OK )
	    {
	        goto exit;
	    }

#if defined(HELIX_FEATURE_SMIL_REPEAT)
	    // repeat support:
	    // make sure we called OnTimeSync() on the active source
	    // before we tearing down its peer source
	    if (!bAtInterrupt	    && 
		m_pPeerSourceInfo   &&
		m_pPeerSourceInfo->m_bRepeatPending)
	    {
		CHXSimpleList*  pRepeatList = m_bLeadingSource?m_pRepeatList:m_pPeerSourceInfo->m_pRepeatList;

		HX_ASSERT(pRepeatList);

		RepeatInfo*	    pRepeatInfo = NULL;
		HXSource*	    pPeerSource = m_pPeerSourceInfo->m_pSource;
		LISTPOSITION	    nextPos = 0;

		// take care repeat="indefinite" when max. duration is set
		if (m_bRepeatIndefinite &&
		    m_ulMaxDuration	&&
		    m_pSource->GetDuration() >= m_pSource->m_ulOriginalDelay + m_ulMaxDuration)
		{
		    m_pPeerSourceInfo->Reset();
		}
		// otherwise, prepare for the new repeat
		else
		{
		    if (m_curPosition != pRepeatList->GetTailPosition())
		    {
			nextPos = m_curPosition;
			pRepeatInfo = (RepeatInfo*)pRepeatList->GetAtNext(nextPos);
		    }		
		    else if (m_bRepeatIndefinite)
		    {
			nextPos = (RepeatInfo*)pRepeatList->GetHeadPosition();
			pRepeatInfo = (RepeatInfo*)pRepeatList->GetAt(nextPos);

			UINT32 ulDelayTimeOffset = m_pSource->GetDelay() + m_ulRepeatInterval - pRepeatInfo->ulDelay;
			m_pPeerSourceInfo->m_ulRepeatDelayTimeOffset = ulDelayTimeOffset;
			m_ulRepeatDelayTimeOffset = ulDelayTimeOffset;
		    }

		    if (nextPos && pRepeatInfo)
		    {
			m_pPeerSourceInfo->m_curPosition = nextPos;

			// initialize the new repeat
			m_pPeerSourceInfo->m_uTrackID = pRepeatInfo->uTrackID;
			m_pPeerSourceInfo->Reset();
			pPeerSource->ReSetup();
		    }
		    // done with the repeat
		    else
		    {
			m_pPeerSourceInfo->Reset();
		    }		
		}
		m_pPeerSourceInfo->m_bRepeatPending = FALSE;
	    }
#endif /* HELIX_FEATURE_SMIL_REPEAT */

	    if ((UINT32) ulRealtime >= pRendInfo->m_ulDuration && pRendInfo->m_bOnEndOfPacketSent)
	    {
		pRendInfo->m_bDurationTimeSyncSent = TRUE;
	    }
	}
	else 
	{
	    if (!pRendInfo->m_pTimeSyncCallback->GetPendingCallback())
	    {
                pRendInfo->m_pTimeSyncCallback->CallbackScheduled(
                 m_pPlayer->m_pScheduler->RelativeEnter(pRendInfo->m_pTimeSyncCallback, 0));
//{FILE* f1 = ::fopen("c:\\temp\\ts.txt", "a+"); ::fprintf(f1, "%p ADDING IMMEDIATE TimeSyncCallback: Handle: %lu Thread: %lu\n", pRendInfo->m_pTimeSyncCallback, (UINT32) pRendInfo->m_PendingHandle, GetCurrentThreadId());::fclose(f1);}
	    }

	    goto exit;
	}

	if(m_pPlayer->m_bCurrentPresentationClosed)
	{
	    goto exit;
	}

	pRendInfo->m_ulLastSyncTime = ulCurrentPlayTime;
    }

    /* Do we need to send more timesyncs */
    if (m_pSource->IsLive() || !pRendInfo->m_bDurationTimeSyncSent ||
	(m_bIsPersistentSource && !m_bActive))
    {
	ULONG32 ulNextCBTime = pRendInfo->m_ulGranularity;

	/* Do we need to increment our next due time sync value?
	 * This check is needed since a lot of WOM_DONEs may get accumulated
	 * and by the time we end up getting the time sync,
	 * we have already played a bunch of data thereby resulting in a
	 * jump in current audio time.
	 * So we only increment next due time sync if the audio clock is
	 * also moving
	 */
	if (pRendInfo->m_ulNextDueSyncTime  <
	    ulCurrentPlayTime + pRendInfo->m_ulGranularity)
	{
	    pRendInfo->m_ulNextDueSyncTime  += pRendInfo->m_ulGranularity;
	}

	/* We wanna reset ourself if its either the first callback OR
	 * somehow we are lagging behind in the timesyncs
	 */
	if (pRendInfo->m_bIsFirstCallback ||
	    pRendInfo->m_ulNextDueSyncTime < pRendInfo->m_ulLastSyncTime)
	{
	    pRendInfo->m_bIsFirstCallback = FALSE;
	    HXTimeval lTime = m_pPlayer->m_pScheduler->GetCurrentSchedulerTime();
	    pRendInfo->m_pTimeSyncCBTime->tv_sec = lTime.tv_sec;
	    pRendInfo->m_pTimeSyncCBTime->tv_usec = lTime.tv_usec;
	    pRendInfo->m_ulNextDueSyncTime  = pRendInfo->m_ulLastSyncTime +
					      pRendInfo->m_ulGranularity;

	    /* Next due time sync at integral multiple of granularity */
	    ULONG32 ulTimeDiff = pRendInfo->m_ulNextDueSyncTime % pRendInfo->m_ulGranularity;
	    pRendInfo->m_ulNextDueSyncTime -= ulTimeDiff;
	    ulNextCBTime -= ulTimeDiff;
	}

	if (pRendInfo->m_ulNextDueSyncTime > pRendInfo->m_ulDuration &&
	    (!m_bIsPersistentSource && m_bActive) && !m_pSource->IsLive())
	{
	    ULONG32 ulDiff = pRendInfo->m_ulNextDueSyncTime - pRendInfo->m_ulDuration;
	    if (ulNextCBTime > ulDiff)
	    {
		ulNextCBTime -= ulDiff;
	    }
	    pRendInfo->m_ulNextDueSyncTime = pRendInfo->m_ulDuration;
	}

	*(pRendInfo->m_pTimeSyncCBTime) += (int) (ulNextCBTime*1000);

	if (!pRendInfo->m_pTimeSyncCallback->GetPendingCallback())
	{
	    UINT32 ulSchedTime = 0;
	    if (pRendInfo->m_ulNextDueSyncTime > ulCurrentPlayTime)
	    {
		ulSchedTime = pRendInfo->m_ulNextDueSyncTime - ulCurrentPlayTime;
	    }
	    pRendInfo->m_pTimeSyncCallback->CallbackScheduled(
             m_pPlayer->m_pScheduler->RelativeEnter(pRendInfo->m_pTimeSyncCallback, ulSchedTime));
	}
    }

exit:
    m_pMutex->Unlock();
    m_bLocked	= FALSE;
    return theErr;
}

void		
SourceInfo::Resumed()
{
    if (m_bTobeInitializedBeforeBegin)
    {
	m_bTobeInitializedBeforeBegin = FALSE;

	if (m_pPlayer->m_uNumSourceToBeInitializedBeforeBegin > 0)
	{
	    m_pPlayer->m_uNumSourceToBeInitializedBeforeBegin--;
	}
    }
}

void
SourceInfo::UpdateDuration(UINT32 ulDuration)
{
    m_ulTotalTrackDuration = m_ulTrackDuration = m_ulSourceDuration = ulDuration;
    if (m_pPeerSourceInfo)
    {
	m_pPeerSourceInfo->m_ulTotalTrackDuration = m_ulTotalTrackDuration;
    }

    CHXMapLongToObj::Iterator i = m_pRendererMap->Begin();
    for (; i != m_pRendererMap->End(); ++i)
    {
	RendererInfo* pRendInfo = (RendererInfo*)(*i);

	IHXUpdateProperties* pUpdateProperties = NULL;
	if (pRendInfo->m_pRenderer &&
	    HXR_OK == pRendInfo->m_pRenderer->QueryInterface(IID_IHXUpdateProperties,
							     (void**)&pUpdateProperties))
	{
	    IHXValues* pValues = new CHXHeader();
	    pValues->AddRef();

	    pValues->SetPropertyULONG32("End", m_pSource->GetEndTime());
	    pValues->SetPropertyULONG32("Duration", ulDuration);

	    pUpdateProperties->UpdatePlayTimes(pValues);
	    HX_RELEASE(pValues);
	}
	HX_RELEASE(pUpdateProperties);

	pRendInfo->m_ulDuration = ulDuration;
	pRendInfo->m_pStreamInfo->m_ulDuration = ulDuration;
    }

    m_pPlayer->AdjustPresentationTime();
}

void
SourceInfo::UpdateDelay(UINT32 ulDelay)
{
    INT32   lOffset = 0;
    UINT32  ulStartTime = 0;
    UINT32  ulDuration = 0;

    ulStartTime = m_pSource->GetStartTime();
    ulDuration = GetActiveDuration();

    CHXMapLongToObj::Iterator i = m_pRendererMap->Begin();
    for (; i != m_pRendererMap->End(); ++i)
    {
	RendererInfo* pRendInfo = (RendererInfo*)(*i);
	lOffset = ulDelay - pRendInfo->m_pStreamInfo->m_ulDelay;

	// adjust the packet time offset within the core
	LISTPOSITION lPos = m_pPlayer->m_EventList.GetHeadPosition();
	while(lPos)
	{	    
	    CHXEvent* pEvent = (CHXEvent*)m_pPlayer->m_EventList.GetNext(lPos);
	    
	    if (pEvent->m_pRendererInfo == pRendInfo)
	    {
		pEvent->SetTimeOffset(ulStartTime - ulDelay); 
	    }
	}

	// adjust the packet time offset within the renderer
	IHXUpdateProperties* pUpdateProperties = NULL;
	if (pRendInfo->m_pRenderer &&
	    HXR_OK == pRendInfo->m_pRenderer->QueryInterface(IID_IHXUpdateProperties,
							     (void**)&pUpdateProperties))
	{
	    pUpdateProperties->UpdatePacketTimeOffset(lOffset);
	}
	HX_RELEASE(pUpdateProperties);

	pRendInfo->m_pStreamInfo->m_ulDelay = ulDelay;
	pRendInfo->m_ulDuration = ulDuration;
	pRendInfo->m_pStreamInfo->m_ulDuration = ulDuration;
    }

    m_pPlayer->AdjustPresentationTime();
}

#ifdef SEQ_DEPENDENCY
void
SourceInfo::SetDependency(IHXBuffer* pDependency)
{
    char* pTemp = = new char[pDependency->GetSize()+1];
    strcpy(pTemp, pDependency->GetBuffer()); /* Flawfinder: ignore */

    UINT16 i = 0;
    
    m_uNumDependencies = 1;

    /* Find the number of dependencies */
    while(pTemp[i])
    {
	if (pTemp[i] == '.')
	{
	    m_uNumDependencies++;
	}
	i++;
    }

    m_pDependNode = new UINT16[m_uNumDependencies];

    char* pNode = strtok(pTemp, ".\0");

    /* At least one node should be present */
    HX_ASSERT(pNode);

    i = 0;
    while(pNode)
    {
	m_pDependNode[i++] = atoi(pNode);
	pNode = strtok(NULL, ".\0");
    }

    HX_ASSERT(i == m_uNumDependencies);

    HX_VECTOR_DELETE(pTemp);
}
#endif /*SEQ_DEPENDENCY*/

BOOL		
SourceInfo::IsRebufferDone(void)
{
    BOOL	    bResult = TRUE;
    RendererInfo*   pRendInfo = NULL;

    if (m_pSource && m_pSource->m_bRebufferingRequired)
    {
	CHXMapLongToObj::Iterator i = m_pRendererMap->Begin();
	for (; i != m_pRendererMap->End(); ++i)
	{
	    pRendInfo = (RendererInfo*)(*i);
	    
	    if (!pRendInfo->m_bOnEndOfPacketSent)
	    {
		bResult = FALSE;
		break;
	    }
	}
    }

    return bResult;
}

void
SourceInfo::ReInitializeStats()
{
    if (!m_bInitialized)
    {
	return;
    }

#if defined(HELIX_FEATURE_STATS) && defined(HELIX_FEATURE_REGISTRY)
    CHXMapLongToObj::Iterator ndxRend = m_pRendererMap->Begin();
    for (UINT16 i = 0; ndxRend != m_pRendererMap->End(); ++ndxRend, i++)
    {
	RendererInfo*	pRendInfo = (RendererInfo*)(*ndxRend);

	HX_ASSERT(pRendInfo->m_pRenderer);
	if (!pRendInfo->m_pRenderer)
	{
	    return;
	}

	IHXStatistics* pStatistics	= NULL;
	STREAM_INFO*    pSrcStreamInfo  = NULL;
	if (HXR_OK == pRendInfo->m_pRenderer->QueryInterface(IID_IHXStatistics, (void**) &pStatistics))
	{
	    if (HXR_OK == m_pSource->GetStreamInfo(i, pSrcStreamInfo) &&
		pSrcStreamInfo && pSrcStreamInfo->m_pStats &&
		pSrcStreamInfo->m_pStats->m_pRenderer)
	    {
		pStatistics->InitializeStatistics(pSrcStreamInfo->m_pStats->m_pRenderer->m_ulRegistryID);
	    }

	    HX_RELEASE (pStatistics);
	}
    }
#endif /* HELIX_FEATURE_STATS && HELIX_FEATURE_REGISTRY */
}

HX_RESULT		
SourceInfo::HandleRedirectRequest(char* pURL)
{
    if (m_pPlayer)
    {
	return m_pPlayer->HandleRedirectRequest(m_uGroupID, m_uTrackID, pURL);
    }

    return HXR_FAILED;
}

void
SourceInfo::ScheduleProcessCallback()
{
    if (!m_pProcessCallback->GetPendingCallback())
    {
        m_pProcessCallback->CallbackScheduled(
         m_pPlayer->m_pScheduler->RelativeEnter(m_pProcessCallback, 0));
    }
}

void 
SourceInfo::SetLiveSyncStartTime(HXSource* pSource, RendererInfo* pTmpRendInfo, 
				 UINT32 ulLowestTime)
{
    UINT32 ulLiveSyncStartTime = ulLowestTime;
    INT32  lTimeOffset = pSource->GetStartTime() - pTmpRendInfo->m_pStreamInfo->m_ulDelay;

    if (pTmpRendInfo->m_pStreamInfo->m_pHeader)
    {
	if (lTimeOffset < 0)
	{
	    ulLiveSyncStartTime += (UINT32) (-lTimeOffset);
	}
	else
	{
	    if (ulLiveSyncStartTime < (UINT32)lTimeOffset)
	    {
		ulLiveSyncStartTime = 0;
	    }
	    else
	    {
		ulLiveSyncStartTime -= (UINT32)lTimeOffset;
	    }
	}

	pTmpRendInfo->m_pStreamInfo->m_pHeader->SetPropertyULONG32("LiveSyncStartTime", ulLiveSyncStartTime);
    }

    return;
}

BOOL
SourceInfo::AllOtherStreamsHaveEnded(STREAM_INFO* pThisStreamInfo)
{
    BOOL bResult = TRUE;
    CHXMapLongToObj::Iterator ndxRend = m_pRendererMap->Begin();
    for (; ndxRend != m_pRendererMap->End(); ++ndxRend)
    {
	RendererInfo* pRendInfo     = (RendererInfo*)(*ndxRend);
	STREAM_INFO*  pStreamInfo   = pRendInfo->m_pStreamInfo;

	/* 
	 * check if any other stream is still active.
	 * do not consider other "CanBeStoppedAnyTime" streams
	 * as active streams
	 */
	if (pStreamInfo != pThisStreamInfo &&
	    !pStreamInfo->m_bCanBeStoppedAnyTime &&
	    !pStreamInfo->m_bSrcInfoStreamDone)
	{
	    bResult = FALSE;
	    break;
	}
    }

    return bResult;
}

HX_RESULT
SourceInfo::AppendRepeatRequest(UINT16 uTrackID, IHXValues* pTrack)
{
#if defined(HELIX_FEATURE_SMIL_REPEAT)
    HX_RESULT	    theErr = HXR_OK;
    BOOL	    bLive = FALSE;
    BOOL	    bDurationSet = FALSE;
    BOOL	    bMaxDurationSet = FALSE;
    UINT32	    ulStart = 0;
    UINT32	    ulEnd = 0;
    UINT32	    ulDelay = 0;
    UINT32	    ulDuration = 0;   
    UINT32	    ulMaxDuration = 0;
    UINT32	    ulRepeatTag = RepeatUnknown;
    UINT32	    ulRepeatInterval = 0;
    UINT32	    ulLastDelay = 0;
    char	    szId[] = "id";
    char	    szStart[] = "Start";
    char	    szEnd[] = "End";
    char	    szDelay[] = "Delay";
    char	    szDuration[] = "Duration";
    char	    szMaxDuration[] = "maxduration";
    char	    szRepeatTag[] = "repeatTag";
    IHXBuffer*	    pBuffer = NULL;
    RepeatInfo*	    pNewRepeatInfo = NULL;

    HX_ASSERT(m_bLeadingSource);    

    if (HXR_OK != pTrack->GetPropertyCString(szId, pBuffer))
    {
	HX_ASSERT(FALSE);
	theErr = HXR_UNEXPECTED;
	goto cleanup;
    }

    // repeated sources should share the same start/end timing attributes
    if (HXR_OK == pTrack->GetPropertyULONG32(szStart,ulStart))
    {
	HX_ASSERT(ulStart == m_pSource->GetStartTime());
    }

    if (HXR_OK == pTrack->GetPropertyULONG32(szEnd,ulEnd))
    {
	HX_ASSERT(ulEnd == m_pSource->GetEndTime());
    }

    if (HXR_OK != pTrack->GetPropertyULONG32(szDelay,ulDelay))
    {
	HX_ASSERT(FALSE);
	theErr = HXR_UNEXPECTED;
	goto cleanup;
    }
    
    ulLastDelay = m_pRepeatList?((RepeatInfo*)m_pRepeatList->GetTail())->ulDelay:m_pSource->GetDelay();
    if (ulDelay <= ulLastDelay)
    {
	goto cleanup;
    }
    
    if (HXR_OK == pTrack->GetPropertyULONG32(szDuration,ulDuration))
    {
	bDurationSet = TRUE;
    }

    if (HXR_OK == pTrack->GetPropertyULONG32(szMaxDuration, ulMaxDuration))
    {
	bMaxDurationSet = TRUE;
    }
    
    // skip this repeat if duration == 0
    if ((bDurationSet && 0 == ulDuration) ||
	(bMaxDurationSet && 0 == ulMaxDuration))
    {
	goto durationset;
    }

    if (HXR_OK != pTrack->GetPropertyULONG32(szRepeatTag, ulRepeatTag))
    {
	ulRepeatTag = RepeatUnknown;
    }

    if (!m_pRepeatList)
    {
	m_pRepeatList = new CHXSimpleList();

	pNewRepeatInfo = new RepeatInfo;
	pNewRepeatInfo->ulStart = m_pSource->GetStartTime();
	pNewRepeatInfo->ulEnd = m_pSource->GetEndTime();
	pNewRepeatInfo->ulDelay = m_pSource->GetDelay();
	pNewRepeatInfo->ulDuration = m_pSource->m_ulOriginalDuration;
	pNewRepeatInfo->uTrackID = m_uTrackID;	
	m_curPosition = m_pRepeatList->AddHead(pNewRepeatInfo);
    }

    pNewRepeatInfo = new RepeatInfo;
    pNewRepeatInfo->ulStart = ulStart?ulStart:m_pSource->GetStartTime();
    pNewRepeatInfo->ulEnd = ulEnd?ulEnd:m_pSource->GetEndTime();
    pNewRepeatInfo->ulDelay = ulDelay;
    pNewRepeatInfo->uTrackID = uTrackID;
    // if the duration is set
    if (bDurationSet)
    {
	pNewRepeatInfo->ulDuration = ulDuration;
    }
    // if max. duration is set, make sure its duration <= ulMaxDuration
    else if (bMaxDurationSet && ulMaxDuration < m_pSource->m_ulOriginalDuration)
    {
	pNewRepeatInfo->ulDuration = ulMaxDuration;
    }
    // set to its leading source's original duration by default
    else
    {
	pNewRepeatInfo->ulDuration = m_pSource->m_ulOriginalDuration;
    }

    m_pRepeatList->AddTail(pNewRepeatInfo);

    if (ulRepeatTag == RepeatIndefiniteOnGroup ||
	ulRepeatTag == RepeatIndefiniteOnMe)
    {
	m_bRepeatIndefinite = TRUE;	    
	ulRepeatInterval = ulDelay - ulLastDelay;

	if (ulRepeatInterval > m_ulRepeatInterval)
	{
	    m_ulRepeatInterval = ulRepeatInterval;
	}
	
	// recaculate the max. duration for repeat="indefinite"
	if (bMaxDurationSet)
	{	    
	    m_ulMaxDuration = ulMaxDuration + m_pSource->m_ulOriginalDuration;
	    ulDuration = ulMaxDuration;
	    if (pNewRepeatInfo->ulDuration > ulMaxDuration)
	    {
		pNewRepeatInfo->ulDuration = ulMaxDuration;
	    }
	}
	else
	{
	    if (ulRepeatTag == RepeatIndefiniteOnGroup)
	    {
		ulDuration = m_pSource->m_ulOriginalDuration;
	    }
	    else
	    {
    		ulDuration = MAX_UINT32;
		bLive = TRUE;
	    }
	}

	if (m_pPeerSourceInfo)
	{
	    m_pPeerSourceInfo->m_bRepeatIndefinite = m_bRepeatIndefinite;
	    m_pPeerSourceInfo->m_ulRepeatInterval = m_ulRepeatInterval;
	    m_pPeerSourceInfo->m_ulMaxDuration = m_ulMaxDuration;
	}
    }
    else
    {
	ulDuration = pNewRepeatInfo->ulDuration;
    }

durationset:

    if (m_pRendererAdviseSink)
    {
	m_pRendererAdviseSink->RepeatedTrackDurationSet((const char*)pBuffer->GetBuffer(),
							ulDuration,
							bLive);
    }

cleanup:

    HX_RELEASE(pBuffer);

    return theErr;
#else
    return HXR_NOTIMPL;
#endif /* HELIX_FEATURE_SMIL_REPEAT */
}

BOOL		
SourceInfo::IsAdjustSeekNeeded(UINT32 ulSeekTime)
{
    BOOL bResult = TRUE;

    // after the repeated sources
    if (ulSeekTime > GetActiveDuration())
    {
	m_pPeerSourceInfo->Reset();
	bResult = FALSE;
    }
    // within the active source
    else if (ulSeekTime >= m_pSource->GetDelay() &&
	     ulSeekTime < m_pSource->GetDuration())
    {
	bResult = FALSE;
    }

    return bResult;
}

SourceInfo*		
SourceInfo::DoAdjustSeek(UINT32 ulSeekTime)
{    
#if defined(HELIX_FEATURE_SMIL_REPEAT)
    BOOL	    bRepeatDelayTimeOffsetAdjusted = FALSE;
    UINT32	    ulOriginalDuration = 0;
    UINT32	    ulOriginalDelay = 0;
    UINT32	    ulActiveDuration = 0;
    UINT32	    ulRepeatDelayTimeOffset = 0;
    SourceInfo*	    pResult = NULL;
    RepeatInfo*	    pRepeatInfo = NULL;
    RepeatInfo*	    pRepeatInfoHead = NULL;
    RepeatInfo*	    pRepeatInfoTail = NULL;
    CHXSimpleList*  pRepeatList = NULL;
    LISTPOSITION    pRepeatPos = 0;
   
    pRepeatPos = m_curPosition;
    ulOriginalDuration = m_pSource->m_ulOriginalDuration;
    ulOriginalDelay = m_pSource->m_ulOriginalDelay;
    ulActiveDuration = GetActiveDuration();
    pRepeatList = m_bLeadingSource?m_pRepeatList:m_pPeerSourceInfo->m_pRepeatList;

    // within the active source
    if (ulSeekTime >= m_pSource->GetDelay() &&
	ulSeekTime < m_pSource->GetDuration())
    {
	pResult = this;
	goto cleanup;
    }

    // after the repeated sources(exclude repeat="indefinite")
    if (!m_bRepeatIndefinite && ulSeekTime > ulActiveDuration)
    {
	pResult = this;
	goto cleanup;
    }

    // end the current source
    if (!m_pSource->IsSourceDone())
    {
	m_pSource->SetEndOfClip(TRUE);
    }
    m_bDone = TRUE;

    // seek to the next spawned source
    if (ulSeekTime >= m_pSource->GetDuration() &&
        ulSeekTime < m_pPeerSourceInfo->m_pSource->GetDuration())
    {
	pResult = m_pPeerSourceInfo;
	goto cleanup;
    }

    // seek before the repeated source
    if (ulSeekTime < ulOriginalDelay)
    {
	pResult = this;
	pResult->m_curPosition = pRepeatList->GetHeadPosition();
	
	if (pResult->m_ulRepeatDelayTimeOffset != 0)
	{
	    pResult->m_ulRepeatDelayTimeOffset = 0;
	    pResult->m_pPeerSourceInfo->m_ulRepeatDelayTimeOffset = 0;
	    bRepeatDelayTimeOffsetAdjusted = TRUE;
	}
    }
    else
    {
	// need to figure out the exact iteration on repeat indefinite
	if (m_bRepeatIndefinite)
	{
	    pRepeatInfoHead = (RepeatInfo*)pRepeatList->GetHead();
	    pRepeatInfoTail = (RepeatInfo*)pRepeatList->GetTail();
	    
	    ulRepeatDelayTimeOffset = pRepeatInfoTail->ulDelay + m_ulRepeatInterval - pRepeatInfoHead->ulDelay;
	}

	// ahead of the current source
	if (ulSeekTime < m_pSource->GetDelay())
	{
	    if (m_bRepeatIndefinite)
	    {
		// need to figure out the exact iteration on repeat indefinite
		while (m_ulRepeatDelayTimeOffset + pRepeatInfoHead->ulDelay > ulSeekTime)
		{
		    m_ulRepeatDelayTimeOffset -= ulRepeatDelayTimeOffset;
		    bRepeatDelayTimeOffsetAdjusted = TRUE;
		}

		// start from its tail
		if (bRepeatDelayTimeOffsetAdjusted)
		{
		    pRepeatPos = pRepeatList->GetTailPosition();
		}
	    }

	    pRepeatInfo = (RepeatInfo*)pRepeatList->GetAt(pRepeatPos);
	    while (pRepeatPos != pRepeatList->GetHeadPosition())
	    {
		if (ulSeekTime >= (pRepeatInfo->ulDelay + m_ulRepeatDelayTimeOffset))
		{
		    break;
		}
		pRepeatInfo = (RepeatInfo*)pRepeatList->GetAtPrev(pRepeatPos);
	    }
	}
	else
	{	    	    	    
	    if (m_bRepeatIndefinite)
	    {
		// need to figure out the exact iteration on repeat indefinite
		while (m_ulRepeatDelayTimeOffset + pRepeatInfoTail->ulDelay + ulOriginalDuration <
		       ulSeekTime)
		{
		    m_ulRepeatDelayTimeOffset += ulRepeatDelayTimeOffset;
		    bRepeatDelayTimeOffsetAdjusted = TRUE;
		}

		// start from its head
		if (bRepeatDelayTimeOffsetAdjusted)
		{
		    pRepeatPos = pRepeatList->GetHeadPosition();
		}
	    }

	    pRepeatInfo = (RepeatInfo*)pRepeatList->GetAt(pRepeatPos);
	    while (pRepeatPos != pRepeatList->GetTailPosition())
	    {
		if (ulSeekTime <= (m_ulRepeatDelayTimeOffset + pRepeatInfo->ulDelay + ulOriginalDuration))
		{
		    break;
		}
		pRepeatInfo = (RepeatInfo*)pRepeatList->GetAtNext(pRepeatPos);
	    }
	}

	pResult = m_pPeerSourceInfo;
	pResult->m_curPosition = pRepeatPos;
	pResult->m_bTobeInitializedBeforeBegin = TRUE;
    }

    if (bRepeatDelayTimeOffsetAdjusted)
    {
	pResult->m_ulRepeatDelayTimeOffset = m_ulRepeatDelayTimeOffset;
	pResult->m_pPeerSourceInfo->m_ulRepeatDelayTimeOffset = m_ulRepeatDelayTimeOffset;	
	m_pPlayer->AdjustPresentationTime();
    }

    pResult->Reset();
    pResult->m_pSource->ReSetup();

cleanup:

    if (pRepeatPos == pRepeatList->GetTailPosition() ||
	ulSeekTime > ulActiveDuration)
    {
	pResult->m_pPeerSourceInfo->Reset();
    }

    pResult->m_bRepeatPending = FALSE;
    pResult->m_pPeerSourceInfo->m_bRepeatPending = TRUE;

    if (pResult != this)
    {
	pResult->m_bSeekPending = TRUE;
	pResult->m_ulSeekTime = ulSeekTime;
    }

    return pResult;
#else
    return NULL;
#endif /* HELIX_FEATURE_SMIL_REPEAT */
}

UINT32
SourceInfo::GetActiveDuration()
{
    UINT32	    ulDuration = 0;
    RepeatInfo*	    pRepeatInfo = NULL;
    CHXSimpleList*  pRepeatList = m_bLeadingSource?m_pRepeatList:m_pPeerSourceInfo->m_pRepeatList;

    if (m_ulTotalTrackDuration)
    {
	return m_ulTotalTrackDuration;
    }

    if (pRepeatList)
    {
	pRepeatInfo = (RepeatInfo*)pRepeatList->GetTail();	
	ulDuration = pRepeatInfo->ulDuration + pRepeatInfo->ulDelay + m_ulRepeatDelayTimeOffset;

	if (m_bRepeatIndefinite &&
	    m_ulMaxDuration)
	{
	    ulDuration = m_pSource->m_ulOriginalDelay + m_ulMaxDuration;
	}
    }
    else
    {	
	ulDuration = m_pSource->GetDuration();
    }
    
    return ulDuration;
}

BOOL		
SourceInfo::KeepSourceActive(void)
{
    BOOL bResult = TRUE;
    CHXSimpleList* pRepeatList = m_bLeadingSource?m_pRepeatList:m_pPeerSourceInfo->m_pRepeatList;
    
    if (pRepeatList)
    {
	// XXX HP re-exam
	// we should keep the repeated source active till the end of its duration
	if (m_bRepeatIndefinite)
	{
	    if (m_ulMaxDuration &&
		m_pSource->GetDuration() >= m_pSource->m_ulOriginalDelay + m_ulMaxDuration)
	    {
		bResult = FALSE;
		goto cleanup;
	    }
	}
	else if (m_curPosition == pRepeatList->GetTailPosition())
	{
	    bResult = FALSE;
	    goto cleanup;
	}
    }
		
    if (m_pPlayer->IsAtSourceMap(this)	&& 
	!m_pSource->IsPaused()		&&
	GetActiveDuration() < m_pPlayer->m_ulCurrentPlayTime)
    {
	bResult = FALSE;
	goto cleanup;
    }

cleanup:

    return bResult;
}

void SourceInfo::TimeSyncCallback(void* pParam)
{
    struct timeSyncParamStruct* pObj = (struct timeSyncParamStruct*)pParam;

    if (pObj)
    {
        HX_RESULT theErr = pObj->pSourceInfo->OnTimeSync(pObj->pRenderInfo);
        //{FILE* f1 = ::fopen("c:\\temp\\ts.txt", "a+"); ::fprintf(f1, "%p TimeSyncCallback::Func: Before False\n", this);::fclose(f1);}
        //{FILE* f1 = ::fopen("c:\\temp\\ts.txt", "a+"); ::fprintf(f1, "%p TimeSyncCallback::Func: Before OnTimeSync\n", this);::fclose(f1);}
        //{FILE* f1 = ::fopen("c:\\temp\\ts.txt", "a+"); ::fprintf(f1, "%p TimeSyncCallback::Func: After OnTimeSync\n", this);::fclose(f1);}
        if (theErr)
        {
            pObj->pSourceInfo->m_pPlayer->Report( HXLOG_ERR, theErr, 0, NULL, NULL );
        }
    }
}

void SourceInfo::ProcessCallback(void* pParam)
{
    HX_RESULT theErr = HXR_OK;
    SourceInfo* pObj = (SourceInfo*)pParam;

    if (pObj)
    {
	if (pObj->m_pSource)
	{
	    theErr = pObj->m_pSource->ProcessIdle();
	}

	if (!theErr && 
            pObj->m_pPlayer && 
            pObj->m_pPlayer->IsInitialized() && 
            !pObj->IsInitialized())
	{
	    theErr = pObj->InitializeAndSetupRendererSites();
	}
    }
}
