hxaudply.cpp
上传用户:zhongxx05
上传日期:2007-06-06
资源大小:33641k
文件大小:59k
- /* ***** BEGIN LICENSE BLOCK *****
- * Version: RCSL 1.0/RPSL 1.0
- *
- * Portions Copyright (c) 1995-2002 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
- * Version 1.0 (the "RPSL") available at
- * http://www.helixcommunity.org/content/rpsl unless you have licensed
- * the file under the RealNetworks Community Source License Version 1.0
- * (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.
- *
- * 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 40
- #define MAXIMUM_AUDIO_GRANULARITY 40
- #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
- *
- */
- STDMETHODIMP
- CHXAudioPlayer::CrossFade(IHXAudioStream* pStreamFrom,
- IHXAudioStream* pStreamTo,
- UINT32 ulFromCrossFadeStartTime,
- UINT32 ulToCrossFadeStartTime,
- UINT32 ulCrossFadeDuration)
- {
- #if defined(HELIX_FEATURE_CROSSFADE)
- HX_RESULT theErr = HXR_OK;
- /* Make sure these two streams are available */
- LISTPOSITION streamPos1 = m_pStreamList->Find(pStreamFrom);
- LISTPOSITION streamPos2 = m_pStreamList->Find(pStreamTo);
- if (!streamPos2 || !streamPos1)
- {
- return HXR_INVALID_PARAMETER;
- }
- CHXAudioStream* pFromStream =
- (CHXAudioStream*) m_pStreamList->GetAt(streamPos1);
- CHXAudioStream* pToStream =
- (CHXAudioStream*) m_pStreamList->GetAt(streamPos2);
- theErr = pFromStream->StartCrossFade(pToStream, ulFromCrossFadeStartTime,
- ulCrossFadeDuration, FALSE);
- if (!theErr)
- {
- theErr = pToStream->StartCrossFade(pFromStream, ulToCrossFadeStartTime,
- ulCrossFadeDuration, TRUE);
- }
- /* 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!
- */
- if (!theErr)
- {
- 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);
- }
- #endif /* HELIX_FEATURE_CROSSFADE */
- return HXR_OK;
- }
- /************************************************************************
- * 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;
- }