sl@0: // Copyright (c) 2006-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 FILES sl@0: #include sl@0: #include sl@0: #include sl@0: #include <3gplibrary/mp4lib.h> sl@0: #include "mp4atom.h" sl@0: #include "mp4memwrap.h" sl@0: #include "asyncfileparser.h" sl@0: sl@0: // MACROS sl@0: // Debug print macro sl@0: #if defined(_DEBUG) && defined(_ASYNCFILEPARSERLOGGING) sl@0: #include sl@0: #define PRINT(x) sl@0: #else sl@0: #define PRINT(x) sl@0: #endif sl@0: sl@0: // ============================ MEMBER FUNCTIONS =============================== sl@0: sl@0: // ----------------------------------------------------------------------------- sl@0: // CFileAsyncParser::CFileAsyncParser sl@0: // C++ default constructor can NOT contain any code, that sl@0: // might leave. sl@0: // ----------------------------------------------------------------------------- sl@0: // sl@0: CFileAsyncParser::CFileAsyncParser() : CActive( EPriorityHigh ), iDiskBufferPointer(NULL,0) sl@0: { sl@0: sl@0: } sl@0: sl@0: // ----------------------------------------------------------------------------- sl@0: // CFileAsyncParser::ConstructL sl@0: // Symbian 2nd phase constructor can leave. sl@0: // ----------------------------------------------------------------------------- sl@0: // sl@0: void CFileAsyncParser::ConstructL( MP4HandleStruct* aHandle, RFile64& aFile ) sl@0: { sl@0: PRINT(_L("CFileAsyncParser::ConstructL() IN")); sl@0: iError = KErrNone; sl@0: iInputFile = &aFile; sl@0: iHandle = aHandle; sl@0: iAudioSize = 0; sl@0: iReturnedAudioFrames = 0; sl@0: iAudioTimeStamp = 0; sl@0: iAudioTimeStamp2 = 1; // always fill timestamp2 too (null = dont fill) sl@0: iAllDataInMemory = EFalse; sl@0: sl@0: if ( iHandle->readBufferSize == 0) sl@0: { sl@0: iReadBufferSize = READBUFSIZE; sl@0: } sl@0: else sl@0: { sl@0: iReadBufferSize = iHandle->readBufferSize; sl@0: } sl@0: sl@0: iDiskBuffer = HBufC8::NewL(iReadBufferSize); sl@0: iCurrentDiskReadPosition = 0; sl@0: iCurrentBufferReadPosition = 0; sl@0: CActiveScheduler::Add(this); sl@0: sl@0: PRINT(_L("CFileAsyncParser::ConstructL() OUT")); sl@0: } sl@0: sl@0: // ----------------------------------------------------------------------------- sl@0: // CFileAsyncParser::NewL sl@0: // Two-phased constructor. sl@0: // ----------------------------------------------------------------------------- sl@0: // sl@0: CFileAsyncParser* CFileAsyncParser::NewL( MP4HandleStruct* aHandle, RFile64& aFile ) sl@0: { sl@0: CFileAsyncParser* self = new(ELeave) CFileAsyncParser; sl@0: CleanupStack::PushL(self); sl@0: self->ConstructL( aHandle, aFile ); sl@0: CleanupStack::Pop(self); sl@0: return self; sl@0: } sl@0: sl@0: // ----------------------------------------------------------------------------- sl@0: // Destructor sl@0: // ----------------------------------------------------------------------------- sl@0: // sl@0: CFileAsyncParser::~CFileAsyncParser() sl@0: { sl@0: PRINT(_L("CFileAsyncParser::~CFileAsyncParser() in")); sl@0: sl@0: if ( IsActive() ) sl@0: { sl@0: if ( iAsyncReadOngoing ) sl@0: { sl@0: Cancel(); sl@0: } sl@0: } sl@0: sl@0: delete iDiskBuffer; sl@0: PRINT(_L("CFileAsyncParser::~CFileAsyncParser() out")); sl@0: } sl@0: sl@0: // ----------------------------------------------------------------------------- sl@0: // CFileAsyncParser::ReadAudioFrames( ); sl@0: // Writes incoming buffer data to internal buffers for writing to disk. sl@0: // (other items were commented in a header). sl@0: // ----------------------------------------------------------------------------- sl@0: // sl@0: TInt CFileAsyncParser::ReadAudioFrames( mp4_u8 *buffer, mp4_i64 aPosition, mp4_u32 aBytesToRead ) sl@0: { sl@0: PRINT(_L("CFileAsyncParser::ReadAudioFrames()")); sl@0: iProcessingAudio = ETrue; sl@0: return ReadDataAsync( buffer, aPosition, aBytesToRead ); sl@0: } sl@0: sl@0: // ----------------------------------------------------------------------------- sl@0: // CFileAsyncParser::ReadVideoFrame( ); sl@0: // Writes incoming buffer data to internal buffers for writing to disk. sl@0: // (other items were commented in a header). sl@0: // ----------------------------------------------------------------------------- sl@0: // sl@0: TInt CFileAsyncParser::ReadVideoFrame( mp4_u8* buffer, mp4_i64 aPosition, mp4_u32 aBytesToRead ) sl@0: { sl@0: PRINT(_L("CFileAsyncParser::ReadVideoFrame()")); sl@0: iProcessingAudio = EFalse; sl@0: return ReadDataAsync( buffer, aPosition, aBytesToRead ); sl@0: } sl@0: sl@0: sl@0: // ----------------------------------------------------------------------------- sl@0: // CFileAsyncParser::ReadDataAsync( ); sl@0: // Reads data from file asynchronously. sl@0: // (other items were commented in a header). sl@0: // ----------------------------------------------------------------------------- sl@0: // sl@0: TInt CFileAsyncParser::ReadDataAsync( mp4_u8 *buffer, mp4_i64 aPosition, mp4_u32 aBytesToRead ) sl@0: { sl@0: PRINT(_L("CFileAsyncParser::ReadDataAsync() in")); sl@0: iBuffer = buffer; sl@0: if ( iAsyncReadOngoing ) sl@0: { sl@0: return -1; // only one async read can be ongoing at one time; sl@0: } sl@0: if (!iDiskBuffer) sl@0: { sl@0: return -1; sl@0: } sl@0: sl@0: // Is the new seek point inside the current disk buffer? sl@0: if ( (iCurrentDiskReadPosition > aPosition) && sl@0: (( iCurrentDiskReadPosition - iDiskBuffer->Length() ) <= aPosition )) sl@0: { sl@0: // Yes sl@0: iCurrentBufferReadPosition = iDiskBuffer->Length() - (iCurrentDiskReadPosition - aPosition); sl@0: } sl@0: else sl@0: { sl@0: // No, set current position and zero diskbuffer sl@0: iCurrentBufferReadPosition = 0; sl@0: iCurrentDiskReadPosition = (mp4_i64)aPosition; sl@0: iDiskBuffer->Des().SetLength(0); sl@0: } sl@0: sl@0: iBytesToRead = aBytesToRead; sl@0: iBytesRead = 0; sl@0: TInt available = 0; sl@0: sl@0: // How much data is available in diskbuffer. sl@0: available = iDiskBuffer->Length() - iCurrentBufferReadPosition; sl@0: if (available > iBytesToRead) sl@0: { sl@0: available = iBytesToRead; sl@0: } sl@0: sl@0: // If any available copy it first to output buffer sl@0: if (available ) sl@0: { sl@0: memcpy(iBuffer, iDiskBuffer->Ptr() + iCurrentBufferReadPosition, available); sl@0: iCurrentBufferReadPosition += available; sl@0: iBytesRead += available; sl@0: } sl@0: sl@0: // If we got everything from diskbuffer process it right away sl@0: if (iBytesRead == iBytesToRead) sl@0: { sl@0: PRINT(_L("CFileAsyncParser::ReadDataAsync() Data found in memory, no need to read file - return right away")); sl@0: iAllDataInMemory = ETrue; sl@0: SetActive(); sl@0: TRequestStatus* tmp = &iStatus; sl@0: User::RequestComplete(tmp, KErrNone); sl@0: PRINT(_L("CFileAsyncParser::ReadDataAsync() out")); sl@0: return MP4_OK; sl@0: } sl@0: else sl@0: { sl@0: // Need to read rest of the requested data from file. sl@0: iAllDataInMemory = EFalse; sl@0: } sl@0: sl@0: // Determine used readbuffer size sl@0: if ( iHandle->readBufferSize == 0) sl@0: { sl@0: iReadBufferSize = READBUFSIZE; sl@0: } sl@0: else sl@0: { sl@0: iReadBufferSize = iHandle->readBufferSize; sl@0: } sl@0: sl@0: // Increase disk read buffer size if requested frames are larger than current disk buffer. sl@0: if ( (iBytesToRead > iReadBufferSize ) || (iReadBufferSize != iDiskBuffer->Des().MaxLength()) ) sl@0: { sl@0: iReadBufferSize = iBytesToRead; sl@0: if (iDiskBuffer) sl@0: { sl@0: delete iDiskBuffer; sl@0: iDiskBuffer = NULL; sl@0: TRAPD(memerror, iDiskBuffer = HBufC8::NewL(iReadBufferSize)); sl@0: if (memerror) sl@0: { sl@0: return MP4_OUT_OF_MEMORY; sl@0: } sl@0: else sl@0: { sl@0: iCurrentBufferReadPosition = 0; sl@0: } sl@0: } sl@0: } sl@0: sl@0: iAsyncReadOngoing = ETrue; sl@0: iDiskBufferPointer.Set(iDiskBuffer->Des()); sl@0: iCurrentDiskReadPosition = aPosition + iBytesRead; sl@0: switch (iHandle->sourceType) sl@0: { sl@0: case MP4_SOURCE_RFILE: sl@0: { sl@0: PRINT(_L("CFileAsyncParser::ReadDataAsync() Data not in memory, reading RFile64")); sl@0: RFile64* rfile = (RFile64*)iHandle->rfile; sl@0: rfile->Read(iCurrentDiskReadPosition, iDiskBufferPointer, iDiskBufferPointer.MaxLength(), iStatus); sl@0: break; sl@0: } sl@0: case MP4_SOURCE_CAF: sl@0: { sl@0: PRINT(_L("CFileAsyncParser::ReadDataAsync() Data not in memory, reading CAF object")); sl@0: iHandle->cafError = iHandle->cfile->Read(iCurrentDiskReadPosition, iDiskBufferPointer, iDiskBufferPointer.MaxLength(), iStatus); sl@0: if ( iHandle->cafError != KErrNone) sl@0: return -2; sl@0: break; sl@0: } sl@0: default: sl@0: return -1; sl@0: } sl@0: sl@0: if ( !IsActive() ) sl@0: { sl@0: SetActive(); sl@0: } sl@0: PRINT(_L("CFileAsyncParser::ReadDataAsync() out")); sl@0: return 0; sl@0: } sl@0: sl@0: sl@0: sl@0: // ----------------------------------------------------------------------------- sl@0: // CFileAsyncParser::DoCancel() sl@0: // From CActive Cancels async request. sl@0: // ----------------------------------------------------------------------------- sl@0: // sl@0: void CFileAsyncParser::DoCancel() sl@0: { sl@0: PRINT(_L("CFileAsyncParser::DoCancel() in")); sl@0: if (iAsyncReadOngoing) sl@0: { sl@0: if (iHandle->sourceType == MP4_SOURCE_RFILE) sl@0: { sl@0: // cancel read from file sl@0: ((RFile64 *)(iHandle->rfile))->ReadCancel(); sl@0: } sl@0: else if (iHandle->sourceType == MP4_SOURCE_CAF) sl@0: { sl@0: // cancel read from caf object sl@0: iHandle->cfile->ReadCancel(iStatus); sl@0: } sl@0: iAsyncReadOngoing = EFalse; sl@0: } sl@0: sl@0: PRINT(_L("CFileAsyncParser::DoCancel() out")); sl@0: } sl@0: sl@0: // ----------------------------------------------------------------------------- sl@0: // CFileAsyncParser::ReturnAudioFrames() sl@0: // Return audio frames to observer. sl@0: // ----------------------------------------------------------------------------- sl@0: // sl@0: void CFileAsyncParser::ReturnAudioFrames() sl@0: { sl@0: PRINT(_L("CFileAsyncParser::ReturnAudioFrames() in")); sl@0: TInt error = KErrNone; sl@0: sl@0: // Update last accessed position in file pointer sl@0: if (iHandle->audioSampleOffset + iHandle->audioSampleSize - 1 > iHandle->lastAccessedPosInFile) sl@0: { sl@0: iHandle->lastAccessedPosInFile = iHandle->audioSampleOffset + iHandle->audioSampleSize - 1; sl@0: } sl@0: sl@0: // Fill audio frame size sl@0: iAudioSize = iHandle->audioSampleSize; sl@0: sl@0: // Fill audio timestamp information sl@0: iAudioTimeStamp = 0; sl@0: iAudioTimeStamp2 = 1; // fill also timestamp2 (wont be filled if 0) sl@0: error = convertAudioSampleToTime(iHandle, iHandle->moov->trakAudio->mdia, &iAudioTimeStamp, &iAudioTimeStamp2); sl@0: if (error == MP4_OK) sl@0: { sl@0: // Fill iReturnedAudioFrames sl@0: iReturnedAudioFrames = 0; sl@0: error = CalculateAudioFrameCount(); sl@0: } sl@0: sl@0: // Move forward in audio samples sl@0: if (error == MP4_OK) sl@0: { sl@0: error = advanceAudioSample(iHandle, iHandle->moov->trakAudio); sl@0: if ( error == -1) sl@0: { sl@0: error = MP4_ERROR; sl@0: } sl@0: else if ( error == -2 ) sl@0: { sl@0: error = MP4_OK; sl@0: iHandle->audioLast = MP4TRUE; sl@0: } sl@0: } sl@0: sl@0: iAsyncReadOngoing = EFalse; sl@0: iHandle->asyncObserver->M3GPMP4LibAudioFramesAvailable(error, sl@0: iAudioSize, sl@0: iAudioTimeStamp, sl@0: iReturnedAudioFrames, sl@0: iAudioTimeStamp2); sl@0: PRINT(_L("CFileAsyncParser::ReturnAudioFrames() out")); sl@0: } sl@0: sl@0: // ----------------------------------------------------------------------------- sl@0: // CFileAsyncParser::ReturnVideoFrame() sl@0: // Return video frame to observer. sl@0: // ----------------------------------------------------------------------------- sl@0: // sl@0: void CFileAsyncParser::ReturnVideoFrame() sl@0: { sl@0: PRINT(_L("CFileAsyncParser::ReturnVideoFrame() in")); sl@0: TInt error = KErrNone; sl@0: sl@0: // Update last accessed position in file pointer sl@0: if (iHandle->videoFrameOffset + iHandle->videoFrameSize - 1 > iHandle->lastAccessedPosInFile) sl@0: { sl@0: iHandle->lastAccessedPosInFile = iHandle->videoFrameOffset + iHandle->videoFrameSize - 1; sl@0: } sl@0: sl@0: // Fill video frame size sl@0: iVideoSize = iHandle->videoFrameSize; sl@0: sl@0: // Fill video timestamp information sl@0: iVideoTimeStamp = 0; sl@0: iVideoTimeStamp2 = 1; // fill also timestamp2 (wont be filled if 0) sl@0: error = convertVideoSampleToTime(iHandle, iHandle->moov->trakVideo->mdia, &iVideoTimeStamp, &iVideoTimeStamp2); sl@0: if (error == MP4_OK) sl@0: { sl@0: // Fill iKeyFrame sl@0: iVideoKeyFrame = 0; sl@0: error = isVideoFrameKeyFrame(iHandle, iHandle->moov->trakVideo, &iVideoKeyFrame); sl@0: } sl@0: sl@0: // Move forward in video frames sl@0: if (error == MP4_OK) sl@0: { sl@0: error = advanceVideoFrame(iHandle, iHandle->moov->trakVideo); sl@0: if ( error == -1) sl@0: { sl@0: error = MP4_ERROR; sl@0: } sl@0: else if ( error == -2 ) sl@0: { sl@0: error = MP4_OK; sl@0: iHandle->videoLast = MP4TRUE; sl@0: } sl@0: } sl@0: sl@0: iAsyncReadOngoing = EFalse; sl@0: iHandle->asyncObserver->M3GPMP4LibVideoFrameAvailable(error, sl@0: iVideoSize, sl@0: iVideoTimeStamp, sl@0: iVideoKeyFrame, sl@0: iVideoTimeStamp2); sl@0: PRINT(_L("CFileAsyncParser::ReturnVideoFrame() out")); sl@0: } sl@0: sl@0: // ----------------------------------------------------------------------------- sl@0: // CFileAsyncParser::CalculateAudioFrameCount() sl@0: // Return video frame to observer. sl@0: // ----------------------------------------------------------------------------- sl@0: // sl@0: TInt CFileAsyncParser::CalculateAudioFrameCount() sl@0: { sl@0: mp4_i32 frameLength = 0; sl@0: mp4_u32 numOfFrames = 0; sl@0: mp4_u8 *framepointer = 0; sl@0: mp4_u32 rawAmrFrameLength[16] = {13,14,16,18,20,21,27,32,6,0,0,0,0,0,0,1}; sl@0: trackAtom *trak = iHandle->moov->trakAudio; sl@0: sl@0: if (!trak) sl@0: { sl@0: return -1; sl@0: } sl@0: sl@0: /* AMR */ sl@0: if (trak->mdia->minf) sl@0: if (trak->mdia->minf->stbl) sl@0: if (trak->mdia->minf->stbl->stsd) sl@0: if (iHandle->type & MP4_TYPE_AMR_NB) sl@0: { sl@0: framepointer = iBuffer; sl@0: numOfFrames = 0; sl@0: while ( iBytesRead > 0 ) sl@0: { sl@0: frameLength = rawAmrFrameLength[(TInt)(((*framepointer) & 0x78) >> 3)]; sl@0: if ( frameLength == 0) sl@0: { sl@0: return -4; sl@0: } sl@0: iBytesRead -= frameLength; sl@0: framepointer += frameLength; sl@0: numOfFrames++; sl@0: } sl@0: iReturnedAudioFrames = numOfFrames; sl@0: } sl@0: else if (iHandle->type & MP4_TYPE_AMR_WB) sl@0: { sl@0: /* Return the number of sample entries listed for this particular sample entry index */ sl@0: if (trak->mdia->minf->stbl->stsd->sawb[iHandle->audioSampleEntryIndex - 1]) sl@0: if (trak->mdia->minf->stbl->stsd->sawb[iHandle->audioSampleEntryIndex - 1]->damr) sl@0: iReturnedAudioFrames = trak->mdia->minf->stbl->stsd->sawb[iHandle->audioSampleEntryIndex - 1]->damr->framesPerSample; sl@0: } sl@0: else sl@0: { sl@0: } sl@0: sl@0: /* MPEG-4 audio */ sl@0: if (trak->mdia->minf) sl@0: if (trak->mdia->minf->stbl) sl@0: if (trak->mdia->minf->stbl->stsd) sl@0: if (trak->mdia->minf->stbl->stsd->mp4a[iHandle->audioSampleEntryIndex - 1]) sl@0: iReturnedAudioFrames = 1; sl@0: sl@0: /* QCELP 13K as QCELPSampleEntry*/ sl@0: if (trak->mdia->minf) sl@0: if (trak->mdia->minf->stbl) sl@0: if (trak->mdia->minf->stbl->stsd) sl@0: if ((iHandle->type & MP4_TYPE_QCELP_13K) && (!iHandle->qcelpStoredAsMPEGAudio)) sl@0: { sl@0: /* Return the number of sample entries listed for this particular sample entry index */ sl@0: if (trak->mdia->minf->stbl->stsd->sqcp[iHandle->audioSampleEntryIndex - 1]) sl@0: if (trak->mdia->minf->stbl->stsd->sqcp[iHandle->audioSampleEntryIndex - 1]->dqcp) sl@0: iReturnedAudioFrames = trak->mdia->minf->stbl->stsd->sqcp[iHandle->audioSampleEntryIndex - 1]->dqcp->framesPerSample; sl@0: } sl@0: sl@0: /* QCELP 13K as MPEG-4 audio */ sl@0: if (trak->mdia->minf) sl@0: if (trak->mdia->minf->stbl) sl@0: if (trak->mdia->minf->stbl->stsd) sl@0: if (trak->mdia->minf->stbl->stsd->mp4a[iHandle->audioSampleEntryIndex - 1]) sl@0: iReturnedAudioFrames = 1; sl@0: sl@0: return MP4_OK; sl@0: } sl@0: sl@0: // ----------------------------------------------------------------------------- sl@0: // CFileAsyncParser::RunL() sl@0: // From CActive Called when async request completes. sl@0: // ----------------------------------------------------------------------------- sl@0: // sl@0: void CFileAsyncParser::RunL() sl@0: { sl@0: PRINT(_L("CFileAsyncParser::RunL() in")); sl@0: if ( iStatus != KErrNone ) sl@0: { sl@0: PRINT((_L("CFileAsyncParser::RunL() error in previous async: %d "), iStatus.Int() )); sl@0: iError = iStatus.Int(); sl@0: iHandle->asyncObserver->M3GPMP4LibAudioFramesAvailable(MP4_FILE_ERROR,0,0,0,0); sl@0: return; sl@0: } sl@0: sl@0: if (!iAllDataInMemory) sl@0: { sl@0: if ((mp4_u32)iDiskBuffer->Length() == 0) // EOF or error sl@0: { sl@0: iError = MP4_FILE_ERROR; // metadata info doesn't match file -> corrupted clip. sl@0: } sl@0: sl@0: memcpy(iBuffer+iBytesRead, iDiskBuffer->Ptr(), iBytesToRead-iBytesRead); sl@0: iCurrentBufferReadPosition += iBytesToRead-iBytesRead; sl@0: iCurrentDiskReadPosition += iDiskBuffer->Length(); sl@0: iBytesRead = iBytesToRead; sl@0: sl@0: // set handle disk buffer sizes to zero just in case. sl@0: iHandle->diskReadBufPos = 0; sl@0: iHandle->diskReadSize = 0; sl@0: iHandle->diskReadBufStart = 0; sl@0: iHandle->diskReadPos = iCurrentDiskReadPosition; sl@0: } sl@0: sl@0: if ( iProcessingAudio ) sl@0: { sl@0: ReturnAudioFrames(); sl@0: } sl@0: else sl@0: { sl@0: ReturnVideoFrame(); sl@0: } sl@0: sl@0: PRINT(_L("CFileAsyncParser::RunL() out")); sl@0: } sl@0: sl@0: // End of File