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 volume state machine
*
*/
// INCLUDES
// avcontroller api
#include <upnpavrenderingsession.h>
#include <upnpavdevice.h> // for device.VolumeCapability()
// volume state machine
#include "upnprenderingstatemachineconstants.h" // option flags
#include "upnpvolumestatemachineobserver.h"
#include "upnpvolumestatemachine.h"
_LIT( KComponentLogfile, "upnprenderingengine.txt");
#include "upnplog.h"
// CONSTANTS
const TInt KCancelTimeMaximum = 3000000;
const TInt KCancelTimeResolution = 500000;
// ======== MEMBER FUNCTIONS ========
TUpnpVSMQueueItem::TUpnpVSMQueueItem(
const TUpnpVSMQueueItem::TPropertyType aProperty, const TInt aValue )
: iProperty( aProperty )
, iValue( aValue )
{
}
TUpnpVSMQueueItem::TPropertyType TUpnpVSMQueueItem::Property() const
{
return iProperty;
}
TInt TUpnpVSMQueueItem::Value() const
{
return iValue;
}
// --------------------------------------------------------------------------
// CUpnpVolumeStateMachine::NewL
// Static constructor.
// --------------------------------------------------------------------------
//
EXPORT_C CUpnpVolumeStateMachine* CUpnpVolumeStateMachine::NewL(
MUPnPAVRenderingSession& aSession )
{
CUpnpVolumeStateMachine* self =
new (ELeave) CUpnpVolumeStateMachine( aSession );
// no 2nd phase construction needed
return self;
}
// --------------------------------------------------------------------------
// CUpnpVolumeStateMachine::CUpnpVolumeStateMachine
// Default constructor.
// --------------------------------------------------------------------------
//
CUpnpVolumeStateMachine::CUpnpVolumeStateMachine(
MUPnPAVRenderingSession& aSession )
: iSession( aSession )
{
__LOG( "VolumeStateMachine: constructor" );
iVolumeCapability = iSession.Device().VolumeCapability();
iMuteCapability = iSession.Device().MuteCapability();
iState = EOffSync;
iCurrentVolume = KErrNotFound;
iVolumeToSetAfterMute = KErrNotFound;
iCurrentMute = KErrNotFound;
iMuteRequestedByClient = EFalse;
iCachedVolume = KErrNotFound;
iCachedMute = KErrNotFound;
}
// --------------------------------------------------------------------------
// CUpnpVolumeStateMachine::~CUpnpVolumeStateMachine
// Destructor.
// --------------------------------------------------------------------------
//
EXPORT_C CUpnpVolumeStateMachine::~CUpnpVolumeStateMachine()
{
__LOG( "VolumeStateMachine: destructor" );
}
// --------------------------------------------------------------------------
// CUpnpVolumeStateMachine::SetOptions
// sets the option flags for this state machine
// --------------------------------------------------------------------------
//
EXPORT_C void CUpnpVolumeStateMachine::SetOptions( TInt aOptions )
{
iOptions = aOptions;
}
// --------------------------------------------------------------------------
// CUpnpVolumeStateMachine::Options
// --------------------------------------------------------------------------
//
EXPORT_C TInt CUpnpVolumeStateMachine::Options() const
{
return iOptions;
}
// --------------------------------------------------------------------------
// CUpnpVolumeStateMachine::SetObserver
// --------------------------------------------------------------------------
//
EXPORT_C void CUpnpVolumeStateMachine::SetObserver(
MUpnpVolumeStateMachineObserver& aObserver )
{
iObserver = &aObserver;
}
// --------------------------------------------------------------------------
// CUpnpVolumeStateMachine::RemoveObserver
// --------------------------------------------------------------------------
//
EXPORT_C void CUpnpVolumeStateMachine::RemoveObserver()
{
iObserver = NULL;
}
// --------------------------------------------------------------------------
// CUpnpVolumeStateMachine::SyncL
// Synchronises this state machine with the renderer
// --------------------------------------------------------------------------
//
EXPORT_C void CUpnpVolumeStateMachine::SyncL()
{
__LOG( "VolumeStateMachine: SyncL" );
if ( iState != EOffSync && iState != EIdle )
{
__LOG( "VolumeStateMachine: not synchronizing" );
return;
}
if ( !iVolumeCapability )
{
User::Leave( KErrNotSupported );
}
iSession.GetVolumeL();
iState = ESyncing;
}
// --------------------------------------------------------------------------
// CUpnpVolumeStateMachine::SetOffSync
// Forces the state machine off sync
// --------------------------------------------------------------------------
//
EXPORT_C void CUpnpVolumeStateMachine::SetOffSync()
{
__ASSERTD( !IsBusy(), __FILE__, __LINE__ );
iState = EOffSync;
iCurrentVolume = KErrNotFound;
iVolumeToSetAfterMute = KErrNotFound;
iCurrentMute = KErrNotFound;
iMuteRequestedByClient = EFalse;
}
// --------------------------------------------------------------------------
// CUpnpVolumeStateMachine::IsInSync
// --------------------------------------------------------------------------
//
EXPORT_C TBool CUpnpVolumeStateMachine::IsInSync() const
{
return iState != EOffSync;
}
// --------------------------------------------------------------------------
// CUpnpVolumeStateMachine::SetVolumeL
// --------------------------------------------------------------------------
//
EXPORT_C void CUpnpVolumeStateMachine::SetVolumeL( TInt aVolume )
{
__LOG1( "VolumeStateMachine: SetVolumeL %d", aVolume );
__ASSERTD( IsInSync(), __FILE__, __LINE__ );
if ( !iVolumeCapability )
{
User::Leave( KErrNotSupported );
}
if ( IsBusy() )
{
PushIntoQueueL( TUpnpVSMQueueItem::EVolume, aVolume );
return;
}
// check volume is between given limits
aVolume = Max( aVolume, KVolumeMin );
aVolume = Min( aVolume, KVolumeMax );
if ( aVolume == KVolumeMin && iCurrentVolume > 0 &&
iMuteCapability && iCurrentMute == KMuteOff &&
iOptions & Upnp::EConvertVolumeZeroToMute )
{
// adjust volume to zero (using mute)
iSession.SetMuteL( ETrue );
iState = EAdjustingVolumeToZero;
}
else if ( aVolume == KVolumeMin && iCurrentMute == KMuteOn )
{
// no need to change volume. Call back directly.
if ( iObserver )
{
iObserver->VolumeChanged(
KErrNone, iCurrentVolume, ETrue );
}
}
else if ( aVolume > KVolumeMin && iCurrentMute == KMuteOn &&
!iMuteRequestedByClient )
{
// volume is at zero, and renderer is muted.
// unmute and after that change volume if needed
iVolumeToSetAfterMute = aVolume;
iSession.SetMuteL( EFalse );
iState = EAdjustingVolumeFromZero;
}
else if ( aVolume != iCurrentVolume )
{
// adjust volume normally
iSession.SetVolumeL( aVolume );
iState = EAdjustingVolume;
}
else
{
// no need to change volume. Call back directly.
if ( iObserver )
{
iObserver->VolumeChanged(
KErrNone, iCurrentVolume, ETrue );
}
}
}
// --------------------------------------------------------------------------
// CUpnpVolumeStateMachine::Volume
// --------------------------------------------------------------------------
//
EXPORT_C TInt CUpnpVolumeStateMachine::Volume() const
{
__ASSERTD( IsInSync(), __FILE__, __LINE__ );
TInt vol = iCurrentVolume;
if ( iCurrentMute && (iOptions & Upnp::EConvertVolumeZeroToMute) )
{
vol = KVolumeMin;
}
return vol;
}
// --------------------------------------------------------------------------
// CUpnpVolumeStateMachine::SetMuteL
// --------------------------------------------------------------------------
//
EXPORT_C void CUpnpVolumeStateMachine::SetMuteL( TBool aMuteState )
{
__LOG1( "VolumeStateMachine: SetMuteL", aMuteState );
__ASSERTD( IsInSync(), __FILE__, __LINE__ );
if ( !iMuteCapability )
{
// Could implement here FAKE MUTE SUPPORT by converting mute
// to Volume(0). However there is no need to that since no clients
// use the mute feature.
User::Leave( KErrNotSupported );
}
if ( IsBusy() )
{
PushIntoQueueL( TUpnpVSMQueueItem::EMute, aMuteState );
return;
}
// check mute state is between given limits
aMuteState = Max( aMuteState, KMuteOff );
aMuteState = Min( aMuteState, KMuteOn );
if ( aMuteState && !iCurrentMute )
{
// mute
iSession.SetMuteL( ETrue );
iState = EAdjustingMute;
}
else if ( !aMuteState && iCurrentMute )
{
// unmute
iSession.SetMuteL( EFalse );
iState = EAdjustingMute;
}
else
{
// no change in mute state. Call back directly.
if ( iObserver )
{
iObserver->MuteChanged(
KErrNone, iCurrentMute, ETrue );
}
}
}
// --------------------------------------------------------------------------
// CUpnpVolumeStateMachine::Mute
// --------------------------------------------------------------------------
//
EXPORT_C TBool CUpnpVolumeStateMachine::Mute() const
{
__ASSERTD( IsInSync(), __FILE__, __LINE__ );
return ( (iCurrentMute == KMuteOn) ? KMuteOn : KMuteOff );
}
// --------------------------------------------------------------------------
// CUpnpVolumeStateMachine::IsBusy
// --------------------------------------------------------------------------
//
EXPORT_C TBool CUpnpVolumeStateMachine::IsBusy() const
{
return iState != EIdle;
}
// --------------------------------------------------------------------------
// CUpnpVolumeStateMachine::HasVolumeCapability
// tests if renderer has volume capability
// --------------------------------------------------------------------------
//
EXPORT_C TBool CUpnpVolumeStateMachine::HasVolumeCapability() const
{
return iVolumeCapability;
}
// --------------------------------------------------------------------------
// CUpnpVolumeStateMachine::Cancel
// --------------------------------------------------------------------------
//
EXPORT_C void CUpnpVolumeStateMachine::Cancel()
{
// actually we can not cancel anything, but providing the interface for
// future purposes
if ( iState != EIdle )
{
__LOG1( "VolumeStateMachine: Canceling in state %d", iState );
iState = ECancelled;
for ( TInt t = KCancelTimeMaximum;
t > 0 && iState != EIdle;
t -= KCancelTimeResolution )
{
User::After( TTimeIntervalMicroSeconds32(
KCancelTimeResolution ) );
}
__LOG1( "VolumeStateMachine: state after cancel: %d", iState );
}
}
void CUpnpVolumeStateMachine::HandleVolumeResultInSyncState(
TInt aError, TInt aVolumeLevel, TBool /*aActionResponse*/ )
{
if ( aError == KErrNone )
{
iCurrentVolume = aVolumeLevel;
if ( iMuteCapability )
{
// continue to sync mute
TRAPD( muteError, iSession.GetMuteL() );
if ( muteError != KErrNone )
{
// error syncing mute - callback
iState = EIdle;
if ( iObserver )
{
iObserver->VolumeSyncReady( muteError );
}
}
}
else
{
// mute not needed - callback
iCurrentMute = KMuteOff; // assume always off
iState = EIdle;
// notify sync initially and volume if changed
if ( iObserver && iCachedVolume == KErrNotFound )
{
iObserver->VolumeSyncReady( KErrNone );
}
else if ( iObserver && iCachedVolume != iCurrentVolume )
{
iObserver->VolumeChanged(
aError, iCurrentVolume, EFalse );
}
}
}
else
{
// error callback
iState = EIdle;
if ( iObserver )
{
iObserver->VolumeSyncReady( aError );
}
}
}
// --------------------------------------------------------------------------
// CUpnpVolumeStateMachine::VolumeResult
// --------------------------------------------------------------------------
//
EXPORT_C void CUpnpVolumeStateMachine::VolumeResult(
TInt aError, TInt aVolumeLevel, TBool aActionResponse )
{
__LOG3( "VolumeStateMachine: VolumeResult %d, %d state=%d",
aError, aVolumeLevel, iState );
//
// RESPONSE TO A REQUEST
//
if ( aActionResponse )
{
//
// SYNC
//
if( iState == ESyncing )
{
HandleVolumeResultInSyncState(aError, aVolumeLevel, aActionResponse);
}
//
// VOLUME ADJUST
//
else if ( iState == EAdjustingVolume ||
iState == EAdjustingVolumeFromZero )
{
if ( aError == KErrNone )
{
iCurrentVolume = aVolumeLevel;
}
iState = EIdle;
// volume adjust done, call back
if ( iObserver )
{
iObserver->VolumeChanged(
aError, iCurrentVolume, ETrue );
}
}
//
// CANCEL
//
else if( iState == ECancelled )
{
iState = EIdle;
}
}
//
// UNSOLICTED VOLUME EVENT
//
else
{
if ( iState == EIdle )
{
if ( aVolumeLevel != iCurrentVolume )
{
if ( iCurrentMute == KMuteOn )
{
__LOG("unsolicted volume event during muted");
}
iCurrentVolume = aVolumeLevel;
if ( iObserver )
{
iObserver->VolumeChanged(
KErrNone, iCurrentVolume, EFalse );
}
}
}
else
{
__LOG("ignoring unsolicted volume event in this state");
}
}
iCachedVolume = iCurrentVolume;
ProcessNextQueuedProperty();
}
// --------------------------------------------------------------------------
// CUpnpVolumeStateMachine::MuteResult
// --------------------------------------------------------------------------
//
EXPORT_C void CUpnpVolumeStateMachine::MuteResult(
TInt aError, TBool aMute, TBool aActionResponse )
{
__LOG3( "VolumeStateMachine: MuteResult %d, %d, %d",
aError, aMute, aActionResponse );
__LOG1( "VolumeStateMachine: MuteResult in state=%d", iState );
//
// RESPONSE TO A REQUEST
//
if( aActionResponse )
{
//
// SYNC
//
if ( iState == ESyncing )
{
if ( aError == KErrNone )
{
iCurrentMute = aMute;
iMuteRequestedByClient = EFalse;
}
iState = EIdle;
// notify sync initially and volume and mute if changed
if ( iObserver && iCachedMute == KErrNotFound )
{
iObserver->VolumeSyncReady( aError );
}
else if ( iObserver )
{
if( iCachedVolume != iCurrentVolume )
{
iObserver->VolumeChanged(
aError, iCurrentVolume, EFalse );
}
if( iCachedMute != iCurrentMute )
{
iObserver->MuteChanged(
aError, iCurrentMute, EFalse );
}
}
}
//
// MUTE ADJUST
//
if ( iState == EAdjustingMute )
{
if ( aError == KErrNone )
{
iCurrentMute = aMute;
iMuteRequestedByClient = aMute;
}
iState = EIdle;
if ( iObserver )
{
iObserver->MuteChanged(
aError, iCurrentMute, ETrue );
}
}
//
// VOLUME ADJUST -> 0
//
if ( iState == EAdjustingVolumeToZero )
{
if ( aError == KErrNone )
{
iCurrentMute = aMute;
iMuteRequestedByClient = EFalse;
}
iState = EIdle;
if ( iObserver )
{
iObserver->VolumeChanged(
aError, KVolumeMin, ETrue );
}
}
//
// VOLUME ADJUST 0 -> up
//
if ( iState == EAdjustingVolumeFromZero )
{
if ( aError == KErrNone )
{
iCurrentMute = aMute;
iMuteRequestedByClient = EFalse;
TRAP( aError, iSession.SetVolumeL(
iVolumeToSetAfterMute ) );
}
if ( aError != KErrNone )
{
// error callback
iVolumeToSetAfterMute = KErrNotFound;
iState = EIdle;
if ( iObserver )
{
iObserver->VolumeChanged(
aError, iCurrentVolume, ETrue );
}
}
}
//
// CANCEL
//
else if( iState == ECancelled )
{
iState = EIdle;
}
}
//
// UNSOLICITED MUTE EVENT
//
else
{
HandleUnsolicitedMuteEvent(aError, aMute, aActionResponse );
}
iCachedMute = iCurrentMute;
ProcessNextQueuedProperty();
}
void CUpnpVolumeStateMachine::HandleUnsolicitedMuteEvent(
TInt aError, TBool aMute, TBool aActionResponse )
{
if ( iState == EIdle )
{
HandleUnsolicitedMuteEventInIdle(aError, aMute, aActionResponse);
}
else
{
__LOG("ignoring unsolicted mute event in this state");
}
}
void CUpnpVolumeStateMachine::HandleUnsolicitedMuteEventInIdle(
TInt /*aError*/, TBool aMute, TBool /*aActionResponse*/ )
{
__LOG3( "VolumeStateMachine: HandleUnsolicitedMuteEventInIdle %d, %d, %d",
aMute, iCurrentMute, iMuteRequestedByClient );
if ( iCurrentMute && !aMute && iMuteRequestedByClient )
{
// client has requested mute, renderer unmuted
iCurrentMute = aMute;
// check if client has changed volume during mute
if ( iVolumeToSetAfterMute > 0 &&
iVolumeToSetAfterMute != iCurrentVolume )
{
TRAP_IGNORE(
iSession.SetVolumeL( iVolumeToSetAfterMute );
iVolumeToSetAfterMute = KErrNotFound;
iState = EAdjustingVolume;
);
}
// notify observer about mute state change
if ( iObserver )
{
iObserver->MuteChanged(
KErrNone, KMuteOff, EFalse );
}
}
else if ( iCurrentMute && !aMute ||
!iCurrentMute && aMute )
{
// renderer unmuted - notify volume state back
iCurrentMute = aMute;
// check if client has changed volume during mute
if ( iVolumeToSetAfterMute > 0 &&
iVolumeToSetAfterMute != iCurrentVolume &&
!aMute &&
( ( (iOptions & Upnp::EConvertVolumeZeroToMute) == 0 ) ||
iMuteRequestedByClient ) )
{
__LOG2( "After unmute adjusting volume %d->%d",
iCurrentVolume, iVolumeToSetAfterMute );
TRAP_IGNORE(
iSession.SetVolumeL( iVolumeToSetAfterMute );
iVolumeToSetAfterMute = KErrNotFound;
iState = EAdjustingVolume;
);
}
// notify observer
if ( iObserver )
{
NotifyObserver(aMute);
}
}
else
{
// no need to report status change
}
}
void CUpnpVolumeStateMachine::NotifyObserver(TBool aMute)
{
__LOG2( "VolumeStateMachine: NotifyObserver %d, %d",
aMute, iOptions );
if ( ( (iOptions & Upnp::EConvertVolumeZeroToMute) == 0 ) ||
iMuteRequestedByClient )
{
// client prefers to use mute, or client has
// explicitely requested this mute session.
iObserver->MuteChanged(
KErrNone, aMute, EFalse );
}
else
{
// convert mute signal to volume callback
TInt givenvol =
( aMute ? KVolumeMin : iCurrentVolume );
iObserver->VolumeChanged(
KErrNone, givenvol, EFalse );
}
}
// --------------------------------------------------------------------------
// CUpnpVolumeStateMachine::CopyValues
// --------------------------------------------------------------------------
//
EXPORT_C void CUpnpVolumeStateMachine::CopyValues(
const CUpnpVolumeStateMachine& aOther )
{
iCurrentVolume = aOther.Volume();
iCurrentMute = aOther.Mute();
}
// --------------------------------------------------------------------------
// CUpnpVolumeStateMachine::PushIntoQueueL
// --------------------------------------------------------------------------
//
void CUpnpVolumeStateMachine::PushIntoQueueL(
const TUpnpVSMQueueItem::TPropertyType aPropery, const TInt aValue )
{
__LOG( "VolumeStateMachine: PushIntoQueueL" );
TUpnpVSMQueueItem queued( aPropery, aValue );
iQueue.AppendL( queued );
CompressQueue();
}
// --------------------------------------------------------------------------
// CUpnpVolumeStateMachine::CompressQueueL
// --------------------------------------------------------------------------
//
void CUpnpVolumeStateMachine::CompressQueue()
{
TInt queueCount( iQueue.Count() );
if( queueCount > 1 )
{
__LOG1( "VolumeStateMachine: CompressQueue (queue count: %d)", queueCount );
TInt lastIndex( queueCount - 1 );
if( iQueue[lastIndex].Property() == TUpnpVSMQueueItem::EVolume )
{
// remove all other except the last 'set volume' request
for( TInt i( queueCount - 2 ); i >= 0; --i )
{
__LOG2( "VolumeStateMachine Queue: Removing %d from queue index %d",
iQueue[i].Property(), i );
iQueue.Remove( i );
}
}
else
{
// remove all other except (the last 'set volume' request and)
// the last 'set mute' request
TBool setVolumeFound( EFalse );
TBool setMuteFound( EFalse );
for( TInt i( queueCount - 1 ); i >= 0; --i )
{
if( iQueue[i].Property() == TUpnpVSMQueueItem::EVolume &&
!setVolumeFound )
{
setVolumeFound = ETrue;
}
else if( iQueue[i].Property() == TUpnpVSMQueueItem::EMute &&
!setMuteFound )
{
setMuteFound = ETrue;
}
else
{
__LOG2( "VolumeStateMachine Queue: Removing %d from queue index %d",
iQueue[i].Property(), i );
iQueue.Remove( i );
}
}
}
}
}
// --------------------------------------------------------------------------
// CUpnpVolumeStateMachine::ProcessNextQueuedProperty
// --------------------------------------------------------------------------
//
void CUpnpVolumeStateMachine::ProcessNextQueuedProperty()
{
if( iQueue.Count() > 0 )
{
__LOG( "VolumeStateMachine: ProcessNextQueuedPropertyL" );
TUpnpVSMQueueItem queuedItem( iQueue[0] );
iQueue.Remove( 0 );
if( queuedItem.Property() == TUpnpVSMQueueItem::EVolume )
{
TRAPD( err, SetVolumeL( queuedItem.Value() ) );
if( err )
{
iObserver->VolumeChanged( err, iCurrentVolume, ETrue );
}
}
else
{
TRAPD( err, SetMuteL( queuedItem.Value() ) );
if( err )
{
iObserver->MuteChanged( err, iCurrentMute, ETrue );
}
}
}
}
// end of file