diff -r 000000000000 -r 951a5db380a0 videoeditorengine/vedengine/videoprocessor/src/mp4parser.cpp --- /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 +#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( const_cast(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