internetradio2.0/streamsourcesrc/iricyflowreader.cpp
changeset 0 09774dfdd46b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/internetradio2.0/streamsourcesrc/iricyflowreader.cpp	Mon Apr 19 14:01:53 2010 +0300
@@ -0,0 +1,714 @@
+/*
+* Copyright (c) 2006-2007 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:  ICY flow reader implementation
+*
+*/
+
+
+/* ---------------------------------------------------------------------------
+*  Version history:
+*  Template version:
+*  <ccm_history>
+*
+*  Version: 2, Tue Feb 28 18:00:00 2008 by Rohit/Kranthi
+*  Ref:
+*  Setting RawDataTransferredL() into DataTransferTracker for Byte Counter Impl
+*
+*  </ccm_history>
+* ============================================================================
+*/
+#include <utf.h>
+
+#include "iricyflowreader.h"
+#include "irdebug.h"
+#include "irmediaenginebuffer.h"
+#include "irmetadata.h"
+#include "irnetworkbuffer.h"
+#include "irstationconnection.h"
+#include "irstationdataobserver.h"
+#include "irstreamsourceerrors.h"
+#include "irnetworkcontroller.h"
+
+const TInt KMaxSongBufferSize = 61440;
+const TInt KNoInputBuffers = 60;
+const TInt KMaxBufferChunkSize = 1024;
+const TInt KMaxSocketBufferSize = 1024;
+const TInt KBufferPercentageInc = 1;
+const TInt KSixteen = 16;
+const TInt KThree=3;
+_LIT8( KIRStreamTitle, "StreamTitle='" );
+_LIT8( KIRStreamUrl, "StreamUrl='" );
+_LIT8( KIRMetaDataEndIdentifier, "';" );
+_LIT8( KIRSongDelimiter, " - " );
+
+// masks and prefices used in UTF-8 recognition
+const TInt KIRUtf8_2B1stByteMask = 0xE0;
+const TInt KIRUtf8_3B1stByteMask = 0xF0;
+const TInt KIRUtf8_4B1stByteMask = 0xF8;
+const TInt KIRUtf8FollowingByteMask = 0xC0;
+
+const TInt KIRUtf8_2B1stBytePrefix = 0xC0;
+const TInt KIRUtf8_3B1stBytePrefix = 0xE0;
+const TInt KIRUtf8_4B1stBytePrefix = 0xF0;
+const TInt KIRUtf8FollowingBytePrefix = 0x80;
+// ========================= MEMBER FUNCTIONS ================================
+
+
+// ---------------------------------------------------------------------------
+// CIRIcyFlowReader::NewL
+// ---------------------------------------------------------------------------
+//
+CIRIcyFlowReader* CIRIcyFlowReader::NewL( RSocket& aSocket, CIRStationConnection& aOwner,
+	               MIRStationDataObserver& aDataObserver, TChannelInfo& aChannelInfo )
+    {
+    CIRIcyFlowReader* self = new ( ELeave ) CIRIcyFlowReader( aSocket, aOwner,
+    					 aDataObserver, aChannelInfo );
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    CleanupStack::Pop( self );
+    return self;
+    }
+
+// ---------------------------------------------------------------------------
+// CIRIcyFlowReader::CIRIcyFlowReader
+// ---------------------------------------------------------------------------
+//
+CIRIcyFlowReader::CIRIcyFlowReader( RSocket& aSocket, CIRStationConnection& aOwner,
+	              MIRStationDataObserver& aDataObserver, TChannelInfo& aChannelInfo )
+    :CActive( EPriorityStandard ), iSocket( aSocket ), iOwner( aOwner ),
+    				 iDataObserver( aDataObserver ),
+      iSocketBufferPtr( NULL, 0 ), iChannelInfo( aChannelInfo )
+    {
+
+    }
+
+// ---------------------------------------------------------------------------
+// CIRIcyFlowReader::~CIRIcyFlowReader
+// ---------------------------------------------------------------------------
+//
+CIRIcyFlowReader::~CIRIcyFlowReader()
+	{
+	Cancel();
+	while(!iSinkQ.IsEmpty())
+		{
+		//Deleting all the entries in sink buffers queue
+		iTempBufferHolder = iSinkQ.First();
+		iSinkQ.Remove(*iTempBufferHolder);
+		delete iTempBufferHolder;
+		}
+	while(!iSourceQ.IsEmpty())
+		{
+		//deleting all the entries in source buffers queue
+		iTempBufferHolder = iSourceQ.First();
+		iSourceQ.Remove(*iTempBufferHolder);
+		delete iTempBufferHolder;
+		}
+	delete[] iSongBuffer;
+	delete iSocketBuffer;
+	delete iTempSongBuffer;
+	delete iTempMetaBuffer;
+    delete iMetaData;
+	if(iNetworkControllerHandle)
+		{
+		iNetworkControllerHandle->Close();
+		}
+	}
+
+// ---------------------------------------------------------------------------
+// CIRIcyFlowReader::ConstructL()
+// ---------------------------------------------------------------------------
+//
+void CIRIcyFlowReader::ConstructL()
+    {
+    IRLOG_DEBUG( "CIRIcyFlowReader::ConstructL" );
+    iAudioDataOffset = iChannelInfo.iAudioDataOffset;
+    TInt f_off = _FOFF( CIRNetworkBuffer, iLink ); //for the buffer queue which is maintained
+    iSourceQ.SetOffset( f_off ); 	// It is Queue of buffer given to socket to fill
+    iSinkQ.SetOffset( f_off );  // It is Queue of buffer given to media engine
+    InitializeBuffersL();
+    iMetaData = CIRMetaData::NewL();
+    CActiveScheduler::Add( this );
+    iNetworkControllerHandle = CIRNetworkController::OpenL();
+    IRLOG_DEBUG( "CIRIcyFlowReader::ConstructL - Exiting." );
+    }
+
+// ---------------------------------------------------------------------------
+// CIRIcyFlowReader::HandleReceivedDataL
+// ---------------------------------------------------------------------------
+//
+void CIRIcyFlowReader::HandleReceivedDataL( const TDesC8& aData )
+    {
+    switch ( iParsingState )
+        {
+        case EIRReadingAudioData:
+            {
+            if ( iAudioDataOffset + aData.Length() > iChannelInfo.iMetaInterval )
+                {
+                // Part of this data contains meta data information already.
+                TInt audioDataAmount = iChannelInfo.iMetaInterval - iAudioDataOffset;
+                // Only the audio part of the data is added to the song buffer.
+                HandleReceivedAudioData( aData.Left( audioDataAmount ) );
+                iParsingState = EIRReadingMetaDataLength;
+                iAudioDataOffset = 0; // Resets the audio data offset, will start increment again after meta data is handled.
+                HandleReceivedDataL( aData.Mid( audioDataAmount ) ); // Recursive call to handle meta data mixed in with this audio data block.
+                }
+            else // All of it is data is audio data.
+                {
+                HandleReceivedAudioData( aData );
+                }
+            break;
+            }
+        case EIRReadingMetaDataLength:
+            {
+            // ICY protocol specifies that meta data length is the first byte of the data multiplied by 16.
+            iMetaDataLength = aData[0] * KSixteen;
+
+            delete iTempMetaBuffer;
+            iTempMetaBuffer = NULL;
+
+            if ( iMetaDataLength > 0 ) // Meta data is provided, so we have to parse it.
+                {
+                iTempMetaBuffer = HBufC8::NewL( iMetaDataLength );
+                iParsingState = EIRReadingMetaData;
+                }
+            else // No meta data available, so resume reading audio data.
+                {
+                iParsingState = EIRReadingAudioData;
+                }
+
+            if ( aData.Length() > 1 ) // Just to check that the data doesn't only contain the length byte.
+                {
+                HandleReceivedDataL( aData.Mid( 1 ) ); // Strips off the length byte. Recursive call as data can also contain audio data.
+                }
+
+            break;
+            }
+        case EIRReadingMetaData:
+            {
+            if ( iTempMetaBuffer->Length() + aData.Length() > iMetaDataLength )
+                {
+                // All of the meta data block is now received, and part of it is continuation to the audio data.
+                TInt metaDataAmount = iMetaDataLength - iTempMetaBuffer->Length();
+                HandleReceivedMetaData( aData.Left( metaDataAmount ) );
+                ExtractMetadataL(); // Extracts the meta data from the temporary meta data buffer.
+                iParsingState = EIRReadingAudioData;
+                HandleReceivedDataL( aData.Mid( metaDataAmount ) ); // Strips off the meta data from the descriptor.
+                }
+            else // All of it is meta data.
+                {
+                HandleReceivedMetaData( aData );
+                }
+            break;
+            }
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CIRIcyFlowReader::HandleReceivedAudioData
+// ---------------------------------------------------------------------------
+//
+void CIRIcyFlowReader::HandleReceivedAudioData( const TDesC8& aData )
+    {
+    // Check to see if we've got enough audio data to fill a buffer.
+    if ( iTempSongBuffer->Length() + aData.Length() >= KMaxBufferChunkSize )
+        {
+		// Data contains more audio data than the buffer can handle.
+		TInt amountToAdd = KMaxBufferChunkSize - iTempSongBuffer->Length();
+
+		// Enough audio data to fill the buffer is added.
+		iTempSongBuffer->Des().Append( aData.Left( amountToAdd ) );
+
+		AddToSinkQueue( *iTempSongBuffer );
+		
+		//while loop is written for only if left over amount in aData is 
+		//greater then 1024, then it should be again added into SinkQueue
+		while(1)
+			{
+			iTempSongBuffer->Des().Zero();
+	 		//calculates the length of the leftover amount to be added
+			TInt length = aData.Length() - amountToAdd;
+
+			if(length <= 0)
+				{
+				break;	
+				}
+			else if(length >= KMaxBufferChunkSize) //if the left over amount is >= 1024 then add to SinkQueue
+				{
+				iTempSongBuffer->Des().Append( aData.Mid( amountToAdd,KMaxBufferChunkSize ) );
+		 		// updates the amountToAdd value by 1024
+				amountToAdd += KMaxBufferChunkSize;
+
+				AddToSinkQueue( *iTempSongBuffer );
+				}
+			else //if the left over amount is < 1024 then append to tempSongBuffer & break
+				{
+				// Then the overflowing audio data part is added to the new clean buffer.
+				iTempSongBuffer->Des().Append( aData.Mid( amountToAdd ) );
+				break;	
+				}
+			}
+        }
+    else // There is enough room in the temporary audio buffer to hold all of the data.
+        {
+        iTempSongBuffer->Des().Append( aData );
+        }
+
+    iAudioDataOffset += aData.Length();
+
+
+
+    }
+
+// ---------------------------------------------------------------------------
+// CIRIcyFlowReader::HandleReceivedMetaData
+// ---------------------------------------------------------------------------
+//
+void CIRIcyFlowReader::HandleReceivedMetaData( const TDesC8& aData )
+    {
+    iTempMetaBuffer->Des().Append( aData );
+    }
+
+// ---------------------------------------------------------------------------
+// CIRIcyFlowReader::RunL
+// ---------------------------------------------------------------------------
+//
+void CIRIcyFlowReader::RunL()
+    {
+     // Active object request complete handler
+    switch ( iStatus.Int() )
+        {
+        case KErrNone:
+            {
+			// Byte Counter Impl
+			iNetworkControllerHandle->DataTransferTracker().RawDataTransferredL( 0,
+					 iSocketBufferPtr.Size(), MIRDataTransferTracker::EIRTransferCategoryAudio);
+
+            HandleReceivedDataL( *iSocketBuffer );
+
+            if ( !iSourceQ.IsEmpty() )
+            	{
+            	//issue a read on empty buffer
+            	IssueRead();
+            	}
+            else
+            	{
+            	if( iReBuffering )
+            		{
+            		//	if rebuffering call continue using sink buffer
+            		FillRemainingBuffers();
+            		}
+            	if( iInitialBuffering )
+            		{
+            		//  if first time intimate media client to fill the buffer
+            		iDataObserver.AudioDataEvent( MIRStationDataObserver::EBufferFilled, KErrNone );
+            		iInitialBuffering = EFalse;
+            		}
+            	}
+
+		 	break;
+			}
+        case KErrDisconnected:
+        	{
+        	IRLOG_ERROR( "CIRIcyFlowReader::RunL - KErrDisconnected");
+        	iOwner.ConnectionError( KIRStreamSourceDisconnected );
+        	}
+            break;
+        case KErrEof:
+        	{
+        	IRLOG_INFO( "CIRIcyFlowReader::RunL - KErrEof" );
+        	iOwner.ConnectionError( KIRStreamSourceNoResponse );
+        	}
+        	break;
+        default:
+        	{
+        	IRLOG_ERROR2( "CIRIcyFlowReader::RunL - Error (%d)", iStatus.Int() );
+            iOwner.ConnectionError( KIRStreamSourceReadError );
+        	}
+            break;
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CIRIcyFlowReader::DoCancel
+// ---------------------------------------------------------------------------
+//
+void CIRIcyFlowReader::DoCancel()
+    {
+    iSocket.CancelRead();
+    }
+
+
+
+// ---------------------------------------------------------------------------
+// CIRIcyFlowReader::IssueRead
+// ---------------------------------------------------------------------------
+//
+void CIRIcyFlowReader::IssueRead()
+    {
+	if( !IsActive() )
+		{
+	    iSocketBufferPtr.Zero();
+	    iSocket.Read( iSocketBufferPtr, iStatus );
+	    SetActive();
+		}
+    }
+
+// ---------------------------------------------------------------------------
+// CIRIcyFlowReader::Start
+// ---------------------------------------------------------------------------
+//
+void CIRIcyFlowReader::Start()
+    {
+    IRLOG_INFO( "CIRIcyFlowReader::Start" );
+    // Initiate a new read from socket into iBuffer
+    iInitialBuffering = ETrue;
+    iBufferCounter = 0;
+    iPublishStationInfo = ETrue;
+	IssueRead();
+	IRLOG_DEBUG( "CIRIcyFlowReader::Start - Exiting." );
+    }
+
+
+// ---------------------------------------------------------------------------
+// CIRIcyFlowReader::InitializeBuffersL
+// ---------------------------------------------------------------------------
+//
+void CIRIcyFlowReader::InitializeBuffersL()
+	{
+	IRLOG_DEBUG( "CIRIcyFlowReader::InitializeBuffersL" );
+	// Allocate the buffer for audio data on heap
+	iSongBuffer = new TUint8[KMaxSongBufferSize];
+	User::LeaveIfNull( iSongBuffer );
+
+	IRLOG_INFO2( "CIRIcyFlowReader::InitializeBuffersL - Reserved %d bytes of memory", KMaxSongBufferSize );
+	TUint8* bufferaddress = iSongBuffer;
+	// since sink buffers are not created initially all buffers are filled with data and appended to sink buffer
+	// Create buffers ans append to source buffer queue
+	for(TInt buffercount = 0; buffercount < KNoInputBuffers; buffercount++ )
+		{
+		iTempBufferHolder = CIRNetworkBuffer::NewL(bufferaddress,
+			KMaxBufferChunkSize);
+		iSourceQ.AddLast(*iTempBufferHolder);
+		bufferaddress += KMaxBufferChunkSize;
+		}
+	// Create a buffer for the data read from socket
+	iSocketBuffer = HBufC8::NewL( KMaxSocketBufferSize );
+	iSocketBufferPtr.Set( iSocketBuffer->Des() );
+    iTempSongBuffer = HBufC8::NewL( KMaxSocketBufferSize );
+	IRLOG_DEBUG( "CIRIcyFlowReader::InitializeBuffersL - Exiting." );
+	}
+
+
+
+// ---------------------------------------------------------------------------
+// CIRIcyFlowReader::FillBuffer
+// Fills the mediaengine buffer and rebuffers if necessary
+// ---------------------------------------------------------------------------
+//
+void CIRIcyFlowReader::FillBuffer(TDes8& aInputBuffer)
+	{
+	FillMediaEngineBuffer(aInputBuffer);
+	}
+
+// ---------------------------------------------------------------------------
+// CIRIcyFlowReader::AddToSinkQueue
+// Adds the filled buffers to the sink Q so that it can be copied to media
+// engine buffer
+// ---------------------------------------------------------------------------
+//
+void CIRIcyFlowReader::AddToSinkQueue( const TDesC8& aData )
+	{
+	//call from runL
+	//removes the buffer from source queue and put in sink queue
+
+	if( !iSourceQ.IsEmpty() )
+		{
+		iTempBufferHolder = iSourceQ.First();
+		TPtr8 bufferPointer(iTempBufferHolder->Des() ,KMaxBufferChunkSize,
+			KMaxBufferChunkSize );
+		bufferPointer.Copy(aData);
+		iSourceQ.Remove(*iTempBufferHolder);
+		iSinkQ.AddLast(*iTempBufferHolder);
+		if( iInitialBuffering )
+			{
+			iBufferCounter += KBufferPercentageInc;
+			iDataObserver.AudioDataEvent( MIRStationDataObserver::EBufferPercentage, iBufferCounter );
+			}
+		}
+	}
+
+// ---------------------------------------------------------------------------
+// CIRIcyFlowReader::FillMediaEngineBuffer
+// Fills the data into media engine's buffer
+// aInputBuffer Buffer into which data is to be filled
+// ---------------------------------------------------------------------------
+//
+
+void CIRIcyFlowReader::FillMediaEngineBuffer(const TDes8& aInputBuffer)
+	{
+
+	if( !iReBuffering )
+		{
+		//Determine no of bytes of data to be filled
+		TInt copyLength = aInputBuffer.MaxLength();
+		// Calculate the no of 1K chunks
+		iNoOfChunks = copyLength/KMaxBufferChunkSize;
+		// Initiailly remaining chunks to be filled is same as total no of
+		// chunks to be filled
+		iChunksRemaining = iNoOfChunks;
+		IRLOG_DEBUG3( "CIRIcyFlowReader::FillMediaEngineBuffer - Copying %d bytes/%d chunks", copyLength, iNoOfChunks );
+		// Store the starting address of buffer into which data is to be
+		// copied
+		iBufferFillPointer = (TUint8 *)aInputBuffer.Ptr();
+		// Start filling of the empty media engine buffers
+		FillRemainingBuffers();
+		}
+	}
+
+
+// ---------------------------------------------------------------------------
+// CIRIcyFlowReader::FillRemainingBuffers
+// Fills the data into media engine's remaining buffers
+// called when the stream source runs out of buffers and
+// there is a pending request to media engine
+// ---------------------------------------------------------------------------
+//
+void CIRIcyFlowReader::FillRemainingBuffers()
+	{
+	TUint8* mediaBufferAddress = iBufferFillPointer;
+	TInt chunksFilled = iNoOfChunks - iChunksRemaining;
+	mediaBufferAddress += ( chunksFilled * KMaxBufferChunkSize );
+
+	TInt bufferNumber = iChunksRemaining;
+	while ( bufferNumber )
+		{
+		if ( !iSinkQ.IsEmpty() )
+			{
+			iTempBufferHolder = iSinkQ.First();
+			TPtr8 mediaBufferPointer(mediaBufferAddress,KMaxBufferChunkSize,
+				KMaxBufferChunkSize );
+			mediaBufferPointer.Copy( iTempBufferHolder->Des() ,
+				KMaxBufferChunkSize);
+			TPtr8 tempBufferPointer(iTempBufferHolder->Des(),
+				KMaxBufferChunkSize,KMaxBufferChunkSize );
+			tempBufferPointer.Delete(KMaxBufferChunkSize,
+				KMaxBufferChunkSize);
+			iSinkQ.Remove(*iTempBufferHolder);
+			iSourceQ.AddLast(*iTempBufferHolder);
+			iChunksRemaining--;
+			bufferNumber--;
+			iReBuffering = EFalse;
+			mediaBufferAddress += KMaxBufferChunkSize;
+			//issue  source rebuffering here if source is not empty
+			if( !iSourceQ.IsEmpty() )
+				{
+				IssueRead();
+				}
+			}
+		else
+			{
+			//rebuffer if sink buffer is empty
+			bufferNumber = 0;
+			iReBuffering = ETrue;
+			//issue  source rebuffering here if source is not empty
+			if( !iSourceQ.IsEmpty() )
+				{
+				IssueRead();
+				}
+			//break from for loop
+			}
+		}
+	iBufferCounter += (K100Percentage - KNoInputBuffers) / KIRInputBufferCount;
+    if ( iBufferCounter > K100Percentage )
+        {
+        iBufferCounter = K100Percentage;
+        }
+    iDataObserver.AudioDataEvent( MIRStationDataObserver::EBufferPercentage, iBufferCounter );
+	if( !iReBuffering )
+		{
+        iDataObserver.AudioDataEvent( MIRStationDataObserver::EOpenComplete, KErrNone );
+		}
+	}
+
+// ---------------------------------------------------------------------------
+// CIRIcyFlowReader::ExtractMetadataL
+// Extracts the meta data from the stream
+// ---------------------------------------------------------------------------
+//
+void CIRIcyFlowReader::ExtractMetadataL()
+	{
+    IRLOG_DEBUG( "CIRIcyFlowReader::ExtractMetaDataL" );
+
+    // Erases old meta data information.
+    iMetaData->SetArtistL( KNullDesC );
+    iMetaData->SetSongL( KNullDesC );
+    iMetaData->SetStreamUrlL( KNullDesC );
+
+    TPtrC8 ptr( *iTempMetaBuffer );
+
+    TInt streamTitleIndex = ptr.Find( KIRStreamTitle );
+    TInt streamUrlIndex = ptr.Find( KIRStreamUrl );
+
+    // Extracts the "StreamTitle" part of the meta data.
+    if ( streamTitleIndex >= 0 )
+        {
+        TPtrC8 streamTitlePtr( ptr.Mid( streamTitleIndex + KIRStreamTitle().Length() ) );
+        TInt streamTitleEndIndex = streamTitlePtr.Find( KIRMetaDataEndIdentifier );
+        if ( streamTitleEndIndex >= 0 )
+            {
+            streamTitlePtr.Set( streamTitlePtr.Left( streamTitleEndIndex ) );
+
+            TPtrC8 artistPtr( KNullDesC8 );
+            TPtrC8 songPtr( KNullDesC8 );
+
+            TInt songDelimiterIndex = streamTitlePtr.Find( KIRSongDelimiter );
+            if ( songDelimiterIndex >= 0 )
+                {
+                artistPtr.Set( streamTitlePtr.Left( songDelimiterIndex ) );
+                songPtr.Set( streamTitlePtr.Mid( songDelimiterIndex + 
+                					KIRSongDelimiter().Length() ) );
+                }
+            else
+                {
+                IRLOG_WARNING( "CIRIcyFlowReader::ExtractMetaDataL - Song delimiter was not found" );
+                artistPtr.Set( streamTitlePtr );
+                }
+
+            HBufC* artist = DecodeMetadataStringLC( artistPtr );
+            iMetaData->SetArtistL( *artist );
+            CleanupStack::PopAndDestroy( artist );
+
+            HBufC* song = DecodeMetadataStringLC( songPtr );
+            iMetaData->SetSongL( *song );
+            CleanupStack::PopAndDestroy( song );
+            }
+        else
+            {
+            IRLOG_WARNING( "CIRIcyFlowReader::ExtractMetaDataL - \"StreamTitle\" end was not found" );
+            }
+        }
+    else
+        {
+        IRLOG_WARNING( "CIRIcyFlowReader::ExtractMetaDataL - \"StreamTitle\" was not found" );
+        }
+
+    // Extracts the "StreamUrl" part of the meta data.
+    if ( streamUrlIndex >= 0 )
+        {
+        TPtrC8 streamUrlPtr( ptr.Mid( streamUrlIndex + KIRStreamUrl().Length() ) );
+
+        TInt streamUrlEndIndex = streamUrlPtr.Find( KIRMetaDataEndIdentifier );
+        if ( streamUrlEndIndex >= 0 )
+            {
+            streamUrlPtr.Set( streamUrlPtr.Left( streamUrlEndIndex ) );
+            HBufC* streamUrl = HBufC::NewLC( streamUrlPtr.Length() );
+            streamUrl->Des().Copy( streamUrlPtr );  // 8 bit to 16 bit descriptor conversion.
+            iMetaData->SetStreamUrlL( *streamUrl );
+            CleanupStack::PopAndDestroy( streamUrl );
+            }
+        else
+            {
+            IRLOG_WARNING( "CIRIcyFlowReader::ExtractMetaDataL - \"StreamUrl\" end was not found" );
+            }
+        }
+    else
+        {
+        IRLOG_WARNING( "CIRIcyFlowReader::ExtractMetaDataL - \"StreamUrl\" was not found" );
+        }
+
+    iDataObserver.MetadataReceived( *iMetaData );
+
+    IRLOG_INFO4( "CIRIcyFlowReader::ExtractMetaDataL - Exit (artist=%S, song=%S, streamUrl=%S)", &iMetaData->Artist(), &iMetaData->Song(), &iMetaData->StreamUrl() );
+	}
+
+// ---------------------------------------------------------------------------
+// CIRIcyFlowReader::DecodeMetadataStringLC
+// ---------------------------------------------------------------------------
+//
+HBufC* CIRIcyFlowReader::DecodeMetadataStringLC( const TDesC8& aString ) const
+    {
+    IRLOG_DEBUG( "CIRIcyFlowReader::DecodeMetadataStringLC" );
+    HBufC* decodedString = NULL;
+    if ( IsUtf8Encoded( aString ) )
+        {
+        TRAPD( err, 
+              IRLOG_DEBUG( "CIRIcyFlowReader::DecodeMetadataStringLC - String is UTF-8 encoded" );
+              decodedString = CnvUtfConverter::ConvertToUnicodeFromUtf8L( aString );
+             )
+        if ( err != KErrNone )
+            {
+            IRLOG_ERROR2( "CIRIcyFlowReader::DecodeMetadataStringLC - UTF-8 conversion failed, err=%d", err );
+            decodedString = HBufC::NewL( aString.Length() ); 
+            decodedString->Des().Copy( aString ); // 8 bit to 16 bit descriptor conversion (ISO-8859-1).    
+            }
+        }
+    else
+        {
+        decodedString = HBufC::NewL( aString.Length() ); 
+        decodedString->Des().Copy( aString ); // 8 bit to 16 bit descriptor conversion (ISO-8859-1).    
+        }
+    CleanupStack::PushL( decodedString );
+    IRLOG_DEBUG2( "CIRIcyFlowReader::DecodeMetadataStringLC - Returning %S", decodedString );    
+    return decodedString;
+    }
+
+// ---------------------------------------------------------------------------
+// CIRIcyFlowReader::IsUtf8Encoded
+// ---------------------------------------------------------------------------
+//
+TBool CIRIcyFlowReader::IsUtf8Encoded( const TDesC8& aData ) const
+    {
+    IRLOG_DEBUG( "CIRIcyFlowReader::IsUtf8Encoded" );    
+    TBool foundUtf8( EFalse );
+    
+    for ( TInt i(0); i + 1 < aData.Length() && !foundUtf8; i++ )
+        {
+        if ( ( aData[i] & KIRUtf8_2B1stByteMask ) == KIRUtf8_2B1stBytePrefix )
+            {
+            // Two-byte presentation: 110yyyyy 10zzzzzz
+            if ( ( aData[i + 1] & KIRUtf8FollowingByteMask ) == KIRUtf8FollowingBytePrefix )
+                {
+                foundUtf8 = ETrue;
+                }
+            }
+        else if ( ( aData[i] & KIRUtf8_3B1stByteMask ) == KIRUtf8_3B1stBytePrefix && 
+                  i + 2 < aData.Length() )
+            {
+            // Three-byte presentation: 1110xxxx 10yyyyyy 10zzzzzz
+            if ( ( aData[i + 1] & KIRUtf8FollowingByteMask ) == KIRUtf8FollowingBytePrefix && 
+                 ( aData[i + 2] & KIRUtf8FollowingByteMask ) == KIRUtf8FollowingBytePrefix )
+                {
+                foundUtf8 = ETrue;
+                }
+            }
+        else if ( ( aData[i] & KIRUtf8_4B1stByteMask ) == KIRUtf8_4B1stBytePrefix &&
+                i + KThree < aData.Length() )
+            {
+            // Four-byte presentation: 11110www 10xxxxxx 10yyyyyy 10zzzzzz
+            if ( ( aData[i + 1] & KIRUtf8FollowingByteMask ) == KIRUtf8FollowingBytePrefix && 
+                 ( aData[i + 2] & KIRUtf8FollowingByteMask ) == KIRUtf8FollowingBytePrefix && 
+                 ( aData[i + KThree] & KIRUtf8FollowingByteMask ) == KIRUtf8FollowingBytePrefix )
+                {
+                foundUtf8 = ETrue;
+                }
+            }
+        else
+            {
+            // NOP
+            }
+        }
+    IRLOG_DEBUG2( "CIRIcyFlowReader::IsUtf8Encoded - Returning %d", foundUtf8 );    
+    return foundUtf8;
+    }
+