upnpframework/upnpcommand/src/upnpimagerenderingengine.cpp
changeset 0 7f85d04be362
child 38 5360b7ddc251
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/upnpframework/upnpcommand/src/upnpimagerenderingengine.cpp	Thu Dec 17 08:52:00 2009 +0200
@@ -0,0 +1,683 @@
+/*
+* 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