upnpmpxplugins/upnpplaybackplugins/src/upnptrack.cpp
changeset 0 7f85d04be362
child 30 5ec426854821
--- /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 );
+    }
+
+