diff -r 000000000000 -r 71ca22bcf22a mmserv/metadatautility/Src/MetaDataParserID3v2.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mmserv/metadatautility/Src/MetaDataParserID3v2.cpp Tue Feb 02 01:08:46 2010 +0200 @@ -0,0 +1,752 @@ +/* +* Copyright (c) 2004 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: This class provides the base class for ID3v2 parsers. +* +*/ + + + +// INCLUDE FILES +#include "MetaDataParserID3v2.h" +#include "MetaDataParserID3v22.h" +#include "MetaDataParserID3v23.h" +#include "MetaDataParserID3v24.h" +#include "MetaDataSourceDescriptor.h" + +#include +#include +#include +#include + +// CONSTANTS +// (ID3v2 specification found in www.id3.org) +const TInt KID3v2HeaderLength = 10; +const TInt KID3v2TagLength = 3; + +const TInt KMinimumConfidenceRequired = 90; +const TInt KMinimumJapaneseConfidenceRequired = 75; +const TUint KParseFromMemory = 1000000; + +//Album Type +const TInt KPictureTypeOther = 0x00; +const TInt KPictureTypePixel = 0x01; +const TInt KPictureTypeFileIcon = 0x02; +const TInt KPictureTypeFrontCover = 0x03; +const TInt KPictureTypeBackCover = 0x04; + +// Album Type priority +const TInt K1stAlbumArt = KPictureTypeOther; +const TInt K2ndAlbumArt = KPictureTypeFrontCover; +const TInt K3rdAlbumArt = KPictureTypeBackCover; + +_LIT8( KID3v2Tag, "ID3" ); + +// ============================ MEMBER FUNCTIONS =============================== + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v2::CMetaDataParserID3v2 +// C++ default constructor can NOT contain any code, that +// might leave. +// ----------------------------------------------------------------------------- +// +CMetaDataParserID3v2::CMetaDataParserID3v2( + CMetaDataSource& aSource ) + : iSource(&aSource), + iParseFromDescriptor(EFalse), + iAlbumType(-1), + iAlbumPtr(NULL) + { + } + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v2::CreateID3v2ParserL +// Two-phased constructor. +// ----------------------------------------------------------------------------- +// +CMetaDataParserID3v2* CMetaDataParserID3v2::CreateID3v2ParserL( + CMetaDataSource& aSource ) + { +#ifdef _DEBUG + RDebug::Print(_L("CMetaDataParserID3v2::CreateID3v2ParserL")); +#endif + TInt version = VersionL(aSource); + + CMetaDataParserID3v2* parser = NULL; + switch ( version ) + { + case 2: + parser = STATIC_CAST( CMetaDataParserID3v2*, CMetaDataParserID3v22::NewL(aSource) ); + break; + case 3: + parser = STATIC_CAST( CMetaDataParserID3v2*, CMetaDataParserID3v23::NewL(aSource) ); + break; + case 4: + parser = STATIC_CAST( CMetaDataParserID3v2*, CMetaDataParserID3v24::NewL(aSource) ); + break; + default: // KErrNotFound + break; + } + return parser; + } + +// Destructor +CMetaDataParserID3v2::~CMetaDataParserID3v2() + { + if(iAutoDetectChinese || iAutoDetectRussian || iAutoDetectJapanese) + { + delete iCharacterSet; + delete iTopCharacterSet; + } + if(iParseFromDescriptor) + { + delete iTag; + delete iSourceDes; + } + if ( iAlbumPtr ) + { + delete iAlbumPtr; + } + + } + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v2::ParseL +// ----------------------------------------------------------------------------- +// +void CMetaDataParserID3v2::ParseL( + const RArray& aWantedFields, + CMetaDataFieldContainer& aContainer ) + { +#ifdef _DEBUG + RDebug::Print(_L("CMetaDataParserID3v2::ParseL")); +#endif + iContainer = &aContainer; + PrepareToParseL(); + CRepository *metadataRepository = CRepository::NewL(KCRUidMetadataUtility); + TInt err = KErrNone; + err = metadataRepository->Get(KMetadataUtilityAutoDetectChineseChars, iAutoDetectChinese); + if(err) + { + iAutoDetectChinese = EFalse; + } + err = metadataRepository->Get(KMetadataUtilityAutoDetectJapaneseChars, iAutoDetectJapanese); + if(err) + { + iAutoDetectJapanese = EFalse; + } + delete metadataRepository; + TLanguage lang = User::Language(); + if(lang == ELangRussian || lang == ELangUkrainian) + { + iAutoDetectRussian = ETrue; + } + if(iAutoDetectChinese || iAutoDetectRussian || iAutoDetectJapanese) + { + CreateCharacterSetsL(); + } + // choose if want to parse from descriptor + if(iFrameDataSize < KParseFromMemory) // 1Mb + { + iTag = HBufC8::NewL(iFrameDataSize); + TPtr8 des( iTag->Des() ); + iSource->ReadL(0, des); + + iSourceDes = CMetaDataSourceDescriptor::NewL(des); + iSource = iSourceDes; + iParseFromDescriptor = ETrue; + } + if ( aWantedFields.Count() == 0 ) + { + ParseFramesL(); + } + else + { + ParseFramesL(aWantedFields); + } + } + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v2::ParseFramesL +// ----------------------------------------------------------------------------- +// +void CMetaDataParserID3v2::ParseFramesL() + { +#ifdef _DEBUG + RDebug::Print(_L("CMetaDataParserID3v2::ParseFramesL")); +#endif + TMetaDataFieldId fieldId; + TInt frameSize; + TInt frameHeaderSize = 0; + while ( iFrameOffset < ( iFrameDataSize - 1 ) ) + { + GetNextFieldL(fieldId, frameHeaderSize, frameSize); + + if ( frameHeaderSize == 0 ) + { + // We have hit the padding -> no more fields to read + break; + } + + if ( frameSize < 0 || frameSize > ( iFrameDataSize - iFrameOffset ) ) + { + // Invalid frame size -> stop + break; + } + TInt err = KErrNone; // ignore err, as some entry may be extracted without exception + if ( fieldId != EUnknownMetaDataField ) + { + switch ( fieldId ) + { + case EMetaDataSongTitle: + case EMetaDataArtist: + case EMetaDataAlbum: + case EMetaDataAlbumTrack: + case EMetaDataComposer: + case EMetaDataCopyright: + case EMetaDataOriginalArtist: + TRAP(err, GetTextInfoL( fieldId, frameSize )); + break; + + case EMetaDataGenre: + TRAP(err, GetGenreL( frameSize )); + break; + + case EMetaDataYear: + TRAP(err, GetTextYearL( frameSize )); + break; + + case EMetaDataComment: + TRAP(err, GetCommentL( frameSize )); + break; + + case EMetaDataUrl: + TRAP(err, GetUrlL( frameSize )); + break; + + case EMetaDataUserUrl: + TRAP(err, GetUserUrlL( frameSize )); + break; + + case EMetaDataJpeg: + // check for 1st album art + if (iAlbumType != K1stAlbumArt) + { + TRAP(err,GetJpegL( frameSize )); + } + break; + + case EMetaDataDuration: + TRAP(err, GetDurationL( frameSize )); + break; + + case EMetaDataDate: + TRAP(err, GetTextDateL( frameSize )); + break; + + case EMetaDataRating: + TRAP(err, GetRatingL( frameSize )); + break; + + default: + break; + } + } + iFrameOffset += (frameSize + frameHeaderSize); + } + + // retrieve the album art + TRAPD (error, RetrieveAlbumArtL()); + if (error) + { + #ifdef _DEBUG + RDebug::Print(_L("CMetaDataParserID3v2::ParseFramesL - RetrieveAlbumArt leave")); + #endif + } + + } + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v2::ParseFramesL +// ----------------------------------------------------------------------------- +// +void CMetaDataParserID3v2::ParseFramesL( + const RArray& aWantedFields ) + { +#ifdef _DEBUG + RDebug::Print(_L("CMetaDataParserID3v2::ParseFramesL - 2")); +#endif + TMetaDataFieldId fieldId; + TInt frameSize; + TInt frameHeaderSize; + while ( iFrameOffset < ( iFrameDataSize - 1 ) ) + { + GetNextFieldL(fieldId, frameHeaderSize, frameSize); + + if ( frameHeaderSize == 0 ) + { + // We have hit the padding -> no more fields to read + break; + } + + if ( frameSize < 0 || frameSize > ( iFrameDataSize - iFrameOffset ) ) + { + // Invalid frame size -> stop + break; + } + TInt err = KErrNone; + if ( fieldId != EUnknownMetaDataField ) + { + // Look for it in the wanted field array + TInt count( aWantedFields.Count() ); + for ( TInt i = 0; i < count; i++ ) + { + if ( aWantedFields[ i ] == fieldId ) + { + switch ( fieldId ) + { + case EMetaDataSongTitle: + case EMetaDataArtist: + case EMetaDataAlbum: + case EMetaDataAlbumTrack: + case EMetaDataComposer: + case EMetaDataCopyright: + case EMetaDataOriginalArtist: + TRAP(err, GetTextInfoL( fieldId, frameSize )); + break; + + case EMetaDataGenre: + TRAP(err, GetGenreL( frameSize )); + break; + + case EMetaDataYear: + TRAP(err, GetTextYearL( frameSize )); + break; + + case EMetaDataComment: + TRAP(err, GetCommentL( frameSize )); + break; + + case EMetaDataUrl: + TRAP(err, GetUrlL( frameSize )); + break; + + case EMetaDataJpeg: + // check for 1st album art + if (iAlbumType != K1stAlbumArt) + { + TRAP(err,GetJpegL( frameSize )); + } + break; + + case EMetaDataUserUrl: + TRAP(err, GetUserUrlL( frameSize )); + break; + + case EMetaDataDuration: + TRAP(err, GetDurationL( frameSize )); + break; + + case EMetaDataDate: + TRAP(err, GetTextDateL( frameSize )); + break; + + case EMetaDataRating: + TRAP(err, GetRatingL( frameSize )); + break; + + default: + break; + } + } + } + } + iFrameOffset += (frameSize + frameHeaderSize); + } + + // retrieve the album art + TRAPD (error, RetrieveAlbumArtL()); + if (error) + { + #ifdef _DEBUG + RDebug::Print(_L("CMetaDataParserID3v2::ParseFramesL-2 - RetrieveAlbumArt leave")); + #endif + } + + } + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v2::RetrieveAlbumArtL +// ----------------------------------------------------------------------------- +// +void CMetaDataParserID3v2::RetrieveAlbumArtL() + { + // If album art exists, add to container + if ( iAlbumPtr != NULL ) + { + TPtr8 des( iAlbumPtr->Des() ); + TPtrC8 pic = des.Mid(iAlbumOffset); + TInt length = pic.Length(); + if ( length ) + { + iContainer->AppendL( EMetaDataJpeg, pic ); + } + + // reset the album related variables + iAlbumType = -1; + iAlbumOffset = 0; + delete iAlbumPtr; + iAlbumPtr = NULL; + } + } + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v2::Handlev2GetGenreL +// ----------------------------------------------------------------------------- +// +void CMetaDataParserID3v2::HandleV2GetGenreL( TInt aSize, TInt aKID3v2FrameHeaderLength, TInt aKID3v2GenreNameLength ) + { + #ifdef _DEBUG + RDebug::Print(_L("CMetaDataParserID3v2::GetGenreL")); + #endif + + HBufC8* frame = HBufC8::NewLC( aSize ); + TPtr8 des( frame->Des() ); + + iSource->ReadL( iFrameOffset + aKID3v2FrameHeaderLength, des, aSize ); + + if ( des.Length() < aSize ) + { + // Partial frame + User::Leave( KErrCorrupt ); + } + + TInt encoding = (TInt) (des.Left(1))[0]; + TPtrC8 info = StripTrailingZeroes( des.Mid(1), encoding); + TInt length = info.Length(); + + if(length <= 0) + { + CleanupStack::PopAndDestroy(); // frame + return; + } + else + { + length = (length > aKID3v2GenreNameLength) ? length : aKID3v2GenreNameLength; + } + + HBufC8* genreData = HBufC8::NewLC( length ); + TPtr8 temp( genreData->Des() ); + temp.Copy(info); + + if ( encoding == 0 ) + { + if ( temp[0] == KID3v2format ) + { + //temp.Delete(0,1); + TLex8 lex(temp.Ptr() + 1); + TInt genreID = 0; + TInt err = lex.Val(genreID); + if(err != KErrNone) + { + CleanupStack::PopAndDestroy(2); // genreData, frame + return; + } + if(genreID >= 0 && genreID <= 125 || genreID == 199) + { + temp.FillZ(0); + MapID3GenreToStringL(genreID,temp); + } + } + + if( length ) + { + HBufC* data16 = HBufC::NewLC( length ); + TPtr unicode( data16->Des() ); + if ( ConvertToUnicodeL(encoding, temp, unicode) == KErrNone ) + { + iContainer->AppendL( EMetaDataGenre, unicode ); + } + CleanupStack::PopAndDestroy(data16); // data16 + } + } + else // non ASCII + { + HBufC* dataGen16 = HBufC::NewLC( length ); + TPtr unicodeGen( dataGen16->Des() ); + if ( ConvertToUnicodeL(encoding, temp, unicodeGen) == KErrNone ) + { + if (temp.Length() <= 2) + { + CleanupStack::PopAndDestroy(3); // dataGen16, genreData, frame + return; + } + temp = temp.Mid(2); + if(temp[0] == KID3v2format) + { + TLex16 lex(unicodeGen.Ptr()+1); + TInt val=0; + TInt err = lex.Val(val); + if(err != KErrNone) + { + CleanupStack::PopAndDestroy(3); // dataGen16, genreData, frame + return; + } + + if(val >= 0 && val <= 125 || val == 199) + { + unicodeGen.FillZ(0); + MapID3GenreToStringL(val,unicodeGen); + } + } + iContainer->AppendL( EMetaDataGenre, unicodeGen ); + } + CleanupStack::PopAndDestroy(dataGen16); // dataGen16 + } + CleanupStack::PopAndDestroy(genreData); // genreData + CleanupStack::PopAndDestroy(frame); // frame + } + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v2::VersionL +// ----------------------------------------------------------------------------- +// +TInt CMetaDataParserID3v2::VersionL( + CMetaDataSource& aSource ) + { + TInt size = 0; + aSource.Size( size ); + if ( size < KID3v2HeaderLength ) + { + // This isn't ID3v2 + return KErrNotFound; + } + + TBuf8 header; + aSource.ReadL( header ); + // ID3v2 header consists of following parts: + // - identifier "ID3", 3 bytes + // - version, 2 bytes + // - flags, 1 byte + // - data length, 4 bytes + + if ( header.Left( KID3v2TagLength ).Compare( KID3v2Tag ) != 0 ) + { + return KErrNotFound; + } + + TInt frameSize = 0; + for ( TInt i = 6; i < 10; i++ ) + { + frameSize <<= 7; + frameSize |= header[i] & 0x7f; + } + + if ( size < ( frameSize + KID3v2HeaderLength ) ) + { + // Partial ID3v2 tag + User::Leave( KErrCorrupt ); + } + + return (TInt) header[3]; + } + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v2::CreateCharacterSetsL +// ----------------------------------------------------------------------------- +// +void CMetaDataParserID3v2::CreateCharacterSetsL() + { + // Get list of charconv supported character sets + iCharacterSet = CCnvCharacterSetConverter::CreateArrayOfCharacterSetsAvailableL(iFs); + iTopCharacterSet = new (ELeave) CArrayFixFlat(12); + GenerateTopCharacterSetsL(); + } + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v1::DetectCharacterSetL +// ----------------------------------------------------------------------------- +// +TInt CMetaDataParserID3v2::DetectCharacterSetL( + const TDesC8& aDesc) + { + TInt confidence = 0; + TInt highestConfidence = 0; + TUint charSetId; + TUint highestConfidencecharSetId = 0; + + CCnvCharacterSetConverter* charSetConv = CCnvCharacterSetConverter::NewLC(); + TInt count = iTopCharacterSet->Count(); + for ( TInt i=0; i < iTopCharacterSet->Count(); i++) + { + charSetId = iTopCharacterSet->At(i).Identifier(); + charSetConv->ConvertibleToCharSetL(confidence, charSetId, *iTopCharacterSet, aDesc); + if ( confidence > highestConfidence ) + { + highestConfidence = confidence; + highestConfidencecharSetId = charSetId; + } + } + CleanupStack::PopAndDestroy(charSetConv); +#ifdef _DEBUG + RDebug::Print(_L("CMetaDataParserID3v1::DetectCharacterSetL :-> Confidence[%d] CharSetId[%x]"), + highestConfidence, highestConfidencecharSetId); +#endif + + if ( (highestConfidence >= KMinimumConfidenceRequired) || (iAutoDetectJapanese && highestConfidence >= KMinimumJapaneseConfidenceRequired )) + { + iCharacterSetId = highestConfidencecharSetId; + return KErrNone; + } + else + { + return KErrNotFound; + } + } + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v2::AutoDetectL +// ----------------------------------------------------------------------------- +// +TInt CMetaDataParserID3v2::AutoDetectL(const TDesC8& aDesc, + TDes16& aUnicode) +{ + if ( iCharacterSetId == 0 ) + { + if ( DetectCharacterSetL(aDesc) == KErrNotFound ) + { + return KErrNotFound; + } + } + + CCnvCharacterSetConverter* charSetConv = CCnvCharacterSetConverter::NewLC(); + TInt state = CCnvCharacterSetConverter::KStateDefault; + TInt numOfUnconvertibleChars = 0; + + charSetConv->PrepareToConvertToOrFromL(iCharacterSetId, *iCharacterSet, iFs); + TInt retVal = charSetConv->ConvertToUnicode(aUnicode, aDesc, state, numOfUnconvertibleChars); +#ifdef _DEBUG + RDebug::Print(_L("CMetaDataParserID3v1::ConvertToUnicode :-> Tag Size[%d] Unicode Tag Size[%d]Bytes Unconverted[%d] retVal[%d]"), + aDesc.Length(), aUnicode.Length(), numOfUnconvertibleChars, retVal); +#endif + if ( retVal < 0 ) + { + CleanupStack::PopAndDestroy(); // charSetConv + return retVal; + //return KErrGeneral; + } + + if ( retVal > 0 || numOfUnconvertibleChars > 0 ) + { + // This is different character set. Need to auto detect again + if ( DetectCharacterSetL(aDesc) == KErrNotFound ) + { + CleanupStack::PopAndDestroy(); // charSetConv + return KErrNotFound; + } + state = CCnvCharacterSetConverter::KStateDefault; + numOfUnconvertibleChars = 0; + charSetConv->PrepareToConvertToOrFromL(iCharacterSetId, *iCharacterSet, iFs); + retVal = charSetConv->ConvertToUnicode(aUnicode, aDesc, state, numOfUnconvertibleChars); +#ifdef _DEBUG + RDebug::Print(_L("CMetaDataParserID3v1::ConvertToUnicode :-> Tag Size[%d] Unicode Tag Size[%d]Bytes Unconverted[%d] retVal[%d]"), + aDesc.Length(), aUnicode.Length(), numOfUnconvertibleChars, retVal); +#endif + if ( retVal != 0 || numOfUnconvertibleChars > 0 ) + { + CleanupStack::PopAndDestroy(); // charSetConv + return KErrGeneral; + } + } + CleanupStack::PopAndDestroy(); // charSetConv + return KErrNone; +} + + + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v2::Version() +// ----------------------------------------------------------------------------- +// +TID3Version CMetaDataParserID3v2::ID3Version() +{ + return EID3Version2; +} + + +//----------------------------------------------------------------------------- +// CMetaDataParserID3v2::NeedRetrieveAlbumArt( TInt aPicType ) +//----------------------------------------------------------------------------- +// +TBool CMetaDataParserID3v2::NeedRetrieveAlbumArt( TInt aPicType ) +{ + TBool ret = EFalse; + + switch (iAlbumType) + { + case K1stAlbumArt : + // return EFalse, no need to retrieve another album art + break; + case K2ndAlbumArt : + if (aPicType == K1stAlbumArt) + { + ret = ETrue; + } + break; + case K3rdAlbumArt : + if ((aPicType == K1stAlbumArt) || + (aPicType == K2ndAlbumArt)) + { + ret = ETrue; + } + break; + default: + // There is no album art, or it is a priority album art + if ((iAlbumType == -1) || + (aPicType == K1stAlbumArt) || + (aPicType == K2ndAlbumArt) || + (aPicType == K3rdAlbumArt)) + { + ret = ETrue; + } + // if the old one is bad, and the new one is good, use it + else + { + if ( IgnoreAlbumType(iAlbumType) && !IgnoreAlbumType(aPicType) ) + { + // get the new one + ret = ETrue; + } + } + + } + + return ret; +} + + +//----------------------------------------------------------------------------- +// CMetaDataParserID3v2::IgnoreAlbumType( TInt aPicType ) +//----------------------------------------------------------------------------- +// +TBool CMetaDataParserID3v2::IgnoreAlbumType (TInt aPicType) +{ + if (aPicType == KPictureTypePixel || aPicType == KPictureTypeFileIcon) + { + return ETrue; + } + + return EFalse; + +} +// End of File