sl@0: // Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // sl@0: sl@0: #include sl@0: #include sl@0: sl@0: #if defined (COMPOSE_DEBUG) sl@0: #define DEBUG_PRINT RDebug::Print sl@0: #else sl@0: #define DEBUG_PRINT sl@0: #endif sl@0: sl@0: const TInt KMinWriteBufferMaxCount = 6; sl@0: _LIT(K3GPComposePanicName, "C3GPCompose"); sl@0: sl@0: // This is video base class containing common video properties. sl@0: T3GPVideoPropertiesBase::T3GPVideoPropertiesBase(T3GPVideoType aType, sl@0: TUint aTimescale, const TSize& aSize) : sl@0: iType(aType), iTimescale(aTimescale), iSize(aSize) sl@0: { sl@0: } sl@0: sl@0: /** sl@0: This structure stores the common and MPEG-4 video specific properties of video data. sl@0: sl@0: @param aTimescale Timescale of the video data. This is the number of time units that sl@0: pass in one second. sl@0: @param aSize Video width and height in pixels. sl@0: @param aMaxBitRate Maximum video bit rate. sl@0: @param aAvgBitRate Average video bit rate. sl@0: @param aDecoderSpecificInfo MPEG-4 video DecoderSpecificInfo data stored in an ESDS atom. sl@0: */ sl@0: EXPORT_C T3GPVideoPropertiesMpeg4Video::T3GPVideoPropertiesMpeg4Video(TUint aTimescale, sl@0: const TSize& aSize, TUint aMaxBitRate, TUint aAvgBitRate, const TDesC8& aDecoderSpecificInfo) : sl@0: T3GPVideoPropertiesBase(E3GPMpeg4Video, aTimescale, aSize), sl@0: iMaxBitRate(aMaxBitRate), sl@0: iAvgBitRate(aAvgBitRate), sl@0: iDecoderSpecificInfo(aDecoderSpecificInfo) sl@0: { sl@0: } sl@0: sl@0: /** sl@0: This structure stores the common and H.263 specific properties of video data. sl@0: sl@0: @param aTimescale Timescale of the video data. This is the number of time units that sl@0: pass in one second. sl@0: @param aSize Video width and height in pixels. sl@0: @param aVideoLevel Indicates the H263 video level. sl@0: @param aProfile Indicates the H263 profile. sl@0: */ sl@0: EXPORT_C T3GPVideoPropertiesH263::T3GPVideoPropertiesH263(TUint aTimescale, const TSize& aSize, sl@0: TInt aVideoLevel, TProfile aProfile) : sl@0: T3GPVideoPropertiesBase((aProfile == EProfile0) ? E3GPH263Profile0 : E3GPH263Profile3, sl@0: aTimescale, aSize), iVideoLevel(aVideoLevel) sl@0: { sl@0: } sl@0: sl@0: /** sl@0: This structure stores the common and AVC specific properties of video data. sl@0: sl@0: @param aTimescale Timescale of the video data. This is the number of time units that sl@0: pass in one second. sl@0: @param aSize Video width and height in pixels. sl@0: @param aDecoderSpecificInfo AVCDecoderConfigurationRecord data that will be stored in the avcC atom. sl@0: */ sl@0: EXPORT_C T3GPVideoPropertiesAvc::T3GPVideoPropertiesAvc(TUint aTimescale, const TSize& aSize, sl@0: const TDesC8& aDecoderSpecificInfo) : sl@0: T3GPVideoPropertiesBase(E3GPAvcProfileBaseline, aTimescale, aSize), sl@0: iDecoderSpecificInfo(aDecoderSpecificInfo) sl@0: { sl@0: /* sl@0: NOTE: Although Baseline profile is being set here, it's just used to indicate sl@0: the fact that we have AVC data. The underlying 3GP lib does not differentiate sl@0: between profiles when composing a file. It simply writes the contents of sl@0: iDecoderSpecificInfo (which contains the profile amongst other things) sl@0: verbatim into the "avcC" box. sl@0: */ sl@0: } sl@0: sl@0: // This is audio base class containing common audio properties. sl@0: T3GPAudioPropertiesBase::T3GPAudioPropertiesBase(T3GPAudioType aType, sl@0: TUint aTimescale, TInt aFramesPerSample) : sl@0: iType(aType), iTimescale(aTimescale), iFramesPerSample(aFramesPerSample) sl@0: { sl@0: __ASSERT_ALWAYS((aTimescale > 0) && (aTimescale <= KMaxTUint16), User::Panic(K3GPComposePanicName, KErrOverflow)); sl@0: } sl@0: sl@0: /** sl@0: This structure stores the common and MPEG-4 audio-specific properties of audio data. sl@0: sl@0: @param aTimescale Timescale of the audio data. This is the number of time units that pass in one sl@0: second. It must be smaller than 65536. sl@0: @param aDecoderSpecificInfo MPEG-4 audio DecoderSpecificInfo data stored in an ESDS atom. sl@0: */ sl@0: EXPORT_C T3GPAudioPropertiesMpeg4Audio::T3GPAudioPropertiesMpeg4Audio(TUint aTimescale, sl@0: const TDesC8& aDecoderSpecificInfo) : sl@0: T3GPAudioPropertiesBase(E3GPMpeg4Audio, aTimescale, 0), sl@0: iDecoderSpecificInfo(aDecoderSpecificInfo) sl@0: { sl@0: } sl@0: sl@0: /** sl@0: This structure stores the common and AMR-specific properties of audio data. sl@0: sl@0: @param aTimescale Timescale of the audio data. This is the number of time units that pass in one sl@0: second. It must be smaller than 65536. sl@0: @param aFramesPerSample Frames per sample. It must be smaller than 256. sl@0: MPEG-4 audio has a fixed value of 1. sl@0: @param aModeSet AMR mode set. sl@0: @param aCodec AMR Speech Codec. sl@0: */ sl@0: EXPORT_C T3GPAudioPropertiesAmr::T3GPAudioPropertiesAmr(TUint aTimescale, TInt aFramesPerSample, sl@0: TInt aModeSet, TSpeechCodec aCodec) : sl@0: T3GPAudioPropertiesBase((aCodec == EAmrNB) ? E3GPAmrNB : E3GPAmrWB, sl@0: aTimescale, aFramesPerSample), iModeSet(aModeSet) sl@0: { sl@0: __ASSERT_ALWAYS((aFramesPerSample > 0) && (aFramesPerSample <= KMaxTUint8), sl@0: User::Panic(K3GPComposePanicName, KErrOverflow)); sl@0: } sl@0: sl@0: /** sl@0: This structure stores the common and QCELP-specific properties of MPEG4 audio data. The storage mode is sl@0: automatically set to MPEG4 Audio Sample Description Box mode. sl@0: sl@0: @param aTimescale Timescale of the audio data. This is the number of time units that pass in one sl@0: second. It must be smaller than 65536. sl@0: @param aFramesPerSample Frames per sample. It must be smaller than 512. sl@0: MPEG-4 audio has a fixed value of 1. sl@0: @param aDecoderSpecificInfo MPEG-4 audio decoder specific information data stored in an ESDS atom. sl@0: */ sl@0: EXPORT_C T3GPAudioPropertiesQcelp::T3GPAudioPropertiesQcelp(TUint aTimescale, TInt aFramesPerSample, sl@0: const TDesC8& aDecoderSpecificInfo) : sl@0: T3GPAudioPropertiesBase(E3GPQcelp13K, aTimescale, aFramesPerSample), sl@0: iMode(E3GPMP4AudioDescriptionBox), sl@0: iDecoderSpecificInfo(aDecoderSpecificInfo) sl@0: { sl@0: __ASSERT_ALWAYS((aFramesPerSample > 0) && (aFramesPerSample <= KMaxTUint8), sl@0: User::Panic(K3GPComposePanicName, KErrOverflow)); sl@0: } sl@0: sl@0: /** sl@0: This structure stores the common and QCELP-specific properties of audio data. sl@0: sl@0: @param aTimescale Timescale of the audio data. This is the number of time units that pass in one sl@0: second. It must be smaller than 65536. sl@0: @param aFramesPerSample Frames per sample. It must be smaller than 512. sl@0: MPEG-4 audio has a fixed value of 1. sl@0: */ sl@0: sl@0: EXPORT_C T3GPAudioPropertiesQcelp::T3GPAudioPropertiesQcelp(TUint aTimescale, TInt aFramesPerSample) : sl@0: T3GPAudioPropertiesBase(E3GPQcelp13K, aTimescale, aFramesPerSample), sl@0: iMode(E3GPQcelpSampleEntryBox), sl@0: iDecoderSpecificInfo(KNullDesC8) sl@0: { sl@0: __ASSERT_ALWAYS((aFramesPerSample > 0) && (aFramesPerSample <= KMaxTUint8), sl@0: User::Panic(K3GPComposePanicName, KErrOverflow)); sl@0: } sl@0: sl@0: /** sl@0: Create an instance of 3GP composer using default buffer count and size. sl@0: sl@0: The default values for buffer count and size are as follow: sl@0: Write Buffer Size is 2048 sl@0: Write Buffer Max Count is 15 sl@0: sl@0: @return A pointer to the newly created 3gp compose object. sl@0: sl@0: @leave KErrGeneral General error. sl@0: @leave KErrNoMemory Out of memory. sl@0: sl@0: @panic C3GPCompose KErrAbort if clients do not a CActiveScheduler installed already. sl@0: */ sl@0: EXPORT_C C3GPCompose* C3GPCompose::NewL() sl@0: { sl@0: // Leave if no scheduler exists sl@0: __ASSERT_ALWAYS ((CActiveScheduler::Current() != NULL), Panic(KErrAbort)); sl@0: C3GPCompose* self = new (ELeave) C3GPCompose(); sl@0: return self; sl@0: } sl@0: sl@0: /** sl@0: Create an instance of 3GP composer, and let the user set a count limit and size of sl@0: internal buffer for composition. sl@0: sl@0: The default values for buffer count and size are as follow: sl@0: Write Buffer Size is 2048 sl@0: Write Buffer Max Count is 15 sl@0: sl@0: An increase of the buffer count and size will lead to a decrease of file I/O activities, thereby, sl@0: improves the performance of the 3GP Composer at the expense of higher memory usage. sl@0: sl@0: @param aMediaWriteBufferSize Size of media data file output buffer (in bytes). sl@0: @param aWriteBufferMaxCount Maximum number of buffers (both media and meta) allowed before file sl@0: output changes to synchronous (by default file writing is asynchronous sl@0: operation). A minimum value of 6 is enforced. sl@0: sl@0: @return A pointer to the newly created 3gp compose object. sl@0: sl@0: @leave KErrGeneral General error. sl@0: @leave KErrNoMemory Out of memory. sl@0: sl@0: @panic C3GPCompose KErrAbort if clients do not a CActiveScheduler installed already. sl@0: @panic C3GPCompose KErrArgument if Write Buffer Size is less or equal to 0 or Write Buffer Max Count is sl@0: less than 6. sl@0: */ sl@0: EXPORT_C C3GPCompose* C3GPCompose::NewL(TInt aMediaWriteBufferSize, TInt aWriteBufferMaxCount) sl@0: { sl@0: __ASSERT_ALWAYS ((aMediaWriteBufferSize > 0 && aWriteBufferMaxCount >= KMinWriteBufferMaxCount), sl@0: Panic(KErrArgument)); sl@0: // Leave if no scheduler exists sl@0: __ASSERT_ALWAYS ((CActiveScheduler::Current() != NULL), Panic(KErrAbort)); sl@0: sl@0: C3GPCompose* self = new (ELeave) C3GPCompose(aMediaWriteBufferSize, aWriteBufferMaxCount); sl@0: return self; sl@0: } sl@0: sl@0: // First phase constructor sl@0: C3GPCompose::C3GPCompose(TInt aMediaWriteBufferSize, TInt aWriteBufferMaxCount) : sl@0: iMediaWriteBufferSize(aMediaWriteBufferSize), sl@0: iWriteBufferMaxCount(aWriteBufferMaxCount), sl@0: iDuplicateFileHandleCreated(EFalse) sl@0: { sl@0: } sl@0: sl@0: /** sl@0: This function initialises the 3GP composer for writing 3GP/3G2/MP4 data into a file. sl@0: Any combination of one video and one audio type is acceptable. sl@0: sl@0: Note: Ownership of aVideo and aAudio remains with the caller. Both aVideo and aAudio are ready for sl@0: deletion after C3GPCompose::Open returns. sl@0: sl@0: @param aFileFormat Specifies the file format in which the data will be created. Refer to sl@0: T3GPFileFormatType for supported file format types. sl@0: @param aVideo Specifies the video stream to be used for video data. The input data given will sl@0: be inserted into 3GP file headers and is ready to be disposed when sl@0: C3GPCompose::Open returns. See Video Properties Classes. sl@0: If aVideo is NULL, audio-only file will be composed. sl@0: @param aAudio Specifies the audio stream to be used for audio data. The input data given will sl@0: be inserted into 3GP file headers and is ready to be disposed when sl@0: C3GPCompose::Open returns. See Audio Properties Classes. sl@0: If aAudio is NULL, video-only file will be composed. sl@0: @param aFilename A full path name of the file to save the data to. An empty path is not allowed. sl@0: @param aFlags Optional flags for composing preferences. Refer to T3GPComposeFlag for supported flags. sl@0: The combined use of flags is acceptable. For example: sl@0: E3GPLongClip | E3GPMetaDataLast sl@0: sl@0: @return KErrNone if successful. Otherwise, returns one of the system wide error codes. sl@0: KErrGeneral if an error has no specific categorisation; sl@0: KErrNoMemory if an attempt to allocate memory has failed; sl@0: KErrArgument if neither video nor audio stream is specified; sl@0: KErrAccessDenied if opening file has failed; sl@0: KErrUnderflow if the file name length is not greater than 0; sl@0: KErrInUse if the composer is currently engaged; C3GPCompose::Complete must be called to sl@0: finish the current composition before the composer can be re-initialised again. sl@0: */ sl@0: EXPORT_C TInt C3GPCompose::Open(T3GPFileFormatType aFileFormat, sl@0: const T3GPVideoPropertiesBase* aVideo, sl@0: const T3GPAudioPropertiesBase* aAudio, sl@0: const TDesC& aFilename, sl@0: TUint aFlags) sl@0: { sl@0: if (iHandler) sl@0: { sl@0: return KErrInUse; sl@0: } sl@0: if (aFilename.Length() <= 0) sl@0: { sl@0: return KErrUnderflow; sl@0: } sl@0: if (!aVideo && !aAudio) sl@0: { sl@0: // if neither video nor audio is supplied sl@0: return KErrArgument; sl@0: } sl@0: sl@0: // Create a zero terminated version of the file name sl@0: RBuf fileName; sl@0: TInt err = fileName.Create(aFilename.Length() + 1); sl@0: if (err == KErrNone) sl@0: { sl@0: fileName.Copy(aFilename); sl@0: mp4_u16* mp4FileName = const_cast(fileName.PtrZ()); sl@0: MP4Err mp4Err = MP4ComposeOpen(&iHandler, reinterpret_cast(mp4FileName), Mp4Type(aVideo, aAudio)); sl@0: sl@0: if (mp4Err == MP4_OK) sl@0: { sl@0: // Write audio and video properties to the 3GP file sl@0: err = SetComposeProperties(aVideo, aAudio, aFileFormat, aFlags); sl@0: if (err != KErrNone) sl@0: { sl@0: Complete(); // Ingore the error sl@0: } sl@0: } sl@0: else sl@0: { sl@0: err = SymbianOSError(mp4Err); sl@0: } sl@0: } sl@0: fileName.Close(); sl@0: return err; sl@0: } sl@0: sl@0: /** sl@0: This function initialises the 3GP composer for writing 3GP/3G2/MP4 data into a file. sl@0: Any combination of one video and one audio type is acceptable. sl@0: sl@0: Note: E3GPMetaDataLast will be defaulted in aFlags if file handle is used for the initialisation sl@0: of the 3GP composer. sl@0: sl@0: Note: Ownership of aVideo and aAudio remains with the caller. Both aVideo and aAudio are ready for sl@0: deletion after C3GPCompose::Open returns. sl@0: sl@0: @param aFileFormat Specifies the file format in which the data will be created. Refer to sl@0: T3GPFileFormatType for supported file format types. sl@0: @param aVideo Specifies the video stream to be used for video data. The input data given will sl@0: be inserted into 3GP file headers and is ready to be disposed when sl@0: C3GPCompose::Open returns. See Video Properties Classes. sl@0: If aVideo is NULL, audio-only file will be composed. sl@0: @param aAudio Specifies the audio stream to be used for audio data. The input data given will sl@0: be inserted into 3GP file headers and is ready to be disposed when sl@0: C3GPCompose::Open returns. See Audio Properties Classes. sl@0: If aAudio is NULL, video-only file will be composed. sl@0: @param aFile File handle of the file to save the data to. E3GPMetaDataLast needs to be set for sl@0: aFlags when this is used. sl@0: @param aFlags Optional flags for composing preferences. Refer to T3GPComposeFlag for supported flags. sl@0: The combined use of flags is acceptable. For example: sl@0: E3GPLongClip | E3GPMetaDataLast sl@0: sl@0: @return KErrNone if successful. Otherwise, returns one of the system wide error codes. sl@0: KErrGeneral if an error has no specific categorisation; sl@0: KErrNoMemory if an attempt to allocate memory has failed; sl@0: KErrArgument if neither video nor audio stream is specified; sl@0: KErrAccessDenied if opening file has failed; sl@0: KErrInUse if the composer is currently engaged; C3GPCompose::Complete must be called to sl@0: finish the current composition before the composer can be re-initialised again. sl@0: */ sl@0: sl@0: EXPORT_C TInt C3GPCompose::Open(T3GPFileFormatType aFileFormat, sl@0: const T3GPVideoPropertiesBase* aVideo, sl@0: const T3GPAudioPropertiesBase* aAudio, sl@0: RFile& aFile, sl@0: TUint aFlags) sl@0: { sl@0: TInt err = KErrNone; sl@0: if (!iDuplicateFileHandleCreated) sl@0: { sl@0: iDuplicateFileHandleCreated = ETrue; sl@0: iFile.Close(); sl@0: err = iFile.Duplicate(aFile); sl@0: if (err != KErrNone) sl@0: { sl@0: return err; sl@0: } sl@0: } sl@0: sl@0: return Open(aFileFormat, aVideo, aAudio, iFile, aFlags); sl@0: } sl@0: sl@0: /** sl@0: This function initialises the 3GP composer for writing 3GP/3G2/MP4 data into a file. sl@0: Any combination of one video and one audio type is acceptable. sl@0: sl@0: Note: E3GPMetaDataLast will be defaulted in aFlags if file handle is used for the initialisation sl@0: of the 3GP composer. sl@0: sl@0: Note: Ownership of aVideo and aAudio remains with the caller. Both aVideo and aAudio are ready for sl@0: deletion after C3GPCompose::Open returns. sl@0: sl@0: @param aFileFormat Specifies the file format in which the data will be created. Refer to sl@0: T3GPFileFormatType for supported file format types. sl@0: @param aVideo Specifies the video stream to be used for video data. The input data given will sl@0: be inserted into 3GP file headers and is ready to be disposed when sl@0: C3GPCompose::Open returns. See Video Properties Classes. sl@0: If aVideo is NULL, audio-only file will be composed. sl@0: @param aAudio Specifies the audio stream to be used for audio data. The input data given will sl@0: be inserted into 3GP file headers and is ready to be disposed when sl@0: C3GPCompose::Open returns. See Audio Properties Classes. sl@0: If aAudio is NULL, video-only file will be composed. sl@0: @param aFile File handle of the file to save the data to. E3GPMetaDataLast needs to be set for sl@0: aFlags when this is used. sl@0: @param aFlags Optional flags for composing preferences. Refer to T3GPComposeFlag for supported flags. sl@0: The combined use of flags is acceptable. For example: sl@0: E3GPLongClip | E3GPMetaDataLast sl@0: sl@0: @return KErrNone if successful. Otherwise, returns one of the system wide error codes. sl@0: KErrGeneral if an error has no specific categorisation; sl@0: KErrNoMemory if an attempt to allocate memory has failed; sl@0: KErrArgument if neither video nor audio stream is specified; sl@0: KErrAccessDenied if opening file has failed; sl@0: KErrInUse if the composer is currently engaged; C3GPCompose::Complete must be called to sl@0: finish the current composition before the composer can be re-initialised again. sl@0: */ sl@0: EXPORT_C TInt C3GPCompose::Open(T3GPFileFormatType aFileFormat, sl@0: const T3GPVideoPropertiesBase* aVideo, sl@0: const T3GPAudioPropertiesBase* aAudio, sl@0: RFile64& aFile, sl@0: TUint aFlags) sl@0: { sl@0: if (iHandler) sl@0: { sl@0: return KErrInUse; sl@0: } sl@0: if (!aVideo && !aAudio) sl@0: { sl@0: // if neither video nor audio is supplied sl@0: return KErrArgument; sl@0: } sl@0: sl@0: TInt driveNumber = EDriveA; sl@0: TDriveInfo driveInfo; sl@0: TInt err = aFile.Drive(driveNumber, driveInfo); sl@0: if (err == KErrNone) sl@0: { sl@0: MP4Err mp4Err = MP4ComposeOpenFileHandle64(&iHandler, &aFile, static_cast(driveNumber), Mp4Type(aVideo, aAudio)); sl@0: if (mp4Err == MP4_OK) sl@0: { sl@0: // Write audio and video properties to the 3GP file sl@0: err = SetComposeProperties(aVideo, aAudio, aFileFormat, aFlags); sl@0: if (err != KErrNone) sl@0: { sl@0: Complete(); // Ingore the error sl@0: } sl@0: } sl@0: else sl@0: { sl@0: err = SymbianOSError(mp4Err); sl@0: } sl@0: } sl@0: sl@0: return err; sl@0: } sl@0: sl@0: /** sl@0: Destructor sl@0: Deletes all objects and releases all resource owned by this instance. sl@0: */ sl@0: EXPORT_C C3GPCompose::~C3GPCompose() sl@0: { sl@0: Complete(); // Ignore the error sl@0: } sl@0: sl@0: /** sl@0: This function completes the composing operation. It frees the memory allocated by the library instance sl@0: and closes the output file. sl@0: sl@0: It is necessary to call this function before the output file is guaranteed to be a valid output file sl@0: even though the file may exist prior to the call. sl@0: sl@0: The composer can be reused again after this call, following another call to C3GPCompose::Open to sl@0: re-initialise the composer. sl@0: sl@0: If C3GPCompose::Complete is called before the composer is initialised, it will be ignored and KErrNone sl@0: is returned. sl@0: sl@0: Although during destruction of C3GPCompose, this function will be automatically called, and no error sl@0: code will be returned. Therefore, when destroying the Composer object that you have used to compose a sl@0: file, you should ensure that data is committed to the file by invoking C3GPCompose::Complete before sl@0: destroying the Composer object. sl@0: sl@0: @return KErrNone if successful. Otherwise, returns one of the system wide error codes. sl@0: KErrGeneral if an error has no specific categorisation; sl@0: KErrWrite if metadata could not be written. sl@0: */ sl@0: EXPORT_C TInt C3GPCompose::Complete() sl@0: { sl@0: MP4Err mp4Err = MP4_OK; sl@0: if (iHandler) sl@0: { sl@0: mp4Err = MP4ComposeClose(iHandler); sl@0: } sl@0: // Always reset the class member data even this function returns error sl@0: Reset(); sl@0: return SymbianOSError(mp4Err); sl@0: } sl@0: sl@0: // Helper function to reset class member data. sl@0: void C3GPCompose::Reset() sl@0: { sl@0: iHasVideo = EFalse; sl@0: iHasAudio = EFalse; sl@0: iHandler = NULL; sl@0: iFile.Close(); sl@0: } sl@0: sl@0: /** sl@0: This function writes one video frame to the output file or buffer. sl@0: sl@0: The frames must be inserted according to their causal sequence. Because the library doesn't analyze sl@0: the video bit stream, frames are inserted into the 3GP file in the same order as they are entered sl@0: with this function. Therefore, the frames will not be retrieved from the resulting 3GP file or sl@0: buffer correctly if they are not in proper order. sl@0: sl@0: A frame inserted with this function call will result in one 3GP sample and one 3GP chunk. sl@0: sl@0: The current frame's dependency information which is using default values (E3GPDependencyUnknown & sl@0: E3GPRedundancyUnknown) is inserted. sl@0: sl@0: The data is available in the output file only after calling C3GPCompose::Complete. C3GPCompose::Complete sl@0: should be called exactly once when all audio and video data has been inserted into the library. sl@0: sl@0: @param aBuffer The descriptor containing the video frame data to be written. sl@0: @param aDuration Duration of video frame in timescale, see T3GPVideoPropertiesBase. sl@0: @param aKeyFrame ETrue to indicate whether this frame is a key frame. EFalse otherwise. sl@0: sl@0: @return KErrNone if successful. Otherwise, returns one of the system wide error codes. sl@0: KErrGeneral if an error has no specific categorisation; sl@0: KErrNotSupported if the composer is setup for an audio-only file; sl@0: KErrUnderflow if the supplied video frame buffer data is empty; sl@0: KErrNotReady if the composer has not yet been initialised; See C3GPCompose::Open. sl@0: */ sl@0: EXPORT_C TInt C3GPCompose::WriteVideoFrame(const TDesC8& aBuffer, TUint aDuration, TBool aKeyFrame) sl@0: { sl@0: if (!iHandler) sl@0: { sl@0: return KErrNotReady; sl@0: } sl@0: if (aBuffer.Length() <= 0) sl@0: { sl@0: return KErrUnderflow; sl@0: } sl@0: if (!iHasVideo) sl@0: { sl@0: return KErrNotSupported; sl@0: } sl@0: sl@0: // Insert video frame data sl@0: mp4_bool keyFrame = aKeyFrame; sl@0: mp4_u32 duration = aDuration; sl@0: MP4Err mp4Err = MP4ComposeWriteVideoFrame(iHandler, const_cast(aBuffer.Ptr()), aBuffer.Length(), duration, keyFrame); sl@0: sl@0: return SymbianOSError(mp4Err); sl@0: } sl@0: sl@0: /** sl@0: This function sets the current frame's dependency information to SDTP box and writes one video frame sl@0: to the output file. sl@0: sl@0: The frames must be inserted according to their causal sequence. Because the library doesn't analyze sl@0: the video bit stream, frames are inserted into the 3GP file in the same order as they are entered sl@0: with this function. Therefore, the frames will not be retrieved from the resulting 3GP file or sl@0: buffer correctly if they are not in proper order. sl@0: sl@0: A frame inserted with this function call will result in one 3GP sample and one 3GP chunk. sl@0: sl@0: The data is available in the output file only after calling C3GPCompose::Complete. C3GPCompose::Complete sl@0: should be called exactly once when all audio and video data has been inserted into the library. sl@0: sl@0: @param aBuffer The descriptor containing the video frame data to be written. sl@0: @param aDuration Duration of video frame in timescale, see T3GPVideoPropertiesBase. sl@0: @param aKeyFrame ETrue to indicate whether this frame is a key frame. EFalse otherwise. sl@0: @param aDependencies This specifies the current frame's dependency information. sl@0: The information will be supplied into the SDTP box. sl@0: See T3GPFrameDependencies. sl@0: sl@0: @return KErrNone if successful. Otherwise, returns one of the system wide error codes. sl@0: KErrGeneral if an error has no specific categorisation; sl@0: KErrNotSupported if the composer is setup for an audio-only file; sl@0: KErrUnderflow if the supplied video frame buffer data is empty; sl@0: KErrNotReady if the composer has not yet been initialised; See C3GPCompose::Open. sl@0: */ sl@0: EXPORT_C TInt C3GPCompose::WriteVideoFrame(const TDesC8& aBuffer, TUint aDuration, TBool aKeyFrame, sl@0: const T3GPFrameDependencies& aDependencies) sl@0: { sl@0: if (!iHandler) sl@0: { sl@0: return KErrNotReady; sl@0: } sl@0: if (aBuffer.Length() <= 0) sl@0: { sl@0: return KErrUnderflow; sl@0: } sl@0: if (!iHasVideo) sl@0: { sl@0: return KErrNotSupported; sl@0: } sl@0: sl@0: // Insert the current frame's dependency information sl@0: MP4Err mp4Err = MP4ComposeWriteNextVideoFrameDependencies(iHandler, aDependencies.iDependsOn, sl@0: aDependencies.iIsDependedOn, aDependencies.iHasRedundancy); sl@0: sl@0: if (mp4Err == MP4_OK) sl@0: { sl@0: // Insert video frame data sl@0: mp4_bool keyFrame = aKeyFrame; sl@0: mp4_u32 duration = aDuration; sl@0: mp4Err = MP4ComposeWriteVideoFrame(iHandler, const_cast(aBuffer.Ptr()), sl@0: aBuffer.Length(), duration, keyFrame); sl@0: } sl@0: sl@0: return SymbianOSError(mp4Err); sl@0: } sl@0: sl@0: /** sl@0: This function writes audio frames into the output file or buffer. The data is available in the sl@0: 3GP output file only after calling C3GPCompose::Complete. C3GPCompose::Complete should be called exactly sl@0: once when all audio and video data has been inserted into the library. sl@0: sl@0: For MPEG-4 audio: sl@0: This function writes one MPEG audio frame to the 3GP file. sl@0: sl@0: For other audio types: sl@0: This function writes a number of audio frames to the 3GP file specified during composer setup sl@0: in the input parameter aAudio when calling C3GPCompose::Open. All audio frames inserted with sl@0: one function call will be placed inside one sample in the resulting file. sl@0: sl@0: Note: Only the last call can have a different number of frames if the number is less than sl@0: the number of frames per sample specified during composer setup. sl@0: sl@0: @see T3GPAudioPropertiesAmr sl@0: @see T3GPAudioPropertiesQcelp sl@0: sl@0: @param aBuffer The descriptor containing the audio data to be written. sl@0: @param aDuration Duration of audio frames in timescale, see T3GPAudioPropertiesBase sl@0: sl@0: @return KErrNone if successful. Otherwise, returns one of the system wide error codes. sl@0: KErrGeneral if an error has no specific categorisation; sl@0: KErrNotSupported if the composer is setup for a video-only file; sl@0: KErrUnderflow if the supplied audio frames buffer data is empty; sl@0: KErrNotReady if the composer has not yet been initialised; See C3GPCompose::Open. sl@0: */ sl@0: EXPORT_C TInt C3GPCompose::WriteAudioFrames(const TDesC8& aBuffer, TUint aDuration) sl@0: { sl@0: if (!iHandler) sl@0: { sl@0: return KErrNotReady; sl@0: } sl@0: if (aBuffer.Length() <= 0) sl@0: { sl@0: return KErrUnderflow; sl@0: } sl@0: if (!iHasAudio) sl@0: { sl@0: return KErrNotSupported; sl@0: } sl@0: sl@0: mp4_u32 duration = aDuration; sl@0: // use 0 for the number of frames since it is being ignored within the mp4 library implementation sl@0: MP4Err mp4Err = MP4ComposeWriteAudioFrames(iHandler, const_cast(aBuffer.Ptr()), sl@0: aBuffer.Length(), 0, duration); sl@0: sl@0: return SymbianOSError(mp4Err); sl@0: } sl@0: sl@0: /** sl@0: Writes a buffer containing whole atom to inside of user data atom (UDTA) defined in aLocation. sl@0: sl@0: The buffer should contain an atom of structure that conforms to the definition of a "full box" sl@0: as specified in ISO/IEC 14496-12:2003: "Information technology – Coding of audio-visual objects sl@0: – Part 12: ISO base media file format." sl@0: sl@0: For more information on user data atoms, see Section 8 – Asset Information of "3GPP TS 26.244 sl@0: version 6.1.0 – 3GP file format (Rel 6)." sl@0: sl@0: @param aLocation Specifies the location of user information to be written. Refer to sl@0: T3GPUdtaLocation for possible values. sl@0: @param aBuffer The descriptor containing the user information to write into file. sl@0: sl@0: @return KErrNone if successful. Otherwise, returns one of the system wide error codes. sl@0: KErrGeneral if an error has no specific categorisation; sl@0: KErrArgument if asked aLocation is invalid; sl@0: KErrUnderflow if the supplied buffer data is empty; sl@0: KErrNotSupported if specify video track user data but no video type is specified, sl@0: or specify audio track user data but no audio type is specified sl@0: KErrNoMemory if an attempt to allocate memory has failed; sl@0: KErrNotReady if the composer has not yet been initialised; See C3GPCompose::Open. sl@0: sl@0: @panic C3GPCompose KErrArgument if the location of user information is not of T3GPUdtaLocation. sl@0: */ sl@0: EXPORT_C TInt C3GPCompose::SetUserData(T3GPUdtaLocation aLocation, const TDesC8& aBuffer) sl@0: { sl@0: if (!iHandler) sl@0: { sl@0: return KErrNotReady; sl@0: } sl@0: if (aBuffer.Length() <= 0) sl@0: { sl@0: return KErrUnderflow; sl@0: } sl@0: if ((!iHasAudio && aLocation == E3GPUdtaAudioTrak) || (!iHasVideo && aLocation == E3GPUdtaVideoTrak)) sl@0: { sl@0: return KErrNotSupported; sl@0: } sl@0: sl@0: mp4_u8 location; sl@0: switch (aLocation) sl@0: { sl@0: case (E3GPUdtaMoov): sl@0: location = MP4_UDTA_MOOV; sl@0: break; sl@0: case (E3GPUdtaVideoTrak): sl@0: location = MP4_UDTA_VIDEOTRAK; sl@0: break; sl@0: case (E3GPUdtaAudioTrak): sl@0: location = MP4_UDTA_AUDIOTRAK; sl@0: break; sl@0: default: sl@0: Panic(KErrArgument); sl@0: break; sl@0: } sl@0: sl@0: mp4_u32 bufferSize = aBuffer.Length(); sl@0: MP4Err mp4Err = MP4ComposeSetUserDataAtom(iHandler, location, const_cast(aBuffer.Ptr()), bufferSize); sl@0: return SymbianOSError(mp4Err); sl@0: } sl@0: sl@0: // Helper function to convert 3GP/MP4 library specific error codes to system wide error codes sl@0: TInt C3GPCompose::SymbianOSError(MP4Err aError) sl@0: { sl@0: TInt error = KErrNone; sl@0: sl@0: switch (aError) sl@0: { sl@0: case (MP4_OK): sl@0: break; sl@0: case (MP4_ERROR): sl@0: error = KErrGeneral; sl@0: break; sl@0: case (MP4_OUT_OF_MEMORY): sl@0: error = KErrNoMemory; sl@0: break; sl@0: case (MP4_FILE_ERROR): sl@0: error = KErrAccessDenied; sl@0: break; sl@0: case (MP4_INVALID_TYPE): sl@0: error = KErrArgument; sl@0: break; sl@0: case (MP4_METADATA_ERROR): sl@0: error = KErrWrite; sl@0: break; sl@0: default: sl@0: Panic(KErrArgument); sl@0: } sl@0: return error; sl@0: } sl@0: sl@0: // Helper function to map 3GP enum type to MP4 audio and video type sl@0: mp4_u32 C3GPCompose::Mp4Type(const T3GPVideoPropertiesBase* aVideo, const T3GPAudioPropertiesBase* aAudio) sl@0: { sl@0: mp4_u32 videoType = MP4_TYPE_NONE; sl@0: mp4_u32 audioType = MP4_TYPE_NONE; sl@0: sl@0: if (aVideo) sl@0: { sl@0: iHasVideo = ETrue; sl@0: switch (aVideo->iType) sl@0: { sl@0: case (E3GPMpeg4Video): sl@0: videoType = MP4_TYPE_MPEG4_VIDEO; sl@0: break; sl@0: case (E3GPH263Profile0): sl@0: videoType = MP4_TYPE_H263_PROFILE_0; sl@0: break; sl@0: case (E3GPH263Profile3): sl@0: videoType = MP4_TYPE_H263_PROFILE_3; sl@0: break; sl@0: sl@0: /* sl@0: * NOTE: The underlying 3GP library does sl@0: * not differentiate between the various AVC sl@0: * profiles when composing. sl@0: * sl@0: * In all cases it will simply copy the data sl@0: * from the iDecoderSpecificInfo member of sl@0: * T3GPVideoPropertiesAvc into the 'avcC' atom. sl@0: * It does not do any checking of that data, so sl@0: * it is the API user's responsibility to ensure sl@0: * that it contains a valid AVCDecoderConfigurationRecord sl@0: * with the correct AVC profile and level. sl@0: * sl@0: * An interesting side-effect of this is that you can sl@0: * compose AVC data with arbitrary profiles even if they sl@0: * are not "supported" by this API. For example, as long sl@0: * as the AVCDecoderConfigurationRecord says there is sl@0: * High 10 profile data and the AVC data is of that profile sl@0: * then you will still end up with a valid file. sl@0: */ sl@0: case (E3GPAvcProfileBaseline): sl@0: videoType = MP4_TYPE_AVC_PROFILE_BASELINE; sl@0: break; sl@0: case (E3GPAvcProfileMain): sl@0: videoType = MP4_TYPE_AVC_PROFILE_MAIN; sl@0: break; sl@0: case (E3GPAvcProfileExtended): sl@0: videoType = MP4_TYPE_AVC_PROFILE_EXTENDED; sl@0: break; sl@0: case (E3GPAvcProfileHigh): sl@0: videoType = MP4_TYPE_AVC_PROFILE_HIGH; sl@0: break; sl@0: default: sl@0: Panic(KErrArgument); sl@0: } sl@0: } sl@0: sl@0: if(aAudio) sl@0: { sl@0: iHasAudio = ETrue; sl@0: switch (aAudio->iType) sl@0: { sl@0: case (E3GPMpeg4Audio): sl@0: audioType = MP4_TYPE_MPEG4_AUDIO; sl@0: break; sl@0: case (E3GPAmrNB): sl@0: audioType = MP4_TYPE_AMR_NB; sl@0: break; sl@0: case (E3GPAmrWB): sl@0: audioType = MP4_TYPE_AMR_WB; sl@0: break; sl@0: case (E3GPQcelp13K): sl@0: audioType = MP4_TYPE_QCELP_13K; sl@0: break; sl@0: default: sl@0: Panic(KErrArgument); sl@0: } sl@0: } sl@0: return (videoType | audioType); sl@0: } sl@0: sl@0: // Helper function to set compose properties sl@0: TInt C3GPCompose::SetComposeProperties(const T3GPVideoPropertiesBase* aVideo, sl@0: const T3GPAudioPropertiesBase* aAudio, T3GPFileFormatType aFileFormat, TUint aFlag) sl@0: { sl@0: mp4_u32 writeBufferSize = iMediaWriteBufferSize; sl@0: mp4_u32 writeBufferMaxCount = iWriteBufferMaxCount; sl@0: MP4Err mp4Err = MP4SetCustomFileBufferSizes(iHandler, writeBufferSize, writeBufferMaxCount, 0); sl@0: if ( mp4Err != MP4_OK) sl@0: { sl@0: return SymbianOSError(mp4Err); sl@0: } sl@0: sl@0: // Set compose flag before other MP4Compose functions sl@0: TInt err = SetComposeFlag(aFileFormat, aFlag); sl@0: if (err != KErrNone) sl@0: { sl@0: return err; sl@0: } sl@0: sl@0: if (aVideo) sl@0: { sl@0: switch (aVideo->iType) sl@0: { sl@0: case (E3GPMpeg4Video): sl@0: err = SetMPeg4VideoProperties(aVideo); sl@0: break; sl@0: case (E3GPAvcProfileBaseline): sl@0: case (E3GPAvcProfileMain): sl@0: case (E3GPAvcProfileExtended): sl@0: case (E3GPAvcProfileHigh): sl@0: err = SetAvcVideoProperties(aVideo); sl@0: break; sl@0: case (E3GPH263Profile0): sl@0: case (E3GPH263Profile3): sl@0: err = SetH263VideoProperties(aVideo); sl@0: break; sl@0: default: sl@0: Panic(KErrArgument); sl@0: } sl@0: } sl@0: if (err != KErrNone) sl@0: { sl@0: return err; sl@0: } sl@0: sl@0: if (aAudio) sl@0: { sl@0: switch (aAudio->iType) sl@0: { sl@0: case (E3GPMpeg4Audio): sl@0: err = SetMpeg4AudioProperties(aAudio); sl@0: break; sl@0: case (E3GPQcelp13K): sl@0: err = SetQcelpAudioProperties(aAudio); sl@0: break; sl@0: case (E3GPAmrNB): sl@0: case (E3GPAmrWB): sl@0: err = SetAmrAudioProperties(aAudio); sl@0: break; sl@0: default: sl@0: Panic(KErrArgument); sl@0: } sl@0: } sl@0: return err; sl@0: } sl@0: sl@0: // Inform the 3GP library about the MPeg4 video data sl@0: TInt C3GPCompose::SetMPeg4VideoProperties(const T3GPVideoPropertiesBase* aVideo) sl@0: { sl@0: const T3GPVideoPropertiesMpeg4Video* mpeg4Video = static_cast(aVideo); sl@0: sl@0: MP4Err err = MP4ComposeWriteVideoDecoderSpecificInfo(iHandler, const_cast(mpeg4Video->iDecoderSpecificInfo.Ptr()), sl@0: mpeg4Video->iDecoderSpecificInfo.Length()); sl@0: sl@0: if ( err == MP4_OK) sl@0: { sl@0: err = MP4ComposeAddVideoDescription(iHandler, mpeg4Video->iTimescale, sl@0: mpeg4Video->iSize.iWidth, mpeg4Video->iSize.iHeight, sl@0: mpeg4Video->iMaxBitRate, mpeg4Video->iAvgBitRate); sl@0: } sl@0: sl@0: return SymbianOSError(err); sl@0: } sl@0: sl@0: // Inform the 3GP library about the AVC video data sl@0: TInt C3GPCompose::SetAvcVideoProperties(const T3GPVideoPropertiesBase* aVideo) sl@0: { sl@0: const T3GPVideoPropertiesAvc* avcVideo = static_cast(aVideo); sl@0: sl@0: MP4Err err = MP4ComposeWriteVideoDecoderSpecificInfo(iHandler, const_cast(avcVideo->iDecoderSpecificInfo.Ptr()), sl@0: avcVideo->iDecoderSpecificInfo.Length ()); sl@0: sl@0: if ( err == MP4_OK) sl@0: { sl@0: // aMaxBitRate and aAvgBitRate are MPEG-4 video specific values. Set 0 for them sl@0: err = MP4ComposeAddVideoDescription(iHandler, avcVideo->iTimescale, sl@0: avcVideo->iSize.iWidth, avcVideo->iSize.iHeight, 0, 0); sl@0: } sl@0: sl@0: return SymbianOSError(err); sl@0: } sl@0: sl@0: // Inform the 3GP library about the H263 video data sl@0: TInt C3GPCompose::SetH263VideoProperties(const T3GPVideoPropertiesBase* aVideo) sl@0: { sl@0: // aMaxBitRate and aAvgBitRate are MPEG-4 video specific values. Set 0 for H263 video sl@0: MP4Err err = MP4ComposeAddVideoDescription(iHandler, aVideo->iTimescale, sl@0: aVideo->iSize.iWidth, aVideo->iSize.iHeight, 0, 0); sl@0: sl@0: if ( err == MP4_OK) sl@0: { sl@0: const T3GPVideoPropertiesH263* h263Video = static_cast(aVideo); sl@0: TVideoClipProperties properties; sl@0: properties.iH263Level = h263Video->iVideoLevel; sl@0: err = MP4ComposeSetVideoClipProperties(iHandler, properties); sl@0: } sl@0: sl@0: return SymbianOSError(err); sl@0: } sl@0: sl@0: // Inform the 3GP library about the MPeg4 audio data sl@0: TInt C3GPCompose::SetMpeg4AudioProperties(const T3GPAudioPropertiesBase* aAudio) sl@0: { sl@0: const T3GPAudioPropertiesMpeg4Audio* mpeg4Audio = static_cast(aAudio); sl@0: sl@0: MP4Err err = MP4ComposeWriteAudioDecoderSpecificInfo(iHandler, const_cast(mpeg4Audio->iDecoderSpecificInfo.Ptr()), sl@0: mpeg4Audio->iDecoderSpecificInfo.Length ()); sl@0: sl@0: if ( err == MP4_OK) sl@0: { sl@0: //modeSet is needed only for AMR audio. Set it to 0 for Mpeg4 audio. sl@0: err = MP4ComposeAddAudioDescription(iHandler, mpeg4Audio->iTimescale, mpeg4Audio->iFramesPerSample, 0); sl@0: } sl@0: sl@0: return SymbianOSError(err); sl@0: } sl@0: sl@0: // Inform the 3GP library about the Amr audio data sl@0: TInt C3GPCompose::SetAmrAudioProperties(const T3GPAudioPropertiesBase* aAudio) sl@0: { sl@0: const T3GPAudioPropertiesAmr* amrAudio = static_cast(aAudio); sl@0: //modeSet is needed only for AMR audio. sl@0: MP4Err err = MP4ComposeAddAudioDescription(iHandler, amrAudio->iTimescale, sl@0: amrAudio->iFramesPerSample, amrAudio->iModeSet); sl@0: return SymbianOSError(err); sl@0: } sl@0: sl@0: // Sets the storage mode of storing 13K QCELP data in a 3G2 file sl@0: TInt C3GPCompose::SetQcelpAudioProperties(const T3GPAudioPropertiesBase* aAudio) sl@0: { sl@0: const T3GPAudioPropertiesQcelp* qcelpAudio = static_cast(aAudio); sl@0: MP4Err err = MP4ComposeSetQCELPStorageMode(iHandler, qcelpAudio->iMode); sl@0: if ( err == MP4_OK) sl@0: { sl@0: if ( qcelpAudio->iMode == E3GPMP4AudioDescriptionBox) sl@0: { sl@0: err = MP4ComposeWriteAudioDecoderSpecificInfo(iHandler, const_cast(qcelpAudio->iDecoderSpecificInfo.Ptr()), qcelpAudio->iDecoderSpecificInfo.Length ()); sl@0: } sl@0: if ( err == MP4_OK) sl@0: { sl@0: //modeSet is needed only for AMR audio. Set it to 0 for Qcelp audio. sl@0: err = MP4ComposeAddAudioDescription(iHandler, qcelpAudio->iTimescale, qcelpAudio->iFramesPerSample, 0); sl@0: } sl@0: } sl@0: sl@0: return SymbianOSError(err); sl@0: } sl@0: sl@0: // Set compose flag sl@0: TInt C3GPCompose::SetComposeFlag(T3GPFileFormatType aFileFormat, TUint aFlag) sl@0: { sl@0: mp4_u32 fileFormat = 0; sl@0: switch (aFileFormat) sl@0: { sl@0: case (E3GPMP4): sl@0: fileFormat = MP4_FLAG_GENERATE_MP4; sl@0: break; sl@0: case (E3GP3G2): sl@0: fileFormat = MP4_FLAG_GENERATE_3G2; sl@0: break; sl@0: case (E3GP3GP): sl@0: fileFormat = MP4_FLAG_NONE; sl@0: break; sl@0: default: sl@0: Panic(KErrArgument); sl@0: } sl@0: sl@0: MP4Err err = MP4ComposeSetFlags(iHandler, aFlag | fileFormat); sl@0: return SymbianOSError(err); sl@0: } sl@0: sl@0: sl@0: void C3GPCompose::Panic(TInt aPanic) sl@0: // Panic client sl@0: { sl@0: User::Panic(K3GPComposePanicName, aPanic); sl@0: }