upnpavcontroller/upnprenderingstatemachine/src/upnprenderingstatemachine.cpp
author Sampo Huttunen <sampo.huttunen@nokia.com>
Thu, 18 Nov 2010 15:46:57 +0200
branchIOP_Improvements
changeset 44 97caed2372ca
parent 40 08b5eae9f9ff
permissions -rw-r--r--
Fixed AVController, it was accidentally set to search only for renderers. Now also servers are added to device list. Also some minor changes in package definition xml and platform API xml definition files.

/*
* 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