diff -r 000000000000 -r 71ca22bcf22a mmserv/metadatautility/Src/MetaDataParserID3v22.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mmserv/metadatautility/Src/MetaDataParserID3v22.cpp Tue Feb 02 01:08:46 2010 +0200 @@ -0,0 +1,755 @@ +/* +* 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 implements parsing of ID3v2.2 according to the +* specification found in www.id3.org. Note that only a selected +* frames are supported at this time. +* +*/ + + + +// INCLUDE FILES +#include + +#include "MetaDataParserID3v22.h" + +// CONSTANTS +// (ID3v2.2 specification found in www.id3.org) +const TInt KID3v2HeaderLength = 10; +const TInt KID3v2FrameHeaderLength = 6; +const TInt KID3v2FrameIdLength = 3; +const TInt KID3v2GenreNameLength = 24; + +_LIT8( KID3v2FrameIdTitle, "TT2" ); +_LIT8( KID3v2FrameIdArtist, "TP1" ); +_LIT8( KID3v2FrameIdAlbum, "TAL" ); +_LIT8( KID3v2FrameIdYear, "TYE" ); +_LIT8( KID3v2FrameIdComment, "COM" ); +_LIT8( KID3v2FrameIdTrack, "TRK" ); +_LIT8( KID3v2FrameIdGenre, "TCO" ); +_LIT8( KID3v2FrameIdComposer, "TCM" ); +_LIT8( KID3v2FrameIdCopyright, "TCR" ); +_LIT8( KID3v2FrameIdOriginalArtist, "TOA" ); +_LIT8( KID3v2FrameIdUrl, "WAF" ); +_LIT8( KID3v2FrameIdUserUrl, "WXX" ); +_LIT8( KID3v2FrameIdJpeg, "PIC" ); +_LIT8( KID3v2FrameIdDuration, "TLE" ); +_LIT8( KID3v2FrameIdDate, "TDA" ); +_LIT8( KID3v2FrameIdRating, "POP" ); + +_LIT8( KID3v2ImageFormatPng, "PNG" ); +_LIT8( KID3v2ImageFormatJpeg, "JPG" ); + + +// ----------------------------------------------------------------------------- +// (ID3v2.2 specification found in www.id3.org) +// +// $00 ISO-8859-1 [ISO-8859-1]. Terminated with $00. +// $01 UCS-2 encoded Unicode [UNICODE]. +// Terminated with $00 00. +// ----------------------------------------------------------------------------- +const TInt KMetaDataParserAsciiEncoding = 0; +const TInt KMetaDataParserUnicodeEncoding = 1; + + +// ============================ MEMBER FUNCTIONS =============================== + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v22::CMetaDataParserID3v22 +// C++ default constructor can NOT contain any code, that +// might leave. +// ----------------------------------------------------------------------------- +// +CMetaDataParserID3v22::CMetaDataParserID3v22( + CMetaDataSource& aSource ) + : CMetaDataParserID3v2(aSource) + { + } + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v22::ConstructL +// Symbian 2nd phase constructor can leave. +// ----------------------------------------------------------------------------- +// +void CMetaDataParserID3v22::ConstructL() + { + User::LeaveIfError(iFs.Connect()); + iExists = ETrue; +#ifdef _DEBUG + RDebug::Print(_L("CMetaDataParserID3v22::ConstructL - Done")); +#endif + } + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v22::NewL +// Two-phased constructor. +// ----------------------------------------------------------------------------- +// +CMetaDataParserID3v22* CMetaDataParserID3v22::NewL( + CMetaDataSource& aSource ) + { +#ifdef _DEBUG + RDebug::Print(_L("CMetaDataParserID3v22::NewL")); +#endif + CMetaDataParserID3v22* self = new( ELeave ) CMetaDataParserID3v22( aSource ); + CleanupStack::PushL( self ); + self->ConstructL(); + CleanupStack::Pop(); + return self; + } + +// Destructor +CMetaDataParserID3v22::~CMetaDataParserID3v22() + { + iFs.Close(); + } + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v22::PrepareToParseL +// ----------------------------------------------------------------------------- +// +void CMetaDataParserID3v22::PrepareToParseL() + { +#ifdef _DEBUG + RDebug::Print(_L("CMetaDataParserID3v22::PrepareToParseL")); +#endif + // Most of validation was already done by builder. + TBuf8 header; + iSource->ReadL( 0, header ); + // ID3v2.2 header consists of following parts: + // File identifier "ID3" + // Version $02 00 + // Flags %xx000000 + // Size 4 * %0xxxxxxx + + // Read the data length + iFrameDataSize = 0; + for ( TInt i = 6; i < 10; i++ ) + { + iFrameDataSize <<= 7; + iFrameDataSize |= header[i] & 0x7f; + } + + iFrameOffset = KID3v2HeaderLength; + // iFrameDataSize is the entire size of ID3 tag including the header + iFrameDataSize += iFrameOffset; + } + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v22::GetNextFieldL +// ----------------------------------------------------------------------------- +// +void CMetaDataParserID3v22::GetNextFieldL( + TMetaDataFieldId& aFieldId, + TInt& aFrameHeaderSize, + TInt& aFrameSize ) + { + TBuf8 frameHeader; + iSource->ReadL( iFrameOffset, frameHeader ); + // ID3v2.2 frame header consists of following parts: + // Frame identifier $xx xx xx + // Size $xx xx xx + + aFrameSize = 0; + + if(!frameHeader.Length()) + User::Leave(KErrCorrupt); + + for ( TInt i = 3; i < 6; i++ ) + { + aFrameSize <<= 8; + aFrameSize |= frameHeader[i]; + } + + aFrameHeaderSize = KID3v2FrameHeaderLength; + TPtrC8 frameId = frameHeader.Left( KID3v2FrameIdLength ); + _LIT8(KEMPTY, "\0\0\0"); + TBuf8<3> empty (KEMPTY); + if ( aFrameSize == 0 && frameId == empty) + { + // We have hit the padding -> no more fields to read + aFieldId = EUnknownMetaDataField; + aFrameHeaderSize = 0; + return; + } + else if (aFrameSize == 0) + { + aFieldId = EUnknownMetaDataField; + return; + } + + if ( frameId.Compare( KID3v2FrameIdTitle ) == 0 ) + { + aFieldId = EMetaDataSongTitle; + } + else if ( frameId.Compare( KID3v2FrameIdArtist ) == 0 ) + { + aFieldId = EMetaDataArtist; + } + else if ( frameId.Compare( KID3v2FrameIdAlbum ) == 0 ) + { + aFieldId = EMetaDataAlbum; + } + else if ( frameId.Compare( KID3v2FrameIdYear ) == 0 ) + { + aFieldId = EMetaDataYear; + } + else if ( frameId.Compare( KID3v2FrameIdComment ) == 0 ) + { + aFieldId = EMetaDataComment; + } + else if ( frameId.Compare( KID3v2FrameIdTrack ) == 0 ) + { + aFieldId = EMetaDataAlbumTrack; + } + else if ( frameId.Compare( KID3v2FrameIdGenre ) == 0 ) + { + aFieldId = EMetaDataGenre; + } + else if ( frameId.Compare( KID3v2FrameIdComposer ) == 0 ) + { + aFieldId = EMetaDataComposer; + } + else if ( frameId.Compare( KID3v2FrameIdCopyright ) == 0 ) + { + aFieldId = EMetaDataCopyright; + } + else if ( frameId.Compare( KID3v2FrameIdOriginalArtist ) == 0 ) + { + aFieldId = EMetaDataOriginalArtist; + } + else if ( frameId.Compare( KID3v2FrameIdUrl ) == 0 ) + { + aFieldId = EMetaDataUrl; + } + else if ( frameId.Compare( KID3v2FrameIdUserUrl ) == 0 ) + { + aFieldId = EMetaDataUserUrl; + } + else if ( frameId.Compare( KID3v2FrameIdJpeg ) == 0 ) + { + aFieldId = EMetaDataJpeg; + } + else if ( frameId.Compare( KID3v2FrameIdDuration ) == 0 ) + { + aFieldId = EMetaDataDuration; + } + else if ( frameId.Compare( KID3v2FrameIdDate ) == 0 ) + { + aFieldId = EMetaDataDate; + } + else if ( frameId.Compare( KID3v2FrameIdRating ) == 0 ) + { + aFieldId = EMetaDataRating; + } + else + { + aFieldId = EUnknownMetaDataField; + } + } + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v22::GetUserUrlL +// ----------------------------------------------------------------------------- +// +void CMetaDataParserID3v22::GetUserUrlL( + TInt aSize ) + { +#ifdef _DEBUG + RDebug::Print(_L("CMetaDataParserID3v23::GetUserUrlL")); +#endif + HBufC8* frame = HBufC8::NewLC( aSize ); + TPtr8 des( frame->Des() ); + + iSource->ReadL( iFrameOffset + KID3v2FrameHeaderLength, des, aSize ); + + if ( des.Length() < aSize ) + { + // Partial frame + User::Leave( KErrCorrupt ); + } + + TInt encoding = (TInt) (des.Left(1))[0]; + + if(encoding == 0x00) + { + _LIT8(KNULL, "\0"); + TInt descOffset = des.Find(KNULL); + if(descOffset + 2 > aSize) + { + CleanupStack::PopAndDestroy(); // frame + return; // empty frame + } + TPtrC8 info = des.Mid(descOffset + 2); + TInt length = info.Length(); + if ( length ) + { + HBufC* data16 = HBufC::NewLC( length ); + TPtr unicode( data16->Des() ); + unicode.Copy( info ); + iContainer->AppendL( EMetaDataUserUrl, unicode ); + CleanupStack::PopAndDestroy(); // data16 + } + } + else + { + _LIT8(KNULL, "\0\0"); + TInt descOffset = des.Find(KNULL); + if(descOffset + 3 > aSize) + { + CleanupStack::PopAndDestroy(); // frame + return; // empty frame + } + TPtrC8 info = des.Mid(descOffset + 3); + TInt length = info.Length(); + if ( length ) + { + HBufC* data16 = HBufC::NewLC( length ); + TPtr unicode( data16->Des() ); + unicode.Copy( info ); + iContainer->AppendL( EMetaDataUserUrl, unicode ); + CleanupStack::PopAndDestroy(); // data16 + } + } + + CleanupStack::PopAndDestroy(); // frame + } + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v22::GetTextInfoL +// ----------------------------------------------------------------------------- +// +void CMetaDataParserID3v22::GetTextInfoL( + TMetaDataFieldId aFieldId, + TInt aSize ) + { +#ifdef _DEBUG + RDebug::Print(_L("CMetaDataParserID3v22::GetTextInfoL")); +#endif + HBufC8* frame = HBufC8::NewLC( aSize ); + TPtr8 des( frame->Des() ); + + iSource->ReadL( iFrameOffset + KID3v2FrameHeaderLength, 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 ) + { + HBufC* data16 = HBufC::NewLC( length ); + TPtr unicode( data16->Des() ); + if ( ConvertToUnicodeL(encoding, info, unicode) == KErrNone ) + { + iContainer->AppendL( aFieldId, unicode ); + } + CleanupStack::PopAndDestroy(); // data16 + } + + CleanupStack::PopAndDestroy(); // frame + } + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v22::GetGenreL +// ----------------------------------------------------------------------------- +// +void CMetaDataParserID3v22::GetGenreL( + TInt aSize ) + { +#ifdef _DEBUG + RDebug::Print(_L("CMetaDataParserID3v22::GetGenreL")); +#endif + + HandleV2GetGenreL( aSize, KID3v2FrameHeaderLength, KID3v2GenreNameLength ); + } + + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v22::GetTextYearL +// ----------------------------------------------------------------------------- +// +void CMetaDataParserID3v22::GetTextYearL( + TInt aSize ) + { +#ifdef _DEBUG + RDebug::Print(_L("CMetaDataParserID3v22::GetTextYearL")); +#endif + GetTextInfoL( EMetaDataYear, aSize ); + } + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v22::GetCommentL +// ----------------------------------------------------------------------------- +// +void CMetaDataParserID3v22::GetCommentL( + TInt aSize ) + { +#ifdef _DEBUG + RDebug::Print(_L("CMetaDataParserID3v22::GetCommentL")); +#endif + HBufC8* frame = HBufC8::NewLC( aSize ); + TPtr8 des( frame->Des() ); + + iSource->ReadL( iFrameOffset + KID3v2FrameHeaderLength, des, aSize ); + + if ( des.Length() < aSize ) + { + // Partial frame + User::Leave( KErrCorrupt ); + } + + if(aSize <= 4) // emppty frame + { + CleanupStack::PopAndDestroy(); // frame + return; + } + TInt encoding = (TInt) (des.Left(1))[0]; + // Skip over TextEncoding(1 byte) and Language(3 bytes) + TPtr8 text = des.MidTPtr(4); + // Skip over Content description from Comment + TInt contentDesLength; + if(encoding == 0) + { + _LIT8(KNull, "\0"); + contentDesLength = text.Find(KNull); + text = text.MidTPtr(contentDesLength + 1); + } + else + { + _LIT8(KNull, "\0\0"); + contentDesLength = text.Find(KNull); + text = text.MidTPtr(contentDesLength + 2); + } + TPtrC8 info = StripTrailingZeroes( text, encoding); + TInt length = info.Length(); + if ( length ) + { + HBufC* data16 = HBufC::NewLC( length ); + TPtr unicode( data16->Des() ); + if ( ConvertToUnicodeL(encoding, info, unicode) == KErrNone ) + { + iContainer->AppendL( EMetaDataComment, unicode ); + } + CleanupStack::PopAndDestroy(); // data16 + } + + CleanupStack::PopAndDestroy(); // frame + } + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v22::GetUrlL +// ----------------------------------------------------------------------------- +// +void CMetaDataParserID3v22::GetUrlL( + TInt aSize ) + { +#ifdef _DEBUG + RDebug::Print(_L("CMetaDataParserID3v22::GetUrlL")); +#endif + HBufC8* frame = HBufC8::NewLC( aSize ); + TPtr8 des( frame->Des() ); + + iSource->ReadL( iFrameOffset + KID3v2FrameHeaderLength, des, aSize ); + + if ( des.Length() < aSize ) + { + // Partial frame + User::Leave( KErrCorrupt ); + } + + // Ignore information followed by termination $00 + TInt trueEnd( des.Locate(0) ); + if ( trueEnd == KErrNotFound ) + { + trueEnd = des.Length(); + } + + TPtrC8 info = des.Left( trueEnd ); + TInt length = info.Length(); + if ( length ) + { + HBufC* data16 = HBufC::NewLC( length ); + TPtr unicode( data16->Des() ); + unicode.Copy( info ); + iContainer->AppendL( EMetaDataUrl, unicode ); + CleanupStack::PopAndDestroy(); // data16 + } + + CleanupStack::PopAndDestroy(); // frame + } + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v22::GetJpegL +// ----------------------------------------------------------------------------- +// +void CMetaDataParserID3v22::GetJpegL( + TInt aSize ) + { +#ifdef _DEBUG + RDebug::Print(_L("CMetaDataParserID3v22::GetJpegL")); +#endif + HBufC8* frame = HBufC8::NewLC( aSize ); + TPtr8 des( frame->Des() ); + + iSource->ReadL( iFrameOffset + KID3v2FrameHeaderLength, des, aSize ); + + if ( des.Length() < aSize ) + { + // Partial frame + User::Leave( KErrCorrupt ); + } + + TInt encoding = (TInt) (des.Left(1))[0]; + + TPtrC8 imageFormat = des.Mid(1, 3); + if ( imageFormat.Compare( KID3v2ImageFormatJpeg ) != 0 && + imageFormat.Compare( KID3v2ImageFormatPng ) != 0 ) + { + CleanupStack::PopAndDestroy(); // frame + return; + } + + if(des.Length() < 5) + { + CleanupStack::PopAndDestroy(); // frame + return; + } + + TInt pictureType = (TInt) (des.Mid(4, 1))[0]; + if ( !NeedRetrieveAlbumArt(pictureType) ) + { + CleanupStack::PopAndDestroy(); // frame + return; + } + + TInt picOffset; + TPtrC8 text8 = des.Mid(5); + if ( encoding == KMetaDataParserAsciiEncoding ) + { + picOffset = text8.Locate(0); + if ( picOffset == KErrNotFound ) + { + CleanupStack::PopAndDestroy(); // frame + return; + } + + picOffset++; // Description termination mark ($00) + } + else if ( encoding == KMetaDataParserUnicodeEncoding ) + { + _LIT8(KNULL, "\0\0"); // 2 bytes of NULL + picOffset = text8.Find(KNULL); + if ( picOffset == KErrNotFound ) + { + CleanupStack::PopAndDestroy(); // frame + return; + } + if (picOffset % 2) // check for offset odd number + { + picOffset++; // add 1 for word boundary + } + picOffset += 2; // Description termination mark ($00 00) + } + else + { + CleanupStack::PopAndDestroy(); // frame + return; + } + TPtrC8 pic = des.Mid(picOffset + 5); + TInt length = pic.Length(); + if ( length ) + { + // save the offset and the size for retrieving the album art later + iAlbumOffset = picOffset + 5; + iAlbumType = pictureType; + + if (iAlbumPtr) + { + delete iAlbumPtr; // delete the previous buffer + } + + CleanupStack::Pop(); // frame - pop from Cleanup stack + iAlbumPtr = frame; // save the buffer + } + else + { + CleanupStack::PopAndDestroy(); // frame + } + } + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v22::GetDurationL +// ----------------------------------------------------------------------------- +// +void CMetaDataParserID3v22::GetDurationL( + TInt aSize ) + { +#ifdef _DEBUG + RDebug::Print(_L("CMetaDataParserID3v23::GetDurationL")); +#endif + HBufC8* frame = HBufC8::NewLC( aSize ); + TPtr8 des( frame->Des() ); + + iSource->ReadL( iFrameOffset + KID3v2FrameHeaderLength, 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 ); // $00 + TInt length = info.Length(); + if ( length ) + { + TReal sec = (TReal) atoi((char*)info.Ptr()) / 1000; + TBuf16<10> info1; + info1.Num(sec, TRealFormat (9, 3)); + iContainer->AppendL( EMetaDataDuration, info1 ); + } + + CleanupStack::PopAndDestroy(); // frame + } + + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v22::GetTextDateL +// ----------------------------------------------------------------------------- +// +void CMetaDataParserID3v22::GetTextDateL( + TInt aSize ) + { +#ifdef _DEBUG + RDebug::Print(_L("CMetaDataParserID3v22::GetTextDateL")); +#endif + GetTextInfoL( EMetaDataDate, aSize ); + } + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v22::GetRatingL +// ----------------------------------------------------------------------------- +// +void CMetaDataParserID3v22::GetRatingL( TInt aSize ) + { +#ifdef _DEBUG + RDebug::Print(_L("CMetaDataParserID3v22::GetRatingL")); +#endif + HBufC8* frame = HBufC8::NewLC( aSize ); + TPtr8 des( frame->Des() ); + + iSource->ReadL( iFrameOffset + KID3v2FrameHeaderLength, des, aSize ); + + if ( des.Length() < aSize ) + { + // Partial frame + User::Leave( KErrCorrupt ); + } + _LIT8(KNull, "\0"); + TInt offset = des.Find(KNull); + if(offset == KErrNotFound) + { + return; + } + TUint8 rating = des[offset + 1]; + if ( rating > 0) + { + TBuf<8> data16; + data16.AppendNum(rating); + iContainer->AppendL( EMetaDataRating, data16 ); + } + CleanupStack::PopAndDestroy(); // frame + } + +// ----------------------------------------------------------------------------- +// CMetaDataParserID3v22::ConvertToUnicodeL +// ----------------------------------------------------------------------------- +// +TInt CMetaDataParserID3v22::ConvertToUnicodeL( + TInt aEncoding, + const TDesC8& aDesc, + TDes16& aUnicode ) + { +#ifdef _DEBUG + RDebug::Print(_L("CMetaDataParserID3v22::ConvertToUnicodeL")); +#endif + TPtrC8 unicodeData; + TUint characterSetId = 0; + TInt err; + + CCnvCharacterSetConverter* charSetConv = CCnvCharacterSetConverter::NewLC(); + TInt state = CCnvCharacterSetConverter::KStateDefault; + + if ( aEncoding == KMetaDataParserAsciiEncoding ) + { + if(iAutoDetectChinese || iAutoDetectRussian || iAutoDetectJapanese) + { + err = AutoDetectL(aDesc, aUnicode); + CleanupStack::PopAndDestroy(); // charSetConv + return err; + } + else + { + // ISO-8859-1 + characterSetId = KCharacterSetIdentifierIso88591; + unicodeData.Set( aDesc ); + } + } + else if ( aEncoding == KMetaDataParserUnicodeEncoding ) + { + // USC-2 with BOM + TInt bom = UnicodeBOM(aDesc); + if ( bom == KUnicodeBOMBigEndian ) + { + //characterSetId = KCharacterSetIdentifierUcs2; + characterSetId = KCharacterSetIdentifierUnicodeBig; + // Skip BOM & Terminate marks + unicodeData.Set( aDesc.Mid( 2, aDesc.Length() - 2 ) ); + } + else if ( bom == KUnicodeBOMLittleEndian ) + { + characterSetId = KCharacterSetIdentifierUnicodeLittle; + // Skip BOM & Terminate marks + unicodeData.Set( aDesc.Mid( 2, aDesc.Length() - 2 ) ); + } + else + { + // USC-2 Big Endian without BOM + characterSetId = KCharacterSetIdentifierUnicodeBig; + unicodeData.Set( aDesc ); + } + } + else + { + User::Leave(KErrNotSupported); + } + + charSetConv->PrepareToConvertToOrFromL(characterSetId, *iCharacterSet, iFs); + err = charSetConv->ConvertToUnicode(aUnicode, unicodeData, state); + +#ifdef _DEBUG + RDebug::Print(_L("CMetaDataParserID3v22::ConvertToUnicode :-> Tag Size[%d] Unicode Tag Size[%d]Bytes Unconverted[%d]"), + unicodeData.Length(), aUnicode.Length(), err); +#endif + + CleanupStack::PopAndDestroy(); // charSetConv + return err; + } + +// End of File