blob: 419cabd4c0fe566ea4652a3218f0f75b594dc01f [file] [log] [blame]
/* ------------------------------------------------------------------
* Copyright (C) 1998-2009 PacketVideo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied.
* See the License for the specific language governing permissions
* and limitations under the License.
* -------------------------------------------------------------------
*/
// -*- c++ -*-
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
//
// M P 3 P A R S E R
//
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
/**
* @file mp3parser.cpp
* @brief This file contains the implementation of the actual MP3
* file parser.
*/
/***********************************************************************
* Include Files
***********************************************************************/
#include "mp3parser.h"
#include "mp3fileio.h"
#include "oscl_mem.h"
#include "oscl_stdstring.h"
#include "oscl_utf8conv.h"
/***********************************************************************
* Constant Defines
***********************************************************************/
// Maximum debug message length
#define KMAXMSGSIZE 1024
#define KMAX_MP3FRAME_LENGTH_IN_BYTES 2884
// Initial search range, resetted to the file size once valid mp3
// frame is found
#define KMAX_INITIAL_SEARCH_FILE_SIZE_IN_BYTES 500000
// If the Xing header reports a size that is smaller than the file length
// by this much, consider the Xing header invalid.
#define XING_SIZE_FUZZINESS 0.90
/***********************************************************************
* Global constant definitions
***********************************************************************/
/***********************************************************************
* XING VBR Header Constants
***********************************************************************/
static const uint32 FRAMES_FLAG = 0x0001;
static const uint32 BYTES_FLAG = 0x0002;
static const uint32 TOC_FLAG = 0x0004;
static const uint32 VBR_SCALE_FLAG = 0x0008;
static const uint32 FRAMES_AND_BYTES = (FRAMES_FLAG | BYTES_FLAG);
static const uint8 MPEG_LAYER_I = 0x03;
static const uint8 MPEG_LAYER_II = 0x02;
static const uint8 MPEG_LAYER_III = 0X01;
static const uint8 CHANNEL_MODE_JOINT_STEREO = 0x01;
static const uint8 CHANNEL_MODE_STEREO = 0x00;
static const uint8 CHANNEL_MODE_DUAL_CHANNEL = 0x02;
static const uint8 CHANNEL_MODE_MONO = 0x03;
static const uint8 FRAME_VESION_MPEG_1 = 0x03;
static const uint8 FRAME_VESION_MPEG_2 = 0x02;
static const uint8 FRAME_VESION_MPEG_2_5 = 0x00;
static const uint8 MP3_FRAME_HEADER_SIZE = 0x04;
static const uint32 MP3_FIRST_FRAME_SIZE = 128;
/***********************************************************************
* End XING VBR Header Constants
***********************************************************************/
/***********************************************************************
* MP3 Frame Header Constants
***********************************************************************/
// MP3 Frame Header Format
// AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM
// Bits 31-21 (A)
static const uint32 FRAME_SYNC_MASK = 0xFFE00000;
static const uint32 FRAME_SYNC_SHIFT = 21;
// Bits 20-19 (B)
static const uint32 FRAME_VER_ID_MASK = 0x00180000;
static const uint32 FRAME_VER_ID_SHIFT = 19;
// Bits 18-17 (C)
static const uint32 FRAME_LAYER_ID_MASK = 0x00060000;
static const uint32 FRAME_LAYER_ID_SHIFT = 17;
// Bits 16 (D)
static const uint32 FRAME_PROT_MASK = 0x00010000;
static const uint32 FRAME_PROT_SHIFT = 16;
// Bits 15-12 (E)
static const uint32 FRAME_BR_INDEX_MASK = 0x0000F000;
static const uint32 FRAME_BR_INDEX_SHIFT = 12;
// Bits 11-10 (F)
static const uint32 FRAME_SR_FREQ_MASK = 0x00000C00;
static const uint32 FRAME_SR_FREQ_SHIFT = 10;
// Bits 9 (G)
static const uint32 FRAME_PADDING_MASK = 0x00000200;
static const uint32 FRAME_PADDING_SHIFT = 9;
// Bits 8 (H)
static const uint32 FRAME_PRIVATE_MASK = 0x00000100;
static const uint32 FRAME_PRIVATE_SHIFT = 8;
// Bits 7-6 (I)
static const uint32 FRAME_CH_MODE_MASK = 0x000000C0;
static const uint32 FRAME_CH_MODE_SHIFT = 6;
// Bits 5-4 (J)
static const uint32 FRAME_MODE_EXTN_MASK = 0x00000030;
static const uint32 FRAME_MODE_EXTN_SHIFT = 4;
// Bits 3 (K)
static const uint32 FRAME_COPYRIGHT_MASK = 0x00000008;
static const uint32 FRAME_COPYRIGHT_SHIFT = 3;
// Bits 2 (L)
static const uint32 FRAME_ORIGINAL_MASK = 0x00000004;
static const uint32 FRAME_ORIGINAL_SHIFT = 2;
// Bits 1-0 (M)
static const uint32 FRAME_EMPHASIS_MASK = 0x00000003;
static const uint32 FRAME_EMPHASIS_SHIFT = 0;
/***********************************************************************
* End MP3 Frame Header Constants
***********************************************************************/
/***********************************************************************
* BitRate Index Table (Version 1)
***********************************************************************/
static const int32 brIndexTableV1[4][16] =
{
// RESERVED
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
// Version 1, Layer III
{0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0},
// Version 1, Layer II
{0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0},
// Version 1, Layer I
{0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0}
};
/***********************************************************************
* BitRate Index Table (Versions 2 and 2.5)
***********************************************************************/
static const int32 brIndexTableV2[4][16] =
{
// RESERVED
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
// Version 2, Layer III
{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0},
// Version 2, Layer II
{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0},
// Version 2, Layer I
{0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0}
};
/***********************************************************************
* SampleRate Index Table
***********************************************************************/
static const int32 srIndexTable[] =
{
// MPEG 2.5
11025, 12000, 8000, 0,
// RESERVED
0, 0, 0, 0,
// MPEG 2
22050, 24000, 16000, 0,
// MPEG 1
44100, 48000, 32000, 0
};
/***********************************************************************
* FrameRate Index Table 10 * sample rate / samples per frame
***********************************************************************/
static const int32 frIndexTable[4][3] =
{
// MPEG Version 2.5
{ 385 / 2, 210, 278 / 2 },
// RESERVED
{ 0, 0, 0 },
// MPEG Versions 2
{ 385, 418, 278 },
// MPEG Version 1
{ 385, 418, 278 }
};
/***********************************************************************
* Samples Per Frame Index Table
***********************************************************************/
static const int32 spfIndexTable[4][4] =
{
// MPEG 2.5
{0, 576, 1152, 384},
// RESERVED
{0, 0, 0, 0},
// MPEG 2
{0, 576, 1152, 384},
// MPEG 1
{0, 1152, 1152, 384}
};
/***********************************************************************
* FUNCTION: SwapFileToHostByteOrderInt32
* DESCRIPTION: Swaps the bytes in an integer to the correct host
* byte order
* INPUT/OUTPUT PARAMETERS:
* RETURN VALUE:
* SIDE EFFECTS:
***********************************************************************/
static uint32 SwapFileToHostByteOrderInt32(uint8 * pBuf2)
{
uint32 temp;
uint8 * pBuf1 = (uint8 *) & temp;
#if (OSCL_BYTE_ORDER_LITTLE_ENDIAN)
pBuf1[3] = pBuf2[0];
pBuf1[2] = pBuf2[1];
pBuf1[1] = pBuf2[2];
pBuf1[0] = pBuf2[3];
#elif (OSCL_BYTE_ORDER_BIG_ENDIAN)
pBuf1[3] = pBuf2[3];
pBuf1[2] = pBuf2[2];
pBuf1[1] = pBuf2[1];
pBuf1[0] = pBuf2[0];
#endif
return temp;
}
/***********************************************************************
* FUNCTION: ReadBuffer
* DESCRIPTION: Read data from buffer
* INPUT/OUTPUT PARAMETERS:
* RETURN VALUE:
* SIDE EFFECTS:
***********************************************************************/
static uint32 ReadBuffer(uint8 * pBuf2, int32 length, int32 &pos)
{
int32 i, b, number = 0;
if (pBuf2)
{
for (i = 0; i < length ; i++)
{
b = length - 1 - i ;
number = number | (uint32)(pBuf2[pos+i] & 0xff) << (8 * b);
}
pos += length ;
return number;
}
else
{
return 0;
}
}
/***********************************************************************
* FUNCTION: SwapFileToHostByteOrderInt16
* DESCRIPTION: Swaps the bytes in an integer to the correct host byte order
* INPUT/OUTPUT PARAMETERS:
* RETURN VALUE:
* SIDE EFFECTS:
***********************************************************************/
static uint16 SwapFileToHostByteOrderInt16(uint8 * pBuf2)
{
uint16 temp;
uint8 * pBuf1 = (uint8 *) & temp;
oscl_memcpy(&temp, pBuf2, 2);
#if (OSCL_BYTE_ORDER_LITTLE_ENDIAN)
pBuf1[1] = pBuf2[0];
pBuf1[0] = pBuf2[1];
#elif (OSCL_BYTE_ORDER_BIG_ENDIAN)
pBuf1[1] = pBuf2[1];
pBuf1[0] = pBuf2[0];
#endif
return temp;
}
/***********************************************************************
* FUNCTION: Constructor
* DESCRIPTION:
* INPUT/OUTPUT PARAMETERS:
* RETURN VALUE:
* SIDE EFFECTS:
***********************************************************************/
MP3Parser::MP3Parser(PVFile* aFileHandle)
{
fp = aFileHandle;
// initialize all member variables
iLocalFileSize = 0;
iFileSizeFromExternalSource = 0;
iInitSearchFileSize = KMAX_INITIAL_SEARCH_FILE_SIZE_IN_BYTES;
iLocalFileSizeSet = false;
iEnableCrcCalc = false;
mp3Type = EVBRType;
iTagSize = 0;
StartOffset = 0;
iCurrFrameNumber = 0;
ConfigSize = 0;
iNumberOfFrames = 0;
// average bitrate values
iAvgBitrateInbps = 0;
iAvgBitrateInbpsFromRandomScan = 0;
iAvgBitrateInbpsFromCompleteScan = 0;
iScannedFrameCount = 0;
// scan completion flag
iDurationScanComplete = false;
// duration values from various sources
iClipDurationInMsec = 0;
iClipDurationFromEstimation = 0;
iClipDurationComputed = 0;
iClipDurationFromVBRIHeader = 0;
iClipDurationFromRandomScan = 0;
iClipDurationFromMetadata = 0;
iSamplesPerFrame = 0;
iSamplingRate = 0;
iTimestamp = 0;
iFirstScan = true;
iTOC = NULL;
iTOCFilledCount = 0;
iTimestampPrev = 0;
iScanTimestamp = 0;
iBinWidth = 0;
iVbriHeader.TOC = NULL;
oscl_memset(&iMP3ConfigInfo, 0, sizeof(iMP3ConfigInfo));
oscl_memset(&iMP3HeaderInfo, 0, sizeof(iMP3HeaderInfo));
oscl_memset(&iXingHeader, 0, sizeof(iXingHeader));
oscl_memset(ConfigData, 0, sizeof(ConfigData));
oscl_memset(&iVbriHeader, 0, sizeof(iVbriHeader));
pSyncBuffer = NULL;
}
/***********************************************************************
* FUNCTION: Destructor
* DESCRIPTION:
* INPUT/OUTPUT PARAMETERS:
* RETURN VALUE:
* SIDE EFFECTS:
***********************************************************************/
MP3Parser::~MP3Parser()
{
// The File Pointer is only used. FileHandles are opened and closed
// as required.
fp = NULL;
iClipDurationInMsec = 0;
iClipDurationComputed = 0;
iLocalFileSize = 0;
iLocalFileSize = false;
iInitSearchFileSize = 0;
iCurrFrameNumber = 0;
iNumberOfFrames = 0;
ConfigSize = 0;
StartOffset = 0;
if (iVbriHeader.TOC != NULL)
OSCL_ARRAY_DELETE(iVbriHeader.TOC);
if (pSyncBuffer)
{
OSCL_ARRAY_DELETE(pSyncBuffer);
pSyncBuffer = NULL;
}
iMaxSyncBufferSize = 0;
if (iTOC)
{
OSCL_ARRAY_DELETE(iTOC);
iTOC = NULL;
}
oscl_memset(&iMP3ConfigInfo, 0, sizeof(iMP3ConfigInfo));
oscl_memset(&iMP3HeaderInfo, 0, sizeof(iMP3HeaderInfo));
oscl_memset(&iXingHeader, 0, sizeof(iXingHeader));
oscl_memset(&ConfigData, 0, sizeof(ConfigData));
oscl_memset(&iVbriHeader, 0, sizeof(iVbriHeader));
}
/***********************************************************************
* FUNCTION: ParseMP3File
* DESCRIPTION: This function MUST be called after the Constructor and before
* any other public function is called. Otherwise the object's
* member data will be uninitialized.
* INPUT/OUTPUT PARAMETERS:
* RETURN VALUE:
* SIDE EFFECTS:
***********************************************************************/
MP3ErrorType MP3Parser::ParseMP3File(PVFile * fpUsed, bool aEnableCRC)
{
//init members
iEnableCrcCalc = aEnableCRC;
fp = fpUsed;
iLocalFileSize = 0;
iLocalFileSizeSet = false;
iCurrFrameNumber = 0;
iNumberOfFrames = 0;
ConfigSize = 0;
StartOffset = 0;
uint32 firstHeader = 0;
uint8 pFirstFrame[MP3_FIRST_FRAME_SIZE];
uint8 pFrameHeader[MP3_FRAME_HEADER_SIZE];
uint8 * pBuf = pFirstFrame;
oscl_memset(&iMP3ConfigInfo, 0, sizeof(iMP3ConfigInfo));
oscl_memset(&iMP3HeaderInfo, 0, sizeof(iMP3HeaderInfo));
oscl_memset(&iXingHeader, 0, sizeof(iXingHeader));
oscl_memset(&ConfigData, 0, sizeof(ConfigData));
oscl_memset(&iVbriHeader, 0, sizeof(iVbriHeader));
MP3ErrorType errCode = MP3_SUCCESS;
// SAVE THE CURRENT FILE POSITION
errCode = MP3Utils::SeektoOffset(fp, 0, Oscl_File::SEEKSET);
// try to retrieve the file size
if (fp->GetFileBufferingCapacity() == 0 && MP3Utils::getCurrentFileSize(fp, iLocalFileSize))
{
iLocalFileSizeSet = true;
iInitSearchFileSize = OSCL_MIN(iInitSearchFileSize, iLocalFileSize);
if (iLocalFileSize == 0)
{
return MP3_END_OF_FILE;
}
}
if (!iLocalFileSizeSet)
{
uint32 remBytes = 0;
if (fp->GetRemainingBytes(remBytes))
{
iInitSearchFileSize = OSCL_MIN(iInitSearchFileSize, remBytes);
}
}
if (fp->GetFileBufferingCapacity() <= 0)
{
// Parse the MetaData (Beginning or End)
// Position the File Pointer at the first Audio Frame
if (iId3TagParser.ParseID3Tag(fp) == PVMFSuccess)
{
// This is the position of the first MP3 Frame in the File
if (iId3TagParser.IsID3V2Present())
{
iTagSize = iId3TagParser.GetByteOffsetToStartOfAudioFrames();
}
}
}
else
{
// get id3 tag size only
iId3TagParser.IsID3V2Present(fp, iTagSize);
}
if (iTagSize > 0)
{
StartOffset = iTagSize;
}
MP3ErrorType err = MP3Utils::SeektoOffset(fp, StartOffset, Oscl_File::SEEKSET);
if (MP3_SUCCESS != err)
{
return err;
}
uint32 seekOffset = 0;
err = mp3FindSync(StartOffset, seekOffset, fp);
if (err != MP3_SUCCESS)
{
// in eof scenario parser reports eof error to the user
// eof will be reported in case when no valid sync
// word is find in the maximum specified search limit
return err;
}
StartOffset += seekOffset;
err = MP3Utils::SeektoOffset(fp, StartOffset, Oscl_File::SEEKSET);
if (MP3_SUCCESS != err)
{
return err;
}
if (!MP3FileIO::readByteData(fp, MP3_FRAME_HEADER_SIZE, pFrameHeader))
{
return MP3_INSUFFICIENT_DATA;
}
firstHeader = SwapFileToHostByteOrderInt32(pFrameHeader);
if (!GetMP3Header(firstHeader, iMP3HeaderInfo))
{
return MP3_FILE_HDR_READ_ERR;
}
if (!DecodeMP3Header(iMP3HeaderInfo, iMP3ConfigInfo, false))
{
return MP3_FILE_HDR_DECODE_ERR;
}
else
{
oscl_memcpy(ConfigData, pFrameHeader, MP3_FRAME_HEADER_SIZE);
ConfigSize = MP3_FRAME_HEADER_SIZE;
}
int32 revSeek = 0 - MP3_FRAME_HEADER_SIZE - seekOffset;
errCode = MP3Utils::SeektoOffset(fp, revSeek, Oscl_File::SEEKCUR);
if (MP3_SUCCESS != errCode)
{
return errCode;
}
if (!MP3FileIO::readByteData(fp, MP3_FIRST_FRAME_SIZE, pFirstFrame))
{
return MP3_INSUFFICIENT_DATA;
}
revSeek = 0 - MP3_FIRST_FRAME_SIZE;
//VBRI header exist exactly 32 bytes after first frame header
if ((oscl_memcmp((pBuf + VBRI_HEADER_OFFSET), STR_VBRI_HEADER_IDENTIFIER, VBR_HEADER_SIZE) == 0))
{
int32 bufferSize = CalculateBufferSizeForHeader(pBuf + 36);
int32 actualBufferSize = bufferSize + VBRI_HEADER_OFFSET + VBR_HEADER_SIZE;
uint8* tempBuf = OSCL_ARRAY_NEW(uint8, actualBufferSize);
MP3ErrorType err = MP3Utils::SeektoOffset(fp, revSeek, Oscl_File::SEEKCUR);
if (MP3_SUCCESS != err)
{
OSCL_ARRAY_DELETE(tempBuf);
return err;
}
if (!MP3FileIO::readByteData(fp, actualBufferSize, tempBuf))
{
OSCL_ARRAY_DELETE(tempBuf);
return MP3_INSUFFICIENT_DATA;
}
else
{
if (pBuf)
{
pBuf = NULL;
pBuf = tempBuf;
pBuf += 36;
}
}
revSeek = 0 - actualBufferSize;
if (!DecodeVBRIHeader(pBuf, iVbriHeader, iMP3HeaderInfo))
{
OSCL_ARRAY_DELETE(tempBuf);
return MP3_FILE_VBRI_HDR_ERR;
}
else
{
mp3Type = EVBRIType;
iLocalFileSize = iVbriHeader.bytes;
OSCL_ARRAY_DELETE(tempBuf);
}
}
else
{
uint32 offset = 0;
// Determine offset of XING headers
if ((iMP3HeaderInfo.layerID == MPEG_LAYER_III))
{
// MPEG 1
if (iMP3HeaderInfo.frameVer == 3) //MPEG version 1
{
if (iMP3HeaderInfo.chMode != 3)
{
offset = (32 + 4);
pBuf += offset;
}
else
{
offset = (17 + 4);
pBuf += offset;
}
}
else
{
// MPEG 2
if (iMP3HeaderInfo.chMode != 3)
{
offset = (17 + 4);
pBuf += offset;
}
else
{
offset = (9 + 4);
pBuf += offset;
}
}
}
// Check for MP3 Header Tags, XING or INFO
if ((oscl_memcmp(pBuf, STR_XING_HEADER_IDENTIFIER, VBR_HEADER_SIZE) == 0) ||
(oscl_memcmp(pBuf, STR_INFO_HEADER_IDENTIFIER, VBR_HEADER_SIZE) == 0))
{
MP3ErrorType err = MP3Utils::SeektoOffset(fp, offset - MP3_FIRST_FRAME_SIZE, Oscl_File::SEEKCUR);
if (MP3_SUCCESS != err)
{
return err;
}
revSeek = 0 - offset;
if (!MP3FileIO::readByteData(fp, MP3_FIRST_FRAME_SIZE, pFirstFrame))
{
return MP3_INSUFFICIENT_DATA;
}
revSeek -= MP3_FIRST_FRAME_SIZE;
if (!DecodeXINGHeader(pFirstFrame, iXingHeader, iMP3HeaderInfo))
return MP3_FILE_XING_HDR_ERR;
else
mp3Type = EXINGType;
}
}
// Calculate the sampling rate and samples per frame.
iSamplesPerFrame = spfIndexTable[iMP3HeaderInfo.frameVer][iMP3HeaderInfo.layerID];
iSamplingRate = srIndexTable[((iMP3HeaderInfo.frameVer)*4) + iMP3HeaderInfo.srIndex];
// If the mp3Type is XING or VBRI, then first check if they have a valid duration
// If the header has no valid duration then just mark the content to as a VBR content
if (mp3Type == EXINGType || mp3Type == EVBRIType)
{
// Get the duration
uint32 clipduration = 0;
GetDurationFromVBRIHeader(clipduration);
if (clipduration == 0)
{
// not a valid duration, just set the clip to be VBR type
mp3Type = EVBRType;
}
}
// If XING or VBRI Headers are not present then we need to build our own TOC for
// repositioning.
// And even if XING header is present and TOC flags are not present we need to build
// our own TOC.
if ((mp3Type != EXINGType || !(iXingHeader.flags & TOC_FLAG)) &&
(mp3Type != EVBRIType))
{
iTOC = OSCL_ARRAY_NEW(int32, MAX_TOC_ENTRY_COUNT + 1);
oscl_memset(iTOC, 0, sizeof(iTOC));
}
iAvgBitrateInbps = iMP3ConfigInfo.BitRate;
// Set the position to the position of the first MP3 frame
errCode = MP3Utils::SeektoOffset(fp, revSeek + seekOffset, Oscl_File::SEEKCUR);
if (MP3_SUCCESS != errCode)
{
return errCode;
}
iCurrFrameNumber = 0;
return MP3_SUCCESS;
}
/***********************************************************************
* Function : ScanMP3File
* Purpose : Fetches duration of the clip playing
* Duration is returned, by different
* means by the pre-defined priorities
* Input : fpUsed
* Output : iClipDurationComputed
* Return : error code
* Modified :
***********************************************************************/
MP3ErrorType MP3Parser::ScanMP3File(PVFile * fpUsed, uint32 aFramesToScan)
{
uint32 firstHeader = 0;
uint8 pFrameHeader[MP3_FRAME_HEADER_SIZE];
int32 audioOffset = 0;
uint32 seekOffset = 0;
MP3ErrorType status = MP3_ERROR_UNKNOWN;
MP3HeaderType mp3HeaderInfo;
MP3ConfigInfoType mp3ConfigInfo;
if (iClipDurationFromMetadata || (iClipDurationFromVBRIHeader &&
((iVbriHeader.entriesTOC >= 0) ||
(iXingHeader.flags & TOC_FLAG)))
)
{
// if Duration can be obtained from either VBRI/XING Headers or from metadata,
// we will not scan the file for duration
return MP3_DURATION_PRESENT;
}
if (iTOCFilledCount == MAX_TOC_ENTRY_COUNT)
{
status = FillTOCTable(0, 0);
if (status == MP3_ERROR_UNKNOWN)
{
// This will happen when FillTocTable returns error because of
// NULL TOCTable.
// Not a valid condition this should never happen, except if
// there was memory allocation failure during ParseMP3File.
// If happens return Duration Present to avoid any further ScanMp3File calls.
return MP3_DURATION_PRESENT;
}
return MP3_SUCCESS;
}
if (iFirstScan)
{
if (iTagSize > 0)
{
audioOffset = StartOffset;
MP3ErrorType err = MP3Utils::SeektoOffset(fpUsed, audioOffset, Oscl_File::SEEKSET);
if (MP3_SUCCESS != err)
{
return err;
}
}
iFirstScan = false;
}
else
{
audioOffset = iLastScanPosition;
}
// Set length of initial search to the min between default and filesize
iInitSearchFileSize = OSCL_MIN(iInitSearchFileSize, iLocalFileSize);
uint32 numFrames = 0;
int32 bitrate = 0;
uint32 frameDur = 0;
while (numFrames < aFramesToScan)
{
firstHeader = 0;
oscl_memset(&pFrameHeader, 0, sizeof(pFrameHeader));
if (fpUsed->Read(pFrameHeader, 1, MP3_FRAME_HEADER_SIZE) != MP3_FRAME_HEADER_SIZE)
{
if (fpUsed->GetFileBufferingCapacity() == 0)
{
iDurationScanComplete = true;
}
status = FillTOCTable(audioOffset, 0);
if (status == MP3_ERROR_UNKNOWN)
{
// This will happen when FillTocTable returns error because of
// NULL TOCTable.
// Not a valid condition this should never happen, except if
// there was memory allocation failure during ParseMP3File.
// If happens return Duration Present to avoid any further ScanMp3File calls.
return MP3_DURATION_PRESENT;
}
return MP3_INSUFFICIENT_DATA;
}
firstHeader = SwapFileToHostByteOrderInt32(pFrameHeader);
uint32 offset = MP3Utils::getCurrentFilePosition(fpUsed);
if (!GetMP3Header(firstHeader, mp3HeaderInfo))
{
MP3Utils::SeektoOffset(fp, 0 - MP3_FRAME_HEADER_SIZE, Oscl_File::SEEKCUR);
MP3ErrorType err = mp3FindSync(offset, seekOffset, fpUsed);
if (err == MP3_SUCCESS)
{
offset += seekOffset;
err = MP3Utils::SeektoOffset(fpUsed, seekOffset, Oscl_File::SEEKCUR);
if (MP3_SUCCESS != err)
{
return err;
}
if (!MP3FileIO::readByteData(fpUsed, MP3_FRAME_HEADER_SIZE, pFrameHeader))
{
iDurationScanComplete = true;
status = FillTOCTable(offset, iScanTimestamp);
if (status == MP3_ERROR_UNKNOWN)
{
// This will happen when FillTocTable returns error because of
// NULL TOCTable.
// Not a valid condition this should never happen, except if
// there was memory allocation failure during ParseMP3File.
// If happens return Duration Present to avoid any further ScanMp3File calls.
return MP3_DURATION_PRESENT;
}
return MP3_INSUFFICIENT_DATA;
}
firstHeader = SwapFileToHostByteOrderInt32(pFrameHeader);
if (! GetMP3Header(firstHeader, mp3HeaderInfo))
{
iDurationScanComplete = true;
status = FillTOCTable(offset, iScanTimestamp);
if (status == MP3_ERROR_UNKNOWN)
{
// This will happen when FillTocTable returns error because of
// NULL TOCTable.
// Not a valid condition this should never happen, except if
// there was memory allocation failure during ParseMP3File.
// If happens return Duration Present to avoid any further ScanMp3File calls.
return MP3_DURATION_PRESENT;
}
return MP3_FILE_HDR_READ_ERR;
}
}
else
{
iDurationScanComplete = true;
status = FillTOCTable(offset, iScanTimestamp);
if (status == MP3_ERROR_UNKNOWN)
{
// This will happen when FillTocTable returns error because of
// NULL TOCTable.
// Not a valid condition this should never happen, except if
// there was memory allocation failure during ParseMP3File.
// If happens return Duration Present to avoid any further ScanMp3File calls.
return MP3_DURATION_PRESENT;
}
return err;
}
}
if (!DecodeMP3Header(mp3HeaderInfo, mp3ConfigInfo, false))
{
iDurationScanComplete = true;
status = FillTOCTable(offset, iScanTimestamp);
if (status == MP3_ERROR_UNKNOWN)
{
// This will happen when FillTocTable returns error because of
// NULL TOCTable.
// Not a valid condition this should never happen, except if
// there was memory allocation failure during ParseMP3File.
// If happens return Duration Present to avoid any further ScanMp3File calls.
return MP3_DURATION_PRESENT;
}
return MP3_FILE_HDR_DECODE_ERR;
}
MP3Utils::SeektoOffset(fpUsed, mp3ConfigInfo.FrameLengthInBytes - MP3_FRAME_HEADER_SIZE, Oscl_File::SEEKCUR);
bitrate = mp3ConfigInfo.BitRate;
frameDur = frameDur + (uint32)((OsclFloat) mp3ConfigInfo.FrameLengthInBytes * 8000.00f / mp3ConfigInfo.BitRate);
iLastScanPosition = fpUsed->Tell();
numFrames++;
iScannedFrameCount++;
if (iScannedFrameCount > 1)
{
if (bitrate != iAvgBitrateInbpsFromCompleteScan)
{
iAvgBitrateInbpsFromCompleteScan += (int32)((int32)bitrate - (int32)iAvgBitrateInbpsFromCompleteScan) / (int32)iScannedFrameCount;
}
}
else
{
iAvgBitrateInbpsFromCompleteScan = bitrate;
mp3Type = ECBRType;
}
}
// After scan of frames we need to fill the TOC table
status = FillTOCTable(audioOffset, iScanTimestamp);
if (status == MP3_ERROR_UNKNOWN)
{
// This will happen when FillTocTable returns error because of
// NULL TOCTable.
// Not a valid condition this should never happen, except if
// there was memory allocation failure during ParseMP3File.
// If happens return Duration Present to avoid any further ScanMp3File calls.
return MP3_DURATION_PRESENT;
}
iScanTimestamp = iScanTimestamp + frameDur;
return MP3_SUCCESS;
}
/***********************************************************************
* FUNCTION: GetMP3Header
* DESCRIPTION: Parse Header Bit fields into a structure (Pass in 4 bytes)
* Validate ranges and reserved fields.
* INPUT/OUTPUT PARAMETERS:
* RETURN VALUE:
* SIDE EFFECTS:
***********************************************************************/
bool MP3Parser::GetMP3Header(uint32 &aFrameHeader, MP3HeaderType &aMP3HeaderInfo)
{
oscl_memset(&aMP3HeaderInfo, 0, sizeof(aMP3HeaderInfo));
if ((aFrameHeader & FRAME_SYNC_MASK) != (FRAME_SYNC_MASK))
{
return false;
}
aMP3HeaderInfo.frameVer = ((aFrameHeader & FRAME_VER_ID_MASK) >> FRAME_VER_ID_SHIFT);
aMP3HeaderInfo.layerID = ((aFrameHeader & FRAME_LAYER_ID_MASK) >> FRAME_LAYER_ID_SHIFT);
aMP3HeaderInfo.crcFollows = ((aFrameHeader & FRAME_PROT_MASK) >> FRAME_PROT_SHIFT);
aMP3HeaderInfo.brIndex = ((aFrameHeader & FRAME_BR_INDEX_MASK) >> FRAME_BR_INDEX_SHIFT);
aMP3HeaderInfo.srIndex = ((aFrameHeader & FRAME_SR_FREQ_MASK) >> FRAME_SR_FREQ_SHIFT);
aMP3HeaderInfo.prvBit = ((aFrameHeader & FRAME_PRIVATE_MASK) >> FRAME_PRIVATE_SHIFT);
aMP3HeaderInfo.padBit = ((aFrameHeader & FRAME_PADDING_MASK) >> FRAME_PADDING_SHIFT);
aMP3HeaderInfo.chMode = ((aFrameHeader & FRAME_CH_MODE_MASK) >> FRAME_CH_MODE_SHIFT);
aMP3HeaderInfo.modeExtn = ((aFrameHeader & FRAME_MODE_EXTN_MASK) >> FRAME_MODE_EXTN_SHIFT);
// Validate the header
// Skip Frames with Invalid/Reserved Fields set
if ((aMP3HeaderInfo.srIndex == 3) || (aMP3HeaderInfo.brIndex == 15) ||
(aMP3HeaderInfo.frameVer == 1) || (aMP3HeaderInfo.layerID != 1)) /* layerID == 1 <> layer III or mp3 */
{
return false;
}
return true;
}
/***********************************************************************
* FUNCTION: DecodeMP3Header
* DESCRIPTION: Decode the MP3 Header struct and place the derived values
* into the supplied MP3 Config data structure.
* INPUT/OUTPUT PARAMETERS:
* RETURN VALUE:
* SIDE EFFECTS:
***********************************************************************/
bool MP3Parser::DecodeMP3Header(MP3HeaderType &aMP3HeaderInfo, MP3ConfigInfoType &aMP3ConfigInfo, bool aComputeAvgBitrate)
{
uint32 bitRate, samplingRate;
uint32 FrameLengthInBytes;
uint32 FrameSizeUnComp;
aMP3ConfigInfo.SamplingRate = 0;
aMP3ConfigInfo.BitRate = 0;
aMP3ConfigInfo.FrameLengthInBytes = 0;
aMP3ConfigInfo.FrameSizeUnComp = 0;
aMP3ConfigInfo.NumberOfChannels = 0;
if (aMP3HeaderInfo.frameVer == 3) // MPEG Ver 1
{
bitRate = 1000 * brIndexTableV1[aMP3HeaderInfo.layerID][aMP3HeaderInfo.brIndex];
}
else if ((aMP3HeaderInfo.frameVer == 2) || (aMP3HeaderInfo.frameVer == 0))
{ // MPEG Ver 2.0, 2.5
bitRate = 1000 * brIndexTableV2[aMP3HeaderInfo.layerID][aMP3HeaderInfo.brIndex];
}
else
{
return false;
}
if (bitRate == 0)
{
return false;
}
samplingRate = srIndexTable[((aMP3HeaderInfo.frameVer)*4) + aMP3HeaderInfo.srIndex];
if (samplingRate == 0)
{
return false;
}
// Compressed Frame Size
if (aMP3HeaderInfo.layerID == 3) // Layer I
{
if (aMP3HeaderInfo.frameVer == 3) // MPEG Ver 1
{
FrameLengthInBytes = (12 * bitRate / samplingRate + aMP3HeaderInfo.padBit) * 4;
FrameSizeUnComp = 384;
}
else // MPEG Ver 2, 2.5
{
FrameLengthInBytes = (6 * bitRate / samplingRate + aMP3HeaderInfo.padBit) * 4;
FrameSizeUnComp = 192;
}
}
else // Layer II & III
{
if (aMP3HeaderInfo.frameVer == 3) // MPEG Ver 1
{
FrameLengthInBytes = (144 * bitRate / samplingRate + aMP3HeaderInfo.padBit);
FrameSizeUnComp = 1152;
}
else // MPEG Ver 2,2.5
{
FrameLengthInBytes = (72 * bitRate / samplingRate + aMP3HeaderInfo.padBit);
FrameSizeUnComp = 576;
}
}
switch (aMP3HeaderInfo.chMode)
{
case 0:
aMP3ConfigInfo.NumberOfChannels = 2; // Stereo
break;
case 1: // Joint Ch. Stereo
case 2: // Dual Ch. Stereo
aMP3ConfigInfo.NumberOfChannels = 2; // Stereo
break;
case 3:
aMP3ConfigInfo.NumberOfChannels = 1; // Mono
break;
default:
break;
}
aMP3ConfigInfo.SamplingRate = samplingRate;
aMP3ConfigInfo.BitRate = bitRate;
aMP3ConfigInfo.FrameLengthInBytes = FrameLengthInBytes;
aMP3ConfigInfo.FrameSizeUnComp = FrameSizeUnComp;
if (mp3Type != EVBRIType)
{
if ((mp3Type == EXINGType && !(iXingHeader.flags & TOC_FLAG) && !(iXingHeader.flags & FRAMES_FLAG)) || mp3Type == EVBRType)
{
if (aComputeAvgBitrate)
{
int32 filesize = OSCL_MAX(iFileSizeFromExternalSource, iLocalFileSize);
uint32 audioDataSize = (filesize - StartOffset);
if (iId3TagParser.IsID3V1Present())
{
// The TAG in an ID3V1.x MP3 File is 128 bytes long
audioDataSize -= ID3_V1_TAG_SIZE;
}
iNumberOfFrames = audioDataSize / (aMP3ConfigInfo.FrameLengthInBytes);
if (aMP3ConfigInfo.BitRate <= 0)
{
return true;
}
if (iCurrFrameNumber == 1)
{
iAvgBitrateInbps = aMP3ConfigInfo.BitRate;
}
if (iCurrFrameNumber > 1)
{
if (aMP3ConfigInfo.BitRate != iAvgBitrateInbps)
{
iAvgBitrateInbps += (aMP3ConfigInfo.BitRate - (int32)iAvgBitrateInbps) / iCurrFrameNumber;
}
}
}
}
}
return true;
}
/***********************************************************************
* FUNCTION: DecodeVBRIHeader
* DESCRIPTION: Decode VBRI Header and store TOC entries used for
repositioning
* INPUT/OUTPUT PARAMETERS:
* RETURN VALUE:
* SIDE EFFECTS:
***********************************************************************/
bool MP3Parser::DecodeVBRIHeader(uint8 *VbriBuffer, VBRIHeaderType &vbriHDType,
MP3HeaderType &aMP3HeaderInfo)
{
uint8 * pBuf = VbriBuffer;
int32 pos = 0;
int32 i, tableLength;
pBuf += 4;
vbriHDType.hId = aMP3HeaderInfo.layerID;
vbriHDType.sampleRate = srIndexTable[((aMP3HeaderInfo.frameVer)*4) + aMP3HeaderInfo.srIndex];
pBuf += 6;
vbriHDType.bytes = SwapFileToHostByteOrderInt32(pBuf);
pBuf += 4;
vbriHDType.frames = SwapFileToHostByteOrderInt32(pBuf);
iNumberOfFrames = vbriHDType.frames;
pBuf += 4;
vbriHDType.entriesTOC = SwapFileToHostByteOrderInt16(pBuf);
pBuf += 2;
vbriHDType.scale = SwapFileToHostByteOrderInt16(pBuf);
pBuf += 2;
vbriHDType.sTableEntry = SwapFileToHostByteOrderInt16(pBuf);
pBuf += 2;
vbriHDType.fTableEntry = SwapFileToHostByteOrderInt16(pBuf);
pBuf += 2;
tableLength = vbriHDType.entriesTOC * vbriHDType.sTableEntry;
vbriHDType.TOC = OSCL_ARRAY_NEW(int32, vbriHDType.entriesTOC + 1);
for (i = 0; i <= (vbriHDType.entriesTOC); i++)
{
vbriHDType.TOC[i] = ReadBuffer(pBuf, vbriHDType.sTableEntry, pos) * vbriHDType.scale;
}
return true;
}
/***********************************************************************
* FUNCTION: DecodeXINGHeader
* DESCRIPTION:
* INPUT/OUTPUT PARAMETERS:
* RETURN VALUE:
* SIDE EFFECTS:
***********************************************************************/
bool MP3Parser::DecodeXINGHeader(uint8 *XingBuffer,
XINGHeaderType &mp3XingHD,
MP3HeaderType &hdrInfo)
{
/* 4 XING - 4 flags - 4 frames - 4 bytes - 100 toc */
uint8 * pBuf = XingBuffer;
int32 i, head_flags;
mp3XingHD.flags = 0;
pBuf += 4;
mp3XingHD.hId = hdrInfo.layerID;
head_flags = mp3XingHD.flags = SwapFileToHostByteOrderInt32(pBuf);
pBuf += 4;
if (head_flags & FRAMES_FLAG)
{
mp3XingHD.frames = SwapFileToHostByteOrderInt32(pBuf);
pBuf += 4;
}
if (head_flags & BYTES_FLAG)
{
mp3XingHD.bytes = SwapFileToHostByteOrderInt32(pBuf);
pBuf += 4;
if (head_flags & FRAMES_FLAG)
{
// check if the number of frames and the number of
// bytes roughly match up
int bytesperframe = mp3XingHD.bytes / mp3XingHD.frames;
// 52 and 1440 are the minimum and maximum number of bytes per frame for
// a compliant mp3 stream (8kbps@22050Hz and 320kbps@32000Hz respectively)
if (bytesperframe < 52 || bytesperframe > 1440)
{
head_flags = mp3XingHD.flags = 0;
}
}
if (iLocalFileSize != 0)
{
// check if the number of bytes and the file size roughly
// match up
if (mp3XingHD.bytes > iLocalFileSize)
{
head_flags = mp3XingHD.flags = 0;
}
if (mp3XingHD.bytes < iLocalFileSize * XING_SIZE_FUZZINESS)
{
head_flags = mp3XingHD.flags = 0;
}
}
}
if (head_flags & TOC_FLAG)
{
for (i = 0; i < 100; i++)
{
mp3XingHD.TOC[i] = pBuf[i];
}
pBuf += 100;
}
mp3XingHD.vbr_scale = 0;
if (head_flags & VBR_SCALE_FLAG)
{
mp3XingHD.vbr_scale = SwapFileToHostByteOrderInt32(pBuf);
pBuf += 4;;
}
if (head_flags & FRAMES_FLAG)
{
iNumberOfFrames = mp3XingHD.frames;
}
return true;
}
/***********************************************************************
* FUNCTION: GetMP3FileHeader
* DESCRIPTION: Returns information necessary to configure the audio device
* INPUT/OUTPUT PARAMETERS:
* RETURN VALUE:
* SIDE EFFECTS:
***********************************************************************/
bool MP3Parser::GetMP3FileHeader(MP3ConfigInfoType * pMP3Config)
{
if (pMP3Config != NULL)
{
// For CBR (Constatnt Bit Rate) files, the frame sizes do not
// vary. So it is sufficient to simply use the first frame's
// sizes and lengths. For VBR, we'll want to use the maximum
// sizes and lengths possible.
oscl_memcpy(pMP3Config, &iMP3ConfigInfo, sizeof(iMP3ConfigInfo));
if (mp3Type == EXINGType || mp3Type == EVBRIType)
{
// Ensure the Application reserves enough space for the largest
// data rate for this file.
// This should be the Maximum Data Rate in the file, for a
// Variable Bitrate File
if (iMP3HeaderInfo.frameVer == 3) // MPEG Ver 1
{
pMP3Config->BitRate = 1000 * brIndexTableV1[iMP3HeaderInfo.layerID][14];
}
else // MPEG Ver 2, 2.5
{
pMP3Config->BitRate = 1000 * brIndexTableV2[iMP3HeaderInfo.layerID][14];
}
// Compressed Frame Size
uint32 bitRate = pMP3Config->BitRate;
uint32 samplingRate = pMP3Config->SamplingRate;
uint32 FrameLengthInBytes;
uint32 FrameSizeUnComp;
if (iMP3HeaderInfo.layerID == 3) // Layer I
{
if (iMP3HeaderInfo.frameVer == 3) // MPEG Ver 1
{
FrameLengthInBytes = (12 * bitRate / samplingRate + 1) * 4;
FrameSizeUnComp = 384;
}
else // MPEG Ver 2, 2.5
{
FrameLengthInBytes = (6 * bitRate / samplingRate + 1) * 4;
FrameSizeUnComp = 192;
}
}
else // Layer II & III
{
if (iMP3HeaderInfo.frameVer == 3) // MPEG Ver 1
{
FrameLengthInBytes = (144 * bitRate / samplingRate + 1);
FrameSizeUnComp = 1152;
}
else // MPEG Ver 2,2.5
{
FrameLengthInBytes = (72 * bitRate / samplingRate + 1);
FrameSizeUnComp = 576;
}
}
// This should be the largest Frame Size in the File.
// The Application uses this information for allocating Audio
// Device Buffers.
// Maximum possible at any sample rate is 2880 with a pad byte; use 2884 to word align
// however we have just calculated the maximum at this particular sample rate
pMP3Config->FrameLengthInBytes = FrameLengthInBytes;
pMP3Config->FrameSizeUnComp = FrameSizeUnComp;
}
return true;
}
return false;
}
/***********************************************************************
* Function : GetChannelMode
* Purpose : Fetch Channel mode for the clip
* Input : None
* Output :
* Return : ChannelMode
* Modified :
***********************************************************************/
uint32 MP3Parser::GetChannelMode() const
{
return iMP3HeaderInfo.chMode;
}
/***********************************************************************
* Function : GetDurationFromMetadata
* Purpose : Fetch duration value from id3 frame (TLEN - track length)
* Input : None
* Output : iClipDurationFromMetadata
* Return : clip duration
* Modified :
***********************************************************************/
uint32 MP3Parser::GetDurationFromMetadata()
{
if (iClipDurationFromMetadata <= 0)
{
PvmiKvpSharedPtrVector frame;
PVMFMetadataList keyList;
keyList.push_back("duration-from-metadata");
iId3TagParser.GetID3Frame(keyList[0], frame);
if (frame.size() > 0)
{
iClipDurationFromMetadata = frame[0]->value.uint32_value;
}
}
return iClipDurationFromMetadata;
}
/***********************************************************************
* Function : ConvertSizeToTime
* Purpose : Fetches duration of the clip playing
* Duration is returned, by different
* means by the pre-defined priorities
* Input : aMetadataDuration, true if duration from metadata is needed
* Output : None
* Return : Clip duration
* Modified :
**********************************************************************/
int32 MP3Parser::ConvertSizeToTime(uint32 aFileSize, uint32& aNPTInMS)
{
uint32 duration = 0;
uint32 fileSize = aFileSize;
if (iId3TagParser.IsID3V2Present())
{
if (iTagSize > fileSize)
{
return -1;
}
fileSize -= iTagSize;
}
if (iId3TagParser.IsID3V1Present())
{
// id3v1.x tags are 128 bytes long
fileSize -= ID3_V1_TAG_SIZE;
}
if (iAvgBitrateInbps > 0)
{
duration = (uint32)((OsclFloat)(fileSize * 8000.00f / iAvgBitrateInbps));
aNPTInMS = duration;
return 0;
}
return -1;
}
/***********************************************************************
* Function : GetDuration
* Purpose : Fetches duration of the clip playing
* Duration is returned, by different
* means by the pre-defined priorities
* Input : aMetadataDuration, true if duration from metadata is needed
* Output : None
* Return : Clip duration
* Modified :
**********************************************************************/
uint32 MP3Parser::GetDuration(bool aMetadataDuration)
{
if (aMetadataDuration)
{
return GetDurationFromMetadata();
}
uint32 clipDuration = 0;
// local clip playback
if (fp->GetFileBufferingCapacity() == 0)
{
// if scanning is complete, send the clip duration from scan
// else if vbri/xing headers exist send duration from that
// else scan "N" random frames to estimate duration from avg bitrate
if (!iDurationScanComplete)
{
if (mp3Type == EXINGType || mp3Type == EVBRIType)
{
if (MP3_SUCCESS != GetDurationFromVBRIHeader(clipDuration))
{
clipDuration = 0;
}
}
if (clipDuration == 0 && GetDurationFromMetadata() > 0)
{
clipDuration = iClipDurationFromMetadata;
// random scan will not be performed,
// estimate bitrate from filesize and duration
}
else if (clipDuration == 0)
{
if (MP3_SUCCESS == GetDurationFromRandomScan(clipDuration))
{
iClipDurationInMsec = clipDuration;
return clipDuration;
}
}
// if control gets here, that means avg bit rate from random scan is not calculated.
if (iAvgBitrateInbpsFromRandomScan <= 0)
{
uint32 fileSize = iLocalFileSize;
if (iId3TagParser.IsID3V2Present())
{
fileSize -= StartOffset;
}
if (iId3TagParser.IsID3V1Present())
{
// id3v1.x tags are 128 bytes long
fileSize -= ID3_V1_TAG_SIZE;
}
iAvgBitrateInbpsFromRandomScan = (int32)((OsclFloat)(fileSize * 8000.00f) / clipDuration);
}
}
else
{
GetDurationFromCompleteScan(clipDuration);
}
}
else
{
// PD/PS playback
// duration can only be estimated using content length provided by user
// If content length has not been recieved, then duration cant be estimated
// in that case duration value "unknown" is notified to the user
if (mp3Type == EXINGType || mp3Type == EVBRIType)
{
if (MP3_SUCCESS != GetDurationFromVBRIHeader(clipDuration))
{
clipDuration = 0;
}
}
if (clipDuration == 0 && MP3_SUCCESS == EstimateDurationFromExternalFileSize(clipDuration))
{
clipDuration = iClipDurationFromEstimation;
}
}
iClipDurationInMsec = clipDuration;
return clipDuration;
}
/***********************************************************************
* Function : GetMetadataSize
* Purpose : Fetches size of id3 data
* Input : None
* Output : aSize, size of metadata
* Return : error code
* Modified :
***********************************************************************/
MP3ErrorType MP3Parser::GetMetadataSize(uint32 &aMetadataSize)
{
if (fp)
{
if (iId3TagParser.IsID3V2Present(fp, iTagSize) && iTagSize > 0)
{
aMetadataSize = iTagSize;
return MP3_SUCCESS;
}
}
aMetadataSize = 0;
return MP3_METADATA_NOTPARSED;
}
/***********************************************************************
* Function : GetMinBytesRequired
* Purpose : Fetches size of maximum Mp3 frame
* Input : None
* Output : None
* Return : aSize, size of max mp3 frame
* Modified :
***********************************************************************/
uint32 MP3Parser::GetMinBytesRequired(bool aNextBytes)
{
uint32 minBytes = KMAX_MP3FRAME_LENGTH_IN_BYTES;
if (aNextBytes && fp)
{
// case where parse file has failed due to lack of data
// in that case request next n bytes.
minBytes += fp->Tell();
}
return minBytes;
}
/***********************************************************************
* FUNCTION: GetNextBundledAccessUnits
* DESCRIPTION:
* INPUT/OUTPUT PARAMETERS:
* RETURN VALUE:
* SIDE EFFECTS:
***********************************************************************/
int32 MP3Parser::GetNextBundledAccessUnits(uint32 *n, GAU *pgau, MP3ErrorType &error)
{
uint32 nBytesRead = 0;
uint32 framets = 0;
uint32 nBytesReadTotal = 0;
int32 i;
error = MP3_ERROR_UNKNOWN;
if ((pgau == NULL) || (pgau->buf.num_fragments > 1)
|| (n == NULL))
{
return 0;
}
uint8 * pOutputBuffer = (uint8 *)pgau->buf.fragments[0].ptr;
int32 iLength = pgau->buf.fragments[0].len;
for (i = 0; (i < (int32)*n) && (iLength > 0); i++)
{
pgau->numMediaSamples = i;
error = GetNextMediaSample(pOutputBuffer, iLength, nBytesRead, framets);
if ((error == MP3_SUCCESS))
{
if (nBytesRead > 0)
{
// Frame was read successfully
pgau->info[i].len = nBytesRead;
pgau->info[i].ts = framets;
}
}
else
{
// Read failure
break;
}
iLength -= nBytesRead;
pOutputBuffer += nBytesRead;
nBytesReadTotal += nBytesRead;
}
*n = i;
return nBytesReadTotal;
}
/***********************************************************************
* FUNCTION: PeekNextBundledAccessUnits
* DESCRIPTION:
* INPUT/OUTPUT PARAMETERS:
* RETURN VALUE:
* SIDE EFFECTS:
***********************************************************************/
int32 MP3Parser::PeekNextBundledAccessUnits(uint32 *n, MediaMetaInfo *mInfo)
{
uint32 nBytesToRead = 0;
if ((mInfo == NULL) || (n == NULL))
{
return 0;
}
for (uint32 i = 0; i < *n; i++)
{
if ((iCurrFrameNumber + (int32)i) >= iNumberOfFrames)
{
break;
}
mInfo->ts = GetTimestampForSample(iCurrFrameNumber + i);
// Don't care
mInfo->layer = 0;
// Maximum Frame Length
mInfo->len = (iMP3ConfigInfo.FrameLengthInBytes + MP3_FRAME_HEADER_SIZE);
mInfo->sample_info = 0;
mInfo->dropFlag = 0;
nBytesToRead += mInfo->len;
}
return nBytesToRead;
}
/***********************************************************************
* FUNCTION: GetNextMediaSample
* DESCRIPTION:
* INPUT/OUTPUT PARAMETERS:
* RETURN VALUE:
* SIDE EFFECTS:
***********************************************************************/
MP3ErrorType MP3Parser::GetNextMediaSample(uint8 *buffer, uint32 size, uint32& framesize, uint32& timestamp)
{
MP3ErrorType mp3Err = MP3_SUCCESS;
BEGIN:
uint32 currentFilePosn = 0;
uint32 mp3Header = 0;
uint32 mp3FrameSizeInBytes = 0;
MP3HeaderType mp3HeaderInfo = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
MP3ConfigInfoType mp3CDInfo = {0, 0, 0, 0, 0};
uint8 *buf = buffer;
framesize = 0;
timestamp = 0;
// Read and Decode the MP3 Frame Header to obtain the
// correct number of bytes in this frame.
currentFilePosn = MP3Utils::getCurrentFilePosition(fp);
// If content length is known (non-0), avoid reading beyond EOF
uint32 contentLength = MP3FileIO::getContentLength(fp);
if (0 != contentLength)
{
// check for reading beyond EOF
if ((currentFilePosn + MP3_FRAME_HEADER_SIZE) >= contentLength)
{
return MP3_END_OF_FILE;
}
}
if (!MP3FileIO::readByteData(fp, MP3_FRAME_HEADER_SIZE, buf))
{
return MP3_INSUFFICIENT_DATA;
}
// Convert the File Byte Order to Host Memory Byte Order
// for 32 bit integers
mp3Header = SwapFileToHostByteOrderInt32(buf);
// Adjust the buffer write location in preparation for
// the next read
if (! GetMP3Header(mp3Header, mp3HeaderInfo))
{
// ////////////////////////////////////////////////////////////////////////////
// If we don't find a valid MP3 Marker point we will attempt recovery.
uint32 seekOffset = 0;
MP3Utils::SeektoOffset(fp, 0 - MP3_FRAME_HEADER_SIZE, Oscl_File::SEEKCUR);
MP3ErrorType err = mp3FindSync(currentFilePosn, seekOffset, fp);
if (err == MP3_SUCCESS)
{
err = MP3Utils::SeektoOffset(fp, seekOffset, Oscl_File::SEEKCUR);
if (MP3_SUCCESS != err)
{
return err;
}
currentFilePosn += seekOffset;
if (0 != contentLength)
{
// if content length is known, check for reading beyond EOF
if ((currentFilePosn + MP3_FRAME_HEADER_SIZE) >= contentLength)
{
return MP3_END_OF_FILE;
}
}
if (!MP3FileIO::readByteData(fp, MP3_FRAME_HEADER_SIZE, buf))
{
return MP3_INSUFFICIENT_DATA;
}
mp3Header = SwapFileToHostByteOrderInt32(buf);
if (! GetMP3Header(mp3Header, mp3HeaderInfo))
{
return MP3_FILE_HDR_READ_ERR;
}
}
else
{
return err;
}
}
buf += MP3_FRAME_HEADER_SIZE;
iCurrFrameNumber++;
if (! DecodeMP3Header(mp3HeaderInfo, mp3CDInfo, true))
{
iCurrFrameNumber--;
return MP3_FILE_HDR_DECODE_ERR;
}
int32 revSeek = 0 - MP3_FRAME_HEADER_SIZE;
mp3FrameSizeInBytes = mp3CDInfo.FrameLengthInBytes;
MP3ErrorType err = MP3Utils::SeektoOffset(fp, revSeek, Oscl_File::SEEKCUR);
if (MP3_SUCCESS != err)
{
iCurrFrameNumber--;
return err;
}
mp3Err = mp3VerifyCRC(mp3HeaderInfo, mp3CDInfo);
if (mp3Err == MP3_CRC_ERR)
{
//wrong CRC skip frame. since crc was wrong there
//could be error in calculating frame size. So try to find sync again.
iCurrFrameNumber--;
currentFilePosn += MP3_FRAME_HEADER_SIZE;
if (MP3_SUCCESS != MP3Utils::SeektoOffset(fp, MP3_FRAME_HEADER_SIZE, Oscl_File::SEEKCUR))
{
return err;
}
uint32 seekOffset = 0;
MP3Utils::SeektoOffset(fp, 0 - MP3_FRAME_HEADER_SIZE, Oscl_File::SEEKCUR);
MP3ErrorType err = mp3FindSync(currentFilePosn, seekOffset, fp);
if (err == MP3_SUCCESS)
{
err = MP3Utils::SeektoOffset(fp, seekOffset, Oscl_File::SEEKCUR);
if (MP3_SUCCESS != err)
{
return err;
}
currentFilePosn += seekOffset;
goto BEGIN;
}
else
{
//no sync found return error
return err;
}
}
else if (mp3Err == MP3_INSUFFICIENT_DATA)
{
iCurrFrameNumber--;
return mp3Err;
}
err = MP3Utils::SeektoOffset(fp, MP3_FRAME_HEADER_SIZE, Oscl_File::SEEKCUR);
if (MP3_SUCCESS != err)
{
iCurrFrameNumber--;
return err;
}
currentFilePosn = MP3Utils::getCurrentFilePosition(fp);
uint32 fileSz = 0;
MP3Utils::getCurrentFileSize(fp, fileSz);
if ((fileSz != 0) && (currentFilePosn + (mp3FrameSizeInBytes - MP3_FRAME_HEADER_SIZE) > (uint32)fileSz))
{
// At EOF
iCurrFrameNumber--;
framesize = 0;
timestamp = GetTimestampForCurrentSample();
if (mp3CDInfo.BitRate > 0)
{
iTimestamp = uint32(timestamp + (OsclFloat) mp3CDInfo.FrameLengthInBytes * 8000.00f / mp3CDInfo.BitRate);
}
if (0 != contentLength)
{
// if content length is known, check for reading beyond EOF
if ((currentFilePosn + mp3FrameSizeInBytes - MP3_FRAME_HEADER_SIZE) >= contentLength)
{
return MP3_END_OF_FILE;
}
}
err = MP3Utils::SeektoOffset(fp, 0 - MP3_FRAME_HEADER_SIZE, Oscl_File::SEEKCUR);
if (MP3_SUCCESS != err)
{
return err;
}
return MP3_INSUFFICIENT_DATA;
}
if (size < mp3FrameSizeInBytes)
{
framesize = mp3FrameSizeInBytes;
iCurrFrameNumber--;
return MP3_FILE_READ_ERR;
}
framesize = mp3FrameSizeInBytes;
timestamp = GetTimestampForCurrentSample();
// update timestamp for next sample
// calculate frameDuration
if (mp3CDInfo.BitRate > 0)
{
iTimestamp = uint32(timestamp + (OsclFloat) mp3CDInfo.FrameLengthInBytes * 8000.00f / mp3CDInfo.BitRate);
}
// Take into account the header (4 Bytes) already read up front
// to obtain the correct Frame Size in Bytes
if (0 != contentLength)
{
// if content length is known, check for reading beyond EOF
if ((currentFilePosn + mp3FrameSizeInBytes - MP3_FRAME_HEADER_SIZE) >= contentLength)
{
return MP3_END_OF_FILE;
}
}
// Take into account the header (4 Bytes) already read up front
// to obtain the correct Frame Size in Bytes
bool res = MP3FileIO::readByteData(fp, mp3FrameSizeInBytes - MP3_FRAME_HEADER_SIZE, buf);
if ((iLocalFileSize == 0) && res == false)
{
iCurrFrameNumber--;
return MP3_INSUFFICIENT_DATA;
}
return MP3_SUCCESS;
}
/***********************************************************************
* FUNCTION: SeekToTimestamp
* DESCRIPTION:
* INPUT/OUTPUT PARAMETERS:
* RETURN VALUE:
* SIDE EFFECTS:
***********************************************************************/
uint32 MP3Parser::SeekToTimestamp(uint32 timestampInMsec)
{
uint32 SeekPosition = 0;
SeekPosition = SeekPointFromTimestamp(timestampInMsec);
if (!((fp->GetFileBufferingCapacity() == 0) && (SeekPosition == iLocalFileSize) && (timestampInMsec == iClipDurationInMsec)))
{
SeekPosition += StartOffset;
}
MP3Utils::SeektoOffset(fp, SeekPosition, Oscl_File::SEEKSET);
return timestampInMsec;
}
/***********************************************************************
* FUNCTION: SeekPointFromTimestamp
* DESCRIPTION:
* INPUT/OUTPUT PARAMETERS:
* RETURN VALUE:
* SIDE EFFECTS:
***********************************************************************/
uint32 MP3Parser::SeekPointFromTimestamp(uint32 &timestamp)
{
uint32 seekPoint = 0;
uint32 seekOffset = 0;
OsclFloat percent = 0;
uint32 binNo = 0;
bool bUseTOCForRepos = false;
uint32 maxTSInTOC = iTOCFilledCount * iBinWidth;
if (iTOCFilledCount > 1 && (timestamp < maxTSInTOC || iDurationScanComplete))
{
bUseTOCForRepos = true;
}
// XING - Use VBR TOC Header
if ((mp3Type == EXINGType) && (iXingHeader.flags & TOC_FLAG))
{
// Interpolate in TOC to get file seek point in bytes
OsclFloat fpc = (OsclFloat)timestamp / (OsclFloat)iClipDurationFromVBRIHeader;
OsclFloat fa, fb, fx;
uint32 pc;
if (fpc < 0.00f)
fpc = 0.00f;
if (fpc > 1.00f)
fpc = 1.00f;
pc = (uint32)(fpc * 100.00f);
fa = (OsclFloat)iXingHeader.TOC[pc];
if (pc > 99)
pc = 99;
if (pc < 99)
fb = (OsclFloat)iXingHeader.TOC[pc+1];
else
fb = 256.00f;
// //////////////////////////////////////////////////////////
// Linearly interpolate between fa and fb
// TOC's appear to max out at TOC[80] = 0cff (255)
// //////////////////////////////////////////////////////////
fx = fa + (fb - fa) * ((100.00f * fpc) - (OsclFloat)pc);
percent = fx;
if (iXingHeader.flags == 15 || iXingHeader.flags == 7 || iXingHeader.flags == 3)
{
seekPoint = (int32)((fx / 256.00f) * (OsclFloat)iXingHeader.bytes);
if ((seekPoint > (uint32)iXingHeader.bytes))
{
seekPoint = 0;
percent = 0;
}
}
else
{
seekPoint = 0;
percent = 0;
}
}
else if (mp3Type == EVBRIType)
// //////////////////////////////////////////////////////////
// CBR or VBRI
{
uint32 i = 0, fraction = 0, SamplesPerFrame;
OsclFloat fLengthMS;
OsclFloat fLengthMSPerTOCEntry;
OsclFloat fAccumulatedTimeMS = 0.0f;
(iVbriHeader.sampleRate >= 32000) ? (SamplesPerFrame = 1152) : (SamplesPerFrame = 576);
fLengthMS = ((OsclFloat)iVbriHeader.frames * (OsclFloat)SamplesPerFrame)
/ (OsclFloat)iVbriHeader.sampleRate * 1000.0f;
fLengthMSPerTOCEntry = (OsclFloat)fLengthMS / (OsclFloat)(iVbriHeader.entriesTOC + 1);
if (timestamp > fLengthMS)
timestamp = (uint32)fLengthMS;
while (fAccumulatedTimeMS <= timestamp)
{
seekPoint += iVbriHeader.TOC[i];
fAccumulatedTimeMS += fLengthMSPerTOCEntry;
i++;
}
fraction = ((int)((((fAccumulatedTimeMS - timestamp) / fLengthMSPerTOCEntry)
+ (1.0f / (2.0f * (OsclFloat)iVbriHeader.fTableEntry))) * (OsclFloat)iVbriHeader.fTableEntry));
seekPoint -= (int)((OsclFloat)iVbriHeader.TOC[i-1] * (OsclFloat)(fraction)
/ (OsclFloat)iVbriHeader.fTableEntry);
}
else if (bUseTOCForRepos)
{
// Use TOC for calculating seek point.
OsclFloat fpc = (OsclFloat)timestamp / (OsclFloat)iClipDurationInMsec;
binNo = (uint32)(fpc * iTOCFilledCount);
uint32 TScurr = binNo * iBinWidth;
while (TScurr > timestamp)
{
binNo--;
TScurr = binNo * iBinWidth;
}
uint32 offsetDiff = iTOC[binNo+1] - iTOC[binNo];
uint32 tsDiff = timestamp - TScurr;
seekPoint = iTOC[binNo] + tsDiff * (offsetDiff / iBinWidth);
}
else
{
/**
* vbri and xing headers are not present. seek offset will be
* calculated on the basis of average bit rate
**/
int32 avgBR = 0;
if (fp->GetFileBufferingCapacity() > 0)
{
avgBR = iAvgBitrateInbps;
}
else
{
if (iDurationScanComplete && (iAvgBitrateInbpsFromCompleteScan > 0))
{
avgBR = iAvgBitrateInbpsFromCompleteScan;
}
else
{
avgBR = iAvgBitrateInbpsFromRandomScan;
}
}
seekPoint = (uint32)((OsclFloat)(avgBR * (OsclFloat)timestamp) / 8000.0f);
}
/**
* If we don't find a sync point we will start playing from the beginning again
* try finding seek points only for local playback
* Since in PD/PS scenarios we might not be having enough data to find the seek point
* We can find the seek point when we are resuming the playback
**/
if (seekPoint > 0 && fp->GetFileBufferingCapacity() == 0)
{
// seek to the reposition point location
MP3Utils::SeektoOffset(fp, seekPoint + StartOffset, Oscl_File::SEEKSET);
uint32 retVal = mp3FindSync(seekPoint + StartOffset, seekOffset, fp);
if (retVal == MP3_SUCCESS)
{
seekPoint += seekOffset;
MP3Utils::SeektoOffset(fp, seekOffset, Oscl_File::SEEKCUR);
if (iDurationScanComplete)
{
uint32 offsetDiff = iTOC[binNo+1] - iTOC[binNo];
timestamp = (binNo * iBinWidth) + (iBinWidth * (seekPoint - iTOC[binNo]) / offsetDiff);
}
}
else if (retVal == MP3_INSUFFICIENT_DATA || retVal == MP3_END_OF_FILE)
{
// if parser hits Insufficent data during local playback or end of file,
// we need to set seekpoint and timestamp to clip duration and node will report as end of track.
seekPoint = iLocalFileSize;
timestamp = iClipDurationInMsec;
iTimestamp = timestamp;
// return from here as we need not compute Current Frame Number
return seekPoint;
}
else
{
seekPoint = 0;
timestamp = 0;
}
}
if (seekPoint > 0)
{
if (iMP3ConfigInfo.FrameSizeUnComp > 0 && iMP3ConfigInfo.SamplingRate > 0)
{
iCurrFrameNumber = (int32)(timestamp * (iMP3ConfigInfo.SamplingRate / iMP3ConfigInfo.FrameSizeUnComp) / 1000.00f);
}
}
else
{
iCurrFrameNumber = 0;
timestamp = 0;
}
iTimestamp = timestamp;
return seekPoint;
}
/***********************************************************************
* FUNCTION: mp3FindSync
* DESCRIPTION: This function reads the whole file searching for a sync
* word. Once it finds one, it check for 4 continuous sync
* words to avoid a false synchronization
* INPUT/OUTPUT PARAMETERS:
* RETURN VALUE:
* SIDE EFFECTS:
***********************************************************************/
MP3ErrorType MP3Parser::mp3FindSync(uint32 seekPoint, uint32 &syncOffset, PVFile* aFile)
{
syncOffset = 0;
iMaxSyncBufferSize = 627; /* default for 192 kbps, 44.1 kHz */
if (aFile->GetFileBufferingCapacity() > 0)
{
iLocalFileSizeSet = (int32)MP3Utils::getCurrentFileSize(aFile, iLocalFileSize);
}
uint32 contentLength = MP3FileIO::getContentLength(aFile);
if ((contentLength != 0) && ((seekPoint + iMaxSyncBufferSize) >= contentLength))
{
// if content length is known, check for reading beyond EOF
return MP3_END_OF_FILE;
}
if ((iLocalFileSize != 0) && (seekPoint + iMaxSyncBufferSize > (uint32)iLocalFileSize))
{
return MP3_INSUFFICIENT_DATA;
}
if (pSyncBuffer)
{
OSCL_ARRAY_DELETE(pSyncBuffer);
pSyncBuffer = NULL;
}
int32 leavecode = 0;
OSCL_TRY(leavecode, pSyncBuffer = OSCL_ARRAY_NEW(uint8, iMaxSyncBufferSize + 1));
if (leavecode || pSyncBuffer == NULL)
{
return MP3_ERROR_UNKNOWN; /* buffer couldn't be allocated */
}
seekPoint = aFile->Tell();
uint32 i = 0;
uint32 j = 0;
uint32 BufferSize = 0;
pSyncBuffer[0] = 0;
bool syncFound = false;
MP3ErrorType mp3Err = MP3_SUCCESS;
uint32 maxSearchOffset = 0;
int32 revSeek = 0;
if (iLocalFileSizeSet)
{
maxSearchOffset = OSCL_MIN(iInitSearchFileSize, iLocalFileSize - seekPoint);
}
else
{
uint32 remBytes = 0;
if (aFile->GetRemainingBytes(remBytes))
{
maxSearchOffset = OSCL_MIN(iInitSearchFileSize, aFile->Tell() + remBytes - seekPoint);
}
}
for (j = 0; j < maxSearchOffset; j += iMaxSyncBufferSize)
{
revSeek = 0;
// Grab a new buffer for a byte by byte search
if (!MP3FileIO::readByteData(aFile, iMaxSyncBufferSize, &pSyncBuffer[1], &BufferSize))
{
if (pSyncBuffer)
{
OSCL_ARRAY_DELETE(pSyncBuffer);
pSyncBuffer = NULL;
}
return MP3_ERROR_UNKNOWN_OBJECT;
}
revSeek -= j;
// Find the first Sync Marker by doing a byte by byte search.
// Once we have found the sync words, the frame is validated.
// For frame header validation, we read four bytes.
// So the search for sync words should be stopped when we have less than
// 4 bytes in the buffer.
if (BufferSize > 3)
{
for (i = 0; i < (BufferSize - 3); i++)
{
if (pSyncBuffer[i] == 0xFF)
{
// MPEG 1, 2
if ((pSyncBuffer[i+1] & 0xF0) == 0xF0)
{
// if partial match is found verify that 4 consecutives sync word are valid
MP3Utils::SeektoOffset(aFile, 0 - (int32) iMaxSyncBufferSize + (int32) i - 1, Oscl_File::SEEKCUR);
mp3Err = IsValidFrame(&(pSyncBuffer[i]), j + i - 1, seekPoint, aFile);
if (mp3Err == MP3_SUCCESS)
{
MP3Utils::SeektoOffset(aFile, 0 - (int32)j - i + 1, Oscl_File::SEEKCUR);
break;
}
else if (mp3Err == MP3_INSUFFICIENT_DATA)
{
if (pSyncBuffer)
{
OSCL_ARRAY_DELETE(pSyncBuffer);
pSyncBuffer = NULL;
}
return mp3Err;
}
else
{
// Drop the frame
MP3Utils::SeektoOffset(aFile, iMaxSyncBufferSize - i + 1, Oscl_File::SEEKCUR);
}
}
// MPEG 2.5
else if ((pSyncBuffer[i+1] & 0xF0) == 0xE0)
{
// if partial match is found verify that 4 consecutives sync word are valid
MP3Utils::SeektoOffset(aFile, 0 - (int32) iMaxSyncBufferSize + (int32) i - 1, Oscl_File::SEEKCUR);
mp3Err = IsValidFrame(&(pSyncBuffer[i]), j + i - 1, seekPoint, aFile);
if (mp3Err == MP3_SUCCESS)
{
MP3Utils::SeektoOffset(aFile, 0 - (int32)j - i + 1, Oscl_File::SEEKCUR);
break;
}
else if (mp3Err == MP3_INSUFFICIENT_DATA)
{
if (pSyncBuffer)
{
OSCL_ARRAY_DELETE(pSyncBuffer);
pSyncBuffer = NULL;
}
return mp3Err;
}
else
{
// Drop the frame
MP3Utils::SeektoOffset(aFile, iMaxSyncBufferSize - i + 1, Oscl_File::SEEKCUR);
}
}
}
}
if (i < (BufferSize - 3))
{
syncFound = true;
break; /* sync was found */
} /* else grab new buffer and keep searching */
pSyncBuffer[0] = pSyncBuffer[iMaxSyncBufferSize];
}
else
{
break;
}
}
if (pSyncBuffer)
{
OSCL_ARRAY_DELETE(pSyncBuffer);
pSyncBuffer = NULL;
}
if (!syncFound)
{
return MP3_END_OF_FILE; /* File does not have any valid sync word */
}
if (iLocalFileSizeSet)
{
/* One valid frame was found -> reset initial size to file size */
iInitSearchFileSize = iLocalFileSize;
}
else
{
uint32 remBytes = 0;
if (aFile->GetRemainingBytes(remBytes))
{
/* One valid frame was found -> reset initial size remaining file size*/
iInitSearchFileSize = OSCL_MIN(iInitSearchFileSize, remBytes);
}
}
syncOffset = j + i - 1; /* set offset */
return MP3_SUCCESS;
}
/***********************************************************************
* FUNCTION: IsValidFrame
* DESCRIPTION:
* INPUT/OUTPUT PARAMETERS:
* RETURN VALUE:
* SIDE EFFECTS:
***********************************************************************/
MP3ErrorType MP3Parser::IsValidFrame(uint8 * pBuffer,
uint32 offset,
uint32 seekPoint,
PVFile* aFile)
{
bool bCRCPresent = false;
MP3ErrorType err = MP3_SUCCESS;
// Is the MP3 Frame Header Valid?
err = IsValidFrameHeader(pBuffer, bCRCPresent, offset, seekPoint, aFile);
return err;
}
/***********************************************************************
* FUNCTION: IsValidFrameHeader
* DESCRIPTION: This function now check the sync word and then with the
* information retrieved from the header, predict the location
* of the following 3 headers. Then, if the sampling frequencies
* and number of channels match for all headers, the frame header
* is considered valid
* INPUT/OUTPUT PARAMETERS:
* RETURN VALUE:
* SIDE EFFECTS:
***********************************************************************/
MP3ErrorType MP3Parser::IsValidFrameHeader(uint8 *mp3Frame, bool &bCRCPresent,
uint32 firstSyncOffset, uint32 seekPoint,
PVFile* aFile)
{
OSCL_UNUSED_ARG(firstSyncOffset);
OSCL_UNUSED_ARG(seekPoint);
PVFile* fpUsed = fp;
if (aFile)
{
fpUsed = aFile;
}
MP3HeaderType mp3HeaderInfo = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
MP3ConfigInfoType mp3CDInfo = {0, 0, 0, 0, 0};
MP3ConfigInfoType mp3CDInfo2 = {0, 0, 0, 0, 0};
MP3ConfigInfoType mp3CDInfo3 = {0, 0, 0, 0, 0};
MP3ConfigInfoType mp3CDInfo4 = {0, 0, 0, 0, 0};
int32 flength = 0;
int32 revSeek = 0;
bool status;
uint32 mp3Header = SwapFileToHostByteOrderInt32(mp3Frame);
bCRCPresent = false;
if (!GetMP3Header(mp3Header, mp3HeaderInfo))
{
return MP3_FILE_HDR_READ_ERR;
}
if (!DecodeMP3Header(mp3HeaderInfo, mp3CDInfo, false))
{
return MP3_FILE_HDR_READ_ERR;
}
// A flag of 0 means the CRC is present
bCRCPresent = !(mp3HeaderInfo.crcFollows);
/*
* Search 4 consecutives header to guarantee that we
* really latch on a valid sync word
*/
flength = mp3CDInfo.FrameLengthInBytes;
MP3ErrorType err = MP3Utils::SeektoOffset(fpUsed, flength, Oscl_File::SEEKCUR);
if (MP3_SUCCESS != err)
{
return err;
}
revSeek -= flength;
if (!MP3FileIO::readByteData(fpUsed, MP3_FRAME_HEADER_SIZE, (uint8 *)&mp3Header))
{
MP3Utils::SeektoOffset(fpUsed, revSeek , Oscl_File::SEEKCUR);
return MP3_INSUFFICIENT_DATA;
}
mp3Header = SwapFileToHostByteOrderInt32((uint8 *) & mp3Header);
status = GetMP3Header(mp3Header, mp3HeaderInfo);
status = DecodeMP3Header(mp3HeaderInfo, mp3CDInfo2, false);
flength = mp3CDInfo2.FrameLengthInBytes;
err = MP3Utils::SeektoOffset(fpUsed, flength - MP3_FRAME_HEADER_SIZE, Oscl_File::SEEKCUR);
if (MP3_SUCCESS != err)
{
MP3Utils::SeektoOffset(fpUsed, revSeek - MP3_FRAME_HEADER_SIZE, Oscl_File::SEEKCUR);
return err;
}
revSeek -= flength;
if (!MP3FileIO::readByteData(fpUsed, MP3_FRAME_HEADER_SIZE, (uint8 *)&mp3Header))
{
MP3Utils::SeektoOffset(fpUsed, revSeek , Oscl_File::SEEKCUR);
return MP3_INSUFFICIENT_DATA;
}
mp3Header = SwapFileToHostByteOrderInt32((uint8 *) & mp3Header);
status = GetMP3Header(mp3Header, mp3HeaderInfo);
status = DecodeMP3Header(mp3HeaderInfo, mp3CDInfo3, false);
flength = mp3CDInfo3.FrameLengthInBytes;
err = MP3Utils::SeektoOffset(fpUsed, flength - MP3_FRAME_HEADER_SIZE, Oscl_File::SEEKCUR);
if (MP3_SUCCESS != err)
{
MP3Utils::SeektoOffset(fpUsed, revSeek - MP3_FRAME_HEADER_SIZE, Oscl_File::SEEKCUR);
return err;
}
revSeek -= flength;
if (!MP3FileIO::readByteData(fpUsed, MP3_FRAME_HEADER_SIZE, (uint8 *)&mp3Header))
{
MP3Utils::SeektoOffset(fpUsed, revSeek, Oscl_File::SEEKCUR);
return MP3_INSUFFICIENT_DATA;
}
revSeek -= MP3_FRAME_HEADER_SIZE;
mp3Header = SwapFileToHostByteOrderInt32((uint8 *) & mp3Header);
status = GetMP3Header(mp3Header, mp3HeaderInfo);
status = DecodeMP3Header(mp3HeaderInfo, mp3CDInfo4, false);
/*
* Check that the sampling rate and the number of channels is
* the same for all the frames (everything else may change)
*/
if ((mp3CDInfo.SamplingRate != mp3CDInfo2.SamplingRate) |
(mp3CDInfo3.SamplingRate != mp3CDInfo4.SamplingRate) |
(mp3CDInfo3.SamplingRate != mp3CDInfo.SamplingRate))
{
MP3Utils::SeektoOffset(fpUsed, revSeek, Oscl_File::SEEKCUR);
return MP3_FILE_HDR_READ_ERR;
}
if ((mp3CDInfo.NumberOfChannels != mp3CDInfo2.NumberOfChannels) |
(mp3CDInfo3.NumberOfChannels != mp3CDInfo4.NumberOfChannels) |
(mp3CDInfo3.NumberOfChannels != mp3CDInfo.NumberOfChannels))
{
MP3Utils::SeektoOffset(fpUsed, revSeek, Oscl_File::SEEKCUR);
return MP3_FILE_HDR_READ_ERR;
}
// seek back to position from where we started
MP3Utils::SeektoOffset(fpUsed, revSeek, Oscl_File::SEEKCUR);
return MP3_SUCCESS;
}
/***********************************************************************
* FUNCTION: mp3VerifyCRC
* DESCRIPTION:
* INPUT/OUTPUT PARAMETERS: verifies the crc if crc check is enabled and
* crc flag is present
* RETURN VALUE:
* SIDE EFFECTS:
***********************************************************************/
MP3ErrorType MP3Parser::mp3VerifyCRC(MP3HeaderType mp3HdrInfo, MP3ConfigInfoType mp3CI)
{
if (!iEnableCrcCalc || mp3HdrInfo.crcFollows)
{
return MP3_SUCCESS;
}
uint32 numberOfBits;
uint32 bound = 32;
uint32 numberOfBytes = 0;
uint8 crcData[2];
if (mp3HdrInfo.chMode == CHANNEL_MODE_JOINT_STEREO)
bound = 4 + mp3HdrInfo.modeExtn * 4;
switch (mp3HdrInfo.layerID)
{
case MPEG_LAYER_I:
numberOfBits = 4 * (mp3CI.NumberOfChannels * bound + (32 - bound));
break;
case MPEG_LAYER_II:
// no check for Layer II
return MP3_SUCCESS;
case MPEG_LAYER_III:
numberOfBits = (mp3HdrInfo.frameVer == FRAME_VESION_MPEG_1) ?
(mp3HdrInfo.chMode == CHANNEL_MODE_MONO ? 17 * 8 : 32 * 8) :
(mp3HdrInfo.chMode == CHANNEL_MODE_MONO ? 9 * 8 : 17 * 8) ;
break;
default:
return MP3_SUCCESS;
}
// Add header size and CRC value.
//CalcCRC16 will take care of removing first 2 bytes of Hdr and 2 bytes of CRCvalue
numberOfBits += MP3_FRAME_HEADER_SIZE * 8 + 16;
numberOfBytes = numberOfBits % 8 ? numberOfBits / 8 + 1 : numberOfBits / 8;
uint8 *buffer = OSCL_ARRAY_NEW(uint8 , numberOfBytes + 1);
int32 revSeek = 0;
if (!MP3FileIO::readByteData(fp, numberOfBytes, (uint8 *)buffer))
{
return MP3_INSUFFICIENT_DATA;
}
revSeek -= numberOfBytes;
uint16 calcCRC16 = CalcCRC16(buffer, numberOfBits);
// read crc from frame
uint32 remBytes = 0;
if (fp->GetRemainingBytes(remBytes))
{
if (remBytes >= MP3_FRAME_HEADER_SIZE)
{
MP3Utils::SeektoOffset(fp, MP3_FRAME_HEADER_SIZE, Oscl_File::SEEKCUR);
revSeek -= MP3_FRAME_HEADER_SIZE;
}
else
{
MP3Utils::SeektoOffset(fp, revSeek, Oscl_File::SEEKCUR);
return MP3_INSUFFICIENT_DATA;
}
}
else
{
MP3Utils::SeektoOffset(fp, revSeek, Oscl_File::SEEKCUR);
return MP3_ERROR_UNKNOWN;
}
if (!MP3FileIO::readByteData(fp, 2, (uint8 *)crcData))
{
MP3Utils::SeektoOffset(fp, revSeek, Oscl_File::SEEKCUR);
return MP3_INSUFFICIENT_DATA;
}
revSeek -= 2;
uint16 crcVal = SwapFileToHostByteOrderInt16(&crcData[0]);
// seek back to original start position
MP3ErrorType err = MP3Utils::SeektoOffset(fp, revSeek, Oscl_File::SEEKCUR);
if (MP3_SUCCESS != err)
{
return err;
}
if (calcCRC16 == crcVal)
{
OSCL_ARRAY_DELETE(buffer);
return MP3_SUCCESS;
}
OSCL_ARRAY_DELETE(buffer);
return MP3_CRC_ERR;
}
uint16 MP3Parser::CalcCRC16(uint8* pBuffer, uint32 dwBitSize)
{
uint32 n;
uint16 tmpchar, crcmask, tmpi;
crcmask = tmpchar = 0;
uint16 crc = 0xffff; // start with inverted value of 0
// start with byte 2 of header
for (n = 16; n < dwBitSize; n++)
{
if (n < 32 || n >= 48) // skip the 2 bytes of the crc itself
{
if (n % 8 == 0)
{
crcmask = 1 << 8;
tmpchar = pBuffer[n/8];
}
crcmask >>= 1;
tmpi = crc & 0x8000;
crc <<= 1;
if (!tmpi ^ !(tmpchar & crcmask))
crc ^= 0x8005;
}
}
crc &= 0xffff; // invert the result
return crc;
}
/***********************************************************************
* FUNCTION: GetSampleCountInFile
* DESCRIPTION:
* INPUT/OUTPUT PARAMETERS:
* RETURN VALUE:
* SIDE EFFECTS:
***********************************************************************/
uint32 MP3Parser::GetSampleCountInFile()
{
return iNumberOfFrames;
}
/***********************************************************************
* FUNCTION: GetMaximumDecodeBufferSize
* DESCRIPTION:
* INPUT/OUTPUT PARAMETERS:
* RETURN VALUE:
* SIDE EFFECTS:
***********************************************************************/
uint32 MP3Parser::GetMaximumDecodeBufferSize()
{
// Compressed Frame Size
uint32 maxBitRate;
uint32 minSamplingRate ;
uint32 frameLengthInBytes;
uint32 samplesPerFrame;
if (iMP3HeaderInfo.frameVer == 3) // MPEG Ver 1
{
maxBitRate = brIndexTableV1[iMP3HeaderInfo.layerID][14];
}
else // MPEG Ver 2, 2.5
{
maxBitRate = brIndexTableV2[iMP3HeaderInfo.layerID][14];
}
samplesPerFrame = spfIndexTable[iMP3HeaderInfo.frameVer][iMP3HeaderInfo.layerID];
minSamplingRate = srIndexTable[((iMP3HeaderInfo.frameVer)*4) + 2];
if (minSamplingRate != 0)
{
frameLengthInBytes = 125 * (maxBitRate * samplesPerFrame) / (minSamplingRate);
}
else
{
frameLengthInBytes = KMAX_MP3FRAME_LENGTH_IN_BYTES; // allow for pad byte
}
return frameLengthInBytes;
}
/***********************************************************************
* FUNCTION: GetFileSize
* DESCRIPTION:
* INPUT/OUTPUT PARAMETERS:
* RETURN VALUE:
* SIDE EFFECTS:
***********************************************************************/
uint32 MP3Parser::GetFileSize()
{
return iLocalFileSize;
}
/***********************************************************************
* FUNCTION: GetTimestampForCurrentSample
* DESCRIPTION:
* INPUT/OUTPUT PARAMETERS:
* RETURN VALUE:
* SIDE EFFECTS:
***********************************************************************/
uint32 MP3Parser::GetTimestampForCurrentSample() const
{
return iTimestamp;
}
uint32 MP3Parser::GetTimestampForSample(int32 aFrameNumber) const
{
uint32 timestamp = 0;
timestamp = (uint32)((1000.00f * (OsclFloat)aFrameNumber *
(OsclFloat)iMP3ConfigInfo.FrameSizeUnComp) /
(OsclFloat)iMP3ConfigInfo.SamplingRate);
return timestamp;
}
/***********************************************************************
* FUNCTION: GetDecoderSpecificInfoSize
* DESCRIPTION:
* INPUT/OUTPUT PARAMETERS:
* RETURN VALUE:
* SIDE EFFECTS:
***********************************************************************/
uint32 MP3Parser::GetDecoderSpecificInfoSize()
{
return ConfigSize;
}
/***********************************************************************
* FUNCTION: GetDecoderSpecificInfoContent
* DESCRIPTION:
* INPUT/OUTPUT PARAMETERS:
* RETURN VALUE:
* SIDE EFFECTS:
***********************************************************************/
uint8 const * MP3Parser::GetDecoderSpecificInfoContent() const
{
return (const uint8 *)&(ConfigData);
}
int32 MP3Parser :: CalculateBufferSizeForHeader(uint8 *VbriHead)
{
int32 tableLength;
int32 tempEntriesTOC, tempSizePerTableEntry;
VbriHead += 18;
tempEntriesTOC = SwapFileToHostByteOrderInt16(VbriHead);
VbriHead += 4;
tempSizePerTableEntry = SwapFileToHostByteOrderInt16(VbriHead);
tableLength = tempEntriesTOC * tempSizePerTableEntry;
int32 returnValue = tableLength + 26; //TOC bytes + Upper header bytes
VbriHead -= 22; //Reset the file pointer to its original place
return returnValue;
}
/***********************************************************************
* Function : IsMp3File
* Purpose : Verifies whether the file passed in is a possibly
* valid mp3 clip
* Input : aFile, file to check
* aInitSearchFileSize, amount of data to use for verification
* Output : None
* Return : error code
* Modified :
***********************************************************************/
MP3ErrorType MP3Parser::IsMp3File(MP3_FF_FILE* aFile, uint32 aInitSearchFileSize)
{
MP3ErrorType errCode = MP3_SUCCESS;
uint8 pFrameHeader[MP3_FRAME_HEADER_SIZE];
uint32 firstHeader = 0;
StartOffset = 0;
int32 revSeek = 0;
// get the file pointer
fp = &(aFile->_pvfile);
errCode = MP3Utils::SeektoOffset(fp, 0, Oscl_File::SEEKSET);
// try to retrieve the file size
if (MP3Utils::getCurrentFileSize(fp, iLocalFileSize))
{
iLocalFileSizeSet = true;
iInitSearchFileSize = OSCL_MIN(aInitSearchFileSize, iLocalFileSize);
if (iLocalFileSize == 0)
{
return MP3_END_OF_FILE;
}
}
if (!iLocalFileSizeSet)
{
uint32 remBytes = 0;
if (fp->GetRemainingBytes(remBytes))
{
iInitSearchFileSize = OSCL_MIN(iInitSearchFileSize, remBytes);
}
}
// seek to the begining position in the file
// verify if the id3 tags are present in this clip
PVID3ParCom tagParser;
iTagSize = 0;
if (true == tagParser.IsID3V2Present(fp, iTagSize))
{
// move the file read pointer to begining of audio data
StartOffset += iTagSize;
}
// seek to the begining position in the file
MP3Utils::SeektoOffset(fp, StartOffset, Oscl_File::SEEKSET);
if (!MP3FileIO::readByteData(fp, MP3_FRAME_HEADER_SIZE, (uint8 *)pFrameHeader))
{
return MP3_INSUFFICIENT_DATA;
}
revSeek = 0 - MP3_FRAME_HEADER_SIZE;
firstHeader = SwapFileToHostByteOrderInt32(pFrameHeader);
if (!GetMP3Header(firstHeader, iMP3HeaderInfo))
{
uint32 seekOffset = 0;
MP3Utils::SeektoOffset(fp, 0 - MP3_FRAME_HEADER_SIZE, Oscl_File::SEEKCUR);
errCode = mp3FindSync(StartOffset, seekOffset, fp);
if (errCode == MP3_SUCCESS)
{
errCode = MP3Utils::SeektoOffset(fp, seekOffset, Oscl_File::SEEKCUR);
if (MP3_SUCCESS != errCode)
{
return errCode;
}
StartOffset += seekOffset;
if (!MP3FileIO::readByteData(fp, MP3_FRAME_HEADER_SIZE, pFrameHeader))
{
return MP3_INSUFFICIENT_DATA;
}
firstHeader = SwapFileToHostByteOrderInt32(pFrameHeader);
if (!GetMP3Header(firstHeader, iMP3HeaderInfo))
{
return MP3_FILE_HDR_READ_ERR;
}
// retrieval of header was successful, try to decode it here.
if (! DecodeMP3Header(iMP3HeaderInfo, iMP3ConfigInfo, false))
{
// Header was invalid, decoding of header failed.
return MP3_FILE_HDR_DECODE_ERR;
}
return MP3_SUCCESS;
}
else if (errCode == MP3_INSUFFICIENT_DATA)
{
MP3Utils::SeektoOffset(fp, fp->Tell() - StartOffset, Oscl_File::SEEKCUR);
return errCode;
}
else
{
// File is not identified with the provided data
return MP3_ERROR_UNKNOWN_OBJECT;
}
}
// retrieval of header was successful, try to decode it here.
if (! DecodeMP3Header(iMP3HeaderInfo, iMP3ConfigInfo, false))
{
// Header was invalid, decoding of header failed.
return MP3_FILE_HDR_DECODE_ERR;
}
// mp3 header was valid it is an mp3 clip
// return success.
return MP3_SUCCESS;
}
uint32 MP3Parser::GetFileOffsetForAutoResume(uint32& aOffset)
{
uint32 ts = GetTimestampForCurrentSample() + 10000;
uint32 SeekPosition = 0;
uint32 seekpoint = SeekPointFromTimestamp(ts);
if (seekpoint)
{
SeekPosition = StartOffset + seekpoint;
}
aOffset = SeekPosition;
return 0;
}
/***********************************************************************
* Function : SetFileSize
* Purpose : Notification from the lib user for the file size,
* Once File size is received, the same is used to
* estimate the clip duration
* Return : error code
* Input : aFileSize
* Output : None
* Modified :
***********************************************************************/
MP3ErrorType MP3Parser::SetFileSize(const uint32 aFileSize)
{
iFileSizeFromExternalSource = aFileSize;
iLocalFileSize = aFileSize;
return MP3_SUCCESS;
}
/**
* Function : EstimateDurationFromExternalFileSize
* Purpose : Estimates the clip duration from external file size
* Return : error code
* Input : None
* Output : Clip duration
* Modified :
**/
MP3ErrorType MP3Parser::EstimateDurationFromExternalFileSize(uint32 &aClipDuration)
{
if (iClipDurationFromEstimation > 0)
{
aClipDuration = iClipDurationFromEstimation;
return MP3_SUCCESS;
}
if (iFileSizeFromExternalSource <= 0 || iMP3ConfigInfo.FrameLengthInBytes <= 0)
{
aClipDuration = 0;
return MP3_ERROR_UNKNOWN;
}
uint32 fileSize = iFileSizeFromExternalSource;
if (iId3TagParser.IsID3V2Present())
{
fileSize -= StartOffset;
}
if (iId3TagParser.IsID3V1Present())
{
// id3v1.x tags are 128 bytes long
fileSize -= ID3_V1_TAG_SIZE;
}
if ((iMP3HeaderInfo.srIndex == 3) || (iMP3HeaderInfo.brIndex == 15) ||
(iMP3HeaderInfo.frameVer == 1) || (iMP3HeaderInfo.layerID != 1))
{
// invalid frame data, can not estimate duration
return MP3_SUCCESS;
}
iClipDurationFromEstimation = (uint32)((OsclFloat)(fileSize * 8000.00f / iAvgBitrateInbps));
aClipDuration = iClipDurationFromEstimation;
return MP3_SUCCESS;
}
/***********************************************************************
* Function : GetDurationFromVBRIHeader
* Purpose : Estimates the clip duration from Vbri/Xing headers
* Return : error code
* Input : None
* Output : Clip duration
* Modified :
***********************************************************************/
MP3ErrorType MP3Parser::GetDurationFromVBRIHeader(uint32 &aClipDuration)
{
if (mp3Type != EVBRIType && mp3Type != EXINGType)
{
return MP3_ERROR_UNKNOWN;
}
if ((mp3Type == EXINGType) && !(iXingHeader.flags & FRAMES_FLAG))
{
return MP3_ERROR_UNKNOWN;
}
if (iClipDurationFromVBRIHeader > 0)
{
aClipDuration = iClipDurationFromVBRIHeader;
return MP3_SUCCESS;
}
if ((iMP3HeaderInfo.srIndex == 3) || (iMP3HeaderInfo.brIndex == 15) ||
(iMP3HeaderInfo.frameVer == 1) || (iMP3HeaderInfo.layerID != 1))
{
// invalid frame data, can not estimate duration
return MP3_ERROR_UNKNOWN;
}
iClipDurationFromVBRIHeader = (uint32)((OsclFloat)(iNumberOfFrames * iSamplesPerFrame) / iSamplingRate * 1000.00f);
aClipDuration = iClipDurationFromVBRIHeader;
return MP3_SUCCESS;
}
/***********************************************************************
* Function : GetDurationFromRandomScan
* Purpose : Estimates the clip duration by average bitrate
* Average bit rate is calculated by randomnly scannning
* predefined number of frames
* Return : error code
* Input : None
* Output : Clip duration
* Modified :
***********************************************************************/
MP3ErrorType MP3Parser::GetDurationFromRandomScan(uint32 &aClipDuration)
{
if (iClipDurationFromRandomScan > 0)
{
aClipDuration = iClipDurationFromRandomScan;
return MP3_SUCCESS;
}
MP3ErrorType status = MP3_SUCCESS;
uint32 currFilePos = MP3Utils::getCurrentFilePosition(fp);
status = ComputeDurationFromNRandomFrames(fp);
if (MP3_ERROR_UNKNOWN != status)
{
uint32 fileSz = iLocalFileSize - StartOffset;
iClipDurationFromRandomScan = (uint32)(fileSz * 8000.00f / iAvgBitrateInbpsFromRandomScan);
aClipDuration = iClipDurationFromRandomScan;
}
MP3Utils::SeektoOffset(fp, currFilePos, Oscl_File::SEEKSET);
return status;
}
/***********************************************************************
* Function : ComputeDurationFromNRandomFrames
* Purpose : Estimates average bit rate by randomnly scannning input
* number of frames
* Return : error code
* Input : numFrames
* Output : iAvgBitrateInbpsFromRandomScan
* Modified :
***********************************************************************/
MP3ErrorType MP3Parser::ComputeDurationFromNRandomFrames(PVFile * fpUsed, int32 aNumFrames, int32 aNumRandomLoc)
{
uint32 firstHeader = 0;
uint8 pFrameHeader[MP3_FRAME_HEADER_SIZE];
uint32 audioOffset = 0;
int32 totBR = 0;
int32 avgBitRate = 0;
int32 framecount = 0;
uint32 randomByteOffset = 0;
int32 audioDataSize = 0;
MP3HeaderType mp3HeaderInfo;
MP3ConfigInfoType mp3ConfigInfo;
MP3ErrorType err = MP3_SUCCESS;
oscl_memset(&mp3ConfigInfo, 0, sizeof(mp3ConfigInfo));
oscl_memset(&mp3HeaderInfo, 0, sizeof(mp3HeaderInfo));
// try to fetch file size
if (iLocalFileSizeSet)
{
audioDataSize = iLocalFileSize;
}
audioDataSize -= StartOffset;
if (iId3TagParser.IsID3V1Present())
{
audioDataSize -= ID3_V1_TAG_SIZE;
}
randomByteOffset = StartOffset;
uint32 skipMultiple = audioDataSize / (aNumRandomLoc + 1);
int32 numSearchLoc = 0;
while (numSearchLoc < aNumRandomLoc)
{
// find random location to which we should seek in order to find
uint32 currFilePosn = MP3Utils::getCurrentFilePosition(fpUsed);
randomByteOffset = currFilePosn + skipMultiple;
if (randomByteOffset > iLocalFileSize)
{
break;
}
// initialize frame count
framecount = 0;
audioOffset = randomByteOffset;
MP3Utils::SeektoOffset(fpUsed, audioOffset, Oscl_File::SEEKSET);
// Find sync
uint32 seekOffset = 0;
err = mp3FindSync(audioOffset, seekOffset, fpUsed);
if (err != MP3_SUCCESS)
{
break;
}
audioOffset += seekOffset;
MP3Utils::SeektoOffset(fpUsed, seekOffset, Oscl_File::SEEKCUR);
// lets check rest of the frames
while (framecount < aNumFrames)
{
// Read 4 bytes
if (!MP3FileIO::readByteData(fpUsed, MP3_FRAME_HEADER_SIZE, pFrameHeader))
{
err = MP3_INSUFFICIENT_DATA;
break;
}
firstHeader = SwapFileToHostByteOrderInt32(pFrameHeader);
// Read header
if (!GetMP3Header(firstHeader, mp3HeaderInfo))
{
err = MP3_FILE_HDR_READ_ERR;
break;
}
// Decode header
if (!DecodeMP3Header(mp3HeaderInfo, mp3ConfigInfo, false))
{
err = MP3_FILE_HDR_DECODE_ERR;
break;
}
MP3Utils::SeektoOffset(fpUsed, mp3ConfigInfo.FrameLengthInBytes - MP3_FRAME_HEADER_SIZE, Oscl_File::SEEKCUR);
framecount++;
// initialize avgBitRate first time only
if (1 == framecount)
{
avgBitRate = mp3ConfigInfo.BitRate;
}
if (mp3ConfigInfo.BitRate != avgBitRate)
{
avgBitRate += (mp3ConfigInfo.BitRate - avgBitRate) / framecount;
}
}
totBR += avgBitRate;
numSearchLoc++;
}
// calculate average bitrate
iAvgBitrateInbpsFromRandomScan = numSearchLoc > 0 ? totBR / numSearchLoc : 0;
if (!iAvgBitrateInbpsFromRandomScan)
{
return MP3_ERROR_UNKNOWN;
}
return err;
}
void MP3Parser::GetDurationFromCompleteScan(uint32 &aClipDuration)
{
if (iClipDurationComputed > 0)
{
aClipDuration = iClipDurationComputed;
return;
}
uint32 samplesPerFrame = spfIndexTable[iMP3HeaderInfo.frameVer][iMP3HeaderInfo.layerID];
uint32 samplingRate = srIndexTable[((iMP3HeaderInfo.frameVer)*4) + iMP3HeaderInfo.srIndex];
OsclFloat samplingRateinKHz = (OsclFloat)samplingRate / 1000;
iClipDurationComputed = (uint32)(iScannedFrameCount * (OsclFloat)(samplesPerFrame / samplingRateinKHz));
aClipDuration = iClipDurationComputed;
}
MP3ErrorType MP3Parser::FillTOCTable(uint32 aFilePos, uint32 aTimeStampToFrame)
{
if (iTOC == NULL)
{
// Not a valid condition this should never happen, except if
// there was memory allocation failure during ParseMP3File.
// Just return Failure here to avoid any further calls of FillTOCTable
return MP3_ERROR_UNKNOWN;
}
if (iDurationScanComplete)
{
iTOC[iTOCFilledCount] = aFilePos;
iTOCFilledCount++;
if (0 == iTimestampPrev)
{
GetDurationFromCompleteScan(iBinWidth);
}
return MP3_SUCCESS;
}
if ((iTOCFilledCount < MAX_TOC_ENTRY_COUNT) && ((aTimeStampToFrame - iTimestampPrev) >= iBinWidth))
{
if (iTimestampPrev != aTimeStampToFrame)
{
if ((aTimeStampToFrame - iTimestampPrev) > iBinWidth)
{
iBinWidth = aTimeStampToFrame - iTimestampPrev;
}
}
// push the file offset into TOC table
iTOC[iTOCFilledCount] = aFilePos - StartOffset;
iTOCFilledCount++;
iTimestampPrev = aTimeStampToFrame;
}
else if (iTOCFilledCount == MAX_TOC_ENTRY_COUNT)
{
// run the compaction algorithm to compress the TOC table
for (uint32 i = 0; i < (MAX_TOC_ENTRY_COUNT / 2); i++)
{
iTOC[i] = iTOC[2*i];
}
iTimestampPrev = iTimestampPrev - iBinWidth;
iBinWidth = 2 * iBinWidth;
iTOCFilledCount = MAX_TOC_ENTRY_COUNT / 2;
}
return MP3_SUCCESS;
}