diff -r 000000000000 -r 58be5850fb6c omxilcomp/omxilaudioemulator/pcmrenderer/src/omxilpcmrendererprocessingfunction.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/omxilcomp/omxilaudioemulator/pcmrenderer/src/omxilpcmrendererprocessingfunction.cpp Thu Sep 02 20:13:57 2010 +0300 @@ -0,0 +1,1685 @@ +/* +* 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 + +#include + +#include "log.h" +#include +#include +#include +#include "omxilpcmrendererprocessingfunction.h" + +const TInt COmxILPcmRendererProcessingFunction::CPFHelper::KMaxMsgQueueEntries; + +COmxILPcmRendererProcessingFunction* +COmxILPcmRendererProcessingFunction::NewL(MOmxILCallbackNotificationIf& aCallbacks, + MOmxILClockComponentCmdsIf& aClientClockPort) + { + DEBUG_PRINTF(_L8("COmxILPcmRendererProcessingFunction::NewL")); + + COmxILPcmRendererProcessingFunction* self = + new (ELeave)COmxILPcmRendererProcessingFunction(aCallbacks, aClientClockPort); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + + } + +void +COmxILPcmRendererProcessingFunction::ConstructL() + { + DEBUG_PRINTF(_L8("COmxILPcmRendererProcessingFunction::ConstructL")); + + iAudioDevice = CAudioDevice::NewL(*this); + iPFHelper = CPFHelper::NewL(*iAudioDevice); + } + +COmxILPcmRendererProcessingFunction::COmxILPcmRendererProcessingFunction( + MOmxILCallbackNotificationIf& aCallbacks, + MOmxILClockComponentCmdsIf& aClientClockPort) + : + COmxILProcessingFunction(aCallbacks), + iClientClockPortPtr(&aClientClockPort) + { + DEBUG_PRINTF(_L8("COmxILPcmRendererProcessingFunction::COmxILPcmRendererProcessingFunction")); + + } + +COmxILPcmRendererProcessingFunction::~COmxILPcmRendererProcessingFunction() + { + DEBUG_PRINTF(_L8("COmxILPcmRendererProcessingFunction::~COmxILPcmRendererProcessingFunction")); + + // Check in case the Sound Device has not been closed. That would happen in + // an scenario where the component is not being deleted in an orderer way. + if(iAudioDevice && iPFHelper && + (iState == OMX_StateInvalid || + iState == OMX_StateExecuting || + iState == OMX_StatePause)) + { + // Ignore error if the following call fails + iPFHelper->CloseDeviceOnError(); + } + + // Buffer headers are not owned by the processing function + iBuffersToEmpty.Close(); + iBuffersEmptied.Close(); + delete iAudioDevice; + delete iPFHelper; + } + + +OMX_ERRORTYPE +COmxILPcmRendererProcessingFunction::StateTransitionIndication(TStateIndex aNewState) + { + DEBUG_PRINTF(_L8("COmxILPcmRendererProcessingFunction::StateTransitionIndication")); + + OMX_ERRORTYPE err = OMX_ErrorNone; + switch(aNewState) + { + case EStateExecuting: + { + DEBUG_PRINTF(_L8("StateTransitionIndication : OMX_StateExecuting")); + if (iPFHelper->Execute() != KErrNone) + { + return OMX_ErrorInsufficientResources; + } + } + break; + case EStateInvalid: + { + DEBUG_PRINTF(_L8("StateTransitionIndication : OMX_StateInvalid")); + if (iPFHelper->Stop() != KErrNone) + { // InsufficientResources to stop??? + return OMX_ErrorInsufficientResources; + } + } + break; + case EStatePause: + { + DEBUG_PRINTF(_L8("StateTransitionIndication : OMX_StatePause")); + err = iAudioDevice->MoveToPausedState(); + } + break; + case EStateIdle: + { + DEBUG_PRINTF(_L8("StateTransitionIndication : OMX_StateIdle")); + iBuffersToEmpty.Reset(); + if (iPFHelper->Stop() != KErrNone) + { // InsufficientResources to stop??? + return OMX_ErrorInsufficientResources; + } + } + break; + case EStateLoaded: + case EStateWaitForResources: + { + DEBUG_PRINTF(_L8("StateTransitionIndication : OMX_StateLoaded, OMX_StateWaitForResources")); + if (iPFHelper->Stop() != KErrNone) + { // InsufficientResources to stop??? + return OMX_ErrorInsufficientResources; + } + } + break; + case ESubStateLoadedToIdle: + { + DEBUG_PRINTF(_L8("StateTransitionIndication : ESubStateLoadedToIdle")); + if (iPFHelper->OpenDevice() != KErrNone) + { + return OMX_ErrorInsufficientResources; + } + } + break; + case ESubStateIdleToLoaded: + { + DEBUG_PRINTF(_L8("StateTransitionIndication : ESubStateIdleToLoaded")); + if (iPFHelper->CloseDevice() != KErrNone) + { // InsufficientResources to close??? + return OMX_ErrorInsufficientResources; + } + } + break; + case ESubStateExecutingToIdle: + case ESubStatePauseToIdle: + { + // Ignore these transitions... + return OMX_ErrorNone; + } + default: + { + // Always ASSERT; This would be a problem in the framework. + ASSERT(0); + return OMX_ErrorIncorrectStateTransition; + } + }; + + return err; + + } + + +OMX_ERRORTYPE +COmxILPcmRendererProcessingFunction::BufferFlushingIndication( + TUint32 aPortIndex, + OMX_DIRTYPE aDirection) + { + DEBUG_PRINTF2(_L8("COmxILPcmRendererProcessingFunction::BufferFlushingIndication : aPortIndex[%d]"), aPortIndex); + + if ((aPortIndex == OMX_ALL && aDirection == OMX_DirMax) || + (aPortIndex == KPCMRENDERER_APB0PORT_INDEX && aDirection == OMX_DirInput)) + { + // If we are currently processing a buffer then cancel + if (iPFHelper->CancelDevice() != KErrNone) + { + return OMX_ErrorInsufficientResources; + } + + // Send BufferDone notifications for each emptied buffer... + FlushBufferList(iBuffersEmptied); + + // Send BufferDone notifications for each pending buffer... + FlushBufferList(iBuffersToEmpty); + + return OMX_ErrorNone; + } + else if (aPortIndex == KPCMRENDERER_OPB0PORT_INDEX && aDirection == OMX_DirInput) + { + // Since the clock port buffers are returned immediately, + // there's nothing to flush for the port + return OMX_ErrorNone; + } + else + { + // Always ASSERT; This would be a problem in the framework. + ASSERT(0); + return OMX_ErrorBadParameter; + } + } + +void +COmxILPcmRendererProcessingFunction::FlushBufferList( + RPointerArray& aBufferList) + { + DEBUG_PRINTF2(_L8("COmxILPcmRendererProcessingFunction::FlushBufferList : [%s]"), + &aBufferList == &iBuffersToEmpty ? "iBuffersToEmpty" : "iBuffersEmptied"); + + const TUint bufferCount = aBufferList.Count(); + OMX_BUFFERHEADERTYPE* pBufferHeader = 0; + // We know there is only one input port... + OMX_DIRTYPE portDirection = OMX_DirInput; + + for (TUint i=0; inFilledLen = 0; + iCallbacks. + BufferDoneNotification( + pBufferHeader, + pBufferHeader->nInputPortIndex, + portDirection + ); + } + + // Empty buffer list... + aBufferList.Reset(); + + } + + +OMX_ERRORTYPE +COmxILPcmRendererProcessingFunction::ParamIndication( + OMX_INDEXTYPE aParamIndex, + const TAny* apComponentParameterStructure) + { + DEBUG_PRINTF(_L8("COmxILPcmRendererProcessingFunction::ParamIndication")); + + OMX_ERRORTYPE err = OMX_ErrorNone; + switch(aParamIndex) + { + case OMX_IndexParamAudioPcm: + { + const OMX_AUDIO_PARAM_PCMMODETYPE* pPcmProfile + = static_cast( + apComponentParameterStructure); + + if((pPcmProfile->nChannels == 1 || pPcmProfile->nChannels == 2) && + (pPcmProfile->eNumData == OMX_NumericalDataSigned) && + (pPcmProfile->eEndian == OMX_EndianBig) && + (pPcmProfile->bInterleaved == OMX_TRUE) && + (pPcmProfile->nBitPerSample == 16) && + ((pPcmProfile->nSamplingRate == 8000) || + (pPcmProfile->nSamplingRate == 11025) || + (pPcmProfile->nSamplingRate == 12000) || + (pPcmProfile->nSamplingRate == 16000) || + (pPcmProfile->nSamplingRate == 22050) || + (pPcmProfile->nSamplingRate == 24000) || + (pPcmProfile->nSamplingRate == 32000) || + (pPcmProfile->nSamplingRate == 44100) || + (pPcmProfile->nSamplingRate == 48000)) && + (pPcmProfile->ePCMMode == OMX_AUDIO_PCMModeLinear) && + (pPcmProfile->eChannelMapping[0] == OMX_AUDIO_ChannelLF) && + (pPcmProfile->eChannelMapping[1] == OMX_AUDIO_ChannelRF)) + { + if (iPFHelper->ParamIndication(pPcmProfile) != KErrNone) + { + err = OMX_ErrorInsufficientResources; + } + } + else + { + err = OMX_ErrorBadParameter; + } + } + break; + default: + { + // Ignore other port param changes... + } + }; + + return err; + + } + +OMX_ERRORTYPE +COmxILPcmRendererProcessingFunction::ConfigIndication(OMX_INDEXTYPE aConfigIndex, + const TAny* apComponentConfigStructure) + { + DEBUG_PRINTF2(_L8("COmxILPcmRendererProcessingFunction::ConfigIndication %X"), aConfigIndex); + + OMX_ERRORTYPE err = OMX_ErrorNone; + switch(aConfigIndex) + { + case OMX_SymbianIndexConfigAudioPcmVolumeRamp: + { + const OMX_SYMBIAN_AUDIO_CONFIG_PCM_VOLUMERAMP* + pPcmVolumeRamp + = static_cast< + const OMX_SYMBIAN_AUDIO_CONFIG_PCM_VOLUMERAMP*>( + apComponentConfigStructure); + + if (iPFHelper->SetVolumeRamp(pPcmVolumeRamp->nRampDuration) != KErrNone) + { + err = OMX_ErrorInsufficientResources; + } + } + break; + + case OMX_IndexConfigAudioVolume: + { + const OMX_AUDIO_CONFIG_VOLUMETYPE* pVolumeType + = static_cast( + apComponentConfigStructure); + + if (pVolumeType->bLinear == OMX_TRUE) + { + // Some configuration structures contain read-only fields. The + // OMX_SetConfig method will preserve read-only fields in configuration + // structures that contain them, and shall not generate an error when + // the caller attempts to change the value of a read-only field. + err = OMX_ErrorNone; + break; + } + + if ((pVolumeType->sVolume.nValue <= pVolumeType->sVolume.nMax) && + (pVolumeType->sVolume.nValue >= pVolumeType->sVolume.nMin)) + { + if (iPFHelper->SetVolume(pVolumeType->sVolume.nValue) != KErrNone) + { + err = OMX_ErrorInsufficientResources; + } + } + else + { + err = OMX_ErrorBadParameter; + } + } + break; + + case OMX_IndexConfigAudioMute: + { + const OMX_AUDIO_CONFIG_MUTETYPE* pVolumeType + = static_cast( + apComponentConfigStructure); + + if (iPFHelper->SetMuted(pVolumeType->bMute) != KErrNone) + { + err = OMX_ErrorInsufficientResources; + } + } + break; + + default: + { + // Ignore other port config changes... + } + }; + + return err; + + } + +OMX_ERRORTYPE +COmxILPcmRendererProcessingFunction::BufferIndication( + OMX_BUFFERHEADERTYPE* apBufferHeader, + OMX_DIRTYPE aDirection) + { + DEBUG_PRINTF2(_L8("COmxILPcmRendererProcessingFunction::BufferIndication : [%X]"), apBufferHeader); + + if (aDirection != OMX_DirInput) + { + return OMX_ErrorBadParameter; + } + + if (iBuffersToEmpty.Append(apBufferHeader) != KErrNone) + { + return OMX_ErrorInsufficientResources; + } + + // If we are not in an executing state or if the audio device is busy, delay playing back the buffer + if (iState != OMX_StateExecuting || iAudioDevice->IsActive()) + { + return OMX_ErrorNone; + } + + if (iPFHelper->BufferIndication() != KErrNone) + { + return OMX_ErrorInsufficientResources; + } + + return OMX_ErrorNone; + } + +OMX_ERRORTYPE COmxILPcmRendererProcessingFunction::MediaTimeIndication(const OMX_TIME_MEDIATIMETYPE& aMediaTimeType) + { + // Received a requested media time notification. + DEBUG_PRINTF5(_L8("MediaTimeIndication : eUpdateType = %d eState = %d xScale = %d nMediaTimestamp = %d "), + aMediaTimeType.eUpdateType, aMediaTimeType.eState, aMediaTimeType.xScale, aMediaTimeType.nMediaTimestamp); + + iPFHelper->MediaTimeIndication(aMediaTimeType); + return OMX_ErrorNone; + } + +OMX_BOOL +COmxILPcmRendererProcessingFunction::BufferRemovalIndication( + OMX_BUFFERHEADERTYPE* apBufferHeader, + OMX_DIRTYPE /* aDirection */) + { + DEBUG_PRINTF2(_L8("COmxILPcmRendererProcessingFunction::BufferRemovalIndication : BUFFER [%X]"), apBufferHeader); + + TBool headerDeletionResult = ETrue; + // Check if the buffer we want to remove is the one is being currently processed + if (iAudioDevice->IsActive() && iAudioDevice->GetCurrentBuffer() == apBufferHeader) + { + if (iPFHelper->CancelDevice() != KErrNone) + { + return OMX_FALSE; + } + + // if you cancel the audio device then you send the buffer to the other end of the tunnel + // so you shouldn't say that you had the buffer in the processing function in this situation. + headerDeletionResult = EFalse; + } + else + { + TInt headerIndexInArray = KErrNotFound; + if (KErrNotFound != + (headerIndexInArray = + iBuffersToEmpty.Find(apBufferHeader))) + { + iBuffersToEmpty.Remove(headerIndexInArray); + } + else if(KErrNotFound != + (headerIndexInArray = + iBuffersEmptied.Find(apBufferHeader))) + { + iBuffersEmptied.Remove(headerIndexInArray); + } + else + { + headerDeletionResult = EFalse; + } + } + + DEBUG_PRINTF2(_L8("BufferRemovalIndication : Removal result [%s]"), (headerDeletionResult ? "YES" : "NO")); + return (headerDeletionResult ? OMX_TRUE : OMX_FALSE); + } + +TInt +COmxILPcmRendererProcessingFunction::GetBytesPlayed() const + { + return iAudioDevice->GetBytesPlayed(); + } + + +COmxILPcmRendererProcessingFunction::CAudioDevice* COmxILPcmRendererProcessingFunction::CAudioDevice::NewL(COmxILPcmRendererProcessingFunction& aParent) + { + CAudioDevice* self = new (ELeave) CAudioDevice(aParent); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + +COmxILPcmRendererProcessingFunction::CAudioDevice::CAudioDevice(COmxILPcmRendererProcessingFunction& aParent) +: CActive(EPriorityUserInput), + iParent(aParent), + iSampleRate(KDefaultSampleRate), + iChannels(KDefaultNumberChannels), + iEncoding(KDefaultEncoding), + iVolume(KDefaultVolume), + iMuted(KDefaultMuted), + iBufferSize(KBufferSize), + iClockStateRunning(EFalse), + iPausedClockViaScale(EFalse), + iIsStartTimeFlagSet(EFalse) + { + CActiveScheduler::Add(this); + } + + +void COmxILPcmRendererProcessingFunction::CAudioDevice::ConstructL() + { + iCachedPlayBuffer.CreateL(0); + } + +COmxILPcmRendererProcessingFunction::CAudioDevice::~CAudioDevice() + { + delete iPeriodic; + Cancel(); + iCachedPlayBuffer.Close(); + } + +void COmxILPcmRendererProcessingFunction::CAudioDevice::RunL() + { + DEBUG_PRINTF(_L8("CAudioDevice::RunL : ")); + if (iStatus != KErrNone) + { + switch(iStatus.Int()) + { + case KErrUnderflow: + DEBUG_PRINTF(_L8("CAudioDevice::RunL : KErrUnderflow")); + iParent.iCallbacks.ErrorEventNotification(OMX_ErrorUnderflow); + break; + + case KErrOverflow: + DEBUG_PRINTF(_L8("CAudioDevice::RunL : KErrOverflow")); + iParent.iCallbacks.ErrorEventNotification(OMX_ErrorOverflow); + break; + + default: + DEBUG_PRINTF2(_L8("CAudioDevice::RunL : [%d] -> OMX_ErrorHardware"), iStatus.Int()); + iParent.iCallbacks.ErrorEventNotification(OMX_ErrorHardware); + }; + } + + ASSERT(iCurrentBuffer); + // Update the last value of bytes played... + iLastBytesPlayedValue = iSoundDevice.BytesPlayed(); + + // Return the emptied buffer to the IL Client or the tunnelled + // component.. + SignalBufferCompletion(iCurrentBuffer); + iCurrentBuffer = 0; + + // Make sure to clear the aggregated cache buffer, if it was used + iCachedPlayBuffer.Zero(); + } + +void COmxILPcmRendererProcessingFunction::CAudioDevice::SignalBufferCompletion( + OMX_BUFFERHEADERTYPE* apCurrentBuffer) + { + DEBUG_PRINTF2(_L8("CAudioDevice::SignalBufferCompletion : BUFFER = [%X]"), apCurrentBuffer); + + iParent.iBuffersEmptied.Append(apCurrentBuffer); + + // Process the queue only if in executing state... + if (iParent.iState == OMX_StateExecuting) + { + const TUint bufferCount = iParent.iBuffersEmptied.Count(); + OMX_BUFFERHEADERTYPE* pBufferHeader = 0; + for (TUint i=0; inFlags & OMX_BUFFERFLAG_EOS) + { + lastBuffer = ETrue; + } + + pBufferHeader->nFilledLen = 0; + iParent.iCallbacks.BufferDoneNotification(pBufferHeader, + pBufferHeader->nInputPortIndex, + OMX_DirInput); + if (lastBuffer) + { + pBufferHeader->nFlags |= OMX_BUFFERFLAG_EOS; + // propagate the EOS flag + iParent.iCallbacks.EventNotification( + OMX_EventBufferFlag, + KPCMRENDERER_APB0PORT_INDEX, + pBufferHeader->nFlags, + NULL); + } + } + + // Empty list... + iParent.iBuffersEmptied.Reset(); + } + + if (iParent.iBuffersToEmpty.Count() > 0) + { + DEBUG_PRINTF2(_L8("CAudioDevice::RunL : iBuffersToEmpty.Count = [%d]"), + iParent.iBuffersToEmpty.Count()); + iParent.iPFHelper->BufferIndication(); + } + + } + + +TBool COmxILPcmRendererProcessingFunction::CAudioDevice::ConstructAndStartUpdateTimer() + { + // Need this check if: + // - The component state transitions from Execution-Idle-Execution + // - The Clock's state transitions from Running-Stop-Running + if (iPeriodic == NULL) + { + iPeriodic = CPeriodic::New(EPriorityStandard); + + if (iPeriodic == NULL) + { + iParent.iCallbacks.ErrorEventNotification(OMX_ErrorInsufficientResources); + return EFalse; + } + } + + StartUpdateTimer(); + + return ETrue; + } + + +void COmxILPcmRendererProcessingFunction::CAudioDevice::ProcessNextBuffer() + { + if (iParent.iBuffersToEmpty.Count() == 0) + return; + + // To implement A/V Sync, we should start playing only once the clock component gives the appopriate command + // If the clock component is not available, we start playing immediately + // Since the PCM Renderer supplies the reference clock, we make sure to initialise the clock component with + // the reference when we receive the first buffer + + if (!iParent.iClientClockPortPtr->IsClockComponentAvailable()) + { + PlayData(); + return; + } + + OMX_BUFFERHEADERTYPE* bufferPtr = iParent.iBuffersToEmpty[0]; + + TBool bufferHasStartTime = bufferPtr->nFlags & OMX_BUFFERFLAG_STARTTIME; + + if (!iClockStateRunning) + { + if (!bufferHasStartTime) + { + // Connected with the Clock but OMX_BUFFERFLAG_STARTTIME isn't set; simply queue the buffer + return; + } + else + { + OMX_ERRORTYPE err = iParent.iClientClockPortPtr->SetStartTime(static_cast(bufferPtr->nTimeStamp)); + + if (err == OMX_ErrorNone) + { + // Clear the returning buffer's flag + bufferPtr->nFlags &= ~OMX_BUFFERFLAG_STARTTIME; + } + else + { + // NOTE: If the Clock is not in OMX_TIME_ClockStateWaitingForStartTime, + // currently SetStartTime will return OMX_ErrorIncorrectStateOperation + + // It is not the PCM renderer to flag a Clock component error; + // therefore, ignore the error. + // + // As the Clock is not in OMX_TIME_ClockStateRunning state, the Renderer needs + // to keep the OMX_BUFFERFLAG_STARTTIME in the first buffer until the Clock + // moves into OMX_TIME_ClockStateWaitingForStartTime or OMX_TIME_ClockStateRunning + // state + DEBUG_PRINTF2(_L8("CAudioDevice::ProcessNextBuffer SetStartTime() return %d"), err); + } + + // Update the iStartMediaTime + iParent.iStartMediaTime = static_cast(bufferPtr->nTimeStamp); + iIsStartTimeFlagSet = ETrue; + } + } // (!iClockStateRunning) + else + { + if (bufferHasStartTime) + { + // The Clock moves straight into OMX_TIME_ClockStateRunning state, + // clear the returning buffer's flag. + bufferPtr->nFlags &= ~OMX_BUFFERFLAG_STARTTIME; + } + + if (!iPlayData) + { + // Not allowed to render audio. This could be due to: + // - The renderer is waiting for a time completion notification from the Clock; + return; + } + + if (!iIsStartTimeFlagSet) + { + // As the StartTimeFlag is not mandatory; therefore it might be missing from the first audio buffer + // In such a case, we use the first buffer's timestamp as the StartMediaTime. + // + // NOTE: Since the Clock is running, calling SetStartTime() to the Clock is meaningless + + // Update the iStartMediaTime + iParent.iStartMediaTime = static_cast(bufferPtr->nTimeStamp); + iIsStartTimeFlagSet = ETrue; + + // Cross checking the Clock's media timestamp with iStartMediaTime to see + // data can be rendered straight away + if (!CanPlayNow()) + { + return; + } + + if (!ConstructAndStartUpdateTimer()) + { + return; + } + } + + DEBUG_PRINTF3(_L8("ProcessNextBuffer : iStartMediaTime = %d nTimeStamp = %d"), + I64LOW(iParent.iStartMediaTime), I64LOW(bufferPtr->nTimeStamp)); + + if (!iPausedClockViaScale) //if clock scale is zero then we are effectively paused + { + PlayData(); + } + } // else + } + + +void COmxILPcmRendererProcessingFunction::CAudioDevice::PlayData() + { + DEBUG_PRINTF(_L8("COmxILPcmRendererProcessingFunction::CAudioDevice::PlayData()++")); + if (iParent.iBuffersToEmpty.Count() == 0 || iSoundDevice.Handle() == 0 || IsActive()) + { + DEBUG_PRINTF(_L8("COmxILPcmRendererProcessingFunction::CAudioDevice::PlayData() nothing to play, or there is an outstanding request")); + return; + } + + iCurrentBuffer = iParent.iBuffersToEmpty[0]; + + iParent.iBuffersToEmpty.Remove(0); + + CMMFDataBuffer* mmfSrcBuffer = static_cast(iCurrentBuffer->pInputPortPrivate); + mmfSrcBuffer->Data().SetLength(iCurrentBuffer->nFilledLen); + + // Attenuate the amplitude of the samples if volume ramping has been changed + if (iRampAudioSample) + { + iRampAudioSample = RampAudio(mmfSrcBuffer); + } + + // First, check whether the buffer length is sufficient not to cause underflows in the device driver + TBool isFilledLengthSufficient = IsBufferLengthSufficient(iCurrentBuffer->nFilledLen); + // This variable defines whether we should send the data to the driver directly, or append it to an aggregated buffer + // We append the buffer instead of sending it directly, if (i) the buffer is too small, or (ii) we have already aggregated something. + TBool appendBuffer = (!isFilledLengthSufficient || iCachedPlayBuffer.Length() > 0) ? ETrue : EFalse; + if (!appendBuffer) + { + SendBufferToSoundDevice(mmfSrcBuffer->Data()); + } + else + { + // Check if we need to allocate the cached buffer + if (iCachedPlayBuffer.MaxLength() == 0) + { + // The RMdaDevSound shim allocates the shared chunk according to the maxLength of the descriptor it receives + // For this reason, we must allocate our buffer conservatively, otherwise the chunk may be insufficient and the RMdaDevSound shim will panic + TInt err = iCachedPlayBuffer.ReAlloc(GetMinBufferLength() + iCurrentBuffer->nAllocLen); + if (err != KErrNone) + { + iParent.iCallbacks.ErrorEventNotification(OMX_ErrorInsufficientResources); + return; + } + } + + iCachedPlayBuffer.Append(mmfSrcBuffer->Data()); + + // If we have sufficient length aggregated, play the cached buffer + // Also if this is the last buffer, we have to play it now, there's nothing left to cache + if (IsBufferLengthSufficient(iCachedPlayBuffer.Length()) || iCurrentBuffer->nFlags & OMX_BUFFERFLAG_EOS) + { + SendBufferToSoundDevice(iCachedPlayBuffer); + } + // If not, make sure to notify that we notify that the buffer is done for the OMX tunnnel, so that the port will be able to reuse it + else + { + SignalBufferCompletion(iCurrentBuffer); + iCurrentBuffer = NULL; + } + } + } + +void COmxILPcmRendererProcessingFunction::CAudioDevice::SendBufferToSoundDevice(TDes8& aBuffer) + { + ASSERT(!IsActive()); + iStatus = KRequestPending; + DEBUG_PRINTF2(_L8("COmxILPcmRendererProcessingFunction::CAudioDevice::SendBufferToSoundDevice() PlayData [%d]"), aBuffer.Length()); + iSoundDevice.PlayData(iStatus, aBuffer); + if (iResumeAfterNextPlay) + { + iResumeAfterNextPlay = EFalse; + iSoundDevice.ResumePlaying(); + } + SetActive(); + } + +TInt COmxILPcmRendererProcessingFunction::CAudioDevice::GetMinBufferLength() const + { + TInt minBufSize = KMinBufferMilliseconds * iSampleRate * iChannels / 1000; + if (iEncoding == RMdaDevSound::EMdaSoundEncoding16BitPCM) + { + minBufSize *= 2; // All other encodings use 1 byte per sample + } + return minBufSize; + } + +TBool COmxILPcmRendererProcessingFunction::CAudioDevice::IsBufferLengthSufficient(TInt aBufferLength) const + { + return aBufferLength >= GetMinBufferLength() ? ETrue : EFalse; + } + + +void COmxILPcmRendererProcessingFunction::CAudioDevice::ProcessMediaTimeIndication(OMX_TIME_MEDIATIMETYPE& aMediaTimeType) + { + switch (aMediaTimeType.eUpdateType) + { + case OMX_TIME_UpdateClockStateChanged: + { + HandleClockStateChanged(aMediaTimeType); + } + break; + + case OMX_TIME_UpdateRequestFulfillment: + { + // As the Clock was using another earlier start time; + // it is time for the PCM renderer to start playing the audio. + iPlayData = ETrue; + PlayData(); + + if (!ConstructAndStartUpdateTimer()) + { + return; + } + } + break; + + case OMX_TIME_UpdateScaleChanged: + { + HandleClockScaleChanged(aMediaTimeType); + } + break; + + default: + { + // Do nothing here + } + } + } + + +void COmxILPcmRendererProcessingFunction::CAudioDevice::StartUpdateTimer() + { + if (!iIsStartTimeFlagSet) + { + DEBUG_PRINTF(_L8("COmxILPcmRendererProcessingFunction::CAudioDevice::StartUpdateTimer() iIsStartTimeFlagSet == EFalse!!")); + return; + } + + // In case the timer already started + ASSERT(iPeriodic); + iPeriodic->Cancel(); + + TCallBack callback(UpdateClockMediaTime, this); + + // Start updating the Clock comp. every KPcmRendererTimePlayedDelay + iPeriodic->Start(KPcmRendererTimePlayedDelay, KPcmRendererTimePlayedDelay, callback); + } + + +TBool COmxILPcmRendererProcessingFunction::CAudioDevice::CanPlayNow() + { + if (iParent.iState != OMX_StateExecuting) + { + return EFalse; + } + + if (iClockMediaTime < iParent.iStartMediaTime) + { + // Once all required Clock's clients have responded, the clock component starts + // the media clock using the earliest client start time. + // Therefore, start playing the audio only when such time comes; send a time + // completion request to the Clock component + OMX_ERRORTYPE err = iParent.iClientClockPortPtr->MediaTimeRequest(NULL, iParent.iStartMediaTime, 0); + + if (err != OMX_ErrorNone) + { + DEBUG_PRINTF2(_L8("CAudioDevice::CanPlayNow() MediaTimeRequest() return %d"), err); + } + + // Indicate that processing a new buffer should not trigger a false start on playback. + iPlayData = EFalse; + return EFalse; + } + else if (iClockMediaTime > iParent.iStartMediaTime) + { + // NOTE: The spec. states that the clock should use the minimum of the received start times + // Therefore the Clock should NOT jump forwards with timestamp greater than the PCM + // Renderer iStartMediaTime + DEBUG_PRINTF3(_L8("CanPlayNow() nMediaTimestamp(%d) > iStartMediaTime(%d) IGNORE this use case"), + I64LOW(iClockMediaTime), I64LOW(iParent.iStartMediaTime)); + + // However if the Clock sends out a timestamp greater than the buffer's timestamp + // drop the buffers that fall outside the Clock specified MediaTimestamp; + + OMX_BUFFERHEADERTYPE* bufferPtr = iParent.iBuffersToEmpty[0]; + iParent.iBuffersToEmpty.Remove(0); + SignalBufferCompletion(bufferPtr); + bufferPtr = NULL; + + // Since the iStartMediaTime doesn't make sense anymore, reset iIsStartTimeFlagSet until the buffer's timestamp + // is within the the Clock's Media timestamp + iIsStartTimeFlagSet = EFalse; + return EFalse; + } + + return ETrue; + } + + +void COmxILPcmRendererProcessingFunction::CAudioDevice::HandleClockStateChanged(const OMX_TIME_MEDIATIMETYPE& aMediaTimeType) + { + switch (aMediaTimeType.eState) + { + case OMX_TIME_ClockStateRunning: + { + iClockMediaTime = aMediaTimeType.nMediaTimestamp; + + // There are two possibilities: + // case 1 - The clock goes straight into running and the PCM renderer processes the + // StateChanged notification prior the audio buffer + + if (iIsStartTimeFlagSet) + { + // OR + // case 2 - The PCM recieves the audio buffer, and the clock StateChanged notification + // comes later + + // Clear the returning buffer's flag. + iParent.iBuffersToEmpty[0]->nFlags &= ~OMX_BUFFERFLAG_STARTTIME; + + // Cross checking the Clock's media timestamp with iStartMediaTime to see + // data can be rendered straight away + if (!CanPlayNow()) + { + break; + } + + // Start playing the audio and start updating the Clock media time regularly + PlayData(); + + if (!ConstructAndStartUpdateTimer()) + { + return; + } + } + else if ((!iClockStateRunning) && (iParent.iState == OMX_StateExecuting)) + { + // The clock has gone to running but no start time flag was received. This would + // indicate that the client moved it straight from stopped to running. As we may + // have received buffers while in stopped state, we need to start processing + // them now. + DEBUG_PRINTF(_L8("HandleClockStateChanged() Gone to running without start time flag set")); + iClockStateRunning = ETrue; + ProcessNextBuffer(); + } + + // Otherwise, the queue is empty; + // + // NOTE: When !iIsStartTimeFlagSet && !iClockStateRunning would drop the incoming buffers; + // if the first buffer does not have the OMX_BUFFERFLAG_STARTTIME set + } + break; + + case OMX_TIME_ClockStateWaitingForStartTime: + { + if (iClockStateRunning) + { + DEBUG_PRINTF(_L8("HandleClockStateChanged() OMX_TIME_ClockStateRunning -> OMX_TIME_ClockStateWaitingForStartTime IGNORED!!")); + } + else + { + // Let's try to process buffers (if any). + ProcessNextBuffer(); + } + } + break; + + case OMX_TIME_ClockStateStopped: + { + if (iClockStateRunning) + { + // The Clock was in "Running" state but not anymore; stop the audio + if (IsActive()) + { + Cancel(); + } + else + { + DoCancel(); + } + + iPlayData = ETrue; + if (iIsStartTimeFlagSet) + { + iIsStartTimeFlagSet = EFalse; + iPeriodic->Cancel(); + } + } + + // Otherwise, ignore other possibilities + } + + break; + + default: + { + DEBUG_PRINTF2(_L8("HandleClockStateChanged() aMediaTimeType.eState = %d IGNORED!!"), aMediaTimeType.eState); + } + break; + } + + iClockStateRunning = (aMediaTimeType.eState == OMX_TIME_ClockStateRunning) ? ETrue : EFalse; + } + + +void COmxILPcmRendererProcessingFunction::CAudioDevice::HandleClockScaleChanged(const OMX_TIME_MEDIATIMETYPE& aMediaTimeType) + { + if (aMediaTimeType.xScale == 0) + { + PauseAudio(); + iPausedClockViaScale = ETrue; + DEBUG_PRINTF2(_L8("HandleClockScaleChanged() pausing iPausedClockViaScale = %d"), iPausedClockViaScale); + } + else if (aMediaTimeType.xScale == 0x10000) + { + // The scale is a Q16 value + iPausedClockViaScale = EFalse; + DEBUG_PRINTF2(_L8("HandleClockScaleChanged() resuming iPausedClockViaScale = %d"), iPausedClockViaScale); + + // If we are active then there is an outstanding PlayData() request so we need to call ResumePlaying(). + // However calling ResumePlaying() without an outstanding PlayData() request can cause the TimePLayed() API + // to go awry, so we should defer calling ResumePlaying() until the next PlayData() call. + if (IsActive()) + { + iSoundDevice.ResumePlaying(); + } + else + { + iResumeAfterNextPlay = ETrue; + } + iParent.iPFHelper->BufferIndication(); //handles the race condition where both 1) iSoundDevice was paused after the last PlayData() completed and before it was passed any data & 2) BufferIndication()s came in for all the IL buffers while in this paused state + StartUpdateTimer(); + } + + // TODO: Handle the rest of the scale values + + } + + +void COmxILPcmRendererProcessingFunction::CAudioDevice::DoCancel() + { + if (iSoundDevice.Handle() != 0) + { + iSoundDevice.CancelPlayData(); + iSoundDevice.FlushPlayBuffer(); + } + + if (iCurrentBuffer) + { + iCurrentBuffer->nFilledLen = 0; + + iParent.iCallbacks.BufferDoneNotification(iCurrentBuffer, + iCurrentBuffer->nInputPortIndex, + OMX_DirInput); + + iCachedPlayBuffer.Zero(); + iCurrentBuffer = NULL; + } + } + + +OMX_ERRORTYPE COmxILPcmRendererProcessingFunction::CAudioDevice::OpenDevice() + { + OMX_ERRORTYPE err = OMX_ErrorNone; + if(!iSoundDevice.Handle()) + { + if(KErrNone != iSoundDevice.Open(KSoundScTxUnit0)) + { + err = OMX_ErrorHardware; + } + } + return err; + } + +OMX_ERRORTYPE COmxILPcmRendererProcessingFunction::CAudioDevice::CloseDevice() + { + OMX_ERRORTYPE err = OMX_ErrorNone; + if(iSoundDevice.Handle()) + { + iSoundDevice.Close(); + iResumeAfterNextPlay = EFalse; + } + return err; + } + +OMX_ERRORTYPE COmxILPcmRendererProcessingFunction::CAudioDevice::Execute() + { + if(!iSoundDevice.Handle()) + { + if(KErrNone != iSoundDevice.Open()) + { + return OMX_ErrorHardware; + } + } + + if(iParent.iState == OMX_StatePause) + { + // Now we can send BufferDone notifications for each emptied + // buffer... + iParent.FlushBufferList(iParent.iBuffersEmptied); + // If we are active then there is an outstanding PlayData() request so we need to call ResumePlaying(). + // However calling ResumePlaying() without an outstanding PlayData() request can cause the TimePLayed() API + // to go awry, so we should defer calling ResumePlaying() until the next PlayData() call. + if (IsActive()) + { + iSoundDevice.ResumePlaying(); + } + else + { + iResumeAfterNextPlay = ETrue; + } + StartUpdateTimer(); + } + else + { + // Set play format + RMdaDevSound::TCurrentSoundFormatBuf buf; + iSoundDevice.GetPlayFormat(buf); + buf().iRate = iSampleRate; + buf().iChannels = iChannels; + buf().iBufferSize = iBufferSize; + buf().iEncoding = iEncoding; + if(KErrNone != iSoundDevice.SetPlayFormat(buf)) + { + return OMX_ErrorHardware; + } + + if (iMuted) + { + iSoundDevice.SetVolume(0); + } + else + { + iSoundDevice.SetVolume(iVolume); + } + } + + iParent.iState = OMX_StateExecuting; + + // Make sure to start processing of queued up buffers (if any) + if (!IsActive() && iParent.iBuffersToEmpty.Count() > 0) + { + ProcessNextBuffer(); + } + + return OMX_ErrorNone; + } + +OMX_ERRORTYPE COmxILPcmRendererProcessingFunction::CAudioDevice::MoveToPausedState() + { + iParent.iState = OMX_StatePause; + PauseAudio(); + + return OMX_ErrorNone; + } + +OMX_ERRORTYPE COmxILPcmRendererProcessingFunction::CAudioDevice::Stop() + { + if(iParent.iState == OMX_StateExecuting || iParent.iState == OMX_StatePause) + { + // Cancel and flush the device driver + Cancel(); + + // Cancel timer to stop calling RSoundSc::TimePlayed() + if (iIsStartTimeFlagSet) + { + ASSERT(iPeriodic); + iPeriodic->Cancel(); + } + + iParent.iState = OMX_StateIdle; + + // If the audio device is still open, store the last value of bytes + // played before closing the audio device... + if(iSoundDevice.Handle() != 0) + { + iLastBytesPlayedValue = iSoundDevice.BytesPlayed(); + // Close the sound device + iSoundDevice.Close(); + iResumeAfterNextPlay = EFalse; + } + } + + iClockStateRunning = EFalse; + iIsStartTimeFlagSet = EFalse; + iPlayData = ETrue; + + return OMX_ErrorNone; + } + + +void COmxILPcmRendererProcessingFunction::CAudioDevice::PauseAudio() + { + // Cancel timer to stop calling RSoundSc::TimePlayed() + if (iIsStartTimeFlagSet) + { + ASSERT(iPeriodic); + iPeriodic->Cancel(); + } + + if (iSoundDevice.Handle()) + { + iSoundDevice.PausePlayBuffer(); + } + } + + +OMX_ERRORTYPE COmxILPcmRendererProcessingFunction::CAudioDevice::SetChannels(TUint aChannels) + { + iChannels = aChannels; + return SetPlayFormat(); + } + +OMX_ERRORTYPE COmxILPcmRendererProcessingFunction::CAudioDevice::SetSampleRate(TUint aSampleRate) + { + iSampleRate = aSampleRate; + return SetPlayFormat(); + } + +TInt ConvertOmxToSymbianVolume(TInt aVolume) + { + // OpenMax volume is in millibels while Symbian volume is in 0.5 db increments in the [0..255] range + // We divide by 50 as 0.5 dB = 50 millibells + TInt res = KMedianVolume + aVolume / 50; + if (res < 0) + return 0; + if (res > KMaxVolume) + return KMaxVolume; + return res; + } + +OMX_ERRORTYPE COmxILPcmRendererProcessingFunction::CAudioDevice::SetVolume(TInt aVolume) + { + iVolume = ConvertOmxToSymbianVolume(aVolume); + if ((!iMuted) && (iSoundDevice.Handle() != 0)) + { + iSoundDevice.SetVolume(iVolume); + } + return OMX_ErrorNone; + } + +OMX_ERRORTYPE COmxILPcmRendererProcessingFunction::CAudioDevice::SetVolumeRamp(const TTimeIntervalMicroSeconds& aRampDuration) + { + iVolumeRamp = aRampDuration; + if(iVolumeRamp.Int64() != 0) + { + iRampAudioSample = ETrue; + ConfigAudioRamper( + iVolumeRamp.Int64()); + } + else + { + iRampAudioSample = EFalse; + } + return OMX_ErrorNone; + } + +OMX_ERRORTYPE COmxILPcmRendererProcessingFunction::CAudioDevice::SetMuted(TBool aMuted) + { + iMuted = aMuted; + + if (iSoundDevice.Handle() == 0) + { + // Just cache the value; the value will be set once the the device is opened + return OMX_ErrorNone; + } + + if (iMuted) + { + iSoundDevice.SetVolume(0); + } + else + { + iSoundDevice.SetVolume(iVolume); + } + return OMX_ErrorNone; + } + +OMX_ERRORTYPE COmxILPcmRendererProcessingFunction::CAudioDevice::SetPlayFormat() + { + if (iParent.iState == OMX_StateExecuting) + { + RMdaDevSound::TCurrentSoundFormatBuf buf; + iSoundDevice.GetPlayFormat(buf); + buf().iRate = iSampleRate; + buf().iChannels = iChannels; + buf().iBufferSize = iBufferSize; + buf().iEncoding = iEncoding; + if(KErrNone != iSoundDevice.SetPlayFormat(buf)) + { + return OMX_ErrorHardware; + } + } + return OMX_ErrorNone; + } + +OMX_BUFFERHEADERTYPE* COmxILPcmRendererProcessingFunction::CAudioDevice::GetCurrentBuffer() + { + return iCurrentBuffer; + } + +TInt COmxILPcmRendererProcessingFunction::CAudioDevice::GetBytesPlayed() + { + if(iSoundDevice.Handle() != 0) + { + return iSoundDevice.BytesPlayed(); + } + + return iLastBytesPlayedValue; + } + +void COmxILPcmRendererProcessingFunction::CAudioDevice::ConfigAudioRamper(TInt64 aRampTime) + { + iRampSamples = I64LOW(((TInt64(iSampleRate) * aRampTime) /1000000 )); // Add this + iRampSamplesLeft = iRampSamples; + iRampIncr = 0; + iSkip = ETrue; + } + +TBool COmxILPcmRendererProcessingFunction::CAudioDevice::RampAudio(CMMFDataBuffer* aBuffer) + { + TInt i=0; + TInt length = aBuffer->Data().Length()>>1; + if (length == 0) + { + return EFalse; + } + + TInt16* sample = REINTERPRET_CAST(TInt16*,&aBuffer->Data()[0]); + TInt64 theResult(0); + while ((i < length) && (iRampIncr < iRampSamples)) + { + theResult = sample[i]; + theResult *= iRampIncr; + theResult /= iRampSamples; + sample[i] = STATIC_CAST(TInt16, I64LOW(theResult) ); + + if ((iChannels == 1) || (!iSkip)) + { + iRampIncr++; + } + iSkip = !iSkip; + i++; + } + + if (iRampIncr < iRampSamples) + return ETrue; + else + return EFalse; + } + + +TInt COmxILPcmRendererProcessingFunction::CAudioDevice::UpdateClockMediaTime(TAny* aPtr) + { + CAudioDevice* ptr = (CAudioDevice*)aPtr; + TTimeIntervalMicroSeconds playedTime(0); + + if (ptr->iSoundDevice.GetTimePlayed(playedTime) != KErrNone) + { + ptr->iParent.iCallbacks.ErrorEventNotification(OMX_ErrorHardware); + return EFalse; + } + + OMX_ERRORTYPE err; + + // Update the clock component audio reference clock + err = ptr->iParent.iClientClockPortPtr->SetAudioReference(ptr->iParent.iStartMediaTime + playedTime.Int64()); + + if (err != OMX_ErrorNone) + { + ptr->iParent.iCallbacks.ErrorEventNotification(err); + return EFalse; + } + + DEBUG_PRINTF2(_L8("CAudioDevice::UpdateClockMediaTime : playedTime = %d"), + I64LOW(playedTime.Int64())); + + return ETrue; + } + + +void COmxILPcmRendererProcessingFunction::CAudioDevice::ProcessParamIndication(const OMX_AUDIO_PARAM_PCMMODETYPE& aPcmModeType) + { + if(SetChannels(aPcmModeType.nChannels) != OMX_ErrorNone) + { + iParent.iCallbacks.ErrorEventNotification(OMX_ErrorHardware); + return; + } + + if (SetSampleRate(aPcmModeType.nSamplingRate) != OMX_ErrorNone) + { + iParent.iCallbacks.ErrorEventNotification(OMX_ErrorHardware); + return; + } + } + +COmxILPcmRendererProcessingFunction::CPFHelper* COmxILPcmRendererProcessingFunction::CPFHelper::NewL(CAudioDevice& aAudioDevice) + { + CPFHelper* self = new (ELeave) CPFHelper(aAudioDevice); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + +COmxILPcmRendererProcessingFunction::CPFHelper::CPFHelper(CAudioDevice& aAudioDevice) +: CActive(EPriorityUserInput), + iAudioDevice(aAudioDevice) + { + } + +void COmxILPcmRendererProcessingFunction::CPFHelper::ConstructL() + { + CActiveScheduler::Add(this); + User::LeaveIfError(iCallerSemaphore.CreateGlobal(KNullDesC, 0)); + User::LeaveIfError(iMsgQueue.CreateLocal(KMaxMsgQueueEntries)); + iMsgQueue.NotifyDataAvailable(iStatus); + RThread thisThread; + iHelperThreadId = thisThread.Id(); + thisThread.Close(); + SetActive(); + } + +COmxILPcmRendererProcessingFunction::CPFHelper::~CPFHelper() + { + Cancel(); + iMsgQueue.Close(); + iCallerSemaphore.Close(); + } + +void COmxILPcmRendererProcessingFunction::CPFHelper::RunL() + { + + TProcMessage msg; + while (iMsgQueue.Receive(msg)==KErrNone) + { + switch (msg.iType) + { + case EOpenDevice: + { + iAudioDevice.OpenDevice(); + break; + } + case ECloseDevice: + { + iAudioDevice.CloseDevice(); + break; + } + case ECloseDeviceOnError: + { + iAudioDevice.Cancel(); + iAudioDevice.CloseDevice(); + iCallerSemaphore.Signal(); + break; + } + case EExecuteCommand: + { + iAudioDevice.Execute(); + break; + } + + case EStopCommand: + { + iAudioDevice.Stop(); + break; + } + + case ECancelCommand: + { + iAudioDevice.Cancel(); + break; + } + + case EBufferIndication: + { + iAudioDevice.ProcessNextBuffer(); + break; + } + case EMediaTimeIndication: + { + iAudioDevice.ProcessMediaTimeIndication(msg.iMediaTimeType); + break; + } + case EParamIndication: + { + iAudioDevice.ProcessParamIndication(msg.iPcmModeType); + break; + } + case ESetVolumeRamp: + { + iAudioDevice.SetVolumeRamp(TTimeIntervalMicroSeconds(msg.iRampDuration)); + break; + } + case ESetVolume: + { + iAudioDevice.SetVolume(msg.iVolumeValue); + break; + } + case ESetMuted: + { + iAudioDevice.SetMuted(msg.iMuted); + break; + } + default: + { + break; + } + } + } + DEBUG_PRINTF2(_L8("COmxILPcmRendererProcessingFunction::CPFHelper::RunL : msg.iType[%d]"), msg.iType); + // setup for next callbacks + iMsgQueue.NotifyDataAvailable(iStatus); + SetActive(); + } + +void COmxILPcmRendererProcessingFunction::CPFHelper::DoCancel() + { + if (iMsgQueue.Handle()!=NULL) + { + iMsgQueue.CancelDataAvailable(); + } + } + +TInt COmxILPcmRendererProcessingFunction::CPFHelper::OpenDevice() + { + TProcMessage message; + message.iType = EOpenDevice; + return iMsgQueue.Send(message); + } + +TInt COmxILPcmRendererProcessingFunction::CPFHelper::CloseDevice() + { + DEBUG_PRINTF2(_L8("COmxILPcmRendererProcessingFunction::CPFHelper::CloseDevice : IsActive[%s]"), + (IsActive() ? "YES" : "NO")); + TProcMessage message; + message.iType = ECloseDevice; + return iMsgQueue.Send(message); + } + +TInt COmxILPcmRendererProcessingFunction::CPFHelper::CloseDeviceOnError() + { + DEBUG_PRINTF2(_L8("COmxILPcmRendererProcessingFunction::CPFHelper::CloseDeviceOnError : IsActive[%d]"), (IsActive() ? 1 : 0)); + + RThread thisThread; + if (thisThread.Id() == iHelperThreadId) + { + // Just do it... + iAudioDevice.Cancel(); + iAudioDevice.CloseDevice(); + } + else + { + + TProcMessage message; + message.iType = ECloseDeviceOnError; + TInt error = iMsgQueue.Send(message); + if (KErrNone != error) + { + // only wait if the message was sent into the queue... + iCallerSemaphore.Wait(); + } + } + + thisThread.Close(); + + return KErrNone; + + } + +TInt COmxILPcmRendererProcessingFunction::CPFHelper::Execute() + { + DEBUG_PRINTF2(_L8("CPFHelper::Execute : IsActive[%s]"), (IsActive() ? "YES" : "NO")); + TProcMessage message; + message.iType = EExecuteCommand; + return iMsgQueue.Send(message); + } + +TInt COmxILPcmRendererProcessingFunction::CPFHelper::Stop() + { + DEBUG_PRINTF2(_L8("CPFHelper::Stop : IsActive[%s]"), (IsActive() ? "YES" : "NO")); + TProcMessage message; + message.iType = EStopCommand; + return iMsgQueue.Send(message); + } + +TInt COmxILPcmRendererProcessingFunction::CPFHelper::CancelDevice() + { + DEBUG_PRINTF2(_L8("CPFHelper::CancelDevice : IsActive[%s]"), (IsActive() ? "YES" : "NO")); + TProcMessage message; + message.iType = ECancelCommand; + return iMsgQueue.Send(message); + } + +TInt COmxILPcmRendererProcessingFunction::CPFHelper::BufferIndication() + { + DEBUG_PRINTF2(_L8("CPFHelper::BufferIndication : IsActive[%s]"), (IsActive() ? "YES" : "NO")); + TProcMessage message; + message.iType = EBufferIndication; + return iMsgQueue.Send(message); + } + +TInt COmxILPcmRendererProcessingFunction::CPFHelper::MediaTimeIndication(const OMX_TIME_MEDIATIMETYPE& aMediaTimeType) + { + DEBUG_PRINTF2(_L8("CPFHelper::MediaTimeIndication : IsActive[%s]"), (IsActive() ? "YES" : "NO")); + TProcMessage message; + message.iType = EMediaTimeIndication; + message.iMediaTimeType.eUpdateType = aMediaTimeType.eUpdateType; + message.iMediaTimeType.eState = aMediaTimeType.eState; + message.iMediaTimeType.xScale = aMediaTimeType.xScale; + message.iMediaTimeType.nMediaTimestamp = aMediaTimeType.nMediaTimestamp; + + return iMsgQueue.Send(message); + } + +TInt COmxILPcmRendererProcessingFunction::CPFHelper::ParamIndication(const OMX_AUDIO_PARAM_PCMMODETYPE* aPcmModeType) + { + DEBUG_PRINTF2(_L8("CPFHelper::ParamIndication : IsActive[%s]"), (IsActive() ? "YES" : "NO")); + TProcMessage message; + message.iType = EParamIndication; + message.iPcmModeType.nChannels = aPcmModeType->nChannels; + message.iPcmModeType.nSamplingRate = aPcmModeType->nSamplingRate; + + return iMsgQueue.Send(message); + } + +TInt COmxILPcmRendererProcessingFunction::CPFHelper::SetVolumeRamp(const OMX_U64 aRampDuration) + { + DEBUG_PRINTF2(_L8("CPFHelper::SetVolumeRamp : IsActive[%s]"), (IsActive() ? "YES" : "NO")); + TProcMessage message; + message.iType = ESetVolumeRamp; + message.iRampDuration = aRampDuration; + + return iMsgQueue.Send(message); + } + +TInt COmxILPcmRendererProcessingFunction::CPFHelper::SetVolume(const OMX_S32 aVolumeValue) + { + DEBUG_PRINTF2(_L8("CPFHelper::SetVolume : IsActive[%s]"), (IsActive() ? "YES" : "NO")); + TProcMessage message; + message.iType = ESetVolume; + message.iVolumeValue = aVolumeValue; + + return iMsgQueue.Send(message); + } + +TInt COmxILPcmRendererProcessingFunction::CPFHelper::SetMuted(const OMX_BOOL aMuted) + { + DEBUG_PRINTF2(_L8("CPFHelper::SetMuted : IsActive[%s]"), (IsActive() ? "YES" : "NO")); + TProcMessage message; + message.iType = ESetMuted; + message.iMuted = aMuted; + + return iMsgQueue.Send(message); + }