omxil/video/omxilvideoscheduler2/src/comxilvideoschedulerpf.cpp
branchOpenMAX-IL_SHAI
changeset 16 eedf2dcd43c6
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/omxil/video/omxilvideoscheduler2/src/comxilvideoschedulerpf.cpp	Fri May 07 16:25:23 2010 +0100
@@ -0,0 +1,809 @@
+/*
+* Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+
+/**
+@file
+@internalComponent
+*/
+
+#include "omxilcallbackmanager.h"
+#include "omxilutil.h"
+#include "comxilvideoschedulerpf.h"
+#include "comxilvideoscheduler.h"
+#include "resourcefilereader.h"
+#include "buffercopierstatemonitor.h"
+#include "omxilvideoschedulerextensionsindexes.h"
+#include <openmax/il/extensions/omxildroppedframeeventextension.h>
+#include "log.h"
+
+_LIT(KVideoSchedulerPanicCategory, "omxilvscheduler"); //should restrict to 16 characters as it is used in User::Panic
+_LIT(KResourceFileName, "Z:\\resource\\videoscheduler\\videoscheduler.rsc");
+const TInt KMaxRenderTime = 1000000;
+const TInt KMaxGraphicSinkBufferCount(2); //This is set as the maximum number of buffers that can be sent to the graphic sink without the risk of overloading it.
+
+
+
+COmxILVideoSchedulerPF* COmxILVideoSchedulerPF::NewL(MOmxILCallbackNotificationIf& aCallbacks, COmxILVideoScheduler& aComponent, OMX_COMPONENTTYPE* aHandle)
+	{
+	COmxILVideoSchedulerPF* self = new (ELeave) COmxILVideoSchedulerPF(aCallbacks, aComponent, aHandle);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+COmxILVideoSchedulerPF::COmxILVideoSchedulerPF(MOmxILCallbackNotificationIf& aCallbacks, COmxILVideoScheduler& aComponent, OMX_COMPONENTTYPE* aHandle)
+: COmxILProcessingFunction(aCallbacks),
+  iComponent(aComponent),
+  iIsClockStopped(ETrue),
+  iInvalid(EFalse),
+  iTimeStamp(KMinTInt64),
+  iHandle(aHandle)
+ 	{
+	}
+
+void COmxILVideoSchedulerPF::ConstructL()
+	{
+	User::LeaveIfError(iMutex.CreateLocal());
+	iBufferCopierStateMonitor = CBufferCopierStateMonitor::NewL(*this, iComponent);
+	// get timer info
+	CResourceFileReader* reader = CResourceFileReader::NewLC(KResourceFileName);
+	reader->ReadTimerInfoL(iRenderTime, iMaxLateness);
+	CleanupStack::PopAndDestroy(reader);
+
+	// Prefill the render time array with the default render time read from resource file
+	for (TInt count = 0; count < KRenderTimeListLength; ++count)
+		{
+		iRenderTimeList[count] = iRenderTime;
+		}
+	iRenderTimeSum = iRenderTime * KRenderTimeListLength;
+	}
+
+COmxILVideoSchedulerPF::~COmxILVideoSchedulerPF()
+	{
+	delete iBufferCopierStateMonitor;
+    iMutex.Wait();
+	iWaitingBuffers.Reset();
+	iCompletedBuffersHeldByPause.Reset();
+    iMutex.Signal();	
+	iMutex.Close();
+	}
+
+OMX_ERRORTYPE COmxILVideoSchedulerPF::StateTransitionIndication(COmxILFsm::TStateIndex aNewState)
+	{
+	switch(aNewState)
+		{
+		case COmxILFsm::EStateExecuting:
+			{
+			if (iPausedState)
+				{
+			    iMutex.Wait();
+				iPausedState = EFalse;
+				
+				// send any buffers that received time updates during paused state
+				if (iOutputBufferSentCount < KMaxGraphicSinkBufferCount)   // only allowed to send 2 buffers at a time
+					{
+					SubmitBufferHeldByPause();
+					}
+                iMutex.Signal();
+				}
+			break;
+			}
+		case COmxILFsm::EStatePause:
+			{
+			iPausedState = ETrue;
+			break;
+			}
+		case COmxILFsm::ESubStateLoadedToIdle:
+			{
+			TUint32 bufferCount = iComponent.BufferCount();
+			
+			TInt error = iBufferCopierStateMonitor->SetState(CBufferCopierStateMonitor::ESubLoadedToIdle);
+			
+			if (error != KErrNone)
+			    {
+			    return SymbianErrorToOmx(error);                    
+			    }
+			
+			error = iWaitingBuffers.Reserve(bufferCount);
+			if (error != KErrNone)
+			    {
+			    return SymbianErrorToOmx(error);                    
+			    }
+
+			error = iCompletedBuffersHeldByPause.Reserve(bufferCount);
+			if (error != KErrNone)
+			    {
+			    return SymbianErrorToOmx(error);                    
+			    }
+			
+			break;
+			}
+		case COmxILFsm::EStateIdle:
+			{
+			iOutputBufferSentCount = iComponent.BufferCount();
+			break;
+			}
+		case COmxILFsm::ESubStateIdleToLoaded:
+			{
+			TInt error = iBufferCopierStateMonitor->SetState(CBufferCopierStateMonitor::ESubIdleToLoaded);
+			if (error != KErrNone)
+			    {
+			    return SymbianErrorToOmx(error);                    
+			    }
+			break;
+			}
+		default:
+			{
+			break;
+			}	
+		}
+    
+	return OMX_ErrorNone;	
+	}
+
+OMX_ERRORTYPE COmxILVideoSchedulerPF::BufferFlushingIndication(TUint32 aPortIndex, OMX_DIRTYPE aDirection)
+	{
+	// called from command thread
+	
+    iMutex.Wait();
+	if (iBufferCopierStateMonitor->BufferCopier())
+		{
+		if (aPortIndex == OMX_ALL)
+			{
+			iBufferCopierStateMonitor->BufferCopier()->FlushBuffers(OMX_DirInput);
+			iBufferCopierStateMonitor->BufferCopier()->FlushBuffers(OMX_DirOutput);
+			}
+		else
+			{
+			iBufferCopierStateMonitor->BufferCopier()->FlushBuffers(aDirection);
+			}
+		}
+
+	if (aDirection == OMX_DirOutput || aPortIndex == OMX_ALL)
+		{
+		while (iWaitingBuffers.Count() > 0)
+			{
+			iWaitingBuffers[0]->nFilledLen = 0;
+			iWaitingBuffers[0]->nOffset = 0;
+			iWaitingBuffers[0]->nTimeStamp = 0;
+			iCallbacks.BufferDoneNotification(iWaitingBuffers[0], 1, OMX_DirOutput);
+			iWaitingBuffers.Remove(0);
+			iOutputBufferSentCount++;
+			}
+		if(iSinkPendingBuffer)
+			{
+			iSinkPendingBuffer->nFilledLen = 0;
+			iSinkPendingBuffer->nOffset = 0;
+			iSinkPendingBuffer->nTimeStamp = 0;
+			iCallbacks.BufferDoneNotification(iSinkPendingBuffer, 1, OMX_DirOutput);
+			iSinkPendingBuffer = NULL;
+			iOutputBufferSentCount++;
+			}
+		}
+    iMutex.Signal();        
+
+	return OMX_ErrorNone;
+	}
+
+OMX_ERRORTYPE COmxILVideoSchedulerPF::ParamIndication(OMX_INDEXTYPE aParamIndex,
+													const TAny* apComponentParameterStructure)
+	{
+	DEBUG_PRINTF(_L8("COmxILVideoSchedulerProcessingFunction::ParamIndication"));
+
+	if(aParamIndex == OMX_NokiaIndexParamDroppedFrameEvent)
+		{
+		const OMX_NOKIA_PARAM_DROPPEDFRAMEEVENT* dropFrame = static_cast<const OMX_NOKIA_PARAM_DROPPEDFRAMEEVENT*>(apComponentParameterStructure);
+		iEnableDropFrameEvent = dropFrame->bEnabled;
+		}
+
+	return OMX_ErrorNone;
+	}
+
+OMX_ERRORTYPE COmxILVideoSchedulerPF::ConfigIndication(OMX_INDEXTYPE /*aConfigIndex*/, const TAny* /*apComponentConfigStructure*/)
+	{
+	return OMX_ErrorNone;	
+	}
+
+OMX_ERRORTYPE COmxILVideoSchedulerPF::BufferIndication(OMX_BUFFERHEADERTYPE* apBufferHeader,
+	  												   OMX_DIRTYPE aDirection)			
+	{
+	if (iInvalid)
+	    {
+	    return OMX_ErrorInvalidState;
+	    }
+
+	// called from decoder data thread or sink data thread
+	iMutex.Wait();
+	if(aDirection == OMX_DirOutput)
+		{
+		apBufferHeader->nFlags = 0;
+		iOutputBufferSentCount--;
+		ASSERT(iOutputBufferSentCount <= iComponent.BufferCount());
+		
+		DEBUG_PRINTF2(_L8("VS2::BufferIndication : apBufferHeader->nTickCount = %d"), apBufferHeader->nTickCount);
+
+		// update the render time if it is set
+		if (apBufferHeader->nTickCount > 0 && apBufferHeader->nTickCount <= KMaxRenderTime)
+			{
+			// Add new render time to render time list, and recalculate average
+			iRenderTimeSum -= iRenderTimeList[iRenderTimeListPos];
+			iRenderTimeSum += apBufferHeader->nTickCount;
+			iRenderTimeList[iRenderTimeListPos] = apBufferHeader->nTickCount;
+			++iRenderTimeListPos;
+			iRenderTimeListPos %= KRenderTimeListLength;
+
+			iRenderTime = iRenderTimeSum / KRenderTimeListLength;
+
+			DEBUG_PRINTF2(_L8("VS2::BufferIndication : New iRenderTime = %ld"), iRenderTime);
+			}
+
+		// previously sent buffer has come back
+		// send any buffers that received time updates
+		// at startup, iOutputBufferSentCount may be >2 if output port is non-supplier
+		if (!iPausedState && iOutputBufferSentCount < KMaxGraphicSinkBufferCount)
+			{
+			SubmitBufferHeldByPause();
+			}
+		}
+	else if(apBufferHeader->nFlags & OMX_BUFFERFLAG_DECODEONLY)
+		{
+		// this frame is not to be rendered (probably as part of an accurate seek)
+		// drop the data and send it back to the decoder
+		apBufferHeader->nFilledLen = 0;
+		apBufferHeader->nFlags = 0;
+		apBufferHeader->nOffset = 0;
+		iCallbacks.BufferDoneNotification(apBufferHeader, 0, OMX_DirInput);
+		iMutex.Signal();
+		return OMX_ErrorNone;
+		}
+	if (iBufferCopierStateMonitor->BufferCopier())
+	    {
+	    iBufferCopierStateMonitor->BufferCopier()->DeliverBuffer(apBufferHeader, aDirection);
+	    }
+    iMutex.Signal();
+    
+	return OMX_ErrorNone;
+	}
+
+OMX_ERRORTYPE COmxILVideoSchedulerPF::MediaTimeIndication(const OMX_TIME_MEDIATIMETYPE& aTimeInfo)
+	{
+	// called from clock thread
+	
+	switch(aTimeInfo.eUpdateType)
+		{
+	case OMX_TIME_UpdateRequestFulfillment:
+		{
+		
+		iMutex.Wait();
+
+		TInt index = -1;
+		OMX_BUFFERHEADERTYPE* buffer = reinterpret_cast<OMX_BUFFERHEADERTYPE*>(aTimeInfo.nClientPrivate);
+		__ASSERT_DEBUG(buffer->nTimeStamp == aTimeInfo.nMediaTimestamp, Panic(EPanicBadAssociation));
+		if(FindWaitingBuffer(buffer, aTimeInfo.nMediaTimestamp, index))
+			{
+			if (iPausedState || iCompletedBuffersHeldByPause.Count() > 0)
+				{
+				TBufferMessage bufferMessage;
+				bufferMessage.iBufferHeader = buffer;
+				bufferMessage.iMediaTimeInfo = aTimeInfo;
+				
+				OMX_ERRORTYPE error = SymbianErrorToOmx(iCompletedBuffersHeldByPause.Append(bufferMessage)); // note append cannot fail, allocated enough slots
+				iMutex.Signal();
+				return error;
+				}
+			else 
+				{
+				SendTimedOutputBuffer(buffer, aTimeInfo, index);
+				}
+			}
+		else
+			{
+			// TODO [SL] now what?
+			User::Invariant();
+			}
+
+		iMutex.Signal();
+		return OMX_ErrorNone;
+		}
+
+	case OMX_TIME_UpdateScaleChanged:
+		if(aTimeInfo.xScale >= 0)
+			{
+			// the clock takes care completing requests at the correct media time
+			return OMX_ErrorNone;
+			}
+		else
+			{
+			// TODO think harder about implications of negative scale
+			// certainly the iTimeStamp checking must be reversed
+			ASSERT(0);
+			return OMX_ErrorNotImplemented;
+			}
+
+	case OMX_TIME_UpdateClockStateChanged:
+		iClockState.eState = aTimeInfo.eState;
+		switch(aTimeInfo.eState)
+			{
+		case OMX_TIME_ClockStateStopped:
+		    {
+		    // clock stopped so remove any pending buffers from the list as time requests
+		    // will be resent when the clock is running again
+		    
+	        iIsClockStopped	= ETrue;	 
+
+	        iMutex.Wait();
+			while (iCompletedBuffersHeldByPause.Count() > 0)
+                { 
+				iCompletedBuffersHeldByPause.Remove(0);
+                }           	         
+	        
+			if(iSinkPendingBuffer && iBufferCopierStateMonitor)
+				{
+				// if sink pending buffer exist (as sink is bottleneck) then drop the frame
+				iBufferCopierStateMonitor->BufferCopier()->DeliverBuffer(iSinkPendingBuffer, OMX_DirOutput);
+
+
+				iSinkPendingBuffer = NULL;
+				}
+            iMutex.Signal();            
+	        }
+			break;
+			
+		case OMX_TIME_ClockStateWaitingForStartTime:
+		    {
+		    iIsClockStopped = EFalse;
+		    // if now in WaitingForStartTime state and start time already received, send it now
+		    if (iStartTimePending)
+		        {
+		        OMX_ERRORTYPE error = iComponent.SetVideoStartTime(iStartTime);
+		        if (error != OMX_ErrorNone)
+		            {
+		            // iStartTimePending = EFalse; // FIXME - Is this required?
+		            return error;
+		            }
+		        }
+		    }
+			break;
+			
+		case OMX_TIME_ClockStateRunning:
+			{
+			iTimeStamp = KMinTInt64;
+			if(iIsClockStopped)
+				{
+				// the clock is running after being stopped previously
+				// resend time requests for waiting buffers
+				iIsClockStopped = EFalse;
+				
+				for (TInt i = 0; i < iWaitingBuffers.Count(); ++i)
+					{
+					iComponent.MediaTimeRequest(iWaitingBuffers[i], iWaitingBuffers[i]->nTimeStamp, iRenderTime);
+					}
+				}
+			}
+			break;
+			}
+		
+		iStartTimePending = EFalse;
+		DEBUG_PRINTF2(_L8("VS2::MediaTimeIndication : ClockStateChanged = %d"), aTimeInfo.eState);
+		
+		return OMX_ErrorNone;
+		
+	default:
+		return OMX_ErrorBadParameter;
+		}
+	
+	}
+
+/* Check if aBuffer still exist in the waiting queue */ 
+TBool COmxILVideoSchedulerPF::FindWaitingBuffer(const OMX_BUFFERHEADERTYPE* aBuffer, const OMX_TICKS& aMediaTime, TInt& aIndex) const
+	{
+	__ASSERT_DEBUG(const_cast<RMutex&>(iMutex).IsHeld(), Panic(EPanicMutexUnheld));
+	
+	TBool found = EFalse;
+		
+	for (TInt i=0; i<iWaitingBuffers.Count(); ++i)
+		{
+		if ((iWaitingBuffers[i] == aBuffer) && (iWaitingBuffers[i]->nTimeStamp == aMediaTime))
+			{
+			found = ETrue;
+			aIndex = i;
+			break;
+			}
+		}
+
+	return found;
+	}
+
+/**
+Check if a specified buffer is currently held by the processing function,
+and remove it if found.
+
+@param apBufferHeader Buffer to remove
+@param aDirection Port direction
+@return Flag to indicate if buffer was removed.
+*/
+OMX_BOOL COmxILVideoSchedulerPF::BufferRemovalIndication(OMX_BUFFERHEADERTYPE* apBufferHeader, OMX_DIRTYPE aDirection)
+	{
+    iMutex.Wait();	
+	if(iBufferCopierStateMonitor->BufferCopier() && iBufferCopierStateMonitor->BufferCopier()->RemoveBuffer(apBufferHeader, aDirection))
+		{
+		if(aDirection == OMX_DirOutput)
+			{
+			iOutputBufferSentCount++;
+			}
+		iMutex.Signal();
+		return OMX_TRUE;
+		}
+	else if(aDirection == OMX_DirOutput)
+		{
+		for (TInt i = 0; i < iWaitingBuffers.Count(); ++i)
+			{
+			if (iWaitingBuffers[i] == apBufferHeader)
+				{
+				iWaitingBuffers[i]->nFilledLen = 0;
+				iWaitingBuffers.Remove(i);
+				iOutputBufferSentCount++;
+			    iMutex.Signal();
+				return OMX_TRUE;
+				}
+			}
+		if(apBufferHeader == iSinkPendingBuffer)
+			{
+			iSinkPendingBuffer = NULL;
+			iOutputBufferSentCount++;
+			iMutex.Signal();
+			return OMX_TRUE;
+			}
+		}
+ 
+	iMutex.Signal();    
+	return OMX_FALSE;
+	}
+
+/* Submit the first time update buffer that still exists in the waiting queue. */
+void COmxILVideoSchedulerPF::SubmitBufferHeldByPause()
+	{
+	__ASSERT_DEBUG(iMutex.IsHeld(), Panic(EPanicMutexUnheld));
+	__ASSERT_DEBUG(iOutputBufferSentCount < KMaxGraphicSinkBufferCount, Panic(EPanicBadOutputRegulation));
+
+	if(iSinkPendingBuffer)
+		{
+		DEBUG_PRINTF(_L8("VS2::SubmitBufferHeldByPause ***************************SEND SINK PENDING BUFFER"));
+		OMX_BUFFERHEADERTYPE* buffer = iSinkPendingBuffer;
+		iSinkPendingBuffer = NULL;
+		SendOutputBuffer(buffer);
+		return;
+		}
+
+	TInt index = -1;
+	TBool bufferSent = EFalse;
+	while (iCompletedBuffersHeldByPause.Count() > 0 && !bufferSent)
+		{
+		TBufferMessage& msg = iCompletedBuffersHeldByPause[0];
+		if (FindWaitingBuffer(msg.iBufferHeader, 
+					msg.iMediaTimeInfo.nMediaTimestamp, index))
+			{
+			DEBUG_PRINTF(_L8("VS2::SubmitBufferHeldByPause ***************************SEND HELD BUFFER"));
+			bufferSent = SendTimedOutputBuffer(msg.iBufferHeader, msg.iMediaTimeInfo, index);
+			}
+		iCompletedBuffersHeldByPause.Remove(0);
+		}
+	}
+
+/** Returns ETrue if aBuffer was sent, EFalse otherwise */
+TBool COmxILVideoSchedulerPF::SendTimedOutputBuffer(OMX_BUFFERHEADERTYPE* aBuffer, const OMX_TIME_MEDIATIMETYPE& aMediaTimeInfo, TInt aIndex)
+	{
+	__ASSERT_DEBUG(iMutex.IsHeld(), Panic(EPanicMutexUnheld));
+	__ASSERT_DEBUG(aBuffer->nTimeStamp == aMediaTimeInfo.nMediaTimestamp, Panic(EPanicBadAssociation));
+	__ASSERT_DEBUG(aBuffer == reinterpret_cast<OMX_BUFFERHEADERTYPE*>(aMediaTimeInfo.nClientPrivate), Panic(EPanicBadAssociation));
+	
+#ifdef _OMXIL_COMMON_DEBUG_TRACING_ON	
+	DEBUG_PRINTF(_L8("VS2::SendTimedOutputBuffer **********************************"));
+	TTime t;
+	t.HomeTime();
+	DEBUG_PRINTF2(_L8("VS2::SendTimedOutputBuffer : t.HomeTime() = %ld"), t.Int64());
+	DEBUG_PRINTF2(_L8("VS2::SendTimedOutputBuffer : aMediaTimeInfo.nClientPrivate = 0x%X"), aMediaTimeInfo.nClientPrivate);
+	DEBUG_PRINTF2(_L8("VS2::SendTimedOutputBuffer : aMediaTimeInfo.nMediaTimestamp = %ld"), aMediaTimeInfo.nMediaTimestamp);
+	DEBUG_PRINTF2(_L8("VS2::SendTimedOutputBuffer : aMediaTimeInfo.nOffset = %ld"), aMediaTimeInfo.nOffset);
+	DEBUG_PRINTF2(_L8("VS2::SendTimedOutputBuffer : aMediaTimeInfo.nWallTimeAtMediaTime = %ld"), aMediaTimeInfo.nWallTimeAtMediaTime);
+#endif
+	
+	TBool bufferSent = EFalse;
+
+	OMX_U32 flags = aBuffer->nFlags;
+
+	// Work out how long it is from now until the frame will be rendered.
+	// This will be the time it takes the sink to render, minus the offset
+	// value from the clock completion (i.e how far before the requested
+	// time that the clock has completed us). A lateness of 0 means we are at
+	// the correct time to send the buffer, a positive lateness means we are
+	// late sending the buffer, and a lateness waitTime means we are early.
+	// For the first frame we were not able to request an early completion to
+	// offset the render time, so assume that the render time is 0.
+	OMX_TICKS lateness = 0 - aMediaTimeInfo.nOffset;
+	if (!(flags & OMX_BUFFERFLAG_STARTTIME))
+		{
+		lateness += iRenderTime;
+		}
+
+	DEBUG_PRINTF2(_L8("VS2::SendTimedOutputBuffer : iRenderTime = %ld"), iRenderTime);
+	DEBUG_PRINTF2(_L8("VS2::SendTimedOutputBuffer : lateness = %ld"), lateness);
+	DEBUG_PRINTF2(_L8("VS2::SendTimedOutputBuffer : iMaxLateness = %ld"), iMaxLateness);
+	DEBUG_PRINTF2(_L8("VS2::SendTimedOutputBuffer : iTimeStamp = %ld"), iTimeStamp);
+	DEBUG_PRINTF2(_L8("VS2::SendTimedOutputBuffer : iFrameDroppedCount = %d"), iFrameDroppedCount);
+	DEBUG_PRINTF2(_L8("VS2::SendTimedOutputBuffer : flags = %d"), flags);
+
+	iWaitingBuffers.Remove(aIndex);
+
+	// Send the buffer if the wait time is within the maximum allowed delay and timestamp is later than the previous timestamp, otherwise skip the buffer	
+	if ((lateness <= iMaxLateness || iFrameDroppedCount >= KMaxGraphicSinkBufferCount) && aMediaTimeInfo.nMediaTimestamp > iTimeStamp)   // shouldn't drop more than 2 frames at a time when decoder is slow
+		{
+		DEBUG_PRINTF(_L8("VS2::SendTimedOutputBuffer ***************************SHOW"));
+		
+		bufferSent = ETrue;
+		iFrameDroppedCount = 0;
+
+		SendOutputBuffer(aBuffer);
+		}
+	else
+		{
+		DEBUG_PRINTF(_L8("VS2::SendTimedOutputBuffer ***************************DROP"));
+		
+		iFrameDroppedCount++;
+
+
+		// if EOS was on the buffer, send an empty buffer with EOS and send the EOS event
+		// if not, discard the buffer contents and post the buffer for another copy
+		if(flags & OMX_BUFFERFLAG_EOS)
+			{
+			DEBUG_PRINTF(_L8("VS2::SendTimedOutputBuffer ***************************SEND EMPTY EOS BUFFER"));
+			aBuffer->nFilledLen = 0;
+			aBuffer->nOffset = 0;
+			aBuffer->nTimeStamp = 0;
+			SendOutputBuffer(aBuffer);
+			}
+		else
+			{
+			TOmxILUtil::ClearBufferContents(aBuffer);
+			aBuffer->nOffset = 0;
+			if (iBufferCopierStateMonitor->BufferCopier())
+			    {
+			    iBufferCopierStateMonitor->BufferCopier()->DeliverBuffer(aBuffer, OMX_DirOutput);
+			    }
+			}
+		}
+	
+	return bufferSent;	
+	}
+
+void COmxILVideoSchedulerPF::SendOutputBuffer(OMX_BUFFERHEADERTYPE* aBuffer)
+	{
+	__ASSERT_DEBUG(iMutex.IsHeld(), Panic(EPanicMutexUnheld));
+	__ASSERT_DEBUG(iTimeStamp < aBuffer->nTimeStamp || aBuffer->nFlags & OMX_BUFFERFLAG_EOS, Panic(EPanicTimestampEmissionUnordered));
+
+	if(iOutputBufferSentCount >= KMaxGraphicSinkBufferCount)
+		{
+		DEBUG_PRINTF(_L8("VS2::SendOutputBuffer : *****************STORING SINK PENDING BUFFER"));
+
+		// sink is bottleneck, keep the most recent pending frame but return the rest so decoder keeps running
+		// when sink returns a buffer send the most recent frame
+		if(iSinkPendingBuffer && iBufferCopierStateMonitor->BufferCopier())
+			{
+			if (iSinkPendingBuffer->nFlags & OMX_BUFFERFLAG_EOS)
+			    {
+			    //if (bizarrely) pending buffer has EOS flag and another buffer replaces it.
+			    DoSendOutputBuffer(iSinkPendingBuffer);
+			    }
+			else
+			    {
+			    iBufferCopierStateMonitor->BufferCopier()->DeliverBuffer(iSinkPendingBuffer, OMX_DirOutput);
+			    }
+
+			DEBUG_PRINTF(_L8("VS2::SendOutputBuffer : *****************DROPPED EXISTING SINK PENDING BUFFER"));
+
+			}
+
+		iSinkPendingBuffer = aBuffer;
+		}
+	else
+		{
+		DoSendOutputBuffer(aBuffer);
+		}
+	}
+
+/** Called when the buffer copier has transferred the data from an input buffer to an output buffer. */
+void COmxILVideoSchedulerPF::MbcBufferCopied(OMX_BUFFERHEADERTYPE* aInBuffer, OMX_BUFFERHEADERTYPE* aOutBuffer)
+	{
+	iMutex.Wait();
+	
+	// send input buffer back
+	aInBuffer->nFilledLen = 0;
+	aInBuffer->nOffset = 0;
+	aInBuffer->nFlags = 0;
+	aInBuffer->nTimeStamp = 0;
+
+	// Deal with any buffer marks. Currently the component framework makes an attempt to deal with
+	// them, but it cannot associate the input buffer mark with the corresponding output buffer so
+	// we may need to do some tweaking here.
+	if (aInBuffer->hMarkTargetComponent)
+		{
+		if (aInBuffer->hMarkTargetComponent == iHandle)
+			{
+			// There was a buffer mark on the input buffer intended for us. That means there is no
+			// need to send it out on the output buffer. Also, it is OK to let the component framework
+			// deal with it in this situation.
+			aOutBuffer->hMarkTargetComponent = NULL;
+			aOutBuffer->pMarkData = NULL;
+			}
+		else
+			{
+			// There was a buffer mark on the input buffer but it is not intended for us. If
+			// we let the component framework deal with it then we will get multiple marks sent
+			// out because we have copied it to the output buffer, and the framework will also
+			// store it to send out later. Clear it here so the framework does not see it.
+			aInBuffer->hMarkTargetComponent = NULL;
+			aInBuffer->pMarkData = NULL;
+			}
+		}
+
+	OMX_ERRORTYPE error;
+
+	iCallbacks.BufferDoneNotification(aInBuffer, 0, OMX_DirInput);
+	
+	if(aOutBuffer->nFilledLen > 0 || (aOutBuffer->nFlags & OMX_BUFFERFLAG_EOS))
+		{
+		iWaitingBuffers.Append(aOutBuffer);  // note append cannot fail, allocated enough slots
+		}
+	
+	if(aOutBuffer->nFlags & OMX_BUFFERFLAG_STARTTIME)
+		{
+		if(OMX_TIME_ClockStateWaitingForStartTime == iClockState.eState)
+			{
+			error = iComponent.SetVideoStartTime(aOutBuffer->nTimeStamp);
+			if (error != OMX_ErrorNone)
+			    {
+			    HandleIfError(error);
+			    }
+			iStartTimePending = EFalse;
+			}
+		else
+			{
+			// delay sending until clock transitions to WaitingForStartTime
+			iStartTime = aOutBuffer->nTimeStamp;
+			iStartTimePending = ETrue;
+			}
+		}
+		
+#ifdef _OMXIL_COMMON_DEBUG_TRACING_ON	
+	DEBUG_PRINTF(_L8("VS2::MbcBufferCopied **********************************"));
+	TTime t;
+	t.HomeTime();
+	DEBUG_PRINTF2(_L8("VS2::MbcBufferCopied : t.HomeTime() = %ld"), t.Int64());
+	DEBUG_PRINTF2(_L8("VS2::MbcBufferCopied : aOutBuffer = 0x%X"), aOutBuffer);
+	DEBUG_PRINTF2(_L8("VS2::MbcBufferCopied : aOutBuffer->nTimeStamp = %ld"), aOutBuffer->nTimeStamp);
+#endif	
+	iMutex.Signal();
+	
+	if (aOutBuffer->nFilledLen == 0 && !(aOutBuffer->nFlags & OMX_BUFFERFLAG_EOS))
+		{
+		// A likely cause of receiving an empty buffer is if the decoder implements a flush as
+		// returning buffers to supplier or always sending buffers to peer, rather than emptied
+		// and queued on the output port. In this case we return the buffer immediately without
+		// making a media time request. Probably the timestamp is invalid or the clock is not in
+		// the running state, in either case we could deadlock by queueing the empty buffers after
+		// a flush and preventing new data from being delivered. However these buffers were not
+		// returned in BufferIndication() in case there were any flags that need processing.
+		iBufferCopierStateMonitor->BufferCopier()->DeliverBuffer(aOutBuffer, OMX_DirOutput);
+		}
+	else
+		{
+		if (!iIsClockStopped)
+		    {
+		    error = iComponent.MediaTimeRequest(aOutBuffer, aOutBuffer->nTimeStamp, iRenderTime);
+		    if (error != OMX_ErrorNone)
+		        {
+		        HandleIfError(error);
+		        }
+		    }
+		}
+	}
+
+/** Called when a buffer is flushed from the buffer copier. */
+void COmxILVideoSchedulerPF::MbcBufferFlushed(OMX_BUFFERHEADERTYPE* aBuffer, OMX_DIRTYPE aDirection)
+	{
+	TInt portIndex = 0;
+	aBuffer->nFilledLen = 0;
+	aBuffer->nOffset = 0;
+	aBuffer->nFlags = 0;
+	aBuffer->nTimeStamp = 0;
+
+	if (aDirection == OMX_DirOutput)
+		{
+		++iOutputBufferSentCount;
+		portIndex = 1;
+		}
+
+	iCallbacks.BufferDoneNotification(aBuffer, portIndex, aDirection);
+	}
+
+void COmxILVideoSchedulerPF::DoSendOutputBuffer(OMX_BUFFERHEADERTYPE* aBuffer)
+    {
+    OMX_ERRORTYPE error;
+    // A zero length buffer means this buffer is just being sent because it
+    // has the EOS flag.
+    if (aBuffer->nFilledLen > 0)
+        {
+        aBuffer->nTickCount = 0xC0C0C0C0;
+        iTimeStamp = aBuffer->nTimeStamp;
+        }
+    
+    error = iCallbacks.BufferDoneNotification(aBuffer, 1, OMX_DirOutput);
+    if (error != OMX_ErrorNone)
+        {
+        HandleIfError(error);
+        }
+
+    iOutputBufferSentCount++;
+
+    OMX_U32 flags = aBuffer->nFlags;
+    if(flags & OMX_BUFFERFLAG_EOS)
+        {
+        error = iCallbacks.EventNotification(OMX_EventBufferFlag, 1, flags, NULL);
+        if (error != OMX_ErrorNone)
+            {
+            HandleIfError(error);
+            }
+        }
+    }
+
+void COmxILVideoSchedulerPF::HandleIfError(OMX_ERRORTYPE aOmxError)
+    {
+    if (aOmxError != OMX_ErrorNone)
+        {
+        iInvalid = ETrue;
+        iCallbacks.ErrorEventNotification(aOmxError);
+        }
+    }
+
+OMX_ERRORTYPE COmxILVideoSchedulerPF::SymbianErrorToOmx(TInt aError)
+	{
+	switch(aError)
+		{
+	case KErrNone:
+		return OMX_ErrorNone;
+	case KErrNoMemory:
+		return OMX_ErrorInsufficientResources;
+	default:
+		return OMX_ErrorUndefined;
+		}
+	}
+
+
+
+void COmxILVideoSchedulerPF::Panic(TVideoSchedulerPanic aPanicCode) const
+	{
+	// const allows const methods to panic using this method
+	// however we wish to release the mutex to avoid blocking other threads
+	RMutex& mutex = const_cast<RMutex&>(iMutex);
+	if(mutex.IsHeld())
+		{
+		mutex.Signal();
+		}
+	User::Panic(KVideoSchedulerPanicCategory, aPanicCode);
+	}