--- /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;
+ }
+