mmplugins/lib3gpunittest/src/tsu_3gplibrary_parse_compose.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 02 Sep 2010 22:02:55 +0300
changeset 51 613e4e943120
parent 0 40261b775718
permissions -rw-r--r--
Revision: 201035 Kit: 201035

// 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:
//

#include "tsu_3gplibrary_parse_compose.h"

const TUint16 KAudioModeSet = 0x81ff;
const TReal KMillisecondsInSecond = 1000;
const TReal KOneHalf = 0.5;
const TInt KLargeFileWriteBufferSize = 16384;  // 16K
const TInt KLargeFileWriteBufferMaxCount = 15;

_LIT( KAvcBaseline, "Baseline" );
_LIT( KAvcMain, "Main" );
_LIT( KAvcExtended, "Extended" );
_LIT( KAvcHigh, "High" );

const static TDesC* KAvcProfileNames[] = {
        &KAvcBaseline,
        &KAvcMain,
        &KAvcExtended,
        &KAvcHigh
    };


C3GPLibParseComposeFile::C3GPLibParseComposeFile() :
	iCompareOriginal(EFalse),
	iLargeFile(EFalse)	
	{
	}

TVerdict C3GPLibParseComposeFile::doTestStepPreambleL()
	{
	#ifdef __WINSCW__
	_LIT(KInputDir, "inputDirEmul");
	_LIT(KOutputDir, "outputDirEmul");
	#else
	_LIT(KInputDir, "inputDirHw");
	_LIT(KOutputDir, "outputDirHw");
	#endif
	// ensure test always starts with clean results
	SetTestStepResult(EPass);
	SetTestStepError(KErrNone);
	
	// ensure there's always a Active Scheduler for the Composer
	if (!CActiveScheduler::Current())
		{
		iScheduler = new (ELeave) CActiveScheduler;
		CActiveScheduler::Install(iScheduler);	
		}
	
	User::LeaveIfError(iFs.Connect());
	// retrieve the input files' directory 
	TPtrC inputFileDir;
	TBool composeFile = EFalse;
	TPtrC outputFileDir;					
	if (GetStringFromConfig(ConfigSection(), KInputDir, inputFileDir))
		{
		if (inputFileDir.Length() == 0)
			{
			ERR_PRINTF1(_L("Ensure input directory is specified."));
			SetTestStepResult(EInconclusive);			
			return TestStepResult();
			}
		else 
			{
			iInputDir.CreateL(inputFileDir);
			}
		
		GetIntFromConfig(ConfigSection(), _L("expectedFailure"), iExpectedNumberOfFailure);
		
		if (GetBoolFromConfig(ConfigSection(), _L("composeFile"), composeFile))
			{
			if (composeFile)
				{
				if (GetStringFromConfig(ConfigSection(), KOutputDir, outputFileDir))
					{
					if (outputFileDir.Length() == 0)
						{
						ERR_PRINTF1(_L("Ensure output directory is specified."));
						SetTestStepResult(EInconclusive);			
						return TestStepResult();
						}
					else if (outputFileDir == inputFileDir)
						{
						ERR_PRINTF1(_L("Ensure output directory is not the same as input directory."));
						SetTestStepResult(EInconclusive);			
						return TestStepResult();						
						}
					else if (!GetIntFromConfig(ConfigSection(), _L("fileFormat"), iFileFormat))
						{
						ERR_PRINTF1(_L("Ensure a output file format is specified."));
						SetTestStepResult(EInconclusive);			
						return TestStepResult();												
						}
					
					GetIntFromConfig(ConfigSection(), _L("composeFlag"), iComposeFlag);
					GetBoolFromConfig(ConfigSection(), _L("compareOriginal"), iCompareOriginal);
					GetBoolFromConfig(ConfigSection(), _L("largeFile"), iLargeFile);					
					}
				else
					{
					ERR_PRINTF1(_L("Ensure output directory is specified."));
					SetTestStepResult(EInconclusive);
					}
				}
			}
		}
	else
		{
		ERR_PRINTF1(_L("Ensure input directory is specified."));
		SetTestStepResult(EInconclusive);
		}
	
	if (TestStepResult() == EPass)
		{
		// Create a list of all files contained in the input directory
		User::LeaveIfError(iFs.GetDir(inputFileDir, KEntryAttNormal, ESortNone, iDirList));			
		
		if (!iDirList || iDirList->Count() == 0)
			{
			ERR_PRINTF1(_L("Input directory is empty."));
			SetTestStepResult(EInconclusive);		
			}
		}
	
	if (TestStepResult() == EPass)
		{
		iParser = C3GPParse::NewL();
		if (composeFile)
			{
			if (iLargeFile)
				{
				iComposer = C3GPCompose::NewL(KLargeFileWriteBufferSize, KLargeFileWriteBufferMaxCount);				
				}
			else
				{
				iComposer = C3GPCompose::NewL();				
				}

			iOutputDir.CreateL(outputFileDir);
			TInt err = iFs.MkDirAll(iOutputDir);
			if (err != KErrNone && err != KErrAlreadyExists)
				{
				User::Leave(err);
				}
			}
		}
	
	return TestStepResult();
	}		

TVerdict C3GPLibParseComposeFile::doTestStepL()
	{
	if( TestStepResult() == EPass)
		{
		TInt failCount = 0;
		TInt fileCount = 0;
		RBuf filePath;
		CleanupClosePushL(filePath);
		filePath.CreateL(KMaxFileName);
		
		for (TInt i = 0; i < iDirList->Count(); i++)
			{
			const TEntry& entry = (*iDirList)[i];
			if (!entry.IsDir())
				{
				fileCount++;
				
				filePath.Copy(iInputDir);
				filePath.Append(entry.iName);
				
				INFO_PRINTF2(_L("ParseAndComposeL: file=%S"), &filePath);				
				TRAPD(err, ParseFileL(filePath));
				INFO_PRINTF2(_L("ParseAndComposeL returns: err = %d"), err);
				if( err != KErrNone )
					{
					iParser->Complete();
					if (iComposer)
						{
						iComposer->Complete();
						}
					failCount++;
					}
				else
					{
					if (iCompareOriginal && iComposer)
						{
						if (!CompareInputOuputFileL(filePath))
							{
							ERR_PRINTF1(_L("Input & output file is not the same"));
							failCount ++;
							}
						else
							{
							INFO_PRINTF1(_L("Input & output file is the same."));
							}
						}
					}				
				}
			}
		
		// clean up
		CleanupStack::PopAndDestroy(&filePath);
		
		INFO_PRINTF3(_L("%d of %d files failed"), failCount, fileCount);
		if (failCount != iExpectedNumberOfFailure)
			{
			ERR_PRINTF2(_L("Expected failure: %d - Test Failed"), iExpectedNumberOfFailure);
			SetTestStepResult(EFail);
			}		
		}
	
	return TestStepResult();
	}

TVerdict C3GPLibParseComposeFile::doTestStepPostambleL()
	{
	if( iScheduler )
		{
		CActiveScheduler::Install(NULL);
		delete iScheduler;
		}
	
	iOutputDir.Close();
	iInputDir.Close();
	iComposedFile.Close();
	
	delete iDirList;
	
	iParsedFileHandle64.Close();
	iComposedFileHandle64.Close();
	iFs.Close();
			
	delete iParser;	
	if (iComposer)
		{
		delete iComposer;
		}	
	
	return TestStepResult();
	}

void C3GPLibParseComposeFile::ParseFileL(const TDesC& aInputFile)
	{
	ASSERT(iParser);
	TInt err = 0;
	
	if (iLargeFile)
		{
		iParsedFileHandle64.Close();
		err = iParsedFileHandle64.Open(iFs, aInputFile, EFileShareReadersOrWriters);
		if (err == KErrNone)
			{
			err = iParser->Open(iParsedFileHandle64);		
			}
		}
	else
		{
		err = iParser->Open(aInputFile);		
		}
	
	if (err != KErrNone)
		{
		ERR_PRINTF2(_L("C3GPParse::Open() returns %d"), err);
		User::Leave(err);
		}
	
	//
	// Retrieve Video Properties
	//	
	T3GPVideoPropertiesBase* videoProperties = NULL;
	CleanupStack::PushL(videoProperties);
	
	RBuf8 videoDecoderSpecificInfo;
	CleanupClosePushL(videoDecoderSpecificInfo);
	
	TUint videoLengthInMs = 0;
		
	err = ParseVideoProperties(*iParser, videoDecoderSpecificInfo, videoProperties, videoLengthInMs);
	if (err != KErrNone && err != KErrNotSupported)
		{
		ERR_PRINTF2(_L("ParseVideoProperties failed: err = %d"), err);
		User::Leave(err);
		}
	
	//
	// Retrieve Audio Properties
	//
	T3GPAudioPropertiesBase* audioProperties = NULL;
	CleanupStack::PushL(audioProperties);	

	RBuf8 audioDecoderSpecificInfo;
	CleanupClosePushL(audioDecoderSpecificInfo);
	
	TUint audioLengthInMs = 0;
	err = ParseAudioProperties(*iParser, audioDecoderSpecificInfo, audioProperties, audioLengthInMs);
	if( err != KErrNone && err != KErrNotSupported)
		{
		ERR_PRINTF2(_L("ParseAudioProperties failed: err = %d"), err);		
		User::Leave(err);
		}

	if (!videoProperties && !audioProperties)
		{
		ERR_PRINTF1(_L("File contains neither video nor audio data"));
		User::Leave(KErrNotFound);		
		}
	
	//
	// Prepare for the Composer if file output is expected 
	//	
	if (iComposer)
		{
		RBuf outputFile;
		CleanupClosePushL(outputFile);
		
		outputFile.CreateL(KMaxFileName);
		outputFile.Copy(iOutputDir);
		
		TParsePtrC parsePtr(aInputFile);
		TPtrC name = parsePtr.NameAndExt();
				
		outputFile.Append(parsePtr.NameAndExt());

		if (iLargeFile)
			{
			iComposedFileHandle64.Close();
			err = iComposedFileHandle64.Create(iFs, outputFile, EFileShareAny|EFileStream|EFileWrite);
			if (err == KErrAlreadyExists)
				{
				err = iComposedFileHandle64.Replace(iFs, outputFile, EFileShareAny|EFileStream|EFileWrite);
				}
			
			if (err == KErrNone)
				{
				err = iComposer->Open((T3GPFileFormatType)iFileFormat, videoProperties, audioProperties, iComposedFileHandle64, iComposeFlag);
				}
			}
		else
			{			
			err = iComposer->Open((T3GPFileFormatType)iFileFormat, videoProperties, audioProperties, outputFile, iComposeFlag);
			}
				
		if (err != KErrNone)
			{
			ERR_PRINTF2(_L("C3GPCompose::Open failed: err = %d"), err);		
			User::Leave(err);			
			}
		
		CleanupStack::PopAndDestroy(&outputFile);
		}
	
	//
	// Parse and compose (if specified) video / audio frame data 
	//
	ReadWriteAudioVideoFramesL(videoProperties, audioProperties, videoLengthInMs, audioLengthInMs);
	
	//
	// Clean up
	//
	CleanupStack::PopAndDestroy(4);	 
									// audioDecoderSpecificInfo
									// audioProperties 
									// videoDecoderSpecificInfo 
									// videoProperties
	
	if (iComposer)
		{
		err = iComposer->Complete();
		if (err != KErrNone)
			{
			ERR_PRINTF1(_L("aComposer->Complete() failed"));
			User::Leave(err);
			}
			
		if (iLargeFile)
			{
			iComposedFileHandle64.Close();
			}
		}
	
	err = iParser->Complete();
	if (err != KErrNone)
		{
		ERR_PRINTF1(_L("aParser->Complete() failed"));
		User::Leave(err);
		}

	if (iLargeFile)
		{
		iParsedFileHandle64.Close();
		}
	
	INFO_PRINTF1(_L("C3GPLibParseComposeFile::ParseAndComposeL OUT"));		
	}

void C3GPLibParseComposeFile::ReadWriteAudioVideoFramesL(const T3GPVideoPropertiesBase* aVideoProperties, 
														 const T3GPAudioPropertiesBase* aAudioProperties,
														 TUint aVideoLengthInMs,
														 TUint aAudioLengthInMs)
	{
	ASSERT(iParser);

	TUint videoLengthInTS = 0;
	TUint audioLengthInTS = 0;
	if (aVideoProperties)
		{
        // The "+KOneHalf" in the following calculation has the effect of rounding
        // to the nearest integer instead of just flooring it when converting from
        // TReal to TUint.
        // This is to avoid videoLengthInTS being off by one and leading to false
        // positives when the video properties are compared later on.
		videoLengthInTS = (TUint) ((TReal)(aVideoLengthInMs * aVideoProperties->iTimescale) / KMillisecondsInSecond + KOneHalf );
		}
	if (aAudioProperties)
		{
		// The "+KOneHalf" in the following calculation has the effect of rounding
		// to the nearest integer instead of just flooring it when converting from
		// TReal to TUint.
		// This is to avoid audioLengthInTS being off by one and leading to false
		// positives when the audio properties are compared later on.
		audioLengthInTS = (TUint) ((TReal)(aAudioLengthInMs * aAudioProperties->iTimescale) / KMillisecondsInSecond + KOneHalf );
		}
	
	TInt framePerSample1 = 0;
	TInt framePerSample2 = 0;
	TUint audioTS1 = 0;
	TUint audioTS2 = 0;
	RBuf8 audioBuf1;	
	RBuf8 audioBuf2;
	CleanupClosePushL(audioBuf1);
	CleanupClosePushL(audioBuf2);

	TUint videoTS1;
	TUint videoTS2;
	TBool keyFrame1;
	TBool keyFrame2;
	T3GPFrameDependencies dep1;
	T3GPFrameDependencies dep2;
	RBuf8 videoBuf1;
	RBuf8 videoBuf2;
	CleanupClosePushL(videoBuf1);		
	CleanupClosePushL(videoBuf2);	

	TInt err = KErrNone;
	T3GPFrameType frameType;
	
	RArray<T3GPFrameType> frameTypeArray(10);
	CleanupClosePushL(frameTypeArray);
	
	TInt videoFrameCount = 0;
	TInt audioFrameCount = 0;
	
	while (err == KErrNone)
		{
		err = iParser->GetFrameType(frameType);
		if (err == KErrNone)
			{		
			switch(frameType)
				{
				case E3GPAudio:
					audioFrameCount++;
					frameTypeArray.AppendL(frameType);		
					err = ReadAudioFrames(*iParser, audioBuf1, framePerSample1, audioTS1);
					audioBuf1.Close();				
					break;
					
				case E3GPVideo:
					videoFrameCount++;
					frameTypeArray.AppendL(frameType);
					err = ReadVideoFrame(*iParser, videoBuf1, keyFrame1, videoTS1, dep1);
					videoBuf1.Close();				
					break;
					
				default:
					ERR_PRINTF1(_L("GetFrameType retrieves an unsupported frame type"));
					User::Leave(KErrUnknown);
					break;
				}
			
			// if there's anything wrong w/ retrieving the frame, exit the function now
			User::LeaveIfError(err);
			}
		}
	
	INFO_PRINTF2(_L("ReadWriteAudioVideoFramesL read %d video frames."), videoFrameCount);
	INFO_PRINTF2(_L("ReadWriteAudioVideoFramesL read %d audio frames."), audioFrameCount);
		
	// reset the video and audio cursor at the beginning of the clip 
	TUint videoPos = 0;
	TUint audioPos = 0;
	iParser->Seek(0, EFalse, videoPos, audioPos);
	
	videoFrameCount = 0;
	audioFrameCount = 0;
	
	if (iComposer)
		{	
		// if the data should be supplied into a composer to construct an output file
		TInt index = 0;	
		while (index < frameTypeArray.Count())
			{
			frameType = frameTypeArray[index++]; 
			switch (frameType)
				{
				case E3GPAudio:
					if (audioBuf1.Length() == 0)
						{					
						User::LeaveIfError(ReadAudioFrames(*iParser, audioBuf1, framePerSample1, audioTS1));
						}
					err = ReadAudioFrames(*iParser, audioBuf2, framePerSample2, audioTS2);
					
					if (audioBuf1.Length() > 0 && audioBuf2.Length() > 0 || 
						audioBuf1.Length() > 0 && err == KErrNotFound)
						{
						TUint duration = 0;
						if (audioBuf2.Length() == 0)
							{
							// duration for the last batch of audio frames = total audio length - the 2nd last audio frame
							duration = audioLengthInTS - audioTS1;
							}
						else
							{
							duration = audioTS2 - audioTS1;			
							}	
							
						User::LeaveIfError(iComposer->WriteAudioFrames(audioBuf1, duration));
						audioFrameCount++;
						
						audioBuf1.Close();
						audioBuf1.Swap(audioBuf2);
						framePerSample1 = framePerSample2;
						audioTS1 = audioTS2;
						}
					break;
					
				case E3GPVideo:
					if (videoBuf1.Length() == 0)
						{
						User::LeaveIfError(ReadVideoFrame(*iParser, videoBuf1, keyFrame1, videoTS1, dep1));
						}					
					err = ReadVideoFrame(*iParser, videoBuf2, keyFrame2, videoTS2, dep2);
					
					if (videoBuf1.Length() > 0 && videoBuf2.Length() > 0 || 
						videoBuf1.Length() > 0 && err == KErrNotFound)
						{
						TUint duration = 0;
						if (videoBuf2.Length() == 0)
							{
							// duration for the last batch of audio frames = total audio length - the 2nd last audio frame
							duration = videoLengthInTS - videoTS1;
							}
						else
							{
							duration = videoTS2 - videoTS1;			
							}	
							
						User::LeaveIfError(iComposer->WriteVideoFrame(videoBuf1, duration, keyFrame1, dep1));
						videoFrameCount++;
						
						videoBuf1.Close();
						videoBuf1.Swap(videoBuf2);
						keyFrame1 = keyFrame2;
						videoTS1 = videoTS2;
						dep1.iDependsOn = dep2.iDependsOn;
						dep1.iIsDependedOn = dep2.iIsDependedOn;
						dep1.iHasRedundancy = dep2.iHasRedundancy;		
						}
					break;
					
				default:
					ERR_PRINTF1(_L("Unknown frame type detected."));
					User::Leave(KErrUnknown);
					break;
				}
			}	// end of while loop
		}	// if (iComposer)
	
	CleanupStack::PopAndDestroy(5);
	
	INFO_PRINTF2(_L("ReadWriteAudioVideoFramesL wrote %d video frames."), videoFrameCount);
	INFO_PRINTF2(_L("ReadWriteAudioVideoFramesL wrote %d audio frames."), audioFrameCount);	
	}

TInt C3GPLibParseComposeFile::GetAudioDecoderSpecificInfo(RBuf8& aBuffer)
	{
	ASSERT(iParser);
	
	TInt size;
	TInt err = iParser->GetAudioDecoderSpecificInfoSize(size);
	if( err != KErrNone )
		{
		ERR_PRINTF2(_L("GetAudioDecoderSpecificInfoSize() failed: %d"), err);
		return err;
		}

	err = aBuffer.Create(size);
	if( err != KErrNone )
		{
		ERR_PRINTF1(_L("Create buffer failed"));
		return err;
		}

	err = iParser->GetAudioDecoderSpecificInfo(aBuffer);
	if( err != KErrNone )
		{
		ERR_PRINTF2(_L("GetAudioDecoderSpecificInfo() failed: %d"), err);
		}
	
	return err;
	}

TInt C3GPLibParseComposeFile::ParseVideoProperties(C3GPParse& aParser,
												   RBuf8& aDecoderSpecificInfo,
												   T3GPVideoPropertiesBase*& aProperties,
												   TUint& aLengthInMs) 
	{
	ASSERT(iParser);
	
	T3GPVideoType type; 
	TReal frameRate;
	TUint avgBitRate;
	TSize size;
	TUint timeScale;	
	
	TInt err = aParser.GetVideoProperties(type, aLengthInMs, frameRate, avgBitRate, size, timeScale);
	if ((err != KErrNone) && (err != KErrNotSupported))
		{
		ERR_PRINTF2(_L("aParser->GetVideoProperties failed: %d"), err);
		return err;
		}

	switch(type)
		{
		case E3GPMpeg4Video:
			{
			INFO_PRINTF1(_L("Video Type: Mpeg4"));			
			err = GetVideoDecoderSpecificInfo(aDecoderSpecificInfo);		
			if (err == KErrNone)
				{
				aProperties = new T3GPVideoPropertiesMpeg4Video(timeScale, size,
						64000, avgBitRate, aDecoderSpecificInfo);
				if (!aProperties)
					{
					ERR_PRINTF1(_L("T3GPVideoPropertiesMpeg4Video allocation failed"));
					err = KErrNoMemory;
					}
				}
			break;			
			}
		
		case E3GPAvcProfileBaseline:
		case E3GPAvcProfileMain:
		case E3GPAvcProfileExtended:
		case E3GPAvcProfileHigh:
			{
			err = GetVideoDecoderSpecificInfo(aDecoderSpecificInfo);
			if (err == KErrNone)
				{
				INFO_PRINTF2(_L("Video Type: Avc Profile %S"), KAvcProfileNames[type-E3GPAvcProfileBaseline] );
				aProperties = new T3GPVideoPropertiesAvc(timeScale, size, aDecoderSpecificInfo);
				if (!aProperties)
					{
					ERR_PRINTF1(_L("T3GPVideoPropertiesAvc allocation failed"));
					err = KErrNoMemory;
					}
                else
                    {
                    // T3GPVideoPropertiesAvc defaults the video type to AVC baseline profile.
                    // Need to override that here because we want to check for the specific
                    // profile in this test.
                    aProperties->iType = type;
                    }
				}
			break;
			}
			
		case E3GPH263Profile0:
		case E3GPH263Profile3:
			{
			INFO_PRINTF1(_L("Video Type: H263"));			
			T3GPVideoPropertiesH263::TProfile profile = T3GPVideoPropertiesH263::EProfile0;
			if (type == E3GPH263Profile3)
				{
				profile = T3GPVideoPropertiesH263::EProfile3;
				}
			
			TInt videoLevel;
			err = iParser->GetH263VideoLevel(videoLevel);
			if( err != KErrNone )
				{
				ERR_PRINTF1(_L("aParser->GetH263VideoLevel() failed"));
				}
			else
				{			
				aProperties = new T3GPVideoPropertiesH263(timeScale, size, videoLevel, profile);
				if( !aProperties )
					{
					ERR_PRINTF1(_L("T3GPVideoPropertiesH263 allocation failed"));
					err = KErrNoMemory;
					}
				}
			break;			
			}
		
		case E3GPNoVideo:
			INFO_PRINTF1(_L("Video Type: None"));
			break;
			
		default:
			err = KErrNotSupported;
			break;
		}
	
	return err;
	}

TInt C3GPLibParseComposeFile::GetVideoDecoderSpecificInfo(RBuf8& aBuffer)
	{	
	TInt size;
	TInt err = iParser->GetVideoDecoderSpecificInfoSize(size);
	if (err != KErrNone)
		{
		ERR_PRINTF1(_L("GetVideoDecoderSpecificInfoSize() failed"));
		return err;
		}
	
	err = aBuffer.Create(size);
	if (err != KErrNone)
		{
		ERR_PRINTF1(_L("Create buffer failed"));
		return err;
		}

	err = iParser->GetVideoDecoderSpecificInfo(aBuffer);
	if( err != KErrNone )
		{
		ERR_PRINTF1(_L("GetVideoDecoderSpecificInfo() failed"));
		}
	
	return err;
	}

TInt C3GPLibParseComposeFile::ParseAudioProperties(C3GPParse& aParser, 
												   RBuf8& aAudioDecoderSpecificInfo,
												   T3GPAudioPropertiesBase*& aAudioProperties,
												   TUint& aLength)
	{
	
	ASSERT(iParser);
	
	T3GPAudioType type; 
	TInt audioFPS; 
	TUint audioAvgBitRate; 
	TUint timeScale;	
	
	TInt err = aParser.GetAudioProperties(type, aLength, audioFPS, audioAvgBitRate, timeScale);
	if(( err != KErrNone ) && ( err != KErrNotSupported ))
		{
		ERR_PRINTF2(_L("GetAudioProperties() failed: %d"), err);
		return err;
		}

	switch(type)
		{
		case E3GPMpeg4Audio:
			{
			INFO_PRINTF1(_L("Audio Type: Mpeg4"));
			err = GetAudioDecoderSpecificInfo(aAudioDecoderSpecificInfo);
			if (err == KErrNone)
				{
				aAudioProperties = new T3GPAudioPropertiesMpeg4Audio(timeScale, aAudioDecoderSpecificInfo);
				if( !aAudioProperties )
					{
					ERR_PRINTF1(_L("T3GPAudioPropertiesMpeg4Audio allocation failed"));
					err = KErrNoMemory;
					}
				}
			break;
			}
			
		case E3GPQcelp13K:
			{
			INFO_PRINTF1(_L("Audio Type: Qcelp13K"));
			T3GPQcelpStorageMode mode;
			err = iParser->GetQcelpStorageMode(mode);
			if (err != KErrNone)
				{
				ERR_PRINTF1(_L("GetQcelpStorageMode failed"));
				}
			else 
				{
				if( mode == E3GPMP4AudioDescriptionBox)
					{
					err = GetAudioDecoderSpecificInfo(aAudioDecoderSpecificInfo);
					aAudioProperties = new T3GPAudioPropertiesQcelp(timeScale, audioFPS, aAudioDecoderSpecificInfo);
					}
				else
					{
					aAudioProperties = new T3GPAudioPropertiesQcelp(timeScale, audioFPS);
					}
				
				if( !aAudioProperties )
					{
					ERR_PRINTF1(_L("T3GPAudioPropertiesQcelp allocation failed"));
					err = KErrNoMemory;
					}
				}
			break;
			}
			
		case E3GPAmrNB:
			{
			INFO_PRINTF1(_L("Audio Type: AMR NB"));
			aAudioProperties = new T3GPAudioPropertiesAmr(timeScale, audioFPS, KAudioModeSet, T3GPAudioPropertiesAmr::EAmrNB);
			if( !aAudioProperties )
				{
				ERR_PRINTF1(_L("T3GPAudioPropertiesAmr allocation failed"));
				err = KErrNoMemory;
				}
			break;
			}
			
		case E3GPAmrWB:
			{
			INFO_PRINTF1(_L("Audio Type: AMR WB"));
			aAudioProperties = new T3GPAudioPropertiesAmr(timeScale, audioFPS, KAudioModeSet, T3GPAudioPropertiesAmr::EAmrWB);
			if( !aAudioProperties )
				{
				ERR_PRINTF1(_L("T3GPAudioPropertiesAmr allocation failed"));
				err = KErrNoMemory;
				}
			break;
			}
			
		case E3GPNoAudio:
			INFO_PRINTF1(_L("Audio Type: None"));			
			break;
			
		default:
			INFO_PRINTF1(_L("Audio Type: Unrecognized!"));
			err = KErrNotSupported;
			break;
		}
	
	return err;
	}

TInt C3GPLibParseComposeFile::ReadVideoFrame(C3GPParse& aParser,
											 RBuf8& aVideoBuffer, 
											 TBool& aVideoKeyFrame, 
											 TUint& aVideoTimestampInTS, 
											 T3GPFrameDependencies& aDependencies)
	{
	ASSERT(iParser);
	
	// flush all data in the buffer
	aVideoBuffer.Close();
	
	TInt err = aParser.GetVideoFrameDependencies(aDependencies);
	if (err != KErrNone)
		{
		aDependencies.iDependsOn = E3GPDependencyUnknown;
		aDependencies.iIsDependedOn = E3GPDependencyUnknown;
		aDependencies.iHasRedundancy = E3GPRedundancyUnknown;
		}
	
	TUint frameSize = 0;
	err = aParser.GetVideoFrameSize(frameSize);
	if (err == KErrNone)
		{
		if (frameSize > KMaxTInt / 2)
			{
			ERR_PRINTF1(_L("Video Frame too large!"));
			err = KErrOverflow;
			}
		else
			{
			err = aVideoBuffer.Create((TInt)frameSize);
			if (err == KErrNone)
				{
				TUint videoTimestampMS = 0;
				err = aParser.ReadVideoFrame(aVideoBuffer, aVideoKeyFrame, videoTimestampMS, aVideoTimestampInTS);				
				if (err == KErrNone)
					{
					if (err == KErrNotSupported)
						{
						// Not supported error is OK
						err = KErrNone;
						}
					else if (err != KErrNone)
						{
						ERR_PRINTF2(_L("GetVideoFrameDependencies failed with err = %d"), err);					
						}
					}
				else
					{
					ERR_PRINTF2(_L("ReadVideoFrame failed with err = %d"), err);					
					}
				}
			}
		}
	else
		{
		ERR_PRINTF2(_L("GetVideoFrameSize fails with %d"), err);		
		}
	
	return err;	
	}

TInt C3GPLibParseComposeFile::ReadAudioFrames(C3GPParse& aParser, 
											  RBuf8& aBuffer, 
											  TInt& aFramesInSample, 
											  TUint& aTimestampInTS)
	{
	// flush all existing data
	aBuffer.Close();
	
	TUint size = 0;
	TInt err = aParser.GetAudioFramesSize(size);
	if (err == KErrNone)
		{
		if (size > KMaxTInt / 2)	
			{
			// cannot create RBuf of size > KMaxTInt / 2 
			ERR_PRINTF1(_L("Audio Frames too large!"));
			err = KErrOverflow;
			}
		else
			{
			err = aBuffer.Create((TInt)size);
			if (err == KErrNone)
				{
				TUint audioTimestampMS = 0;
				err = aParser.ReadAudioFrames(aBuffer, aFramesInSample, audioTimestampMS, aTimestampInTS);
				if (err != KErrNone)
					{
					ERR_PRINTF2(_L("ReadAudioFrames failed with err = %d"), err);							
					}
				}
			}
		}
	else
		{
		ERR_PRINTF2(_L("GetAudioFramesSize failed with err = %d"), err);		
		}
	
	return err;
	}

TBool C3GPLibParseComposeFile::CompareInputOuputFileL(const TDesC& aInputFile)
	{
	ASSERT(iParser);
	TInt err = 0;

	if (iLargeFile)
		{
		iParsedFileHandle64.Close();
		err = iParsedFileHandle64.Open(iFs, aInputFile, EFileShareReadersOrWriters);
		if (err == KErrNone)
			{
			err = iParser->Open(iParsedFileHandle64);		
			}
		}
	else
		{
		err = iParser->Open(aInputFile);		
		}

	if (err != KErrNone)
		{
		ERR_PRINTF2(_L("C3GPParse 1 Open() returns %d"), err);
		User::Leave(err);
		}
	
	RBuf outputFile;
	CleanupClosePushL(outputFile);
	
	outputFile.CreateL(KMaxFileName);
	outputFile.Copy(iOutputDir);
	
	TParsePtrC parsePtr(aInputFile);
	TPtrC name = parsePtr.NameAndExt();	
	outputFile.Append(parsePtr.NameAndExt());

	C3GPParse* parser2 = C3GPParse::NewL();
	CleanupStack::PushL(parser2);

	RFile64 file2;
	if (iLargeFile)
		{
		err = file2.Open(iFs, outputFile, EFileShareReadersOrWriters);
		if (err == KErrNone)
			{
			err = parser2->Open(file2);		
			}
		}
	else
		{
		err = parser2->Open(outputFile);		
		}
		
	if (err != KErrNone)
		{
		ERR_PRINTF2(_L("C3GPParse 2 Open() returns %d"), err);
		User::Leave(err);
		}
	
	TBool result = EFalse;	
	if (CompareVideoPropertiesL(*iParser, *parser2))
		{
		if (CompareAudioPropertiesL(*iParser, *parser2))
			{
			if (CompareAudioVideoData(*iParser, *parser2))
				{
				result = ETrue;
				}			
			else
				{
				ERR_PRINTF1(_L("Audio/Video Data not matching"));
				}					
			}
		else
			{
			ERR_PRINTF1(_L("Audio Properites not matching"));
			}		
		}
	else
		{
		ERR_PRINTF1(_L("Video Properites not matching"));
		}
	
	parser2->Complete();
	iParser->Complete();
	
	if (iLargeFile)
		{
		file2.Close();
		iParsedFileHandle64.Close();
		}
	
	CleanupStack::PopAndDestroy(2);		// outputFile, parser2
	
	return result;
	}

TBool C3GPLibParseComposeFile::CompareVideoPropertiesL(C3GPParse& aParser1, 
													   C3GPParse& aParser2)
	{
	//
	// Retrieve Video Properties from Parser 1
	//	
	T3GPVideoPropertiesBase* videoProperties1 = NULL;
	CleanupStack::PushL(videoProperties1);
	
	RBuf8 videoDecoderSpecificInfo1;
	CleanupClosePushL(videoDecoderSpecificInfo1);
	
	TUint videoLengthInMs1 = 0;
		
	TInt err1 = ParseVideoProperties(aParser1, videoDecoderSpecificInfo1, videoProperties1, videoLengthInMs1);
	if (err1 != KErrNone && err1 != KErrNotSupported)
		{
		ERR_PRINTF2(_L("ParseVideoProperties 1 failed: err = %d"), err1);
		User::Leave(err1);
		}
	
	//
	// Retrieve Video Properties from Parser 2
	//	
	T3GPVideoPropertiesBase* videoProperties2 = NULL;
	CleanupStack::PushL(videoProperties2);
	
	RBuf8 videoDecoderSpecificInfo2;
	CleanupClosePushL(videoDecoderSpecificInfo2);
	
	TUint videoLengthInMs2 = 0;
		
	TInt err2 = ParseVideoProperties(aParser2, videoDecoderSpecificInfo2, videoProperties2, videoLengthInMs2);
	if (err2 != KErrNone && err2 != KErrNotSupported)
		{
		ERR_PRINTF2(_L("ParseVideoProperties 2 failed: err = %d"), err2);
		User::Leave(err2);
		}

	TBool result = EFalse;
	if (err1 == err2 && err1 != KErrNotSupported)
		{
		if (videoLengthInMs1 == videoLengthInMs2)
			{
			if (videoProperties1 && videoProperties2)
				{
				result = (videoProperties1->iType == videoProperties2->iType) &&
						 (videoProperties1->iSize == videoProperties2->iSize) &&
						 (videoProperties1->iTimescale == videoProperties2->iTimescale);
					
				if (result)
					{
					switch(videoProperties1->iType)
						{
						case E3GPMpeg4Video:
							{
							T3GPVideoPropertiesMpeg4Video* video1 = (T3GPVideoPropertiesMpeg4Video*)videoProperties1;
							T3GPVideoPropertiesMpeg4Video* video2 = (T3GPVideoPropertiesMpeg4Video*)videoProperties2;
							
							// NOTE: Comparison of the avg bit rate is omitted.  The reason is that the avg. bit 
							//       rate is calculated by: 
							//       video avg bit rate = stream avg bit rate - audio avg bit rate
							//       However, stream avg. bit rate is not be properly set by the 3GP composer
							//       as it is not very important.  Thus leading to the video avg. bit rate  
							//       retrieved from the output file to NOT match the original.
							result = ((video1->iMaxBitRate == video2->iMaxBitRate) && 
									 //(video1->iAvgBitRate == video2->iAvgBitRate) &&
									 (video1->iDecoderSpecificInfo == video2->iDecoderSpecificInfo)); 
							break;			
							}
								
						case E3GPAvcProfileBaseline:
						case E3GPAvcProfileMain:
						case E3GPAvcProfileExtended:
						case E3GPAvcProfileHigh:
							{
							T3GPVideoPropertiesAvc* video1 = (T3GPVideoPropertiesAvc*)videoProperties1;
							T3GPVideoPropertiesAvc* video2 = (T3GPVideoPropertiesAvc*)videoProperties2;
							
							result = (video1->iDecoderSpecificInfo == video2->iDecoderSpecificInfo); 
							break;
							}
									
						case E3GPH263Profile0:
						case E3GPH263Profile3:
							{
							T3GPVideoPropertiesH263* video1 = (T3GPVideoPropertiesH263*)videoProperties1;
							T3GPVideoPropertiesH263* video2 = (T3GPVideoPropertiesH263*)videoProperties2;

							result = (video1->iVideoLevel == video2->iVideoLevel);
							break;			
							}
						}
					}
				}			
			}		
		}	
	else if (err1 == KErrNotSupported && err2 == err1)
		{
		result = ETrue;
		}

	CleanupStack::PopAndDestroy(4);		// videoProperties1, 
										// videoProperties2, 
										// videoDecoderSpecificInfo1, 
										// videoDecoderSpecificInfo2
	
	return result;
	}

TBool C3GPLibParseComposeFile::CompareAudioPropertiesL(C3GPParse& aParser1, 
													   C3GPParse& aParser2)
	{
	//
	// Retrieve Audio Properties 1
	//
	T3GPAudioPropertiesBase* audioProperties1 = NULL;
	CleanupStack::PushL(audioProperties1);	

	RBuf8 audioDecoderSpecificInfo1;
	CleanupClosePushL(audioDecoderSpecificInfo1);
	
	TUint audioLengthInMs1 = 0;
	TInt err1 = ParseAudioProperties(aParser1, audioDecoderSpecificInfo1, audioProperties1, audioLengthInMs1);
	if( err1 != KErrNone && err1 != KErrNotSupported)
		{
		ERR_PRINTF2(_L("ParseAudioProperties failed: err = %d"), err1);		
		User::Leave(err1);
		}

	//
	// Retrieve Audio Properties 2
	//
	T3GPAudioPropertiesBase* audioProperties2 = NULL;
	CleanupStack::PushL(audioProperties2);	

	RBuf8 audioDecoderSpecificInfo2;
	CleanupClosePushL(audioDecoderSpecificInfo2);
	
	TUint audioLengthInMs2 = 0;
	TInt err2 = ParseAudioProperties(aParser2, audioDecoderSpecificInfo2, audioProperties2, audioLengthInMs2);
	if( err2 != KErrNone && err2 != KErrNotSupported)
		{
		ERR_PRINTF2(_L("ParseAudioProperties failed: err = %d"), err2);		
		User::Leave(err2);
		}
	
	TBool result = EFalse;
	if (err1 == err2 && err1 != KErrNotSupported)
		{
		if (audioLengthInMs2 == audioLengthInMs1)
			{
			if (audioProperties1 && audioProperties2)
				{
				result = (audioProperties1->iType == audioProperties2->iType) &&
						 (audioProperties1->iFramesPerSample == audioProperties2->iFramesPerSample) &&
						 (audioProperties1->iTimescale == audioProperties2->iTimescale);
				
				if (result)
					{
					switch(audioProperties1->iType)
						{
						case E3GPMpeg4Audio:
							{
							T3GPAudioPropertiesMpeg4Audio* audio1 = (T3GPAudioPropertiesMpeg4Audio*) audioProperties1;
							T3GPAudioPropertiesMpeg4Audio* audio2 = (T3GPAudioPropertiesMpeg4Audio*) audioProperties2;
							result = (audio1->iDecoderSpecificInfo == audio2->iDecoderSpecificInfo);
							}
							break;							
							
						case E3GPAmrNB:
						case E3GPAmrWB:
							{
							T3GPAudioPropertiesAmr* audio1 = (T3GPAudioPropertiesAmr*) audioProperties1;
							T3GPAudioPropertiesAmr* audio2 = (T3GPAudioPropertiesAmr*) audioProperties2;
							result = (audio1->iModeSet == audio2->iModeSet);
							}
							break;														
							
						case E3GPQcelp13K:
							{
							T3GPAudioPropertiesQcelp* audio1 = (T3GPAudioPropertiesQcelp*) audioProperties1;
							T3GPAudioPropertiesQcelp* audio2 = (T3GPAudioPropertiesQcelp*) audioProperties2;
							result = ((audio1->iDecoderSpecificInfo == audio2->iDecoderSpecificInfo) &&
									 (audio1->iMode == audio2->iMode));
							}
							break;														
						}
					}
				}
			}
		}	
	else if (err1 == KErrNotSupported && err2 == err1)
		{
		result = ETrue;
		}

	CleanupStack::PopAndDestroy(4);		// audioProperties1, audioProperties2, audioDecoderSpecificInfo1, audioDecoderSpecificInfo2
	
	return result;
	}

TBool C3GPLibParseComposeFile::CompareAudioVideoData(C3GPParse& aParser1, C3GPParse& aParser2)
	{
	T3GPFrameType frameType1;
	T3GPFrameType frameType2;
	TInt err1 = KErrNone;
	TInt err2 = KErrNone;
	TBool keepLooping = ETrue;
	TBool result = EFalse;
	
	while (keepLooping)
		{
		err1 = aParser1.GetFrameType(frameType1);
		err2 = aParser2.GetFrameType(frameType2);
		
		if (err1 != err2)
			{
			break;
			}		
		else
			{
			if (err1 == KErrNotFound)
				{
				// completed looping thru all the frames
				break;
				}
			
			if (err1 != KErrNone)
				{
				// if the error is not KErrNotFound, something's wrong!
				result = EFalse;
				}			
			else if (frameType1 != frameType2)
				{			
				result = EFalse;
				}		
			else if (frameType1 == E3GPVideo)
				{
				result = CompareVideoFrame(aParser1, aParser2);
				}				
			else if (frameType1 == E3GPAudio)			
				{
				result = CompareAudioFrames(aParser1, aParser2);
				}
			
			if (!result)
				{
				break;
				}
			}
		} 
	
	return result;
	}

TBool C3GPLibParseComposeFile::CompareVideoFrame(C3GPParse& aParser1, C3GPParse& aParser2)
	{
	TUint timestamp1 = 0;
	TUint timestamp2 = 0;
	RBuf8 buf1;
	RBuf8 buf2;
	TBool keyFrame1 = EFalse;
	TBool keyFrame2 = EFalse;
	T3GPFrameDependencies dep1;
	T3GPFrameDependencies dep2;
	
	TInt err1 = ReadVideoFrame(aParser1, buf1, keyFrame1, timestamp1, dep1);
	TInt err2 = ReadVideoFrame(aParser2, buf2, keyFrame2, timestamp2, dep2);

	TBool result = EFalse;
	if (err1 == err2)
		{
		if (keyFrame1 == keyFrame2 && timestamp1 == timestamp2)
			{
			if (dep1.iDependsOn == dep2.iDependsOn && 
				dep1.iHasRedundancy == dep2.iHasRedundancy && 
				dep1.iIsDependedOn == dep2.iIsDependedOn)
				{
				if (buf1 == buf2)
					{
					result = ETrue;
					}
				else 
					{
					result = EFalse;
					}
				}
			else 
				{
				result = EFalse;
				}			
			}
		else 
			{
			result = EFalse;
			}		
		}
	else 
		{
		result = EFalse;
		}	

	// cleanup
	buf1.Close();
	buf2.Close();
	
	return result;
	}

TBool C3GPLibParseComposeFile::CompareAudioFrames(C3GPParse& aParser1, C3GPParse& aParser2)
	{
	TInt fps1 = 0;
	TInt fps2 = 0;
	TUint timestamp1 = 0;
	TUint timestamp2 = 0;
	RBuf8 buf1;
	RBuf8 buf2;
	
	TInt err1 = ReadAudioFrames(aParser1, buf1, fps1, timestamp1);
	TInt err2 = ReadAudioFrames(aParser2, buf2, fps2, timestamp2);

	TBool result = EFalse;
	if (err1 == err2)
		{
		if (fps1 == fps2 && timestamp1 == timestamp2)
			{
			if (buf1 == buf2)
				{
				result = ETrue;
				}
			else 
				{
				result = EFalse;
				}			
			}
		else 
			{
			result = EFalse;
			}		
		}
	else 
		{
		result = EFalse;
		}
	
	// cleanup	
	buf1.Close();
	buf2.Close();
	
	return result;
	}