/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: rtsptran.cpp,v 1.22.8.1 2004/07/09 02:04:41 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 "hxassert.h"
#include "debug.h"
#include "hxcom.h"
#include "hxmarsh.h"
#include "hxstrutl.h"
#include "netbyte.h"
#include "hxengin.h"
#include "ihxpckts.h"
#include "hxcomm.h"
#include "hxmon.h"
#include "netbyte.h"
#include "hxstring.h"
#include "chxpckts.h"
#include "hxslist.h"
#include "hxmap.h"
#include "hxdeque.h"
#include "hxbitset.h"
#include "timebuff.h"
#include "timeval.h"
#include "tconverter.h"
#include "rtptypes.h"
#include "rtspmsg.h"
#include "hxcorgui.h"

#include "ntptime.h"

#include "rtspif.h"
#include "rtsptran.h"
//#include "rtpwrap.h"	// Wrappers for PMC generated base classes
#include "basepkt.h"
#include "hxtbuf.h"
#include "transbuf.h"
#include "hxtick.h"
#include "random32.h"	// random32()
#include "pkthndlr.h"	// in rtpmisc for RTCP routine
#include "rtcputil.h"	// takes care of RTCP in RTP mode
#include "hxprefs.h"	// IHXPreferences
#include "hxmime.h"
#include "hxcore.h"

#include "hxheap.h"
#ifdef PAULM_IHXTCPSCAR
#include "objdbg.h"
#endif

#ifdef PAULM_TNGTCPTRANSTIMING
#include "classtimer.h"
ClassTimer g_TNGTCPTransTimer("TNGTCPTransport", 0, 3600);
#endif

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

#define STREAM_END_DELAY_BASE_TOLERANCE	3000

static const UINT32 TRANSPORT_BUF_GROWTH_RATE  = 1000;

void
dump(const char* pFile, const char* pc)
{
    FILE* fp = fopen(pFile, "a");
    if(!fp)
    {
	    return;
    }
    fprintf(fp, "%s", pc);
    fclose(fp);    
}


RTSPStreamData::RTSPStreamData(BOOL needReliable):
    m_seqNo(0),
    m_streamNumber(0),
    m_reliableSeqNo(0),
    m_lastTimestamp(0),
    m_pTransportBuffer(0),
    m_pResendBuffer(0),
    m_pStreamStats(0),
    m_bNeedReliable(needReliable),
    m_packetSent(FALSE),
    m_bReceivedAllPackets(FALSE),
    m_bNeedToACK(FALSE),
    m_bFirstPacket(TRUE),
    m_bUsesRTPPackets(FALSE),
    m_pTSConverter(NULL),
    m_pTSOrderHack(NULL),
    m_eMediaType(RTSPMEDIA_TYPE_UNKNOWN)
{
    ;
}

RTSPStreamData::~RTSPStreamData()
{
    if (m_pTransportBuffer)
    {
	delete m_pTransportBuffer;
    }

    if (m_pResendBuffer)
    {
	delete m_pResendBuffer;
    }

    HX_DELETE(m_pTSConverter);
    HX_DELETE(m_pTSOrderHack);
}

RTSPStreamHandler::RTSPStreamHandler(RTSPTransport* pOwner)
    : m_pOwner(pOwner)
    , m_lRefCount(0)
{
    m_pStreamDataMap = new CHXMapLongToObj;
}

RTSPStreamHandler::~RTSPStreamHandler()
{
    CHXMapLongToObj::Iterator i;
    RTSPStreamData* pStreamData;

    for(i=m_pStreamDataMap->Begin();i!=m_pStreamDataMap->End();++i)
    {
	pStreamData = (RTSPStreamData*)(*i);
	delete pStreamData;
    }
    delete m_pStreamDataMap;
}


HX_RESULT
RTSPStreamHandler::initStreamData(
    UINT16 streamNumber, BOOL needReliable, BOOL bIsSource, INT16 rtpPayloadType,
    BOOL bRecordFlag, UINT32 wrapSequenceNumber, UINT32 ulBufferDepth,
    BOOL bHasOutOfOrderTS, CHXTimestampConverter* pTSConverter,
    RTSPMediaType eMediaType)
{
    RTSPStreamData* pStreamData;

    if(!m_pStreamDataMap->Lookup(streamNumber, (void*&)pStreamData))
    {
	pStreamData = new RTSPStreamData(needReliable);
	pStreamData->m_streamNumber = streamNumber;
        pStreamData->m_pTSConverter = pTSConverter;
	pStreamData->m_eMediaType = eMediaType;
	pStreamData->m_lastSeqNo = 0;
	(*m_pStreamDataMap)[streamNumber] = pStreamData;

	if (!bIsSource)
	{
	    UINT32 ulSetMaximumBufferDepth;
	    UINT32 ulSetBufferDepth;

	    if (bRecordFlag && ulBufferDepth != TRANSPORT_BUF_DURATION_UNDEF)
	    {
	    	ulSetMaximumBufferDepth = ulBufferDepth;
	    	ulSetBufferDepth = ulBufferDepth;
	    }
	    else if (bRecordFlag)
	    {
	    	ulSetMaximumBufferDepth = MAX_TRANSPORT_BUF_DURATION;
	    	ulSetBufferDepth = MAX_TRANSPORT_BUF_DURATION;
	    }
	    else
	    {
	    	ulSetMaximumBufferDepth = MAX_TRANSPORT_BUF_DURATION;
	    	ulSetBufferDepth = TRANSPORT_BUF_DURATION;
	    }

	    RTSPTransportBuffer* pTransportBuffer = 
		new RTSPTransportBuffer(m_pOwner,
	                                streamNumber,
	                                ulSetBufferDepth,
	                                ulSetMaximumBufferDepth,
	                                TRANSPORT_BUF_GROWTH_RATE,
	                                wrapSequenceNumber);


	    pStreamData->m_pTransportBuffer = pTransportBuffer;
	    pStreamData->m_pStreamStats = NULL;
	    pStreamData->m_bUsesRTPPackets = bHasOutOfOrderTS;
	}

	return HXR_OK;
    }

    return HXR_FAIL;
}

RTSPStreamData*
RTSPStreamHandler::getStreamData(UINT16 streamNumber)
{
    RTSPStreamData* pStreamData = 0;

    if(!m_pStreamDataMap->Lookup(streamNumber, (void*&)pStreamData))
    {
	return 0;
    }

    return pStreamData;
}

RTSPStreamData*
RTSPStreamHandler::firstStreamData()
{
    streamIterator = m_pStreamDataMap->Begin();
    if(streamIterator == m_pStreamDataMap->End())
    {
	return 0;
    }
    return (RTSPStreamData*)(*streamIterator);
}

RTSPStreamData*
RTSPStreamHandler::nextStreamData()
{
    ++streamIterator;
    if(streamIterator == m_pStreamDataMap->End())
    {
	return 0;
    }
    return (RTSPStreamData*)(*streamIterator);
}

BOOL
RTSPStreamHandler::endStreamData()
{
    return (streamIterator == m_pStreamDataMap->End());
}

HX_RESULT
RTSPStreamHandler::createResendBuffer(UINT16 streamNumber,
				      UINT32 wrapSequenceNumber)
{
    RTSPStreamData* pStreamData;

    if(!m_pStreamDataMap->Lookup(streamNumber, (void*&)pStreamData))
    {
	return HXR_FAILED;
    }

    RTSPResendBuffer* pResendBuffer;

    pResendBuffer = new RTSPResendBuffer(RESEND_BUF_DURATION,
                                         MAX_RESEND_BUF_DURATION,
                                         RESEND_BUF_GROWTH_RATE,
                                         wrapSequenceNumber);

    pStreamData->m_pResendBuffer = pResendBuffer;
    return HXR_OK;
}

RTSPResendBuffer*
RTSPStreamHandler::getResendBuffer(UINT16 streamNumber)
{
    RTSPStreamData* pStreamData = 0;

    if(!m_pStreamDataMap->Lookup(streamNumber, (void*&)pStreamData))
    {
	return 0;
    }
    return pStreamData->m_pResendBuffer;
}

/*
 * RTSPTransport methods
 */

RTSPTransport::RTSPTransport(BOOL bIsSource):
    m_pContext(NULL),
    m_pCommonClassFactory(0),
    m_pScheduler(0),
    m_pResp(0),
    m_pStreamHandler(0),
    m_pRegistry(0),
    m_pInternalReset(0),
    m_pSrcBufferStats(0),
    m_bIsSource(bIsSource),
    m_bIsInitialized(FALSE),
    m_bIsUpdated(FALSE),
    m_ulPacketsSent(0),
    m_lBytesSent(0),
    m_bIsReceivedData(FALSE),
    m_bSourceDone(FALSE),
    m_bHackedRecordFlag(FALSE),
    m_wrapSequenceNumber(0),
    m_bMulticast(FALSE),
    m_pPlayerState(NULL),
    m_pPacketFilter(NULL),
    m_pClientPacketList(NULL),
    m_bPrefetch(FALSE),
    m_bFastStart(FALSE),
    m_ulPlayRangeFrom(RTSP_PLAY_RANGE_BLANK),
    m_ulPlayRangeTo(RTSP_PLAY_RANGE_BLANK),
    m_bPlayRequestSent(FALSE),
    m_packets_since_last_drop(0),
    m_ulTotalSuccessfulResends(0),
    m_ulTotalFailedResends(0),
    m_ulSendingTime(0),
    m_drop_packets(FALSE),
    m_bSkipTimeAdjustment(FALSE)
#ifdef RDT_MESSAGE_DEBUG
    ,m_bRDTMessageDebug(FALSE)
#endif    
{
    m_ulStartTime = HX_GET_TICKCOUNT();
}

RTSPTransport::~RTSPTransport()
{
    HX_RELEASE(m_pCommonClassFactory);
    HX_RELEASE(m_pScheduler);
    HX_RELEASE(m_pResp);
    HX_RELEASE(m_pRegistry);
    if (m_pStreamHandler)
    {
        m_pStreamHandler->Release();
        m_pStreamHandler = NULL;
    }
    HX_RELEASE(m_pInternalReset);
    HX_RELEASE(m_pSrcBufferStats);
    HX_RELEASE(m_pPlayerState);
    HX_RELEASE(m_pPacketFilter);
    HX_RELEASE(m_pContext);

    HX_DELETE(m_pClientPacketList);
}

void
RTSPTransport::addStreamInfo(RTSPStreamInfo* pStreamInfo, UINT32 ulBufferDepth)
{
    if(pStreamInfo)
    {
	if(!m_pStreamHandler)
	{
	    m_pStreamHandler = new RTSPStreamHandler(this);
	    m_pStreamHandler->AddRef();
	}

	CHXTimestampConverter*  pTSConverter = NULL;

	if (pStreamInfo->m_HXFactor && pStreamInfo->m_RTPFactor)
	{
	    pTSConverter = new CHXTimestampConverter(CHXTimestampConverter::FACTORS,
						     pStreamInfo->m_HXFactor,
						     pStreamInfo->m_RTPFactor);
	 }
	 else if (pStreamInfo->m_sampleRate)
	 {
	     
	     pTSConverter =  new CHXTimestampConverter(CHXTimestampConverter::SAMPLES,
						       pStreamInfo->m_sampleRate);
	 }	

	 m_pStreamHandler->initStreamData(
	     pStreamInfo->m_streamNumber,
	     pStreamInfo->m_bNeedReliablePackets,
	     m_bIsSource,
	     pStreamInfo->m_rtpPayloadType,
	     m_bHackedRecordFlag,
	     m_wrapSequenceNumber,
	     ulBufferDepth,
	     pStreamInfo->m_bHasOutOfOrderTS,
	     pTSConverter,
	     pStreamInfo->m_eMediaType);

        m_bSkipTimeAdjustment = pStreamInfo->m_bRealMedia;

	RTSPStreamData* pStreamData = NULL;
	pStreamData = m_pStreamHandler->getStreamData(pStreamInfo->m_streamNumber);
	if (pStreamData && pStreamData->m_pTransportBuffer && m_bPrefetch)
	{
	    pStreamData->m_pTransportBuffer->EnterPrefetch();
	}
    }
}

void
RTSPTransport::setFirstTimeStamp(UINT16 uStreamNumber, UINT32 ulTS, 
                                 BOOL bIsRaw)
{
    RTSPStreamData* pStreamData = 
	m_pStreamHandler->getStreamData(uStreamNumber);

    if (pStreamData)
    {
	if (pStreamData->m_pTSConverter)
	{
	    pStreamData->m_pTSConverter->setHXAnchor(ulTS);
	}
	    	    
	HX_DELETE(pStreamData->m_pTSOrderHack);
    }

    if (!m_bIsSource)
    {
	RTSPTransportBuffer* pTransportBuffer = pStreamData->m_pTransportBuffer;
		
	if (pTransportBuffer && pStreamData)
	{
	    if ((m_ulPlayRangeFrom != RTSP_PLAY_RANGE_BLANK) &&
		(m_ulPlayRangeTo != RTSP_PLAY_RANGE_BLANK))
	    {
		if ((pStreamData->m_eMediaType == RTSPMEDIA_TYPE_AUDIO) ||
		    (pStreamData->m_eMediaType == RTSPMEDIA_TYPE_VIDEO))
		{
		    // For audio & video media, we'll inform transport of
		    // the media duration to help determining stream termination
		    pStreamData->m_pTransportBuffer->InformTimestampRange(
			m_ulPlayRangeFrom,
			m_ulPlayRangeTo,
			STREAM_END_DELAY_BASE_TOLERANCE);
		}
	    }
	}
    }
}

void 
RTSPTransport::setPlayRange(UINT32 ulFrom, UINT32 ulTo)
{
    // this is the Range values in PLAY request in RMA time (ms) called on PLAY 
    // request
    m_ulPlayRangeFrom = ulFrom; 
    m_ulPlayRangeTo = ulTo;
}

void
RTSPTransport::setSessionID(const char* pSessionID)
{
    m_sessionID = pSessionID;
}

UINT16
RTSPTransport::getSeqNum(UINT16 streamNumber)
{
    RTSPStreamData* pStreamData;

    pStreamData = m_pStreamHandler->getStreamData(streamNumber);

    if(pStreamData)
    {
	return pStreamData->m_seqNo;
    }
    else
    {
	return 0;	//XXXBAB - 0xffff?
    }
}

UINT32
RTSPTransport::getTimestamp(UINT16 streamNumber)
{
    RTSPStreamData* pStreamData;

    pStreamData = m_pStreamHandler->getStreamData(streamNumber);

    if(pStreamData)
    {
	// XXXGo - this is RTP time w/ offset (NOT RMA time) if RTPUDPTransprot...
	return pStreamData->m_lastTimestamp;
    }
    else
    {
	return 0;
    }
}

HX_RESULT
RTSPTransport::getPacket(UINT16 uStreamNumber, IHXPacket*& pPacket)
{
    RTSPTransportBuffer* pTransportBuffer = getTransportBuffer(uStreamNumber);
    RTSPStreamData* pStreamData = m_pStreamHandler->getStreamData(uStreamNumber);

    if ((!pTransportBuffer) || (!pStreamData))
    {
	return HXR_FAIL;
    }

    ClientPacket* clientPacket;

    HX_RESULT result = pTransportBuffer->GetPacket(clientPacket);

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

    pPacket = clientPacket->GetPacket();

    if (!pPacket)
    {
	/*
	 * This is a lost packet
	 */

	result = m_pCommonClassFactory->CreateInstance(CLSID_IHXPacket,
	                                               (void**)&pPacket);

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

	UINT8 unASMFlags = 0;
	UINT32 ulTime = 0;
	if (clientPacket->IsDroppedPacket())
	{
	    // Preserve dropped flag as an ASM flag.
	    // This allows other code along the packet
	    // path to differentiate this packet from a 
	    // true lost packet.
	    unASMFlags |= HX_ASM_DROPPED_PKT;

	    // We have a valid timestamp for a dropped
	    // packet so lets put it in the IHXPacket
	    ulTime = clientPacket->GetTime();
	}

	pPacket->Set(0, ulTime, uStreamNumber, unASMFlags, 0);
	pPacket->SetAsLost();
    }
    else if (pStreamData->m_bUsesRTPPackets)
    {
	if (!pStreamData->m_pTSOrderHack)
	{
	    pStreamData->m_pTSOrderHack = new RTSPStreamData::TSOrderHackInfo();
	    
	    if (pStreamData->m_pTSOrderHack)
	    {
		pStreamData->m_pTSOrderHack->m_ulLastSentTS = 
		    pPacket->GetTime();
		pStreamData->m_pTSOrderHack->m_ulLastRecvTS = 
		    pStreamData->m_pTSOrderHack->m_ulLastSentTS;
	    }
	}

	if (pStreamData->m_pTSOrderHack)
	{
	    IHXBuffer* pBuf=NULL;	    
	    UINT32 ulHX;
	    UINT32 ulRTP;
	    UINT16 unStrmNo;
	    UINT8  uchASMFlag;
	    UINT16 unRuleNo;

	    // pkts've been sorted based on seq_no
	    IHXRTPPacket* pRTPPacket = NULL;

	    pPacket->QueryInterface(IID_IHXRTPPacket, (void**) &pRTPPacket);

	    if (pRTPPacket)
	    {
		// RTP transport generates RTP packets
		result = pRTPPacket->GetRTP(pBuf, ulHX, ulRTP, 
					    unStrmNo, uchASMFlag, unRuleNo);
	    }
	    else
	    {
		// RDT transport generates RMA packets
		result = pPacket->Get(pBuf, ulHX,
				      unStrmNo, uchASMFlag, unRuleNo);

		if (pStreamData->m_pTSConverter)
		{
		    ulRTP = pStreamData->m_pTSConverter->hxa2rtp(ulHX);
		}
		else
		{
		    ulRTP = ulHX;
		}
	    }

	    HX_ASSERT(result == HXR_OK);

	    if (result == HXR_OK)
	    {
		if (((LONG32) (ulHX - pStreamData->m_pTSOrderHack->m_ulLastSentTS)) > 0)
		{
		    pStreamData->m_pTSOrderHack->m_ulLastSentTS = ulHX;
		    pStreamData->m_pTSOrderHack->m_ulLastRecvTS = ulHX;
		}
		else if (ulHX == pStreamData->m_pTSOrderHack->m_ulLastRecvTS)
		{
		    ulHX = pStreamData->m_pTSOrderHack->m_ulLastSentTS;	    	    
		}
		else
		{
		    pStreamData->m_pTSOrderHack->m_ulLastRecvTS = ulHX;
		    ulHX = (++pStreamData->m_pTSOrderHack->m_ulLastSentTS);
		}
		
		HX_RELEASE(pRTPPacket);
		HX_RELEASE(pPacket);
		pRTPPacket = new CHXRTPPacket;
		pRTPPacket->AddRef();
		result = pRTPPacket->SetRTP(pBuf,
					    ulHX,
					    ulRTP,
					    unStrmNo,
					    uchASMFlag,
					    unRuleNo);
		HX_ASSERT(result == HXR_OK);		
		pRTPPacket->QueryInterface(IID_IHXPacket, (void**) &pPacket);
		HX_ASSERT(pPacket);
	    }
	    HX_RELEASE(pBuf);

	    HX_RELEASE(pRTPPacket);
	}
    }

    /*
     * No longer need the ClientPacket wrapper
     */
    HX_RELEASE(clientPacket);    

    return HXR_OK;
}

HX_RESULT
RTSPTransport::startPackets(UINT16 uStreamNumber)
{
    RTSPTransportBuffer* pTransportBuffer = getTransportBuffer(uStreamNumber);

    if (!pTransportBuffer)
    {
	return HXR_FAIL;
    }

    return pTransportBuffer->StartPackets();
}

HX_RESULT
RTSPTransport::stopPackets(UINT16 uStreamNumber)
{
    RTSPTransportBuffer* pTransportBuffer = getTransportBuffer(uStreamNumber);

    if (!pTransportBuffer)
    {
	return HXR_FAIL;
    }

    return pTransportBuffer->StopPackets();
}

void
RTSPTransport::SetFilterResponse(RawPacketFilter* pResp)
{
    HX_RELEASE(m_pPacketFilter);
    m_pPacketFilter = pResp;
    if (m_pPacketFilter)
    {
	m_pPacketFilter->AddRef();
    }
}

void
RTSPTransport::FilterPacket(IHXPacket* pPacket)
{
    UINT16 uStreamNumber;

    ClientPacket* clientPacket = 0;
    clientPacket = (ClientPacket*)m_pClientPacketList->RemoveTail();
    clientPacket->SetPacket(pPacket);
    
    uStreamNumber = clientPacket->GetStreamNumber();
    RTSPTransportBuffer* pTransportBuffer = getTransportBuffer(uStreamNumber);
    pTransportBuffer->Add(clientPacket);
}

void RTSPTransport::GetContext(IUnknown*& pContext)   
{   
	pContext = m_pContext;   

	if (pContext)   
	{   
		pContext->AddRef();   
	}   
} 

void 
RTSPTransport::LeavePrefetch(void)
{
    RTSPStreamData* pStreamData = NULL; 
    
    m_bPrefetch = FALSE;

    HX_ASSERT(m_pStreamHandler);

    pStreamData = m_pStreamHandler->firstStreamData();
    while(pStreamData)    
    {
	if (pStreamData->m_pTransportBuffer)
	{
	    pStreamData->m_pTransportBuffer->LeavePrefetch();
	}
	pStreamData = m_pStreamHandler->nextStreamData();
    }

    return;
}

void 
RTSPTransport::EnterFastStart(void)
{
    RTSPStreamData* pStreamData = NULL; 
    
    m_bFastStart = TRUE;

    HX_ASSERT(m_pStreamHandler);

    pStreamData = m_pStreamHandler->firstStreamData();
    while(pStreamData)    
    {
	if (pStreamData->m_pTransportBuffer)
	{
	    pStreamData->m_pTransportBuffer->EnterFastStart();
	}
	pStreamData = m_pStreamHandler->nextStreamData();
    }

    return;
}

void 
RTSPTransport::LeaveFastStart(void)
{
    RTSPStreamData* pStreamData = NULL; 
    
    m_bFastStart = FALSE;

    HX_ASSERT(m_pStreamHandler);

    pStreamData = m_pStreamHandler->firstStreamData();
    while(pStreamData)    
    {
	if (pStreamData->m_pTransportBuffer)
	{
	    pStreamData->m_pTransportBuffer->LeaveFastStart();
	}
	pStreamData = m_pStreamHandler->nextStreamData();
    }

    return;
}

BOOL 
RTSPTransport::isSparseStream(UINT16 uStreamNumber)
{
    BOOL		bResult = FALSE;
    const char*		pMimeType = NULL;
    IUnknown*		pUnknown = NULL;
    IHXStreamSource*	pStreamSource = NULL;    
    IHXStream*		pStream = NULL;

    if (m_pContext  &&
	HXR_OK == m_pContext->QueryInterface(IID_IHXStreamSource, (void**)&pStreamSource))
    {
	if (HXR_OK == pStreamSource->GetStream(uStreamNumber, pUnknown))
	{
	    if (HXR_OK == pUnknown->QueryInterface(IID_IHXStream, (void**)&pStream))
	    {
		pMimeType = pStream->GetStreamType();

		// special handling for sparsed streams for multicast
		if (pMimeType							&&
		    (strcasecmp(SYNCMM_MIME_TYPE, pMimeType) == 0		||
		     strcasecmp(REALEVENT_MIME_TYPE, pMimeType) == 0		||
		     strcasecmp("application/vnd.rn-realtext", pMimeType) == 0	||
		     strcasecmp("application/x-pn-realtext", pMimeType) == 0))
		{
		    bResult = TRUE;
		}
	    }
	    HX_RELEASE(pStream);
	}
	HX_RELEASE(pUnknown);
    }
    HX_RELEASE(pStreamSource);

    return bResult;
}

HX_RESULT
RTSPTransport::storePacket(IHXPacket* pPacket,
                           UINT16 uStreamNumber,
                           UINT16 uSeqNo,
                           UINT16 uReliableSeqNo,
                           BOOL isReliable)
{
    RTSPTransportBuffer* pTransportBuffer = getTransportBuffer(uStreamNumber);

    if (!pTransportBuffer)
    {
	return HXR_FAIL;
    }

    ClientPacket* clientPacket = 0;
    
    m_bIsReceivedData = TRUE;

    if (pPacket->IsLost())
    {
        clientPacket = new ClientPacket(uSeqNo, uReliableSeqNo,
		                        pPacket->GetTime(), 0, isReliable,
		                        0, pTransportBuffer->GetTime(), FALSE);

	clientPacket->AddRef();

	return pTransportBuffer->Add(clientPacket);
    }
    
    IHXBuffer* pBuffer = pPacket->GetBuffer();

    if (m_pPacketFilter)
    {
	if (!m_pClientPacketList)
	{
	    m_pClientPacketList = new CHXSimpleList;
	    m_pPacketFilter->SetFilterResponse(this);
	}
	clientPacket = new ClientPacket(uSeqNo, uReliableSeqNo,
		pPacket->GetTime(), pBuffer->GetSize(), isReliable,
		NULL, pTransportBuffer->GetTime(),
		FALSE);
	
	clientPacket->AddRef();
	pBuffer->Release();

	m_pClientPacketList->AddHead((void*)clientPacket);
	m_pPacketFilter->FilterPacket(pPacket);

	// just guessing on what this should be?????
        return HXR_OK;
    }
    else
    {
       clientPacket = new ClientPacket(uSeqNo, uReliableSeqNo,
		pPacket->GetTime(), pBuffer->GetSize(), isReliable,
		pPacket, pTransportBuffer->GetTime(), FALSE);

	clientPacket->AddRef();
	pBuffer->Release();

	return pTransportBuffer->Add(clientPacket);
    }
}

HX_RESULT			
RTSPTransport::packetReady(HX_RESULT status, RTSPStreamData* pStreamData, IHXPacket* pPacket)
{
    HX_RESULT result = HXR_OK;

    ASSERT(!m_bIsSource);

    if (!pStreamData)
    {
	return HXR_UNEXPECTED;
    }

    HX_ASSERT(pStreamData);
    
    if (pPacket)
    {
	if (pStreamData->m_bUsesRTPPackets)
	{
	    IHXRTPPacket* pRTPPacket = NULL;

	    pPacket->QueryInterface(IID_IHXRTPPacket, (void**) &pRTPPacket);

	    if (pRTPPacket)
	    {
		// This already is RTP Packet - proceed
		pRTPPacket->Release();
	    }
	    else
	    {
		// Must Transfer to an RTP Packet
		result = m_pCommonClassFactory->CreateInstance(
				CLSID_IHXRTPPacket,
				(void**) &pRTPPacket);

		if (pRTPPacket)
		{
		    ULONG32 ulTime;
		    ULONG32 ulRTPTime;
		    UINT16 uStreamNumber;
		    UINT8 unASMFlags;
		    UINT16 unASMRuleNumber;
		    BOOL bIsLost = pPacket->IsLost();
		    IHXBuffer* pBuffer = NULL;

		    pPacket->Get(pBuffer,
				 ulTime,
				 uStreamNumber,
				 unASMFlags,
				 unASMRuleNumber);

		    if (pStreamData->m_pTSConverter)
		    {
			ulRTPTime = pStreamData->m_pTSConverter->hxa2rtp(ulTime);
		    }
		    else
		    {
			ulRTPTime = ulTime;
		    }

		    pRTPPacket->SetRTP(pBuffer,
				       ulTime,
				       ulRTPTime,
				       uStreamNumber,
				       unASMFlags,
				       unASMRuleNumber);

		    if (bIsLost)
		    {
			pRTPPacket->SetAsLost();
		    }

		    HX_RELEASE(pBuffer);

		    result = m_pResp->PacketReady(status, m_sessionID, pRTPPacket);
		    pRTPPacket->Release();
		}

		return result;
	    }
	}
    }
    else
    {
	/*
	 * This is a lost packet
	 */
	if (pStreamData->m_bUsesRTPPackets)
	{
	    result = m_pCommonClassFactory->CreateInstance(
			CLSID_IHXRTPPacket,
			(void**) &pPacket);
	}
	else
	{
	    result = m_pCommonClassFactory->CreateInstance(
			CLSID_IHXPacket,
			(void**) &pPacket);
	}
	    
	if (pPacket)
	{
	    pPacket->Set(0, 0, pStreamData->m_streamNumber, 0, 0);
	    pPacket->SetAsLost();
	    result = m_pResp->PacketReady(status, m_sessionID, pPacket);
	    pPacket->Release();
	}
	return result;
    }

    return m_pResp->PacketReady(status, m_sessionID, pPacket);
}

HX_RESULT
RTSPTransport::packetReady(HX_RESULT status, UINT16 uStreamNumber, IHXPacket* pPacket)
{
    RTSPStreamData* pStreamData = NULL;

    ASSERT(!m_bIsSource);

    pStreamData = m_pStreamHandler->getStreamData(uStreamNumber);

    HX_ASSERT(pStreamData);

    return packetReady(status, pStreamData, pPacket);
}

HX_RESULT
RTSPTransport::getStatus
(
    UINT16& uStatusCode, 
    IHXBuffer*& pStatusDesc, 
    UINT16& ulPercentDone
)
{
#if 0
    if (!m_pStreamHandler)
    {
	uStatusCode = HX_STATUS_INITIALIZING;
	pStatusDesc = 0;
	ulPercentDone = 0;

	return HXR_OK;
    }

    uStatusCode = HX_STATUS_READY;
    pStatusDesc = 0;
    ulPercentDone = 100;

    RTSPStreamData* pStreamData = m_pStreamHandler->firstStreamData();

    ASSERT(pStreamData);

    while(pStreamData)
    {
	UINT16 tempStatusCode;
	UINT16 tempPercentDone;

	pStreamData->m_pTransportBuffer->GetStatus(tempStatusCode,
	                                           tempPercentDone);

	/*
	 * The status is always that of the stream which is least ready
	 */

	if (tempStatusCode < uStatusCode)
	{
	    uStatusCode	    = tempStatusCode;
	    ulPercentDone   = tempPercentDone;
	}
	else if (tempStatusCode == uStatusCode && 
		 tempPercentDone < ulPercentDone)
	{
	    ulPercentDone = tempPercentDone;
	}

	pStreamData = m_pStreamHandler->nextStreamData();
    }

    return HXR_OK;
#else
    return HXR_NOTIMPL;
#endif
}

HX_RESULT
RTSPTransport::GetCurrentBuffering(UINT16  uStreamNumber,
				   INT64&  llLowestTimestamp, 
				   INT64&  llHighestTimestamp,
				   UINT32& ulNumBytes,
				   BOOL&   bDone)
{
    if (!m_pStreamHandler)
    {
	return HXR_OK;
    }

    RTSPStreamData* pStreamData;

    pStreamData = m_pStreamHandler->getStreamData(uStreamNumber);

    HX_ASSERT(pStreamData);

    return pStreamData ? 
           pStreamData->m_pTransportBuffer->GetCurrentBuffering(
                                            llLowestTimestamp,
                                            llHighestTimestamp,
                                            ulNumBytes,
                                            bDone) : HXR_OK;
}

HX_RESULT
RTSPTransport::SeekFlush(UINT16 uStreamNumber)
{
    if (!m_pStreamHandler)
    {
	return HXR_OK;
    }

    RTSPStreamData* pStreamData;

    pStreamData = m_pStreamHandler->getStreamData(uStreamNumber);

    HX_ASSERT(pStreamData);

    if (pStreamData)
    {
	pStreamData->m_pTransportBuffer->SeekFlush();
    }

    return HXR_OK;
}

BOOL
RTSPTransport::IsSourceDone(void)
{
    return m_bSourceDone;
}

void      
RTSPTransport::CheckForSourceDone(UINT16 uStreamNumber)
{
    if (m_bSourceDone)
    {
	return;
    }

    HX_ASSERT(m_pStreamHandler);

    if (!m_pStreamHandler)
    {
	return;
    }

    RTSPStreamData* pStreamData = m_pStreamHandler->firstStreamData();
    HX_ASSERT(pStreamData);

    m_bSourceDone = TRUE;
    while(pStreamData)
    {
	if (pStreamData->m_streamNumber == uStreamNumber)
	{
	    pStreamData->m_bReceivedAllPackets = TRUE;
	}
	else if (!pStreamData->m_bReceivedAllPackets)
	{
	    m_bSourceDone = FALSE;
	    return;
	}

	pStreamData = m_pStreamHandler->nextStreamData();
    }

    /* We have received all the packets... Tell the reposnse object */
    if (!m_bIsSource)
    {
	m_pResp->OnSourceDone();
    }

}    

void      
RTSPTransport::HandleBufferError()
{
    if (m_pResp)
    {
	m_pResp->OnProtocolError(HXR_BUFFERING);
    }
}

RTSPResendBuffer*
RTSPTransport::getResendBuffer(UINT16 uStreamNumber)
{
    return m_pStreamHandler->getResendBuffer(uStreamNumber);
}

HX_RESULT
RTSPTransport::initializeStatistics
(
    UINT32 ulRegistryID
)
{
    m_bIsInitialized = TRUE;

    if (m_bIsSource)
    {
	m_ulRegistryID = ulRegistryID;
    }
    else
    {
	// XXX HP
	// rather than create a new copy of STREAM_STATS, we simply
	// obtain the same STREAM_STATS* from RTSPProtocol via SetStatistics().
	// this could save us on both the memory and several ms during the startup
/*
	IHXBuffer* pParentName = NULL;
	CHAR RegKeyName[MAX_DISPLAY_NAME] = {0};

	RTSPStreamData* pStreamData = m_pStreamHandler->firstStreamData();

	ASSERT(pStreamData);

	if (!m_pRegistry)
	{
	    return HXR_FAIL;
	}

	HX_RESULT result = m_pRegistry->GetPropName(ulRegistryID, pParentName);

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

	// create registry entries of each stream
	while (pStreamData)
	{
	    UINT16 uStreamNumber = pStreamData->m_streamNumber;

	    sprintf(RegKeyName, "%s.Stream%d", pParentName->GetBuffer(),
	                                       uStreamNumber);
	
	    UINT32 uStreamId = m_pRegistry->GetId(RegKeyName);

	    // create stream reg key if it has not been created
	    // by the source yet
	    if (!uStreamId)
	    {
		return HXR_FAIL;
	    }

	    pStreamData->m_pStreamStats = new STREAM_STATS(m_pRegistry,
	                                                   uStreamId);
							   
	    pStreamData = m_pStreamHandler->nextStreamData();
	}

	pParentName->Release();
*/
    }

    return HXR_OK;
}

HX_RESULT
RTSPTransport::SetStatistics(UINT16 uStreamNumber, STREAM_STATS* pStats)
{
    HX_RESULT	    rc = HXR_OK;
#if defined(HELIX_FEATURE_STATS) && defined(HELIX_FEATURE_REGISTRY)
    RTSPStreamData* pStreamData = NULL;

    if (!m_pStreamHandler)
    {
	rc = HXR_FAILED;
	goto cleanup;
    }

    pStreamData = m_pStreamHandler->getStreamData(uStreamNumber);
    if (!pStreamData)
    {
	rc = HXR_FAILED;
	goto cleanup;
    }

    pStreamData->m_pStreamStats = pStats;

cleanup:
#endif /* HELIX_FEATURE_STATS && HELIX_FEATURE_REGISTRY */

    return rc;
}

HX_RESULT 
RTSPTransport::updateStatistics(BOOL bUseRegistry)
{
    m_bIsUpdated = TRUE;

#if defined(HELIX_FEATURE_STATS) && defined(HELIX_FEATURE_REGISTRY)
    if (m_bIsSource)
    {
	RTSPStreamData* pStreamData = m_pStreamHandler->firstStreamData();

	if (!pStreamData)
	{
	    return HXR_FAIL;
	}

	/*
	 * XXXGH...streams are managed by the PPM and should not show up
	 *         under the transport
	 */
	while (pStreamData)
	{
	    UINT32 ulResendSuccess = 0;
	    UINT32 ulResendFailure = 0;

	    if (pStreamData->m_pResendBuffer)
	    {
		pStreamData->m_pResendBuffer->UpdateStatistics(ulResendSuccess,
		                                               ulResendFailure);
	    }

	    m_ulTotalSuccessfulResends += ulResendSuccess;
	    m_ulTotalFailedResends += ulResendFailure;

	    pStreamData = m_pStreamHandler->nextStreamData();
	}

        m_ulSendingTime = CALCULATE_ELAPSED_TICKS(m_ulStartTime, HX_GET_TICKCOUNT()) / 1000;

	IHXBuffer* pName;

	if (bUseRegistry
        &&  m_pRegistry 
        &&  HXR_OK == m_pRegistry->GetPropName(m_ulRegistryID, pName))
	{
	    char str[512]; /* Flawfinder: ignore */
	    const char* name = (const char*)pName->GetBuffer();
	    char pTemp[32]; /* Flawfinder: ignore */

	    i64toa(m_lBytesSent, pTemp, 10);

	    sprintf(str, "%-.400s.PacketsSent", name); /* Flawfinder: ignore */
	    m_pRegistry->AddInt(str, m_ulPacketsSent);
	    sprintf(str, "%-.400s.BytesSent", name); /* Flawfinder: ignore */
	    m_pRegistry->AddInt(str, INT64_TO_INT32(m_lBytesSent));

	    // Add the total bytes sent as a string because we don't want
	    // to have to truncate the INT64 value that is storing it
	    sprintf(str, "%-.400s.TotalBytesSent", name); /* Flawfinder: ignore */
	    IHXBuffer* pBuffer = new CHXBuffer();
	    pBuffer->AddRef();
	    pBuffer->Set((UCHAR*)pTemp, strlen(pTemp) + 1);
	    m_pRegistry->AddStr(str, pBuffer);
	    HX_RELEASE(pBuffer);

	    sprintf(str, "%-.400s.SendingTime", name); /* Flawfinder: ignore */
	    m_pRegistry->AddInt(str, m_ulSendingTime); 
	    sprintf(str, "%-.400s.ResendSuccess", name); /* Flawfinder: ignore */
	    m_pRegistry->AddInt(str, m_ulTotalSuccessfulResends);
	    sprintf(str, "%-.400s.ResendFailure", name); /* Flawfinder: ignore */
	    m_pRegistry->AddInt(str, m_ulTotalFailedResends);
	    pName->Release();
	}
    }
    else
    {
	ULONG32 ulNormal = 0;
	ULONG32 ulReceived = 0;
	ULONG32 ulLost = 0;
	ULONG32 ulLate = 0;
	ULONG32 ulTotal = 0;
	ULONG32 ulResendRequested = 0;
	ULONG32 ulResendReceived = 0;
	ULONG32 ulAvgBandwidth = 0;
	ULONG32 ulCurBandwidth = 0;

	UINT32	ulTotal30 = 0;
	UINT32	ulLost30 = 0;
	UINT32  ulDuplicate = 0;
	UINT32	ulOutOfOrder = 0;

	RTSPStreamData* pStreamData = m_pStreamHandler->firstStreamData();

	if (!pStreamData)
	{
	    return HXR_FAIL;
	}

	while (pStreamData)
	{
	    STREAM_STATS* pStreamStats = pStreamData->m_pStreamStats;

            if (!pStreamStats || !pStreamStats->m_bInitialized)
	    {
		goto updateContinue;
	    }

	    pStreamData->m_pTransportBuffer->UpdateStatistics(ulNormal,
	                                                      ulLost,
	                                                      ulLate,
	                                                      ulResendRequested,
	                                                      ulResendReceived,
	                                                      ulAvgBandwidth,
	                                                      ulCurBandwidth,
							      ulTotal30,
							      ulLost30,
							      ulDuplicate,
							      ulOutOfOrder);
	                  
	    ulReceived = ulNormal + ulResendReceived;
	    ulTotal = ulReceived + ulLost + ulLate;

	    pStreamStats->m_pNormal->SetInt((INT32)ulNormal);
	    pStreamStats->m_pRecovered->SetInt((INT32)ulResendReceived);
	    pStreamStats->m_pReceived->SetInt((INT32)ulReceived);
	    pStreamStats->m_pLost->SetInt((INT32)ulLost);
	    pStreamStats->m_pLate->SetInt((INT32)ulLate);
	    pStreamStats->m_pDuplicate->SetInt((INT32)ulDuplicate);
	    pStreamStats->m_pOutOfOrder->SetInt((INT32)ulOutOfOrder);
	    pStreamStats->m_pTotal->SetInt((INT32)ulTotal);
	    pStreamStats->m_pLost30->SetInt((INT32)ulLost30);
	    pStreamStats->m_pTotal30->SetInt((INT32)ulTotal30);
	    pStreamStats->m_pResendRequested->SetInt((INT32)ulResendRequested);
	    pStreamStats->m_pResendReceived->SetInt((INT32)ulResendReceived);
	    pStreamStats->m_pAvgBandwidth->SetInt((INT32)ulAvgBandwidth);
	    pStreamStats->m_pCurBandwidth->SetInt((INT32)ulCurBandwidth);

updateContinue:

	    pStreamData = m_pStreamHandler->nextStreamData();
	}
    }
#endif /* HELIX_FEATURE_STATS && HELIX_FEATURE_REGISTRY */

    return HXR_OK;
}

HX_RESULT 
RTSPTransport::UpdateRegistry(UINT32 ulStreamNumber,
			      UINT32 ulRegistryID)
{
    if (m_bIsSource)
    {
	m_ulRegistryID = ulRegistryID;
    }
    else
    {
	// XXX HP
	// rather than create a new copy of STREAM_STATS, we simply
	// obtain the same STREAM_STATS* from RTSPProtocol via SetStatistics().
	// this could save us on both the memory and several ms during the startup
/*
	if (!m_pRegistry)
	{
	    return HXR_FAIL;
	}

	RTSPStreamData* pStreamData = m_pStreamHandler->firstStreamData();
	while (pStreamData)
	{
	    if (pStreamData->m_streamNumber == (UINT16)ulStreamNumber)
	    {
		HX_DELETE(pStreamData->m_pStreamStats);
		pStreamData->m_pStreamStats = new STREAM_STATS(m_pRegistry,
							       ulRegistryID);
		break;
	    }							   
	    pStreamData = m_pStreamHandler->nextStreamData();
	}
*/
    }

    return HXR_OK;
}

RTSPTransportBuffer*
RTSPTransport::getTransportBuffer(UINT16 uStreamNumber)
{
    if (!m_pStreamHandler)
    {
	return NULL;
    }

    RTSPStreamData* pStreamData;	    

    pStreamData = m_pStreamHandler->getStreamData(uStreamNumber);

    if (!pStreamData)
    {
	return NULL;
    }

    return pStreamData->m_pTransportBuffer;
}

HX_RESULT
RTSPTransport::playReset()
{
    m_bSourceDone = FALSE;

    if(m_pStreamHandler)
    {
	RTSPStreamData* pStreamData = m_pStreamHandler->firstStreamData();

	while(pStreamData)
	{
	    if (m_bIsSource)
	    {
		pStreamData->m_packetSent = FALSE;
	    }
	    else
	    {
		pStreamData->m_pTransportBuffer->Reset();
		pStreamData->m_bReceivedAllPackets = FALSE;
	    }

	    pStreamData = m_pStreamHandler->nextStreamData();
	}
    }
    return HXR_OK;
}

HX_RESULT
RTSPTransport::pauseBuffers()
{
    if (m_pStreamHandler)
    {
	RTSPStreamData* pStreamData = m_pStreamHandler->firstStreamData();

	ASSERT(pStreamData);

	while(pStreamData)
	{
	    pStreamData->m_pTransportBuffer->Pause();

	    pStreamData = m_pStreamHandler->nextStreamData();
	}
    }

    return HXR_OK;
}

HX_RESULT
RTSPTransport::resumeBuffers()
{
    m_bIsReceivedData = FALSE;

    if (m_pStreamHandler)
    {
	RTSPStreamData* pStreamData = m_pStreamHandler->firstStreamData();

	ASSERT(pStreamData);

	while(pStreamData)
	{
	    pStreamData->m_pTransportBuffer->Resume();
	    pStreamData = m_pStreamHandler->nextStreamData();
	}
    }

    return HXR_OK;
}

HX_RESULT
RTSPTransport::setFirstSeqNum(UINT16 uStreamNumber, UINT16 uSeqNum)
{
    RTSPStreamData* pStreamData;

    pStreamData = m_pStreamHandler->getStreamData(uStreamNumber);

    if(pStreamData)
    {
	if (m_bIsSource)
	{	    
	    pStreamData->m_seqNo = uSeqNum;

	    if(pStreamData->m_pResendBuffer)
	    {
		pStreamData->m_pResendBuffer->SetFirstSequenceNumber(uSeqNum);
	    }
	}
	else
	{
	    if (!pStreamData->m_pTransportBuffer)
	    {
		return HXR_FAIL;
	    }

	    if (!m_bMulticast)
	    {
		pStreamData->m_lastSeqNo = uSeqNum;
    		pStreamData->m_pTransportBuffer->Init(uSeqNum);
	    }
	}
    }

    return HXR_OK;
}

HX_RESULT
RTSPTransport::Init(IUnknown* pContext)
{
    HX_RESULT			hresult;
    
    if (!m_pContext)
    {
	m_pContext = pContext;
	m_pContext->AddRef();
    }

    hresult = pContext->QueryInterface(IID_IHXCommonClassFactory,
				       (void**)&m_pCommonClassFactory);

    if (HXR_OK != hresult)
    {
	return hresult;
    }
					    
    hresult = pContext->QueryInterface(IID_IHXScheduler,
                                       (void**)&m_pScheduler);

    if (HXR_OK != hresult)
    {
	DPRINTF(D_INFO, ("could not get scheduler...\n"));
	return hresult;
    }

    pContext->QueryInterface(IID_IHXRegistry, (void**)&m_pRegistry);

    pContext->QueryInterface(IID_IHXInternalReset,
	                               (void**)&m_pInternalReset);

    pContext->QueryInterface(IID_IHXPlayerState,
	                               (void**)&m_pPlayerState);

    pContext->QueryInterface(IID_IHXSourceBufferingStats2,
			     (void**)&m_pSrcBufferStats);

#ifdef RDT_MESSAGE_DEBUG
    IHXPreferences* pPreferences = NULL;
    
    if (pContext &&
	(HXR_OK == pContext->QueryInterface(IID_IHXPreferences,
                                            (void**) &pPreferences)))
    {
	IHXBuffer* pBuffer = NULL;

        ReadPrefBOOL(pPreferences, "RDTMessageDebug", m_bRDTMessageDebug);
	if (m_bRDTMessageDebug)
	{
	    if (HXR_OK == pPreferences->ReadPref("RDTMessageDebugFile", pBuffer))
	    {
		if (pBuffer->GetSize() <= 0)
		{
		    // no file name, no log
		    m_bRDTMessageDebug = FALSE;
		}
		else
		{
		    m_RDTmessageDebugFileName = (const char*) pBuffer->GetBuffer();
		}			
	    }
            HX_RELEASE(pBuffer);
	}
    }

    HX_RELEASE(pPreferences);
#endif	// RDT_MESSAGE_DEBUG
    
    return HXR_OK;
}

HX_RESULT
RTSPTransport::SetResendBufferDepth(UINT32 uMilliseconds)
{
    RTSPStreamData* pStreamData = m_pStreamHandler->firstStreamData();

    ASSERT(pStreamData);

    while (pStreamData)
    {
	if (m_bIsSource)
	{
	    if (pStreamData->m_pResendBuffer)
	    {
		pStreamData->m_pResendBuffer->SetBufferDepth(uMilliseconds);
	    }
	}
	else
	{
	    if (pStreamData->m_pTransportBuffer)
	    {
		pStreamData->m_pTransportBuffer->SetBufferDepth(uMilliseconds);
	    }
	}

	pStreamData = m_pStreamHandler->nextStreamData();
    }

    return HXR_OK;
}

#ifdef RDT_MESSAGE_DEBUG
void RTSPTransport::RDTmessageFormatDebugFileOut(const char* fmt, ...)
{
    if(m_bRDTMessageDebug)
    {
        char buf[4096]; /* Flawfinder: ignore */
	va_list args;
	va_start(args, fmt);
	FILE* fp = fopen((const char*)m_RDTmessageDebugFileName, "a");
	if (fp)
	{
	    vsprintf(buf, fmt, args);
	    fprintf(fp, "%s\n", buf);
	    fclose(fp);
	}

	va_end(args);
    }
}
#endif	// RDT_MESSAGE_DEBUG
