--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/videoeditorengine/vedengine/videoprocessor/src/mp4parser.cpp Fri Jan 29 14:08:33 2010 +0200
@@ -0,0 +1,1264 @@
+/*
+* 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:
+*
+*/
+
+
+
+// INCLUDE FILES
+
+#include "movieprocessorimpl.h"
+#include "mp4parser.h"
+#include "vedvideosettings.h"
+#include "vedaudiosettings.h"
+#include "vedvolreader.h"
+#include "vedavcedit.h"
+
+// ASSERTIONS
+#define MPASSERT(x) __ASSERT_DEBUG(x, User::Panic(_L("CMovieProcessorImpl"), EInternalAssertionFailure))
+
+#ifdef _DEBUG
+#include <e32svr.h>
+#define PRINT(x) RDebug::Print x;
+#else
+#define PRINT(x)
+#endif
+
+// LOCAL CONSTANTS AND MACROS
+#ifdef _DEBUG
+const TInt KErrorCode = CParser::EParserFailure;
+#else
+const TInt KErrorCode = KErrGeneral;
+#endif
+
+//const TUint KNumFramesInOneRun = 10;
+const TUint KVOLHeaderBufferSize = 256;
+const TUint KAVCDCRBufferSize = 1024;
+const TUint KMinBitrate = 128;
+
+// ================= MEMBER FUNCTIONS =======================
+
+// C++ default constructor can NOT contain any code, that
+// might leave.
+//
+
+CMP4Parser::CMP4Parser()
+{
+ iMP4Handle = 0;
+ iVideoType = 0;
+ iAudioType = 0;
+ iBytesRead = 0;
+ iFirstRead = ETrue; // added for Mp4
+ iFirstFrameInfo = ETrue; // added for Mp4
+ iOutputNumberOfFrames = 0;
+ iStreamSource = ESourceNone;
+ iFrameNumber=0;
+ iFirstTimeClipParsing=ETrue;
+ iStartFrameIndex=0;
+}
+
+// Two-phased constructor.
+
+CMP4Parser* CMP4Parser::NewL(CMovieProcessorImpl* aProcessor, const TDesC &aFileName)
+{
+ CMP4Parser *self = new (ELeave) CMP4Parser;
+ CleanupStack::PushL(self);
+
+ if ( aFileName.Length() > 0 )
+ self->iStreamSource = ESourceFile;
+ else
+ self->iStreamSource = ESourceUser;
+
+ self->ConstructL(aProcessor,aFileName);
+
+ CleanupStack::Pop(); // self
+
+ return self;
+}
+
+CMP4Parser* CMP4Parser::NewL(CMovieProcessorImpl* aProcessor, RFile* aFileHandle)
+{
+ CMP4Parser *self = new (ELeave) CMP4Parser;
+ CleanupStack::PushL(self);
+
+ self->iStreamSource = ESourceFile;
+
+ self->ConstructL(aProcessor,aFileHandle);
+
+ CleanupStack::Pop(); // self
+
+ return self;
+}
+
+// Symbian OS default constructor can leave.
+
+void CMP4Parser::ConstructL(CMovieProcessorImpl* aProcessor, const TDesC &aFileName)
+{
+ MP4Err error;
+ iProcessor = aProcessor;
+
+ // open MP4 library
+ if ( iStreamSource == ESourceFile )
+ {
+ TBuf<258> temp(aFileName);
+ temp.ZeroTerminate();
+
+ MP4FileName name = reinterpret_cast<MP4FileName>( const_cast<TUint16*>(temp.Ptr()) );
+
+ error = MP4ParseOpen(&iMP4Handle, name);
+ if ( error == MP4_OK )
+ {
+ // set buffer sizes; only parser buffer size is effective for this instance
+ error = MP4SetCustomFileBufferSizes(iMP4Handle, K3gpMp4ComposerWriteBufferSize, K3gpMp4ComposerNrOfWriteBuffers, K3gpMp4ParserReadBufferSize );
+ }
+ }
+ else
+ {
+ // buffer
+ error = MP4ParseOpen(&iMP4Handle, 0);
+ }
+
+ if (error == MP4_OUT_OF_MEMORY)
+ {
+ User::Leave(KErrNoMemory);
+ }
+ else if ( error != MP4_OK )
+ {
+ User::Leave(KErrorCode);
+ }
+}
+
+void CMP4Parser::ConstructL(CMovieProcessorImpl* aProcessor, RFile* aFileHandle)
+{
+ MP4Err error;
+ iProcessor = aProcessor;
+
+ // open MP4 library
+ error = MP4ParseOpenFileHandle(&iMP4Handle, aFileHandle);
+
+ if ( error == MP4_OK )
+ {
+ // set buffer sizes; only parser buffer size is 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(KErrorCode);
+ }
+}
+
+// Destructor
+CMP4Parser::~CMP4Parser()
+{
+ if (iMP4Handle)
+ MP4ParseClose(iMP4Handle);
+
+ iMP4Handle = 0;
+}
+
+
+
+// ---------------------------------------------------------
+// CMP4Parser::WriteDataBlockL
+// Write a block of data to parser
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+
+TInt CMP4Parser::WriteDataBlock(const TDes8& aBlock)
+{
+
+ MPASSERT(iStreamSource != ESourceFile);
+ MP4Err error = MP4ParseWriteData(iMP4Handle, (mp4_u8*)(aBlock.Ptr()), mp4_u32(aBlock.Length()) );
+
+ if ( error == MP4_OUT_OF_MEMORY )
+ return KErrNoMemory;
+ else if ( error == MP4_ERROR )
+ return KErrorCode;
+ else
+ return KErrNone;
+
+}
+
+// ---------------------------------------------------------
+// CMP4Parser::GetNextFrameInformation
+// Get type (streaming-case), length and availability of next frame to be fetched
+// using MP4 library API functions
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+
+TInt CMP4Parser::GetNextFrameInformation(TFrameType& aType, TUint& aLength, TBool& aIsAvailable)
+{
+
+ // If aType == EFrameTypeNone, the type of next frame is retrieved
+ // (valid only in streaming case)
+ // Otherwise, only the length of next specified type of frame is retrieved
+ MPASSERT(iStreamSource != ESourceNone);
+
+ mp4_u32 type = MP4_TYPE_NONE;
+ MP4Err error;
+
+ aIsAvailable = 0;
+ if ( iNextFrameType == EFrameTypeNone )
+ // if the mp4 library is reading a file, a frame has always been read when
+ // we come here
+ // otherwise it might be that a complete frame was not available yet
+ // and we come here to ask again
+ {
+ if ( aType == EFrameTypeNone )
+ {
+ MPASSERT(iStreamSource == ESourceUser);
+
+ // get next frame type
+ error = MP4ParseNextFrameType(iMP4Handle, &type);
+
+ if ( error == MP4_NOT_AVAILABLE )
+ return KErrNone;
+ else if ( error == MP4_NO_FRAME )
+ return EParserEndOfStream; // no video or audio frames left, stream ended
+ else if ( error == MP4_INVALID_INPUT_STREAM )
+ return KErrCorrupt;
+ else if ( error != MP4_OK )
+ return KErrorCode;
+ else
+ {
+ MPASSERT(error == MP4_OK);
+ }
+
+ switch ( type )
+ {
+ case MP4_TYPE_H263_PROFILE_0:
+ case MP4_TYPE_MPEG4_VIDEO:
+
+ MPASSERT( type == iVideoType );
+ iNextFrameType = EFrameTypeVideo;
+ break;
+
+ case MP4_TYPE_AMR_NB:
+ MPASSERT( type == iAudioType );
+ iNextFrameType = EFrameTypeAudio;
+ break;
+
+ default:
+ return KErrNotSupported;
+ }
+ }
+ else
+ {
+ // library reads the file
+ //MPASSERT(iStreamSource == ESourceFile);
+ type = ( aType == EFrameTypeVideo ) ? iVideoType : iAudioType;
+ iNextFrameType = aType;
+ }
+
+ // get length for the frame
+ mp4_u32 length = 0, mp4Specific = 0;
+ MPASSERT( type != MP4_TYPE_NONE );
+ if ( (iFirstFrameInfo) &&
+ ((iVideoType == MP4_TYPE_MPEG4_VIDEO) || (iVideoType == MP4_TYPE_AVC_PROFILE_BASELINE)) )
+ {
+ error = MP4ParseReadVideoDecoderSpecificInfo( iMP4Handle, 0, 0, &mp4Specific );
+ iFirstFrameInfo = EFalse;
+ }
+ error = MP4ParseNextFrameSize(iMP4Handle, type, &length);
+ MPASSERT( error != MP4_NOT_AVAILABLE );
+
+ if ( length == 0 || error == MP4_NO_REQUESTED_FRAME )
+ {
+ // file-reading case: all frames of this type have been read
+ // and the caller should try with the other type
+ MPASSERT( length == 0 );
+ iNextFrameType = EFrameTypeNone;
+ aLength = 0;
+ return KErrNone;
+ }
+ else if ( error == MP4_INVALID_INPUT_STREAM )
+ return KErrCorrupt;
+ else if ( error != MP4_OK )
+ return KErrorCode;
+ else if ( length > iMaxVideoFrameLength )
+ {
+ PRINT((_L("CMP4Parser::GetNextFrameInformation() too large video frame size %d, return KErrCorrupt"),length));
+ return KErrCorrupt;
+ }
+ else
+ iNextFrameLength = TUint(length + mp4Specific);
+
+ }
+ MPASSERT(iNextFrameType != EFrameTypeNone);
+ MPASSERT(iNextFrameLength != 0);
+
+ // check if frame is available
+ if ( iStreamSource == ESourceUser )
+ {
+ error = MP4ParseIsFrameAvailable(iMP4Handle, type);
+ if ( error != MP4_OK && error != MP4_NOT_AVAILABLE )
+ return KErrorCode;
+ aIsAvailable = ( error == MP4_NOT_AVAILABLE ) ? EFalse : ETrue;
+ }
+ else
+ aIsAvailable = ETrue;
+
+ aType = iNextFrameType;
+ aLength = iNextFrameLength;
+
+ return KErrNone;
+}
+
+// ---------------------------------------------------------
+// CMP4Parser::ReadFrames
+// Read the next frame(s) from file / stream
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+
+TInt CMP4Parser::ReadFrames(TDes8& aDstBuffer, TFrameType aType, TUint32& aNumRead,
+ TUint32& aTimeStamp)
+{
+ MP4Err error;
+ mp4_u32 returnedSize = 0;
+ mp4_bool keyFrame = 0;
+
+ MPASSERT( iNextFrameType != EFrameTypeNone && aType == iNextFrameType );
+ MPASSERT( iNextFrameLength != 0 );
+
+#ifdef _DEBUG
+ mp4_u32 type = MP4_TYPE_NONE; // buffer support
+ type = ( aType == EFrameTypeVideo ) ? iVideoType : iAudioType; // buffer support
+ if (iStreamSource == ESourceUser)
+ MPASSERT( MP4ParseIsFrameAvailable(iMP4Handle, type) == MP4_OK );
+#endif
+
+
+ if (aType == EFrameTypeVideo)
+ {
+ TUint32 iTimeStampInTicks=0;
+ mp4_u32 mp4Specific = 0;
+
+ if ( (iFirstRead) &&
+ ((iVideoType == MP4_TYPE_MPEG4_VIDEO) || (iVideoType == MP4_TYPE_AVC_PROFILE_BASELINE)) )
+ {
+ error = MP4ParseReadVideoDecoderSpecificInfo( iMP4Handle, (mp4_u8*)(aDstBuffer.Ptr() + aDstBuffer.Length()),
+ mp4_u32( aDstBuffer.MaxLength() ), &mp4Specific );
+ iFirstRead = EFalse;
+ }
+
+ error = MP4ParseReadVideoFrame(iMP4Handle, (mp4_u8*)(aDstBuffer.Ptr() + aDstBuffer.Length()+ mp4Specific),
+ mp4_u32( aDstBuffer.MaxLength() ), &returnedSize, (mp4_u32*)&aTimeStamp,
+ &keyFrame, &iTimeStampInTicks);
+
+ returnedSize += mp4Specific;
+ iFrameNumber++;
+ aNumRead = 1;
+ }
+ else
+ {
+ error = MP4ParseReadAudioFrames(iMP4Handle, (mp4_u8*)(aDstBuffer.Ptr()),
+ mp4_u32(aDstBuffer.MaxLength()), &returnedSize, (mp4_u32*)&aTimeStamp,
+ (mp4_u32*)&aNumRead, NULL);
+
+ //PRINT((_L("audio TS:%d, "), aTimeStamp));
+ }
+
+ MPASSERT(error != MP4_BUFFER_TOO_SMALL);
+ aDstBuffer.SetLength(aDstBuffer.Length() + TInt(returnedSize));
+ iBytesRead += returnedSize;
+ iNextFrameType = EFrameTypeNone;
+ iNextFrameLength = 0;
+
+ //PRINT((_L("error=%d, numReturned=%d, returnedSize=%d, bufferSize=%d\n"),
+ // error, aNumRead, returnedSize, aDstBuffer.MaxLength()));
+
+ if (error == MP4_NOT_AVAILABLE)
+ return EParserNotEnoughData;
+ else if ( error == MP4_INVALID_INPUT_STREAM )
+ return KErrCorrupt;
+ else if ( error != MP4_OK )
+ return KErrorCode;
+ else
+ return KErrNone;
+}
+
+
+// ---------------------------------------------------------
+// CMP4Parser::Reset
+// Resets the parser to its initial state
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+
+TInt CMP4Parser::Reset()
+{
+ MP4Err error;
+
+ if ( iStreamSource == ESourceFile )
+ {
+ mp4_u32 audioPos, videoPos;
+
+ // seek to very beginning
+ error = MP4ParseSeek(iMP4Handle, 0, &audioPos, &videoPos, EFalse);
+ if ( error != MP4_OK )
+ return KErrorCode;
+
+ MPASSERT( videoPos == 0 && (iAudioType == 0 || audioPos == 0) );
+
+ }
+ else
+ {
+ // close & open library to make sure old data is flushed
+ error = MP4ParseClose(iMP4Handle);
+
+ if ( error != MP4_OK )
+ return KErrorCode;
+ error = MP4ParseOpen(&iMP4Handle, 0);
+ if ( error != MP4_OK )
+ return KErrorCode;
+ }
+
+ iBytesRead = 0;
+ iNextFrameType = EFrameTypeNone;
+ iNextFrameLength = 0;
+
+ return KErrNone;
+}
+
+
+// ---------------------------------------------------------
+// CMP4Parser::ParseHeaderL
+// Get relevant stream parameters by calling mp4 library functions
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+
+TInt CMP4Parser::ParseHeaderL(CParser::TStreamParameters& aStreamParameters)
+{
+
+ PRINT((_L("CMP4Parser::ParseHeaderL() begin")));
+
+ MP4Err error;
+ mp4_double frameRate = 0;
+ TBool hasVideo = ETrue;
+
+ // Reset channel info
+ aStreamParameters.iHaveVideo = EFalse;
+ aStreamParameters.iHaveAudio = EFalse;
+ aStreamParameters.iNumDemuxChannels = 0;
+ aStreamParameters.iFileFormat = EFileFormatUnrecognized;
+ aStreamParameters.iVideoFormat = EVideoFormatNone;
+ aStreamParameters.iAudioFormat = EAudioFormatNone;
+ aStreamParameters.iVideoLength = 0;
+ aStreamParameters.iAudioLength = 0;
+ aStreamParameters.iStreamLength = 0;
+ aStreamParameters.iAudioFramesInSample = 0;
+ aStreamParameters.iVideoPicturePeriodNsec = 0;
+ aStreamParameters.iCanSeek = EFalse;
+ aStreamParameters.iFrameRate=0;
+ aStreamParameters.iVideoTimeScale=0;
+ aStreamParameters.iAudioTimeScale=0;
+ iAudioType = 0;
+ iVideoType = 0;
+ iNumberOfFrames=0;
+
+ // get video description
+ error = MP4ParseRequestVideoDescription(iMP4Handle, (mp4_u32 *)&aStreamParameters.iVideoLength,
+ &frameRate, &iVideoType, (mp4_u32 *)&aStreamParameters.iVideoWidth,
+ (mp4_u32 *)&aStreamParameters.iVideoHeight, (mp4_u32 *)&aStreamParameters.iVideoTimeScale);
+
+ if ( error == MP4_NOT_AVAILABLE )
+ User::Leave(KErrorCode);
+ else if ( error == MP4_INVALID_INPUT_STREAM )
+ User::Leave(KErrCorrupt);
+ else if ( error == MP4_NO_VIDEO )
+ {
+ hasVideo = EFalse;
+ aStreamParameters.iVideoWidth = aStreamParameters.iVideoHeight = 0;
+ }
+ else if ( error != MP4_OK )
+ User::Leave(KErrorCode);
+ else
+ {
+ MPASSERT(error == MP4_OK);
+ }
+
+ // get audio description. ask also for averagebitrate to get error if the track is empty; the information is needed later on
+ mp4_u32 averagebitrate = 0;
+
+ error = MP4ParseRequestAudioDescription(iMP4Handle, (mp4_u32 *)&aStreamParameters.iAudioLength,
+ &iAudioType, (mp4_u8*)&aStreamParameters.iAudioFramesInSample, (mp4_u32 *)&aStreamParameters.iAudioTimeScale, &averagebitrate );
+
+ if ( (error == MP4_ERROR) && ((iAudioType == MP4_TYPE_MPEG4_AUDIO) || (iAudioType == MP4_TYPE_AMR_NB) || (iAudioType == MP4_TYPE_AMR_WB)))
+ {
+ // a special case: there may be audio track but it is empty; if type was recognized, mark as no audio
+ PRINT((_L("CMP4Parser::ParseHeaderL() problems with getting audio description, mark no audio since audio type was recognized")));
+ iAudioType = MP4_NO_AUDIO;
+ error = MP4_NO_AUDIO;
+ }
+ if(error == MP4_NOT_AVAILABLE)
+ User::Leave(EParserNotEnoughData);
+ else if ( error == MP4_INVALID_INPUT_STREAM )
+ User::Leave(KErrCorrupt);
+ else if ( error != MP4_NO_AUDIO && error != MP4_OK )
+ User::Leave(KErrorCode);
+ else
+ {
+ MPASSERT(error == MP4_OK || error == MP4_NO_AUDIO);
+ }
+
+ // store the sample size for sanity checking purposes
+ iMaxAMRSampleSize = KVedMaxAMRFrameSize * aStreamParameters.iAudioFramesInSample;
+
+ if (aStreamParameters.iVideoLength > 0)
+ aStreamParameters.iStreamLength = aStreamParameters.iVideoLength;
+
+ if (aStreamParameters.iAudioLength > aStreamParameters.iVideoLength)
+ aStreamParameters.iStreamLength = aStreamParameters.iAudioLength;
+
+ aStreamParameters.iFrameRate = frameRate;
+
+ if(hasVideo)
+ {
+
+ if ( iVideoType == MP4_TYPE_MPEG4_VIDEO )
+ {
+ // read video resolution from VOL header
+
+ HBufC8* tmpBuffer = (HBufC8*) HBufC8::NewLC(KVOLHeaderBufferSize);
+ TPtr8 tmpPtr = tmpBuffer->Des();
+ mp4_u32 volSize = 0;
+ MP4Err volError = 0;
+
+ volError = MP4ParseReadVideoDecoderSpecificInfo( iMP4Handle,
+ (mp4_u8*)(tmpPtr.Ptr()),
+ KVOLHeaderBufferSize,
+ &volSize );
+
+ if ( volError != MP4_OK )
+ {
+ User::Leave(KErrorCode);
+ }
+ tmpPtr.SetLength(volSize);
+
+ CVedVolReader* tmpVolReader = CVedVolReader::NewL();
+ CleanupStack::PushL(tmpVolReader);
+
+ tmpVolReader->ParseVolHeaderL(tmpPtr);
+
+ aStreamParameters.iVideoWidth = tmpVolReader->Width();
+ aStreamParameters.iVideoHeight = tmpVolReader->Height();
+
+ CleanupStack::PopAndDestroy(tmpVolReader);
+ CleanupStack::PopAndDestroy(tmpBuffer);
+
+ }
+
+ else if ( iVideoType == MP4_TYPE_AVC_PROFILE_BASELINE )
+ {
+ // read resolution from SPS
+
+ HBufC8* tmpBuffer = (HBufC8*) HBufC8::NewLC(KAVCDCRBufferSize);
+ TPtr8 ptr = tmpBuffer->Des();
+
+ // read decoder specific info
+ User::LeaveIfError( ReadAVCDecoderSpecificInfo(ptr) );
+
+ // create AVC editing instance
+ CVedAVCEdit* avcEdit = CVedAVCEdit::NewL();
+ CleanupStack::PushL(avcEdit);
+
+ TSize resolution(0,0);
+ User::LeaveIfError( avcEdit->GetResolution(ptr, resolution) );
+
+ CleanupStack::PopAndDestroy(avcEdit);
+ CleanupStack::PopAndDestroy(tmpBuffer);
+
+ aStreamParameters.iVideoWidth = resolution.iWidth;
+ aStreamParameters.iVideoHeight = resolution.iHeight;
+ }
+
+
+ iNumberOfFrames = GetNumberOfVideoFrames();
+ MPASSERT(iNumberOfFrames);
+
+ if (iFirstTimeClipParsing) // update only at the first parsing of a clip
+ {
+ // update the frame numbers
+
+ iOutputNumberOfFrames = iProcessor->GetOutputNumberOfFrames();
+
+ if (iOutputNumberOfFrames == 0)
+ {
+ iOutputNumberOfFrames += iNumberOfFrames; // total number of frames in all clips
+ }
+ else if (!iProcessor->IsThumbnailInProgress())
+ {
+ iOutputNumberOfFrames += iNumberOfFrames;
+ }
+ }
+
+ MPASSERT(frameRate > 0);
+ if (frameRate > 0)
+ aStreamParameters.iVideoPicturePeriodNsec = TInt64( TReal(1000000000) / TReal(frameRate) );
+ else
+ aStreamParameters.iVideoPicturePeriodNsec = TInt64(33366667);
+
+ if ( iVideoType == MP4_TYPE_H263_PROFILE_0 || iVideoType == MP4_TYPE_H263_PROFILE_3 )
+ {
+ TVideoClipProperties prop;
+ prop.iH263Level = 0;
+ MP4ParseGetVideoClipProperties(iMP4Handle, prop);
+
+ iMaxVideoFrameLength = KMaxCodedPictureSizeQCIF;
+ if (prop.iH263Level == 45)
+ {
+ aStreamParameters.iVideoFormat = EVideoFormatH263Profile0Level45;
+ }
+ else
+ {
+ aStreamParameters.iVideoFormat = EVideoFormatH263Profile0Level10;
+ }
+ }
+ else if ( iVideoType == MP4_TYPE_MPEG4_VIDEO )
+ {
+ aStreamParameters.iVideoFormat = EVideoFormatMPEG4;
+ if ( aStreamParameters.iVideoWidth <= KVedResolutionQCIF.iWidth )
+ {
+ iMaxVideoFrameLength = KMaxCodedPictureSizeMPEG4L0BQCIF;//distinction between L0 and L0B not possible here
+ }
+ else if (aStreamParameters.iVideoWidth <= KVedResolutionCIF.iWidth )
+ {
+ iMaxVideoFrameLength = KMaxCodedPictureSizeMPEG4CIF;
+ }
+ else
+ {
+ // VGA
+ iMaxVideoFrameLength = KMaxCodedPictureSizeVGA;
+ }
+ }
+
+ else if ( iVideoType == MP4_TYPE_AVC_PROFILE_BASELINE )
+ {
+
+ // : Is it possible to dig up the level here ??
+ aStreamParameters.iVideoFormat = EVideoFormatAVCProfileBaseline;
+
+ if ( aStreamParameters.iVideoWidth <= KVedResolutionQCIF.iWidth )
+ {
+ iMaxVideoFrameLength = KMaxCodedPictureSizeAVCLevel1B; //distinction between L0 and L0B not possible here ??
+ }
+ else if (aStreamParameters.iVideoWidth <= KVedResolutionCIF.iWidth )
+ {
+ iMaxVideoFrameLength = KMaxCodedPictureSizeAVCLevel1_2;
+ }
+ else
+ {
+ // default
+ iMaxVideoFrameLength = KMaxCodedPictureSizeAVCLevel1_2;
+ }
+ }
+ }
+
+ if ( error == MP4_OK )
+ {
+ // stream contains audio
+ if ( iAudioType == MP4_TYPE_AMR_NB )
+ aStreamParameters.iAudioFormat = EAudioFormatAMR;
+ else
+ {
+ if ( iAudioType == MP4_TYPE_MPEG4_AUDIO )
+ aStreamParameters.iAudioFormat = EAudioFormatAAC;
+ }
+ }
+
+ TBool videoMpeg4OrAVC = ( iVideoType == MP4_TYPE_MPEG4_VIDEO ||
+ iVideoType == MP4_TYPE_AVC_PROFILE_BASELINE );
+
+ if ( (videoMpeg4OrAVC && iAudioType == MP4_TYPE_MPEG4_AUDIO) ||
+ (videoMpeg4OrAVC && iAudioType == MP4_TYPE_NONE) ||
+ (iVideoType == MP4_TYPE_NONE && iAudioType == MP4_TYPE_MPEG4_AUDIO) )
+ aStreamParameters.iFileFormat = EFileFormatMP4;
+
+ else if (iVideoType != MP4_TYPE_NONE || iAudioType != MP4_TYPE_NONE)
+ aStreamParameters.iFileFormat = EFileFormat3GP;
+
+ if ( aStreamParameters.iStreamLength == 0 )
+ aStreamParameters.iFileFormat = EFileFormatUnrecognized;
+
+ if ( aStreamParameters.iVideoFormat != EVideoFormatNone )
+ {
+ aStreamParameters.iHaveVideo = ETrue;
+ aStreamParameters.iNumDemuxChannels++;
+ }
+
+ if ( aStreamParameters.iAudioFormat != EAudioFormatNone )
+ {
+ aStreamParameters.iHaveAudio = ETrue;
+ aStreamParameters.iNumDemuxChannels++;
+ }
+
+ aStreamParameters.iMaxPacketSize = 0; // N/A
+ aStreamParameters.iLogicalChannelNumberVideo = 0; // N/A
+ aStreamParameters.iLogicalChannelNumberAudio = 0; // N/A
+
+ // get stream description
+ error = MP4ParseRequestStreamDescription(iMP4Handle, (mp4_u32 *)&aStreamParameters.iStreamSize,
+ (mp4_u32 *)&aStreamParameters.iStreamBitrate);
+ if ( error != MP4_OK )
+ User::Leave(KErrorCode);
+
+ // do sanity-check for bitrate
+ if (aStreamParameters.iStreamBitrate < KMinBitrate)
+ User::Leave(KErrCorrupt);
+
+ aStreamParameters.iReferencePicturesNeeded = 0;
+ aStreamParameters.iNumScalabilityLayers = 0;
+
+ // determine if the stream contains INTRA frames so seeking is possible
+ // If the stream contains more than one INTRA frame, seeking is
+ // allowed.
+
+ if (hasVideo)
+ {
+ mp4_u32 position = aStreamParameters.iStreamLength + 1000;
+ mp4_u32 audioTime, videoTime;
+
+ // Seek past stream duration to find out the position of last INTRA frame
+ error = MP4ParseSeek(iMP4Handle, position, &audioTime, &videoTime, ETrue);
+ if ( error != MP4_OK )
+ User::Leave(KErrorCode);
+
+ if (videoTime != 0)
+ {
+ // at least two INTRAs
+ aStreamParameters.iCanSeek = ETrue;
+ }
+
+ // rewind file back to beginning
+ error = MP4ParseSeek(iMP4Handle, 0, &audioTime, &videoTime, EFalse);
+ if ( error != MP4_OK )
+ User::Leave(KErrorCode);
+ }
+
+ PRINT((_L("CMP4Parser::ParseHeaderL() end")));
+
+ return KErrNone;
+}
+
+
+// ---------------------------------------------------------
+// CMP4Parser::IsStreamable
+// Finds out whether input stream is multiplexed so that
+// it can be streamed, i.e. played while receiving the stream.
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+
+TInt CMP4Parser::IsStreamable()
+{
+ MP4Err error;
+
+ error = MP4ParseIsStreamable(iMP4Handle);
+ if ( error == MP4_NOT_AVAILABLE )
+ return EParserNotEnoughData;
+ else if ( error == MP4_INVALID_INPUT_STREAM )
+ return KErrNotSupported;
+ else if ( error == MP4_ERROR )
+ return KErrorCode;
+ else
+ return ( error == MP4_OK ) ? 1 : 0;
+}
+
+
+// ---------------------------------------------------------
+// CMP4Parser::Seek
+// Seeks the file to desired position in milliseconds
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+TInt CMP4Parser::Seek(TUint32 aPositionInMs, TUint32& anAudioTimeAfter, TUint32& aVideoTimeAfter)
+{
+
+ MP4Err error = MP4_OK;
+ MPASSERT(iStreamSource == ESourceFile);
+ error = MP4ParseSeek(iMP4Handle, aPositionInMs, &anAudioTimeAfter, &aVideoTimeAfter, ETrue);
+ if (error != MP4_OK)
+ return KErrorCode;
+
+ return KErrNone;
+}
+
+
+
+TInt CMP4Parser::GetNumberOfFrames()
+{
+ return iNumberOfFrames;
+}
+
+// ---------------------------------------------------------
+// CMP4Parser::SeekOptimalIntraFrame
+// Seeks to INTRA frame position before given time
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+TInt CMP4Parser::SeekOptimalIntraFrame(TTimeIntervalMicroSeconds aStartTime, TInt /*aIndex*/, TBool aFirstTime)
+{
+ MP4Err error = KErrNone;
+ TInt revisedNumberOfFrames = 0;
+ mp4_u32 audioTime = 0;
+ mp4_u32 videoTime = 0;
+// mp4_u32 timeScale = 0;
+
+ // calculate the start time of the cut operation
+ TInt64 startTime = aStartTime.Int64() / TInt64(1000);
+ mp4_u32 startPosition = (mp4_u32)( I64INT(startTime) ); // in milliseconds
+
+ TBool intraFound = (startPosition == 0);
+
+ if (!aFirstTime)
+ intraFound = 0;
+
+ // First check if the first included frame is intra
+ if (!intraFound)
+ {
+ // seek to previous frame preceding start time or at start time
+ error = MP4ParseSeek(iMP4Handle, startPosition, &audioTime, &videoTime, EFalse);
+ if (error != MP4_OK)
+ {
+ return KErrorCode;
+ }
+ MPASSERT(videoTime <= startPosition);
+
+ // get index of the frame
+ TInt index = iProcessor->GetVideoFrameIndex(TTimeIntervalMicroSeconds(videoTime*1000));
+
+ if (videoTime < startPosition)
+ {
+ // if there was no frame at start time, seek
+ // one frame forward to the first included frame
+ index++;
+ }
+
+ // get frame type
+ mp4_bool frameType;
+ error = MP4ParseGetVideoFrameType(iMP4Handle, index, &frameType);
+ if (error != MP4_OK)
+ {
+ return KErrorCode;
+ }
+ if (frameType == 1)
+ {
+ intraFound = ETrue;
+ iStartFrameIndex = index;
+
+ mp4_u32 timeInTicks;
+ // get timestamp of matched frame to startPosition
+ error = MP4ParseGetVideoFrameStartTime(iMP4Handle, index, &timeInTicks, &startPosition);
+ if (error != MP4_OK)
+ {
+ return KErrorCode;
+ }
+
+ // Now seek to found Intra in 1 ms increments. The loop is needed
+ // because due to rounding error, MP4Parser may seek to previous
+ // frame instead of the I-frame at startPosition
+ TInt seekTime = startPosition;
+ videoTime = 0;
+
+ while (videoTime != startPosition)
+ {
+ error = MP4ParseSeek(iMP4Handle, seekTime, &audioTime, &videoTime, EFalse);
+ if (error != MP4_OK)
+ {
+ return KErrorCode;
+ }
+ MPASSERT(videoTime <= startPosition);
+ seekTime++; // add 1 ms
+ }
+
+ }
+ }
+
+ if (!intraFound)
+ {
+ // seek to the I-frame preceding the start time
+ error = MP4ParseSeek(iMP4Handle, startPosition, &audioTime, &videoTime, ETrue);
+ if (error != MP4_OK)
+ {
+ return KErrorCode;
+ }
+ }
+
+ if (videoTime != 0)
+ {
+ if (!intraFound)
+ {
+ // get index of the intra frame
+ TInt64 time = TInt64(TUint(videoTime)) * TInt64(1000);
+ iStartFrameIndex = iProcessor->GetVideoFrameIndex(TTimeIntervalMicroSeconds( time ));
+ }
+
+ if (aFirstTime)
+ {
+ revisedNumberOfFrames = iNumberOfFrames - iStartFrameIndex;
+ // update movie and clip number of frames
+ iOutputNumberOfFrames -= iStartFrameIndex;
+ iNumberOfFrames = revisedNumberOfFrames;
+ }
+
+ PRINT((_L("CMP4Parser::SeekOptimalIntraFrame() revised = %d"),revisedNumberOfFrames));
+ PRINT((_L("CMP4Parser::SeekOptimalIntraFrame() iNumberOfFrames = %d"),iNumberOfFrames));
+ PRINT((_L("CMP4Parser::SeekOptimalIntraFrame() iStartFrameIndex = %d"),iStartFrameIndex));
+ PRINT((_L("CMP4Parser::SeekOptimalIntraFrame() iOutputNumberOfFrames = %d"),iOutputNumberOfFrames));
+ iFrameNumber = iStartFrameIndex;
+ }
+ return KErrNone;
+}
+
+// ---------------------------------------------------------
+// CMP4Parser::GetNumberOfVideoFrames
+// Gets the number of video frames in clip
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+TInt CMP4Parser::GetNumberOfVideoFrames()
+{
+ mp4_u32 numberOfFrames = 0;
+ MP4Err error = 0;
+
+ error = MP4ParseGetNumberOfVideoFrames(iMP4Handle, &numberOfFrames);
+
+ if (error != MP4_OK)
+ return 0;
+
+ return numberOfFrames;
+
+}
+
+// ---------------------------------------------------------
+// CMP4Parser::GetVideoFrameSize
+// Gets the size of video frame at given index
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+TInt CMP4Parser::GetVideoFrameSize(TInt aIndex)
+{
+ mp4_u32 frameSize = 0;
+ mp4_u32 mp4Specific = 0;
+ MP4Err error = 0;
+
+ if ( aIndex == 0 && iVideoType == MP4_TYPE_MPEG4_VIDEO )
+ {
+ error = MP4ParseReadVideoDecoderSpecificInfo( iMP4Handle, 0, 0, &mp4Specific );
+
+ if ( error != MP4_OK && error != MP4_BUFFER_TOO_SMALL )
+ return KErrorCode;
+ }
+
+ error = MP4ParseGetVideoFrameSize(iMP4Handle, aIndex, &frameSize);
+
+ if (error != MP4_OK)
+ return KErrorCode;
+
+ return frameSize + mp4Specific;
+
+}
+
+
+
+// ---------------------------------------------------------
+// CMP4Parser::GetVideoFrameStartTime
+// Returns frame start time in millisec - optional iTimestamp for start time in ticks
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+TInt CMP4Parser::GetVideoFrameStartTime(TInt aIndex, TInt* aTimeStampInTicks)
+{
+
+ MP4Err error = 0;
+ mp4_u32 timeStampInMs = 0;
+
+ MPASSERT(aTimeStampInTicks);
+
+ PRINT((_L("CMP4Parser::GetVideoFrameStartTime(), get time for index %d"), aIndex));
+ error = MP4ParseGetVideoFrameStartTime(iMP4Handle, aIndex, (mp4_u32*)aTimeStampInTicks, &timeStampInMs);
+
+ if (error != MP4_OK)
+ {
+ PRINT((_L("CMP4Parser::GetVideoFrameStartTime(), error from MP4 parser %d"), error));
+ return KErrorCode;
+ }
+
+ PRINT((_L("CMP4Parser::GetVideoFrameStartTime(), time in ms %d"), timeStampInMs));
+ return timeStampInMs;
+
+}
+
+
+// ---------------------------------------------------------
+// CMP4Parser::GetVideoFrameType
+// Gets the type of video frame at given index
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+TInt8 CMP4Parser::GetVideoFrameType(TInt aIndex)
+{
+
+ MP4Err error = 0;
+ mp4_bool frameType;
+
+ error = MP4ParseGetVideoFrameType(iMP4Handle, aIndex, &frameType);
+
+ if (error != MP4_OK)
+ return KErrGeneral;
+
+ return TInt8(frameType);
+}
+
+
+// ---------------------------------------------------------
+// CMP4Parser::GetVideoFrameProperties
+// Gets frame properties
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+TInt CMP4Parser::GetVideoFrameProperties(TFrameInfoParameters* aVideoFrameInfoArray,
+ TUint32 aStartIndex, TUint32 aSizeOfArray)
+{
+ MP4Err error;
+ error = MP4GetVideoFrameProperties(iMP4Handle,aStartIndex,aSizeOfArray,aVideoFrameInfoArray);
+
+ if (error != MP4_OK)
+ return KErrorCode;
+
+ return KErrNone;
+}
+
+
+// ---------------------------------------------------------
+// CMP4Parser::ParseAudioInfo
+// Gets the frame information (frame size) for audio in the current clip
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+TInt CMP4Parser::ParseAudioInfo(TInt& aAudioFrameSize)
+{
+ MP4Err error;
+ mp4_u32 audioLength=0;
+ mp4_u32 audioType=0;
+ mp4_u32 audioFramesInSample=0;
+ mp4_u32 audioTimeScale=0;
+ mp4_u32 avgBitRate=0;
+ mp4_double frameLength = 0.02; // 20 ms
+
+ error = MP4ParseRequestAudioDescription(iMP4Handle, (mp4_u32 *)&audioLength,
+ &audioType, (mp4_u8*)&audioFramesInSample, (mp4_u32 *)&audioTimeScale, (mp4_u32*)&avgBitRate);
+ aAudioFrameSize = ((mp4_u32)((mp4_double)avgBitRate*frameLength)>>3);
+
+ return error;
+}
+
+TInt CMP4Parser::GetMP4SpecificSize()
+{
+ MP4Err error = 0;
+ mp4_u32 mp4Specific = 0;
+ if ( iVideoType == MP4_TYPE_MPEG4_VIDEO )
+ {
+ error = MP4ParseReadVideoDecoderSpecificInfo( iMP4Handle, 0, 0, &mp4Specific );
+ if ( error != MP4_OK && error != MP4_BUFFER_TOO_SMALL )
+ return KErrGeneral;
+ }
+ return mp4Specific;
+}
+
+// ---------------------------------------------------------
+// CMP4Parser::ReadAudioDecoderSpecificInfoL
+// Gets the decoder specific info from the current file filled so that it can be composed to output file
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+TInt CMP4Parser::ReadAudioDecoderSpecificInfoL(HBufC8*& aBytes, TInt aBufferSize)
+{
+ aBytes = HBufC8::NewL(aBufferSize);
+ CleanupStack::PushL(aBytes);
+ mp4_u8 *buffer = new (ELeave) mp4_u8[aBufferSize];
+ mp4_u32 decspecinfosize = 0;
+
+ MP4Err err = MP4ParseReadAudioDecoderSpecificInfo(
+ iMP4Handle,
+ buffer,
+ aBufferSize,
+ &decspecinfosize);
+ if (err == MP4_OK)
+ {
+ for (TInt a = 0 ; a < (TInt)decspecinfosize ; a++)
+ {
+ aBytes->Des().Append(buffer[a]);
+ }
+ }
+ else
+ {
+ delete[] buffer;
+ buffer = 0;
+ CleanupStack::PopAndDestroy(aBytes);
+ aBytes = 0;
+ User::Leave(KErrGeneral);
+ }
+ delete[] buffer;
+ buffer = 0;
+ CleanupStack::Pop(aBytes);
+ return ETrue;
+}
+
+// ---------------------------------------------------------
+// CMP4Parser::SetDefaultAudioDecoderSpecificInfoL
+// Gets the default decoder specific info filled so that it can be composed to output file
+// the default info is for a 16 KHz, mono LC type AAC only
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+TInt CMP4Parser::SetDefaultAudioDecoderSpecificInfoL(HBufC8*& aBytes, TInt aBufferSize)
+{
+ aBytes = HBufC8::NewL(aBufferSize);
+ CleanupStack::PushL(aBytes);
+
+ const TUint8 frameDecSpecInfo[] = {0x14,0x08}; //the decoder specific
+ const TInt frameSize = 2; //constant as maximum size of decoderspecific info is 2
+
+ for (TInt a = 0 ; a < frameSize ; a++)
+ {
+ aBytes->Des().Append(frameDecSpecInfo[a]);
+ }
+ CleanupStack::Pop(aBytes);
+ return ETrue;
+}
+
+
+TInt CMP4Parser::GetAudioBitrate(TInt& aBitrate)
+{
+
+ mp4_u32 length;
+ mp4_u32 type;
+ mp4_u8 framesPerSample;
+ mp4_u32 timeScale;
+ mp4_u32 averageBitrate;
+
+ MP4Err error = 0;
+
+ error = MP4ParseRequestAudioDescription(iMP4Handle, &length,
+ &type, &framesPerSample, &timeScale, &averageBitrate);
+
+ if ( error != 0 )
+ return KErrGeneral;
+
+ aBitrate = averageBitrate;
+
+ return KErrNone;
+
+
+}
+
+TInt CMP4Parser::GetVideoFrameRate(TReal& aFrameRate)
+{
+
+ mp4_u32 length;
+ mp4_double frameRate;
+ mp4_u32 videoType;
+ mp4_u32 width;
+ mp4_u32 height;
+ mp4_u32 timeScale;
+
+ MP4Err error = 0;
+
+ // get video description
+ error = MP4ParseRequestVideoDescription(iMP4Handle, &length, &frameRate,
+ &videoType, &width, &height, &timeScale);
+
+ if ( error != 0 )
+ return KErrGeneral;
+
+ TReal temp = frameRate * 2.0;
+ TInt temp2 = TInt(temp + 0.5);
+
+ aFrameRate = temp2 / 2.0;
+
+ return KErrNone;
+}
+
+TInt CMP4Parser::GetDecoderSpecificInfoSize()
+{
+
+ MPASSERT(iMP4Handle);
+
+ mp4_u32 mp4Specific = 0;
+
+ MP4Err error = MP4ParseReadVideoDecoderSpecificInfo( iMP4Handle, 0, 0, &mp4Specific );
+
+ if ( error != MP4_OK && error != MP4_BUFFER_TOO_SMALL )
+ return KErrGeneral;
+
+ return mp4Specific;
+}
+
+TInt CMP4Parser::ReadAVCDecoderSpecificInfo(TDes8& buf)
+{
+
+ mp4_u32 mp4Specific = 0;
+
+ MP4Err error = MP4ParseReadVideoDecoderSpecificInfo( iMP4Handle, (mp4_u8*)(buf.Ptr()),
+ mp4_u32( buf.MaxLength() ), &mp4Specific );
+
+
+ if (error != MP4_OK)
+ return KErrorCode;
+
+ buf.SetLength(mp4Specific);
+
+ return KErrNone;
+
+}
+
+TInt CMP4Parser::GetVideoDuration(TInt& aDurationInMs)
+{
+
+ mp4_u32 length;
+ mp4_double frameRate;
+ mp4_u32 videoType;
+ mp4_u32 width;
+ mp4_u32 height;
+ mp4_u32 timeScale;
+
+ MP4Err error = 0;
+
+ // get video description
+ error = MP4ParseRequestVideoDescription(iMP4Handle, &length, &frameRate,
+ &videoType, &width, &height, &timeScale);
+
+ if ( error != MP4_OK )
+ return KErrGeneral;
+
+ aDurationInMs = length;
+
+ return KErrNone;
+
+}
+
+
+// End of File