sl@0: // Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // sl@0: sl@0: /** sl@0: @file sl@0: @internalComponent sl@0: */ sl@0: sl@0: #include sl@0: #include sl@0: #include "rendererrelay.h" sl@0: #include "buflistener.h" sl@0: #include "buffermanager.h" sl@0: #include "videoframebuffer.h" sl@0: #include "rendererutil.h" sl@0: #include "renderertimer.h" sl@0: sl@0: /** static factory contruction */ sl@0: CRendererRelay* CRendererRelay::NewL(MVideoRendererObserver& aObserver) sl@0: { sl@0: CRendererRelay* self = new (ELeave) CRendererRelay(aObserver); sl@0: CleanupStack::PushL(self); sl@0: self->ConstructL(); sl@0: CleanupStack::Pop(self); sl@0: return self; sl@0: } sl@0: sl@0: CRendererRelay::CRendererRelay(MVideoRendererObserver& aObserver) sl@0: :iObserver(aObserver), sl@0: iBufAvailListeners(CBufAvailListener::iOffset), sl@0: iAvailListenersIter(iBufAvailListeners) sl@0: { sl@0: } sl@0: sl@0: void CRendererRelay::ConstructL() sl@0: { sl@0: User::LeaveIfError(iSurfaceUpdateSession.Connect()); sl@0: User::LeaveIfError(HAL::Get(HAL::EFastCounterFrequency, iFastCounterFrequency)); sl@0: User::LeaveIfError(HAL::Get(HAL::EFastCounterCountsUp, iFastCounterCountsUp)); sl@0: } sl@0: sl@0: /** Destructor */ sl@0: CRendererRelay::~CRendererRelay() sl@0: { sl@0: delete iDisplayListener; sl@0: sl@0: iAvailListenersIter.SetToFirst(); sl@0: CBufAvailListener* listener = NULL; sl@0: while ((listener = iAvailListenersIter++) != NULL) sl@0: { sl@0: listener->iDblQueLink.Deque(); sl@0: delete listener; sl@0: } sl@0: sl@0: iSurfaceUpdateSession.Close(); sl@0: } sl@0: sl@0: /** Update buffer manager pointer in this class and listeners */ sl@0: void CRendererRelay::SetBufferManager(CRendererBufferManager* aBufManager) sl@0: { sl@0: iBufManager = aBufManager; sl@0: sl@0: // change buffer manager pointer for listeners sl@0: iAvailListenersIter.SetToFirst(); sl@0: CBufAvailListener* listener = NULL; sl@0: while ((listener = iAvailListenersIter++) != NULL) sl@0: { sl@0: listener->SetBufferManager(aBufManager); sl@0: sl@0: if (!aBufManager && listener->IsAdded()) sl@0: { sl@0: listener->Deque(); sl@0: } sl@0: else if (aBufManager && !listener->IsAdded()) sl@0: { sl@0: CActiveScheduler::Add(listener); sl@0: } sl@0: } sl@0: } sl@0: sl@0: /** Return the next unused CBufAvailListener. */ sl@0: CBufAvailListener* CRendererRelay::BufAvailListener() sl@0: { sl@0: CBufAvailListener* listener = NULL; sl@0: iAvailListenersIter.SetToFirst(); sl@0: while ((listener = iAvailListenersIter++) != NULL) sl@0: { sl@0: if (listener->IsActive() == EFalse) sl@0: { sl@0: // Move to end so that the next search is a bit faster sl@0: listener->iDblQueLink.Deque(); sl@0: iBufAvailListeners.AddLast(*listener); sl@0: return listener; sl@0: } sl@0: } sl@0: sl@0: // Should not reach here as the number of listeners is same as number of buffer sl@0: __ASSERT_DEBUG(EFalse, User::Panic(_L("CRR::BufAvailListener"), KErrUnknown)); sl@0: return listener; sl@0: } sl@0: sl@0: /** Set update parameter and create listeners */ sl@0: void CRendererRelay::PrepareL(const TSurfaceId& aSurfaceId, TInt aNumBuffers, TRequestStatus* /*aRequestStatus*/) sl@0: { sl@0: iUpdateSubmitted = EFalse; sl@0: iSurfaceId = aSurfaceId; sl@0: sl@0: if (!iDisplayListener) sl@0: { sl@0: iDisplayListener = CBufDisplayListener::NewL(iObserver, iSurfaceUpdateSession, *this, iFastCounterFrequency, iFastCounterCountsUp); sl@0: } sl@0: sl@0: TInt numListeners = 0; sl@0: iAvailListenersIter.SetToFirst(); sl@0: while (iAvailListenersIter++ != NULL) sl@0: { sl@0: numListeners++; sl@0: } sl@0: sl@0: CBufAvailListener* listener = NULL; sl@0: // create new listeners if there are more numBuffers than listeners sl@0: for (TInt i = numListeners; i < aNumBuffers; ++i) sl@0: { sl@0: listener = CBufAvailListener::NewL(iObserver, iSurfaceUpdateSession); sl@0: iBufAvailListeners.AddFirst(*listener); sl@0: } sl@0: } sl@0: sl@0: /** Handle update buffer request in non-timed mode */ sl@0: void CRendererRelay::UpdateBuffer(TVideoFrameBuffer* aBuffer, const TTime& aPresentationTime, TRequestStatus* /* aRequestStatus */) sl@0: { sl@0: __ASSERT_DEBUG(iBufManager != NULL, User::Panic(_L("CRR::UpdateBuffer"), KErrNotReady)); sl@0: sl@0: iBufManager->UpdateBuffer(aBuffer, aPresentationTime); sl@0: sl@0: if (iUpdateSubmitted == EFalse) sl@0: { sl@0: // an update haven't been submitted, submit one now sl@0: SubmitBuffer(); sl@0: } sl@0: } sl@0: sl@0: /** Callback from display listener that a buffer has been displayed or skipped*/ sl@0: void CRendererRelay::BufferDisplayed(TBool aDisplayed, TInt64 aDelay) sl@0: { sl@0: DEBUGPRINT3(_L("CRendererRelay::BufferDisplayed entered aDisplayed=%d, aDelay=%Ld"), aDisplayed, aDelay); sl@0: sl@0: iUpdateSubmitted = EFalse; sl@0: sl@0: if (iRendererTimer) sl@0: { sl@0: // rendering in timed mode sl@0: if (aDisplayed) sl@0: { sl@0: // update delay value sl@0: iDelay = aDelay; sl@0: } sl@0: SubmitBufferTimed(); sl@0: } sl@0: else sl@0: { sl@0: // rendering in non timed mode sl@0: SubmitBuffer(); sl@0: } sl@0: } sl@0: sl@0: /** Submit the next buffer that is waiting to be submitted in non-timed mode*/ sl@0: void CRendererRelay::SubmitBuffer() sl@0: { sl@0: DEBUGPRINT1(_L("CRendererRelay::SubmitBuffer entered")); sl@0: __ASSERT_DEBUG(iBufManager != NULL, User::Panic(_L("CRR::SubmitBuffer"), KErrCorrupt)); sl@0: __ASSERT_DEBUG(iUpdateSubmitted == EFalse, User::Panic(_L("CRR::SubmitBuffer"), KErrGeneral)); sl@0: sl@0: TBool lastBuf; sl@0: TVideoFrameBuffer* buf = iBufManager->WaitingBuffer(ETrue, lastBuf); sl@0: TInt numBufferSkipped = 0; sl@0: sl@0: TTime now; sl@0: now.UniversalTime(); sl@0: TUint32 fastCounter = User::FastCounter(); sl@0: while (buf != NULL) sl@0: { sl@0: TInt bufId = buf->BufferId(); sl@0: sl@0: DEBUGPRINT4(_L("bufId=%d presTime=%Ld, now=%Ld"), sl@0: bufId, buf->PresentationTime().Int64(), now.Int64()); sl@0: sl@0: // submit the buffer for update if presntation time is not in past sl@0: // or if the buffer is the last in queue or presentation time is zero sl@0: if (buf->PresentationTime() >= now || lastBuf || buf->PresentationTime().Int64() == 0) sl@0: { sl@0: DoUpdateBuffer(bufId, now, fastCounter); sl@0: break; sl@0: } sl@0: sl@0: // The buffer presentation time occurs in past if codeflow reaches here. sl@0: // Change the buffer status to available and notify observer about the skipped buffer sl@0: iBufManager->BufferAvailable(bufId); sl@0: iObserver.MvroBufferSkipped(bufId); sl@0: numBufferSkipped++; sl@0: sl@0: // get next buffer sl@0: buf = iBufManager->WaitingBuffer(ETrue, lastBuf); sl@0: } sl@0: sl@0: //notifiy observer about the available buffers sl@0: for (TInt i = 0; i < numBufferSkipped; ++i) sl@0: { sl@0: iObserver.MvroVideoBufferAvailable(); sl@0: } sl@0: } sl@0: sl@0: /** Set a pointer to renderer timer in timed mode */ sl@0: void CRendererRelay::SetRendererTimer(CRendererTimer* aRendererTimer) sl@0: { sl@0: iRendererTimer = aRendererTimer; sl@0: } sl@0: sl@0: /** Callback function when a renderer timer has expired */ sl@0: void CRendererRelay::RendererTimerExpired() sl@0: { sl@0: SubmitBufferTimed(); sl@0: } sl@0: sl@0: /** Submit the next buffer in timed mode */ sl@0: void CRendererRelay::SubmitBufferTimed() sl@0: { sl@0: DEBUGPRINT1(_L("CRendererRelay::SubmitBufferTimed entered")); sl@0: sl@0: __ASSERT_DEBUG(iBufManager != NULL, User::Panic(_L("CRR::SubmitBufferTimed"), KErrCorrupt)); sl@0: __ASSERT_DEBUG(iUpdateSubmitted == EFalse, sl@0: User::Panic(_L("CRR::SubmitBufferTimed"), KErrGeneral)); sl@0: __ASSERT_DEBUG(iRendererTimer && iRendererTimer->IsActive() == EFalse, sl@0: User::Panic(_L("CRR::SubmitBufferTimed"), KErrInUse)); sl@0: sl@0: TBool lastBuf; sl@0: TVideoFrameBuffer* buf = iBufManager->WaitingBuffer(EFalse, lastBuf); sl@0: TInt numBufferSkipped = 0; sl@0: sl@0: TTime now; sl@0: now.UniversalTime(); sl@0: TUint32 fastCounter = User::FastCounter(); sl@0: while (buf != NULL) sl@0: { sl@0: TInt bufId = buf->BufferId(); sl@0: TTimeIntervalMicroSeconds deltaFromNow = buf->PresentationTime().MicroSecondsFrom(now); sl@0: sl@0: TInt64 waitTime = 0; sl@0: if (buf->PresentationTime().Int64() != 0) sl@0: { sl@0: // presentation time is not zero, calculate wait time. Otherwise, wait time is zero. sl@0: waitTime = deltaFromNow.Int64() - iDelay; sl@0: } sl@0: sl@0: DEBUGPRINT4(_L("bufId=%d presTime=%Ld, now=%Ld"), sl@0: bufId, buf->PresentationTime().Int64(), now.Int64()); sl@0: sl@0: // submit the buffer for update if presntation time is not in past sl@0: // or if the buffer is the last in queue sl@0: if (waitTime > 0) sl@0: { sl@0: iRendererTimer->Start(waitTime); sl@0: break; sl@0: } sl@0: else if (buf->PresentationTime().Int64() == 0 || sl@0: deltaFromNow.Int64() + iMaxDelay >= 0 || sl@0: lastBuf) sl@0: { sl@0: // if presentation time is zero (waitTime is not used for checking because it may be zero from calculation) sl@0: // or the frame is within maximun allowed delay, i.e. (presentation time + max delay >= now) sl@0: // or submission time has passed but this is the last buf, submit the buffer now sl@0: sl@0: iBufManager->BufferSubmitted(buf); sl@0: DoUpdateBuffer(bufId, now, fastCounter); sl@0: break; sl@0: } sl@0: sl@0: // The buffer presentation time has passed maxDelay if codeflow reaches here, skip the buffer. sl@0: // Change the buffer status to available and notify observer sl@0: iBufManager->BufferAvailable(bufId); sl@0: iObserver.MvroBufferSkipped(bufId); sl@0: numBufferSkipped++; sl@0: sl@0: // get next buffer sl@0: buf = iBufManager->WaitingBuffer(EFalse, lastBuf); sl@0: } sl@0: sl@0: //notifiy observer about the available buffers sl@0: for (TInt i = 0; i < numBufferSkipped; ++i) sl@0: { sl@0: iObserver.MvroVideoBufferAvailable(); sl@0: } sl@0: } sl@0: sl@0: /** sl@0: Submits a buffer to be updated at the specified time. sl@0: */ sl@0: sl@0: void CRendererRelay::DoUpdateBuffer(TInt aBufferId, const TTime& aTime, TUint32 aFastCounter) sl@0: { sl@0: CBufAvailListener* availListener = BufAvailListener(); sl@0: sl@0: availListener->Start(aBufferId); sl@0: iDisplayListener->Start(aBufferId, aTime, aFastCounter); sl@0: sl@0: TInt err = iSurfaceUpdateSession.SubmitUpdate(KAllScreens, iSurfaceId, aBufferId); sl@0: sl@0: DEBUGPRINT2(_L("SubmitUpdate return %d"), err); sl@0: sl@0: // error will also be returned from listener, so the next submit updated will be triggered by display listener sl@0: iUpdateSubmitted = ETrue; sl@0: } sl@0: sl@0: /** sl@0: Return ETrue if an update has been submitted and the display notification sl@0: haven't been received yet. i.e. Need to wait till for listener callback before sl@0: the next buffer can be submitted. sl@0: */ sl@0: TBool CRendererRelay::UpdateSubmitted() sl@0: { sl@0: return iUpdateSubmitted; sl@0: } sl@0: sl@0: /** return the delay for surface update */ sl@0: TInt64 CRendererRelay::Delay() sl@0: { sl@0: return iDelay; sl@0: } sl@0: sl@0: /** Cancel all update notification when a surface is destroyed */ sl@0: void CRendererRelay::DestroySurface(TRequestStatus* /* aRequestStatus */ ) sl@0: { sl@0: iSurfaceUpdateSession.CancelAllUpdateNotifications(); sl@0: } sl@0: sl@0: /* This function is not used */ sl@0: void CRendererRelay::SetRendererThread(RThread* /* aRendererThread */) sl@0: { sl@0: __ASSERT_DEBUG(EFalse, User::Panic(_L("CRR::SetRendererThread"), KErrUnknown)); sl@0: } sl@0: sl@0: /* This function is not used */ sl@0: void CRendererRelay::Terminate(TRequestStatus& /* aRequestStatus */) sl@0: { sl@0: __ASSERT_DEBUG(EFalse, User::Panic(_L("CRR::Terminate"), KErrUnknown)); sl@0: } sl@0: sl@0: /** Set timer info for timed mode, this function is not called in non-timed mode */ sl@0: void CRendererRelay::SetTimerInfo(TInt64 aDefaultDelay, TInt64 aMaxDelay) sl@0: { sl@0: iDelay = aDefaultDelay; sl@0: iMaxDelay = aMaxDelay; sl@0: } sl@0: sl@0: /** Two-phased constructor. */ sl@0: CRendererThreadRelay* CRendererThreadRelay::NewL(MVideoRendererObserver& aObserver, TThreadId aMainThreadId) sl@0: { sl@0: CRendererThreadRelay* self = new (ELeave) CRendererThreadRelay(aObserver); sl@0: CleanupStack::PushL(self); sl@0: self->ConstructL(aMainThreadId); sl@0: CleanupStack::Pop(self); sl@0: return self; sl@0: } sl@0: sl@0: CRendererThreadRelay::CRendererThreadRelay(MVideoRendererObserver& aObserver) sl@0: : CActive(EPriorityStandard), iObserver(aObserver) sl@0: { sl@0: CActiveScheduler::Add(this); sl@0: } sl@0: sl@0: /** Second-phase constructor */ sl@0: void CRendererThreadRelay::ConstructL(TThreadId aMainThreadId) sl@0: { sl@0: User::LeaveIfError(iMainThread.Open(aMainThreadId)); sl@0: iRendererRelay = CRendererRelay::NewL(*this); sl@0: iRendererTimer = CRendererTimer::NewL(*iRendererRelay); sl@0: iRendererRelay->SetRendererTimer(iRendererTimer); sl@0: } sl@0: sl@0: /** sl@0: Destructor. sl@0: This active object is always cancelled by main thread so no need to cancel this active object here sl@0: */ sl@0: CRendererThreadRelay::~CRendererThreadRelay() sl@0: { sl@0: delete iRendererCallbackListener; sl@0: delete iRendererTimer; sl@0: delete iRendererRelay; sl@0: iMainThread.Close(); sl@0: } sl@0: sl@0: void CRendererThreadRelay::DoCancel() sl@0: { sl@0: // Don't need to do anything, will be stopped by main thread sl@0: } sl@0: sl@0: /** Function for making the initial request */ sl@0: void CRendererThreadRelay::Start() sl@0: { sl@0: __ASSERT_DEBUG(IsActive() == EFalse, User::Panic(_L("CRTR::Start"), KErrInUse)); sl@0: iStatus = KRequestPending; sl@0: SetActive(); sl@0: } sl@0: sl@0: /** Handle requests from main thread */ sl@0: void CRendererThreadRelay::RunL() sl@0: { sl@0: __ASSERT_DEBUG(iStatus == KErrNone, User::Panic(_L("CRTR::RunL"), KErrUnknown)); sl@0: TInt result = KErrNone; sl@0: sl@0: if (iFunctionCode == ESubmitUpdate) sl@0: { sl@0: Start(); sl@0: RunUpdateBuffer(iBuffer, iPresentationTime); sl@0: } sl@0: else if (iFunctionCode == EDestroySurface) sl@0: { sl@0: Start(); sl@0: RunDestroySurface(); sl@0: } sl@0: else if (iFunctionCode == EPrepare) sl@0: { sl@0: Start(); sl@0: TRAP(result, RunPrepareL()); sl@0: } sl@0: else if (iFunctionCode == ESetBufferManager) sl@0: { sl@0: Start(); sl@0: RunSetBufferManager(); sl@0: } sl@0: else // iFunctionCode == ETermination sl@0: { sl@0: CActiveScheduler::Stop(); sl@0: } sl@0: sl@0: TRequestStatus *status = iCallingStatus; sl@0: iMainThread.RequestComplete(status, result); sl@0: } sl@0: sl@0: /** Send a signal to the main thread to indicate that the thread startup was successful. */ sl@0: void CRendererThreadRelay::SignalSetupComplete(TRequestStatus* aSetupComplete) sl@0: { sl@0: iMainThread.RequestComplete(aSetupComplete, KErrNone); sl@0: } sl@0: sl@0: /** Send update buffer request from main thread to renderer thread */ sl@0: void CRendererThreadRelay::UpdateBuffer(TVideoFrameBuffer* aBuffer, const TTime& aPresentationTime, TRequestStatus* aRequestStatus) sl@0: { sl@0: __ASSERT_DEBUG(aRequestStatus != NULL, User::Panic(_L("CRTR::UpdateBuffer"), KErrArgument)); sl@0: sl@0: // set function parameters sl@0: iCallingStatus = aRequestStatus; sl@0: iFunctionCode = ESubmitUpdate; sl@0: iBuffer = aBuffer; sl@0: iPresentationTime = aPresentationTime; sl@0: sl@0: // send request to renderer thread sl@0: TRequestStatus* rendererRequest = Status(); sl@0: iRendererThread->RequestComplete(rendererRequest, KErrNone); sl@0: } sl@0: sl@0: /** Send terminate renderer thread request from main thread to renderer thread */ sl@0: void CRendererThreadRelay::Terminate(TRequestStatus& aRequestStatus) sl@0: { sl@0: iCallingStatus = &aRequestStatus; sl@0: iFunctionCode = ETermination; sl@0: sl@0: if (iRendererCallbackListener) sl@0: { sl@0: iRendererCallbackListener->Cancel(); sl@0: } sl@0: sl@0: // send request to renderer thread sl@0: TRequestStatus* rendererRequest = Status(); sl@0: iRendererThread->RequestComplete(rendererRequest, KErrNone); sl@0: } sl@0: sl@0: /** Send destroy surface request from main thread to renderer thread */ sl@0: void CRendererThreadRelay::DestroySurface(TRequestStatus* aRequestStatus) sl@0: { sl@0: __ASSERT_DEBUG(aRequestStatus != NULL, User::Panic(_L("CRTR::DestroySurface"), KErrArgument)); sl@0: sl@0: iCallingStatus = aRequestStatus; sl@0: iFunctionCode = EDestroySurface; sl@0: sl@0: // send request to renderer thread sl@0: TRequestStatus* rendererRequest = Status(); sl@0: iRendererThread->RequestComplete(rendererRequest, KErrNone); sl@0: } sl@0: sl@0: /* Prepare the object after a surface is created */ sl@0: void CRendererThreadRelay::PrepareL(const TSurfaceId& aSurfaceId, TInt aNumBuffers, TRequestStatus* aRequestStatus) sl@0: { sl@0: __ASSERT_DEBUG(aRequestStatus != NULL, User::Panic(_L("CRTR::PrepareL"), KErrArgument)); sl@0: sl@0: if(!iRendererCallbackListener) sl@0: { sl@0: // first, create callback listener in the main thread sl@0: iRendererCallbackListener = CRendererCallbackListener::NewL(iObserver, aNumBuffers); sl@0: iRendererCallbackListener->Start(); sl@0: } sl@0: else if (iNumBuffers < aNumBuffers) sl@0: { sl@0: iRendererCallbackListener->Cancel(); sl@0: iRendererCallbackListener->ExtendMsgQueueL(aNumBuffers); sl@0: iRendererCallbackListener->Start(); sl@0: } sl@0: sl@0: // set function parameters sl@0: iCallingStatus = aRequestStatus; sl@0: iFunctionCode = EPrepare; sl@0: iSurfaceId = aSurfaceId; sl@0: iNumBuffers = aNumBuffers; sl@0: sl@0: // send request to renderer thread sl@0: TRequestStatus* rendererRequest = Status(); sl@0: iRendererThread->RequestComplete(rendererRequest, KErrNone); sl@0: } sl@0: sl@0: /* Prepare the object after a surface is created */ sl@0: void CRendererThreadRelay::RunPrepareL() sl@0: { sl@0: iRendererRelay->PrepareL(iSurfaceId, iNumBuffers, NULL); sl@0: } sl@0: sl@0: /** Run update buffer in renderer thread */ sl@0: void CRendererThreadRelay::RunUpdateBuffer(TVideoFrameBuffer* aBuffer, const TTime& aPresentationTime) sl@0: { sl@0: DEBUGPRINT1(_L("CRendererThreadRelay::RunUpdateBuffer entered")); sl@0: __ASSERT_DEBUG(iBufManager != NULL, User::Panic(_L("CRTR::RunUpdateBuffer"), KErrCorrupt)); sl@0: sl@0: /* sl@0: Buffer update is determined as follow: sl@0: sl@0: If wait list is empty (imply no active timer), always add buffer to list, sl@0: If a preceding buffer hasn't been displayed, the new buffer will be handled after display callback. sl@0: otherwise, decide whether timer should be started for submit update sl@0: If wait list is not empty (imply either timer is active or waiting for display callback) sl@0: If waiting for display callback, add new buffer to list and it will be handled after display callback. sl@0: (note: presentation time is not check because the new buffer may be newer than the waiting buffers even though both have passed due time) sl@0: If timer is active, first check if presentation time is zero. If so, display right away sl@0: 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 sl@0: otherwise, then check if presentation time < head list presentation time sl@0: if so, add the buffer to wait list, stop the timer and start timer with new time sl@0: else, just need to add buffer to wait list, the next timer expiry will hander the head buffer sl@0: */ sl@0: sl@0: if (iBufManager->WaitingListIsEmpty()) sl@0: { sl@0: iBufManager->UpdateBuffer(aBuffer, aPresentationTime); sl@0: sl@0: if (iRendererRelay->UpdateSubmitted() == EFalse) sl@0: { sl@0: // an update haven't been submitted, prepare to submit one now sl@0: iRendererRelay->SubmitBufferTimed(); sl@0: } sl@0: } sl@0: else sl@0: { sl@0: // waiting list is not empty sl@0: if (iRendererRelay->UpdateSubmitted()) sl@0: { sl@0: // waiting for listener callback, just update waiting list sl@0: iBufManager->UpdateBuffer(aBuffer, aPresentationTime); sl@0: } sl@0: else sl@0: { sl@0: // the timer is waiting sl@0: sl@0: if (aPresentationTime.Int64() == 0) sl@0: { sl@0: // presentation time is zero, display right away. sl@0: iRendererTimer->Cancel(); sl@0: iBufManager->UpdateBuffer(aBuffer, aPresentationTime); sl@0: iRendererRelay->SubmitBufferTimed(); sl@0: return; sl@0: } sl@0: sl@0: TTime now; sl@0: now.UniversalTime(); sl@0: TTimeIntervalMicroSeconds deltaFromNow = aPresentationTime.MicroSecondsFrom(now); sl@0: TInt64 waitTime = deltaFromNow.Int64() - iRendererRelay->Delay(); sl@0: sl@0: if (waitTime <= 0) sl@0: { sl@0: // the wait time has passed sl@0: if (deltaFromNow.Int64() + iMaxDelay >= 0) sl@0: { sl@0: // the frame is within maximun allowed delay, i.e. (presentation time + max delay >= now) sl@0: // display right away sl@0: iRendererTimer->Cancel(); sl@0: iBufManager->UpdateBuffer(aBuffer, aPresentationTime); sl@0: iRendererRelay->SubmitBufferTimed(); sl@0: } sl@0: else sl@0: { sl@0: // skip the buffer sl@0: iBufManager->ReleaseBuffer(aBuffer); sl@0: MvroBufferSkipped(aBuffer->BufferId()); sl@0: sl@0: //notifiy observer about the available buffers sl@0: MvroVideoBufferAvailable(); sl@0: } sl@0: } sl@0: else sl@0: { sl@0: // wait time has not passed, add to waiting list sl@0: if (iBufManager->UpdateBuffer(aBuffer, aPresentationTime)) sl@0: { sl@0: // head of waiting list has changed, start timer with new wait time sl@0: iRendererTimer->Cancel(); sl@0: iRendererTimer->Start(waitTime); sl@0: } sl@0: } sl@0: } sl@0: } sl@0: } sl@0: sl@0: /** Run destroy surface in renderer thread */ sl@0: void CRendererThreadRelay::RunDestroySurface() sl@0: { sl@0: iRendererTimer->Cancel(); sl@0: iRendererRelay->DestroySurface(NULL); sl@0: } sl@0: sl@0: /* Update buffer manager pointer */ sl@0: void CRendererThreadRelay::SetBufferManager(CRendererBufferManager* aBufManager) sl@0: { sl@0: TRequestStatus request = KRequestPending; sl@0: TRequestStatus logonRequest = KRequestPending; sl@0: sl@0: // While a function call is in progress this thread is suspended sl@0: // and the undertaker will not catch panics, listen for these here sl@0: iRendererThread->Logon(logonRequest); sl@0: sl@0: // Set the call parameters sl@0: iCallingStatus = &request; sl@0: iFunctionCode = ESetBufferManager; sl@0: iBufManager = aBufManager; sl@0: sl@0: // send request to renderer thread sl@0: TRequestStatus* rendererRequest = Status(); sl@0: iRendererThread->RequestComplete(rendererRequest, KErrNone); sl@0: sl@0: User::WaitForRequest(logonRequest, request); sl@0: sl@0: if(logonRequest != KRequestPending) sl@0: { sl@0: // renderer thread got panic from surface update session, so panic client sl@0: TInt reason = iRendererThread->ExitReason(); sl@0: TExitCategoryName category = iRendererThread->ExitCategory(); sl@0: User::Panic(category,reason); sl@0: } sl@0: sl@0: // Thread is still alive and well sl@0: iRendererThread->LogonCancel(logonRequest); sl@0: User::WaitForRequest(logonRequest); // Consume the signal sl@0: sl@0: __ASSERT_DEBUG(request != KRequestPending, User::Panic(_L("CRTR::SetBufferManager"), KErrCorrupt)); sl@0: } sl@0: sl@0: void CRendererThreadRelay::RunSetBufferManager() sl@0: { sl@0: iRendererRelay->SetBufferManager(iBufManager); sl@0: } sl@0: sl@0: /** Store a pointer to the renderer thread object. */ sl@0: void CRendererThreadRelay::SetRendererThread(RThread* aRendererThread) sl@0: { sl@0: iRendererThread = aRendererThread; sl@0: } sl@0: sl@0: /** Return a pointer to the function call listener's request status. */ sl@0: TRequestStatus* CRendererThreadRelay::Status() sl@0: { sl@0: return &iStatus; sl@0: } sl@0: sl@0: /** Set timer info for timed mode */ sl@0: void CRendererThreadRelay::SetTimerInfo(TInt64 aDefaultDelay, TInt64 aMaxDelay) sl@0: { sl@0: iMaxDelay = aMaxDelay; sl@0: iRendererRelay->SetTimerInfo(aDefaultDelay, aMaxDelay); sl@0: } sl@0: sl@0: void CRendererThreadRelay::MvroVideoBufferAvailable() sl@0: { sl@0: iRendererCallbackListener->SendCallback(CRendererCallbackListener::EBufferAvailable, -1, 0); sl@0: } sl@0: sl@0: void CRendererThreadRelay::MvroBufferDisplayed(TInt aBufferId, const TTime& aTime) sl@0: { sl@0: iRendererCallbackListener->SendCallback(CRendererCallbackListener::EBufferDisplayed, aBufferId, aTime); sl@0: } sl@0: sl@0: void CRendererThreadRelay::MvroBufferSkipped(TInt aBufferId) sl@0: { sl@0: iRendererCallbackListener->SendCallback(CRendererCallbackListener::EBufferSkipped, aBufferId, 0); sl@0: } sl@0: sl@0: sl@0: /** Private constructor */ sl@0: CRendererCallbackListener::CRendererCallbackListener(MVideoRendererObserver& aObserver) sl@0: : CActive(EPriorityStandard), sl@0: iObserver(aObserver) sl@0: { sl@0: CActiveScheduler::Add(this); sl@0: } sl@0: sl@0: /** Two-phased constructor */ sl@0: CRendererCallbackListener* CRendererCallbackListener::NewL(MVideoRendererObserver& aObserver, TInt aNumBuffer) sl@0: { sl@0: CRendererCallbackListener* self = new (ELeave) CRendererCallbackListener(aObserver); sl@0: CleanupStack::PushL(self); sl@0: self->ConstructL(aNumBuffer); sl@0: CleanupStack::Pop(self); sl@0: return self; sl@0: } sl@0: sl@0: /** Second-phase constructor */ sl@0: void CRendererCallbackListener::ConstructL(TInt aNumBuffer) sl@0: { sl@0: // There could potentially be three messages outstanding per buffer sl@0: // size message queue accordingly sl@0: TInt slot = aNumBuffer * 3; sl@0: if (slot > RMsgQueueBase::KMaxLength) sl@0: { sl@0: slot = RMsgQueueBase::KMaxLength; sl@0: } sl@0: User::LeaveIfError(iMsgQueue.CreateLocal(slot)); sl@0: } sl@0: sl@0: CRendererCallbackListener::~CRendererCallbackListener() sl@0: { sl@0: Cancel(); // Cancel any request, if outstanding sl@0: iMsgQueue.Close(); sl@0: } sl@0: sl@0: void CRendererCallbackListener::ExtendMsgQueueL(TInt aNumBuffer) sl@0: { sl@0: // close the message queue and construct another one sl@0: iMsgQueue.Close(); sl@0: ConstructL(aNumBuffer); sl@0: } sl@0: sl@0: void CRendererCallbackListener::DoCancel() sl@0: { sl@0: iMsgQueue.CancelDataAvailable(); sl@0: } sl@0: sl@0: /** Start listener */ sl@0: void CRendererCallbackListener::Start() sl@0: { sl@0: if (!IsActive()) sl@0: { sl@0: iMsgQueue.NotifyDataAvailable(iStatus); sl@0: SetActive(); sl@0: } sl@0: } sl@0: sl@0: /** Set the callback function */ sl@0: void CRendererCallbackListener::SendCallback(TFunctionCode aFunctionCode, TInt aBufferId, const TTime& aTime) sl@0: { sl@0: DEBUGPRINT2(_L("CRendererCallbackListener::SendCallback entered aFunctionCode=%d"), aFunctionCode); sl@0: sl@0: TCallbackData data; sl@0: data.iFunctionCode = aFunctionCode; sl@0: data.iBufferId = aBufferId; sl@0: data.iTime = aTime; sl@0: sl@0: iMsgQueue.Send(data); sl@0: } sl@0: sl@0: /** Call the callback function within the main thread */ sl@0: void CRendererCallbackListener::RunL() sl@0: { sl@0: TCallbackData data; sl@0: TInt err = iMsgQueue.Receive(data); sl@0: if (err == KErrNone) sl@0: { sl@0: if (data.iFunctionCode == EBufferAvailable) sl@0: { sl@0: DEBUGPRINT1(_L("CRendererCallbackListener::RunL - EBufferAvailable")); sl@0: iObserver.MvroVideoBufferAvailable(); sl@0: } sl@0: else if (data.iFunctionCode == EBufferDisplayed) sl@0: { sl@0: DEBUGPRINT1(_L("CRendererCallbackListener::RunL - EBufferDisplayed")); sl@0: iObserver.MvroBufferDisplayed(data.iBufferId, data.iTime); sl@0: } sl@0: else if (data.iFunctionCode == EBufferSkipped) sl@0: { sl@0: DEBUGPRINT1(_L("CRendererCallbackListener::RunL - EBufferSkipped")); sl@0: iObserver.MvroBufferSkipped(data.iBufferId); sl@0: } sl@0: } sl@0: else sl@0: { sl@0: DEBUGPRINT2(_L("CRendererCallbackListener::RunL err=%d"), err); sl@0: } sl@0: sl@0: Start(); sl@0: }