First public contribution.
1 // Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
2 // All rights reserved.
3 // This component and the accompanying materials are made available
4 // under the terms of "Eclipse Public License v1.0"
5 // which accompanies this distribution, and is available
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
16 #include "mmfclientaudioinputstream.h"
17 #include "mmfclientaudiostreamutils.h"
18 #include <mmf/server/devsoundstandardcustominterfaces.h>
19 #include <mmf/common/mmfpaniccodes.h>
21 #include <mdaaudioinputstream.h>
23 const TInt KMicroSecsInOneSec = 1000000;
26 enum TMmfAudioInputPanic
28 EAudioInputPanicNotSupported
31 _LIT(KAudioInputStreamCategory, "CMMFMdaAudioInputStream");
32 LOCAL_C void Panic(const TMmfAudioInputPanic aPanic)
34 User::Panic(KAudioInputStreamCategory, aPanic);
41 * @return CMdaAudioInputStream*
44 EXPORT_C CMdaAudioInputStream* CMdaAudioInputStream::NewL(MMdaAudioInputStreamCallback& aCallback)
46 return NewL(aCallback, EMdaPriorityNormal, EMdaPriorityPreferenceTimeAndQuality);
53 * @return CMdaAudioInputStream*
56 EXPORT_C CMdaAudioInputStream* CMdaAudioInputStream::NewL(MMdaAudioInputStreamCallback& aCallback, TInt aPriority, TInt aPref)
58 CMdaAudioInputStream* self = new(ELeave) CMdaAudioInputStream();
59 CleanupStack::PushL(self);
60 self->iProperties = CMMFMdaAudioInputStream::NewL(aCallback, aPriority, aPref);
61 CleanupStack::Pop(self);
65 CMdaAudioInputStream::CMdaAudioInputStream()
69 CMdaAudioInputStream::~CMdaAudioInputStream()
73 iProperties->ShutDown();
79 EXPORT_C void CMdaAudioInputStream::SetAudioPropertiesL(TInt aSampleRate, TInt aChannels)
82 iProperties->SetAudioPropertiesL(aSampleRate, aChannels);
85 EXPORT_C void CMdaAudioInputStream::Open(TMdaPackage* aSettings)
88 iProperties->Open(aSettings);
91 EXPORT_C void CMdaAudioInputStream::SetGain(TInt aNewGain)
94 iProperties->SetGain(aNewGain);
97 EXPORT_C TInt CMdaAudioInputStream::Gain() const
100 return iProperties->Gain();
103 EXPORT_C TInt CMdaAudioInputStream::MaxGain() const
106 return iProperties->MaxGain();
109 EXPORT_C void CMdaAudioInputStream::SetBalanceL(TInt aBalance)
112 iProperties->SetBalanceL(aBalance);
115 EXPORT_C TInt CMdaAudioInputStream::GetBalanceL() const
118 return iProperties->GetBalanceL();
121 EXPORT_C void CMdaAudioInputStream::SetPriority(TInt aPriority, TInt aPref)
124 iProperties->SetPriority(aPriority, aPref);
127 EXPORT_C void CMdaAudioInputStream::ReadL(TDes8& aData)
130 iProperties->ReadL(aData);
133 EXPORT_C void CMdaAudioInputStream::Stop()
139 EXPORT_C void CMdaAudioInputStream::RequestStop()
142 iProperties->RequestStop();
145 EXPORT_C const TTimeIntervalMicroSeconds& CMdaAudioInputStream::Position()
148 return iProperties->Position();
151 EXPORT_C TInt CMdaAudioInputStream::GetBytes()
154 return iProperties->GetBytes();
157 EXPORT_C void CMdaAudioInputStream::SetSingleBufferMode(TBool aSingleMode)
160 iProperties->SetSingleBufferMode(aSingleMode);
166 EXPORT_C void CMdaAudioInputStream::SetDataTypeL(TFourCC aAudioType)
169 iProperties->SetDataTypeL(aAudioType);
175 EXPORT_C TFourCC CMdaAudioInputStream::DataType() const
178 return iProperties->DataType();
182 EXPORT_C TInt CMdaAudioInputStream::BitRateL() const
185 return iProperties->BitRateL();
188 EXPORT_C void CMdaAudioInputStream::SetBitRateL(TInt aBitRate)
191 iProperties->SetBitRateL(aBitRate);
195 EXPORT_C void CMdaAudioInputStream::GetSupportedBitRatesL(RArray<TInt>& aSupportedBitRates)
198 iProperties->GetSupportedBitRatesL(aSupportedBitRates);
201 EXPORT_C TAny* CMdaAudioInputStream::CustomInterface(TUid aInterfaceId)
204 return iProperties->CustomInterface(aInterfaceId);
209 CMMFMdaAudioInputStream* CMMFMdaAudioInputStream::NewL(MMdaAudioInputStreamCallback& aCallback)
211 return CMMFMdaAudioInputStream::NewL(aCallback, EMdaPriorityNormal, EMdaPriorityPreferenceTimeAndQuality);
214 CMMFMdaAudioInputStream* CMMFMdaAudioInputStream::NewL(MMdaAudioInputStreamCallback& aCallback, TInt aPriority, TInt aPref)
216 CMMFMdaAudioInputStream* self = new(ELeave) CMMFMdaAudioInputStream(aCallback);
217 CleanupStack::PushL(self);
218 self->ConstructL(aPriority, aPref);
219 CleanupStack::Pop(self);
226 * @param "MMdaAudioInputStreamCallback&"
227 * a reference to MMdaAudioInputStreamCallback
231 CMMFMdaAudioInputStream::CMMFMdaAudioInputStream(MMdaAudioInputStreamCallback& aCallback)
232 : iCallback(aCallback), iStorageItem (NULL, 0), iBufferPtr(NULL, 0)
233 // Depending on zero for construction (i.e. attribute of CBase)
234 // iSingleBuffer (EFalse)
237 // iCallbackMade(EFalse)
238 // iAudioDataStored(EFalse)
240 iDataTypeCode.Set(TFourCC(' ','P','1','6'));
245 * Second phase constructor
248 void CMMFMdaAudioInputStream::ConstructL(TInt aPriority, TInt aPref)
250 iDevSound = CMMFDevSound::NewL();
251 SetPriority(aPriority, aPref);
252 iFifo = new(ELeave) CMMFFifo<TDes8>();
253 iActiveCallback = new(ELeave) CActiveCallback(iCallback);
254 iActiveSchedulerWait = new(ELeave) CActiveSchedulerWait;
262 CMMFMdaAudioInputStream::~CMMFMdaAudioInputStream()
266 delete iActiveCallback;
267 delete iActiveSchedulerWait;
272 * Set audio input stream properties
274 * @param "TInt aSampleRate"
275 * a specified priority value
276 * @param "TInt aChannels"
277 * a specified preference value
280 void CMMFMdaAudioInputStream::SetAudioPropertiesL(TInt aSampleRate, TInt aChannels)
282 if (iIsOpenState == EIsOpen)
284 RealSetAudioPropertiesL(aSampleRate, aChannels);
288 // cache for application later
289 iSettings.iSampleRate = aSampleRate;
290 iSettings.iChannels = aChannels;
291 iAudioDataStored = ETrue;
295 void CMMFMdaAudioInputStream::RealSetAudioPropertiesL(TInt aSampleRate, TInt aChannels)
297 TMMFCapabilities capabilities = iDevSound->Config();
298 capabilities.iChannels = StreamUtils::MapChannelsMdaToMMFL(aChannels);
299 capabilities.iRate = StreamUtils::MapSampleRateMdaToMMFL(aSampleRate);
300 iDevSound->SetConfigL(capabilities);
305 * Open a audio ouptut stream
307 * @param "TMdaPackage* Settings"
308 * a pointer point to TMdaPackage
311 void CMMFMdaAudioInputStream::Open(TMdaPackage* aSettings)
313 iIsOpenState = EIsOpening;
315 if (aSettings && (aSettings->Type().iUid == KUidMdaDataTypeSettingsDefine))
317 TMdaAudioDataSettings* tmpSettings = STATIC_CAST(TMdaAudioDataSettings*, aSettings);
318 iSettings = *tmpSettings;
319 iAudioDataStored = ETrue;
321 TRAPD(err, iDevSound->InitializeL(*this, iDataTypeCode, EMMFStateRecording));
324 // Signal for the MaiscOpenComplete callback to be called asynchronously
325 iActiveCallback->Signal(err);
326 iIsOpenState = EIsNotOpen;
327 iAudioDataStored = EFalse; // reset - if was set we throw away due to failure
333 * To get the maximum gain level
336 * the maximum gain value in integer
339 TInt CMMFMdaAudioInputStream::MaxGain() const
341 return iDevSound->MaxGain();
346 * To get the current gain level
349 * the current gain value in integer
352 TInt CMMFMdaAudioInputStream::Gain() const
354 return iDevSound->Gain();
359 * Set audio input stream gain to the specified value
361 * @param "TInt aGain"
362 * a specified gain value
365 void CMMFMdaAudioInputStream::SetGain(TInt aGain)
367 iDevSound->SetGain(aGain);
372 * Set audio input stream balance
374 * @param "TInt aBalance"
375 * a specified balance value
378 void CMMFMdaAudioInputStream::SetBalanceL(TInt aBalance)
380 // test and clip balance to min / max range [-100 <-> 100]
381 // clip rather than leave as this isn't a leaving function
382 if (aBalance < KMMFBalanceMaxLeft) aBalance = KMMFBalanceMaxLeft;
383 if (aBalance > KMMFBalanceMaxRight) aBalance = KMMFBalanceMaxRight;
385 // separate out left and right balance
388 StreamUtils::CalculateLeftRightBalance( left, right, aBalance );
390 // send the balance to SoundDevice
391 iDevSound->SetRecordBalanceL(left, right);
396 * To get the current balance value.This function may not return the same value
397 * as passed to SetBalanceL depending on the internal implementation in
398 * the underlying components.
401 * the current balance value in integer
404 TInt CMMFMdaAudioInputStream::GetBalanceL() const
406 TInt rightBalance = 0;
407 TInt leftBalance = 0;
408 iDevSound->GetRecordBalanceL(leftBalance, rightBalance);
410 StreamUtils::CalculateBalance( balance, leftBalance, rightBalance );
416 * Set audio input stream priority
418 * @param "TInt aPriority"
419 * a specified priority value
420 * @param "TInt aPref"
421 * a specified preference value
424 void CMMFMdaAudioInputStream::SetPriority(TInt aPriority, TInt aPref)
426 TMMFPrioritySettings settings;
427 settings.iPriority = aPriority;
428 settings.iPref = aPref;
429 iDevSound->SetPrioritySettings(settings);
435 * To read data from input stream
437 * @param "TDesC8& aData"
440 * @capability UserEnvironment
441 * For recording - the requesting client process must have the
442 * UserEnvironment capability.
444 void CMMFMdaAudioInputStream::ReadL(TDes8& aData)
446 User::LeaveIfError(Read(aData));
448 if (iState == EStopped)
450 iDevSound->RecordInitL();
457 * To read data from input stream
459 * @param "TDesC8& aData"
462 * @capability UserEnvironment
463 * For recording - the requesting client process must have the
464 * UserEnvironment capability.
466 TInt CMMFMdaAudioInputStream::Read(TDes8& aData)
468 TMMFFifoItem<TDes8>* item = new TMMFFifoItem<TDes8>(aData);
474 iFifo->AddToFifo(*item); // no issue with memory alloc
475 // each element contains storage space for link to the next
481 * To stop write data to stream
484 void CMMFMdaAudioInputStream::Stop()
486 // Need to take for the case where Stop is invoked directly after a call to RequestStop.
487 // We have chosen to allow the Stop to go through as this could be more important.
488 // This is non-reentrant code but will suffice for our needs.
489 if (iState != EStopped)
491 // Amend the state so RequestStop or Stop initiated by it (indirectly) won't function
494 if (iSingleBuffer && iStorageItem.Ptr() != NULL)
496 iCallback.MaiscBufferCopied(KErrAbort, iStorageItem);
497 iStorageItem.Set (NULL,0,0);
500 // Delete all buffers in the fifo and notify the observer
501 TMMFFifoItem<TDes8>* firstItem;
502 while((firstItem = iFifo->Get()) != NULL)
504 iFifo->RemoveFirstItem();
507 iCallback.MaiscBufferCopied(KErrAbort,firstItem->GetData());
519 * To pause write data to stream
520 * Allow yet unprocessed buffers to be processed and passed back via BufferToBeEmptied.
521 * When the last (empty) buffer arrives, Stop is called.
523 void CMMFMdaAudioInputStream::RequestStop()
525 // [ precondition that we are not already stopped
526 // && if we are stopped do nothing.
527 // If we are stopping a recording, we need to give the server a chance to
528 // process that data which has already been captured. We therefore stay in the EPause
530 if (iState != EStopped)
532 if (iState != EStopping)
534 // We can only be Recording, if we have other states later they can be tested here.
544 * To get the current position in the data stream
546 * @return "TTimeIntervalMicroSeconds&"
547 * the current position in integer
550 const TTimeIntervalMicroSeconds& CMMFMdaAudioInputStream::Position()
552 TInt64 position = iDevSound->SamplesRecorded();
553 position = position * KMicroSecsInOneSec / StreamUtils::SampleRateAsValue(iDevSound->Config());
554 iPosition = (iState == ERecording) ? position : 0; // Shouldn't need to check for playing but CMMFDevSound doesn't reset bytes played after a stop
562 * To return the current number of bytes recorded by audio hardware
563 * @return "the current current number of bytes rendered by audio hardware in integer"
566 TInt CMMFMdaAudioInputStream::GetBytes()
568 return iBytesRecorded;
571 void CMMFMdaAudioInputStream::SetDataTypeL(TFourCC aAudioType)
573 if(iState != EStopped)
574 User::Leave(KErrServerBusy);
576 if(aAudioType == iDataTypeCode)
579 TMMFPrioritySettings prioritySettings ;
580 prioritySettings.iState = EMMFStateRecording;
582 RArray<TFourCC> supportedDataTypes;
584 CleanupClosePushL(supportedDataTypes);
586 TRAPD(err, iDevSound->GetSupportedOutputDataTypesL(supportedDataTypes, prioritySettings));
590 if (supportedDataTypes.Find(aAudioType) == KErrNotFound)
592 User::Leave(KErrNotSupported);
594 //if match, set the 4CC of AudioType to match
595 iDataTypeCode.Set(aAudioType);
597 else //we had a real leave error from GetSupportedInputDataTypesL
601 CleanupStack::PopAndDestroy(&supportedDataTypes);
603 if(iIsOpenState!=EIsNotOpen)
605 // need to recall or restart InitializeL() process
606 iDevSound->CancelInitialize(); // call just in case mid-InitializeL. No harm if not.
607 // if not supported then assume old DevSound behaviour anyway
608 // where InitializeL() implicitly cancels, so no harm either way
609 iIsOpenState = EIsOpening;
610 iInitCallFrmSetDataType = ETrue;
611 TRAPD(err, iDevSound->InitializeL(*this, iDataTypeCode, EMMFStateRecording));
615 iIsOpenState = EIsNotOpen;
616 iInitCallFrmSetDataType = EFalse;
619 // In some implementations InitializeComplete is sent
620 // in context, so check before starting activeSchedulerWait.
621 else if(iIsOpenState == EIsOpening)
623 iInitializeState = KRequestPending;
624 iActiveSchedulerWait->Start();
626 iInitCallFrmSetDataType = EFalse;
627 User::LeaveIfError(iInitializeState);
634 TFourCC CMMFMdaAudioInputStream::DataType() const
636 return iDataTypeCode;
639 TInt CMMFMdaAudioInputStream::BitRateL() const
641 TAny* ptr = iDevSound->CustomInterface(KUidCustomInterfaceDevSoundBitRate);
643 User::Leave(KErrNotSupported);
645 MMMFDevSoundCustomInterfaceBitRate* bitrate = static_cast<MMMFDevSoundCustomInterfaceBitRate*>(ptr);
646 return bitrate->BitRateL();
650 void CMMFMdaAudioInputStream::SetBitRateL(TInt aBitRate)
652 TAny* ptr = iDevSound->CustomInterface(KUidCustomInterfaceDevSoundBitRate);
654 User::Leave(KErrNotSupported);
656 MMMFDevSoundCustomInterfaceBitRate* bitrate = static_cast<MMMFDevSoundCustomInterfaceBitRate*>(ptr);
657 bitrate->SetBitRateL(aBitRate);
660 void CMMFMdaAudioInputStream::GetSupportedBitRatesL(RArray<TInt>& aSupportedBitRates)
662 // ensure that the array is empty before passing it in
663 aSupportedBitRates.Reset();
664 TAny* ptr = iDevSound->CustomInterface(KUidCustomInterfaceDevSoundBitRate);
666 User::Leave(KErrNotSupported);
667 MMMFDevSoundCustomInterfaceBitRate* bitrate = static_cast<MMMFDevSoundCustomInterfaceBitRate*>(ptr);
668 bitrate->GetSupportedBitRatesL(aSupportedBitRates);
674 CMMFMdaAudioInputStream::CActiveCallback::CActiveCallback(MMdaAudioInputStreamCallback& aCallback)
675 : CActive(EPriorityStandard), iCallback(aCallback)
677 CActiveScheduler::Add(this);
680 CMMFMdaAudioInputStream::CActiveCallback::~CActiveCallback()
685 void CMMFMdaAudioInputStream::CActiveCallback::RunL()
687 iCallback.MaiscOpenComplete(iStatus.Int());
690 void CMMFMdaAudioInputStream::CActiveCallback::DoCancel()
694 void CMMFMdaAudioInputStream::CActiveCallback::Signal(const TInt aReason)
698 // Signal ourselves to run with the given completion code
699 TRequestStatus* status = &iStatus;
700 User::RequestComplete(status, aReason);
708 * To be called when intialize stream complete
710 * @param "TInt aError"
711 * error code, initialize stream succeed when aError = 0
714 void CMMFMdaAudioInputStream::InitializeComplete(TInt aError)
717 if(iIsOpenState == EIsOpening)
721 // Use settings to set audio properties after the dev sound has been
722 // successfully initialised
725 TRAP(err, RealSetAudioPropertiesL(iSettings.iSampleRate, iSettings.iChannels));
730 // Signal for the MaiscOpenComplete callback to be called asynchronously.Ignore if InitializeL is called from SetDataTypeL
731 if(!iInitCallFrmSetDataType)
733 iActiveCallback->Signal(err);
735 iIsOpenState = err ? EIsNotOpen : EIsOpen;
736 //reset iAudioDataStored flag if set - whatever don't want to use next time
737 iAudioDataStored = EFalse;
738 if(iInitializeState == KRequestPending)
740 iInitializeState = err;
741 iActiveSchedulerWait->AsyncStop();
745 iInitializeState = err;//Set the error.
755 void CMMFMdaAudioInputStream::ToneFinished(TInt /*aError*/)
757 Panic(EAudioInputPanicNotSupported);
762 * Called when sound device has filled data buffer
764 * @param "CMMFBuffer* aBuffer"
765 * a pointer point to CMMFBuffer, which is used for recieved data
768 void CMMFMdaAudioInputStream::BufferToBeEmptied(CMMFBuffer* aBuffer)
770 // Simply put, tries to copy the data from aBuffer into the clients storage buffers.
772 // The input stream iFifo data member is used to store the clients storage buffers
773 // that are passed to it via a call to ReadL.
775 // If iSingleBuffer is False, the first buffer on the fifo is copied to.
776 // This buffer is then removed off the fifo.
777 // The callback MaiscBufferCopied is invoked after each copy, passing that buffer.
778 // If the data is greater than the buffer then this process repeats with the next buffer.
780 // If iSingleBuffer is True, it is assumed only one buffer is on the fifo.
781 // The behaviour is the same as above except that a descriptor representing the
782 // buffers empty part is placed at the end of the fifo, and the callback
783 // MaiscBufferCopied is invoked only when the buffer is full.
785 // If the client sets iSingleBuffer to True and places more than one buffer on the fifo
786 // the behaviour is undefined and unsupported.
788 // If there are no more storage buffers on the fifo, the callback
789 // MaiscRecordComplete(KErrOverflow) is invoked.
791 const TDesC8& buffer = STATIC_CAST(CMMFDataBuffer*, aBuffer)->Data();
793 TInt lengthCopied = 0;
794 iBytesRecorded += buffer.Length();
796 // A stop was requested after all the data has been received
797 if (iState == EStopping && buffer.Length() == 0)
800 iCallback.MaiscRecordComplete(KErrNone);
805 // The fifo may have multiple storage buffers, i.e. one in each of its entries.
806 // Fill what we can in each. If we get an empty buffer then we have finished recording.
807 while (lengthCopied < buffer.Length())
809 // Chop up aBuffer into slices the buffers in iFifo can handle
810 TMMFFifoItem<TDes8>* firstItem = iFifo->Get();
812 if(firstItem != NULL)
814 TDes8& writeBuf = firstItem->GetData();
816 // We have a spare buffer slot
817 TInt slotLength = Min(buffer.Length()-lengthCopied, writeBuf.MaxLength());
818 writeBuf = buffer.Mid(lengthCopied, slotLength);
819 lengthCopied += slotLength;
821 // Determine whether to callback the client or not.
822 // I.e. if we have multiple small buffers that we want to process quickly or
823 // when a singular buffer is FULL.
824 // Note: That if the client asks to Stop, the buffer may not be filled!
827 // Remember this item for later.
828 // We really only want the first item as this covers the entire
829 // client storage buffer. We will adjust the actual length later.
830 if (iStorageItem.Ptr() == NULL)
832 iStorageItem.Set (const_cast<TUint8*>(writeBuf.Ptr()), 0, writeBuf.MaxLength());
835 // In this iteration we may just be looking at a right-part of the original
836 // buffer. Update the actual length of data.
837 TInt actualLength = (writeBuf.Ptr()-iStorageItem.Ptr()) + writeBuf.Length();
838 iStorageItem.SetLength(actualLength);
840 // Is the buffer full?
841 if (writeBuf.Length() == writeBuf.MaxLength())
843 // The singular buffer has been filled so pass it back to the client
844 iCallback.MaiscBufferCopied(KErrNone, iStorageItem);
845 iStorageItem.Set (NULL,0,0);
849 // Create a window to the 'remaining' free section of the storage buffer
850 iBufferPtr.Set (const_cast<TUint8*>(writeBuf.Ptr())+lengthCopied, 0, writeBuf.MaxLength()-lengthCopied);
852 // Add the window to the fifo
853 TInt err = Read(iBufferPtr);
857 iCallback.MaiscRecordComplete(err);
860 ASSERT(iState == ERecording);
866 iCallback.MaiscBufferCopied(KErrNone, writeBuf);
868 //Check if client called Stop from the MaiscBufferCopied.
869 //If so, we should neither continue this loop nor delete the first item. Stop cleans up all the buffers
870 if(iState == EStopped)
876 // Remove this storage buffer from the fifo as we want to have access to any others behind it.
877 iFifo->RemoveFirstItem();
883 // run out of buffers - report an overflow error
885 iCallback.MaiscRecordComplete(KErrOverflow);
891 // Keep recording if there are free buffers
892 if (!iFifo->IsEmpty())
893 iDevSound->RecordData();
898 * Called when record operation complete, successfully or otherwise
900 * @param "TInt aError"
901 * an error value which will indicate playing successfully complete
902 * if error value is 0
905 void CMMFMdaAudioInputStream::PlayError(TInt /*aError*/)
907 Panic(EAudioInputPanicNotSupported);
916 void CMMFMdaAudioInputStream::BufferToBeFilled(CMMFBuffer* /*aBuffer*/)
918 Panic(EAudioInputPanicNotSupported);
926 void CMMFMdaAudioInputStream::RecordError(TInt aError)
928 if (iState == ERecording)
930 if (aError != KErrCancel)
932 iCallback.MaiscRecordComplete(aError);
934 // else must have been cancelled by client. Doesn't need to be notified
945 void CMMFMdaAudioInputStream::ConvertError(TInt /*aError*/)
947 Panic(EAudioInputPanicNotSupported);
955 void CMMFMdaAudioInputStream::DeviceMessage(TUid /*aMessageType*/, const TDesC8& /*aMsg*/)
957 Panic(EAudioInputPanicNotSupported);
960 // CustomInferface - just pass on to DevSound.
961 TAny* CMMFMdaAudioInputStream::CustomInterface(TUid aInterfaceId)
963 return iDevSound->CustomInterface(aInterfaceId);
967 void CMMFMdaAudioInputStream::SetSingleBufferMode(TBool aSingleMode)
969 iSingleBuffer = aSingleMode;
972 void CMMFMdaAudioInputStream::ShutDown()
974 // Need to take for the case where Stop is invoked from the destructor of CMdaAudioInputStream
975 // Need to ensure that there are no callbacks to the client at this stage
976 if (iState != EStopped)
980 if (iSingleBuffer && iStorageItem.Ptr() != NULL)
982 iStorageItem.Set (NULL,0,0);
985 // Delete all buffers in the fifo
986 TMMFFifoItem<TDes8>* firstItem;
987 while((firstItem = iFifo->Get()) != NULL)
989 iFifo->RemoveFirstItem();