os/mm/mmswadaptation/videorenderer/src/rendererrelay.cpp
changeset 0 bde4ae8d615e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/os/mm/mmswadaptation/videorenderer/src/rendererrelay.cpp	Fri Jun 15 03:10:57 2012 +0200
     1.3 @@ -0,0 +1,815 @@
     1.4 +// Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
     1.5 +// All rights reserved.
     1.6 +// This component and the accompanying materials are made available
     1.7 +// under the terms of "Eclipse Public License v1.0"
     1.8 +// which accompanies this distribution, and is available
     1.9 +// at the URL "http://www.eclipse.org/legal/epl-v10.html".
    1.10 +//
    1.11 +// Initial Contributors:
    1.12 +// Nokia Corporation - initial contribution.
    1.13 +//
    1.14 +// Contributors:
    1.15 +//
    1.16 +// Description:
    1.17 +//
    1.18 +
    1.19 +/**
    1.20 + @file
    1.21 + @internalComponent
    1.22 +*/
    1.23 +
    1.24 +#include <hal.h>
    1.25 +#include <graphics/suerror.h>
    1.26 +#include "rendererrelay.h"
    1.27 +#include "buflistener.h"
    1.28 +#include "buffermanager.h"
    1.29 +#include "videoframebuffer.h"
    1.30 +#include "rendererutil.h"
    1.31 +#include "renderertimer.h"
    1.32 +
    1.33 +/** static factory contruction */
    1.34 +CRendererRelay* CRendererRelay::NewL(MVideoRendererObserver& aObserver)
    1.35 +	{
    1.36 +	CRendererRelay* self = new (ELeave) CRendererRelay(aObserver);
    1.37 +	CleanupStack::PushL(self);
    1.38 +	self->ConstructL();
    1.39 +	CleanupStack::Pop(self);
    1.40 +	return self;
    1.41 +	}
    1.42 +
    1.43 +CRendererRelay::CRendererRelay(MVideoRendererObserver& aObserver)
    1.44 +:iObserver(aObserver), 
    1.45 + iBufAvailListeners(CBufAvailListener::iOffset), 
    1.46 + iAvailListenersIter(iBufAvailListeners)
    1.47 +	{
    1.48 +	}
    1.49 +
    1.50 +void CRendererRelay::ConstructL()
    1.51 +	{
    1.52 +	User::LeaveIfError(iSurfaceUpdateSession.Connect());
    1.53 +	User::LeaveIfError(HAL::Get(HAL::EFastCounterFrequency, iFastCounterFrequency));
    1.54 +	User::LeaveIfError(HAL::Get(HAL::EFastCounterCountsUp, iFastCounterCountsUp));
    1.55 +	}
    1.56 +
    1.57 +/** Destructor */
    1.58 +CRendererRelay::~CRendererRelay()
    1.59 +	{
    1.60 +	delete iDisplayListener;
    1.61 +	
    1.62 +	iAvailListenersIter.SetToFirst();
    1.63 +	CBufAvailListener* listener = NULL;
    1.64 +	while ((listener = iAvailListenersIter++) != NULL)
    1.65 +		{
    1.66 +		listener->iDblQueLink.Deque();
    1.67 +		delete listener;
    1.68 +		}
    1.69 +	
    1.70 +	iSurfaceUpdateSession.Close();
    1.71 +	}
    1.72 +
    1.73 +/** Update buffer manager pointer in this class and listeners */
    1.74 +void CRendererRelay::SetBufferManager(CRendererBufferManager* aBufManager)
    1.75 +	{
    1.76 +	iBufManager = aBufManager;
    1.77 +	
    1.78 +	// change buffer manager pointer for listeners
    1.79 +	iAvailListenersIter.SetToFirst();
    1.80 +	CBufAvailListener* listener = NULL;
    1.81 +	while ((listener = iAvailListenersIter++) != NULL)
    1.82 +		{
    1.83 +		listener->SetBufferManager(aBufManager);
    1.84 +		
    1.85 +		if (!aBufManager && listener->IsAdded())
    1.86 +			{
    1.87 +			listener->Deque();
    1.88 +			}
    1.89 +		else if (aBufManager && !listener->IsAdded())
    1.90 +			{
    1.91 +			CActiveScheduler::Add(listener);
    1.92 +			}
    1.93 +		}
    1.94 +	}
    1.95 +
    1.96 +/** Return the next unused CBufAvailListener. */
    1.97 +CBufAvailListener* CRendererRelay::BufAvailListener()
    1.98 +	{
    1.99 +	CBufAvailListener* listener = NULL;
   1.100 +	iAvailListenersIter.SetToFirst();
   1.101 +	while ((listener = iAvailListenersIter++) != NULL)
   1.102 +		{
   1.103 +		if (listener->IsActive() == EFalse)
   1.104 +			{
   1.105 +			// Move to end so that the next search is a bit faster
   1.106 +			listener->iDblQueLink.Deque();
   1.107 +			iBufAvailListeners.AddLast(*listener);
   1.108 +			return listener;
   1.109 +			}
   1.110 +		}
   1.111 +
   1.112 +	// Should not reach here as the number of listeners is same as number of buffer
   1.113 +	__ASSERT_DEBUG(EFalse, User::Panic(_L("CRR::BufAvailListener"), KErrUnknown));
   1.114 +	return listener;
   1.115 +	}
   1.116 +
   1.117 +/** Set update parameter and create listeners */
   1.118 +void CRendererRelay::PrepareL(const TSurfaceId& aSurfaceId, TInt aNumBuffers, TRequestStatus* /*aRequestStatus*/)
   1.119 +	{
   1.120 +	iUpdateSubmitted = EFalse;
   1.121 +	iSurfaceId = aSurfaceId;
   1.122 +	
   1.123 +	if (!iDisplayListener)
   1.124 +		{
   1.125 +		iDisplayListener = CBufDisplayListener::NewL(iObserver, iSurfaceUpdateSession, *this, iFastCounterFrequency, iFastCounterCountsUp);
   1.126 +		}
   1.127 +
   1.128 +	TInt numListeners = 0;
   1.129 +	iAvailListenersIter.SetToFirst();
   1.130 +	while (iAvailListenersIter++ != NULL)
   1.131 +		{
   1.132 +		numListeners++;
   1.133 +		}
   1.134 +	
   1.135 +	CBufAvailListener* listener = NULL;
   1.136 +	// create new listeners if there are more numBuffers than listeners
   1.137 +	for (TInt i = numListeners; i < aNumBuffers; ++i)
   1.138 +		{
   1.139 +		listener = CBufAvailListener::NewL(iObserver, iSurfaceUpdateSession);
   1.140 +		iBufAvailListeners.AddFirst(*listener);
   1.141 +		}
   1.142 +	}
   1.143 +
   1.144 +/** Handle update buffer request in non-timed mode */
   1.145 +void CRendererRelay::UpdateBuffer(TVideoFrameBuffer* aBuffer, const TTime& aPresentationTime, TRequestStatus* /* aRequestStatus */)
   1.146 +	{
   1.147 +	__ASSERT_DEBUG(iBufManager != NULL, User::Panic(_L("CRR::UpdateBuffer"), KErrNotReady));
   1.148 +	
   1.149 +	iBufManager->UpdateBuffer(aBuffer, aPresentationTime);
   1.150 +	
   1.151 +	if (iUpdateSubmitted == EFalse)
   1.152 +		{
   1.153 +		// an update haven't been submitted, submit one now
   1.154 +		SubmitBuffer();
   1.155 +		}
   1.156 +	}
   1.157 +
   1.158 +/** Callback from display listener that a buffer has been displayed or skipped*/
   1.159 +void CRendererRelay::BufferDisplayed(TBool aDisplayed, TInt64 aDelay)
   1.160 +	{
   1.161 +	DEBUGPRINT3(_L("CRendererRelay::BufferDisplayed entered aDisplayed=%d, aDelay=%Ld"), aDisplayed, aDelay);
   1.162 +
   1.163 +	iUpdateSubmitted = EFalse;
   1.164 +	
   1.165 +	if (iRendererTimer)
   1.166 +		{
   1.167 +		// rendering in timed mode
   1.168 +		if (aDisplayed)
   1.169 +			{
   1.170 +			// update delay value
   1.171 +			iDelay = aDelay;
   1.172 +			}
   1.173 +		SubmitBufferTimed();
   1.174 +		}
   1.175 +	else
   1.176 +		{
   1.177 +		// rendering in non timed mode
   1.178 +		SubmitBuffer();
   1.179 +		}
   1.180 +	}
   1.181 +
   1.182 +/** Submit the next buffer that is waiting to be submitted in non-timed mode*/
   1.183 +void CRendererRelay::SubmitBuffer()
   1.184 +	{
   1.185 +	DEBUGPRINT1(_L("CRendererRelay::SubmitBuffer entered"));
   1.186 +	__ASSERT_DEBUG(iBufManager != NULL, User::Panic(_L("CRR::SubmitBuffer"), KErrCorrupt));
   1.187 +	__ASSERT_DEBUG(iUpdateSubmitted == EFalse, User::Panic(_L("CRR::SubmitBuffer"), KErrGeneral));
   1.188 +
   1.189 +	TBool lastBuf;
   1.190 +	TVideoFrameBuffer* buf = iBufManager->WaitingBuffer(ETrue, lastBuf);
   1.191 +	TInt numBufferSkipped = 0;
   1.192 +	
   1.193 +	TTime now;
   1.194 +	now.UniversalTime();
   1.195 +	TUint32 fastCounter = User::FastCounter();
   1.196 +	while (buf != NULL)
   1.197 +		{
   1.198 +		TInt bufId = buf->BufferId();
   1.199 +		
   1.200 +		DEBUGPRINT4(_L("bufId=%d presTime=%Ld, now=%Ld"), 
   1.201 +					bufId, buf->PresentationTime().Int64(), now.Int64());
   1.202 +
   1.203 +		// submit the buffer for update if presntation time is not in past 
   1.204 +		// or if the buffer is the last in queue or presentation time is zero
   1.205 +		if (buf->PresentationTime() >= now || lastBuf || buf->PresentationTime().Int64() == 0)
   1.206 +			{
   1.207 +			DoUpdateBuffer(bufId, now, fastCounter);
   1.208 +			break;
   1.209 +			}
   1.210 +		
   1.211 +		// The buffer presentation time occurs in past if codeflow reaches here.
   1.212 +		// Change the buffer status to available and notify observer about the skipped buffer
   1.213 +		iBufManager->BufferAvailable(bufId);
   1.214 +		iObserver.MvroBufferSkipped(bufId);
   1.215 +		numBufferSkipped++;
   1.216 +
   1.217 +		// get next buffer
   1.218 +		buf = iBufManager->WaitingBuffer(ETrue, lastBuf);
   1.219 +		}
   1.220 +	
   1.221 +	//notifiy observer about the available buffers
   1.222 +	for (TInt i = 0; i < numBufferSkipped; ++i)
   1.223 +		{
   1.224 +		iObserver.MvroVideoBufferAvailable();
   1.225 +		}
   1.226 +	}
   1.227 +
   1.228 +/** Set a pointer to renderer timer in timed mode */
   1.229 +void CRendererRelay::SetRendererTimer(CRendererTimer* aRendererTimer)
   1.230 +	{
   1.231 +	iRendererTimer = aRendererTimer;
   1.232 +	}
   1.233 +
   1.234 +/** Callback function when a renderer timer has expired */
   1.235 +void CRendererRelay::RendererTimerExpired()
   1.236 +	{
   1.237 +	SubmitBufferTimed();
   1.238 +	}
   1.239 +
   1.240 +/** Submit the next buffer in timed mode */
   1.241 +void CRendererRelay::SubmitBufferTimed()
   1.242 +	{
   1.243 +	DEBUGPRINT1(_L("CRendererRelay::SubmitBufferTimed entered"));
   1.244 +
   1.245 +	__ASSERT_DEBUG(iBufManager != NULL, User::Panic(_L("CRR::SubmitBufferTimed"), KErrCorrupt));
   1.246 +	__ASSERT_DEBUG(iUpdateSubmitted == EFalse, 
   1.247 +			User::Panic(_L("CRR::SubmitBufferTimed"), KErrGeneral));
   1.248 +	__ASSERT_DEBUG(iRendererTimer && iRendererTimer->IsActive() == EFalse, 
   1.249 +					User::Panic(_L("CRR::SubmitBufferTimed"), KErrInUse));
   1.250 +	
   1.251 +	TBool lastBuf;
   1.252 +	TVideoFrameBuffer* buf = iBufManager->WaitingBuffer(EFalse, lastBuf);
   1.253 +	TInt numBufferSkipped = 0;
   1.254 +	
   1.255 +	TTime now;
   1.256 +	now.UniversalTime();
   1.257 +	TUint32 fastCounter = User::FastCounter();
   1.258 +	while (buf != NULL)
   1.259 +		{
   1.260 +		TInt bufId = buf->BufferId();
   1.261 +		TTimeIntervalMicroSeconds deltaFromNow = buf->PresentationTime().MicroSecondsFrom(now);
   1.262 +		
   1.263 +		TInt64 waitTime = 0;
   1.264 +		if (buf->PresentationTime().Int64() != 0)
   1.265 +			{
   1.266 +			// presentation time is not zero, calculate wait time. Otherwise, wait time is zero.
   1.267 +			waitTime = deltaFromNow.Int64() - iDelay;
   1.268 +			}
   1.269 +		
   1.270 +		DEBUGPRINT4(_L("bufId=%d presTime=%Ld, now=%Ld"), 
   1.271 +				bufId, buf->PresentationTime().Int64(), now.Int64());
   1.272 +
   1.273 +		// submit the buffer for update if presntation time is not in past 
   1.274 +		// or if the buffer is the last in queue
   1.275 +		if (waitTime > 0)
   1.276 +			{
   1.277 +			iRendererTimer->Start(waitTime);
   1.278 +			break;
   1.279 +			}
   1.280 +		else if (buf->PresentationTime().Int64() == 0 ||
   1.281 +					deltaFromNow.Int64() + iMaxDelay >= 0 || 
   1.282 +					lastBuf)
   1.283 +			{
   1.284 +			// if presentation time is zero (waitTime is not used for checking because it may be zero from calculation)
   1.285 +			// or the frame is within maximun allowed delay, i.e. (presentation time + max delay >= now)
   1.286 +			// or submission time has passed but this is the last buf, submit the buffer now
   1.287 +
   1.288 +			iBufManager->BufferSubmitted(buf);
   1.289 +			DoUpdateBuffer(bufId, now, fastCounter);
   1.290 +			break;
   1.291 +			}
   1.292 +		
   1.293 +		// The buffer presentation time has passed maxDelay if codeflow reaches here, skip the buffer.
   1.294 +		// Change the buffer status to available and notify observer
   1.295 +		iBufManager->BufferAvailable(bufId);
   1.296 +		iObserver.MvroBufferSkipped(bufId);
   1.297 +		numBufferSkipped++;
   1.298 +
   1.299 +		// get next buffer
   1.300 +		buf = iBufManager->WaitingBuffer(EFalse, lastBuf);
   1.301 +		}
   1.302 +	
   1.303 +	//notifiy observer about the available buffers
   1.304 +	for (TInt i = 0; i < numBufferSkipped; ++i)
   1.305 +		{
   1.306 +		iObserver.MvroVideoBufferAvailable();
   1.307 +		}
   1.308 +	}
   1.309 +
   1.310 +/**
   1.311 +Submits a buffer to be updated at the specified time.
   1.312 +*/
   1.313 +
   1.314 +void CRendererRelay::DoUpdateBuffer(TInt aBufferId, const TTime& aTime, TUint32 aFastCounter)
   1.315 +	{
   1.316 +	CBufAvailListener* availListener = BufAvailListener();
   1.317 +			
   1.318 +	availListener->Start(aBufferId);
   1.319 +	iDisplayListener->Start(aBufferId, aTime, aFastCounter);
   1.320 +	
   1.321 +	TInt err = iSurfaceUpdateSession.SubmitUpdate(KAllScreens, iSurfaceId, aBufferId);
   1.322 +
   1.323 +	DEBUGPRINT2(_L("SubmitUpdate return %d"), err);
   1.324 +			
   1.325 +	// error will also be returned from listener, so the next submit updated will be triggered by display listener
   1.326 +	iUpdateSubmitted = ETrue;
   1.327 +	}
   1.328 +
   1.329 +/** 
   1.330 +Return ETrue if an update has been submitted and the display notification 
   1.331 +haven't been received yet. i.e. Need to wait till for listener callback before 
   1.332 +the next buffer can be submitted.
   1.333 +*/
   1.334 +TBool CRendererRelay::UpdateSubmitted()
   1.335 +	{
   1.336 +	return iUpdateSubmitted;
   1.337 +	}
   1.338 +
   1.339 +/** return the delay for surface update */
   1.340 +TInt64 CRendererRelay::Delay()
   1.341 +	{
   1.342 +	return iDelay;
   1.343 +	}
   1.344 +
   1.345 +/** Cancel all update notification when a surface is destroyed */
   1.346 +void CRendererRelay::DestroySurface(TRequestStatus* /* aRequestStatus */ )
   1.347 +	{
   1.348 +	iSurfaceUpdateSession.CancelAllUpdateNotifications();
   1.349 +	}
   1.350 +
   1.351 +/* This function is not used */
   1.352 +void CRendererRelay::SetRendererThread(RThread* /* aRendererThread */)
   1.353 +	{
   1.354 +	__ASSERT_DEBUG(EFalse, User::Panic(_L("CRR::SetRendererThread"), KErrUnknown));
   1.355 +	}
   1.356 +
   1.357 +/* This function is not used */
   1.358 +void CRendererRelay::Terminate(TRequestStatus& /* aRequestStatus */)
   1.359 +	{
   1.360 +	__ASSERT_DEBUG(EFalse, User::Panic(_L("CRR::Terminate"), KErrUnknown));
   1.361 +	}
   1.362 +
   1.363 +/** Set timer info for timed mode, this function is not called in non-timed mode */
   1.364 +void CRendererRelay::SetTimerInfo(TInt64 aDefaultDelay, TInt64 aMaxDelay)
   1.365 +	{
   1.366 +	iDelay = aDefaultDelay;
   1.367 +	iMaxDelay = aMaxDelay;
   1.368 +	}
   1.369 +
   1.370 +/** Two-phased constructor. */
   1.371 +CRendererThreadRelay* CRendererThreadRelay::NewL(MVideoRendererObserver& aObserver, TThreadId aMainThreadId)
   1.372 +	{
   1.373 +	CRendererThreadRelay* self = new (ELeave) CRendererThreadRelay(aObserver);
   1.374 +	CleanupStack::PushL(self);
   1.375 +	self->ConstructL(aMainThreadId);
   1.376 +	CleanupStack::Pop(self);
   1.377 +	return self;
   1.378 +	}
   1.379 +
   1.380 +CRendererThreadRelay::CRendererThreadRelay(MVideoRendererObserver& aObserver) 
   1.381 +: CActive(EPriorityStandard), iObserver(aObserver)
   1.382 +	{
   1.383 +	CActiveScheduler::Add(this);
   1.384 +	}
   1.385 +
   1.386 +/** Second-phase constructor */
   1.387 +void CRendererThreadRelay::ConstructL(TThreadId aMainThreadId)
   1.388 +	{
   1.389 +	User::LeaveIfError(iMainThread.Open(aMainThreadId));
   1.390 +	iRendererRelay = CRendererRelay::NewL(*this);
   1.391 +	iRendererTimer = CRendererTimer::NewL(*iRendererRelay);
   1.392 +	iRendererRelay->SetRendererTimer(iRendererTimer);
   1.393 +	}
   1.394 +
   1.395 +/** 
   1.396 +Destructor. 
   1.397 +This active object is always cancelled by main thread so no need to cancel this active object here
   1.398 +*/
   1.399 +CRendererThreadRelay::~CRendererThreadRelay()
   1.400 +	{
   1.401 +	delete iRendererCallbackListener;
   1.402 +	delete iRendererTimer;
   1.403 +	delete iRendererRelay;
   1.404 +	iMainThread.Close();
   1.405 +	}
   1.406 +
   1.407 +void CRendererThreadRelay::DoCancel()
   1.408 +	{
   1.409 +	// Don't need to do anything, will be stopped by main thread
   1.410 +	}
   1.411 +
   1.412 +/** Function for making the initial request */
   1.413 +void CRendererThreadRelay::Start()
   1.414 +	{
   1.415 +	__ASSERT_DEBUG(IsActive() == EFalse, User::Panic(_L("CRTR::Start"), KErrInUse));
   1.416 +	iStatus = KRequestPending;
   1.417 +	SetActive();
   1.418 +	}
   1.419 +
   1.420 +/** Handle requests from main thread */
   1.421 +void CRendererThreadRelay::RunL()
   1.422 +	{
   1.423 +	__ASSERT_DEBUG(iStatus == KErrNone, User::Panic(_L("CRTR::RunL"), KErrUnknown));
   1.424 +	TInt result = KErrNone;
   1.425 +
   1.426 +	if (iFunctionCode == ESubmitUpdate)
   1.427 +		{
   1.428 +		Start();
   1.429 +		RunUpdateBuffer(iBuffer, iPresentationTime);
   1.430 +		}
   1.431 +	else if (iFunctionCode == EDestroySurface)
   1.432 +		{
   1.433 +		Start();
   1.434 +		RunDestroySurface();
   1.435 +		}
   1.436 +	else if (iFunctionCode == EPrepare)
   1.437 +		{
   1.438 +		Start();
   1.439 +		TRAP(result, RunPrepareL());
   1.440 +		}
   1.441 +	else if (iFunctionCode == ESetBufferManager)
   1.442 +		{
   1.443 +		Start();
   1.444 +		RunSetBufferManager();
   1.445 +		}
   1.446 +	else // iFunctionCode == ETermination
   1.447 +		{
   1.448 +		CActiveScheduler::Stop();
   1.449 +		}
   1.450 +
   1.451 +	TRequestStatus *status = iCallingStatus;
   1.452 +	iMainThread.RequestComplete(status, result);
   1.453 +	}
   1.454 +
   1.455 +/** Send a signal to the main thread to indicate that the thread startup was successful. */
   1.456 +void CRendererThreadRelay::SignalSetupComplete(TRequestStatus* aSetupComplete)
   1.457 +	{
   1.458 +	iMainThread.RequestComplete(aSetupComplete, KErrNone);
   1.459 +	}
   1.460 +
   1.461 +/** Send update buffer request from main thread to renderer thread */
   1.462 +void CRendererThreadRelay::UpdateBuffer(TVideoFrameBuffer* aBuffer, const TTime& aPresentationTime, TRequestStatus* aRequestStatus)
   1.463 +	{
   1.464 +	__ASSERT_DEBUG(aRequestStatus != NULL, User::Panic(_L("CRTR::UpdateBuffer"), KErrArgument));
   1.465 +
   1.466 +	// set function parameters
   1.467 +	iCallingStatus = aRequestStatus;
   1.468 +	iFunctionCode = ESubmitUpdate;
   1.469 +	iBuffer = aBuffer;
   1.470 +	iPresentationTime = aPresentationTime;
   1.471 +	
   1.472 +	// send request to renderer thread
   1.473 +	TRequestStatus* rendererRequest = Status();
   1.474 +	iRendererThread->RequestComplete(rendererRequest, KErrNone);
   1.475 +	}
   1.476 +
   1.477 +/** Send terminate renderer thread request from main thread to renderer thread */
   1.478 +void CRendererThreadRelay::Terminate(TRequestStatus& aRequestStatus)
   1.479 +	{
   1.480 +	iCallingStatus = &aRequestStatus;
   1.481 +	iFunctionCode = ETermination;
   1.482 +	
   1.483 +	if (iRendererCallbackListener)
   1.484 +		{
   1.485 +		iRendererCallbackListener->Cancel();
   1.486 +		}
   1.487 +	
   1.488 +	// send request to renderer thread
   1.489 +	TRequestStatus* rendererRequest = Status();
   1.490 +	iRendererThread->RequestComplete(rendererRequest, KErrNone);
   1.491 +	}
   1.492 +
   1.493 +/** Send destroy surface request from main thread to renderer thread */
   1.494 +void CRendererThreadRelay::DestroySurface(TRequestStatus* aRequestStatus)
   1.495 +	{
   1.496 +	__ASSERT_DEBUG(aRequestStatus != NULL, User::Panic(_L("CRTR::DestroySurface"), KErrArgument));
   1.497 +	
   1.498 +	iCallingStatus = aRequestStatus;
   1.499 +	iFunctionCode = EDestroySurface;
   1.500 +	
   1.501 +	// send request to renderer thread
   1.502 +	TRequestStatus* rendererRequest = Status();
   1.503 +	iRendererThread->RequestComplete(rendererRequest, KErrNone);
   1.504 +	}
   1.505 +
   1.506 +/* Prepare the object after a surface is created */
   1.507 +void CRendererThreadRelay::PrepareL(const TSurfaceId& aSurfaceId, TInt aNumBuffers, TRequestStatus* aRequestStatus)
   1.508 +	{
   1.509 +	__ASSERT_DEBUG(aRequestStatus != NULL, User::Panic(_L("CRTR::PrepareL"), KErrArgument));
   1.510 +	
   1.511 +	if(!iRendererCallbackListener)
   1.512 +		{
   1.513 +		// first, create callback listener in the main thread
   1.514 +		iRendererCallbackListener = CRendererCallbackListener::NewL(iObserver, aNumBuffers);
   1.515 +		iRendererCallbackListener->Start();		
   1.516 +		}
   1.517 +	else if (iNumBuffers < aNumBuffers)
   1.518 +		{
   1.519 +		iRendererCallbackListener->Cancel();
   1.520 +		iRendererCallbackListener->ExtendMsgQueueL(aNumBuffers);
   1.521 +		iRendererCallbackListener->Start();		
   1.522 +		}
   1.523 +
   1.524 +	// set function parameters
   1.525 +	iCallingStatus = aRequestStatus;
   1.526 +	iFunctionCode = EPrepare;
   1.527 +	iSurfaceId = aSurfaceId;
   1.528 +	iNumBuffers = aNumBuffers;
   1.529 +	
   1.530 +	// send request to renderer thread
   1.531 +	TRequestStatus* rendererRequest = Status();
   1.532 +	iRendererThread->RequestComplete(rendererRequest, KErrNone);
   1.533 +	}
   1.534 +
   1.535 +/* Prepare the object after a surface is created */
   1.536 +void CRendererThreadRelay::RunPrepareL()
   1.537 +	{
   1.538 +	iRendererRelay->PrepareL(iSurfaceId, iNumBuffers, NULL);
   1.539 +	}
   1.540 +
   1.541 +/** Run update buffer in renderer thread */
   1.542 +void CRendererThreadRelay::RunUpdateBuffer(TVideoFrameBuffer* aBuffer, const TTime& aPresentationTime)
   1.543 +	{
   1.544 +	DEBUGPRINT1(_L("CRendererThreadRelay::RunUpdateBuffer entered"));
   1.545 +	__ASSERT_DEBUG(iBufManager != NULL, User::Panic(_L("CRTR::RunUpdateBuffer"), KErrCorrupt));
   1.546 +
   1.547 +	/*
   1.548 +	Buffer update is determined as follow:
   1.549 +	 
   1.550 +	If wait list is empty (imply no active timer), always add buffer to list, 
   1.551 +		If a preceding buffer hasn't been displayed, the new buffer will be handled after display callback.
   1.552 +			otherwise, decide whether timer should be started for submit update
   1.553 +	If wait list is not empty (imply either timer is active or waiting for display callback)
   1.554 +		If waiting for display callback, add new buffer to list and it will be handled after display callback.
   1.555 +			(note: presentation time is not check because the new buffer may be newer than the waiting buffers even though both have passed due time)
   1.556 +		If timer is active, first check if presentation time is zero. If so, display right away
   1.557 +			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
   1.558 +			otherwise, then check if presentation time < head list presentation time
   1.559 +				if so, add the buffer to wait list, stop the timer and start timer with new time
   1.560 +				else, just need to add buffer to wait list, the next timer expiry will hander the head buffer
   1.561 +	*/
   1.562 +	
   1.563 +	if (iBufManager->WaitingListIsEmpty())
   1.564 +		{
   1.565 +		iBufManager->UpdateBuffer(aBuffer, aPresentationTime);
   1.566 +
   1.567 +		if (iRendererRelay->UpdateSubmitted() == EFalse)
   1.568 +			{
   1.569 +			// an update haven't been submitted, prepare to submit one now
   1.570 +			iRendererRelay->SubmitBufferTimed();
   1.571 +			}
   1.572 +		}
   1.573 +	else
   1.574 +		{
   1.575 +		// waiting list is not empty
   1.576 +		if (iRendererRelay->UpdateSubmitted())
   1.577 +			{
   1.578 +			// waiting for listener callback, just update waiting list
   1.579 +			iBufManager->UpdateBuffer(aBuffer, aPresentationTime);
   1.580 +			}
   1.581 +		else
   1.582 +			{
   1.583 +			// the timer is waiting
   1.584 +
   1.585 +			if (aPresentationTime.Int64() == 0)
   1.586 +				{
   1.587 +				// presentation time is zero, display right away.
   1.588 +				iRendererTimer->Cancel();
   1.589 +				iBufManager->UpdateBuffer(aBuffer, aPresentationTime);
   1.590 +				iRendererRelay->SubmitBufferTimed();
   1.591 +				return;
   1.592 +				}
   1.593 +
   1.594 +			TTime now;
   1.595 +			now.UniversalTime();
   1.596 +			TTimeIntervalMicroSeconds deltaFromNow = aPresentationTime.MicroSecondsFrom(now);
   1.597 +			TInt64 waitTime = deltaFromNow.Int64() - iRendererRelay->Delay();
   1.598 +				
   1.599 +			if (waitTime <= 0)
   1.600 +				{
   1.601 +				// the wait time has passed
   1.602 +				if (deltaFromNow.Int64() + iMaxDelay >= 0)
   1.603 +					{
   1.604 +					// the frame is within maximun allowed delay, i.e. (presentation time + max delay >= now)
   1.605 +					// display right away
   1.606 +					iRendererTimer->Cancel();
   1.607 +					iBufManager->UpdateBuffer(aBuffer, aPresentationTime);
   1.608 +					iRendererRelay->SubmitBufferTimed();
   1.609 +					}
   1.610 +				else
   1.611 +					{
   1.612 +					// skip the buffer
   1.613 +					iBufManager->ReleaseBuffer(aBuffer);
   1.614 +					MvroBufferSkipped(aBuffer->BufferId());
   1.615 +	
   1.616 +					//notifiy observer about the available buffers
   1.617 +					MvroVideoBufferAvailable();
   1.618 +					}
   1.619 +				}
   1.620 +			else
   1.621 +				{
   1.622 +				// wait time has not passed, add to waiting list
   1.623 +				if (iBufManager->UpdateBuffer(aBuffer, aPresentationTime))
   1.624 +					{
   1.625 +					// head of waiting list has changed, start timer with new wait time
   1.626 +					iRendererTimer->Cancel();
   1.627 +					iRendererTimer->Start(waitTime);
   1.628 +					}
   1.629 +				}
   1.630 +			}
   1.631 +		}
   1.632 +	}
   1.633 +
   1.634 +/** Run destroy surface in renderer thread */
   1.635 +void CRendererThreadRelay::RunDestroySurface()
   1.636 +	{
   1.637 +	iRendererTimer->Cancel();
   1.638 +	iRendererRelay->DestroySurface(NULL);
   1.639 +	}
   1.640 +
   1.641 +/* Update buffer manager pointer */
   1.642 +void CRendererThreadRelay::SetBufferManager(CRendererBufferManager* aBufManager)
   1.643 +	{
   1.644 +		TRequestStatus request = KRequestPending;
   1.645 +		TRequestStatus logonRequest = KRequestPending;
   1.646 +
   1.647 +		// While a function call is in progress this thread is suspended
   1.648 +		// and the undertaker will not catch panics, listen for these here
   1.649 +		iRendererThread->Logon(logonRequest);
   1.650 +
   1.651 +		// Set the call parameters
   1.652 +		iCallingStatus = &request;
   1.653 +		iFunctionCode = ESetBufferManager;
   1.654 +		iBufManager = aBufManager;
   1.655 +	
   1.656 +		// send request to renderer thread
   1.657 +		TRequestStatus* rendererRequest = Status();
   1.658 +		iRendererThread->RequestComplete(rendererRequest, KErrNone);
   1.659 +		
   1.660 +		User::WaitForRequest(logonRequest, request);
   1.661 +
   1.662 +		if(logonRequest != KRequestPending)
   1.663 +			{
   1.664 +			// renderer thread got panic from surface update session, so panic client
   1.665 +			TInt reason = iRendererThread->ExitReason();
   1.666 +			TExitCategoryName category = iRendererThread->ExitCategory();
   1.667 +			User::Panic(category,reason);
   1.668 +			}
   1.669 +
   1.670 +		// Thread is still alive and well
   1.671 +		iRendererThread->LogonCancel(logonRequest);
   1.672 +		User::WaitForRequest(logonRequest); // Consume the signal
   1.673 +
   1.674 +		__ASSERT_DEBUG(request != KRequestPending, User::Panic(_L("CRTR::SetBufferManager"), KErrCorrupt));
   1.675 +	}
   1.676 +
   1.677 +void CRendererThreadRelay::RunSetBufferManager()
   1.678 +	{
   1.679 +	iRendererRelay->SetBufferManager(iBufManager);
   1.680 +	}
   1.681 +
   1.682 +/** Store a pointer to the renderer thread object. */
   1.683 +void CRendererThreadRelay::SetRendererThread(RThread* aRendererThread)
   1.684 +	{
   1.685 +	iRendererThread = aRendererThread;
   1.686 +	}
   1.687 +
   1.688 +/** Return a pointer to the function call listener's request status. */
   1.689 +TRequestStatus* CRendererThreadRelay::Status()
   1.690 +	{
   1.691 +	return &iStatus;
   1.692 +	}
   1.693 +
   1.694 +/** Set timer info for timed mode */
   1.695 +void CRendererThreadRelay::SetTimerInfo(TInt64 aDefaultDelay, TInt64 aMaxDelay)
   1.696 +	{
   1.697 +	iMaxDelay = aMaxDelay;
   1.698 +	iRendererRelay->SetTimerInfo(aDefaultDelay, aMaxDelay);
   1.699 +	}
   1.700 +
   1.701 +void CRendererThreadRelay::MvroVideoBufferAvailable()
   1.702 +	{
   1.703 +	iRendererCallbackListener->SendCallback(CRendererCallbackListener::EBufferAvailable, -1, 0);
   1.704 +	}
   1.705 +
   1.706 +void CRendererThreadRelay::MvroBufferDisplayed(TInt aBufferId, const TTime& aTime)
   1.707 +	{
   1.708 +	iRendererCallbackListener->SendCallback(CRendererCallbackListener::EBufferDisplayed, aBufferId, aTime);
   1.709 +	}
   1.710 +
   1.711 +void CRendererThreadRelay::MvroBufferSkipped(TInt aBufferId)
   1.712 +	{
   1.713 +	iRendererCallbackListener->SendCallback(CRendererCallbackListener::EBufferSkipped, aBufferId, 0);
   1.714 +	}
   1.715 +
   1.716 +
   1.717 +/** Private constructor */
   1.718 +CRendererCallbackListener::CRendererCallbackListener(MVideoRendererObserver& aObserver)
   1.719 +: CActive(EPriorityStandard),
   1.720 +  iObserver(aObserver)
   1.721 +	{
   1.722 +	CActiveScheduler::Add(this);
   1.723 +	}
   1.724 +
   1.725 +/** Two-phased constructor */
   1.726 +CRendererCallbackListener* CRendererCallbackListener::NewL(MVideoRendererObserver& aObserver, TInt aNumBuffer)
   1.727 +	{
   1.728 +	CRendererCallbackListener* self = new (ELeave) CRendererCallbackListener(aObserver);
   1.729 +	CleanupStack::PushL(self);
   1.730 +	self->ConstructL(aNumBuffer);
   1.731 +	CleanupStack::Pop(self);
   1.732 +	return self;
   1.733 +	}
   1.734 +
   1.735 +/** Second-phase constructor */
   1.736 +void CRendererCallbackListener::ConstructL(TInt aNumBuffer)
   1.737 +	{
   1.738 +	// There could potentially be three messages outstanding per buffer
   1.739 +	// size message queue accordingly
   1.740 +	TInt slot = aNumBuffer * 3;
   1.741 +	if (slot > RMsgQueueBase::KMaxLength)
   1.742 +		{
   1.743 +		slot = RMsgQueueBase::KMaxLength;
   1.744 +		}
   1.745 +	User::LeaveIfError(iMsgQueue.CreateLocal(slot));
   1.746 +	}
   1.747 +
   1.748 +CRendererCallbackListener::~CRendererCallbackListener()
   1.749 +	{
   1.750 +	Cancel(); // Cancel any request, if outstanding
   1.751 +	iMsgQueue.Close();
   1.752 +	}
   1.753 +
   1.754 +void CRendererCallbackListener::ExtendMsgQueueL(TInt aNumBuffer)
   1.755 +	{
   1.756 +	// close the message queue and construct another one
   1.757 +	iMsgQueue.Close();
   1.758 +	ConstructL(aNumBuffer);
   1.759 +	}
   1.760 +
   1.761 +void CRendererCallbackListener::DoCancel()
   1.762 +	{
   1.763 +	iMsgQueue.CancelDataAvailable();
   1.764 +	}
   1.765 +
   1.766 +/** Start listener */
   1.767 +void CRendererCallbackListener::Start()
   1.768 +	{
   1.769 +	if (!IsActive())
   1.770 +		{
   1.771 +		iMsgQueue.NotifyDataAvailable(iStatus);
   1.772 +		SetActive();
   1.773 +		}
   1.774 +	}
   1.775 +
   1.776 +/** Set the callback function */
   1.777 +void CRendererCallbackListener::SendCallback(TFunctionCode aFunctionCode, TInt aBufferId, const TTime& aTime)
   1.778 +	{
   1.779 +	DEBUGPRINT2(_L("CRendererCallbackListener::SendCallback entered aFunctionCode=%d"), aFunctionCode);
   1.780 +	
   1.781 +	TCallbackData data;
   1.782 +	data.iFunctionCode = aFunctionCode;
   1.783 +	data.iBufferId = aBufferId;
   1.784 +	data.iTime = aTime;
   1.785 +	
   1.786 +	iMsgQueue.Send(data);
   1.787 +	}
   1.788 +
   1.789 +/** Call the callback function within the main thread */
   1.790 +void CRendererCallbackListener::RunL()
   1.791 +	{
   1.792 +	TCallbackData data;
   1.793 +	TInt err = iMsgQueue.Receive(data);
   1.794 +	if (err == KErrNone)
   1.795 +		{
   1.796 +		if (data.iFunctionCode == EBufferAvailable)
   1.797 +			{
   1.798 +			DEBUGPRINT1(_L("CRendererCallbackListener::RunL - EBufferAvailable"));
   1.799 +			iObserver.MvroVideoBufferAvailable();
   1.800 +			}
   1.801 +		else if (data.iFunctionCode == EBufferDisplayed)
   1.802 +			{
   1.803 +			DEBUGPRINT1(_L("CRendererCallbackListener::RunL - EBufferDisplayed"));
   1.804 +			iObserver.MvroBufferDisplayed(data.iBufferId, data.iTime);
   1.805 +			}
   1.806 +		else if (data.iFunctionCode == EBufferSkipped)
   1.807 +			{
   1.808 +			DEBUGPRINT1(_L("CRendererCallbackListener::RunL - EBufferSkipped"));
   1.809 +			iObserver.MvroBufferSkipped(data.iBufferId);
   1.810 +			}
   1.811 +		}
   1.812 +	else
   1.813 +		{
   1.814 +		DEBUGPRINT2(_L("CRendererCallbackListener::RunL err=%d"), err);
   1.815 +		}
   1.816 +
   1.817 +	Start();
   1.818 +	}