multimediacommscontroller/mmccvideosourcesink/src/mccvideosinkuser.cpp
changeset 0 1bce908db942
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/multimediacommscontroller/mmccvideosourcesink/src/mccvideosinkuser.cpp	Tue Feb 02 01:04:58 2010 +0200
@@ -0,0 +1,1065 @@
+/*
+* 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 "mccvideosinkuser.h"
+#include "mccvideosourcesinklogs.h"
+#include "mccdef.h"
+#include "mccinternaldef.h"
+#include <CXPSPacketSink.h>
+
+// CONSTANTS
+const TUint32 KMccModifyMonitoringShortPeriodMicrosecs = 2000000;
+const TUint32 KMccModifyMonitoringLongPeriodMicrosecs = 10000000;
+const TUint32 KMccMaxDenyPeriodMicrosecs = 1000000;
+const TReal KMccMonitoringShortPeriodTimestampIncreaseFactorDown = 0.6;
+const TReal KMccMonitoringShortPeriodTimestampIncreaseFactorUp = 1.4;
+const TReal KMccMonitoringLongPeriodTimestampIncreaseFactorDown = 0.8;
+const TReal KMccMonitoringLongPeriodTimestampIncreaseFactorUp = 1.2;
+
+const TInt KMccJitterBufferThresholdPacketTime = 20;
+
+// Minimum times for preroll
+const TInt KMccOneMediaMinPreroll = 1000;
+const TInt KMccSeveralMediaMinPreroll = 2000;
+
+// MACROS
+#define MCC_CONVERT_TO_90KHZ_CLOCK( valInMicroSecs ) ( valInMicroSecs / 100 * 9 )
+
+// ---------------------------------------------------------------------------
+// TMccTimeStampEntry::TMccTimeStampEntry
+// ---------------------------------------------------------------------------
+//
+TMccTimeStampEntry::TMccTimeStampEntry() : 
+    iTime( 0 ), iSeq( 0 )
+    {
+    }
+
+// ---------------------------------------------------------------------------
+// TMccTimeStampEntry::TMccTimeStampEntry
+// ---------------------------------------------------------------------------
+//    
+TMccTimeStampEntry::TMccTimeStampEntry( TInt64 aTime, TUint16 aSeq ) :
+    iTime( aTime ), iSeq( aSeq )
+    {
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::NewL
+// ---------------------------------------------------------------------------
+//
+CMccVideoSinkUser* CMccVideoSinkUser::NewL( 
+    MAsyncEventHandler* aAsyncEventHandler, 
+    TUid aMediaType,
+    TUint aStreamId,
+    TReal aFrameRate,
+    CXPSPacketSink& aPacketSink )
+    {
+    CMccVideoSinkUser* self = 
+        CMccVideoSinkUser::NewLC( aAsyncEventHandler,
+                                  aMediaType,
+                                  aStreamId,
+                                  aFrameRate,
+                                  aPacketSink );
+    CleanupStack::Pop( self );
+    return self;
+    }
+    
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::NewLC
+// ---------------------------------------------------------------------------
+//
+CMccVideoSinkUser* CMccVideoSinkUser::NewLC( 
+    MAsyncEventHandler* aAsyncEventHandler, 
+    TUid aMediaType,
+    TUint aStreamId,
+    TReal aFrameRate,
+    CXPSPacketSink& aPacketSink )
+    {
+    CMccVideoSinkUser* self = 
+        new ( ELeave ) CMccVideoSinkUser( 
+            aAsyncEventHandler, aMediaType, aStreamId, aFrameRate, aPacketSink );
+    CleanupStack::PushL( self );
+    return self;
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::~CMccVideoSinkUser
+// ---------------------------------------------------------------------------
+//        
+CMccVideoSinkUser::~CMccVideoSinkUser()
+    {
+    delete iJitterBuf;
+    }
+        
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::CMccVideoSinkUser
+// ---------------------------------------------------------------------------
+//
+CMccVideoSinkUser::CMccVideoSinkUser( 
+    MAsyncEventHandler* aAsyncEventHandler, 
+    TUid aMediaType,
+    TUint aStreamId,
+    TReal aFrameRate,
+    CXPSPacketSink& aPacketSink ) :
+    iAsyncEventHandler( aAsyncEventHandler ),
+    iMediaType( aMediaType ),
+    iStreamId( aStreamId ),
+    iStartedOnce( EFalse ),
+    iFrameRate( aFrameRate ),
+    iPacketSink( aPacketSink ),
+    iPacketOverflowState( ENormal ),
+    iNumTimeStamps( 0 ),
+    iAverageTimeStampDifference( 0 ),
+    iResetNeeded( EFalse ),
+    iModifyMode( EModifyInit ),
+    iFirstModifyCheck( 0 ),
+    iFirstModifyCheckTimestamp( 0 ),
+    iFirstTimestamp( 0 ),
+    iAllowFrame( ETrue ),
+    iDenyFramesStarted( 0 ),
+    iMultipleMediaTypes( EFalse ),
+    iPreviousPacketOriginalTimeStamp( KMaxTUint32 ),
+    iPreviousPacketModifiedTimeStamp( 0 ),
+    iPreviousPacketTime( 0 ),
+    iCurrentMonitoringPeriod( KMccModifyMonitoringShortPeriodMicrosecs ),
+    iTimestampIncreaseFactorDown( KMccMonitoringShortPeriodTimestampIncreaseFactorDown ),
+    iTimestampIncreaseFactorUp( KMccMonitoringShortPeriodTimestampIncreaseFactorUp )
+    {
+    } 
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::EnqueueL
+// ---------------------------------------------------------------------------
+//
+CMccVideoJitterBuffer::TMccPacketBufferingStatus CMccVideoSinkUser::EnqueueL( 
+    const TRtpRecvHeader& aHeaderInfo, const TDesC8& aPayloadData )
+    {
+    CMccVideoJitterBuffer::TMccPacketBufferingStatus 
+        bufferingStatus( CMccVideoJitterBuffer::EPlaying );
+
+    if ( aPayloadData.Length() > GetPayloadSize() )
+        {
+        // Too big buffer causes playback to stop at Helix, drop the packet
+        // silently
+        __V_SOURCESINK_CONTROLL( 
+            "CMccVideoSinkUser::EnqueueL, Too big packet dropped" ) 
+        }
+    else if ( iJitterBuf )
+        {
+        TBool importantPacket = CodecSpecificDataHandling( aPayloadData );
+        bufferingStatus = iJitterBuf->EnqueueL( iStreamId, 
+                                                aHeaderInfo, 
+                                                aPayloadData, 
+                                                importantPacket );
+        }
+    else
+        {
+        TInt err = iPacketSink.Enqueue( iStreamId, aHeaderInfo, aPayloadData );
+        if ( err )
+            {
+            CheckErrorL( err );
+            }
+        }
+    return bufferingStatus;
+    }
+    
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::Set
+// ---------------------------------------------------------------------------
+//
+void CMccVideoSinkUser::Set( 
+    MAsyncEventHandler* aAsyncEventHandler, 
+    TUid aMediaType,
+    TUint aStreamId )
+    {
+    iAsyncEventHandler = aAsyncEventHandler;
+    iMediaType = aMediaType;
+    iStreamId = aStreamId;
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::SetFrameRateL
+// ---------------------------------------------------------------------------
+//    
+void CMccVideoSinkUser::SetFrameRateL( TReal aFrameRate )
+    {
+    if ( aFrameRate != iFrameRate )
+        {
+        if ( iJitterBuf )
+            {
+            __V_SOURCESINK_CONTROLL( 
+            "CMccVideoSinkUser::SetFrameRateL, configure jitterbuffer" ) 
+            
+            iJitterBuf->ConfigureL( KMccJitterBufferDefaultLowLimit, 
+                                    KMccJitterBufferDefaultHighLimit, 
+                                    KMccJitterBufferDefaultPlayThreshold,
+                                    KMccJitterBufferDefaultMaxSize,
+                                    aFrameRate );
+            }
+        
+        iFrameRate = aFrameRate;
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::GetPreroll
+// Helix does not restrict min preroll (buffering time) but it is safer to
+// have some  restriction as otherwise continuous buffering may occur if
+// Helix side buffering is used. If we are on real time mode, buffering
+// is handled at MCC side (this is indicated with preroll value zero).
+// Especially when two or more media types are used, data may not be received
+// in sync and longer buffering guards against running out of data of certain
+// media type.
+// ---------------------------------------------------------------------------
+//    
+TInt CMccVideoSinkUser::GetPreroll()
+    {
+    TInt preroll = iCodecInfo.iJitterBufThreshold * KMccJitterBufferThresholdPacketTime;
+    
+    __V_SOURCESINK_CONTROLL_INT1( 
+                "CMccVideoSinkUser::GetPreroll, preroll before adjust:", preroll )
+                	
+    if ( iMultipleMediaTypes || preroll > KMccOneMediaMinPreroll )
+        {    
+        TInt minPreroll = iMultipleMediaTypes ? KMccSeveralMediaMinPreroll : 
+                                                KMccOneMediaMinPreroll;           
+        if ( preroll < minPreroll )
+            {
+            preroll = minPreroll;
+            }
+        }
+    else
+        {
+        // Real time mode enabled as client set jitter buffer threshold
+        // value to small enough and only one media type is used.
+        preroll = 0;
+        }
+        
+    __V_SOURCESINK_CONTROLL_INT1( 
+            "CMccVideoSinkUserImpl::GetPreroll, preroll:", preroll )   
+            
+    return preroll;
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::GetActualPreroll
+// In case of real-time mode, actual preroll value is defined by jitterbuffer
+// ---------------------------------------------------------------------------
+// 
+TInt CMccVideoSinkUser::GetActualPreroll()
+    {      
+    TInt preroll( 0 );
+    if ( iJitterBuf )
+        {
+        preroll = iJitterBuf->PlayThresholdInMs();
+        }
+    else
+        {
+        preroll = GetPreroll();
+        }
+        
+     __V_SOURCESINK_CONTROLL_INT1( "CMccVideoSinkUser::GetActualPreroll, preroll:", 
+                                   preroll )
+                
+    return preroll;
+    }
+    
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::Play
+// ---------------------------------------------------------------------------
+// 
+void CMccVideoSinkUser::Play()
+    {
+    if ( iJitterBuf )
+        {
+        iJitterBuf->Play();
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::Pause
+// ---------------------------------------------------------------------------
+//         
+void CMccVideoSinkUser::Pause()
+    {
+    if ( iJitterBuf )
+        {
+        iJitterBuf->Pause();
+        }
+    }
+    
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::IsQueueSizeDefined
+// ---------------------------------------------------------------------------
+//                           
+TBool CMccVideoSinkUser::IsQueueSizeDefined() const
+    {
+    return ( iCodecInfo.iJitterBufBufferLength > 0 );
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::QueueSize
+// ---------------------------------------------------------------------------
+//    
+TUint CMccVideoSinkUser::QueueSize() const
+    {
+    TInt queueSize = 
+        ( iCodecInfo.iJitterBufBufferLength < KMccMinXPSQueueSize ) ?
+            KMccMinXPSQueueSize : iCodecInfo.iJitterBufBufferLength;
+
+    return queueSize;
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::PacketOverflowState
+// ---------------------------------------------------------------------------
+//
+CMccVideoSinkUser::TMccPacketOverflowState 
+    CMccVideoSinkUser::PacketOverflowState() const
+    {
+    return iPacketOverflowState;
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::SetPacketOverflow
+// ---------------------------------------------------------------------------
+//
+void CMccVideoSinkUser::SetPacketOverflow( TMccPacketOverflowState aState )
+    {
+    iPacketOverflowState = aState;
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::AddTimeStamp
+// ---------------------------------------------------------------------------
+//   
+void CMccVideoSinkUser::AddTimeStamp( TInt64 aTimeStamp, TUint16 aSeq )
+    {
+    if ( iResetNeeded )
+        {
+        return;
+        }
+    
+    UpdateFirstTimestamp( aTimeStamp );
+                
+    TInt lastTimeStamp = iNumTimeStamps - 1;
+    if ( lastTimeStamp >= 0 && iTimeStamps[ lastTimeStamp ].iTime == aTimeStamp )
+        {
+        // Do not allow subsequent same timestamps, update sequence number
+        iTimeStamps[ lastTimeStamp ].iSeq = aSeq;
+        return;
+        }
+        
+    if ( iNumTimeStamps >= KMccTimeStampArraySize )
+        {
+        RemoveFirstTimeStamp();
+        }
+    
+    iNumTimeStamps++;
+    TInt lastIndex = iNumTimeStamps - 1;
+    iTimeStamps[ lastIndex ].iTime = aTimeStamp;
+    iTimeStamps[ lastIndex ].iSeq = aSeq;
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::RemoveFirstTimeStamp
+// ---------------------------------------------------------------------------
+//   
+void CMccVideoSinkUser::RemoveFirstTimeStamp()
+    {
+    for ( TInt i = 1; i < iNumTimeStamps; i++ )
+        {
+        iTimeStamps[ i - 1 ] = iTimeStamps[ i ];
+        }
+    if ( iNumTimeStamps > 0 )
+        {
+        iNumTimeStamps--;
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::RemoveAllTimeStamps
+// ---------------------------------------------------------------------------
+//    
+void CMccVideoSinkUser::RemoveAllTimeStamps()
+    {
+    iNumTimeStamps = 0;
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::NumTimeStamps
+// ---------------------------------------------------------------------------
+//   
+TInt CMccVideoSinkUser::NumTimeStamps() const
+    {
+    return iNumTimeStamps;
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::Reset
+// ---------------------------------------------------------------------------
+//     
+void CMccVideoSinkUser::Reset( TBool aFullReset )
+    {
+    __V_SOURCESINK_CONTROLL_INT1( 
+        "CMccVideoSinkUser::Reset, fullreset:", aFullReset )
+        
+    if ( aFullReset )
+        {
+        SetPacketOverflow( ENormal );
+        RemoveAllTimeStamps();
+        iAverageTimeStampDifference = 0;
+        iResetNeeded = EFalse;
+    
+        SetModifyingState( EModifyInit );
+        iFirstModifyCheck = 0;
+        iFirstModifyCheckTimestamp = 0;
+        iFirstTimestamp = 0;
+        
+        iAllowFrame = ETrue;
+        iDenyFramesStarted = TTime( 0 );
+        
+        iPreviousPacketOriginalTimeStamp = KMaxTUint32;
+        iPreviousPacketModifiedTimeStamp = 0;
+        iPreviousPacketTime = TTime( 0 );
+        
+        iCurrentMonitoringPeriod = KMccModifyMonitoringShortPeriodMicrosecs;
+        
+        iTimestampIncreaseFactorDown = 
+            KMccMonitoringShortPeriodTimestampIncreaseFactorDown;
+        iTimestampIncreaseFactorUp = 
+            KMccMonitoringShortPeriodTimestampIncreaseFactorUp;
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::CalculateAverageTimeStampDifference
+// ---------------------------------------------------------------------------
+//   
+void CMccVideoSinkUser::CalculateAverageTimeStampDifference()
+    {
+    CheckDenyFramesMaxPeriod();
+    
+    // Calculate difference only if there's enough timestamps and average
+    // difference has not been yet calculated
+    if ( !iResetNeeded &&
+         !iAverageTimeStampDifference &&
+         iNumTimeStamps == KMccTimeStampArraySize )
+        {
+        TInt lastTimeStamp( iNumTimeStamps - 1 );
+        
+        // Calculate first the average timestamp difference between last N packets
+        TInt seqNumDif( 0 );
+        for ( TInt i = lastTimeStamp; i > 0; i-- )
+            {
+            seqNumDif = iTimeStamps[ i ].iSeq - iTimeStamps[ i - 1 ].iSeq;
+            seqNumDif = Abs( seqNumDif );
+            seqNumDif = ( seqNumDif == 0 ) ? 1 : seqNumDif;
+                
+            iAverageTimeStampDifference += (TUint32)(
+                ( iTimeStamps[ i ].iTime - iTimeStamps[ i - 1 ].iTime ) / seqNumDif );
+            }
+            
+        iAverageTimeStampDifference = 
+            iAverageTimeStampDifference / ( KMccTimeStampArraySize - 1 ); 
+
+        __V_SOURCESINK_CONTROLL_INT1( 
+            "CMccVideoSinkUser::CalculateAverageTimeStampDifference, average dif:", 
+            iAverageTimeStampDifference )
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::IsResetNeeded
+// If there's big gap in timestamps, videoplayer needs to be resetted to
+// succesfully continue playback. If modifying is in use, gaps are ignored.
+// ---------------------------------------------------------------------------
+//    
+TBool CMccVideoSinkUser::IsResetNeeded( TRtpRecvHeader& aHeaderInfo )
+    {
+    if ( !iResetNeeded )
+        {
+        if ( RealTimeUser() )
+            {
+            iResetNeeded = IsResetNeededRealTimeMode( aHeaderInfo );
+            }
+        else
+            {
+            iResetNeeded = IsResetNeededNormalMode( aHeaderInfo );
+            }
+        }    
+    return iResetNeeded;
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::IsResetNeeded
+// ---------------------------------------------------------------------------
+// 
+TBool CMccVideoSinkUser::IsResetNeeded() const
+    {
+    return iResetNeeded;
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::IsModifyNeeded
+// Check if other end is using video timestamps incorrectly and modify
+// them to be as correct as possible as Helix cannot deal with incorrect
+// timestamps. Modify check is not used in audio+video scenario as packet
+// flow is not as stable in that case.
+// ---------------------------------------------------------------------------
+//
+TBool CMccVideoSinkUser::IsModifyNeeded( TRtpRecvHeader& aHeaderInfo )
+    {
+    if ( iMultipleMediaTypes )
+        {
+        return EFalse;
+        }
+    
+    if ( iModifyMode == EModifyInit )
+        {
+        DoModifyCheckInit( aHeaderInfo );
+        }
+    
+    if ( MonitoringModify() )
+        {
+        DoModifyCheckMonitoring( aHeaderInfo );
+        }
+        
+    return DoModify( aHeaderInfo );
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::SetAllowFrame
+// If some other user has resetted XPS because of timestamp skew, this
+// user cannot pass media until it has also detected timestamp skew. 
+// However, there's guard "timer" which stops denial if it takes too long
+// (i.e. timestamp skew didn't occur/was not detected for some reason).
+// This kind of error situtation can cause medias to go out of sync but
+// it is anyway better than not being able to continue playback at all.
+// ---------------------------------------------------------------------------
+//
+void CMccVideoSinkUser::SetAllowFrame( TBool aAllowFrame )
+    {
+    __V_SOURCESINK_CONTROLL_INT1( 
+        "CMccVideoSinkUser::SetAllowFrame, allow:", aAllowFrame )
+            
+    iAllowFrame = aAllowFrame;
+    
+    if ( !iAllowFrame )
+        {
+        iDenyFramesStarted.HomeTime();
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::AllowFrame
+// ---------------------------------------------------------------------------
+//            
+TBool CMccVideoSinkUser::AllowFrame() const
+    {
+    return iAllowFrame;
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::SetMultipleMediaTypesL
+// ---------------------------------------------------------------------------
+// 
+void CMccVideoSinkUser::SetMultipleMediaTypesL( TBool aMultipleMediaTypes )
+    {
+    iMultipleMediaTypes = aMultipleMediaTypes;
+    if ( RealTimeUser() && !iJitterBuf )
+        {
+        __V_SOURCESINK_CONTROLL( 
+        "CMccVideoSinkUser::SetMultipleMediaTypesL, creating jitterbuffer" )
+        
+        iJitterBuf = CMccVideoJitterBuffer::NewL( 
+            *this, iPacketSink, iCodecInfo.iJitterBufInactivityTimeOut );
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::SetCodecInfoL
+// ---------------------------------------------------------------------------
+// 
+void CMccVideoSinkUser::SetCodecInfoL( const TMccCodecInfo& aCodecInfo )
+    {
+    iCodecInfo = aCodecInfo;
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::CodecInfo
+// ---------------------------------------------------------------------------
+//         
+TMccCodecInfo& CMccVideoSinkUser::CodecInfo()
+    {
+    return iCodecInfo;
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::GetPayloadSize
+// ---------------------------------------------------------------------------
+//
+TInt CMccVideoSinkUser::GetPayloadSize()
+    {
+    if ( !iPayloadSize )
+        {
+        TInt payloadSize( iCodecInfo.iFrameSize );
+        __V_SOURCESINK_CONTROLL_INT1( 
+            "CMccVideoSinkUser::GetPayloadSize, size:", payloadSize )
+            
+        if ( payloadSize < KMccVideoSinkMinPayloadBufSize )
+            {
+            payloadSize = KMccVideoSinkMinPayloadBufSize;
+            __V_SOURCESINK_CONTROLL_INT1( 
+            "CMccVideoSinkUser::GetPayloadSize, size modified:", payloadSize )
+            }
+        iPayloadSize = payloadSize;
+        }
+    return iPayloadSize;
+    }
+    
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::ErrorOccured
+// ---------------------------------------------------------------------------
+//
+void CMccVideoSinkUser::ErrorOccured( TInt aError )
+    {
+    TRAP_IGNORE( CheckErrorL( aError ) )
+    }
+    
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::DoModify
+// ---------------------------------------------------------------------------
+// 
+TBool CMccVideoSinkUser::DoModify( TRtpRecvHeader& aHeaderInfo )
+    {
+    TBool modifyRequired( EFalse );
+    
+    if ( ModifyRequired() )
+        {
+        TUint32 timeStamp = aHeaderInfo.iTimestamp;
+        
+        TTime currentTime;
+        currentTime.HomeTime();
+            
+        if ( timeStamp == iPreviousPacketOriginalTimeStamp )
+            {
+            // Frame is divided into several packets, packet time of
+            // first packet in the frame is used until frame changes.
+            timeStamp = iPreviousPacketModifiedTimeStamp;
+            currentTime = iPreviousPacketTime;
+            }
+        else if ( iPreviousPacketTime.Int64() != 0 )
+            {
+            TTimeIntervalMicroSeconds interval = 
+                currentTime.MicroSecondsFrom( iPreviousPacketTime );
+            
+            __V_SOURCESINK_CONTROLL_INT1( 
+            "CMccVideoSinkUser diffr:", interval.Int64() )
+            
+            timeStamp = iPreviousPacketModifiedTimeStamp + 
+                MCC_CONVERT_TO_90KHZ_CLOCK( interval.Int64() );
+            }
+         else
+            {
+            // NOP
+            }
+        
+        __V_SOURCESINK_CONTROLL_INT2( 
+            "CMccVideoSinkUser orig:", aHeaderInfo.iTimestamp, 
+            " new:", timeStamp ) 
+            
+        iPreviousPacketOriginalTimeStamp = aHeaderInfo.iTimestamp;   
+        iPreviousPacketModifiedTimeStamp = timeStamp;
+        aHeaderInfo.iTimestamp = timeStamp; 
+        iPreviousPacketTime = currentTime;
+            
+        modifyRequired = ETrue;        
+        }
+        
+    return modifyRequired;
+    }
+    
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::SeqNumDifToLastSeqNum
+// ---------------------------------------------------------------------------
+// 
+TInt CMccVideoSinkUser::SeqNumDifToLastSeqNum( TUint16 aSeq )
+    {
+    TInt seqNumDif( 1 );
+    TInt lastTimeStampIndex( iNumTimeStamps - 1 );
+    if ( lastTimeStampIndex >= 0 )
+        {
+        TUint16 lastSeqNum = iTimeStamps[ lastTimeStampIndex ].iSeq;
+        seqNumDif = aSeq - lastSeqNum;
+        }
+    return seqNumDif;
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::SetModifyingState
+// ---------------------------------------------------------------------------
+//
+void CMccVideoSinkUser::SetModifyingState( TMccTimeStampModifyMode aModifyMode )
+    {
+    __V_SOURCESINK_CONTROLL_INT1( 
+        "CMccVideoSinkUser::SetModifyingState, state:", aModifyMode )
+    iModifyMode = aModifyMode;
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::MonitoringModify
+// ---------------------------------------------------------------------------
+//
+TBool CMccVideoSinkUser::MonitoringModify()
+    {
+    return ( iModifyMode == EModifyRequiredMonitoring ||
+             iModifyMode == EModifyNotRequiredMonitoring );
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::ModifyRequired
+// ---------------------------------------------------------------------------
+//
+TBool CMccVideoSinkUser::ModifyRequired()
+    {
+    return ( iModifyMode == EModifyRequired || 
+             iModifyMode == EModifyRequiredMonitoring );
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::DoModifyCheckInit
+// Check if other end does not use timestamps correctly.
+// ---------------------------------------------------------------------------
+//
+void CMccVideoSinkUser::DoModifyCheckInit( 
+    const TRtpRecvHeader& aHeaderInfo )
+    {
+    TUint32 timeStamp = aHeaderInfo.iTimestamp;
+
+    TUint32 timeStampDifference = GetTimestampDifferenceToPrevious( timeStamp );
+    
+    if ( timeStampDifference > 0 )
+        {
+        const TReal KMccPreferredTimeStampStep = 
+                TReal( KMccDefaultVideoFrequency / iFrameRate );
+        
+        // If timestamp increased less than half of preferred step,
+        // there is something wrong and timestamps need to be modified       
+        const TInt KMccTimeStampIncreaseMin = KMccPreferredTimeStampStep / 2;
+        
+        if ( timeStampDifference < KMccTimeStampIncreaseMin )
+            {
+            __V_SOURCESINK_CONTROLL_INT1( 
+                "CMccVideoSinkUser::DoModifyCheckInit, inc, difference:",
+                timeStampDifference )
+            
+            SetModifyingState( EModifyRequiredMonitoring );
+            }
+        else
+            {
+            SetModifyingState( EModifyNotRequiredMonitoring );
+            }
+        }        
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::DoModifyCheckMonitoring
+// Monitor certain period of time at the beginning of streaming whether
+// other end is using timestamp incorrectly. Or stop modifying if it looks 
+// that initial decision to modify timestamps was incorrect (other end is 
+// after all using them correctly).
+// ---------------------------------------------------------------------------
+//
+void CMccVideoSinkUser::DoModifyCheckMonitoring( 
+    const TRtpRecvHeader& aHeaderInfo )
+    {
+    TUint32 timeStamp = aHeaderInfo.iTimestamp;
+    
+    TTime currentTime;
+    currentTime.HomeTime(); 
+        
+    if ( iFirstModifyCheck == 0 )
+        {
+        iFirstModifyCheck.HomeTime();
+        iFirstModifyCheckTimestamp = timeStamp;
+        return;
+        }
+        
+    TTimeIntervalMicroSeconds timeMonitored = 
+        currentTime.MicroSecondsFrom( iFirstModifyCheck );
+    
+    if ( timeMonitored > iCurrentMonitoringPeriod )
+        {
+        const TInt KMicrosecsToSecs = 1000000;
+
+        TInt timestampIncreaseWithinMonitoringPeriod = 
+                timeStamp - iFirstModifyCheckTimestamp;
+                
+        TReal idealTimestampIncrease = 
+            KMccDefaultVideoFrequency * timeMonitored.Int64() / KMicrosecsToSecs;
+        
+         __V_SOURCESINK_CONTROLL_INT2( 
+                "CMccVideoSinkUser::DoModifyCheckMonitoring, period:",
+                timeMonitored.Int64(),
+                "inc:", timestampIncreaseWithinMonitoringPeriod )
+            
+        __V_SOURCESINK_CONTROLL_REAL( 
+                "CMccVideoSinkUser::DoModifyCheckMonitoring, ideal inc:", 
+                idealTimestampIncrease )
+                    
+        if ( timestampIncreaseWithinMonitoringPeriod < 
+                ( idealTimestampIncrease * iTimestampIncreaseFactorDown ) ||
+             timestampIncreaseWithinMonitoringPeriod >
+                ( idealTimestampIncrease * iTimestampIncreaseFactorUp ) )
+            {        
+            // Need to calculate average difference again
+            iAverageTimeStampDifference = 0;
+            iNumTimeStamps = 0;
+            
+            DecideMonitoringContinuation( EModifyRequired );
+            }
+        else
+            {
+            DecideMonitoringContinuation( EModifyNotRequired );
+            }
+        }      
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::GetTimestampDifferenceToPrevious
+// ---------------------------------------------------------------------------
+//
+TUint32 CMccVideoSinkUser::GetTimestampDifferenceToPrevious( 
+    TUint32 aCurrentTimestamp )
+    {
+    TUint32 timeStampDifference( 0 );
+    TInt lastTimeStampIndex( iNumTimeStamps - 1 ); 
+    if ( lastTimeStampIndex >= 0 && 
+         aCurrentTimestamp != iTimeStamps[ lastTimeStampIndex ].iTime )
+        {
+        TUint32 lastTimeStamp = iTimeStamps[ lastTimeStampIndex ].iTime;
+
+        timeStampDifference = 
+            aCurrentTimestamp > lastTimeStamp ? 
+                ( aCurrentTimestamp - lastTimeStamp ) : 
+                ( lastTimeStamp - aCurrentTimestamp );
+        }
+    return timeStampDifference;
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::UpdateFirstTimestamp
+// ---------------------------------------------------------------------------
+//
+void CMccVideoSinkUser::UpdateFirstTimestamp( TUint32 aTimestamp )
+    {      
+    if ( iFirstTimestamp == 0 )
+        {
+        iFirstTimestamp = aTimestamp;
+        
+        __V_SOURCESINK_CONTROLL_INT1( 
+        "CMccVideoSinkUser::UpdateFirstTimestamp, first timestamp:", 
+        iFirstTimestamp )
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::DifferenceThreshold
+// If there's some gap in sequence numbers which is not anyhow too big, it
+// might be that some frames are just dropped by network or sender. 
+// In such case take seq num dif in count for timestamp difference threshold.
+// With negative seq num, threshold is always more strict.
+// ---------------------------------------------------------------------------
+//    
+TUint32 CMccVideoSinkUser::DifferenceThreshold( TInt aSeqNumDif ) const
+    {
+    if ( aSeqNumDif > KMccSeqNumDifferenceThresholdMin &&
+         aSeqNumDif < KMccSeqNumDifferenceThresholdMax )
+        {
+        return iAverageTimeStampDifference * 
+            ( aSeqNumDif + KMccTimeStampDifferenceTreshold );
+        }
+    return iAverageTimeStampDifference * KMccTimeStampDifferenceTreshold;
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::CheckDenyFramesMaxPeriod
+// ---------------------------------------------------------------------------
+//    
+void CMccVideoSinkUser::CheckDenyFramesMaxPeriod()
+    {
+    if ( iDenyFramesStarted.Int64() > 0 )
+        {
+        TTime currentTime;
+        currentTime.HomeTime();
+        
+        TTimeIntervalMicroSeconds denyPeriod = 
+            currentTime.MicroSecondsFrom( iDenyFramesStarted );
+            
+        if ( denyPeriod > KMccMaxDenyPeriodMicrosecs )
+            {
+            __V_SOURCESINK_CONTROLL( 
+                "CMccVideoSinkUser::CheckDenyFramesMaxPeriod, end denial" )
+        
+            Reset( ETrue );
+            }
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::DecideMonitoringContinuation
+// ---------------------------------------------------------------------------
+// 
+void CMccVideoSinkUser::DecideMonitoringContinuation( 
+    TMccTimeStampModifyMode aNextState )
+    {
+    TMccTimeStampModifyMode decidedNextState( aNextState );
+    if ( iCurrentMonitoringPeriod == KMccModifyMonitoringShortPeriodMicrosecs )
+        {
+        // Monitor still for longer period, after longer period of sampling
+        // more strict rules can be used to detect incorrect behavior
+        iCurrentMonitoringPeriod = KMccModifyMonitoringLongPeriodMicrosecs;
+        iTimestampIncreaseFactorDown = 
+            KMccMonitoringLongPeriodTimestampIncreaseFactorDown;
+        iTimestampIncreaseFactorUp = 
+            KMccMonitoringLongPeriodTimestampIncreaseFactorUp;
+        if ( aNextState == EModifyRequired )
+            {
+            decidedNextState = EModifyRequiredMonitoring;
+            }
+        else if ( aNextState == EModifyNotRequired )
+            {
+            decidedNextState = EModifyNotRequiredMonitoring;
+            }
+        else
+            {
+            }
+        }
+    SetModifyingState( decidedNextState );
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::IsResetNeededNormalMode
+// ---------------------------------------------------------------------------
+// 
+TBool CMccVideoSinkUser::IsResetNeededNormalMode( 
+    TRtpRecvHeader& aHeaderInfo )
+    {
+    TBool isResetNeeded( EFalse );
+    TUint32 oldTimeStamp = aHeaderInfo.iTimestamp;
+        
+    if ( !ModifyRequired() && iAverageTimeStampDifference )
+        {
+        TUint32 timeStamp = aHeaderInfo.iTimestamp;
+        
+        // Check if new timestamp is totally off the charts.
+        
+        TInt lastTimeStamp( iNumTimeStamps - 1 );
+        
+        TInt seqNumDif = SeqNumDifToLastSeqNum( aHeaderInfo.iSeqNum );
+
+        if ( timeStamp > iTimeStamps[ lastTimeStamp ].iTime )
+            {
+            isResetNeeded = ( timeStamp - iTimeStamps[ lastTimeStamp ].iTime ) > 
+                DifferenceThreshold( seqNumDif );
+            }
+        else
+            {
+            isResetNeeded = ( timeStamp + DifferenceThreshold( seqNumDif ) ) < 
+                    iTimeStamps[ lastTimeStamp ].iTime;
+            }
+        }
+        
+    if ( isResetNeeded )
+        {
+        // Rollback changes if resetted
+        aHeaderInfo.iTimestamp = oldTimeStamp; 
+        }
+    else
+        {
+        // Return value is not interesting
+        IsModifyNeeded( aHeaderInfo );
+        }
+        
+    return isResetNeeded;
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::IsResetNeededRealTimeMode
+// ---------------------------------------------------------------------------
+// 
+TBool CMccVideoSinkUser::IsResetNeededRealTimeMode( 
+    TRtpRecvHeader& /*aHeaderInfo*/ )
+    {
+    // Reset never needed    
+    return EFalse;
+    }
+    
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::RealTimeUser
+// ---------------------------------------------------------------------------
+// 
+TBool CMccVideoSinkUser::RealTimeUser()
+    {
+    return ( GetPreroll() == 0 );
+    }
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::CheckErrorL
+// ---------------------------------------------------------------------------
+//
+void CMccVideoSinkUser::CheckErrorL( TInt aError )
+    {
+    __V_SOURCESINK_CONTROLL_INT1( "CMccVideoSinkUser::CheckErrorL, error", aError )
+    if ( aError == KErrOverflow )
+		{		
+	    SetPacketOverflow( CMccVideoSinkUser::EOccured );	
+	    
+	    // Overflow is not cosidered as a fatal error as it can be recovered
+	    aError = KErrNone;	
+		}
+    User::LeaveIfError( aError );
+    }
+
+
+// ---------------------------------------------------------------------------
+// CMccVideoSinkUser::CodecSpecificDataHandling
+// ---------------------------------------------------------------------------
+//  
+TBool CMccVideoSinkUser::CodecSpecificDataHandling( const TDesC8& aPayloadData )
+    {
+    TBool importantData( EFalse );
+    if ( MCC_IS_AVC_USER_ENTRY( this ) )
+        {
+        // Avc sprop-parameter-set values are dropped due helix seems to have
+        // currently some issue with those.
+        if ( TMccCodecInfo::IsAvcPpsOrSpsData( aPayloadData, ETrue ) )
+            {
+            __V_SOURCESINK_CONTROLL_STR8( 
+            "CMccVideoSinkUser::CodecSpecificDataHandling, data:", aPayloadData )
+            importantData = ETrue;
+            }
+        }
+    return importantData;
+    }
+        
+// End of file
+