diff -r 000000000000 -r 1bce908db942 multimediacommscontroller/mmccqoscontroller/src/mccqoscontroller.cpp --- /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( + &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(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& 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 (tempSources[ i ]); + iResultEvent = TMccEvent(); + result = item->RateAdaptationRequest( iEvent, iResultEvent ); + CheckRateChangeResult( result ); + } + const RPointerArray& 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 (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 + 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 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 +