upnpframework/upnpcommand/src/upnpimagerenderingengine.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 17 Dec 2009 08:52:00 +0200
changeset 0 7f85d04be362
child 38 5360b7ddc251
permissions -rw-r--r--
Revision: 200947 Kit: 200951

/*
* Copyright (c) 2007 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:  Engine for rendering images remotely
*
*/


// INCLUDE FILES
// upnp stack api
#include <upnpitem.h>                   // CUpnpItem
#include <upnpobject.h>                 // CUpnpObject (cast)

// upnpframework / avcontroller api
#include "upnpavrenderingsession.h"     // MUPnPAVRenderingSession
#include "upnpavsessionobserverbase.h" 

// upnpframework / avcontroller helper api
#include "upnpconstantdefs.h"           // KFilterCommon
#include "upnpitemresolver.h"           // MUPnPItemResolver
#include "upnpitemresolverobserver.h"   // MUPnPItemResolverObserver
#include "upnpitemresolverfactory.h"    // UPnPItemResolverFactory
#include "upnpitemutility.h"            // UPnPItemUtility::BelongsToClass

// upnpframework / commonui
#include "upnpcommonui.h"               // common UI for upnp video player dlg

// command internal
#include "upnpimagerenderingengineobserver.h"   // the observer interface
#include "upnpimagerenderingengine.h"   // myself
#include "upnpperiodic.h"

_LIT( KComponentLogfile, "upnpcommand.log");
#include "upnplog.h"

// CONSTANT DEFINITIONS
const TInt KReactionTimerMicrosec = 100000; // 100 millisec.


// --------------------------------------------------------------------------
// CUpnpImageRenderingEngine::NewL
// --------------------------------------------------------------------------
//
CUpnpImageRenderingEngine* CUpnpImageRenderingEngine::NewL(
            MUPnPAVController& aAVController,
            MUPnPAVRenderingSession& aSession,
            MUpnpImageRenderingEngineObserver& aObserver )
    {
    __LOG( "[UpnpCommand]\t CUpnpImageRenderingEngine::NewL" );

    // Create instance
    CUpnpImageRenderingEngine* self = NULL;
    self = new (ELeave) CUpnpImageRenderingEngine(
            aAVController, aSession, aObserver );
    CleanupStack::PushL( self );
    self->ConstructL( );
    CleanupStack::Pop( self );
    return self;
    }

// --------------------------------------------------------------------------
// CUpnpImageRenderingEngine::CUpnpImageRenderingEngine
// --------------------------------------------------------------------------
//
CUpnpImageRenderingEngine::CUpnpImageRenderingEngine(
            MUPnPAVController& aAVController,
            MUPnPAVRenderingSession& aSession,
            MUpnpImageRenderingEngineObserver& aObserver )
    : iAVController( aAVController )
    , iRenderingSession( aSession )
    , iObserver( aObserver )
    {
    __LOG( "[UpnpCommand]\t CUpnpImageRenderingEngine: Constructor" );

    // Initialise member variables
    iState = EIdle;
    iCurrentResolver = 0;
    iBufferedResolver = 0;

    // set observer
    iRenderingSession.SetObserver( *this );
    }

// --------------------------------------------------------------------------
// Second phase constructor.
// --------------------------------------------------------------------------
//
void CUpnpImageRenderingEngine::ConstructL()
    {
    __LOG( "[UpnpCommand]\t CUpnpImageRenderingEngine::ConstructL" );
    iTimer = CUPnPPeriodic::NewL( CActive::EPriorityStandard );
    
    iWlanActive = ETrue;
    }
    
// --------------------------------------------------------------------------
// Destructor.
// --------------------------------------------------------------------------
//
CUpnpImageRenderingEngine::~CUpnpImageRenderingEngine()
    {
    __LOG( "[UpnpCommand]\t CUpnpImageRenderingEngine: Destructor" );

    Cleanup();

    // Stop observing the rendering session
    iRenderingSession.RemoveObserver();

    __LOG( "[UpnpCommand]\t CUpnpImageRenderingEngine::~CUpnpImageRenderingEngine delete iCurrentResolver" );
    MUPnPItemResolver* tempCurrentResolver = iCurrentResolver;
    iCurrentResolver = NULL;
    delete tempCurrentResolver;
    
       if( iTimer )
        {    
        iTimer->Cancel();
        delete iTimer;
        iTimer = 0;
        }
    }

// --------------------------------------------------------------------------
// CUpnpImageRenderingEngine::Cleanup
// --------------------------------------------------------------------------
//
void CUpnpImageRenderingEngine::Cleanup()
    {
    __LOG( "[UpnpCommand]\t CUpnpImageRenderingEngine::Cleanup" );

    // reset state
    if ( iState != EShuttingDown )
        {
        iState = EIdle;
        }

    if( iTimer )
        {    
        iTimer->Cancel();
        }

    iBufferingNewImage = EFalse;

    // Delete resolvers
    // Delete for resolvers is done using temporary variables so that we can
    // first nullify the members and then delete the actual objects.
    // This is because local resolver deletion uses active scheduler loops
    // and therefore other asynchronous events may orrur. So we may end
    // up here in Cleanup again, during the resolver is being deleted.
    // if deletion is done the conventional way, the objects get deleted
    // twice, which is not what we want. :-)
    
    __LOG( "[UpnpCommand]\t CUpnpImageRenderingEngine::Cleanup delete iBufferedResolver" );
    MUPnPItemResolver* tempBufferedResolver = iBufferedResolver;
    iBufferedResolver = NULL;
    delete tempBufferedResolver;

    __LOG( "[UpnpCommand]\t CUpnpImageRenderingEngine::Cleanup end" );
    }

// --------------------------------------------------------------------------
// CUpnpImageRenderingEngine::PlayL
// --------------------------------------------------------------------------
//
void CUpnpImageRenderingEngine::PlayL()
    {
    __LOG1( "[UpnpCommand]\t CUpnpImageRenderingEngine::PlayL in state %d",
        iState);

    if ( iState != EShuttingDown )
        {
        if( iTimer->IsActive() )
            {
            __LOG( "[UpnpCommand]\t timer already active" );
            }
        else
            {
            TTimeIntervalMicroSeconds32 delay( KReactionTimerMicrosec );
            iTimer->Start( delay, delay, TCallBack( Timer, this ) );
            }
        }
    else
        {
        __LOG( "[UpnpCommand]\t not doing play in shutting down state" );
        }
    }

// --------------------------------------------------------------------------
// CUpnpImageRenderingEngine::StopL
// --------------------------------------------------------------------------
//
void CUpnpImageRenderingEngine::StopL()
    {
    __LOG1( "[UpnpCommand]\t CUpnpImageRenderingEngine::StopL in state %d",
        iState);

    // cancel any timers that are going on
    iTimer->Cancel();

    // remove buffered images
    iBufferingNewImage = EFalse;
    delete iBufferedResolver;
    iBufferedResolver = 0;

    switch( iState )
        {
        case EIdle:
        case EResolvingItem:
        case EResolveComplete:
        case ESettingUri: // fall through
            {
            // just cancel the sequence and do nothing
            iState = EIdle;
            break;
            }
        case EStartingPlay:
            {
            // wait for PLAY complete, then do STOP
            // then wait for STOP complete
            iState = EStopping;
            break;
            }
        case EPlaying:
            {
            // Send stop action.
            iRenderingSession.StopL();
            iState = EStopping;
            break;
            }
        case EStopping:
            {
            // already stopping - do nothing
            break;
            }
        case EShuttingDown:
            {
            // command not allowed in this state
            break;
            }
        default:
            {
            __PANICD( __FILE__, __LINE__ );
            break;
            }
        }
    }

// --------------------------------------------------------------------------
// CUpnpImageRenderingEngine::Timer
// timer callback
// --------------------------------------------------------------------------
//
TInt CUpnpImageRenderingEngine::Timer( TAny* aArg )
    {    
    CUpnpImageRenderingEngine* self =
        static_cast<CUpnpImageRenderingEngine*>( aArg );
    TRAPD( error, self->RunTimerL() )
    if ( error != KErrNone )
        self->RunError( error );
    return 0; // do not call again
    }

// --------------------------------------------------------------------------
// CUpnpImageRenderingEngine::RunTimerL
// Timer has triggered, start rendering media.
// --------------------------------------------------------------------------
//
void CUpnpImageRenderingEngine::RunTimerL()
    {
    __LOG1( "[UpnpCommand]\t CUpnpImageRenderingEngine::RunTimerL, state %d",
        iState );
        
    iTimer->Cancel();

    delete iBufferedResolver;
    iBufferedResolver = iObserver.GetMedia();
    if ( iBufferedResolver == 0 )
        {
        __LOG( "[UpnpCommand]\t resolver returned zero" );
        User::Leave( KErrCancel );
        }

    switch( iState )
        {
        case EIdle: // fall through
            {
            StartResolvingL();
            break;
            }
        case EResolvingItem: // fall through
        case EResolveComplete:
        case ESettingUri: // fall through
        case EStartingPlay:
            {
            // indicate that new image is being buffered. It will be popped
            // from buffer in next callback.
            iBufferingNewImage = ETrue;
            break;
            }
        case EPlaying:
            {
            // indicate that new image is being buffered. Send stop signal.
            // new item will be handled after stop completed.
            iBufferingNewImage = ETrue;
            iRenderingSession.StopL();
            iState = EStopping;
            break;
            }
        case EStopping:
            {
            // indicate that new image is being buffered. It will be popped
            // from buffer in next callback.
            iBufferingNewImage = ETrue;
            break;
            }
        case EShuttingDown:
            {
            // command not allowed in this state
            break;
            }
        default:
            {
            __PANICD( __FILE__, __LINE__ );
            break;
            }
        }
    }

// --------------------------------------------------------------------------
// CUpnpImageRenderingEngine::StartResolvingL
// Handles the start up of the item resolving.
// --------------------------------------------------------------------------
//
void CUpnpImageRenderingEngine::StartResolvingL()
    {
    __LOG1( "[UpnpCommand]\t CUpnpImageRenderingEngine::StartResolvingL\
 in state %d",
        iState );

    __ASSERTD( iBufferedResolver, __FILE__, __LINE__ );
    if ( !iBufferedResolver )
        {
        // something is very wrong
        User::Leave( KErrDisconnected );
        }
    
    // delete old resolver
    // destruction takes time due to unsharing, so set to null first
    // so that this wont be used else where
    MUPnPItemResolver* tempCurrentResolver = iCurrentResolver;
    iCurrentResolver = NULL;
    delete tempCurrentResolver;
    
    // take queued resolver in use
    iCurrentResolver = iBufferedResolver;
    iBufferedResolver = NULL;
    iBufferingNewImage = EFalse;

    // Update the state
    iState = EResolvingItem;

    // Start resolving the item
    iCurrentResolver->ResolveL( *this );
    }

// --------------------------------------------------------------------------
// CUpnpImageRenderingEngine::ResolveComplete
// Indicates that resolving of an item is complete.
// --------------------------------------------------------------------------
//
void CUpnpImageRenderingEngine::ResolveComplete(
    const MUPnPItemResolver& aResolver,
    TInt aError )
    {
    __LOG1( "[UpnpCommand]\t CUpnpImageRenderingEngine::ResolveComplete\
 in state %d", iState );

    // if engine is shutting down, no need to check these
    if ( iState == EResolvingItem )
        {
        __ASSERTD( &aResolver == iCurrentResolver, __FILE__, __LINE__ );
        if( iBufferingNewImage )
            {
            TRAP( aError, StartResolvingL() );
            }
        else if( aError == KErrNone )
            {
            iState = EResolveComplete;
            
            // Now that we have the full metadata of the item available, we
            // can start the rendering
            TRAP( aError, InitiateShowingL() );
            }
        // error handling
        if( aError != KErrNone && iState != EShuttingDown )
            {
            SendRenderAck( aError );
            }
        }
    else if( iState == EShuttingDown )
        {
        // do nothing.
        iState = EIdle;
        }
    else
        {
        __LOG( "[UpnpCommand]\t CUpnpImageRenderingEngine: state error." );
        __PANICD( __FILE__, __LINE__ );
        iState = EIdle;
        }

    }

// --------------------------------------------------------------------------
// CUpnpImageRenderingEngine::InitiateShowingL
// Handles the initiation of rendering (SetUri or video player launching).
// --------------------------------------------------------------------------
void CUpnpImageRenderingEngine::InitiateShowingL()
    {
    __LOG1( "[UpnpCommand]\t CUpnpImageRenderingEngine::InitiateShowingL\
 in state %d",
        iState );
    __ASSERTD( iCurrentResolver, __FILE__, __LINE__ );

    if ( UPnPItemUtility::BelongsToClass(
        iCurrentResolver->Item(), KClassImage ) )
        {
         // Send the setUri action
        iRenderingSession.SetURIL(
            iCurrentResolver->Resource().Value(),
            iCurrentResolver->Item() );
        // update the state
        iState = ESettingUri;
        }
    else
        {
        User::Leave( KErrNotSupported );
        }
    }


// --------------------------------------------------------------------------
// CUpnpImageRenderingEngine::SetURIResult
// UPnP AV Controller calls this method as a result for the 'set uri' request.
// --------------------------------------------------------------------------
//
void CUpnpImageRenderingEngine::SetURIResult( TInt aError )
    {
    __LOG1( "[UpnpCommand]\t CUpnpImageRenderingEngine::SetURIResult\
 in state %d",
        iState );

    if ( iState == ESettingUri )
        {
        //need check the aError in case of SetURIL cause a error.
        if( aError != KErrNone )
            {
            Cleanup();
            return;         
            }
        __ASSERTD( iCurrentResolver, __FILE__, __LINE__ );
        if( iBufferingNewImage )
            {
            TRAP( aError, StartResolvingL() );
            }
        else if( aError == KErrNone )
            {
            TRAP( aError, iRenderingSession.PlayL() );
            if( aError == KErrNone )
                {
                // Update the state
                iState = EStartingPlay;
                }
            }
        // error handling
        if( aError != KErrNone )
            {
            SendRenderAck( aError );
            }
        }
    else if ( iState == EShuttingDown )
        {
        // do nothing
        }
    else
        {
        __LOG( "[UpnpCommand]\t CUpnpImageRenderingEngine: state error." );
        __PANICD( __FILE__, __LINE__ );
        iState = EIdle;
        }

    }

// --------------------------------------------------------------------------
// CUpnpImageRenderingEngine::InteractOperationComplete
// Called by UpnpAvController to indicate that play is complete.
// --------------------------------------------------------------------------
//
void CUpnpImageRenderingEngine::InteractOperationComplete(
    TInt aError,
    TUPnPAVInteractOperation aOperation )
    {
    __LOG2( "[UpnpCommand]\t CUpnpImageRenderingEngine::\
InteractOperationComplete (%d) in state %d", aOperation, iState );

    if ( iState == EStartingPlay )
        {
        __ASSERTD( iCurrentResolver, __FILE__, __LINE__ );
        if( aOperation == EUPnPAVPlay && iBufferingNewImage )
            {
            // New image in buffer! call stop, then play new item.
            TRAP( aError, iRenderingSession.StopL() );
            if ( aError == KErrNone )
                {
                iState = EStopping;
                }
            }
        else if ( aOperation == EUPnPAVPlay && aError == KErrNone )
            {
            // update status
            iState = EPlaying;
            // response for play request
            SendRenderAck( KErrNone );
            }
        // error handling
        if ( aError != KErrNone )
            {
            SendRenderAck( aError );
            }
        }
    else if ( iState == EPlaying )
        {
        if( aOperation == EUPnPAVPlayUser )
            {
            // state change event notification
            // no need to do anything here
            }
        else if( aOperation == EUPnPAVStopUser )
            {
            // user stop notification
            // state to idle, so that no stop event will be sent
            // if starting to process new item
            iState = EIdle;
            }
        }
    else if ( iState == EStopping )
        {
        __ASSERTD( iCurrentResolver, __FILE__, __LINE__ );
        if( aOperation == EUPnPAVStop && iBufferingNewImage )
            {
            TRAP( aError, StartResolvingL() );
            }
        else if ( aOperation == EUPnPAVStop && aError == KErrNone )
            {
            // succesful stop - go IDLE
            iState = EIdle;
            }
        // error handling
        if ( aError != KErrNone )
            {
            SendRenderAck( aError );
            }
        }
    else if ( iState == EShuttingDown )
        {
        if ( aOperation == EUPnPAVStop || aOperation == EUPnPAVPlay )
            {
            iState = EIdle;
            }
        }

    __LOG( "[UpnpCommand]\t CUpnpImageRenderingEngine::InteractOperationComplete end " );
    }




// --------------------------------------------------------------------------
// CUpnpImageRenderingEngine::MediaRendererDisappeared
// Notifies that the Media Renderer we have a session with has disappeared.
// Session is now unusable and must be closed. 
// --------------------------------------------------------------------------
//
void CUpnpImageRenderingEngine::MediaRendererDisappeared(
    TUPnPDeviceDisconnectedReason aReason )
    {
    __LOG1( "[UpnpCommand]\t CUpnpImageRenderingEngine::\
MediaRendererDisappeared in state %d", iState );

    if( iState == EShuttingDown )
        {
        __LOG( "[UpnpCommand]\t CUpnpImageRenderingEngine::\
        MediaRendererDisappeared engine already shutting down, do nothing" );
        }
    else
        {
        if( aReason == MUPnPAVSessionObserverBase::EWLANLost )
            {
            iWlanActive = EFalse;
            }
        iState = EShuttingDown; // avoid all callbacks
        iObserver.EngineShutdown( KErrDisconnected );
        }
    }

// --------------------------------------------------------------------------
// CUpnpImageRenderingEngine::RunError
// Exception occurred in the timer body
// --------------------------------------------------------------------------
//
TInt CUpnpImageRenderingEngine::RunError( TInt aError )
    {
    __LOG2( "[UpnpCommand]\t CUpnpImageRenderingEngine::\
RunError in state %d aError %d", iState, aError );
    Cleanup();
    SendRenderAck( aError );
    return KErrNone;
    }



// --------------------------------------------------------------------------
// CUpnpImageRenderingEngine::SendRenderAck
// Exception occurred in the timer body
// --------------------------------------------------------------------------
//
void CUpnpImageRenderingEngine::SendRenderAck( TInt aError )
    {
    __LOG1( "[UpnpCommand]\t CUpnpImageRenderingEngine::\
SendRenderAck(%d)", aError );

    // take a stack copy of some members
    MUpnpImageRenderingEngineObserver& observer = iObserver;
    const CUpnpItem* item = NULL;
    if ( iCurrentResolver  && 
        !( iState == EIdle || iState == EResolvingItem ) )
        {
        item = &iCurrentResolver->Item();
        }

    // cleanup if this was an error
    if ( aError != KErrNone )
        {
        __LOG1( "[UpnpCommand]\t CUpnpImageRenderingEngine::\
SendRenderAck aError=%d -> Cleanup", aError );
        Cleanup();
        }

    // call the observer
    TInt resp = observer.RenderAck( aError, item );

    // in case of disconnected error, do engine shutdown
    if ( resp == KErrDisconnected )
        {
        __LOG1( "[UpnpCommand]\t CUpnpImageRenderingEngine::\
SendRenderAck resp=%d -> EngineShutdown", resp );
        iState = EShuttingDown;
        observer.EngineShutdown( resp );
        }

    }
    
    
// --------------------------------------------------------------------------
// CUpnpImageRenderingEngine::IsWlanActive
// If connection to renderer is lost, checks if wlan is still active
// --------------------------------------------------------------------------
//
TBool CUpnpImageRenderingEngine::IsWlanActive()
    {        
    return iWlanActive;
    }
    
// End of File