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