diff -r 000000000000 -r 7f85d04be362 upnpmpxplugins/upnpplaybackplugins/src/upnptrack.cpp --- /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 +#include +#include +#include +#include +#include +#include // for messaging +#include // for messaging + +#include // for unicode conversion + +#include +#include +#include "upnpitemutility.h" // for FindElementByName & ResourceFromItemL +#include "upnpfileutility.h" // for IsFileProtectedL +#include "upnpconstantdefs.h" // browse filtering and item element names +#include // 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& 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 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 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( + KMPXMessageGeneralId, KMPXMessagePbMediaChanged ); + msg->SetCObjectValueL( + KMPXMessagePbMedia, media ); + + // send message + aObs.HandlePlaybackMessage( *msg ); + + CleanupStack::PopAndDestroy( msg ); + CleanupStack::PopAndDestroy( media ); + CleanupStack::PopAndDestroy( &attrs ); + } + +// -------------------------------------------------------------------------- +// CUPnPTrack::DeliverMedatataL +// -------------------------------------------------------------------------- +// +void CUPnPTrack::DeliverMedataL( const TArray& aAttrs, + MMPXPlaybackPluginObserver& aObs, TInt aError ) + { + __LOG( "CUPnPTrack::DeliverMedatata"); + + // Create and fill CMPXMedia class + RArray 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& 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( + 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( + 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& 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 ); + } + +