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

hxaudply.cpp

/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: hxaudply.cpp,v 1.25.2.4 2005/01/19 19:33:03 ping Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

#include "hlxclib/stdio.h"
#include "hlxclib/stdlib.h"
#include "hlxclib/string.h"

#ifndef WIN32_PLATFORM_PSPC
#include "hlxclib/signal.h"
#endif

#include "hxresult.h"
#include "hxtypes.h"
#include "hxslist.h"
#include "hxcom.h"

#include "ihxpckts.h"
#include "hxengin.h"
#include "hxprefs.h"
#include "hxausvc.h"
#include "hxrasyn.h"
#include "hxerror.h"
#include "hxcore.h"

#include "hxmap.h"

#include "hxaudply.h"
#include "hxaudstr.h"
#include "hxaudses.h"
#include "hxaudvol.h"

#include "hxaudtyp.h"

#include "timeval.h"
#include "hxtick.h"

#ifdef _MACINTOSH
#include "hxmm.h"

extern  ULONG32   gTIMELINE_MUTEX;
#endif

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

#if defined(HELIX_FEATURE_PREFERENCES)
#include "hxprefs.h"
#include "hxprefutil.h"
#endif /* HELIX_FEATURE_PREFERENCES */

#ifdef HELIX_CONFIG_MIN_PCM_PUSHDOWN_BYTES
// These values were arrived at via trial and error on a linux, xi586 (pentium)
#define MINIMUM_AUDIO_GRANULARITY   50
#define MAXIMUM_AUDIO_GRANULARITY   100
#else
#define MINIMUM_AUDIO_GRANULARITY   50
#define MAXIMUM_AUDIO_GRANULARITY   100
#endif

#define MAX_WAIT_AT_SAME_TIME       (MAXIMUM_AUDIO_GRANULARITY+50)

CHXAudioPlayer::CHXAudioPlayer( CHXAudioSession* owner )
:     m_lRefCount(0)
,       m_ulCallbackID(0)
,     m_pContext(0)
,     m_bInited(FALSE)
,     m_ulGranularity(0)
,     m_Owner(owner)
,     m_pStreamList(0)
,     m_pRealAudioStreamList(0)
,     m_bAdjustForRealAudio(FALSE)
,     m_pPlayerResponse(0)
,     m_pScheduler (0)
,     m_pPreferences(0)
,     m_bHasStreams(FALSE)
,     m_bIsLive(FALSE)
,     m_ulCurrentTime(0)
,     m_ulLastCurrentTimeReturned(0)
,     m_ulLastDeviceTimeAdjusted(0)
,     m_bTimeReturned(FALSE)
,     m_pPMixHookList(0)
,     m_ulBytesPerGran(0)
,     m_pStreamRespList(0)
,     m_ulASstartTime(0)
,     m_ulAPplaybackTime(0)
,     m_ulAPstartTime(0)
,     m_ulADresumeTime(0)
,     m_eState(E_STOPPED)
,     m_bPrefUse11khz(FALSE)
,     m_uPrefBitsPerSample(16)
,     m_uPrefAudioQuality(4)
,     m_pFakeAudioCBTime(0)
,     m_ulLastFakeCallbackTime(0)
,     m_ulIncreasingTimer(0)
,     m_bDisableWrite(FALSE)
,     m_bIsResumed(FALSE)
,     m_ulTimeAdjustDoneAt(0)
,     m_bIsDonePlayback(TRUE)
,     m_bIsFirstResume(TRUE)
,     m_bCanBeRewound(FALSE)
,     m_uVolume(0)
,     m_bMute(FALSE)
,     m_bHasDataInAudioDevice(FALSE)
,     m_llLastWriteTime(0)
{
#ifdef HELIX_FEATURE_VOLUME
    m_pPlayerVolume = NULL;
#endif

    m_Owner->AddRef();
    m_pFakeAudioCBTime              = new Timeval;
    // NOTE: we should add some check on the success of this allocation.

    /* Default value of Player format */
    m_PlayerFmt.uChannels     = 2;
    m_PlayerFmt.uBitsPerSample      = 16;
    m_PlayerFmt.ulSamplesPerSec     = 16000;
    m_PlayerFmt.uMaxBlockSize = 64000;
}

/************************************************************************
 *  Method:
 *          IHXAudioPlayer::~CHXAudioPlayer()
 *    Purpose:
 *          Destructor. Clean up and set free.
 */
CHXAudioPlayer::~CHXAudioPlayer()
{
    Close();
}

void CHXAudioPlayer::Close(void)
{
    ResetPlayer();

    HX_DELETE(m_pStreamList);
    HX_DELETE(m_pRealAudioStreamList);

    HX_RELEASE(m_pContext);
#if defined(HELIX_FEATURE_PREFERENCES)
    HX_RELEASE(m_pPreferences);
#endif /* HELIX_FEATURE_PREFERENCES */
    HX_RELEASE(m_pPlayerResponse);

    if ( m_pPMixHookList )
    {
      HXAudioHookInfo* pMixHookInfo  = 0;
      while(!m_pPMixHookList->IsEmpty())
      {
          pMixHookInfo = (HXAudioHookInfo*) m_pPMixHookList->RemoveHead();
          pMixHookInfo->pHook->Release();
          delete pMixHookInfo;
      }

      delete m_pPMixHookList;
      m_pPMixHookList = 0;
    }

    // Delete all stream response items.
    if ( m_pStreamRespList )
    {
      IHXAudioStreamInfoResponse*   pAudioStreamInfoResponse = 0;
      while(!m_pStreamRespList->IsEmpty())
      {
          pAudioStreamInfoResponse =
           (IHXAudioStreamInfoResponse*) m_pStreamRespList->RemoveHead();

          pAudioStreamInfoResponse->Release();
      }

      delete m_pStreamRespList;
      m_pStreamRespList = 0;
    }

#ifdef HELIX_FEATURE_VOLUME
    if( m_pPlayerVolume )
    {
        m_pPlayerVolume->RemoveAdviseSink((IHXVolumeAdviseSink*)this);
        m_pPlayerVolume->Release();
        m_pPlayerVolume = NULL;
    }
#endif

    // Delete IRMA volume object.
    HX_DELETE(m_pFakeAudioCBTime);
    HX_RELEASE(m_Owner);
    HX_RELEASE(m_pScheduler);
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//      IUnknown::QueryInterface
//  Purpose:
//      Implement this to export the interfaces supported by your
//      object.
//
STDMETHODIMP CHXAudioPlayer::QueryInterface(REFIID riid, void** ppvObj)
{
    QInterfaceList qiList[] =
        {
            { GET_IIDHANDLE(IID_IHXAudioPlayer), (IHXAudioPlayer*)this },
#if defined(HELIX_FEATURE_CROSSFADE)
            { GET_IIDHANDLE(IID_IHXAudioCrossFade), (IHXAudioCrossFade*)this },
#endif /* HELIX_FEATURE_CROSSFADE */
#if defined(HELIX_FEATURE_VOLUME)
            { GET_IIDHANDLE(IID_IHXVolumeAdviseSink), (IHXVolumeAdviseSink*)this },
#endif /* HELIX_FEATURE_VOLUME */
            { GET_IIDHANDLE(IID_IUnknown), (IUnknown*)(IHXAudioPlayer*)this },
        };

    HX_RESULT res = ::QIFind(qiList, QILISTSIZE(qiList), riid, ppvObj);

    // if succeeded, return immediately...
    if (SUCCEEDED(res))
    {
        return res;
    }
    // ...otherwise, proceed.

    if (m_Owner &&
           m_Owner->QueryInterface(riid, ppvObj) == HXR_OK)
    {
      return HXR_OK;
    }

    *ppvObj = NULL;
    return HXR_NOINTERFACE;
}

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

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

    delete this;
    return 0;
}

/*
 *  IHXAudioPlayer methods
 */
/************************************************************************
 *  Method:
 *              IHXAudioPlay::AddPostMixHook
 *      Purpose:
 *       Call this to add a post mix hook of the audio data.
 */
STDMETHODIMP CHXAudioPlayer::AddPostMixHook
(
    IHXAudioHook* pHook,
    const BOOL          bDisableWrite,
    const BOOL          bFinal
)
{
    /* We only allow adding Hooks before the playback has started */
    if (m_bInited)
    {
      return HXR_FAILED;
    }

    return ActualAddPostMixHook(pHook, bDisableWrite, bFinal);
}

/************************************************************************
 *  Method:
 *              IHXAudioPlay::RemovePostMixHook
 *      Purpose:
 *       Call this to remove a post mix hook.
 */
STDMETHODIMP CHXAudioPlayer::RemovePostMixHook
(
    IHXAudioHook* pHook
)
{
    /* We only allow removing Hooks after the playback has stopped */
    if (m_bInited)
    {
      return HXR_FAILED;
    }

    return ActualRemovePostMixHook(pHook);
}

/************************************************************************
*  Method:
*      IHXAudioPlayer::GetAudioStreamCount
*  Purpose:
*           Get the number of audio streams currently active in the
*           audio player. Since streams can be added mid-presentation
*           this function may return different values on different calls.
*           If the user needs to know about all the streams as they get
*           get added to the player, IHXAudioStreamInfoResponse should
*           be implemented and passed in SetStreamInfoResponse.
*/
STDMETHODIMP_(UINT16) CHXAudioPlayer::GetAudioStreamCount()
{
    HX_ASSERT(m_pStreamList);
    if (m_pStreamList)
    {
      return (UINT16) m_pStreamList->GetCount();
    }
    else
    {
      return 0;
    }
}

/************************************************************************
 *  Method:
 *              IHXAudioPlayer::GetAudioStream
 *      Purpose:
 */
STDMETHODIMP_(IHXAudioStream*) CHXAudioPlayer::GetAudioStream
(
    UINT16  uIndex
)
{
    LISTPOSITION lp = 0;
    lp = m_pStreamList->FindIndex( (int) uIndex );
    if ( lp )
    {
      CHXAudioStream* s = 0;
      s = (CHXAudioStream*) m_pStreamList->GetAt(lp);
      s->AddRef();
      return s;
    }
    else
        return 0;
}

/************************************************************************
 *  Method:
 *              IHXAudioPlayer::GetAudioVolume
 *      Purpose:
 *          Return this player's IRMA volume interface.
 */
STDMETHODIMP_(IHXVolume*) CHXAudioPlayer::GetAudioVolume()
{
    IHXVolume* pRet = NULL;
#ifdef HELIX_FEATURE_VOLUME
    if( m_pPlayerVolume )
    {
        m_pPlayerVolume->AddRef();
        pRet = m_pPlayerVolume;
    }
#endif
    return pRet;
}

/************************************************************************
 *  Method:
 *              IHXAudioPlayer::GetDeviceVolume
 *      Purpose:
 *          Return this audio device volume interface.
 */
STDMETHODIMP_(IHXVolume*) CHXAudioPlayer::GetDeviceVolume()
{
    return ( m_Owner->GetDeviceVolume() );
}

/*
 *  IHXAudioCrossFade methods
 */

/************************************************************************
 *  Method:
 *      IHXAudioCrossFade::CrossFade
 *  Purpose:
 *    Cross-fade two audio streams.
 *    pStreamFrom       - Stream to be cross faded from
 *    pStreamTo         - Stream to be cross faded to
 *    ulFromCrossFadeStartTime- "From" Stream time when cross fade is 
 *                        to be started
 *    ulToCrossFadeStartTime  - "To" Stream time when cross fade is to 
 *                        be started
 *    ulCrossFadeDuration     - Duration over which cross-fade needs
 *                            to be done
 *    All the timing parameters are in milliseconds.
 *      
 *      This function can also be used to achieve fade-out or fade-in only audio
 *      effect by specifying either pStreamFrom or pStreamTo parameter.
 *      
 */
STDMETHODIMP
CHXAudioPlayer::CrossFade(IHXAudioStream*  pStreamFrom,
                    IHXAudioStream*  pStreamTo,
                    UINT32        ulFromCrossFadeStartTime,
                    UINT32        ulToCrossFadeStartTime,
                    UINT32        ulCrossFadeDuration)
{
#if defined(HELIX_FEATURE_CROSSFADE)
    HX_RESULT theErr = HXR_OK;
    CHXAudioStream* pFromStream = NULL;
    CHXAudioStream* pToStream = NULL;

    LISTPOSITION streamPos1 = m_pStreamList->Find(pStreamFrom);
    LISTPOSITION streamPos2 = m_pStreamList->Find(pStreamTo);
    if (!streamPos2 && !streamPos1)
    {
      return HXR_INVALID_PARAMETER;
    }

    if (streamPos1)
    {
        pFromStream = (CHXAudioStream*) m_pStreamList->GetAt(streamPos1);
        theErr = pFromStream->StartCrossFade(pToStream, ulFromCrossFadeStartTime,
                                   ulCrossFadeDuration, FALSE);
    }

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

    if (streamPos2)
    {
        pToStream = (CHXAudioStream*) m_pStreamList->GetAt(streamPos2);
      theErr = pToStream->StartCrossFade(pFromStream, ulToCrossFadeStartTime,
                                 ulCrossFadeDuration, TRUE);
    }

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

    if (streamPos1 && streamPos2)
    {
        /* Adjust the streams in list so that the "ToStream" List appears before
         * the "FromStream" 
         * A lame but quick-and-dirty way to do it: Remove these two stream from 
         * the listand them in order at the tail!
         */
      LISTPOSITION lPos = m_pStreamList->GetHeadPosition();
      while (lPos)
      {
          CHXAudioStream* pStream = 
            (CHXAudioStream* ) m_pStreamList->GetAt(lPos);
          if (pStream == pFromStream ||
            pStream == pToStream)
          {
            /* RemoveAt returns the next position in the list.
             * DO NOT use GetNext if you remove a node.
             */
            lPos = m_pStreamList->RemoveAt(lPos);
          }
          else
          {
            m_pStreamList->GetNext(lPos);
          }
      }

      m_pStreamList->AddTail((void*) pToStream);
      m_pStreamList->AddTail((void*) pFromStream);
    }

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

/************************************************************************
 *  Method:
 *          CHXAudioPlayer::Init
 *    Purpose:
 *          Initialize the Audio Player object called by rmaplayer.
 */
HX_RESULT CHXAudioPlayer::Init
(
    IUnknown*     pContext
)
{
    if (!pContext)
      return HXR_INVALID_PARAMETER;

    m_pContext = pContext;
    m_pContext->AddRef();

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

    if (HXR_OK != pContext->QueryInterface(IID_IHXAudioPlayerResponse,
                        (void **) &m_pPlayerResponse))
    {
      return HXR_INVALID_PARAMETER;
    }
#if defined( HELIX_FEATURE_PREFERENCES )
    m_pContext->QueryInterface(IID_IHXPreferences, (void**) &m_pPreferences);
#endif
    return HXR_OK;
}


/************************************************************************
 *  Method:
 *          CHXAudioPlayer::_Init
 *    Purpose:
 *          Create internal lists, etc. Called by Audio Session
 *          CreateAudioPlayer() method.
 */
HX_RESULT CHXAudioPlayer::InitializeStructures()
{
    HX_RESULT theErr = HXR_OK;

    // Create the Stream list.
    // Create the Post process hook list.
    // Create the Stream response list.
    m_pStreamList = new CHXSimpleList;
#if defined(HELIX_FEATURE_AUDIO_POSTMIXHOOK)
    m_pPMixHookList     = new CHXSimpleList;
#endif /* HELIX_FEATURE_AUDIO_POSTMIXHOOK */
    m_pStreamRespList   = new CHXSimpleList;

    if ( !m_pStreamList || !m_pStreamList->IsPtrListValid())
      theErr = HXR_OUTOFMEMORY;

#if defined(HELIX_FEATURE_AUDIO_POSTMIXHOOK)
    if ( !m_pPMixHookList || !m_pPMixHookList->IsPtrListValid())
      theErr = HXR_OUTOFMEMORY;
#endif /* HELIX_FEATURE_AUDIO_POSTMIXHOOK */

    if ( !m_pStreamRespList || !m_pStreamRespList->IsPtrListValid())
      theErr = HXR_OUTOFMEMORY;

#if defined(HELIX_FEATURE_VOLUME)
    if( !theErr )
    {
        m_pPlayerVolume = (IHXVolume*)new CHXVolume;
        if( m_pPlayerVolume )
        {
            m_pPlayerVolume->AddRef();
            m_pPlayerVolume->AddAdviseSink(this);
            //Start off with the volume at max.
            m_pPlayerVolume->SetVolume(HX_MAX_VOLUME);
        }
        else
        {
            theErr = HXR_OUTOFMEMORY;
        }
    }
#endif /* HELIX_FEATURE_VOLUME */

    return ( theErr );
}

/************************************************************************
 *  Method:
 *          CHXAudioPlayer::GetFormat
 *    Purpose:
 *          Return the player's device format which was determined in
 *          InitPlayer() and is based on all the streams.
 */
void CHXAudioPlayer::GetFormat
(
      HXAudioFormat*   pAudioFormat
)
{
    memcpy(pAudioFormat, &m_PlayerFmt, sizeof( HXAudioFormat) );
}


/* ***********************************************************************
 *  Method:
 *      CHXAudioPlayer::GetAudioPrefs
 *  Purpose:
 *    Get audio related preferences.
 */
HX_RESULT CHXAudioPlayer::GetAudioPrefs()
{
    IHXBuffer* pBuffer = NULL;
    IHXPreferences* pPreferences = 0;

#if defined(HELIX_FEATURE_PREFERENCES)
    /* Reason we query for Preferences here and not at Init() is because
     * Preferences may have been overwritten in HXPlayer by SetupClient()
     * call by upper level client and this happens AFTER CHXAudioPlayer::Init()
     * is called
     */
    if (!m_pContext)
    {
      return HXR_INVALID_PARAMETER;
    }

    m_pContext->QueryInterface(IID_IHXPreferences, (void**) &pPreferences);

    //  What is the pref for this?
    if( pPreferences )
    {

        if (pPreferences->ReadPref("SamplingRate", pBuffer) == HXR_OK)
        {
            m_bPrefUse11khz = (11025 == ::atol((const char*) pBuffer->GetBuffer()));
            pBuffer->Release();
            pBuffer = 0;
        }

      ReadPrefINT16(pPreferences, "BitsPerSample", m_uPrefBitsPerSample);
      ReadPrefINT16(pPreferences, "Quality", m_uPrefAudioQuality);

        /* hmmm... Looks like the client override default Preferences implementation*/
        if (m_pPreferences != pPreferences)
        {
            if (m_pPreferences)
            {
                m_pPreferences->Release();
            }
            m_pPreferences = pPreferences;
            m_pPreferences->AddRef();
        }

        pPreferences->Release();
    }
#endif /* HELIX_FEATURE_PREFERENCES */

    return HXR_OK;
}


/************************************************************************
 *  Method:
 *          IHXAudioPlay::CreateAudioStream
 *    Purpose:
 *       The renderer calls this to create a unique audio stream with its
 *     unique audio format.
 */
STDMETHODIMP CHXAudioPlayer::CreateAudioStream
(
    IHXAudioStream**            pAudioStream
)
{
   HX_RESULT theErr = HXR_OK;

    // Create a new IRMA audio stream
    *pAudioStream = 0;
    *pAudioStream = (IHXAudioStream*) new CHXAudioStream(this, m_pContext);
    if ( !*pAudioStream )
    {
      theErr = HXR_OUTOFMEMORY;
    }

    // Add audio stream to my list
    if (!theErr)
    {
      theErr = _CreateAudioStream(pAudioStream);
    }

    return theErr;
}

HX_RESULT
CHXAudioPlayer::_CreateAudioStream(IHXAudioStream** pAudioStream)
{
    (*pAudioStream)->AddRef();   // once for user
    (*pAudioStream)->AddRef();   // once for me

    // Add to the stream list.
    m_pStreamList->AddTail((void*) *pAudioStream);

    ((CHXAudioStream*)(*pAudioStream))->SetLive(m_bIsLive);

    m_Owner->CheckIfLastNMilliSecsToBeStored();

    /* Already initialized with no streams?*/
    if (m_bInited && !m_bHasStreams)
    {
      /* If we are already initialized and there were no audio streams before
       * initialization, we must be using our fake timer to send time syncs
       * This needs to change now to get time syncs from the audio device
       */
      ((CHXAudioStream*)(*pAudioStream))->SetupToBeDone();

      return HXR_OK;
    }

    m_bHasStreams = TRUE;

    /* If we are already initialized, it means CreateAudioStream was
     * called in the midst of the presentation. In this case, we already know
     * the granularity and the Device format and thus call Setup right away
     */
    if ((*pAudioStream) && m_bInited)
    {
      ((CHXAudioStream*)(*pAudioStream))->Setup( &m_DeviceFmt, m_ulGranularity );
    }
    return HXR_OK;
}

HX_RESULT
CHXAudioPlayer::SetSoundLevel(CHXSimpleList* pAudioStreamList, UINT16 uSoundLevel, BOOL bReflushAudioDevice)
{
    HX_RESULT     rc = HXR_OK;
    IHXVolume* pStreamVolume = NULL;

    if (pAudioStreamList && !pAudioStreamList->IsEmpty())
    {
      CHXSimpleList::Iterator lIter = pAudioStreamList->Begin();
      for (; lIter != pAudioStreamList->End(); ++lIter)
      {
          CHXAudioStream* pAudioStream = (CHXAudioStream*) (*lIter);

          pStreamVolume = pAudioStream->GetAudioVolume();
          if (pStreamVolume)
          {
            pStreamVolume->SetVolume(uSoundLevel);
          }
          HX_RELEASE(pStreamVolume);
      }

      if (bReflushAudioDevice)
      {
          AudioStreamStateChanged(E_PLAYING);
      }
    }

    return rc;
}



HX_RESULT CHXAudioPlayer::ManageAudioStreams( CHXSimpleList* pStreamList,
                                              STREAM_ACTION  what,
                                              UINT32 ulTime )
{
    HX_RESULT    rc  = HXR_OK;
    LISTPOSITION pos = 0;

    if(pStreamList && !pStreamList->IsEmpty())
    {
      CHXSimpleList::Iterator it = pStreamList->Begin();
      while( it != pStreamList->End() )
      {
          CHXAudioStream* pAudioStream = (CHXAudioStream*) (*it);
            switch(what)
            {
               case STR_STOP:
                   pAudioStream->Stop();
                   break;
               case STR_SEEK:
                   pAudioStream->Seek(ulTime);
                   break;
               case STR_RESUME:
                   pAudioStream->Resume(FALSE);
                   break;
               case STR_PAUSE:
                   pAudioStream->Pause(FALSE);
                   break;
               case STR_SETHINT:
                   pAudioStream->SetAudioDeviceReflushHint(TRUE);
                   break;
               case STR_REMOVE:
                   pos = m_pStreamList->Find(pAudioStream);
                   if (pos)
                       m_pStreamList->RemoveAt(pos);
                   pAudioStream->Stop();
                   HX_RELEASE(pAudioStream);
                   break;
               default:
                   HX_ASSERT("bad stream action taken"==NULL);
            }
            ++it;
      }

        //Post stream iteration actions.
        switch(what)
        {
           case STR_STOP:
               AudioStreamStateChanged(E_STOPPED);
               break;
           case STR_PAUSE:
               AudioStreamStateChanged(E_PAUSED);
               break;
           case STR_SETHINT:
               m_Owner->CheckIfLastNMilliSecsToBeStored();
               break;
           case STR_REMOVE:
               if( 0 == m_pStreamList->GetCount())
               {
                   m_bHasStreams = FALSE;
                   m_Owner->Stop(this, TRUE);
                   m_bInited = FALSE;
                   if(HXR_OK != (rc=Setup(m_ulGranularity)))
                   {
                       IHXErrorMessages* pErrorMessage = NULL;
                       m_pContext->QueryInterface(IID_IHXErrorMessages, (void**) &pErrorMessage);
                       if (pErrorMessage)
                       {
                           pErrorMessage->Report(HXLOG_ERR, rc, 0, NULL, NULL);
                           pErrorMessage->Release();
                       }
                       rc = HXR_OK;
                   }
                   else
                   {
                       rc = ResumeFakeTimeline();
                   }
               }
               else
               {
                   AudioStreamStateChanged(E_STOPPED);
               }

               break;
           default:
               HX_ASSERT("bad stream action taken"==NULL );
        }

    }

    return rc;

}



HX_RESULT
CHXAudioPlayer::AudioStreamStateChanged(EPlayerState eState)
{
    if(!m_Owner->GetDisableMultiPlayPauseSupport())
    {
      // we only concern about the state change of audio stream
      // while its parent audio player is in playing mode
      if (m_eState == E_PLAYING)
      {
          switch (eState)
          {
          case E_PLAYING:
            m_Owner->RewindSession();
            m_Owner->ActualResume();
            break;
          case E_PAUSED:
          case E_STOPPED:
            m_Owner->RewindSession();
            if (NumberOfResumedStreams() > 0 ||
                m_Owner->NumberOfResumedPlayers() > 0)
            {
                m_Owner->ActualResume();
            }
            break;
          default:
            break;
          }
      }
    }

    return HXR_OK;
}

CHXAudioStream*
CHXAudioPlayer::GetCHXAudioStream(UINT16 uIndex)
{
    LISTPOSITION lp = 0;
    lp = m_pStreamList->FindIndex( (int) uIndex );
    if ( lp )
    {
      return (CHXAudioStream*)m_pStreamList->GetAt(lp);
    }
    else
    {
        return NULL;
    }
}

BOOL
CHXAudioPlayer::IsLastNMilliSecsToBeStored()
{
    BOOL bResult = FALSE;

    if (m_bHasStreams)
    {
      CHXAudioStream* s = 0;
      CHXSimpleList::Iterator lIter = m_pStreamList->Begin();
      for (; lIter != m_pStreamList->End(); ++lIter)
      {
          s = (CHXAudioStream*) (*lIter);
          if (s->IsAudioDeviceReflushHint())
          {
            bResult = TRUE;
            break;
          }
      }
    }

    return bResult;
}

HX_RESULT
CHXAudioPlayer::ActualAddPostMixHook(IHXAudioHook* pHook,
                             const BOOL       bDisableWrite,
                             const BOOL       bFinal)
{
    if (!m_pPMixHookList || !pHook)
    {
      return HXR_FAILED;
    }

#if defined(HELIX_FEATURE_AUDIO_POSTMIXHOOK)
    /* Check if this one already exists */
    HXAudioHookInfo* h = 0;
    LISTPOSITION lp = 0;
    lp = m_pPMixHookList->GetHeadPosition();
    while( lp )
    {
      h = (HXAudioHookInfo*) m_pPMixHookList->GetNext(lp);
      if (pHook == h->pHook)
      {
          return HXR_FAILED;
      }
    }

    h = (HXAudioHookInfo*) new HXAudioHookInfo;
    h->pHook   = pHook;
    h->bDisableWrite = bDisableWrite;
    h->bFinal  = bFinal;
    h->bIgnoreAudioData = FALSE;
    h->bMultiChannelSupport = FALSE;

    IHXValues* pValues = NULL;
    if (pHook && pHook->QueryInterface(IID_IHXValues, (void**) &pValues) == HXR_OK)
    {
      UINT32 ulValue = 0;
      pValues->GetPropertyULONG32("IgnoreAudioData", ulValue);
      h->bIgnoreAudioData = (ulValue == 1);
      HX_RELEASE(pValues);
    }

    IHXAudioMultiChannel* pMultiChannel = NULL;
    if (pHook && HXR_OK == pHook->QueryInterface(IID_IHXAudioMultiChannel, (void**) &pMultiChannel))
    {
        h->bMultiChannelSupport = pMultiChannel->GetMultiChannelSupport();
    }
    HX_RELEASE(pMultiChannel);

    if (bDisableWrite)
    {
      m_bDisableWrite = bDisableWrite;
    }

    pHook->AddRef();

    // Order list by putting all bFinal == TRUE at end of list.
    if ( m_pPMixHookList->IsEmpty() || !bFinal )
    {
      m_pPMixHookList->AddHead((void*) h);
    }
    else
    {
      m_pPMixHookList->AddTail((void*) h);
    }
    m_Owner->PostMixHooksUpdated();

    ProcessAudioHook(ACTION_ADD, pHook);

    /* If we are already initialized, send the device format to the
     * post hook
     */
    if (m_bInited)
    {
      if (h->bIgnoreAudioData ||
          HXR_OK == ProcessAudioHook(ACTION_CHECK, pHook))
      {
          HXAudioFormat audioFmt;
          m_Owner->GetFormat( &audioFmt );
          pHook->OnInit( &audioFmt );
      }
    }
#endif /* HELIX_FEATURE_AUDIO_POSTMIXHOOK */

    return HXR_OK;
}

HX_RESULT
CHXAudioPlayer::ActualRemovePostMixHook(IHXAudioHook* pHook)
{
    if (!m_pPMixHookList || !pHook)
    {
      return HXR_FAILED;
    }

#if defined(HELIX_FEATURE_AUDIO_POSTMIXHOOK)
    BOOL bCheckForDisableWrite      = FALSE;
    BOOL bFound               = FALSE;

    HXAudioHookInfo* h = 0;
    LISTPOSITION lp, lastlp;
    lp = lastlp = 0;
    lp = m_pPMixHookList->GetHeadPosition();
    while( lp )
    {
      lastlp = lp;
      h = (HXAudioHookInfo*) m_pPMixHookList->GetNext(lp);
      if ( pHook == h->pHook )
      {
          if (h->bDisableWrite)
          {
            m_bDisableWrite         = FALSE;
            bCheckForDisableWrite   = TRUE;
          }

          ProcessAudioHook(ACTION_REMOVE, pHook);

          h->pHook->Release();
          delete h;
          h = 0;
          m_pPMixHookList->RemoveAt(lastlp);
          bFound = TRUE;
          break;
      }
    }

    if (!bFound)
    {
      return HXR_FAILED;
    }
    m_Owner->PostMixHooksUpdated();

    if ( m_pPMixHookList && bCheckForDisableWrite && m_pPMixHookList->GetCount() > 0)
    {
      HXAudioHookInfo* h = 0;
      LISTPOSITION lp, lastlp;
      lp = lastlp = 0;
      lp = m_pPMixHookList->GetHeadPosition();
      while( lp )
      {
          h = (HXAudioHookInfo*) m_pPMixHookList->GetNext(lp);
          if (h->bDisableWrite)
          {
            m_bDisableWrite = TRUE;
            break;
          }
      }

    }
#endif /* HELIX_FEATURE_AUDIO_POSTMIXHOOK */

    return HXR_OK;
}

/************************************************************************
 *  Method:
 *      CHXAudioPlayer::SetGranularity
 *  Purpose:
 *       The HELIX player object calls this BEFORE starting audio playback
 *       and AFTER all audio streams are created for the renderers.
 */
void CHXAudioPlayer::SetGranularity
(
    const ULONG32  ulGranularity
)
{
    m_ulGranularity = ulGranularity;
    return;
}


/************************************************************************
 *  Method:
 *          CHXAudioPlayer::Resume
 *    Purpose:
 *          Resume audio playback by writing data to the audio device.
 *          Open the audio device if it is not opened already.
 */
HX_RESULT CHXAudioPlayer::Resume()
{
    HX_RESULT theErr = HXR_OK;

    if (!m_bInited)
      return HXR_NOT_INITIALIZED;

    if ( m_eState == E_PLAYING )
    {
      return HXR_OK;
    }

    m_bIsDonePlayback = FALSE;
    m_eState          = E_PLAYING;
    m_bCanBeRewound   = TRUE;


    /* Use Audio Session Object ONLY if there are any audio streams
     * in the presentation
     */
    if (m_bHasStreams)
    {
      CHXAudioStream* s = 0;
      CHXSimpleList::Iterator lIter = m_pStreamList->Begin();
      for (; lIter != m_pStreamList->End(); ++lIter)
      {
          s = (CHXAudioStream*) (*lIter);
          if ( s )
            s->Resume(TRUE);
      }

      // This is the audio device playback time that corresponds to
      // when this audio player resumed.
      m_ulADresumeTime = m_Owner->GetCurrentPlayBackTime();

      // This is this player's start time within its timeline. This is
      // modified when the player is seeked or resumed.
      m_ulAPstartTime = m_ulAPplaybackTime;

      UpdateStreamLastWriteTime();

      // Resume the audio device playback
      if ( !theErr )
          theErr = m_Owner->Resume(this);
    }
    else
    {
      theErr = ResumeFakeTimeline();

      /* Send time 0 at first Resume */
      if (!theErr && m_bIsFirstResume)
      {
          m_bIsFirstResume = FALSE;
          OnTimeSync(m_ulIncreasingTimer);
      }
    }

    m_bIsResumed = TRUE;
    return ( !theErr ) ? HXR_OK : HXR_FAILED;
}

/************************************************************************
 *  Method:
 *          CHXAudioPlayer::Pause
 *    Purpose:
 *          The player object calls this function to pause audio playback.
 */
HX_RESULT CHXAudioPlayer::Pause()
{
    if (m_eState == E_PAUSED)
    {
      return HXR_OK;
    }

    m_eState = E_PAUSED;

    if (m_bHasStreams)
    {
      CHXAudioStream* s = 0;
      CHXSimpleList::Iterator lIter = m_pStreamList->Begin();
      for (; lIter != m_pStreamList->End(); ++lIter)
      {
          s = (CHXAudioStream*) (*lIter);
          if ( s )
            s->Pause(TRUE);
      }

      m_Owner->Pause(this);
    }
    else
    {
      StopFakeTimeline();
    }

    m_bCanBeRewound = FALSE;

    return HXR_OK;
}

/************************************************************************
 *  Method:
 *          CHXAudioPlay::Stop
 *    Purpose:
 *      The player object calls this function to stop audio playback.
 *          If bFlush is TRUE, flush any data in the audio device.
 */
HX_RESULT CHXAudioPlayer::Stop
(
    const BOOL bFlush
)
{
    m_eState = E_STOPPED;

    m_ulAPstartTime     = 0;

    if (m_bHasStreams)
    {
      CHXAudioStream* s = 0;
      CHXSimpleList::Iterator lIter = m_pStreamList->Begin();
      for (; lIter != m_pStreamList->End(); ++lIter)
      {
          s = (CHXAudioStream*) (*lIter);
          if ( s )
            s->Stop();
      }

      m_Owner->Stop(this, bFlush);
    }
    else
    {
      StopFakeTimeline();
    }

    ResetPlayer();

    return HXR_OK;
}

/************************************************************************
 *  Method:
 *          CHXAudioPlayer::Seek
 *    Purpose:
 *          The player object calls this function to seek audio playback to
 *          the time (in milliseconds) given.
 */
HX_RESULT CHXAudioPlayer::Seek
(
const       UINT32                  ulSeekTime
)
{
    /* always remember this seek time.. even though there may not be any streams
     * yet for this player. This is because the streams may be created later and
     * we need to correctly apply the seek time to get the accurate time.
     */
    m_ulAPstartTime = m_ulAPplaybackTime = ulSeekTime;      // current start time for this player
    m_llLastWriteTime = (INT64) ulSeekTime;

    if (m_bHasStreams)
    {
      // Make each stream seek, too, since they own the resampling buffers.
      CHXAudioStream* s = 0;
      CHXSimpleList::Iterator lIter = m_pStreamList->Begin();
      for (; lIter != m_pStreamList->End(); ++lIter)
      {
          s = (CHXAudioStream*) (*lIter);
          if ( s )
            s->Seek(ulSeekTime);
      }

      m_Owner->Seek( this, ulSeekTime );

      m_ulADresumeTime = m_Owner->GetCurrentPlayBackTime();
    }
    else
    {
      StopFakeTimeline();
      m_bIsFirstResume    = TRUE;
    }

    m_ulCurrentTime         = ulSeekTime;

    m_ulLastCurrentTimeReturned     = m_ulCurrentTime;
    m_bTimeReturned         = FALSE;
    m_bHasDataInAudioDevice = FALSE;

    return HXR_OK;
}

void CHXAudioPlayer::ResetPlayer(void)
{
    m_bInited           = FALSE;
    m_bHasStreams = FALSE;
    m_bIsFirstResume    = TRUE;

    m_ulAPstartTime     = 0;
    m_ulAPplaybackTime  = 0;
    m_ulADresumeTime    = 0;

    m_ulCurrentTime     = 0;
    m_ulLastCurrentTimeReturned     = 0;
    m_ulLastDeviceTimeAdjusted = 0;
    m_bTimeReturned     = FALSE;
    m_bIsLive           = FALSE;
    m_bIsResumed  = FALSE;
    m_bIsDonePlayback   = TRUE;
    m_llLastWriteTime   = 0;
    m_bCanBeRewound     = FALSE;
    m_bHasDataInAudioDevice = FALSE;

    // Delete all streams.  Remove all list items.
    if ( m_pStreamList )
    {
      CHXAudioStream* pAudioStream = 0;
      while(!m_pStreamList->IsEmpty())
      {
          pAudioStream = (CHXAudioStream*) m_pStreamList->RemoveHead();
          pAudioStream->ResetStream();
          pAudioStream->Release();
      }
    }

    /* We do not remove post mix hooks any more */
    /* We do not remove Stream Response Objects any more */

    /* Default value of Player format */
    m_PlayerFmt.uChannels     = 2;
    m_PlayerFmt.uBitsPerSample      = 16;
    m_PlayerFmt.ulSamplesPerSec     = 16000;
    m_PlayerFmt.uMaxBlockSize = 64000;

    m_ulLastFakeCallbackTime = 0;

    StopFakeTimeline();
}

/************************************************************************
 *  Method:
 *          CHXAudioPlayer::SetupStreams
 *    Purpose:
 *          Tell each stream about the audio device format so
 *          they can setup their resamplers and buffer.
 */
void CHXAudioPlayer::SetupStreams(void)
{
    // Get audio device format
    m_Owner->GetFormat(&m_DeviceFmt);

    // Calculate bytes per gran
    m_ulBytesPerGran = (ULONG32)
                (((m_DeviceFmt.uChannels * ((m_DeviceFmt.uBitsPerSample==8)?1:2) *  m_DeviceFmt.ulSamplesPerSec)
                                / 1000.0) * m_ulGranularity);

    // Make sure that number of bytes per granularity is an even number.
    if ( (m_ulBytesPerGran % 2) != 0 )
        m_ulBytesPerGran++;
    /* Don't we have to calculate granularity again if we adjust
     * for even byte boundary - XXX Rahul 06/15/97
     */

    // Notify each stream
    CHXAudioStream* s = 0;
    CHXSimpleList::Iterator lIter = m_pStreamList->Begin();
    for (; lIter != m_pStreamList->End(); ++lIter)
    {
      s = (CHXAudioStream*) (*lIter);
      if ( s )
      {
          s->Setup( &m_DeviceFmt, m_ulGranularity );
      }
    }
}

/************************************************************************
 *  Method:
 *          CHXAudioPlayer::OnTimeSync
 *    Purpose:
 */
HX_RESULT   CHXAudioPlayer::OnTimeSync(ULONG32 ulCurrentTime)
{
    HX_RESULT theErr = HXR_OK;

#ifdef _MACINTOSH
    if (InterlockedIncrement(&gTIMELINE_MUTEX) > 1)
    {
       InterlockedDecrement(&gTIMELINE_MUTEX);
       return;
    }
    InterlockedDecrement(&gTIMELINE_MUTEX);
#endif

    if (m_bHasStreams)
    {
      ULONG32 ulADplaybackTime;

      ulADplaybackTime = m_Owner->GetCurrentPlayBackTime();
      m_ulAPplaybackTime = (ulADplaybackTime - m_ulADresumeTime) +
                        m_ulAPstartTime;
    }
    else
    {
      m_ulAPplaybackTime  = ulCurrentTime;
    }

    m_ulCurrentTime = m_ulAPplaybackTime ;

    AdjustForRealAudio();

    // Here we need to fudge the actual time for this player
    // For now we support only one player/timeline
    if (m_pPlayerResponse)
    {
      // The current playback time of any player is the difference
      // of the current audio device playback time minus the audio
      // device time when this player started (resumed) playback
      // plus the initial start time of playback within this player's
      // timeline (usually 0 but can be something else esp. after a
      // seek).
      theErr = m_pPlayerResponse->OnTimeSync(m_ulCurrentTime);
    }
    return theErr;
}

/************************************************************************
 *  Method:
 *          CHXAudioPlayer::Setup
 *    Purpose:
 *          This is called after AS receives format and stream info
 *          from the renderers AND before packets are received from
 *          the renderer.
 */
HX_RESULT CHXAudioPlayer::Setup( ULONG32 ulGranularity)
{
    HX_RESULT theErr = HXR_OK;


    if (m_bInited)
      return HXR_OK;

    /* Always write 100ms audio blocks for now. */
    m_ulGranularity = MAXIMUM_AUDIO_GRANULARITY;      //ulGranularity;

    /* We do not go below MINIMUM_AUDIO_GRANULARITY. This will not affect
     * sending timesyncs at this lower granularity since HXPlayer object
     * uses the scheduler to send individual timesyncs anyway
     */
    if (m_ulGranularity < MINIMUM_AUDIO_GRANULARITY)
    {
      m_ulGranularity = MINIMUM_AUDIO_GRANULARITY;
    }
    else if (m_ulGranularity > MAXIMUM_AUDIO_GRANULARITY)
    {
      m_ulGranularity = MAXIMUM_AUDIO_GRANULARITY;
    }

    if (!m_bHasStreams)
    {
      m_bInited = TRUE;
      return HXR_OK;
    }

    /* If this is the second player, session object may overide
     * the granularity value.
     */
    m_ulGranularity = m_Owner->SetGranularity(m_ulGranularity);

    // Determine this player's audio format parameters based on
    // the mixer channels attributes supplied in RegisterRenderer.
    //
    // 1. Spin thru the list of registered streams and
    //    determine the desired audio device parameters.
    // 2. Check the audio format with the audio device.
    //
    CHXAudioStream* pAudioStream = 0;
    ULONG32 maxSamplesPerSec = 8000;
    ULONG32 minSamplesPerSec = 44100;
    BOOL    bFirst = TRUE;
    UINT16  maxChannels = 1;
    UINT16  maxBlocksize = 0;
    UINT16  maxBitsPerSample = 0;
    HXAudioFormat audioFmt;

    theErr = GetAudioPrefs();

    if (!theErr && m_pStreamList->GetCount() > 0)
    {
      CHXSimpleList::Iterator lIter = m_pStreamList->Begin();
      for (;lIter != m_pStreamList->End(); ++lIter)
      {
          pAudioStream = (CHXAudioStream*) (*lIter); //m_pStreamList->GetNext(lp);

          if (!pAudioStream->IsAudioFormatKnown())
          {
            continue;
          }

          pAudioStream->GetFormat( &audioFmt );

          if (bFirst)
          {
            bFirst = FALSE;
            maxSamplesPerSec    = audioFmt.ulSamplesPerSec;
            minSamplesPerSec    = audioFmt.ulSamplesPerSec;
            maxChannels     = audioFmt.uChannels;
            maxBlocksize          = audioFmt.uMaxBlockSize;
            maxBitsPerSample    = audioFmt.uBitsPerSample;
          }
          else
          {
            //
            // NOTE: upsampling typically costs more CPU than downsampling
            if ( audioFmt.ulSamplesPerSec > maxSamplesPerSec )
                maxSamplesPerSec = audioFmt.ulSamplesPerSec;
            if ( audioFmt.ulSamplesPerSec < minSamplesPerSec)
                minSamplesPerSec = audioFmt.ulSamplesPerSec;
            //
            // NOTE: converting mono to stereo and vice versa cost about the
            // same in CPU usage.
            if ( audioFmt.uChannels > maxChannels)
                maxChannels = audioFmt.uChannels;

            // Get max block size.
            if ( audioFmt.uMaxBlockSize > maxBlocksize )
                maxBlocksize = audioFmt.uMaxBlockSize;

            // Get max sample width.
            if ( audioFmt.uBitsPerSample > maxBitsPerSample )
                maxBitsPerSample = audioFmt.uBitsPerSample;
          }
      }

      // Set the audio format for this Player.
      m_PlayerFmt.uMaxBlockSize     = maxBlocksize;
      m_PlayerFmt.uChannels         = maxChannels;
      m_PlayerFmt.uBitsPerSample    = maxBitsPerSample;

      // If user wants upsampling
      if ( m_uPrefAudioQuality > 2 )
          m_PlayerFmt.ulSamplesPerSec = maxSamplesPerSec;
      else
          m_PlayerFmt.ulSamplesPerSec = minSamplesPerSec;
    }

    if (m_bPrefUse11khz)
    {
      m_PlayerFmt.ulSamplesPerSec = 11025;
    }

    // Do audio session setup. (e.g., determine device audio
    // format, etc.
    if ( !theErr )
      theErr = m_Owner->Setup( m_bHasStreams );

    // Now let all streams know the final audio format so they
    // can resample to this format.
    if ( !theErr )
    {
      SetupStreams();
    }
    // if audio device is failed to initialized, we
    // will keep the video playing if this is not audio only source
    else if (!IsAudioOnlyTrue())
    {
      m_bHasStreams = FALSE;
      m_bInited = TRUE;
      return HXR_OK;
    }

    // Let all stream response know total number of streams.
    if (!theErr && m_pStreamRespList)
    {
      IHXAudioStreamInfoResponse* pAudioStreamInfoResponse = 0;
      CHXSimpleList::Iterator lIter = m_pStreamRespList->Begin();
      for (; lIter != m_pStreamRespList->End(); ++lIter)
      {
          pAudioStreamInfoResponse = (IHXAudioStreamInfoResponse*) (*lIter);

          CHXSimpleList::Iterator lIter2 = m_pStreamList->Begin();

          for (; lIter2 != m_pStreamList->End(); ++lIter2)
          {
            CHXAudioStream* pStream = (CHXAudioStream*) (*lIter2);
            /* Only if a stream is initialized, send it to
             * Response object. If not, we will send it when it
             * gets initialized (in StreamInitialized() call)
             */
            if (pStream->IsInitialized())
            {
                pAudioStreamInfoResponse->OnStream(pStream);
            }
          }
      }
    }

    // All renderers should have checked in by now!
    // Call post mix process hooks in list and provide the audio format.
    if (!theErr && m_pPMixHookList)
    {
      HXAudioFormat audioFmt;
      m_Owner->GetFormat( &audioFmt );
      HXAudioHookInfo* pPMixHookInfo = 0;
      CHXSimpleList::Iterator lIter = m_pPMixHookList->Begin();
      for (; lIter != m_pPMixHookList->End(); ++lIter)
      {
          pPMixHookInfo = (HXAudioHookInfo*) (*lIter);
          if (pPMixHookInfo->bIgnoreAudioData ||
            HXR_OK == ProcessAudioHook(ACTION_CHECK, pPMixHookInfo->pHook))
          {
            pPMixHookInfo->pHook->OnInit( &audioFmt );
          }
      }
    }

    if (!theErr)
    {
      m_bInited = TRUE;
      /* Only change the state to initialized if we were in a stopped
       * state earlier. It is possible to be in Playing state and be still
       * in this function. This will happen if we have started the
       * timeline as a fake timeline and later an audio stream joins the
       * presentation thereby converting fake to audio timeline
       * (delayed audio source in SMIL playback)
       */
      if (m_eState == E_STOPPED)
      {
          m_eState = E_INITIALIZED;
      }
    }

    return theErr;
}

ULONG32     CHXAudioPlayer::GetCurrentPlayBackTime(void)
{


    if (m_eState != E_PLAYING)
    {
      return m_ulCurrentTime;
    }

    // The current playback time of any player is the difference
    // of the current audio device playback time minus the audio
    // device time when this player started (resumed) playback
    // plus the initial start time of playback within this player's
    // timeline (usually 0 but can be something else esp. after a
    // seek).
    if (!m_bHasStreams)
    {
      ULONG32 ulCurrentTime   = HX_GET_TICKCOUNT();

      m_ulIncreasingTimer         += CALCULATE_ELAPSED_TICKS(m_ulLastFakeCallbackTime, ulCurrentTime);
      m_ulLastFakeCallbackTime    = ulCurrentTime;

      m_ulCurrentTime             = m_ulIncreasingTimer;
    }
    else
    {
      m_ulCurrentTime   = (m_Owner->GetCurrentPlayBackTime() -
                m_ulADresumeTime) + m_ulAPstartTime;
    }

    m_ulAPplaybackTime = m_ulCurrentTime;


    AdjustForRealAudio();

    return m_ulCurrentTime;
}

/************************************************************************
 *  Method:
 *          CHXAudioPlayer::GetStreamCount
 *    Purpose:
 *          Get the number of streams associated with this player.
 */
UINT16 CHXAudioPlayer::GetStreamCount()
{
    return m_pStreamList->GetCount();
}

/************************************************************************
 *  Method:
 *          CHXAudioPlayer::SetStreamInfoResponse
 *    Purpose:
 *          Add the stream info response interface to our list.
 */
STDMETHODIMP CHXAudioPlayer::SetStreamInfoResponse
(
    IHXAudioStreamInfoResponse*    pResponse
)
{
    if (!pResponse || !m_pStreamRespList)
    {
      return HXR_FAILED;
    }

    /* Add to the stream response list */
    LISTPOSITION lPos = m_pStreamRespList->Find(pResponse);
    if (lPos)
    {
      return HXR_FAILED;
    }

    m_pStreamRespList->AddTail((void*) pResponse);
    pResponse->AddRef();         // Released in destructor
    return HXR_OK;
}

/************************************************************************
 *  Method:
 *  CHXAudioPlayer::RemoveStreamInfoResponse
 *  Purpose:
 *  Remove stream info response that was added earlier
 */
STDMETHODIMP CHXAudioPlayer::RemoveStreamInfoResponse
(
    IHXAudioStreamInfoResponse*    pResponse
)
{
    /* Add to the stream response list */
    if (pResponse && m_pStreamRespList)
    {
      LISTPOSITION lPos = m_pStreamRespList->Find(pResponse);
      if (lPos)
      {
          m_pStreamRespList->RemoveAt(lPos);
          pResponse->Release();         // Released in destructor
          return HXR_OK;
      }
    }

    return HXR_FAILED;
}

UINT16
CHXAudioPlayer::NumberOfResumedStreams(void)
{
    UINT16 uNumActive = 0;
    if (m_pStreamList && m_pStreamList->GetCount() > 0)
    {
      CHXAudioStream* pStream = 0;
      CHXSimpleList::Iterator lIter = m_pStreamList->Begin();
      for (; lIter != m_pStreamList->End(); ++lIter)
      {
          pStream = (CHXAudioStream*) (*lIter);
          if (pStream->GetState() == E_PLAYING)
          {
            uNumActive++;
          }
      }
    }

    return uNumActive;
}

void CHXAudioPlayer::StreamInitialized(CHXAudioStream* pAudioStream)
{
    /* If we are already initialized, it means this stream was added mid-
     * presentation and gogt initialized later. In this case, report arrival
     * of this stream to all StreamInfoResponse objects registered with the
     * player
     */

    if (m_pStreamRespList && m_bInited)
    {
      IHXAudioStreamInfoResponse* pAudioStreamInfoResponse = 0;
      CHXSimpleList::Iterator lIter = m_pStreamRespList->Begin();
      for (; lIter != m_pStreamRespList->End(); ++lIter)
      {
          pAudioStreamInfoResponse = (IHXAudioStreamInfoResponse*) (*lIter);
          pAudioStreamInfoResponse->OnStream(pAudioStream);
      }
    }

    m_bHasStreams = TRUE;
}

ULONG32     CHXAudioPlayer::GetInitialPushdown(BOOL bAtStart /* = FALSE*/)
{
    /* If there are any audio streams, initial pushdown is session's
     * initial pushdown
     */
    if (m_pStreamList->GetCount() > 0)
    {
        ULONG32 ulRet = 
            m_Owner->GetInitialPushdown(bAtStart) + m_ulGranularity;

#ifdef HELIX_FEATURE_MIN_HEAP
        // The MIN_HEAP code seems to need 1 extra block's worth
        // of data to avoid rebuffers during playback. This
        // is a low impact temporary solution that fixes the problem.
        ulRet += m_ulGranularity;
#endif /* HELIX_FEATURE_MIN_HEAP */

        return ulRet;
    }
    else
    {
      return 0;
    }
}

/************************************************************************
 *  Method:
 *      CHXAudioPlayer::OnTimerCallback
 *  Purpose:
 *      Timer callback when implementing fake timeline.
 */
void  CHXAudioPlayer::OnTimerCallback()
{
    ULONG32 ulCurrentTime     = HX_GET_TICKCOUNT();

    m_ulIncreasingTimer       += CALCULATE_ELAPSED_TICKS(m_ulLastFakeCallbackTime, ulCurrentTime);
    m_ulLastFakeCallbackTime    = ulCurrentTime;

    OnTimeSync(m_ulIncreasingTimer);

    /* A call to timesync may result in stopping
     * playback and we do not want to have any more
     * time syncs.
     */
    /* Put this back in the scheduler.
     */
    if (m_bInited && m_eState == E_PLAYING && !m_ulCallbackID)
    {
      *m_pFakeAudioCBTime += (int) (m_ulGranularity*1000);
      m_ulCallbackID = m_pScheduler->AbsoluteEnter( this,
                                                      *((HXTimeval*)m_pFakeAudioCBTime));
    }
}


void
CHXAudioPlayer::SetLive(BOOL bIsLive)
{
    m_bIsLive = bIsLive;
    CHXAudioStream* s = 0;
    CHXSimpleList::Iterator lIter = m_pStreamList->Begin();
    for (; lIter != m_pStreamList->End(); ++lIter)
    {
      s = (CHXAudioStream*) (*lIter);
      s->SetLive(m_bIsLive);
    }
}

void
CHXAudioPlayer::AudioFormatNowKnown(void)
{
    HX_ASSERT(m_bInited);

    /* When : More than one audio stream created after initialization
     * and one of them already called this function earlier
     */
    if (m_bHasStreams)
    {
      return;
    }

    m_bHasStreams   = TRUE;
    m_bInited     = FALSE;
    /* internal setup */
    HX_RESULT theErr = Setup(m_ulGranularity);
    if (theErr != HXR_OK)
    {
      IHXErrorMessages* pErrorMessage = NULL;
      m_pContext->QueryInterface(IID_IHXErrorMessages, (void**) &pErrorMessage);
      if (pErrorMessage)
      {
          pErrorMessage->Report(HXLOG_ERR, theErr, 0, NULL, NULL);
          pErrorMessage->Release();
      }

      return;
    }

    HX_ASSERT(m_bInited);

    /* If we have not yet resumed, it is simply setting the audio
     * player from fake timeline mode to audio timeline mode.
     * Instead if we have already been resumed and are acting as a
     * fake timeline, we need to kinda pause from being a fake timeline to
     * an audio timeline, In this process, we may have to seek the audio
     * device to generate right timesyncs.
     */

    /* If we are already resumed, we need to call resume again internally */
    if (m_bIsResumed)
    {
      StopFakeTimeline();
      Seek(m_ulCurrentTime);
      // only resume the owner if we are in a play state...
      // otherwise owner will be resumed when the player gets resumed.
      if (m_eState == E_PLAYING)
      {
          m_Owner->Resume();
      }
    }
    else
    {
      /* Cool! HXPlayer will call resume later */
    }
}

void
CHXAudioPlayer::RegisterRealAudioStream(CHXAudioStream* pAudioStream)
{
    if (!m_pRealAudioStreamList)
    {
      m_pRealAudioStreamList = new CHXSimpleList;
    }

    m_pRealAudioStreamList->AddTail((void*) pAudioStream);
    m_bAdjustForRealAudio = TRUE;
}

void
CHXAudioPlayer::UnRegisterRealAudioStream(CHXAudioStream* pAudioStream)
{
    if (!m_pRealAudioStreamList)
    {
      return;
    }

    LISTPOSITION posStream = m_pRealAudioStreamList->Find((void*) pAudioStream);
    if (posStream)
    {
      m_pRealAudioStreamList->RemoveAt(posStream);
    }

    /* Check if there are any more RealAudio Streams registered */
    if (m_pRealAudioStreamList->GetCount() == 0)
    {
      m_bAdjustForRealAudio = FALSE;
    }
}

BOOL
CHXAudioPlayer::IsAudioOnlyTrue(void)
{
    BOOL bRetValue = TRUE;
    IHXPlayer* pPlayer = NULL;
    m_pContext->QueryInterface(IID_IHXPlayer, (void**)&pPlayer);

    HX_ASSERT(pPlayer);

    UINT16 uNumSources = pPlayer->GetSourceCount();

    IUnknown*           pUnknown    = NULL;
    IHXStreamSource*    pStreamSource     = NULL;
    IHXStream*          pStream           = NULL;
    for (UINT16 i=0; bRetValue && i < uNumSources; i++)
    {
      pPlayer->GetSource(i, pUnknown);
      pUnknown->QueryInterface(IID_IHXStreamSource,
                         (void**) &pStreamSource);
      HX_RELEASE(pUnknown);
      HX_ASSERT(pStreamSource);

      UINT16 uNumStreams = pStreamSource->GetStreamCount();
      for (UINT16 j=0; bRetValue && j < uNumStreams; j++)
      {
          pStreamSource->GetStream(j, pUnknown);
          pUnknown->QueryInterface(IID_IHXStream,
                             (void**) &pStream);
          HX_RELEASE(pUnknown);
          HX_ASSERT(pStream);

          IHXValues* pHeader = pStream->GetHeader();
          if (pHeader)
          {
            if (!IsThisAudioStream(pHeader))
            {
                bRetValue = FALSE;
            }
            pHeader->Release();
          }

          pStream->Release();
      }

      HX_RELEASE(pStreamSource);
    }

    HX_RELEASE(pPlayer);

    return bRetValue;
}

BOOL
CHXAudioPlayer::IsThisAudioStream(IHXValues* pHeader)
{
    CHXSimpleList::Iterator ndxStream = m_pStreamList->Begin();
    for(; ndxStream != m_pStreamList->End(); ++ndxStream)
    {
      CHXAudioStream* pAudioStream = (CHXAudioStream*) (*ndxStream);
      IHXValues* pAudioHeader = pAudioStream->GetStreamInfo();
      if (pAudioHeader == pHeader)
      {
          HX_RELEASE(pAudioHeader);
          return TRUE;
      }

      HX_RELEASE(pAudioHeader);
    }

    return FALSE;
}

void
CHXAudioPlayer::AdjustForRealAudio()
{
    UINT32 ulCurrentDeviceTime = m_ulCurrentTime;

#if defined(HELIX_FEATURE_AUDIO_INACCURATESAMPLING)
    if (m_bAdjustForRealAudio)
    {
      CHXAudioStream* pAudioStream = NULL;

        // Only use a playing audio stream for time adjustment:
        for (CHXSimpleList::Iterator streamIterator = m_pRealAudioStreamList->Begin();
             streamIterator != m_pRealAudioStreamList->End();
             ++streamIterator)
        {
            if ( ((CHXAudioStream*) (*streamIterator))->GetState() == E_PLAYING)
            {
                pAudioStream = (CHXAudioStream*) *streamIterator;
                break;
            }
        }

        if (pAudioStream)
        {
            UINT32 ulAdjustedTime = 0L;

            double dBytesPlayed = m_Owner->GetNumBytesPlayed();
            if (HXR_OK == pAudioStream->ConvertCurrentTime( dBytesPlayed,
                                                            m_ulCurrentTime,
                                                            ulAdjustedTime))
            {
                UINT32 ulTickCount = HX_GET_TICKCOUNT();
                /* This is to avoid stall at end of the presentation */
                /* The RA stream may have a duration of say 30 seconds
                 * but the actual data may be only for 29.9
                 * seconds. In this case, audio stream will never
                 * return time more than 29.9 seconds and we will get
                 * stalled.  To avoid this, we wait for atmost
                 * MAX_WAIT_AT_SAME_TIME (== max granularity+50 ms) at
                 * the same timestamp. If we find that we are pushing
                 * more data in the audio device but the audio stream
                 * is reporting the same time for the past
                 * MAX_WAIT_AT_SAME_TIME, we increment our timestamp
                 * by the real time elapsed since the last update.
                 * This code will only trigger when we are near the
                 * end of presentation.
                 */
                if (m_bTimeReturned &&
                    ulAdjustedTime <= m_ulLastCurrentTimeReturned &&
                    ulCurrentDeviceTime > m_ulLastDeviceTimeAdjusted &&
                    ulCurrentDeviceTime - m_ulLastDeviceTimeAdjusted > MAX_WAIT_AT_SAME_TIME)
                {
                    UINT32 ulElapsedTime = CALCULATE_ELAPSED_TICKS(m_ulTimeAdjustDoneAt,
                                                                   ulTickCount);
                    if (ulElapsedTime >= MAX_WAIT_AT_SAME_TIME)
                    {
                        m_ulCurrentTime = m_ulLastCurrentTimeReturned + ulElapsedTime;
                        m_ulTimeAdjustDoneAt        = ulTickCount;
                        m_ulLastDeviceTimeAdjusted  = ulCurrentDeviceTime;
                    }
                    else
                    {
                        m_ulCurrentTime = ulAdjustedTime;
                    }
                }
                else
                {
                    m_ulTimeAdjustDoneAt = ulTickCount;
                    m_ulCurrentTime      = ulAdjustedTime;
                }
            }
        }
    }
#endif /* HELIX_FEATURE_AUDIO_INACCURATESAMPLING */

    /* Never go back in time */
    if (!m_bTimeReturned)
    {
      m_bTimeReturned             = TRUE;
      m_ulLastCurrentTimeReturned = m_ulCurrentTime;
      m_ulTimeAdjustDoneAt        = HX_GET_TICKCOUNT();
      m_ulLastDeviceTimeAdjusted  = ulCurrentDeviceTime;
    }
    else if (m_ulCurrentTime <= m_ulLastCurrentTimeReturned)
    {
      m_ulCurrentTime = m_ulLastCurrentTimeReturned;
    }
    else
    {
      m_ulLastDeviceTimeAdjusted  = ulCurrentDeviceTime;
      m_ulLastCurrentTimeReturned = m_ulCurrentTime;
    }

}

HX_RESULT
CHXAudioPlayer::ResumeFakeTimeline(void)
{
    HX_RESULT     rc = HXR_OK;

    HX_ASSERT(!m_bHasStreams);

    HXTimeval lTime = m_pScheduler->GetCurrentSchedulerTime();

    m_pFakeAudioCBTime->tv_sec = lTime.tv_sec;
    m_pFakeAudioCBTime->tv_usec = lTime.tv_usec;

    m_ulIncreasingTimer = m_ulCurrentTime;
    m_ulLastFakeCallbackTime = HX_GET_TICKCOUNT();

    *m_pFakeAudioCBTime += (int) (m_ulGranularity*1000);

    m_ulCallbackID = m_pScheduler->AbsoluteEnter(this,
                                                 *((HXTimeval*) m_pFakeAudioCBTime));

    return rc;
}

HX_RESULT CHXAudioPlayer::Func()
{
    m_ulCallbackID = 0;
    OnTimerCallback();
    return HXR_OK;
}


HX_RESULT CHXAudioPlayer::StopFakeTimeline(void)
{
    HX_RESULT     rc = HXR_OK;

    if(m_ulCallbackID && m_pScheduler)
    {
        m_pScheduler->Remove(m_ulCallbackID);
    }

    return rc;
}

double
CHXAudioPlayer::NumberOfBytesWritten()
{
    return m_Owner->NumberOfBytesWritten();
}

double
CHXAudioPlayer::ConvertMsToBytes(UINT32 ulTime)
{
    return m_Owner->ConvertMsToBytes(ulTime);
}

void
CHXAudioPlayer::UpdateStreamLastWriteTime()
{
    // Make each stream seek, too, since they own the resampling buffers.
    CHXAudioStream* s = 0;
    CHXSimpleList::Iterator lIter = m_pStreamList->Begin();
    for (; lIter != m_pStreamList->End(); ++lIter)
    {
      s = (CHXAudioStream*) (*lIter);
      s->UpdateStreamLastWriteTime();
    }
}

void
CHXAudioPlayer::SaveLastNMilliSeconds(BOOL bSave, UINT32 ulNMilliSeconds)
{
    // Make each stream seek, too, since they own the resampling buffers.
    CHXAudioStream* s = 0;
    CHXSimpleList::Iterator lIter = m_pStreamList->Begin();
    for (; lIter != m_pStreamList->End(); ++lIter)
    {
      s = (CHXAudioStream*) (*lIter);
      s->SaveLastNMilliSeconds(bSave, ulNMilliSeconds);
    }
}

void
CHXAudioPlayer::RewindPlayer(UINT32 ulTimeToRewind)
{
    if (m_pStreamList->GetCount() == 0    || !m_bCanBeRewound)
    {
      return;
    }

    // Make each stream seek, too, since they own the resampling buffers.
    CHXAudioStream* s = 0;
    CHXSimpleList::Iterator lIter = m_pStreamList->Begin();
    for (; lIter != m_pStreamList->End(); ++lIter)
    {
      s = (CHXAudioStream*) (*lIter);
      s->RewindStream(ulTimeToRewind);
    }

    HX_ASSERT(m_llLastWriteTime >= ulTimeToRewind);
    if (m_llLastWriteTime >= ulTimeToRewind)
    {
      m_llLastWriteTime -= ulTimeToRewind;
    }
}

HX_RESULT
CHXAudioPlayer::ProcessAudioHook(PROCESS_ACTION action,
                         IHXAudioHook* pAudioHook)
{
    return HXR_OK;
}

STDMETHODIMP CHXAudioPlayer::SetError( HX_RESULT theErr )
{
    if (theErr != HXR_OK)
    {
      IHXErrorMessages* pErrorMessage = NULL;
      m_pContext->QueryInterface(IID_IHXErrorMessages, (void**) &pErrorMessage);
      if (pErrorMessage)
      {
          pErrorMessage->Report(HXLOG_ERR, theErr, 0, NULL, NULL);
          pErrorMessage->Release();
      }
    }
    return HXR_OK;
}


Generated by  Doxygen 1.6.0   Back to index