diff -r 000000000000 -r 71ca22bcf22a mmfenh/advancedaudiocontroller/audiocontrollerpluginsvariant/AdvancedAudioControllerUtility/Src/MP3AudioControllerUtility.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mmfenh/advancedaudiocontroller/audiocontrollerpluginsvariant/AdvancedAudioControllerUtility/Src/MP3AudioControllerUtility.cpp Tue Feb 02 01:08:46 2010 +0200 @@ -0,0 +1,520 @@ +/* +* Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: Class definition for the MP3 utility functions. +* +*/ + + +// INCLUDES +#include "MP3AudioControllerUtility.h" +#include "DebugMacros.h" +#include + +// CONSTANTS +const TUint KFlagsOffset = 4; +const TUint KFramesOffset = 8; + +// Bit rates in bits/sec supported by MPEG2, MPEG1 and MPEG2.5 respectively +const TInt16 cBitRateTable[3][16] = + { + {-1,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0}, + {-1,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0}, + {-1,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0}, + }; + +// Sampling frequencies supported by MPEG2, MPEG1 and MPEG2.5 respectively +const TUint16 cSamplingFrequencyTable[3][4] = + { + {22050,24000,16000,0}, + {44100,48000,32000,0}, + {11025,12000,8000,0}, + }; + +// Maximum MP3 frame size +const TInt KRawMp3MaxFrameSize = 1441; // 320kbit/s @ 32kHz = int((144*320000/32000)+1) + +// The size of MP3 header, header must include bits for determining frame length +const TInt KRawMp3FrameHeaderSize = 5; + + +// ============================ MEMBER FUNCTIONS =============================== + +// ----------------------------------------------------------------------------- +// CMP3AudioControllerUtility::CMP3AudioControllerUtility +// C++ default constructor can NOT contain any code, that +// might leave. +// ----------------------------------------------------------------------------- +// +CMP3AudioControllerUtility::CMP3AudioControllerUtility() + { + } + +// ----------------------------------------------------------------------------- +// CMP3AudioControllerUtility::ConstructL +// Symbian 2nd phase constructor can leave. +// ----------------------------------------------------------------------------- +// +void CMP3AudioControllerUtility::ConstructL() + { + DP0(_L("CMP3AudioControllerUtility::ConstructL")); + } + +// ----------------------------------------------------------------------------- +// CMP3AudioControllerUtility::NewL +// Two-phased constructor. +// ----------------------------------------------------------------------------- +// +EXPORT_C CMP3AudioControllerUtility* CMP3AudioControllerUtility::NewL() + { + DP0(_L("CMP3AudioControllerUtility::NewL")); + + CMP3AudioControllerUtility* self = new(ELeave) CMP3AudioControllerUtility; + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + +// Destructor +EXPORT_C CMP3AudioControllerUtility::~CMP3AudioControllerUtility() + { + iFramesArray.Close(); + } + +// ----------------------------------------------------------------------------- +// CMP3AudioControllerUtility::ScanHeaderL +// Used to scan the header information +// ----------------------------------------------------------------------------- +// +void CMP3AudioControllerUtility::ScanHeaderL( + CMMFDataBuffer* aDataBuf) + { + DP0(_L("CMP3AudioControllerUtility::ScanHeaderL")); + iBitRateFrozen = EFalse; + const TUint8* bufferPtr = aDataBuf->Data().Ptr(); + TInt bufferSize = aDataBuf->Data().Size(); + + TInt scannedBuffer = 0; + + if (iLenMetaData == 0) + { + iSyncOffset = 0; + iLenMetaData = ID3HeaderLength(aDataBuf); + } + + TInt bufferRemaining = bufferSize; + + while (iLenMetaData > 0) + { + if (iLenMetaData >= bufferRemaining) + { + // this buffer contains all metadata + iSyncOffset += bufferRemaining; + iLenMetaData -= bufferRemaining; + User::Leave(KErrNotReady); + } + else + { + iSyncOffset += iLenMetaData; + scannedBuffer += iLenMetaData; + // be sure to check for following id3 tags + bufferRemaining = bufferSize - scannedBuffer; + aDataBuf->SetPosition(scannedBuffer); + iLenMetaData = ID3HeaderLength(aDataBuf); + } + } + + + TInt seekOffset = 0; + aDataBuf->SetPosition(scannedBuffer); + seekOffset = SeekSync(aDataBuf, bufferSize); + + iSyncOffset += seekOffset; // offset to this point from content beginning + scannedBuffer += seekOffset; // offset to this point in this buffer + aDataBuf->SetPosition(scannedBuffer); + + if (seekOffset == bufferSize) + { + User::Leave(KErrNotReady); + } + + DP0(_L("CMP3AudioControllerUtility::ScanHeaderL - Calling FrameInfo")); + + TAudioFrameInfo frameInfo; + TInt frameLen = FrameInfo(bufferPtr+scannedBuffer, bufferSize-scannedBuffer, frameInfo); + + iHeaderOffset = 0; + iBitRate = 0; + iDurationUs = 0; + + iSamplingRate = frameInfo.iSamplingRate; + iBitRate = frameInfo.iBitRate; + iSamplesPerFrame = frameInfo.iFrameSamples; + iChannels = frameInfo.iChannels; + iSamplingRateOut = frameInfo.iSamplingRateOut; + if (iSamplingRate > 0) + { + iFrameTimeMs = (iSamplesPerFrame*1000)/iSamplingRate; + } + + TInt aNumFrames; + iFormatIsVBR = ParseFirstFrame(aDataBuf, &aNumFrames); + iNumFrames = aNumFrames; + + TInt frameBytes = 0; + TInt avgFrameLen = 0; + TInt frames = 0; + // We could have used the bitrate found above for CBR MP3 content and not continue to average + // the bitrate. However, some users can manipulated the file and append other bitrate content. + // Since it is not too difficult, we can update the bitrate above with an averaged bitrate. + // If we want to only use the bitate above for cbr, we would set the bitrate frozen flag and + // on the condition that it's not iFormatIsVBR and the bitrate is non-zero. + for (TInt i=1; i<=100; i++) + { + if (scannedBuffer > bufferSize) + { + break; + } + frameLen = FrameInfo(bufferPtr+scannedBuffer, bufferSize-scannedBuffer, frameInfo); + scannedBuffer += frameLen; + if (frameLen <= 0) + { + break; + } + if (i == 10) // ignore the first 10 frames + { + frameBytes = frameLen; + frames = 1; + } + else + { + frameBytes += frameLen; + frames++; + } + avgFrameLen = frameBytes / frames; // frames is always non-zero here + } + iBitRate = (avgFrameLen * 8 * iSamplingRate) / iSamplesPerFrame; + } + +// ----------------------------------------------------------------------------- +// CMP3AudioControllerUtility::SeekSync +// ----------------------------------------------------------------------------- +// +TInt CMP3AudioControllerUtility::SeekSync(CMMFDataBuffer* aBuf, TInt aBufLen) + { + const TUint bufStart = aBuf->Position(); + const TUint8* buf = aBuf->Data().Ptr() + bufStart; + const TInt KMaxFrames = 3; // number of frames to check + const TInt KNotFound = aBufLen; // sync not found position + TAudioFrameInfo frameInfo; // frame parameters + TInt i = 0; + TInt syncPos = KNotFound; + TInt maxSeek = KMaxFrames; + const TUint8* endPtr = buf+aBufLen-bufStart; + + // Seek a valid frame candidate byte by byte until a valid frame + // is found or all bytes have been checked. + while (buf < endPtr && syncPos == KNotFound) + { + TInt seekCount = 0; + const TUint8* framePtr = buf; + TInt frameBufLen = aBufLen; + syncPos = i; + + // Check the validity of this frame candidate and the nearest next + // frames. If they are not OK, syncPos will be set to KNotFound. + while (framePtr < endPtr && syncPos != KNotFound && seekCount < maxSeek) + { + TInt length = FrameInfo(framePtr, frameBufLen, frameInfo); + + if (length == 0) + { + syncPos = KNotFound; + } + if ((length > 0) && (frameInfo.iBitRate < 0)) + { + maxSeek = 1; // free formatcase + } + framePtr += length; + frameBufLen -= length; + seekCount++; + + // consider SYNC not found if we reach end of buffer before finding 3 SYNC frames + if ((framePtr >= endPtr) && (seekCount < maxSeek)) + { + syncPos = KNotFound; + buf += (aBufLen-1); // force an exit from while loop + } + + } + buf++; aBufLen--; i++; + } + return syncPos; + } + +// ----------------------------------------------------------------------------- +// CMP3AudioControllerUtility::FrameInfo +// ----------------------------------------------------------------------------- +// +TInt CMP3AudioControllerUtility::FrameInfo( + const TUint8* aBuf, + TInt aBufLen, + TAudioFrameInfo& aInfo) + { + TInt length = 0; + TUint temp; + TUint lTempVal; + + if (aBufLen >= KRawMp3FrameHeaderSize) + { + // Extract header fields to aInfo and check their bit syntax + // (including the sync word!). If the syntax is not OK the length + // is set to zero. + + temp = 0; + temp = aBuf[0] << 24; + temp |= (aBuf[1] << 16); + temp |= (aBuf[2] << 8); + temp |= aBuf[3]; + if (((temp >> 21) & 0x7FF) != 0x7FF) + { + return length; + } + + lTempVal = (temp >> 19) & 0x03; + switch (lTempVal) + { + case 0: + aInfo.iId = 2; // MPEG2.5 + aInfo.iFrameSamples = 576; + break; + case 1: + return length; + case 2: + aInfo.iId = 0; // MPEG 2 + aInfo.iFrameSamples = 576; + break; + case 3: + aInfo.iId = 1; // MPEG 1 + aInfo.iFrameSamples = 1152; + break; + } + + lTempVal = (temp >> 17) & 0x03; + if (lTempVal != 1) + { + return length; + } + + lTempVal = (temp >> 12) & 0x0F; + aInfo.iBitRate = cBitRateTable[aInfo.iId][lTempVal]*1000; + + if (aInfo.iBitRate == 0) + { + return length; + } + + lTempVal = (temp >> 10) & 0x03; + if (lTempVal == 3) + { + return length; + } + else + { + aInfo.iSamplingRate = cSamplingFrequencyTable[aInfo.iId][lTempVal]; + } + + aInfo.iPadding = (temp >> 9) & 0x01; + + lTempVal = (temp >> 6) & 0x03; + aInfo.iMode = lTempVal; + + if (lTempVal == 3) + { + aInfo.iChannels = 1; + } + else + { + aInfo.iChannels = 2; + } + + aInfo.iSamplingRateOut = aInfo.iSamplingRate; +// aInfo.iChannelsOut = 2; /* always set to stereo output */ + aInfo.iChannelsOut = aInfo.iChannels; + aInfo.iFrameSamplesOut = aInfo.iFrameSamples; + + if (aInfo.iBitRate == -1) + { + // For free mode operation + length = KRawMp3MaxFrameSize; + } + + if (aInfo.iSamplingRate > 0 && aInfo.iBitRate > 0) + { + length = (144*aInfo.iBitRate)/aInfo.iSamplingRate; + + if (aInfo.iId != 1) + { + length >>= 1; /*for MPEG2 and MPEG2.5 */ + } + + if (aInfo.iPadding) + { + length++; + } + } + + aInfo.iFrameSize = length; + iChannelsOut = aInfo.iChannelsOut; + } + return length; + } + +// ----------------------------------------------------------------------------- +// CMP3AudioControllerUtility::SetCodecConfigData +// ----------------------------------------------------------------------------- +// +void CMP3AudioControllerUtility::SetCodecConfigData( + RArray& aCodecConfig) + { +// aCodecConfig[0] = 0; /*(iChannels == 2) ? 0 : 1*/; // don't have decoder downmix stereo to mono + // the sw codec always outputs 2 ch for mono and stereo, + // but for mono, we will downmix the 2ch to mono out + aCodecConfig[0] = (iChannels == 2) ? 0 : 1; // stereo to mono downmix + aCodecConfig[5] = iSamplingRate; + aCodecConfig[8] = iSamplesPerFrame; + } + +// ----------------------------------------------------------------------------- +// CMP3AudioControllerUtility::ParseFirstFrame +// ----------------------------------------------------------------------------- +// +TBool CMP3AudioControllerUtility::ParseFirstFrame( + const CMMFDataBuffer* aBuf, + TInt* aNumFrames) + { + _LIT8(KTagXing, "Xing"); + _LIT8(KTagVBRI, "VBRI"); + +// const TInt tocPresent = 0x00000001; +// const TInt bytesPresent = 0x00000002; + const TInt framesPresent = 0x00000004; +// const TInt qualityPresent = 0x00000008; + + TBool firstFrameIsVBR = EFalse; + + TPtrC8 searchBuf; + TInt numFrames = 0; + + // Set search buffer +// searchBuf.Set(aBuf->Data()); + const TUint8* ptr = aBuf->Data().Ptr(); + TInt pos = aBuf->Position(); + TInt len = aBuf->Data().Size(); + searchBuf.Set(ptr+pos, len-pos); + + TInt startTagXing = searchBuf.Find (KTagXing); + TInt startTagVBRI = searchBuf.Find (KTagVBRI); + + + if (startTagXing != KErrNotFound) + { + TInt flags = searchBuf[startTagXing+KFlagsOffset]; + flags = (flags << 8) | searchBuf[startTagXing+KFlagsOffset+1]; + flags = (flags << 8) | searchBuf[startTagXing+KFlagsOffset+2]; + flags = (flags << 8) | searchBuf[startTagXing+KFlagsOffset+3]; + firstFrameIsVBR = ETrue; + + if (flags & framesPresent) + { + numFrames = searchBuf[startTagXing+KFramesOffset]; + numFrames = (numFrames << 8) | searchBuf[startTagXing+KFramesOffset+1]; + numFrames = (numFrames << 8) | searchBuf[startTagXing+KFramesOffset+2]; + numFrames = (numFrames << 8) | searchBuf[startTagXing+KFramesOffset+3]; + } + } + else if (startTagVBRI != KErrNotFound) + { + numFrames = searchBuf[startTagVBRI+14]; + numFrames = (numFrames << 8) | searchBuf[startTagVBRI+14+1]; + numFrames = (numFrames << 8) | searchBuf[startTagVBRI+14+2]; + numFrames = (numFrames << 8) | searchBuf[startTagVBRI+14+3]; + firstFrameIsVBR = ETrue; + } + else + { + firstFrameIsVBR = EFalse; + } + + *aNumFrames = numFrames; + return firstFrameIsVBR; + } + +// ----------------------------------------------------------------------------- +// CMP3AudioControllerUtility::FrameHeaderSize +// Returns the Frame Header Size for the MP3 file. +// ----------------------------------------------------------------------------- +// +TInt CMP3AudioControllerUtility::FrameHeaderSize() + { + return KRawMp3FrameHeaderSize; + } + +// ----------------------------------------------------------------------------- +// CMP3AudioControllerUtility::Duration +// ----------------------------------------------------------------------------- +// +TInt64 CMP3AudioControllerUtility::Duration() + { + if (iFormatIsVBR && (iNumFrames > 0)) + { + if (iSamplingRateOut > 0) + { // numframes may not be present + iDurationUs = ((TInt64)iSamplesPerFrame*(TInt64)iNumFrames*(TInt64)1000000)/iSamplingRateOut; + } + } + else + { + CAdvancedAudioUtility::Duration(); + } + return iDurationUs; + } + +// ----------------------------------------------------------------------------- +// CMP3AudioControllerUtility::BitRate +// ----------------------------------------------------------------------------- +// +TInt CMP3AudioControllerUtility::BitRate() + { + if (iFormatIsVBR && (iNumFrames > 0)) + { + Duration(); // for VBR bitrate depends on duration + TInt nonAudioBytes = iSyncOffset+iHeaderOffset; + TInt audioBytes = iClipSize - nonAudioBytes; + if (iDurationUs > 0) + { + iBitRate = 8*(((TInt64)audioBytes*(TInt64)1000000)/iDurationUs); + } + } + else + { + CAdvancedAudioUtility::BitRate(); + } + return iBitRate; + } + +// ========================== OTHER EXPORTED FUNCTIONS ========================= + +// End of File +