|
1 // Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // |
|
15 |
|
16 /** |
|
17 @file |
|
18 @internalComponent |
|
19 */ |
|
20 |
|
21 #include <hal.h> |
|
22 #include <graphics/suerror.h> |
|
23 #include "rendererrelay.h" |
|
24 #include "buflistener.h" |
|
25 #include "buffermanager.h" |
|
26 #include "videoframebuffer.h" |
|
27 #include "rendererutil.h" |
|
28 #include "renderertimer.h" |
|
29 |
|
30 /** static factory contruction */ |
|
31 CRendererRelay* CRendererRelay::NewL(MVideoRendererObserver& aObserver) |
|
32 { |
|
33 CRendererRelay* self = new (ELeave) CRendererRelay(aObserver); |
|
34 CleanupStack::PushL(self); |
|
35 self->ConstructL(); |
|
36 CleanupStack::Pop(self); |
|
37 return self; |
|
38 } |
|
39 |
|
40 CRendererRelay::CRendererRelay(MVideoRendererObserver& aObserver) |
|
41 :iObserver(aObserver), |
|
42 iBufAvailListeners(CBufAvailListener::iOffset), |
|
43 iAvailListenersIter(iBufAvailListeners) |
|
44 { |
|
45 } |
|
46 |
|
47 void CRendererRelay::ConstructL() |
|
48 { |
|
49 User::LeaveIfError(iSurfaceUpdateSession.Connect()); |
|
50 User::LeaveIfError(HAL::Get(HAL::EFastCounterFrequency, iFastCounterFrequency)); |
|
51 User::LeaveIfError(HAL::Get(HAL::EFastCounterCountsUp, iFastCounterCountsUp)); |
|
52 } |
|
53 |
|
54 /** Destructor */ |
|
55 CRendererRelay::~CRendererRelay() |
|
56 { |
|
57 delete iDisplayListener; |
|
58 |
|
59 iAvailListenersIter.SetToFirst(); |
|
60 CBufAvailListener* listener = NULL; |
|
61 while ((listener = iAvailListenersIter++) != NULL) |
|
62 { |
|
63 listener->iDblQueLink.Deque(); |
|
64 delete listener; |
|
65 } |
|
66 |
|
67 iSurfaceUpdateSession.Close(); |
|
68 } |
|
69 |
|
70 /** Update buffer manager pointer in this class and listeners */ |
|
71 void CRendererRelay::SetBufferManager(CRendererBufferManager* aBufManager) |
|
72 { |
|
73 iBufManager = aBufManager; |
|
74 |
|
75 // change buffer manager pointer for listeners |
|
76 iAvailListenersIter.SetToFirst(); |
|
77 CBufAvailListener* listener = NULL; |
|
78 while ((listener = iAvailListenersIter++) != NULL) |
|
79 { |
|
80 listener->SetBufferManager(aBufManager); |
|
81 |
|
82 if (!aBufManager && listener->IsAdded()) |
|
83 { |
|
84 listener->Deque(); |
|
85 } |
|
86 else if (aBufManager && !listener->IsAdded()) |
|
87 { |
|
88 CActiveScheduler::Add(listener); |
|
89 } |
|
90 } |
|
91 } |
|
92 |
|
93 /** Return the next unused CBufAvailListener. */ |
|
94 CBufAvailListener* CRendererRelay::BufAvailListener() |
|
95 { |
|
96 CBufAvailListener* listener = NULL; |
|
97 iAvailListenersIter.SetToFirst(); |
|
98 while ((listener = iAvailListenersIter++) != NULL) |
|
99 { |
|
100 if (listener->IsActive() == EFalse) |
|
101 { |
|
102 // Move to end so that the next search is a bit faster |
|
103 listener->iDblQueLink.Deque(); |
|
104 iBufAvailListeners.AddLast(*listener); |
|
105 return listener; |
|
106 } |
|
107 } |
|
108 |
|
109 // Should not reach here as the number of listeners is same as number of buffer |
|
110 __ASSERT_DEBUG(EFalse, User::Panic(_L("CRR::BufAvailListener"), KErrUnknown)); |
|
111 return listener; |
|
112 } |
|
113 |
|
114 /** Set update parameter and create listeners */ |
|
115 void CRendererRelay::PrepareL(const TSurfaceId& aSurfaceId, TInt aNumBuffers, TRequestStatus* /*aRequestStatus*/) |
|
116 { |
|
117 iUpdateSubmitted = EFalse; |
|
118 iSurfaceId = aSurfaceId; |
|
119 |
|
120 if (!iDisplayListener) |
|
121 { |
|
122 iDisplayListener = CBufDisplayListener::NewL(iObserver, iSurfaceUpdateSession, *this, iFastCounterFrequency, iFastCounterCountsUp); |
|
123 } |
|
124 |
|
125 TInt numListeners = 0; |
|
126 iAvailListenersIter.SetToFirst(); |
|
127 while (iAvailListenersIter++ != NULL) |
|
128 { |
|
129 numListeners++; |
|
130 } |
|
131 |
|
132 CBufAvailListener* listener = NULL; |
|
133 // create new listeners if there are more numBuffers than listeners |
|
134 for (TInt i = numListeners; i < aNumBuffers; ++i) |
|
135 { |
|
136 listener = CBufAvailListener::NewL(iObserver, iSurfaceUpdateSession); |
|
137 iBufAvailListeners.AddFirst(*listener); |
|
138 } |
|
139 } |
|
140 |
|
141 /** Handle update buffer request in non-timed mode */ |
|
142 void CRendererRelay::UpdateBuffer(TVideoFrameBuffer* aBuffer, const TTime& aPresentationTime, TRequestStatus* /* aRequestStatus */) |
|
143 { |
|
144 __ASSERT_DEBUG(iBufManager != NULL, User::Panic(_L("CRR::UpdateBuffer"), KErrNotReady)); |
|
145 |
|
146 iBufManager->UpdateBuffer(aBuffer, aPresentationTime); |
|
147 |
|
148 if (iUpdateSubmitted == EFalse) |
|
149 { |
|
150 // an update haven't been submitted, submit one now |
|
151 SubmitBuffer(); |
|
152 } |
|
153 } |
|
154 |
|
155 /** Callback from display listener that a buffer has been displayed or skipped*/ |
|
156 void CRendererRelay::BufferDisplayed(TBool aDisplayed, TInt64 aDelay) |
|
157 { |
|
158 DEBUGPRINT3(_L("CRendererRelay::BufferDisplayed entered aDisplayed=%d, aDelay=%Ld"), aDisplayed, aDelay); |
|
159 |
|
160 iUpdateSubmitted = EFalse; |
|
161 |
|
162 if (iRendererTimer) |
|
163 { |
|
164 // rendering in timed mode |
|
165 if (aDisplayed) |
|
166 { |
|
167 // update delay value |
|
168 iDelay = aDelay; |
|
169 } |
|
170 SubmitBufferTimed(); |
|
171 } |
|
172 else |
|
173 { |
|
174 // rendering in non timed mode |
|
175 SubmitBuffer(); |
|
176 } |
|
177 } |
|
178 |
|
179 /** Submit the next buffer that is waiting to be submitted in non-timed mode*/ |
|
180 void CRendererRelay::SubmitBuffer() |
|
181 { |
|
182 DEBUGPRINT1(_L("CRendererRelay::SubmitBuffer entered")); |
|
183 __ASSERT_DEBUG(iBufManager != NULL, User::Panic(_L("CRR::SubmitBuffer"), KErrCorrupt)); |
|
184 __ASSERT_DEBUG(iUpdateSubmitted == EFalse, User::Panic(_L("CRR::SubmitBuffer"), KErrGeneral)); |
|
185 |
|
186 TBool lastBuf; |
|
187 TVideoFrameBuffer* buf = iBufManager->WaitingBuffer(ETrue, lastBuf); |
|
188 TInt numBufferSkipped = 0; |
|
189 |
|
190 TTime now; |
|
191 now.UniversalTime(); |
|
192 TUint32 fastCounter = User::FastCounter(); |
|
193 while (buf != NULL) |
|
194 { |
|
195 TInt bufId = buf->BufferId(); |
|
196 |
|
197 DEBUGPRINT4(_L("bufId=%d presTime=%Ld, now=%Ld"), |
|
198 bufId, buf->PresentationTime().Int64(), now.Int64()); |
|
199 |
|
200 // submit the buffer for update if presntation time is not in past |
|
201 // or if the buffer is the last in queue or presentation time is zero |
|
202 if (buf->PresentationTime() >= now || lastBuf || buf->PresentationTime().Int64() == 0) |
|
203 { |
|
204 DoUpdateBuffer(bufId, now, fastCounter); |
|
205 break; |
|
206 } |
|
207 |
|
208 // The buffer presentation time occurs in past if codeflow reaches here. |
|
209 // Change the buffer status to available and notify observer about the skipped buffer |
|
210 iBufManager->BufferAvailable(bufId); |
|
211 iObserver.MvroBufferSkipped(bufId); |
|
212 numBufferSkipped++; |
|
213 |
|
214 // get next buffer |
|
215 buf = iBufManager->WaitingBuffer(ETrue, lastBuf); |
|
216 } |
|
217 |
|
218 //notifiy observer about the available buffers |
|
219 for (TInt i = 0; i < numBufferSkipped; ++i) |
|
220 { |
|
221 iObserver.MvroVideoBufferAvailable(); |
|
222 } |
|
223 } |
|
224 |
|
225 /** Set a pointer to renderer timer in timed mode */ |
|
226 void CRendererRelay::SetRendererTimer(CRendererTimer* aRendererTimer) |
|
227 { |
|
228 iRendererTimer = aRendererTimer; |
|
229 } |
|
230 |
|
231 /** Callback function when a renderer timer has expired */ |
|
232 void CRendererRelay::RendererTimerExpired() |
|
233 { |
|
234 SubmitBufferTimed(); |
|
235 } |
|
236 |
|
237 /** Submit the next buffer in timed mode */ |
|
238 void CRendererRelay::SubmitBufferTimed() |
|
239 { |
|
240 DEBUGPRINT1(_L("CRendererRelay::SubmitBufferTimed entered")); |
|
241 |
|
242 __ASSERT_DEBUG(iBufManager != NULL, User::Panic(_L("CRR::SubmitBufferTimed"), KErrCorrupt)); |
|
243 __ASSERT_DEBUG(iUpdateSubmitted == EFalse, |
|
244 User::Panic(_L("CRR::SubmitBufferTimed"), KErrGeneral)); |
|
245 __ASSERT_DEBUG(iRendererTimer && iRendererTimer->IsActive() == EFalse, |
|
246 User::Panic(_L("CRR::SubmitBufferTimed"), KErrInUse)); |
|
247 |
|
248 TBool lastBuf; |
|
249 TVideoFrameBuffer* buf = iBufManager->WaitingBuffer(EFalse, lastBuf); |
|
250 TInt numBufferSkipped = 0; |
|
251 |
|
252 TTime now; |
|
253 now.UniversalTime(); |
|
254 TUint32 fastCounter = User::FastCounter(); |
|
255 while (buf != NULL) |
|
256 { |
|
257 TInt bufId = buf->BufferId(); |
|
258 TTimeIntervalMicroSeconds deltaFromNow = buf->PresentationTime().MicroSecondsFrom(now); |
|
259 |
|
260 TInt64 waitTime = 0; |
|
261 if (buf->PresentationTime().Int64() != 0) |
|
262 { |
|
263 // presentation time is not zero, calculate wait time. Otherwise, wait time is zero. |
|
264 waitTime = deltaFromNow.Int64() - iDelay; |
|
265 } |
|
266 |
|
267 DEBUGPRINT4(_L("bufId=%d presTime=%Ld, now=%Ld"), |
|
268 bufId, buf->PresentationTime().Int64(), now.Int64()); |
|
269 |
|
270 // submit the buffer for update if presntation time is not in past |
|
271 // or if the buffer is the last in queue |
|
272 if (waitTime > 0) |
|
273 { |
|
274 iRendererTimer->Start(waitTime); |
|
275 break; |
|
276 } |
|
277 else if (buf->PresentationTime().Int64() == 0 || |
|
278 deltaFromNow.Int64() + iMaxDelay >= 0 || |
|
279 lastBuf) |
|
280 { |
|
281 // if presentation time is zero (waitTime is not used for checking because it may be zero from calculation) |
|
282 // or the frame is within maximun allowed delay, i.e. (presentation time + max delay >= now) |
|
283 // or submission time has passed but this is the last buf, submit the buffer now |
|
284 |
|
285 iBufManager->BufferSubmitted(buf); |
|
286 DoUpdateBuffer(bufId, now, fastCounter); |
|
287 break; |
|
288 } |
|
289 |
|
290 // The buffer presentation time has passed maxDelay if codeflow reaches here, skip the buffer. |
|
291 // Change the buffer status to available and notify observer |
|
292 iBufManager->BufferAvailable(bufId); |
|
293 iObserver.MvroBufferSkipped(bufId); |
|
294 numBufferSkipped++; |
|
295 |
|
296 // get next buffer |
|
297 buf = iBufManager->WaitingBuffer(EFalse, lastBuf); |
|
298 } |
|
299 |
|
300 //notifiy observer about the available buffers |
|
301 for (TInt i = 0; i < numBufferSkipped; ++i) |
|
302 { |
|
303 iObserver.MvroVideoBufferAvailable(); |
|
304 } |
|
305 } |
|
306 |
|
307 /** |
|
308 Submits a buffer to be updated at the specified time. |
|
309 */ |
|
310 |
|
311 void CRendererRelay::DoUpdateBuffer(TInt aBufferId, const TTime& aTime, TUint32 aFastCounter) |
|
312 { |
|
313 CBufAvailListener* availListener = BufAvailListener(); |
|
314 |
|
315 availListener->Start(aBufferId); |
|
316 iDisplayListener->Start(aBufferId, aTime, aFastCounter); |
|
317 |
|
318 TInt err = iSurfaceUpdateSession.SubmitUpdate(KAllScreens, iSurfaceId, aBufferId); |
|
319 |
|
320 DEBUGPRINT2(_L("SubmitUpdate return %d"), err); |
|
321 |
|
322 // error will also be returned from listener, so the next submit updated will be triggered by display listener |
|
323 iUpdateSubmitted = ETrue; |
|
324 } |
|
325 |
|
326 /** |
|
327 Return ETrue if an update has been submitted and the display notification |
|
328 haven't been received yet. i.e. Need to wait till for listener callback before |
|
329 the next buffer can be submitted. |
|
330 */ |
|
331 TBool CRendererRelay::UpdateSubmitted() |
|
332 { |
|
333 return iUpdateSubmitted; |
|
334 } |
|
335 |
|
336 /** return the delay for surface update */ |
|
337 TInt64 CRendererRelay::Delay() |
|
338 { |
|
339 return iDelay; |
|
340 } |
|
341 |
|
342 /** Cancel all update notification when a surface is destroyed */ |
|
343 void CRendererRelay::DestroySurface(TRequestStatus* /* aRequestStatus */ ) |
|
344 { |
|
345 iSurfaceUpdateSession.CancelAllUpdateNotifications(); |
|
346 } |
|
347 |
|
348 /* This function is not used */ |
|
349 void CRendererRelay::SetRendererThread(RThread* /* aRendererThread */) |
|
350 { |
|
351 __ASSERT_DEBUG(EFalse, User::Panic(_L("CRR::SetRendererThread"), KErrUnknown)); |
|
352 } |
|
353 |
|
354 /* This function is not used */ |
|
355 void CRendererRelay::Terminate(TRequestStatus& /* aRequestStatus */) |
|
356 { |
|
357 __ASSERT_DEBUG(EFalse, User::Panic(_L("CRR::Terminate"), KErrUnknown)); |
|
358 } |
|
359 |
|
360 /** Set timer info for timed mode, this function is not called in non-timed mode */ |
|
361 void CRendererRelay::SetTimerInfo(TInt64 aDefaultDelay, TInt64 aMaxDelay) |
|
362 { |
|
363 iDelay = aDefaultDelay; |
|
364 iMaxDelay = aMaxDelay; |
|
365 } |
|
366 |
|
367 /** Two-phased constructor. */ |
|
368 CRendererThreadRelay* CRendererThreadRelay::NewL(MVideoRendererObserver& aObserver, TThreadId aMainThreadId) |
|
369 { |
|
370 CRendererThreadRelay* self = new (ELeave) CRendererThreadRelay(aObserver); |
|
371 CleanupStack::PushL(self); |
|
372 self->ConstructL(aMainThreadId); |
|
373 CleanupStack::Pop(self); |
|
374 return self; |
|
375 } |
|
376 |
|
377 CRendererThreadRelay::CRendererThreadRelay(MVideoRendererObserver& aObserver) |
|
378 : CActive(EPriorityStandard), iObserver(aObserver) |
|
379 { |
|
380 CActiveScheduler::Add(this); |
|
381 } |
|
382 |
|
383 /** Second-phase constructor */ |
|
384 void CRendererThreadRelay::ConstructL(TThreadId aMainThreadId) |
|
385 { |
|
386 User::LeaveIfError(iMainThread.Open(aMainThreadId)); |
|
387 iRendererRelay = CRendererRelay::NewL(*this); |
|
388 iRendererTimer = CRendererTimer::NewL(*iRendererRelay); |
|
389 iRendererRelay->SetRendererTimer(iRendererTimer); |
|
390 } |
|
391 |
|
392 /** |
|
393 Destructor. |
|
394 This active object is always cancelled by main thread so no need to cancel this active object here |
|
395 */ |
|
396 CRendererThreadRelay::~CRendererThreadRelay() |
|
397 { |
|
398 delete iRendererCallbackListener; |
|
399 delete iRendererTimer; |
|
400 delete iRendererRelay; |
|
401 iMainThread.Close(); |
|
402 } |
|
403 |
|
404 void CRendererThreadRelay::DoCancel() |
|
405 { |
|
406 // Don't need to do anything, will be stopped by main thread |
|
407 } |
|
408 |
|
409 /** Function for making the initial request */ |
|
410 void CRendererThreadRelay::Start() |
|
411 { |
|
412 __ASSERT_DEBUG(IsActive() == EFalse, User::Panic(_L("CRTR::Start"), KErrInUse)); |
|
413 iStatus = KRequestPending; |
|
414 SetActive(); |
|
415 } |
|
416 |
|
417 /** Handle requests from main thread */ |
|
418 void CRendererThreadRelay::RunL() |
|
419 { |
|
420 __ASSERT_DEBUG(iStatus == KErrNone, User::Panic(_L("CRTR::RunL"), KErrUnknown)); |
|
421 TInt result = KErrNone; |
|
422 |
|
423 if (iFunctionCode == ESubmitUpdate) |
|
424 { |
|
425 Start(); |
|
426 RunUpdateBuffer(iBuffer, iPresentationTime); |
|
427 } |
|
428 else if (iFunctionCode == EDestroySurface) |
|
429 { |
|
430 Start(); |
|
431 RunDestroySurface(); |
|
432 } |
|
433 else if (iFunctionCode == EPrepare) |
|
434 { |
|
435 Start(); |
|
436 TRAP(result, RunPrepareL()); |
|
437 } |
|
438 else if (iFunctionCode == ESetBufferManager) |
|
439 { |
|
440 Start(); |
|
441 RunSetBufferManager(); |
|
442 } |
|
443 else // iFunctionCode == ETermination |
|
444 { |
|
445 CActiveScheduler::Stop(); |
|
446 } |
|
447 |
|
448 TRequestStatus *status = iCallingStatus; |
|
449 iMainThread.RequestComplete(status, result); |
|
450 } |
|
451 |
|
452 /** Send a signal to the main thread to indicate that the thread startup was successful. */ |
|
453 void CRendererThreadRelay::SignalSetupComplete(TRequestStatus* aSetupComplete) |
|
454 { |
|
455 iMainThread.RequestComplete(aSetupComplete, KErrNone); |
|
456 } |
|
457 |
|
458 /** Send update buffer request from main thread to renderer thread */ |
|
459 void CRendererThreadRelay::UpdateBuffer(TVideoFrameBuffer* aBuffer, const TTime& aPresentationTime, TRequestStatus* aRequestStatus) |
|
460 { |
|
461 __ASSERT_DEBUG(aRequestStatus != NULL, User::Panic(_L("CRTR::UpdateBuffer"), KErrArgument)); |
|
462 |
|
463 // set function parameters |
|
464 iCallingStatus = aRequestStatus; |
|
465 iFunctionCode = ESubmitUpdate; |
|
466 iBuffer = aBuffer; |
|
467 iPresentationTime = aPresentationTime; |
|
468 |
|
469 // send request to renderer thread |
|
470 TRequestStatus* rendererRequest = Status(); |
|
471 iRendererThread->RequestComplete(rendererRequest, KErrNone); |
|
472 } |
|
473 |
|
474 /** Send terminate renderer thread request from main thread to renderer thread */ |
|
475 void CRendererThreadRelay::Terminate(TRequestStatus& aRequestStatus) |
|
476 { |
|
477 iCallingStatus = &aRequestStatus; |
|
478 iFunctionCode = ETermination; |
|
479 |
|
480 if (iRendererCallbackListener) |
|
481 { |
|
482 iRendererCallbackListener->Cancel(); |
|
483 } |
|
484 |
|
485 // send request to renderer thread |
|
486 TRequestStatus* rendererRequest = Status(); |
|
487 iRendererThread->RequestComplete(rendererRequest, KErrNone); |
|
488 } |
|
489 |
|
490 /** Send destroy surface request from main thread to renderer thread */ |
|
491 void CRendererThreadRelay::DestroySurface(TRequestStatus* aRequestStatus) |
|
492 { |
|
493 __ASSERT_DEBUG(aRequestStatus != NULL, User::Panic(_L("CRTR::DestroySurface"), KErrArgument)); |
|
494 |
|
495 iCallingStatus = aRequestStatus; |
|
496 iFunctionCode = EDestroySurface; |
|
497 |
|
498 // send request to renderer thread |
|
499 TRequestStatus* rendererRequest = Status(); |
|
500 iRendererThread->RequestComplete(rendererRequest, KErrNone); |
|
501 } |
|
502 |
|
503 /* Prepare the object after a surface is created */ |
|
504 void CRendererThreadRelay::PrepareL(const TSurfaceId& aSurfaceId, TInt aNumBuffers, TRequestStatus* aRequestStatus) |
|
505 { |
|
506 __ASSERT_DEBUG(aRequestStatus != NULL, User::Panic(_L("CRTR::PrepareL"), KErrArgument)); |
|
507 |
|
508 if(!iRendererCallbackListener) |
|
509 { |
|
510 // first, create callback listener in the main thread |
|
511 iRendererCallbackListener = CRendererCallbackListener::NewL(iObserver, aNumBuffers); |
|
512 iRendererCallbackListener->Start(); |
|
513 } |
|
514 else if (iNumBuffers < aNumBuffers) |
|
515 { |
|
516 iRendererCallbackListener->Cancel(); |
|
517 iRendererCallbackListener->ExtendMsgQueueL(aNumBuffers); |
|
518 iRendererCallbackListener->Start(); |
|
519 } |
|
520 |
|
521 // set function parameters |
|
522 iCallingStatus = aRequestStatus; |
|
523 iFunctionCode = EPrepare; |
|
524 iSurfaceId = aSurfaceId; |
|
525 iNumBuffers = aNumBuffers; |
|
526 |
|
527 // send request to renderer thread |
|
528 TRequestStatus* rendererRequest = Status(); |
|
529 iRendererThread->RequestComplete(rendererRequest, KErrNone); |
|
530 } |
|
531 |
|
532 /* Prepare the object after a surface is created */ |
|
533 void CRendererThreadRelay::RunPrepareL() |
|
534 { |
|
535 iRendererRelay->PrepareL(iSurfaceId, iNumBuffers, NULL); |
|
536 } |
|
537 |
|
538 /** Run update buffer in renderer thread */ |
|
539 void CRendererThreadRelay::RunUpdateBuffer(TVideoFrameBuffer* aBuffer, const TTime& aPresentationTime) |
|
540 { |
|
541 DEBUGPRINT1(_L("CRendererThreadRelay::RunUpdateBuffer entered")); |
|
542 __ASSERT_DEBUG(iBufManager != NULL, User::Panic(_L("CRTR::RunUpdateBuffer"), KErrCorrupt)); |
|
543 |
|
544 /* |
|
545 Buffer update is determined as follow: |
|
546 |
|
547 If wait list is empty (imply no active timer), always add buffer to list, |
|
548 If a preceding buffer hasn't been displayed, the new buffer will be handled after display callback. |
|
549 otherwise, decide whether timer should be started for submit update |
|
550 If wait list is not empty (imply either timer is active or waiting for display callback) |
|
551 If waiting for display callback, add new buffer to list and it will be handled after display callback. |
|
552 (note: presentation time is not check because the new buffer may be newer than the waiting buffers even though both have passed due time) |
|
553 If timer is active, first check if presentation time is zero. If so, display right away |
|
554 otherwise, then check if this frame can be timed (presentation time - delay <= now), if not, check max display to skip the frame or display right away |
|
555 otherwise, then check if presentation time < head list presentation time |
|
556 if so, add the buffer to wait list, stop the timer and start timer with new time |
|
557 else, just need to add buffer to wait list, the next timer expiry will hander the head buffer |
|
558 */ |
|
559 |
|
560 if (iBufManager->WaitingListIsEmpty()) |
|
561 { |
|
562 iBufManager->UpdateBuffer(aBuffer, aPresentationTime); |
|
563 |
|
564 if (iRendererRelay->UpdateSubmitted() == EFalse) |
|
565 { |
|
566 // an update haven't been submitted, prepare to submit one now |
|
567 iRendererRelay->SubmitBufferTimed(); |
|
568 } |
|
569 } |
|
570 else |
|
571 { |
|
572 // waiting list is not empty |
|
573 if (iRendererRelay->UpdateSubmitted()) |
|
574 { |
|
575 // waiting for listener callback, just update waiting list |
|
576 iBufManager->UpdateBuffer(aBuffer, aPresentationTime); |
|
577 } |
|
578 else |
|
579 { |
|
580 // the timer is waiting |
|
581 |
|
582 if (aPresentationTime.Int64() == 0) |
|
583 { |
|
584 // presentation time is zero, display right away. |
|
585 iRendererTimer->Cancel(); |
|
586 iBufManager->UpdateBuffer(aBuffer, aPresentationTime); |
|
587 iRendererRelay->SubmitBufferTimed(); |
|
588 return; |
|
589 } |
|
590 |
|
591 TTime now; |
|
592 now.UniversalTime(); |
|
593 TTimeIntervalMicroSeconds deltaFromNow = aPresentationTime.MicroSecondsFrom(now); |
|
594 TInt64 waitTime = deltaFromNow.Int64() - iRendererRelay->Delay(); |
|
595 |
|
596 if (waitTime <= 0) |
|
597 { |
|
598 // the wait time has passed |
|
599 if (deltaFromNow.Int64() + iMaxDelay >= 0) |
|
600 { |
|
601 // the frame is within maximun allowed delay, i.e. (presentation time + max delay >= now) |
|
602 // display right away |
|
603 iRendererTimer->Cancel(); |
|
604 iBufManager->UpdateBuffer(aBuffer, aPresentationTime); |
|
605 iRendererRelay->SubmitBufferTimed(); |
|
606 } |
|
607 else |
|
608 { |
|
609 // skip the buffer |
|
610 iBufManager->ReleaseBuffer(aBuffer); |
|
611 MvroBufferSkipped(aBuffer->BufferId()); |
|
612 |
|
613 //notifiy observer about the available buffers |
|
614 MvroVideoBufferAvailable(); |
|
615 } |
|
616 } |
|
617 else |
|
618 { |
|
619 // wait time has not passed, add to waiting list |
|
620 if (iBufManager->UpdateBuffer(aBuffer, aPresentationTime)) |
|
621 { |
|
622 // head of waiting list has changed, start timer with new wait time |
|
623 iRendererTimer->Cancel(); |
|
624 iRendererTimer->Start(waitTime); |
|
625 } |
|
626 } |
|
627 } |
|
628 } |
|
629 } |
|
630 |
|
631 /** Run destroy surface in renderer thread */ |
|
632 void CRendererThreadRelay::RunDestroySurface() |
|
633 { |
|
634 iRendererTimer->Cancel(); |
|
635 iRendererRelay->DestroySurface(NULL); |
|
636 } |
|
637 |
|
638 /* Update buffer manager pointer */ |
|
639 void CRendererThreadRelay::SetBufferManager(CRendererBufferManager* aBufManager) |
|
640 { |
|
641 TRequestStatus request = KRequestPending; |
|
642 TRequestStatus logonRequest = KRequestPending; |
|
643 |
|
644 // While a function call is in progress this thread is suspended |
|
645 // and the undertaker will not catch panics, listen for these here |
|
646 iRendererThread->Logon(logonRequest); |
|
647 |
|
648 // Set the call parameters |
|
649 iCallingStatus = &request; |
|
650 iFunctionCode = ESetBufferManager; |
|
651 iBufManager = aBufManager; |
|
652 |
|
653 // send request to renderer thread |
|
654 TRequestStatus* rendererRequest = Status(); |
|
655 iRendererThread->RequestComplete(rendererRequest, KErrNone); |
|
656 |
|
657 User::WaitForRequest(logonRequest, request); |
|
658 |
|
659 if(logonRequest != KRequestPending) |
|
660 { |
|
661 // renderer thread got panic from surface update session, so panic client |
|
662 TInt reason = iRendererThread->ExitReason(); |
|
663 TExitCategoryName category = iRendererThread->ExitCategory(); |
|
664 User::Panic(category,reason); |
|
665 } |
|
666 |
|
667 // Thread is still alive and well |
|
668 iRendererThread->LogonCancel(logonRequest); |
|
669 User::WaitForRequest(logonRequest); // Consume the signal |
|
670 |
|
671 __ASSERT_DEBUG(request != KRequestPending, User::Panic(_L("CRTR::SetBufferManager"), KErrCorrupt)); |
|
672 } |
|
673 |
|
674 void CRendererThreadRelay::RunSetBufferManager() |
|
675 { |
|
676 iRendererRelay->SetBufferManager(iBufManager); |
|
677 } |
|
678 |
|
679 /** Store a pointer to the renderer thread object. */ |
|
680 void CRendererThreadRelay::SetRendererThread(RThread* aRendererThread) |
|
681 { |
|
682 iRendererThread = aRendererThread; |
|
683 } |
|
684 |
|
685 /** Return a pointer to the function call listener's request status. */ |
|
686 TRequestStatus* CRendererThreadRelay::Status() |
|
687 { |
|
688 return &iStatus; |
|
689 } |
|
690 |
|
691 /** Set timer info for timed mode */ |
|
692 void CRendererThreadRelay::SetTimerInfo(TInt64 aDefaultDelay, TInt64 aMaxDelay) |
|
693 { |
|
694 iMaxDelay = aMaxDelay; |
|
695 iRendererRelay->SetTimerInfo(aDefaultDelay, aMaxDelay); |
|
696 } |
|
697 |
|
698 void CRendererThreadRelay::MvroVideoBufferAvailable() |
|
699 { |
|
700 iRendererCallbackListener->SendCallback(CRendererCallbackListener::EBufferAvailable, -1, 0); |
|
701 } |
|
702 |
|
703 void CRendererThreadRelay::MvroBufferDisplayed(TInt aBufferId, const TTime& aTime) |
|
704 { |
|
705 iRendererCallbackListener->SendCallback(CRendererCallbackListener::EBufferDisplayed, aBufferId, aTime); |
|
706 } |
|
707 |
|
708 void CRendererThreadRelay::MvroBufferSkipped(TInt aBufferId) |
|
709 { |
|
710 iRendererCallbackListener->SendCallback(CRendererCallbackListener::EBufferSkipped, aBufferId, 0); |
|
711 } |
|
712 |
|
713 |
|
714 /** Private constructor */ |
|
715 CRendererCallbackListener::CRendererCallbackListener(MVideoRendererObserver& aObserver) |
|
716 : CActive(EPriorityStandard), |
|
717 iObserver(aObserver) |
|
718 { |
|
719 CActiveScheduler::Add(this); |
|
720 } |
|
721 |
|
722 /** Two-phased constructor */ |
|
723 CRendererCallbackListener* CRendererCallbackListener::NewL(MVideoRendererObserver& aObserver, TInt aNumBuffer) |
|
724 { |
|
725 CRendererCallbackListener* self = new (ELeave) CRendererCallbackListener(aObserver); |
|
726 CleanupStack::PushL(self); |
|
727 self->ConstructL(aNumBuffer); |
|
728 CleanupStack::Pop(self); |
|
729 return self; |
|
730 } |
|
731 |
|
732 /** Second-phase constructor */ |
|
733 void CRendererCallbackListener::ConstructL(TInt aNumBuffer) |
|
734 { |
|
735 // There could potentially be three messages outstanding per buffer |
|
736 // size message queue accordingly |
|
737 TInt slot = aNumBuffer * 3; |
|
738 if (slot > RMsgQueueBase::KMaxLength) |
|
739 { |
|
740 slot = RMsgQueueBase::KMaxLength; |
|
741 } |
|
742 User::LeaveIfError(iMsgQueue.CreateLocal(slot)); |
|
743 } |
|
744 |
|
745 CRendererCallbackListener::~CRendererCallbackListener() |
|
746 { |
|
747 Cancel(); // Cancel any request, if outstanding |
|
748 iMsgQueue.Close(); |
|
749 } |
|
750 |
|
751 void CRendererCallbackListener::ExtendMsgQueueL(TInt aNumBuffer) |
|
752 { |
|
753 // close the message queue and construct another one |
|
754 iMsgQueue.Close(); |
|
755 ConstructL(aNumBuffer); |
|
756 } |
|
757 |
|
758 void CRendererCallbackListener::DoCancel() |
|
759 { |
|
760 iMsgQueue.CancelDataAvailable(); |
|
761 } |
|
762 |
|
763 /** Start listener */ |
|
764 void CRendererCallbackListener::Start() |
|
765 { |
|
766 if (!IsActive()) |
|
767 { |
|
768 iMsgQueue.NotifyDataAvailable(iStatus); |
|
769 SetActive(); |
|
770 } |
|
771 } |
|
772 |
|
773 /** Set the callback function */ |
|
774 void CRendererCallbackListener::SendCallback(TFunctionCode aFunctionCode, TInt aBufferId, const TTime& aTime) |
|
775 { |
|
776 DEBUGPRINT2(_L("CRendererCallbackListener::SendCallback entered aFunctionCode=%d"), aFunctionCode); |
|
777 |
|
778 TCallbackData data; |
|
779 data.iFunctionCode = aFunctionCode; |
|
780 data.iBufferId = aBufferId; |
|
781 data.iTime = aTime; |
|
782 |
|
783 iMsgQueue.Send(data); |
|
784 } |
|
785 |
|
786 /** Call the callback function within the main thread */ |
|
787 void CRendererCallbackListener::RunL() |
|
788 { |
|
789 TCallbackData data; |
|
790 TInt err = iMsgQueue.Receive(data); |
|
791 if (err == KErrNone) |
|
792 { |
|
793 if (data.iFunctionCode == EBufferAvailable) |
|
794 { |
|
795 DEBUGPRINT1(_L("CRendererCallbackListener::RunL - EBufferAvailable")); |
|
796 iObserver.MvroVideoBufferAvailable(); |
|
797 } |
|
798 else if (data.iFunctionCode == EBufferDisplayed) |
|
799 { |
|
800 DEBUGPRINT1(_L("CRendererCallbackListener::RunL - EBufferDisplayed")); |
|
801 iObserver.MvroBufferDisplayed(data.iBufferId, data.iTime); |
|
802 } |
|
803 else if (data.iFunctionCode == EBufferSkipped) |
|
804 { |
|
805 DEBUGPRINT1(_L("CRendererCallbackListener::RunL - EBufferSkipped")); |
|
806 iObserver.MvroBufferSkipped(data.iBufferId); |
|
807 } |
|
808 } |
|
809 else |
|
810 { |
|
811 DEBUGPRINT2(_L("CRendererCallbackListener::RunL err=%d"), err); |
|
812 } |
|
813 |
|
814 Start(); |
|
815 } |