diff -r 6369bfd1b60d -r 08b5eae9f9ff upnpavcontroller/upnprenderingstatemachine/src/upnprenderingstatemachine.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upnpavcontroller/upnprenderingstatemachine/src/upnprenderingstatemachine.cpp Wed Nov 03 11:45:09 2010 +0200 @@ -0,0 +1,1690 @@ +/* +* Copyright (c) 2007,2009 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: Implementation of generic upnp remote rendering state machine +* +*/ + +// INCLUDES +// dlnasrv / mediaserver api +#include + +// dlnasrv / avcontroller api +#include "upnpavrenderingsession.h" +#include "upnpavdevice.h" +#include "upnpitemutility.h" +#include "upnpconstantdefs.h" +#include "upnprenderingstatemachineconstants.h" +#include "upnprenderingstatemachineobserver.h" + +// dlnasrv / component internal +#include "upnprenderingoperation.h" +#include "upnprenderingplaytimecalculator.h" +#include "upnpretraces.h" +#include "upnprenderingstatemachine.h" +#include "upnpavcontrollerglobals.h" + +_LIT( KComponentLogfile, "upnprenderingstatemachine.txt"); +#include "upnplog.h" + +// CONSTANTS +const TInt64 KMicrosecondsInMillisecond = 1000; +const TInt KSeekThresholdTime = 2000; //ms + +// ======== MEMBER FUNCTIONS ======== + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::NewL +// Static constructor. +// -------------------------------------------------------------------------- +// +EXPORT_C CUpnpRenderingStateMachine* CUpnpRenderingStateMachine::NewL( + MUPnPAVRenderingSession& aSession ) + { + CUpnpRenderingStateMachine* self = + new (ELeave) CUpnpRenderingStateMachine( aSession ); + CleanupStack::PushL( self ); + self->ConstructL(); + CleanupStack::Pop( self ); + return self; + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::CUpnpRenderingStateMachine +// default constructor +// -------------------------------------------------------------------------- +// +CUpnpRenderingStateMachine::CUpnpRenderingStateMachine( + MUPnPAVRenderingSession& aSession ) + : iSession( aSession ) + { + iState = Upnp::EOffSync; + iObserver = NULL; + iOptions = 0; + iSelector = 0; + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::ConstructL +// 2nd phase constructor +// -------------------------------------------------------------------------- +// +void CUpnpRenderingStateMachine::ConstructL() + { + __LOG( "RenderingStateMachine::ConstructL" ); + iPlaytimeCalculator = new(ELeave) CUpnpRenderingPlaytimeCalculator(); + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::~CUpnpRenderingStateMachine +// destructor +// -------------------------------------------------------------------------- +// +EXPORT_C CUpnpRenderingStateMachine::~CUpnpRenderingStateMachine() + { + __LOG( "RenderingStateMachine: ~CUpnpRenderingStateMachine" ); + delete iPlaytimeCalculator; + iQueue.Close(); + iObserver = NULL; + __LOG( "RenderingStateMachine: ~CUpnpRenderingStateMachine end" ); + } + + + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::SetOptions +// sets the option flags for this state machine +// -------------------------------------------------------------------------- +// +EXPORT_C void CUpnpRenderingStateMachine::SetOptions( TInt aOptions ) + { + iOptions = aOptions; + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::Options +// -------------------------------------------------------------------------- +// +EXPORT_C TInt CUpnpRenderingStateMachine::Options() const + { + return iOptions; + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::SetObserver +// -------------------------------------------------------------------------- +// +EXPORT_C void CUpnpRenderingStateMachine::SetObserver( + MUpnpRenderingStateMachineObserver& aObserver ) + { + iObserver = &aObserver; + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::RemoveObserver +// -------------------------------------------------------------------------- +// +EXPORT_C void CUpnpRenderingStateMachine::RemoveObserver() + { + iObserver = NULL; + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::SetResourceSelector +// custom selection for resource inside item +// selects which resource is used in SetAvTransportUri +// -------------------------------------------------------------------------- +// +EXPORT_C void CUpnpRenderingStateMachine::SetResourceSelector( + MUPnPResourceSelector& aSelector ) + { + __LOG( "RenderingStateMachine: SetResourceSelector" ); + iSelector = &aSelector; + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::SyncL +// synchronises the state machine with renderer +// -------------------------------------------------------------------------- +// +EXPORT_C void CUpnpRenderingStateMachine::SyncL() + { + __LOG( "CUpnpRenderingStateMachine::SyncL" ); + __ASSERT( !IsInSync(), __FILE__, __LINE__ ); + __ASSERT( !IsBusy(), __FILE__, __LINE__ ); + if ( IsInSync() ) + { + User::Leave( KErrGeneral ); + } + if ( IsBusy() ) + { + User::Leave( KErrServerBusy ); + } + TUpnpRenderingOperation sync( + Upnp::ESync ); + PushAndExecuteL( sync ); + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::SetOffSync +// sets this state machine off sync +// -------------------------------------------------------------------------- +// +EXPORT_C void CUpnpRenderingStateMachine::SetOffSync() + { + __LOG( "CUpnpRenderingStateMachine::SetOffSync" ); + __ASSERT( !IsBusy(), __FILE__, __LINE__ ); + iState = Upnp::EOffSync; + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::IsInSync +// -------------------------------------------------------------------------- +// +EXPORT_C TBool CUpnpRenderingStateMachine::IsInSync() const + { + return (iState & Upnp::EStateMaskInSync) != 0; + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::Command +// Issues a command to the renderer through the state machine +// -------------------------------------------------------------------------- +// +EXPORT_C void CUpnpRenderingStateMachine::CommandL( + Upnp::TCommand aCommand, + TInt aCommandParameter, + const CUpnpItem* aMedia ) + { + __LOG1( "CUpnpRenderingStateMachine::CommandL cmd = %d",aCommand ); + TUpnpRenderingOperation cmd( + aCommand, aCommandParameter, aMedia ); + PushAndExecuteL( cmd ); + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::IsBusy +// -------------------------------------------------------------------------- +// +EXPORT_C TBool CUpnpRenderingStateMachine::IsBusy() const + { + return !IsFree(); + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::State +// -------------------------------------------------------------------------- +// +EXPORT_C Upnp::TState CUpnpRenderingStateMachine::State() const + { + return iState; + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::Duration +// -------------------------------------------------------------------------- +// +EXPORT_C TInt CUpnpRenderingStateMachine::Duration() const + { + __LOG( "CUpnpRenderingStateMachine::Duration" ); + __ASSERT( iState & Upnp::EStateMaskActive, + __FILE__, __LINE__ ); + __ASSERT( iPlaytimeCalculator, __FILE__, __LINE__ ); + return iPlaytimeCalculator->Duration(); + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::Position +// -------------------------------------------------------------------------- +// +EXPORT_C TInt CUpnpRenderingStateMachine::Position() const + { + __LOG( "CUpnpRenderingStateMachine::Position" ); + __ASSERT( iState & Upnp::EStateMaskActive, + __FILE__, __LINE__ ); + __ASSERT( iPlaytimeCalculator, __FILE__, __LINE__ ); + return iPlaytimeCalculator->Position(); + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::HasPauseCapability +// -------------------------------------------------------------------------- +// +EXPORT_C TBool CUpnpRenderingStateMachine::HasPauseCapability() const + { + return iSession.Device().PauseCapability(); + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::InteractOperationComplete +// -------------------------------------------------------------------------- +// +EXPORT_C void CUpnpRenderingStateMachine::InteractOperationComplete( + TInt aError, TUPnPAVInteractOperation aOperation ) + { + __LOG( "CUpnpRenderingStateMachine::InteractOperationComplete" ); + Process( + CUpnpRenderingStateMachine::EInteractOperationComplete, + aError, &aOperation ); + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::PositionInfoResult +// -------------------------------------------------------------------------- +// +EXPORT_C void CUpnpRenderingStateMachine::PositionInfoResult( + TInt aError, const TDesC8& aPosition, const TDesC8& aLength ) + { + __LOG( "CUpnpRenderingStateMachine::PositionInfoResult" ); + Process( + CUpnpRenderingStateMachine::EPositionInfo, + aError, (TAny*)&aPosition, (TAny*)&aLength ); + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::SetURIResult +// -------------------------------------------------------------------------- +// +EXPORT_C void CUpnpRenderingStateMachine::SetURIResult( TInt aError ) + { + __LOG( "CUpnpRenderingStateMachine::SetURIResult" ); + Process( + CUpnpRenderingStateMachine::ESetUriResult, + aError ); + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::SetNextURIResult +// -------------------------------------------------------------------------- +// +EXPORT_C void CUpnpRenderingStateMachine::SetNextURIResult( TInt /*aError*/ ) + { + __LOG( "CUpnpRenderingStateMachine::SetNextURIResult" ); + __PANIC( __FILE__, __LINE__ ); + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::ReportSyncReady +// -------------------------------------------------------------------------- +// +void CUpnpRenderingStateMachine::ReportSyncReady( + TInt aError, Upnp::TState aNewState ) + { + __LOG3( "RenderingStateMachine: SyncReady err=%i %s->%s", + aError, StateName( iState ), StateName( aNewState ) ); + iState = aNewState; + if ( iObserver ) + { + iObserver->RendererSyncReady( aError, iState ); + } + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::ReportStateChanged +// -------------------------------------------------------------------------- +// +void CUpnpRenderingStateMachine::ReportStateChanged( + TInt aError, Upnp::TState aNewState, + TBool aActionResponse, TInt aStateParam ) + { + __LOG3( "RenderingStateMachine: StateChanged err=%i %s->%s", + aError, StateName( iState ), StateName( aNewState ) ); + iState = aNewState; + if ( iObserver ) + { + iObserver->RenderingStateChanged( + aError, iState, aActionResponse, aStateParam ); + } + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::ReportPositionSync +// -------------------------------------------------------------------------- +// +void CUpnpRenderingStateMachine::ReportPositionSync( TInt aError, + Upnp::TPositionMode aMode, + TInt aDuration, TInt aPosition ) + { + __LOG1( "RenderingStateMachine: PositionSync %i", aError ); + if ( iObserver ) + { + iObserver->PositionSync( aError, aMode, aDuration, aPosition ); + } + } + + + +/***************************************** + ** State machine states implementation ** + *****************************************/ + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::ExecuteOperationL +// -------------------------------------------------------------------------- +// +MUpnpRenderingOperationExecutor::TResult +CUpnpRenderingStateMachine::ExecuteOperationL( + const TUpnpRenderingOperation& aOperation, + const TUpnpRenderingOperation& aCurrent ) + { + __LOG1( "RenderingStateMachine: ExecuteOperationL (operation=%d)", + aOperation.Command() ); + MUpnpRenderingOperationExecutor::TResult ret(ECompleted); + switch( aOperation.Command() ) + { + case Upnp::ESync: + { + ret = ExecuteOperationSyncL( aOperation, aCurrent ); + break; + } + case Upnp::EPlay: + { + ret = ExecuteOperationPlayL( aOperation, aCurrent ); + break; + } + case Upnp::EStop: + { + ret = ExecuteOperationStopL( aOperation, aCurrent ); + break; + } + case Upnp::EPause: + { + ret = ExecuteOperationPauseL( aOperation, aCurrent ); + break; + } + case Upnp::EResume: + { + ret = ExecuteOperationResumeL( aOperation, aCurrent ); + break; + } + case Upnp::ERestart: + { + ret = ExecuteOperationRestartL( aOperation, aCurrent ); + break; + } + case Upnp::ESeek: + { + ret = ExecuteOperationSeekL( aOperation, aCurrent ); + break; + } + case Upnp::ECalibrate: + { + ret = ExecuteOperationCalibrateL( aOperation, aCurrent ); + break; + } + case Upnp::ESetUri: + { + ret = ExecuteOperationSetUriL( aOperation, aCurrent ); + break; + } + default: + __PANIC( __FILE__, __LINE__ ); + } + return ret; + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::ProcessOperationL +// -------------------------------------------------------------------------- +// +MUpnpRenderingOperationExecutor::TResult +CUpnpRenderingStateMachine::ProcessOperationL( + const TUpnpRenderingOperation& aOperation, TInt aEvent, + TInt aError, const TAny* aParam1, const TAny* aParam2 ) + { + __LOG2( "RenderingStateMachine: ProcessOperationL (event=%d) (aError=%d)", + aEvent, aError ); + + MUpnpRenderingOperationExecutor::TResult ret(ECompleted); + switch( aEvent ) + { + case EInteractOperationComplete: + { + TUPnPAVInteractOperation op = + *(TUPnPAVInteractOperation*)aParam1; + if ( op == EUPnPAVPlay + || op == EUPnPAVPlayUser + || op == EUPnPAVStop + || op == EUPnPAVStopUser + || op == EUPnPAVPause + || op == EUPnPAVPauseUser + || op == EUPnPAVSeek ) + { + ret = ProcessAVEvents( aOperation, op, aError ); + } + else if ( op == EUPnPAVTransition ) + { + ret = EContinue; + __LOG1( "RenderingStateMachine: Transition %d", aError ); + } + else + { + __PANIC( __FILE__, __LINE__ ); + } + break; + } + case EPositionInfo: + { + const TDesC8& pos = *(const TDesC8*)aParam1; + const TDesC8& len = *(const TDesC8*)aParam2; + ret = ProcessPositionInfo( aOperation, aError, pos, len ); + break; + } + case ESetUriResult: + { + ret = ProcessSetURIResultL( aOperation, aError ); + break; + } + default: + { + __PANIC( __FILE__, __LINE__ ); + } + } + return ret; + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::HandleAsyncError +// cancels the ongoing operation +// -------------------------------------------------------------------------- +// +void CUpnpRenderingStateMachine::HandleAsyncError( + const TUpnpRenderingOperation& aOperation, TInt aError ) + { + __LOG( "RenderingStateMachine: HandleAsyncError" ); + switch ( aOperation.Command() ) + { + case Upnp::ESync: + { + ReportSyncReady( aError, State() ); + break; + } + case Upnp::EPlay: + { + if ( aOperation.IsUserOriented() ) + { + ReportStateChanged( aError, + Upnp::EStopped, ETrue ); + } + break; + } + case Upnp::EStop: + { + if ( aOperation.IsUserOriented() ) + { + ReportStateChanged( aError, State(), ETrue ); + } + break; + } + case Upnp::EPause: + { + if ( aOperation.IsUserOriented() ) + { + ReportStateChanged( aError, State(), ETrue ); + } + break; + } + case Upnp::ECalibrate: + { + // do nothing + break; + } + case Upnp::ESeek: + { + TInt seekCode = Upnp::EPositionChanged; + ReportStateChanged( aError, + State(), ETrue, seekCode ); + break; + } + default: + // __PANICD( __FILE__, __LINE__ ); + break; + } + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::ExecuteOperationSyncL +// -------------------------------------------------------------------------- +// +MUpnpRenderingOperationExecutor::TResult +CUpnpRenderingStateMachine::ExecuteOperationSyncL( + const TUpnpRenderingOperation& /*aOperation*/, + const TUpnpRenderingOperation& /*aCurrent*/ ) + { + __LOG( "RenderingStateMachine: ExecuteOperationSyncL" ); + // GetRendererState() method to be implemented in AVController + // 1. if renderer has already sent its first state event, it will be + // returned immediately. + // 2. if not (SUBSCRIBE just sent) there will be a small wait loop to + // wait for the initial event + // 3. if the initial even does not arrive + // 3.1 Request the state from the renderer (optional) + // 3.2 Assume it is in stopped state. (maybe enough) + + TUPnPAVInteractOperation op = iSession.GetRendererStateL(); + __LOG1( "RenderingStateMachine: ExecuteOperationSyncL \ +current op = %d" ,op ); + Upnp::TState state = Upnp::EPlaying; + if( op == EUPnPAVStopUser ) + { + state = Upnp::EStopped; + } + ReportSyncReady( KErrNone , state ); + return ECompleted; + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::ExecuteOperationPlayL +// -------------------------------------------------------------------------- +// +MUpnpRenderingOperationExecutor::TResult +CUpnpRenderingStateMachine::ExecuteOperationPlayL( + const TUpnpRenderingOperation& aOperation, + const TUpnpRenderingOperation& aCurrent ) + { + __LOG( "RenderingStateMachine: ExecuteOperationPlayL" ); + MUpnpRenderingOperationExecutor::TResult ret(ECompleted); + if ( aCurrent == Upnp::EPlay ) + { + __LOG( "RenderingStateMachine: ExecuteOperationPlayL \ +aCurrent == Upnp::EPlay " ); + // Play request repeats. Compare content. + const CUpnpItem* item = static_cast( + aOperation.Data() ); + if ( ResourceFromL( *item ).Value() != iCurrentUri ) + { + ret = EQueue; + } + } + else if ( aCurrent != Upnp::ENone ) + { + __LOG( "RenderingStateMachine: ExecuteOperationPlayL \ +aCurrent == Upnp::ENone " ); + ret = EQueue; + } + else + { + switch( State() ) + { + case Upnp::EPaused: + { + __LOG( "RenderingStateMachine: ExecuteOperationPlayL \ +aCurrent == Upnp::EPaused " ); + + const CUpnpItem* item = + static_cast( aOperation.Data() ); + if ( item && ResourceFromL( *item ).Value() != iCurrentUri ) + { + // track changed during pause -> stop paused and start + // playing the new + TUpnpRenderingOperation stop( Upnp::EStop ); + stop.SetUserOriented( EFalse ); + PushL( stop ); + TUpnpRenderingOperation play( aOperation ); + PushL( play ); + } + else + { + iSession.PlayL(); + ret = EContinue; + } + break; + } + case Upnp::EPlaying: + case Upnp::EStopped: + { + __LOG( "RenderingStateMachine: ExecuteOperationPlayL aCurrent == Upnp::EStopped " ); + + // start play sequence + const CUpnpItem* item = static_cast( + aOperation.Data() ); + + __LOG1( "RenderingStateMachine: ExecuteOperationPlayL item 0x%x ", item ); + + if ( item ) + { + // improvements !!! + // check if user requests playing the same track that was + // previously playing. In that case we just call Play(). + // Much faster. + const TDesC8& httpuri = ResourceFromL( *item ).Value(); + + __LOG( "RenderingStateMachine: ExecuteOperationPlayL current:" ); + __LOG8( iCurrentUri ); + __LOG( "RenderingStateMachine: ExecuteOperationPlayL new:" ); + __LOG8( httpuri ); + if( iCurrentUri == httpuri ) + { + iSession.PlayL(); + } + else + { + SetUriL( aOperation ); + } + } + else + { + // uri not available + User::Leave( KErrArgument ); + } + ret = EContinue; + break; + } + case Upnp::EOffSync: + case Upnp::EBusy: + case Upnp::EDead: + case Upnp::EBuffering: + case Upnp::EStateMaskInSync: + case Upnp::EStateMaskActive: + case Upnp::EStateMaskRendering: + default: + { + User::Leave( KErrNotReady ); + } + } + } + return ret; + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::ExecuteOperationSetUriL +// -------------------------------------------------------------------------- +// +MUpnpRenderingOperationExecutor::TResult +CUpnpRenderingStateMachine::ExecuteOperationSetUriL( + const TUpnpRenderingOperation& aOperation, + const TUpnpRenderingOperation& /*aCurrent*/ ) + { + __LOG( "RenderingStateMachine: ExecuteOperationSetUriL" ); + + iStateBeforeSetUri = State(); + SetUriL( aOperation ); + + return EContinue; + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::SetUriL +// -------------------------------------------------------------------------- +// +void CUpnpRenderingStateMachine::SetUriL( + const TUpnpRenderingOperation& aOperation ) + { + __LOG( "RenderingStateMachine: ExecuteOperationSetUriL" ); + + const CUpnpItem* item = static_cast( + aOperation.Data() ); + + __LOG1( "RenderingStateMachine: SetUriL item 0x%x ", item ); + + if ( item ) + { + const TDesC8& httpuri = ResourceFromL( *item ).Value(); + + __LOG( "RenderingStateMachine: SetUriL current:" ); + __LOG8( iCurrentUri ); + __LOG( "RenderingStateMachine: SetUriL new:" ); + __LOG8( httpuri ); + + __LOG( "RenderingStateMachine: SetUriL iSession.SetURIL" ); + + iSession.SetURIL( httpuri, *item ); + iCurrentUri.Copy( httpuri ); + ReportStateChanged( KErrNone, Upnp::EBuffering ); + } + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::ExecuteOperationStopL +// -------------------------------------------------------------------------- +// +MUpnpRenderingOperationExecutor::TResult +CUpnpRenderingStateMachine::ExecuteOperationStopL( + const TUpnpRenderingOperation& /*aOperation*/, + const TUpnpRenderingOperation& aCurrent ) + { + __LOG( "RenderingStateMachine: ExecuteOperationStopL" ); + MUpnpRenderingOperationExecutor::TResult ret(ECompleted); + if ( aCurrent == Upnp::EStop ) + { + // already stopping, no need to stop twice + } + else if ( aCurrent != Upnp::ENone ) + { + ret = EQueue; + } + else + { + switch( State() ) + { + case Upnp::EPlaying: + case Upnp::EPaused: + { + iSession.StopL(); + ret = EContinue; + break; + } + case Upnp::EStopped: + { + ReportStateChanged( KErrNone, State() ); + break; + } + case Upnp::EOffSync: + case Upnp::EBusy: + case Upnp::EDead: + case Upnp::EBuffering: + case Upnp::EStateMaskInSync: + case Upnp::EStateMaskActive: + case Upnp::EStateMaskRendering: + default: + { + User::Leave( KErrNotReady ); + } + } + } + return ret; + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::ExecuteOperationPauseL +// -------------------------------------------------------------------------- +// +MUpnpRenderingOperationExecutor::TResult +CUpnpRenderingStateMachine::ExecuteOperationPauseL( + const TUpnpRenderingOperation& /*aOperation*/, + const TUpnpRenderingOperation& aCurrent ) + { + __LOG( "RenderingStateMachine: ExecuteOperationPauseL" ); + MUpnpRenderingOperationExecutor::TResult ret(ECompleted); + if ( aCurrent == Upnp::EPause ) + { + // pause during pause request + } + else if ( aCurrent != Upnp::ENone ) + { + ret = EQueue; + } + else + { + switch( State() ) + { + case Upnp::EPlaying: + { + iSession.PauseL(); + ret = EContinue; + break; + } + case Upnp::EPaused: + { + ReportStateChanged( KErrNone, State() ); + ret = ECompleted; + break; + } + case Upnp::EOffSync: + case Upnp::EBusy: + case Upnp::EDead: + case Upnp::EStopped: + case Upnp::EBuffering: + case Upnp::EStateMaskInSync: + case Upnp::EStateMaskActive: + case Upnp::EStateMaskRendering: + default: + { + User::Leave( KErrNotReady ); + } + } + } + return ret; + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::ExecuteOperationResumeL +// -------------------------------------------------------------------------- +// +MUpnpRenderingOperationExecutor::TResult +CUpnpRenderingStateMachine::ExecuteOperationResumeL( + const TUpnpRenderingOperation& /*aOperation*/, + const TUpnpRenderingOperation& aCurrent ) + { + __LOG( "RenderingStateMachine: ExecuteOperationResumeL" ); + MUpnpRenderingOperationExecutor::TResult ret(ECompleted); + if ( aCurrent == Upnp::EPlay ) + { + // resume during play + } + else if ( aCurrent != Upnp::ENone ) + { + ret = EQueue; + } + else + { + switch( State() ) + { + case Upnp::EPlaying: + { + ReportStateChanged( KErrNone, State() ); + break; + } + case Upnp::EPaused: + { + iSession.PlayL(); + ret = EContinue; + break; + } + case Upnp::EOffSync: + case Upnp::EBusy: + case Upnp::EDead: + case Upnp::EStopped: + case Upnp::EBuffering: + case Upnp::EStateMaskInSync: + case Upnp::EStateMaskActive: + case Upnp::EStateMaskRendering: + default: + { + User::Leave( KErrNotReady ); + } + } + + } + return ret; + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::ExecuteOperationRestartL +// -------------------------------------------------------------------------- +// +MUpnpRenderingOperationExecutor::TResult +CUpnpRenderingStateMachine::ExecuteOperationRestartL( + const TUpnpRenderingOperation& /*aOperation*/, + const TUpnpRenderingOperation& aCurrent ) + { + __LOG( "RenderingStateMachine: ExecuteOperationRestartL" ); + // handle parallel + MUpnpRenderingOperationExecutor::TResult ret(ECompleted); + if ( aCurrent != Upnp::ENone ) + { + ret = EQueue; + } + else + { + switch( State() ) + { + case Upnp::EPlaying: + { + TUpnpRenderingOperation stop( + Upnp::EStop ); + stop.SetUserOriented( EFalse ); + PushL( stop ); + TUpnpRenderingOperation play( + Upnp::EPlay ); + PushL( play ); + break; + } + case Upnp::EPaused: + { + TUpnpRenderingOperation stop( + Upnp::EStop ); + PushL( stop ); + break; + } + case Upnp::EOffSync: + case Upnp::EBusy: + case Upnp::EDead: + case Upnp::EStopped: + case Upnp::EBuffering: + case Upnp::EStateMaskInSync: + case Upnp::EStateMaskActive: + case Upnp::EStateMaskRendering: + default: + { + User::Leave( KErrNotReady ); + } + } + + } + return ret; + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::ExecuteOperationSeekL +// -------------------------------------------------------------------------- +// +MUpnpRenderingOperationExecutor::TResult +CUpnpRenderingStateMachine::ExecuteOperationSeekL( + const TUpnpRenderingOperation& aOperation, + const TUpnpRenderingOperation& aCurrent ) + { + __LOG( "RenderingStateMachine: ExecuteOperationSeekL" ); + MUpnpRenderingOperationExecutor::TResult ret(ECompleted); + + TInt currentPosition = iPlaytimeCalculator->Position(); + TInt seekPosition = aOperation.Param(); + __LOG3( "RenderingStateMachine: state 0x%x, currentPosition %d, seekPosition %d", + State(), currentPosition, seekPosition ); + if( seekPosition < currentPosition + KSeekThresholdTime && + seekPosition > currentPosition - KSeekThresholdTime ) + { + // ignore seek + } + else if ( aCurrent != Upnp::ENone ) + { + ret = EQueue; + } + else if ( State() == Upnp::EStopped || + State() == Upnp::EPlaying ) + { + iSeekPostion = seekPosition; + TTime seekTime = (TInt64)iSeekPostion * KMicrosecondsInMillisecond; + iSession.SeekRelTimeL( seekTime ); + ret = EContinue; + } + else if( State() == Upnp::EPaused ) + { + TInt lastIndex( iQueue.Count() - 1 ); + TBool found(EFalse); + for( TInt i(lastIndex); i > 0; i-- ) + { + if( iQueue[i].Command() == Upnp::EPause ) + { + break; + } + else if( iQueue[i].Command() == Upnp::EPlay || + iQueue[i].Command() == Upnp::EResume ) + { + __LOG( "RenderingStateMachine: ExecuteOperationSeekL \ + play or resume found adding seek to queue" ); + TUpnpRenderingOperation seek( aOperation ); + PushL( seek ); + found = ETrue; + break; + } + } + /** + * if play or resume was not found it is + * illigal to execute seek in renderer + */ + if ( !found ) + { + User::Leave( KErrNotReady ); + } + } + else + { + User::Leave( KErrNotReady ); + } + return ret; + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::ExecuteOperationCalibrateL +// -------------------------------------------------------------------------- +// +MUpnpRenderingOperationExecutor::TResult +CUpnpRenderingStateMachine::ExecuteOperationCalibrateL( + const TUpnpRenderingOperation& /*aOperation*/, + const TUpnpRenderingOperation& aCurrent ) + { + __LOG( "RenderingStateMachine: ExecuteOperationCalibrateL" ); + MUpnpRenderingOperationExecutor::TResult ret(ECompleted); + if ( aCurrent != Upnp::ENone ) + { + ret = EQueue; + } + else if ( State() == Upnp::EPlaying || + State() == Upnp::EPaused ) + { + iGetPositionStartMark.UniversalTime(); + iSession.GetPositionInfoL(); + ret = EContinue; + } + else + { + User::Leave( KErrNotReady ); + } + return ret; + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::ProcessEvents +// -------------------------------------------------------------------------- +// +MUpnpRenderingOperationExecutor::TResult +CUpnpRenderingStateMachine::ProcessAVEvents( + const TUpnpRenderingOperation& aOperation, + const TUPnPAVInteractOperation aIntOp, + TInt aError ) + { + MUpnpRenderingOperationExecutor::TResult ret(ECompleted); + if( !aError ) + { + switch( aIntOp ) + { + case EUPnPAVPlay: + { + if ( State() == Upnp::EPaused ) + { + iPlaytimeCalculator->Resume(); + } + else if ( !iPlaytimeCalculator->IsPlaying() ) + { + iPlaytimeCalculator->Start(); + } + ReportStateChanged( aError, + Upnp::EPlaying ); + break; + } + case EUPnPAVPlayUser: + { + // spontaneous state change to playing + if ( State() == Upnp::EStopped ) + { + iPlaytimeCalculator->Start(); + } + else if ( State() == Upnp::EPaused ) + { + iPlaytimeCalculator->Resume(); + } + ReportStateChanged( aError, + Upnp::EPlaying, EFalse ); + ret = EContinue; + break; + } + case EUPnPAVStop: + { + iPlaytimeCalculator->Stop(); + ReportStateChanged( aError, + Upnp::EStopped , ETrue ); + break; + } + case EUPnPAVStopUser: + { + // spontaneous state change to stopped + iPlaytimeCalculator->Stop(); + TInt stopcode = Upnp::ENoMedia; + if( iPlaytimeCalculator->Duration() != KErrNotFound ) + { + stopcode = ( iPlaytimeCalculator->IsTrackComplete() ? + Upnp::ETrackCompleted : + Upnp::EStopRequested ); + } + ReportStateChanged( aError, + Upnp::EStopped, EFalse, stopcode ); + ret = EContinue; + break; + } + case EUPnPAVPause: + { + iPlaytimeCalculator->Pause(); + ReportStateChanged( aError, + Upnp::EPaused ); + break; + } + case EUPnPAVPauseUser: + { + // spontaneous state change to paused + iPlaytimeCalculator->Pause(); + ReportStateChanged( aError, + Upnp::EPaused, EFalse ); + ret = EContinue; + break; + } + case EUPnPAVSeek: + { + iPlaytimeCalculator->RestartAt( iSeekPostion ); + TInt seekCode = Upnp::EPositionChanged; + ReportStateChanged( aError, + State(), ETrue, seekCode ); + break; + } + default: + { + __LOG2( "RenderingStateMachine: aIntOp = %d event during %s", + aIntOp , CommandName(aOperation.Command()) ); + break; + } + } + } + else + { + ReportStateChanged( aError, + State() ); + } + return ret; + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::ProcessPositionInfo +// -------------------------------------------------------------------------- +// +MUpnpRenderingOperationExecutor::TResult +CUpnpRenderingStateMachine::ProcessPositionInfo( + const TUpnpRenderingOperation& aOperation, + TInt aError, const TDesC8& aPosition, const TDesC8& aLength ) + { + __LOG( "RenderingStateMachine: ProcessPositionInfo" ); + __ASSERT( aOperation == Upnp::ECalibrate, + __FILE__, __LINE__ ); + + TInt duration = 0; + TInt position = 0; + if ( aError == KErrNone ) + { + aError = UPnPItemUtility::UPnPDurationAsMilliseconds( + aLength, duration ); + } + if ( aError == KErrNone ) + { + aError = UPnPItemUtility::UPnPDurationAsMilliseconds( + aPosition, position ); + } + if ( aError == KErrNone ) + { + //Incorporate the delay in receiving the response. + TTime posStopMark; posStopMark.UniversalTime(); + TTimeIntervalMicroSeconds delay = + posStopMark.MicroSecondsFrom( iGetPositionStartMark ); + TInt delayInMSec = ( delay.Int64() / KMicrosecondsInMillisecond ); + position += (delayInMSec/2); + if ( position > duration ) + { + position = duration; + } + __LOG2( "RenderingStateMachine: ProcessPositionInfo added delay(%d) \ +position(%d)", delayInMSec/2, position); + iPlaytimeCalculator->AcknowledgePositionInfo( + duration, position ); + } + // response + // trick states + ReportPositionSync( aError, + Upnp::EPlayingNormally, + iPlaytimeCalculator->Duration(), + iPlaytimeCalculator->Position() ); + return ECompleted; + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::ProcessSetURIResultL +// -------------------------------------------------------------------------- +// +MUpnpRenderingOperationExecutor::TResult +CUpnpRenderingStateMachine::ProcessSetURIResultL( + const TUpnpRenderingOperation& aOperation, + TInt aError ) + { + __LOG( "RenderingStateMachine: ProcessSetURIResultL" ); + TBool validOp( aOperation == Upnp::EPlay || aOperation == Upnp::ESetUri ); + __ASSERT( validOp, __FILE__, __LINE__ ); + MUpnpRenderingOperationExecutor::TResult ret(ECompleted); + if ( aError == KErrNone ) + { + // get duration from item and write to playtime calculator + const CUpnpItem* item = static_cast( + aOperation.Data() ); + const CUpnpAttribute* attr = + UPnPItemUtility::FindAttributeByName( + ResourceFromL( *item ), KAttributeDuration ); + if ( attr ) + { + TInt ms = 0; + if ( UPnPItemUtility::UPnPDurationAsMilliseconds( + attr->Value(), ms ) == KErrNone ) + { + iPlaytimeCalculator->SetDuration( ms ); + } + } + if( aOperation.Command() == Upnp::EPlay ) + { + iSession.PlayL(); + ret = EContinue; + } + else + { + ReportStateChanged( KErrNone, Upnp::EBuffering, EFalse, ETrue ); + iState = iStateBeforeSetUri; + ret = ECompleted; + } + } + //if seturi failed send first stop in that case! + else if( aError == KErrAccessDenied || aError == KErrLocked ) + { + iCurrentUri.Zero(); + iQueue.Remove( 0 ); // removes play from queue + TUpnpRenderingOperation stop( Upnp::EStop ); + stop.SetUserOriented( EFalse ); + PushL( stop ); // add stop + iSession.StopL(); + ret = EContinue; // continue = waits for operation to complete + TUpnpRenderingOperation play( aOperation ); + PushL( play ); + } + else + { + HandleAsyncError( aOperation, aError ); + } + return ret; + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::ResourceFrom +// -------------------------------------------------------------------------- +// +const CUpnpElement& CUpnpRenderingStateMachine::ResourceFromL( + const CUpnpItem& aItem ) + { + const CUpnpElement* element( NULL ); + if ( iSelector ) + { + element = &iSelector->SelectResourceL( aItem ); + } + else + { + element = &iDefaultSelector.SelectResourceL( aItem ); + } + return *element; + } + + +/************************** + ** Queue implementation ** + **************************/ + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::Size +// -------------------------------------------------------------------------- +// +TInt CUpnpRenderingStateMachine::Size() const + { + __LOG1( "CUpnpRenderingStateMachine::Size(%d)", iQueue.Count() ); + return iQueue.Count(); + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::IsFree +// Checks if the queue is free for new operations +// -------------------------------------------------------------------------- +// +TBool CUpnpRenderingStateMachine::IsFree() const + { + return Size() == 0; + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::PushL +// push operation to queue. Don't try to execute it yet. +// -------------------------------------------------------------------------- +// +void CUpnpRenderingStateMachine::PushL( + const TUpnpRenderingOperation& aOperation ) + { + __LOG( "CUpnpRenderingStateMachine::PushL" ); + iQueue.AppendL( aOperation ); + CompressQueueL(); + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::PushAndExecuteL +// push operation to queue and execute it immediately +// -------------------------------------------------------------------------- +// +void CUpnpRenderingStateMachine::PushAndExecuteL( + const TUpnpRenderingOperation& aOperation ) + { + __LOG( "CUpnpRenderingStateMachine::PushAndExecuteL" ); + if ( IsFree() ) + { + PushL( aOperation ); + ExecuteRecursivelyL(); + } + else + { + ExecuteParallelL( aOperation ); + } + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingAlgorithms::Execute +// Runs an operation in queue +// -------------------------------------------------------------------------- +// +void CUpnpRenderingStateMachine::Execute() + { + __LOG( "CUpnpRenderingStateMachine::Execute" ); + TUpnpRenderingOperation op = Current(); + if ( op.IsValid() ) + { + __LOG1( "OperationQueue: Executing %s", + CommandName(op.Command()) ); + MUpnpRenderingOperationExecutor::TResult result = ECompleted; + TRAPD( leave, result = ExecuteOperationL( op, + Upnp::ENone ) ); + __LOG1( "OperationQueue: leave code(%d) ", leave ); + + if ( leave != KErrNone ) + { + __LOG1( "OperationQueue: %s failed while executing", + CommandName(op.Command()) ); + iQueue.Reset(); + HandleAsyncError( op, leave ); + } + else if ( result == ECompleted ) + { + __LOG1( "OperationQueue: %s completed while executing", + CommandName(op.Command()) ); + if( iQueue.Count() > 0 ) + { + iQueue.Remove( 0 ); + Execute(); // recur + } + } + else if ( result == EContinue ) + { + // operation continues + } + else + { + // illegal answer + __PANIC( __FILE__, __LINE__ ); + } + } + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::Process +// Abstract process of an operation +// -------------------------------------------------------------------------- +// +void CUpnpRenderingStateMachine::Process( + TInt aEvent, + TInt aError, const TAny* aParam1, const TAny* aParam2 ) + { + __LOG( "CUpnpRenderingStateMachine::Process" ); + TUpnpRenderingOperation op = Current(); + MUpnpRenderingOperationExecutor::TResult result = ECompleted; + __LOG2( "OperationQueue: Processing %s with event %d", + CommandName(op.Command()), aEvent ); + __LOG( "CUpnpRenderingStateMachine::Process - call ProcessOperationL" ); + TRAPD( leave, + result = ProcessOperationL( op, aEvent, + aError, aParam1, aParam2 ) ); + if ( leave != KErrNone ) + { + __LOG1( "OperationQueue: %s failed while processing", + CommandName(op.Command()) ); + iQueue.Reset(); + HandleAsyncError( op, leave ); + } + else if ( result == ECompleted ) + { + __LOG2( + "OperationQueue: %s completed while processing iQueue count = %d" + ,CommandName(op.Command()) , iQueue.Count() ); + if( iQueue.Count() > 0 ) + { + iQueue.Remove( 0 ); + Execute(); // pop new + } + } + else if ( result == EContinue ) + { + // operation continues + } + else + { + // illegal answer + __PANIC( __FILE__, __LINE__ ); + } + } + +void CUpnpRenderingStateMachine::CompressQueueL() + { + TInt queueCount( iQueue.Count() ); + + if( queueCount > 1 ) + { + TInt lastIndex( queueCount - 1 ); + + switch( iQueue[lastIndex].Command() ) + { + case Upnp::EStop: + { + /* + + TODO : check how to do later!! + + TInt removeStartIndex( 0 ); + + if( iQueue[0] == Upnp::EStop ) + { + // remove all except current ongoing stop command + removeStartIndex = lastIndex ; + } + else + { + // remove all except current ongoing command and last stop + // command + removeStartIndex = lastIndex - 1; + } + + for( TInt i = removeStartIndex; i > 0; i-- ) + { + __LOG2( "OperationQueue: Removing %s from queue index %d", + CommandName( iQueue[i].Command() ), i ); + iQueue.Remove( i ); + } */ + + break; + } + case Upnp::EPlay: + { + /* + + TODO : check how to do later!! + + TInt removeStartIndex( 0 ); + + const CUpnpItem* item = static_cast( + iQueue[lastIndex].Data() ); + + if( iQueue[0] == Upnp::EPlay && + item && + ResourceFromL( *item ).Value() == iCurrentUri ) + { + // remove all except current ongoing play command + removeStartIndex = lastIndex; + } + else + { + // remove all except current ongoing command and + // (last stop and) play command + if( queueCount > 2 && + iQueue[lastIndex - 1] == Upnp::EStop ) + { + // stop found before last play command + removeStartIndex = lastIndex - 2; + } + else + { + removeStartIndex = lastIndex - 1; + } + } + + for( TInt i = removeStartIndex; i > 0; i-- ) + { + __LOG2( "OperationQueue: Removing %s from queue index %d", + CommandName( iQueue[i].Command() ), i ); + iQueue.Remove( i ); + } + */ + break; + } + case Upnp::ECalibrate: + { + RemoveAllDublicateCommands( iQueue[lastIndex].Command() ); + break; + } + case Upnp::ERestart: + case Upnp::EPause: + case Upnp::ESeek: + case Upnp::EResume: + case Upnp::ENone: + case Upnp::ESync: + case Upnp::EClose: + case Upnp::ENext: + case Upnp::EPrev: + case Upnp::EBack: + case Upnp::EJump: + default: + { + //not implemented -> do nothing + break; + } + } + } + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::RemoveAllDublicateCommands +// -------------------------------------------------------------------------- +// +void CUpnpRenderingStateMachine::RemoveAllDublicateCommands( +Upnp::TCommand aCommand ) + { + __LOG1( "CUpnpRenderingStateMachine::RemoveAllDublicateCommands \ + command = %d" , aCommand ); + TInt lastIndex( iQueue.Count() - 1 ); + + for( TInt i(lastIndex-1); i > 0; i-- ) + { + if( iQueue[i].Command() == aCommand ) + { + __LOG1( "CUpnpRenderingStateMachine::RemoveAllDublicateCommands \ + found dublicate command index = %d",i); + // remove dublicate command + iQueue.Remove( i ); + } + } + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::Current +// -------------------------------------------------------------------------- +// +TUpnpRenderingOperation CUpnpRenderingStateMachine::Current() const + { + __LOG( "CUpnpRenderingStateMachine::Current" ); + return ( iQueue.Count() > 0 ) ? iQueue[0] : Upnp::ENone; + } + +// -------------------------------------------------------------------------- +// ResetArray +// -------------------------------------------------------------------------- +// +void ResetArray( TAny* iParam ) + { + __LOG( "CUpnpRenderingStateMachine::ResetArray" ); + RArray* array = + static_cast< RArray* >( iParam ); + array->Reset(); + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::ExecuteRecursivelyL +// Runs an operation first in the queue. If during the operation there +// were more operations added to queue, continues running recursively. +// -------------------------------------------------------------------------- +// +void CUpnpRenderingStateMachine::ExecuteRecursivelyL() + { + __LOG( "CUpnpRenderingStateMachine::ExecuteRecursivelyL" ); + TUpnpRenderingOperation op = Current(); + if ( op.IsValid() ) + { + __LOG1( "OperationQueue: Executing %s", + CommandName(op.Command()) ); + // if the operation fails, OperationArrayClose method will + // clean up the entire operations queue ! + CleanupStack::PushL( TCleanupItem( + ResetArray, &iQueue ) ); + MUpnpRenderingOperationExecutor::TResult result = + ExecuteOperationL( op, + Upnp::ENone ); + CleanupStack::Pop(); + switch( result ) + { + case ECompleted: + { + __LOG1( "OperationQueue: %s completed while executing", + CommandName(op.Command()) ); + if( iQueue.Count() > 0 ) + { + iQueue.Remove( 0 ); + ExecuteRecursivelyL(); // recur + } + break; + } + case EContinue: + { + // operation continues + break; + } + case EQueue: + default: + { + // illegal answer + __PANIC( __FILE__, __LINE__ ); + } + } + } + } + +// -------------------------------------------------------------------------- +// CUpnpRenderingStateMachine::ExecuteParallelL +// Runs an operation that was requested to put to queue. Checks if it +// completes right away or if it should wait for its turn to run. +// -------------------------------------------------------------------------- +// +void CUpnpRenderingStateMachine::ExecuteParallelL( + const TUpnpRenderingOperation& aOperation ) + { + __LOG( "CUpnpRenderingStateMachine::ExecuteParallelL" ); + TUpnpRenderingOperation current = Current(); + __LOG1( "OperationQueue: Executing %s", + CommandName(aOperation.Command()) ); + MUpnpRenderingOperationExecutor::TResult result = + ExecuteOperationL( aOperation, current ); + switch( result ) + { + case ECompleted: + { + __LOG1( "OperationQueue: %s completed while executing", + CommandName(aOperation.Command()) ); + break; + } + case EQueue: + { + PushL( aOperation ); + __LOG1( "OperationQueue: %s added to queue", + CommandName(aOperation.Command()) ); + break; + } + case EContinue: + default: + { + // illegal answer + __PANIC( __FILE__, __LINE__ ); + } + } + } + +// end of file