0
|
1 |
/*
|
|
2 |
* Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
|
|
3 |
* All rights reserved.
|
|
4 |
* This component and the accompanying materials are made available
|
|
5 |
* under the terms of "Eclipse Public License v1.0"
|
|
6 |
* which accompanies this distribution, and is available
|
|
7 |
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
|
|
8 |
*
|
|
9 |
* Initial Contributors:
|
|
10 |
* Nokia Corporation - initial contribution.
|
|
11 |
*
|
|
12 |
* Contributors:
|
|
13 |
*
|
|
14 |
* Description:
|
|
15 |
*
|
|
16 |
*/
|
|
17 |
|
|
18 |
|
|
19 |
#ifndef CLOCKSUPERVISOR_H_
|
|
20 |
#define CLOCKSUPERVISOR_H_
|
|
21 |
|
|
22 |
#include <e32base.h>
|
|
23 |
#include <openmax/il/khronos/v1_x/OMX_Other.h>
|
|
24 |
|
|
25 |
/**
|
|
26 |
@file
|
|
27 |
@internalComponent
|
|
28 |
*/
|
|
29 |
|
|
30 |
// forward declaration as supervisor calls back to processing function
|
|
31 |
class COmxILClockProcessingFunction;
|
|
32 |
|
|
33 |
///////////////////////////////////////////////////////////////////////////////
|
|
34 |
// List of structures declared in this file:
|
|
35 |
//
|
|
36 |
// TMediaRequest
|
|
37 |
// TEntryPoint
|
|
38 |
// TIndexToFunctionMapping
|
|
39 |
// CClockSupervisor
|
|
40 |
//
|
|
41 |
///////////////////////////////////////////////////////////////////////////////
|
|
42 |
// Storage of Clock requests.
|
|
43 |
//
|
|
44 |
// The Media Update storage holds a number of request updates to send to
|
|
45 |
// clients.
|
|
46 |
// The updates are entered in a queue based on the delta of the
|
|
47 |
// 'expected MediaTime minus Offset' giving an absolute time (the delta).
|
|
48 |
// This difference is then calculated by the queue implementation on insertion
|
|
49 |
// of the element.
|
|
50 |
//
|
|
51 |
// When the clock changes state it notifies the clients by sending them a state
|
|
52 |
// change notification broadcast.
|
|
53 |
// The important thing to note here is that there is no seperate queue for
|
|
54 |
// outgoing notifications.
|
|
55 |
// As soon as a request timer expires that request is forwarded.
|
|
56 |
// Broadcast requests are raised and placed first on the queue so are sent
|
|
57 |
// as and when they occur.
|
|
58 |
//
|
|
59 |
// When the scale is a change of direction (i.e. playing in the backwards
|
|
60 |
// direction), the queue is cleared and the delta's absolute value is taken.
|
|
61 |
//
|
|
62 |
// The delta is a TInt which matches the Ticks datatype returned by the system.
|
|
63 |
//
|
|
64 |
// We do not expect the phone to be in operation beyond the maximum duration
|
|
65 |
// held by the 64-bit Wall clock. Behaviour beyond this is undefined.
|
|
66 |
//
|
|
67 |
// The reason for choosing a list over an array is mainly access efficiency.
|
|
68 |
//
|
|
69 |
// 1) The insertions must be in order so as to be pulled off quickly.
|
|
70 |
// Updates are pulled off from the front of the queue, so you would have
|
|
71 |
// to adjust the whole array each time.
|
|
72 |
// 2) Updates arrive at different times and can expect to be fulfilled prior
|
|
73 |
// to earlier updates, again the list would need to be adjusted after the
|
|
74 |
// point of insertion.
|
|
75 |
//
|
|
76 |
// Clients are attached via ports and the notifications are sent to them via
|
|
77 |
// these ports
|
|
78 |
//
|
|
79 |
///////////////////////////////////////////////////////////////////////////////
|
|
80 |
// Clock functionality is invoked both externally & internally.
|
|
81 |
//
|
|
82 |
// Based on the OMX IL 1.1.1 standard, we route these incoming external
|
|
83 |
// requests to their relevant behaviour.
|
|
84 |
//
|
|
85 |
// This Clock component is intended to be created per use case.
|
|
86 |
// That is for every use case that requires synchronisation.
|
|
87 |
// This could result in several clocks being created, each with an audio and/or
|
|
88 |
// video as an input, and each with a high-priority clock thread.
|
|
89 |
// At most it is considered that there should only be a few of these components
|
|
90 |
// in existance at any one time, as mobile devices should not really require it.
|
|
91 |
// However, saying this, it should be factored into the design.
|
|
92 |
// At this stage it is envisioned that any clock component should reduce its
|
|
93 |
// timer events to once at just after the 32-bit wraparound when its
|
|
94 |
// application is not in focus.
|
|
95 |
// This is due to the time being reported to us in a 32-bit value, whilst our
|
|
96 |
// Wall clock is 64-bits.
|
|
97 |
// To avoid any wraparound and losing time by not regulary updating the upper
|
|
98 |
// word of our wall clock, we need to somehow check the system clock at this
|
|
99 |
// inteval. This is not a problem during normal operation but can be when the
|
|
100 |
// application is Paused or goes out of focus.
|
|
101 |
//
|
|
102 |
// NOTE: Assumes OMX_SKIP64BIT is not set when building OMX CORE
|
|
103 |
///////////////////////////////////////////////////////////////////////////////
|
|
104 |
|
|
105 |
|
|
106 |
/**
|
|
107 |
*
|
|
108 |
*
|
|
109 |
*/
|
|
110 |
class TMediaRequest
|
|
111 |
{
|
|
112 |
public:
|
|
113 |
TInt64 iMediaTime;
|
|
114 |
TInt64 iOffset;
|
|
115 |
TInt64 iTriggerWallTime; // == iWallTimeAtMediaTime - iOffset
|
|
116 |
TInt iPortIndex;
|
|
117 |
TAny* iClientPrivate;
|
|
118 |
|
|
119 |
inline void Deque();
|
|
120 |
inline void AddBefore(TMediaRequest*);
|
|
121 |
|
|
122 |
public:
|
|
123 |
TMediaRequest *iPrev;
|
|
124 |
TMediaRequest *iNext;
|
|
125 |
};
|
|
126 |
|
|
127 |
/**
|
|
128 |
*
|
|
129 |
*
|
|
130 |
*/
|
|
131 |
class TMediaTimeContext
|
|
132 |
{
|
|
133 |
public:
|
|
134 |
TMediaTimeContext(): iScaleQ16 (1 << 16), iInverseScaleQ16 (1 << 16)
|
|
135 |
{/*do nothing*/};
|
|
136 |
|
|
137 |
void SetScaleQ16(TInt32 aScaleQ16, TInt64 aWallTimeNow);
|
|
138 |
public:
|
|
139 |
TInt64 iWallTimeBase;
|
|
140 |
TInt64 iMediaTimeBase;
|
|
141 |
|
|
142 |
/**
|
|
143 |
* Scale ranges map to modes of playback.
|
|
144 |
* A Q16 value relative to a 1X forward advancement of the media clock.
|
|
145 |
*/
|
|
146 |
TInt32 iScaleQ16;
|
|
147 |
|
|
148 |
/**
|
|
149 |
* The reciprocal of iScaleQ16 (i.e. 1 / iScaleQ16), but adjusted for the
|
|
150 |
* Q16 format.
|
|
151 |
*
|
|
152 |
* It therefore has the value 2^32 / iScaleQ16.
|
|
153 |
*
|
|
154 |
* If iScaleQ16 == 0, this field takes the value KMaxTInt (2^31 - 1)
|
|
155 |
*
|
|
156 |
* If magnitude of iInverseScaleQ16 would be too large for a signed 32-bit
|
|
157 |
* value, the value is clipped to KMaxTInt or KMinTInt (-2^31 or
|
|
158 |
* (2^31 - 1)). This can only happen if iScaleQ16 == +/- 1.
|
|
159 |
*/
|
|
160 |
TInt32 iInverseScaleQ16;
|
|
161 |
};
|
|
162 |
|
|
163 |
/**
|
|
164 |
*
|
|
165 |
*
|
|
166 |
*/
|
|
167 |
class TRequestDeltaQue
|
|
168 |
{
|
|
169 |
public:
|
|
170 |
TRequestDeltaQue();
|
|
171 |
|
|
172 |
void Add(TMediaRequest* aElement, TInt64 aDelta);
|
|
173 |
TMediaRequest* RemoveFirst();
|
|
174 |
TBool FirstDelta(TInt64& aDelta) const;
|
|
175 |
void RecalculateAndReorder(TMediaTimeContext& aMTC);
|
|
176 |
TBool IsEmpty() const;
|
|
177 |
TUint Count() const;
|
|
178 |
|
|
179 |
private:
|
|
180 |
TBool InsertBeforeFoundPosition(TInt64 aDelta, TMediaRequest*& aItem) const;
|
|
181 |
|
|
182 |
#ifdef _DEBUG
|
|
183 |
void DbgCheck() const;
|
|
184 |
#endif
|
|
185 |
#ifdef _OMXIL_COMMON_DEBUG_TRACING_ON
|
|
186 |
void DbgPrint() const;
|
|
187 |
#endif
|
|
188 |
|
|
189 |
private:
|
|
190 |
TMediaRequest* iHead;
|
|
191 |
TInt iCount;
|
|
192 |
};
|
|
193 |
|
|
194 |
|
|
195 |
/**
|
|
196 |
*
|
|
197 |
*
|
|
198 |
*/
|
|
199 |
class CClockSupervisor : public CBase
|
|
200 |
{
|
|
201 |
public:
|
|
202 |
#ifdef STABILITY_TEST_WRAPPER
|
|
203 |
friend class CStabilityTestWrapper;
|
|
204 |
friend class CStabilityTestNegativeWrapper;
|
|
205 |
#endif
|
|
206 |
friend class CClockThreadNotifier;
|
|
207 |
public:
|
|
208 |
static CClockSupervisor* NewL(COmxILClockProcessingFunction& aCallbacks);
|
|
209 |
~CClockSupervisor();
|
|
210 |
|
|
211 |
/**
|
|
212 |
* Defines how a request arrived at the clock component
|
|
213 |
*/
|
|
214 |
enum TEntryPoint
|
|
215 |
{
|
|
216 |
EGetConfig,
|
|
217 |
ESetConfig
|
|
218 |
};
|
|
219 |
|
|
220 |
// Producer function
|
|
221 |
// Invoked via clients of this component, i.e. GetConfig() and SetConfig()
|
|
222 |
OMX_ERRORTYPE ProduceRequest(OMX_INDEXTYPE aIndex, TEntryPoint aEntryPoint,
|
|
223 |
TAny* aPassedStructPtr);
|
|
224 |
|
|
225 |
const OMX_TIME_CONFIG_CLOCKSTATETYPE GetMediaClockState() const
|
|
226 |
{
|
|
227 |
return iMediaClockState;
|
|
228 |
}
|
|
229 |
|
|
230 |
private:
|
|
231 |
CClockSupervisor(COmxILClockProcessingFunction& aProcessingFunction);
|
|
232 |
void ConstructL();
|
|
233 |
|
|
234 |
private:
|
|
235 |
//////////////
|
|
236 |
// This section describes the Clock thread related functions
|
|
237 |
//////////////
|
|
238 |
|
|
239 |
// The timing thread's entry point
|
|
240 |
static TInt ThreadEntryPoint(TAny* aPtr);
|
|
241 |
void RunTimingThreadL();
|
|
242 |
|
|
243 |
private:
|
|
244 |
//////////////
|
|
245 |
// This section describes the request handler functions
|
|
246 |
//////////////
|
|
247 |
|
|
248 |
// From the Audio/Video feeds
|
|
249 |
OMX_ERRORTYPE HandleUpdateAudioReference(TEntryPoint aEntry, OMX_PTR aPassedStructPtr);
|
|
250 |
OMX_ERRORTYPE HandleUpdateVideoReference(TEntryPoint aEntry, OMX_PTR aPassedStructPtr);
|
|
251 |
|
|
252 |
// From the OMX component
|
|
253 |
OMX_ERRORTYPE HandleSetPortClientStartTime(TEntryPoint aEntry, OMX_PTR aPassedStructPtr);
|
|
254 |
OMX_ERRORTYPE HandleSubmitMediaTimeRequest(TEntryPoint aEntry, OMX_PTR aPassedStructPtr);
|
|
255 |
|
|
256 |
// From the IL client
|
|
257 |
OMX_ERRORTYPE HandleGetSetTimeScale(TEntryPoint aEntry, OMX_PTR aPassedStructPtr);
|
|
258 |
OMX_ERRORTYPE HandleGetSetSeekMode(TEntryPoint aEntry, OMX_PTR aPassedStructPtr);
|
|
259 |
OMX_ERRORTYPE HandleGetSetClockState(TEntryPoint aEntry, OMX_PTR aPassedStructPtr);
|
|
260 |
OMX_ERRORTYPE HandleGetSetActiveRefClock(TEntryPoint aEntry, OMX_PTR aPassedStructPtr);
|
|
261 |
|
|
262 |
// Called from either IL client or IL components
|
|
263 |
OMX_ERRORTYPE HandleQueryCurrentWallTime(TEntryPoint aEntry, OMX_PTR aPassedStructPtr);
|
|
264 |
OMX_ERRORTYPE HandleQueryCurrentMediaTime(TEntryPoint aEntry, OMX_PTR aPassedStructPtr);
|
|
265 |
|
|
266 |
private:
|
|
267 |
//////////////
|
|
268 |
// This section describes the Clock components internal house-keeping
|
|
269 |
// routines
|
|
270 |
//////////////
|
|
271 |
|
|
272 |
// Perform actions related to placing the clock into the Stopped state
|
|
273 |
void DoTransitionToStoppedState();
|
|
274 |
|
|
275 |
// Perform actions related to placing the clock into the Running state
|
|
276 |
void DoTransitionToRunningState();
|
|
277 |
|
|
278 |
// Perform actions related to placing the clock into the Waiting state
|
|
279 |
void DoTransitionToWaitingState(OMX_U32 nWaitMask);
|
|
280 |
|
|
281 |
// Update the tick counter, generating higher 32 bits
|
|
282 |
void UpdateWallTicks();
|
|
283 |
|
|
284 |
// Consumer function
|
|
285 |
// Invoked at initialisation and runs in event loop.
|
|
286 |
TInt ConsumeRequests();
|
|
287 |
|
|
288 |
// The timer loop to enable us to wake up on heart beat or requests
|
|
289 |
void TimerLoop();
|
|
290 |
|
|
291 |
// obtains the most appropriate start time based on scale (direction)
|
|
292 |
void CalculateStartTime();
|
|
293 |
|
|
294 |
// reports whether all clients have reported their start times
|
|
295 |
TBool AllStartTimesReported();
|
|
296 |
|
|
297 |
TInt64 WallTime();
|
|
298 |
|
|
299 |
void UpdateMediaTime(TInt64 aMediaTime);
|
|
300 |
void BroadcastUpdate(const OMX_TIME_MEDIATIMETYPE& aUpdate);
|
|
301 |
|
|
302 |
// reports error when clock thread panics
|
|
303 |
|
|
304 |
void ReportClockThreadPanic();
|
|
305 |
|
|
306 |
private:
|
|
307 |
//////////////
|
|
308 |
// This section describes the clock's miscellaneous structures
|
|
309 |
//////////////
|
|
310 |
|
|
311 |
/**
|
|
312 |
* State of the clock component's media clock:
|
|
313 |
*/
|
|
314 |
OMX_TIME_CONFIG_CLOCKSTATETYPE iMediaClockState;
|
|
315 |
|
|
316 |
/**
|
|
317 |
* Choice of the clock component's reference clock:
|
|
318 |
*/
|
|
319 |
OMX_TIME_REFCLOCKTYPE iActiveRefClock;
|
|
320 |
|
|
321 |
/**
|
|
322 |
* Array of clients' start times:
|
|
323 |
*/
|
|
324 |
RArray<TInt64> iStartTimes;
|
|
325 |
TUint iStartTimesSet; /* bit mask representing which elements of
|
|
326 |
the start time array are valid */
|
|
327 |
|
|
328 |
private:
|
|
329 |
//////////////
|
|
330 |
// This section describes the Clock component's routing structures
|
|
331 |
//////////////
|
|
332 |
|
|
333 |
/*
|
|
334 |
* typedef for function to handle incoming requests
|
|
335 |
*/
|
|
336 |
typedef OMX_ERRORTYPE (CClockSupervisor::*FunctionPtr)(TEntryPoint, OMX_PTR);
|
|
337 |
|
|
338 |
/*
|
|
339 |
* Function jump table. Note that this is declared in class scope so that the table
|
|
340 |
* definition has access to private methods.
|
|
341 |
*/
|
|
342 |
static const FunctionPtr iJumpTable[];
|
|
343 |
|
|
344 |
private:
|
|
345 |
//////////////
|
|
346 |
// This section describes the Clock component's Timer Control
|
|
347 |
//////////////
|
|
348 |
|
|
349 |
/**
|
|
350 |
* Holds the current Wall Clock. The duration of a 'tick' is platform specific.
|
|
351 |
* This needs to be 4-byte aligned to allow for atomic access.
|
|
352 |
*/
|
|
353 |
OMX_TICKS iWallTicks;
|
|
354 |
|
|
355 |
/**
|
|
356 |
* Encapsulates the relationship between wall time and media time.
|
|
357 |
*/
|
|
358 |
TMediaTimeContext iMtc;
|
|
359 |
|
|
360 |
// conversion factors from wall 'ticks' to microseconds
|
|
361 |
TInt iMicroConvNum;
|
|
362 |
TInt iMicroConvDen;
|
|
363 |
TInt iMicroConvShift;
|
|
364 |
|
|
365 |
// maximum time between calls to WallTime() - avoids 32-bit counter overflow
|
|
366 |
TInt iHeartbeatTimerInterval;
|
|
367 |
|
|
368 |
// on some platforms User::FastCounter() counts backwards
|
|
369 |
TBool iSystemClockReversed;
|
|
370 |
|
|
371 |
private:
|
|
372 |
//////////////
|
|
373 |
// This section describes the Clock component's thread and other resources
|
|
374 |
//////////////
|
|
375 |
|
|
376 |
/**
|
|
377 |
* This Clock component object needs to run in a high priority thread as it
|
|
378 |
* represents the OMX component timings
|
|
379 |
*/
|
|
380 |
RThread iThread;
|
|
381 |
|
|
382 |
/**
|
|
383 |
* Thread access control Mutex,
|
|
384 |
* used to control updating of the components requests by mutiple threads
|
|
385 |
*/
|
|
386 |
RMutex iQueMutex;
|
|
387 |
|
|
388 |
/**
|
|
389 |
* Event timer to wake up this object to complete fulfillment of a request
|
|
390 |
* This handle belongs to the High-Pri thread
|
|
391 |
*/
|
|
392 |
RTimer iTimer;
|
|
393 |
|
|
394 |
/**
|
|
395 |
* Flags to control timing thread exit/destroy.
|
|
396 |
*/
|
|
397 |
TBool iThreadStarted;
|
|
398 |
TBool iThreadRunning;
|
|
399 |
|
|
400 |
/**
|
|
401 |
* Complete this status on the timing thread to interrupt the timer sleep.
|
|
402 |
* Timer itself can't be cancelled since timer handle is local to timing
|
|
403 |
* thread.
|
|
404 |
*/
|
|
405 |
TRequestStatus iCancelStatus;
|
|
406 |
|
|
407 |
private:
|
|
408 |
//////////////
|
|
409 |
// This section describes the Clock component's request management
|
|
410 |
// infrastructure
|
|
411 |
//////////////
|
|
412 |
|
|
413 |
/**
|
|
414 |
* Pending Media Time Requests
|
|
415 |
*/
|
|
416 |
TRequestDeltaQue iPendingRequestQue;
|
|
417 |
|
|
418 |
/**
|
|
419 |
* The free queue header.
|
|
420 |
* Items ared remove from here and placed on the pending queue.
|
|
421 |
*/
|
|
422 |
TRequestDeltaQue iFreeRequestQue;
|
|
423 |
|
|
424 |
/**
|
|
425 |
* memory block where request list nodes are allocated
|
|
426 |
*/
|
|
427 |
TMediaRequest* iRequestBlock;
|
|
428 |
|
|
429 |
/**
|
|
430 |
* Max number of request list nodes allocated
|
|
431 |
*/
|
|
432 |
const TUint iMaxRequests;
|
|
433 |
|
|
434 |
COmxILClockProcessingFunction& iProcessingFunction;
|
|
435 |
}; // class CClockSupervisor
|
|
436 |
|
|
437 |
|
|
438 |
#endif // CLOCKSUPERVISOR_H_
|