omxilvideocomps/omxilclock/src/clocksupervisor.h
author hgs
Fri, 08 Oct 2010 22:09:17 +0100
changeset 0 5d29cba61097
permissions -rw-r--r--
2010wk38_02

/*
* 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:
*
*/


#ifndef CLOCKSUPERVISOR_H_
#define CLOCKSUPERVISOR_H_

#include <e32base.h>
#include <openmax/il/khronos/v1_x/OMX_Other.h>

/**
 @file
 @internalComponent
 */

// forward declaration as supervisor calls back to processing function
class COmxILClockProcessingFunction;

///////////////////////////////////////////////////////////////////////////////
// List of structures declared in this file:
// 
// TMediaRequest
// TEntryPoint
// TIndexToFunctionMapping
// CClockSupervisor
// 
///////////////////////////////////////////////////////////////////////////////
// Storage of Clock requests.
//
// The Media Update storage holds a number of request updates to send to 
// clients.
// The updates are entered in a queue based on the delta of the 
// 'expected MediaTime minus Offset' giving an absolute time (the delta). 
// This difference is then calculated by the queue implementation on insertion
// of the element.
//
// When the clock changes state it notifies the clients by sending them a state 
// change notification broadcast.
// The important thing to note here is that there is no seperate queue for 
// outgoing notifications.
// As soon as a request timer expires that request is forwarded. 
// Broadcast requests are raised and placed first on the queue so are sent 
// as and when they occur.
//
// When the scale is a change of direction (i.e. playing in the backwards 
// direction), the queue is cleared and the delta's absolute value is taken.
// 
// The delta is a TInt which matches the Ticks datatype returned by the system.
// 
// We do not expect the phone to be in operation beyond the maximum duration 
// held by the 64-bit Wall clock. Behaviour beyond this is undefined.
//
// The reason for choosing a list over an array is mainly access efficiency.
//
// 1) The insertions must be in order so as to be pulled off quickly.
//    Updates are pulled off from the front of the queue, so you would have 
//    to adjust the whole array each time.
// 2) Updates arrive at different times and can expect to be fulfilled prior 
//    to earlier updates, again the list would need to be adjusted after the 
//    point of insertion.
// 
// Clients are attached via ports and the notifications are sent to them via 
// these ports
//
///////////////////////////////////////////////////////////////////////////////
// Clock functionality is invoked both externally & internally.
//
// Based on the OMX IL 1.1.1 standard, we route these incoming external 
// requests to their relevant behaviour.
//
// This Clock component is intended to be created per use case.
// That is for every use case that requires synchronisation.
// This could result in several clocks being created, each with an audio and/or
// video as an input, and each with a high-priority clock thread.
// At most it is considered that there should only be a few of these components
// in existance at any one time, as mobile devices should not really require it.
// However, saying this, it should be factored into the design. 
// At this stage it is envisioned that any clock component should reduce its 
// timer events to once at just after the 32-bit wraparound when its 
// application is not in focus. 
// This is due to the time being reported to us in a 32-bit value, whilst our 
// Wall clock is 64-bits. 
// To avoid any wraparound and losing time by not regulary updating the upper 
// word of our wall clock, we need to somehow check the system clock at this 
// inteval. This is not a problem during normal operation but can be when the 
// application is Paused or goes out of focus.
//
// NOTE: Assumes OMX_SKIP64BIT is not set when building OMX CORE
///////////////////////////////////////////////////////////////////////////////


/**
 * 
 *
 */
class TMediaRequest
	{
public:
	TInt64 iMediaTime;
	TInt64 iOffset;
	TInt64 iTriggerWallTime;		// == iWallTimeAtMediaTime - iOffset
	TInt iPortIndex;
	TAny* iClientPrivate;
	
	inline void Deque();
	inline void AddBefore(TMediaRequest*);
	
public:
	TMediaRequest *iPrev;
	TMediaRequest *iNext;
	};

/**
 * 
 *
 */
class TMediaTimeContext
	{
public:
	TMediaTimeContext(): iScaleQ16 (1 << 16), iInverseScaleQ16 (1 << 16)
		{/*do nothing*/};

	void SetScaleQ16(TInt32 aScaleQ16, TInt64 aWallTimeNow);
public:
	TInt64 iWallTimeBase;
	TInt64 iMediaTimeBase;
	
	/**
	 * Scale ranges map to modes of playback.
	 * A Q16 value relative to a 1X forward advancement of the media clock.
	 */
	TInt32 iScaleQ16;
	
	/**
	 * The reciprocal of iScaleQ16 (i.e. 1 / iScaleQ16), but adjusted for the
	 * Q16 format.
	 * 
	 * It therefore has the value 2^32 / iScaleQ16.
	 * 
	 * If iScaleQ16 == 0, this field takes the value KMaxTInt (2^31 - 1)
	 * 
	 * If magnitude of iInverseScaleQ16 would be too large for a signed 32-bit
	 * value, the value is clipped to KMaxTInt or KMinTInt (-2^31 or
	 * (2^31 - 1)). This can only happen if iScaleQ16 == +/- 1.
	 */
	TInt32 iInverseScaleQ16;
	};

/**
 * 
 *
 */
class TRequestDeltaQue
	{
public:
	TRequestDeltaQue();
	
	void Add(TMediaRequest* aElement, TInt64 aDelta);
	TMediaRequest* RemoveFirst();
	TBool FirstDelta(TInt64& aDelta) const;
	void RecalculateAndReorder(TMediaTimeContext& aMTC);
	TBool IsEmpty() const;
	TUint Count() const;
	
private:
	TBool InsertBeforeFoundPosition(TInt64 aDelta, TMediaRequest*& aItem) const;

#ifdef _DEBUG
	void DbgCheck() const;
#endif
#ifdef _OMXIL_COMMON_DEBUG_TRACING_ON
	void DbgPrint() const;
#endif

private:
	TMediaRequest* iHead;
	TInt iCount;
	};


/**
 * 
 *
 */
class CClockSupervisor : public CBase
	{
public:
#ifdef STABILITY_TEST_WRAPPER
	friend class CStabilityTestWrapper;
	friend class CStabilityTestNegativeWrapper;
#endif
friend class CClockThreadNotifier;	
public:
	static CClockSupervisor* NewL(COmxILClockProcessingFunction& aCallbacks);
	~CClockSupervisor();

	/**
	 * Defines how a request arrived at the clock component
	 */
	enum TEntryPoint
		{
		EGetConfig,
		ESetConfig
		};
	
	// Producer function
	// Invoked via clients of this component, i.e. GetConfig() and SetConfig()
	OMX_ERRORTYPE ProduceRequest(OMX_INDEXTYPE aIndex, TEntryPoint aEntryPoint,
								 TAny* aPassedStructPtr);
								 
	const OMX_TIME_CONFIG_CLOCKSTATETYPE GetMediaClockState() const
		{
		return iMediaClockState;
		}

private:
	CClockSupervisor(COmxILClockProcessingFunction& aProcessingFunction);
	void ConstructL();

private:
	//////////////
	// This section describes the Clock thread related functions
	//////////////
	
	// The timing thread's entry point
	static TInt ThreadEntryPoint(TAny* aPtr);
	void RunTimingThreadL();
	
private:
	//////////////
	// This section describes the request handler functions
	//////////////

	// From the Audio/Video feeds
	OMX_ERRORTYPE HandleUpdateAudioReference(TEntryPoint aEntry, OMX_PTR aPassedStructPtr);
	OMX_ERRORTYPE HandleUpdateVideoReference(TEntryPoint aEntry, OMX_PTR aPassedStructPtr);
	
	// From the OMX component
	OMX_ERRORTYPE HandleSetPortClientStartTime(TEntryPoint aEntry, OMX_PTR aPassedStructPtr);
	OMX_ERRORTYPE HandleSubmitMediaTimeRequest(TEntryPoint aEntry, OMX_PTR aPassedStructPtr);

	// From the IL client
	OMX_ERRORTYPE HandleGetSetTimeScale(TEntryPoint aEntry, OMX_PTR aPassedStructPtr);
	OMX_ERRORTYPE HandleGetSetSeekMode(TEntryPoint aEntry, OMX_PTR aPassedStructPtr);
	OMX_ERRORTYPE HandleGetSetClockState(TEntryPoint aEntry, OMX_PTR aPassedStructPtr);
	OMX_ERRORTYPE HandleGetSetActiveRefClock(TEntryPoint aEntry, OMX_PTR aPassedStructPtr);

	// Called from either IL client or IL components
	OMX_ERRORTYPE HandleQueryCurrentWallTime(TEntryPoint aEntry, OMX_PTR aPassedStructPtr);
	OMX_ERRORTYPE HandleQueryCurrentMediaTime(TEntryPoint aEntry, OMX_PTR aPassedStructPtr);
	
private:
	//////////////
	// This section describes the Clock components internal house-keeping 
	// routines
	//////////////

	// Perform actions related to placing the clock into the Stopped state
	void DoTransitionToStoppedState();
	
	// Perform actions related to placing the clock into the Running state
	void DoTransitionToRunningState();
	
	// Perform actions related to placing the clock into the Waiting state
	void DoTransitionToWaitingState(OMX_U32 nWaitMask);
	
	// Update the tick counter, generating higher 32 bits
	void UpdateWallTicks();
	
	// Consumer function
	// Invoked at initialisation and runs in event loop.
	TInt ConsumeRequests();
	
	// The timer loop to enable us to wake up on heart beat or requests
	void TimerLoop();

	// obtains the most appropriate start time based on scale (direction)
	void CalculateStartTime();
	
	// reports whether all clients have reported their start times
	TBool AllStartTimesReported();
	
	TInt64 WallTime();
	
	void UpdateMediaTime(TInt64 aMediaTime);
	void BroadcastUpdate(const OMX_TIME_MEDIATIMETYPE& aUpdate);
	
	// reports error when clock thread panics
	
	void ReportClockThreadPanic();
	
private:
	//////////////
	// This section describes the clock's miscellaneous structures
	//////////////

	/**
	 * State of the clock component's media clock:
	 */
	OMX_TIME_CONFIG_CLOCKSTATETYPE iMediaClockState;

	/**
	 * Choice of the clock component's reference clock:
	 */
	OMX_TIME_REFCLOCKTYPE iActiveRefClock;
	
	/**
	 * Array of clients' start times:
	 */
	RArray<TInt64> iStartTimes;
	TUint iStartTimesSet;			/* bit mask representing which elements of
	                                   the start time array are valid */

private:
	//////////////
	// This section describes the Clock component's routing structures
	//////////////
	
	/*
	 * typedef for function to handle incoming requests
	 */
	typedef OMX_ERRORTYPE (CClockSupervisor::*FunctionPtr)(TEntryPoint, OMX_PTR);

	/*
	 * Function jump table. Note that this is declared in class scope so that the table
	 * definition has access to private methods.
	 */
	static const FunctionPtr iJumpTable[];
	
private:
	//////////////
	// This section describes the Clock component's Timer Control
	//////////////

	/**
	 * Holds the current Wall Clock. The duration of a 'tick' is platform specific.
	 * This needs to be 4-byte aligned to allow for atomic access.
	 */
	OMX_TICKS iWallTicks;
	
	/**
	 * Encapsulates the relationship between wall time and media time.
	 */
	TMediaTimeContext iMtc;
	
	// conversion factors from wall 'ticks' to microseconds
	TInt iMicroConvNum;
	TInt iMicroConvDen;
	TInt iMicroConvShift;
	
	// maximum time between calls to WallTime() - avoids 32-bit counter overflow
	TInt iHeartbeatTimerInterval;
	
	// on some platforms User::FastCounter() counts backwards
	TBool iSystemClockReversed;
	
private:
	//////////////
	// This section describes the Clock component's thread and other resources
	//////////////

	/**
	 * This Clock component object needs to run in a high priority thread as it 
	 * represents the OMX component timings
	 */
	RThread iThread;
	
	/**
	 * Thread access control Mutex, 
	 * used to control updating of the components requests by mutiple threads
	 */
	RMutex iQueMutex;

	/**
	 * Event timer to wake up this object to complete fulfillment of a request
	 * This handle belongs to the High-Pri thread
	 */
	RTimer iTimer;
	
	/**
	 * Flags to control timing thread exit/destroy.
	 */
	TBool iThreadStarted;
	TBool iThreadRunning;
	
	/**
	 * Complete this status on the timing thread to interrupt the timer sleep.
	 * Timer itself can't be cancelled since timer handle is local to timing
	 * thread.
	 */
	TRequestStatus iCancelStatus;

private:
	//////////////
	// This section describes the Clock component's request management 
	// infrastructure
	//////////////

	/**
	 * Pending Media Time Requests
	 */
	TRequestDeltaQue iPendingRequestQue;

	/**
	 * The free queue header.
	 * Items ared remove from here and placed on the pending queue.
	 */
	TRequestDeltaQue iFreeRequestQue;

	/**
	 * memory block where request list nodes are allocated
	 */
	TMediaRequest* iRequestBlock;

	/**
	 * Max number of request list nodes allocated
	 */
	const TUint iMaxRequests;
	
	COmxILClockProcessingFunction& iProcessingFunction;
	}; // class CClockSupervisor


#endif // CLOCKSUPERVISOR_H_