diff -r 000000000000 -r 1bce908db942 multimediacommscontroller/mmccdtmfpayloadformat/src/dtmfpayloadformatwrite.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/multimediacommscontroller/mmccdtmfpayloadformat/src/dtmfpayloadformatwrite.cpp Tue Feb 02 01:04:58 2010 +0200 @@ -0,0 +1,1108 @@ +/* +* Copyright (c) 2006-2008 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: Contains functionality for DTMF digit encoding and sending. +* +*/ + + + + +// INCLUDES +#include +#include "mmccnetworksettings.h" +#include "dtmfpayloadformatwrite.h" +#include "dtmfpayloadencoder.h" +#include "dtmfeventpayloadinfo.h" +#include "dtmftonepayloadinfo.h" +#include "mccrtpdatasink.h" +#include "mccinternaldef.h" +#include "rtpdatasink.h" +#include "mccrtpmediaclock.h" +#include "mccinternalevents.h" +#include "dtmfhighresolutiontimer.h" + +// CONSTANTS +const TUint8 KValidDTMFDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'A', 'B', 'C', 'D', '*', '#', 'p', 'w' }; + +const TInt KSignalInbandDtmf = 1; +const TInt KSignalOutbandDtmf = 2; + +const TInt KDurationNotApplicable = 1000000000; + +// Pause in DTMF string sending when next digit to handle is 'p' char +const TUint KDTMFPauseLengthInUs = 2500000; +const TInt KNumOfFinalPackets = 3; +const TReal KUsToSecFactor = 0.000001; +const TReal KMsToSecFactor = 0.001; +const TUint KDtmfDefSampleRate = 8000; +const TUint KFactor1000 = 1000; +const TInt KDefaultTickPeriodInMs = 1; + +// ======== MEMBER FUNCTIONS ======== + + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::CDTMFPayloadFormatWrite +// C++ default constructor can NOT contain any code, that +// might leave. +// --------------------------------------------------------------------------- +// +CDTMFPayloadFormatWrite::CDTMFPayloadFormatWrite() + : + iSampleRate( KDtmfDefSampleRate ), + iTickPeriod( KDefaultTickPeriodInMs ) + { + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::CDTMFPayloadFormatWrite") ); + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::ConstructL +// Symbian 2nd phase constructor can leave. +// --------------------------------------------------------------------------- +// +void CDTMFPayloadFormatWrite::ConstructL( MDataSink* aSink ) + { + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::ConstructL") ); + + iFourCC = TFourCC( KMccFourCCIdDTMF ); + + // Set data sink + CMccRtpDataSink* tmp = static_cast( aSink ); + iRtpDataSink = static_cast( tmp ); + iClip = aSink; + + // Allocate buffer for payload encoder + iSinkBuffer = CMMFDataBuffer::NewL( KDTMFDefaultPayloadSize ); + iPayloadEncoder = CDTMFPayloadEncoder::NewL(); + + // Initialize state machine + iStateMachine = CDtmfEncStateMachine::NewL( *this ); + + TUint validDigitCount = sizeof ( KValidDTMFDigits ) / sizeof ( TUint8 ); + for ( TUint i = 0; i < validDigitCount; i++ ) + { + iValidDigits.Append( KValidDTMFDigits[ i ] ); + } + + TInt err = HAL::Get( HAL::ENanoTickPeriod, iTickPeriod ); + if ( KErrNone != err ) + { + DP_DTMF_WRITE2( _L("CDTMFPayloadFormatWrite::ConstructL HAL ERR: %d"), + err ); + } + + iTickPeriod = TInt( iTickPeriod * KMsToSecFactor ); + iSendTimer = CDtmfHighResTimer::NewL( CActive::EPriorityHigh ); + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::NewL +// Two-phased constructor. +// --------------------------------------------------------------------------- +// +CDTMFPayloadFormatWrite* CDTMFPayloadFormatWrite::NewL( MDataSink* aSink ) + { + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::NewL") ); + + if ( !aSink ) + { + User::Leave ( KErrArgument ); + } + + if ( KMccRtpSinkUid != aSink->DataSinkType() ) + { + User::Leave ( KErrNotSupported ); + } + + CDTMFPayloadFormatWrite* self = new( ELeave ) CDTMFPayloadFormatWrite; + + CleanupStack::PushL( self ); + self->ConstructL( aSink ); + CleanupStack::Pop( self ); + + return self; + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::~CDTMFPayloadFormatWrite +// Destructor. +// --------------------------------------------------------------------------- +// +CDTMFPayloadFormatWrite::~CDTMFPayloadFormatWrite() + { + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::~CDTMFPayloadFormatWrite") ); + + delete iSinkBuffer; + delete iPayloadEncoder; + delete iSendTimer; + delete iStateMachine; + iEventHandler = NULL; + iRtpDataSink = NULL; + + // Media clock is not owned + if ( iRtpMediaClock ) + { + iRtpMediaClock->UnregisterMediaFormat( iKey ); + iRtpMediaClock = NULL; + } + + iSendBuffer.Close(); + iValidDigits.Close(); + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::HandleTimerEventL +// STATIC METHOD. Passes timer events to the class instance interested +// about them. +// --------------------------------------------------------------------------- +// +TInt CDTMFPayloadFormatWrite::HandleTimerEventL( TAny* aObjectPtr ) + { + if ( aObjectPtr ) + { + return static_cast( aObjectPtr )-> + DoHandleTimerEventL(); + } + else + { + return KErrArgument; + } + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::SinkThreadLogon +// Passes the logon command to the sink clip. +// --------------------------------------------------------------------------- +// +TInt CDTMFPayloadFormatWrite::SinkThreadLogon( + MAsyncEventHandler& aEventHandler ) + { + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::SinkThreadLogon") ); + + iEventHandler = &aEventHandler; + return iClip->SinkThreadLogon( aEventHandler ); + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::SinkThreadLogoff +// Passes the logoff command to the sink clip. +// --------------------------------------------------------------------------- +// +void CDTMFPayloadFormatWrite::SinkThreadLogoff() + { + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::SinkThreadLogoff") ); + + iClip->SinkThreadLogoff(); + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::CanCreateSinkBuffer +// EmptyBufferL() is not used. +// --------------------------------------------------------------------------- +// +TBool CDTMFPayloadFormatWrite::CanCreateSinkBuffer() + { + return EFalse; + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::CreateSinkBufferL +// Create a sink buffer. +// --------------------------------------------------------------------------- +// +CMMFBuffer* CDTMFPayloadFormatWrite::CreateSinkBufferL( + TMediaId /*aMediaId*/, + TBool& /*aReference*/ ) + { + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::CreateSinkBufferL") ); + + User::Leave( KErrNotSupported ); + // PC_LINT #527, for compiler warning + return NULL; + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::SetSinkDataTypeCode +// Set the sink data type to the given FourCC code for the given media. +// --------------------------------------------------------------------------- +// +TInt CDTMFPayloadFormatWrite::SetSinkDataTypeCode( TFourCC aSinkFourCC, + TMediaId aMediaId ) + { + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::SetSinkDataTypeCode") ); + + if ( KUidMediaTypeAudio != aMediaId.iMediaType ) + { + return KErrNotSupported; + } + + iFourCC = aSinkFourCC; + return KErrNone; + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::SinkDataTypeCode +// Return the sink data type (four CC code) for the given media. +// --------------------------------------------------------------------------- +// +TFourCC CDTMFPayloadFormatWrite::SinkDataTypeCode( TMediaId aMediaId ) + { + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::SinkDataTypeCode") ); + + if ( KUidMediaTypeAudio == aMediaId.iMediaType ) + { + return iFourCC; + } + else + { + // Defaults to 'NULL' fourCC + return TFourCC(); + } + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::FrameTimeInterval +// Gets audio frame size in milliseconds. +// --------------------------------------------------------------------------- +// +TTimeIntervalMicroSeconds CDTMFPayloadFormatWrite::FrameTimeInterval( + TMediaId aMediaType ) const + { + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::FrameTimeInterval") ); + + if ( KUidMediaTypeAudio == aMediaType.iMediaType ) + { + return TTimeIntervalMicroSeconds( TInt64( 0 ) ); + } + else + { + return TTimeIntervalMicroSeconds( TInt64( 0 ) ); + } + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::Duration +// Gets the duration of the sink clip for the specified media ID. +// --------------------------------------------------------------------------- +// +TTimeIntervalMicroSeconds CDTMFPayloadFormatWrite::Duration( + TMediaId /*aMediaType*/ ) const + { + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::Duration") ); + + return TTimeIntervalMicroSeconds( TInt64( KDurationNotApplicable ) ); + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::BufferEmptiedL +// Called by RTP data sink. Indicates that DTMF data is sent to the network. +// --------------------------------------------------------------------------- +// +void CDTMFPayloadFormatWrite::BufferEmptiedL( CMMFBuffer* /*aBuffer*/ ) + { + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::BufferEmptiedL") ); + + // Nothing to do + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::SinkPrimeL +// Passes prime transition to the RTP data sink. +// --------------------------------------------------------------------------- +// +void CDTMFPayloadFormatWrite::SinkPrimeL() + { + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::SinkPrimeL") ); + + iClip->SinkPrimeL(); + + // If the possible state machine in clip does not leave, let dtmf payload + // formatter also allow re-priming from any state. + CancelDTMFStringSending(); + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::SinkPlayL +// Passes play transition to the RTP data sink and resets state variables. +// --------------------------------------------------------------------------- +// +void CDTMFPayloadFormatWrite::SinkPlayL() + { + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::SinkPlayL") ); + + iClip->SinkPlayL(); + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::SinkPauseL +// Passes pause transition to the RTP data sink. +// --------------------------------------------------------------------------- +// +void CDTMFPayloadFormatWrite::SinkPauseL() + { + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::SinkPauseL") ); + + CancelDTMFStringSending(); + iClip->SinkPauseL(); + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::SinkStopL +// Passes stop transition to the RTP data sink. +// --------------------------------------------------------------------------- +// +void CDTMFPayloadFormatWrite::SinkStopL() + { + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::SinkStopL") ); + + CancelDTMFStringSending(); + iClip->SinkStopL(); + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::EmptyBufferL +// Not used because DTMF payload formatter generates DTMF independently. +// --------------------------------------------------------------------------- +// +void CDTMFPayloadFormatWrite::EmptyBufferL( CMMFBuffer* /*aBuffer*/, + MDataSource* /*aSupplier*/, + TMediaId /*aMediaId*/ ) + { + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::EmptyBufferL") ); + + User::Leave( KErrNotSupported ); + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::PayloadFormat +// Returns payload format used in DTMF encoding. +// --------------------------------------------------------------------------- +// +TDTMFPayloadFormat CDTMFPayloadFormatWrite::PayloadFormat() const + { + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::PayloadFormat") ); + + return iPayloadFormat; + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::SetPayloadFormat +// Sets payload format used in DTMF encoding. +// --------------------------------------------------------------------------- +// +TInt CDTMFPayloadFormatWrite::SetPayloadFormat( + TDTMFPayloadFormat aPayloadFormat ) + { + DP_DTMF_WRITE2( _L("CDTMFPayloadFormatWrite::SetPayloadFormat - Format: %d"), aPayloadFormat ); + + TInt err = iPayloadEncoder->SetPayloadFormat( aPayloadFormat ); + if ( KErrNone == err ) + { + iPayloadFormat = aPayloadFormat; + } + + return err; + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::StartDTMFToneL +// Starts a timer which sends specified DTMF digit periodically until +// StopDTMFTone() is called. +// --------------------------------------------------------------------------- +// +void CDTMFPayloadFormatWrite::StartDTMFToneL( const TChar aTone ) + { + TUint32 tickCount( User::NTickCount() ); + + DP_DTMF_WRITE3( _L("CDTMFPayloadFormatWrite::StartDTMFToneL aTone = %c, tick = %u"), + static_cast( aTone ), tickCount ); + + DP_DTMF_WRITE2( _L("CDTMFPayloadFormatWrite::StartDTMFToneL enc state %d"), + iStateMachine->State() ); + + __ASSERT_ALWAYS( IsValidDigit( aTone ), User::Leave( KErrArgument ) ); + + iSendingMode = EModeManual; + + TSendBufferElement element( aTone, tickCount ); + iSendBuffer.AppendL( element ); + + if ( EStateSendingIdle == iStateMachine->State() ) + { + if ( KSignalOutbandDtmf == iGenerationMode ) + { + iSinkBuffer->Data().FillZ( KDTMFDefaultPayloadSize ); + } + + iStateMachine->ChangeStateTo( EStateEncodeNextDigit ); + } + else + { + DP_DTMF_WRITE( + _L("CDTMFPayloadFormatWrite::StartDTMFToneL - REQ BUFFERED") ); + } + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::StopDTMFToneL +// Stops the timer for DTMF digit update packet sending. Starts a timer for +// sending Final Packet three times according to RFC 2833, section 2.5.1.4. +// --------------------------------------------------------------------------- +// +void CDTMFPayloadFormatWrite::StopDTMFToneL() + { + TUint32 tickCount( User::NTickCount() ); + DP_DTMF_WRITE2( _L("CDTMFPayloadFormatWrite::StopDTMFTone, tick = %u"), + tickCount ); + + // set stop time to the first unupdated element + TBool elementFound( EFalse ); + TInt index( 0 ); + while ( !elementFound && index < iSendBuffer.Count() ) + { + if ( iSendBuffer[index].StopTime() == 0 ) + { + iSendBuffer[index].SetStopTime( tickCount ); + if ( iSendBuffer[index].StopTime() == 0 ) + { + // mark stop time as updated to prevent update of the same + // element in future + iSendBuffer[index].SetStopTime( 1 ); + } + + elementFound = ETrue; + } + + index++; + } + + if ( EStateToneOn == iStateMachine->State() ) + { + if ( KSignalOutbandDtmf == iGenerationMode ) + { + if ( EDTMFPayloadFormatEvent == iPayloadFormat + || EDTMFPayloadFormatRedEvents == iPayloadFormat ) + { + // Send final packet with "E" bit set. According to + // RFC 2833 2.5.1.4 final packet SHOULD be sent three times. + iPayloadEncoder + ->UpdateEventPayload( ETrue, iToneDuration, iSinkBuffer ); + iStateMachine->ChangeStateTo( EStateToneOff ); + } + else if ( EDTMFPayloadFormatTone == iPayloadFormat ) + { + // Tone Payload Format doesn't have final packet redundancy + iPayloadEncoder->UpdateTonePayload( iToneDuration, + iSinkBuffer ); + } + else + { + // Not acceptable payload format + } + } + else + { + iStateMachine->ChangeStateTo( EStateToneOff ); + } + } + else + { + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::StopDTMFTone OUT-OF-SYNC") ); + } + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::SendDTMFTonesL +// Starts to send DTMF string incrementally until the string is sent. +// --------------------------------------------------------------------------- +// +void CDTMFPayloadFormatWrite::SendDTMFTonesL( const TDesC8& aTones ) + { + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::SendDTMFTonesL") ); + + TInt digitCount( aTones.Length() ); + __ASSERT_ALWAYS( digitCount != 0, User::Leave( KErrArgument ) ); + __ASSERT_ALWAYS( IsValidDigits( aTones ), User::Leave( KErrArgument ) ); + __ASSERT_ALWAYS( !iSendTimer->IsActive(), User::Leave( KErrNotReady ) ); + + iSendingMode = EModeAutomatic; + + iSendBuffer.Reset(); + for ( TInt i = 0; i < digitCount; i++ ) + { + // in automatic mode default durations for tones are used + TSendBufferElement element( aTones[i], 0 ); + iSendBuffer.AppendL( element ); + } + + iSinkBuffer->Data().FillZ( KDTMFDefaultPayloadSize ); + iStateMachine->ChangeStateTo( EStateEncodeNextDigit ); + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::NotifyStopInDTMFString +// Request to notify client when stop in DTMF string is +// encountered ('w' char). +// --------------------------------------------------------------------------- +// +void CDTMFPayloadFormatWrite::NotifyStopInDTMFString( TBool aFlag ) + { + DP_DTMF_WRITE2( _L("CDTMFPayloadFormatWrite::NotifyStopInDTMFString - Flag: %d"), aFlag ); + + iNotifyStopReq = aFlag; + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::ContinueDTMFStringSending +// Allows a client to continue or cancel the sending of a DTMF string when it +// was stopped by the use of 'w' char in the string. +// --------------------------------------------------------------------------- +// +TInt CDTMFPayloadFormatWrite::ContinueDTMFStringSending( TBool aContinue ) + { + DP_DTMF_WRITE2( _L("CDTMFPayloadFormatWrite::ContinueDTMFStringSending - aContinue: %d"), aContinue ); + + if ( aContinue ) + { + // Start continue send rest digits incrementally + iStateMachine->ChangeStateTo( EStateEncodeNextDigit ); + } + else + { + iSendBuffer.Reset(); + iStateMachine->ChangeStateTo( EStateSendingIdle ); + } + + return KErrNone; + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::CancelDTMFStringSending +// +// --------------------------------------------------------------------------- +// +void CDTMFPayloadFormatWrite::CancelDTMFStringSending() + { + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::CancelDTMFStringSending") ); + + iSendBuffer.Reset(); + iSendTimer->Cancel(); + iStateMachine->ResetStateMachine(); + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::DeliverPacketL +// Prepare the RTP packet header and deliver the packet to the datasink. +// --------------------------------------------------------------------------- +// +void CDTMFPayloadFormatWrite::DeliverPacketL( CMMFDataBuffer& aPayload, + TBool aMarkerBit ) + { + DP_DTMF_WRITE4( + _L("CDTMFPayloadFormatWrite::DeliverPacketL - TSTAMP: %u, TDUR: %d, tick = %u"), + TUint32( aPayload.TimeToPlay().Int64() ), iToneDuration, + User::NTickCount() ); + + if ( KSignalOutbandDtmf == iGenerationMode ) + { + // Set the marker bit if it is very first packet. + if ( aMarkerBit ) + { + iRtpSendHeader.iMarker = 1; + } + else + { + iRtpSendHeader.iMarker = 0; + } + + // Construct RTP header. + if ( EGenRedUsed == iCInfo.iAlgoUsed ) + { + iRtpSendHeader.iPayloadType = iCInfo.iRedundantPayload; + } + else + { + iRtpSendHeader.iPayloadType = iCInfo.iPayloadType; + } + + // Timestamp must be updated before coming here + iRtpSendHeader.iTimestamp = TUint32( aPayload.TimeToPlay().Int64() ); + + // Deliver the packet + iRtpDataSink->EmptyBufferL( &aPayload, this, + TMediaId( KUidMediaTypeAudio ), iRtpSendHeader ); + + // Do not reset payload buffer because update packets + // are send based on same buffer. + } + else + { + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::DeliverPacketL - INBAND TONE") ); + } + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::IsValidDigit +// Check is digit valid DTMF. +// --------------------------------------------------------------------------- +// +TBool CDTMFPayloadFormatWrite::IsValidDigits( + const TDesC8& aDigits ) const + { + TBool isValid( ETrue ); + TInt index( aDigits.Length() - 1 ); + while ( isValid && 0 <= index ) + { + if ( KErrNotFound == iValidDigits.Find( aDigits[ index ] ) ) + { + isValid = EFalse; + } + + index--; + } + + return isValid; + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::SetSinkPrioritySettings +// Used to tell audio policy whether to mix locally played DTMFs to the record +// stream. +// --------------------------------------------------------------------------- +// +void CDTMFPayloadFormatWrite::SetSinkPrioritySettings( + const TMMFPrioritySettings& /*aPrioritySettings*/ ) + { + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::SetSinkPrioritySettings") ); + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::HandleControlChar +// Takes care of handling 'p' and 'w' chars in a DTMF string. +// --------------------------------------------------------------------------- +// +void CDTMFPayloadFormatWrite::HandleControlChar( const TChar& aChar ) + { + if ( 'p' == aChar ) + { + // 'p' char is interpreted as a 2.5 second pause + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::HandleControlChar - PAUSE DETECTED") ); + iSendTimer->Cancel(); + iStateMachine->ChangeStateTo( EStateSendPaused ); + TCallBack callBack( HandleTimerEventL, this ); + iSendTimer->Start( + TTimeIntervalMicroSeconds32( KDTMFPauseLengthInUs ), + TTimeIntervalMicroSeconds32( KDTMFPauseLengthInUs ), + callBack ); + } + else if ( 'w' == aChar ) + { + // Stop in a DTMF string + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::HandleControlChar - STOP DETECTED ") ); + + if ( iNotifyStopReq ) + { + iSendTimer->Cancel(); + iStateMachine->ChangeStateTo( EStateSendStopped ); + + SendEventSignalToClient( KMccDtmfStopInDtmfString ); + } + } + else + { + // not a control character + } + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::ConfigurePayloadFormatL +// Configure payload encoding parameters. +// --------------------------------------------------------------------------- +// +void CDTMFPayloadFormatWrite::ConfigurePayloadFormatL( + const TDesC8& aConfigParams, + CMccRtpMediaClock& aClock ) + { + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::ConfigurePayloadFormatL IN") ); + + __ASSERT_ALWAYS( aConfigParams.Size() == sizeof( TMccCodecInfo ), + User::Leave( KErrArgument ) ); + + TMccCodecInfoBuffer infoBuffer; + infoBuffer.Copy( aConfigParams ); + iCInfo = infoBuffer(); + + if ( !iPayloadEncoder ) + { + iPayloadEncoder = CDTMFPayloadEncoder::NewL(); + } + + iPayloadEncoder->SetPayloadType( iCInfo.iPayloadType ); + + // Redundancy configuration, always using generic redundacy. + if ( iCInfo.iRedundancyCount && KMaxDtmfRedCount >= + iCInfo.iRedundancyCount ) + { + DP_DTMF_WRITE2( _L("CDTMFPayloadFormatWrite::ConfigurePayloadFormatL iAlgoUsed: %d"), + iCInfo.iAlgoUsed ); + + __ASSERT_ALWAYS( EGenRedUsed == iCInfo.iAlgoUsed, + User::Leave( KErrArgument ) ); + + User::LeaveIfError( + iPayloadEncoder->SetPayloadFormat( EDTMFPayloadFormatRedEvents ) ); + } + else + { + // Make sure that the EGenRedUsed is not set + iCInfo.iAlgoUsed = ENoAlgoUsed; + } + + // Inband/Outband configuration + if ( EDTMFModeInband == iCInfo.iCodecMode ) + { + iGenerationMode = KSignalInbandDtmf; + } + else + { + iGenerationMode = KSignalOutbandDtmf; + + // Check if we need to unreg from current media clock. + if ( iRtpMediaClock ) + { + iRtpMediaClock->UnregisterMediaFormat( iKey ); + } + + iKey = aClock.RegisterMediaFormat( iSampleRate, iCInfo.iPtime ); + } + + iRtpMediaClock = &aClock; + + DP_DTMF_WRITE( _L("CDTMFPayloadFormatWrite::ConfigurePayloadFormatL OUT") ); + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::DoHandleTimerEvent +// Does actions depending on current state on timer event. +// --------------------------------------------------------------------------- +// +TInt CDTMFPayloadFormatWrite::DoHandleTimerEventL() + { + switch ( iStateMachine->State() ) + { + case EStateToneOn: + this->DoToneOnActionsL( EFalse ); + break; + + case EStateToneOff: + this->DoToneOffActionsL( EFalse ); + break; + + case EStateSendPaused: + iSendTimer->Cancel(); + iStateMachine->ChangeStateTo( EStateEncodeNextDigit ); + break; + + default: + DP_DTMF_WRITE(_L("CDTMFPayloadFormatWrite::DoHandleTimerEvent DEFAULT") ); + break; + } + + return ETrue; + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::EncodeNextDigitL +// Encodes next digit in sequence. +// --------------------------------------------------------------------------- +// +void CDTMFPayloadFormatWrite::EncodeNextDigitL() + { + DP_DTMF_WRITE2( _L("CDTMFPayloadFormatWrite::EncodeNextDigitL, tick = %u"), + User::NTickCount() ); + + iToneDuration = 0; + iToneDuration += static_cast + ( iCInfo.iPtime * iSampleRate * KMsToSecFactor ); + + if ( iSendBuffer.Count() && ('p' == iSendBuffer[0].Tone() || 'w' == + iSendBuffer[0].Tone() ) ) + { + HandleControlChar( iSendBuffer[0].Tone() ); + iSendBuffer.Remove( 0 ); + } + else if ( iSendBuffer.Count() ) + { + if ( EDTMFPayloadFormatEvent == iPayloadFormat || + EDTMFPayloadFormatRedEvents == iPayloadFormat ) + { + // Encode digit to RTP packet using Payload Format + // for Named Events + TDTMFEventPayloadInfo eventInfo; + eventInfo.SetEvent( iSendBuffer[0].Tone() ); + eventInfo.SetVolume( KDTMFDefaultToneVolume ); + eventInfo.SetDuration( iToneDuration ); + + if ( KSignalOutbandDtmf == iGenerationMode ) + { + TUint32 timeStamp( 0 ); + User::LeaveIfError( + iRtpMediaClock->GetTimeStamp( iKey, timeStamp ) ); + iSinkBuffer->SetTimeToPlay( + TTimeIntervalMicroSeconds( TInt64( timeStamp ) ) ); + } + + iPayloadEncoder->EncodeEvent( eventInfo, iSinkBuffer ); + } + else if ( EDTMFPayloadFormatTone == iPayloadFormat ) + { + TDTMFTonePayloadInfo toneInfo; + toneInfo.SetModulation( KDTMFDefaultModulation ); + toneInfo.SetVolume( KDTMFDefaultToneVolume ); + toneInfo.SetDuration( iToneDuration ); + iPayloadEncoder->EncodeTone( toneInfo, iSinkBuffer ); + } + else + { + // Not acceptable payload format + DP_DTMF_WRITE2( _L("PayloadFormat not Acceptable!: %d"), + iPayloadFormat ); + } + + iStateMachine->ChangeStateTo( EStateToneOn ); + } + else + { + if ( EModeAutomatic == iSendingMode ) + { + SendEventSignalToClient( KMccDtmfSendingComplete ); + } + + iStateMachine->ChangeStateTo( EStateSendingIdle ); + } + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::DoToneOnActionsL +// Called by sendtimer. Does actions belonging to tone on state and exits +// state when exit conditions are met. Actions to do are: Send update packets +// at certain time interval. +// --------------------------------------------------------------------------- +// +void CDTMFPayloadFormatWrite::DoToneOnActionsL( TBool aEntryToState ) + { + DP_DTMF_WRITE2( _L("CDTMFPayloadFormatWrite::DoToneOnActionsL, tick = %u"), + User::NTickCount() ); + + if ( aEntryToState ) + { + iSendTimer->Cancel(); + + if ( EModeManual == iSendingMode ) + { + SendEventSignalToClient( KMccDtmfManualStart ); + } + else + { + SendEventSignalToClient( KMccDtmfSequenceStart ); + } + + DeliverPacketL( *iSinkBuffer, ETrue ); + + TCallBack callBack( HandleTimerEventL, this ); + iSendTimer->Start( + TTimeIntervalMicroSeconds32( iCInfo.iPtime * KFactor1000 ), + TTimeIntervalMicroSeconds32( iCInfo.iPtime * KFactor1000 ), + callBack ); + } + else + { + iToneDuration += static_cast + ( iCInfo.iPtime * iSampleRate * KMsToSecFactor ); + + if ( EModeAutomatic == iSendingMode ) + { + TUint targetDuration = + TUint( KDTMFDefToneOnLengthInUs * KUsToSecFactor * + iSampleRate ); + + if ( iToneDuration >= targetDuration ) + { + // Exit condition + iSendTimer->Cancel(); + iPayloadEncoder->UpdateEventPayload( ETrue, iToneDuration, + iSinkBuffer ); + iStateMachine->ChangeStateTo( EStateToneOff ); + return; + } + } + else + { + if ( iSendBuffer[0].StopTime() != 0 && + iSendBuffer[0].Duration() * iTickPeriod + * iSampleRate * KUsToSecFactor <= iToneDuration ) + { + // Exit condition + iSendTimer->Cancel(); + iPayloadEncoder->UpdateEventPayload( ETrue, iToneDuration, + iSinkBuffer ); + iStateMachine->ChangeStateTo( EStateToneOff ); + return; + } + } + + if ( EDTMFPayloadFormatEvent == iPayloadFormat + || EDTMFPayloadFormatRedEvents == iPayloadFormat ) + { + // Check is this long duration event + // Disabling a PC-lint warning due to if-statement always being false, + // as it seems the possibility should be there if values are changed + /*lint -e685 */ + if ( KDTMFMaxDurationFieldValue < iToneDuration ) + { + iToneDuration -= KDTMFMaxDurationFieldValue; + iPayloadEncoder->UpdateEventPayload( EFalse, iToneDuration, + iSinkBuffer ); + DeliverPacketL( *iSinkBuffer, EFalse ); + } + /*lint +e685 */ + + else + { + // The update packet MUST have the same RTP timestamp value as + // the initial packet for the event (RFC 2833, 2.5.1.2). + iPayloadEncoder->UpdateEventPayload( EFalse, iToneDuration, + iSinkBuffer ); + DeliverPacketL( *iSinkBuffer, EFalse ); + } + } + else if ( EDTMFPayloadFormatTone == iPayloadFormat ) + { + iPayloadEncoder->UpdateTonePayload( iToneDuration, iSinkBuffer ); + DeliverPacketL( *iSinkBuffer, EFalse ); + } + else + { + // Not acceptable payload format + } + } + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::DoToneOffActionsL +// Called by sendtimer. Does actions belonging to tone off state and exits +// state when exit conditions are met. Actions include: sending final packets. +// --------------------------------------------------------------------------- +// +void CDTMFPayloadFormatWrite::DoToneOffActionsL( TBool aEntryToState ) + { + DP_DTMF_WRITE2( _L("CDTMFPayloadFormatWrite::DoToneOffActionsL, tick = %u"), + User::NTickCount() ); + + #ifdef VOIP_TRACE_ENABLED + VoipTrace( "%x %x", MCC_TRACE, MCC_DTMF_PLF_WRITE_SENDFINALPKT ); + #endif + + if ( aEntryToState ) + { + iSendTimer->Cancel(); + iFinalPacketCtr = 0; + + TCallBack callBack( HandleTimerEventL, this ); + iSendTimer->Start( + TTimeIntervalMicroSeconds32( iCInfo.iPtime * KFactor1000 ), + TTimeIntervalMicroSeconds32( iCInfo.iPtime * KFactor1000 ), + callBack ); + } + else + { + iFinalPacketCtr++; + if ( KNumOfFinalPackets >= iFinalPacketCtr ) + { + DeliverPacketL( *iSinkBuffer, EFalse ); + } + else + { + TUint curOffDur = iFinalPacketCtr * iCInfo.iPtime * KFactor1000; + if ( EModeManual == iSendingMode || + KDTMFDefToneOffLengthInUs <= curOffDur ) + { + // Prevent audio sending during final packet retransmission by + // sending completion event only when also retransmissions are + // done. + if ( EModeManual == iSendingMode ) + { + SendEventSignalToClient( KMccDtmfManualStop ); + } + else + { + SendEventSignalToClient( KMccDtmfSequenceStop ); + } + + iSendTimer->Cancel(); + iFinalPacketCtr = 0; + if ( iSendBuffer.Count() > 0 ) + { + iSendBuffer.Remove( 0 ); + } + iStateMachine->ChangeStateTo( EStateEncodeNextDigit ); + } + } + } + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::SendEventSignalToClient +// Signaling about events to the client. +// --------------------------------------------------------------------------- +// +void CDTMFPayloadFormatWrite::SendEventSignalToClient( + TMccDtmfEventType aEventType ) const + { + TMccDtmfEventData eventSignal; + eventSignal.iDtmfEventType = aEventType; + TMccDtmfEventDataPackage signalPkg( eventSignal ); + + if ( iEventHandler ) + { + TMccEvent event( 0, + 0, + 0, + 0, + KMccEventCategoryDtmf, + KMccDtmfControl, + KErrNone, + signalPkg ); + + TMccInternalEvent internalEvent( KMccDtmfFormatterUid, + EMccInternalEventNone, + event ); + + iEventHandler->SendEventToClient( internalEvent ); + } + } + +// --------------------------------------------------------------------------- +// CDTMFPayloadFormatWrite::IsValidDigit +// --------------------------------------------------------------------------- +// +TBool CDTMFPayloadFormatWrite::IsValidDigit( const TChar& aTone ) const + { + return ( KErrNotFound != iValidDigits.Find( aTone ) ); + } + + +// End of File +