📄 mp3ff.cpp
字号:
//pHeaderObj->SetPropertyBuffer("OpaqueData", pHeader);
// Move back to the beginning of the file
m_pFileObj->Seek(MY_FILE_HEADER_START+m_ulFileOffset, FALSE);
// do not allow recording of MP3 audio at this time
//pHeaderObj->SetPropertyULONG32("Flags",HX_SAVE_ENABLED);
#if defined(HELIX_FEATURE_MP3FF_ONDEMANDMETAINFO)
// If additional meta info has been requested, then
// provide it here.
if (m_bAcceptMetaInfo)
{
if (m_bAllMetaInfo)
{
SetMetaInfo(pHeaderObj, "SrcCodec");
SetMetaInfo(pHeaderObj, "SrcBitRate");
SetMetaInfo(pHeaderObj, "SrcVBREnabled");
SetMetaInfo(pHeaderObj, "SrcInterleaved");
SetMetaInfo(pHeaderObj, "SrcSamplesPerSecond");
SetMetaInfo(pHeaderObj, "SrcBitsPerSample");
SetMetaInfo(pHeaderObj, "SrcNumChannels");
}
else
{
if (m_pMetaProps)
{
const char* pszProp = NULL;
UINT32 ulTmp = 0;
HX_RESULT rv = m_pMetaProps->GetFirstPropertyULONG32(pszProp, ulTmp);
while (SUCCEEDED(rv))
{
SetMetaInfo(pHeaderObj, pszProp);
rv = m_pMetaProps->GetNextPropertyULONG32(pszProp, ulTmp);
}
}
}
}
#endif /* #if defined(HELIX_FEATURE_MP3FF_ONDEMANDMETAINFO) */
// Notify the RMA core that header object is ready
m_pStatus->FileHeaderReady(status, pHeaderObj);
// Release the object since we are done with it
pHeaderObj->Release();
}
return HXR_OK;
}
///////////////////////////////////////////////////////////////////////////////
// IHXFileFormatObject::GetStreamHeader ref: hxformt.h
//
// This routine returns to the RMA core an object containing the stream
// header information for a particular stream. Several routines are actually
// required to complete the process due to the asynchronous nature of the
// RMA file system. This method is called (after the file header has been
// read) by the RMA core for each stream in the file format.
//
STDMETHODIMP CRnMp3Fmt::GetStreamHeader(UINT16 streamNo)
{
if ((m_State != Ready) || (streamNo != MY_STREAM_NO))
return HXR_UNEXPECTED;
if (eHXUnknown == m_Info.eType)
return HXR_INVALID_FILE;
m_State = GetStreamHeaderSeekPending;
MyCreateStreamHeaderObj_v(HX_STATUS_OK, NULL);
return HXR_OK;
}
///////////////////////////////////////////////////////////////////////////////
// CRnMp3Fmt::MyCreateStreamHeaderObj_v ref: filefmt1.h
//
// This routine creates a "stream header" object and passes it off to the
// RMA core, which in turn transports it to the renderer. This object must
// contain certain properties required to characterize the stream. Any
// additional stream information read from the file can also be placed in
// this object. This method is called after the stream header data from the
// file has been completely read.
void
CRnMp3Fmt::MyCreateStreamHeaderObj_v(HX_RESULT status,
IHXBuffer* pStreamHeader)
{
m_State = Ready;
// Create new object containing the stream header data
IHXValues* pStreamHeaderObj = NULL;
m_pClassFactory->CreateInstance(CLSID_IHXValues,
(void**)&pStreamHeaderObj);
if (pStreamHeaderObj != NULL)
{
// REQUIRED Properties:
pStreamHeaderObj->SetPropertyULONG32("StreamNumber", MY_STREAM_NO);
pStreamHeaderObj->SetPropertyULONG32("AvgBitRate", m_Info.ulBitRate);
pStreamHeaderObj->SetPropertyULONG32("SampleRate", m_ulMaxSampRate);
pStreamHeaderObj->SetPropertyULONG32("NumChannels", m_nChannels);
UINT32 ulPreRoll = 1000;
if (m_pFileObj->Advise(HX_FILEADVISE_RANDOMACCESS) == HXR_ADVISE_PREFER_LINEAR)
ulPreRoll += 2000;
pStreamHeaderObj->SetPropertyULONG32("Preroll", ulPreRoll);
m_Info.nPacketSize = min(m_Info.nPacketSize, kReadSize);
double dDur;
dDur = m_ulFileSize / (double)(m_Info.ulBitRate>>3) * 1000.0;
pStreamHeaderObj->SetPropertyULONG32("Duration", (UINT32)dDur);
pStreamHeaderObj->SetPropertyULONG32("MaxPacketSize", 1024);
pStreamHeaderObj->SetPropertyULONG32("AvgPacketSize", m_Info.nPacketSize);
pStreamHeaderObj->SetPropertyULONG32("StartTime", 0);
#if defined (MPA_FMT_DRAFT00)
// MPA is 14 Dynamic is > 96
if (m_bRtp)
{
pStreamHeaderObj->SetPropertyULONG32("RTPPayloadType", 14);
pStreamHeaderObj->SetPropertyULONG32("RTPTimestampConversionFactor", 90);
pStreamHeaderObj->SetPropertyULONG32("HXTimestampConversionFactor", 1);
}
#endif //MPA_FMT_DRAFT00
// "MimeType": this stream's MIME type. This associates this
// stream type with a particular renderer.
const char* pszTmp = MY_LOCAL_MIME_TYPE;
#if defined (MPA_FMT_DRAFT00)
if (m_bStreaming)
{
if(m_bRtp)
pszTmp = MY_RTP_MIME_TYPE;
else
pszTmp = MY_STREAM_MIME_TYPE;
}
#endif //MPA_FMT_DRAFT00
SetCStringProperty(pStreamHeaderObj, "MimeType", pszTmp, m_pContext);
// Set the ASM rule book
char* pRuleBook = new char[sizeof(char) * 100];
if (pRuleBook)
{
// Create the string
if (m_bRtp)
{
SafeSprintf(pRuleBook, 100,
"marker=0, AverageBandwidth=%ld, Priority=9, "
"timestampdelivery=true;",
m_Info.ulBitRate);
}
else
{
SafeSprintf(pRuleBook, 100,
"AverageBandwidth=%ld, AverageBandwidthStd=0, "
"Priority=9;",
m_Info.ulBitRate);
}
// Set it into the stream header
SetCStringProperty(pStreamHeaderObj, "ASMRuleBook",
(const char*) pRuleBook, m_pContext);
}
HX_VECTOR_DELETE(pRuleBook);
#if defined(HELIX_FEATURE_SERVER)
if (!m_bLicensed)
{
if (m_pError)
{
m_pError->Report(HXLOG_ALERT, HXR_NOT_LICENSED,
0, "This server is NOT licensed to deliver MPEG Audio "
"streams. A Player attempting to access MPEG Audio content "
"has been disconnected. Please contact RealNetworks to "
"obtain a license for this feature.\n", NULL);
}
status = HXR_NOT_LICENSED;
}
#endif /* #if defined(HELIX_FEATURE_SERVER) */
// Notify the RMA core that stream header object is ready
m_pStatus->StreamHeaderReady(status, pStreamHeaderObj);
// Release the object since we are done with it
pStreamHeaderObj->Release();
}
}
///////////////////////////////////////////////////////////////////////////////
// IHXFileFormatObject::GetPacket ref: hxformt.h
//
// This routine returns to the RMA core an object containing the packet
// data for a particular stream. Several routines are actually required to
// complete the process due to the asynchronous nature of the RMA file
// system. This method is called by the RMA core each time it needs another
// packet.
//
STDMETHODIMP
CRnMp3Fmt::GetPacket(UINT16 streamNo)
{
// with asyncfsys it is possible that a GetPacket() call is outstanding
// even after Close() has been called. so check for m_bClosed for that
// case (b'cuz m_pFileObj is released inside Close())
if (m_bClosed || (m_State != Ready) || (streamNo != MY_STREAM_NO))
return HXR_UNEXPECTED;
// Read our first buffer
if (!m_ReadBuf.pReadBuffer)
{
m_bNeedPacket = 1;
m_State = GetPacketReadPending;
m_pFileObj->Read(kReadSize);
return HXR_OK;
}
return MyCreatePacketObj_hr(m_ReadBuf.status, &m_ReadBuf);
}
///////////////////////////////////////////////////////////////////////////////
// CRnMp3Fmt::MyCreatePacketObj_hr ref: filefmt1.h
//
// This routine creates a packet object and passes it off to the RMA core,
// which in turn transports it to the renderer. The object contains the
// packet data read from the file along with the time when it should be
// delivered to the renderer. If there are no more packets available for
// this stream, the RMA core is notified. This method is called after the
// packet data from the file has been read.
//
HX_RESULT
CRnMp3Fmt::MyCreatePacketObj_hr(HX_RESULT status,
tReadBuffer* pPacketData)
{
m_State = Ready;
if (status == HX_STATUS_OK)
{
// Create new object containing the packet data
IHXPacket* pPacketObj = NULL;
m_pClassFactory->CreateInstance(CLSID_IHXPacket,
(void**)&pPacketObj);
if (pPacketObj != NULL)
{
// Fill in the packet
UINT32 deliveryTime = m_ulNextPacketDeliveryTime;
UINT16 streamNo = MY_STREAM_NO;
UINT8 ASMFlags = HX_ASM_SWITCH_ON | HX_ASM_SWITCH_OFF;
UINT16 ASMRuleNo = 0;
IHXBuffer* pPacketBuffer = NULL;
TOP:
int nSyncSize = 0;
UCHAR *pModFrameStart = NULL;
// Extract an audio frame from the read buffer
pModFrameStart = GetMP3Frame_p(pPacketData, nSyncSize);
// Handle read buffer wraps
if (!pModFrameStart)
{
UCHAR bNoData = m_bEOF;
// Check for mp3 streams w/ garbage on the end (non-SureStream)
// If the next 2 bytes are not a sync word, stop playback
if (pPacketData->dwBytesRemaining >= 2)
{
if (pPacketData->pBuffer[0] != 0xFF ||
!m_pMp3Fmt->IsValidSyncWord(pPacketData->pBuffer[1]))
{
// The rest of this buffer does not contain
// a syncword, so skip the buffer
m_bCheckBadData = 1;
}
else
{
// We have a syncword, but just not
// enough data in the buffer to complete the
// frame. So this is NOT bad data, just another
// read is necessary
m_bCheckBadData = 0;
}
}
if (bNoData)
{
#if defined (MPA_FMT_DRAFT00)
// Check for last frame when streaming - we buffer one frame
if (m_bStreaming && !m_bRtp)
{
UINT32 ulTemp = 0;
pModFrameStart = m_pFmtBuf->GetReadPointer(ulTemp);
bNoData = (ulTemp == 0);
}
#endif //MPA_FMT_DRAFT00
if (bNoData)
{
HX_RELEASE(pPacketData->pReadBuffer);
pPacketData->dwBytesRemaining = 0;
m_bEOF = TRUE;
m_pStatus->StreamDone(0);
pPacketObj->Release();
if (pPacketBuffer)
pPacketBuffer->Release();
return HXR_OK;
}
}
// If we need more data, seek back the number of bytes
// we have left and read more.
if (!pModFrameStart)
{
m_bNeedPacket = 1;
m_State = GetPacketSeekPending;
// XXXMEH - if m_bCheckBadData == 0, then we
// found a frame, but it is just incomplete.
// Therefore, we need to read some more to
// complete the frame. This is a normal occurrence.
// We will seek to the end of the last frame we
// found and do a read. This should allow us to
// get the entire frame in the read buffer.
// However, if m_bCheckBadData == 1, then we
// didn't find a frame in the rest of the buffer
// we currently have. Therefore, don't want to
// seek to the end of the last frame, we want to
// seek to the end of where we just read, thus
// skipping this data in which we know no frames
// are present.
if (!m_bCheckBadData)
{
m_ulBytesRead -= pPacketData->dwBytesRemaining;
}
else
{
// Add to the amount of bad data we're skipping
m_ulGarbageBytesRead += pPacketData->dwBytesRemaining;
}
pPacketData->dwBytesRemaining = 0;
HX_RELEASE(pPacketData->pReadBuffer);
if (m_ulGarbageBytesRead < MAX_GARBAGE_BYTES)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -