omxilvideocomps/omxil3gpmuxer/src/c3gpmuxer.cpp
changeset 0 5d29cba61097
equal deleted inserted replaced
-1:000000000000 0:5d29cba61097
       
     1 /*
       
     2 * Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 /**
       
    20 @file
       
    21 @internalComponent
       
    22 */
       
    23 
       
    24 #include "comxil3gpmuxeraudioinputport.h"
       
    25 #include "comxil3gpmuxervideoinputport.h"
       
    26 #include "c3gpmuxer.h"
       
    27 #include "log.h"
       
    28 #include <openmax/il/common/omxilcallbacknotificationif.h>
       
    29 
       
    30 _LIT(K3GPMuxerPanic, "C3GPMuxer");
       
    31 const TUint KAudioTimeScale = 44100;
       
    32 static const TUint KThreadStackSize = 8192;	// 8KB
       
    33 const TInt KMaxBufferSize = 62400 * 2;
       
    34 const TInt KMicroSecondsPerSecond = 1000000;
       
    35 
       
    36 
       
    37 C3GPMuxer* C3GPMuxer::NewL(MOmxILCallbackNotificationIf& aCallbacks)
       
    38 	{
       
    39 	DEBUG_PRINTF(_L8("C3GPMuxer::NewL"));
       
    40 	C3GPMuxer* self = new (ELeave) C3GPMuxer(aCallbacks);
       
    41 	CleanupStack::PushL(self);
       
    42 	self->ConstructL();
       
    43 	CleanupStack::Pop(self);
       
    44 	return self;
       
    45 	}
       
    46 
       
    47 C3GPMuxer::C3GPMuxer(MOmxILCallbackNotificationIf& aCallbacks): 
       
    48  CActive(EPriorityStandard), 
       
    49  iCallbacks(aCallbacks)
       
    50 	{
       
    51 	}
       
    52 
       
    53 void C3GPMuxer::ConstructL()
       
    54 	{
       
    55 	User::LeaveIfError(iVideoQueue.CreateLocal(KMaxVideoBuffers));	
       
    56 	iPartialFrame.CreateL(KMaxBufferSize);	
       
    57 	User::LeaveIfError(iAudioQueue.CreateLocal(KMaxAudioBuffers));
       
    58 	User::LeaveIfError(iRemoveQueue.CreateLocal(Max(KMaxVideoBuffers, KMaxAudioBuffers)));
       
    59 	User::LeaveIfError(iQueMutex.CreateLocal());
       
    60 
       
    61 	iComposer = C3GPCompose::NewL();
       
    62 	}
       
    63 
       
    64 void C3GPMuxer::StartL(TBool aAudioPortEnabled, TBool aVideoPortEnabled)
       
    65 	{
       
    66 	DEBUG_PRINTF(_L8("C3GPMuxer::StartL"));
       
    67 
       
    68 	iPaused = EFalse;
       
    69 
       
    70 	if (!iThreadRunning)
       
    71 		{
       
    72 		iAudioPortEnabled = aAudioPortEnabled;
       
    73 		iVideoPortEnabled = aVideoPortEnabled;
       
    74 
       
    75 		TBuf<40> threadName;
       
    76 		threadName.Format(_L("C3GPMuxerThread@%08X"), this);
       
    77 	
       
    78 		User::LeaveIfError(iThread.Create(threadName, ThreadEntryPoint, KThreadStackSize, NULL, this));
       
    79 
       
    80 		// start the thread and wait for it to start the active scheduler
       
    81 		TRequestStatus status;
       
    82 		iThread.Rendezvous(status);
       
    83 		iThread.Resume();
       
    84 		User::WaitForRequest(status);
       
    85 		User::LeaveIfError(status.Int());
       
    86 		}
       
    87 	else
       
    88 	    {
       
    89 	    // self complete
       
    90         iQueMutex.Wait();
       
    91         if (!iRequestOutstanding)
       
    92             {
       
    93             TRequestStatus* statusPtr = &iStatus;
       
    94             iThread.RequestComplete(statusPtr, KErrNone);        
       
    95             }
       
    96         iQueMutex.Signal();
       
    97 	    }
       
    98 	}
       
    99 
       
   100 TInt C3GPMuxer::ThreadEntryPoint(TAny* aPtr)
       
   101 	{
       
   102 	CTrapCleanup* cleanup = CTrapCleanup::New();
       
   103 	if(!cleanup)
       
   104 		{
       
   105 		return KErrNoMemory;
       
   106 		}
       
   107 
       
   108 	// Call this objects clock initialisation routine
       
   109 	TRAPD(err, static_cast<C3GPMuxer*>(aPtr)->RunThreadL());
       
   110 	delete cleanup;
       
   111 	return err;
       
   112 	}
       
   113 
       
   114 void C3GPMuxer::RunThreadL()
       
   115 	{
       
   116 	DEBUG_PRINTF(_L8("C3GPMuxer::RunThreadL"));
       
   117 	CActiveScheduler* sched = new (ELeave) CActiveScheduler;
       
   118 	CleanupStack::PushL(sched);
       
   119 	
       
   120 	CActiveScheduler::Install(sched);
       
   121 	CActiveScheduler::Add(this);
       
   122 
       
   123 	if (iAudioPortEnabled)
       
   124 		{
       
   125 		iAudioQueue.NotifyDataAvailable(iStatus);
       
   126 		SetActive();												
       
   127         iRequestOutstanding = ETrue;
       
   128 		}
       
   129 	else if (iVideoPortEnabled)
       
   130 		{
       
   131 		iVideoQueue.NotifyDataAvailable(iStatus);
       
   132 		SetActive();									
       
   133         iRequestOutstanding = ETrue;
       
   134 		}
       
   135 
       
   136 	iEndWaitAO = CEndWaitAO::NewL();
       
   137 	
       
   138 	iThreadRunning = ETrue;
       
   139 
       
   140 	iThread.Rendezvous(KErrNone);
       
   141 
       
   142 	CActiveScheduler::Start();
       
   143 
       
   144 	HandleError(iComposer->Complete());
       
   145 	iComposerOpened = EFalse;
       
   146 	
       
   147 	CleanupStack::PopAndDestroy(sched);
       
   148 	}
       
   149 
       
   150 C3GPMuxer::~C3GPMuxer()
       
   151 	{
       
   152 	DEBUG_PRINTF(_L8("C3GPMuxer::~C3GPMuxer"));
       
   153 
       
   154 	if(iThreadRunning)
       
   155 		{		
       
   156 		iThreadRunning = EFalse;
       
   157 		TRequestStatus logonStatus;
       
   158 		iThread.Logon(logonStatus);
       
   159 
       
   160 		iEndWaitAO->Call();
       
   161 		User::WaitForRequest(logonStatus);
       
   162 
       
   163 		delete iEndWaitAO;
       
   164 		iEndWaitAO = NULL;
       
   165 		}
       
   166 
       
   167 	iVideoQueue.Close();
       
   168 	iPartialFrame.Close();
       
   169 	iAudioQueue.Close();
       
   170 	iRemoveQueue.Close();
       
   171 
       
   172 	delete iComposer;
       
   173 
       
   174 	iThread.Close();
       
   175 	iQueMutex.Close();
       
   176 	}
       
   177 
       
   178 void C3GPMuxer::ProcessThisBufferL(OMX_BUFFERHEADERTYPE* aBufferHeader, TUint32 aPortIndex)
       
   179 	{
       
   180 	DEBUG_PRINTF3(_L8("C3GPMuxer::ProcessThisBufferL : aBufferHeader[%X]; aPortIndex[%u]"), aBufferHeader, aPortIndex);
       
   181 	__ASSERT_ALWAYS(aBufferHeader->nOffset + aBufferHeader->nFilledLen <= aBufferHeader->nAllocLen, User::Invariant());
       
   182 	
       
   183 	if (aPortIndex == COmxIL3GPMuxer::EPortIndexAudioInput && iAudioPortEnabled)
       
   184 		{
       
   185 		User::LeaveIfError(iAudioQueue.Send(aBufferHeader));		
       
   186 		}
       
   187 	else if (aPortIndex == COmxIL3GPMuxer::EPortIndexVideoInput && iVideoPortEnabled)
       
   188 		{
       
   189 		User::LeaveIfError(iVideoQueue.Send(aBufferHeader));		
       
   190 		}
       
   191 	// FIXME buffer arrived on disabled port?? if we don't consider this an error why bother checking?
       
   192 	}
       
   193 
       
   194 void C3GPMuxer::FlushBuffers(TUint32 aPortIndex)
       
   195 	{
       
   196 	DEBUG_PRINTF2(_L8("C3GPMuxer::FlushBuffers : aPortIndex[%u]"), aPortIndex);
       
   197 
       
   198 	__ASSERT_DEBUG(aPortIndex == OMX_ALL || aPortIndex < COmxIL3GPMuxer::EPortIndexMax, User::Invariant());
       
   199 
       
   200 	if (aPortIndex == OMX_ALL || aPortIndex < COmxIL3GPMuxer::EPortIndexMax)
       
   201 		{
       
   202 		iQueMutex.Wait();
       
   203 
       
   204 		if (aPortIndex == OMX_ALL)
       
   205 			{
       
   206 			for (TInt portIndex = 0; portIndex < COmxIL3GPMuxer::EPortIndexMax; ++portIndex)
       
   207 				{
       
   208 				DoFlushBuffers(portIndex);
       
   209 				}
       
   210 			}
       
   211 		else
       
   212 			{
       
   213 			DoFlushBuffers(aPortIndex);
       
   214 			}
       
   215 
       
   216 		iQueMutex.Signal();
       
   217 		}
       
   218 	}
       
   219 
       
   220 void C3GPMuxer::DoFlushBuffers(TUint32 aPortIndex)
       
   221 	{
       
   222 	DEBUG_PRINTF2(_L8("C3GPMuxer::DoFlushBuffers : aPortIndex[%u]"), aPortIndex);
       
   223 
       
   224 	OMX_BUFFERHEADERTYPE* buffer = NULL;
       
   225 	switch(aPortIndex)
       
   226 		{
       
   227 		case COmxIL3GPMuxer::EPortIndexAudioInput:
       
   228 			{
       
   229 			if (iAudioBuffer)
       
   230 				{
       
   231 				ReturnBuffer(iAudioBuffer, aPortIndex);
       
   232 				}
       
   233 				
       
   234 			while(iAudioQueue.Receive(buffer) != KErrUnderflow)
       
   235 				{
       
   236 				ReturnBuffer(buffer, aPortIndex); 
       
   237 				}
       
   238 			break;
       
   239 			}
       
   240 		case COmxIL3GPMuxer::EPortIndexVideoInput:
       
   241 			{
       
   242 			if (iCurrentVideoBuffer)
       
   243 				{
       
   244 				ReturnBuffer(iCurrentVideoBuffer, aPortIndex);
       
   245 				iCurrentVideoBuffer = NULL;
       
   246 				}
       
   247 			if (iNextVideoBuffer)
       
   248 				{
       
   249 				ReturnBuffer(iNextVideoBuffer, aPortIndex);
       
   250 				iNextVideoBuffer = NULL;
       
   251 				}
       
   252 			while(iVideoQueue.Receive(buffer) != KErrUnderflow)
       
   253 				{
       
   254 				ReturnBuffer(buffer, aPortIndex); 
       
   255 				}
       
   256 			break;			
       
   257 			}
       
   258 		default:
       
   259 			{
       
   260 			User::Panic(K3GPMuxerPanic, KErrArgument);
       
   261 			break;
       
   262 			}
       
   263 		}
       
   264 	}
       
   265 
       
   266 TBool C3GPMuxer::RemoveBuffer(OMX_BUFFERHEADERTYPE* aBufferHeader)
       
   267 	{
       
   268 	DEBUG_PRINTF2(_L8("C3GPMuxer::RemoveBuffer : aBufferHeader[%X]"), aBufferHeader);
       
   269 	iQueMutex.Wait();	
       
   270 	TBool found = EFalse;
       
   271 	
       
   272 	// check the current audio/video buffers first
       
   273 	if(aBufferHeader == iAudioBuffer)
       
   274 		{
       
   275 		iAudioBuffer = NULL;
       
   276 		found = ETrue;
       
   277 		}
       
   278 
       
   279 	if(!found && aBufferHeader == iCurrentVideoBuffer)
       
   280 		{
       
   281 		iCurrentVideoBuffer = NULL;
       
   282 		found = ETrue;
       
   283 		}
       
   284 	if(!found && aBufferHeader == iNextVideoBuffer)
       
   285 		{
       
   286 		iNextVideoBuffer = NULL;
       
   287 		found = ETrue;
       
   288 		}
       
   289 
       
   290 	if (!found)
       
   291 		{
       
   292 		found = RemoveFromQueue(iAudioQueue, aBufferHeader);
       
   293 		}
       
   294 
       
   295 	if (!found)
       
   296 		{
       
   297 		found = RemoveFromQueue(iVideoQueue, aBufferHeader);
       
   298 		}
       
   299 
       
   300 	iQueMutex.Signal();
       
   301 
       
   302 	return found;
       
   303 	}
       
   304 
       
   305 void C3GPMuxer::SetFilename(const HBufC* aFilename)
       
   306 	{
       
   307 	DEBUG_PRINTF(_L8("C3GPMuxer::SetFilename"));
       
   308  	iFilename = aFilename;
       
   309 	}
       
   310 
       
   311 void C3GPMuxer::SetAudioVideoProperties(OMX_U32& aFrameWidth, OMX_U32& aFrameHeight, 
       
   312 									OMX_U32& aFramerate, OMX_U32& aBitRate, OMX_U32& /*aAudioFramerate*/)
       
   313 	{
       
   314 	DEBUG_PRINTF(_L8("C3GPMuxer::SetAudioVideoProperties"));
       
   315 	iVideoSize.iWidth = static_cast<TInt>(aFrameWidth);
       
   316 	iVideoSize.iHeight = static_cast<TInt>(aFrameHeight);
       
   317 	
       
   318 	iBitRate = aBitRate;
       
   319 
       
   320 	CalculateVideoTimeScale(aFramerate);
       
   321 	}
       
   322 
       
   323 /**
       
   324 Calculate video timescale and frame duration.
       
   325 The video timescale is the nearest whole number multiple of the frame rate.
       
   326 Where the frame rate includes more than 2 decimal places, it is rounded down
       
   327 to 2 decimal places. This means the calculation is not wholly accurate in those
       
   328 circumstances, but hopefully it is close enough.
       
   329 */
       
   330 void C3GPMuxer::CalculateVideoTimeScale(OMX_U32& aFramerate)
       
   331 	{
       
   332 	// Get rid of floating point and round to 2 decimal places
       
   333 	TInt fps = ((static_cast<TReal>(aFramerate) / (1<<16)) * 100.0) + 0.5;
       
   334 
       
   335 	iVideoTimeScale = 0;
       
   336 	iDefaultVideoFrameDuration = 1;
       
   337 
       
   338 	// Find nearest whole number multiple
       
   339 	for (TInt i = 1; i <= 100; ++i)
       
   340 		{
       
   341 		if ((fps * i) % 100 == 0)
       
   342 			{
       
   343 			iVideoTimeScale = fps * i / 100;
       
   344 			iDefaultVideoFrameDuration = i;
       
   345 			break;
       
   346 			}
       
   347 		}
       
   348 	}
       
   349 
       
   350 void C3GPMuxer::RunL()
       
   351 	{
       
   352 	DEBUG_PRINTF(_L8("C3GPMuxer::RunL"));
       
   353 	iQueMutex.Wait();
       
   354 
       
   355 	iRequestOutstanding = EFalse;	
       
   356 	
       
   357 	if (!iPaused)
       
   358 	    {
       
   359 	    if (iAudioPortEnabled && iVideoPortEnabled)
       
   360 	        {
       
   361 	        RunAudioVideoL();
       
   362 	        }
       
   363 	    else if (iAudioPortEnabled)
       
   364 	        {
       
   365 	        RunAudioL();
       
   366 	        }
       
   367 	    else if (iVideoPortEnabled)
       
   368 	        {
       
   369 	        RunVideoL();
       
   370 	        }
       
   371 	    }
       
   372 	else
       
   373 	    {
       
   374 	    iStatus = KRequestPending;	    
       
   375 	    }
       
   376 
       
   377     SetActive();
       
   378 	iQueMutex.Signal();
       
   379 	}
       
   380 
       
   381 void C3GPMuxer::RunAudioVideoL()
       
   382 	{
       
   383 	// When opening the composer, both audio and video properties must be passed in
       
   384 	if(!iAudioBuffer)
       
   385 		{
       
   386 		// ignore error, if KErrUnderflow then we go back to waiting on iAudioQueue
       
   387 		iAudioQueue.Receive(iAudioBuffer);
       
   388 		}
       
   389 	else
       
   390 		{
       
   391 		TInt err = KErrNone;
       
   392 		if (!iCurrentVideoBuffer)
       
   393 			{
       
   394 			err = iVideoQueue.Receive(iCurrentVideoBuffer);					
       
   395 
       
   396 			// if KErrUnderflow then we go back to waiting on iVideoQueue
       
   397 			if (err == KErrNone)
       
   398 				{
       
   399 				// both audio and video buffers has been received
       
   400 				if (!iComposerOpened)
       
   401 					{
       
   402 					OpenComposerL();
       
   403 					iComposerOpened = ETrue;
       
   404 
       
   405 					iVideoDuration = (iCurrentVideoBuffer->nTimeStamp * iVideoTimeScale) / KMicroSecondsPerSecond;
       
   406 
       
   407 					ReturnBuffer(iAudioBuffer, COmxIL3GPMuxer::EPortIndexAudioInput);
       
   408 					iAudioBuffer = NULL;
       
   409 					// [SL] FIXME may also need to return iVideoBuffer in nFilledLen = 0
       
   410 					// (VideoSpecificInfo was entire buffer)
       
   411 					}
       
   412 
       
   413 				// We need the timestamp of the next buffer to work out the frame duration of the current video buffer
       
   414 				// so wait to receive the next video buffer
       
   415 				}
       
   416 			}
       
   417 		else
       
   418 			{
       
   419 			if(!iNextVideoBuffer)
       
   420 				{
       
   421 				err = iVideoQueue.Receive(iNextVideoBuffer);
       
   422 				}
       
   423 
       
   424 			// if KErrUnderflow then we go back to waiting on iVideoQueue
       
   425 			if(err == KErrNone)
       
   426 				{
       
   427 				// next video buffer received
       
   428 						
       
   429 				// audio/video frames are added into the composer in the order of timestap
       
   430 				if (iAudioBuffer->nTimeStamp < iCurrentVideoBuffer->nTimeStamp)
       
   431 					{
       
   432 					WriteAudioBuffer();
       
   433 					iAudioBuffer = NULL;					
       
   434 					}
       
   435 				else
       
   436 					{
       
   437 					WriteVideoBufferL();
       
   438 					iCurrentVideoBuffer = iNextVideoBuffer;
       
   439 					iNextVideoBuffer = NULL;									
       
   440 							
       
   441 					if (iCurrentVideoBuffer->nFlags & OMX_BUFFERFLAG_EOS)
       
   442 						{
       
   443 						// this is the last video buffer
       
   444 						WriteVideoBufferL();
       
   445 						iCurrentVideoBuffer = NULL;
       
   446 						}			
       
   447 					}
       
   448 				}			
       
   449 			}
       
   450 		}
       
   451 			
       
   452 	if(!iAudioBuffer)
       
   453 		{
       
   454 		iAudioQueue.NotifyDataAvailable(iStatus);
       
   455 		}
       
   456 	else if(!iNextVideoBuffer)
       
   457 		{
       
   458 		iVideoQueue.NotifyDataAvailable(iStatus);
       
   459 		}
       
   460 	else
       
   461 		{
       
   462 		// already have both buffers available
       
   463 		TRequestStatus* statusPtr = &iStatus;
       
   464 		User::RequestComplete(statusPtr, KErrNone);
       
   465 		}
       
   466 
       
   467 	iRequestOutstanding = ETrue;
       
   468 	}
       
   469 
       
   470 void C3GPMuxer::RunAudioL()
       
   471 	{
       
   472 	if(iAudioBuffer == NULL)
       
   473 		{
       
   474 		TInt err = iAudioQueue.Receive(iAudioBuffer);
       
   475 		// KErrUnderflow if RemoveBuffer or FlushBuffers occurs between notify completion and RunL
       
   476 		if(err != KErrNone && err != KErrUnderflow)
       
   477 			{
       
   478 			HandleError(err);
       
   479 			return;
       
   480 			}
       
   481 		}
       
   482 
       
   483 	if(iAudioBuffer != NULL)
       
   484 		{
       
   485 		if (!iComposerOpened)
       
   486 			{
       
   487 			OpenComposerL();
       
   488 			iComposerOpened = ETrue;
       
   489 			//return the buffer
       
   490 			ReturnBuffer(iAudioBuffer, COmxIL3GPMuxer::EPortIndexAudioInput);
       
   491 			}
       
   492 		else
       
   493 			{
       
   494 			//write the non header audio buffer.
       
   495 			WriteAudioBuffer();
       
   496 			}
       
   497 		iAudioBuffer = NULL;
       
   498 		}
       
   499 	
       
   500 	iAudioQueue.NotifyDataAvailable(iStatus);
       
   501     iRequestOutstanding = ETrue;	
       
   502 	}
       
   503 
       
   504 void C3GPMuxer::RunVideoL()
       
   505 	{
       
   506 	if (!iCurrentVideoBuffer)
       
   507 		{
       
   508 		// FIXME KErrUnderflow if RemoveBuffer or FlushBuffers occurs between notify completion and RunL
       
   509 		HandleError(iVideoQueue.Receive(iCurrentVideoBuffer));				
       
   510 
       
   511 		if (!iComposerOpened)
       
   512 			{
       
   513 			OpenComposerL();
       
   514 			iComposerOpened = ETrue;
       
   515 			}
       
   516 
       
   517 		iVideoDuration = (iCurrentVideoBuffer->nTimeStamp * iVideoTimeScale) / KMicroSecondsPerSecond;
       
   518 
       
   519 		// We need the timestamp of the next buffer to work out the frame duration of the current buffer
       
   520 		// so wait to receive the next buffer
       
   521 		}
       
   522 	else
       
   523 		{
       
   524 		// next buffer received
       
   525 		// FIXME KErrUnderflow if RemoveBuffer or FlushBuffers occurs between notify completion and RunL
       
   526 		HandleError(iVideoQueue.Receive(iNextVideoBuffer));
       
   527 
       
   528 		WriteVideoBufferL();
       
   529 
       
   530 		iCurrentVideoBuffer = iNextVideoBuffer;
       
   531 		iNextVideoBuffer = NULL;
       
   532 		
       
   533 		if (iCurrentVideoBuffer->nFlags & OMX_BUFFERFLAG_EOS)
       
   534 			{
       
   535 			// this is the last buffer
       
   536 			WriteVideoBufferL();
       
   537 			iCurrentVideoBuffer = NULL;
       
   538 			}
       
   539 		}
       
   540 		
       
   541 	iVideoQueue.NotifyDataAvailable(iStatus);
       
   542 	iRequestOutstanding = ETrue;	
       
   543 	}
       
   544 
       
   545 void C3GPMuxer::OpenComposerL()
       
   546 	{
       
   547 	DEBUG_PRINTF(_L8("C3GPMuxer::OpenComposer"));
       
   548 	T3GPAudioPropertiesBase* audioProperties = NULL;
       
   549 	TPtr8 audioPtr(NULL,0);
       
   550 	
       
   551 	if (iAudioPortEnabled)
       
   552 		{
       
   553  		// The first buffer is the decoder specific info.
       
   554  		audioPtr.Set(iAudioBuffer->pBuffer + iAudioBuffer->nOffset, 
       
   555  					iAudioBuffer->nFilledLen, 
       
   556  					iAudioBuffer->nAllocLen - iAudioBuffer->nOffset);
       
   557 
       
   558 		}
       
   559 	//The following obj need to be declared in the scope of this function and 
       
   560 	//also after audioPtr is set.
       
   561 	//Mpeg4Audio is hardcoded for now.
       
   562 	T3GPAudioPropertiesMpeg4Audio audioPropertiesObj(KAudioTimeScale, audioPtr);
       
   563 	
       
   564 	// If audio port is disabled, audioProperties needs to be NULL
       
   565 	if (iAudioPortEnabled)
       
   566 		{
       
   567 		audioProperties=&audioPropertiesObj;
       
   568 		}
       
   569 	
       
   570 	T3GPVideoPropertiesBase* videoProperties = NULL;
       
   571 	TPtr8 videoPtr(NULL,0);
       
   572 	TBool foundVideoFrame = EFalse;
       
   573 	if (iVideoPortEnabled)
       
   574 		{
       
   575 		// get decoder specific info length
       
   576 		TInt offset;	
       
   577 		const TUint8* buf = reinterpret_cast<const TUint8*>(iCurrentVideoBuffer->pBuffer + iCurrentVideoBuffer->nOffset);
       
   578 		TUint32 stitch = 0xFFFFFFFF;
       
   579 		for(offset = 0; offset < iCurrentVideoBuffer->nFilledLen; offset++)
       
   580 			{
       
   581 			stitch <<= 8;
       
   582 			stitch |= buf[offset];
       
   583 			if(stitch == 0x000001B6)	// start of a frame
       
   584 				{
       
   585 				foundVideoFrame = ETrue;
       
   586 				break;
       
   587 				}
       
   588 			}		
       
   589 
       
   590 		if (foundVideoFrame)
       
   591 			{
       
   592 			offset -= 3;
       
   593 			videoPtr.Set(iCurrentVideoBuffer->pBuffer + iCurrentVideoBuffer->nOffset, 
       
   594 			             offset, iCurrentVideoBuffer->nAllocLen - iCurrentVideoBuffer->nOffset);
       
   595 			iCurrentVideoBuffer->nOffset += offset;
       
   596 			iCurrentVideoBuffer->nFilledLen -= offset;
       
   597 			}
       
   598 		else
       
   599 			{
       
   600 			// We assume that the whole frame is the decoder specific info
       
   601 			videoPtr.Set(iCurrentVideoBuffer->pBuffer + iCurrentVideoBuffer->nOffset,
       
   602 			             iCurrentVideoBuffer->nFilledLen, iCurrentVideoBuffer->nAllocLen - iCurrentVideoBuffer->nOffset);
       
   603 
       
   604 			// Whole buffer has been used. Set remaining length to zero so that when the
       
   605 			// next buffer arrives we don't try to write out the decoder specific info
       
   606 			// as a frame
       
   607 			iCurrentVideoBuffer->nOffset += iCurrentVideoBuffer->nFilledLen;
       
   608 			iCurrentVideoBuffer->nFilledLen = 0;
       
   609 			}
       
   610 
       
   611 		// FIXME unhandled allocation error - store this on the stack?
       
   612 		videoProperties = new T3GPVideoPropertiesMpeg4Video(iVideoTimeScale, iVideoSize, iBitRate, iBitRate, videoPtr);
       
   613 		}
       
   614 
       
   615 	HandleError(iComposer->Open(E3GP3GP, videoProperties, audioProperties, *iFilename, E3GPLongClip));
       
   616 
       
   617 	delete videoProperties;
       
   618 	}
       
   619 
       
   620 void C3GPMuxer::WriteVideoBufferL()
       
   621 	{
       
   622 	DEBUG_PRINTF(_L8("C3GPMuxer::WriteVideoBuffer"));
       
   623 	TUint8* currentBuffer = NULL;
       
   624 	TInt currentBufferLength = 0;
       
   625 	TInt currentBufferMaxLength = 0;
       
   626 	TBool multipleFrameInBuffer = EFalse;
       
   627 			
       
   628 	if (iPartialFrame.Length() > 0)
       
   629 		{
       
   630 		// merge the partial frame from the previous buffer
       
   631 				
       
   632 		if (iPartialFrame.Length()+iCurrentVideoBuffer->nFilledLen > KMaxBufferSize)
       
   633 			{
       
   634 			iPartialFrame.ReAllocL(iPartialFrame.Length()+iCurrentVideoBuffer->nFilledLen);
       
   635 			}
       
   636 				
       
   637 		iPartialFrame.Append(iCurrentVideoBuffer->pBuffer + iCurrentVideoBuffer->nOffset, iCurrentVideoBuffer->nFilledLen);			
       
   638 				
       
   639 		currentBuffer = const_cast<TUint8*>(iPartialFrame.Ptr());
       
   640 		currentBufferLength = iPartialFrame.Length();
       
   641 		currentBufferMaxLength = iPartialFrame.MaxLength();
       
   642 		
       
   643 		multipleFrameInBuffer = ETrue;		
       
   644 		}
       
   645 	else
       
   646 		{
       
   647 		currentBuffer = iCurrentVideoBuffer->pBuffer + iCurrentVideoBuffer->nOffset;
       
   648 		currentBufferLength = iCurrentVideoBuffer->nFilledLen;
       
   649 		currentBufferMaxLength = iCurrentVideoBuffer->nAllocLen - iCurrentVideoBuffer->nOffset;
       
   650 		}
       
   651 
       
   652 	// Write video frames from buffer.
       
   653 	// Check for non-zero buffer length in case we have been sent an empty buffer
       
   654 	if (currentBufferLength > 0)
       
   655 		{
       
   656 		if(!multipleFrameInBuffer && (iCurrentVideoBuffer->nFlags & OMX_BUFFERFLAG_SYNCFRAME))
       
   657 			{
       
   658 			// buffer contains one full I frame
       
   659 			TInt nextFrameDuration = CalculateNextFrameDuration();
       
   660 			TPtr8 videoPtr(currentBuffer, currentBufferLength, currentBufferMaxLength);									
       
   661 			HandleError(iComposer->WriteVideoFrame(videoPtr, nextFrameDuration, ETrue));
       
   662 			}
       
   663 		else if (!multipleFrameInBuffer && (iCurrentVideoBuffer->nFlags & OMX_BUFFERFLAG_ENDOFFRAME))
       
   664 			{
       
   665 			// buffer contains one full frame
       
   666 	
       
   667 			// Evaluate whether it is an I frame 
       
   668 			TInt offset = 0;	
       
   669 			TUint32 stitch = 0xFFFFFFFF;
       
   670 			TBool isIframe = EFalse;
       
   671 					
       
   672 			for(offset = 0; offset < currentBufferLength; offset++)
       
   673 				{					
       
   674 				stitch <<= 8;
       
   675 				stitch |= currentBuffer[offset];
       
   676 		
       
   677 				if(stitch == 0x000001B6)		// start of a frame
       
   678 					{
       
   679 					TUint8 iframebits = currentBuffer[offset+1] >> 6;	// get the next 2 bits					
       
   680 					if (iframebits == 0)
       
   681 						{
       
   682 						isIframe = ETrue;
       
   683 						}
       
   684 					else
       
   685 						{
       
   686 						isIframe = EFalse;							
       
   687 						}
       
   688 								
       
   689 					break;		
       
   690 					}
       
   691 				}
       
   692 		 
       
   693 			TInt nextFrameDuration = CalculateNextFrameDuration();
       
   694 			TPtr8 videoPtr(currentBuffer, currentBufferLength, currentBufferMaxLength);								
       
   695 			HandleError(iComposer->WriteVideoFrame(videoPtr, nextFrameDuration, isIframe));
       
   696 			}
       
   697 		else
       
   698 			{
       
   699 			// buffer either contains multiple frames or part of a single frame
       
   700 			// so need to parse the buffer to retrieve the frames
       
   701 	
       
   702 			TInt offset = 0;
       
   703 			TUint32 stitch = 0xFFFFFFFF;
       
   704 	
       
   705 			TBool foundNextFrame = EFalse;
       
   706 			TInt frameLength = 0;
       
   707 			TInt frameOffset = 0;
       
   708 			TBool isIframe = EFalse;
       
   709 			TBool frameWritten = EFalse;
       
   710 					
       
   711 			// retieve and write video frames
       
   712 			for(offset = 0; offset < currentBufferLength; offset++)
       
   713 				{					
       
   714 				stitch <<= 8;
       
   715 				stitch |= currentBuffer[offset];
       
   716 	
       
   717 				frameLength++;	
       
   718 		
       
   719 				if(!foundNextFrame && stitch == 0x000001B6)
       
   720 					{
       
   721 					foundNextFrame = ETrue;
       
   722 							
       
   723 					// Evaluate whether it is an I frame 
       
   724 					if (offset+1 >= currentBufferLength)
       
   725 						{
       
   726 						break; // reached the end of the buffer
       
   727 						}
       
   728 								
       
   729 					TUint8 iframebits = currentBuffer[offset+1] >> 6;	// get the next 2 bits					
       
   730 					if (iframebits == 0)
       
   731 						{
       
   732 						isIframe = ETrue;
       
   733 						}
       
   734 					else
       
   735 						{
       
   736 						isIframe = EFalse;							
       
   737 						}	
       
   738 					}
       
   739 				else if (foundNextFrame && (stitch == 0x000001B0 || stitch == 0x00000120))
       
   740 					{
       
   741 					// start of next frame (which includes VOS and/or VOL header)
       
   742 	
       
   743 					// write the current frame
       
   744 					TInt nextFrameDuration = CalculateNextFrameDurationPartial(frameWritten);
       
   745 					TPtr8 videoPtr(currentBuffer + frameOffset, frameLength - 4, currentBufferMaxLength - frameOffset);										
       
   746 					HandleError(iComposer->WriteVideoFrame(videoPtr, nextFrameDuration, isIframe));
       
   747 					frameWritten = ETrue;
       
   748 
       
   749 					frameOffset = offset - 3; // to include the VOS/VOL start code in the next frame
       
   750 					frameLength = 4;
       
   751 
       
   752 					foundNextFrame = EFalse;
       
   753 					isIframe = EFalse;
       
   754 					}
       
   755 				else if (foundNextFrame && stitch == 0x000001B6)
       
   756 					{
       
   757 					// start of next frame
       
   758 	
       
   759 					// write the current frame
       
   760 					TInt nextFrameDuration = CalculateNextFrameDurationPartial(frameWritten);
       
   761 					TPtr8 videoPtr(currentBuffer + frameOffset,	frameLength - 4, currentBufferMaxLength - frameOffset);
       
   762 					HandleError(iComposer->WriteVideoFrame(videoPtr, nextFrameDuration, isIframe));
       
   763 					frameWritten = ETrue;
       
   764 		
       
   765 					frameOffset = offset - 3; // to include the VOP start code in the next frame		
       
   766 					frameLength = 4;						
       
   767 							
       
   768 					foundNextFrame = ETrue;
       
   769 							
       
   770 					// Evaluate whether it is an I frame 	
       
   771 					if (offset+1 >= currentBufferLength)
       
   772 						{
       
   773 						break; // reached the end of the buffer
       
   774 						}
       
   775 	
       
   776 					TUint8 iframebits = currentBuffer[offset+1] >> 6;  // get the next 2 bits						
       
   777 					if (iframebits == 0)
       
   778 						{
       
   779 						isIframe = ETrue;
       
   780 						}
       
   781 					else
       
   782 						{
       
   783 						isIframe = EFalse;							
       
   784 						}	
       
   785 					}
       
   786 				}
       
   787 					
       
   788 			if (frameLength > 0)
       
   789 				{
       
   790 				if (iCurrentVideoBuffer->nFlags & OMX_BUFFERFLAG_EOS)
       
   791 					{
       
   792 					// this is the last buffer, so we can assume that the rest of the buffer contains one full frame
       
   793 					TInt nextFrameDuration = CalculateNextFrameDurationPartial(frameWritten);
       
   794 					TPtr8 videoPtr(currentBuffer + frameOffset,	frameLength, currentBufferMaxLength - frameOffset);
       
   795 					HandleError(iComposer->WriteVideoFrame(videoPtr, nextFrameDuration, isIframe));
       
   796 					frameWritten = ETrue;
       
   797 					}
       
   798 				else
       
   799 					{
       
   800 					// the buffer may contain part of a frame
       
   801 					// save it for use later
       
   802 					iPartialFrame.Copy(currentBuffer + frameOffset, frameLength);
       
   803 					}
       
   804 				}
       
   805 			else
       
   806 				{
       
   807 				User::Panic(K3GPMuxerPanic, KErrGeneral);  // frameLength should never be 0
       
   808 				}	
       
   809 			}
       
   810 		}
       
   811 
       
   812 	ReturnBuffer(iCurrentVideoBuffer, COmxIL3GPMuxer::EPortIndexVideoInput);										
       
   813 	}
       
   814 
       
   815 TInt C3GPMuxer::CalculateNextFrameDuration()
       
   816 	{
       
   817 	TInt64 durationSinceStart = (iCurrentVideoBuffer->nTimeStamp * iVideoTimeScale) / KMicroSecondsPerSecond;
       
   818 	TInt nextFrameDuration = durationSinceStart - iVideoDuration;
       
   819 	if (nextFrameDuration < iDefaultVideoFrameDuration)
       
   820 		{
       
   821 		nextFrameDuration = iDefaultVideoFrameDuration;
       
   822 		}
       
   823 
       
   824 	iVideoDuration = durationSinceStart;
       
   825 
       
   826 	return nextFrameDuration;
       
   827 	}
       
   828 
       
   829 TInt C3GPMuxer::CalculateNextFrameDurationPartial(TBool aFrameWritten)
       
   830 	{
       
   831 	if (!aFrameWritten)
       
   832 		{
       
   833 		return CalculateNextFrameDuration();
       
   834 		}
       
   835 
       
   836 	iVideoDuration += iDefaultVideoFrameDuration;
       
   837 	
       
   838 	return iDefaultVideoFrameDuration;
       
   839 	}
       
   840 
       
   841 void C3GPMuxer::WriteAudioBuffer()
       
   842 	{
       
   843 	DEBUG_PRINTF(_L8("C3GPMuxer::WriteAudioBuffer"));
       
   844 	
       
   845 	if (iAudioBuffer->nFilledLen != 0)
       
   846 		{
       
   847 		TPtr8 audioPtr(iAudioBuffer->pBuffer + iAudioBuffer->nOffset, 
       
   848 								iAudioBuffer->nFilledLen, iAudioBuffer->nAllocLen - iAudioBuffer->nOffset);
       
   849 
       
   850 
       
   851  		//Calculate the audio frame duration in timescale
       
   852 		//nTimeStamp is in microseconds
       
   853 		TUint durationInTimeSlice=KAudioTimeScale*iAudioBuffer->nTimeStamp/KMicroSecondsPerSecond;
       
   854  		TUint duration=durationInTimeSlice-iAudioFrameDuration;
       
   855  		iAudioFrameDuration=durationInTimeSlice;
       
   856  		HandleError(iComposer->WriteAudioFrames(audioPtr, duration));		
       
   857 
       
   858 
       
   859 	
       
   860 		}
       
   861 	ReturnBuffer(iAudioBuffer, COmxIL3GPMuxer::EPortIndexAudioInput);
       
   862 	}
       
   863 
       
   864 void C3GPMuxer::ReturnBuffer(OMX_BUFFERHEADERTYPE* aBuffer, TUint32 aPortIndex)
       
   865 	{
       
   866 	DEBUG_PRINTF3(_L8("C3GPMuxer::ReturnBuffer : aBuffer[%X]; aPortIndex[%u]"), aBuffer, aPortIndex);
       
   867 	OMX_U32 flags = aBuffer->nFlags;
       
   868 	aBuffer->nFilledLen = 0;
       
   869 	aBuffer->nFlags = 0;
       
   870 	aBuffer->nOffset = 0;
       
   871 	aBuffer->nTimeStamp = 0;
       
   872 	iCallbacks.BufferDoneNotification(aBuffer, aPortIndex, OMX_DirInput);
       
   873 
       
   874 	if(flags & OMX_BUFFERFLAG_EOS)
       
   875 		{
       
   876 		iCallbacks.EventNotification(OMX_EventBufferFlag, aPortIndex, flags, NULL);
       
   877 		}
       
   878 	}
       
   879 	
       
   880 void C3GPMuxer::DoCancel()
       
   881 	{
       
   882 	DEBUG_PRINTF(_L8("C3GPMuxer::DoCancel"));
       
   883 	if (iAudioPortEnabled)
       
   884 		{
       
   885 		iAudioQueue.CancelDataAvailable();
       
   886 		}
       
   887 	
       
   888 	if (iVideoPortEnabled)
       
   889 		{
       
   890 		iVideoQueue.CancelDataAvailable();		
       
   891 		}
       
   892 	}
       
   893 
       
   894 TBool C3GPMuxer::RemoveFromQueue(RMsgQueue<OMX_BUFFERHEADERTYPE*>& aQueue, OMX_BUFFERHEADERTYPE* aBufferHeader)
       
   895 	{
       
   896 	DEBUG_PRINTF3(_L8("C3GPMuxer::RemoveFromQueue : aQueue[%X]; aBufferHeader[%X]"), &aQueue, aBufferHeader);
       
   897 	TBool removed = EFalse;
       
   898 	OMX_BUFFERHEADERTYPE* bufferHeader = NULL;
       
   899 	while(aQueue.Receive(bufferHeader) != KErrUnderflow)
       
   900 		{
       
   901 		if(bufferHeader != aBufferHeader)
       
   902 			{
       
   903 			TInt error = iRemoveQueue.Send(bufferHeader);
       
   904 			__ASSERT_DEBUG(error == KErrNone, User::Panic(K3GPMuxerPanic, error));
       
   905 			}
       
   906 		else
       
   907 			{
       
   908 			removed = ETrue;
       
   909 			}
       
   910 		}
       
   911 	while(iRemoveQueue.Receive(bufferHeader) != KErrUnderflow)
       
   912 		{
       
   913 		TInt error = aQueue.Send(bufferHeader);
       
   914 		__ASSERT_DEBUG(error == KErrNone, User::Panic(K3GPMuxerPanic, error));
       
   915 		}
       
   916 	return removed;
       
   917 	}
       
   918 
       
   919 void C3GPMuxer::Pause()
       
   920 	{
       
   921 	DEBUG_PRINTF(_L8("C3GPMuxer::Pause"));
       
   922 	iPaused = ETrue;
       
   923 	}
       
   924 
       
   925 void C3GPMuxer::HandleError(TInt aError)
       
   926 	{
       
   927 	DEBUG_PRINTF2(_L8("C3GPMuxer::HandleError : aError[%d]"), aError);
       
   928 	OMX_ERRORTYPE omxError = SymbianErrorToOmx(aError);
       
   929 	if (omxError != OMX_ErrorNone)
       
   930 		{
       
   931 		iMuxerInvalid = ETrue;
       
   932 		iCallbacks.ErrorEventNotification(omxError);
       
   933 		}
       
   934 	}
       
   935 	
       
   936 TBool C3GPMuxer::IsMuxerInvalid() const
       
   937 	{
       
   938 	return iMuxerInvalid;				
       
   939 	}	
       
   940 
       
   941 OMX_ERRORTYPE C3GPMuxer::SymbianErrorToOmx(TInt aError)
       
   942 	{
       
   943 	switch(aError)
       
   944 		{
       
   945 	case KErrNone:
       
   946 		return OMX_ErrorNone;
       
   947 	case KErrNoMemory:
       
   948 		return OMX_ErrorInsufficientResources;
       
   949 	case KErrWrite:
       
   950 		return OMX_ErrorContentPipeOpenFailed;
       
   951 	default:
       
   952 		return OMX_ErrorUndefined;
       
   953 		}
       
   954 	}