--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/omxilvideocomps/omxil3gpdemuxer/src/c3gpdemuxer.cpp Fri Oct 08 22:09:17 2010 +0100
@@ -0,0 +1,1063 @@
+/*
+* Copyright (c) 2008 - 2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "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:
+*
+* Description:
+*
+*/
+
+
+/**
+ @file
+ @internalComponent
+ */
+
+#include "log.h"
+#include <openmax/il/common/omxilcallbacknotificationif.h>
+#include <openmax/il/common/omxilutil.h>
+#include "c3gpdemuxer.h"
+#include "omxil3gpdemuxerpanic.h"
+#include "comxil3gpdemuxertimeinputport.h"
+#include "comxil3gpdemuxeraudiooutputport.h"
+#include "comxil3gpdemuxervideooutputport.h"
+
+C3GPDemuxer::CPort* C3GPDemuxer::CPort::NewL(TInt aBufferCount)
+ {
+ CPort* self = new (ELeave) CPort();
+ CleanupStack::PushL(self);
+ self->ConstructL(aBufferCount);
+ CleanupStack::Pop(self);
+ return self;
+ }
+
+C3GPDemuxer::CPort::CPort() :
+ iEOS(EFalse)
+ {
+ }
+
+void C3GPDemuxer::CPort::ConstructL(TInt aBufferCount)
+ {
+ iBuffers.ReserveL(aBufferCount);
+ }
+
+C3GPDemuxer::CPort::~CPort()
+ {
+ iBuffers.Reset();
+ }
+
+C3GPDemuxer* C3GPDemuxer::NewL(MOmxILCallbackNotificationIf& aCallbacks)
+ {
+ C3GPDemuxer* self = new (ELeave) C3GPDemuxer(aCallbacks);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop(self);
+ return self;
+ }
+
+void C3GPDemuxer::ConstructL()
+ {
+ i3GPParser = C3GPParse::NewL();
+ }
+
+C3GPDemuxer::C3GPDemuxer(MOmxILCallbackNotificationIf& aCallbacks) :
+ CActive(EPriorityStandard),
+ iCallbacks(aCallbacks),
+ iVideoType(E3GPNoVideo),
+ iAudioType(E3GPNoAudio),
+ iFirstVideoFrame(ETrue),
+ iFirstAudioFrame(ETrue),
+ iAsyncBuf(0, 0)
+ {
+ iOmxAudioFormat.iCoding = OMX_AUDIO_CodingMax;
+ iOmxVideoFormat.iCoding = OMX_VIDEO_CodingMax;
+
+ for (TInt port = 0; port < COmxIL3GPDemuxer::EPortIndexMax; ++port)
+ {
+ iPort[port] = NULL;
+ }
+
+ CActiveScheduler::Add(this);
+ }
+
+C3GPDemuxer::~C3GPDemuxer()
+ {
+ Cancel();
+
+ DeleteBufferQueue();
+
+ if (i3GPParser)
+ {
+ HandleIfError(i3GPParser->Complete());
+ delete i3GPParser;
+ }
+ }
+
+OMX_ERRORTYPE C3GPDemuxer::AcquireResources(const TDesC& aFilename)
+ {
+ DEBUG_PRINTF(_L8("C3GPDemuxer::AcquireResources"));
+
+ TInt error = i3GPParser->Open(aFilename);
+ if (error == KErrNone)
+ {
+ iParserOpened = ETrue;
+
+ if (iStartTimePosition != 0)
+ {
+ SetPosition(iStartTimePosition, iStartKeyFrame);
+ iSeekPosition = iStartTimePosition;
+ iStartTimePosition = 0;
+ }
+
+ TRAP(error, CreateBufferQueueL());
+ }
+
+ return SymbianOSErrorToOmx(error);
+ }
+
+void C3GPDemuxer::ReleaseResources()
+ {
+ DeleteBufferQueue();
+
+ if (i3GPParser)
+ {
+ HandleIfError(i3GPParser->Complete());
+ iParserOpened = EFalse;
+ }
+ }
+
+void C3GPDemuxer::Start()
+ {
+ if (iPaused && iState != EStateWaitingToStart)
+ {
+ if (iState != EStateFillingBuffer)
+ {
+ Cancel();
+
+ // Self complete so that we start to process any buffer received while
+ // we were in paused state
+ CompleteSelf();
+ }
+ }
+ else
+ {
+ Cancel();
+ iState = EStateWaitingForBuffer;
+ // Self complete so that we start to process any buffer received while
+ // we were in idle state
+ CompleteSelf();
+ }
+
+ iPaused = EFalse;
+ }
+
+OMX_ERRORTYPE C3GPDemuxer::Stop()
+ {
+ Cancel();
+
+ if (iState == EStateFillingBuffer)
+ {
+ i3GPParser->CancelReadFrame();
+ }
+
+ iState = EStateWaitingToStart;
+ StartWaitingForBuffer();
+
+ iAudioHeadersSent = EFalse;
+ iFirstAudioFrame = ETrue;
+ iVideoHeadersSent = EFalse;
+ iFirstVideoFrame = ETrue;
+ iPaused = EFalse;
+
+ TInt error = SetPosition(0, EFalse);
+
+ return SymbianOSErrorToOmx(error);
+ }
+
+void C3GPDemuxer::Pause()
+ {
+ iPaused = ETrue;
+ }
+
+TBool C3GPDemuxer::Invalid() const
+ {
+ return iInvalid;
+ }
+
+void C3GPDemuxer::ProcessThisBufferL(OMX_BUFFERHEADERTYPE* aBufferHeader,
+ TUint32 aPortIndex)
+ {
+ __ASSERT_DEBUG(iBufferQueueCreated, Panic(EProcessThisBufferLNoBufferQueue));
+
+ aBufferHeader->nFilledLen = 0;
+ aBufferHeader->nOffset = 0;
+ aBufferHeader->nTimeStamp = 0;
+ aBufferHeader->nFlags = 0;
+
+ if (iBufferQueueCreated)
+ {
+ TBufferMessage buffer;
+ buffer.iBufferHeader = aBufferHeader;
+ buffer.iPortIndex = aPortIndex;
+ User::LeaveIfError(iBufferQueue.Send(buffer));
+ }
+ else
+ {
+ User::Leave(KErrNotReady);
+ }
+ }
+
+void C3GPDemuxer::FlushBuffers(TUint32 aPortIndex)
+ {
+ __ASSERT_DEBUG(aPortIndex == OMX_ALL || aPortIndex < COmxIL3GPDemuxer::EPortIndexMax, User::Invariant());
+
+ if (aPortIndex == OMX_ALL || aPortIndex < COmxIL3GPDemuxer::EPortIndexMax)
+ {
+ Cancel();
+ ReceiveQueuedBuffers();
+
+ if (aPortIndex == OMX_ALL || iCurrentPort == aPortIndex)
+ {
+ if (iState == EStateFillingBuffer)
+ {
+ // We are about to flush a buffer that is being filled.
+ // Cancel the read operation.
+ i3GPParser->CancelReadFrame();
+ }
+
+ iState = EStateWaitingForBuffer;
+ }
+
+ if (aPortIndex == OMX_ALL)
+ {
+ for (TInt portIndex = 0; portIndex < COmxIL3GPDemuxer::EPortIndexMax; ++portIndex)
+ {
+ DoFlushBuffers(portIndex);
+ }
+ }
+ else
+ {
+ DoFlushBuffers(aPortIndex);
+ }
+
+ // Unless we are waiting for a read to complete, we need to self
+ // complete just in case the ReceiveQueuedBuffers() call above
+ // received new buffers
+ if (iState != EStateFillingBuffer)
+ {
+ CompleteSelf();
+ }
+ }
+ }
+
+void C3GPDemuxer::DoFlushBuffers(TUint32 aPortIndex)
+ {
+ if (iPort[aPortIndex])
+ {
+ OMX_DIRTYPE direction = OMX_DirOutput;
+ if (aPortIndex == COmxIL3GPDemuxer::EPortIndexTimeInput)
+ {
+ direction = OMX_DirInput;
+ }
+
+ RQueuedBuffers& buffers = iPort[aPortIndex]->iBuffers;
+ while (buffers.Count() > 0)
+ {
+ iCallbacks.BufferDoneNotification(buffers[0], aPortIndex, direction);
+ buffers.Remove(0);
+ }
+ }
+ }
+
+TBool C3GPDemuxer::RemoveBuffer(OMX_BUFFERHEADERTYPE* aBufferHeader,
+ OMX_DIRTYPE aDirection)
+ {
+ TInt port = 0;
+ if (aDirection == OMX_DirOutput)
+ {
+ port = aBufferHeader->nOutputPortIndex;
+ }
+ else if (aDirection == OMX_DirInput)
+ {
+ port = aBufferHeader->nInputPortIndex;
+ }
+ else
+ {
+ Panic(ERemoveBufferInvalidDirection);
+ }
+
+ __ASSERT_DEBUG(port >= 0 && port < COmxIL3GPDemuxer::EPortIndexMax, Panic(ERemoveBufferInvalidPort));
+
+ TBool found = EFalse;
+
+ if (port >= 0 && port < COmxIL3GPDemuxer::EPortIndexMax)
+ {
+ Cancel();
+ ReceiveQueuedBuffers();
+
+ if (iPort[port])
+ {
+ RQueuedBuffers& buffers = iPort[port]->iBuffers;
+ for (TInt buf = 0; buf < buffers.Count(); ++buf)
+ {
+ if (buffers[buf] == aBufferHeader)
+ {
+ if (iCurrentPort == port && buf == 0)
+ {
+ if (iState == EStateFillingBuffer)
+ {
+ // We are about to remove a buffer that is being filled.
+ // Cancel the read operation.
+ i3GPParser->CancelReadFrame();
+ }
+ iState = EStateWaitingForBuffer;
+ }
+
+ buffers[buf]->nFilledLen = 0;
+ buffers.Remove(buf);
+ found = ETrue;
+ break;
+ }
+ }
+ }
+
+ // Unless we are waiting for a read to complete, we need to self
+ // complete just in case the ReceiveQueuedBuffers() call above
+ // received new buffers
+ if (iState != EStateFillingBuffer)
+ {
+ CompleteSelf();
+ }
+ }
+
+ return found;
+ }
+
+TBool C3GPDemuxer::GetVideoFormat(TSize& aFrameSize, TVideoFormat& aFormat) const
+ {
+ if (!iVideoPropertiesRead)
+ {
+ return EFalse;
+ }
+ aFrameSize.iWidth = iVideoWidth;
+ aFrameSize.iHeight = iVideoHeight;
+ aFormat = iOmxVideoFormat;
+ return ETrue;
+ }
+
+TBool C3GPDemuxer::GetAudioFormat(TAudioFormat& aFormat) const
+ {
+ if (!iAudioPropertiesRead)
+ {
+ return EFalse;
+ }
+ aFormat = iOmxAudioFormat;
+ return ETrue;
+ }
+
+OMX_ERRORTYPE C3GPDemuxer::GetVideoTimestamp(OMX_TICKS& aOmxticks)
+ {
+ TInt err = KErrNone;
+
+ TUint timestampInMilliSec(0);
+
+ if (iParserOpened)
+ {
+ // return last requested seek time
+ timestampInMilliSec = iSeekPosition;
+ }
+ else
+ {
+ timestampInMilliSec = iStartTimePosition;
+ }
+
+ if (err == KErrNone)
+ {
+ aOmxticks = timestampInMilliSec * 1000;
+ }
+
+ return SymbianOSErrorToOmx(err);
+ }
+
+OMX_ERRORTYPE C3GPDemuxer::Seek(const OMX_TICKS& aOmxticks, OMX_TIME_SEEKMODETYPE aSeekModeType)
+ {
+ TInt err = KErrNone;
+
+ //Set the firstFrame flags to true
+ iFirstVideoFrame = ETrue;
+ iFirstAudioFrame = ETrue;
+
+ TUint timeInMilliSec = aOmxticks / 1000;
+ TBool keyFrame(aSeekModeType == OMX_TIME_SeekModeFast);
+
+ if (iParserOpened)
+ {
+ err = SetPosition(timeInMilliSec, keyFrame);
+ if (err != KErrNone)
+ {
+ iSeekPosition = timeInMilliSec;
+ }
+ }
+ else
+ {
+ iStartTimePosition = timeInMilliSec;
+ iStartKeyFrame = keyFrame;
+ }
+
+ return SymbianOSErrorToOmx(err);
+ }
+
+OMX_ERRORTYPE C3GPDemuxer::DetectStreams()
+ {
+ iAudioPropertiesRead = EFalse;
+
+ TUint s_audioLength;
+ TInt s_audioFramesPerSample;
+ TUint s_audioAvgBitRate;
+ TUint s_audioTimeScale;
+ TInt error = i3GPParser->GetAudioProperties(iAudioType, s_audioLength,
+ s_audioFramesPerSample, s_audioAvgBitRate, s_audioTimeScale);
+
+ if (error != KErrNotSupported)
+ {
+ if (error)
+ {
+ return SymbianOSErrorToOmx(error);
+ }
+ else
+ {
+ iOmxAudioFormat.iFramesPerSample = s_audioFramesPerSample;
+ iOmxAudioFormat.iSampleRate = s_audioTimeScale;
+ iOmxAudioFormat.iAverageBitrate = s_audioAvgBitRate;
+ switch (iAudioType)
+ {
+ case E3GPQcelp13K:
+ {
+ iOmxAudioFormat.iCoding = OMX_AUDIO_CodingQCELP13;
+ }
+ break;
+ case E3GPMpeg4Audio:
+ {
+ iOmxAudioFormat.iCoding = OMX_AUDIO_CodingAAC;
+ }
+ break;
+ case E3GPAmrNB:
+ case E3GPAmrWB:
+ {
+ iOmxAudioFormat.iCoding = OMX_AUDIO_CodingAMR;
+ }
+ break;
+ default:
+ {
+ iOmxAudioFormat.iCoding = OMX_AUDIO_CodingMax;
+ }
+ }
+
+ iAudioPropertiesRead = ETrue;
+ }
+
+ }
+ iVideoPropertiesRead = EFalse;
+
+ TSize vidSize(iVideoWidth, iVideoHeight);
+ TUint s_vidLength = 0;
+ TReal s_vidFrameRate = 0.0;
+ TUint s_vidAvgBitRate = 0;
+ TSize s_vidSize;
+ TUint s_vidTimeScale = 0;
+
+ error = i3GPParser->GetVideoProperties(iVideoType, s_vidLength,
+ s_vidFrameRate, s_vidAvgBitRate, s_vidSize, s_vidTimeScale);
+
+ if (error != KErrNotSupported)
+ {
+ if (error)
+ {
+ return SymbianOSErrorToOmx(error);
+ }
+ else
+ {
+ iVideoWidth = s_vidSize.iWidth;
+ iVideoHeight = s_vidSize.iHeight;
+
+ // translate library video type to OpenMAX IL coding and profile type
+ switch (iVideoType)
+ {
+ case E3GPMpeg4Video:
+ iOmxVideoFormat.iCoding = OMX_VIDEO_CodingMPEG4;
+ break;
+ case E3GPH263Profile0:
+ iOmxVideoFormat.iCoding = OMX_VIDEO_CodingH263;
+ iOmxVideoFormat.iProfile.h263
+ = OMX_VIDEO_H263ProfileBaseline;
+ break;
+ case E3GPH263Profile3:
+ iOmxVideoFormat.iCoding = OMX_VIDEO_CodingH263;
+ iOmxVideoFormat.iProfile.h263 = OMX_VIDEO_H263ProfileISWV2;
+ break;
+ case E3GPAvcProfileBaseline:
+ iOmxVideoFormat.iCoding = OMX_VIDEO_CodingAVC;
+ iOmxVideoFormat.iProfile.avc = OMX_VIDEO_AVCProfileBaseline;
+ break;
+ default:
+ // do not return an error here, the error is signalled after the transition to Executing
+ iOmxVideoFormat.iCoding = OMX_VIDEO_CodingMax;
+ break;
+ }
+ }
+ iVideoPropertiesRead = ETrue;
+ }
+
+ return OMX_ErrorNone;
+ }
+
+
+OMX_ERRORTYPE C3GPDemuxer::GetMetadataL(OMX_CONFIG_METADATAITEMTYPE* aMetadata)
+ {
+ // Metadata key size must be at least 4
+ if (aMetadata->nKeySizeUsed < 4)
+ {
+ return OMX_ErrorBadParameter;
+ }
+
+ T3GPUdtaLocation udtaLocation;
+ TUint32 udtaAtomType = Pack32(aMetadata->nKey);
+ TUint32 buffersize = aMetadata->nValueMaxSize;
+
+ RBuf8 buffer;
+ CleanupClosePushL(buffer);
+ User::LeaveIfError(buffer.Create(buffersize));
+
+ TUint atomIndex = 0;
+ if (aMetadata->nMetadataItemIndex != OMX_ALL)
+ {
+ atomIndex = aMetadata->nMetadataItemIndex;
+ }
+
+ TInt error = KErrNone;
+
+ switch (aMetadata->eScopeMode)
+ {
+ case OMX_MetadataScopeAllLevels:
+ {
+ TUint subatomCount = 0;
+ udtaLocation = E3GPUdtaMoov;
+
+ error = i3GPParser->GetUserDataAtom(udtaAtomType, udtaLocation, buffer, subatomCount);
+
+ if (error == KErrNone)
+ {
+ if (atomIndex == 0)
+ {
+ break;
+ }
+ else if (atomIndex <= subatomCount)
+ {
+ error = i3GPParser->GetUserDataAtom(udtaAtomType, udtaLocation, buffer, atomIndex);
+ break;
+ }
+ else
+ {
+ atomIndex -= (subatomCount + 1);
+ }
+ }
+ else if (error != KErrNotFound)
+ {
+ break;
+ }
+
+ subatomCount = 0;
+ udtaLocation = E3GPUdtaVideoTrak;
+ error = i3GPParser->GetUserDataAtom(udtaAtomType, udtaLocation,
+ buffer, subatomCount);
+ if (error == KErrNone)
+ {
+ if (atomIndex == 0)
+ {
+ break;
+ }
+ else if (atomIndex <= subatomCount)
+ {
+ error = i3GPParser->GetUserDataAtom(udtaAtomType, udtaLocation, buffer, atomIndex);
+ break;
+ }
+ else
+ {
+ atomIndex -= (subatomCount + 1);
+ }
+ }
+ else if (error != KErrNotFound)
+ {
+ break;
+ }
+
+ udtaLocation = E3GPUdtaAudioTrak;
+ error = i3GPParser->GetUserDataAtom(udtaAtomType, udtaLocation,
+ buffer, atomIndex);
+ }
+ break;
+ case OMX_MetadataScopeTopLevel:
+ {
+ udtaLocation = E3GPUdtaMoov;
+ error = i3GPParser->GetUserDataAtom(udtaAtomType, udtaLocation,
+ buffer, atomIndex);
+ }
+ break;
+ case OMX_MetadataScopePortLevel:
+ {
+ if (aMetadata->nScopeSpecifier
+ == COmxIL3GPDemuxer::EPortIndexVideoOutput)
+ {
+ udtaLocation = E3GPUdtaVideoTrak;
+ error = i3GPParser->GetUserDataAtom(udtaAtomType, udtaLocation,
+ buffer, atomIndex);
+ }
+ else if (aMetadata->nScopeSpecifier == COmxIL3GPDemuxer::EPortIndexAudioOutput)
+ {
+ udtaLocation = E3GPUdtaAudioTrak;
+ error = i3GPParser->GetUserDataAtom(udtaAtomType, udtaLocation, buffer, atomIndex);
+ }
+ else
+ {
+ error = KErrArgument;
+ }
+ }
+ break;
+ default:
+ {
+ error = KErrArgument;
+ }
+ }
+
+ if (error == KErrNone)
+ {
+ // strip size and atom type from the buffer
+ Mem::Copy(aMetadata->nValue, (buffer.Ptr() + 8), (buffer.Size() - 8));
+ aMetadata->nValueSizeUsed = (buffer.Size() - 8);
+ }
+
+ CleanupStack::PopAndDestroy();//buffer
+ return SymbianOSErrorToOmx(error);
+ }
+
+void C3GPDemuxer::RunL()
+ {
+ iWaitingOnBufferQueue = EFalse;
+ ReceiveQueuedBuffers();
+
+ if (iPaused || iInvalid || iState == EStateWaitingToStart)
+ {
+ StartWaitingForBuffer();
+ return;
+ }
+
+ if (iState == EStateWaitingToSubmit)
+ {
+ SubmitBuffer();
+ }
+
+ if (!ProcessBuffers())
+ {
+ iState = EStateWaitingForBuffer;
+ StartWaitingForBuffer();
+ }
+ }
+
+void C3GPDemuxer::DoCancel()
+ {
+ if (iWaitingOnBufferQueue)
+ {
+ iWaitingOnBufferQueue = EFalse;
+ iBufferQueue.CancelDataAvailable();
+ }
+ }
+
+TBool C3GPDemuxer::ProcessBuffers()
+ {
+ TUint32 startPort = iCurrentPort;
+
+ do
+ {
+ if (iPort[iCurrentPort]->iBuffers.Count() > 0 && !iPort[iCurrentPort]->iEOS)
+ {
+ DoProcessBuffer();
+ return ETrue;
+ }
+ }
+ while (NextPort() != startPort);
+
+ return EFalse;
+ }
+
+void C3GPDemuxer::DoProcessBuffer()
+ {
+ iState = EStateFillingBuffer;
+
+ iCurrentBuffer = iPort[iCurrentPort]->iBuffers[0];
+ iPort[iCurrentPort]->iBuffers.Remove(0);
+
+ switch(iCurrentPort)
+ {
+ case COmxIL3GPDemuxer::EPortIndexTimeInput:
+ {
+ ProcessTimeBuffer();
+ break;
+ }
+ case COmxIL3GPDemuxer::EPortIndexAudioOutput:
+ {
+ FillAudioBuffer();
+ break;
+ }
+ case COmxIL3GPDemuxer::EPortIndexVideoOutput:
+ {
+ FillVideoBuffer();
+ break;
+ }
+ }
+ }
+
+void C3GPDemuxer::ProcessTimeBuffer()
+ {
+ // TODO
+ User::Invariant();
+ }
+
+void C3GPDemuxer::FillAudioBuffer()
+ {
+ if (!iAudioHeadersSent)
+ {
+ TPtr8 audioBuffer(iCurrentBuffer->pBuffer + iCurrentBuffer->nOffset + iCurrentBuffer->nFilledLen,
+ 0,
+ static_cast<TInt>(iCurrentBuffer->nAllocLen - iCurrentBuffer->nOffset - iCurrentBuffer->nFilledLen));
+ TInt error = i3GPParser->GetAudioDecoderSpecificInfo(audioBuffer);
+
+ if (error == KErrNone)
+ {
+ iCurrentBuffer->nFilledLen = audioBuffer.Length();
+ iCurrentBuffer->nFlags |= OMX_BUFFERFLAG_CODECCONFIG;
+ iAudioHeadersSent = ETrue;
+ SubmitBuffer();
+ CompleteSelf();
+ }
+ else
+ {
+ HandleIfError(error);
+ }
+ return;
+ }
+
+ iAsyncBuf.Set(iCurrentBuffer->pBuffer + iCurrentBuffer->nOffset + iCurrentBuffer->nFilledLen
+ ,0
+ ,static_cast<TInt>(iCurrentBuffer->nAllocLen - iCurrentBuffer->nOffset - iCurrentBuffer->nFilledLen));
+ i3GPParser->ReadAudioFrames(*this, iAsyncBuf);
+ }
+
+void C3GPDemuxer::FillVideoBuffer()
+ {
+ if (!iVideoHeadersSent)
+ {
+ iCurrentBuffer->nOffset = 0;
+
+ TPtr8 videoBuffer(iCurrentBuffer->pBuffer + iCurrentBuffer->nOffset + iCurrentBuffer->nFilledLen,
+ 0,
+ static_cast<TInt>(iCurrentBuffer->nAllocLen - iCurrentBuffer->nOffset - iCurrentBuffer->nFilledLen));
+ TInt error = i3GPParser->GetVideoDecoderSpecificInfo(videoBuffer);
+
+ if (error == KErrNone)
+ {
+ iCurrentBuffer->nFilledLen = videoBuffer.Length();
+ iCurrentBuffer->nFlags |= OMX_BUFFERFLAG_CODECCONFIG;
+ iVideoHeadersSent = ETrue;
+ SubmitBuffer();
+ CompleteSelf();
+ }
+ else
+ {
+ HandleIfError(error);
+ }
+ return;
+ }
+
+ iAsyncBuf.Set(&iCurrentBuffer->pBuffer[0]
+ ,static_cast<TInt>((iCurrentBuffer->nFilledLen + iCurrentBuffer->nOffset))
+ ,static_cast<TInt>(iCurrentBuffer->nAllocLen));
+ i3GPParser->ReadVideoFrame(*this, iAsyncBuf);
+ }
+
+void C3GPDemuxer::AudioFramesAvailable(TInt aError, TUint /*aReturnedFrames*/,
+ TUint aTimeStampInMs, TUint /*aTimeStampInTimescale*/)
+ {
+ HandleIfError(aError);
+
+ if (!iInvalid)
+ {
+ // Check if this is the last frame. It is the last frame if
+ // GetAudioFramesSize returns KErrNotFound
+ TUint frameSize = 0;
+ TInt nextFrameError = i3GPParser->GetAudioFramesSize(frameSize);
+
+ FileReadComplete(nextFrameError, aTimeStampInMs, iFirstAudioFrame, EFalse);
+ }
+ }
+
+void C3GPDemuxer::VideoFrameAvailable(TInt aError, TBool aKeyFrame, TUint
+aTimeStampInMs, TUint /*aTimeStampInTimescale*/)
+ {
+ HandleIfError(aError);
+
+ if (!iInvalid)
+ {
+ // Check if this is the last frame. It is the last frame if
+ // GetVideoFramesSize returns KErrNotFound
+ TUint frameSize = 0;
+ TInt nextFrameError = i3GPParser->GetVideoFrameSize(frameSize);
+
+ FileReadComplete(nextFrameError, aTimeStampInMs, iFirstVideoFrame, aKeyFrame);
+ }
+ }
+
+void C3GPDemuxer::FileReadComplete(TInt aNextFrameError, TUint32 aTimeStamp, TBool& aFirstFrame, TBool aKeyFrame)
+ {
+ // aNextFrameError is the error code returned when checking the size of the
+ // next audio or video frame. If the error code is KErrNotFound, this
+ // shows that no more frames exist in that stream so the end of stream flag
+ // should be set when sending out the buffer.
+ if (aNextFrameError == KErrNotFound)
+ {
+ iPort[iCurrentPort]->iEOS = ETrue;
+ }
+ else
+ {
+ HandleIfError(aNextFrameError);
+ }
+
+ if (!iInvalid)
+ {
+ iCurrentBuffer->nFilledLen += iAsyncBuf.Length();
+
+ // Set presentation time
+ iCurrentBuffer->nTimeStamp = aTimeStamp * 1000;
+
+ if (aFirstFrame)
+ {
+ iCurrentBuffer->nFlags |= OMX_BUFFERFLAG_STARTTIME;
+ aFirstFrame = EFalse;
+ }
+ else
+ {
+ iCurrentBuffer->nFlags &= ~OMX_BUFFERFLAG_STARTTIME;
+ }
+
+ if (aKeyFrame)
+ {
+ iCurrentBuffer->nFlags |= OMX_BUFFERFLAG_SYNCFRAME;
+ }
+ else
+ {
+ iCurrentBuffer->nFlags &= ~OMX_BUFFERFLAG_SYNCFRAME;
+ }
+
+ // Demuxer just puts one whole frame into a buffer so we
+ // can set the end of frame flag
+ iCurrentBuffer->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME;
+
+ SubmitBuffer();
+ CompleteSelf();
+ }
+ }
+
+void C3GPDemuxer::SubmitBuffer()
+ {
+ if (!iPaused)
+ {
+ if (iPort[iCurrentPort]->iEOS)
+ {
+ iCurrentBuffer->nFlags |= OMX_BUFFERFLAG_EOS;
+ iCallbacks.EventNotification(OMX_EventBufferFlag, iCurrentPort,
+ iCurrentBuffer->nFlags, NULL);
+ }
+ #if 0
+ else if (iCurrentBuffer->nFlags & OMX_BUFFERFLAG_STARTTIME)
+ {
+ iCallbacks.EventNotification(OMX_EventBufferFlag, iCurrentPort,
+ iCurrentBuffer->nFlags, NULL);
+ }
+ #endif
+
+ iCallbacks.BufferDoneNotification(iCurrentBuffer, iCurrentPort,
+ OMX_DirOutput);
+
+ iState = EStateWaitingForBuffer;
+ NextPort();
+ }
+ else
+ {
+ iState = EStateWaitingToSubmit;
+ }
+ }
+
+void C3GPDemuxer::CreateBufferQueueL()
+ {
+ DEBUG_PRINTF(_L8("C3GPDemuxer::CreateBufferQueueL++"));
+
+ iPort[COmxIL3GPDemuxer::EPortIndexTimeInput] = CPort::NewL(KMaxTimeBuffers);
+ iPort[COmxIL3GPDemuxer::EPortIndexAudioOutput] = CPort::NewL(KMaxAudioBuffers);
+ iPort[COmxIL3GPDemuxer::EPortIndexVideoOutput] = CPort::NewL(KMaxVideoBuffers);
+
+ User::LeaveIfError(iBufferQueue.CreateLocal(KMaxTimeBuffers + KMaxAudioBuffers + KMaxVideoBuffers));
+ iBufferQueueCreated = ETrue;
+ iState = EStateWaitingToStart;
+ StartWaitingForBuffer();
+
+ DEBUG_PRINTF(_L8("C3GPDemuxer::CreateBufferQueueL--"));
+ }
+
+void C3GPDemuxer::DeleteBufferQueue()
+ {
+ if (iBufferQueueCreated)
+ {
+ Cancel();
+ iBufferQueueCreated = EFalse;
+ iBufferQueue.Close();
+ }
+
+ for (TInt port = 0; port < COmxIL3GPDemuxer::EPortIndexMax; ++port)
+ {
+ delete iPort[port];
+ iPort[port] = NULL;
+ }
+ }
+
+void C3GPDemuxer::HandleIfError(TInt aError)
+ {
+ OMX_ERRORTYPE omxError = SymbianOSErrorToOmx(aError);
+
+ if (omxError != OMX_ErrorNone)
+ {
+ iInvalid = ETrue;
+ iCallbacks.ErrorEventNotification(omxError);
+ }
+ }
+
+// Helper function to convert Symbian OS standard error code to c style error code
+OMX_ERRORTYPE C3GPDemuxer::SymbianOSErrorToOmx(TInt aError) const
+ {
+ OMX_ERRORTYPE error = OMX_ErrorUndefined;
+
+ switch (aError)
+ {
+ case (KErrNone):
+ {
+ error = OMX_ErrorNone;
+ }
+ break;
+ case (KErrNoMemory):
+ {
+ error = OMX_ErrorInsufficientResources;
+ }
+ break;
+ case (KErrOverflow):
+ {
+ error = OMX_ErrorOverflow;
+ }
+ break;
+ case (KErrAccessDenied):
+ {
+ error = OMX_ErrorContentPipeOpenFailed;
+ }
+ break;
+ case (KErrCorrupt):
+ {
+ error = OMX_ErrorStreamCorrupt;
+ }
+ break;
+ case (KErrArgument):
+ {
+ error = OMX_ErrorUnsupportedSetting;
+ }
+ break;
+ default:
+ {
+ error = OMX_ErrorUndefined;
+ }
+ }
+ return error;
+ }
+
+void C3GPDemuxer::ReceiveQueuedBuffers()
+ {
+ if (iBufferQueueCreated)
+ {
+ TBufferMessage message;
+ while(iBufferQueue.Receive(message) != KErrUnderflow)
+ {
+ // Port buffers are pre-reserved so append should always work
+ if (iPort[message.iPortIndex]->iBuffers.Append(message.iBufferHeader) != KErrNone)
+ {
+ Panic(EReceiveQueuedBuffersAppendFailed);
+ }
+ }
+ }
+ }
+
+void C3GPDemuxer::StartWaitingForBuffer()
+ {
+ if (iBufferQueueCreated)
+ {
+ iWaitingOnBufferQueue = ETrue;
+ iBufferQueue.NotifyDataAvailable(iStatus);
+ SetActive();
+ }
+ }
+
+void C3GPDemuxer::CompleteSelf()
+ {
+ iStatus = KRequestPending;
+ SetActive();
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete(status, KErrNone);
+ }
+
+TInt C3GPDemuxer::NextPort()
+ {
+ ++iCurrentPort;
+ iCurrentPort %= COmxIL3GPDemuxer::EPortIndexMax;
+ return iCurrentPort;
+ }
+
+/** Packs four bytes into a 32bit number */
+TUint32 C3GPDemuxer::Pack32(const TUint8* aPtr)
+ {
+ TUint32 x = *aPtr++ << 24;
+ x |= *aPtr++ << 16;
+ x |= *aPtr++ << 8;
+ x |= *aPtr++;
+ return x;
+ }
+
+TInt C3GPDemuxer::SetPosition(TUint aTimePosition, TBool aKeyFrame)
+ {
+ TUint audioPosition = 0;
+ TUint videoPosition = 0;
+
+ TInt err = i3GPParser->Seek(aTimePosition, aKeyFrame, audioPosition, videoPosition);
+ if(err)
+ {
+ return err;
+ }
+
+ // clear EOS state so buffer handling resumes if previously hit EOS
+ // TODO is this thread safe?
+ iPort[0]->iEOS = EFalse;
+ iPort[1]->iEOS = EFalse;
+
+ return KErrNone;
+ }