upnpavcontroller/upnprenderingstatemachine/src/upnprenderingstatemachine.cpp
author Sampo Huttunen <sampo.huttunen@nokia.com>
Wed, 24 Nov 2010 09:39:46 +0200
branchIOP_Improvements
changeset 45 a6c41ca11adf
parent 40 08b5eae9f9ff
permissions -rw-r--r--
Updated the SIS package, there was some BC issue with the earlier version. Also updated the platform UID to S^3 version.

/*
* 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 <upnpitem.h>

// 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<const CUpnpItem*>(
            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<const CUpnpItem*>( 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<const CUpnpItem*>(
                    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<const CUpnpItem*>(
        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<const CUpnpItem*>(
            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<const CUpnpItem*>(
                    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<TUpnpRenderingOperation>* array =
        static_cast< RArray<TUpnpRenderingOperation>* >( 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