--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/upnpmpxplugins/upnpplaybackplugins/src/upnptrack.cpp Thu Dec 17 08:52:00 2009 +0200
@@ -0,0 +1,754 @@
+/*
+* Copyright (c) 2008 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: Class for containing and obtaining music track-specific data
+* from media server
+*
+*/
+
+
+
+
+
+
+// INCLUDES
+#include <mpxmediaaudiodefs.h>
+#include <mpxmediamusicdefs.h>
+#include <mpxmediageneraldefs.h>
+#include <mpxmedia.h>
+#include <mpxattribute.h>
+#include <mpxplaybackpluginobserver.h>
+#include <mpxmessagegeneraldefs.h> // for messaging
+#include <mpxplaybackmessagedefs.h> // for messaging
+
+#include <escapeutils.h> // for unicode conversion
+
+#include <upnpitem.h>
+#include <upnpobject.h>
+#include "upnpitemutility.h" // for FindElementByName & ResourceFromItemL
+#include "upnpfileutility.h" // for IsFileProtectedL
+#include "upnpconstantdefs.h" // browse filtering and item element names
+#include <upnpdlnaprotocolinfo.h> // for resolving object mimetype
+#include "upnpavcontroller.h"
+#include "upnpavbrowsingsession.h"
+#include "upnpavdevice.h"
+#include "upnpavdevicelist.h"
+
+#include "upnpitemresolverobserver.h" // MUPnPItemResolverObserver
+#include "upnpitemresolverfactory.h" // factory class
+#include "upnpitemresolver.h" // MUPnPItemResolver
+
+#include "upnptrackobserver.h"
+#include "upnptrack.h"
+
+_LIT( KComponentLogfile, "musicplugins.txt");
+#include "upnplog.h"
+
+// CONSTANTS
+_LIT16( KUPnPPrefix, "upnp:" ); // Prefix for separate local/remote song
+const TInt KUPnPPrefixLength = 5;
+const TInt KCharCodeColon = 58;
+const TInt KCharCodeSeparate = 42;
+const TInt InitialTrackDuration = 1;
+_LIT( KTimeFormatYearOnly, "%F%Y" );
+
+// ======== MEMBER FUNCTIONS ========
+
+// --------------------------------------------------------------------------
+// CUPnPTrack::NewL
+// 1st phase constructor.
+// --------------------------------------------------------------------------
+//
+CUPnPTrack* CUPnPTrack::NewL( MUPnPAVController& aAvController )
+ {
+ __LOG( "CUPnPTrack::NewL" );
+ CUPnPTrack* self = new( ELeave ) CUPnPTrack( aAvController );
+ return self;
+ }
+
+// --------------------------------------------------------------------------
+// CUPnPTrack::Delete
+// deletor
+// --------------------------------------------------------------------------
+//
+void CUPnPTrack::Delete()
+ {
+ __LOG1( "CUPnPTrack::Delete, state=%d", iState );
+ if ( iState == EStateResolving )
+ {
+ // asynchronous delete - will be deleted after
+ // asynchronous operation is complete. This is because the
+ // S60 command can not be cancelled
+ iState = EStateSelfDestruct;
+ }
+ else
+ {
+ delete this;
+ }
+ }
+
+// --------------------------------------------------------------------------
+// CUPnPTrack::CUPnPTrack
+// Default constructor.
+// --------------------------------------------------------------------------
+//
+CUPnPTrack::CUPnPTrack( MUPnPAVController& aAvController )
+ : iAvController( aAvController )
+ , iQueriedAttributes()
+ {
+ iBrowsingSession = NULL;
+ iOriginalURI = NULL;
+ iMediaServer = NULL;
+ iObjectId = NULL;
+ iTrackObserver = NULL;
+ // Use initial duration value (1). It will guarantee that in
+ // case were remote device does not support duration query and playback
+ // is unpaused, SetPosition is called with real value even
+ // if remote player does not return real value for getposition
+ // call. Note that SetPosition(0) is used when user really wants
+ // to move to track beginning.
+ iTrackDuration = InitialTrackDuration;
+ iIsItemSolved = EFalse;
+ iState = EStateIdle;
+ }
+
+// --------------------------------------------------------------------------
+// CUPnPTrack::~CUPnPTrack
+// destructor.
+// --------------------------------------------------------------------------
+//
+CUPnPTrack::~CUPnPTrack()
+ {
+ __LOG( "CUPnPTrack::~CUPnPTrack()" );
+ StopBrowsingSession();
+
+ // Free memory of owned members
+ delete iOriginalURI;
+ delete iMediaServer;
+ delete iObjectId;
+ delete iItemResolver;
+ iItemResolver = 0;
+ __LOG( "CUPnPTrack::~CUPnPTrack End" );
+ }
+
+// --------------------------------------------------------------------------
+// CUPnPTrack::ResolveURIL
+// Resolve local / remote URI.
+// --------------------------------------------------------------------------
+//
+void CUPnPTrack::ResolveURIL( const TDesC& aCodedTrackUri,
+ MUPnPTrackObserver& aTrackObserver,
+ TPlaybackDirection aPlaybackDirection )
+ {
+ __LOG( "CUPnPTrack::ResolveURI" );
+
+ delete iOriginalURI;
+ iOriginalURI = 0;
+ delete iMediaServer;
+ iMediaServer = 0;
+ delete iObjectId;
+ iObjectId = 0;
+ iTrackObserver = &aTrackObserver;
+
+ // Either local to remote or remote to remote
+ if( aPlaybackDirection == EDirectionRemote )
+ {
+ if( aCodedTrackUri.Find( KUPnPPrefix ) == 0 )
+ {
+ // track is located in remote
+ iTrackLocation = ETrackLocationRemote;
+ // parse and save media server id and object id
+ ParsePiecesL( aCodedTrackUri );
+ // start a browsing session
+ StartBrowsingSessionL();
+ // create resolver
+ iItemResolver =
+ UPnPItemResolverFactory::NewRemoteItemResolverL(
+ *iObjectId, *iBrowsingSession,
+ iDefaultSelector, KFilterCommon );
+ }
+ else // Local stored. Save URI to local member
+ {
+ // track is located in local file system
+ iTrackLocation = ETrackLocationLocal;
+ // store original URI
+ iOriginalURI = HBufC::NewL( aCodedTrackUri.Length() );
+ iOriginalURI->Des().Copy( aCodedTrackUri );
+ // check for DRM protection
+ if ( UPnPFileUtility::IsFileProtectedL( iOriginalURI->Des() ) )
+ {
+ // call back directly with an error code
+ iTrackObserver->ResolveURIComplete( KErrNotSupported );
+ return;
+ }
+ else
+ {
+ // create resolver.
+ iItemResolver =
+ UPnPItemResolverFactory::NewLocalItemResolverL(
+ *iOriginalURI, iAvController, iFirstSelector,
+ UPnPItemResolverFactory::EOmitDrmCheck );
+ }
+ }
+ }
+ else // Remote to local direction
+ {
+ // track is located in remote
+ iTrackLocation = ETrackLocationRemote;
+ // parse and save media server id and object id
+ ParsePiecesL( aCodedTrackUri );
+ // start a browsing session
+ StartBrowsingSessionL();
+ // create resolver
+ iItemResolver =
+ UPnPItemResolverFactory::NewDownloadItemResolverL(
+ *iObjectId, iAvController, *iBrowsingSession,
+ iFirstSelector, KFilterCommon );
+ }
+
+ // Resolve remote item. Calls back to ResolveComplete
+ iState = EStateResolving;
+ TRAPD( e, iItemResolver->ResolveL( *this ); )
+ if ( e != KErrNone )
+ {
+ if ( iState == EStateSelfDestruct )
+ {
+ __LOG( "CUPnPTrack: self-destructing" );
+ delete this;
+ }
+ else
+ {
+ iState = EStateIdle;
+ User::Leave( e );
+ }
+ }
+ }
+
+// --------------------------------------------------------------------------
+// CUPnPTrack::StartBrowsingSessionL
+// Finds the apropriate mediaserver and starts a browsing session
+// --------------------------------------------------------------------------
+//
+void CUPnPTrack::StartBrowsingSessionL()
+ {
+ // Get list of media servers
+ CUpnpAVDeviceList* devices = iAvController.GetMediaServersL();
+ CleanupStack::PushL( devices );
+
+ // Find out index of wanted media server from media server list
+ TInt index = KErrNotFound;
+ TInt count = devices->Count();
+
+ for( TInt i = 0; i < count; i++ )
+ {
+ // If media server id match.
+ if( !iMediaServer->Compare( (*devices)[ i ]->Uuid() ) )
+ {
+ index = i;
+ break;
+ }
+ }
+
+ // Leave if media server does not find by id
+ if( index == KErrNotFound )
+ {
+ __LOG1( "CUPnPTrack: Media server not found: %S",
+ iMediaServer );
+ User::Leave( KErrNotFound );
+ }
+
+ // start a browsing session
+ iBrowsingSession = &iAvController.StartBrowsingSessionL(
+ *(*devices)[ index ] );
+ CleanupStack::PopAndDestroy( devices );
+ }
+
+// --------------------------------------------------------------------------
+// CUPnPTrack::StopBrowsingSession
+// Finds the apropriate mediaserver and starts a browsing session
+// --------------------------------------------------------------------------
+//
+void CUPnPTrack::StopBrowsingSession()
+ {
+ // Stop browsing session if exist.
+ if( iBrowsingSession )
+ {
+ __LOG( "Stop browsing session" );
+ iAvController.StopBrowsingSession( *iBrowsingSession );
+ iBrowsingSession = 0;
+ }
+ }
+
+// --------------------------------------------------------------------------
+// CUPnPTrack::URI
+// Return original URI of track.
+// --------------------------------------------------------------------------
+//
+const TDesC& CUPnPTrack::URI() const
+ {
+ __ASSERTD( iOriginalURI != 0,__FILE__, __LINE__ );
+ return *iOriginalURI;
+ }
+
+// --------------------------------------------------------------------------
+// CUPnPTrack::UpnpURI
+// Return True UPNP URI of track.
+// --------------------------------------------------------------------------
+//
+const TDesC8& CUPnPTrack::UpnpURI() const
+ {
+ __ASSERTD( iIsItemSolved,__FILE__, __LINE__ );
+ return iItemResolver->Resource().Value();
+ }
+
+// --------------------------------------------------------------------------
+// CUPnPTrack::UpnpItem
+// Return True UPNP item representing the track
+// --------------------------------------------------------------------------
+//
+const CUpnpItem& CUPnPTrack::UpnpItem() const
+ {
+ __ASSERTD( iIsItemSolved,__FILE__, __LINE__ );
+ return iItemResolver->Item();
+ }
+
+// --------------------------------------------------------------------------
+// CUPnPTrack::FilePath
+// Returns the file path of local item
+// --------------------------------------------------------------------------
+//
+const TDesC& CUPnPTrack::FilePath() const
+ {
+ __ASSERTD( iTrackLocation == ETrackLocationRemote, __FILE__, __LINE__ );
+ __ASSERTD( iIsItemSolved,__FILE__, __LINE__ );
+ return iItemResolver->Resource().FilePath();
+ }
+
+// --------------------------------------------------------------------------
+// CUPnPTrack::IsRemote
+// Return location of track. ETrue = remote. EFalse = local
+// --------------------------------------------------------------------------
+//
+TBool CUPnPTrack::IsRemote() const
+ {
+ return iTrackLocation == ETrackLocationRemote;
+ }
+
+// --------------------------------------------------------------------------
+// CUPnPTrack::TrackDuration
+// returns duration of the track
+// --------------------------------------------------------------------------
+//
+TInt CUPnPTrack::TrackDuration()
+ {
+ __LOG1( "CUPnPTrack::TrackDuration duration: %d", iTrackDuration );
+ TInt ms = InitialTrackDuration;
+ if ( iTrackDuration > InitialTrackDuration )
+ {
+ ms = iTrackDuration;
+ }
+ else if ( iIsItemSolved )
+ {
+ const CUpnpAttribute* attr = UPnPItemUtility
+ ::FindAttributeByName( iItemResolver->Resource(),
+ KAttributeDuration );
+ if ( attr != 0 )
+ {
+ if ( UPnPItemUtility::UPnPDurationAsMilliseconds(
+ attr->Value(), ms ) == KErrNone )
+ {
+ // store duration for quicker future queries
+ iTrackDuration = ms;
+ __LOG1( "store duration: %d", iTrackDuration );
+ }
+ }
+ }
+ return ms;
+ }
+
+// --------------------------------------------------------------------------
+// CUPnPTrack::SetTrackDuration
+// overrides duration of the track
+// --------------------------------------------------------------------------
+//
+void CUPnPTrack::SetTrackDuration( TInt aMilliseconds )
+ {
+ iTrackDuration = aMilliseconds;
+ }
+
+// --------------------------------------------------------------------------
+// CUPnPTrack::GetMetaDataL
+// Get metadata information for track from media server
+// --------------------------------------------------------------------------
+//
+void CUPnPTrack::GetMetaDataL( const TArray<TMPXAttribute>& aAttrs,
+ MMPXPlaybackPluginObserver& aObs )
+ {
+ // Check if remote track
+ if( iTrackLocation == ETrackLocationLocal )
+ {
+ __LOG( "CUPnPTrack::GetMetaData - No metadata for local track!" );
+ User::Leave( KErrNotSupported );
+ }
+
+ if ( iIsItemSolved )
+ {
+ __LOG( "CUPnPTrack::GetMetaDataL" );
+ DeliverMedataL( aAttrs, aObs );
+ }
+ else
+ {
+ __LOG( "CUPnPTrack::GetMetaDataL - pending" );
+ iMetadataObserver = &aObs;
+ for( TInt i = 0; i < aAttrs.Count(); ++i )
+ {
+ iQueriedAttributes.Append( aAttrs[i] );
+ }
+ }
+ }
+
+// --------------------------------------------------------------------------
+// CUPnPTrack::SendMediaChangedEventL
+// --------------------------------------------------------------------------
+//
+void CUPnPTrack::SendMediaChangedEventL(
+ MMPXPlaybackPluginObserver& aObs )
+ {
+ RArray<TMPXAttribute> attrs;
+ CleanupClosePushL( attrs );
+ attrs.AppendL( KMPXMediaGeneralTitle );
+ attrs.AppendL( KMPXMediaMusicArtist );
+ attrs.AppendL( KMPXMediaMusicAlbum );
+ attrs.AppendL( KMPXMediaMusicGenre );
+ attrs.AppendL( KMPXMediaGeneralDate );
+ attrs.AppendL( KMPXMediaMusicYear );
+ attrs.AppendL( KMPXMediaGeneralComment );
+ attrs.AppendL( KMPXMediaGeneralMimeType );
+
+ // Create and fill CMPXMedia class
+ RArray<TInt> suppIds;
+ CleanupClosePushL( suppIds );
+ suppIds.AppendL( KMPXMediaIdMusic );
+ suppIds.AppendL( KMPXMediaIdGeneral );
+ suppIds.AppendL( KMPXMediaIdAudio );
+ CMPXMedia* media = CMPXMedia::NewL( suppIds.Array() );
+ CleanupStack::PopAndDestroy( &suppIds );
+ CleanupStack::PushL( media );
+ FillMediaFromItemL( *media, attrs.Array() );
+
+ // create an MPX message
+ CMPXMessage* msg = CMPXMessage::NewL();
+ CleanupStack::PushL( msg );
+ msg->SetTObjectValueL<TMPXMessageId>(
+ KMPXMessageGeneralId, KMPXMessagePbMediaChanged );
+ msg->SetCObjectValueL<CMPXMedia>(
+ KMPXMessagePbMedia, media );
+
+ // send message
+ aObs.HandlePlaybackMessage( *msg );
+
+ CleanupStack::PopAndDestroy( msg );
+ CleanupStack::PopAndDestroy( media );
+ CleanupStack::PopAndDestroy( &attrs );
+ }
+
+// --------------------------------------------------------------------------
+// CUPnPTrack::DeliverMedatataL
+// --------------------------------------------------------------------------
+//
+void CUPnPTrack::DeliverMedataL( const TArray<TMPXAttribute>& aAttrs,
+ MMPXPlaybackPluginObserver& aObs, TInt aError )
+ {
+ __LOG( "CUPnPTrack::DeliverMedatata");
+
+ // Create and fill CMPXMedia class
+ RArray<TInt> suppIds;
+ CleanupClosePushL( suppIds );
+ suppIds.AppendL( KMPXMediaIdMusic );
+ suppIds.AppendL( KMPXMediaIdGeneral );
+ suppIds.AppendL( KMPXMediaIdAudio );
+ CMPXMedia* media = CMPXMedia::NewL( suppIds.Array() );
+ CleanupStack::PopAndDestroy( &suppIds );
+ CleanupStack::PushL( media );
+
+ if ( aError == KErrNone )
+ {
+ FillMediaFromItemL( *media, aAttrs );
+ }
+
+ // Return metadata
+ aObs.HandleMedia( *media, aError );
+ CleanupStack::PopAndDestroy( media );
+ }
+
+// --------------------------------------------------------------------------
+// CUPnPTrack::FillMediaFromItemL
+// --------------------------------------------------------------------------
+//
+void CUPnPTrack::FillMediaFromItemL( CMPXMedia& aMedia,
+ const TArray<TMPXAttribute>& aAttrs )
+ {
+ __ASSERTD( iIsItemSolved,__FILE__, __LINE__ );
+
+ // Fill metadata
+ const CUpnpElement* elem;
+ // Song title
+ if( Exists( aAttrs, KMPXMediaGeneralTitle ) )
+ {
+ __LOG( "CUPnPTrack: Metadata: Title");
+ aMedia.SetTextValueL(
+ KMPXMediaGeneralTitle, *To16LC(
+ iItemResolver->Item().Title() ) );
+ CleanupStack::PopAndDestroy();
+ }
+ // Artist
+ if( Exists( aAttrs, KMPXMediaMusicArtist ) )
+ {
+ if ( ( elem = UPnPItemUtility::FindElementByName(
+ iItemResolver->Item(), KElementArtist ) ) != 0 )
+ {
+ __LOG( "CUPnPTrack: Metadata: Artist" );
+ aMedia.SetTextValueL(
+ KMPXMediaMusicArtist, *To16LC( elem->Value() ) );
+ CleanupStack::PopAndDestroy();
+ }
+ else if ( ( elem = UPnPItemUtility::FindElementByName(
+ iItemResolver->Item(), KElementCreator ) ) != 0 )
+ {
+ __LOG( "CUPnPTrack: Metadata Creator" );
+ aMedia.SetTextValueL(
+ KMPXMediaMusicArtist, *To16LC( elem->Value() ) );
+ CleanupStack::PopAndDestroy();
+ }
+ }
+ // Album
+ if( Exists( aAttrs, KMPXMediaMusicAlbum ) )
+ {
+ if ( ( elem = UPnPItemUtility::FindElementByName(
+ iItemResolver->Item(), KElementAlbum ) ) != 0 )
+ {
+ __LOG( "CUPnPTrack: Metadata: Album");
+ aMedia.SetTextValueL(
+ KMPXMediaMusicAlbum, *To16LC( elem->Value() ) );
+ CleanupStack::PopAndDestroy();
+ }
+ }
+ // Genre
+ if( Exists( aAttrs, KMPXMediaMusicGenre ) )
+ {
+ if ( ( elem = UPnPItemUtility::FindElementByName(
+ iItemResolver->Item(), KElementGenre ) ) != 0 )
+ {
+ __LOG( "CUPnPTrack: Metadata: Genre" );
+ aMedia.SetTextValueL(
+ KMPXMediaMusicGenre, *To16LC( elem->Value() ) );
+ CleanupStack::PopAndDestroy();
+ }
+ }
+ // Date / Year
+ if( ( elem = UPnPItemUtility::FindElementByName(
+ iItemResolver->Item(), KElementDate ) ) != 0 )
+ {
+ TTime timestamp;
+ TInt conversionError =
+ UPnPItemUtility::UPnPDateAsTTime( elem->Value(), timestamp );
+ if ( conversionError == KErrNone )
+ {
+ if ( Exists( aAttrs, KMPXMediaGeneralDate ) )
+ {
+ __LOG( "CUPnPTrack: Metadata: Date" );
+ aMedia.SetTextValueL(
+ KMPXMediaGeneralDate, iTempBuf );
+ }
+ if ( Exists( aAttrs, KMPXMediaMusicYear ) )
+ {
+ __LOG( "CUPnPTrack: Metadata: Year" );
+ timestamp.FormatL( iTempBuf, KTimeFormatYearOnly );
+ aMedia.SetTextValueL(
+ KMPXMediaMusicYear, iTempBuf );
+ }
+ }
+ }
+ // Duration
+ if( Exists( aAttrs, KMPXMediaGeneralDuration ) )
+ {
+ TInt duration = TrackDuration();
+ if ( duration >= 0 )
+ {
+ aMedia.SetTObjectValueL<TInt>(
+ KMPXMediaGeneralDuration, duration );
+ }
+ }
+ // Size
+ if( Exists( aAttrs, KMPXMediaGeneralSize ) )
+ {
+ const CUpnpAttribute* attr = UPnPItemUtility
+ ::FindAttributeByName( iItemResolver->Resource(),
+ KAttributeSize );
+ if ( attr != 0 )
+ {
+ __LOG( "CUPnPTrack: Metadata: Size" );
+ TInt size;
+ TLex8 sizeconvert( attr->Value() );
+ if ( sizeconvert.Val( size ) == KErrNone )
+ {
+ aMedia.SetTObjectValueL<TInt>(
+ KMPXMediaGeneralSize, size );
+ }
+ }
+ }
+ // Mimetype
+ if( Exists( aAttrs, KMPXMediaGeneralMimeType ) )
+ {
+ const CUpnpAttribute* attr = UPnPItemUtility
+ ::FindAttributeByName( iItemResolver->Resource(),
+ KAttributeProtocolInfo );
+ if ( attr != 0 )
+ {
+ __LOG( "CUPnPTrack: Metadata: MimeType" );
+ CUpnpDlnaProtocolInfo* pInfo =
+ CUpnpDlnaProtocolInfo::NewL( attr->Value() );
+ CleanupStack::PushL( pInfo );
+ aMedia.SetTextValueL(
+ KMPXMediaGeneralMimeType, *To16LC( pInfo->ThirdField() ) );
+ CleanupStack::PopAndDestroy();
+ CleanupStack::PopAndDestroy( pInfo );
+ pInfo = NULL;
+ }
+ }
+ }
+
+// --------------------------------------------------------------------------
+// CUPnPTrack::Exists
+// --------------------------------------------------------------------------
+//
+TBool CUPnPTrack::Exists( const TArray<TMPXAttribute>& aAttrs,
+ const TMPXAttributeData& aAttrData ) const
+ {
+ TBool found = EFalse;
+ for( TInt i = 0; i < aAttrs.Count() && !found; ++i )
+ {
+ if ( aAttrs[i].ContentId() == aAttrData.iContentId &&
+ aAttrs[i].AttributeId() & aAttrData.iAttributeId )
+ {
+ found = ETrue;
+ }
+ }
+ return found;
+ }
+
+// --------------------------------------------------------------------------
+// CUPnPTrack::To16LC
+// --------------------------------------------------------------------------
+//
+const HBufC16* CUPnPTrack::To16LC( const TDesC8& aText )
+ {
+ HBufC16* result = 0;
+ result = EscapeUtils::ConvertToUnicodeFromUtf8L( aText );
+ CleanupStack::PushL( result );
+ return result;
+ }
+
+// --------------------------------------------------------------------------
+// callbacks from MUPnPItemResolverObserver
+// --------------------------------------------------------------------------
+//
+void CUPnPTrack::ResolveComplete( const MUPnPItemResolver& /*aResolver*/,
+ TInt aError )
+ {
+ __LOG1( "CUPnPTrack::ResolveComplete err: %d", aError );
+ iIsItemSolved = ETrue;
+ if ( iState == EStateResolving )
+ {
+ iState = EStateReady;
+
+ // handle pending metadata query
+ if ( iTrackLocation == ETrackLocationRemote && iMetadataObserver )
+ {
+ __LOG( "Handle pending metadata query");
+ TRAP_IGNORE(
+ DeliverMedataL( iQueriedAttributes.Array(), *iMetadataObserver,
+ aError ) );
+ iMetadataObserver = 0;
+ iQueriedAttributes.Reset();
+ }
+
+ // call back
+ MUPnPTrackObserver& observer = *iTrackObserver;
+ iTrackObserver = 0;
+ observer.ResolveURIComplete( aError );
+ }
+ else if ( iState == EStateSelfDestruct )
+ {
+ __LOG( "CUPnPTrack: self-destructing" );
+ delete this;
+ }
+ }
+
+// --------------------------------------------------------------------------
+// Private methods of CUPnPTrack
+// --------------------------------------------------------------------------
+
+// --------------------------------------------------------------------------
+// CUPnPTrack::ParsePiecesL
+// Parse and save media server id and object id from given descriptor
+// --------------------------------------------------------------------------
+//
+void CUPnPTrack::ParsePiecesL( const TDesC& aSong )
+ {
+ // Delete if already exist
+ if( iMediaServer )
+ {
+ delete iMediaServer;
+ iMediaServer = 0;
+ }
+ if( iObjectId )
+ {
+ delete iObjectId;
+ iObjectId = 0;
+ }
+
+ // Leave if argument is not valid
+ if( aSong.Length() < KUPnPPrefixLength )
+ {
+ User::Leave( KErrArgument );
+ }
+
+ TInt lenght = aSong.Length();
+ TInt position = 0;
+ TChar colon( KCharCodeColon );
+ TChar separate( KCharCodeSeparate );
+
+ // At first separe "upnp:" prefix from descriptor
+ position = aSong.Locate( colon );
+ TPtrC tmp = aSong.Mid( position + 1, (lenght - KUPnPPrefixLength ) );
+
+ // Get media server id
+ position = tmp.Locate( separate );
+ // Leave if separator character not found
+ if( position == KErrNotFound )
+ {
+ User::Leave( KErrNotFound );
+ }
+ TPtrC mediaserverId = tmp.Left( position );
+ iMediaServer = HBufC8::NewL( mediaserverId.Length() );
+ iMediaServer->Des().Copy( mediaserverId );
+
+ // Get object id
+ TPtrC objId = tmp.Mid( position + 1, ( (tmp.Length()
+ - mediaserverId.Length() ) - 1 ) );
+ iObjectId = HBufC8::NewL( objId.Length() );
+ iObjectId->Des().Copy( objId );
+ }
+
+