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 "AudioBufferArray.h" sl@0: #include "BtSBCFrameParameters.h" sl@0: #include "A2dpCodecUtilities.h" sl@0: sl@0: /** sl@0: Audio Buffer Array Panics sl@0: **/ sl@0: enum TAudioBufferArrayPanic sl@0: { sl@0: EAudioBufferArrayIncompleteFrame, //0 sl@0: EAudioBufferArrayMiscount, //1 sl@0: EAudioBufferArrayNonA2dpDataType, //2 sl@0: EAudioBufferArrayNoRTPPacketsPerAudioBuffer //3 sl@0: }; sl@0: sl@0: sl@0: static void Panic(TAudioBufferArrayPanic aPanic) sl@0: // Panic client sl@0: { sl@0: _LIT(KAudioBufferArrayPanicName, "A2DP Audio Buf Panic"); sl@0: User::Panic(KAudioBufferArrayPanicName, aPanic); sl@0: } sl@0: sl@0: sl@0: /** sl@0: Creates a CRtpSendPackets array of RRtpSendPackets sl@0: sl@0: @param aRtpSendSource sl@0: @param aNumberOfPackets this is the number of RRtpSendPackets stored in sl@0: the array sl@0: */ sl@0: CRtpSendPackets* CRtpSendPackets::NewL(RRtpSendSource& aRtpSendSource, TUint aNumberOfPackets) sl@0: { sl@0: CRtpSendPackets* self = new (ELeave) CRtpSendPackets (); sl@0: CleanupStack::PushL(self); sl@0: self->ConstructL(aRtpSendSource, aNumberOfPackets); sl@0: CleanupStack::Pop(self); sl@0: return self; sl@0: } sl@0: sl@0: sl@0: void CRtpSendPackets::ConstructL(RRtpSendSource& aRtpSendSource, TUint aNumberOfPackets) sl@0: { sl@0: // create all the RTP send packets now sl@0: TInt err = KErrNone; sl@0: for (TInt i=0; iConstructL(aRtpSendSource, aNumberOfAudioBuffers, aAudioBufferLength, aMTULength, aTotalRTPHeaderLength, aFrameLength); sl@0: CleanupStack::Pop(self); sl@0: return self; sl@0: } sl@0: sl@0: sl@0: void CAudioBufferArray::ConstructL(RRtpSendSource& aRtpSendSource, sl@0: TUint aNumberOfAudioBuffers, sl@0: TUint aAudioBufferLength, sl@0: TUint aMTULength, sl@0: TUint aTotalRTPHeaderLength, sl@0: TUint aFrameLength) sl@0: { sl@0: //the buffer should always contain an intiger number of audio frames sl@0: //the following ASSERT_DEBUG should be present but is commented out as the RVCT sl@0: //compiler generates a warning sl@0: //__ASSERT_DEBUG(!(aAudioBufferLength%aFrameLength),EAudioBufferArrayIncompleteFrame); sl@0: sl@0: //calculate the number of frames in the audio buffer size and if sl@0: //more than 15 frames then calculate the highest common factor and use this sl@0: //calculate the frames length sl@0: iFrameLength = aFrameLength; sl@0: sl@0: //calculate the total number of frames in the buffer sl@0: TUint numberOfFramesPerAudioBuffer = aAudioBufferLength/iFrameLength; sl@0: //for now set the number of audio frames in an RTP packet to the total sl@0: iNumberOfFramesPerRtpPacket = numberOfFramesPerAudioBuffer; sl@0: sl@0: TUint lengthOfAudioDataInRtpPacket = 0; sl@0: TInt usableRTPPayloadLength = aMTULength-aTotalRTPHeaderLength; sl@0: sl@0: //check whether all the audio frames will actually fit into one RTP packet sl@0: if ((numberOfFramesPerAudioBuffer > KMaxNumberOfSBCFramesPerRTPPacket)||((aAudioBufferLength+iFrameLength)>usableRTPPayloadLength))//+iFrameLength in case of cached frame sl@0: { sl@0: //we cannot get all the audio frames into a single RTP packet sl@0: //for SBC only a max of 15 SBC frames allowed per packet sl@0: //if the buffer size exceeds 15 frames and/or is too large sl@0: //for the underlying MTU size then we need to break sl@0: //the buffer into buffers of less then 16 frames. sl@0: //for non SBC the frames are generaly larger so we'll keep sl@0: //the 15 for now even for non SBC sl@0: //we need to calculate how may frames should go into sl@0: //each RTP packet and how many RTP packets we need to send a sl@0: //complete audio buffer sl@0: //we are going to calculate such that every RTP packet sl@0: //has the same number of frames sl@0: //note that if we are using non SBC then the frame size tends to be larger sl@0: //so the MTU limit is likely to be the dominant factor sl@0: //although in principle this code should only use the 15 frame limit for sl@0: //for SBC sl@0: iNumberOfFramesPerRtpPacket = 0; sl@0: sl@0: sl@0: //only a max of 15 SBC frames allowed per packet sl@0: //if the buffer size exceeds 15 frames then we need to break sl@0: //the buffer into buffers of less then 16 frames. sl@0: //so find HCF sl@0: //-1 for cached frames ie if iNumberOfFramesPerRtpPacket sl@0: //was the same as KMaxNumberOfSBCFramesPerRTPPacket and we got a cached frame sl@0: //then we would blow the limit sl@0: for (TUint i=KMaxNumberOfSBCFramesPerRTPPacket-1; i; i--) sl@0: { sl@0: if (!(numberOfFramesPerAudioBuffer%i)) sl@0: { sl@0: //check we don't blow the MTU size sl@0: if ((i*iFrameLength) <= usableRTPPayloadLength) sl@0: { sl@0: iNumberOfFramesPerRtpPacket = i; sl@0: break; sl@0: } sl@0: } sl@0: } sl@0: if (!iNumberOfFramesPerRtpPacket) sl@0: {//the frame length was too big for the MTU length sl@0: //one frame of audio + one frame of cached audio sl@0: //would exceed the length supported by the underlying bearer sl@0: //note that the A2DP specification section 4.3.4 does allow sl@0: //SBC frames to be fragmented across multiple packets if sl@0: //less than one frame, but this ref implementation sl@0: //does not support this, since this should be rare. sl@0: //this may happen more often for non SBC eg mp3 frames sl@0: //but we don't support fragmented frames sl@0: User::Leave(KErrTooBig); sl@0: } sl@0: iNumberOfRtpPacketsPerAudioBuffer = numberOfFramesPerAudioBuffer/iNumberOfFramesPerRtpPacket; sl@0: sl@0: //this could probably be optimized somewhat such that the sl@0: //iInputBytesPerRtpPacket value was such that no caching was required sl@0: //in the codec sl@0: sl@0: if (!iNumberOfRtpPacketsPerAudioBuffer)//this isn't really necessary or could be ASSERT but needed supress armv5 compiler warning sl@0: { sl@0: Panic(EAudioBufferArrayNoRTPPacketsPerAudioBuffer); sl@0: } sl@0: sl@0: iInputBytesPerRtpPacket = aAudioBufferLength/iNumberOfRtpPacketsPerAudioBuffer; sl@0: if (iInputBytesPerRtpPacket%2) sl@0: {//we have an odd number of bytes sl@0: iInputBytesPerRtpPacket++;//round up to next byte sl@0: } sl@0: lengthOfAudioDataInRtpPacket = iNumberOfFramesPerRtpPacket*iFrameLength; sl@0: }//if ((numberOfFramesPerAudioBuffer > KMaxNumberOfSBCFramesPerRTPPacket)||((encodedAudioBufferLength+iFrameLength)>aMaxMTULength)) sl@0: else sl@0: {//we can fit the entire buffer in one RTP packet sl@0: iNumberOfRtpPacketsPerAudioBuffer = 1; sl@0: iInputBytesPerRtpPacket = aAudioBufferLength; sl@0: lengthOfAudioDataInRtpPacket = aAudioBufferLength; sl@0: } sl@0: sl@0: TUint payloadSize = aTotalRTPHeaderLength+lengthOfAudioDataInRtpPacket+iFrameLength;//+ extra framelength for cached frames sl@0: aRtpSendSource.SetDefaultPayloadSize(payloadSize); sl@0: sl@0: //now we have set the payload size we can create the audio buffers sl@0: //stored as CRtpSendPackets* sl@0: for (TInt i=0; i= iAudioBufferArray.Count()) sl@0: { sl@0: iNextAudioBufferToFill = 0; sl@0: } sl@0: iNumberOfReadyAudioBuffers++; sl@0: __ASSERT_DEBUG((iNumberOfReadyAudioBuffers<=iAudioBufferArray.Count()),Panic(EAudioBufferArrayMiscount)); sl@0: } sl@0: sl@0: sl@0: /** sl@0: This function cancels the most recently filled audio buffer that is ready to send sl@0: The audio buffer corresponds to the audio buffer sent in the last CActiveRTPStreamer::Send() sl@0: This is used in order to cancel a Send request. sl@0: It effectively undoes the operation performed in CurrentAudioBufferReadyToSend() sl@0: so CurrentAudioBufferReadyToSend() must have been called at least once prior. sl@0: sl@0: @param aSendInProgress set to ETrue if an audio buffer is currently being sl@0: sent sl@0: */ sl@0: void CAudioBufferArray::CancelMostRecentAudioBuffer(TBool aSendInProgress) sl@0: { sl@0: __ASSERT_DEBUG((iNumberOfReadyAudioBuffers),Panic(EAudioBufferArrayMiscount)); sl@0: if ((iNumberOfReadyAudioBuffers == 1) && (aSendInProgress)) sl@0: {//then we only have one ready buffer , which is being sent sl@0: //so we want to stop any further sending of the current buffer sl@0: __ASSERT_DEBUG((iAudioBufferBeingSent == iNextAudioBufferToFill),Panic(EAudioBufferArrayMiscount)); sl@0: //now we need prevent any further send packets in the current audio buffer being sent sl@0: //the following line of code will force us to move onto the next audio sl@0: //buffer discarding any RTP packets in the current audio buffer sl@0: //see CurrentSendPacketSent() sl@0: iNextRtpPacketToSend = iNumberOfRtpPacketsPerAudioBuffer-1; sl@0: } sl@0: else if (iNumberOfReadyAudioBuffers) sl@0: { sl@0: if (!iNextAudioBufferToFill) sl@0: { sl@0: iNextAudioBufferToFill = iAudioBufferArray.Count(); sl@0: } sl@0: else sl@0: { sl@0: iNextAudioBufferToFill--; sl@0: } sl@0: iNumberOfReadyAudioBuffers--; sl@0: } sl@0: } sl@0: sl@0: sl@0: /** sl@0: This function flushes the pending send packets that are ready to send. sl@0: Only the current send packet is valid sl@0: */ sl@0: void CAudioBufferArray::FlushPendingPackets() sl@0: { sl@0: //check that we actually have some audio buffers to flush sl@0: if (iNumberOfReadyAudioBuffers > 1) sl@0: { sl@0: if (iAudioBufferBeingSent >= iAudioBufferArray.Count()) sl@0: { sl@0: iNextAudioBufferToFill = 0; sl@0: } sl@0: else sl@0: { sl@0: iNextAudioBufferToFill = iAudioBufferBeingSent+1; sl@0: } sl@0: iNumberOfReadyAudioBuffers = 1; //the current send packet sl@0: } sl@0: else if (iNumberOfReadyAudioBuffers == 1) sl@0: { sl@0: //now we need to flush out the send packets in the current audio buffer being sent sl@0: //the following line of code will force us to move onto the next audio sl@0: //buffer discarding any RTP packets in the current audio buffer sl@0: //see CurrentSendPacketSent() sl@0: iNextRtpPacketToSend = iNumberOfRtpPacketsPerAudioBuffer-1; sl@0: } sl@0: } sl@0: sl@0: sl@0: /* sl@0: This function returns the current RTP packet to be sent to the headet sl@0: if there are no packets that are ready to be sent sl@0: ie iNumberOfReadyPackets = 0 then the RRtpSendPacket sl@0: will be invalid. sl@0: CurrentSendPacketSent() needs to be called when a send packet has been sl@0: acknowledged as being sent by the RTP stack sl@0: */ sl@0: RRtpSendPacket& CAudioBufferArray::CurrentSendPacket() sl@0: { sl@0: CRtpSendPackets* currentSendAudioBuffer = iAudioBufferArray[iAudioBufferBeingSent]; sl@0: return currentSendAudioBuffer->Packet(iNextRtpPacketToSend); sl@0: } sl@0: sl@0: sl@0: /* sl@0: This function is called when the RTP module has made the callback indicating that the sl@0: current send packet has been sent. sl@0: The function updates the current send packet to the next packet to be sent sl@0: sl@0: @param aEntireAudioBufferSent this is set to true if the sl@0: current entire audio buffer has been sent. The RTPStreamer uses sl@0: this information to determine whether to complete the send request status sl@0: */ sl@0: void CAudioBufferArray::CurrentSendPacketSent(TBool& aEntireAudioBufferSent) sl@0: { sl@0: aEntireAudioBufferSent = EFalse; sl@0: if (iNumberOfReadyAudioBuffers)//this could be 0 if the current packet sent was sent and subsequently cancelled sl@0: { sl@0: iNextRtpPacketToSend++; sl@0: if (iNextRtpPacketToSend >= iNumberOfRtpPacketsPerAudioBuffer) sl@0: {//then we have sent all the RTP packets in the current audio buffer sl@0: iNextRtpPacketToSend = 0; sl@0: iAudioBufferBeingSent++; //we've finished with this audio buffer so move onto the next one sl@0: //do something to show we are finished with audio buffer and complete request status sl@0: if (iAudioBufferBeingSent >= iAudioBufferArray.Count()) sl@0: { sl@0: iAudioBufferBeingSent = 0; sl@0: } sl@0: iNumberOfReadyAudioBuffers--; sl@0: aEntireAudioBufferSent = ETrue; sl@0: } sl@0: } sl@0: //else if iNumberOfReadyAudioBuffers = 0 then the packet must have been canceled so do nothing sl@0: __ASSERT_DEBUG((iNumberOfReadyAudioBuffers<=iAudioBufferArray.Count()),Panic(EAudioBufferArrayMiscount));//check underflow sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: