📄 audrend.cpp
字号:
if (SUCCEEDED(retVal))
{
retVal = InitAudioStream(pHeader, &m_ppAudioStream[m_ulCurAudioStream]);
}
// Setup preroll
if (SUCCEEDED(retVal))
{
// check that stream header preroll value is not too big
ULONG32 ulMaxPreroll = m_pAudioFormat->GetMaximumPreroll(pHeader);
if (m_ulPreroll > ulMaxPreroll)
{
m_ulPreroll = ulMaxPreroll;
}
else
// check that stream header preroll value is set
if (m_ulPreroll == 0)
{
// preload is not set for this stream - assume default
m_ulPreroll = m_pAudioFormat->GetDefaultPreroll(pHeader);
}
// note that we are not enforcing any minimum preroll here
if (m_ulPreroll > 0)
{
pHeader->SetPropertyULONG32( "Preroll", m_ulPreroll );
}
// Set the flag saying whether or not the audio
// stream parameters can change on the fly
m_bCanChangeAudioStream = m_pAudioFormat->CanChangeAudioStream();
// Save the stream header
HX_RELEASE(m_pHeader);
m_pHeader = pHeader;
m_pHeader->AddRef();
}
return retVal;
}
/////////////////////////////////////////////////////////////////////////////
// Method:
// CAudioRenderer::CheckStreamVersions
// copied from CRealAudioRenderer
HX_RESULT CAudioRenderer::CheckStreamVersions(IHXValues* pHeader)
{
// check stream and content versions so an upgrade can
// be called if necessary...
HX_RESULT pnr = HXR_OK;
#if defined(HELIX_FEATURE_AUTOUPGRADE)
BOOL bVersionOK = TRUE;
UINT32 ulStreamVersion = 0;
UINT32 ulContentVersion = 0;
if(HXR_OK == pHeader->GetPropertyULONG32("StreamVersion",
ulStreamVersion))
{
UINT32 ulMajorVersion = HX_GET_MAJOR_VERSION(ulStreamVersion);
UINT32 ulMinorVersion = HX_GET_MINOR_VERSION(ulStreamVersion);
ULONG32 ulThisMajorVersion = STREAM_MAJOR_VERSION;
ULONG32 ulThisMinorVersion = STREAM_MINOR_VERSION;
GetStreamVersion(ulThisMajorVersion, ulThisMinorVersion);
if((ulMajorVersion > ulThisMajorVersion) ||
((ulMinorVersion > ulThisMinorVersion) &&
(ulMajorVersion == ulThisMajorVersion)))
{
bVersionOK = FALSE;
}
}
if(bVersionOK &&
(HXR_OK == pHeader->GetPropertyULONG32("ContentVersion",
ulContentVersion)))
{
UINT32 ulMajorVersion = HX_GET_MAJOR_VERSION(ulContentVersion);
UINT32 ulMinorVersion = HX_GET_MINOR_VERSION(ulContentVersion);
ULONG32 ulThisMajorVersion = CONTENT_MAJOR_VERSION;
ULONG32 ulThisMinorVersion = CONTENT_MINOR_VERSION;
GetContentVersion(ulThisMajorVersion, ulThisMinorVersion);
if((ulMajorVersion > ulThisMajorVersion) ||
((ulMinorVersion > ulThisMinorVersion) &&
(ulMajorVersion == ulMajorVersion)))
{
bVersionOK = FALSE;
}
}
if(!bVersionOK)
{
AddToAutoUpgradeCollection(GetUpgradeMimeType(), m_pContext);
pnr = HXR_FAIL;
}
#endif /* #if defined(HELIX_FEATURE_AUTOUPGRADE) */
return pnr;
}
/////////////////////////////////////////////////////////////////////////
// Method:
// IHXRenderer::OnPacket
// Purpose:
// Called by client engine when a packet for this renderer is
// due.
// lTimeOffset is the amount of time that we lag behind the main player time line
// so if the start time of the track is 10 seconds, lTimeOffset will be 10000 (msec)
// (the first packet's time stamp will be 0 but the player will be at time=10sec)
//
STDMETHODIMP
CAudioRenderer::OnPacket(IHXPacket* pPacket, LONG32 lTimeOffset)
{
HX_RESULT retVal = HXR_OK;
UINT16 uStreamForThisPacket = 0;
/* Ignore any pre-seek packets or NULL packets */
if (m_bInSeekMode || pPacket == NULL)
{
return HXR_OK;
}
m_lTimeOffset = lTimeOffset;
m_bProcessingPacket = TRUE;
m_pMutex->Lock();
m_bFirstPacket = FALSE;
m_pAudioFormat->Enqueue(pPacket);
if (m_PlayState != playing)
{
// Take this chance to write audio to audio services
UINT32 ulAudioTime;
DoAudio(ulAudioTime);
}
m_bProcessingPacket = FALSE;
m_pMutex->Unlock();
return retVal;
}
/////////////////////////////////////////////////////////////////////////
// Method:
// IHXRenderer::OnTimeSync
// Purpose:
// Called by client engine to inform the renderer of the current
// time relative to the streams synchronized time-line. The
// renderer should use this time value to update its display or
// render it's stream data accordingly.
//
STDMETHODIMP CAudioRenderer::OnTimeSync(ULONG32 ulTime)
{
MLOG_MISCEX(m_pErrorMessages, "OTS(%lu)\n", ulTime);
// we never enter the play state on Mac so that we write audio
// in both packet and timesync
#ifndef _MACINTOSH
// if we get a timesync we must be playing
m_PlayState = playing;
#endif
m_pMutex->Lock();
#ifdef _MACINTOSH
/* On Mac, since we do not have Mutex, we do not want to process
* data if we are within OnPacket call
*/
if (m_bProcessingPacket)
{
goto exit;
}
m_bProcessingPacket = TRUE;
#endif /*_MACINTOSH*/
#if !defined(HELIX_CONFIG_MIN_PCM_PUSHDOWN_BYTES)
// Write to AS
UINT32 ulAudioTime;
DoAudio(ulAudioTime);
#endif
#ifdef _MACINTOSH
m_bProcessingPacket = FALSE;
exit:
#endif /*_MACINTOSH*/
m_pMutex->Unlock();
return HXR_OK;
}
/////////////////////////////////////////////////////////////////////////
// Method:
// IHXRenderer::OnPreSeek
// Purpose:
// Called by client engine to inform the renderer that a seek is
// about to occur. The render is informed the last time for the
// stream's time line before the seek, as well as the first new
// time for the stream's time line after the seek will be completed.
//
STDMETHODIMP CAudioRenderer::OnPreSeek(ULONG32 ulOldTime,
ULONG32 ulNewTime)
{
m_pMutex->Lock();
m_PlayState = seeking;
m_bEndOfPackets = FALSE;
m_bDoneWritingPackets = FALSE;
m_bInSeekMode = TRUE;
m_bFirstPacket = TRUE;
m_ulLastWriteTime = NO_TIME_SET;
// get out of our buffering state if we are in one
if (IsRebuffering())
{
EndRebuffer();
}
m_pAudioFormat->SetStartTime(ulNewTime);
m_pAudioFormat->Reset();
m_pMutex->Unlock();
return HXR_OK;
}
/////////////////////////////////////////////////////////////////////////
// Method:
// IHXRenderer::OnPostSeek
// Purpose:
// Called by client engine to inform the renderer that a seek has
// just occured. The render is informed the last time for the
// stream's time line before the seek, as well as the first new
// time for the stream's time line after the seek.
//
STDMETHODIMP CAudioRenderer::OnPostSeek(ULONG32 ulOldTime, ULONG32 ulNewTime)
{
m_bInSeekMode = FALSE;
return HXR_OK;
}
/////////////////////////////////////////////////////////////////////////
// Method:
// IHXRenderer::OnPause
// Purpose:
// Called by client engine to inform the renderer that a pause has
// just occured. The render is informed the last time for the
// stream's time line before the pause.
//
STDMETHODIMP CAudioRenderer::OnPause(ULONG32 ulTime)
{
m_pMutex->Lock();
m_PlayState = paused;
m_pMutex->Unlock();
return HXR_OK;
}
/////////////////////////////////////////////////////////////////////////
// Method:
// IHXRenderer::OnBegin
// Purpose:
// Called by client engine to inform the renderer that a begin or
// resume has just occured. The render is informed the first time
// for the stream's time line after the resume.
//
STDMETHODIMP CAudioRenderer::OnBegin(ULONG32 ulTime)
{
return HXR_OK;
}
/////////////////////////////////////////////////////////////////////////
// Method:
// IHXRenderer::OnBuffering
// Purpose:
// Called by client engine to inform the renderer that buffering
// of data is occuring. The render is informed of the reason for
// the buffering (start-up of stream, seek has occured, network
// congestion, etc.), as well as percentage complete of the
// buffering process.
//
STDMETHODIMP CAudioRenderer::OnBuffering(ULONG32 ulFlags, UINT16 unPercentComplete)
{
m_PlayState = buffering;
return HXR_OK;
}
/////////////////////////////////////////////////////////////////////////
// Method:
// IHXRenderer::GetDisplayType
// Purpose:
// Called by client engine to ask the renderer for it's preferred
// display type. When layout information is not present, the
// renderer will be asked for it's prefered display type. Depending
// on the display type a buffer of additional information may be
// needed. This buffer could contain information about preferred
// window size.
//
STDMETHODIMP CAudioRenderer::GetDisplayType(REF(HX_DISPLAY_TYPE) ulFlags,
REF(IHXBuffer*) pBuffer)
{
ulFlags = HX_DISPLAY_NONE;
return HXR_OK;
}
/************************************************************************
* Method:
* IHXRenderer::OnEndofPackets
* Purpose:
* Called by client engine to inform the renderer that all the
* packets have been delivered. However, if the user seeks before
* EndStream() is called, renderer may start getting packets again
* and the client engine will eventually call this function again.
*/
STDMETHODIMP CAudioRenderer::OnEndofPackets(void)
{
HX_RESULT pnr = HXR_OK;
/* we should release any remaining sub-superblocks to audio services here*/
m_bEndOfPackets = TRUE;
if (IsRebuffering())
{
EndRebuffer();
}
m_pMutex->Lock();
#ifdef _MACINTOSH
// since we don't get as many time sync calls on the mac
// and we want to make sure to write all of the packets
// to audio services, make sure we have 2 seconds of
// data buffered in audio services now.
AttemptToSatisfyDryRequest(m_ulLastWriteTime + 2000);
#endif
m_pMutex->Unlock();
return HXR_OK;
}
/*
* IHXDryNotification methods
*/
/************************************************************************
* Method:
* OnDryNotification
* Purpose:
* This function is called when it is time to write to audio device
* and there is not enough data in the audio stream. The renderer can
* then decide to add more data to the audio stream. This should be
* done synchronously within the call to this function.
* It is OK to not write any data. Silence will be played instead.
*/
STDMETHODIMP CAudioRenderer::OnDryNotification(UINT32 /*IN*/ ulCurrentStreamTime,
UINT32 /*IN*/ ulMinimumDurationRequired)
{
MLOG_MISC(m_pErrorMessages, "ODN (%lu,%lu)\n",
ulCurrentStreamTime, ulMinimumDurationRequired);
/* If the renderer is delayed, do not report rebuffer status until the
* packets are really due i.e. until Current time + Preroll is greater
* than the Delay time.
*/
m_pMutex->Lock();
if (m_bDoneWritingPackets)
{
goto exit;
}
if (NO_TIME_SET != m_ulLastWriteTime &&
IsTimeGreater(ulCurrentStreamTime, m_ulLastWriteTime + TIME_FUDGE))
{
// if the stream time reported by audio services is ahead of the
// current writing time of the renderer, update the writing time
// of the renderer so it catches up with the audio services stream
m_ulLastWriteTime = ulCurrentStreamTime;
}
if (!m_bFirstPacket &&
IsTimeGreater(ulCurrentStreamTime + m_ulPreroll, m_ulDelay) &&
(NO_TIME_SET == m_ulLastWriteTime ||
IsTimeGreater(ulCurrentStreamTime + TIME_FUDGE, m_ulLastWriteTime)))
{
// Try to write some audio to satisfy the audio stream
HX_RESULT pnr = HXR_OK;
UINT32 ulAudioWantedTime =
(ulCurrentStreamTime + ulMinimumDurationRequired);
pnr = AttemptToSatisfyDryRequest(ulAudioWantedTime);
// if we couldn't satisfy the request and we have not
// recieved all of our packets, and we have started playing,
// tell the core to rebuffer
if ((FAILED(pnr) || HXR_NO_DATA == pnr) && !m_bEndOfPackets &&
IsTimeGreaterOrEqual(ulCurrentStreamTime, m_ulDelay) &&
IsTimeLess(m_ulLastWriteTime, ulAudioWantedTime))
{
StartRebuffer(ulAudioWantedTime);
}
}
exit:
m_pMutex->Unlock();
return HXR_OK;
}
/************************************************************************
* IHXStatistics Methods
*/
/************************************************************************
* InitializeStatistics
*/
STDMETHODIMP CAudioRenderer::InitializeStatistics(UINT32 ulRegistryID)
{
BOOL bCodecNameKnown = FALSE;
char* pValue = NULL;
HX_RESULT retVal = HXR_UNEXPECTED;
m_ulRegistryID = ulRegistryID;
#if defined(HELIX_FEATURE_STATS)
if (m_pAudioStats)
{
retVal = HXR_OK;
}
if (SUCCEEDED(retVal))
{
pValue = (char*) GetCodecName();
if (pValue != NULL)
{
ReportStat(AS_CODEC_NAME, pValue);
bCodecNameKnown = TRUE;
}
}
if (SUCCEEDED(retVal))
{
pValue = (char*) GetRendererName();
if (pValue != NULL)
{
ReportStat(AS_REND_NAME, pValue);
// If Codec name is unknown, use a more generic renderer name
if (!bCodecNameKnown)
{
ReportStat(AS_CODEC_NAME, pValue);
}
}
}
if (SUCCEEDED(retVal))
{
pValue = (char*) GetCodecFourCC();
if (pValue != NULL)
{
ReportStat(AS_CODEC_4CC, pValue);
}
}
if (SUCCEEDED(retVal))
{
HXAudioFormat audioFmt;
audioFmt.uChannels = 0;
audioFmt.ulSamplesPerSec = 0;
audioFmt.uBitsPerSample = 0;
if (m_pAudioFormat)
{
m_pAudioFormat->GetAudioFormat(audioFmt);
}
ReportStat(AS_CHANNELS, (INT32) audioFmt.uChannels);
ReportStat(AS_SAMPLING_RATE, (INT32) audioFmt.ulSamplesPerSec);
ReportStat(AS_SAMPLE_SIZE, (INT32) audioFmt.uBitsPerSample);
}
if (SUCCEEDED(retVal))
{
retVal = m_pAudioStats->DisplayStats(m_ulRegistryID);
}
return retVal;
#else
return HXR_NOTIMPL;
#endif /* HELIX_FEATURE_STATS */
}
/************************************************************************
* UpdateStatistics
*/
STDMETHODIMP CAudioRenderer::UpdateStatistics()
{
#if defined(HELIX_FEATURE_STATS)
HX_RESULT retVal = HXR_UNEXPECTED;
if (m_pAudioStats)
{
retVal = HXR_OK;
}
if (SUCCEEDED(retVal))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -