mmplugins/lib3gp/wrapper/src/c3gpcompose.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 02 Feb 2010 01:56:55 +0200
changeset 0 40261b775718
permissions -rw-r--r--
Revision: 201003 Kit: 201005

// 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 <e32debug.h>
#include <c3gplibrary.h>

#if defined (COMPOSE_DEBUG)
#define DEBUG_PRINT RDebug::Print
#else
#define DEBUG_PRINT
#endif

const TInt KMinWriteBufferMaxCount = 6;
_LIT(K3GPComposePanicName, "C3GPCompose");

// This is video base class containing common video properties.
T3GPVideoPropertiesBase::T3GPVideoPropertiesBase(T3GPVideoType aType,
		TUint aTimescale, const TSize& aSize) :
		iType(aType), iTimescale(aTimescale), iSize(aSize)
	{
	}

/**
This structure stores the common and MPEG-4 video specific properties of video data.
 
@param	aTimescale				Timescale of the video data.  This is the number of time units that 
 								pass in one second.
@param	aSize					Video width and height in pixels.
@param	aMaxBitRate 			Maximum video bit rate.
@param	aAvgBitRate				Average video bit rate.
@param	aDecoderSpecificInfo	MPEG-4 video DecoderSpecificInfo data stored in an ESDS atom.
*/
EXPORT_C T3GPVideoPropertiesMpeg4Video::T3GPVideoPropertiesMpeg4Video(TUint aTimescale,
		const TSize& aSize, TUint aMaxBitRate, TUint aAvgBitRate, const TDesC8& aDecoderSpecificInfo) :
		T3GPVideoPropertiesBase(E3GPMpeg4Video, aTimescale, aSize),
		iMaxBitRate(aMaxBitRate),
		iAvgBitRate(aAvgBitRate),
		iDecoderSpecificInfo(aDecoderSpecificInfo)
	{
	}

/**
This structure stores the common and H.263 specific properties of video data.
 
@param	aTimescale	Timescale of the video data.  This is the number of time units that 
					pass in one second.
@param	aSize		Video width and height in pixels.
@param	aVideoLevel	Indicates the H263 video level.
@param	aProfile	Indicates the H263 profile.
*/
EXPORT_C T3GPVideoPropertiesH263::T3GPVideoPropertiesH263(TUint aTimescale, const TSize& aSize,
		TInt aVideoLevel, TProfile aProfile) :
T3GPVideoPropertiesBase((aProfile == EProfile0) ? E3GPH263Profile0 : E3GPH263Profile3,
		aTimescale, aSize), iVideoLevel(aVideoLevel)
	{
	}

/**
This structure stores the common and AVC specific properties of video data.
 
@param	aTimescale				Timescale of the video data.  This is the number of time units that 
								pass in one second.
@param	aSize					Video width and height in pixels.
@param	aDecoderSpecificInfo	AVCDecoderConfigurationRecord data that will be stored in the avcC atom.
*/
EXPORT_C T3GPVideoPropertiesAvc::T3GPVideoPropertiesAvc(TUint aTimescale, const TSize& aSize,
		const TDesC8& aDecoderSpecificInfo) :
T3GPVideoPropertiesBase(E3GPAvcProfileBaseline, aTimescale, aSize),
iDecoderSpecificInfo(aDecoderSpecificInfo)
	{
	/*
		NOTE: Although Baseline profile is being set here, it's just used to indicate
		the fact that we have AVC data. The underlying 3GP lib does not differentiate
		between profiles when composing a file. It simply writes the contents of
		iDecoderSpecificInfo (which contains the profile amongst other things)
		verbatim into the "avcC" box.
	*/
	}

// This is audio base class containing common audio properties.
T3GPAudioPropertiesBase::T3GPAudioPropertiesBase(T3GPAudioType aType,
		TUint aTimescale, TInt aFramesPerSample) :
		iType(aType), iTimescale(aTimescale), iFramesPerSample(aFramesPerSample)
	{
	__ASSERT_ALWAYS((aTimescale > 0) && (aTimescale <= KMaxTUint16), User::Panic(K3GPComposePanicName, KErrOverflow));		
	}

/**
This structure stores the common and MPEG-4 audio-specific properties of audio data.
 
@param	aTimescale			Timescale of the audio data.  This is the number of time units that pass in one 
 							second. It must be smaller than 65536.
@param	aDecoderSpecificInfo MPEG-4 audio DecoderSpecificInfo data stored in an ESDS atom.
*/
EXPORT_C T3GPAudioPropertiesMpeg4Audio::T3GPAudioPropertiesMpeg4Audio(TUint aTimescale,
		const TDesC8& aDecoderSpecificInfo) :
		T3GPAudioPropertiesBase(E3GPMpeg4Audio, aTimescale, 0), 
		iDecoderSpecificInfo(aDecoderSpecificInfo)
	{
	}

/**
This structure stores the common and AMR-specific properties of audio data. 
 
@param	aTimescale			Timescale of the audio data.  This is the number of time units that pass in one 
 							second. It must be smaller than 65536.
@param	aFramesPerSample	Frames per sample.  It must be smaller than 256.
 							MPEG-4 audio has a fixed value of 1.
@param	aModeSet			AMR mode set.
@param	aCodec				AMR Speech Codec.
*/
EXPORT_C T3GPAudioPropertiesAmr::T3GPAudioPropertiesAmr(TUint aTimescale, TInt aFramesPerSample,
		TInt aModeSet, TSpeechCodec aCodec) :
		T3GPAudioPropertiesBase((aCodec == EAmrNB) ? E3GPAmrNB : E3GPAmrWB, 
		aTimescale, aFramesPerSample), iModeSet(aModeSet)
	{	
	__ASSERT_ALWAYS((aFramesPerSample > 0) && (aFramesPerSample <= KMaxTUint8), 
					User::Panic(K3GPComposePanicName, KErrOverflow));	
	}

/**
This structure stores the common and QCELP-specific properties of MPEG4 audio data. The storage mode is 
automatically set to MPEG4 Audio Sample Description Box mode.
 
@param	aTimescale			Timescale of the audio data.  This is the number of time units that pass in one 
 							second. It must be smaller than 65536.
@param	aFramesPerSample	Frames per sample.  It must be smaller than 512.
 							MPEG-4 audio has a fixed value of 1.
@param	aDecoderSpecificInfo MPEG-4 audio decoder specific information data stored in an ESDS atom.
*/
EXPORT_C T3GPAudioPropertiesQcelp::T3GPAudioPropertiesQcelp(TUint aTimescale, TInt aFramesPerSample,
		const TDesC8& aDecoderSpecificInfo) :
		T3GPAudioPropertiesBase(E3GPQcelp13K, aTimescale, aFramesPerSample),
		iMode(E3GPMP4AudioDescriptionBox),
		iDecoderSpecificInfo(aDecoderSpecificInfo)
	{
	__ASSERT_ALWAYS((aFramesPerSample > 0) && (aFramesPerSample <= KMaxTUint8), 
					User::Panic(K3GPComposePanicName, KErrOverflow));		
	}

/**
This structure stores the common and QCELP-specific properties of audio data.  
 
@param	aTimescale			Timescale of the audio data.  This is the number of time units that pass in one 
 							second. It must be smaller than 65536.
@param	aFramesPerSample	Frames per sample.  It must be smaller than 512.
 							MPEG-4 audio has a fixed value of 1.
*/

EXPORT_C T3GPAudioPropertiesQcelp::T3GPAudioPropertiesQcelp(TUint aTimescale, TInt aFramesPerSample) :
		T3GPAudioPropertiesBase(E3GPQcelp13K, aTimescale, aFramesPerSample),
		iMode(E3GPQcelpSampleEntryBox),
		iDecoderSpecificInfo(KNullDesC8)
	{
	__ASSERT_ALWAYS((aFramesPerSample > 0) && (aFramesPerSample <= KMaxTUint8), 
					User::Panic(K3GPComposePanicName, KErrOverflow));		
	}

/** 
Create an instance of 3GP composer using default buffer count and size.

The default values for buffer count and size are as follow:
Write Buffer Size is 2048 
Write Buffer Max Count is 15

@return A pointer to the newly created 3gp compose object.

@leave	KErrGeneral		General error.
@leave	KErrNoMemory	Out of memory.

@panic	C3GPCompose	KErrAbort	if clients do not a CActiveScheduler installed already.
*/
EXPORT_C C3GPCompose* C3GPCompose::NewL()
	{
	// Leave if no scheduler exists
	__ASSERT_ALWAYS ((CActiveScheduler::Current() != NULL), Panic(KErrAbort));
	C3GPCompose* self = new (ELeave) C3GPCompose(); 
	return self;
	}

/** 
Create an instance of 3GP composer, and let the user set a count limit and size of 
internal buffer for composition.

The default values for buffer count and size are as follow:
 Write Buffer Size is 2048 
 Write Buffer Max Count is 15

An increase of the buffer count and size will lead to a decrease of file I/O activities, thereby, 
improves the performance of the 3GP Composer at the expense of higher memory usage.

@param	aMediaWriteBufferSize Size of media data file output buffer (in bytes).
@param	aWriteBufferMaxCount	Maximum number of buffers (both media and meta) allowed before file 
 								output changes to synchronous (by default file writing is asynchronous 
 								operation).  A minimum value of 6 is enforced.

@return A pointer to the newly created 3gp compose object.

@leave	KErrGeneral		General error.
@leave	KErrNoMemory	Out of memory.

@panic	C3GPCompose	KErrAbort	if clients do not a CActiveScheduler installed already.
@panic	C3GPCompose	KErrArgument	if Write Buffer Size is less or equal to 0 or Write Buffer Max Count is
									less than 6.
 */
EXPORT_C C3GPCompose* C3GPCompose::NewL(TInt aMediaWriteBufferSize, TInt aWriteBufferMaxCount)
	{
	__ASSERT_ALWAYS ((aMediaWriteBufferSize > 0 && aWriteBufferMaxCount >= KMinWriteBufferMaxCount), 
					Panic(KErrArgument));
	// Leave if no scheduler exists
	__ASSERT_ALWAYS ((CActiveScheduler::Current() != NULL), Panic(KErrAbort));
	
	C3GPCompose* self = new (ELeave) C3GPCompose(aMediaWriteBufferSize, aWriteBufferMaxCount);
	return self;
	}

// First phase constructor
C3GPCompose::C3GPCompose(TInt aMediaWriteBufferSize, TInt aWriteBufferMaxCount) :
		iMediaWriteBufferSize(aMediaWriteBufferSize), 
		iWriteBufferMaxCount(aWriteBufferMaxCount),
		iDuplicateFileHandleCreated(EFalse)
	{
	}

/** 
This function initialises the 3GP composer for writing 3GP/3G2/MP4 data into a file.
Any combination of one video and one audio type is acceptable.  

Note: Ownership of aVideo and aAudio remains with the caller.  Both aVideo and aAudio are ready for 
deletion after C3GPCompose::Open returns.

@param	aFileFormat	Specifies the file format in which the data will be created.  Refer to 
 					T3GPFileFormatType for supported file format types.
@param 	aVideo 		Specifies the video stream to be used for video data.  The input data given will 
 					be inserted into 3GP file headers and is ready to be disposed when 
 					C3GPCompose::Open returns. See Video Properties Classes.
 					If aVideo is NULL, audio-only file will be composed.
@param 	aAudio 		Specifies the audio stream to be used for audio data.  The input data given will 
 					be inserted into 3GP file headers and is ready to be disposed when 
 					C3GPCompose::Open returns. See Audio Properties Classes.
 					If aAudio is NULL, video-only file will be composed.
@param	aFilename  	A full path name of the file to save the data to.  An empty path is not allowed.
@param	aFlags		Optional flags for composing preferences.  Refer to T3GPComposeFlag for supported flags.
 					The combined use of flags is acceptable.  For example:
 					E3GPLongClip | E3GPMetaDataLast 

@return KErrNone		if successful. Otherwise, returns one of the system wide error codes.
 		KErrGeneral		if an error has no specific categorisation;
  		KErrNoMemory	if an attempt to allocate memory has failed;
  		KErrArgument	if neither video nor audio stream is specified;  
  		KErrAccessDenied	if opening file has failed;
  		KErrUnderflow	if the file name length is not greater than 0;
  		KErrInUse		if the composer is currently engaged; C3GPCompose::Complete must be called to 
  		 		 		finish the current composition before the composer can be re-initialised again.
*/
EXPORT_C TInt C3GPCompose::Open(T3GPFileFormatType aFileFormat,
		const T3GPVideoPropertiesBase* aVideo,
		const T3GPAudioPropertiesBase* aAudio,
		const TDesC& aFilename,
		TUint aFlags)
	{
	if (iHandler)
		{
		return KErrInUse;
		}
	if (aFilename.Length() <= 0)
		{
		return KErrUnderflow;
		}
	if (!aVideo && !aAudio)
		{
		// if neither video nor audio is supplied
		return KErrArgument;
		}
	
	// Create a zero terminated version of the file name
	RBuf fileName;	
	TInt err = fileName.Create(aFilename.Length() + 1);
	if (err == KErrNone)
		{
		fileName.Copy(aFilename);
		mp4_u16* mp4FileName = const_cast<mp4_u16*>(fileName.PtrZ());
		MP4Err mp4Err = MP4ComposeOpen(&iHandler, reinterpret_cast<MP4FileName>(mp4FileName), Mp4Type(aVideo, aAudio));
		
		if (mp4Err == MP4_OK)
			{
			// Write audio and video properties to the 3GP file 
			err = SetComposeProperties(aVideo, aAudio, aFileFormat, aFlags);
			if (err != KErrNone)
				{
				Complete(); // Ingore the error
				}
			}
		else
			{
			err = SymbianOSError(mp4Err);
			}
		}
	fileName.Close();
	return err;
	}

/**
This function initialises the 3GP composer for writing 3GP/3G2/MP4 data into a file.
Any combination of one video and one audio type is acceptable.  

Note: E3GPMetaDataLast will be defaulted in aFlags if file handle is used for the initialisation 
of the 3GP composer.

Note: Ownership of aVideo and aAudio remains with the caller.  Both aVideo and aAudio are ready for 
deletion after C3GPCompose::Open returns.

@param  aFileFormat Specifies the file format in which the data will be created.  Refer to 
                    T3GPFileFormatType for supported file format types.
@param  aVideo      Specifies the video stream to be used for video data.  The input data given will 
                    be inserted into 3GP file headers and is ready to be disposed when 
                    C3GPCompose::Open returns. See Video Properties Classes.
                    If aVideo is NULL, audio-only file will be composed.
@param  aAudio      Specifies the audio stream to be used for audio data.  The input data given will 
                    be inserted into 3GP file headers and is ready to be disposed when 
                    C3GPCompose::Open returns. See Audio Properties Classes.
                    If aAudio is NULL, video-only file will be composed.
@param  aFile       File handle of the file to save the data to.  E3GPMetaDataLast needs to be set for 
                    aFlags when this is used.
@param  aFlags      Optional flags for composing preferences.  Refer to T3GPComposeFlag for supported flags.
                    The combined use of flags is acceptable.  For example:
                    E3GPLongClip | E3GPMetaDataLast 

@return KErrNone        if successful. Otherwise, returns one of the system wide error codes.
        KErrGeneral     if an error has no specific categorisation;
        KErrNoMemory    if an attempt to allocate memory has failed;
        KErrArgument    if neither video nor audio stream is specified;         
        KErrAccessDenied    if opening file has failed;
        KErrInUse   if the composer is currently engaged; C3GPCompose::Complete must be called to 
                        finish the current composition before the composer can be re-initialised again.
*/

EXPORT_C TInt C3GPCompose::Open(T3GPFileFormatType aFileFormat,
		const T3GPVideoPropertiesBase* aVideo,
		const T3GPAudioPropertiesBase* aAudio,
		RFile& aFile,
		TUint aFlags)
	{
	TInt err = KErrNone;
	if (!iDuplicateFileHandleCreated)
		{
		iDuplicateFileHandleCreated = ETrue;
		iFile.Close();
		err = iFile.Duplicate(aFile);
		if (err != KErrNone)
			{
			return err;
			}
		}

	return Open(aFileFormat, aVideo, aAudio, iFile, aFlags);
	}

/**
This function initialises the 3GP composer for writing 3GP/3G2/MP4 data into a file.
Any combination of one video and one audio type is acceptable.  

Note: E3GPMetaDataLast will be defaulted in aFlags if file handle is used for the initialisation 
of the 3GP composer.

Note: Ownership of aVideo and aAudio remains with the caller.  Both aVideo and aAudio are ready for 
deletion after C3GPCompose::Open returns.

@param	aFileFormat Specifies the file format in which the data will be created.  Refer to 
 					T3GPFileFormatType for supported file format types.
@param 	aVideo 		Specifies the video stream to be used for video data.  The input data given will 
 					be inserted into 3GP file headers and is ready to be disposed when 
 					C3GPCompose::Open returns. See Video Properties Classes.
 					If aVideo is NULL, audio-only file will be composed.
@param 	aAudio 		Specifies the audio stream to be used for audio data.  The input data given will 
 					be inserted into 3GP file headers and is ready to be disposed when 
 					C3GPCompose::Open returns. See Audio Properties Classes.
 					If aAudio is NULL, video-only file will be composed.
@param	aFile  		File handle of the file to save the data to.  E3GPMetaDataLast needs to be set for 
 					aFlags when this is used.
@param	aFlags  	Optional flags for composing preferences.  Refer to T3GPComposeFlag for supported flags.
 					The combined use of flags is acceptable.  For example:
 					E3GPLongClip | E3GPMetaDataLast 

@return KErrNone		if successful. Otherwise, returns one of the system wide error codes.
 		KErrGeneral		if an error has no specific categorisation;
  		KErrNoMemory	if an attempt to allocate memory has failed;
  		KErrArgument	if neither video nor audio stream is specified;    		
  		KErrAccessDenied	if opening file has failed;
  		KErrInUse	if the composer is currently engaged; C3GPCompose::Complete must be called to 
  		 		 		finish the current composition before the composer can be re-initialised again.
*/
EXPORT_C TInt C3GPCompose::Open(T3GPFileFormatType aFileFormat,
		const T3GPVideoPropertiesBase* aVideo,
		const T3GPAudioPropertiesBase* aAudio,
		RFile64& aFile,
		TUint aFlags)
	{
	if (iHandler)
		{
		return KErrInUse;
		}
	if (!aVideo && !aAudio)
		{
		// if neither video nor audio is supplied
		return KErrArgument;
		}
	
	TInt driveNumber = EDriveA;
	TDriveInfo driveInfo;
	TInt err = aFile.Drive(driveNumber, driveInfo);
	if (err == KErrNone)
		{
		MP4Err mp4Err = MP4ComposeOpenFileHandle64(&iHandler, &aFile, static_cast<TDriveNumber>(driveNumber), Mp4Type(aVideo, aAudio));
		if (mp4Err == MP4_OK)
			{			
			// Write audio and video properties to the 3GP file 
			err = SetComposeProperties(aVideo, aAudio, aFileFormat, aFlags);
			if (err != KErrNone)
				{
				Complete(); // Ingore the error
				}
			}
		else
			{
			err = SymbianOSError(mp4Err);
			}
		}

	return err;
	}

/**
Destructor
Deletes all objects and releases all resource owned by this instance.
*/
EXPORT_C C3GPCompose::~C3GPCompose()
	{
	Complete(); // Ignore the error
	}

/**
This function completes the composing operation. It frees the memory allocated by the library instance 
and closes the output file.

It is necessary to call this function before the output file is guaranteed to be a valid output file 
even though the file may exist prior to the call.

The composer can be reused again after this call, following another call to C3GPCompose::Open to 
re-initialise the composer. 

If C3GPCompose::Complete is called before the composer is initialised, it will be ignored and KErrNone 
is returned.

Although during destruction of C3GPCompose, this function will be automatically called, and no error 
code will be returned.  Therefore, when destroying the Composer object that you have used to compose a 
file, you should ensure that data is committed to the file by invoking C3GPCompose::Complete before 
destroying the Composer object.

@return KErrNone		if successful. Otherwise, returns one of the system wide error codes.
 		KErrGeneral		if an error has no specific categorisation;
 		KErrWrite		if metadata could not be written.
*/
EXPORT_C TInt C3GPCompose::Complete()
	{
	MP4Err mp4Err = MP4_OK;
	if (iHandler)
		{
		mp4Err = MP4ComposeClose(iHandler);
		}
	// Always reset the class member data even this function returns error
	Reset();
	return SymbianOSError(mp4Err);
	}

// Helper function to reset class member data.
void C3GPCompose::Reset()
	{
	iHasVideo = EFalse;
	iHasAudio = EFalse;
	iHandler = NULL;
	iFile.Close();
	}

/**
This function writes one video frame to the output file or buffer.

The frames must be inserted according to their causal sequence.  Because the library doesn't analyze 
the video bit stream, frames are inserted into the 3GP file in the same order as they are entered 
with this function.  Therefore, the frames will not be retrieved from the resulting 3GP file or 
buffer correctly if they are not in proper order.

A frame inserted with this function call will result in one 3GP sample and one 3GP chunk.

The current frame's dependency information which is using default values (E3GPDependencyUnknown & 
E3GPRedundancyUnknown) is inserted.

The data is available in the output file only after calling C3GPCompose::Complete. C3GPCompose::Complete 
should be called exactly once when all audio and video data has been inserted into the library.

@param	aBuffer		The descriptor containing the video frame data to be written.
@param	aDuration	Duration of video frame in timescale, see T3GPVideoPropertiesBase.
@param	aKeyFrame	ETrue to indicate whether this frame is a key frame.  EFalse otherwise.

@return	KErrNone		if successful. Otherwise, returns one of the system wide error codes.
 		KErrGeneral		if an error has no specific categorisation;
 		KErrNotSupported if the composer is setup for an audio-only file;
 		KErrUnderflow	if the supplied video frame buffer data is empty;
 		KErrNotReady	if the composer has not yet been initialised;  See C3GPCompose::Open.
*/
EXPORT_C TInt C3GPCompose::WriteVideoFrame(const TDesC8& aBuffer, TUint aDuration, TBool aKeyFrame)
	{
	if (!iHandler)
		{
		return KErrNotReady;
		}
	if (aBuffer.Length() <= 0)
		{
		return KErrUnderflow;
		}
	if (!iHasVideo)
		{
		return KErrNotSupported;
		}

	// Insert video frame data
	mp4_bool keyFrame = aKeyFrame;
	mp4_u32 duration = aDuration;
	MP4Err mp4Err = MP4ComposeWriteVideoFrame(iHandler, const_cast<mp4_u8*>(aBuffer.Ptr()), aBuffer.Length(), duration, keyFrame);
	
	return SymbianOSError(mp4Err);
	}

/**
This function sets the current frame's dependency information to SDTP box and writes one video frame 
to the output file.

The frames must be inserted according to their causal sequence.  Because the library doesn't analyze 
the video bit stream, frames are inserted into the 3GP file in the same order as they are entered 
with this function.  Therefore, the frames will not be retrieved from the resulting 3GP file or 
buffer correctly if they are not in proper order.

A frame inserted with this function call will result in one 3GP sample and one 3GP chunk.

The data is available in the output file only after calling C3GPCompose::Complete. C3GPCompose::Complete 
should be called exactly once when all audio and video data has been inserted into the library.

@param	aBuffer		The descriptor containing the video frame data to be written.
@param	aDuration	Duration of video frame in timescale, see T3GPVideoPropertiesBase.
@param	aKeyFrame	ETrue to indicate whether this frame is a key frame.  EFalse otherwise.
@param	aDependencies   This specifies the current frame's dependency information.  
					The information will be supplied into the SDTP box.
 					See T3GPFrameDependencies.

@return	KErrNone		if successful. Otherwise, returns one of the system wide error codes.
 		KErrGeneral		if an error has no specific categorisation;
 		KErrNotSupported if the composer is setup for an audio-only file;
 		KErrUnderflow	if the supplied video frame buffer data is empty;
 		KErrNotReady	if the composer has not yet been initialised;  See C3GPCompose::Open.
*/
EXPORT_C TInt C3GPCompose::WriteVideoFrame(const TDesC8& aBuffer, TUint aDuration, TBool aKeyFrame,
		const T3GPFrameDependencies& aDependencies)
	{
	if (!iHandler)
		{
		return KErrNotReady;
		}
	if (aBuffer.Length() <= 0)
		{
		return KErrUnderflow;
		}
	if (!iHasVideo)
		{
		return KErrNotSupported;
		}

	// Insert the current frame's dependency information
	MP4Err mp4Err = MP4ComposeWriteNextVideoFrameDependencies(iHandler, aDependencies.iDependsOn,
			aDependencies.iIsDependedOn, aDependencies.iHasRedundancy);

	if (mp4Err == MP4_OK)
		{
		// Insert video frame data
		mp4_bool keyFrame = aKeyFrame;
		mp4_u32 duration = aDuration;
		mp4Err = MP4ComposeWriteVideoFrame(iHandler, const_cast<mp4_u8*>(aBuffer.Ptr()), 
				aBuffer.Length(), duration, keyFrame);
		}

	return SymbianOSError(mp4Err);
	}

/**
This function writes audio frames into the output file or buffer.  The data is available in the 
3GP output file only after calling C3GPCompose::Complete.  C3GPCompose::Complete should be called exactly 
once when all audio and video data has been inserted into the library.

For MPEG-4 audio:
This function writes one MPEG audio frame to the 3GP file. 

For other audio types:
This function writes a number of audio frames to the 3GP file specified during composer setup 
in the input parameter aAudio when calling C3GPCompose::Open.  All audio frames inserted with 
one function call will be placed inside one sample in the resulting file.

Note: Only the last call can have a different number of frames if the number is less than 
the number of frames per sample specified during composer setup.

@see T3GPAudioPropertiesAmr
@see T3GPAudioPropertiesQcelp  		

@param	aBuffer			The descriptor containing the audio data to be written.
@param	aDuration		Duration of audio frames in timescale, see T3GPAudioPropertiesBase

@return	KErrNone		if successful.  Otherwise, returns one of the system wide error codes.
 		KErrGeneral		if an error has no specific categorisation;
 		KErrNotSupported if the composer is setup for a video-only file;
 		KErrUnderflow	if the supplied audio frames buffer data is empty;
 		KErrNotReady	if the composer has not yet been initialised;  See C3GPCompose::Open.
*/
EXPORT_C TInt C3GPCompose::WriteAudioFrames(const TDesC8& aBuffer, TUint aDuration)
	{
	if (!iHandler)
		{
		return KErrNotReady;
		}
	if (aBuffer.Length() <= 0)
		{
		return KErrUnderflow;
		}
	if (!iHasAudio)
		{
		return KErrNotSupported;
		}

	mp4_u32 duration = aDuration;
    // use 0 for the number of frames since it is being ignored within the mp4 library implementation
	MP4Err mp4Err = MP4ComposeWriteAudioFrames(iHandler, const_cast<mp4_u8*>(aBuffer.Ptr()), 
			aBuffer.Length(), 0, duration);
	
	return SymbianOSError(mp4Err);
	}

/**
Writes a buffer containing whole atom to inside of user data atom (UDTA) defined in aLocation.

The buffer should contain an atom of structure that conforms to the definition of a "full box" 
as specified in ISO/IEC 14496-12:2003: "Information technology – Coding of audio-visual objects
 – Part 12: ISO base media file format."
 
For more information on user data atoms, see Section 8 – Asset Information of "3GPP TS 26.244 
version 6.1.0 – 3GP file format (Rel 6)."

@param	aLocation	Specifies the location of user information to be written.  Refer to 
 					T3GPUdtaLocation for possible values.
@param	aBuffer		The descriptor containing the user information to write into file.

@return	KErrNone		if successful.  Otherwise, returns one of the system wide error codes.
 		KErrGeneral		if an error has no specific categorisation;
 		KErrArgument	if asked aLocation is invalid;
 		KErrUnderflow	if the supplied buffer data is empty;
 		KErrNotSupported	if specify video track user data but no video type is specified, 
 						or specify audio track user data but no audio type is specified
 		KErrNoMemory	if an attempt to allocate memory has failed;
 		KErrNotReady	if the composer has not yet been initialised;  See C3GPCompose::Open.
 		
@panic	C3GPCompose	KErrArgument if the location of user information is not of T3GPUdtaLocation. 
*/
EXPORT_C TInt C3GPCompose::SetUserData(T3GPUdtaLocation aLocation, const TDesC8& aBuffer)
	{
	if (!iHandler)
		{
		return KErrNotReady;
		}
	if (aBuffer.Length() <= 0)
		{
		return KErrUnderflow;
		}
	if ((!iHasAudio && aLocation == E3GPUdtaAudioTrak) || (!iHasVideo && aLocation == E3GPUdtaVideoTrak))
		{
		return KErrNotSupported;
		}
	
	mp4_u8 location;
	switch (aLocation)
		{
		case (E3GPUdtaMoov):
			location = MP4_UDTA_MOOV;
		break;
		case (E3GPUdtaVideoTrak):
			location = MP4_UDTA_VIDEOTRAK;
		break;
		case (E3GPUdtaAudioTrak):
			location = MP4_UDTA_AUDIOTRAK;
		break;
		default:
			Panic(KErrArgument);
		break;
		}

	mp4_u32 bufferSize = aBuffer.Length();
	MP4Err mp4Err = MP4ComposeSetUserDataAtom(iHandler, location, const_cast<mp4_u8*>(aBuffer.Ptr()), bufferSize);
	return SymbianOSError(mp4Err);
	}

// Helper function to convert 3GP/MP4 library specific error codes to system wide error codes
TInt C3GPCompose::SymbianOSError(MP4Err aError)
	{
	TInt error = KErrNone;

	switch (aError)
		{
		case (MP4_OK):
		break;
		case (MP4_ERROR):
			error = KErrGeneral;
		break;
		case (MP4_OUT_OF_MEMORY):
			error = KErrNoMemory;
		break;
		case (MP4_FILE_ERROR):
			error = KErrAccessDenied;
		break;
		case (MP4_INVALID_TYPE):
			error = KErrArgument;
		break;
		case (MP4_METADATA_ERROR):
			error = KErrWrite;
		break;
		default:
			Panic(KErrArgument);
		}
	return error;
	}

// Helper function to map 3GP enum type to MP4 audio and video type
mp4_u32 C3GPCompose::Mp4Type(const T3GPVideoPropertiesBase* aVideo, const T3GPAudioPropertiesBase* aAudio)
	{
	mp4_u32 videoType = MP4_TYPE_NONE;
	mp4_u32 audioType = MP4_TYPE_NONE;
	
	if (aVideo)
		{
		iHasVideo = ETrue;
		switch (aVideo->iType)
			{
			case (E3GPMpeg4Video):
				videoType = MP4_TYPE_MPEG4_VIDEO;
			break;
			case (E3GPH263Profile0):
				videoType = MP4_TYPE_H263_PROFILE_0;
			break;
			case (E3GPH263Profile3):
				videoType = MP4_TYPE_H263_PROFILE_3;
			break;
			
			/*
			 * NOTE: The underlying 3GP library does
			 * not differentiate between the various AVC
			 * profiles when composing.
			 * 
			 * In all cases it will simply copy the data
			 * from the iDecoderSpecificInfo member of
			 * T3GPVideoPropertiesAvc into the 'avcC' atom.
			 * It does not do any checking of that data, so
			 * it is the API user's responsibility to ensure
			 * that it contains a valid AVCDecoderConfigurationRecord
			 * with the correct AVC profile and level.
			 * 
			 * An interesting side-effect of this is that you can
			 * compose AVC data with arbitrary profiles even if they
			 * are not "supported" by this API. For example, as long
			 * as the AVCDecoderConfigurationRecord says there is
			 * High 10 profile data and the AVC data is of that profile
			 * then you will still end up with a valid file.
			 */
			case (E3GPAvcProfileBaseline):
				videoType = MP4_TYPE_AVC_PROFILE_BASELINE;
			break;
            case (E3GPAvcProfileMain):
                videoType = MP4_TYPE_AVC_PROFILE_MAIN;
            break;
            case (E3GPAvcProfileExtended):
                videoType = MP4_TYPE_AVC_PROFILE_EXTENDED;
            break;
            case (E3GPAvcProfileHigh):
                videoType = MP4_TYPE_AVC_PROFILE_HIGH;
            break;
			default:
				Panic(KErrArgument);
			}
		}

	if(aAudio)
		{
		iHasAudio = ETrue;
		switch (aAudio->iType)
			{
			case (E3GPMpeg4Audio):
				audioType = MP4_TYPE_MPEG4_AUDIO;
			break;
			case (E3GPAmrNB):
				audioType = MP4_TYPE_AMR_NB;
			break;
			case (E3GPAmrWB):
				audioType = MP4_TYPE_AMR_WB;
			break;
			case (E3GPQcelp13K):
				audioType = MP4_TYPE_QCELP_13K;
			break;
			default:
				Panic(KErrArgument);
			}
		}
	return (videoType | audioType);
	}
	
// Helper function to set compose properties
TInt C3GPCompose::SetComposeProperties(const T3GPVideoPropertiesBase* aVideo,
		const T3GPAudioPropertiesBase* aAudio, T3GPFileFormatType aFileFormat, TUint aFlag)
	{
	mp4_u32 writeBufferSize = iMediaWriteBufferSize;
	mp4_u32 writeBufferMaxCount = iWriteBufferMaxCount;
	MP4Err mp4Err = MP4SetCustomFileBufferSizes(iHandler, writeBufferSize, writeBufferMaxCount, 0);
	if ( mp4Err != MP4_OK)
		{
		return SymbianOSError(mp4Err);
		}
	
	// Set compose flag before other MP4Compose functions
	TInt err = SetComposeFlag(aFileFormat, aFlag);
	if (err != KErrNone)
		{
		return err;
		}
	
	if (aVideo)
		{
		switch (aVideo->iType)
			{
			case (E3GPMpeg4Video):
				err = SetMPeg4VideoProperties(aVideo);
			break;
			case (E3GPAvcProfileBaseline):
            case (E3GPAvcProfileMain):
            case (E3GPAvcProfileExtended):
            case (E3GPAvcProfileHigh):
				err = SetAvcVideoProperties(aVideo);
			break;
			case (E3GPH263Profile0):
			case (E3GPH263Profile3):
				err = SetH263VideoProperties(aVideo);
			break;
			default:
				Panic(KErrArgument);
			}
		}
	if (err != KErrNone)
		{
		return err;
		}

	if (aAudio)
		{
		switch (aAudio->iType)
			{
			case (E3GPMpeg4Audio):
				err = SetMpeg4AudioProperties(aAudio);
			break;
			case (E3GPQcelp13K):
				err = SetQcelpAudioProperties(aAudio);
			break;
			case (E3GPAmrNB):
			case (E3GPAmrWB):
				err = SetAmrAudioProperties(aAudio);
			break;
			default:
				Panic(KErrArgument);
			}
		}
	return err;
	}

// Inform the 3GP library about the MPeg4 video data
TInt C3GPCompose::SetMPeg4VideoProperties(const T3GPVideoPropertiesBase* aVideo)
	{
	const T3GPVideoPropertiesMpeg4Video* mpeg4Video = static_cast<const T3GPVideoPropertiesMpeg4Video*>(aVideo);

	MP4Err err = MP4ComposeWriteVideoDecoderSpecificInfo(iHandler, const_cast<mp4_u8*>(mpeg4Video->iDecoderSpecificInfo.Ptr()), 
			mpeg4Video->iDecoderSpecificInfo.Length());

	if ( err == MP4_OK)
		{
		err = MP4ComposeAddVideoDescription(iHandler, mpeg4Video->iTimescale,
				mpeg4Video->iSize.iWidth, mpeg4Video->iSize.iHeight,
				mpeg4Video->iMaxBitRate, mpeg4Video->iAvgBitRate);
		}

	return SymbianOSError(err);
	}

// Inform the 3GP library about the AVC video data
TInt C3GPCompose::SetAvcVideoProperties(const T3GPVideoPropertiesBase* aVideo)
	{
	const T3GPVideoPropertiesAvc* avcVideo = static_cast<const T3GPVideoPropertiesAvc*>(aVideo);

	MP4Err err = MP4ComposeWriteVideoDecoderSpecificInfo(iHandler, const_cast<mp4_u8*>(avcVideo->iDecoderSpecificInfo.Ptr()), 
			avcVideo->iDecoderSpecificInfo.Length ());

	if ( err == MP4_OK)
		{
		// aMaxBitRate and aAvgBitRate are MPEG-4 video specific values.  Set 0 for them
		err = MP4ComposeAddVideoDescription(iHandler, avcVideo->iTimescale,
				avcVideo->iSize.iWidth, avcVideo->iSize.iHeight, 0, 0);
		}

	return SymbianOSError(err);
	}

// Inform the 3GP library about the H263 video data
TInt C3GPCompose::SetH263VideoProperties(const T3GPVideoPropertiesBase* aVideo)
	{
	// aMaxBitRate and aAvgBitRate are MPEG-4 video specific values.  Set 0 for H263 video
	MP4Err err = MP4ComposeAddVideoDescription(iHandler, aVideo->iTimescale,
			aVideo->iSize.iWidth, aVideo->iSize.iHeight, 0, 0);

	if ( err == MP4_OK)
		{
		const T3GPVideoPropertiesH263* h263Video = static_cast<const T3GPVideoPropertiesH263*>(aVideo);
		TVideoClipProperties properties;
		properties.iH263Level = h263Video->iVideoLevel;
		err = MP4ComposeSetVideoClipProperties(iHandler, properties);
		}

	return SymbianOSError(err);
	}

// Inform the 3GP library about the MPeg4 audio data
TInt C3GPCompose::SetMpeg4AudioProperties(const T3GPAudioPropertiesBase* aAudio)
	{
	const T3GPAudioPropertiesMpeg4Audio* mpeg4Audio = static_cast<const T3GPAudioPropertiesMpeg4Audio*>(aAudio);

	MP4Err err = MP4ComposeWriteAudioDecoderSpecificInfo(iHandler, const_cast<mp4_u8*>(mpeg4Audio->iDecoderSpecificInfo.Ptr()), 
			mpeg4Audio->iDecoderSpecificInfo.Length ());

	if ( err == MP4_OK)
		{
		//modeSet is needed only for AMR audio. Set it to 0 for Mpeg4 audio.
		err = MP4ComposeAddAudioDescription(iHandler, mpeg4Audio->iTimescale, mpeg4Audio->iFramesPerSample, 0);
		}

	return SymbianOSError(err);
	}

// Inform the 3GP library about the Amr audio data
TInt C3GPCompose::SetAmrAudioProperties(const T3GPAudioPropertiesBase* aAudio)
	{
	const T3GPAudioPropertiesAmr* amrAudio = static_cast<const T3GPAudioPropertiesAmr*>(aAudio);
	//modeSet is needed only for AMR audio. 
	MP4Err err = MP4ComposeAddAudioDescription(iHandler, amrAudio->iTimescale,
			amrAudio->iFramesPerSample, amrAudio->iModeSet);
	return SymbianOSError(err);
	}

// Sets the storage mode of storing 13K QCELP data in a 3G2 file
TInt C3GPCompose::SetQcelpAudioProperties(const T3GPAudioPropertiesBase* aAudio)
	{
	const T3GPAudioPropertiesQcelp* qcelpAudio = static_cast<const T3GPAudioPropertiesQcelp*>(aAudio);
	MP4Err err = MP4ComposeSetQCELPStorageMode(iHandler, qcelpAudio->iMode);
	if ( err == MP4_OK)
		{
		if ( qcelpAudio->iMode == E3GPMP4AudioDescriptionBox)
			{
			err = MP4ComposeWriteAudioDecoderSpecificInfo(iHandler, const_cast<mp4_u8*>(qcelpAudio->iDecoderSpecificInfo.Ptr()), qcelpAudio->iDecoderSpecificInfo.Length ());
			}
		if ( err == MP4_OK)
			{
			//modeSet is needed only for AMR audio. Set it to 0 for Qcelp audio.
			err = MP4ComposeAddAudioDescription(iHandler, qcelpAudio->iTimescale, qcelpAudio->iFramesPerSample, 0);
			}
		}

	return SymbianOSError(err);
	}

// Set compose flag
TInt C3GPCompose::SetComposeFlag(T3GPFileFormatType aFileFormat, TUint aFlag)
	{
	mp4_u32 fileFormat = 0;
	switch (aFileFormat)
		{
		case (E3GPMP4):
			fileFormat = MP4_FLAG_GENERATE_MP4;
		break;
		case (E3GP3G2):
			fileFormat = MP4_FLAG_GENERATE_3G2;
		break;
		case (E3GP3GP):
			fileFormat = MP4_FLAG_NONE;
		break;
		default:
			Panic(KErrArgument);
		}

	MP4Err err = MP4ComposeSetFlags(iHandler, aFlag | fileFormat);
	return SymbianOSError(err);
	}


void C3GPCompose::Panic(TInt aPanic)
// Panic client
	{
	User::Panic(K3GPComposePanicName, aPanic);
	}