omxilvideocomps/omxilclock/src/clocksupervisor.cpp
changeset 0 5d29cba61097
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/omxilvideocomps/omxilclock/src/clocksupervisor.cpp	Fri Oct 08 22:09:17 2010 +0100
@@ -0,0 +1,1668 @@
+/*
+* Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+
+// NOTE: Assumes OMX_SKIP64BIT is not set when building OMX CORE
+
+#include <hal.h>
+#include <openmax/il/khronos/v1_x/OMX_Other.h>	// OMX port
+#include <openmax/il/common/omxilspecversion.h>	// OMX version number
+#include "clocksupervisor.h"
+#include "comxilclockprocessingfunction.h"
+#include "clockpanics.h"
+#include "omxilclock.hrh"
+
+#include "log.h"
+
+//////////////
+// File scoped constants
+//
+
+/** The maximum number of simultaneous outstanding requests (across all ports) */
+static const TUint KMaxRequests = 20;
+static const TUint KThreadStackSize = 1024;
+
+/**
+ * Assumption that timer will always take at least this long to complete.
+ * How soon an outstanding delay must be before we complete immediately instead of setting a timer.
+ */
+static const TInt KMinTimerOverhead = 1000;	// 1000usecs = 1ms
+
+// assumption that timers go off at some time rounded to this many microseconds
+static const TInt KTimerQuantization = 
+#ifdef __WINSCW__
+	15000;
+#else
+	1;
+#endif
+
+// aim to select a counter accurate to ~1ms
+#ifdef __WINSCW__
+#define USE_FASTCOUNTER // typically faster than 1ms on emulator, 1ms on hardware
+#else
+#define USE_NTICKCOUNT	// typically 5ms on emulator, 1ms on hardware
+#endif
+
+#if !defined(USE_FASTCOUNTER) && !defined(USE_NTICKCOUNT)
+#error Either USE_FASTCOUNTER or USE_NTICKCOUNT must be defined
+#endif
+
+/**
+ * Structure to map OMX_INDEXTYPE to behaviour required by the Clock component.
+ */
+
+// Indexes for time configs start after OMX_IndexTimeStartUnused = 0x09000000
+// Simply deduct this to index this table
+
+static const OMX_INDEXTYPE KMinJumpTableIndex = OMX_IndexConfigTimeScale;
+static const OMX_INDEXTYPE KMaxJumpTableIndex = OMX_IndexConfigTimeClientStartTime;
+
+const CClockSupervisor::FunctionPtr CClockSupervisor::iJumpTable[] = 
+	{
+	/*OMX_IndexConfigTimeScale*/					&CClockSupervisor::HandleGetSetTimeScale	     ,// requires buffers
+	/*OMX_IndexConfigTimeClockState*/				&CClockSupervisor::HandleGetSetClockState	     ,// requires buffers
+	/*OMX_IndexConfigTimeActiveRefClock*/			&CClockSupervisor::HandleGetSetActiveRefClock  ,
+	/*OMX_IndexConfigTimeCurrentMediaTime*/			&CClockSupervisor::HandleQueryCurrentMediaTime ,
+	/*OMX_IndexConfigTimeCurrentWallTime*/			&CClockSupervisor::HandleQueryCurrentWallTime	 ,
+	/*OMX_IndexConfigTimeCurrentAudioReference*/	&CClockSupervisor::HandleUpdateAudioReference	 ,
+	/*OMX_IndexConfigTimeCurrentVideoReference*/	&CClockSupervisor::HandleUpdateVideoReference	 ,
+	/*OMX_IndexConfigTimeMediaTimeRequest*/			&CClockSupervisor::HandleSubmitMediaTimeRequest,// requires buffers
+	/*OMX_IndexConfigTimeClientStartTime*/			&CClockSupervisor::HandleSetPortClientStartTime,
+	};
+
+#ifdef _DEBUG
+#define CHECK_DEBUG()	DbgCheck()
+#else
+#define CHECK_DEBUG()
+#endif
+
+
+//////////////
+
+
+/**
+ * Euclid's algorithm.
+ * Returns the largest common factor of aX and aY.
+ */
+static TInt LargestCommonFactor(TInt aX, TInt aY)
+	{
+	// based on knowledge that lcf(x,0)=x, lcf(x,y)=lcf(y,x) and lcf(x,y)=lcf(y,x%y)
+	while(aX != 0)
+		{
+		aY %= aX;
+		if(aY == 0)
+			{
+			return aX;
+			}
+		aX %= aY;
+		}
+	return aY;
+	}
+
+
+/**
+ *
+ *
+ */
+CClockSupervisor* CClockSupervisor::NewL(COmxILClockProcessingFunction& aProcessingFunction)
+	{
+	CClockSupervisor* self = new (ELeave) CClockSupervisor(aProcessingFunction);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+
+/**
+ *
+ *
+ */
+void CClockSupervisor::ConstructL()
+	{
+	// calculate tick frequency
+	//
+#ifdef USE_FASTCOUNTER
+	TInt frequency;	// tick frequency in Hz
+	User::LeaveIfError(HAL::Get(HAL::EFastCounterFrequency, frequency));
+	// conversion factor from ticks into microseconds
+	// using a fraction in integer arithmetic
+	iMicroConvNum = 1000000;
+	iMicroConvDen = frequency;
+	TInt countsUp;
+	User::LeaveIfError(HAL::Get(HAL::EFastCounterCountsUp, countsUp));
+	iSystemClockReversed = !countsUp;
+#elif defined(USE_NTICKCOUNT)
+	User::LeaveIfError(HAL::Get(HAL::ENanoTickPeriod, iMicroConvNum)); // tick period in microseconds
+	iMicroConvDen = 1;
+#else	
+#error
+#endif
+	
+	// divide out any common factor to reduce chance of overflow
+	TInt factor = LargestCommonFactor(iMicroConvNum, iMicroConvDen);
+	iMicroConvNum /= factor;
+	iMicroConvDen /= factor;
+
+	// wraparound time in microseconds is 2^32 * iMicroConv
+	// take the heartbeat interval as half of this (i.e shift left by 31 places) to ensure we wake up often enough to implement the carry properly
+	TUint64 interval = (static_cast<TUint64>(iMicroConvNum) << 31) / iMicroConvDen;
+	if (interval > KMaxTInt32)
+		{
+		iHeartbeatTimerInterval = KMaxTInt32;
+		}
+	else
+		{
+		iHeartbeatTimerInterval = interval;
+		}
+
+	// if denominator is a power of 2, use shift instead
+	// (shifting is faster than division)
+	if(iMicroConvDen & (iMicroConvDen - 1) == 0)
+		{
+		// PRECONDITION:  iMicroConvDen = 2^n, iMicroConvShift = 0
+		// POSTCONDITION: iMicroConvDen = 1,   iMicroConvShift = n
+		while(iMicroConvDen >= 2)
+			{
+			iMicroConvDen >>= 1;
+			iMicroConvShift++;
+			}
+		}
+
+	// Create the locks
+	User::LeaveIfError(iQueMutex.CreateLocal());
+	
+	// Create the memory block of empty Time requests that make up the free list.
+	iRequestBlock = new(ELeave) TMediaRequest [iMaxRequests];
+	
+	// zero the fields for peace of mind
+	Mem::FillZ(iRequestBlock, (sizeof(TMediaRequest)*iMaxRequests));
+	
+   	// Initialise the free list
+   	for(TInt requestIndex = 0; requestIndex < iMaxRequests; requestIndex++)
+   		{
+   		// Add all free items with delta 0
+   		iFreeRequestQue.Add(iRequestBlock+requestIndex, static_cast<TInt64>(0));
+   		}
+   	
+   	iStartTimes.ReserveL(KNumPorts);
+   	for(TInt portIndex = 0; portIndex < KNumPorts; portIndex++)
+   		{
+   		iStartTimes.Append(0);
+   		}
+   	
+	__ASSERT_DEBUG(iMaxRequests == iFreeRequestQue.Count(), Panic(ERequestQueueCorrupt));
+
+	// Create the timer consumer thread
+	TBuf<19> threadName;
+	threadName.Format(_L("OmxILClock@%08X"), this);
+	
+	// Timer created on creation of the thread as it is thread relative
+	User::LeaveIfError(iThread.Create(threadName, ThreadEntryPoint, KThreadStackSize, NULL, this));
+
+	// High priority thread
+	iThread.SetPriority(EPriorityRealTime);
+	// start the thread and wait for it to create the timer
+	TRequestStatus status;
+	iThread.Rendezvous(status);
+	iThread.Resume();
+	iThreadStarted = ETrue;
+	User::WaitForRequest(status);
+	User::LeaveIfError(status.Int());
+	}
+
+
+/**
+ *
+ *
+ */
+CClockSupervisor::CClockSupervisor(COmxILClockProcessingFunction& aProcessingFunction) 
+:	iActiveRefClock(OMX_TIME_RefClockAudio),
+	iMaxRequests(KMaxRequests),
+	iProcessingFunction(aProcessingFunction)
+	{
+	// compile time assertion that fields requiring atomic access are 
+	// 4-byte aligned
+	__ASSERT_COMPILE((_FOFF(CClockSupervisor, iWallTicks) & 3) == 0);
+
+	// run time assertion that class itself is allocated on a 4-byte boundary
+	__ASSERT_ALWAYS((reinterpret_cast<TInt>(this) & 3) == 0, Panic(EBadAlignment));
+
+	iMediaClockState.nSize = sizeof(iMediaClockState);
+	iMediaClockState.nVersion = TOmxILSpecVersion();
+	iMediaClockState.eState = OMX_TIME_ClockStateStopped;
+	
+	iCancelStatus = KRequestPending;
+	}
+
+/**
+ *
+ *
+ */
+CClockSupervisor::~CClockSupervisor()
+	{
+	// signal the timing thread and wait for it to terminate
+	if(iThreadStarted)
+		{
+		iThreadRunning = EFalse;
+		TRequestStatus logonStatus;
+		iThread.Logon(logonStatus);
+		// Want the thread to terminate ASAP instead of waiting for a media
+		// time request completion or a wall time heartbeat. Can't cancel the
+		// timer without duplicating the handle, and that gives
+		// KErrPermissionDenied. So we use a second TRequestStatus to wake the
+		// timing thread before the timer completes.
+		TRequestStatus* cancelStatus = &iCancelStatus;
+		iThread.RequestComplete(cancelStatus, KErrCancel);
+		User::WaitForRequest(logonStatus);
+		}
+	
+	if (iRequestBlock)
+		{
+		delete[] iRequestBlock;
+		iRequestBlock = NULL;
+		}
+	
+	iStartTimes.Close();
+	iThread.Close();
+	iQueMutex.Close();
+	}
+
+
+/**
+ * Timing thread entry point.
+ */
+TInt CClockSupervisor::ThreadEntryPoint(TAny* aPtr)
+	{
+	CClockSupervisor& supervisor = *static_cast<CClockSupervisor*>(aPtr);
+	supervisor.iThreadRunning = ETrue;
+	CTrapCleanup* cleanup = CTrapCleanup::New();
+	if(cleanup == NULL)
+		{
+		return KErrNoMemory;
+		}
+
+	// Delegate to object's clock timing routine
+	TRAPD(err, supervisor.RunTimingThreadL());
+	delete cleanup;
+	return err;
+	}
+
+
+/**
+ *
+ *
+ */
+void CClockSupervisor::RunTimingThreadL()
+	{
+	// Create the timer (thread relative)
+	User::LeaveIfError(iTimer.CreateLocal());
+	// rendezvous with the creating thread as it must be blocked until the timer is created
+	iThread.Rendezvous(KErrNone);
+	
+	// Start the timer loop to enable us to wake up on heart beat or requests
+	TimerLoop();
+	
+	iTimer.Close();
+	}
+
+/**
+ * Services media time requests.
+ */
+void CClockSupervisor::TimerLoop()
+	{
+	// Here we are only interested in setting the timer for a request timeout
+	// or the heart beat. States are taken care of in ConsumeRequests().
+	// On shutdown the flag iThreadRunning is set to EFalse and the timer
+	// completed with KErrCancel or something other than KRequestPending
+	// Therefore we no longer try to get another request.
+	
+	TInt measuredTimerOverhead = KTimerQuantization;
+	TRequestStatus timerStatus;
+	
+	while(iThreadRunning)
+		{
+		// Call our consumer function
+		TInt timeout = ConsumeRequests();
+		
+		// round down sleep based on overhead and quantization of timers
+		timeout -= timeout % KTimerQuantization;
+		timeout -= measuredTimerOverhead;
+		timeout -= KMinTimerOverhead;
+		
+		// limit sleep by the heartbeat interval
+		if(timeout > iHeartbeatTimerInterval)
+			{
+			timeout = iHeartbeatTimerInterval;
+			}
+		
+		// Perhaps timeout not positive from ConsumeRequests(), or due to
+		// rounding down it is no longer positive. In this case we may spin, so
+		// be careful when setting KTimerQuantization, KMinTimerOverhead and
+		// KRequestDeltaLimit
+		if(timeout <= 0)
+			{
+			continue;
+			}
+
+		iQueMutex.Wait();
+		TInt64 actualSleepTime = WallTime();
+		iQueMutex.Signal();
+		iTimer.HighRes(timerStatus, timeout);			
+		// can't cancel iTimer from another thread, instead we use a second
+		// TRequestStatus to wake up early, then we can cancel the timer in
+		// this thread.
+		User::WaitForRequest(timerStatus, iCancelStatus);
+		
+		if(iCancelStatus.Int() != KRequestPending)
+			{
+			iTimer.Cancel();
+			iCancelStatus = KRequestPending;
+			}
+		else
+			{
+			// update measuredTimerOverhead
+			iQueMutex.Wait();
+			actualSleepTime = WallTime() - actualSleepTime;
+			iQueMutex.Signal();
+			TInt sleepOverhead = (TInt) (actualSleepTime - timeout);
+			
+			/* Dampen adjustments to measuredTimerOverhead
+			 * 
+			 *   measuredTimerOverhead = max(sleepOverhead,
+			 *                           avg(measuredTimerOverhead, sleepOverhead))
+			 * 
+			 * i.e. immediate increase, gradual decrease
+			 */
+			measuredTimerOverhead = (measuredTimerOverhead + sleepOverhead) >> 1;
+			if(measuredTimerOverhead < sleepOverhead)
+				{
+				measuredTimerOverhead = sleepOverhead;
+				}
+			}
+		}
+	DEBUG_PRINTF(_L("Clock thread shutting down..."));
+	}
+
+
+/**
+ * Update the wall clock with the system ticks
+ *
+ */
+void CClockSupervisor::UpdateWallTicks()
+	{
+	__ASSERT_DEBUG(iQueMutex.IsHeld(), Panic(EMutexUnheld));
+	
+	TUint32 oldLowPart(I64LOW(iWallTicks));		// save lower 32-bits
+#ifdef USE_FASTCOUNTER
+	// Gets the fast iCounter.
+	// This is the current value of the machine's high resolution timer. 
+	// If a high resolution timer is not available, it uses the millisecond timer instead.
+	// The freqency of this iCounter can be determined by reading the HAL attribute EFastCounterFrequency.
+	TUint32 newLowPart(User::FastCounter());	// set lower 32-bits
+	if(iSystemClockReversed)
+		{
+		newLowPart = -newLowPart;
+		}
+#elif defined(USE_NTICKCOUNT)
+	TUint32 newLowPart(User::NTickCount());		// set lower 32-bits
+#else
+#error
+#endif
+	TUint32 newHighPart(I64HIGH(iWallTicks));	// save upper 32-bits
+	
+	// note: did not use LockedInc() here because:
+	// We need a critical section to capture the system time and update the wall clock
+	// at the same time.
+	// The wall clock is unsigned.
+	// LockedInc doesn't stop a preemption directly after the test, only during the inc	
+	if (newLowPart < oldLowPart) 
+		{
+		newHighPart++;
+		}
+	
+	iWallTicks = MAKE_TUINT64(newHighPart,newLowPart);
+	}
+
+
+/**
+ * Returns the current wall time in microseconds.
+ */
+TInt64 CClockSupervisor::WallTime()
+	 {
+	 __ASSERT_DEBUG(iQueMutex.IsHeld(), Panic(EMutexUnheld));
+	 
+	 UpdateWallTicks();
+	 
+	 // if power of two use shift (faster than division)
+	 if (iMicroConvDen == 1)
+		 {
+		 return iWallTicks * iMicroConvNum >> iMicroConvShift;
+		 }
+	 return iWallTicks * iMicroConvNum / iMicroConvDen;
+	 }
+
+/**
+ *
+ *
+ */
+TBool CClockSupervisor::AllStartTimesReported ()
+	{
+	return !(static_cast<TBool>(iMediaClockState.nWaitMask));
+	}
+
+/**
+ * Called when timer expires or is cancelled.
+ *
+ */
+TInt CClockSupervisor::ConsumeRequests()
+	{
+	// Events are consumed here only in the Running state.
+	// Waiting events, State change events, etc. are handled elsewhere.
+	// These are called broadcast events and have a higher priority 
+	// than the request events requested by the clients.
+	// This function is called by the TimerLoop only.
+	
+	// IMPORTANT: every code path through here must result in a call to UpdateWallTicks to ensure
+	// that 64-bit time is updated correctly (heartbeat interval makes sure ConsumeRequests is called
+	// often enough).
+	
+	// Our event loop - return if stopped but not for pause (keep heart beat going)
+	if(OMX_TIME_ClockStateStopped != iMediaClockState.eState)
+		{
+		// Acquire the mutex
+		iQueMutex.Wait();
+
+		// this can be entered from the timer on heartbeat or on a call to a state change. 
+		// Don't want this to happen on a state change!
+		if (OMX_TIME_ClockStateWaitingForStartTime == iMediaClockState.eState)
+			{
+			// We need to check if all clients have reported their start times
+			if (!AllStartTimesReported())
+				{
+				// Not all reported as yet...
+				UpdateWallTicks();
+				
+				// Release the mutex
+				iQueMutex.Signal();
+				
+				// wait until they have! keep heat beat going in case it takes looooong!
+				return iHeartbeatTimerInterval;
+				}
+			else
+				{
+				// depending on scale decide which start time to use
+				CalculateStartTime();
+
+				// no need to check error, we are in the waiting state
+				DoTransitionToRunningState();
+				
+				// Now just go and send the iNext request!
+				}
+			}
+		
+		///////////////////////////////
+		// There is a request pending !!!
+		// 
+				
+		// check the timeout period against the wall clock to determine if this is a heart beat
+		
+		if(iMtc.iScaleQ16 == 0)
+			{
+			// do not pop front of the queue because we are effectively paused
+			UpdateWallTicks();
+			
+			// Release the mutex
+			iQueMutex.Signal();
+			
+			return iHeartbeatTimerInterval;
+			}
+		
+		// try to pop the next pending request
+		TInt64 delta;
+		TBool present = iPendingRequestQue.FirstDelta(delta);
+		
+		// Is there nothing there?
+		if (!present)
+			{
+			// Nothing on the queue
+			// Here because of heart beat
+			UpdateWallTicks();
+			
+			// Release the mutex
+			iQueMutex.Signal();
+			
+			return iHeartbeatTimerInterval;
+			}
+		
+		// update the delta against clock 
+		delta -= WallTime();
+		
+		// Some time to go before head of queue should be serviced?
+		if (delta > KMinTimerOverhead)
+			{
+			// Release the mutex
+			iQueMutex.Signal();
+			
+			return delta;
+			}
+
+		// Request timed out, delta expired!
+		// Fulfill the request
+		TMediaRequest* request = iPendingRequestQue.RemoveFirst();
+		
+		// Acquire fulfillment buffer for a specific port
+		OMX_BUFFERHEADERTYPE* buffer = iProcessingFunction.AcquireBuffer(request->iPortIndex);
+		if(buffer == NULL)
+			{
+			// starved of buffers!
+			DEBUG_PRINTF(_L("ConsumeRequests starved of buffers, transitioning to OMX_StateInvalid"));
+			iThreadRunning = EFalse;
+			iQueMutex.Signal();
+			iProcessingFunction.InvalidateComponent();
+			return 0;
+			}
+		OMX_TIME_MEDIATIMETYPE* mT = reinterpret_cast<OMX_TIME_MEDIATIMETYPE*>(buffer->pBuffer);
+		buffer->nOffset = 0;
+		buffer->nFilledLen = sizeof(OMX_TIME_MEDIATIMETYPE);
+		
+		mT->nSize = sizeof(OMX_TIME_MEDIATIMETYPE);
+		mT->nVersion = TOmxILSpecVersion();
+		mT->eUpdateType = OMX_TIME_UpdateRequestFulfillment;
+		mT->nClientPrivate = reinterpret_cast<OMX_U32>(request->iClientPrivate);
+		mT->xScale = iMtc.iScaleQ16;
+		mT->eState = OMX_TIME_ClockStateRunning;
+		mT->nMediaTimestamp = request->iMediaTime;
+		mT->nWallTimeAtMediaTime = request->iTriggerWallTime + request->iOffset;
+		mT->nOffset = mT->nWallTimeAtMediaTime - WallTime(); // could be waiting on buffer b4 this!
+
+#ifdef _OMXIL_COMMON_DEBUG_TRACING_ON		
+		DEBUG_PRINTF(_L8("CLOCK::ConsumeRequest*******************************OMX_TIME_UpdateRequestFulfillment***VS2"));
+		TTime t;
+		t.HomeTime();
+		DEBUG_PRINTF2(_L8("CLOCK::ConsumeRequest : t.HomeTime() = %ld"), t.Int64());
+		DEBUG_PRINTF2(_L8("CLOCK::ConsumeRequest : Buffer = 0x%X"), mT->nClientPrivate);
+		DEBUG_PRINTF2(_L8("CLOCK::ConsumeRequest : mT->nMediaTimestamp = %ld"), mT->nMediaTimestamp);
+#endif 
+		
+		iProcessingFunction.SendBuffer(buffer);
+		
+		// clear the delta on this now free request
+		request->iTriggerWallTime = 0;
+		
+		// Add element back to the free pool
+		iFreeRequestQue.Add(request, 0);
+
+		// Release the mutex
+		iQueMutex.Signal();
+
+		// Update delta for next request.
+		// NOTE: we do not know if scale change or not so when we re-enter here
+		// we should recalculate the delta above and perform the appropriate action.
+		return 0;
+		}
+	else
+		{
+		// clock is stopped - sleep for the heartbeat interval
+		return iHeartbeatTimerInterval;
+		}
+	}
+
+/**
+ *
+ *
+ */
+OMX_ERRORTYPE CClockSupervisor::ProduceRequest(OMX_INDEXTYPE aIndex, TEntryPoint aEntryPoint, TAny* aPassedStructPtr)
+	{
+	// Range checking on parameter/config index
+	if(aIndex < KMinJumpTableIndex || aIndex > KMaxJumpTableIndex)
+		{
+		return OMX_ErrorUnsupportedIndex;
+		}
+	// Note also that certain combinations on Get/Set within the supported range are unsupported.
+	// This is left to the function in the jump table.
+	
+	// Acquire the mutex
+	iQueMutex.Wait();
+	
+	// Index the routing table for the correct handler
+	FunctionPtr jumpTableFptr = iJumpTable[aIndex-KMinJumpTableIndex];
+	OMX_ERRORTYPE ret = (this->*jumpTableFptr)(aEntryPoint, aPassedStructPtr);
+	
+	// Release the mutex
+	iQueMutex.Signal();
+	
+	return ret;
+	}
+
+
+/**
+ * According to scale, choose the earliest start time among the set of client
+ * start times. For forward play this is the minimum, for reverse play this is
+ * the maximum.
+ */
+void CClockSupervisor::CalculateStartTime()
+	{
+	// The nWaitMask field of the Media clock state is a bit mask specifying the client 
+	// components that the clock component will wait on in the 
+	// OMX_TIME_ClockStateWaitingForStartTime state. Bit masks are defined 
+	// as OMX_CLOCKPORT0 through OMX_CLOCKPORT7.
+		
+	// Based on scale locate the minimum or maximum start times of all clients
+	TInt64 startTime;
+	
+	if (iMtc.iScaleQ16 >= 0)
+		{
+		startTime = KMaxTInt64;
+		
+		// choose minimum
+		for (TInt portIndex = 0; portIndex < iStartTimes.Count(); portIndex++)
+			{
+			if(iStartTimesSet & (1 << portIndex))
+				{
+				startTime = Min(startTime, iStartTimes[portIndex]);
+				}
+			}
+		}
+	else
+		{
+		startTime = KMinTInt64;
+		
+		// choose maximum
+		for (TInt portIndex = 0; portIndex < iStartTimes.Count(); portIndex++)
+			{
+			if(iStartTimesSet & (1 << portIndex))
+				{
+				startTime = Max(startTime, iStartTimes[portIndex]);
+				}
+			}
+		}
+	
+	// adjust the media time to the new start time
+	UpdateMediaTime(startTime);
+	}
+
+
+/**
+ * Perform actions related to placing the clock into the Stopped state
+ */
+void CClockSupervisor::DoTransitionToStoppedState()
+	{
+	// OMX_TIME_ClockStateStopped: Immediately stop the media clock, clear all
+	// pending media time requests, clear all client start times, and transition to the
+	// stopped state. This transition is valid from all other states.
+
+	// We should already have the mutex!
+	__ASSERT_DEBUG(iQueMutex.IsHeld(), Panic(EMutexUnheld));
+		
+	// clear any current start time
+	iMediaClockState.nStartTime = 0;
+	iMediaClockState.nWaitMask = 0;
+	
+	// clear client start times
+	for(TInt index = 0, count = iStartTimes.Count(); index < count; index++)
+		{
+		iStartTimes[index] = 0;
+		}
+	iStartTimesSet = 0;
+	
+	// clear all pending requests, placing them back on the free queue
+	while (!iPendingRequestQue.IsEmpty())
+		{
+		TMediaRequest* request = iPendingRequestQue.RemoveFirst();
+
+		// clear the delta on this now free request
+		request->iTriggerWallTime = 0;
+
+		iFreeRequestQue.Add(request,0);
+		}
+
+	TInt64 wallTimeNow = WallTime();
+	
+	// if clock was previously running, stop the media time at the present value
+	if(iMediaClockState.eState == OMX_TIME_ClockStateRunning)
+		{
+		TInt64 mediaTimeNow = ((wallTimeNow - iMtc.iWallTimeBase) * iMtc.iScaleQ16 >> 16) + iMtc.iMediaTimeBase;
+		iMtc.iWallTimeBase = wallTimeNow;
+		iMtc.iMediaTimeBase = mediaTimeNow;
+		}
+	
+	// Indicate stopped state
+	iMediaClockState.eState = OMX_TIME_ClockStateStopped;
+
+	// Indicate to clients a state change
+	OMX_TIME_MEDIATIMETYPE update;
+	update.nSize = sizeof(OMX_TIME_MEDIATIMETYPE);
+	update.nVersion = TOmxILSpecVersion();
+	update.nClientPrivate = NULL;
+	update.eUpdateType = OMX_TIME_UpdateClockStateChanged;
+	update.xScale = iMtc.iScaleQ16;
+	update.eState = OMX_TIME_ClockStateStopped;
+	update.nMediaTimestamp = iMtc.iMediaTimeBase;
+	update.nWallTimeAtMediaTime = wallTimeNow;
+	update.nOffset = 0;
+
+	BroadcastUpdate(update);
+	}
+
+
+/**
+ * Perform actions related to placing the clock into the Running state
+ */
+void CClockSupervisor::DoTransitionToRunningState()
+	{
+	// OMX_TIME_ClockStateRunning: Immediately start the media clock using the given
+	// start time and offset, and transition to the running state. This transition is valid from
+	// all other states.
+	
+	// We should already have the mutex!
+	__ASSERT_DEBUG(iQueMutex.IsHeld(), Panic(EMutexUnheld));
+	
+	// if we transistioned from stopped to running then start time will be cleared.
+	// we only set it on transition from waiting to running
+	// this is enforced not by a check but by logic flow!
+	// If this is not the case then start time should have been cleared and we can start now!
+
+	// Indicate running state
+	iMediaClockState.eState = OMX_TIME_ClockStateRunning;
+	
+	// Indicate to clients a state change
+	OMX_TIME_MEDIATIMETYPE update;
+	update.nSize = sizeof(OMX_TIME_MEDIATIMETYPE);
+	update.nVersion = TOmxILSpecVersion();
+	update.nClientPrivate = NULL;
+	update.eUpdateType = OMX_TIME_UpdateClockStateChanged;
+	update.xScale = iMtc.iScaleQ16;
+	update.eState = iMediaClockState.eState;
+	update.nMediaTimestamp = iMtc.iMediaTimeBase;
+	update.nWallTimeAtMediaTime = iMtc.iWallTimeBase;
+	update.nOffset = 0;
+	
+	BroadcastUpdate(update);
+	}
+
+/**
+ * Perform actions related to placing the clock into the Waiting state
+ */
+void CClockSupervisor::DoTransitionToWaitingState(OMX_U32 nWaitMask)
+	{
+	// OMX_TIME_WaitingForStartTime: Transition immediately to the waiting state, wait
+	// for all clients specified in nWaitMask to report their start time, start the media clock
+	// using the minimum of all client start times and transition to
+	// OMX_TIME_ClockStateRunning. This transition is only valid from the
+	// OMX_TIME_ClockStateStopped state.
+	// (*Added*) If in the backwards direction start the media clock using the maximum
+	// of all client start times.
+	
+	// validity of state transition has been checked in HandleGetSetClockState()
+	
+	// We should already have the mutex!
+	__ASSERT_DEBUG(iQueMutex.IsHeld(), Panic(EMutexUnheld));
+	
+	iMediaClockState.eState = OMX_TIME_ClockStateWaitingForStartTime;
+	
+	// Start times set in calling function HandleClientStartTime()
+	
+	// Remember all clients that need to report their start times
+	iMediaClockState.nWaitMask = nWaitMask;
+
+	// Indicate to clients a state change
+	OMX_TIME_MEDIATIMETYPE update;
+	update.nSize = sizeof(OMX_TIME_MEDIATIMETYPE);
+	update.nVersion = TOmxILSpecVersion();
+	update.nClientPrivate = NULL;
+	update.eUpdateType = OMX_TIME_UpdateClockStateChanged;
+	update.xScale = iMtc.iScaleQ16;
+	update.eState = OMX_TIME_ClockStateWaitingForStartTime;
+	update.nMediaTimestamp = iMtc.iMediaTimeBase;
+	update.nWallTimeAtMediaTime = WallTime();
+	update.nOffset = 0;
+	
+	BroadcastUpdate(update);
+	}
+
+
+ /**
+  * Update the wall clock with the system ticks
+  *
+  */
+ OMX_ERRORTYPE CClockSupervisor::HandleSubmitMediaTimeRequest(TEntryPoint aEntry, OMX_PTR aPassedStructPtr)
+ 	{
+ 	// We should already have the mutex!
+ 	__ASSERT_DEBUG(iQueMutex.IsHeld(), Panic(EMutexUnheld));
+ 	
+	// A client requests the transmission of a particular timestamp via OMX_SetConfig on its
+ 	// clock port using the OMX_IndexConfigTimeMediaTimeRequest configuration
+ 	// and structure OMX_TIME_CONFIG_MEDIATIMEREQUESTTYPE
+
+ 	// OMX_GetConfig doesn't make any sense for MediaTimeRequest
+ 	if(aEntry == EGetConfig)
+ 		{
+ 		return OMX_ErrorUnsupportedIndex;
+ 		}
+ 	
+ 	TMediaRequest *request = iFreeRequestQue.RemoveFirst();
+	if(request == NULL)
+		{
+		// too many pending requests!
+		return OMX_ErrorInsufficientResources;
+		}
+
+	OMX_TIME_CONFIG_MEDIATIMEREQUESTTYPE* rT = static_cast<OMX_TIME_CONFIG_MEDIATIMEREQUESTTYPE*>(aPassedStructPtr);
+	
+	request->iMediaTime = rT->nMediaTimestamp;
+	request->iOffset = rT->nOffset;
+	request->iTriggerWallTime = 
+		((rT->nMediaTimestamp - iMtc.iMediaTimeBase) * iMtc.iInverseScaleQ16 >> 16) 
+			+ iMtc.iWallTimeBase - rT->nOffset;
+	request->iPortIndex = rT->nPortIndex;
+	request->iClientPrivate = rT->pClientPrivate;
+	
+	TInt64 prevHeadTime(0);
+	TBool nonEmpty = iPendingRequestQue.FirstDelta(prevHeadTime);
+	iPendingRequestQue.Add(request, request->iTriggerWallTime);
+	// Wake the thread immediately if head of queue now will complete sooner
+	// than it would have previously, or if the queue was empty previously.
+	// This causes the timing thread to recalculate the sleep time.
+	if(!nonEmpty || prevHeadTime > request->iTriggerWallTime)
+		{
+		TRequestStatus* status = &iCancelStatus;
+		iThread.RequestComplete(status, KErrCancel);
+		}
+	return OMX_ErrorNone;
+ 	}
+ 	
+
+ /**
+  *
+  *
+  */
+ OMX_ERRORTYPE CClockSupervisor::HandleQueryCurrentWallTime(TEntryPoint aEntry, OMX_PTR aPassedStructPtr)
+	 {
+	 // An IL client may query the current wall time via OMX_GetConfig on OMX_IndexConfigTimeCurrentWallTime.	 
+	 // A client may obtain the current wall time, which is obtained via OMX_GetConfig on
+	 // OMX_IndexConfigTimeCurrentWallTime.
+	 
+	 // OMX_SetConfig doesn't make sense for wall time
+	 if(aEntry == ESetConfig)
+		 {
+		 return OMX_ErrorUnsupportedIndex;
+		 }
+	 
+	// We should already have the mutex!
+	__ASSERT_DEBUG(iQueMutex.IsHeld(), Panic(EMutexUnheld));
+	
+	OMX_TIME_CONFIG_TIMESTAMPTYPE& wallTs = *static_cast<OMX_TIME_CONFIG_TIMESTAMPTYPE*>(aPassedStructPtr);
+	wallTs.nTimestamp = WallTime();
+	return OMX_ErrorNone;
+	}
+
+
+/**
+ * Calculates and returns the current media time.
+ */
+OMX_ERRORTYPE CClockSupervisor::HandleQueryCurrentMediaTime(TEntryPoint aEntry, OMX_PTR aPassedStructPtr)
+	{
+	// The clock component can be queried for the current media clock time using
+	// OMX_GetConfig with the read-only index OMX_IndexConfigTimeCurrentMediaTime and structure
+	// OMX_TIME_CONFIG_TIMESTAMPTYPE.	
+
+	// OMX_SetConfig cannot be used for Media Time (audio or video reference updates are used instead)
+	if(aEntry == ESetConfig)
+		{
+		return OMX_ErrorUnsupportedIndex;
+		}
+	
+	// We should already have the mutex!
+	__ASSERT_DEBUG(iQueMutex.IsHeld(), Panic(EMutexUnheld));
+	
+	OMX_TIME_CONFIG_TIMESTAMPTYPE* ts = static_cast<OMX_TIME_CONFIG_TIMESTAMPTYPE*>(aPassedStructPtr);
+	if(iMediaClockState.eState == OMX_TIME_ClockStateRunning)
+		{
+		ts->nTimestamp = ((WallTime() - iMtc.iWallTimeBase) * iMtc.iScaleQ16 >> 16) + iMtc.iMediaTimeBase;
+		}
+	else
+		{
+		ts->nTimestamp = iMtc.iMediaTimeBase;
+		}
+	return OMX_ErrorNone;
+	}
+
+/**
+ * An external component is providing an Audio reference time update.
+ */
+OMX_ERRORTYPE CClockSupervisor::HandleUpdateAudioReference(TEntryPoint aEntry, OMX_PTR aPassedStructPtr)
+	{
+	// The clock component can accept an audio reference clock. 
+	// The reference clock tracks the media time at its associated component 
+	// (i.e., the timestamp of the data currently being processed at that component) 
+	// and provides periodic references to the clock component via OMX_SetConfig 
+	// using OMX_IndexConfigTimeCurrentAudioReference, and structure OMX_TIME_CONFIG_TIMESTAMPTYPE
+	//
+	// When the clock component receives a reference, it updates its internally maintained
+	// media time with the reference. This action synchronizes the clock component with the
+	// component that is providing the reference clock.
+	
+	// OMX_GetConfig not supported on reference time as it will generally be
+	// some arbitary time in the past
+	if(aEntry == EGetConfig)
+		{
+		return OMX_ErrorUnsupportedIndex;
+		}
+	
+	// We should already have the mutex!
+	__ASSERT_DEBUG(iQueMutex.IsHeld(), Panic(EMutexUnheld));
+
+	if(iActiveRefClock == OMX_TIME_RefClockAudio)
+		{
+		OMX_TIME_CONFIG_TIMESTAMPTYPE* ts = static_cast<OMX_TIME_CONFIG_TIMESTAMPTYPE*>(aPassedStructPtr);
+		UpdateMediaTime(ts->nTimestamp);
+		}
+	
+	return OMX_ErrorNone;
+	}
+
+/**
+ * An external component is providing a Video reference time update.
+ */
+OMX_ERRORTYPE CClockSupervisor::HandleUpdateVideoReference(TEntryPoint aEntry, OMX_PTR aPassedStructPtr)
+	{
+	// The clock component can accept a video reference clock. 
+	// The reference clock tracks the media time at its associated component 
+	// (i.e., the timestamp of the data currently being processed at that component) 
+	// and provides periodic references to the clock component via OMX_SetConfig 
+	// using OMX_IndexConfigTimeCurrentVideoReference, and structure OMX_TIME_CONFIG_TIMESTAMPTYPE
+	//
+	// When the clock component receives a reference, it updates its internally maintained
+	// media time with the reference. This action synchronizes the clock component with the
+	// component that is providing the reference clock.
+
+	// OMX_GetConfig not supported on reference time as it will generally be
+	// some arbitary time in the past
+	if(aEntry == EGetConfig)
+		{
+		return OMX_ErrorUnsupportedIndex;
+		}
+	
+	// We should already have the mutex!
+	__ASSERT_DEBUG(iQueMutex.IsHeld(), Panic(EMutexUnheld));
+	
+	if(iActiveRefClock == OMX_TIME_RefClockVideo)
+		{
+		OMX_TIME_CONFIG_TIMESTAMPTYPE* ts = static_cast<OMX_TIME_CONFIG_TIMESTAMPTYPE*>(aPassedStructPtr);
+		UpdateMediaTime(ts->nTimestamp);
+		}
+	
+	return OMX_ErrorNone;
+	}
+
+ /**
+  *
+  *
+  */
+ OMX_ERRORTYPE CClockSupervisor::HandleSetPortClientStartTime(TEntryPoint aEntry, OMX_PTR aPassedStructPtr)
+	{
+	// We should already have the mutex!
+	__ASSERT_DEBUG(iQueMutex.IsHeld(), Panic(EMutexUnheld));
+	
+	// When a clock component client receives a buffer with flag OMX_BUFFERFLAG_STARTTIME set, 
+	// it performs an OMX_SetConfig call with OMX_IndexConfigTimeClientStartTime 
+	// on the clock component that is sending the buffer’s timestamp. 
+	// The transmission of the start time informs the clock component that the client’s stream 
+	// is ready for presentation and the timestamp of the first data to be presented.
+	// 
+	// If the IL client requests a transition to OMX_TIME_ClockStateWaitingForStartTime, it
+	// designates which clock component clients to wait for. The clock component then waits
+	// for these clients to send their start times via the
+	// OMX_IndexConfigTimeClientStartTime configuration. Once all required
+	// clients have responded, the clock component starts the media clock using the earliest
+	// client start time.
+	// 
+	// When a client is sent a start time (i.e., the timestamp of a buffer marked with the
+	// OMX_BUFFERFLAG_STARTTIME flag ), it sends the start time to the clock component
+	// via OMX_SetConfig on OMX_IndexConfigTimeClientStartTime. This
+	// action communicates to the clock component the following information about the client’s
+	// data stream:
+	// - The stream is ready.
+	// - The starting timestamp of the stream
+	
+	// TODO Perhaps OMX_GetConfig can be done for client start time, after all
+	// the start times on each port have been stored. But for now this is not
+	// supported.
+	if(aEntry == EGetConfig)
+		{
+		return OMX_ErrorUnsupportedIndex;
+		}
+	
+	if(iMediaClockState.eState != OMX_TIME_ClockStateWaitingForStartTime)
+		{
+		return OMX_ErrorIncorrectStateOperation;
+		}
+	
+	OMX_TIME_CONFIG_TIMESTAMPTYPE* state = static_cast<OMX_TIME_CONFIG_TIMESTAMPTYPE*>(aPassedStructPtr);
+	iMediaClockState.nWaitMask &= ~(1 << state->nPortIndex); // switch off bit
+	iStartTimesSet |= 1 << state->nPortIndex;	// switch on bit
+	iStartTimes[state->nPortIndex] = state->nTimestamp;
+	
+	if(iMediaClockState.nWaitMask == 0)
+		{
+		// cancel timer so transition occurs immediately instead of waiting for a heartbeat
+		TRequestStatus* cancelStatus = &iCancelStatus;
+		iThread.RequestComplete(cancelStatus, KErrCancel);
+		}
+	
+	return OMX_ErrorNone;
+	}
+
+/**
+ * Sets the current media time to the specified value.
+ */
+void CClockSupervisor::UpdateMediaTime(TInt64 aMediaTime)
+	{
+#ifdef _OMXIL_COMMON_DEBUG_TRACING_ON
+	OMX_TIME_CONFIG_TIMESTAMPTYPE ts;
+	HandleQueryCurrentMediaTime(EGetConfig, &ts);
+	DEBUG_PRINTF4(_L8("Clock::UpdateMediaTime=[%ld]currentmediaTime=<%d> MediaTime <%ld>"), ts.nTimestamp-aMediaTime,ts.nTimestamp, aMediaTime);
+#endif // _OMXIL_COMMON_DEBUG_TRACING_ON
+	iMtc.iWallTimeBase = WallTime();
+	iMtc.iMediaTimeBase = aMediaTime;
+	iPendingRequestQue.RecalculateAndReorder(iMtc);
+	
+	TRequestStatus* status = &iCancelStatus;
+    iThread.RequestComplete(status, KErrCancel);
+	}
+
+ /**
+  *
+  *
+  */
+ OMX_ERRORTYPE CClockSupervisor::HandleGetSetTimeScale(TEntryPoint aEntry, OMX_PTR aPassedStructPtr)
+	{
+	// The IL client queries and sets the media clock’s scale via the
+	// OMX_IndexConfigTimeScale configuration, passing  structure
+	// OMX_TIME_CONFIG_SCALETYPE 
+
+	// We should already have the mutex!
+	__ASSERT_DEBUG(iQueMutex.IsHeld(), Panic(EMutexUnheld));
+	
+	OMX_TIME_CONFIG_SCALETYPE* sT = static_cast<OMX_TIME_CONFIG_SCALETYPE*>(aPassedStructPtr);
+
+	if (EGetConfig == aEntry)
+		{
+		sT->xScale = iMtc.iScaleQ16;
+		}
+	else 
+		{ // ESetConfig
+		
+		if(sT->xScale == iMtc.iScaleQ16)
+			{
+			// do not broadcast update if the scale changed to the same value
+			// IL client causes the scale change but only IL components receive the notification
+			return OMX_ErrorNone;
+			}
+		iMtc.SetScaleQ16(sT->xScale, WallTime());
+		
+		// Reorder before sending notifications
+		iPendingRequestQue.RecalculateAndReorder(iMtc);
+		
+		// Indicate to clients a scale change
+		// The buffer payload is written here then copied to all clients' buffers
+		// It should be noted that as this is happening across all ports, time can pass
+		// making nMediaTimestamp and nWallTimeAtMediaTime inaccurate. Since at present we do
+		// not recover buffer exhaustion scenarios, it is assumed that the messaging time is short
+		// thus we do not recalculate the time.
+		
+		OMX_TIME_MEDIATIMETYPE update;
+		update.nSize = sizeof(OMX_TIME_MEDIATIMETYPE);
+		update.nVersion = TOmxILSpecVersion();
+		update.nClientPrivate = NULL;
+		update.eUpdateType = OMX_TIME_UpdateScaleChanged;
+		update.xScale = iMtc.iScaleQ16;
+		update.eState = iMediaClockState.eState;
+		update.nMediaTimestamp = iMtc.iMediaTimeBase;
+		update.nWallTimeAtMediaTime = iMtc.iWallTimeBase;
+		update.nOffset = 0;
+		
+		BroadcastUpdate(update);
+		
+		// Signal the Timer thread as it may need to fulfill all requests on a direction change,
+		// of fulfill some requests as we have moved forward in time
+		TRequestStatus* cancelStatus = &iCancelStatus;
+		iThread.RequestComplete(cancelStatus, KErrCancel);
+		//******************************************
+		}
+	return OMX_ErrorNone;
+	}
+ 
+ 
+ /**
+  *
+  *
+  */
+ OMX_ERRORTYPE CClockSupervisor::HandleGetSetClockState(TEntryPoint aEntry, OMX_PTR aPassedStructPtr)
+	{
+	// An OMX_GetConfig execution using index OMX_IndexConfigTimeClockState
+	// and structure OMX_TIME_CONFIG_CLOCKSTATETYPE queries the current clock state.
+	// An OMX_SetConfig execution using index OMX_IndexConfigTimeClockState
+	// and structure OMX_TIME_CONFIG_CLOCKSTATETYPE commands the clock
+	// component to transition to the given state, effectively providing the IL client a
+	// mechanism for starting and stopping the media clock.
+	// 
+	// Upon receiving OMX_SetConfig from the IL client that requests a transition to the
+	// given state, the clock component will do the following:
+	//
+	// - OMX_TIME_ClockStateStopped: Immediately stop the media clock, clear all
+	// pending media time requests, clear and all client start times, and transition to the
+	// stopped state. This transition is valid from all other states.
+	//
+	// - OMX_TIME_ClockStateRunning: Immediately start the media clock using the given
+	// start time and offset, and transition to the running state. This transition is valid from
+	// all other states.
+	//
+	// - OMX_TIME_WaitingForStartTime: Transition immediately to the waiting state, wait
+	// for all clients specified in nWaitMask to report their start time, start the media clock
+	// using the minimum of all client start times and transition to
+	// OMX_TIME_ClockStateRunning. This transition is only valid from the
+	// OMX_TIME_ClockStateStopped state.
+
+	// We should already have the mutex!
+	__ASSERT_DEBUG(iQueMutex.IsHeld(), Panic(EMutexUnheld));
+
+	OMX_ERRORTYPE ret = OMX_ErrorNone;
+	
+	OMX_TIME_CONFIG_CLOCKSTATETYPE* const &clkState 
+		= static_cast<OMX_TIME_CONFIG_CLOCKSTATETYPE*>(aPassedStructPtr);
+	
+	if (ESetConfig == aEntry)
+		{		
+		if (iMediaClockState.eState == clkState->eState)
+			{
+			// Already in this state!
+			return OMX_ErrorSameState;
+			}
+		if (clkState->eState != OMX_TIME_ClockStateStopped &&
+			clkState->eState != OMX_TIME_ClockStateWaitingForStartTime &&
+			clkState->eState != OMX_TIME_ClockStateRunning)
+			{
+			return OMX_ErrorUnsupportedSetting;
+			}
+		
+		// need buffers to notify clock state changes, so require to be in Executing
+		if(!iProcessingFunction.IsExecuting())
+			{
+			return OMX_ErrorIncorrectStateOperation;
+			}
+		
+		switch (clkState->eState)
+			{
+			case OMX_TIME_ClockStateStopped:
+				{
+				DoTransitionToStoppedState();
+				break;
+				}
+			case OMX_TIME_ClockStateWaitingForStartTime:
+				{
+				// Can't go into this state from Running state
+				if (OMX_TIME_ClockStateRunning == iMediaClockState.eState)
+					{
+					ret = OMX_ErrorIncorrectStateTransition;
+					break;
+					}
+				// Waiting for no ports makes no sense. Also don't allow wait on ports we don't have.
+				if (clkState->nWaitMask == 0 || clkState->nWaitMask >= 1 << KNumPorts)
+					{
+					ret = OMX_ErrorUnsupportedSetting;
+					break;
+					}
+				DoTransitionToWaitingState(clkState->nWaitMask);
+				break;
+				}			
+			case OMX_TIME_ClockStateRunning:
+				{
+				// set media time to that passed by the IL client
+				iMtc.iWallTimeBase = WallTime();
+				iMtc.iMediaTimeBase = clkState->nStartTime;
+				// changed time base so pending trigger wall times may be different
+				iPendingRequestQue.RecalculateAndReorder(iMtc);
+				DoTransitionToRunningState();
+ 				// wake the timer thread to reset timer or service pending updates
+				TRequestStatus* statusPtr = &iCancelStatus;
+				iThread.RequestComplete(statusPtr, KErrCancel);
+
+				break;
+				}
+			// default condition already checked before Executing test
+			}
+		}
+	else
+		{
+		clkState->eState = iMediaClockState.eState;
+		}
+	
+	return ret;
+	}
+ 
+ /**
+  *
+  *
+  */
+ OMX_ERRORTYPE CClockSupervisor::HandleGetSetActiveRefClock(TEntryPoint aEntry, OMX_PTR aPassedStructPtr)
+	{
+	// The IL client controls which reference clock the clock component uses (if any) via the
+	// OMX_IndexConfigTimeActiveRefClock configuration and structure OMX_TIME_CONFIG_ACTIVEREFCLOCKTYPE
+	
+	// don't need the mutex here as just getting/setting one machine word
+	
+	OMX_TIME_CONFIG_ACTIVEREFCLOCKTYPE& ref = *static_cast<OMX_TIME_CONFIG_ACTIVEREFCLOCKTYPE*>(aPassedStructPtr);
+
+	if (ESetConfig == aEntry)
+		{
+		if (ref.eClock != OMX_TIME_RefClockAudio &&
+			ref.eClock != OMX_TIME_RefClockVideo &&
+			ref.eClock != OMX_TIME_RefClockNone)
+			{
+			return OMX_ErrorUnsupportedSetting;
+			}
+		iActiveRefClock = ref.eClock;
+		}
+	else // EGetConfig
+		{
+		ref.eClock = iActiveRefClock;
+		}
+	return OMX_ErrorNone;
+	}
+
+void CClockSupervisor::BroadcastUpdate(const OMX_TIME_MEDIATIMETYPE& aUpdate)
+	{
+	// notify state change on all enabled ports
+ 	for(TInt portIndex = 0; portIndex < KNumPorts; portIndex++)
+ 		{
+ 		if(iProcessingFunction.PortEnabled(portIndex))
+ 			{
+ 			OMX_BUFFERHEADERTYPE* buffer = iProcessingFunction.AcquireBuffer(portIndex);
+ 			if(buffer == NULL)
+ 				{
+ 				// starved of buffers!
+ 				iThreadRunning = EFalse;
+ 				iProcessingFunction.InvalidateComponent();
+ 				return;
+ 				}
+ 			OMX_TIME_MEDIATIMETYPE& mT = *reinterpret_cast<OMX_TIME_MEDIATIMETYPE*>(buffer->pBuffer);
+ 			mT = aUpdate;
+ 			buffer->nOffset = 0;
+ 			buffer->nFilledLen = sizeof(OMX_TIME_MEDIATIMETYPE);
+ 			iProcessingFunction.SendBuffer(buffer);
+ 			}
+ 		}
+	}
+
+void CClockSupervisor::ReportClockThreadPanic()
+    {
+        iThreadRunning = EFalse;
+        iProcessingFunction.InvalidateComponent();
+    }
+/**
+ * Adjusts the media time scale.
+ * The wall/media time bases are updated so there is no instantaneous change to media time.
+ * iInverseScaleQ16 is also recalculated.
+ */
+void TMediaTimeContext::SetScaleQ16(TInt32 aScaleQ16, TInt64 aWallTimeNow)
+	{
+	TInt64 mediaTimeNow = ((aWallTimeNow - iWallTimeBase) * iScaleQ16 >> 16) + iMediaTimeBase;
+	iWallTimeBase = aWallTimeNow;
+	iMediaTimeBase = mediaTimeNow;
+	iScaleQ16 = aScaleQ16;
+	
+	// calculate inverse scale
+	// 1.0/scale in Q16 format becomes 2^32/scaleQ16
+	// values of -1 and +1 will cause overflow and are clipped
+	// division by zero also yields KMaxTInt (2^31 - 1)
+	if(iScaleQ16 == 0 || iScaleQ16 == 1)
+		{
+		iInverseScaleQ16 = 0x7FFFFFFF;
+		}
+	else if(iScaleQ16 == -1)
+		{
+		iInverseScaleQ16 = 0x80000000;
+		}
+	else
+		{
+		iInverseScaleQ16 = static_cast<TInt32>(0x100000000LL / iScaleQ16);
+		}
+	}
+
+
+TRequestDeltaQue::TRequestDeltaQue()
+ :	iHead(0),
+ 	iCount(0)
+	 {
+	 // do nothing
+	 }
+
+ void TRequestDeltaQue::Add(TMediaRequest* aElement, TInt64 aDelta)
+	{
+	CHECK_DEBUG();
+	
+	__ASSERT_DEBUG(((iHead == NULL && iCount == 0) || (iHead != NULL && iCount > 0)), 
+					Panic(ERequestQueueCorrupt));
+	
+	if (!iHead)
+		{
+		iHead = aElement;
+		aElement->iPrev = aElement;
+		aElement->iNext = aElement;
+		}
+	else // set the new element links
+		{
+		TBool front(EFalse);
+		TMediaRequest* item(NULL);
+		front = InsertBeforeFoundPosition (aDelta, item);
+		
+		if (front)
+			{
+			// insert infront BEFORE as we have the lesser delta
+			// set up the element
+			aElement->iPrev = item->iPrev;
+			aElement->iNext = item;
+			
+			// set up the item before in the list
+			item->iPrev->iNext = aElement;
+			
+			// set up the item after in the list
+			item->iPrev = aElement;
+			
+			// setup the new head
+			iHead = aElement;
+			}
+		else
+			{
+			// insert this element AFTER the item in the list
+			// set up the element
+			aElement->iPrev = item;
+			aElement->iNext = item->iNext;
+			
+			// set up the item before in the list
+			item->iNext = aElement;
+			
+			// set up the item after in the list
+			aElement->iNext->iPrev = aElement;
+			}
+		}
+	iCount++;
+	
+#ifdef _OMXIL_COMMON_DEBUG_TRACING_ON
+	// print the trigger times in debug mode
+	DbgPrint();
+#endif
+	
+	CHECK_DEBUG();
+	}
+
+ TBool TRequestDeltaQue::InsertBeforeFoundPosition(TInt64 aDelta, TMediaRequest*& aItem) const
+	{
+	CHECK_DEBUG();
+	
+	__ASSERT_DEBUG(((iHead == NULL && iCount == 0) || (iHead != NULL && iCount > 0)), 
+						Panic(ERequestQueueCorrupt));
+	
+	// search for the position where to insert
+	// and insert after this
+	
+	aItem = iHead->iPrev; // tail
+	
+	// start from the end and linearly work backwards
+	while (aItem->iTriggerWallTime > aDelta && aItem != iHead)
+		{
+		aItem = aItem->iPrev;
+		}
+	
+	// indicates that we insert before the item and not after it
+	if (aItem == iHead && aItem->iTriggerWallTime > aDelta)
+		{
+		return ETrue;
+		}
+
+	// no CHECK_DEBUG required as this method is const
+	
+	return EFalse;
+	}
+ 
+/**
+ * If scale changes, the iTriggerWallTimes must be recalculated.
+ *
+ * In addition, because offset is not affected by scale, it is possible for
+ * the order of the elements to change. This event is assumed to be rare, and
+ * when it does occur we expect the list to remain 'roughly sorted' requiring
+ * few exchanges.
+ * 
+ * So we choose ** bubble sort **.
+ *
+ * Time recalculation is merged with the first pass of bubble sort. Times are
+ * recalculated in the first iteration only. Swaps can occur as necessary in
+ * all iterations. We expect that in most cases there will only be one pass
+ * with no swaps.
+ *
+ * Note that bubble sort would be worst-case complexity if reversing the list
+ * due to a time direction change. In such cases future requests complete
+ * immediately (because they are now in the past). So we do not sort at at all
+ * in this case.
+ */
+ void TRequestDeltaQue::RecalculateAndReorder(TMediaTimeContext& aMtc)
+	{
+	CHECK_DEBUG();
+	
+	__ASSERT_DEBUG(((iHead == NULL && iCount == 0) || (iHead != NULL && iCount > 0)), 
+						Panic(ERequestQueueCorrupt));
+	
+	if(iCount == 0)
+		{
+		// nothing to do
+		return;
+		}
+	// note if there is 1 item there is no reorder but we do need to recalculate
+	
+	TBool swapped(EFalse);
+	TBool deltaCalculated(EFalse);
+
+	do
+		{
+		// start from end of queue
+		swapped = EFalse;
+		TMediaRequest* item(iHead->iPrev); // tail
+		
+		if (!deltaCalculated)
+			{
+			// calculate the tails new delta
+			item->iTriggerWallTime = 
+				((item->iMediaTime - aMtc.iMediaTimeBase) * aMtc.iInverseScaleQ16 >> 16) 
+					+ aMtc.iWallTimeBase - item->iOffset;
+			}
+
+		while (item != iHead)
+			{
+			TMediaRequest* swap = item->iPrev;
+			if (!deltaCalculated)
+				{
+				// recalculate the Prev item delta
+				swap->iTriggerWallTime = 
+					((swap->iMediaTime - aMtc.iMediaTimeBase) * aMtc.iInverseScaleQ16 >> 16) 
+						+ aMtc.iWallTimeBase - swap->iOffset;
+				}
+
+			if (swap->iTriggerWallTime > item->iTriggerWallTime)
+				{
+				// switch (swap, item) for (item, swap)
+				item->Deque();
+				item->AddBefore(swap);
+				if(swap == iHead)
+					{
+					iHead = item;
+					}
+
+				swapped = ETrue;
+				}
+			else
+				{
+				// move along list
+				item = item->iPrev;
+				}
+			
+			} // while
+
+		// after the first pass of the queue, all deltas calculated
+		deltaCalculated = ETrue;
+		
+		} while (swapped);
+	
+#ifdef _OMXIL_COMMON_DEBUG_TRACING_ON
+	DbgPrint();
+#endif
+
+	CHECK_DEBUG();
+	}
+ 
+ 
+ TMediaRequest* TRequestDeltaQue::RemoveFirst()
+	{
+	CHECK_DEBUG();
+	
+	__ASSERT_DEBUG(((iHead == NULL && iCount == 0) || (iHead != NULL && iCount > 0)), 
+						Panic(ERequestQueueCorrupt));
+	
+	TMediaRequest* item = NULL;
+	
+	// empty?
+	if (!iHead)
+		{
+		return NULL;
+		}
+	else
+	// only one element?
+	if (1 == iCount)
+		{
+		item = iHead;
+		iHead = NULL;
+		}
+	else
+		{
+		item = iHead;
+		item->iPrev->iNext = item->iNext;
+		item->iNext->iPrev = item->iPrev;
+		
+		// setup the new head
+		iHead = item->iNext;
+		}
+	
+	iCount--;
+	
+	CHECK_DEBUG();
+	
+	return item;
+	}
+
+ TBool TRequestDeltaQue::FirstDelta(TInt64& aDelta) const
+	{
+	CHECK_DEBUG();
+	
+	__ASSERT_DEBUG(((iHead == NULL && iCount == 0) || (iHead != NULL && iCount > 0)), 
+						Panic(ERequestQueueCorrupt));
+		
+	if (!iHead)
+		{
+		return EFalse;
+		}
+	
+	aDelta = iHead->iTriggerWallTime;
+	return ETrue;
+	}
+
+ TBool TRequestDeltaQue::IsEmpty() const
+	{
+	__ASSERT_DEBUG(((iHead == NULL && iCount == 0) || (iHead != NULL && iCount > 0)), 
+						Panic(ERequestQueueCorrupt));
+		
+	return (!iHead);
+	}
+
+ TUint TRequestDeltaQue::Count() const
+	{
+	return iCount;
+	}
+
+void TMediaRequest::Deque()
+	{
+	iPrev->iNext = iNext;
+	iNext->iPrev = iPrev;
+	}
+
+void TMediaRequest::AddBefore(TMediaRequest* x)
+	{
+	x->iPrev->iNext = this;
+	iPrev = x->iPrev;
+	iNext = x;
+	x->iPrev = this;
+	}
+
+#ifdef _DEBUG
+ 
+ /**
+  * Checks the linked list for consistency.
+  */
+ void TRequestDeltaQue::DbgCheck() const
+	 {
+	 if(iCount == 0)
+		 {
+		 __ASSERT_DEBUG(iHead == NULL, Panic(ERequestQueueCorrupt));
+		 }
+	 else
+		 {
+		 TMediaRequest* last = iHead;
+		 TInt index = iCount - 1;
+		 while(index-- > 0)
+			 {
+			 TMediaRequest* current = last->iNext;
+			 __ASSERT_DEBUG(current->iPrev == last, Panic(ERequestQueueCorrupt));
+			 __ASSERT_DEBUG(current->iTriggerWallTime >= last->iTriggerWallTime, Panic(ERequestQueueUnordered));
+			 last = current;
+			 }
+		 __ASSERT_DEBUG(last->iNext == iHead, Panic(ERequestQueueCorrupt));
+		 __ASSERT_DEBUG(iHead->iPrev == last, Panic(ERequestQueueCorrupt));
+		 }
+	 }
+ 
+#endif
+ 
+#ifdef _OMXIL_COMMON_DEBUG_TRACING_ON
+
+void TRequestDeltaQue::DbgPrint() const
+ 	{
+	TMediaRequest* x = iHead;
+	TBuf8<256> msg;
+ 	msg.Append(_L8("pending times: "));
+ 	for(TInt index = 0; index < iCount; index++)
+ 		{
+ 		if(index > 0)
+ 			{
+ 			msg.Append(_L8(", "));
+ 			}
+ 		msg.AppendFormat(_L8("%ld"), x->iTriggerWallTime);
+ 		x = x->iNext;
+ 		}
+ 	DEBUG_PRINTF(msg);
+ 	}
+ 
+#endif