📄 audrend.cpp
字号:
{
retVal = m_pAudioStats->DisplayStats(m_ulRegistryID);
}
return retVal;
#else
return HXR_NOTIMPL;
#endif /* HELIX_FEATURE_STATS */
}
/****************************************************************************
* Renderer's customizable fuctions - can be called any time
*/
/****************************************************************************
* GetStreamVersion
*/
void CAudioRenderer::GetStreamVersion(ULONG32 &ulThisMajorVersion,
ULONG32 &ulThisMinorVersion)
{
ulThisMajorVersion = STREAM_MAJOR_VERSION;
ulThisMinorVersion = STREAM_MINOR_VERSION;
}
/****************************************************************************
* GetContentVersion
*/
void CAudioRenderer::GetContentVersion(ULONG32 &ulThisMajorVersion,
ULONG32 &ulThisMinorVersion)
{
ulThisMajorVersion = CONTENT_MAJOR_VERSION;
ulThisMinorVersion = CONTENT_MINOR_VERSION;
}
/****************************************************************************
* GetUpgradeMimeType
*/
const char* CAudioRenderer::GetUpgradeMimeType(void)
{
const char** pStreamMimeTypes = NULL;
UINT32 ulInitialGranularity;
GetRendererInfo(pStreamMimeTypes, ulInitialGranularity);
if (pStreamMimeTypes)
{
return pStreamMimeTypes[0];
}
return NULL;
}
/****************************************************************************
* GetRendererName
*/
const char* CAudioRenderer::GetRendererName(void)
{
return BASE_AUDIO_RENDERER_NAME;
}
/****************************************************************************
* GetCodecName
*/
const char* CAudioRenderer::GetCodecName(void)
{
return NULL;
}
/****************************************************************************
* GetCoGetCodecFourCCdec4CC
*/
const char* CAudioRenderer::GetCodecFourCC(void)
{
return NULL;
}
/****************************************************************************
* CreateFormatObject
*/
CAudioFormat* CAudioRenderer::CreateFormatObject(IHXValues* pHeader)
{
return new CAudioFormat(m_pCommonClassFactory,
this);
}
/////////////////////////////////////////////////////////////////////////////
// Method:
// CAudioRenderer::InitAudioStream
HX_RESULT CAudioRenderer::InitAudioStream(IHXValues* pHeader,
IHXAudioStream** ppAudioStream)
{
HX_RESULT retVal = HXR_OK;
// init so we can HX_RELEASE on error.
*ppAudioStream = NULL;
retVal = m_pAudioPlayer->CreateAudioStream(ppAudioStream);
if (SUCCEEDED(retVal))
{
IHXCommonClassFactory* pCommonClassFactory;
if (HXR_OK == (*ppAudioStream)->QueryInterface(IID_IHXCommonClassFactory,
(void**)&pCommonClassFactory))
{
m_pAudioFormat->OverrideFactory(pCommonClassFactory);
pCommonClassFactory->Release();
}
HXAudioFormat audioFmt;
m_pAudioFormat->GetAudioFormat(audioFmt);
#if defined(HELIX_FEATURE_STATS)
ReportStat(AS_CHANNELS, (INT32) audioFmt.uChannels);
ReportStat(AS_SAMPLING_RATE, (INT32) audioFmt.ulSamplesPerSec);
ReportStat(AS_SAMPLE_SIZE, (INT32) audioFmt.uBitsPerSample);
#endif /* #if defined(HELIX_FEATURE_STATS) */
/* Add default dry notification BEFORE initializing the audio
* stream. This is so that if we are started mid presentation
* and there was no audio present earlier, the timeline will
* change from being a fake timeline to audio timeline and
* the audio services will write audio for initial pushdown
* time. We need to get dry notifications so that we can halt
* the timeline, if the renderer does not have enough data.
*/
IHXDryNotification* pDryNot = NULL;
// Get my own DryNotification interface with an add
QueryInterface(IID_IHXDryNotification, (void**)&pDryNot);
retVal = (*ppAudioStream)->AddDryNotification(pDryNot);
HX_ASSERT(SUCCEEDED(retVal));
MLOG_MISC(m_pErrorMessages, "AS Init (%u,%u,%lu,%u)\n",
audioFmt.uChannels, audioFmt.uBitsPerSample,
audioFmt.ulSamplesPerSec, audioFmt.uMaxBlockSize);
retVal = (*ppAudioStream)->Init(&audioFmt, pHeader);
HX_RELEASE(pDryNot);
}
if (HXR_OK != retVal)
{
HX_RELEASE((*ppAudioStream));
}
return retVal;
}
/////////////////////////////////////////////////////////////////////////////
// Method:
// CAudioRenderer::WriteToAudioServices
HX_RESULT CAudioRenderer::WriteToAudioServices(HXAudioData* pAudioData)
{
HX_RESULT pnr = HXR_OK;
BOOL bTryWrite = TRUE;
// Can the audio stream change on the fly?
// If so, then check for any change. If not,
// then skip the check.
if (m_bCanChangeAudioStream)
{
BOOL bAudioStreamChanged = FALSE;
pnr = CheckForAudioStreamChange(bAudioStreamChanged);
if (FAILED(pnr))
{
return pnr;
}
if (bAudioStreamChanged)
{
pAudioData->uAudioStreamType = TIMED_AUDIO;
}
}
while (bTryWrite)
{
pnr = CheckAudioServices();
if (FAILED(pnr))
{
return pnr;
}
MLOG_MISC(m_pErrorMessages, "AS Write (%lu,%lu,%lu) ms=%lu tick=%lu\n",
(pAudioData->pData ? pAudioData->pData->GetSize() : 0),
pAudioData->ulAudioTime,
pAudioData->uAudioStreamType,
m_pAudioFormat->ConvertBytesToMs((pAudioData->pData ? pAudioData->pData->GetSize() : 0)),
HX_GET_BETTERTICKCOUNT());
DEBUG_OUTF_IDX(m_ulCurAudioStream, AUDREND_FLOW_FILE,
(s, "Audio Write: Time=%u, Bytes=%u, Duration=%u, %s\n",
pAudioData->ulAudioTime,
(pAudioData->pData ? pAudioData->pData->GetSize() : 0),
m_pAudioFormat->ConvertBytesToMs((pAudioData->pData ? pAudioData->pData->GetSize() : 0)),
((pAudioData->uAudioStreamType == STREAMING_AUDIO) ? "STREAMING" : "TIMED")
));
// Write to AS
if (m_ppAudioStream[m_ulCurAudioStream])
pnr = m_ppAudioStream[m_ulCurAudioStream]->Write( pAudioData );
if (SUCCEEDED(pnr))
{
CalculateMaxTimeStamp(pAudioData);
bTryWrite = FALSE;
}
else
{
// we got an error on write, check what time the audio stream
// expects data for
HXAudioData audioData;
audioData.pData = NULL;
if (m_ppAudioStream[m_ulCurAudioStream])
m_ppAudioStream[m_ulCurAudioStream]->Write( &audioData );
if (IsTimeLess(audioData.ulAudioTime, pAudioData->ulAudioTime))
{
// we are skipping ahead and should just mark this packet
// as timed and write it again
pAudioData->uAudioStreamType = TIMED_AUDIO;
}
else if (IsTimeGreater(audioData.ulAudioTime, pAudioData->ulAudioTime) &&
IsTimeLessOrEqual(audioData.ulAudioTime, pAudioData->ulAudioTime +
m_pAudioFormat->ConvertBytesToMs(pAudioData->pData->GetSize())))
{
// we are a little behind but at least part of this stream
// is on time, we should clip off this buffer to the time
// the audio stream wants and try again.
bTryWrite = m_pAudioFormat->ClipAudioBuffer(pAudioData,
audioData.ulAudioTime, TRUE);
}
else
{
// we are a lot behind and should tell this format to discard
// data until the audio stream time.
m_pAudioFormat->
DiscardAudioUntil(audioData.ulAudioTime);
// we don't want to try again with this data
bTryWrite = FALSE;
}
}
}
// Handle exiting rebuffer started by OnDryNotification from
// Audio Services
// if we just wrote audio to audio services that is greater than
// or equal to the audio wanted time from the dry notification
// call then we can leave buffering
if (IsRebuffering() &&
IsTimeGreaterOrEqual(m_ulLastWriteTime, m_ulAudioWantedTime))
{
EndRebuffer();
}
return pnr;
}
/////////////////////////////////////////////////////////////////////////////
// Method:
// CAudioRenderer::DoAudio
//
// Note: See Switchsod.txt for how this is supposed to work, please keep
// the sod up to date with changes here too.
//
HX_RESULT CAudioRenderer::DoAudio(UINT32& ulAudioTime,
AUDIO_STATE audioState)
{
HX_RESULT retVal;
HXAudioData audioData;
ULONG32 ulPreviousLastWriteTime;
LONG32 lTimeDelta;
audioData.pData = NULL;
audioData.ulAudioTime = ulAudioTime = 0;
// write the lowest stream to audio services
ulPreviousLastWriteTime = m_ulLastWriteTime;
do
{
retVal = m_pAudioFormat->CreateAudioFrame(
audioData,
(m_bEndOfPackets) ? AUDIO_END_OF_PACKETS : audioState);
if (retVal == HXR_OK)
{
audioData.uAudioStreamType = TIMED_AUDIO;
// Update the timestamp of the audio services write
audioData.ulAudioTime = AdjustTimestamp(audioData.ulAudioTime, m_lTimeOffset);
if (m_ulLastWriteTime != NO_TIME_SET)
{
lTimeDelta = audioData.ulAudioTime -
m_ulLastWriteTime;
HX_ASSERT(lTimeDelta >= (-TIME_FUDGE));
if (lTimeDelta <= TIME_FUDGE)
{
audioData.uAudioStreamType = STREAMING_AUDIO;
}
#ifdef _AUDREND_FLOW_LOG
else if (lTimeDelta < (-TIME_FUDGE))
{
DEBUG_OUTF_IDX(m_ulCurAudioStream, AUDREND_FLOW_FILE,
(s, "Overlapping Audio: Time=%u, LastWriteTime=%u, Overlap=%d\n",
audioData.ulAudioTime,
m_ulLastWriteTime,
-lTimeDelta
));
}
#endif // _AUDREND_FLOW_LOG
}
retVal = WriteToAudioServices(&audioData);
}
else
{
break;
}
// do not loop here if writing to
// satisfy dry notification
} while (audioState != AUDIO_DRYNOTIFICATION &&
((m_ulLastWriteTime - ulPreviousLastWriteTime) <
MAX_AUDIO_WRITE_TIME));
// release the data buffer if we got one
HX_RELEASE(audioData.pData);
// update the out param
ulAudioTime = audioData.ulAudioTime;
return retVal;
}
HX_RESULT CAudioRenderer::AttemptToSatisfyDryRequest(UINT32 ulAudioWantedTime)
{
HX_RESULT pnr = HXR_OK;
UINT32 ulAudioTime = 0;
while (HXR_OK == pnr &&
IsTimeGreaterOrEqual(ulAudioWantedTime, m_ulLastWriteTime))
{
pnr = DoAudio(ulAudioTime, AUDIO_DRYNOTIFICATION);
}
return pnr;
}
void
CAudioRenderer::CalculateMaxTimeStamp(HXAudioData* pAudioData)
{
UINT32 ulTimestamp = pAudioData->ulAudioTime +
m_pAudioFormat->ConvertBytesToMs(pAudioData->pData->GetSize());
if (m_ulLastWriteTime == NO_TIME_SET ||
IsTimeLess(m_ulLastWriteTime, ulTimestamp))
{
m_ulLastWriteTime = ulTimestamp;
}
}
HX_RESULT CAudioRenderer::CheckForAudioStreamChange(REF(BOOL) rbAudioStreamChanged)
{
HX_RESULT retVal = HXR_FAIL;
if (m_pAudioFormat)
{
retVal = HXR_OK;
// Check to see if the audio format has changed
if (HasAudioFormatChanged())
{
// Transfer the current audio stream to
// a newly created audio stream
retVal = IncrementAudioStream();
rbAudioStreamChanged = SUCCEEDED(retVal);
}
}
return retVal;
}
BOOL CAudioRenderer::HasAudioFormatChanged()
{
BOOL bRet = FALSE;
if (m_pAudioFormat)
{
// Get the audio format from the CAudioFormat object
HXAudioFormat cAudioFormat1;
HX_RESULT retVal = m_pAudioFormat->GetAudioFormat(cAudioFormat1);
if (SUCCEEDED(retVal))
{
// Get the audio format from the current IHXAudioStream object
if (m_ppAudioStream &&
m_ulCurAudioStream < m_ulNumAudioStreams &&
m_ppAudioStream[m_ulCurAudioStream])
{
IHXAudioStream2* pStream2 = NULL;
retVal = m_ppAudioStream[m_ulCurAudioStream]->QueryInterface(IID_IHXAudioStream2,
(void**) &pStream2);
if (SUCCEEDED(retVal))
{
HXAudioFormat cAudioFormat2;
retVal = pStream2->GetAudioFormat(&cAudioFormat2);
if (SUCCEEDED(retVal))
{
// Check if the formats are different
if (cAudioFormat1.uChannels != cAudioFormat2.uChannels ||
cAudioFormat1.uBitsPerSample != cAudioFormat2.uBitsPerSample ||
cAudioFormat1.ulSamplesPerSec != cAudioFormat2.ulSamplesPerSec ||
cAudioFormat1.uMaxBlockSize > cAudioFormat2.uMaxBlockSize)
{
bRet = TRUE;
}
}
}
HX_RELEASE(pStream2);
}
}
}
return bRet;
}
HX_RESULT CAudioRenderer::IncrementAudioStream()
{
HX_RESULT retVal = HXR_FAIL;
if (m_ulCurAudioStream + 1 >= m_ulNumAudioStreams)
{
// We need to create a larger buffer to hold the
// audio stream pointers
UINT32 ulNewSize = m_ulNumAudioStreams * 2;
IHXAudioStream** ppAudioStream = new IHXAudioStream* [ulNewSize];
if (ppAudioStream)
{
// NULL out the buffer
memset((void*) ppAudioStream, 0, ulNewSize * sizeof(IHXAudioStream*));
// Copy the current pointers
memcpy((void*) ppAudioStream, /* Flawfinder: ignore */
(const void*) m_ppAudioStream,
m_ulNumAudioStreams * sizeof(IHXAudioStream*));
// Delete the old array
HX_VECTOR_DELETE(m_ppAudioStream);
// Assign the new one
m_ppAudioStream = ppAudioStream;
// Assign the new size
m_ulNumAudioStreams = ulNewSize;
}
}
if (m_ulCurAudioStream + 1 < m_ulNumAudioStreams)
{
// Remove the dry notification from the old stream
if (m_ppAudioStream[m_ulCurAudioStream])
{
IHXAudioStream2* pStream2 = NULL;
m_ppAudioStream[m_ulCurAudioStream]->QueryInterface(IID_IHXAudioStream2,
(void**) &pStream2);
if (pStream2)
{
// Get our own IHXDryNotification interface
IHXDryNotification* pDryNot = NULL;
QueryInterface(IID_IHXDryNotification, (void**) &pDryNot);
if (pDryNot)
{
pStream2->RemoveDryNotification(pDryNot);
}
HX_RELEASE(pDryNot);
}
HX_RELEASE(pStream2);
}
// Init the new stream
retVal = InitAudioStream(m_pHeader, &m_ppAudioStream[m_ulCurAudioStream + 1]);
if (SUCCEEDED(retVal))
{
// Increment the current audio stream index
m_ulCurAudioStream += 1;
}
}
return retVal;
}
BOOL CAudioRenderer::IsRebuffering() const
{
return (NO_TIME_SET != m_ulAudioWantedTime) ? TRUE : FALSE;
}
void CAudioRenderer::StartRebuffer(UINT32 ulAudioWantedTime)
{
if (m_pStream)
{
m_ulAudioWantedTime = ulAudioWantedTime;
m_pStream->ReportRebufferStatus(1,0);
}
}
void CAudioRenderer::EndRebuffer()
{
m_ulAudioWantedTime = NO_TIME_SET;
if (m_pStream)
{
m_pStream->ReportRebufferStatus(1,1);
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -