multimediacommscontroller/mmccqoscontroller/src/mccqoscontroller.cpp
changeset 0 1bce908db942
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/multimediacommscontroller/mmccqoscontroller/src/mccqoscontroller.cpp	Tue Feb 02 01:04:58 2010 +0200
@@ -0,0 +1,574 @@
+/*
+* Copyright (c) 2006 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:    
+*
+*/
+
+
+
+// INCLUDE FILES
+#include "mccqoscontroller.h"
+#include "mccrateadaptationobserver.h"
+#include "mmccevents.h"
+#include "mccresources.h"
+#include "mccdatasource.h"
+#include "mccdatasink.h"
+#include "QosControllerLog.h"
+#include "mccinternalevents.h"
+
+// EXTERNAL DATA STRUCTURES
+
+// EXTERNAL FUNCTION PROTOTYPES  
+
+// CONSTANTS
+const TInt KPluginMicroSecInSec = 1;
+// MACROS
+
+// LOCAL CONSTANTS AND MACROS
+
+// MODULE DATA STRUCTURES
+
+// LOCAL FUNCTION PROTOTYPES
+
+// FORWARD DECLARATIONS
+
+// ============================= LOCAL FUNCTIONS ===============================
+
+// ============================ MEMBER FUNCTIONS ===============================
+
+// -----------------------------------------------------------------------------
+// CMccQosController::NewL
+// -----------------------------------------------------------------------------
+//
+EXPORT_C CMccQosController* CMccQosController::NewL( 
+    MMccRateAdaptationObserver& aObserver,
+    MMccResources& aResources )
+    {
+    __TRACE_MCC_QOS_CONTROLLER1("CMccQosController::NewL");
+    CMccQosController* self = 
+        new ( ELeave ) CMccQosController( aObserver, aResources );
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    CleanupStack::Pop( self );
+    return self;
+    }
+
+// -----------------------------------------------------------------------------
+// CMccQosController::~CMccQosController
+// -----------------------------------------------------------------------------
+//        
+EXPORT_C CMccQosController::~CMccQosController()
+    {
+    __TRACE_MCC_QOS_CONTROLLER1("CMccQosController::~CMccQosController");
+    if ( iDeltaTimer )
+        {
+        iDeltaTimer->Remove( iDeltaTimerEntry );
+        }
+    delete iDeltaTimer;
+    
+    iIcmpErrors.Reset();
+    iIcmpErrors.Close();
+    
+    __TRACE_MCC_QOS_CONTROLLER1("CMccQosController::~CMccQosController end");
+    }
+
+// -----------------------------------------------------------------------------
+// CMccQosController::EventReceived
+// -----------------------------------------------------------------------------
+//
+EXPORT_C TBool CMccQosController::EventReceived( const TMccEvent& aEvent )
+    {
+    __TRACE_MCC_QOS_CONTROLLER1("CMccQosController::EventReceived");
+    
+    // Check if interesting event and then do calculations.
+    // If adaptation is needed, make async brake and continue with it once
+    // dummy async completes. QosController has also responsibility of
+    // icmp error handling (random errors are filtered etc.).
+    
+    TBool ignoreEvent( EFalse );
+    
+    if ( aEvent.iEventCategory == KMccEventCategoryRtcp )
+    	{
+		__TRACE_MCC_QOS_CONTROLLER3(
+		    "CMccQosController::EventReceived TMccEvent:iSessionId=", 
+		    aEvent.iSessionId);
+		__TRACE_MCC_QOS_CONTROLLER3(
+		    "CMccQosController::EventReceived TMccEvent:iLinkId=", 
+		    aEvent.iLinkId);
+		__TRACE_MCC_QOS_CONTROLLER3(
+		    "CMccQosController::EventReceived TMccEvent:iStreamId=", 
+		    aEvent.iStreamId);
+		__TRACE_MCC_QOS_CONTROLLER3(
+		    "CMccQosController::EventReceived TMccEvent:iEndpointId=", 
+		    aEvent.iEndpointId);
+		__TRACE_MCC_QOS_CONTROLLER3(
+		    "CMccQosController::EventReceived TMccEvent:iErrorCode=", 
+		    aEvent.iErrorCode);
+
+		const TMccRtcpEventData& eventdata = 
+    		(*reinterpret_cast<const TMccRtcpEventDataPackage*>( 
+                &aEvent.iEventData ))(); 
+                      
+    	if ( eventdata.iRtcpPacketType == KRtcpRrPacket )
+            {
+            // RR received
+    
+            iStreamStat = eventdata.iStats;
+			__TRACE_MCC_QOS_CONTROLLER3(
+			    "CMccQosController::EventReceived TRtpPeerStat:iNumPacketsSent=", 
+			    iStreamStat.iNumPacketsSent);
+			__TRACE_MCC_QOS_CONTROLLER3(
+			    "CMccQosController::EventReceived TRtpPeerStat:iCumNumOctetsSent=", 
+			    iStreamStat.iCumNumOctetsSent);
+			__TRACE_MCC_QOS_CONTROLLER3(
+			    "CMccQosController::EventReceived TRtpPeerStat:RoundTripDelay=", 
+			    iStreamStat.iRoundTripDelay);
+  			__TRACE_MCC_QOS_CONTROLLER3(
+  			    "CMccQosController::EventReceived TRtpPeerStat:iTxBandwidth=", 
+  			    iStreamStat.iTxBandwidth);
+ 			__TRACE_MCC_QOS_CONTROLLER3(
+ 			    "CMccQosController::EventReceived TRtpPeerStat:iCumNumPacketsLost=", 
+ 			    iStreamStat.iCumNumPacketsLost);
+ 			__TRACE_MCC_QOS_CONTROLLER3(
+ 			    "CMccQosController::EventReceived TRtpPeerStat:iFractionLost=", 
+ 			    iStreamStat.iFractionLost);
+ 			__TRACE_MCC_QOS_CONTROLLER3(
+ 			    "CMccQosController::EventReceived TRtpPeerStat:iArrivalJitter=", 
+ 			    iStreamStat.iArrivalJitter);
+  			__TRACE_MCC_QOS_CONTROLLER3(
+  			    "CMccQosController::EventReceived TRtpPeerStat:iRxBandwidth=", 
+  			    iStreamStat.iRxBandwidth);
+   			__TRACE_MCC_QOS_CONTROLLER3(
+   			    "CMccQosController::EventReceived TRtpPeerStat:iChannelBufferSize=", 
+   			    iStreamStat.iChannelBufferSize);
+  			__TRACE_MCC_QOS_CONTROLLER3(
+  			    "CMccQosController::EventReceived TRtpPeerStat:iNTPTimeStampSec=", 
+  			    iStreamStat.iNTPTimeStampSec);   			
+  			__TRACE_MCC_QOS_CONTROLLER3(
+  			    "CMccQosController::EventReceived TRtpPeerStat:iNTPTimeStampFrac=", 
+  			    iStreamStat.iNTPTimeStampFrac);
+  			
+            DoAdaptationCalculations();
+            }
+        }
+    else if ( aEvent.iEventCategory == KMccEventCategoryRtp && 
+              aEvent.iEventType == KMccStreamError &&
+              aEvent.iErrorCode == KErrHostUnreach )
+        {
+        ignoreEvent = HandleIcmpError( aEvent );
+        }
+    else
+        {
+        // NOP
+        }
+    
+    __TRACE_MCC_QOS_CONTROLLER1("CMccQosController::EventReceived end");   
+     
+    return ignoreEvent;
+    }
+    
+//-----------------------------------------------------------------------------
+// CMccQosController::DoAdaptationCalculations  
+// -----------------------------------------------------------------------------
+//  
+void CMccQosController::DoAdaptationCalculations()
+	{
+	__TRACE_MCC_QOS_CONTROLLER1("CMccQosController::DoAdaptationCalculations");
+
+	#ifdef RATECONTROL
+	
+	ClearRateEventData();
+	
+	__TRACE_MCC_QOS_CONTROLLER3("CMccQosController::RoundTripTime", 
+	                            iRoundTripTime );
+	__TRACE_MCC_QOS_CONTROLLER3("CMccQosController::RoundTripDelay", 
+	                            iStreamStat.iRoundTripDelay );
+		
+	if ( iRoundTripTime )
+		{
+		// Temporary change will not affect immediately so much (kind of mean value)
+		const TReal q = 0.9;
+		iRoundTripTime = 
+		    TInt ( q * iRoundTripTime + (1.0 - q) * iStreamStat.iRoundTripDelay );
+		__TRACE_MCC_QOS_CONTROLLER3("CMccQosController::New RoundTripTime=", 
+		                            iRoundTripTime );
+		}
+	else
+		{
+		iRoundTripTime = iStreamStat.iRoundTripDelay; 
+		}
+	
+	// Store some amount of previous round trip delay values in order to
+	// calculate average value of certain period	
+    iRoundTripDelays[ iEventCounter % KMccShortTermCalculation ] = iStreamStat.iRoundTripDelay;
+    
+    iEventCounter++;
+    
+    TBool adaptedAtThisRound( EFalse );
+	
+	if ( iStreamStat.iFractionLost > iThresholdValues.iFractionLostHigh )
+	    {
+	    // Immediately drop bitrate if significant number of packets got lost
+	    __TRACE_MCC_QOS_CONTROLLER1(
+	        "CMccQosController::DoAdaptationCalculations, fractions lost, decrease");
+	    iRateEventData.iRateAdaptationAdvice = ( 1.0 - KAdaptation );
+	    DoAdaptation();
+	    adaptedAtThisRound = ETrue;
+	    }	 
+    
+    if ( !( iEventCounter % KMccShortTermCalculation ) )
+        {
+        // If long term calculation resulted in change, we are interested 
+        // how it affected to average round trip delay
+        __TRACE_MCC_QOS_CONTROLLER1(
+            "CMccQosController::DoAdaptationCalculations, short term");
+            
+        TInt roundTripDelayOfThisPeriod = CalculateDelayOfShortTermPeriod();
+        
+        __TRACE_MCC_QOS_CONTROLLER3(
+            "CMccQosController::DoAdaptationCalculations, avg delay of this period", 
+            roundTripDelayOfThisPeriod );
+        
+        if ( !adaptedAtThisRound )
+            {
+            TInt comparisonDelay = GetShortTermComparisonDelay();
+            
+            __TRACE_MCC_QOS_CONTROLLER3(
+                "CMccQosController::DoAdaptationCalculations, comparison delay", 
+                comparisonDelay );
+            
+            if ( roundTripDelayOfThisPeriod - comparisonDelay > KRoundTripTimeLimit )
+                {
+                __TRACE_MCC_QOS_CONTROLLER1(
+                    "CMccQosController::DoAdaptationCalculations, decrease");
+                iRateEventData.iRateAdaptationAdvice = 1.0 - KAdaptation;
+                DoAdaptation();
+                adaptedAtThisRound = ETrue;
+                }
+            else if ( comparisonDelay - roundTripDelayOfThisPeriod > KRoundTripTimeLimit )
+                {
+                __TRACE_MCC_QOS_CONTROLLER1(
+                    "CMccQosController::DoAdaptationCalculations, increase");
+                iRateEventData.iRateAdaptationAdvice = 1.0 + KAdaptation;
+                DoAdaptation();
+                adaptedAtThisRound = ETrue;
+                }
+            else
+                {
+                }
+            }
+            
+        iRoundTripDelayOfPreviousShortPeriod = roundTripDelayOfThisPeriod;
+        }
+	
+	if ( !( iEventCounter % KMccLongTermCalculation ) )
+	    {
+	    __TRACE_MCC_QOS_CONTROLLER1(
+	        "CMccQosController::DoAdaptationCalculations, long term");
+	    
+	    if ( !adaptedAtThisRound && !iAdapted )
+	        {
+	        // If adaptation wasn't needed within previous long term period, try to
+	        // increase bitrate. Remember round trip time of this period so that
+	        // we can see what kind of impact the increasing of bitrate has. 
+	        __TRACE_MCC_QOS_CONTROLLER1(
+	            "CMccQosController::DoAdaptationCalculations, try to increase");
+	        iRateEventData.iRateAdaptationAdvice = ( 1.0 + KAdaptationMore );
+	        DoAdaptation();
+	        adaptedAtThisRound = ETrue;
+	        }
+	        
+	    iAdapted = EFalse;
+	    }
+			
+	#endif //RATECONTROL	
+	__TRACE_MCC_QOS_CONTROLLER1("CMccQosController::DoAdaptationCalculations end");
+    }
+    
+// -----------------------------------------------------------------------------
+// CMccQosController::FillRateAdaptationEventL
+// -----------------------------------------------------------------------------
+//      
+void CMccQosController::FillRateAdaptationEvent()
+	{
+	__TRACE_MCC_QOS_CONTROLLER1("CMccQosController::FillRateAdaptationEvent");
+	iEvent = TMccEvent();
+	TMccRateAdaptationEventDataPackage temp(iRateEventData);
+	iEvent.iEventData.Copy( temp );	 
+	__TRACE_MCC_QOS_CONTROLLER1("CMccQosController::FillRateAdaptationEvent end");
+	}
+	
+// -----------------------------------------------------------------------------
+// CMccQosController::ClearRateEventData
+// -----------------------------------------------------------------------------
+//      
+void CMccQosController::ClearRateEventData()
+	{
+	__TRACE_MCC_QOS_CONTROLLER1("CMccQosController::ClearRateEventData");	
+	iRateEventData = TMccRateAdaptationEventData();
+	__TRACE_MCC_QOS_CONTROLLER1("CMccQosController::ClearRateEventData end");	
+	}
+	
+// -----------------------------------------------------------------------------
+// CMccQosController::StartTimerForAsync
+// -----------------------------------------------------------------------------
+//
+void CMccQosController::StartTimerForAsync ()
+	{
+	__TRACE_MCC_QOS_CONTROLLER1("CMccQosController::StartTimerForAsync");
+	iDeltaTimer->Remove(iDeltaTimerEntry);
+	TTimeIntervalMicroSeconds32 interval(KPluginMicroSecInSec);
+	iDeltaTimer->Queue(interval, iDeltaTimerEntry);
+	__TRACE_MCC_QOS_CONTROLLER1("CMccQosController::StartTimerForAsync end");	
+	}	
+ 
+// -----------------------------------------------------------------------------
+// CMccQosController::AsyncTimerExpired
+// -----------------------------------------------------------------------------
+//
+TInt CMccQosController::AsyncTimerExpired(TAny* aPtr)
+	{
+	__TRACE_MCC_QOS_CONTROLLER1("CMccQosController::AsyncTimerExpired");
+
+	CMccQosController* self = reinterpret_cast<CMccQosController*>(aPtr);
+	self ->GetSinkSources();
+	
+	__TRACE_MCC_QOS_CONTROLLER1("CMccQosController::AsyncTimerExpired end");
+  	return ETrue;
+	} 
+    
+// -----------------------------------------------------------------------------
+// CMccQosController::GetSinkSources
+// -----------------------------------------------------------------------------
+//
+void CMccQosController::GetSinkSources()
+	{
+	__TRACE_MCC_QOS_CONTROLLER1("CMccQosController::GetSinkSources");
+    iBitRateChanged = EFalse;
+    const RPointerArray<MDataSource>& tempSources = iResources.Sources( ETrue );
+  	FillRateAdaptationEvent();
+  	TInt result = 0;
+  	for ( TInt i = 0; i < tempSources.Count(); i++ )
+        {
+        iResultEvent = TMccEvent();
+		__TRACE_MCC_QOS_CONTROLLER3("CMccQosController::GetSinkSources Sources", i);
+
+        CMccDataSource* item = static_cast<CMccDataSource*> (tempSources[ i ]); 
+        iResultEvent = TMccEvent(); 	
+        result = item->RateAdaptationRequest( iEvent, iResultEvent );
+        CheckRateChangeResult( result );
+        }
+  	const RPointerArray<MDataSink>& tempSinks = iResources.Sinks( ETrue );
+  	for ( TInt j = 0; j < tempSinks.Count(); j++ )
+        {
+        iResultEvent = TMccEvent();
+		__TRACE_MCC_QOS_CONTROLLER3("CMccQosController::GetSinkSources Sinks", j);        
+        CMccDataSink* item = static_cast<CMccDataSink*> (tempSinks[ j ]);
+        result = item->RateAdaptationRequest( iEvent, iResultEvent );
+        CheckRateChangeResult( result );
+        }
+    if ( !iBitRateChanged )
+    	{
+    	iMainObserver.RateAdaptationAlert( iEvent,
+    				 MMccRateAdaptationObserver::ERateAdaptationNotPossible );
+    	}
+	__TRACE_MCC_QOS_CONTROLLER1("CMccQosController::GetSinkSources END"); 
+	}    
+    
+// -----------------------------------------------------------------------------
+// CMccQosController::CheckRateChangeResult
+// -----------------------------------------------------------------------------
+// 
+void CMccQosController::CheckRateChangeResult( TInt aValue )
+    {
+	__TRACE_MCC_QOS_CONTROLLER1("CMccQosController::CheckRateChangeResult");    
+    if ( aValue == KErrNone )
+    	{
+		TMccRateAdaptationEventDataPackage resultdatapkg;
+		resultdatapkg.Copy(iResultEvent.iEventData);
+		TMccRateAdaptationEventData resultdata = resultdatapkg();
+        if ( resultdata.iBitrateOriginal != resultdata.iBitrateModified )
+        	{
+        	__TRACE_MCC_QOS_CONTROLLER1("CMccQosController::BitRateChanged ETRUE");
+        	iBitRateChanged = ETrue;
+        	}
+    	}
+	__TRACE_MCC_QOS_CONTROLLER1("CMccQosController::CheckRateChangeResult end");
+    }
+
+// -----------------------------------------------------------------------------
+// CMccQosController::HandleIcmpError
+// -----------------------------------------------------------------------------
+// 
+TBool CMccQosController::HandleIcmpError( const TMccEvent& aEvent )
+    {
+    __TRACE_MCC_QOS_CONTROLLER1("CMccQosController::HandleIcmpError");   
+    
+    // By default error is ignored. Only if there has been certain amount
+    // of errors within certain time window, error is reported.
+    //
+    TBool ignoreError( ETrue );
+    
+    // Do first cleanup of all entries which have exceeded their monitoring
+    // timewindow.
+    TTime currentTime;
+    currentTime.HomeTime();
+    TMccQosControllerIcmpError cleanupIcmpError;
+    cleanupIcmpError.iErrorTimeWindowBeginning = currentTime;
+    TIdentityRelation<TMccQosControllerIcmpError> 
+        cleanupComparison( IcmpErrorCleanup );
+    TInt index = iIcmpErrors.Find( cleanupIcmpError, cleanupComparison );
+    while ( index != KErrNotFound )
+        {
+        iIcmpErrors.Remove( index );
+        index = iIcmpErrors.Find( cleanupIcmpError, cleanupComparison );
+        }
+    
+    __TRACE_MCC_QOS_CONTROLLER1("CMccQosController::HandleIcmpError, cleanup done");  
+    
+    TIdentityRelation<TMccQosControllerIcmpError> comparison( IcmpErrorMatch );
+    TMccQosControllerIcmpError icmpError;
+    icmpError.iLinkId = aEvent.iLinkId;
+    index = iIcmpErrors.Find( icmpError, comparison );
+    if ( index != KErrNotFound )
+        {
+        TMccQosControllerIcmpError& existingIcmpError = iIcmpErrors[ index ];
+        existingIcmpError.iErrorCount++;
+        if ( existingIcmpError.iErrorCount >= KMccIcmpErrorThreshold )
+            {
+            // Report the error
+            ignoreError = EFalse;
+            iIcmpErrors.Remove( index );
+            }
+        }
+    else
+        {
+        icmpError.iErrorCount = KMccFirstIcmpError;
+        icmpError.iErrorTimeWindowBeginning = currentTime;
+        
+        // It does not matter if entry cannot be added because of low memory
+        iIcmpErrors.Append( icmpError );
+        }
+    
+    __TRACE_MCC_QOS_CONTROLLER3("CMccQosController::HandleIcmpError ignore:", 
+                                ignoreError );  
+       
+    return ignoreError;
+    }
+
+// -----------------------------------------------------------------------------
+// CMccQosController::IcmpErrorMatch
+// -----------------------------------------------------------------------------
+//     
+TBool CMccQosController::IcmpErrorMatch( 
+    const TMccQosControllerIcmpError& aError1, 
+    const TMccQosControllerIcmpError& aError2 )
+    {
+    // First argument is the search term
+    return ( aError1.iLinkId == aError2.iLinkId );
+    }
+
+// -----------------------------------------------------------------------------
+// CMccQosController::IcmpErrorCleanup
+// -----------------------------------------------------------------------------
+//     
+TBool CMccQosController::IcmpErrorCleanup( 
+    const TMccQosControllerIcmpError& aError1, 
+    const TMccQosControllerIcmpError& aError2 )
+    {
+    // First argument is the search term
+    TInt64 timeWindow = 
+        aError1.iErrorTimeWindowBeginning.MicroSecondsFrom( 
+            aError2.iErrorTimeWindowBeginning ).Int64();
+    return ( timeWindow > KMccIcmpErrorTimeWindow );
+    }
+       
+// -----------------------------------------------------------------------------
+// CMccQosController::CMccQosController
+// -----------------------------------------------------------------------------
+//      
+CMccQosController::CMccQosController(
+    MMccRateAdaptationObserver& aObserver,
+    MMccResources& aResources ) :
+    iMainObserver( aObserver ),
+    iResources( aResources ),
+    iDeltaTimerCallBack(AsyncTimerExpired, this),
+    iBitRateChanged(EFalse)
+    {
+    __TRACE_MCC_QOS_CONTROLLER1("CMccQosController::CMccQosController");
+    iDeltaTimerEntry.Set(iDeltaTimerCallBack);
+    }
+
+// -----------------------------------------------------------------------------
+// CMccQosController::ConstructL
+// -----------------------------------------------------------------------------
+// 
+void CMccQosController::ConstructL()
+    {
+	__TRACE_MCC_QOS_CONTROLLER1("CMccQosController::ConstructL");    
+    iDeltaTimer = CDeltaTimer::NewL(CActive::EPriorityStandard);
+    iEventCounter = 0;
+    #ifdef RATECONTROL
+    //rate control threshold values
+
+    iThresholdValues.iFractionLostHigh = 30;
+    iThresholdValues.iFractionLostLow = 0;
+
+	iRoundTripTime = 0;
+	
+	iAdapted = EFalse;
+
+	#endif //RATECONTROL
+    }
+
+// -----------------------------------------------------------------------------
+// CMccQosController::DoAdaptation
+// -----------------------------------------------------------------------------
+// 
+void CMccQosController::DoAdaptation()
+    {
+    StartTimerForAsync();
+    iAdapted = ETrue;
+    }
+
+// -----------------------------------------------------------------------------
+// CMccQosController::GetShortTermComparisonDelay
+// -----------------------------------------------------------------------------
+// 
+TInt CMccQosController::GetShortTermComparisonDelay()
+    {    
+    if ( iRoundTripDelayOfPreviousShortPeriod )
+        {
+        return iRoundTripDelayOfPreviousShortPeriod;
+        }
+        
+    return iRoundTripTime;
+    }
+
+// -----------------------------------------------------------------------------
+// CMccQosController::CalculateDelayOfShortTermPeriod
+// -----------------------------------------------------------------------------
+//
+TInt CMccQosController::CalculateDelayOfShortTermPeriod()
+    {
+    TInt roundTripDelayOfThisPeriod( 0 );
+    for ( TInt i = 0; i < KMccShortTermCalculation; i++ )
+        {
+        roundTripDelayOfThisPeriod += iRoundTripDelays[ i ];
+        }
+    return ( roundTripDelayOfThisPeriod / KMccShortTermCalculation );
+    }
+    
+// End of file
+