diff -r 000000000000 -r 951a5db380a0 videoeditorengine/vedengine/videoprocessor/src/mp4composer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/videoeditorengine/vedengine/videoprocessor/src/mp4composer.cpp Fri Jan 29 14:08:33 2010 +0200 @@ -0,0 +1,796 @@ +/* +* Copyright (c) 2010 Ixonos Plc. +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of the "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: +* Ixonos Plc +* +* Description: +* +*/ + + +//FC Based on Cmp4parser + + +// INCLUDE FILES +#include +#include "mp4composer.h" +#include "vedvideosettings.h" +#include "vedavcedit.h" + + +// ASSERTIONS +//#define MPASSERT(x) __ASSERT_DEBUG(x, User::Panic(_L("CVideoProcessorImpl"), EInternalAssertionFailure)) + + +// MACROS +//#define ?macro ?macro_def + +#ifdef _DEBUG +#include +#define PRINT(x) RDebug::Print x; +#else +#define PRINT(x) +#endif + +// LOCAL CONSTANTS AND MACROS + +const TUint KFreeDiskSpaceCounter = 10; // Interval when to find out real free disk space +const TUint KMaxComposeBufferSize = 512000; // : Adjust buffer size dynamically + +#ifdef _DEBUG +const TInt KLeaveCode = CComposer::EComposerFailure; +#else +const TInt KLeaveCode = KErrGeneral; +#endif + +// ============================= LOCAL FUNCTIONS =============================== + +// ----------------------------------------------------------------------------- +// RateIndex: converts sample rate to rate index used in decoder config info +// ----------------------------------------------------------------------------- +// +static TUint8 RateIndex(TInt aRate) + { + switch ( aRate ) + { + case 96000: + return 0x0; + case 88200: + return 0x1; + case 64000: + return 0x2; + case 48000: + return 0x3; + case 44100: + return 0x4; + case 32000: + return 0x5; + case 24000: + return 0x6; + case 22050: + return 0x7; + case 16000: + return 0x8; + case 12000: + return 0x9; + case 11025: + return 0xa; + case 8000: + return 0xb; + case 7350: + return 0xc; + default: + return 0xf; + } + } + + +// ================= MEMBER FUNCTIONS ======================= + +// C++ default constructor can NOT contain any code, that +// might leave. +// + +CMP4Composer::CMP4Composer() +{ + iMP4Handle = 0; + iFreeDiskSpace = 0; + iFreeDiskSpaceCounter = 0; + iFirstWrite = ETrue; + iFsOpened = EFalse; +} + +// Two-phased constructor. +CMP4Composer* CMP4Composer::NewL(const TDesC &aFileName, + CParser::TVideoFormat aVideoFormat, + CParser::TAudioFormat aAudioFormat, + CVedAVCEdit *aAvcEdit) +{ + CMP4Composer *self = new (ELeave) CMP4Composer; + CleanupStack::PushL(self); + self->ConstructL(aFileName, aVideoFormat, aAudioFormat, aAvcEdit); + CleanupStack::Pop(); // self + return self; +} + +CMP4Composer* CMP4Composer::NewL(RFile* aFileHandle, + CParser::TVideoFormat aVideoFormat, + CParser::TAudioFormat aAudioFormat, + CVedAVCEdit *aAvcEdit) +{ + CMP4Composer *self = new (ELeave) CMP4Composer; + CleanupStack::PushL(self); + self->ConstructL(aFileHandle, aVideoFormat, aAudioFormat, aAvcEdit); + CleanupStack::Pop(); // self + return self; +} + +// Symbian OS default constructor can leave. +void CMP4Composer::ConstructL(const TDesC &aFileName, + CParser::TVideoFormat aVideoFormat, + CParser::TAudioFormat aAudioFormat, + CVedAVCEdit *aAvcEdit) +{ + TUint mediaType = 0; + + iOutputMovieFileName = TFileName(aFileName); + iFileHandle = NULL; + + iAvcEdit = aAvcEdit; + + // open MP4 library + + TBuf<258> temp(aFileName); + temp.ZeroTerminate(); + + MP4FileName name = reinterpret_cast( const_cast(temp.Ptr()) ); + + SetMediaOptions(aVideoFormat, aAudioFormat, mediaType); + + MP4Err error; + + // if the filename length is greater 0, then compose to file else buffer + if(aFileName.Length() > 0) + { + error = MP4ComposeOpen(&iMP4Handle, name, mediaType); + } + else + { + // initialize to compose to buffer + iComposeBuffer = (TUint8*)HBufC::NewL( KMaxComposeBufferSize ); + iComposedSize = KMaxComposeBufferSize; + error = MP4ComposeOpenToBuffer(&iMP4Handle, mediaType,(mp4_u8*)iComposeBuffer,&iComposedSize); + } + + if ( error != MP4_OK ) + User::Leave(KLeaveCode); + + SetComposerOptionsL(aVideoFormat, aAudioFormat); + +} + +void CMP4Composer::ConstructL(RFile* aFileHandle, + CParser::TVideoFormat aVideoFormat, + CParser::TAudioFormat aAudioFormat, + CVedAVCEdit *aAvcEdit) +{ + TUint mediaType = 0; + + iOutputMovieFileName.Zero(); + iFileHandle = aFileHandle; + + iAvcEdit = aAvcEdit; + + SetMediaOptions(aVideoFormat, aAudioFormat, mediaType); + + // open MP4 library + MP4Err error; + + error = MP4ComposeOpenFileHandle(&iMP4Handle, aFileHandle, EDriveC, mediaType); + + if ( error != MP4_OK ) + User::Leave(KLeaveCode); + + SetComposerOptionsL(aVideoFormat, aAudioFormat); + +} + + +void CMP4Composer::SetMediaOptions(CParser::TVideoFormat aVideoFormat, + CParser::TAudioFormat aAudioFormat, + TUint& aMediaFlags) + +{ + + if ( (aVideoFormat == CParser::EVideoFormatH263Profile0Level10) || + (aVideoFormat == CParser::EVideoFormatH263Profile0Level45) ) + { + aMediaFlags = MP4_TYPE_H263_PROFILE_0; + iVideoType = MP4_TYPE_H263_PROFILE_0; + } + +#ifdef VIDEOEDITORENGINE_AVC_EDITING + else if (aVideoFormat == CParser::EVideoFormatAVCProfileBaseline) + { + aMediaFlags = MP4_TYPE_AVC_PROFILE_BASELINE; + iVideoType = MP4_TYPE_AVC_PROFILE_BASELINE; + } +#endif + else + { + aMediaFlags = MP4_TYPE_MPEG4_VIDEO; + iVideoType = MP4_TYPE_MPEG4_VIDEO; + } + + if ( aAudioFormat == CParser::EAudioFormatAMR ) + { + aMediaFlags |= MP4_TYPE_AMR_NB; + iAudioType = MP4_TYPE_AMR_NB; + } + + else if ( aAudioFormat == CParser::EAudioFormatAAC ) + { + aMediaFlags |= MP4_TYPE_MPEG4_AUDIO; // added for AAC support. + iAudioType = MP4_TYPE_MPEG4_AUDIO; + } + +} + +void CMP4Composer::SetComposerOptionsL(CParser::TVideoFormat aVideoFormat, + CParser::TAudioFormat aAudioFormat) +{ + + MP4Err error; + + TBool videoMpeg4OrAvc = ( aVideoFormat == CParser::EVideoFormatMPEG4 || + aVideoFormat == CParser::EVideoFormatAVCProfileBaseline ); + + mp4_u32 flags = 0; + flags |= MP4_FLAG_LARGEFILEBUFFER; // Note: What does this do when using RFile ? + flags |= MP4_FLAG_METADATALAST; + + // generate MP4 file format if MPEG-4/AVC video & AAC audio + if ( (videoMpeg4OrAvc && aAudioFormat == CParser::EAudioFormatAAC) || + (videoMpeg4OrAvc && aAudioFormat == CParser::EAudioFormatNone) || + (aVideoFormat == CParser::EVideoFormatNone && aAudioFormat == CParser::EAudioFormatAAC) ) + { + flags |= MP4_FLAG_GENERATE_MP4; + } + + error = MP4ComposeSetFlags(iMP4Handle, flags); + + if (error != MP4_OK) + if (error == MP4_OUT_OF_MEMORY) + { + User::LeaveNoMemory(); + } + else + { + User::Leave(KLeaveCode); + } + + // set buffer sizes; only composer buffer settings are effective for this instance + error = MP4SetCustomFileBufferSizes(iMP4Handle, K3gpMp4ComposerWriteBufferSize, K3gpMp4ComposerNrOfWriteBuffers, K3gpMp4ParserReadBufferSize ); + + if (error == MP4_OUT_OF_MEMORY) + { + User::Leave(KErrNoMemory); + } + else if ( error != MP4_OK ) + { + User::Leave(KLeaveCode); + } +} + + +// Destructor +CMP4Composer::~CMP4Composer() +{ + if (iMP4Handle) + { + MP4ComposeClose(iMP4Handle); + } + + if(iComposeBuffer) // added for Buffer support + { + User::Free(iComposeBuffer); + iComposeBuffer=0; + } + + iMP4Handle = 0; + + if (iFsOpened) + { + iFS.Close(); + iFsOpened = EFalse; + } + + +} + +// --------------------------------------------------------- +// CMP4Composer::WriteFrames +// Write the next frame(s) to file +// --------------------------------------------------------- +// +TInt CMP4Composer::WriteFrames(TDesC8& aSrcBuffer, TInt aFrameSize, + TInt aDuration, TInt aKeyFrame, + TInt aNumberOfFrames, TInt aFrameType) +{ + MP4Err error = KErrNone; + + // get the parameters + TUint32 frameSize = aFrameSize; + TUint32 duration = aDuration; + mp4_bool keyframe = (mp4_bool)aKeyFrame; + TInt numberOfFrames = aNumberOfFrames; + TFrameType frameType = (aFrameType>0 ? (aFrameType==1 ? EFrameTypeAudio : EFrameTypeVideo) + : EFrameTypeNone); + + if (frameType == EFrameTypeVideo) + { + error = MP4ComposeWriteVideoFrame(iMP4Handle, (mp4_u8*)(aSrcBuffer.Ptr()), + frameSize, duration, keyframe); + if ( error != MP4_OK ) + return KLeaveCode; + } + + else if (frameType == EFrameTypeAudio) + { + error = MP4ComposeWriteAudioFrames(iMP4Handle,(mp4_u8*)(aSrcBuffer.Ptr()), + frameSize, numberOfFrames, duration); + if ( error != MP4_OK ) + return KLeaveCode; + } + else + User::Panic(_L("CMovieProcessorImpl"), EComposerFailure); + + return KErrNone; + +} + + +TInt CMP4Composer::WriteFrames(TDesC8& aSrcBuffer, TInt aFrameSize, + TInt aDuration, TInt aKeyFrame, + TInt aNumberOfFrames, TInt aFrameType, + TInt& aMP4Size, TBool aModeChanged, + TBool aFirstFrameOfClip, TInt aMode, TBool /*aFromEncoder*/) +{ + MP4Err error = KErrNone; + + // get the parameters + TUint32 frameSize = aFrameSize; + TUint32 duration = aDuration; + mp4_bool keyframe = ( aKeyFrame ) ? ETrue : EFalse; + TInt numberOfFrames = aNumberOfFrames; + TFrameType frameType = (aFrameType > 0 ? (aFrameType == 1 ? EFrameTypeAudio : EFrameTypeVideo) + : EFrameTypeNone); + TUint8* dataPtr = (TUint8*)(aSrcBuffer.Ptr()); + TInt tmpSize = 0; + + // call this only for the first frame of every clip + if ( aFirstFrameOfClip && (iVideoType == MP4_TYPE_MPEG4_VIDEO) && (aMP4Size == 0)) + { + if ((tmpSize = GetMp4SpecificSize(aSrcBuffer,aModeChanged,aMode)) != 0) + aMP4Size = tmpSize; //This will be the new Mp4Size as it will be consider and since by reference it is maintained + } + + if (frameType == EFrameTypeVideo) + { + if ( iVideoType == MP4_TYPE_MPEG4_VIDEO ) + { + if ( iFirstWrite ) + { + // VOS + error = MP4ComposeWriteVideoDecoderSpecificInfo( iMP4Handle, + (mp4_u8*)(dataPtr/*aSrcBuffer.Ptr()*/), aMP4Size ); + iFirstWrite = EFalse; + + error = MP4ComposeWriteVideoFrame(iMP4Handle, (mp4_u8*)(dataPtr/*aSrcBuffer.Ptr()*/ + aMP4Size), + ( frameSize - aMP4Size ), duration, keyframe); + } + else + { + // for MPEG4 - check the first 32 bits to make sure we don't + // have VOS pre-pended to VOP data + if (dataPtr[0] == 0x00 && dataPtr[1] == 0x00 && dataPtr[2] == 0x01 && dataPtr[3] == 0xb0) + { // since intermediate Vos set to proper value + // Not Short Header may have User Data space problem with PSC + if ((tmpSize = GetMp4SpecificSize(aSrcBuffer,aModeChanged,aMode)) != 0) + aMP4Size = tmpSize; //This will be the new Mp4Size as it will be considered + dataPtr += aMP4Size; + frameSize -= aMP4Size; + } + + if (frameSize == 0) + return KErrWrite; + + error = MP4ComposeWriteVideoFrame(iMP4Handle, (mp4_u8*)(dataPtr), + frameSize, duration, keyframe); + } + } + +#ifdef VIDEOEDITORENGINE_AVC_EDITING + else if ( iVideoType == MP4_TYPE_AVC_PROFILE_BASELINE ) + { + + if ( iFirstWrite || aFirstFrameOfClip) + { + if(iFirstWrite) + { + iFrameNumber = 0; + iFirstWrite = EFalse; + } + + aMP4Size = 0; + error = MP4ComposeWriteVideoFrame(iMP4Handle, (mp4_u8*)(dataPtr/*aSrcBuffer.Ptr()*/ + aMP4Size), + ( frameSize - aMP4Size ), duration, keyframe); + } + else + { + error = MP4ComposeWriteVideoFrame(iMP4Handle, (mp4_u8*)(dataPtr), + frameSize, duration, keyframe); + } + + iFrameNumber++; + } +#endif + + else // H.263 + { + error = MP4ComposeWriteVideoFrame(iMP4Handle, (mp4_u8*)(dataPtr/*aSrcBuffer.Ptr()*/), + frameSize, duration, keyframe); + + } + if ( error != MP4_OK ) + return KLeaveCode; + } + else if (frameType == EFrameTypeAudio) + { + error = MP4ComposeWriteAudioFrames(iMP4Handle,(mp4_u8*)(dataPtr/*aSrcBuffer.Ptr()*/), + frameSize, numberOfFrames, duration); + if ( error != MP4_OK ) + return KLeaveCode; + } + else + User::Panic(_L("CMovieProcessorImpl"), EComposerFailure); + + return KErrNone; +} + + + +// --------------------------------------------------------- +// CMP4Composer::ComposeHeaderL +// Get relevant stream parameters by calling mp4 library functions +// (other items were commented in a header). +// --------------------------------------------------------- +// + + +void CMP4Composer::ComposeHeaderL(CComposer::TStreamParameters& aStreamParameters, TInt aOutputVideoTimeScale, + TInt aOutputAudioTimeScale, TInt aAudioFramesInSample) +{ + MP4Err iError=KErrNone; + + TInt width = aStreamParameters.iVideoWidth; + TInt height = aStreamParameters.iVideoHeight; + TInt avgbitrate = aStreamParameters.iStreamBitrate; + TInt maxbitrate = avgbitrate; + TInt audioFramesPerSample = aAudioFramesInSample;//aStreamParameters.iAudioFramesInSample; + TInt audioTimescale = aOutputAudioTimeScale; + TInt modeSet=0x8180; + + if(iAudioType == MP4_TYPE_MPEG4_AUDIO) // added for AAC support + { + // reset for AAC audio according to the code sent for AAC support. + audioFramesPerSample = 0; + modeSet = 0; + } + + // set this to first clip's time scale + TInt timescale = aOutputVideoTimeScale; + + iError = MP4ComposeAddAudioDescription(iMP4Handle,(mp4_u32)audioTimescale, + (mp4_u8)audioFramesPerSample,(mp4_u16)modeSet); + if (iError != MP4_OK) + User::Leave(KLeaveCode); + + // write video description + iError = MP4ComposeAddVideoDescription(iMP4Handle,(mp4_u32)timescale, + (mp4_u16)width,(mp4_u16)height,(mp4_u32)maxbitrate,(mp4_u32)avgbitrate); + + if (iError != MP4_OK) + User::Leave(KLeaveCode); + + if ( aStreamParameters.iVideoFormat == EVideoFormatH263Profile0Level10 ) + { + TVideoClipProperties prop; + prop.iH263Level = 10; + MP4ComposeSetVideoClipProperties(iMP4Handle, prop); + } + + else if ( aStreamParameters.iVideoFormat == EVideoFormatH263Profile0Level45 ) + { + TVideoClipProperties prop; + prop.iH263Level = 45; + MP4ComposeSetVideoClipProperties(iMP4Handle, prop); + } + + if (!iFsOpened) // Check if file server is open already + { + User::LeaveIfError(iFS.Connect()); + iFsOpened = ETrue; + } + + if (iFileHandle == 0) + { + // get target drive number + TParse fp; + User::LeaveIfError(iFS.Parse(iOutputMovieFileName, fp)); + TPtrC driveletter = fp.Drive(); + TChar drl = driveletter[0]; + User::LeaveIfError(RFs::CharToDrive(drl, iDriveNumber)); + } + else + { + // get target drive number + TDriveInfo info; + TInt error = iFileHandle->Drive(iDriveNumber, info); + } +} + +// ----------------------------------------------------------------------------- +// CMP4Composer::DriveFreeSpaceL +// Calculate free space on a drive in bytes. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TInt64 CMP4Composer::DriveFreeSpaceL() +{ + TVolumeInfo volumeinfo; + + if (iFreeDiskSpaceCounter % KFreeDiskSpaceCounter == 0) + { + User::LeaveIfError(iFS.Volume(volumeinfo, iDriveNumber)); + iFreeDiskSpace = volumeinfo.iFree; + } + + iFreeDiskSpaceCounter++; + return iFreeDiskSpace; + +} + +// ----------------------------------------------------------------------------- +// CMP4Composer::GetComposedBuffer New Function added for Buffer support +// Gets the Composed buffer from the composer +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TUint8* CMP4Composer::GetComposedBuffer() +{ + return iComposeBuffer; +} + +// ----------------------------------------------------------------------------- +// CMP4Composer::GetComposedBufferSize New Function added for Buffer support +// Gets the Composed buffer size from the composer +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TUint CMP4Composer::GetComposedBufferSize() +{ + return iComposedSize; +} + +// --------------------------------------------------------- +// CMP4Composer::Close +// Closes the composer instance & creates the output 3gp file +// (other items were commented in a header). +// --------------------------------------------------------- +// +TInt CMP4Composer::Close() +{ + +#ifdef VIDEOEDITORENGINE_AVC_EDITING + + if (iVideoType == MP4_TYPE_AVC_PROFILE_BASELINE) + { + HBufC8* AVCDCR = NULL; + TInt error; + + TRAP(error, AVCDCR = (HBufC8*) HBufC8::NewL(16384)); + if (error != KErrNone) + return error; + + TPtr8 ptr = AVCDCR->Des(); + + // Construct AVC Decoder Configuration Record from the SPS / PPS sets + TRAP(error, iAvcEdit->ConstructAVCDecoderConfigurationRecordL(ptr)); + if (error != KErrNone) + { + delete AVCDCR; + return error; + } + + // Pass the AVC Decoder Configuration Record to the 3GPMP4 library + MP4Err mp4Error = MP4ComposeWriteVideoDecoderSpecificInfo( iMP4Handle, + (mp4_u8*)(ptr.Ptr()), ptr.Length()); + + delete AVCDCR; + + if (mp4Error != MP4_OK) + return EComposerFailure; + } + +#endif + + MP4Err error = MP4ComposeClose(iMP4Handle); + + iMP4Handle = 0; + + if (error != MP4_OK) + return EComposerFailure; + + if (iFsOpened) + { + iFS.Close(); + iFsOpened = EFalse; + } + + return KErrNone; +} + + +// ----------------------------------------------------------------------------- +// CMP4Composer::GetMp4SpecificSize +// Gets the length of MPEG-4 decoder specific info in aSrcBuf +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TInt CMP4Composer::GetMp4SpecificSize(TDesC8& aSrcBuf,TBool aModeChange,TInt aStreamMode) +{ + TUint8* dtPtr = (TUint8*)(aSrcBuf.Ptr()); + TInt mp4size =0; + TInt bufSize; + TInt flag; + + if (!aModeChange && (aStreamMode == 2)) //2 indicates short header in VedCommon + { + bufSize = aSrcBuf.Size()-3; + for(TInt i=0;i>2)<<2; + if (dtPtr[i] == 0x00 && dtPtr[i+1] == 0x00 && flag == 0x80) // user data ???????????? + { + mp4size=i; + break; + } + } + } + else + { + bufSize = aSrcBuf.Size()-4; + for(TInt i=0;i(aSrcBuf->Ptr()); + error = MP4ComposeWriteAudioDecoderSpecificInfo(iMP4Handle, aSrcB, aSrcBuf->Size()); + + if (error != MP4_OK) + return KErrGeneral; + + return KErrNone; +} + +// --------------------------------------------------------- +// CMP4Composer::WriteAudioSpecificInfo +// Writes the Audio decoder Specific info required in case of AAC +// Decoder specific info is derived from samplerate and # of channels +// (other items were commented in a header). +// --------------------------------------------------------- +// +// added to Support AAC audio files +TInt CMP4Composer::WriteAudioSpecificInfo(TInt aSampleRate, TInt aNumChannels) +{ + TInt error = KErrNone; + + TUint8 data[2]; + + data[0] = 2<<3; // AAC-LC + TUint8 rate = RateIndex(aSampleRate); + data[0] |= rate>>1; + data[1] = TUint8(rate<<7); + data[1] |= TUint8(aNumChannels<<3); + + error = MP4ComposeWriteAudioDecoderSpecificInfo(iMP4Handle, data, 2); + + if (error != MP4_OK) + return KErrGeneral; + + return KErrNone; +} + + +// ----------------------------------------------------------------------------- +// CMP4Composer::GetAVCDecoderSpecificInfoSize +// Gets the length of AVC decoder specific info in aSrcBuf +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TInt CMP4Composer::GetAVCDecoderSpecificInfoSize(TDesC8& aSrcBuf) +{ + TUint8* srcPtr = (TUint8*)(aSrcBuf.Ptr()); + TInt skip = 0; +// TInt error = KErrNone; + + // skip 4 bytes for + // configVersion, profile, profile compatibility and Level + skip += 4; + + // skip 1 bytes for lengthSizeMinusOne + skip += 1; + + // skip 1 bytes for num of seq Param sets + TInt numOfSSP = 0x1F & srcPtr[skip]; + skip += 1; + + for (TInt i = 0; i < numOfSSP; i++) + { + TInt sspSize = srcPtr[skip]*256 + srcPtr[skip+1]; + skip += 2; + skip += sspSize; + } + + TInt numOfPSP = srcPtr[skip]; + skip += 1; + + for (TInt i = 0; i < numOfPSP; i++) + { + TInt pspSize = srcPtr[skip]*256 + srcPtr[skip+1]; + skip += 2; + skip += pspSize; + } + + return skip; +} + + + +// End of File