Logo Search packages:      
Sourcecode: helix-player version File versions

ppffplin.cpp

/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: ppffplin.cpp,v 1.8.16.1 2004/07/19 21:04:16 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 ***** */

/////////////////////////////////////////////////////////////////////////////
// 
//  RealAudio Server side plugin that will sink to get live packets, create
//  a virtual .SDP file for the HTTP server to send to clients which will
//  start listening to the live multicast packets formatted in RTP.
//

/****************************************************************************
 *  Defines
 */
// #define _TIMELINE_TRACE
#define _GETPACKET_TRACE

#define INITGUID

#define RA_MINETYPE                 "audio/x-pn-realaudio"
#define STANDARD_START_HXOFFSET     200       // 100 ms
#define HX_COMMON_MIME_TYPE_FRAGMENT      "x-pn-"

#define TRANSPORT_BUFFERING_TIME    10        // ms

#define MAX_TIME_GAP_FORWARD        20000
#define MAX_TIME_GAP_BACKWARD       5000
#define MAX_SUSPENDED_BUFFER_SIZE   1000

#define FLEX_BUFFER_SWITCHOVER_TIME 40000     // 40 seconds

#define LISTENINING_TIME_INTERVAL   PP_SYNC_TIMEOUT_MS


/****************************************************************************
 *  Includes
 */
#include "hxtypes.h"
#include "hlxclib/windows.h"
#include "hxcom.h"
#include "hxcomm.h"
#include "ihxpckts.h"
#include "hxfiles.h"
#include "hxformt.h"
#include "hxplugn.h"
#include "hxmon.h"
#include "hxerror.h"
#include "hxcore.h"
#include "hxprefs.h"
#include "rtptypes.h"
#include "hxrand.h"
#include "hxslist.h"
#include "hxstring.h"
#include "hxstrutl.h"
#include "chxpckts.h"
#include "hxbuffer.h"
#include "tconverter.h"
#include "asmrulep.h"
#include "hxtick.h"
#include "hxengin.h"
#include "netbyte.h"    // rninet_addr

#include "statinfo.h" // STREAM_STATS
#include "hxmangle.h" // CLIENT_ID_REGNAME
#include "hxurl.h"

// error reporting 
#include "hxxres.h"
#include "hxxrsmg.h"
#include "resid.h"
#include "scalmres.h"   // in coreres module

#include "packetq.h"

#include "carray.h"
#include "ppbin.h"

#include "ppfformat.ver"
#include "rtpwrap.h"
#include "ppffplin.h"
#include "ppffsrc.h"
#include "ppffstrm.h"
#include "ppffhttp.h"
#include "hxver.h"

#include "sdpchunk.h"
#include "sdppyldinfo.h"

#include "hxhyper.h"    // IHXHyperNavigation

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

#ifdef _AIX
#include "hxtbuf.h"
#include "dllpath.h"
ENABLE_MULTILOAD_DLLACCESS_PATHS(Ppffplin);
#endif

const UINT32      MS_PER_SECOND     = 1000;
const UINT32      MICRO_PER_MS      = 1000;


/***************************************************************************
 *  CPurePlayFileFormat static variables                         ref:  rprxypln.h
 *
 *  These variables are passed to the RMA core to provide information about
 *  this plug-in. They are required to be static in order to remain valid
 *  for the lifetime of the plug-in.
 */
const char*  CPurePlayFileFormat::zm_pDescription     = "RealNetworks Scalable Multicast Plugin";
const char*  CPurePlayFileFormat::zm_pCopyright       = HXVER_COPYRIGHT;
const char*  CPurePlayFileFormat::zm_pMoreInfoURL     = HXVER_MOREINFO;
const char*  CPurePlayFileFormat::zm_pFileMimeTypes[] = {"application/sdp", NULL};
const char*  CPurePlayFileFormat::zm_pFileExtensions[]      = {"sdp", NULL};
const char*  CPurePlayFileFormat::zm_pFileOpenNames[] = {"Scalable multicast stream description", NULL};


#ifdef _WINDOWS

#if defined(WIN32_PLATFORM_PSPC)
#define HDLL_TYPE HANDLE
#else
#define HDLL_TYPE HINSTANCE
#endif /* defined(WIN32_PLATFORM_PSPC) */

extern HDLL_TYPE g_hInstance;
#endif


#if defined(_TIMELINE_TRACE) || defined(_GETPACKET_TRACE)
FILE* m_tfile = NULL;
#endif      // _TIMELINE_TRACE || _GETPACKET_TRACE



/****************************************************************************
 * 
 *  Function:
 * 
 *    HXCreateInstance()
 * 
 *  Purpose:
 * 
 *    Function implemented by all plugin DLL's to create an instance of 
 *    any of the objects supported by the DLL. This method is similar to 
 *    Window's CoCreateInstance() in its purpose, except that it only 
 *    creates objects from this plugin DLL.
 *
 *    NOTE: Aggregation is never used. Therefore and outer unknown is
 *    not passed to this function, and you do not need to code for this
 *    situation.
 * 
 */

STDAPI ENTRYPOINT(HXCREATEINSTANCE)
(
    IUnknown**  /*OUT*/ ppIUnknown
)
{
    *ppIUnknown = (IUnknown*)(IHXFileSystemObject*)new CPurePlayFileFormat;
    if(*ppIUnknown)
    {
      (*ppIUnknown)->AddRef();
      return HXR_OK;
    }
    return HXR_OUTOFMEMORY;
}

CPurePlayFileFormat::CPurePlayFileFormat()
    : m_lRefCount(0)
    , m_pContext(NULL)
    , m_pStreamDesc(NULL)
    , m_pRTPInfo(NULL)
    , m_pLog(NULL)
    , m_pFileObject(NULL)
    , m_pFileBufferList(NULL)
    , m_pScheduler(NULL)
    , m_pClassFactory(NULL)
    , m_pFileHeader(NULL)
    , m_pRegistry(NULL)
    , m_pSDPDesc(NULL)
    , m_state(Init)
    , m_pFFResponse(NULL)
    , m_uActiveStreams(0)
    , m_pMasterSyncStream(NULL)
    , m_bInitialPacket(TRUE)
    , m_uStatusCode(HX_STATUS_READY)
    , m_pStatusDesc(NULL)
    , m_pCName(NULL)
    , m_pUserName(NULL)
    , m_pTool(NULL)  
    , m_pEmail(NULL)
    , m_hFileHeader(0)
    , m_bRMPresentation(TRUE)
#ifdef _ALLOW_TS_OFFSETTING
    , m_bDisableOffsetting(FALSE)
#else // _ALLOW_TS_OFFSETTING
    , m_bDisableOffsetting(TRUE)
#endif      // _ALLOW_TS_OFFSETTING
    , m_bSourceAdmissionClosed(FALSE)
    , m_ulFileHeaderWaitStartTime(0)
    , m_ulListeningWaitStartTime(0)
    , m_ulServerTimeout(0)
    , m_bOneMoreTry(TRUE)
    , m_lSmallestTSStrm(-1)
    , m_tryUnicast(NO_TRY)
    , m_pRequest(NULL)
    , m_lRAStreamNum(NO_EXIST)
    , m_bWantClientStats(TRUE)
    , m_bAttempMulticast(TRUE)
    , m_ulFirstTSSent(0)
    , m_bFirstPktSent(FALSE)
    , m_bFirstPktRcvd(FALSE)
    , m_ulHXOffset(0)
    , m_pInterfaceMgr(NULL)
    , m_ppInterfacesReady(NULL)
    , m_ulNumInterfacesReady(0)
    , m_ulStartTimeMS(0)
#ifdef BW_BASED
    , m_lRuleNumber(-1)
#endif    
#ifdef _DEBUG
    , m_bFileHeaderReadyCalled(FALSE)
#endif    
{
    ;
}


CPurePlayFileFormat::~CPurePlayFileFormat() 
{ 
    HX_RELEASE(m_pContext);
    
    HX_RELEASE(m_pCName);
    HX_RELEASE(m_pUserName);
    HX_RELEASE(m_pTool);
    HX_RELEASE(m_pEmail);
}

// *** IUnknown methods ***
/////////////////////////////////////////////////////////////////////////
//  Method:
//    IUnknown::QueryInterface
//  Purpose:
//    Implement this to export the interfaces supported by your 
//    object.
//
STDMETHODIMP CPurePlayFileFormat::QueryInterface(REFIID riid, void** ppvObj)
{
    if (IsEqualIID(riid, IID_IHXTransportSyncServer))
    {
      AddRef();
      *ppvObj = (IHXTransportSyncServer*) this;
      return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IUnknown))
    {
      AddRef();
      *ppvObj = this;
      return HXR_OK;
    }
    else if(IsEqualIID(riid, IID_IHXPlugin))
    {
      AddRef();
      *ppvObj = (IHXPlugin*)this;
      return HXR_OK;
    }
    else if(IsEqualIID(riid, IID_IHXFileFormatObject))
    {
      AddRef();
      *ppvObj = (IHXFileFormatObject*)this;
      return HXR_OK;
    }
    else if(IsEqualIID(riid, IID_IHXFileResponse))
    {
      AddRef();
      *ppvObj = (IHXFileResponse*)this;
      return HXR_OK;
    }
    else if(IsEqualIID(riid, IID_IHXASMSource))
    {
      AddRef();
      *ppvObj = (IHXASMSource*)this;
      return HXR_OK;
    }
    else if(IsEqualIID(riid, IID_IHXPendingStatus))
    {
      AddRef();
      *ppvObj = (IHXPendingStatus*)this;
      return HXR_OK;
    }
    else if(IsEqualIID(riid, IID_IHXPropertyAdviser))
    {
      AddRef();
      *ppvObj = (IHXPropertyAdviser*)this;
      return HXR_OK;
    }
    
    *ppvObj = NULL;
    return HXR_NOINTERFACE;
}

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

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

    delete this;
    return 0;
}

/****************************************************************************
 *  IHXPlugin::GetPluginInfo                                ref:  hxplugn.h
 *
 *  This routine returns descriptive information about the plug-in, most
 *  of which is used in the About box of the user interface. It is called
     *  when the RMA core application is launched.
 */
STDMETHODIMP
CPurePlayFileFormat::GetPluginInfo
(
    REF(BOOL)         /*OUT*/ bLoadMultiple,
    REF(const char*)/*OUT*/ pDescription,
    REF(const char*)/*OUT*/ pCopyright,
    REF(const char*)/*OUT*/ pMoreInfoURL,
    REF(UINT32)     /*OUT*/ ulVersionNumber
)
{
    bLoadMultiple = TRUE;   // Must be true for file formats.

    pDescription    = zm_pDescription;
    pCopyright        = zm_pCopyright;
    pMoreInfoURL    = zm_pMoreInfoURL;
    ulVersionNumber = TARVER_ULONG32_VERSION;

    return HXR_OK;
}


/****************************************************************************
 *  IHXFileFormatObject::GetFileFormatInfo                  ref:  hxformt.h
 *
 *  This routine returns crucial information required to associate this
 *  plug-in with a given MIME type. This information tells the core which
 *  File Format plug-in to use for a particular URL. The routine is called
 *  when the RMA core application is launched.
 */
STDMETHODIMP
CPurePlayFileFormat::GetFileFormatInfo
(
    REF(const char**) /*OUT*/ pFileMimeTypes,
    REF(const char**) /*OUT*/ pFileExtensions,
    REF(const char**) /*OUT*/ pFileOpenNames
)
{
    pFileMimeTypes  = zm_pFileMimeTypes;
    pFileExtensions = zm_pFileExtensions;
    pFileOpenNames  = zm_pFileOpenNames;

    return HXR_OK;
}


/****************************************************************************
 *  IHXPlugin::InitPlugin                                   ref:  hxplugn.h
 *
 *  This routine performs initialization steps such as determining whether
 *  required interfaces are available. It is called when the RMA core 
 *  application is launched, and whenever an URL associated with this
 *  plug-in is opened.
 */
STDMETHODIMP
CPurePlayFileFormat::InitPlugin(IUnknown* /*IN*/ pContext)
{
    HX_RESULT theErr = HXR_OK;     
    int localizedErrCode = 0;

    m_pContext = pContext;
    
    if (NULL == m_pContext)
    {
      theErr = HXR_INVALID_PARAMETER;
      localizedErrCode = IDS_ERR_SM_NO_CTXT;
      goto bail;
    }
    m_pContext->AddRef();

    /*
     * Get the necessary interfaces
     */
    theErr = m_pContext->QueryInterface(IID_IHXErrorMessages,
                              (void**)&m_pLog);
    if (theErr != HXR_OK || NULL == m_pLog)
    {
      theErr = HXR_INVALID_PARAMETER;
      localizedErrCode = IDS_ERR_SM_CTXT_QI_FAILED;
      goto bail;
    }

    theErr = m_pContext->QueryInterface(IID_IHXScheduler,
                              (void**)&m_pScheduler);
      
    if (theErr != HXR_OK || NULL == m_pScheduler)
    {
      theErr = HXR_INVALID_PARAMETER;
      localizedErrCode = IDS_ERR_SM_CTXT_QI_FAILED;
      goto bail;
    }

    theErr = m_pContext->QueryInterface(IID_IHXRegistry,
                              (void**)&m_pRegistry);

    if (theErr != HXR_OK || NULL == m_pRegistry)
    {
      theErr = HXR_INVALID_PARAMETER;
      localizedErrCode = IDS_ERR_SM_CTXT_QI_FAILED;
      goto bail;
    }

    theErr = m_pContext->QueryInterface(IID_IHXCommonClassFactory, 
                              (void**)&m_pClassFactory);

    if (theErr != HXR_OK || NULL == m_pClassFactory)
    {
      theErr = HXR_INVALID_PARAMETER;
      localizedErrCode = IDS_ERR_SM_CTXT_QI_FAILED;
      goto bail;
    }

bail:
    if (theErr != HXR_OK)
    {
      ReportError(localizedErrCode, HXLOG_WARNING, HXR_FAIL);
    }
    return theErr;
}


/****************************************************************************
 *  IHXFileFormatObject::InitFileFormat                     ref:  hxformt.h
 *
 *  This routine initializes the file, and stores references to objects used
 *  throughout the example. It is called whenever an URL associated with this
 *  plug-in is opened.
 */
STDMETHODIMP
CPurePlayFileFormat::InitFileFormat
(
    IHXRequest*       /*IN*/  pRequest, 
    IHXFormatResponse*      /*IN*/  pFormatResponse,
    IHXFileObject*          /*IN*/  pFileObject
)
{
    HX_RESULT theErr = HXR_OK;    

    // used to create HTTP POST msg
    m_pRequest = pRequest;
    m_pRequest->AddRef();

#ifdef XXXGo_DEBUG
    m_file = fopen("c:\\live.txt", "wt");
#endif      // XXXGo_DEBUG

#if defined(_TIMELINE_TRACE) || defined(_GETPACKET_TRACE)
    m_tfile = fopen("c:\\live_t.txt", "wt");
#endif      // _TIMELINE_TRACE || _GETPACKET_TRACE

    /*
     * Create and save off the UDP socket, and start reading in 
     * the file object to parse out to get the file and stream headers
     * when we successfully get the headers we will call 
     * IHXFormatResponse::InitDone
     */


    /*
     * Save off the user values for the RTCP SDES messages
     */

    BOOL          bAutoTransport = TRUE;
    IHXPreferences*     pPrefs = NULL;
    IHXBuffer*          pBuf = NULL;
    HX_VERIFY(HXR_OK == m_pContext->QueryInterface(IID_IHXPreferences, 
      (void**)&pPrefs));

    if(HXR_OK == pPrefs->ReadPref("AutoTransport", pBuf))
    {
      bAutoTransport = atoi((const char*) pBuf->GetBuffer());
    }
    HX_RELEASE(pBuf);

    if (bAutoTransport)
    {
      m_bAttempMulticast = TRUE;
    }
    else
    {
      /* check the pref to see if Multicast is disabled */
      if (SUCCEEDED(pPrefs->ReadPref("AttemptRTSPvMulticast", pBuf)))
      {
          HX_ASSERT(pBuf);    
          m_bAttempMulticast = (BOOL)atol((const char*)pBuf->GetBuffer());    
          HX_RELEASE(pBuf);
      }
      if (m_bAttempMulticast && 
          SUCCEEDED(pPrefs->ReadPref("AttemptPNAvMulticast", pBuf)))
      {
          HX_ASSERT(pBuf);    
          m_bAttempMulticast = (BOOL)atol((const char*)pBuf->GetBuffer());    
          HX_RELEASE(pBuf);
      }
    }

    /*
     *      Handle multiple interface...
     */
    IHXNetworkInterfaceEnumerator* pIFEnum = NULL;
    if (SUCCEEDED(pPrefs->ReadPref("MulticastInterface", pBuf)))
    { 
      m_pInterfaceMgr = new InterfaceManager(1);
      m_pInterfaceMgr->SetInterface
          (0, DwToHost(HXinet_addr((const char*)pBuf->GetBuffer())));
      HX_RELEASE(pBuf);
    }
    else if (m_pContext->QueryInterface(IID_IHXNetworkInterfaceEnumerator, 
      (void**)&pIFEnum) == HXR_OK)
    { 
      const int initialBufferSize = 56;
      UINT32 ulNumIF = initialBufferSize;
      UINT32* pulIF = new UINT32[ulNumIF];
      theErr = pIFEnum->EnumerateInterfaces(pulIF, ulNumIF);
      if (HXR_BUFFERTOOSMALL == theErr)
      {
          HX_ASSERT(ulNumIF > initialBufferSize);
          // well, try it again...We know exactly how many IFs there are
          HX_VECTOR_DELETE(pulIF);
          pulIF = new UINT32[ulNumIF];
          theErr = pIFEnum->EnumerateInterfaces(pulIF, ulNumIF);      
      }
      if (HXR_OK == theErr)
      {
          HX_ASSERT(ulNumIF);
          m_pInterfaceMgr = new InterfaceManager(ulNumIF);
          for (UINT32 i = 0; i < ulNumIF; i++)
          {
            m_pInterfaceMgr->SetInterface(i, pulIF[i]);    
          }
      }
      HX_VECTOR_DELETE(pulIF);
      HX_RELEASE(pIFEnum);
    }

    if (!m_pInterfaceMgr)
    {
      // we have no choice...
      m_pInterfaceMgr = new InterfaceManager(1);
      m_pInterfaceMgr->SetInterface(0, HX_INADDR_ANY);
    }
    
    // sometimes, m_pCName gets just "" with size == 1
    // This is not the right string to use.
    // so if the string is <= 1, assign a different one.
    if (HXR_OK != pPrefs->ReadPref(REG_CNAME, m_pCName) ||
      m_pCName->GetSize() <= 1)
    {
      /*
       * Check to see if "UserAddress" exists and use that
       */

      CHXString strTemp;
      strTemp.Format("%s.%s",HXREGISTRY_PREFPROPNAME,"UserAddress");
      if (HXR_OK != m_pRegistry->GetStrByName(strTemp, m_pCName) ||
          m_pCName->GetSize() <= 1)
      {
          /*
           * Fill it with junk for now, XXXST Use better junk
           */

          char    szNewValue[256]; /* Flawfinder: ignore */
          UINT16  cbNewValue;
          
          m_pCName = new CHXBuffer;
          m_pCName->AddRef();

          CMultiplePrimeRandom randGen;

          cbNewValue = sprintf(szNewValue, "RealNetworks%ul", randGen.GetRandomNumber()); /* Flawfinder: ignore */

          m_pCName->Set((unsigned char*)szNewValue, cbNewValue + 1);
      }

      pPrefs->WritePref(REG_CNAME, m_pCName);
    }

    if (HXR_OK != pPrefs->ReadPref(REG_USERNAME, m_pUserName))
    {
      m_pUserName = new CHXBuffer;
      m_pUserName->AddRef();

      m_pUserName->Set((unsigned char*)DEFAULT_USERNAME, 
          strlen(DEFAULT_USERNAME) + 1);

      pPrefs->WritePref(REG_USERNAME, m_pUserName);
    }

    if (HXR_OK != pPrefs->ReadPref(REG_TOOL, m_pTool))
    {
      m_pTool = new CHXBuffer;
      m_pTool->AddRef();

      m_pTool->Set((unsigned char*)DEFAULT_TOOL, strlen(DEFAULT_TOOL) + 1);

      pPrefs->WritePref(REG_TOOL, m_pTool);
    }

    const char*    pDefEmailVal = "Not Set";
    if (HXR_OK != pPrefs->ReadPref(REG_EMAIL, m_pEmail))
    {
      /*
       * Write an empty key so the user knows that it is available
       * to be set
       */
       
      m_pEmail = new CHXBuffer;
      m_pEmail->AddRef();
      m_pEmail->Set((unsigned char*)pDefEmailVal, strlen(pDefEmailVal) + 1);
       
      pPrefs->WritePref(REG_EMAIL, m_pEmail);

      m_pEmail->Release();
      m_pEmail = NULL;
    }
    else
    {
      /*
       * Leave the key there but if there is no value then don't
       * store the string
       */

      if (strcasecmp(pDefEmailVal, (char*)m_pEmail->GetBuffer()) == 0)
      {
          m_pEmail->Release();
          m_pEmail = NULL;
      }
    }

    IHXBuffer* pBuffer = NULL;
    // Get server timeout, if available
    if (HXR_OK != pPrefs->ReadPref("ServerTimeOut", pBuffer))
    {
      // we need a timeout value
      m_ulServerTimeout = 90000;
    }
    else
    {
      m_ulServerTimeout = (UINT32) (atol((const char*)pBuffer->GetBuffer()) * 1000);      
    }     
    HX_RELEASE(pBuffer);

    // done with prefs
    HX_RELEASE(pPrefs);

    /*
     * Find a stream description converter
     */

    IHXPluginEnumerator*   pEnum = NULL;
    IUnknown*               pPlugin = NULL;
    
    m_pContext->QueryInterface(IID_IHXPluginEnumerator,
      (void**)&pEnum);

    HX_ASSERT(pEnum != NULL);
    HX_ASSERT(m_pStreamDesc == NULL);

    for (int i = pEnum->GetNumOfPlugins() - 1; 
       i >= 0 && (m_pStreamDesc == NULL || m_pRTPInfo == NULL); i--)
    {
      HX_VERIFY(pEnum->GetPlugin(i, pPlugin) == HXR_OK);
      HX_ASSERT(pPlugin != NULL);

      if (!m_pStreamDesc)
      {
          pPlugin->QueryInterface(IID_IHXStreamDescription,
            (void**)&m_pStreamDesc);

          if (m_pStreamDesc)
          {
            ((IHXPlugin*) pPlugin)->InitPlugin(m_pContext);
          }
      }
      if (!m_pRTPInfo)
      {     
          pPlugin->QueryInterface(IID_IHXRTPPayloadInfo,
            (void**)&m_pRTPInfo);
      }           

      pPlugin->Release();
    }
    pEnum->Release();

    if (m_pStreamDesc == NULL || m_pRTPInfo == NULL)
    {
      theErr = HXR_INVALID_PARAMETER;
      ReportError(IDS_ERR_SM_NOSDPPLIN, HXLOG_ERR, theErr);
      Close();
      goto bail;
    }

    m_pFFResponse = pFormatResponse;
    if (m_pFFResponse == NULL)
    {
      theErr = HXR_INVALID_PARAMETER;
      goto bail;
    }
    m_pFFResponse->AddRef();

    m_pFileObject = pFileObject;
    if (m_pFileObject == NULL)
    {
      theErr = HXR_INVALID_PARAMETER;
      goto bail;
    }
    m_pFileObject->AddRef();

    m_state = Init;
    m_uStatusCode = HX_STATUS_INITIALIZING;
    
    theErr = m_pFileObject->Init(0, this);
    
bail:    
    if (theErr != HXR_OK) m_pFFResponse->InitDone(theErr);
    
    return theErr;
}


/****************************************************************************
 *  IHXFileResponse::InitDone                               ref:  hxfiles.h
 *
 *  This routine notifies the RMA core that the initialization of the file is
 *  done. It is called automatically when the initialization of the file
 *  has completed.
 */
STDMETHODIMP
CPurePlayFileFormat::InitDone
(
    HX_RESULT     status
)
{
    HX_ASSERT(m_state == Init);
    
    if (SUCCEEDED(status))
    {
      m_state = InitHeader;
    }

    m_pFFResponse->InitDone(status);
    return HXR_OK;
}


/****************************************************************************
 *  IHXFileFormatObject::GetFileHeader                      ref:  hxformt.h
 *
 *  This routine returns to the RMA core an object containing the file
 *  header information. Several routines are actually required to complete
 *  the process due to the asynchronous nature of the RMA file system. This
 *  method is called by the RMA core after the file has been initialized.
 */
STDMETHODIMP
CPurePlayFileFormat::GetFileHeader()
{
    if (m_state != InitHeader)
    {
      return m_pFFResponse->FileHeaderReady(HXR_UNEXPECTED, NULL);
    }

    // XXXMEH - previously we had just been reading 0xffff
    // (65535 bytes), and if the SDP file was longer than
    // that, then we just threw a general error. Ack.
    // So now we will continue to read 64K until we have read until
    // we get an error.
    m_state = PendingFileHeader;
    return m_pFileObject->Read(READ_SIZE);
}


HX_RESULT CPurePlayFileFormat::MakeHeader(HX_RESULT status)
{
    if (m_state != PendingFileHeader)
    {
      return HXR_UNEXPECTED;
    }

    if (status != HXR_OK)
    {
      return Close();
    }

    /*
     *  RM:
     *  As soon as we have received packets for all the streams, good to go.
     *  After ServerTimeout and still haven't gotten packets for all the 
     *  streams, NO PRESENTATION
     *  STANDARD:
     *  As soon as we have received a first packet, we will wait extra 5 to 
     *  10 sec to decide the number of streams for this presentation
     *  After ServerTimeout and still haven't gotten any packet, 
     *  NO PRESENTATION: otherwise, go with whatever we've got
     */
    
    // we have to wait for a while, so streams can decide how many senders
    // exist!
    CPurePlayFileFormat::CFileHeaderCallback*    
      pFHCB = new CPurePlayFileFormat::CFileHeaderCallback(this);
    
    m_ulFileHeaderWaitStartTime = HX_GET_BETTERTICKCOUNT();
    
    pFHCB->AddRef();
    m_hFileHeader = m_pScheduler->RelativeEnter(pFHCB, PP_RM_TIMEOUT_MS);
    pFHCB->Release();
    
    m_uStatusCode = HX_STATUS_CONTACTING;
    
    m_bInitialPacket = TRUE;

    return HXR_OK;
}


/****************************************************************************
 *  IHXFileFormatObject::GetStreamHeader                    ref:  hxformt.h
 *
 *  This routine returns to the RMA core an object containing the stream
 *  header information for a particular stream. Several routines are actually
 *  required to complete the process due to the asynchronous nature of the
 *  RMA file system. This method is called (after the file header has been
 *  read) by the RMA core for each stream in the file format.
 */
STDMETHODIMP
CPurePlayFileFormat::GetStreamHeader(UINT16 unStreamNumber)
{
    if (unStreamNumber > m_rgStreams.GetSize())
    {
      m_pFFResponse->StreamHeaderReady(HXR_FAIL, NULL);
    }
    else
    {
      // get the right source
      CPurePlaySource* pSrc;
      pSrc = (CPurePlaySource*)m_rgStreams.GetAt(unStreamNumber);
      pSrc->AddRef();

      IHXValues* pHeader = NULL;
      pSrc->GetHeader(pHeader, unStreamNumber);

      HX_ASSERT(pHeader != NULL);

#ifdef XXXGo_DEBUG
          ULONG32 ulStrmNum = 1000;
          pHeader->GetPropertyULONG32("StreamNumber", ulStrmNum);
          if (pSrc->m_pFFLog)
          {
            fprintf(pSrc->m_pFFLog, "Sending Stream Header for stream %d (%d)\n", 
                              unStreamNumber, ulStrmNum);
          } 
#endif              

      // it's ready!
//    HX_ASSERT(HXR_OK == m_pFFResponse->StreamHeaderReady(HXR_OK, pHeader));

      if (m_pFFResponse->StreamHeaderReady(HXR_OK, pHeader) != HXR_OK)
      {
          HX_ASSERT(FALSE);
      }

      pHeader->Release();
      pSrc->Release();
    }
        
    return HXR_OK;
}


/****************************************************************************
 *  IHXFileFormatObject::GetPacket                          ref:  hxformt.h
 *
 *  This routine returns to the RMA core an object containing the packet
 *  data for a particular stream. Several routines are actually required to
 *  complete the process due to the asynchronous nature of the RMA file
 *  system. This method is called by the RMA core each time it needs another
 *  packet.
 */
STDMETHODIMP
CPurePlayFileFormat::GetPacket(UINT16 unStreamNumber)
{
#ifdef XXXGo_DEBUG
static UINT32 ulCount = 0;
#endif
    /*
     * There be packets wait'n for us on the socket
     */
    if (m_bInitialPacket)
    {
      m_bInitialPacket = FALSE;

      /*
       * Get the registry ID for this source so we can update the
       * clip bandwidth info when we get time.
       */

      IHXStreamSource* pSource = NULL;
      if (HXR_OK == m_pFFResponse->QueryInterface(IID_IHXStreamSource,
          (void**)&pSource))
      {
          IHXRegistryID* pSourceRegistryID = NULL;
          pSource->QueryInterface(IID_IHXRegistryID, (void**)&pSourceRegistryID);
          if (pSourceRegistryID)
          {
            UINT32 ulSourceID = 0;
            IHXBuffer* pSourceRegName = NULL;
            IHXBuffer* pTransportName = NULL;
            char        szTransportID[256]; /* Flawfinder: ignore */

            /* Need to come from a resource file */
            char szTransportName[] = "Scalable Multicast";
            
            pSourceRegistryID->GetID(ulSourceID);               
            m_pRegistry->GetPropName(ulSourceID, pSourceRegName);
            HX_ASSERT(pSourceRegName);

            if (pSourceRegName)
            {
                SafeSprintf(szTransportID, 256, "%s.TransportMode", pSourceRegName->GetBuffer());
                pTransportName = new CHXBuffer;
                pTransportName->AddRef();
                pTransportName->Set((UCHAR*) szTransportName, sizeof(szTransportName) + 1);
                m_pRegistry->SetStrByName(szTransportID, pTransportName);
            }

            HX_RELEASE(pTransportName);
            HX_RELEASE(pSourceRegName);

            HX_RELEASE(pSourceRegistryID);
          }

          UINT16  cStreams = pSource->GetStreamCount();
          for (UINT16 iStream = 0; iStream < cStreams; iStream++)
          {
            IUnknown*       pSrcUnk = NULL;
              IHXStream*          pSrc = NULL;
            IHXRegistryID*        pRegistryID = NULL;
            IHXBuffer*      pSrcReg = NULL;
            CPurePlaySource*    pPpSrc = NULL;
            CStream*        pStream = NULL;
            
            UINT32            ulStreamRegID = 0;
            char        szClipID[256]; /* Flawfinder: ignore */
            UINT32            ulClipID = 0;
            UINT16            uStreamNum = 0;
            
              if (HXR_OK != pSource->GetStream(iStream, pSrcUnk))
                goto NextSource;

            if (HXR_OK != pSrcUnk->QueryInterface(IID_IHXStream,
                (void**)&pSrc))
                goto NextSource;              
                
            if (HXR_OK != pSrc->QueryInterface(IID_IHXRegistryID,
                (void**)&pRegistryID))
                goto NextSource;
                
            if (HXR_OK != pRegistryID->GetID(ulStreamRegID))
                goto NextSource;

            // save this id so we can get stream stats later.
//          m_ulStreamRegID = ulStreamRegID;

            // deal with bandwidth reg key                                          
            if (HXR_OK != m_pRegistry->GetPropName(ulStreamRegID, pSrcReg))
                goto NextSource;
            
            SafeSprintf(szClipID, 256, "%s.ClipBandwidth", pSrcReg->GetBuffer());


            // Get corresponding CPurePlaySource 
            uStreamNum = pSrc->GetStreamNumber();
            pPpSrc = (CPurePlaySource*) m_rgStreams[uStreamNum];
            HX_ASSERT(pPpSrc);
            if (!pPpSrc)
                goto NextSource;
                
            pPpSrc->AddRef();

            // Get the CStream
            pStream = pPpSrc->GetStreamByStrmId(uStreamNum);
            HX_ASSERT(pStream);
            if (!pStream)
                goto NextSource;

            // save the StreamRegID, so we can get stream stats later.
            pStream->m_ulStreamRegId = ulStreamRegID;

            
            // if this is Standard payload, then "AvgBitRate" is hard coded 
            // in sdpplin, and so can't trust them.
            // Also, pPpSrc->m_ulBandwidth of 1 suggest the absence of 
            // "AvgBitRate" in SDP file
            if ((!pPpSrc->IsRMSource()) || 1 == pPpSrc->GetBandwidth())
            {
                ulClipID = m_pRegistry->GetId(szClipID);    
                if (ulClipID == 0)
                  goto NextSource;

                pStream->m_ulClipBandwidthID = ulClipID;

            }
            else
            {
                HX_ASSERT(pPpSrc->GetBandwidth() != 1);
                // trust bandwidth in the header
                pStream->m_ulClipBandwidthID = 0;
                m_pRegistry->SetIntByName(szClipID, pPpSrc->GetBandwidth());
            }
            
NextSource:       
            HX_RELEASE(pPpSrc);
            HX_RELEASE(pSrcReg);
            HX_RELEASE(pRegistryID);
            HX_RELEASE(pSrc);
            HX_RELEASE(pSrcUnk);
          }
          
          pSource->Release();
      }
      
    }

#ifdef XXXGo_DEBUG
if (m_file)
{
      fprintf(m_file, "\t\t\t\t\t%u: GetPacket(): streamNumber = %u\n", 
                                  ulCount++, 
                                  unStreamNumber);

}
#endif
    
    ULONG32 ulTimeNow = HX_GET_BETTERTICKCOUNT();

#ifdef _GETPACKET_TRACE
    /*
    if (m_tfile)
    {
      fprintf(m_tfile, "**GetPacket strm=%u  RT=%u\n", 
            unStreamNumber, 
            ulTimeNow - m_ulStartTimeMS);
    }
    */
#endif      // _GETPACKET_TRACE

    CPurePlaySource*    pSrc = (CPurePlaySource*)m_rgStreams.GetAt(unStreamNumber);
    pSrc->AddRef();
    pSrc->SetPendingFlag(unStreamNumber, TRUE);
    pSrc->UpdateStatistics(m_pRegistry, ulTimeNow);
    pSrc->Release();
    
    return ProcessPendingPackets(FALSE, ulTimeNow);
}


/****************************************************************************
 *  IHXFileResponse::SeekDone                               ref:  hxfiles.h
 *
 *  This routine is automatically called when the Seek() operation in the
 *  file is complete. Other actions are then taken dependent on the current
 *  processing state.
 */
STDMETHODIMP
CPurePlayFileFormat::SeekDone(HX_RESULT status)
{
    return HXR_NOTIMPL;
}


/****************************************************************************
 *  IHXFileResponse::ReadDone                               ref:  hxfiles.h
 *
 *  This routine is automatically called when the Read() from the file is
 *  done. Other actions are then taken dependent on the current processing
 *  state.
 */
STDMETHODIMP
CPurePlayFileFormat::ReadDone
(
    HX_RESULT           status,
    IHXBuffer*          pBuffer
)
{
    HX_ASSERT(m_state == PendingFileHeader);
    HX_ASSERT(m_pStreamDesc != NULL);

    HX_RESULT         theErr  = HXR_OK;
    UINT16      cHeaders      = 0;
    IHXValues**    ppHeaders  = NULL;  // all the headers
    CHXSimpleList   newConns;
    UINT32      ulAddr  = 0;
    IHXBuffer*        pFileAddr     = NULL;
    IHXUDPResponse* pResp     = NULL;

    int               i       = 0;
    IHXPreferences*     pPrefs = NULL;
    IHXBuffer* pBandwidth = NULL;

    IHXValues**    ppRealHeaders = NULL;;// headers of right BW
    UINT32* pulSubscriptionBW = NULL;

    // XXXMEH - we are reading READ_SIZE (64k) bytes
    // at a time. When we finally fail, then we will aggregate
    // all our 64K buffers into one and then continue below.
    IHXBuffer* pAggBuffer = NULL;
    if (SUCCEEDED(status))
    {
        // Do we have a file buffer list?
        if (!m_pFileBufferList)
        {
            m_pFileBufferList = new CHXSimpleList();
        }
        if (m_pFileBufferList)
        {
            // AddRef the buffer before it goes on the list
            pBuffer->AddRef();
            // Put it on the list
            m_pFileBufferList->AddTail((void*) pBuffer);
            // Was this read less than READ_SIZE?
            if (pBuffer->GetSize() < READ_SIZE)
            {
                // We've hit the end of the file, go
                // ahead and aggregate
                AggregateBuffers(pAggBuffer);
            }
            else
            {
                // Set the state
                m_state = PendingFileHeader;
                // Read another READ_SIZE bytes
                return m_pFileObject->Read(READ_SIZE);
            }
        }
    }
    else
    {
        // Aggregate the buffers. The only time we should
        // hit this is if the .sdp file is exactly a multiple
        // of READ_SIZE
        AggregateBuffers(pAggBuffer);
    }

    // Now we can clear the buffer list
    ClearFileBufferList();
    HX_DELETE(m_pFileBufferList);

    if (!pAggBuffer)
    {
        return m_pFFResponse->FileHeaderReady(HXR_FAIL, NULL);
    }
    
    /* NULL terminate the description buffer */
    m_pSDPDesc = new CHXBuffer;
    m_pSDPDesc->AddRef();
    m_pSDPDesc->SetSize(pAggBuffer->GetSize() + 1);
    ::memcpy(m_pSDPDesc->GetBuffer(), pAggBuffer->GetBuffer(), pAggBuffer->GetSize()); /* Flawfinder: ignore */
    m_pSDPDesc->GetBuffer()[pAggBuffer->GetSize()] = '\0';

    // Release the aggregated buffer
    HX_RELEASE(pAggBuffer);

    /*
     * Now that we have a file, create the stream and file headers
     */

    theErr = m_pStreamDesc->GetValues(m_pSDPDesc, cHeaders, ppHeaders);
    if (theErr != HXR_OK || cHeaders == 0 || ppHeaders == NULL)
    {
      theErr = HXR_INVALID_FILE;
      ReportError(IDS_ERR_SM_BADSDPFILE, HXLOG_ERR, theErr);
      goto bail;
    }    
    
    /*
     *      A SDP file open may not contain a stream that we can play at all.
     *  so, make sure there is at least one stream in this header.
     *  If there is no strearm for presentation, tell the user and quit
     */    
    if (1 == cHeaders)
    { 
      // there is only file header
      theErr = HXR_INVALID_FILE;    
      ReportError(IDS_ERR_SM_NOPLAYTYPE, HXLOG_ERR, theErr);
      goto bail;
    }

    /*
     * Save off the headers then try to find our custom file attributes 
     * which will allow us to setup a network socket
     *
     * ppHeaders[0] == File Header
     * ppHeaders[1-N] == Stream Headers
     */

    m_pFileHeader = ppHeaders[0];
    if (m_pFileHeader == NULL)
    {
      theErr = HXR_INVALID_FILE;
      goto bail;
    }
    m_pFileHeader->AddRef();

    /* check to see if we wanna try this feed at all */
    if (!m_bAttempMulticast)
    { 
      m_tryUnicast = NO_MULTICAST;
      goto bail;
    }


    HX_VERIFY(HXR_OK == m_pContext->QueryInterface(IID_IHXPreferences, 
      (void**)&pPrefs));

    // Client stats enabled?
    // m_bWantClientStats defaults to FALSE
    UINT32 ul;
    if (HXR_OK == m_pFileHeader->GetPropertyULONG32("StatsStyle", ul) &&
      HXR_OK == m_pFileHeader->GetPropertyULONG32("StatsMask", ul))
    {
      IHXBuffer* pPrefBuffer = NULL;      
      
      if (SUCCEEDED(pPrefs->ReadPref("SendStatistics", pPrefBuffer)))
      {
          m_bWantClientStats = (BOOL)atoi((const char*) pPrefBuffer->GetBuffer());    
          HX_RELEASE(pPrefBuffer);
      }    
    }

    
    BOOL bFoundBW;    
    if (HXR_OK != pPrefs->ReadPref("Bandwidth", pBandwidth))
    {
      bFoundBW = FALSE;
      HX_ASSERT(FALSE);
      pBandwidth = new CHXBuffer();
      pBandwidth->AddRef();
      pBandwidth->Set((const unsigned char*)"64000", strlen("64000"));
    }
    else
    {
      bFoundBW = TRUE;
    }
    HX_RELEASE(pPrefs);

    /* Timeout */
    if (SUCCEEDED(m_pFileHeader->GetPropertyULONG32("Timeout", ul)))
    {
      // it's in sec
      m_ulFailoverTimeout = ul * 1000;
    }
    else
    {
      m_ulFailoverTimeout = m_ulServerTimeout;
    }
    
    /*
     * The connection line could be stored in the session header
     * rather than the media description so try to get it even if fails
     */

    m_pFileHeader->GetPropertyCString(PROP_MULTI_ADDRESS, pFileAddr);
    
    /*
     * Since we only server live streams make sure the core knows this
     * The stream count may not be included in the SDP file so add it
     * here
     */
    m_pFileHeader->SetPropertyULONG32("LiveStream", 1);

    /*
     * Get a number of streams in this presentation
     */
    ULONG32 ulNumStreams;
    ulNumStreams = 0;
    
    // don't trust the "StreamCount"
    // cHeaders will represent a number of m= line in SDP
    // ulNumStreams is actual number of streams in this presentation
    // They will be different on sure stream presentation becacause we are
    // duplicating headers for each bandwidth.    
    // we have to iterate over the stream headers...        
    if (GetStreamCountNoTrust(&ppHeaders[1], cHeaders - 1, ulNumStreams) != TRUE)
    {
      // Well, This is not a SDP file generated by pplyfobj.cpp.
      // Trust the StreamCount!
      m_pFileHeader->GetPropertyULONG32("StreamCount", ulNumStreams);       
      ppRealHeaders = new IHXValues*[ulNumStreams];
      for ( i = 0; i < (int)ulNumStreams; i++)
      {
          ppRealHeaders[i] = ppHeaders[i+1];
          ppRealHeaders[i]->AddRef();
      }
    }
    else
    {
      // we should have at least one stream
      HX_ASSERT(ulNumStreams > 0);
    
        // set the StreamCount
      m_pFileHeader->SetPropertyULONG32("StreamCount", ulNumStreams);       
    
      /*
      * Get a BW for each stream to "subscribe" to
      */
      pulSubscriptionBW = new UINT32[ulNumStreams];
      memset(pulSubscriptionBW, 0, sizeof(UINT32) * ulNumStreams);

      if (GetSubscriptionBW(m_pFileHeader, 
                    &ppHeaders[1], 
                    cHeaders - 1, 
                    pulSubscriptionBW, 
                    ulNumStreams,
                    pBandwidth) != TRUE)
      {
          // this should never happen
          theErr = HXR_FAIL;
          goto bail;
      }

      // if we have gotten BW info from pref., then check for an actual 
      // BW info.
      if (bFoundBW)
      {
          UINT32 ulBW = 0;
          for (UINT32 i = 0; i < ulNumStreams; i++)
          {
            ulBW += pulSubscriptionBW[i];
          }
      
          if (ulBW > (UINT32)atol((const char*)pBandwidth->GetBuffer()))
          {       
            // this will end a presentation         
            theErr = HXR_FAIL;
            ReportError(IDS_ERR_SM_NOTENOUGHBANDWIDTH, HXLOG_ERR, theErr);
            goto bail;
          }
      }

      
      /**************************
      * Get right stream headers to pass to CPurePlaySource
      * We have figured out BW to use for each stream
      * This is expensive, but...hey, it's done only once
      * Iterate over all the stream headers AGAIN, and Get right stream headers
      * for each stream depending on subscription BW
      */
      if (GetRightHeaders(ppRealHeaders,
                  ulNumStreams,
                  &ppHeaders[1],
                  cHeaders - 1,
                  pulSubscriptionBW) != TRUE)
      {
          // this should never happen
          theErr = HXR_FAIL;
          goto bail;
      }
    } 

    m_bRMPresentation = DetermineIfRMPresentation(
                         ppHeaders[0],
                         &(ppHeaders[1]),
                         ulNumStreams);
    /* 
     *      Almost done!
     *  Now that we have the right headers, create CPurePlaySource
     *  for each stream
     */    
    UINT32  ulRTPPayload;
    ulRTPPayload = 0;

    for (i = 0; i < (int)ulNumStreams; i++)
    {    
      HX_ASSERT(NULL != ppRealHeaders[i]);
      
      if (NULL == ppRealHeaders[i])
      {
          theErr = HXR_FAIL;
          goto bail;
      }
      /*
       * CPurePlaySource will addref the header
       */
       
      IHXValues*  pSrcHeader;
      IHXBuffer*  pSrcAddr;
      UINT32            ulPort;
      UINT32            ulRTPFactor;
      UINT32            ulHXFactor;
      ULONG32           ulPreroll;

      ulRTPFactor = 0;
      ulHXFactor = 0;
      ulPreroll = 0;

      pSrcHeader = ppRealHeaders[i];
      pSrcHeader->AddRef();

      theErr = pSrcHeader->GetPropertyULONG32(PROP_RTP_PAYLOAD,
                                    ulRTPPayload);
      if (HXR_OK != theErr)
      {
          HX_ASSERT(FALSE);
          ReportError(IDS_ERR_SM_NOTFOUNDPAYLOAD, HXLOG_ERR, theErr);
          goto bail;
      }
      
      theErr = pSrcHeader->GetPropertyCString(PROP_MULTI_ADDRESS, 
          pSrcAddr);
      if (theErr != HXR_OK)
      {
          /*
           * It is OK for now that we don't have the address
           * we'll use it from the file header if there
           */
          if (pFileAddr == NULL)
          {
            pSrcHeader->Release();
            ReportError(IDS_ERR_SM_NOTFOUNDADDRESS, HXLOG_ERR, theErr);
            goto bail;
          }

          pSrcAddr = pFileAddr;
          pSrcAddr->AddRef();
          
          theErr = HXR_OK;
      }
    
      theErr = pSrcHeader->GetPropertyULONG32(PROP_MULTI_PORT, ulPort);
      if (theErr != HXR_OK)
      {
          /*
           * This is a problem.  No port, no play
           */
          pSrcHeader->Release();
          ReportError(IDS_ERR_SM_NOTFOUNDPORT, HXLOG_ERR, theErr);
          goto bail;
      }

      /*
       * Get the RTP->RMA timestamp conversion ratio from the stream
       * headers.
       */
      pSrcHeader->GetPropertyULONG32(PROP_RTP_FACTOR, ulRTPFactor);
      pSrcHeader->GetPropertyULONG32(PROP_HX_FACTOR, ulHXFactor);

      if ((ulRTPFactor == 0) || (ulHXFactor == 0))
      {
          ulHXFactor = 1000;

          if (!m_bRMPresentation)
          {
            pSrcHeader->GetPropertyULONG32("SamplesPerSecond", ulRTPFactor);
            
            /*
            * Try to map payload number to sampling rate
            */
            if (ulRTPFactor == 0)
            {
                ulRTPFactor = SDPMapPayloadToSamplesPerSecond(ulRTPPayload);
            }
            
            if (ulRTPFactor == 0)
            {
                IHXBuffer* pMimeType = NULL;
                
                pSrcHeader->GetPropertyCString("MimeType", pMimeType);
                
                /*
                * Try to map mime type to sampling rate
                */
                if (pMimeType)
                {
                  ulRTPFactor = SDPMapMimeToSamplesPerSecond(pMimeType);
                }
                
                HX_RELEASE(pMimeType);
            }
          }

          if (ulRTPFactor == 0)
          {
            ulRTPFactor = ulHXFactor;
          }
      }

      pSrcHeader->GetPropertyULONG32("Preroll", ulPreroll);
      if (ulPreroll == 0)
      {
          pSrcHeader->GetPropertyULONG32("Predata", ulPreroll);
      }

      if (ulPreroll == 0)
      {
          IHXBuffer* pSDPData = NULL;

          if (SUCCEEDED(pSrcHeader->GetPropertyCString("SDPData", pSDPData)))
          {
            char *pData = (char*) pSDPData->GetBuffer();
            IHXValues *pValues = NULL;

            if (pData &&
                SUCCEEDED(SDPParseChunk(pData,
                                  strlen(pData),
                                  pValues,
                                  m_pClassFactory,
                                  SDPCTX_Renderer)))
            {
                pValues->GetPropertyULONG32("Preroll", ulPreroll);
            }

            HX_RELEASE(pValues);
          }

          if (ulPreroll != 0)
          {
            pSrcHeader->SetPropertyULONG32("Preroll", ulPreroll);
          }

          HX_RELEASE(pSDPData);
      }

      /*
       *  take care of interfaces
       */    
      CHXPtrArray* prgStreams = NULL;
      for (UINT32 j = 0; j < m_pInterfaceMgr->m_ulNumInterfaces; ++j)
      {
          // only one interface to worry about...
          CPurePlaySource*  pSrc = new CPurePlaySource(this,
                                    pSrcHeader,
                                    i,
                                    ulRTPFactor,
                                    ulHXFactor,
                                    m_bRMPresentation);                                 
          pSrc->AddRef();
    
          theErr = pSrc->Init(m_pContext,
                        (char*)pSrcAddr->GetBuffer(),
                        m_pInterfaceMgr->m_pulInterfaces[j],
                        HX_SAFEUINT16(ulPort));
                                                                
          if (theErr != HXR_OK) goto bail;

          // get an array for this interface
          prgStreams = m_pInterfaceMgr->GetSession(j);
          if (!prgStreams)
          {
            HX_ASSERT(0 == i);
            prgStreams = new CHXPtrArray();
          }

          // set a source for this interface
          prgStreams->SetAtGrow(i, pSrc);       
          m_pInterfaceMgr->SetSession(j, prgStreams);
      }

      pSrcHeader->Release();
      pSrcAddr->Release();
    }

bail:

    /*
     * We no longer need the file object so release it on CloseDone
     */
    if (m_pFileObject) 
    {
      m_pFileObject->Close();
      HX_RELEASE(m_pFileObject);
    } 

    HX_VECTOR_DELETE(pulSubscriptionBW);
    HX_RELEASE(pBandwidth);


    // release ppRealHeaders too
    if (NULL != ppRealHeaders)
    {
      for (int i = 0; i < (int)ulNumStreams; i++)
      {
          HX_RELEASE(ppRealHeaders[i]);
      }
      HX_VECTOR_DELETE(ppRealHeaders);
    }

    if (NULL != ppHeaders)
    {
      for (int i = 0; i < (int)cHeaders; i++)
      {
          // we didn't AddRef() those
          HX_RELEASE(ppHeaders[i]);
      }
      HX_VECTOR_DELETE(ppHeaders);
    }
        
    HX_RELEASE(pFileAddr);

    if ((NO_MULTICAST == m_tryUnicast) || (theErr != HXR_OK))
    {
      Close();
      theErr = HXR_CLOSED;
    }
    else
    {
      /*
       * Start listening
       */          
      CHXPtrArray* prgStreams = NULL;
      CPurePlaySource* pSrc = NULL;
      for (UINT32 j = 0; j < m_pInterfaceMgr->m_ulNumInterfaces; j++)
      {     
          prgStreams =m_pInterfaceMgr->GetSession(j);           
          for (i = 0; i < prgStreams->GetSize(); i++)           
          {
            pSrc = (CPurePlaySource*)prgStreams->GetAt(i);
            pSrc->Begin();
          }       
      }
    }

    m_state = PendingFileHeader;

    if (theErr != HXR_CLOSED)
    {
      return MakeHeader(theErr);
    }

    return HXR_OK;
}


BOOL
CPurePlayFileFormat::GetStreamCountNoTrust(IHXValues** ppHeaders, 
                                 UINT16 unNumHeader,
                                 REF(ULONG32) ulNumStreams)                           
{
    HX_ASSERT(NULL != ppHeaders);

    ULONG32 ulID;
    BOOL    rgFound[256];    
    
    memset(rgFound, 0, 256);

    for (UINT16 i = 0; i < unNumHeader; i++)
    {
      HX_ASSERT(ppHeaders[i] != NULL);
      IHXValues*      pSrcHeader;

      pSrcHeader = ppHeaders[i];
      pSrcHeader->AddRef();

      // "StreamId" is the field that ppfobj.cpp puts for a group of
      // streams 
      if (HXR_OK == pSrcHeader->GetPropertyULONG32("StreamId", ulID))
      {
          if (!rgFound[ulID])
          {   
            rgFound[ulID] = TRUE;
            ulNumStreams++;
          }
      }
      else
      {
          // OK, trust the "StreamCount".  This is not a SDP file generated
          // by pplyfobj.cpp
          ulNumStreams = 0;
          HX_RELEASE(pSrcHeader);
          return FALSE;
      }
      HX_RELEASE(pSrcHeader);
    }

    return TRUE;
}


BOOL CPurePlayFileFormat::DetermineIfRMPresentation(
                        IHXValues* pFileHeader,
                        IHXValues** ppStrmHeaders,
                        ULONG32 ulNumStreams)
{
    BOOL bIsRMPresentation = FALSE;

    if (pFileHeader && ppStrmHeaders && ulNumStreams)
    {
      IHXValues* pStrmHdr = NULL;
      IHXBuffer* pASMRuleBook = NULL;
      IHXBuffer* pMimeType = NULL;
      ULONG32 ulRTPPayload = RTP_PAYLOAD_RTSP + 1;
      BOOL bIsRMStream = FALSE;
      ULONG32 ulIdx;

      bIsRMPresentation = TRUE;

      for (ulIdx = 0; bIsRMPresentation && (ulIdx < ulNumStreams); ulIdx++)
      {
          pStrmHdr = ppStrmHeaders[ulIdx];

          bIsRMStream = FALSE;

          if (pStrmHdr)
          {
            pStrmHdr->GetPropertyULONG32(PROP_RTP_PAYLOAD, ulRTPPayload);

            if (ulRTPPayload == RTP_PAYLOAD_RTSP)
            {
                bIsRMStream = TRUE;
            }

            ulRTPPayload = RTP_PAYLOAD_RTSP + 1;

            if (bIsRMStream)
            {
                bIsRMStream = FALSE;
                
                pStrmHdr->GetPropertyCString("ASMRuleBook", pASMRuleBook);
                
                if (pASMRuleBook)
                {
                  bIsRMStream = TRUE;
                }
            }

            HX_RELEASE(pASMRuleBook);

            if (bIsRMStream)
            {
                bIsRMStream = FALSE;

                pStrmHdr->GetPropertyCString("MimeType", pMimeType);

                if (pMimeType)
                {
                  if (strstr((const char*) pMimeType->GetBuffer(),
                           HX_COMMON_MIME_TYPE_FRAGMENT))
                  {
                      bIsRMStream = TRUE;
                  }
                }
            }

            HX_RELEASE(pMimeType);
          }

          bIsRMPresentation = (bIsRMStream && bIsRMPresentation);
      }
    }

    return bIsRMPresentation;
}


BOOL
CPurePlayFileFormat::GetSubscriptionBW(IHXValues* pFileHeader, 
                               IHXValues** ppStrmHeaders,
                               UINT16 unNumStrmHeaders,
                               REF(UINT32*) pulSubscriptionBW,
                               UINT32 ulNumStreams,
                               IHXBuffer* pBandwidth)                           
{
    HX_ASSERT(pFileHeader);
    HX_ASSERT(ppStrmHeaders);
    HX_ASSERT(unNumStrmHeaders >= 1);
    HX_ASSERT(pulSubscriptionBW);
    HX_ASSERT(ulNumStreams >= 1);
    HX_ASSERT(pBandwidth);

    pFileHeader->AddRef();
    pBandwidth->AddRef();

    IHXBuffer*    pRuleBuf = NULL;

    if (HXR_OK != pFileHeader->GetPropertyCString("ASMRuleBook", pRuleBuf))
    {
      // OK, this is a single stream presentation.  Take an ASMRuleBook from 
      // any of stream headers (they are all the same), and use it to decide
      // which stream header to use depending on bit rate
      HX_ASSERT(1 == ulNumStreams);

      // get ASMRuleBook
      HX_ASSERT(NULL != ppStrmHeaders[0]);
      IHXValues* pHeader = ppStrmHeaders[0];
      pHeader->AddRef();

      if (HXR_OK == pHeader->GetPropertyCString("ASMRuleBook", pRuleBuf))
      {
          ASMRuleBook       rules((char*)pRuleBuf->GetBuffer());
          HX_RELEASE(pRuleBuf);
          IHXBuffer*        pBuffer     = NULL;
          UINT16      unRules;
    
          unRules = rules.GetNumRules();
    
          // get subscriptions for this bandwidth
          BOOL bSubInfo[256];
          UINT16 unRuleNum;
          IHXValues* pValues;
          pValues = new CHXHeader();
          pValues->AddRef();
          
          pValues->SetPropertyCString("Bandwidth", pBandwidth);
          rules.GetSubscription(bSubInfo, pValues);
          
          HX_RELEASE(pValues);
          // get a rule number that we are interested in
          int y;
          for (y = 0; y < (int)unRules; y++)
          {
            if (TRUE == bSubInfo[y])
            {
                IHXBuffer* pBw = 0;
                unRuleNum = y;
                
                // make sure AverageBandwidth != 0
                rules.GetProperties(y, pValues);                
                
                if (HXR_OK == pValues->GetPropertyCString("AverageBandwidth", 
                                                pBw))
                {
                  pulSubscriptionBW[0] += atol((const char*)pBw->GetBuffer());
                  HX_RELEASE(pBw);              
                }
                else
                {
                  // TimeStampDelivery only stream
                  pulSubscriptionBW[0] = 0;
                }
                HX_RELEASE(pValues);
            }
          }
      }
      else
      {
          // There is no ASMRuleBook at all...
          // This should never happen.
          HX_RELEASE(pFileHeader);
          HX_RELEASE(pBandwidth);
          HX_RELEASE(pHeader);
          HX_ASSERT(FALSE);
          return FALSE;
      }

      HX_RELEASE(pHeader);
    }
    else // for if (HXR_OK == m_pFileHeader->GetPropertyCString("ASMRuleBook", pRuleBuf))
    {
      // this is a multiple stream presentation.
      // take ASMRuleBook for a file and figure out BW to use for 
      // each stream
      ASMRuleBook rules((char*)pRuleBuf->GetBuffer());
      HX_RELEASE(pRuleBuf);
      IHXBuffer*  pBuffer         = NULL;
      UINT16            unRules;

      unRules = rules.GetNumRules();    

      // get subscriptions for this bandwidth
      BOOL bSubInfo[256];
      UINT16 unRuleNum;
      IHXValues* pValues;
      pValues = new CHXHeader();
      pValues->AddRef();
      
      pValues->SetPropertyCString("Bandwidth", pBandwidth);
      rules.GetSubscription(bSubInfo, pValues);
      HX_RELEASE(pValues);
      
      // get a rule number that we are interested in
      // Assuming there is only one TRUE
      int y;
      for (y = 0; y < (int)unRules; y++)
      {
          if (TRUE == bSubInfo[y])
          {
            // there should be only one
            unRuleNum = y;
            break;
          }
      }

#ifdef BW_BASED
      // unRuleNum is the rule number that we are interested in
      m_lRuleNumber = (INT32)unRuleNum;         
#endif      


      // Get a BW for each stream
        rules.GetProperties((int)unRuleNum, pValues);
        for (int i = 0; i < (int)ulNumStreams; i++)
        {
            char rgStreamBW[32]; /* Flawfinder: ignore */
            sprintf(rgStreamBW, "Stream%dBandwidth", i); /* Flawfinder: ignore */
            if (HXR_OK == pValues->GetPropertyCString((const char*)rgStreamBW, 
                                          pBuffer))
          {
            pulSubscriptionBW[i] = (UINT32)atol((const char*)pBuffer->GetBuffer());
            HX_RELEASE(pBuffer);
          }
        }
      HX_RELEASE(pValues);    

    }

    HX_RELEASE(pFileHeader);
    HX_RELEASE(pBandwidth);
    return TRUE;
}

BOOL
CPurePlayFileFormat::GetRightHeaders(REF(IHXValues**) ppRealHeaders, // out
                             UINT32   ulNumStreams,
                             IHXValues** ppHeaders,
                             UINT32   cHeaders,
                             UINT32*      pulSubscriptionBW)
{
    HX_ASSERT(ulNumStreams >= 1);
    HX_ASSERT(ppHeaders);
    HX_ASSERT(pulSubscriptionBW);

    
    ppRealHeaders = new IHXValues*[ulNumStreams];
    memset(ppRealHeaders, NULL, sizeof(IHXValues*) * ulNumStreams);
    
    
    for (int i = 0; i < (int)ulNumStreams; i++)
    {
      ULONG32 ulID = 0;
      ULONG32 ulBW = 0;;
      BOOL    bFound = FALSE;
      for (int j = 0; j < (int)cHeaders; j++)
      {
          HX_ASSERT(NULL != ppHeaders[j]);
          IHXValues* pSrcH;
          pSrcH = ppHeaders[j];
          pSrcH->AddRef();
          if ((HXR_OK == pSrcH->GetPropertyULONG32("AvgBitRate", ulBW)) &&
            (ulBW == pulSubscriptionBW[i]))
          {
            // this one has the right BW, how about stream number?
            if ((HXR_OK == pSrcH->GetPropertyULONG32("StreamId", ulID)) &&
                ((int)ulID == i))
            {
#ifdef BW_BASED         
                // if this is more than one stream presentation,
                // we have to make sure the rule number.
                // if "RuleNumber" does not exist, this is from the old 
                // plugin, then, it should ignore this check.
                UINT32 ulRuleNum = 0;
                if ((-1 == m_lRuleNumber) ||
                  (HXR_OK != pSrcH->GetPropertyULONG32("RuleNumber", ulRuleNum)) ||
                  ((INT32)ulRuleNum == m_lRuleNumber))
                {
#endif                
                bFound = TRUE;
                
                // This is the right heaader, 
                ppRealHeaders[i] = pSrcH;
                ppRealHeaders[i]->AddRef();
                HX_RELEASE(pSrcH);            
                break; // we found for this stream, go to next one
#ifdef BW_BASED             
                }
#endif                
            }
          }
          HX_RELEASE(pSrcH);
      }

      if (!bFound)
      {
          // this should never haappen
          ppRealHeaders[i] = NULL;
          HX_ASSERT(FALSE);       
          return FALSE;
      }
    }

    return TRUE;
}


/****************************************************************************
 *  IHXFileFormatObject::Seek                               ref:  hxformt.h
 *
 *  This routine moves to the packet nearest to the requested time in the
 *  file. It is called, for example, in response to the user moving the
 *  slider to a different location in the playback timeline.
 */
STDMETHODIMP
CPurePlayFileFormat::Seek(UINT32 ulOffset)
{  
    m_pFFResponse->SeekDone(HXR_NOTIMPL);

    return HXR_OK;
}


/****************************************************************************
 *  IHXFileFormatObject::Close                              ref:  hxformt.h
 *
 *  This routine handles any cleanup required before closing the plug-in. All
 *  references to objects should be released and memory deallocated. This is
 *  called by the RMA core when the playback is finished or stopped.
 */
STDMETHODIMP
CPurePlayFileFormat::Close()
{
    /*
     * Release all of our resources that were allocated in 
     * InitFileFormat and along the way
     */
    AddRef();

    if (m_state != Closed)
    {    
      PurePlayFFState incommingState = m_state;
      BOOL bUnicastLaunched = FALSE;

      m_state = Closed;

      if (m_tryUnicast != NO_TRY)
      {
          if (HXR_OK == SwitchToUnicast())
          {
            bUnicastLaunched = TRUE;
          }
          else
          {
            HX_ASSERT(!m_bFileHeaderReadyCalled);         
            switch (m_tryUnicast)         
            {
            case TIMEOUT:
                ReportError(IDS_ERR_SM_NOACTIVESENDERS, HXLOG_ERR, HXR_FAIL);
                break;
            case SOCKET_FAIL:
                // there is no way to put address/port info through 
                // FileHeaderReady(), so just display error here.
                ReportErrorWithFormat(IDS_ERR_SM_SOCKFAILED, 
                                m_strLastErr, 
                                HXLOG_ERR, 
                                HXR_FAIL);
                break;
            case NO_MULTICAST:
                ReportError(IDS_ERR_SM_NOMULTICAST, HXLOG_ERR, HXR_FAIL);
                break;
            default:
                HX_ASSERT(!"Don't know this m_tryUnicast");
                ReportError(IDS_ERR_SM_GENERAL, HXLOG_ERR, HXR_FAIL);
            }

            HX_ASSERT(m_pFFResponse);           
          }

          m_tryUnicast = NO_TRY;
      }

      if (!bUnicastLaunched)
      {
          if (incommingState == PendingFileHeader)
          {
            if (m_pFFResponse)
            {
                m_pFFResponse->FileHeaderReady(HXR_FAIL, NULL);
            }
          }
      }

      if (m_hFileHeader)
      {
          m_pScheduler->Remove(m_hFileHeader);
          m_hFileHeader = 0;
      }

      HX_ASSERT((m_rgStreams.IsEmpty() && m_pInterfaceMgr) ||
              (!m_rgStreams.IsEmpty() && !m_pInterfaceMgr)); 


      if (m_rgStreams.GetSize() >= 1 && WantClientStats())
      {         
          SendStatistics();
      }         

      for (int i = m_rgStreams.GetSize() - 1; i >= 0; i--)
      {
          m_pFFResponse->StreamDone(i);
          CPurePlaySource* pSrc = (CPurePlaySource*)m_rgStreams.GetAt(i);       
          pSrc->Close();
          pSrc->Release();
      }

      m_rgStreams.RemoveAll();

      HX_DELETE(m_pInterfaceMgr);
      HX_VECTOR_DELETE(m_ppInterfacesReady);
      m_ulNumInterfacesReady = 0;
      HX_RELEASE(m_pLog);
      HX_RELEASE(m_pScheduler);
      HX_RELEASE(m_pRegistry);
      HX_RELEASE(m_pClassFactory);

      HX_RELEASE(m_pStreamDesc);
      HX_RELEASE(m_pRTPInfo);
      HX_RELEASE(m_pFFResponse);
      if (m_pFileObject)
      {
          m_pFileObject->Close();
          HX_RELEASE(m_pFileObject);
      }
        ClearFileBufferList();
        HX_DELETE(m_pFileBufferList);
      HX_RELEASE(m_pSDPDesc);
      HX_RELEASE(m_pFileHeader);
      HX_RELEASE(m_pStatusDesc);

      HX_RELEASE(m_pRequest);

      HX_RELEASE(m_pCName);
      HX_RELEASE(m_pUserName);
      HX_RELEASE(m_pTool);
      HX_RELEASE(m_pEmail);
    }

#ifdef XXXGo_DEBUG
    if (m_file)
    {
      fclose(m_file); 
    }
#endif      // XXXGo_DEBUG

#if defined(_TIMELINE_TRACE) || defined(_GETPACKET_TRACE)
    if (m_tfile)
    {
      fclose(m_tfile); 
    }
#endif      // _TIMELINE_TRACE || _GETPACKET_TRACE
    
    Release();

    return HXR_OK;
}


/****************************************************************************
 *  IHXFileFormatObject::CloseDone                          ref:  hxformt.h
 *
 */
STDMETHODIMP
CPurePlayFileFormat::CloseDone(HX_RESULT status)
{
    // it should have been released in ReadDone()
    HX_ASSERT(m_pFileObject);
    return HXR_OK;
}


/****************************************************************************
 *  IHXFileFormatObject::WriteDone                          ref:  hxformt.h
 *
 */
STDMETHODIMP
CPurePlayFileFormat::WriteDone(HX_RESULT status)
{
    return HXR_UNEXPECTED;
}

/****************************************************************************
 *  IHXASMSource::Subscribe                                   ref:  hxasm.h
 *
 */
STDMETHODIMP
CPurePlayFileFormat::Subscribe
(
    UINT16 uStreamNumber,
    UINT16 uRuleNumber
)
{
    return HXR_NOTIMPL;
}


/****************************************************************************
 *  IHXASMSource::Unsubscribe                                 ref:  hxasm.h
 *
 */
STDMETHODIMP
CPurePlayFileFormat::Unsubscribe
(
    UINT16 uStreamNumber,
    UINT16 uRuleNumber
)
{
    return HXR_NOTIMPL;
}

STDMETHODIMP
CPurePlayFileFormat::GetStatus
(                       
    REF(UINT16)         uStatusCode, 
    REF(IHXBuffer*)     pStatusDesc, 
    REF(UINT16)         ulPercentDone
)
{
    // The message given here will be deleted by the core
    // "Loading (%)" message once we start returning packets to the core
    uStatusCode = m_uStatusCode;

    if (uStatusCode == HX_STATUS_CONTACTING)
    {
      pStatusDesc = new CHXBuffer();
      pStatusDesc->AddRef();
    
      CHXString statusDesc;
      statusDesc.Format("Connecting...");
      pStatusDesc->Set((UCHAR*)(const char*) statusDesc,
                   strlen((const char*) statusDesc) + 1);

    }
    else if (uStatusCode == HX_STATUS_BUFFERING)
    {
      ulPercentDone = (100 * (HX_GET_BETTERTICKCOUNT() - m_ulListeningWaitStartTime)) /
                  LISTENINING_TIME_INTERVAL;

      if (ulPercentDone > 99)
      {
          ulPercentDone = 99;
      }

      pStatusDesc = new CHXBuffer();
      pStatusDesc->AddRef();

      CHXString statusDesc;
      statusDesc.Format("Listening");
      pStatusDesc->Set((UCHAR*)(const char*) statusDesc,
                   strlen((const char*) statusDesc) + 1);
    }

    return HXR_OK;
}


/*
** First figure out the stream with the smallest TS to send to the core.
** If packet is pending for the stream with smallest TS, send it.
*/
HX_RESULT
CPurePlayFileFormat::ProcessPendingPackets(BOOL bUseAutoTime,
                                 ULONG32 ulTimeNow)
{
    /* we need to look for a packet with the smallest TS */
    INT32 j;
    UINT32 ulTS = 0;
    HX_RESULT theErr = HXR_OK;
    CPurePlaySource* pSource = NULL;
    CStream* pStream = NULL;

    if (bUseAutoTime)
    {
      ulTimeNow = HX_GET_BETTERTICKCOUNT();
    }

    /*
     * We may be waiting for a packet off the wire or we already have the
     * packet and need to send it to the core.  In GetPacket, the pending
     * flag is set for each stream that the core needs.  DataFromSocket will
     * add the packets for each stream to the pending list.
     */

    if (m_state == Closed)
    {
      // Since we have to send BYE msg right before we stop, and since we get 
      // what we send from the same socket, this pkt is our RTCP_BYE msg.
      return HXR_CLOSED;
    }
 
    if (0 >= m_uActiveStreams)
    {
      // there is no active stream.  
      return HXR_OK;
    }    

    HX_ASSERT(m_uActiveStreams == m_rgStreams.GetSize());
    
    if (!m_bFirstPktRcvd)
    {
      m_bFirstPktRcvd = TRUE;
      m_ulStartTimeMS = HX_GET_BETTERTICKCOUNT();
      m_ulHXOffset = 0;
    }

    /* Check if we need to look for a packet with the smallest TS */
    if (m_lSmallestTSStrm == -1)
    {
      INT32 lSmallest = -1;
      ULONG32 ulSmallest = 0;
      INT32 lLargest = -1;
      ULONG32 ulLargest = 0;
      BOOL bInitial = TRUE;
      BOOL bIsLost = FALSE;
      
      // Find the stream with the smallest time stamp
      // iterate through all the streams and compare the TS's
      for (j = 0; j < m_rgStreams.GetSize(); j++)
      {
          if ((pSource = (CPurePlaySource*)m_rgStreams.GetAt(j)) &&
            (pStream = pSource->GetStreamByStrmId(j)))
          {     
            theErr = pStream->GetNextTS(ulTS, ulTimeNow);
            if (HXR_OK == theErr)
            {
                if (bInitial)
                {
                  ulSmallest = ulTS;
                  lSmallest = j;
                  ulLargest = ulTS;
                  lLargest = j;
                  bInitial = FALSE;
                }
                else 
                {
                  if (!IS_TS1_LESS_THAN_EQUAL_TS2(ulSmallest, ulTS))
                  {
                      ulSmallest = ulTS;
                      lSmallest = j;
                  }
                  else if (!IS_TS1_LESS_THAN_EQUAL_TS2(ulTS, ulLargest))
                  {
                      ulLargest = ulTS;
                      lLargest = j;
                  }
                }
            }
            else if (HXR_BUFFERING == theErr)
            {                     
                // We must fullfill buffering for all streams before
                // proceeding to successfully resequence any
                // out-of-order packets.  However, if we have nothing
                // in the transport buffer, there is no limit to
                // how long we may need to wait, so we do not wait
                // in such case.
                if (pStream->GetBufferedPacketCount() > 0)
                {
#ifdef _TIMELINE_TRACE
                  if (m_tfile)
                  {
                      fprintf(m_tfile, 
                            "BUFFERING #%d  BufSze=%d BufAge=%d\n", 
                            j,
                            pStream->GetBufferedPacketCount(),
                            pStream->GetBufferAge(ulTimeNow));
                  }                 
#endif      // _TIMELINE_TRACE
                  // if (!m_bFirstPktSent)
                  {
                      // During initial buffering make sure we wait
                      // long enough to have good selection of the
                      // smallest time stamped packet
                      lSmallest = -1;
                      break;
                  }
                }
#ifdef _TIMELINE_TRACE
                else if (m_tfile)
                {
                  fprintf(m_tfile, "EMPTY #%d\n", j);
                }             
#endif      // _TIMELINE_TRACE
            }
            else if (HXR_STREAM_DONE == theErr)
            {
                pSource->RemoveSource(pStream->m_ulSSRC);
#ifdef _TIMELINE_TRACE
                if (m_tfile)
                {
                  fprintf(m_tfile, "STREAM_DONE #%d\n", j);
                }             
#endif      // _TIMELINE_TRACE                            
            }
            else 
            {
                // this packet is lost...
                // Since a TS for next packet is unknown, we will use Last 
                // TS for this stream -> this stream has to be the one with 
                // the smallest TS as a packet with this TS has already been sent to 
                // the core.
                lSmallest = j;
                bIsLost = TRUE;
#ifdef _TIMELINE_TRACE
                if (m_tfile)
                {
                  fprintf(m_tfile, "LOST #%d\n", j);
                }
#endif      // _TIMELINE_TRACE  
                break;
            }                 
          }       
      }
      
      m_lSmallestTSStrm = lSmallest;
      
      // Handle Offset and Resync (only on data of non-lost packets)
      if ((!bIsLost) && (m_lSmallestTSStrm != -1))
      {
#ifdef _ALLOW_TS_OFFSETTING
#ifdef _DO_RESYNC
          if (m_bFirstPktSent)
          {
            LONG32 lTSDelta = (LONG32) (ulSmallest - m_ulSmallestTS);
            
            // If there is a jolt in the timeline - resync
            if ((!m_bDisableOffsetting) &&
                ((lTSDelta > MAX_TIME_GAP_FORWARD) ||
                 (lTSDelta < (-MAX_TIME_GAP_BACKWARD))))
            {
                // Resync
                m_ulHXOffset += lTSDelta;
                
#ifdef _TIMELINE_TRACE
                if (m_tfile)
                {
                  fprintf(m_tfile, "RESYNC #%d by %dms\n", m_lSmallestTSStrm, lTSDelta);
                }
#endif      // _TIMELINE_TRACE 
                
                if (!m_bRMPresentation)
                {
                  // Time stamps do not have to be ordered
                  m_ulHXOffset -= STANDARD_START_HXOFFSET;
                }
            }
#ifdef _DO_BUFFER_OCCUPANCY_CHECKS
            else
            {

                lTSDelta = (LONG32) (ulLargest - m_ulSmallestTS);
                
                if (lTSDelta > MAX_TIME_GAP_FORWARD)
                {
                  if ((pSource = (CPurePlaySource*) m_rgStreams.GetAt(lLargest)) &&
                      (pStream = pSource->GetStreamByStrmId(lLargest)))
                  {
                      if (pStream->GetBufferedPacketCount() > MAX_SUSPENDED_BUFFER_SIZE)
                      {
                        // We have large number of packets accumulated in the
                        // transport buffer and no chance of reducing the
                        // buffer count any time soon.
                        // Remove a packet from this stream.
                        IHXPacket*  pPacket = NULL;
                        pStream->GetPacket(pPacket, ulTimeNow);
                        HX_RELEASE(pPacket);
                      }
                  }
                }
            }
#endif      // _DO_BUFFER_OCCUPANCY_CHECKS
          }
#endif      // _DO_RESYNC
#endif      // _ALLOW_TS_OFFSETTING
          
          m_ulSmallestTS = ulSmallest;
      }
    }
      
    // Check if we found the smallest time stamp stream
    if (m_lSmallestTSStrm != -1)
    { 
      pSource = (CPurePlaySource*)m_rgStreams.GetAt(m_lSmallestTSStrm);
      
      if (pSource->GetPendingFlag(m_lSmallestTSStrm))
      {    
          pStream = pSource->GetStreamByStrmId(m_lSmallestTSStrm);
          HX_ASSERT(pStream);
          
          IHXPacket*    pPacket = NULL;
          theErr = pStream->GetPacket(pPacket, ulTimeNow);
          if (HXR_OK == theErr)
          { 
            /*
             * Fixup the packet with the correct stream number
             */
            IHXBuffer* pBuffer = NULL;
            UINT16          uStreamNumber;
            UINT8     chASMFlags;
            UINT16          uASMRule;
            ULONG32         ulNewTS;
            ULONG32         ulRTPTS;
            BOOL      bLostPacket = pPacket->IsLost();
            BOOL      bIsRMStream = pSource->IsRMSource();
            
            if (bIsRMStream)
            {
                pPacket->Get(pBuffer,
                         ulTS, 
                         uStreamNumber, 
                         chASMFlags, 
                         uASMRule);
            }
            else
            {
                ((IHXRTPPacket*) pPacket)->GetRTP(pBuffer,
                                           ulTS, 
                                           ulRTPTS,
                                           uStreamNumber, 
                                           chASMFlags, 
                                           uASMRule);
            }

            if (bLostPacket)
            {
                ulNewTS = pStream->GetLastHXTime();
            }
            else
            {
                ulNewTS = ulTS;

#ifdef _ALLOW_TS_OFFSETTING
                ulNewTS -= m_ulHXOffset;
                
                if ((m_ulHXOffset != 0) && bIsRMStream)
                {
                  IHXBuffer* pSubBuffer = NULL;
                  LONG32 lOffset = ulNewTS - ulTS;
                  
                  if (lOffset != 0)
                  {
                      switch (pSource->GetStreamType())
                      {
                      case CStream::STREAM_TYPE_VIDEO:
                        OffsetRMVideoPacket(ulTS,
                            pBuffer->GetBuffer(),
                            pBuffer->GetSize(),
                            lOffset,
                            pSubBuffer,
                            m_pClassFactory);
                        break;
                      case CStream::STREAM_TYPE_EVENT:
                        OffsetRMEventPacket(ulTS,
                            pBuffer->GetBuffer(),
                            pBuffer->GetSize(),
                            lOffset,
                            pSubBuffer,
                            m_pClassFactory);
                        break;
                      case CStream::STREAM_TYPE_IMGMAP:
                        OffsetRMImageMapPacket(ulTS,
                            pBuffer->GetBuffer(),
                            pBuffer->GetSize(),
                            lOffset,
                            pSubBuffer,
                            m_pClassFactory);
                        break;
                      default:
                        /* nothing to do */
                        break;
                      }
                  }
                  
                  if (pSubBuffer)
                  {
                      pBuffer->Release();
                      pBuffer = pSubBuffer;
                      pSubBuffer = NULL;
                  }
                }
#endif      // _ALLOW_TS_OFFSETTING
            }

            if (bIsRMStream)
            {
                pPacket->Set(pBuffer,
                         ulNewTS,
                         m_lSmallestTSStrm, 
                         chASMFlags,
                         uASMRule);
            }
            else
            {
                ((IHXRTPPacket*) pPacket)->SetRTP(pBuffer,
                                           ulNewTS,
                                           ulRTPTS,
                                           m_lSmallestTSStrm, 
                                           chASMFlags,
                                           uASMRule);
            }
            
            if (bLostPacket)
            {
                pStream->m_ulLost++;
                
#ifdef XXXGo_DEBUG      
                if (m_file)
                {
                  fprintf(m_file, "    LOST: Last Time (%u) \n", pStream->GetLastHXTime());
                }             
                if (pSource->m_pFFLog)
                {
                  fprintf(pSource->m_pFFLog, "    LOST: Last Time (%u) \n", pStream->GetLastHXTime());
                }             
#endif
                if (!m_bFirstPktSent)
                {
                  HX_ASSERT(m_bFirstPktSent);
                  return HXR_OK;
                }
            }
            else
            {
                pStream->SetLastHXTime(ulNewTS);
#ifdef XXXGo_DEBUG            
                if (m_file)
                {
                  fprintf(m_file, "        ALLGOOD(Strm=%u): ulTime(%u) - offset(%d) = %u\n", 
                      m_lSmallestTSStrm,
                      ulTS, 
                      m_ulHXOffset, ulTS - m_ulHXOffset);    
                }
                if (pSource->m_pFFLog)
                {
                  fprintf(pSource->m_pFFLog, "%d:  rule(%d): ulTime(%u) - offset(%d) = %u\n", 
                      m_lSmallestTSStrm, uASMRule, ulTS, m_ulHXOffset, 
                      ulTS - m_ulHXOffset);
                }
#endif      // XXXGo_DEBUG
                
#ifdef _TIMELINE_TRACE
                if (m_tfile)
                {
                  fprintf(m_tfile, "        ALLGOOD(Strm=%u): ulTime(%u) - offset(%d) = %u; RT=%u RelTS=%d BufSze=%d BufAge=%d\n", 
                      m_lSmallestTSStrm,
                      ulTS,
                      m_ulHXOffset, ulTS - m_ulHXOffset,
                      HX_GET_BETTERTICKCOUNT() - m_ulStartTimeMS,
                      ulNewTS - m_ulFirstTSSent,
                      pStream->GetBufferedPacketCount(),
                      pStream->GetBufferAge(ulTimeNow));
                }
#endif      // _TIMELINE_TRACE
                
                HX_RELEASE(pBuffer);

                // We allow over-drawing from the transport buffer once the
                // the preroll is completed and normal packet-fetch time line
                // is established.
                // This is not needed for application to work, but rather
                // a safety fallback in case preroll is exhausted.
                if ((!pStream->IsBufferFlexible()) && m_bFirstPktSent)
                {
                  if (((ULONG32) (ulNewTS - m_ulFirstTSSent)) >=
                      FLEX_BUFFER_SWITCHOVER_TIME)
                  {
                      pStream->SetBufferFlexible(TRUE);
                  }
                }
            }
            
            pStream->SetPendingFlag(FALSE);   
            if (!m_bFirstPktSent)
            {
                m_ulFirstTSSent = ulNewTS;
                m_bFirstPktSent = TRUE;
            }
            
#ifdef _GETPACKET_TRACE
            if (m_tfile)
            {
                if (bIsRMStream)
                {
                  fprintf(m_tfile, "<<PacketReady strm=%2u  RT=%10u RelTS=%10d BSze=%7d BAge=%7d L=%d\n", 
                      m_lSmallestTSStrm, 
                      HX_GET_BETTERTICKCOUNT() - m_ulStartTimeMS,
                      ulNewTS - m_ulFirstTSSent,
                      pStream->GetBufferedPacketCount(),
                      pStream->GetBufferAge(ulTimeNow),
                      bLostPacket ? 1 : 0);
                }
                else
                {
                  fprintf(m_tfile, "<<PacketReady strm=%2u  RT=%10u TS=%10d RTPTS=%10d BSze=%7d BAge=%7d L=%d\n", 
                      m_lSmallestTSStrm, 
                      HX_GET_BETTERTICKCOUNT() - m_ulStartTimeMS,
                      ulNewTS,
                      ulRTPTS,
                      pStream->GetBufferedPacketCount(),
                      pStream->GetBufferAge(ulTimeNow),
                      bLostPacket ? 1 : 0);
                }
            }                 
#endif      // _GETPACKET_TRACE

            m_pFFResponse->PacketReady(HXR_OK, pPacket);
            
            pPacket->Release();
          }
          else if (HXR_STREAM_DONE == theErr)
          {     
            pSource->RemoveSource(pStream->m_ulSSRC);
            
#ifdef XXXGo_DEBUG               
            if (pStream->IsMarkedAsEnd())
            {
                fprintf(pSource->m_pFFLog, "BYE %d \n", i);
            }
#endif
            // it should be buffering.  Look at rtpmisc/packetq.cpp GetPacket()
            HX_ASSERT(pStream->IsMarkedAsEnd());
          }
          else
          {
            HX_ASSERT(!"Should never never get here since GetNextTS is"
                     "checking against buffering");
          }
      }
    }

    m_lSmallestTSStrm = -1;

    return HXR_OK;
}

HX_RESULT
CPurePlayFileFormat::StreamDone(HX_RESULT status, UINT16 iStream)
{
    if (status != HXR_OK)
    {
      CHXString str;
      str.Format("%d", iStream);
      ReportErrorWithFormat(IDS_ERR_SM_ABNORMALTERMINATION, str, HXLOG_ERR,
                        status);          
    }
    
    if (m_pFFResponse)
    {
      m_pFFResponse->StreamDone(iStream);
    }

    return HXR_OK;
};


HX_RESULT
CPurePlayFileFormat::GetCName(REF(IHXBuffer*) pCName)
{
    pCName = m_pCName;
    if (pCName != NULL)
    {
      pCName->AddRef();
      return HXR_OK;
    }

    return HXR_FAIL;
}

HX_RESULT
CPurePlayFileFormat::GetUserName(REF(IHXBuffer*) pUserName)
{
    pUserName = m_pUserName;
    if (pUserName != NULL)
    {
      pUserName->AddRef();
      return HXR_OK;
    }

    return HXR_FAIL;
}

HX_RESULT
CPurePlayFileFormat::GetTool(REF(IHXBuffer*) pTool)
{
    pTool = m_pTool;
    if (pTool != NULL)
    {
      pTool->AddRef();
      return HXR_OK;
    }

    return HXR_FAIL;
}

HX_RESULT
CPurePlayFileFormat::GetEmailName(REF(IHXBuffer*) pEmail)
{
    pEmail = m_pEmail;
    if (pEmail != NULL)
    {
      pEmail->AddRef();
      return HXR_OK;
    }

    return HXR_FAIL;
}

/*
 *  Iterate throu all interfaces on which we have received multicast pkts on,
 *  and start assigning stream numbers.  After this process m_rgStreams will 
 *  contain all PurePlaySource obj from which interface.
 */
UINT32
CPurePlayFileFormat::AssignStreamNumbers
(
    UINT32 ulNumInterfacesReady,
    CHXPtrArray** ppInterfacesReady)
{
    HX_ASSERT(m_rgStreams.IsEmpty());

    // accross all the IFs
    UINT16  unBaseStreamNum = 0;
    UINT16  unNextStreamNum = 0;    

    // for each IF
    CHXPtrArray* prgStreams = NULL;
    UINT16  unNumStreams = 0;
    CPurePlaySource* pSrc = NULL;
    UINT16  unNumStrm = 0;
    
    // go throu each IF, and consolidate them in one.
    for (UINT32 j = 0; j < ulNumInterfacesReady; j++)
    {
      prgStreams = ppInterfacesReady[j];
      ppInterfacesReady[j] = NULL;
      
      unNumStreams          = prgStreams->GetSize();
      pSrc            = NULL;
      unNumStrm       = 0;
      
      HX_ASSERT(1 <= unNumStreams); // got to be at least one stream
            
      // since the IP in SDP may not contain a source, we may have to remove that
      // source from the m_rgStreams. 
    
      for (UINT32 i = 0; i < unNumStreams; i++)
      {
          pSrc = (CPurePlaySource*)prgStreams->GetAt(i);
          
          unNumStrm = (UINT16)pSrc->GetTrueNumSrc();

          if (pSrc->IsEncryptedSource() &&
            pSrc->IsRMSource())
          {
            m_bDisableOffsetting = TRUE;
          }
          
          if (1 <= unNumStrm)
          {
            // make This Src ID
            pSrc->MakeSsrc();
      
            // if there is more than one sender source, we have to assign stream
            // numbers for them.
            unNextStreamNum = pSrc->SetBaseStreamNum(unBaseStreamNum);
            // since we have waited for a period of time, if we haven't 
            // received RTCP pkt, just set it as synced.
            pSrc->StopBuffering();
            
            // we need to put those streams
            for (UINT32 ul = unBaseStreamNum; 
                 ul < unNextStreamNum; 
                 ul++)
            {
                m_rgStreams.SetAtGrow(ul, pSrc);
                pSrc->AddRef();
            }
    
            // release the original AddRef
            HX_RELEASE(pSrc);
            unBaseStreamNum = unNextStreamNum;
          }
          else
          {
            // the IP listed in SDP file contains no active source at
            // this moment.  Delete the source
            pSrc->Close();
            // this should delete the CPurePlaySource obj
            HX_RELEASE(pSrc);
          }
      }

      prgStreams->RemoveAll();
    }

    return unNextStreamNum;
}

/*
 *  If there are multiple sessions in multiple interfaces, we will play them 
 *  all!
 */
HX_RESULT
CPurePlayFileFormat::ProcessFileHeaderCallback()
{
    UINT32 i;
    UINT32  ulNextTimeout = 0;
    UINT32  ulTimeoutVal = 0;
    BOOL    bAtLeastOneReady = FALSE;
    ULONG32 ulWaitTime = HX_GET_BETTERTICKCOUNT() - m_ulFileHeaderWaitStartTime;

    CHXPtrArray* prgStreams = NULL;
    HX_ASSERT(m_pInterfaceMgr && m_pInterfaceMgr->m_ulNumInterfaces);
    BOOL bLastOneMoreTry = m_bOneMoreTry;

    m_hFileHeader = NULL;

    if (m_ppInterfacesReady)
    {
      HX_ASSERT(m_ulNumInterfacesReady > 0);
      
      m_uActiveStreams =      
          (UINT16)AssignStreamNumbers(m_ulNumInterfacesReady, m_ppInterfacesReady);
      
      m_pFileHeader->SetPropertyULONG32("StreamCount", m_uActiveStreams);
      
#ifdef _DEBUG     
      m_bFileHeaderReadyCalled = TRUE;
#endif                
      
      // they are already synched
      m_bInitialPacket = TRUE;
      
      HX_ASSERT(m_uActiveStreams >= 1);
      m_uStatusCode = HX_STATUS_READY; //HX_STATUS_BUFFERING;
      
      m_bSourceAdmissionClosed = TRUE;
      
      // if there RA stream?
      UINT32 ulRAStreamNum = 0;
      if (GetRAStreamNum(ulRAStreamNum))
      {
          m_lRAStreamNum = (INT32)ulRAStreamNum;
      }
      else
      {
          // there is no RA stream
          m_lRAStreamNum = NO_EXIST;
      }
      
      // clean up 
      HX_DELETE(m_pInterfaceMgr);
      HX_VECTOR_DELETE(m_ppInterfacesReady);
      m_ulNumInterfacesReady = 0;

      m_state = Ready;

      return m_pFFResponse->FileHeaderReady(HXR_OK, m_pFileHeader);
    }
    
    // go through each IF
    for (i = 0; i < m_pInterfaceMgr->m_ulNumInterfaces; i++)
    {
      prgStreams = m_pInterfaceMgr->GetSession(i);
      if (IsFileHeaderReady(prgStreams, ulTimeoutVal))
      {     
          if (!m_ppInterfacesReady)
          {
            m_ppInterfacesReady = new 
                CHXPtrArray*[m_pInterfaceMgr->m_ulNumInterfaces];
            memset(m_ppInterfacesReady, 0, 
                sizeof (CHXPtrArray*) * m_pInterfaceMgr->m_ulNumInterfaces);      
          }
    
          m_ppInterfacesReady[m_ulNumInterfacesReady++] = prgStreams;
          bAtLeastOneReady = TRUE;
      }
      else 
      {
          // When FileHeader is not ready, ulTimeoutVal is set...
          // Take the bigger one
          if (ulTimeoutVal > ulNextTimeout)
          {
            ulNextTimeout = ulTimeoutVal;
          }

          if (bLastOneMoreTry && !m_bOneMoreTry)
          {
            // we are going to wait one more CB for the last time
            break;
          }
      }
    }

    /*
     * sanity check!!!
     */
    HX_ASSERT(m_ulNumInterfacesReady <= m_pInterfaceMgr->m_ulNumInterfaces);
    HX_ASSERT((bAtLeastOneReady && m_ulNumInterfacesReady && m_ppInterfacesReady) ||
      (!bAtLeastOneReady && !m_ulNumInterfacesReady && !m_ppInterfacesReady && 
      ulNextTimeout));

    if (!bAtLeastOneReady)
    {
      // If we are timed out, Close(), otherwise, schedule a CB
      BOOL bTimedOut = ulWaitTime > m_ulFailoverTimeout;

      if (bTimedOut)
      {
          // this is it...there is no pkts coming in from any interface at all
          m_tryUnicast = TIMEOUT;
          m_bSourceAdmissionClosed = TRUE;
          // this should be empty
          HX_ASSERT(m_rgStreams.IsEmpty());
          Close();                
      }
      else
      {
          // needs to be some valid number
          HX_ASSERT(ulNextTimeout && ulNextTimeout < 0xffffffff);
          
          // there's got to be some stream, wait some more.
          CPurePlayFileFormat::CFileHeaderCallback*    
          pFHCB = new CPurePlayFileFormat::CFileHeaderCallback(this);
    
          pFHCB->AddRef();
          m_hFileHeader = m_pScheduler->RelativeEnter(pFHCB, ulNextTimeout);
          pFHCB->Release();   
      }

      return HXR_OK;
    }

    // Give transport buffer some time to buffer up
    {
      CPurePlayFileFormat::CFileHeaderCallback*  
          pFHCB = new CPurePlayFileFormat::CFileHeaderCallback(this);
    
      pFHCB->AddRef();
      m_hFileHeader = m_pScheduler->RelativeEnter(pFHCB, TRANSPORT_BUFFERING_TIME);
      pFHCB->Release();
    }

    return HXR_OK;
}

/*
 *  returns TRUE iff we have a session to play
 */
BOOL
CPurePlayFileFormat::IsFileHeaderReady(CHXPtrArray* prgStreams, 
                               REF(UINT32) ulTimeoutVal)
{
    HX_ASSERT(prgStreams);

    BOOL          bReady = TRUE;
    UINT16        unNumStreams = prgStreams->GetSize();    
    CPurePlaySource*    pSrc = NULL;
    HX_ASSERT(unNumStreams);

    ulTimeoutVal = PP_RM_TIMEOUT_MS;

    if (m_bRMPresentation)
    {
      // we know exactly how many source is in each session...ONE!
      for (UINT16 i = 0; i < unNumStreams; i++)
      {
          pSrc = (CPurePlaySource*)prgStreams->GetAt(i);
          HX_ASSERT(pSrc);
          
          if (pSrc->GetStartedSourceCount() == 0)
          {
            // we haven't received coherent packets yet.
            bReady = FALSE;
            break;
          }
      }
    }
    else
    {
      bReady = FALSE;
      // we have to decide a number of sources in each session...
      for (UINT16 i = 0; i < unNumStreams; i++)
      {
          pSrc = (CPurePlaySource*)prgStreams->GetAt(i);
          HX_ASSERT(pSrc);
      
          if (pSrc->GetStartedSourceCount() >= 1)
          {
            m_uStatusCode = HX_STATUS_BUFFERING;
            m_ulListeningWaitStartTime = HX_GET_BETTERTICKCOUNT();

            if (m_bOneMoreTry)
            {
                // we have recieved at least one packet in this session.
                // wait a couple more second to decide HOW MANY there are.
                ulTimeoutVal = PP_SYNC_TIMEOUT_MS;
                m_bOneMoreTry = FALSE;

            }
            else
            {
                bReady = TRUE;
            }

            // we've made up our mind!!!, so don't have to go throu the rest...
            break;
          }
      }    
    }

    return bReady;
}


/* 
*   Error reporting routines
*   Given an error id, it will try to read in a error string from a resource 
*   file.  If it fails, get a default error string.
*   Then, report!
*/ 
void
CPurePlayFileFormat::ReportError(UINT32 ulErrID, const UINT8 chSeverity, HX_RESULT ulHXCode)
{
    if (!m_pLog)
    {
      // try to get it one more time!
      HX_RESULT theErr = HXR_FAIL;
      if (m_pContext)
      {
          theErr = m_pContext->QueryInterface(IID_IHXErrorMessages, (void**)&m_pLog);
      }         

      if (HXR_OK != theErr)
      {
          // there is nothing we can do :(
          HX_ASSERT(FALSE && "ReportError: no Logger");
          return;
      }
    }

    HX_ASSERT(m_pLog);
    CHXString strErr;
    HX_ASSERT(strErr.IsEmpty());
    
    if (HXR_OK != GetResourceErrorString(ulErrID, strErr))
    {
      GetDefaultErrorString(ulErrID, strErr);
    }

    HX_ASSERT(!strErr.IsEmpty());
    m_pLog->Report(chSeverity, ulHXCode, 0, (const char*)strErr, 0);
}

void
CPurePlayFileFormat::ReportErrorWithFormat(UINT32 ulErrID, REF(CHXString) strText, 
                                 const UINT8 chSeverity, HX_RESULT ulHXCode)
{
    if (!m_pLog)
    {
      // try to get it one more time!
      HX_RESULT theErr = HXR_FAIL;
      if (m_pContext)
      {
          theErr = m_pContext->QueryInterface(IID_IHXErrorMessages, (void**)&m_pLog);
      }         

      if (HXR_OK != theErr)
      {
          // there is nothing we can do :(
          HX_ASSERT(FALSE && "ReportError: no Logger");
          return;
      }
    }

    HX_ASSERT(m_pLog);
    CHXString strErr;
    HX_ASSERT(strErr.IsEmpty());
    
    if (HXR_OK != GetResourceErrorString(ulErrID, strErr))
    {
      GetDefaultErrorString(ulErrID, strErr);
    }

    HX_ASSERT(!strErr.IsEmpty());

    // look for %s in theErr
    HX_ASSERT(strErr.Find("%s") != -1);

    CHXString strTheErr;
    strTheErr.Format((const char*)strErr, (const char*)strText);
    
    m_pLog->Report(chSeverity, ulHXCode, 0, (const char*)strTheErr, 0);
}


HX_RESULT
CPurePlayFileFormat::GetResourceErrorString(UINT32 ulErrID, REF(CHXString) strErr)
{
    if (!m_pContext)
    {
      HX_ASSERT(FALSE && "GetResourceErrorString: no context");
      return HXR_FAIL;
    }

    HX_RESULT theErr = HXR_FAIL;
    IHXExternalResourceManager* pResMgr = NULL;
    theErr = m_pContext->QueryInterface(IID_IHXExternalResourceManager, (void**)&pResMgr);
    if (HXR_OK != theErr)
    {
      return theErr;
    }
    
    IHXExternalResourceReader* pResRdr = NULL;
    theErr = pResMgr->CreateExternalResourceReader(CORE_RESOURCE_SHORT_NAME, pResRdr);
    if (HXR_OK != theErr)
    {
      HX_RELEASE(pResMgr);
      return theErr;
    }

    
#ifdef _WINDOWS
    char szDLLPath[1024]; /* Flawfinder: ignore */
    GetModuleFileName((HMODULE)g_hInstance, 
                  OS_STRING2(szDLLPath, sizeof(szDLLPath)), 
                  sizeof(szDLLPath));
    pResRdr->SetDefaultResourceFile(szDLLPath);
#endif

    IHXXResource* pRes = pResRdr->GetResource(HX_RT_STRING, ulErrID);
    if (!pRes)
    {
      HX_RELEASE(pResRdr);
      HX_RELEASE(pResMgr);
      return HXR_FAIL;
    }

    // wirte the error string to the out parameter
    strErr = (const char*)pRes->ResourceData();

    HX_RELEASE(pRes);
    HX_RELEASE(pResRdr);
    HX_RELEASE(pResMgr);

    return HXR_OK;
}


void
CPurePlayFileFormat::GetDefaultErrorString(UINT32 ulErrID, REF(CHXString) strErr)
{
    switch(ulErrID)
    {
      case IDS_ERR_SM_GENERAL:
          strErr = ERRSTR_SM_GENERAL;
          break;
      case IDS_ERR_SM_INITFAILED:
          strErr = ERRSTR_SM_INITFAILED;
          break;
      case IDS_ERR_SM_SOCKFAILED:
          strErr = ERRSTR_SM_SOCKFAILED;
          break;
      case IDS_ERR_SM_ABNORMALTERMINATION:
          strErr = ERRSTR_SM_ABNORMALTERMINATION;
          break;
      case IDS_ERR_SM_NOACTIVESENDERS:
          strErr = ERRSTR_SM_NOACTIVESENDERS;
          break;
      case IDS_ERR_SM_NOSDPPLIN:
          strErr = ERRSTR_SM_NOSDPPLIN;
          break;
      case IDS_ERR_SM_BADSDPFILE:
          strErr = ERRSTR_SM_BADSDPFILE;
          break;
      case IDS_ERR_SM_NOPLAYTYPE:
          strErr = ERRSTR_SM_NOPLAYTYPE;
          break;
      case IDS_ERR_SM_NOTFOUNDPAYLOAD:
          strErr = ERRSTR_SM_NOTFOUNDPAYLOAD;
          break;
      case IDS_ERR_SM_NOTFOUNDADDRESS:
          strErr = ERRSTR_SM_NOTFOUNDADDRESS;
          break;
      case IDS_ERR_SM_NOTFOUNDPORT:
          strErr = ERRSTR_SM_NOTFOUNDPORT;
          break;
      case IDS_ERR_SM_UNEXPECTEDPAYLOAD:
          strErr = ERRSTR_SM_UNEXPECTEDPAYLOAD;
          break;
      case IDS_ERR_SM_NOTENOUGHBANDWIDTH:
          strErr = ERRSTR_SM_NOTENOUGHBANDWIDTH;
          break;
      case IDS_ERR_SM_NOMULTICAST:
          strErr = ERRSTR_SM_NOMULTICAST;
          break;
      default:
          strErr = ERRSTR_SM_GENERAL;
          break;
    }
}

void CPurePlayFileFormat::ClearFileBufferList()
{
    if (m_pFileBufferList)
    {
        LISTPOSITION pos = m_pFileBufferList->GetHeadPosition();
        while (pos)
        {
            IHXBuffer* pBuffer = (IHXBuffer*) m_pFileBufferList->GetNext(pos);
            HX_RELEASE(pBuffer);
        }
        m_pFileBufferList->RemoveAll();
    }
}

HX_RESULT CPurePlayFileFormat::AggregateBuffers(REF(IHXBuffer*) rpBuffer)
{
    HX_RESULT retVal = HXR_FAIL;

    // Do we have a file buffer list?
    if (m_pFileBufferList && m_pFileBufferList->GetCount() > 0)
    {
        // Do we have only 1 buffer?
        if (m_pFileBufferList->GetCount() == 1)
        {
            // Get the only buffer in the list
            IHXBuffer* pAggBuffer = (IHXBuffer*) m_pFileBufferList->GetHead();
            if (pAggBuffer)
            {
                // Assign the out parameter
                HX_RELEASE(rpBuffer);
                rpBuffer = pAggBuffer;
                rpBuffer->AddRef();
                // Clear the return value
                retVal = HXR_OK;
            }
        }
        else
        {
            // We have more than one buffer, must aggregate
            //
            // Get the total size of the buffers
            UINT32 ulTotalSize = 0;
            LISTPOSITION pos = m_pFileBufferList->GetHeadPosition();
            while (pos)
            {
                IHXBuffer* pListBuffer =
                    (IHXBuffer*) m_pFileBufferList->GetNext(pos);
                if (pListBuffer)
                {
                    ulTotalSize += pListBuffer->GetSize();
                }
            }
            if (ulTotalSize)
            {
                // Create an aggregated buffer
                IHXBuffer* pAggBuffer = NULL;
                retVal = m_pClassFactory->CreateInstance(CLSID_IHXBuffer,
                                                         (void**) &pAggBuffer);
                if (SUCCEEDED(retVal))
                {
                    retVal = pAggBuffer->SetSize(ulTotalSize);
                    if (SUCCEEDED(retVal))
                    {
                        // Run through the list, copying in the buffers
                        UINT32 ulOffset = 0;
                        pos = m_pFileBufferList->GetHeadPosition();
                        while (pos)
                        {
                            IHXBuffer* pListBuffer =
                                (IHXBuffer*) m_pFileBufferList->GetNext(pos);
                            if (pListBuffer)
                            {
                                memcpy(pAggBuffer->GetBuffer() + ulOffset, /* Flawfinder: ignore */
                                       pListBuffer->GetBuffer(),
                                       pListBuffer->GetSize());
                                ulOffset += pListBuffer->GetSize();
                            }
                        }
                        // Assign the out parameter
                        HX_RELEASE(rpBuffer);
                        rpBuffer = pAggBuffer;
                        rpBuffer->AddRef();
                    }
                }
                HX_RELEASE(pAggBuffer);
            }
        }
    }

    return retVal;
}

HX_RESULT
CPurePlayFileFormat::SwitchToUnicast()
{
    HX_RESULT theErr = HXR_OK;
    IHXBuffer* pBuf = NULL;
    IHXPlayer* pPlayer = NULL;
    IHXHyperNavigate* pHyperNavigate = NULL;
    CHXURL* pURL = NULL;
    CHXString str;
    int lCount = -1;
    
    HX_ASSERT(m_pContext);
    HX_ASSERT(m_pScheduler);
    HX_ASSERT(m_pFileHeader);
    
    // get a URL from the file header.  No URL, no Unicast!
    theErr = m_pFileHeader->GetPropertyCString("UnicastURL", pBuf);
    if (HXR_OK != theErr || NULL == pBuf) goto bail;

    /* Depends on the URL, we want either IHXPlayer or IHXHyperNavigate */
    // XXXGo - Hack!!!
    // CHXURL thinks http://www.real.com and http://www.real.com/ are
    // invalid because there is no filename at the end, so always append 
    // "name.rm" at the end.
    // DON"T EVER USE RESOURCE OUT OF pURL.
    str = (const char*)pBuf->GetBuffer();
    lCount = str.ReverseFind('/');
    if (lCount != (str.GetLength() - 1))
    {
      str += "/name.rm";
    }
    else
    {
      str += "name.rm";
    }
    
    pURL = new CHXURL((const char*)str);
    if (!pURL) 
    {
      theErr = HXR_OUTOFMEMORY;
      goto bail;
    } 
    else if (FAILED(theErr = pURL->GetLastError()))
    {
      goto bail;
    }

    if (pURL->GetProtocol() == httpProtocol)
    {
      // this is HTTP, get IHXHyperNavigate
      theErr = m_pContext->QueryInterface(IID_IHXHyperNavigate, 
                                  (void**)&pHyperNavigate);
      if (HXR_OK != theErr || NULL == pHyperNavigate) 
          goto bail;
    }
    else
    {
      // this is something else, get IHXPlayer    
      theErr = m_pContext->QueryInterface(IID_IHXPlayer, (void**)&pPlayer);
      if (HXR_OK != theErr || NULL == pPlayer) 
          goto bail;
    }           

    HX_ASSERT((pHyperNavigate && !pPlayer) || (pPlayer && ! pHyperNavigate));

    // in the constractor, it will schedule itself, and by doing so,
    // a scheduler will addRef this obj.
    CUnicastCallback* pCB;
    pCB = new CUnicastCallback(m_pScheduler,
                         pPlayer,
                         pHyperNavigate,
                         (const char*) pBuf->GetBuffer());
    
    if (!pCB)
    {
      theErr = HXR_OUTOFMEMORY;
      goto bail;
    }

    theErr = HXR_OK;

bail:
    HX_RELEASE(pBuf);
    HX_RELEASE(pPlayer);
    HX_RELEASE(pHyperNavigate);
    HX_DELETE(pURL);
    return theErr;
}

BOOL
CPurePlayFileFormat::GetSourceObj(REF(CPurePlaySource*)pSrc, UINT32 i)
{
    if (m_rgStreams.GetSize() <= 0 || i >= (UINT32)m_rgStreams.GetSize())
    {
      // no source or out of range
      return FALSE;
    }

    pSrc = (CPurePlaySource*)m_rgStreams.GetAt(i);   

    return pSrc ? TRUE : FALSE;
}

BOOL
CPurePlayFileFormat::GetStreamObj(REF(CStream*)pStrm, UINT32 i)
{
    if (m_rgStreams.GetSize() <= 0 || i >= (UINT32)m_rgStreams.GetSize())
    {
      // no source or out of range
      return FALSE;
    }

    CPurePlaySource* pSrc = NULL;
    if (GetSourceObj(pSrc, i))
    {
      HX_ASSERT(pSrc);
      pStrm = (CStream*)pSrc->GetStreamByStrmId(i);

      return pStrm ? TRUE : FALSE;
    }

    return FALSE;
}


void 
CPurePlayFileFormat::statistics_cat(char* pStats, UINT32 ulStatsBufLen, LONG32 lData)
{
    char numb[12]; /* Flawfinder: ignore */

    ::sprintf(numb,"%10lu ", lData); /* Flawfinder: ignore */
    SafeStrCat(pStats,numb, ulStatsBufLen);
}

BOOL
CPurePlayFileFormat::IsRAStream(UINT32 ulStrmNum)
{
    if (NO_EXIST == m_lRAStreamNum)
      return FALSE;

    return m_lRAStreamNum == (INT32)ulStrmNum;
}

BOOL
CPurePlayFileFormat::GetRAStreamNum(REF(UINT32)ulRAStreamNum)
{
    HX_ASSERT(TRUE == IsSourceAdmissionClosed());

    CPurePlaySource* pSrc = NULL;
    IHXValues*    pHeader = NULL;
    IHXBuffer*    pCString = NULL;
    
    for (UINT32 i = 0; (UINT32)m_rgStreams.GetSize() > i; i++)
    {
      if (GetSourceObj(pSrc, i))
      {
          HX_ASSERT(pSrc);
          pSrc->GetHeader(pHeader, (UINT16)i);

          if (pHeader)
          {
            if (HXR_OK == pHeader->GetPropertyCString("MimeType", pCString) ||
                HXR_OK == pHeader->GetPropertyCString("mimetype", pCString)) 
            {     
                // case insensitive.
                if (0 == strcasecmp(RA_MINETYPE, (const char*)pCString->GetBuffer()))
                {
                  HX_RELEASE(pHeader);
                  HX_RELEASE(pCString);
                  ulRAStreamNum = i;
                  return TRUE;
                }
            
                HX_RELEASE(pHeader);
                HX_RELEASE(pCString);                           
            }
          }
      }
      else
      {
          return FALSE;
      }
    }

    return FALSE;
}


// table for all the registry entries that we are interested.
const char* CPurePlayFileFormat::zm_pRegistryEntryies[] = 
{
    "ResendRequested",
    "CurrentBandwidth",
    "Late",
    "AverageLatency",
    "LowLatency",
    "HightLatency",
    "AverageBandwidth",
    "Received",
    "ClipBandwidth",
    "Lost30",
    "Total30",
    "Renderer",
    "Recovered",
    "Total",
    "Lost",
    "Normal",
    NULL
};


// most of code taken from sned_statistics() in rmacore/rmartsppr.cpp 
/*
*   If any one of "StatsStyle", "StatsMask", and "StatsURL" is absent in SDP
*   file, no stats!!!
*/
BOOL
CPurePlayFileFormat::SendStatistics()
{
    HX_ASSERT(TRUE == m_bWantClientStats);
    BOOL        bRetVal = TRUE;
    UINT32      ulStatsLen = 0;
    CHAR*       pszStats = NULL;
    CHAR*       pszCodec = NULL;
    STREAM_STATS*   pStreamStats = NULL;
    
    UINT32      ulLength = 0;
    UINT32      i = 0;
    UINT32      ulTransport = 0;

    CHAR        szRegKeyName[MAX_DISPLAY_NAME] = {0};
    IHXBuffer*        pParentName = NULL;
    IHXBuffer*        pValue = NULL;

    CStream*          pStrm = NULL;

    BOOL        bSendStats = 0;

    CHXString         strTemp;
    IHXBuffer*        pGUID = NULL;
    IHXBuffer*     pClientID = NULL;

    UINT32      ulStatsMask = 0;
    UINT32      ulStatsStyle = 0;
    IHXBuffer*     pBuf = NULL;

    if (!m_pRegistry)
    {
      bRetVal = FALSE;
      goto bail;
    }

    // Make sure RA stream exists
    if (NO_EXIST == m_lRAStreamNum)
    {
      // RA stream doesn't exist -> no stats
      bRetVal = FALSE;
      goto bail;
    }

    // if any one of them doesn't exist, no stats!
    if (HXR_OK != m_pFileHeader->GetPropertyULONG32("StatsStyle", ulStatsStyle) ||
      HXR_OK != m_pFileHeader->GetPropertyULONG32("StatsMask", ulStatsMask) ||
      HXR_OK != m_pFileHeader->GetPropertyCString("StatsURL", pBuf))
    {
      // no stats
      bRetVal = FALSE;
      goto bail;
    }
    HX_RELEASE(pBuf);
    
    // we have RA stream, create stats1 & stats2
    // stats1 & stats2 are only for RA    
    HX_ASSERT(m_lRAStreamNum < m_rgStreams.GetSize());
    
    if (!GetStreamObj(pStrm, (UINT32)m_lRAStreamNum))
    {
          bRetVal = FALSE;
          goto bail;
    }

    HX_ASSERT(pStrm);

    // get the core stats from registry
    pStreamStats = new STREAM_STATS(m_pRegistry, pStrm->m_ulStreamRegId);

    if (!pStreamStats || !pStreamStats->m_bInitialized)
    {
      bRetVal = FALSE;
      goto bail;
    }

    ulStatsLen = MAX_DISPLAY_NAME + 40;
    pszStats   = new CHAR[ulStatsLen];

    if (!pszStats)
    {
      bRetVal = FALSE;
      goto bail;
    }
    else
    {
      memset(pszStats, 0, ulStatsLen);
    }

    // retreive the pszStats set by the renderer
    if (pStreamStats->m_pRenderer &&
        HXR_OK == m_pRegistry->GetPropName(pStreamStats->m_pRenderer->m_ulRegistryID, pParentName))
    {
      SafeSprintf(szRegKeyName, MAX_DISPLAY_NAME, "%s.Codec", pParentName->GetBuffer());

      if (HXR_OK == m_pRegistry->GetStrByName(szRegKeyName, pValue) && pValue)
      {
          ulLength = pValue->GetSize();
    
          pszCodec = new char[ulLength+1];
          strcpy(pszCodec, (const char*)pValue->GetBuffer()); /* Flawfinder: ignore */

          // replace space with underscore
          for (i = 0; i < ulLength; i++)
          {
            if (pszCodec[i] == ' ')
            {
            pszCodec[i] = '_';
            }
          }
          HX_RELEASE(pValue);
      }
    }
    HX_RELEASE(pParentName);

    if (ulStatsMask & 1UL)
    {
      // build statistics string
      SafeStrCat(pszStats, "Stat1:", ulStatsLen);

      // everytime we have a lost packet, we just create one and send it
      // off to the core, so the core thinks there is no lost packet.
      // Therefore, overwrite some stats here.
      
      // packet received -- all the pkt received
      statistics_cat(pszStats, ulStatsLen, pStrm->m_ulReceived);
      // Out of Order -- just out of order
      statistics_cat(pszStats, ulStatsLen, pStrm->m_ulReceived - pStrm->m_ulNormal);
      // Lost
      statistics_cat(pszStats, ulStatsLen, pStrm->m_ulLost);
      // early
      statistics_cat(pszStats, ulStatsLen, 0);  // no packets sent early in 6.0     
      // late     -- too late to send it to core
      statistics_cat(pszStats, ulStatsLen, pStrm->m_pTransBuf->GetLatePktCount());

      // audio_format
      if (!pszCodec)
      {
          SafeStrCat(pszStats, "N/A", ulStatsLen);    
      }
      else
      {
          SafeStrCat(pszStats, pszCodec, ulStatsLen);
      }
    }

    if (ulStatsMask & 2UL)
    {
      // divide pszStats levels if necessary
      if (ulStatsMask & 1UL)
      {
          SafeStrCat(pszStats, "][", ulStatsLen);
      }

      SafeStrCat(pszStats, "Stat2:", ulStatsLen);

      // need src obj for this...
      CPurePlaySource* pSrc = NULL; 
      GetSourceObj(pSrc, (UINT32)m_lRAStreamNum);     

      // Bandwidth info 
      if (pStreamStats->m_pClipBandwidth)
          statistics_cat(pszStats, ulStatsLen, pStreamStats->m_pClipBandwidth->GetInt());
      else if (pSrc)
          statistics_cat(pszStats, ulStatsLen, pSrc->GetBandwidth());
      else
          statistics_cat(pszStats, ulStatsLen, 0);

      if (pStreamStats->m_pAvgBandwidth)
          statistics_cat(pszStats, ulStatsLen, pStreamStats->m_pAvgBandwidth->GetInt());
      else
          statistics_cat(pszStats, ulStatsLen, 0);
          
      // Latency info
      if (pStreamStats->m_pHighLatency)
          statistics_cat(pszStats, ulStatsLen, pStreamStats->m_pHighLatency->GetInt());
      else
          statistics_cat(pszStats, ulStatsLen, 0);
          
      if (pStreamStats->m_pLowLatency)
          statistics_cat(pszStats, ulStatsLen, pStreamStats->m_pLowLatency->GetInt());
      else
          statistics_cat(pszStats, ulStatsLen, 0);    
          
      if (pStreamStats->m_pAvgLatency)
          statistics_cat(pszStats, ulStatsLen, pStreamStats->m_pAvgLatency->GetInt());
      else
          statistics_cat(pszStats, ulStatsLen, 0);    

      // Resend info
      if (pStreamStats->m_pResendRequested)
          statistics_cat(pszStats, ulStatsLen, pStreamStats->m_pResendRequested->GetInt());
      else
          statistics_cat(pszStats, ulStatsLen, 0);    

      if (pStreamStats->m_pResendReceived)
          statistics_cat(pszStats, ulStatsLen, pStreamStats->m_pResendReceived->GetInt());
      else
          statistics_cat(pszStats, ulStatsLen, 0);    

      if (pStreamStats->m_pLate)
          statistics_cat(pszStats, ulStatsLen, pStreamStats->m_pLate->GetInt());
      else
          statistics_cat(pszStats, ulStatsLen, 0);    

      // rebuffer info (in percent)
      statistics_cat(pszStats, ulStatsLen, 0);


      // Transport
      // 0 or UDP, 1 for TCP, 2 for Multicast...
      // and it is always Multicast
      ulTransport = 2L;
      
      statistics_cat(pszStats, ulStatsLen, ulTransport);

      // Startup latency, b/n join and first pkt      
      if (pSrc)
      {
          statistics_cat(pszStats, ulStatsLen, CALCULATE_ELAPSED_TICKS(pSrc->m_ulBeginClockTick, 
                                               pStrm->m_ulStartTimeMS));
      }                                             
      else
      {
          HX_ASSERT(FALSE);
          statistics_cat(pszStats, ulStatsLen, 0);
      }           
    }

    // put some extra info
    char duration[128]; /* Flawfinder: ignore */
    sprintf(duration, "#%u", /* Flawfinder: ignore */
          CALCULATE_ELAPSED_TICKS(pStrm->m_ulStartTimeMS, 
                            HX_GET_TICKCOUNT()) / MILLISECS_PER_SECOND);

    SafeStrCat(pszStats, duration, ulStatsLen);


    // actually POST it
    BeginPost(pszStats);

bail:

    HX_DELETE(pStreamStats);
    
    HX_VECTOR_DELETE(pszStats);
    HX_VECTOR_DELETE(pszCodec);

    HX_RELEASE(pParentName);
    HX_RELEASE(pValue);

    HX_RELEASE(pGUID);
    HX_RELEASE(pClientID);

    HX_RELEASE(pBuf);

    return bRetVal;
    
}


HX_RESULT
CPurePlayFileFormat::BeginPost(CHAR* pszStats)
{
    HX_RESULT theErr = HXR_OK;

    // Keep-Alive Header text
    //
    const char szKeepAlive[] = "\r\nConnection: Keep-Alive";

    const char szUserAgent[] = "\r\nUser-Agent: RMA/1.0 (compatible; RealMedia)";

    const char szHostHeader[] = "\r\nHost: ";

    const char szAcceptLang[] = "\r\nAccept-Language: ";

    IHXValues*    pURLProp = NULL;
    CHXURL* pURL = NULL;

    UINT32      ulPort = 0;
    IHXBuffer* pLanguage = NULL;
    IHXBuffer* pHost = NULL;
    IHXValues* pRequestHeaders = NULL;
    IHXBuffer* pResource = NULL;

    CHXString     strRequest;

    IHXValues*    pHeaders = 0;
    char*   pOutBuffer = 0;

    CScalableHTTP* pHTTP = NULL;

    /*
    * Get everything we need from the stats url
    */
    IHXBuffer* pBuf = NULL;
    HX_VERIFY(SUCCEEDED(m_pFileHeader->GetPropertyCString("StatsURL", pBuf)));        

    pURL = new CHXURL((const char*)pBuf->GetBuffer());
    if (!pURL) 
    {
      theErr = HXR_OUTOFMEMORY;
      goto exit;
    } 
    else if (FAILED(theErr = pURL->GetLastError()))
    {
      goto exit;
    }
    
    pURLProp = pURL->GetProperties();

    theErr = pURLProp->GetPropertyBuffer(PROPERTY_HOST, pHost);
    if (HXR_OK != theErr)
    {
      theErr = HXR_INVALID_URL_HOST;
      goto exit;
    } 

    theErr = pURLProp->GetPropertyULONG32(PROPERTY_PORT, ulPort);
    if (HXR_OK != theErr) goto exit;

    theErr = pURLProp->GetPropertyBuffer(PROPERTY_RESOURCE, pResource);
    if (HXR_OK != theErr) 
    {
      theErr = HXR_INVALID_URL_PATH;
      goto exit;
    }

    if (HXR_OK == m_pRequest->GetRequestHeaders(pRequestHeaders))
    {
      pRequestHeaders->GetPropertyCString("Language", pLanguage);
    }        
    

    /*
     * Get the RFC822 headers from the IHXRequest object
     */

    if (m_pRequest &&
      m_pRequest->GetRequestHeaders(pHeaders) == HXR_OK)
    {
      if (pHeaders)
      {
          IHXValues* pValuesRequestHeaders = new CHXHeader();
          pValuesRequestHeaders->AddRef();

          CHXHeader::mergeHeaders(pValuesRequestHeaders, pHeaders);

          /*
           * First spin through the headers to see how much space they
           * require (the extra 4 bytes per header is for overhead)
           */

          const char* pName;
          IHXBuffer* pValue;
          HX_RESULT result;
          UINT32 uBufferSize = 0;
          UINT32 uBufferPtr = 0;

          result = pValuesRequestHeaders->GetFirstPropertyCString(pName, pValue);

          while (result == HXR_OK)
          {
            uBufferSize += strlen(pName);
            uBufferSize += pValue->GetSize() - 1;
            uBufferSize += 4;

            pValue->Release();

            result = pValuesRequestHeaders->GetNextPropertyCString(pName, pValue);
          }

          /*
           * Allocate space for trailing '\0'
           */

          uBufferSize++;

          pOutBuffer = new char[uBufferSize];

          /*
           * Now spin through and built the outgoing string
           */

          result = pValuesRequestHeaders->GetFirstPropertyCString(pName, pValue);

          while (result == HXR_OK)
          {
            UINT32 uValueLength = pValue->GetSize() - 1;

            memcpy(&pOutBuffer[uBufferPtr], "\r\n", 2); /* Flawfinder: ignore */
            uBufferPtr += 2;
            memcpy(&pOutBuffer[uBufferPtr], pName, strlen(pName)); /* Flawfinder: ignore */
            uBufferPtr += strlen(pName);
            memcpy(&pOutBuffer[uBufferPtr], ": ", 2); /* Flawfinder: ignore */
            uBufferPtr += 2;
            memcpy(&pOutBuffer[uBufferPtr], /* Flawfinder: ignore */
                   pValue->GetBuffer(),
                   uValueLength);
            uBufferPtr += uValueLength;

            pValue->Release();

            result = pValuesRequestHeaders->GetNextPropertyCString(pName, pValue);
          }

          HX_ASSERT(uBufferPtr == uBufferSize - 1);

          pOutBuffer[uBufferPtr] = '\0';

          HX_RELEASE(pValuesRequestHeaders);
          HX_RELEASE(pHeaders);
      }
    }


    // The request is a standard HTTP based request created from the resource...
    // since string.Format() has a max length of 512 chars, we'll build up the string manually
    const char* pc;
    pc = (const char*)pResource->GetBuffer();

    strRequest = "POST ";
    if ('/' != *pc)
    {
      // we need '/' as the first thing
      strRequest += '/';
    }
    strRequest += pc;
    strRequest += " HTTP/1.0\r\nAccept: */*";
    strRequest += szUserAgent;
    strRequest += (pOutBuffer ? pOutBuffer : "");
    strRequest += szKeepAlive;    

    strRequest += szHostHeader;
    // Use actual host, even when going through proxy..
    strRequest += (const char*)pHost->GetBuffer();
    if(ulPort != 80)
    {
          strRequest += ":";
          strRequest.AppendULONG(ulPort);
    }

    if (pLanguage)
    {
        strRequest += szAcceptLang;
      strRequest += pLanguage->GetBuffer();
    }



    // for the body of POST
    strRequest += "\r\n";
    strRequest += "Content-Length: ";


    char szLength[256]; /* Flawfinder: ignore */
    itoa(strlen(pszStats), szLength, 10);
    strRequest += szLength;

    // set the body
    strRequest += "\r\n\r\n";
    strRequest += pszStats;


#ifdef XXXGo_DEBUG
    if (m_file)
    {
      fprintf(m_file, "\n********************\n");    
      fprintf(m_file, (const char*)strRequest);
      fprintf(m_file, "\n********************");
    }
#endif    

    // this is to create the TCP socket and actually send msg
    pHTTP = new CScalableHTTP();
    if (pHTTP)
    {
      pHTTP->AddRef();
      theErr = pHTTP->Init(m_pContext, m_ulServerTimeout);
      if (HXR_OK != theErr) goto exit;

      theErr = pHTTP->Post(strRequest, (const char*)pHost->GetBuffer(), (UINT16)ulPort);
      HX_RELEASE(pHTTP);
    }
   
exit:
    HX_RELEASE(pURLProp);
    HX_RELEASE(pBuf);
    
    HX_DELETE(pURL);

    HX_RELEASE(pLanguage);    
    HX_RELEASE(pHost);
    HX_RELEASE(pRequestHeaders);
    HX_RELEASE(pResource);

    HX_VECTOR_DELETE(pOutBuffer);

    HX_RELEASE(pHTTP);
    
    return theErr;
}


/**************************************************************************
 *  CPurePlayFileFormat::CFileHeaderCallback
 *
 *  Timeout class to send file header to the core with the right number of 
 *  stream count
 */

CPurePlayFileFormat::CFileHeaderCallback::CFileHeaderCallback
(
    CPurePlayFileFormat* pFF
)
    : m_lRefCount(0)
    , m_pFF(pFF)
{
    m_pFF->AddRef();
}

CPurePlayFileFormat::CFileHeaderCallback::~CFileHeaderCallback()
{
    m_pFF->Release();
}

STDMETHODIMP 
CPurePlayFileFormat::CFileHeaderCallback::QueryInterface
(
    REFIID riid, 
    void** ppvObj
)
{
    if (IsEqualIID(riid, IID_IUnknown))
    {
      AddRef();
      *ppvObj = this;
      return HXR_OK;
    }
    else if(IsEqualIID(riid, IID_IHXCallback))
    {
      AddRef();
      *ppvObj = (IHXCallback*)this;
      return HXR_OK;
    }

    *ppvObj = NULL;
    return HXR_NOINTERFACE;
}

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

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

    delete this;
    return 0;
}

STDMETHODIMP
CPurePlayFileFormat::CFileHeaderCallback::Func()
{
    if (m_pFF)
    {
      return m_pFF->ProcessFileHeaderCallback();
    }
    else
    {
      return HXR_UNEXPECTED;
    }
}


HX_RESULT 
CPurePlayFileFormat::GetLogger(REF(IHXErrorMessages*) pLog)
{
    pLog = m_pLog;
    pLog->AddRef();

    return HXR_OK;
}


inline
PurePlayFFState
CPurePlayFileFormat::GetCurrentState(void)
{
    return m_state;
}


/************************************************************************
 *  IHXTransportSyncServer methods            ref:  rtspif.h
 */
STDMETHODIMP
CPurePlayFileFormat::DistributeSyncAnchor(ULONG32 ulHXTime, 
                                ULONG32 ulNTPTime)
{
    return Distribute(DISTRIBUTE_SYNCANCHOR, 
                  ulHXTime, 
                  ulNTPTime);
}

STDMETHODIMP
CPurePlayFileFormat::DistributeSync(ULONG32 ulHXTime, 
                            LONG32 lHXTimeOffset)
{
    return Distribute(DISTRIBUTE_SYNC, 
                  ulHXTime, 
                  (ULONG32) lHXTimeOffset);
}

STDMETHODIMP
CPurePlayFileFormat::DistributeStartTime(ULONG32 ulHXRefTime)
{
    return Distribute(DISTRIBUTE_STARTTIME, 
                  ulHXRefTime);
}

HX_RESULT CPurePlayFileFormat::Distribute(ULONG32 ulDistributionMode,
                                ULONG32 ulVal1,
                                ULONG32 ulVal2)
{
    INT32 i = 0;
    INT32 j = 0;
    CPurePlaySource* pSource = NULL;
    CStream* pStream = NULL;
    HX_RESULT retVal = HXR_UNEXPECTED;

    if (m_rgStreams.GetSize() != 0)
    {
      retVal = HXR_OK;

      do
      {
          if ((pSource = (CPurePlaySource*) m_rgStreams.GetAt(j)) &&
            (pStream = pSource->GetStreamByStrmId(j)))
          {
            CPurePlaySource::DistributeToStream(pStream,
                                        ulDistributionMode,
                                        ulVal1,
                                        ulVal2);
          }

          j++;
      } while (j < m_rgStreams.GetSize());
    }
    else if (m_pInterfaceMgr && 
           (m_pInterfaceMgr->m_ulNumInterfaces != 0))
    { 
      CHXPtrArray* prgStreams = NULL;
      ULONG32 ulNumStreams;

      retVal = HXR_OK;

      do
      {     
          prgStreams = m_pInterfaceMgr->GetSession(j);
          ulNumStreams = 0;

          if (prgStreams)
          {
            ulNumStreams = prgStreams->GetSize();
          }

          for (i = 0; i < ulNumStreams; i++)              
          {
            pSource = (CPurePlaySource*) prgStreams->GetAt(i);
            pSource->Distribute(ulDistributionMode,
                            ulVal1,
                            ulVal2);
          } 
          
          j++;
      } while (j < m_pInterfaceMgr->m_ulNumInterfaces);
    }

    return retVal;
}


/******************************************************************************
*
*   Callback to switch to Unicast
*   Either pPlayer or pHyperNavigate has to be NULL...that's how it will know
*   which one to use.  
*/
CUnicastCallback::CUnicastCallback(
    IHXScheduler*  pScheduler, 
    IHXPlayer*        pPlayer,
    IHXHyperNavigate* pHyperNavigate,
    const char*     pchURL
)
    : m_lRefCount(0)
    , m_pScheduler(pScheduler)
    , m_pPlayer(pPlayer)
    , m_pHyperNavigate(pHyperNavigate)
    , m_strURL(pchURL)
{
    HX_ASSERT(m_pScheduler);
    HX_ASSERT((m_pPlayer && !m_pHyperNavigate) || (m_pHyperNavigate && !m_pPlayer));
    HX_ASSERT(!m_strURL.IsEmpty());
    
    m_pScheduler->AddRef();
    if (m_pPlayer)
    {
      m_pPlayer->AddRef();
    }
    else
    {
      HX_ASSERT(m_pHyperNavigate);
      m_pHyperNavigate->AddRef();
    }
    

    // Need to schedule itself
    m_pScheduler->RelativeEnter(this, 1000);    
}

CUnicastCallback::~CUnicastCallback()
{
    HX_RELEASE(m_pScheduler);
    HX_RELEASE(m_pPlayer);
    HX_RELEASE(m_pHyperNavigate);
}

STDMETHODIMP 
CUnicastCallback::QueryInterface
(
    REFIID riid, 
    void** ppvObj
)
{
    if (IsEqualIID(riid, IID_IUnknown))
    {
      AddRef();
      *ppvObj = this;
      return HXR_OK;
    }
    else if(IsEqualIID(riid, IID_IHXCallback))
    {
      AddRef();
      *ppvObj = (IHXCallback*)this;
      return HXR_OK;
    }

    *ppvObj = NULL;
    return HXR_NOINTERFACE;
}

STDMETHODIMP_(ULONG32) 
CUnicastCallback::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}

STDMETHODIMP_(ULONG32) 
CUnicastCallback::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
        return m_lRefCount;
    }

    delete this;
    return 0;
}

STDMETHODIMP
CUnicastCallback::Func()
{
    if (m_pHyperNavigate)
    {
      m_pHyperNavigate->GoToURL(m_strURL, NULL);
    }
    else if (m_pPlayer)
    {
       // if URL was not HTTP, then we need to stop the player first...
      // we don't wanna stop the player twice    
      if (!m_pPlayer->IsDone())
      {
          m_pPlayer->Stop();
      }

      if (HXR_OK == m_pPlayer->OpenURL(m_strURL))
      {
          m_pPlayer->Begin();  
      }     
    }
#ifdef _DEBUG
    else
    {
      HX_ASSERT(!"This can't be happening...");
    }
#endif    
    return HXR_OK;
}

CPurePlayFileFormat::InterfaceManager::InterfaceManager
(
    UINT32 ulNumInterfaces
)
    : m_ulNumInterfaces(ulNumInterfaces)
    , m_pulInterfaces(NULL)
{
    // better be at least one.
    HX_ASSERT(m_ulNumInterfaces);
    m_pulInterfaces = new UINT32[m_ulNumInterfaces];
    memset(m_pulInterfaces, 0, sizeof(UINT32) * m_ulNumInterfaces);

    // we are expecting this many...
    m_rgInterfaceSessions.SetSize(m_ulNumInterfaces);
}


CPurePlayFileFormat::InterfaceManager::~InterfaceManager()
{
    HX_VECTOR_DELETE(m_pulInterfaces);

    UINT32 ulSize = m_rgInterfaceSessions.GetSize();
    UINT32 j;
    UINT32 ulNumStreams;
    CHXPtrArray* pSession;
    CPurePlaySource* pSrc;
    for (UINT32 i = 0; i < ulSize; ++i)
    {
      // interface session that will be used should have been removed
      // from this array, so close and release ALL that's left    
      pSession = (CHXPtrArray*)m_rgInterfaceSessions.GetAt(i);
      if (!pSession)
      {
          continue;
      }
      
      ulNumStreams = pSession->GetSize();
      for (j = 0; j < ulNumStreams; ++j)
      {
          pSrc = (CPurePlaySource*)pSession->GetAt(j);
          if (pSrc)
          {
            pSrc->Close();
            pSrc->Release();
            pSrc = NULL;
          }
      }         
      pSession->RemoveAll();
      HX_DELETE(pSession);
    }
    m_rgInterfaceSessions.RemoveAll();
}

/* interfaceNo starts from 0 */
void
CPurePlayFileFormat::InterfaceManager::SetInterface
(
    UINT32 ulInterfaceNo, UINT32 ulInterface
)
{
    HX_ASSERT(m_ulNumInterfaces > 0);
    HX_ASSERT(ulInterfaceNo < m_ulNumInterfaces);
    HX_ASSERT(!m_pulInterfaces[ulInterfaceNo]);

    m_pulInterfaces[ulInterfaceNo] = ulInterface;
}

CHXPtrArray*
CPurePlayFileFormat::InterfaceManager::GetSession
(
    UINT32 ulInterfaceNo
)
{
    HX_ASSERT(m_ulNumInterfaces > 0);
    HX_ASSERT(ulInterfaceNo < m_ulNumInterfaces);

    return (CHXPtrArray*)m_rgInterfaceSessions.GetAt(ulInterfaceNo);
}

void
CPurePlayFileFormat::InterfaceManager::SetSession
(
    UINT32 ulInterfaceNo, 
    CHXPtrArray* pSession
)
{
    HX_ASSERT(m_ulNumInterfaces > 0);
    HX_ASSERT(ulInterfaceNo < m_ulNumInterfaces);
    HX_ASSERT(pSession);

    m_rgInterfaceSessions.SetAtGrow(ulInterfaceNo, pSession);
}


/************************************************************************
 *  IHXPropertyAdviser Interface Methods        ref:  hxfwrtr.h
 */
STDMETHODIMP CPurePlayFileFormat::GetPropertyULONG32(const char* pPropertyName,
                                         REF(ULONG32) ulPropertyValue)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pPropertyName &&
      (strcasecmp(pPropertyName, "IsPseudoFile") == 0))
    {
      ulPropertyValue = 1;
      retVal = HXR_OK;
    }

    return retVal;
}

STDMETHODIMP CPurePlayFileFormat::GetPropertyBuffer(const char* pPropertyName,
                                        REF(IHXBuffer*) pPropertyValue)
{
    return HXR_FAIL;
}

STDMETHODIMP CPurePlayFileFormat::GetPropertyCString(const char* pPropertyName,
                                         REF(IHXBuffer*) pPropertyValue)
{
    return HXR_FAIL;
}

STDMETHODIMP CPurePlayFileFormat::GetPropertySet(const char* pPropertySetName,
                                     REF(IHXValues*) pPropertySet)
{
    return HXR_FAIL;
}

Generated by  Doxygen 1.6.0   Back to index