📄 mp3misc.cpp
字号:
/* ***** 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/string.h"
#include "hlxclib/stdlib.h"
#include "hxtypes.h"
#include "audinfo.h"
#include "mp3misc.h"
#include "dbcsutil.h"
#include "hxassert.h"
static char* StrNStr(const char* str1,
const char* str2,
size_t depth1,
size_t depth2);
eHeaderType CMp3Misc::GetHeaderType() {return m_eHeaderType;}
char* StrNStr(const char* str1,
const char* str2,
size_t depth1,
size_t depth2)
{
const char *tracer1;
const char *tracer2;
size_t depth_tracer1;
size_t depth_tracer2;
while(*str1)
{
for (tracer1 = str1,
tracer2 = str2,
depth_tracer1 = depth1,
depth_tracer2 = depth2;
(*tracer1 == *tracer2) && depth_tracer1 && (*tracer1 != '\0');
tracer1++, depth_tracer1--)
{
tracer2++;
depth_tracer2--;
if ((depth_tracer2 == 0) || (*tracer2 == '\0'))
{
return (char *) str1;
}
}
if ((depth_tracer1 == 1) || (*tracer1 == '\0'))
{
return NULL;
}
str1++;
depth1--;
}
return NULL;
}
CMp3Misc::CMp3Misc()
: m_pFmt(NULL)
, m_pTitle(NULL)
, m_pArtist(NULL)
, m_pAlbum(NULL)
, m_pYear(NULL)
, m_pGenre(NULL)
, m_nMetaOffset(0)
, m_nMetaRepeat(0)
, m_eHeaderType(eNone)
{
PrepareString(m_pTitle);
PrepareString(m_pArtist);
PrepareString(m_pAlbum);
PrepareString(m_pYear);
PrepareString(m_pGenre);
}
CMp3Misc::~CMp3Misc()
{
HX_VECTOR_DELETE(m_pTitle);
HX_VECTOR_DELETE(m_pArtist);
HX_VECTOR_DELETE(m_pAlbum);
HX_VECTOR_DELETE(m_pYear);
HX_VECTOR_DELETE(m_pGenre);
}
BOOL CMp3Misc::CheckForHeaders(UINT8 *pBuf,
UINT32 dwSize,
INT32 &lHeaderSize)
{
BOOL cRet = 1;
// Check for ID3v2 tags
UINT32 ulTag = (pBuf[0] << 16) |
(pBuf[1] << 8) |
pBuf[2];
#if defined(HELIX_FEATURE_MP3FF_LENIENT)
const char* pRIFF = "RIFF"; // length 4
const char* pWAVE = "WAVEfmt"; // length 7
const char* pData = "data"; // length 4
#endif /* #if defined(HELIX_FEATURE_MP3FF_LENIENT) */
if (ulTag == MP3_TAG_ID3)
{
m_eHeaderType = eID3v2;
lHeaderSize = GetTagLength(&pBuf[6], dwSize - 6) + 10;
GetId3v2Values(pBuf, dwSize, pBuf[3]);
}
// Check for ID3v1 tags
else if (ulTag == MP3_TAG_TAG)
{
m_eHeaderType = eID3v1;
GetId3v1Values(pBuf+3, dwSize-3);
lHeaderSize = -1;
}
#if defined(HELIX_FEATURE_MP3FF_SHOUTCAST)
// Check for ShoutCast server files
else
if ( ulTag == MP3_TAG_ICY ||
StrNStr((const char*)pBuf,"icy-metaint", dwSize, 11)
)
{
const char* pFull = "Server Full"; // length 11
const char* pICYMeta = "icy-metaint:"; // length 12
const char* pICYName = "icy-name:"; // length 9
m_eHeaderType = eShoutCast;
if (!memcmp(&pBuf[8], pFull, 11))
{
lHeaderSize = 0;
}
else
{
INT32 nSize = dwSize;
// Look for meta data repeat size
for (INT32 nTemp=0; nSize>=5; --nSize, ++nTemp)
{
// If we find the end, set m_nMetaOffset and stop
if (pBuf[nTemp] == 0x0D && pBuf[nTemp+1] == 0x0A)
{
if (pBuf[nTemp+2] == 0x0D && pBuf[nTemp+3] == 0x0A)
{
m_nMetaOffset = nTemp + 4;
break;
}
else if (pBuf[nTemp+3] == 0x0D && pBuf[nTemp+4] == 0x0A)
{
m_nMetaOffset = nTemp + 5;
break;
}
}
else
if (nSize > 12 &&
!memcmp(&pBuf[nTemp],pICYMeta,12))
{
nTemp += 12;
nSize -= 12;
// Skip non-numubers
while (nSize && (pBuf[nTemp] < 0x30 || pBuf[nTemp] > 0x39))
{
++nTemp;
--nSize;
}
m_nMetaRepeat = atoi((const char*)&pBuf[nTemp]);
// Find end of metaint data
while (pBuf[nTemp+1] != 0x0A && nSize > 4)
{
++nTemp;
--nSize;
}
}
else
if (nSize > 9 &&
!memcmp(&pBuf[nTemp],pICYName,9))
{
UINT32 nTitle, nLen;
nTemp += 9;
nTitle = nTemp;
while (pBuf[nTemp+1] != 0x0D && nSize > 4)
{
++nTemp;
--nSize;
}
// If the field is too INT32 for our buffer, truncate it.
nLen = min(nTemp-nTitle+1, 256);
dbcsStrCopy((char*)m_pTitle, (const char*)(pBuf+nTitle), nLen);
// Skip 0x0A
++nTemp;
--nSize;
}
}
HX_ASSERT(m_pFmt);
int nFrame = 0;
lHeaderSize = m_pFmt->ScanForSyncWord(pBuf, dwSize, nFrame);
if (lHeaderSize < 0)
cRet = 0;
}
}
#endif /* #if defined(HELIX_FEATURE_MP3FF_SHOUTCAST) */
#if defined(HELIX_FEATURE_MP3FF_LENIENT)
// Check for bogus RIFFWav MP3s
else
if (!memcmp(pBuf,pRIFF,4) &&
!memcmp(&pBuf[8],pWAVE,7))
{
m_eHeaderType = eOther;
// Default value
lHeaderSize = 72;
// Search for the 'data' section of the RIFF file.
// The data is 8 bytes after it.
UINT8 *pTemp = pBuf;
while (dwSize >= 4)
{
if (!memcmp(pTemp,pData,4))
{
lHeaderSize = pTemp - pBuf + 8;
break;
}
else
{
++pTemp;
--dwSize;
}
}
}
// Check for bogus stuff
else if (ulTag == MP3_TAG_jpg ||
ulTag == MP3_TAG_bmp)
{
m_eHeaderType = eOther;
lHeaderSize = -1;
}
#endif /* #if defined(HELIX_FEATURE_MP3FF_LENIENT) */
else
{
cRet = 0;
lHeaderSize = 0;
}
return cRet;
}
UINT8* CMp3Misc::GetId3Title(int &nLen)
{
return GetId3String(m_pTitle, nLen);
}
UINT8* CMp3Misc::GetId3Artist(int &nLen)
{
return GetId3String(m_pArtist, nLen);
}
UINT8* CMp3Misc::GetId3Album(int &nLen)
{
return GetId3String(m_pAlbum, nLen);
}
UINT8* CMp3Misc::GetId3Genre(int &nLen)
{
return GetId3String(m_pGenre, nLen);
}
UINT8* CMp3Misc::GetId3String(UINT8* pStr, int& nLen)
{
nLen = 0;
if (!pStr)
return NULL;
nLen = strlen((const char*)pStr);
return pStr;
}
void CMp3Misc::PrepareString(UINT8*& rpStr)
{
rpStr = new UINT8[257];
if (rpStr)
rpStr[0] = '\0';
}
void CMp3Misc::GetId3v1Values(UINT8 *pBuf,
UINT32 ulSize)
{
// Get Title
GetId3EntryAndAdvance(m_pTitle, eTitleLen, pBuf, ulSize);
// Get Artist
GetId3EntryAndAdvance(m_pArtist, eArtistLen, pBuf, ulSize);
// Get Album
GetId3EntryAndAdvance(m_pAlbum, eAlbumLen, pBuf, ulSize);
}
void CMp3Misc::GetId3EntryAndAdvance(UINT8* pStr,
UINT32 ulLen,
UINT8*& rpBuf,
UINT32& rulSize)
{
if (rulSize >= ulLen)
{
GetId3Entry(rpBuf, pStr, ulLen);
rpBuf += ulLen;
rulSize -= ulLen;
}
}
void CMp3Misc::GetId3Entry(UINT8 *pBuf,
UINT8 *pEntry,
int nEntryLen)
{
dbcsStrCopy((char*)pEntry, (const char*)pBuf, nEntryLen);
}
void CMp3Misc::GetId3v2Values(UINT8 *pBuf,
UINT32 ulSize,
int nVersion)
{
UINT32 count = 0;
UINT32 tagLength = 0;
UINT32 ulMinVer = pBuf[3];
UINT32 ulNotDone = 0x1F;
UINT8* ppStrArr[5] = {m_pTitle, m_pArtist, m_pAlbum, m_pYear, m_pGenre};
UINT32 ulVer2Tag[5] = {MP3_TAG_TT2, MP3_TAG_TP1, MP3_TAG_TAL, MP3_TAG_TYE, MP3_TAG_TCO};
UINT32 ulVer3Tag[5] = {MP3_TAG_TIT2, MP3_TAG_TPE1, MP3_TAG_TALB, MP3_TAG_TYER, MP3_TAG_TCON};
if (ulMinVer == 2 || ulMinVer == 3)
{
// Get the tag lookup table
UINT32* pTag = (ulMinVer == 2 ? &ulVer2Tag[0] : &ulVer3Tag[0]);
// a tag's size is given in a 4-byte field of the following form:
// 0xxxxxxx0xxxxxxx0xxxxxxx0xxxxxxx where x is a value of 0 or 1
// the 0's are there to avoid potential sync problems
// this means we have bitshift in multiples of 7 rather than 8
tagLength = GetTagLength(&pBuf[count+6], ulSize - count - 6);
// tagLength gives the length of the tag from the point it's specified, so we need
// to add 10 to make it the total from the start of the file
count += 10;
tagLength += 10;
tagLength = min(tagLength, ulSize);
while (count < tagLength && ulNotDone)
{
// Create a 32-bit tag
UINT32 ulTag = (pBuf[count] << 24) |
(pBuf[count+1] << 16) |
(pBuf[count+2] << 8) |
pBuf[count+3];
// If we are v3.2, then we only check three bytes
if (ulMinVer == 2) ulTag &= 0xFFFFFF00;
// Loop through and check for tags
for (UINT32 i = 0; i < 5; i++)
{
if (ulTag == pTag[i])
{
if (ulMinVer == 2)
{
GetId3v2_2Entry(pBuf, ppStrArr[i], count);
}
else
{
GetId3v2_3Entry(pBuf, ppStrArr[i], count);
}
ulNotDone &= ~(1 << i);
break;
}
}
count++;
}
}
}
void CMp3Misc::GetId3v2_2Entry(UINT8 *pBuf,
UINT8 *pEntry,
UINT32 &count)
{
// Data size is at byte 5 and includes the language encoding byte, so subtract one
// Data starts at byte 6 with lang byte, which we ignore
GetId3v2_XEntry(pBuf, pEntry, count, 5, 7, 6);
}
void CMp3Misc::GetId3v2_3Entry(UINT8 *pBuf,
UINT8 *pEntry,
UINT32 &count)
{
// Data size is at byte 7 and includes the language encoding byte, so subtract one
// Data starts at byte 10 with lang byte, which we ignore
GetId3v2_XEntry(pBuf, pEntry, count, 7, 11, 10);
}
void CMp3Misc::GetId3v2_XEntry(UINT8* pBuf, UINT8* pEntry, UINT32& count,
INT32 a, INT32 b, INT32 c)
{
// Data size is at byte a and includes the language encoding byte, so subtract one
UINT8 nFieldLen = pBuf[count + a]-1;
// Data starts at byte c with lang byte, which we ignore
count += dbcsStrCopy((char*)pEntry, (const char*)(pBuf+count+b), nFieldLen) + c;
}
UINT32 CMp3Misc::GetTagLength(UINT8* pBuf, UINT32 ulLen)
{
UINT32 ulRet = 0;
if (ulLen >= 4)
{
ulRet = ((pBuf[0] & 0x7F) << 21) +
((pBuf[1] & 0x7F) << 14) +
((pBuf[2] & 0x7F) << 7) +
(pBuf[3] & 0x7F);
}
return ulRet;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -