omxilvideocomps/omxil3gpmuxer/src/c3gpmuxer.cpp
author hgs
Fri, 08 Oct 2010 22:09:17 +0100
changeset 0 5d29cba61097
permissions -rw-r--r--
2010wk38_02

/*
* Copyright (c) 2008 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 "comxil3gpmuxeraudioinputport.h"
#include "comxil3gpmuxervideoinputport.h"
#include "c3gpmuxer.h"
#include "log.h"
#include <openmax/il/common/omxilcallbacknotificationif.h>

_LIT(K3GPMuxerPanic, "C3GPMuxer");
const TUint KAudioTimeScale = 44100;
static const TUint KThreadStackSize = 8192;	// 8KB
const TInt KMaxBufferSize = 62400 * 2;
const TInt KMicroSecondsPerSecond = 1000000;


C3GPMuxer* C3GPMuxer::NewL(MOmxILCallbackNotificationIf& aCallbacks)
	{
	DEBUG_PRINTF(_L8("C3GPMuxer::NewL"));
	C3GPMuxer* self = new (ELeave) C3GPMuxer(aCallbacks);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

C3GPMuxer::C3GPMuxer(MOmxILCallbackNotificationIf& aCallbacks): 
 CActive(EPriorityStandard), 
 iCallbacks(aCallbacks)
	{
	}

void C3GPMuxer::ConstructL()
	{
	User::LeaveIfError(iVideoQueue.CreateLocal(KMaxVideoBuffers));	
	iPartialFrame.CreateL(KMaxBufferSize);	
	User::LeaveIfError(iAudioQueue.CreateLocal(KMaxAudioBuffers));
	User::LeaveIfError(iRemoveQueue.CreateLocal(Max(KMaxVideoBuffers, KMaxAudioBuffers)));
	User::LeaveIfError(iQueMutex.CreateLocal());

	iComposer = C3GPCompose::NewL();
	}

void C3GPMuxer::StartL(TBool aAudioPortEnabled, TBool aVideoPortEnabled)
	{
	DEBUG_PRINTF(_L8("C3GPMuxer::StartL"));

	iPaused = EFalse;

	if (!iThreadRunning)
		{
		iAudioPortEnabled = aAudioPortEnabled;
		iVideoPortEnabled = aVideoPortEnabled;

		TBuf<40> threadName;
		threadName.Format(_L("C3GPMuxerThread@%08X"), this);
	
		User::LeaveIfError(iThread.Create(threadName, ThreadEntryPoint, KThreadStackSize, NULL, this));

		// start the thread and wait for it to start the active scheduler
		TRequestStatus status;
		iThread.Rendezvous(status);
		iThread.Resume();
		User::WaitForRequest(status);
		User::LeaveIfError(status.Int());
		}
	else
	    {
	    // self complete
        iQueMutex.Wait();
        if (!iRequestOutstanding)
            {
            TRequestStatus* statusPtr = &iStatus;
            iThread.RequestComplete(statusPtr, KErrNone);        
            }
        iQueMutex.Signal();
	    }
	}

TInt C3GPMuxer::ThreadEntryPoint(TAny* aPtr)
	{
	CTrapCleanup* cleanup = CTrapCleanup::New();
	if(!cleanup)
		{
		return KErrNoMemory;
		}

	// Call this objects clock initialisation routine
	TRAPD(err, static_cast<C3GPMuxer*>(aPtr)->RunThreadL());
	delete cleanup;
	return err;
	}

void C3GPMuxer::RunThreadL()
	{
	DEBUG_PRINTF(_L8("C3GPMuxer::RunThreadL"));
	CActiveScheduler* sched = new (ELeave) CActiveScheduler;
	CleanupStack::PushL(sched);
	
	CActiveScheduler::Install(sched);
	CActiveScheduler::Add(this);

	if (iAudioPortEnabled)
		{
		iAudioQueue.NotifyDataAvailable(iStatus);
		SetActive();												
        iRequestOutstanding = ETrue;
		}
	else if (iVideoPortEnabled)
		{
		iVideoQueue.NotifyDataAvailable(iStatus);
		SetActive();									
        iRequestOutstanding = ETrue;
		}

	iEndWaitAO = CEndWaitAO::NewL();
	
	iThreadRunning = ETrue;

	iThread.Rendezvous(KErrNone);

	CActiveScheduler::Start();

	HandleError(iComposer->Complete());
	iComposerOpened = EFalse;
	
	CleanupStack::PopAndDestroy(sched);
	}

C3GPMuxer::~C3GPMuxer()
	{
	DEBUG_PRINTF(_L8("C3GPMuxer::~C3GPMuxer"));

	if(iThreadRunning)
		{		
		iThreadRunning = EFalse;
		TRequestStatus logonStatus;
		iThread.Logon(logonStatus);

		iEndWaitAO->Call();
		User::WaitForRequest(logonStatus);

		delete iEndWaitAO;
		iEndWaitAO = NULL;
		}

	iVideoQueue.Close();
	iPartialFrame.Close();
	iAudioQueue.Close();
	iRemoveQueue.Close();

	delete iComposer;

	iThread.Close();
	iQueMutex.Close();
	}

void C3GPMuxer::ProcessThisBufferL(OMX_BUFFERHEADERTYPE* aBufferHeader, TUint32 aPortIndex)
	{
	DEBUG_PRINTF3(_L8("C3GPMuxer::ProcessThisBufferL : aBufferHeader[%X]; aPortIndex[%u]"), aBufferHeader, aPortIndex);
	__ASSERT_ALWAYS(aBufferHeader->nOffset + aBufferHeader->nFilledLen <= aBufferHeader->nAllocLen, User::Invariant());
	
	if (aPortIndex == COmxIL3GPMuxer::EPortIndexAudioInput && iAudioPortEnabled)
		{
		User::LeaveIfError(iAudioQueue.Send(aBufferHeader));		
		}
	else if (aPortIndex == COmxIL3GPMuxer::EPortIndexVideoInput && iVideoPortEnabled)
		{
		User::LeaveIfError(iVideoQueue.Send(aBufferHeader));		
		}
	// FIXME buffer arrived on disabled port?? if we don't consider this an error why bother checking?
	}

void C3GPMuxer::FlushBuffers(TUint32 aPortIndex)
	{
	DEBUG_PRINTF2(_L8("C3GPMuxer::FlushBuffers : aPortIndex[%u]"), aPortIndex);

	__ASSERT_DEBUG(aPortIndex == OMX_ALL || aPortIndex < COmxIL3GPMuxer::EPortIndexMax, User::Invariant());

	if (aPortIndex == OMX_ALL || aPortIndex < COmxIL3GPMuxer::EPortIndexMax)
		{
		iQueMutex.Wait();

		if (aPortIndex == OMX_ALL)
			{
			for (TInt portIndex = 0; portIndex < COmxIL3GPMuxer::EPortIndexMax; ++portIndex)
				{
				DoFlushBuffers(portIndex);
				}
			}
		else
			{
			DoFlushBuffers(aPortIndex);
			}

		iQueMutex.Signal();
		}
	}

void C3GPMuxer::DoFlushBuffers(TUint32 aPortIndex)
	{
	DEBUG_PRINTF2(_L8("C3GPMuxer::DoFlushBuffers : aPortIndex[%u]"), aPortIndex);

	OMX_BUFFERHEADERTYPE* buffer = NULL;
	switch(aPortIndex)
		{
		case COmxIL3GPMuxer::EPortIndexAudioInput:
			{
			if (iAudioBuffer)
				{
				ReturnBuffer(iAudioBuffer, aPortIndex);
				}
				
			while(iAudioQueue.Receive(buffer) != KErrUnderflow)
				{
				ReturnBuffer(buffer, aPortIndex); 
				}
			break;
			}
		case COmxIL3GPMuxer::EPortIndexVideoInput:
			{
			if (iCurrentVideoBuffer)
				{
				ReturnBuffer(iCurrentVideoBuffer, aPortIndex);
				iCurrentVideoBuffer = NULL;
				}
			if (iNextVideoBuffer)
				{
				ReturnBuffer(iNextVideoBuffer, aPortIndex);
				iNextVideoBuffer = NULL;
				}
			while(iVideoQueue.Receive(buffer) != KErrUnderflow)
				{
				ReturnBuffer(buffer, aPortIndex); 
				}
			break;			
			}
		default:
			{
			User::Panic(K3GPMuxerPanic, KErrArgument);
			break;
			}
		}
	}

TBool C3GPMuxer::RemoveBuffer(OMX_BUFFERHEADERTYPE* aBufferHeader)
	{
	DEBUG_PRINTF2(_L8("C3GPMuxer::RemoveBuffer : aBufferHeader[%X]"), aBufferHeader);
	iQueMutex.Wait();	
	TBool found = EFalse;
	
	// check the current audio/video buffers first
	if(aBufferHeader == iAudioBuffer)
		{
		iAudioBuffer = NULL;
		found = ETrue;
		}

	if(!found && aBufferHeader == iCurrentVideoBuffer)
		{
		iCurrentVideoBuffer = NULL;
		found = ETrue;
		}
	if(!found && aBufferHeader == iNextVideoBuffer)
		{
		iNextVideoBuffer = NULL;
		found = ETrue;
		}

	if (!found)
		{
		found = RemoveFromQueue(iAudioQueue, aBufferHeader);
		}

	if (!found)
		{
		found = RemoveFromQueue(iVideoQueue, aBufferHeader);
		}

	iQueMutex.Signal();

	return found;
	}

void C3GPMuxer::SetFilename(const HBufC* aFilename)
	{
	DEBUG_PRINTF(_L8("C3GPMuxer::SetFilename"));
 	iFilename = aFilename;
	}

void C3GPMuxer::SetAudioVideoProperties(OMX_U32& aFrameWidth, OMX_U32& aFrameHeight, 
									OMX_U32& aFramerate, OMX_U32& aBitRate, OMX_U32& /*aAudioFramerate*/)
	{
	DEBUG_PRINTF(_L8("C3GPMuxer::SetAudioVideoProperties"));
	iVideoSize.iWidth = static_cast<TInt>(aFrameWidth);
	iVideoSize.iHeight = static_cast<TInt>(aFrameHeight);
	
	iBitRate = aBitRate;

	CalculateVideoTimeScale(aFramerate);
	}

/**
Calculate video timescale and frame duration.
The video timescale is the nearest whole number multiple of the frame rate.
Where the frame rate includes more than 2 decimal places, it is rounded down
to 2 decimal places. This means the calculation is not wholly accurate in those
circumstances, but hopefully it is close enough.
*/
void C3GPMuxer::CalculateVideoTimeScale(OMX_U32& aFramerate)
	{
	// Get rid of floating point and round to 2 decimal places
	TInt fps = ((static_cast<TReal>(aFramerate) / (1<<16)) * 100.0) + 0.5;

	iVideoTimeScale = 0;
	iDefaultVideoFrameDuration = 1;

	// Find nearest whole number multiple
	for (TInt i = 1; i <= 100; ++i)
		{
		if ((fps * i) % 100 == 0)
			{
			iVideoTimeScale = fps * i / 100;
			iDefaultVideoFrameDuration = i;
			break;
			}
		}
	}

void C3GPMuxer::RunL()
	{
	DEBUG_PRINTF(_L8("C3GPMuxer::RunL"));
	iQueMutex.Wait();

	iRequestOutstanding = EFalse;	
	
	if (!iPaused)
	    {
	    if (iAudioPortEnabled && iVideoPortEnabled)
	        {
	        RunAudioVideoL();
	        }
	    else if (iAudioPortEnabled)
	        {
	        RunAudioL();
	        }
	    else if (iVideoPortEnabled)
	        {
	        RunVideoL();
	        }
	    }
	else
	    {
	    iStatus = KRequestPending;	    
	    }

    SetActive();
	iQueMutex.Signal();
	}

void C3GPMuxer::RunAudioVideoL()
	{
	// When opening the composer, both audio and video properties must be passed in
	if(!iAudioBuffer)
		{
		// ignore error, if KErrUnderflow then we go back to waiting on iAudioQueue
		iAudioQueue.Receive(iAudioBuffer);
		}
	else
		{
		TInt err = KErrNone;
		if (!iCurrentVideoBuffer)
			{
			err = iVideoQueue.Receive(iCurrentVideoBuffer);					

			// if KErrUnderflow then we go back to waiting on iVideoQueue
			if (err == KErrNone)
				{
				// both audio and video buffers has been received
				if (!iComposerOpened)
					{
					OpenComposerL();
					iComposerOpened = ETrue;

					iVideoDuration = (iCurrentVideoBuffer->nTimeStamp * iVideoTimeScale) / KMicroSecondsPerSecond;

					ReturnBuffer(iAudioBuffer, COmxIL3GPMuxer::EPortIndexAudioInput);
					iAudioBuffer = NULL;
					// [SL] FIXME may also need to return iVideoBuffer in nFilledLen = 0
					// (VideoSpecificInfo was entire buffer)
					}

				// We need the timestamp of the next buffer to work out the frame duration of the current video buffer
				// so wait to receive the next video buffer
				}
			}
		else
			{
			if(!iNextVideoBuffer)
				{
				err = iVideoQueue.Receive(iNextVideoBuffer);
				}

			// if KErrUnderflow then we go back to waiting on iVideoQueue
			if(err == KErrNone)
				{
				// next video buffer received
						
				// audio/video frames are added into the composer in the order of timestap
				if (iAudioBuffer->nTimeStamp < iCurrentVideoBuffer->nTimeStamp)
					{
					WriteAudioBuffer();
					iAudioBuffer = NULL;					
					}
				else
					{
					WriteVideoBufferL();
					iCurrentVideoBuffer = iNextVideoBuffer;
					iNextVideoBuffer = NULL;									
							
					if (iCurrentVideoBuffer->nFlags & OMX_BUFFERFLAG_EOS)
						{
						// this is the last video buffer
						WriteVideoBufferL();
						iCurrentVideoBuffer = NULL;
						}			
					}
				}			
			}
		}
			
	if(!iAudioBuffer)
		{
		iAudioQueue.NotifyDataAvailable(iStatus);
		}
	else if(!iNextVideoBuffer)
		{
		iVideoQueue.NotifyDataAvailable(iStatus);
		}
	else
		{
		// already have both buffers available
		TRequestStatus* statusPtr = &iStatus;
		User::RequestComplete(statusPtr, KErrNone);
		}

	iRequestOutstanding = ETrue;
	}

void C3GPMuxer::RunAudioL()
	{
	if(iAudioBuffer == NULL)
		{
		TInt err = iAudioQueue.Receive(iAudioBuffer);
		// KErrUnderflow if RemoveBuffer or FlushBuffers occurs between notify completion and RunL
		if(err != KErrNone && err != KErrUnderflow)
			{
			HandleError(err);
			return;
			}
		}

	if(iAudioBuffer != NULL)
		{
		if (!iComposerOpened)
			{
			OpenComposerL();
			iComposerOpened = ETrue;
			//return the buffer
			ReturnBuffer(iAudioBuffer, COmxIL3GPMuxer::EPortIndexAudioInput);
			}
		else
			{
			//write the non header audio buffer.
			WriteAudioBuffer();
			}
		iAudioBuffer = NULL;
		}
	
	iAudioQueue.NotifyDataAvailable(iStatus);
    iRequestOutstanding = ETrue;	
	}

void C3GPMuxer::RunVideoL()
	{
	if (!iCurrentVideoBuffer)
		{
		// FIXME KErrUnderflow if RemoveBuffer or FlushBuffers occurs between notify completion and RunL
		HandleError(iVideoQueue.Receive(iCurrentVideoBuffer));				

		if (!iComposerOpened)
			{
			OpenComposerL();
			iComposerOpened = ETrue;
			}

		iVideoDuration = (iCurrentVideoBuffer->nTimeStamp * iVideoTimeScale) / KMicroSecondsPerSecond;

		// We need the timestamp of the next buffer to work out the frame duration of the current buffer
		// so wait to receive the next buffer
		}
	else
		{
		// next buffer received
		// FIXME KErrUnderflow if RemoveBuffer or FlushBuffers occurs between notify completion and RunL
		HandleError(iVideoQueue.Receive(iNextVideoBuffer));

		WriteVideoBufferL();

		iCurrentVideoBuffer = iNextVideoBuffer;
		iNextVideoBuffer = NULL;
		
		if (iCurrentVideoBuffer->nFlags & OMX_BUFFERFLAG_EOS)
			{
			// this is the last buffer
			WriteVideoBufferL();
			iCurrentVideoBuffer = NULL;
			}
		}
		
	iVideoQueue.NotifyDataAvailable(iStatus);
	iRequestOutstanding = ETrue;	
	}

void C3GPMuxer::OpenComposerL()
	{
	DEBUG_PRINTF(_L8("C3GPMuxer::OpenComposer"));
	T3GPAudioPropertiesBase* audioProperties = NULL;
	TPtr8 audioPtr(NULL,0);
	
	if (iAudioPortEnabled)
		{
 		// The first buffer is the decoder specific info.
 		audioPtr.Set(iAudioBuffer->pBuffer + iAudioBuffer->nOffset, 
 					iAudioBuffer->nFilledLen, 
 					iAudioBuffer->nAllocLen - iAudioBuffer->nOffset);

		}
	//The following obj need to be declared in the scope of this function and 
	//also after audioPtr is set.
	//Mpeg4Audio is hardcoded for now.
	T3GPAudioPropertiesMpeg4Audio audioPropertiesObj(KAudioTimeScale, audioPtr);
	
	// If audio port is disabled, audioProperties needs to be NULL
	if (iAudioPortEnabled)
		{
		audioProperties=&audioPropertiesObj;
		}
	
	T3GPVideoPropertiesBase* videoProperties = NULL;
	TPtr8 videoPtr(NULL,0);
	TBool foundVideoFrame = EFalse;
	if (iVideoPortEnabled)
		{
		// get decoder specific info length
		TInt offset;	
		const TUint8* buf = reinterpret_cast<const TUint8*>(iCurrentVideoBuffer->pBuffer + iCurrentVideoBuffer->nOffset);
		TUint32 stitch = 0xFFFFFFFF;
		for(offset = 0; offset < iCurrentVideoBuffer->nFilledLen; offset++)
			{
			stitch <<= 8;
			stitch |= buf[offset];
			if(stitch == 0x000001B6)	// start of a frame
				{
				foundVideoFrame = ETrue;
				break;
				}
			}		

		if (foundVideoFrame)
			{
			offset -= 3;
			videoPtr.Set(iCurrentVideoBuffer->pBuffer + iCurrentVideoBuffer->nOffset, 
			             offset, iCurrentVideoBuffer->nAllocLen - iCurrentVideoBuffer->nOffset);
			iCurrentVideoBuffer->nOffset += offset;
			iCurrentVideoBuffer->nFilledLen -= offset;
			}
		else
			{
			// We assume that the whole frame is the decoder specific info
			videoPtr.Set(iCurrentVideoBuffer->pBuffer + iCurrentVideoBuffer->nOffset,
			             iCurrentVideoBuffer->nFilledLen, iCurrentVideoBuffer->nAllocLen - iCurrentVideoBuffer->nOffset);

			// Whole buffer has been used. Set remaining length to zero so that when the
			// next buffer arrives we don't try to write out the decoder specific info
			// as a frame
			iCurrentVideoBuffer->nOffset += iCurrentVideoBuffer->nFilledLen;
			iCurrentVideoBuffer->nFilledLen = 0;
			}

		// FIXME unhandled allocation error - store this on the stack?
		videoProperties = new T3GPVideoPropertiesMpeg4Video(iVideoTimeScale, iVideoSize, iBitRate, iBitRate, videoPtr);
		}

	HandleError(iComposer->Open(E3GP3GP, videoProperties, audioProperties, *iFilename, E3GPLongClip));

	delete videoProperties;
	}

void C3GPMuxer::WriteVideoBufferL()
	{
	DEBUG_PRINTF(_L8("C3GPMuxer::WriteVideoBuffer"));
	TUint8* currentBuffer = NULL;
	TInt currentBufferLength = 0;
	TInt currentBufferMaxLength = 0;
	TBool multipleFrameInBuffer = EFalse;
			
	if (iPartialFrame.Length() > 0)
		{
		// merge the partial frame from the previous buffer
				
		if (iPartialFrame.Length()+iCurrentVideoBuffer->nFilledLen > KMaxBufferSize)
			{
			iPartialFrame.ReAllocL(iPartialFrame.Length()+iCurrentVideoBuffer->nFilledLen);
			}
				
		iPartialFrame.Append(iCurrentVideoBuffer->pBuffer + iCurrentVideoBuffer->nOffset, iCurrentVideoBuffer->nFilledLen);			
				
		currentBuffer = const_cast<TUint8*>(iPartialFrame.Ptr());
		currentBufferLength = iPartialFrame.Length();
		currentBufferMaxLength = iPartialFrame.MaxLength();
		
		multipleFrameInBuffer = ETrue;		
		}
	else
		{
		currentBuffer = iCurrentVideoBuffer->pBuffer + iCurrentVideoBuffer->nOffset;
		currentBufferLength = iCurrentVideoBuffer->nFilledLen;
		currentBufferMaxLength = iCurrentVideoBuffer->nAllocLen - iCurrentVideoBuffer->nOffset;
		}

	// Write video frames from buffer.
	// Check for non-zero buffer length in case we have been sent an empty buffer
	if (currentBufferLength > 0)
		{
		if(!multipleFrameInBuffer && (iCurrentVideoBuffer->nFlags & OMX_BUFFERFLAG_SYNCFRAME))
			{
			// buffer contains one full I frame
			TInt nextFrameDuration = CalculateNextFrameDuration();
			TPtr8 videoPtr(currentBuffer, currentBufferLength, currentBufferMaxLength);									
			HandleError(iComposer->WriteVideoFrame(videoPtr, nextFrameDuration, ETrue));
			}
		else if (!multipleFrameInBuffer && (iCurrentVideoBuffer->nFlags & OMX_BUFFERFLAG_ENDOFFRAME))
			{
			// buffer contains one full frame
	
			// Evaluate whether it is an I frame 
			TInt offset = 0;	
			TUint32 stitch = 0xFFFFFFFF;
			TBool isIframe = EFalse;
					
			for(offset = 0; offset < currentBufferLength; offset++)
				{					
				stitch <<= 8;
				stitch |= currentBuffer[offset];
		
				if(stitch == 0x000001B6)		// start of a frame
					{
					TUint8 iframebits = currentBuffer[offset+1] >> 6;	// get the next 2 bits					
					if (iframebits == 0)
						{
						isIframe = ETrue;
						}
					else
						{
						isIframe = EFalse;							
						}
								
					break;		
					}
				}
		 
			TInt nextFrameDuration = CalculateNextFrameDuration();
			TPtr8 videoPtr(currentBuffer, currentBufferLength, currentBufferMaxLength);								
			HandleError(iComposer->WriteVideoFrame(videoPtr, nextFrameDuration, isIframe));
			}
		else
			{
			// buffer either contains multiple frames or part of a single frame
			// so need to parse the buffer to retrieve the frames
	
			TInt offset = 0;
			TUint32 stitch = 0xFFFFFFFF;
	
			TBool foundNextFrame = EFalse;
			TInt frameLength = 0;
			TInt frameOffset = 0;
			TBool isIframe = EFalse;
			TBool frameWritten = EFalse;
					
			// retieve and write video frames
			for(offset = 0; offset < currentBufferLength; offset++)
				{					
				stitch <<= 8;
				stitch |= currentBuffer[offset];
	
				frameLength++;	
		
				if(!foundNextFrame && stitch == 0x000001B6)
					{
					foundNextFrame = ETrue;
							
					// Evaluate whether it is an I frame 
					if (offset+1 >= currentBufferLength)
						{
						break; // reached the end of the buffer
						}
								
					TUint8 iframebits = currentBuffer[offset+1] >> 6;	// get the next 2 bits					
					if (iframebits == 0)
						{
						isIframe = ETrue;
						}
					else
						{
						isIframe = EFalse;							
						}	
					}
				else if (foundNextFrame && (stitch == 0x000001B0 || stitch == 0x00000120))
					{
					// start of next frame (which includes VOS and/or VOL header)
	
					// write the current frame
					TInt nextFrameDuration = CalculateNextFrameDurationPartial(frameWritten);
					TPtr8 videoPtr(currentBuffer + frameOffset, frameLength - 4, currentBufferMaxLength - frameOffset);										
					HandleError(iComposer->WriteVideoFrame(videoPtr, nextFrameDuration, isIframe));
					frameWritten = ETrue;

					frameOffset = offset - 3; // to include the VOS/VOL start code in the next frame
					frameLength = 4;

					foundNextFrame = EFalse;
					isIframe = EFalse;
					}
				else if (foundNextFrame && stitch == 0x000001B6)
					{
					// start of next frame
	
					// write the current frame
					TInt nextFrameDuration = CalculateNextFrameDurationPartial(frameWritten);
					TPtr8 videoPtr(currentBuffer + frameOffset,	frameLength - 4, currentBufferMaxLength - frameOffset);
					HandleError(iComposer->WriteVideoFrame(videoPtr, nextFrameDuration, isIframe));
					frameWritten = ETrue;
		
					frameOffset = offset - 3; // to include the VOP start code in the next frame		
					frameLength = 4;						
							
					foundNextFrame = ETrue;
							
					// Evaluate whether it is an I frame 	
					if (offset+1 >= currentBufferLength)
						{
						break; // reached the end of the buffer
						}
	
					TUint8 iframebits = currentBuffer[offset+1] >> 6;  // get the next 2 bits						
					if (iframebits == 0)
						{
						isIframe = ETrue;
						}
					else
						{
						isIframe = EFalse;							
						}	
					}
				}
					
			if (frameLength > 0)
				{
				if (iCurrentVideoBuffer->nFlags & OMX_BUFFERFLAG_EOS)
					{
					// this is the last buffer, so we can assume that the rest of the buffer contains one full frame
					TInt nextFrameDuration = CalculateNextFrameDurationPartial(frameWritten);
					TPtr8 videoPtr(currentBuffer + frameOffset,	frameLength, currentBufferMaxLength - frameOffset);
					HandleError(iComposer->WriteVideoFrame(videoPtr, nextFrameDuration, isIframe));
					frameWritten = ETrue;
					}
				else
					{
					// the buffer may contain part of a frame
					// save it for use later
					iPartialFrame.Copy(currentBuffer + frameOffset, frameLength);
					}
				}
			else
				{
				User::Panic(K3GPMuxerPanic, KErrGeneral);  // frameLength should never be 0
				}	
			}
		}

	ReturnBuffer(iCurrentVideoBuffer, COmxIL3GPMuxer::EPortIndexVideoInput);										
	}

TInt C3GPMuxer::CalculateNextFrameDuration()
	{
	TInt64 durationSinceStart = (iCurrentVideoBuffer->nTimeStamp * iVideoTimeScale) / KMicroSecondsPerSecond;
	TInt nextFrameDuration = durationSinceStart - iVideoDuration;
	if (nextFrameDuration < iDefaultVideoFrameDuration)
		{
		nextFrameDuration = iDefaultVideoFrameDuration;
		}

	iVideoDuration = durationSinceStart;

	return nextFrameDuration;
	}

TInt C3GPMuxer::CalculateNextFrameDurationPartial(TBool aFrameWritten)
	{
	if (!aFrameWritten)
		{
		return CalculateNextFrameDuration();
		}

	iVideoDuration += iDefaultVideoFrameDuration;
	
	return iDefaultVideoFrameDuration;
	}

void C3GPMuxer::WriteAudioBuffer()
	{
	DEBUG_PRINTF(_L8("C3GPMuxer::WriteAudioBuffer"));
	
	if (iAudioBuffer->nFilledLen != 0)
		{
		TPtr8 audioPtr(iAudioBuffer->pBuffer + iAudioBuffer->nOffset, 
								iAudioBuffer->nFilledLen, iAudioBuffer->nAllocLen - iAudioBuffer->nOffset);


 		//Calculate the audio frame duration in timescale
		//nTimeStamp is in microseconds
		TUint durationInTimeSlice=KAudioTimeScale*iAudioBuffer->nTimeStamp/KMicroSecondsPerSecond;
 		TUint duration=durationInTimeSlice-iAudioFrameDuration;
 		iAudioFrameDuration=durationInTimeSlice;
 		HandleError(iComposer->WriteAudioFrames(audioPtr, duration));		


	
		}
	ReturnBuffer(iAudioBuffer, COmxIL3GPMuxer::EPortIndexAudioInput);
	}

void C3GPMuxer::ReturnBuffer(OMX_BUFFERHEADERTYPE* aBuffer, TUint32 aPortIndex)
	{
	DEBUG_PRINTF3(_L8("C3GPMuxer::ReturnBuffer : aBuffer[%X]; aPortIndex[%u]"), aBuffer, aPortIndex);
	OMX_U32 flags = aBuffer->nFlags;
	aBuffer->nFilledLen = 0;
	aBuffer->nFlags = 0;
	aBuffer->nOffset = 0;
	aBuffer->nTimeStamp = 0;
	iCallbacks.BufferDoneNotification(aBuffer, aPortIndex, OMX_DirInput);

	if(flags & OMX_BUFFERFLAG_EOS)
		{
		iCallbacks.EventNotification(OMX_EventBufferFlag, aPortIndex, flags, NULL);
		}
	}
	
void C3GPMuxer::DoCancel()
	{
	DEBUG_PRINTF(_L8("C3GPMuxer::DoCancel"));
	if (iAudioPortEnabled)
		{
		iAudioQueue.CancelDataAvailable();
		}
	
	if (iVideoPortEnabled)
		{
		iVideoQueue.CancelDataAvailable();		
		}
	}

TBool C3GPMuxer::RemoveFromQueue(RMsgQueue<OMX_BUFFERHEADERTYPE*>& aQueue, OMX_BUFFERHEADERTYPE* aBufferHeader)
	{
	DEBUG_PRINTF3(_L8("C3GPMuxer::RemoveFromQueue : aQueue[%X]; aBufferHeader[%X]"), &aQueue, aBufferHeader);
	TBool removed = EFalse;
	OMX_BUFFERHEADERTYPE* bufferHeader = NULL;
	while(aQueue.Receive(bufferHeader) != KErrUnderflow)
		{
		if(bufferHeader != aBufferHeader)
			{
			TInt error = iRemoveQueue.Send(bufferHeader);
			__ASSERT_DEBUG(error == KErrNone, User::Panic(K3GPMuxerPanic, error));
			}
		else
			{
			removed = ETrue;
			}
		}
	while(iRemoveQueue.Receive(bufferHeader) != KErrUnderflow)
		{
		TInt error = aQueue.Send(bufferHeader);
		__ASSERT_DEBUG(error == KErrNone, User::Panic(K3GPMuxerPanic, error));
		}
	return removed;
	}

void C3GPMuxer::Pause()
	{
	DEBUG_PRINTF(_L8("C3GPMuxer::Pause"));
	iPaused = ETrue;
	}

void C3GPMuxer::HandleError(TInt aError)
	{
	DEBUG_PRINTF2(_L8("C3GPMuxer::HandleError : aError[%d]"), aError);
	OMX_ERRORTYPE omxError = SymbianErrorToOmx(aError);
	if (omxError != OMX_ErrorNone)
		{
		iMuxerInvalid = ETrue;
		iCallbacks.ErrorEventNotification(omxError);
		}
	}
	
TBool C3GPMuxer::IsMuxerInvalid() const
	{
	return iMuxerInvalid;				
	}	

OMX_ERRORTYPE C3GPMuxer::SymbianErrorToOmx(TInt aError)
	{
	switch(aError)
		{
	case KErrNone:
		return OMX_ErrorNone;
	case KErrNoMemory:
		return OMX_ErrorInsufficientResources;
	case KErrWrite:
		return OMX_ErrorContentPipeOpenFailed;
	default:
		return OMX_ErrorUndefined;
		}
	}