diff options
-rw-r--r-- | build.symbian/bld.inf | 14 | ||||
-rw-r--r-- | build.symbian/symbian_audio_aps.mmp | 20 | ||||
-rw-r--r-- | build.symbian/symbian_ua.mmp | 48 | ||||
-rw-r--r-- | build.symbian/symsndtest.mmp | 47 | ||||
-rw-r--r-- | pjmedia/include/pjmedia/symbian_sound_aps.h | 52 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/symbian_sound_aps.cpp | 824 | ||||
-rw-r--r-- | pjsip-apps/src/symbian_ua/main_symbian.cpp | 3 | ||||
-rw-r--r-- | pjsip-apps/src/symbian_ua/ua.cpp | 5 | ||||
-rw-r--r-- | pjsip-apps/src/symbian_ua_gui/group/symbian_ua_gui.mmp | 26 | ||||
-rw-r--r-- | pjsip-apps/src/symbian_ua_gui/src/symbian_ua.cpp | 4 | ||||
-rw-r--r-- | pjsip-apps/src/symbian_ua_gui/src/symbian_ua_guiApplication.cpp | 4 | ||||
-rw-r--r-- | pjsip-apps/src/symsndtest/app_main.cpp | 72 | ||||
-rw-r--r-- | pjsip-apps/src/symsndtest/main_symbian.cpp | 3 |
13 files changed, 1041 insertions, 81 deletions
diff --git a/build.symbian/bld.inf b/build.symbian/bld.inf index 80645f64..b624905e 100644 --- a/build.symbian/bld.inf +++ b/build.symbian/bld.inf @@ -1,3 +1,6 @@ +#define SND_USE_NULL 0 +#define SND_USE_APS 0 + prj_platforms winscw //armv5 @@ -10,8 +13,6 @@ pjlib.mmp pjlib_util.mmp pjnath.mmp pjsdp.mmp -//null_audio.mmp -symbian_audio.mmp pjmedia.mmp pjsip.mmp pjsip_simple.mmp @@ -23,6 +24,15 @@ libsrtp.mmp libgsmcodec.mmp libspeexcodec.mmp +/* Sound device impl */ +#if SND_USE_NULL + null_audio.mmp +#elif SND_USE_APS + symbian_audio_aps.mmp +#else + symbian_audio.mmp +#endif + /* Applications */ //pjlib_test.mmp //symsndtest.mmp diff --git a/build.symbian/symbian_audio_aps.mmp b/build.symbian/symbian_audio_aps.mmp new file mode 100644 index 00000000..85cb1e84 --- /dev/null +++ b/build.symbian/symbian_audio_aps.mmp @@ -0,0 +1,20 @@ +TARGET symbian_audio_aps.lib +TARGETTYPE lib + +OPTION CW -lang c++ +OPTION GCCE -O2 -fno-unit-at-a-time + +MACRO PJ_M_I386=1 +MACRO PJ_SYMBIAN=1 + +SYSTEMINCLUDE ..\pjmedia\include +SYSTEMINCLUDE ..\pjlib\include + +SOURCEPATH ..\pjmedia\src\pjmedia +SOURCE symbian_sound_aps.cpp + +SYSTEMINCLUDE \epoc32\include\mmf\server +SYSTEMINCLUDE \epoc32\include\mmf\common +SYSTEMINCLUDE \epoc32\include\mda\common +SYSTEMINCLUDE \epoc32\include\libc +SYSTEMINCLUDE \epoc32\include diff --git a/build.symbian/symbian_ua.mmp b/build.symbian/symbian_ua.mmp index e23d0d86..5bdfebad 100644 --- a/build.symbian/symbian_ua.mmp +++ b/build.symbian/symbian_ua.mmp @@ -1,3 +1,6 @@ +#define SND_USE_NULL 0
+#define SND_USE_APS 0
+
TARGET symbian_ua.exe
TARGETTYPE exe
UID 0x0 0xA000000D
@@ -7,10 +10,6 @@ SOURCEPATH ..\pjsip-apps\src\symbian_ua MACRO PJ_M_I386=1
MACRO PJ_SYMBIAN=1
-#if defined(PJ_BUILD_DLL)
- MACRO PJ_DLL=1
-#endif
-
// Source files
SOURCE ua.cpp
@@ -18,6 +17,10 @@ SOURCE main_symbian.cpp DOCUMENT ua.h
+START RESOURCE symbian_ua_reg.rss
+ TARGETPATH \private\10003a3f\apps
+END
+
SYSTEMINCLUDE ..\pjlib\include
SYSTEMINCLUDE ..\pjlib-util\include
SYSTEMINCLUDE ..\pjnath\include
@@ -27,18 +30,25 @@ SYSTEMINCLUDE ..\pjsip\include SYSTEMINCLUDE \epoc32\include
SYSTEMINCLUDE \epoc32\include\libc
-#if defined(PJ_BUILD_DLL)
- LIBRARY pjsua_lib.lib pjsip_ua.lib
- LIBRARY pjsip_simple.lib pjsip.lib pjsdp.lib pjmedia.lib
- LIBRARY pjnath.lib pjlib_util.lib pjlib.lib
- LIBRARY symbian_audio.lib libsrtp.lib
- LIBRARY libgsmcodec.lib libspeexcodec.lib
+STATICLIBRARY pjsua_lib.lib pjsip_ua.lib
+STATICLIBRARY pjsip_simple.lib pjsip.lib pjsdp.lib pjmedia.lib
+STATICLIBRARY pjnath.lib pjlib_util.lib pjlib.lib
+STATICLIBRARY libsrtp.lib
+STATICLIBRARY libgsmcodec.lib libspeexcodec.lib
+
+#if SND_USE_NULL || defined(WINSCW)
+ STATICLIBRARY null_audio.lib
+ CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment
+#elif SND_USE_APS
+ STATICLIBRARY symbian_audio_aps.lib
+ LIBRARY APSSession2.lib
+ CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment MultimediaDD
+ MACRO PJMEDIA_SYM_SND_USE_APS=1
#else
- STATICLIBRARY pjsua_lib.lib pjsip_ua.lib
- STATICLIBRARY pjsip_simple.lib pjsip.lib pjsdp.lib pjmedia.lib
- STATICLIBRARY pjnath.lib pjlib_util.lib pjlib.lib
- STATICLIBRARY symbian_audio.lib libsrtp.lib
- STATICLIBRARY libgsmcodec.lib libspeexcodec.lib
+ STATICLIBRARY symbian_audio.lib
+ LIBRARY mediaclientaudiostream.lib
+ LIBRARY mediaclientaudioinputstream.lib
+ CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment
#endif
#ifdef WINSCW
@@ -46,14 +56,6 @@ SYSTEMINCLUDE \epoc32\include\libc #endif
LIBRARY esock.lib insock.lib charconv.lib euser.lib estlib.lib
-LIBRARY mediaclientaudiostream.lib
-LIBRARY mediaclientaudioinputstream.lib
-
-CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment
-
-START RESOURCE symbian_ua_reg.rss
- TARGETPATH \private\10003a3f\apps
-END
// The default 8KB seems to be insufficient with all bells and
// whistles turned on
diff --git a/build.symbian/symsndtest.mmp b/build.symbian/symsndtest.mmp index daea7005..ed42770e 100644 --- a/build.symbian/symsndtest.mmp +++ b/build.symbian/symsndtest.mmp @@ -1,3 +1,6 @@ +#define SND_USE_NULL 0 +#define SND_USE_APS 0 + TARGET symsndtest.exe TARGETTYPE exe UID 0x0 0xA000000E @@ -7,15 +10,15 @@ SOURCEPATH ..\pjsip-apps\src\symsndtest MACRO PJ_M_I386=1 MACRO PJ_SYMBIAN=1 -#if defined(PJ_BUILD_DLL) - MACRO PJ_DLL=1 -#endif - // Test files SOURCE app_main.cpp SOURCE main_symbian.cpp +START RESOURCE symsndtest_reg.rss + TARGETPATH \private\10003a3f\apps +END + SYSTEMINCLUDE ..\pjlib\include SYSTEMINCLUDE ..\pjmedia\include @@ -23,24 +26,30 @@ SYSTEMINCLUDE \epoc32\include SYSTEMINCLUDE \epoc32\include\libc LIBRARY charconv.lib euser.lib estlib.lib - -#if defined(PJ_BUILD_DLL) - LIBRARY symbian_audio.lib pjlib.lib +LIBRARY esock.lib insock.lib +STATICLIBRARY pjlib.lib pjmedia.lib + +#if SND_USE_NULL || defined(WINSCW) + STATICLIBRARY null_audio.lib + CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment +#elif SND_USE_APS + SOURCEPATH ..\pjmedia\src\pjmedia + SOURCE symbian_sound_aps.cpp + + SYSTEMINCLUDE \epoc32\include\mmf\server + SYSTEMINCLUDE \epoc32\include\mmf\common + SYSTEMINCLUDE \epoc32\include\mda\common + + //STATICLIBRARY symbian_audio_aps.lib + LIBRARY APSSession2.lib + CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment MultimediaDD #else - STATICLIBRARY symbian_audio.lib pjlib.lib + STATICLIBRARY symbian_audio.lib + LIBRARY mediaclientaudiostream.lib + LIBRARY mediaclientaudioinputstream.lib + CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment #endif -LIBRARY esock.lib insock.lib -LIBRARY mediaclientaudiostream.lib -LIBRARY mediaclientaudioinputstream.lib - #ifdef WINSCW STATICLIBRARY eexe.lib ecrt0.lib #endif - -CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment - -START RESOURCE symsndtest_reg.rss - TARGETPATH \private\10003a3f\apps -END - diff --git a/pjmedia/include/pjmedia/symbian_sound_aps.h b/pjmedia/include/pjmedia/symbian_sound_aps.h new file mode 100644 index 00000000..c42c9580 --- /dev/null +++ b/pjmedia/include/pjmedia/symbian_sound_aps.h @@ -0,0 +1,52 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJMEDIA_SYMBIAN_SOUND_APS_H__ +#define __PJMEDIA_SYMBIAN_SOUND_APS_H__ + + +/** + * @file symbian_sound_aps.h + * @brief Sound device wrapper using Audio Proxy Server on + * Symbian S60 3rd edition. + */ +#include <pjmedia/types.h> + +PJ_BEGIN_DECL + +/** + * Activate/deactivate loudspeaker, when loudspeaker is inactive, audio + * will be routed to earpiece. + * + * @param stream The sound device stream, the stream should be started + * before calling this function. This param can be NULL + * to set the behaviour of next opened stream. + * @param active Specify PJ_TRUE to activate loudspeaker, and PJ_FALSE + * otherwise. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_snd_aps_activate_loudspeaker( + pjmedia_snd_stream *stream, + pj_bool_t active); + + +PJ_END_DECL + + +#endif /* __PJMEDIA_SYMBIAN_SOUND_APS_H__ */ diff --git a/pjmedia/src/pjmedia/symbian_sound_aps.cpp b/pjmedia/src/pjmedia/symbian_sound_aps.cpp new file mode 100644 index 00000000..9b54c3d3 --- /dev/null +++ b/pjmedia/src/pjmedia/symbian_sound_aps.cpp @@ -0,0 +1,824 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjmedia/sound.h> +#include <pjmedia/alaw_ulaw.h> +#include <pjmedia/errno.h> +#include <pj/assert.h> +#include <pj/log.h> +#include <pj/os.h> + +#include <e32msgqueue.h> +#include <sounddevice.h> +#include <APSClientSession.h> + +////////////////////////////////////////////////////////////////////////////// +// + +#define THIS_FILE "symbian_sound_aps.cpp" + +#define BYTES_PER_SAMPLE 2 +#define POOL_NAME "SymbianSoundAps" +#define POOL_SIZE 512 +#define POOL_INC 512 + +#if 1 +# define TRACE_(st) PJ_LOG(3, st) +#else +# define TRACE_(st) +#endif + +static pjmedia_snd_dev_info symbian_snd_dev_info = +{ + "Symbian Sound Device (APS)", + 1, + 1, + 8000 +}; + +/* App UID to open global APS queues to communicate with the APS server. */ +extern TPtrC APP_UID; + +/* Default setting for loudspeaker */ +static pj_bool_t act_loudspeaker = PJ_FALSE; + +/* Forward declaration of CPjAudioEngine */ +class CPjAudioEngine; + +/* + * PJMEDIA Sound Stream instance + */ +struct pjmedia_snd_stream +{ + // Pool + pj_pool_t *pool; + + // Common settings. + pjmedia_dir dir; + unsigned clock_rate; + unsigned channel_count; + unsigned samples_per_frame; + + // Audio engine + CPjAudioEngine *engine; +}; + +static pj_pool_factory *snd_pool_factory; + + +/* + * Utility: print sound device error + */ +static void snd_perror(const char *title, TInt rc) +{ + PJ_LOG(1,(THIS_FILE, "%s (error code=%d)", title, rc)); +} + +////////////////////////////////////////////////////////////////////////////// +// + +/** + * Abstract class for handler of callbacks from APS client. + */ +class MQueueHandlerObserver +{ +public: + virtual void InputStreamInitialized(const TInt aStatus) = 0; + virtual void OutputStreamInitialized(const TInt aStatus) = 0; + virtual void NotifyError(const TInt aError) = 0; + + virtual void RecCb(TAPSCommBuffer &buffer) = 0; + virtual void PlayCb(TAPSCommBuffer &buffer) = 0; +}; + +/** + * Handler for communication and data queue. + */ +class CQueueHandler : public CActive +{ +public: + // Types of queue handler + enum TQueueHandlerType { + ERecordCommQueue, + EPlayCommQueue, + ERecordQueue, + EPlayQueue + }; + + // The order corresponds to the APS Server state, do not change! + enum TState { + EAPSPlayerInitialize = 1, + EAPSRecorderInitialize = 2, + EAPSPlayData = 3, + EAPSRecordData = 4, + EAPSPlayerInitComplete = 5, + EAPSRecorderInitComplete = 6 + }; + + static CQueueHandler* NewL(MQueueHandlerObserver* aObserver, + RMsgQueue<TAPSCommBuffer>* aQ, + TQueueHandlerType aType) + { + CQueueHandler* self = new (ELeave) CQueueHandler(aObserver, aQ, aType); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + + // Destructor + ~CQueueHandler() { Cancel(); } + + // Start listening queue event + void Start() { + iQ->NotifyDataAvailable(iStatus); + SetActive(); + } + +private: + // Constructor + CQueueHandler(MQueueHandlerObserver* aObserver, + RMsgQueue<TAPSCommBuffer>* aQ, + TQueueHandlerType aType) + : CActive(CActive::EPriorityHigh), + iQ(aQ), iObserver(aObserver), iType(aType) + { + CActiveScheduler::Add(this); + + // use lower priority for comm queues + if ((iType == ERecordCommQueue) || (iType == EPlayCommQueue)) + SetPriority(CActive::EPriorityStandard); + } + + // Second phase constructor + void ConstructL() {} + + // Inherited from CActive + void DoCancel() { iQ->CancelDataAvailable(); } + + void RunL() { + if (iStatus != KErrNone) { + iObserver->NotifyError(iStatus.Int()); + return; + } + + TAPSCommBuffer buffer; + TInt ret = iQ->Receive(buffer); + + if (ret != KErrNone) { + iObserver->NotifyError(ret); + return; + } + + switch (iType) { + case ERecordQueue: + if (buffer.iCommand == EAPSRecordData) { + iObserver->RecCb(buffer); + } + break; + + // Callbacks from the APS main thread + case EPlayCommQueue: + switch (buffer.iCommand) { + case EAPSPlayData: + if (buffer.iStatus == KErrUnderflow) { + iObserver->PlayCb(buffer); + } + break; + case EAPSPlayerInitialize: + iObserver->NotifyError(buffer.iStatus); + break; + case EAPSPlayerInitComplete: + iObserver->OutputStreamInitialized(buffer.iStatus); + break; + case EAPSRecorderInitComplete: + iObserver->InputStreamInitialized(buffer.iStatus); + break; + default: + iObserver->NotifyError(buffer.iStatus); + break; + } + break; + + // Callbacks from the APS recorder thread + case ERecordCommQueue: + switch (buffer.iCommand) { + // The APS recorder thread will only report errors + // through this handler. All other callbacks will be + // sent from the APS main thread through EPlayCommQueue + case EAPSRecorderInitialize: + if (buffer.iStatus == KErrNone) { + iObserver->InputStreamInitialized(buffer.iStatus); + break; + } + case EAPSRecordData: + iObserver->NotifyError(buffer.iStatus); + break; + default: + break; + } + break; + + default: + break; + } + + // issue next request + iQ->NotifyDataAvailable(iStatus); + SetActive(); + } + + // Data + RMsgQueue<TAPSCommBuffer> *iQ; // (not owned) + MQueueHandlerObserver *iObserver; // (not owned) + TQueueHandlerType iType; +}; + + +/* + * Implementation: Symbian Input & Output Stream. + */ +class CPjAudioEngine : public CBase, MQueueHandlerObserver +{ +public: + enum State + { + STATE_NULL, + STATE_READY, + STATE_STREAMING + }; + + ~CPjAudioEngine(); + + static CPjAudioEngine *NewL(pjmedia_snd_stream *parent_strm, + pjmedia_dir dir, + pjmedia_snd_rec_cb rec_cb, + pjmedia_snd_play_cb play_cb, + void *user_data); + + TInt StartL(); + void Stop(); + + TInt ActivateSpeaker(TBool active); + +private: + CPjAudioEngine(pjmedia_snd_stream *parent_strm, + pjmedia_dir dir, + pjmedia_snd_rec_cb rec_cb, + pjmedia_snd_play_cb play_cb, + void *user_data); + void ConstructL(); + + TInt InitPlayL(); + TInt InitRecL(); + TInt StartStreamL(); + + // Inherited from MQueueHandlerObserver + virtual void InputStreamInitialized(const TInt aStatus); + virtual void OutputStreamInitialized(const TInt aStatus); + virtual void NotifyError(const TInt aError); + + virtual void RecCb(TAPSCommBuffer &buffer); + virtual void PlayCb(TAPSCommBuffer &buffer); + + State state_; + pjmedia_snd_stream *parentStrm_; + pjmedia_dir dir_; + pjmedia_snd_rec_cb recCb_; + pjmedia_snd_play_cb playCb_; + void *userData_; + pj_uint32_t TsPlay_; + pj_uint32_t TsRec_; + + RAPSSession iSession; + TAPSInitSettings iSettings; + RMsgQueue<TAPSCommBuffer> iReadQ; + RMsgQueue<TAPSCommBuffer> iReadCommQ; + RMsgQueue<TAPSCommBuffer> iWriteQ; + RMsgQueue<TAPSCommBuffer> iWriteCommQ; + + CQueueHandler *iPlayCommHandler; + CQueueHandler *iRecCommHandler; + CQueueHandler *iRecHandler; +}; + + +CPjAudioEngine* CPjAudioEngine::NewL(pjmedia_snd_stream *parent_strm, + pjmedia_dir dir, + pjmedia_snd_rec_cb rec_cb, + pjmedia_snd_play_cb play_cb, + void *user_data) +{ + CPjAudioEngine* self = new (ELeave) CPjAudioEngine(parent_strm, dir, + rec_cb, play_cb, + user_data); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; +} + +CPjAudioEngine::CPjAudioEngine(pjmedia_snd_stream *parent_strm, + pjmedia_dir dir, + pjmedia_snd_rec_cb rec_cb, + pjmedia_snd_play_cb play_cb, + void *user_data) + : state_(STATE_NULL), + parentStrm_(parent_strm), + dir_(dir), + recCb_(rec_cb), + playCb_(play_cb), + userData_(user_data), + iPlayCommHandler(0), + iRecCommHandler(0), + iRecHandler(0) +{ +} + +CPjAudioEngine::~CPjAudioEngine() +{ + Stop(); + + delete iPlayCommHandler; + iPlayCommHandler = NULL; + delete iRecCommHandler; + iRecCommHandler = NULL; + + iSession.Close(); + + if (state_ == STATE_READY) { + if (dir_ != PJMEDIA_DIR_PLAYBACK) { + iReadQ.Close(); + iReadCommQ.Close(); + } + iWriteQ.Close(); + iWriteCommQ.Close(); + } +} + +TInt CPjAudioEngine::InitPlayL() +{ + if (state_ == STATE_STREAMING || state_ == STATE_READY) + return 0; + + TInt err = iSession.InitializePlayer(iSettings); + if (err != KErrNone) { + snd_perror("Failed to initialize player", err); + return err; + } + + // Open message queues for the output stream + TBuf<128> buf2 = iSettings.iGlobal; + buf2.Append(_L("PlayQueue")); + TBuf<128> buf3 = iSettings.iGlobal; + buf3.Append(_L("PlayCommQueue")); + + while (iWriteQ.OpenGlobal(buf2)) + User::After(10); + while (iWriteCommQ.OpenGlobal(buf3)) + User::After(10); + + // Construct message queue handler + iPlayCommHandler = CQueueHandler::NewL(this, + &iWriteCommQ, + CQueueHandler::EPlayCommQueue); + + // Start observing APS callbacks on output stream message queue + iPlayCommHandler->Start(); + + return 0; +} + +TInt CPjAudioEngine::InitRecL() +{ + if (state_ == STATE_STREAMING || state_ == STATE_READY) + return 0; + + // Initialize input stream device + TInt err = iSession.InitializeRecorder(iSettings); + if (err != KErrNone) { + snd_perror("Failed to initialize recorder", err); + return err; + } + + TBuf<128> buf1 = iSettings.iGlobal; + buf1.Append(_L("RecordQueue")); + TBuf<128> buf4 = iSettings.iGlobal; + buf4.Append(_L("RecordCommQueue")); + + // Must wait for APS thread to finish creating message queues + // before we can open and use them. + while (iReadQ.OpenGlobal(buf1)) + User::After(10); + while (iReadCommQ.OpenGlobal(buf4)) + User::After(10); + + // Construct message queue handlers + iRecCommHandler = CQueueHandler::NewL(this, + &iReadCommQ, + CQueueHandler::ERecordCommQueue); + + // Start observing APS callbacks from on input stream message queue + iRecCommHandler->Start(); + + return 0; +} + +TInt CPjAudioEngine::StartL() +{ + TInt err = iSession.Connect(); + if (err != KErrNone && err != KErrAlreadyExists) + return err; + + if (state_ == STATE_READY) + return StartStreamL(); + + // Even if only capturer are opened, playback thread of APS Server need + // to be run(?). Since some messages will be delivered via play comm queue. + return InitPlayL(); +} + +void CPjAudioEngine::Stop() +{ + iSession.Stop(); + + delete iRecHandler; + iRecHandler = NULL; + + state_ = STATE_READY; +} + +void CPjAudioEngine::ConstructL() +{ + iSettings.iFourCC = TFourCC(KMCPFourCCIdG711); + iSettings.iGlobal = APP_UID; + iSettings.iPriority = TMdaPriority(100); + iSettings.iPreference = TMdaPriorityPreference(0x05210001); + iSettings.iSettings.iChannels = EMMFMono; + iSettings.iSettings.iSampleRate = EMMFSampleRate8000Hz; + iSettings.iSettings.iVolume = 0; +} + +TInt CPjAudioEngine::StartStreamL() +{ + if (state_ == STATE_STREAMING) + return 0; + + iSession.SetCng(EFalse); + iSession.SetVadMode(EFalse); + iSession.SetPlc(EFalse); + iSession.SetEncoderMode(EALawOr20ms); + iSession.SetDecoderMode(EALawOr20ms); + iSession.ActivateLoudspeaker(act_loudspeaker); + + // Not only playback + if (dir_ != PJMEDIA_DIR_PLAYBACK) { + iRecHandler = CQueueHandler::NewL(this, &iReadQ, + CQueueHandler::ERecordQueue); + iRecHandler->Start(); + iSession.Read(); + } + + // Not only capture + if (dir_ != PJMEDIA_DIR_CAPTURE) { + iSession.Write(); + } + + state_ = STATE_STREAMING; + return 0; +} + +// Inherited from MQueueHandlerObserver +void CPjAudioEngine::InputStreamInitialized(const TInt aStatus) +{ + TRACE_((THIS_FILE, "InputStreamInitialized %d", aStatus)); + + state_ = STATE_READY; + if (aStatus == KErrNone) { + StartStreamL(); + } +} + +void CPjAudioEngine::OutputStreamInitialized(const TInt aStatus) +{ + TRACE_((THIS_FILE, "OutputStreamInitialized %d", aStatus)); + + if (aStatus == KErrNone) { + if (dir_ == PJMEDIA_DIR_PLAYBACK) { + state_ = STATE_READY; + // Only playback, start directly + StartStreamL(); + } else + InitRecL(); + } +} + +void CPjAudioEngine::NotifyError(const TInt aError) +{ + snd_perror("Error from CQueueHandler", aError); +} + +void CPjAudioEngine::RecCb(TAPSCommBuffer &buffer) +{ + pj_int16_t buf[160]; + pj_assert(buffer.iBuffer[0] == 1 && buffer.iBuffer[1] == 0); + + for (int i=0; i<160; ++i) + buf[i] = pjmedia_alaw2linear(buffer.iBuffer[i+2]); + + recCb_(userData_, 0, buf, sizeof(buf)); +} + +void CPjAudioEngine::PlayCb(TAPSCommBuffer &buffer) +{ + pj_int16_t buf[160]; + + playCb_(userData_, 0, buf, sizeof(buf)); + + buffer.iCommand = CQueueHandler::EAPSPlayData; + buffer.iStatus = 0; + buffer.iBuffer.Zero(); + buffer.iBuffer.Append(1); + buffer.iBuffer.Append(0); + for (int i=0; i<160; ++i) + buffer.iBuffer.Append(pjmedia_linear2alaw(buf[i])); + + iWriteQ.Send(buffer); +} + + +TInt CPjAudioEngine::ActivateSpeaker(TBool active) +{ + if (state_ == STATE_READY || state_ == STATE_STREAMING) { + iSession.ActivateLoudspeaker(active); + return KErrNone; + } + return KErrNotReady; +} +////////////////////////////////////////////////////////////////////////////// +// + + +/* + * Initialize sound subsystem. + */ +PJ_DEF(pj_status_t) pjmedia_snd_init(pj_pool_factory *factory) +{ + snd_pool_factory = factory; + return PJ_SUCCESS; +} + +/* + * Get device count. + */ +PJ_DEF(int) pjmedia_snd_get_dev_count(void) +{ + /* Always return 1 */ + return 1; +} + +/* + * Get device info. + */ +PJ_DEF(const pjmedia_snd_dev_info*) pjmedia_snd_get_dev_info(unsigned index) +{ + /* Always return the default sound device */ + if (index == (unsigned)-1) + index = 0; + + PJ_ASSERT_RETURN(index==0, NULL); + return &symbian_snd_dev_info; +} + +static pj_status_t sound_open(pjmedia_dir dir, + unsigned clock_rate, + unsigned channel_count, + unsigned samples_per_frame, + unsigned bits_per_sample, + pjmedia_snd_rec_cb rec_cb, + pjmedia_snd_play_cb play_cb, + void *user_data, + pjmedia_snd_stream **p_snd_strm) +{ + pj_pool_t *pool; + pjmedia_snd_stream *strm; + + PJ_ASSERT_RETURN(p_snd_strm, PJ_EINVAL); + PJ_ASSERT_RETURN(clock_rate == 8000 && channel_count == 1 && + bits_per_sample == 16, PJ_ENOTSUP); + PJ_ASSERT_RETURN((dir == PJMEDIA_DIR_CAPTURE_PLAYBACK && rec_cb && play_cb) + || (dir == PJMEDIA_DIR_CAPTURE && rec_cb && !play_cb) + || (dir == PJMEDIA_DIR_PLAYBACK && !rec_cb && play_cb), + PJ_EINVAL); + + pool = pj_pool_create(snd_pool_factory, POOL_NAME, POOL_SIZE, POOL_INC, + NULL); + if (!pool) + return PJ_ENOMEM; + + strm = (pjmedia_snd_stream*) pj_pool_zalloc(pool, + sizeof(pjmedia_snd_stream)); + strm->dir = dir; + strm->pool = pool; + strm->clock_rate = clock_rate; + strm->channel_count = channel_count; + strm->samples_per_frame = samples_per_frame; + + // Create the audio engine. + TRAPD(err, strm->engine = CPjAudioEngine::NewL(strm, strm->dir, + rec_cb, play_cb, + user_data)); + if (err != KErrNone) { + pj_pool_release(pool); + return PJ_RETURN_OS_ERROR(err); + } + + // Done. + *p_snd_strm = strm; + return PJ_SUCCESS; +} + + + +/* + * Open sound recorder stream. + */ +PJ_DEF(pj_status_t) pjmedia_snd_open_rec( int index, + unsigned clock_rate, + unsigned channel_count, + unsigned samples_per_frame, + unsigned bits_per_sample, + pjmedia_snd_rec_cb rec_cb, + void *user_data, + pjmedia_snd_stream **p_snd_strm) +{ + if (index < 0) index = 0; + PJ_ASSERT_RETURN(index == 0, PJ_EINVAL); + + return sound_open(PJMEDIA_DIR_CAPTURE, clock_rate, channel_count, + samples_per_frame, bits_per_sample, rec_cb, NULL, + user_data, p_snd_strm); +} + +PJ_DEF(pj_status_t) pjmedia_snd_open_player( int index, + unsigned clock_rate, + unsigned channel_count, + unsigned samples_per_frame, + unsigned bits_per_sample, + pjmedia_snd_play_cb play_cb, + void *user_data, + pjmedia_snd_stream **p_snd_strm ) +{ + if (index < 0) index = 0; + PJ_ASSERT_RETURN(index == 0, PJ_EINVAL); + + return sound_open(PJMEDIA_DIR_PLAYBACK, clock_rate, channel_count, + samples_per_frame, bits_per_sample, NULL, play_cb, + user_data, p_snd_strm); +} + +PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id, + int play_id, + unsigned clock_rate, + unsigned channel_count, + unsigned samples_per_frame, + unsigned bits_per_sample, + pjmedia_snd_rec_cb rec_cb, + pjmedia_snd_play_cb play_cb, + void *user_data, + pjmedia_snd_stream **p_snd_strm) +{ + if (rec_id < 0) rec_id = 0; + if (play_id < 0) play_id = 0; + PJ_ASSERT_RETURN(play_id == 0 && rec_id == 0, PJ_EINVAL); + + return sound_open(PJMEDIA_DIR_CAPTURE_PLAYBACK, clock_rate, channel_count, + samples_per_frame, bits_per_sample, rec_cb, play_cb, + user_data, p_snd_strm); +} + +/* + * Get stream info. + */ +PJ_DEF(pj_status_t) pjmedia_snd_stream_get_info(pjmedia_snd_stream *strm, + pjmedia_snd_stream_info *pi) +{ + PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL); + + pj_bzero(pi, sizeof(*pi)); + pi->dir = strm->dir; + pi->play_id = 0; + pi->rec_id = 0; + pi->clock_rate = strm->clock_rate; + pi->channel_count = strm->channel_count; + pi->samples_per_frame = strm->samples_per_frame; + pi->bits_per_sample = BYTES_PER_SAMPLE * 8; + pi->rec_latency = 0; + pi->play_latency = 0; + + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pjmedia_snd_stream_start(pjmedia_snd_stream *stream) +{ + PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); + + if (stream->engine) { + TInt err = stream->engine->StartL(); + if (err != KErrNone) + return PJ_RETURN_OS_ERROR(err); + } + + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pjmedia_snd_stream_stop(pjmedia_snd_stream *stream) +{ + PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); + + if (stream->engine) { + stream->engine->Stop(); + } + + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pjmedia_snd_stream_close(pjmedia_snd_stream *stream) +{ + pj_pool_t *pool; + + PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); + + if (stream->engine) { + delete stream->engine; + stream->engine = NULL; + } + + pool = stream->pool; + if (pool) { + stream->pool = NULL; + pj_pool_release(pool); + } + + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pjmedia_snd_deinit(void) +{ + /* Nothing to do */ + return PJ_SUCCESS; +} + + +/* + * Set sound latency. + */ +PJ_DEF(pj_status_t) pjmedia_snd_set_latency(unsigned input_latency, + unsigned output_latency) +{ + /* Nothing to do */ + PJ_UNUSED_ARG(input_latency); + PJ_UNUSED_ARG(output_latency); + return PJ_SUCCESS; +} + + +/* + * Activate/deactivate loudspeaker. + */ +PJ_DEF(pj_status_t) pjmedia_snd_aps_activate_loudspeaker( + pjmedia_snd_stream *stream, + pj_bool_t active) +{ + if (stream == NULL) { + act_loudspeaker = active; + } else { + if (stream->engine == NULL) + return PJ_EINVAL; + + TInt err = stream->engine->ActivateSpeaker(active); + if (err != KErrNone) + return PJ_RETURN_OS_ERROR(err); + } + + return PJ_SUCCESS; +} diff --git a/pjsip-apps/src/symbian_ua/main_symbian.cpp b/pjsip-apps/src/symbian_ua/main_symbian.cpp index 62de1581..1786148f 100644 --- a/pjsip-apps/src/symbian_ua/main_symbian.cpp +++ b/pjsip-apps/src/symbian_ua/main_symbian.cpp @@ -27,6 +27,9 @@ // Global Variables CConsoleBase* console; +// Needed by APS +TPtrC APP_UID = _L("A000000E"); + //////////////////////////////////////////////////////////////////////////// class MyTask : public CActive diff --git a/pjsip-apps/src/symbian_ua/ua.cpp b/pjsip-apps/src/symbian_ua/ua.cpp index 5396bc72..7e4c9c3a 100644 --- a/pjsip-apps/src/symbian_ua/ua.cpp +++ b/pjsip-apps/src/symbian_ua/ua.cpp @@ -326,12 +326,17 @@ static pj_status_t app_startup() pjsua_logging_config_default(&log_cfg); log_cfg.console_level = 4; log_cfg.cb = &log_writer; + //log_cfg.log_filename = pj_str("C:\\data\\symbian_ua.log"); pjsua_media_config_default(&med_cfg); med_cfg.thread_cnt = 0; // Disable threading on Symbian med_cfg.has_ioqueue = PJ_FALSE; med_cfg.clock_rate = 8000; +#if defined(PJMEDIA_SYM_SND_USE_APS) && (PJMEDIA_SYM_SND_USE_APS==1) + med_cfg.audio_frame_ptime = 20; +#else med_cfg.audio_frame_ptime = 40; +#endif med_cfg.ec_tail_len = 0; med_cfg.enable_ice = USE_ICE; med_cfg.snd_auto_close_time = 5; // wait for 5 seconds idle before sound dev get auto-closed diff --git a/pjsip-apps/src/symbian_ua_gui/group/symbian_ua_gui.mmp b/pjsip-apps/src/symbian_ua_gui/group/symbian_ua_gui.mmp index c00e1e28..7077b6ca 100644 --- a/pjsip-apps/src/symbian_ua_gui/group/symbian_ua_gui.mmp +++ b/pjsip-apps/src/symbian_ua_gui/group/symbian_ua_gui.mmp @@ -1,3 +1,6 @@ +#define SND_USE_NULL 0
+#define SND_USE_APS 0
+
TARGET symbian_ua_gui.exe
UID 0x100039CE 0xEBD12EE4
VENDORID 0
@@ -32,17 +35,30 @@ LIBRARY commonengine.lib efsrv.lib estor.lib eikcoctl.lib eikdlg.lib LIBRARY eikctl.lib bafl.lib fbscli.lib aknnotify.lib aknicon.lib
LIBRARY etext.lib gdi.lib egul.lib insock.lib
LIBRARY ecom.lib inetprotutil.lib http.lib esock.lib
+LIBRARY charconv.lib estlib.lib
STATICLIBRARY pjsua_lib.lib pjsip_ua.lib
STATICLIBRARY pjsip_simple.lib pjsip.lib pjsdp.lib pjmedia.lib
STATICLIBRARY pjnath.lib pjlib_util.lib pjlib.lib
-STATICLIBRARY symbian_audio.lib libsrtp.lib
+STATICLIBRARY libsrtp.lib
STATICLIBRARY libgsmcodec.lib
STATICLIBRARY libspeexcodec.lib
-LIBRARY charconv.lib estlib.lib
-LIBRARY mediaclientaudiostream.lib
-LIBRARY mediaclientaudioinputstream.lib
+
+#if SND_USE_NULL || defined(WINSCW)
+ STATICLIBRARY null_audio.lib
+ CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment
+#elif SND_USE_APS
+ STATICLIBRARY symbian_audio_aps.lib
+ LIBRARY APSSession2.lib
+ CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment MultimediaDD
+ MACRO PJMEDIA_SYM_SND_USE_APS=1
+#else
+ STATICLIBRARY symbian_audio.lib
+ LIBRARY mediaclientaudiostream.lib
+ LIBRARY mediaclientaudioinputstream.lib
+ CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment
+#endif
LANG 01
@@ -56,5 +72,3 @@ END SOURCEPATH ..\src
SOURCE symbian_ua_guiContainerView.cpp symbian_ua_guiContainer.cpp symbian_ua_guiAppUi.cpp symbian_ua_guiDocument.cpp symbian_ua_guiApplication.cpp symbian_ua_guiSettingItemList.cpp Symbian_ua_guiSettingItemListSets.cpp symbian_ua_guiSettingItemListView.cpp
SOURCE symbian_ua.cpp
-
-CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment
diff --git a/pjsip-apps/src/symbian_ua_gui/src/symbian_ua.cpp b/pjsip-apps/src/symbian_ua_gui/src/symbian_ua.cpp index 7d693b41..7bb75177 100644 --- a/pjsip-apps/src/symbian_ua_gui/src/symbian_ua.cpp +++ b/pjsip-apps/src/symbian_ua_gui/src/symbian_ua.cpp @@ -323,7 +323,11 @@ int symbian_ua_init() med_cfg.thread_cnt = 0; // Disable threading on Symbian
med_cfg.has_ioqueue = PJ_FALSE;
med_cfg.clock_rate = 8000;
+#if defined(PJMEDIA_SYM_SND_USE_APS) && (PJMEDIA_SYM_SND_USE_APS==1)
+ med_cfg.audio_frame_ptime = 20;
+#else
med_cfg.audio_frame_ptime = 40;
+#endif
med_cfg.ec_tail_len = 0;
med_cfg.enable_ice = USE_ICE;
med_cfg.snd_auto_close_time = 5; // wait for 5 seconds idle before sound dev get auto-closed
diff --git a/pjsip-apps/src/symbian_ua_gui/src/symbian_ua_guiApplication.cpp b/pjsip-apps/src/symbian_ua_gui/src/symbian_ua_guiApplication.cpp index e1b5e2bf..a234b036 100644 --- a/pjsip-apps/src/symbian_ua_gui/src/symbian_ua_guiApplication.cpp +++ b/pjsip-apps/src/symbian_ua_gui/src/symbian_ua_guiApplication.cpp @@ -17,6 +17,10 @@ #endif
// ]]] end generated region [Generated Includes]
+
+// Needed by APS
+TPtrC APP_UID = _L("EBD12EE4");
+
/**
* @brief Returns the application's UID (override from CApaApplication::AppDllUid())
* @return UID for this application (KUidsymbian_ua_guiApplication)
diff --git a/pjsip-apps/src/symsndtest/app_main.cpp b/pjsip-apps/src/symsndtest/app_main.cpp index 981e5308..3ee50731 100644 --- a/pjsip-apps/src/symsndtest/app_main.cpp +++ b/pjsip-apps/src/symsndtest/app_main.cpp @@ -16,6 +16,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <pjmedia/delaybuf.h> #include <pjmedia/sound.h> #include <pj/errno.h> #include <pj/os.h> @@ -27,10 +28,9 @@ #define THIS_FILE "app_main.cpp" #define CLOCK_RATE 8000 #define CHANNEL_COUNT 1 -#define PTIME 100 -#define SAMPLES_PER_FRAME (80) +#define PTIME 20 +#define SAMPLES_PER_FRAME (CLOCK_RATE*PTIME/1000) #define BITS_PER_SAMPLE 16 -#define LOOPBACK_BUFF_COUNT 100 extern CConsoleBase* console; @@ -39,9 +39,8 @@ static pjmedia_snd_stream *strm; static unsigned rec_cnt, play_cnt; static pj_time_val t_start; - -static pj_int16_t buff_loopback[SAMPLES_PER_FRAME*LOOPBACK_BUFF_COUNT]; -static pj_uint32_t pointer_w, pointer_r; +pj_pool_t *pool; +pjmedia_delay_buf *delaybuf; /* Logging callback */ static void log_writer(int level, const char *buf, unsigned len) @@ -73,7 +72,7 @@ static pj_status_t app_init() /* Redirect log */ pj_log_set_log_func((void (*)(int,const char*,int)) &log_writer); pj_log_set_decor(PJ_LOG_HAS_NEWLINE); - pj_log_set_level(5); + pj_log_set_level(3); /* Init pjlib */ status = pj_init(); @@ -103,6 +102,26 @@ static pj_status_t app_init() i, info->name, info->input_count, info->output_count, info->default_samples_per_sec)); } + + /* Create pool */ + pool = pj_pool_create(&cp.factory, THIS_FILE, 512, 512, NULL); + if (pool == NULL) { + app_perror("pj_pool_create()", status); + pj_caching_pool_destroy(&cp); + pj_shutdown(); + return status; + } + + /* Init delay buffer */ + status = pjmedia_delay_buf_create(pool, THIS_FILE, CLOCK_RATE, + SAMPLES_PER_FRAME, CHANNEL_COUNT, + 0, 0, &delaybuf); + if (status != PJ_SUCCESS) { + app_perror("pjmedia_delay_buf_create()", status); + //pj_caching_pool_destroy(&cp); + //pj_shutdown(); + //return status; + } return PJ_SUCCESS; } @@ -116,21 +135,15 @@ static pj_status_t rec_cb(void *user_data, { PJ_UNUSED_ARG(user_data); PJ_UNUSED_ARG(timestamp); - PJ_UNUSED_ARG(input); PJ_UNUSED_ARG(size); - pj_memcpy(&buff_loopback[pointer_w*SAMPLES_PER_FRAME], input, size); + pjmedia_delay_buf_put(delaybuf, (pj_int16_t*)input); if (size != SAMPLES_PER_FRAME*2) { PJ_LOG(3, (THIS_FILE, "Size captured = %u", size)); - pj_bzero(&buff_loopback[pointer_w*SAMPLES_PER_FRAME]+size/2, SAMPLES_PER_FRAME*2 - size); } - if (++pointer_w >= LOOPBACK_BUFF_COUNT) { - pointer_w = 0; - } - ++rec_cnt; return PJ_SUCCESS; } @@ -143,13 +156,9 @@ static pj_status_t play_cb(void *user_data, { PJ_UNUSED_ARG(user_data); PJ_UNUSED_ARG(timestamp); + PJ_UNUSED_ARG(size); - //pj_bzero(output, size); - pj_memcpy(output, &buff_loopback[pointer_r*SAMPLES_PER_FRAME], SAMPLES_PER_FRAME*2); - - if (++pointer_r >= LOOPBACK_BUFF_COUNT) { - pointer_r = 0; - } + pjmedia_delay_buf_get(delaybuf, (pj_int16_t*)output); ++play_cnt; return PJ_SUCCESS; @@ -186,6 +195,8 @@ static pj_status_t snd_start(unsigned flag) rec_cnt = play_cnt = 0; pj_gettimeofday(&t_start); + pjmedia_delay_buf_reset(delaybuf); + status = pjmedia_snd_stream_start(strm); if (status != PJ_SUCCESS) { app_perror("snd start", status); @@ -194,9 +205,6 @@ static pj_status_t snd_start(unsigned flag) return status; } - pointer_w = LOOPBACK_BUFF_COUNT/2; - pointer_r = 0; - return PJ_SUCCESS; } @@ -231,6 +239,8 @@ static void app_fini() snd_stop(); pjmedia_snd_deinit(); + pjmedia_delay_buf_destroy(delaybuf); + pj_pool_release(pool); pj_caching_pool_destroy(&cp); pj_shutdown(); } @@ -295,11 +305,11 @@ static void PrintMenu() { PJ_LOG(3, (THIS_FILE, "\n\n" "Menu:\n" - " b Start bidir sound\n" - " r Start recorder\n" + " a Start bidir sound\n" + " t Start recorder\n" " p Start player\n" - " c Stop & close sound\n" - " q Quit\n")); + " d Stop & close sound\n" + " w Quit\n")); } // Implementation: called when read has completed. @@ -309,20 +319,20 @@ void ConsoleUI::RunL() pj_bool_t reschedule = PJ_TRUE; switch (kc) { - case 'q': + case 'w': asw_->AsyncStop(); reschedule = PJ_FALSE; break; - case 'b': + case 'a': snd_start(PJMEDIA_DIR_CAPTURE_PLAYBACK); break; - case 'r': + case 't': snd_start(PJMEDIA_DIR_CAPTURE); break; case 'p': snd_start(PJMEDIA_DIR_PLAYBACK); break; - case 'c': + case 'd': snd_stop(); break; default: diff --git a/pjsip-apps/src/symsndtest/main_symbian.cpp b/pjsip-apps/src/symsndtest/main_symbian.cpp index 8c4d8026..3db9a19c 100644 --- a/pjsip-apps/src/symsndtest/main_symbian.cpp +++ b/pjsip-apps/src/symsndtest/main_symbian.cpp @@ -27,6 +27,9 @@ // Global Variables CConsoleBase* console; +// Needed by APS +TPtrC APP_UID = _L("A000000D"); + int app_main(); |