sl@0: // Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // sl@0: sl@0: #include sl@0: #include sl@0: #include "mmfSBCCodecImplementationUIDs.hrh" sl@0: #include sl@0: #include "A2dpCodecUtilities.h" sl@0: #include "AudioBufferArray.h" sl@0: #include "RTPStreamer.h" sl@0: sl@0: sl@0: /** sl@0: RTP Streamer Panics sl@0: **/ sl@0: enum TRTPStreamerPanic sl@0: { sl@0: ERTPStreamerSendPacketMiscount, //0 sl@0: ERTPStreamerSendNotCompleted, //1 sl@0: ERTPStreamerEmptyBuffer, //2 sl@0: ERTPStreamerRTPEventError, //3 sl@0: ERTPStreamerIncompleteSBCFrame, //4 sl@0: ERTPStreamerUnexpectedEvent, //5 sl@0: ERTPStreamerBufferProcessingLengthMismatch, //6 sl@0: ERTPStreamerInvalidDataType, //7 sl@0: ERTPStreamerNoConfiguration //8 sl@0: }; sl@0: sl@0: sl@0: static void Panic(TRTPStreamerPanic aPanic) sl@0: // Panic client sl@0: { sl@0: _LIT(KRTPStreamerPanicName, "RTP Streamer"); sl@0: User::Panic(KRTPStreamerPanicName, aPanic); sl@0: } sl@0: sl@0: sl@0: sl@0: /** sl@0: Creates CActiveRTPStreamer sl@0: sl@0: @param aSock RSocket that can be used to stream audio to the headset sl@0: @param aRTPStreamObserver mixin used to inform the CA2dpBTHeadsetAudioInterface sl@0: of asynchronous error events sl@0: @return CActiveRTPStreamer* sl@0: */ sl@0: CActiveRTPStreamer* CActiveRTPStreamer::NewL(RSocket& aSock, MRTPStreamerObserver& aRTPStreamerObserver) sl@0: { sl@0: CActiveRTPStreamer* self = new (ELeave) CActiveRTPStreamer (aRTPStreamerObserver); sl@0: CleanupStack::PushL(self); sl@0: self->ConstructL(aSock); sl@0: CleanupStack::Pop(self); sl@0: return self; sl@0: } sl@0: sl@0: sl@0: /** sl@0: Make priortiy high so other RunLs don't impact CTimer accuracy too much sl@0: */ sl@0: CActiveRTPStreamer::CActiveRTPStreamer(MRTPStreamerObserver& aRTPStreamerObserver) : CTimer(EPriorityHigh), iRTPStreamerObserver(aRTPStreamerObserver), iRtpCanSend(ETrue) sl@0: { sl@0: CActiveScheduler::Add(this); sl@0: } sl@0: sl@0: sl@0: CActiveRTPStreamer::~CActiveRTPStreamer() sl@0: { sl@0: Cancel(); sl@0: delete iAudioBufferArray; sl@0: sl@0: //RTP defect fix DEF57144- RRtpSendSource Cancel() sl@0: //If the line of code below does not compile then your build is too old sl@0: iRTPSendSource.Cancel(); sl@0: sl@0: iRTPSendSource.Close(); sl@0: iRTPSession.Close(); sl@0: } sl@0: sl@0: sl@0: void CActiveRTPStreamer::ConstructL(RSocket& aSock) sl@0: { sl@0: CTimer::ConstructL(); sl@0: // get the MTU length limit sl@0: TInt mtu = 0; sl@0: User::LeaveIfError(aSock.GetOpt(EAvdtpMediaGetMaximumPacketSize, KSolBtAVDTPMedia, mtu)); sl@0: iMaxMTULength = mtu;//the line above wont accept iMaxMTULength directly because it is unsigned sl@0: iRTPSession.OpenL(aSock, iMaxMTULength); sl@0: iRTPSendSource = iRTPSession.NewSendSourceL(); sl@0: sl@0: //register callbacks - all terminal callbacks are one shot sl@0: iRTPSession.RegisterEventCallbackL(ERtpAnyEvent,RTPCallback,this); sl@0: iRTPSession.RegisterEventCallbackL(ERtpSessionFail,RTPCallback,this, ERtpOneShot); sl@0: iRTPSession.RegisterEventCallbackL(ERtpBufferOverflow,RTPCallback,this); sl@0: iRTPSession.RegisterEventCallbackL(ERtpUndersizedPacket,RTPCallback,this); sl@0: iRTPSendSource.RegisterEventCallbackL(ERtpAnyEvent,RTPSendSourceCallback,this); sl@0: iRTPSendSource.RegisterEventCallbackL(ERtpSendFail,RTPSendSourceCallback,this, ERtpOneShot); sl@0: } sl@0: sl@0: sl@0: /** sl@0: Function called by the CA2dpBTHeadsetAudioInterface to set the codec and the codec sl@0: settings used by the CActiveRTPStreamer. sl@0: Calling this function forces a recalculation of all the timings the next sl@0: time Send() is called sl@0: sl@0: @param aCodec the codec to be used. If this is set to NULL then no codec is used sl@0: only the CSBCCodec can be used here sl@0: @param aConfigType a Uid to identify the aConfigData used to configure the settings sl@0: only KMmfUidSBCConfigure is currently defined. In future other types may be defined sl@0: for mp3, AAC and ATRAC3 sl@0: @param aConfigData The configuration data sl@0: @return standard SymbianOS error code sl@0: */ sl@0: void CActiveRTPStreamer::SetCodec(CMMFCodec& aCodec) sl@0: { sl@0: //if there is a codec then it must be SBC else codec must be on the headset sl@0: iCodec = &aCodec; sl@0: } sl@0: sl@0: sl@0: void CActiveRTPStreamer::SetAudioConfiguration(const CA2dpAudioCodecConfiguration& aAudioCodecConfiguration) sl@0: { sl@0: iA2dpAudioCodecConfiguration = const_cast(&aAudioCodecConfiguration); sl@0: //if there is a new codec configuration then cannot assume the buffer sl@0: //length will be the same so reset sl@0: iBufferLength = 0; sl@0: iBufferParamsInitialized = EFalse;//will result in a call to InitializeForSendL sl@0: iTimeStampIncrement = 0; sl@0: iNumberOfInputBytesToMakeRTPPacket = 0; sl@0: } sl@0: sl@0: /** sl@0: Internal function to perform frame size related initialization sl@0: ie creation and setting of the RTPSendPacket audio buffer array. sl@0: Assumes all buffers are the same size (except for the last buffer) sl@0: sl@0: @param The length of the audio buffer sent in Send() sl@0: */ sl@0: void CActiveRTPStreamer::InitializeForSendL(const TDesC8& aData) sl@0: { sl@0: iSendBufferSize = aData.Size(); //store buffer length - this shouldn't change till the last buffer sl@0: __ASSERT_DEBUG(iSendBufferSize,Panic(ERTPStreamerEmptyBuffer)); sl@0: __ASSERT_DEBUG(iA2dpAudioCodecConfiguration,Panic(ERTPStreamerNoConfiguration)); sl@0: TUint encodedBufferSize = iSendBufferSize; sl@0: sl@0: if (iCodec) sl@0: { sl@0: iCodec->ResetL(); //clear out any cached data from previous settings sl@0: //if we are using a local codec - ie SBC then we get the frame length sl@0: //and bit rate from the local codec settings sl@0: //since aData will contain pcm16 sl@0: iFrameLength = iA2dpAudioCodecConfiguration->LocalSBCCodecConfiguration().CalcFrameLength(); sl@0: iBitRate = iA2dpAudioCodecConfiguration->LocalSBCCodecConfiguration().CalcBitRate(iFrameLength)*1000;//*1000 as bitrate is in KHz sl@0: //if we are putting data through the local SBC codec then sl@0: //the encoded buffer size sent to the headset in not the same as the aData buffer in Send() sl@0: encodedBufferSize = iA2dpAudioCodecConfiguration->CalculateSBCBufferLength(iSendBufferSize); sl@0: } sl@0: else sl@0: { sl@0: //if we don't use a local codec then we get the frame legth and bit rate sl@0: //direct from the header sl@0: CA2dpCodecFrameHeaderParser* headerParser = CA2dpCodecFrameHeaderParser::NewL(iA2dpAudioCodecConfiguration->HeadsetCodecDataType(), aData); sl@0: iFrameLength = headerParser->FrameLength(); sl@0: iBitRate = headerParser->BitRate(); sl@0: delete headerParser; sl@0: } sl@0: iPayloadType = TRTPa2dpCodecSpecificUtils::PayloadType(iA2dpAudioCodecConfiguration->HeadsetCodecDataType()); sl@0: sl@0: //if the settings have changed then any existing buffered audio buffers sl@0: // will have the old settings so we need to delete the buffer array sl@0: //and recreate from new with the new settings. sl@0: //we also need to cancel in case we are waiting on a RTPSendSourceCallback sl@0: //from a previous send packet sl@0: //RTP defect fix DEF57144- RRtpSendSource Cancel() not in MCL sl@0: //decomment this out when the Cancel method is on the MCL sl@0: //iRTPSendSource.Cancel(); sl@0: sl@0: delete iAudioBufferArray; sl@0: iAudioBufferArray = NULL; sl@0: //calculate the size of the RTP header sl@0: TUint mediaPayloadHeaderLength = TRTPa2dpCodecSpecificUtils::MediaPayloadHeaderLength(iA2dpAudioCodecConfiguration->HeadsetCodecDataType()); sl@0: TUint rtpHeaderLength = KRTPHeaderSize + mediaPayloadHeaderLength; sl@0: iAudioBufferArray = CAudioBufferArray::NewL(iRTPSendSource, KSendBucketSize, encodedBufferSize, iMaxMTULength, rtpHeaderLength, iFrameLength); sl@0: sl@0: //determine the payload header sl@0: switch(const_cast(iA2dpAudioCodecConfiguration->HeadsetCodecDataType()).FourCC()) sl@0: { sl@0: case KMMFFourCCCodeSBC: sl@0: iMediaPayloadHeader.Append(iAudioBufferArray->NumberOfFramesPerRtpPacket()); sl@0: break; sl@0: case KMMFFourCCCodeMP3: sl@0: //RFC2250-section 3.5 MBZ+Frag_Offset sl@0: //= 4 bytes all set to 0 sl@0: iMediaPayloadHeader.FillZ(4); //0000 sl@0: break; sl@0: case KMMFFourCCCodeAAC: sl@0: break; sl@0: case KMMFFourCCCodeATRAC3: sl@0: break; sl@0: default: sl@0: //the datatype is a non A2DP datatype sl@0: //which is not supported so panic sl@0: Panic(ERTPStreamerInvalidDataType); sl@0: break; sl@0: } sl@0: sl@0: //get the number of bytes of data sent that was sent to the RTP streamer sl@0: //that make up one RTP packet sl@0: //in the case of a codec this is the value pre codec processing sl@0: if (iCodec) sl@0: { sl@0: iNumberOfInputBytesToMakeRTPPacket = iSendBufferSize/iAudioBufferArray->NumberOfRtpPacketsPerAudioBuffer(); sl@0: if (iNumberOfInputBytesToMakeRTPPacket%2) sl@0: {//we have an odd number of bytes sl@0: iNumberOfInputBytesToMakeRTPPacket++;//round up to next byte sl@0: } sl@0: } sl@0: else sl@0: { sl@0: iNumberOfInputBytesToMakeRTPPacket = iAudioBufferArray->InputBytesPerRTPPacket(); sl@0: } sl@0: //else other codecs not supported sl@0: sl@0: //we set the iFrameDuration which is used to trigger the timer sl@0: //this means that RunL should be called every iFrameDuration and sl@0: //if an RTP packet is ready to sent then it shall be sent sl@0: //since there is no control channel back from the headset, the best sl@0: //we can hope for is to send the data at approx the rate the headset sl@0: //would expect it and hope that the headset provides it's own approriate sl@0: //internal buffering sl@0: //Note that dues to other AOs running the timing is not accurate and sl@0: //usually slower than the specified time - what is really needed sl@0: //here is a feedback loop where the initial time interval is somewhat sl@0: //faster than the calculated time interval and is adjusted against the sl@0: //system clock and bit rate throughput acordingly so the throughput always sl@0: //matches the bit rate. sl@0: iRTPPacketDuration = TTimeIntervalMicroSeconds32(TFrameTimingUtils::FrameDuration(iFrameLength,iBitRate).Int() * iAudioBufferArray->NumberOfFramesPerRtpPacket()); sl@0: RDebug::Printf("RTP Packet Duration = %d mS", iRTPPacketDuration.Int()); sl@0: TInt fastCounterFrequency; sl@0: HAL::Get(HALData::EFastCounterFrequency,fastCounterFrequency); sl@0: RDebug::Printf("sys clock timing frequency = %d Hz", fastCounterFrequency); sl@0: iTimeStampIncrement = TFrameTimingUtils::TimeStampIncrementPerFrame(iA2dpAudioCodecConfiguration->HeadsetCodecDataType(), iFrameLength, iBitRate, iA2dpAudioCodecConfiguration->SampleRate()) sl@0: * iAudioBufferArray->NumberOfFramesPerRtpPacket(); sl@0: RDebug::Printf("Calculated RTP packet time stamp increment = %d",iTimeStampIncrement); sl@0: RDebug::Printf("FrameLength = %d", iFrameLength); sl@0: RDebug::Printf("Calculated bitRate = %d", iBitRate); sl@0: RDebug::Printf("Number of frames per RTP packet = %d", iAudioBufferArray->NumberOfFramesPerRtpPacket()); sl@0: RDebug::Printf("Number of RTP packets per audio buffer = %d", iAudioBufferArray->NumberOfRtpPacketsPerAudioBuffer()); sl@0: RDebug::Printf("Sample rate = %d", iA2dpAudioCodecConfiguration->SampleRate()); sl@0: } sl@0: sl@0: sl@0: /** sl@0: Internal function to pass the pcm16 audio data in aData and use the codec sl@0: to process the data back in aPayload sl@0: sl@0: @return the number of source bytes processed sl@0: */ sl@0: TUint CActiveRTPStreamer::CodecProcessPayloadL(const TDesC8& aData,TDes8& aPayload) sl@0: { sl@0: TPtr8 srcBufferPtr(const_cast(aData.Ptr()),aData.Length()); sl@0: srcBufferPtr.SetLength(aData.Length()); sl@0: //+1 -1 to skip SBC media payload header sl@0: TPtr8 dstBufferPtr(const_cast(aPayload.Ptr()+1),aPayload.MaxLength()-1); sl@0: CMMFPtrBuffer* srcBuffer = CMMFPtrBuffer::NewL(srcBufferPtr); sl@0: CleanupStack::PushL(srcBuffer); sl@0: CMMFPtrBuffer* dstBuffer = CMMFPtrBuffer::NewL(dstBufferPtr); sl@0: CleanupStack::PushL(dstBuffer); sl@0: TCodecProcessResult result = iCodec->ProcessL(*srcBuffer,*dstBuffer); sl@0: if (result == TCodecProcessResult::EProcessIncomplete) sl@0: { sl@0: User::Leave(KErrArgument); sl@0: } sl@0: aPayload.Append(dstBuffer->Data()); sl@0: CleanupStack::PopAndDestroy(dstBuffer); sl@0: CleanupStack::PopAndDestroy(srcBuffer); sl@0: return result.iSrcBytesProcessed; sl@0: } sl@0: sl@0: sl@0: /** sl@0: This is the main function for CActiveRTPStreamer in that it is the sl@0: function used to send data to the headset over RTP. sl@0: The function is asynchronous to the RunL which does the actual sending. sl@0: The data is stored in the CRtpSendPackets FIFO and will be sent at the sl@0: next RunL provided the RTP can accept the data. If not it just stays sl@0: buffered in the CRtpSendPackets FIFO until it can be sent. sl@0: The request status is completed when the buffer is stored, not when it is sent sl@0: this is to more closely mimic the behaviour of the sound driver. sl@0: If adding the buffer to the CRtpSendPackets FIFO causes it to be full sl@0: then the request status won't be completed until there is space in the FIFO sl@0: which won't be until a callback from the RTP stack has been received sl@0: indicating that the CRtpSendPackets FIFO can now discard that entry. sl@0: Only one Send at a time is accepted ie the request status sl@0: of the previous send must be completed before Send can be called again. sl@0: To simplify the software and improve performance, fixed sized buffers are assumed.ie sl@0: the buffer length is only calculated once on the first frame and when the settings sl@0: change. sl@0: sl@0: @param aData The data to be sent to the headset. This may go via sl@0: a codec eg if the data is pcm16 or sent directly to the headset if the data is SBC,mp3,AAC,ATRAC3 sl@0: It is the responsibility of the client ie CA2dpBTHeadsetAudioInterface to sl@0: call SetCodecConfiguration first. sl@0: sl@0: @param aStatus sl@0: */ sl@0: void CActiveRTPStreamer::Send(const TDesC8& aData, TRequestStatus& aStatus) sl@0: { sl@0: if (iSendStatus) sl@0: { sl@0: __ASSERT_DEBUG((*iSendStatus != KRequestPending),Panic(ERTPStreamerSendNotCompleted)); sl@0: } sl@0: iSendStatus = &aStatus; sl@0: *iSendStatus = KRequestPending; sl@0: sl@0: if (iUnrecoverableError) sl@0: { sl@0: User::RequestComplete(iSendStatus,iUnrecoverableError); sl@0: return; sl@0: } sl@0: sl@0: if (!iBufferParamsInitialized) sl@0: { sl@0: TRAPD(err,InitializeForSendL(aData)); sl@0: if (err) sl@0: { sl@0: User::RequestComplete(iSendStatus,err); sl@0: return; sl@0: } sl@0: iBufferParamsInitialized = ETrue; sl@0: } sl@0: sl@0: TUint numberOfRtpPacketsPerAudioBuffer = iAudioBufferArray->NumberOfRtpPacketsPerAudioBuffer(); sl@0: sl@0: if (aData.Size() != iSendBufferSize) sl@0: { sl@0: //then we are on the last buffer sl@0: //in which case we need to recalculate the number of Rtp packets sl@0: //required to make up the audio frame since the last buffer sl@0: //is smaller. sl@0: TUint lastBufferLength = aData.Size(); sl@0: if (iCodec) sl@0: { sl@0: lastBufferLength = iA2dpAudioCodecConfiguration->CalculateSBCBufferLength(aData.Size()); sl@0: } sl@0: //we keep the same number of RTP packets per audio buffer as before sl@0: TUint numberOfSBCFramesInLastBuffer = lastBufferLength/iFrameLength; sl@0: TUint numberOfFramesPerRtpPacket = iAudioBufferArray->NumberOfFramesPerRtpPacket(); sl@0: sl@0: //the devisor below may not always devide without a remainder sl@0: //which means the last Rtp packet sent will not be full sl@0: //if we have a remainder then we need another Rtp packet sl@0: numberOfRtpPacketsPerAudioBuffer = numberOfSBCFramesInLastBuffer/numberOfFramesPerRtpPacket; sl@0: if (numberOfSBCFramesInLastBuffer%numberOfFramesPerRtpPacket) sl@0: { sl@0: numberOfRtpPacketsPerAudioBuffer++; sl@0: } sl@0: } sl@0: sl@0: sl@0: CRtpSendPackets* sendPackets = iAudioBufferArray->CurrentAudioBufferRtpSendPackets(); sl@0: TUint srcBytesProcessed = 0; sl@0: for (TUint i=0; iPacket(i); sl@0: sendPacket.SetPayloadType(iPayloadType); sl@0: sendPacket.SetTimestamp(iTimeStamp); sl@0: iTimeStamp += iTimeStampIncrement; sl@0: TDes8& payload = sendPacket.WritePayload(); sl@0: payload.Zero(); sl@0: payload.Append(iMediaPayloadHeader); sl@0: sl@0: //aData may have to be sent as multiple packets sl@0: TUint8* sourceDataOffset = const_cast(aData.Ptr())+srcBytesProcessed; sl@0: TPtr8 srcBufferPtr(sourceDataOffset,iNumberOfInputBytesToMakeRTPPacket); sl@0: TUint srcBytesStillRemaining = aData.Size() - srcBytesProcessed; sl@0: TUint lengthOfsrcBuffer = iNumberOfInputBytesToMakeRTPPacket; sl@0: if (srcBytesStillRemaining < lengthOfsrcBuffer ) sl@0: {//probably a last buffer condition or modulo 2 pcm16 rounding condition sl@0: lengthOfsrcBuffer = srcBytesStillRemaining; sl@0: } sl@0: srcBufferPtr.SetLength(lengthOfsrcBuffer); sl@0: sl@0: //sanity check - the following should always be true sl@0: __ASSERT_DEBUG((srcBytesProcessed == iNumberOfInputBytesToMakeRTPPacket*i),Panic(ERTPStreamerBufferProcessingLengthMismatch)); sl@0: sl@0: if (iCodec) sl@0: { sl@0: TRAPD(err,srcBytesProcessed += CodecProcessPayloadL(srcBufferPtr,payload)); sl@0: if (err) sl@0: { sl@0: //something has gone wrong so abort streaming sl@0: User::RequestComplete(iSendStatus,err); sl@0: return; sl@0: } sl@0: } sl@0: else //no need to process via codec - aData can go straight to the headset sl@0: { sl@0: srcBytesProcessed +=lengthOfsrcBuffer; sl@0: payload.Append(srcBufferPtr); sl@0: } sl@0: }// for (TUint i=0; iNumberOfRtpPacketsPerAudioBuffer();i++) sl@0: iAudioBufferArray->CurrentAudioBufferReadyToSend(); sl@0: //else we have no more send packets so cannot complete request status sl@0: //until one of the send packets has been sent and acknowledged. sl@0: sl@0: sl@0: //we'll send an event to ourselves and either send the packet if we can sl@0: //we could complete the iSendStatus TRequestStatus here before returing sl@0: //from the Send but we won't in order to more closely mimic the existing sl@0: // sound driver PlayData behaviour sl@0: //if there is already a request active then it will be the timer sl@0: //ie this will effectively kick things off if the timer is not running sl@0: TRequestStatus* stat = &iStatus; sl@0: if (!IsActive()) sl@0: { sl@0: User::RequestComplete(stat, KErrNone); sl@0: SetActive(); sl@0: } sl@0: } sl@0: sl@0: sl@0: /** sl@0: Function to stop further internally buffered packets being sent to the headset sl@0: */ sl@0: void CActiveRTPStreamer::Pause() sl@0: { sl@0: iPaused = ETrue; sl@0: Cancel(); sl@0: } sl@0: sl@0: sl@0: /** sl@0: Function called after pause to resume sending buffers to the headset sl@0: */ sl@0: void CActiveRTPStreamer::Resume() sl@0: { sl@0: if (iPaused) sl@0: { sl@0: iPaused = EFalse; sl@0: TRequestStatus* stat = &iStatus; sl@0: if (!IsActive()) sl@0: { sl@0: User::RequestComplete(stat, KErrNone); sl@0: SetActive(); sl@0: } sl@0: } sl@0: } sl@0: sl@0: sl@0: /** sl@0: Function called from CA2dpBTHeadsetAudioInterface::CancelPlayData() sl@0: Used to cancel an outstanding status request for a Send() sl@0: */ sl@0: void CActiveRTPStreamer::CancelLastSendBuffer() sl@0: { sl@0: if (iSendStatus) sl@0: { sl@0: if (*iSendStatus == KRequestPending)//make sure there is a pending request to cancel sl@0: { sl@0: iAudioBufferArray->CancelMostRecentAudioBuffer(!iRtpCanSend); sl@0: User::RequestComplete(iSendStatus, KErrCancel); sl@0: } sl@0: } sl@0: } sl@0: sl@0: sl@0: /** sl@0: Function to flush out the bufferes stored in CRtpSenPackets sl@0: */ sl@0: void CActiveRTPStreamer::FlushPendingSendBuffers() sl@0: { sl@0: iAudioBufferArray->FlushPendingPackets(); sl@0: sl@0: if(iCodec) sl@0: {//flush out codec cache sl@0: TRAP_IGNORE(iCodec->ResetL()); sl@0: } sl@0: } sl@0: sl@0: sl@0: /** sl@0: Function to return total number of bytes sent prior to codec processing sl@0: ie bytes of pcm16 not SBC sl@0: Note this the number of bytes sent is only updated when the packet sl@0: has been acknowledged as being sent correctly by the RTP stack sl@0: ie this value will always be slightly less than the bytes sent in Send() sl@0: */ sl@0: TUint CActiveRTPStreamer::BytesSent() const sl@0: { sl@0: return iBytesSent; sl@0: } sl@0: sl@0: sl@0: /** sl@0: Function to reset the number of bytes sent sl@0: */ sl@0: void CActiveRTPStreamer::ResetBytesSent() sl@0: { sl@0: iBytesSent = 0; sl@0: } sl@0: sl@0: sl@0: /** sl@0: The RunL is called every frame duration interval. sl@0: It checks to see if there are any packets to be sent to the headset sl@0: and if so send it. sl@0: One issue to be resolved at integration testing is if there are no packets sl@0: to send then this is analogous to a KErrUnderflow condition on the sl@0: sound driver. Do we need to mimic this behaviour for the a2dp interface? sl@0: sl@0: The Send request status is completed if there is room in the CRtpSendPackets sl@0: for another buffer. sl@0: */ sl@0: void CActiveRTPStreamer::RunL() sl@0: { sl@0: if ((iPaused)||(!iAudioBufferArray)) sl@0: { sl@0: return; sl@0: } sl@0: sl@0: if(iRtpCanSend && iAudioBufferArray->NumberOfAudioBuffersReadyToSend()) sl@0: { sl@0: RRtpSendPacket& sendPacket = iAudioBufferArray->CurrentSendPacket(); sl@0: sendPacket.Send(); sl@0: iRtpCanSend = EFalse; //have to wait for callback before we can send again sl@0: } sl@0: sl@0: if (iSendStatus) sl@0: { sl@0: if ((iAudioBufferArray->NumberOfAudioBuffersReadyToSend() < KSendBucketSize) sl@0: &&(*iSendStatus == KRequestPending)) sl@0: {//still some free packets to fill so complete request status sl@0: User::RequestComplete(iSendStatus, KErrNone); sl@0: iSendStatus = NULL; sl@0: } sl@0: //else if the iRtpSendPackets FIFO is full then we can't complete sl@0: //the request status until we've had an ERtpSendSucceeded event sl@0: } sl@0: //are there any more buffers that are ready to send? sl@0: //if so then send the next packet after a time delay sl@0: //keep calling this RunL every frame duration till as long as we have packets to send sl@0: //if there are no packets ready to send then we need to wait sl@0: //for another call to Send(); sl@0: if (iAudioBufferArray->NumberOfAudioBuffersReadyToSend()) sl@0: {//there are packets ready to send so fire off next RunL after one RTP packet duration sl@0: RDebug::Printf("RTPPacket Sent %d",User::FastCounter()); sl@0: After(iRTPPacketDuration); sl@0: } sl@0: } sl@0: sl@0: sl@0: /** sl@0: Cancel sl@0: */ sl@0: void CActiveRTPStreamer::DoCancel() sl@0: { sl@0: CTimer::DoCancel(); sl@0: CompleteSendRequestStatus(KErrCancel); sl@0: } sl@0: sl@0: sl@0: /** sl@0: Utility function to complete Send TRequestStatus with aError sl@0: */ sl@0: void CActiveRTPStreamer::CompleteSendRequestStatus(TInt aError) sl@0: { sl@0: if (iSendStatus) sl@0: { sl@0: if (*iSendStatus == KRequestPending) sl@0: { sl@0: User::RequestComplete(iSendStatus, aError); sl@0: } sl@0: } sl@0: } sl@0: sl@0: sl@0: /** sl@0: Called by RTP stack when a packet has been sent sl@0: If the packet was sent ok then complete the iSendStatus if it is pending sl@0: and update the number of bytes sent sl@0: If the packet was not sent ok then the error is regarded as unrecoverable sl@0: since this should not happen. If it does happen then the CA2dpBTHeadsetAudioInterface sl@0: is informed. If there is an outstanding Send TRequestStatus then this is sl@0: completed with KErrCommsFrame. Not sure if this is the most appropriate error code? sl@0: */ sl@0: void CActiveRTPStreamer::PacketSent(TRtpEventType aEvent) sl@0: { sl@0: if (aEvent == ERtpSendSucceeded) sl@0: { sl@0: RDebug::Printf("Sent RTPPacket Acknowledged %d",User::FastCounter()); sl@0: TBool entireAudioBufferSent = EFalse; sl@0: iAudioBufferArray->CurrentSendPacketSent(entireAudioBufferSent); sl@0: //check if there is an outstanding send request status sl@0: //we can only complete the send request status if all the sl@0: //RTP packets in the audio buffer have been sent. sl@0: if (entireAudioBufferSent) sl@0: { sl@0: CompleteSendRequestStatus(KErrNone); sl@0: } sl@0: iRtpCanSend = ETrue; sl@0: iBytesSent += iNumberOfInputBytesToMakeRTPPacket; sl@0: } sl@0: else if (aEvent == ERtpSendFail) sl@0: { sl@0: //if we fail to send the packet then chances are something sl@0: //has gone wrong that may not be recoverable sl@0: //so we will complete the request status and halt further streaming sl@0: //some testing may be required to see if it is possible to sl@0: //recover in which case the packet could be sent again sl@0: iUnrecoverableError = KErrCommsFrame;//probably the nearest error code sl@0: CompleteSendRequestStatus(iUnrecoverableError); sl@0: //inform iRTPStreamerObserver ie the CA2dpBTHeadsetAudioInterface sl@0: //this will initiate a GAVDP state machine reset which will destroy the CActiveRTPStreamer sl@0: iRTPStreamerObserver.RTPStreamerEvent(iUnrecoverableError); sl@0: } sl@0: else sl@0: {//we've not registered for any other events so this shouldn't happen sl@0: Panic(ERTPStreamerUnexpectedEvent); sl@0: } sl@0: } sl@0: sl@0: sl@0: /** sl@0: Called by RTP stack when some sort of general error has occured sl@0: eg switching off the headset, or the headset going out of range sl@0: The CA2dpBTHeadsetAudioInterface is informed. sl@0: */ sl@0: void CActiveRTPStreamer::RTPSessionEvent(const TRtpEvent& aEvent) sl@0: { sl@0: switch(aEvent.Type()) sl@0: { sl@0: case ERtpSessionFail: sl@0: iUnrecoverableError = KErrDisconnected; sl@0: break; sl@0: case ERtpBufferOverflow: sl@0: iUnrecoverableError = KErrOverflow; sl@0: break; sl@0: case ERtpUndersizedPacket: sl@0: iUnrecoverableError = KErrCommsFrame; sl@0: break; sl@0: default: sl@0: Panic(ERTPStreamerRTPEventError); //we haven't registered for anything else sl@0: break; sl@0: } sl@0: sl@0: //complete outstanding Send (CA2dpBTHeadsetAudioInterface::PlayData) request status sl@0: CompleteSendRequestStatus(iUnrecoverableError); sl@0: //inform CA2dpBTHeadsetAudioInterface sl@0: iRTPStreamerObserver.RTPStreamerEvent(iUnrecoverableError); sl@0: } sl@0: sl@0: sl@0: /** sl@0: Static callback from RTP stack sl@0: */ sl@0: void CActiveRTPStreamer::RTPSendSourceCallback(CActiveRTPStreamer* aStreamer, const TRtpEvent& aEvent) sl@0: { sl@0: __ASSERT_DEBUG((aEvent.IsSendSourceEvent()),Panic(ERTPStreamerRTPEventError)); sl@0: __ASSERT_DEBUG((aEvent.SendSource() == aStreamer->iRTPSendSource),Panic(ERTPStreamerRTPEventError)); sl@0: // for now assume it was sending complete sl@0: // do next bit sl@0: aStreamer->PacketSent(aEvent.Type()); sl@0: } sl@0: sl@0: sl@0: /** sl@0: Static callback from RTP stack sl@0: */ sl@0: void CActiveRTPStreamer::RTPCallback(CActiveRTPStreamer* aStreamer, const TRtpEvent& aEvent) sl@0: { sl@0: __ASSERT_DEBUG((aEvent.IsSessionEvent()),Panic(ERTPStreamerRTPEventError)); sl@0: __ASSERT_DEBUG((aEvent.Session() == aStreamer->iRTPSession),Panic(ERTPStreamerRTPEventError)); sl@0: sl@0: aStreamer->RTPSessionEvent(aEvent); sl@0: }