// ------------------------------------------------------------------------------------------------
#include <IIEP_Def.H>
#include <CORE/SourceFilter_MP3.H>
// ------------------------------------------------------------------------------------------------
extern BYTE g_ucWaveHeader[WAVE_HEADER_LENGTH];
// ------------------------------------------------------------------------------------------------
// name: CMP3Stream()
// desc:
// ------------------------------------------------------------------------------------------------
static size_t read_sub(int fd, void *pBuf, size_t nSize)
{
if (fd)
{
IIEP::CMP3Stream *pcoStream = (IIEP::CMP3Stream *) fd;
return pcoStream -> ReadFile(pBuf, nSize);
}
return 0;
}
// ------------------------------------------------------------------------------------------------
// name: CMP3Stream()
// desc:
// ------------------------------------------------------------------------------------------------
static off_t seek_sub(int fd, off_t nOffset, int iOrigin)
{
if (fd)
{
IIEP::CMP3Stream *pcoStream = (IIEP::CMP3Stream *) fd;
return pcoStream -> SeekFile(nOffset, iOrigin);
}
return 0;
}
// ------------------------------------------------------------------------------------------------
// name: CMP3Stream()
// desc:
// ------------------------------------------------------------------------------------------------
IIEP::CMP3Stream::CMP3Stream(void)
{
m_dwKBPerSec = INFINITE;
m_dwTimeStart = 0;
m_pcoMH = 0;
m_dwTotalBlocks = 0;
m_dwAlignment = 4;
m_dwLength = 0;
m_dwPosition = 0;
memcpy(m_ucHeader, g_ucWaveHeader, WAVE_HEADER_LENGTH);
m_dwDataBufPos = 0;
m_dwDataBufLen = 0;
m_bSeekFlag = false;
}
// ------------------------------------------------------------------------------------------------
// name: ~CMP3Stream()
// desc:
// ------------------------------------------------------------------------------------------------
IIEP::CMP3Stream::~CMP3Stream(void)
{
Close();
}
// ------------------------------------------------------------------------------------------------
// name: SetSeekFlag()
// desc: called by IIEP::CPlayer::SetMediaPosition(QWORD qwPosition)
// ------------------------------------------------------------------------------------------------
void IIEP::CMP3Stream::SetSeekFlag(void)
{
m_bSeekFlag = true;
}
// ------------------------------------------------------------------------------------------------
// name: Open()
// desc:
// ------------------------------------------------------------------------------------------------
bool IIEP::CMP3Stream::Open(const WORD *pcwsFileName, CMediaType &coMT, DWORD dwKBPerSec)
{
Close();
//
if (MPG123_OK != mpg123_init())
{
goto OPEN_FAIL;
}
m_pcoMH = mpg123_new(NULL, NULL);
if (0 == m_pcoMH)
{
goto OPEN_FAIL;
}
if (false == OpenFile(pcwsFileName))
{
goto OPEN_FAIL;
}
if (MPG123_OK != mpg123_replace_reader(m_pcoMH, read_sub, seek_sub))
{
goto OPEN_FAIL;
}
if (MPG123_OK != mpg123_open_fd(m_pcoMH, (int) this))
{
goto OPEN_FAIL;
}
long nRate;
int iChannels;
int iEncoding;
if (MPG123_OK != mpg123_getformat(m_pcoMH, &nRate, &iChannels, &iEncoding))
{
goto OPEN_FAIL;
}
// Ensure that this output format will not change (it could, when we allow it)
mpg123_format_none(m_pcoMH);
mpg123_format(m_pcoMH, nRate, iChannels, iEncoding);
// scan & get total samples
mpg123_scan(m_pcoMH);
off_t nOff = mpg123_length(m_pcoMH);
if (nOff < 0)
{
goto OPEN_FAIL;
}
m_dwTotalBlocks = (DWORD) nOff;
// set other format
m_dwAlignment = iChannels * 2;
if (0 == m_dwAlignment)
{
m_dwAlignment = 4;
}
m_dwLength = m_dwTotalBlocks * m_dwAlignment + WAVE_HEADER_LENGTH;
WORD wChannels = (WORD ) iChannels;
DWORD dwSamplesPerSec = (DWORD) nRate;
DWORD dwAvgBytesPerSec = m_dwAlignment * dwSamplesPerSec;
WORD wBlockAlign = (WORD ) m_dwAlignment;
WORD wBitsPerSample = 16;
PWORD pwData;
PDWORD pdwData;
pdwData = (PDWORD) &m_ucHeader[0x04];
*pdwData = m_dwTotalBlocks * m_dwAlignment + WAVE_HEADER_LENGTH - 8;
pwData = (PWORD) &m_ucHeader[0x16];
*pwData = wChannels;
pdwData = (PDWORD) &m_ucHeader[0x18];
*pdwData = dwSamplesPerSec;
pdwData = (PDWORD) &m_ucHeader[0x1C];
*pdwData = dwAvgBytesPerSec;
pwData = (PWORD) &m_ucHeader[0x20];
*pwData = wBlockAlign;
pwData = (PWORD) &m_ucHeader[0x22];
*pwData = wBitsPerSample;
pdwData = (PDWORD) &m_ucHeader[0x28];
*pdwData = m_dwTotalBlocks * m_dwAlignment;
coMT.majortype = MEDIATYPE_Stream;
coMT.subtype = MEDIASUBTYPE_WAVE;
SetPointer(0);
m_dwTimeStart = ::timeGetTime();
return true;
OPEN_FAIL:
Close();
return false;
}
// ------------------------------------------------------------------------------------------------
// name: Close()
// desc:
// ------------------------------------------------------------------------------------------------
void IIEP::CMP3Stream::Close(void)
{
m_coFileIn.Close();
if (m_pcoMH)
{
mpg123_close (m_pcoMH);
mpg123_delete(m_pcoMH);
m_pcoMH = 0;
}
mpg123_exit();
m_dwTimeStart = 0;
m_dwTotalBlocks = 0;
m_dwLength = 0;
m_dwPosition = 0;
m_bSeekFlag = false;
m_nDataBegin = 0;
m_nDataTotal = 0;
}
// ------------------------------------------------------------------------------------------------
// name: SetPointer()
// desc:
// ------------------------------------------------------------------------------------------------
HRESULT IIEP::CMP3Stream::SetPointer(LONGLONG llPos)
{
if (llPos < 0 || llPos > (LONGLONG) m_dwLength)
{
return S_FALSE;
}
m_dwPosition = (DWORD) llPos;
if (llPos < WAVE_HEADER_LENGTH)
{
mpg123_seek(m_pcoMH, 0, SEEK_SET);
m_dwDataBufLen = 0;
}
else
{
if (m_bSeekFlag && m_dwAlignment > 0)
{
m_bSeekFlag = false;
mpg123_seek(m_pcoMH, (long) (m_dwPosition - WAVE_HEADER_LENGTH) / m_dwAlignment, SEEK_SET);
m_dwDataBufLen = 0;
}
}
return S_OK;
}
// ------------------------------------------------------------------------------------------------
// name: Read()
// desc:
// ------------------------------------------------------------------------------------------------
HRESULT IIEP::CMP3Stream::Read(PBYTE pucBuffer,
DWORD dwBytesToRead,
BOOL bAlign,
LPDWORD pdwBytesRead)
{
if (0 == m_pcoMH) return S_FALSE;
CAutoLock lck(&m_csLock);
DWORD dwReadLength;
// wait until the bytes are here
DWORD dwTime = ::timeGetTime();
if (m_dwPosition + dwBytesToRead > m_dwLength)
{
dwReadLength = m_dwLength - m_dwPosition;
}
else
{
dwReadLength = dwBytesToRead;
}
DWORD dwTimeToArrive = (m_dwPosition + dwReadLength) / m_dwKBPerSec;
if (dwTime - m_dwTimeStart < dwTimeToArrive)
{
::Sleep(dwTimeToArrive - dwTime + m_dwTimeStart);
}
// read data
DWORD dwRead = 0;
if (m_dwPosition < WAVE_HEADER_LENGTH)
{
DWORD dwPatchLen = WAVE_HEADER_LENGTH - m_dwPosition;
if (dwReadLength < dwPatchLen)
{
dwPatchLen = dwReadLength;
}
memcpy(pucBuffer, m_ucHeader + m_dwPosition, dwPatchLen);
m_dwPosition += dwPatchLen;
dwReadLength -= dwPatchLen;
pucBuffer += dwPatchLen;
dwRead += dwPatchLen;
}
if (dwReadLength > 0)
{
while (m_dwDataBufLen < dwReadLength)
{
if (m_dwDataBufLen > 0)
{
memcpy(pucBuffer, m_ucDataBuffer + m_dwDataBufPos, m_dwDataBufLen);
dwRead += m_dwDataBufLen;
dwReadLength -= m_dwDataBufLen;
pucBuffer += m_dwDataBufLen;
m_dwDataBufPos += m_dwDataBufLen;
m_dwDataBufLen = 0;
}
if (DecodeFrame() == false) break;
}
if (dwReadLength > 0)
{
if (m_dwDataBufLen >= dwReadLength)
{
memcpy(pucBuffer, m_ucDataBuffer + m_dwDataBufPos, dwReadLength);
dwRead += dwReadLength;
m_dwDataBufPos += dwReadLength;
m_dwDataBufLen -= dwReadLength;
}
}
}
m_dwPosition += dwRead;
*pdwBytesRead = dwRead;
return S_OK;
}
// ------------------------------------------------------------------------------------------------
// name: Size()
// desc:
// ------------------------------------------------------------------------------------------------
LONGLONG IIEP::CMP3Stream::Size(LONGLONG *pSizeAvailable)
{
// LONGLONG llCurrentAvailable = Int32x32To64((::timeGetTime() - m_dwTimeStart), m_dwKBPerSec);
LONGLONG llLength = (LONGLONG) m_dwTotalBlocks * m_dwAlignment + WAVE_HEADER_LENGTH;
if (pSizeAvailable)
{
// *pSizeAvailable = min(llLength, llCurrentAvailable);
*pSizeAvailable = llLength;
}
return llLength;
}
// ------------------------------------------------------------------------------------------------
// name: Alignment()
// desc:
// ------------------------------------------------------------------------------------------------
DWORD IIEP::CMP3Stream::Alignment(void)
{
return m_dwAlignment;
}
// ------------------------------------------------------------------------------------------------
// name: Lock()
// desc:
// ------------------------------------------------------------------------------------------------
void IIEP::CMP3Stream::Lock(void)
{
m_csLock.Lock();
}
// ------------------------------------------------------------------------------------------------
// name: Unlock()
// desc:
// ------------------------------------------------------------------------------------------------
void IIEP::CMP3Stream::Unlock(void)
{
m_csLock.Unlock();
}
// ------------------------------------------------------------------------------------------------
// name: DecodeFrame()
// desc:
// ------------------------------------------------------------------------------------------------
bool IIEP::CMP3Stream::DecodeFrame(void)
{
if (0 == m_dwDataBufLen)
{
m_dwDataBufPos = 0;
}
if (m_dwDataBufPos + m_dwDataBufLen + MP3_DECODE_LEN > MP3_DECODE_BUF_LEN)
{
return true;
}
size_t nBytesRead = 0;
mpg123_read(m_pcoMH, m_ucDataBuffer + m_dwDataBufPos + m_dwDataBufLen, MP3_DECODE_LEN, &nBytesRead);
m_dwDataBufLen += (DWORD) nBytesRead;
return (m_dwDataBufLen > 0);
}
// ------------------------------------------------------------------------------------------------
// name: OpenFile()
// desc:
// ------------------------------------------------------------------------------------------------
bool IIEP::CMP3Stream::OpenFile(const WORD *pcwsFileName)
{
if (m_coFileIn.Open(pcwsFileName))
{
m_nDataBegin = 0;
m_nDataTotal = m_coFileIn.GetFileSize();
// check ID3 Tag V2.x
BYTE ucBuffer[8];
bool bFound;
TRY_ID3_AGAIN:
bFound = false;
if (6 == m_coFileIn.Read(ucBuffer, 6))
{
if (ucBuffer[0] == 'I' &&
ucBuffer[1] == 'D' &&
ucBuffer[2] == '3')
{
bFound = true;
DWORD dwHeaderSize;
if (m_coFileIn.ReadInverseDW(dwHeaderSize))
{
dwHeaderSize = ((dwHeaderSize & 0x7F000000) >> 3) +
((dwHeaderSize & 0x007F0000) >> 2) +
((dwHeaderSize & 0x00007F00) >> 1) +
((dwHeaderSize & 0x0000007F) );
dwHeaderSize += 10;
m_nDataBegin += dwHeaderSize;
m_nDataTotal -= dwHeaderSize;
if (ucBuffer[3] >= 0x03) // ID3 Tag version >= 2.3
{
if (ucBuffer[5] & 0x40) // has extended header
{
if (m_coFileIn.ReadInverseDW(dwHeaderSize))
{
dwHeaderSize = ((dwHeaderSize & 0x7F000000) >> 3) +
((dwHeaderSize & 0x007F0000) >> 2) +
((dwHeaderSize & 0x00007F00) >> 1) +
((dwHeaderSize & 0x0000007F) );
dwHeaderSize += 4;
m_nDataBegin += dwHeaderSize;
m_nDataTotal -= dwHeaderSize;
}
}
}
}
}
}
SeekFile(0, SEEK_SET);
if (bFound)
{
goto TRY_ID3_AGAIN;
}
/*
m_coFileIn.Read(ucBuffer, 2);
if (ucBuffer[0] != 0xFF || ucBuffer[1] < 0xF0)
{
m_nDataBegin ++;
m_nDataTotal --;
SeekFile(0, SEEK_SET);
goto TRY_ID3_AGAIN;
}
SeekFile(0, SEEK_SET);
*/
return true;
}
return false;
}
// ------------------------------------------------------------------------------------------------
// name: ReadFile()
// desc:
// ------------------------------------------------------------------------------------------------
DWORD IIEP::CMP3Stream::ReadFile(PVOID pBuf, DWORD dwLen)
{
return m_coFileIn.Read(pBuf, dwLen);
}
// ------------------------------------------------------------------------------------------------
// name: SeekFile()
// desc:
// ------------------------------------------------------------------------------------------------
long IIEP::CMP3Stream::SeekFile(long nOffset, int iOrigin)
{
switch (iOrigin)
{
case SEEK_SET:
if (nOffset < 0 || nOffset >= m_nDataTotal) return -1;
break;
case SEEK_CUR:
nOffset += (m_coFileIn.GetReadPosition() - m_nDataBegin);
if (nOffset < 0 || nOffset >= m_nDataTotal) return -1;
break;
case SEEK_END:
nOffset = m_nDataTotal - 1 + nOffset;
if (nOffset < 0 || nOffset >= m_nDataTotal) return -1;
break;
}
if (m_coFileIn.Seek(nOffset + m_nDataBegin, SEEK_SET))
{
return nOffset;
}
return -1;
}
// ------------------------------------------------------------------------------------------------