mmserv/metadatautility/Src/MetaDataParserID3v22.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 06 Jul 2010 14:50:35 +0300
changeset 32 94fc26b6e006
parent 22 128eb6a32b84
child 43 9894ed580e4a
permissions -rw-r--r--
Revision: 201025 Kit: 2010127

/*
* 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    <libc/stdlib.h>

#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<KID3v2HeaderLength> header;
	iSource->ReadL( iID32Offset, 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<KID3v2FrameHeaderLength> 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 )
		{
		HBufC* data16 = HBufC::NewLC( length );
		TPtr unicode( data16->Des() );
		if ( ConvertToUnicodeL(encoding, info, unicode) == KErrNone )
			{
			TLex16 lex(unicode);
			TReal milliSec = 0;
			TBuf16<10> info1;
			
			lex.Val(milliSec);				
			info1.Num(milliSec/1000, TRealFormat (9, 3));
			iContainer->AppendL( EMetaDataDuration, info1 );
			}
		CleanupStack::PopAndDestroy();  // data16
		
		}

	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