--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mmserv/voipaudioservices/VoIPServer/src/VoIPUplinkThread.cpp Tue Feb 02 01:08:46 2010 +0200
@@ -0,0 +1,828 @@
+/*
+ * Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Nokia Corporation - initial contribution.
+ *
+ * Contributors:
+ *
+ * Description: VoIP Audio Services
+ * Implements uplink thread object.
+ *
+ */
+
+#include <IlbcEncoderIntfc.h>
+#include <G711EncoderIntfc.h>
+#include <G729EncoderIntfc.h>
+#include <SpeechEncoderConfig.h>
+#include "debugtracemacros.h"
+#include "VoIPSharedData.h"
+#include "VoIPServerThread.h"
+
+// -----------------------------------------------------------------------------
+// CVoIPUplinkThread::CVoIPUplinkThread
+// Standard Constructor
+// -----------------------------------------------------------------------------
+//
+CVoIPUplinkThread::CVoIPUplinkThread(TSharedData& aData) :
+ iShared(aData)
+ {
+ }
+
+// -----------------------------------------------------------------------------
+// CVoIPUplinkThread::~CVoIPUplinkThread
+// Standard Constructor
+// -----------------------------------------------------------------------------
+//
+CVoIPUplinkThread::~CVoIPUplinkThread()
+ {
+ TRACE_PRN_FN_ENT;
+
+ Stop();
+ delete iSpeechEncoderConfig;
+ delete iG711EncoderIntfc;
+ delete iG729EncoderIntfc;
+ delete iIlbcEncoderIntfc;
+
+ TRACE_PRN_FN_EXT;
+ }
+
+// -----------------------------------------------------------------------------
+// CVoIPUplinkThread::NewL
+// Symbian two-phase constructor
+// -----------------------------------------------------------------------------
+//
+CVoIPUplinkThread* CVoIPUplinkThread::NewL(TSharedData& aData)
+ {
+ CVoIPUplinkThread* self = new (ELeave) CVoIPUplinkThread(aData);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop(self);
+ return self;
+ }
+
+// -----------------------------------------------------------------------------
+// CVoIPUplinkThread::ConstructL
+// Part two of Symbian two phase construction
+// -----------------------------------------------------------------------------
+//
+void CVoIPUplinkThread::ConstructL()
+ {
+ TRACE_PRN_FN_ENT;
+
+ InitThreadL();
+ iCodecID = iShared.iCodecSettings.iFourCC;
+ InitMsgQueuesL(KUpLinkQueue, KUpLinkThreadComQueue);
+
+ iShared.iMutex.Wait();
+ TRAPD(err, InitDevSoundL(EMMFStateRecording, iShared.iPriority,
+ iShared.iPreference));
+ iShared.iMutex.Signal();
+
+ if (err != KErrNone)
+ {
+ SendCmd(ECmdDnLinkError, err);
+ }
+ iMaxBufLen = DetermineMaxBufferLen();
+
+ // Client must set these before querying!
+ iG711CodecMode = CVoIPFormatIntfc::EG711ALaw;
+ iILBCCodecMode = CVoIPFormatIntfc::EiLBC20mSecFrame;
+
+ TRACE_PRN_FN_EXT;
+ }
+
+// -----------------------------------------------------------------------------
+// CVoIPUplinkThread::ThreadFunction
+// Thread Startup function
+// -----------------------------------------------------------------------------
+//
+TInt CVoIPUplinkThread::ThreadFunction(TAny* aData)
+ {
+ // get a pointer to the shared data object
+ TSharedData& shared = *((TSharedData*) aData);
+
+ // we can set the sync flag here
+ shared.iMutex.Wait();
+
+ // create a cleanup stack
+ CTrapCleanup* cleanupStack = CTrapCleanup::New();
+
+ if (!cleanupStack)
+ {
+ return KErrNoMemory;
+ }
+
+ CVoIPUplinkThread* thread = 0;
+ TRAPD(err, thread = CVoIPUplinkThread::NewL(shared));
+ if (err != KErrNone)
+ {
+ return err;
+ }
+
+ shared.iMutex.Signal();
+
+ // if we're still here, active scheduler has been constructed
+ // start wait loop which runs until it's time to end the thread
+ CActiveScheduler::Start();
+
+ // Termination cleanup
+ delete thread;
+ delete cleanupStack;
+
+ TRACE_PRN_N(_L("CVoIPUplinkThread::ThreadFunction : Thread CLOSED"));
+ return KErrNone;
+ }
+
+// -----------------------------------------------------------------------------
+// CVoIPUplinkThread::Start
+//
+// -----------------------------------------------------------------------------
+//
+void CVoIPUplinkThread::Start()
+ {
+ TRACE_PRN_FN_ENT;
+
+ TInt err = KErrNotReady;
+
+ if (iStatus == EReady)
+ {
+ TRAP(err, iDevSound->RecordInitL());
+ TRACE_PRN_IF_ERR(err);
+
+ if (err != KErrNone)
+ {
+ SendCmd(ECmdUpLinkError, err);
+ iStatus = EReady;
+ }
+#ifdef _DEBUG
+ else
+ {
+ iSamplesRecCount = 0;
+ }
+#endif
+ }
+
+ TRACE_PRN_FN_EXT;
+ }
+
+// -----------------------------------------------------------------------------
+// CVoIPUplinkThread::Stop
+//
+// -----------------------------------------------------------------------------
+//
+void CVoIPUplinkThread::Stop()
+ {
+ TRACE_PRN_FN_ENT;
+
+ if (iStatus == EStreaming)
+ {
+ iDevSound->Stop();
+ iStatus = EReady;
+ }
+
+ TRACE_PRN_FN_EXT;
+ }
+
+// -----------------------------------------------------------------------------
+// CVoIPUplinkThread::InitializeComplete
+// A callback from the DevSound indicating completion of the initialization.
+// It will send config data to the D/S and configure the encoder via CI.
+// If everything goes well, the state of the thread is set EReady.
+// The initialization completion message is sent back to the main thread.
+// -----------------------------------------------------------------------------
+//
+void CVoIPUplinkThread::InitializeComplete(TInt aError)
+ {
+ TRACE_PRN_FN_ENT;
+
+ TInt err = aError;
+
+ if (err == KErrNone)
+ {
+ TMMFCapabilities conf;
+ conf = iDevSound->Config();
+ conf.iRate = EMMFSampleRate8000Hz;
+ conf.iChannels = EMMFMono;
+ TRAP(err, iDevSound->SetConfigL(conf));
+ if (err == KErrNone)
+ {
+ // We are ready to stream even in case of later CI setting failure
+ iStatus = EReady;
+ TInt gain = iDevSound->MaxGain();
+ iShared.iMutex.Wait();
+ iShared.iMaxGain = gain;
+ iShared.iMutex.Signal();
+ }
+
+ // Init Custom Interface API to the Encoder
+ TRAPD(err0, SetCodecCiL());
+ if (err0 != KErrNone)
+ {
+ // DEBUG only
+ // Can ignore error - the encoder is not fully configured but
+ // can still run in the default mode.
+ TRACE_PRN_IF_ERR(err0);
+ }
+ }
+
+ // Notify the main thread
+ SendCmd(ECmdUplinkInitComplete, err);
+
+ TRACE_PRN_IF_ERR(err);
+ TRACE_PRN_FN_EXT;
+ }
+
+// -----------------------------------------------------------------------------
+// CVoIPUplinkThread::BufferToBeEmptied
+// From MDevSoundObserver
+// -----------------------------------------------------------------------------
+//
+void CVoIPUplinkThread::BufferToBeEmptied(CMMFBuffer* aBuffer)
+ {
+ // TRACE_PRN_N1(_L("SAMPLES-RECORDED [%d]"), iSamplesRecCount++);
+
+ iDevSoundBufPtr = static_cast<CMMFDataBuffer*> (aBuffer);
+ TDes8& data = iDevSoundBufPtr->Data();
+
+#ifndef __WINSCW__
+ iBufLen = iDevSoundBufPtr->BufferSize();
+#else //__WINSCW__
+ // Buffer size is always 4kB in WINS (PCM). Since we are testing stub
+ // adaptaions of all other supported codecs, and WINS playback is not
+ // supported anyway, we can throw away generated frames and use only
+ // up to the max length of other codecs just to keep the loopback alive.
+ iBufLen = iMaxBufLen;
+ data.SetLength(iBufLen);
+#endif //__WINSCW__
+
+ TRACE_PRN_N1(_L("VoIP->UPL: BTBE->LEN [%d]"), iBufLen);
+
+ // Adjust/create RChunk if necessary
+ TInt err = DoChunk(KChunkUPL, iBufLen, iMaxBufLen);
+
+ if (err != KErrNone)
+ {
+ Stop();
+ iMsgBuffer.iStatus = err;
+ }
+ else
+ {
+ // Pass buffer parameters to the client
+ iMsgBuffer.iStatus = iChunk.Handle();
+ iMsgBuffer.iInt = iBufLen;
+
+ // Copy data over to RChunk
+ TPtr8 dataPtr(iChunk.Base(), iBufLen, iMaxBufLen);
+ dataPtr = data;
+ iStatus = EStreaming;
+ }
+
+ // Notify client there is buffer ready to be emptied
+ iMsgBuffer.iRequest = ECmdEmptyBuffer;
+ iMsgQueue.Send(iMsgBuffer);
+ }
+
+// -----------------------------------------------------------------------------
+// CVoIPUplinkThread::BufferEmptied
+//
+// -----------------------------------------------------------------------------
+//
+void CVoIPUplinkThread::BufferEmptied()
+ {
+ // TRACE_PRN_N(_L("VoIP->UPL: BE"));
+
+ iDevSound->RecordData();
+ }
+
+// -----------------------------------------------------------------------------
+// CVoIPUplinkThread::RecordError
+// From MDevSoundObserver
+// Recording error is send to the main thread.
+// The state of recorder is rolled back to EReady.
+// -----------------------------------------------------------------------------
+//
+void CVoIPUplinkThread::RecordError(TInt aError)
+ {
+ TRACE_PRN_IF_ERR(aError);
+
+#ifdef _DEBUG
+ iSamplesRecCount = 0;
+#endif
+ iStatus = EReady;
+ SendCmd(ECmdUpLinkError, aError);
+ }
+
+// ---------------------------------------------------------------------------
+// CVoIPUplinkThread::SetCodecCiL
+//
+// ---------------------------------------------------------------------------
+//
+void CVoIPUplinkThread::SetCodecCiL()
+ {
+ TRACE_PRN_FN_ENT;
+
+ switch (iCodecID)
+ {
+ case KMccFourCCIdG711:
+ {
+ if (!iG711EncoderIntfc)
+ {
+ iG711EncoderIntfc = CG711EncoderIntfc::NewL(*iDevSound);
+ }
+ break;
+ }
+ case KMccFourCCIdG729:
+ {
+ if (!iG729EncoderIntfc)
+ {
+ iG729EncoderIntfc = CG729EncoderIntfc::NewL(*iDevSound);
+ }
+ break;
+ }
+ case KMccFourCCIdILBC:
+ {
+ if (!iIlbcEncoderIntfc)
+ {
+ iIlbcEncoderIntfc = CIlbcEncoderIntfc::NewL(*iDevSound);
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ if (!iSpeechEncoderConfig && iCodecID != KMMFFourCCCodePCM16)
+ {
+ iSpeechEncoderConfig = CSpeechEncoderConfig::NewL(*iDevSound);
+ }
+
+ TRACE_PRN_FN_EXT;
+ }
+
+// -----------------------------------------------------------------------------
+// CVoIPUplinkThread::SetGain
+//
+// -----------------------------------------------------------------------------
+//
+void CVoIPUplinkThread::SetGain()
+ {
+ iShared.iMutex.Wait();
+ TInt gain = iShared.iInt;
+ iShared.iMutex.Signal();
+ iDevSound->SetGain(gain);
+ TRACE_PRN_N1(_L("VoIP->UPL: SetGain [%d]"), gain);
+ }
+
+// -----------------------------------------------------------------------------
+// CVoIPUplinkThread::GetGain
+//
+// -----------------------------------------------------------------------------
+//
+void CVoIPUplinkThread::GetGain()
+ {
+ TInt gain = iDevSound->Gain();
+ iShared.iMutex.Wait();
+ iShared.iInt = gain;
+ iShared.iMutex.Signal();
+ SendCmd(ECmdGetGainComplete);
+ }
+
+// -----------------------------------------------------------------------------
+// CVoIPUplinkThread::SetIlbcCodecMode
+//
+// -----------------------------------------------------------------------------
+//
+void CVoIPUplinkThread::SetIlbcCodecMode()
+ {
+ TInt err = KErrNotSupported;
+
+ if (iStatus == EReady)
+ {
+ iShared.iMutex.Wait();
+ iILBCCodecMode = iShared.iCodecSettings.iILBCCodecMode;
+ iShared.iMutex.Signal();
+
+ if (iIlbcEncoderIntfc && iCodecID == KMccFourCCIdILBC)
+ {
+ if (iILBCCodecMode == CVoIPFormatIntfc::EiLBC20mSecFrame)
+ {
+ err = iIlbcEncoderIntfc->SetEncoderMode(
+ CIlbcEncoderIntfc::E20msFrame);
+ TRACE_PRN_0(
+ _L("VoIP->CVoIPUplinkThread[0x%x]::SetIlbcCodecMode [20ms Frame]"));
+ }
+ else
+ {
+ if (iILBCCodecMode == CVoIPFormatIntfc::EiLBC30mSecFrame)
+ {
+ err = iIlbcEncoderIntfc->SetEncoderMode(
+ CIlbcEncoderIntfc::E30msFrame);
+ TRACE_PRN_0(
+ _L("VoIP->CVoIPUplinkThread[0x%x]::SetIlbcCodecMode [30ms Frame]"));
+ }
+ }
+ }
+ }
+
+ if (err != KErrNone)
+ {
+ SendCmd(ECmdUpLinkError, err);
+ }
+
+ TRACE_PRN_IF_ERR(err);
+ }
+
+// -----------------------------------------------------------------------------
+// CVoIPUplinkThread::GetIlbcCodecMode
+//
+// -----------------------------------------------------------------------------
+//
+void CVoIPUplinkThread::GetIlbcCodecMode()
+ {
+ // not available through CIs -> return local value
+ iShared.iMutex.Wait();
+ iShared.iCodecSettings.iILBCCodecMode = iILBCCodecMode;
+ iShared.iMutex.Signal();
+ SendCmd(ECmdGetIlbcCodecModeComplete);
+ }
+
+// -----------------------------------------------------------------------------
+// CVoIPUplinkThread::SetG711CodecMode
+//
+// -----------------------------------------------------------------------------
+//
+void CVoIPUplinkThread::SetG711CodecMode()
+ {
+ TInt err = KErrNotSupported;
+
+ if (iStatus == EReady)
+ {
+ iShared.iMutex.Wait();
+ iG711CodecMode = iShared.iCodecSettings.iG711CodecMode;
+ iShared.iMutex.Signal();
+
+ if (iG711EncoderIntfc && iCodecID == KMccFourCCIdG711)
+ {
+ if (iG711CodecMode == CVoIPFormatIntfc::EG711ALaw)
+ {
+ err = iG711EncoderIntfc->SetEncoderMode(
+ CG711EncoderIntfc::EEncALaw);
+ TRACE_PRN_0(
+ _L("VoIP->CVoIPUplinkThread[0x%x]::SetG711CodecMode [ALaw]"));
+ }
+ else
+ {
+ if (iG711CodecMode == CVoIPFormatIntfc::EG711uLaw)
+ {
+ err = iG711EncoderIntfc->SetEncoderMode(
+ CG711EncoderIntfc::EEncULaw);
+ TRACE_PRN_0(
+ _L("VoIP->CVoIPUplinkThread[0x%x]::SetG711CodecMode [uLaw]"));
+ }
+ }
+ }
+ }
+
+ if (err != KErrNone)
+ {
+ SendCmd(ECmdUpLinkError, err);
+ }
+
+ TRACE_PRN_IF_ERR(err);
+ }
+
+// -----------------------------------------------------------------------------
+// CVoIPUplinkThread::GetG711CodecMode
+//
+// -----------------------------------------------------------------------------
+//
+void CVoIPUplinkThread::GetG711CodecMode()
+ {
+ // not available through CIs -> return local value
+ iShared.iMutex.Wait();
+ iShared.iCodecSettings.iG711CodecMode = iG711CodecMode;
+ iShared.iMutex.Signal();
+ SendCmd(ECmdGetG711CodecModeComplete);
+ }
+
+// -----------------------------------------------------------------------------
+// CVoIPUplinkThread::GetSupportedBitrates
+//
+// -----------------------------------------------------------------------------
+//
+void CVoIPUplinkThread::GetSupportedBitrates()
+ {
+ TInt err = KErrNotSupported;
+
+ if (iSpeechEncoderConfig)
+ {
+ RArray<TUint> bitrates;
+ err = iSpeechEncoderConfig->GetSupportedBitrates(bitrates);
+ iShared.iMutex.Wait();
+ iShared.iCodecSettings.iArrBitrates = bitrates;
+ iShared.iMutex.Signal();
+ }
+
+ SendCmd(ECmdGetSupportedBitratesComplete, err);
+ TRACE_PRN_IF_ERR(err);
+ }
+
+// -----------------------------------------------------------------------------
+// CVoIPUplinkThread::SetBitrate
+//
+// -----------------------------------------------------------------------------
+//
+void CVoIPUplinkThread::SetBitrate()
+ {
+ TInt err = KErrNotSupported;
+ iShared.iMutex.Wait();
+ TUint bitrate = iShared.iCodecSettings.iBitrate;
+ iShared.iMutex.Signal();
+
+ if (iSpeechEncoderConfig)
+ {
+ err = iSpeechEncoderConfig->SetBitrate(bitrate);
+ TRACE_PRN_N1(_L("VoIP->UPL: SetBitrate [%d]"), bitrate);
+ }
+
+ if (err != KErrNone)
+ {
+ SendCmd(ECmdUpLinkError, err);
+ }
+
+ TRACE_PRN_IF_ERR(err);
+ }
+
+// -----------------------------------------------------------------------------
+// CVoIPUplinkThread::GetBitrate
+//
+// -----------------------------------------------------------------------------
+//
+void CVoIPUplinkThread::GetBitrate()
+ {
+ TInt err = KErrNotSupported;
+ TUint bitrate = 0;
+
+ if (iSpeechEncoderConfig)
+ {
+ err = iSpeechEncoderConfig->GetBitrate(bitrate);
+ }
+
+ iShared.iMutex.Wait();
+ iShared.iCodecSettings.iBitrate = bitrate;
+ iShared.iMutex.Signal();
+ SendCmd(ECmdGetBitrateComplete, err);
+ TRACE_PRN_IF_ERR(err);
+ }
+
+// -----------------------------------------------------------------------------
+// CVoIPUplinkThread::SetVad
+//
+// -----------------------------------------------------------------------------
+//
+void CVoIPUplinkThread::SetVad()
+ {
+ TInt err = KErrNotSupported;
+ iShared.iMutex.Wait();
+ TBool vad = iShared.iCodecSettings.iVad;
+ iShared.iMutex.Signal();
+
+ switch (iCodecID)
+ {
+ case KMccFourCCIdG711:
+ {
+ if (iG711EncoderIntfc)
+ {
+ err = iG711EncoderIntfc->SetVadMode(vad);
+ }
+ break;
+ }
+ case KMccFourCCIdG729:
+ {
+ if (iG729EncoderIntfc)
+ {
+ err = iG729EncoderIntfc->SetVadMode(vad);
+ }
+ break;
+ }
+ case KMccFourCCIdILBC:
+ {
+ if (iIlbcEncoderIntfc)
+ {
+ err = iIlbcEncoderIntfc->SetVadMode(vad);
+ }
+ break;
+ }
+ case KMccFourCCIdAMRNB:
+ {
+ if (iSpeechEncoderConfig)
+ {
+ err = iSpeechEncoderConfig->SetVadMode(vad);
+ }
+ break;
+ }
+ default:
+ {
+ break; //KErrNotSupported
+ }
+ }
+
+ if (err != KErrNone)
+ {
+ SendCmd(ECmdUpLinkError, err);
+ }
+
+ TRACE_PRN_IF_ERR(err);
+ }
+
+// -----------------------------------------------------------------------------
+// CVoIPUplinkThread::GetVad
+//
+// -----------------------------------------------------------------------------
+//
+void CVoIPUplinkThread::GetVad()
+ {
+ TInt err = KErrNotSupported;
+ TBool vad = EFalse;
+
+ switch (iCodecID)
+ {
+ case KMccFourCCIdG711:
+ {
+ if (iG711EncoderIntfc)
+ {
+ err = iG711EncoderIntfc->GetVadMode(vad);
+ }
+ break;
+ }
+ case KMccFourCCIdG729:
+ {
+ if (iG729EncoderIntfc)
+ {
+ err = iG729EncoderIntfc->GetVadMode(vad);
+ }
+ break;
+ }
+ case KMccFourCCIdILBC:
+ {
+ if (iIlbcEncoderIntfc)
+ {
+ err = iIlbcEncoderIntfc->GetVadMode(vad);
+ }
+ break;
+ }
+ case KMccFourCCIdAMRNB:
+ {
+ if (iSpeechEncoderConfig)
+ {
+ err = iSpeechEncoderConfig->GetVadMode(vad);
+ }
+ break;
+ }
+ default:
+ {
+ break; //KErrNotSupported
+ }
+ }
+
+ iShared.iMutex.Wait();
+ iShared.iCodecSettings.iVad = vad;
+ iShared.iMutex.Signal();
+ SendCmd(ECmdGetVadComplete, err);
+ TRACE_PRN_IF_ERR(err);
+ }
+
+// -----------------------------------------------------------------------------
+// CVoIPUplinkThread::SendCmd
+// Completes active object's request and sets shared data command value to
+// one of the user requested commands.
+// -----------------------------------------------------------------------------
+//
+void CVoIPUplinkThread::SendCmd(TUserCommand aCmd, TInt aError)
+ {
+ iShared.iMutex.Wait();
+
+ iShared.iCmd = aCmd;
+ TRequestStatus* status = iShared.iMnThreadStatus;
+
+ if (status)
+ {
+ if (status->Int() == KRequestPending)
+ {
+ RThread t;
+ TInt err = t.Open(iShared.iMainThreadID);
+ if (err == KErrNone)
+ {
+ t.RequestComplete(status, aError);
+ }
+ }
+ }
+
+ iShared.iMutex.Signal();
+ }
+
+// -----------------------------------------------------------------------------
+// CVoIPUplinkThread
+// From MQueueHandlerObserverSrv
+// -----------------------------------------------------------------------------
+//
+void CVoIPUplinkThread::Event(TInt aEventType, TInt /*aError*/)
+ {
+ switch (aEventType)
+ {
+ case ECmdStartUplink:
+ {
+ Start();
+ break;
+ }
+ case ECmdStopUplink:
+ {
+ if (iStatus == EStreaming)
+ {
+ Stop();
+ }
+ break;
+ }
+ case ECmdBufferEmptied:
+ {
+ BufferEmptied();
+ break;
+ }
+ case ECmdGetGain:
+ {
+ GetGain();
+ break;
+ }
+ case ECmdSetGain:
+ {
+ SetGain();
+ break;
+ }
+ case ECmdSetIlbcCodecMode:
+ {
+ SetIlbcCodecMode();
+ break;
+ }
+ case ECmdGetIlbcCodecMode:
+ {
+ GetIlbcCodecMode();
+ break;
+ }
+ case ECmdSetG711CodecMode:
+ {
+ SetG711CodecMode();
+ break;
+ }
+ case ECmdGetG711CodecMode:
+ {
+ GetG711CodecMode();
+ break;
+ }
+ case ECmdGetSupportedBitrates:
+ {
+ GetSupportedBitrates();
+ break;
+ }
+ case ECmdSetBitrate:
+ {
+ SetBitrate();
+ break;
+ }
+ case ECmdGetBitrate:
+ {
+ GetBitrate();
+ break;
+ }
+ case ECmdSetVad:
+ {
+ SetVad();
+ break;
+ }
+ case ECmdGetVad:
+ {
+ GetVad();
+ break;
+ }
+ case ECmdTerminateThread:
+ default:
+ {
+ if (iStatus == EStreaming)
+ {
+ Stop();
+ }
+ // if unknown exception is raised, just exit this thread
+ CActiveScheduler::Stop();
+ break;
+ }
+ }
+ }
+
+// End of file