summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2007-08-31 15:04:52 +0000
committerBenny Prijono <bennylp@teluu.com>2007-08-31 15:04:52 +0000
commitb2ce067a55ee3eab4b80e48b3e83eefc02b8ade6 (patch)
treeaa428fefd97ce7bd17f5ecc0078809d1750c77aa
parent73fb6a5d604c925429aff2a618b8acc74f0cf702 (diff)
Continuing ticket #2: initial test with Symbian sound device implementation. Callback works, but not in full duplex
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1428 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--build.symbian/symsndtest.mmp26
-rw-r--r--pjmedia/src/pjmedia/symbian_sound.cpp200
-rw-r--r--pjsip-apps/src/symsndtest/app_main.cpp338
-rw-r--r--pjsip-apps/src/symsndtest/main_symbian.cpp153
4 files changed, 614 insertions, 103 deletions
diff --git a/build.symbian/symsndtest.mmp b/build.symbian/symsndtest.mmp
new file mode 100644
index 00000000..ad659f9f
--- /dev/null
+++ b/build.symbian/symsndtest.mmp
@@ -0,0 +1,26 @@
+TARGET symsndtest.exe
+TARGETTYPE exe
+UID 0x100039CE 0x10004287
+VENDORID 0x70000001
+
+SOURCEPATH ..\pjsip-apps\src\symsndtest
+
+MACRO PJ_M_I386=1
+MACRO PJ_SYMBIAN=1
+MACRO PJ_DLL=1
+
+// Test files
+
+SOURCE app_main.cpp
+SOURCE main_symbian.cpp
+
+SYSTEMINCLUDE ..\pjlib\include
+SYSTEMINCLUDE ..\pjmedia\include
+
+SYSTEMINCLUDE \epoc32\include
+SYSTEMINCLUDE \epoc32\include\libc
+
+LIBRARY charconv.lib euser.lib estlib.lib eexe.lib
+LIBRARY symbian_audio.lib pjlib.lib ecrt0.lib
+CAPABILITY None
+
diff --git a/pjmedia/src/pjmedia/symbian_sound.cpp b/pjmedia/src/pjmedia/symbian_sound.cpp
index c2db57b5..ee49f487 100644
--- a/pjmedia/src/pjmedia/symbian_sound.cpp
+++ b/pjmedia/src/pjmedia/symbian_sound.cpp
@@ -115,13 +115,21 @@ static TInt get_channel_cap(unsigned channel_count)
}
+/*
+ * 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));
+}
+
//////////////////////////////////////////////////////////////////////////////
//
/*
* Implementation: Symbian Input Stream.
*/
-class CPjAudioInputEngine : public MMdaAudioInputStreamCallback
+class CPjAudioInputEngine : public CBase, MMdaAudioInputStreamCallback
{
public:
enum State
@@ -143,7 +151,7 @@ public:
pj_status_t StartRecord();
void Stop();
-public:
+private:
State state_;
pjmedia_snd_stream *parentStrm_;
pjmedia_snd_rec_cb recCb_;
@@ -158,6 +166,7 @@ public:
pjmedia_snd_rec_cb rec_cb,
void *user_data);
void ConstructL();
+ TPtr8 & GetFrame();
public:
virtual void MaiscOpenComplete(TInt aError);
@@ -171,7 +180,7 @@ CPjAudioInputEngine::CPjAudioInputEngine(pjmedia_snd_stream *parent_strm,
pjmedia_snd_rec_cb rec_cb,
void *user_data)
: state_(STATE_INACTIVE), parentStrm_(parent_strm), recCb_(rec_cb),
- iInputStream_(NULL), iStreamBuffer_(NULL), iFramePtr_(NULL, 0),
+ iInputStream_(NULL), iStreamBuffer_(NULL), iFramePtr_(0, 0),
userData_(user_data), lastError_(KErrNone), timeStamp_(0)
{
}
@@ -180,13 +189,14 @@ CPjAudioInputEngine::~CPjAudioInputEngine()
{
Stop();
delete iStreamBuffer_;
+ iStreamBuffer_ = NULL;
}
void CPjAudioInputEngine::ConstructL()
{
- iStreamBuffer_ = HBufC8::NewMaxL(parentStrm_->samples_per_frame *
- parentStrm_->channel_count *
- BYTES_PER_SAMPLE);
+ iStreamBuffer_ = HBufC8::NewL(parentStrm_->samples_per_frame *
+ parentStrm_->channel_count *
+ BYTES_PER_SAMPLE);
}
CPjAudioInputEngine *CPjAudioInputEngine::NewLC(pjmedia_snd_stream *parent,
@@ -241,18 +251,6 @@ pj_status_t CPjAudioInputEngine::StartRecord()
pj_assert(iStreamSettings.iChannels != 0 &&
iStreamSettings.iSampleRate != 0);
- // Create timeout timer to wait for Open to complete
- RTimer timer;
- TRequestStatus reqStatus;
- TInt rc;
-
- rc = timer.CreateLocal();
- if (rc != KErrNone) {
- delete iInputStream_;
- iInputStream_ = NULL;
- return PJ_RETURN_OS_ERROR(rc);
- }
-
PJ_LOG(4,(THIS_FILE, "Opening sound device for capture, "
"clock rate=%d, channel count=%d..",
parentStrm_->clock_rate,
@@ -261,40 +259,7 @@ pj_status_t CPjAudioInputEngine::StartRecord()
// Open stream.
lastError_ = KRequestPending;
iInputStream_->Open(&iStreamSettings);
-
- // Wait until callback is called.
- if (lastError_ == KRequestPending) {
- timer.After(reqStatus, 5 * 1000 * 1000);
-
- do {
- User::WaitForAnyRequest();
- } while (lastError_==KRequestPending && reqStatus==KRequestPending);
-
- if (reqStatus==KRequestPending)
- timer.Cancel();
- }
-
- // Close timer
- timer.Close();
- // Handle timeout
- if (lastError_ == KRequestPending) {
- iInputStream_->Stop();
- delete iInputStream_;
- iInputStream_ = NULL;
- return PJ_ETIMEDOUT;
- }
- else if (lastError_ != KErrNone) {
- // Handle failure.
- delete iInputStream_;
- iInputStream_ = NULL;
- return PJ_RETURN_OS_ERROR(lastError_);
- }
-
- // Feed the first frame.
- iFramePtr_ = iStreamBuffer_->Des();
- iInputStream_->ReadL(iFramePtr_);
-
// Success
PJ_LOG(4,(THIS_FILE, "Sound capture started."));
return PJ_SUCCESS;
@@ -322,17 +287,44 @@ void CPjAudioInputEngine::Stop()
}
+TPtr8 & CPjAudioInputEngine::GetFrame()
+{
+ TInt l = parentStrm_->samples_per_frame *
+ parentStrm_->channel_count *
+ BYTES_PER_SAMPLE;
+ iStreamBuffer_->Des().FillZ(l);
+ iFramePtr_.Set((TUint8*)(iStreamBuffer_->Ptr()), l, l);
+ return iFramePtr_;
+}
+
void CPjAudioInputEngine::MaiscOpenComplete(TInt aError)
{
lastError_ = aError;
+ if (aError != KErrNone) {
+ snd_perror("Error in MaiscOpenComplete()", aError);
+ return;
+ }
+
+ // set stream priority to normal and time sensitive
+ iInputStream_->SetPriority(EPriorityNormal,
+ EMdaPriorityPreferenceTime);
+
+ // Read the first frame.
+ TPtr8 & frm = GetFrame();
+ TRAPD(err2, iInputStream_->ReadL(frm));
+ if (err2) {
+ PJ_LOG(4,(THIS_FILE, "Exception in iInputStream_->ReadL()"));
+ }
}
void CPjAudioInputEngine::MaiscBufferCopied(TInt aError,
const TDesC8 &aBuffer)
{
lastError_ = aError;
- if (aError != KErrNone)
+ if (aError != KErrNone) {
+ snd_perror("Error in MaiscBufferCopied()", aError);
return;
+ }
// Call the callback.
recCb_(userData_, timeStamp_, (void*)aBuffer.Ptr(), aBuffer.Size());
@@ -341,8 +333,11 @@ void CPjAudioInputEngine::MaiscBufferCopied(TInt aError,
timeStamp_ += (aBuffer.Size() * BYTES_PER_SAMPLE);
// Record next frame
- iFramePtr_ = iStreamBuffer_->Des();
- iInputStream_->ReadL(iFramePtr_);
+ TPtr8 & frm = GetFrame();
+ TRAPD(err2, iInputStream_->ReadL(frm));
+ if (err2) {
+ PJ_LOG(4,(THIS_FILE, "Exception in iInputStream_->ReadL()"));
+ }
}
@@ -350,6 +345,9 @@ void CPjAudioInputEngine::MaiscRecordComplete(TInt aError)
{
lastError_ = aError;
state_ = STATE_INACTIVE;
+ if (aError != KErrNone) {
+ snd_perror("Error in MaiscRecordComplete()", aError);
+ }
}
@@ -361,7 +359,7 @@ void CPjAudioInputEngine::MaiscRecordComplete(TInt aError)
* Implementation: Symbian Output Stream.
*/
-class CPjAudioOutputEngine : public MMdaAudioOutputStreamCallback
+class CPjAudioOutputEngine : public CBase, MMdaAudioOutputStreamCallback
{
public:
enum State
@@ -383,7 +381,7 @@ public:
pj_status_t StartPlay();
void Stop();
-public:
+private:
State state_;
pjmedia_snd_stream *parentStrm_;
pjmedia_snd_play_cb playCb_;
@@ -391,6 +389,7 @@ public:
CMdaAudioOutputStream *iOutputStream_;
TUint8 *frameBuf_;
unsigned frameBufSize_;
+ TPtrC8 frame_;
TInt lastError_;
unsigned timestamp_;
@@ -479,22 +478,11 @@ pj_status_t CPjAudioOutputEngine::StartPlay()
"clock rate=%d, channel count=%d..",
parentStrm_->clock_rate,
parentStrm_->channel_count));
-
+
// Open stream.
lastError_ = KRequestPending;
iOutputStream_->Open(&iStreamSettings);
- // Wait until callback is called.
- while (lastError_ == KRequestPending)
- pj_thread_sleep(100);
-
- // Handle failure.
- if (lastError_ != KErrNone) {
- delete iOutputStream_;
- iOutputStream_ = NULL;
- return PJ_RETURN_OS_ERROR(lastError_);
- }
-
// Success
PJ_LOG(4,(THIS_FILE, "Sound playback started"));
return PJ_SUCCESS;
@@ -560,9 +548,11 @@ void CPjAudioOutputEngine::MaoscOpenComplete(TInt aError)
// subsequent calls to WriteL() will be issued in
// MMdaAudioOutputStreamCallback::MaoscBufferCopied()
// until whole data buffer is written.
- TPtrC8 frame(frameBuf_, frameBufSize_);
- iOutputStream_->WriteL(frame);
- }
+ frame_.Set(frameBuf_, frameBufSize_);
+ iOutputStream_->WriteL(frame_);
+ } else {
+ snd_perror("Error in MaoscOpenComplete()", aError);
+ }
}
void CPjAudioOutputEngine::MaoscBufferCopied(TInt aError,
@@ -586,8 +576,8 @@ void CPjAudioOutputEngine::MaoscBufferCopied(TInt aError,
timestamp_ += (frameBufSize_ / BYTES_PER_SAMPLE);
// Write to playback stream.
- TPtrC8 frame(frameBuf_, frameBufSize_);
- iOutputStream_->WriteL(frame);
+ frame_.Set(frameBuf_, frameBufSize_);
+ iOutputStream_->WriteL(frame_);
} else if (aError==KErrAbort) {
// playing was aborted, due to call to CMdaAudioOutputStream::Stop()
@@ -596,6 +586,7 @@ void CPjAudioOutputEngine::MaoscBufferCopied(TInt aError,
// error writing data to output
lastError_ = aError;
state_ = STATE_INACTIVE;
+ snd_perror("Error in MaoscBufferCopied()", aError);
}
}
@@ -603,6 +594,9 @@ void CPjAudioOutputEngine::MaoscPlayComplete(TInt aError)
{
lastError_ = aError;
state_ = STATE_INACTIVE;
+ if (aError != KErrNone) {
+ snd_perror("Error in MaoscPlayComplete()", aError);
+ }
}
@@ -655,6 +649,8 @@ PJ_DEF(pj_status_t) pjmedia_snd_open_rec( int index,
pj_pool_t *pool;
pjmedia_snd_stream *strm;
+ if (index==-1) index = 0;
+
PJ_ASSERT_RETURN(index == 0, PJ_EINVAL);
PJ_ASSERT_RETURN(clock_rate && channel_count && samples_per_frame &&
bits_per_sample && rec_cb && p_snd_strm, PJ_EINVAL);
@@ -671,15 +667,9 @@ PJ_DEF(pj_status_t) pjmedia_snd_open_rec( int index,
strm->channel_count = channel_count;
strm->samples_per_frame = samples_per_frame;
- TMdaAudioDataSettings settings;
- TInt clockRateCap, channelCountCap;
-
- clockRateCap = get_clock_rate_cap(clock_rate);
- channelCountCap = get_channel_cap(channel_count);
-
PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL);
- PJ_ASSERT_RETURN(clockRateCap != 0, PJ_EINVAL);
- PJ_ASSERT_RETURN(channelCountCap != 0, PJ_EINVAL);
+ PJ_ASSERT_RETURN(get_clock_rate_cap(clock_rate) != 0, PJ_EINVAL);
+ PJ_ASSERT_RETURN(get_channel_cap(channel_count) != 0, PJ_EINVAL);
// Create the input stream.
TRAPD(err, strm->inEngine = CPjAudioInputEngine::NewL(strm, rec_cb,
@@ -707,6 +697,8 @@ PJ_DEF(pj_status_t) pjmedia_snd_open_player( int index,
pj_pool_t *pool;
pjmedia_snd_stream *strm;
+ if (index == -1) index = 0;
+
PJ_ASSERT_RETURN(index == 0, PJ_EINVAL);
PJ_ASSERT_RETURN(clock_rate && channel_count && samples_per_frame &&
bits_per_sample && play_cb && p_snd_strm, PJ_EINVAL);
@@ -723,15 +715,9 @@ PJ_DEF(pj_status_t) pjmedia_snd_open_player( int index,
strm->channel_count = channel_count;
strm->samples_per_frame = samples_per_frame;
- TMdaAudioDataSettings settings;
- TInt clockRateCap, channelCountCap;
-
- clockRateCap = get_clock_rate_cap(clock_rate);
- channelCountCap = get_channel_cap(channel_count);
-
PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL);
- PJ_ASSERT_RETURN(clockRateCap != 0, PJ_EINVAL);
- PJ_ASSERT_RETURN(channelCountCap != 0, PJ_EINVAL);
+ PJ_ASSERT_RETURN(get_clock_rate_cap(clock_rate) != 0, PJ_EINVAL);
+ PJ_ASSERT_RETURN(get_channel_cap(channel_count) != 0, PJ_EINVAL);
// Create the output stream.
TRAPD(err, strm->outEngine = CPjAudioOutputEngine::NewL(strm, play_cb,
@@ -760,6 +746,9 @@ PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id,
pj_pool_t *pool;
pjmedia_snd_stream *strm;
+ if (rec_id == -1) rec_id = 0;
+ if (play_id == -1) play_id = 0;
+
PJ_ASSERT_RETURN(rec_id == 0 && play_id == 0, PJ_EINVAL);
PJ_ASSERT_RETURN(clock_rate && channel_count && samples_per_frame &&
bits_per_sample && rec_cb && play_cb && p_snd_strm,
@@ -777,15 +766,9 @@ PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id,
strm->channel_count = channel_count;
strm->samples_per_frame = samples_per_frame;
- TMdaAudioDataSettings settings;
- TInt clockRateCap, channelCountCap;
-
- clockRateCap = get_clock_rate_cap(clock_rate);
- channelCountCap = get_channel_cap(channel_count);
-
PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL);
- PJ_ASSERT_RETURN(clockRateCap != 0, PJ_EINVAL);
- PJ_ASSERT_RETURN(channelCountCap != 0, PJ_EINVAL);
+ PJ_ASSERT_RETURN(get_clock_rate_cap(clock_rate) != 0, PJ_EINVAL);
+ PJ_ASSERT_RETURN(get_channel_cap(channel_count) != 0, PJ_EINVAL);
// Create the output stream.
TRAPD(err, strm->outEngine = CPjAudioOutputEngine::NewL(strm, play_cb,
@@ -795,6 +778,17 @@ PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id,
return PJ_RETURN_OS_ERROR(err);
}
+ // Create the input stream.
+ TRAPD(err1, strm->inEngine = CPjAudioInputEngine::NewL(strm, rec_cb,
+ user_data));
+ if (err1 != KErrNone) {
+ strm->inEngine = NULL;
+ delete strm->outEngine;
+ strm->outEngine = NULL;
+ pj_pool_release(pool);
+ return PJ_RETURN_OS_ERROR(err1);
+ }
+
// Done.
*p_snd_strm = strm;
return PJ_SUCCESS;
@@ -807,18 +801,18 @@ PJ_DEF(pj_status_t) pjmedia_snd_stream_start(pjmedia_snd_stream *stream)
PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
- if (stream->inEngine) {
- status = stream->inEngine->StartRecord();
- if (status != PJ_SUCCESS)
- return status;
- }
-
if (stream->outEngine) {
status = stream->outEngine->StartPlay();
if (status != PJ_SUCCESS)
return status;
}
+ if (stream->inEngine) {
+ status = stream->inEngine->StartRecord();
+ if (status != PJ_SUCCESS)
+ return status;
+ }
+
return PJ_SUCCESS;
}
diff --git a/pjsip-apps/src/symsndtest/app_main.cpp b/pjsip-apps/src/symsndtest/app_main.cpp
new file mode 100644
index 00000000..dcd6ed88
--- /dev/null
+++ b/pjsip-apps/src/symsndtest/app_main.cpp
@@ -0,0 +1,338 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2007 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 <pj/errno.h>
+#include <pj/os.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/unicode.h>
+#include <e32cons.h>
+
+#define THIS_FILE "app_main.cpp"
+#define CLOCK_RATE 8000
+#define CHANNEL_COUNT 1
+#define PTIME 100
+#define SAMPLES_PER_FRAME (2048)
+#define BITS_PER_SAMPLE 16
+
+
+extern CConsoleBase* console;
+
+static pj_caching_pool cp;
+static pjmedia_snd_stream *strm;
+static unsigned rec_cnt, play_cnt;
+static pj_time_val t_start;
+
+
+/* Logging callback */
+static void log_writer(int level, const char *buf, unsigned len)
+{
+ static wchar_t buf16[PJ_LOG_MAX_SIZE];
+
+ PJ_UNUSED_ARG(level);
+
+ pj_ansi_to_unicode(buf, len, buf16, PJ_ARRAY_SIZE(buf16));
+
+ TPtrC16 aBuf((const TUint16*)buf16, (TInt)len);
+ console->Write(aBuf);
+}
+
+/* perror util */
+static void app_perror(const char *title, pj_status_t status)
+{
+ char errmsg[PJ_ERR_MSG_SIZE];
+ pj_strerror(status, errmsg, sizeof(errmsg));
+ PJ_LOG(1,(THIS_FILE, "Error: %s: %s", title, errmsg));
+}
+
+/* Application init */
+static pj_status_t app_init()
+{
+ unsigned i, count;
+ pj_status_t status;
+
+ /* Redirect log */
+ pj_log_set_log_func((void (*)(int,const char*,int)) &log_writer);
+ pj_log_set_decor(PJ_LOG_HAS_NEWLINE);
+
+ /* Init pjlib */
+ status = pj_init();
+ if (status != PJ_SUCCESS) {
+ app_perror("pj_init()", status);
+ return status;
+ }
+
+ pj_caching_pool_init(&cp, NULL, 0);
+
+ /* Init sound subsystem */
+ status = pjmedia_snd_init(&cp.factory);
+ if (status != PJ_SUCCESS) {
+ app_perror("pjmedia_snd_init()", status);
+ pj_caching_pool_destroy(&cp);
+ pj_shutdown();
+ return status;
+ }
+
+ count = pjmedia_snd_get_dev_count();
+ PJ_LOG(3,(THIS_FILE, "Device count: %d", count));
+ for (i=0; i<count; ++i) {
+ const pjmedia_snd_dev_info *info;
+
+ info = pjmedia_snd_get_dev_info(i);
+ PJ_LOG(3, (THIS_FILE, "%d: %s %d/%d %dHz",
+ i, info->name, info->input_count, info->output_count,
+ info->default_samples_per_sec));
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/* Sound capture callback */
+static pj_status_t rec_cb(void *user_data,
+ pj_uint32_t timestamp,
+ void *input,
+ unsigned size)
+{
+ PJ_UNUSED_ARG(user_data);
+ PJ_UNUSED_ARG(timestamp);
+ PJ_UNUSED_ARG(input);
+ PJ_UNUSED_ARG(size);
+
+ ++rec_cnt;
+ return PJ_SUCCESS;
+}
+
+/* Play cb */
+static pj_status_t play_cb(void *user_data,
+ pj_uint32_t timestamp,
+ void *output,
+ unsigned size)
+{
+ PJ_UNUSED_ARG(user_data);
+ PJ_UNUSED_ARG(timestamp);
+
+ pj_bzero(output, size);
+
+ ++play_cnt;
+ return PJ_SUCCESS;
+}
+
+/* Start sound */
+static pj_status_t snd_start(unsigned flag)
+{
+ pj_status_t status;
+
+ if (strm != NULL) {
+ app_perror("snd already open", PJ_EINVALIDOP);
+ return PJ_EINVALIDOP;
+ }
+
+ if (flag==PJMEDIA_DIR_CAPTURE_PLAYBACK)
+ status = pjmedia_snd_open(-1, -1, CLOCK_RATE, CHANNEL_COUNT,
+ SAMPLES_PER_FRAME, BITS_PER_SAMPLE,
+ &rec_cb, &play_cb, NULL, &strm);
+ else if (flag==PJMEDIA_DIR_CAPTURE)
+ status = pjmedia_snd_open_rec(-1, CLOCK_RATE, CHANNEL_COUNT,
+ SAMPLES_PER_FRAME, BITS_PER_SAMPLE,
+ &rec_cb, NULL, &strm);
+ else
+ status = pjmedia_snd_open_player(-1, CLOCK_RATE, CHANNEL_COUNT,
+ SAMPLES_PER_FRAME, BITS_PER_SAMPLE,
+ &play_cb, NULL, &strm);
+
+ if (status != PJ_SUCCESS) {
+ app_perror("snd open", status);
+ return status;
+ }
+
+ rec_cnt = play_cnt = 0;
+ pj_gettimeofday(&t_start);
+
+ status = pjmedia_snd_stream_start(strm);
+ if (status != PJ_SUCCESS) {
+ app_perror("snd start", status);
+ pjmedia_snd_stream_close(strm);
+ strm = NULL;
+ return status;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/* Stop sound */
+static pj_status_t snd_stop()
+{
+ pj_time_val now;
+ pj_status_t status;
+
+ if (strm == NULL) {
+ app_perror("snd not open", PJ_EINVALIDOP);
+ return PJ_EINVALIDOP;
+ }
+
+ status = pjmedia_snd_stream_close(strm);
+ strm = NULL;
+
+ pj_gettimeofday(&now);
+ PJ_TIME_VAL_SUB(now, t_start);
+
+ PJ_LOG(3,(THIS_FILE, "Duration: %d.%03d", now.sec, now.msec));
+ PJ_LOG(3,(THIS_FILE, "Captured: %d", rec_cnt));
+ PJ_LOG(3,(THIS_FILE, "Played: %d", play_cnt));
+
+ return status;
+}
+
+/* Shutdown application */
+static void app_fini()
+{
+ if (strm)
+ snd_stop();
+
+ pjmedia_snd_deinit();
+ pj_caching_pool_destroy(&cp);
+ pj_shutdown();
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+/*
+ * The interractive console UI
+ */
+#include <e32base.h>
+
+class ConsoleUI : public CActive
+{
+public:
+ ConsoleUI(CActiveSchedulerWait *asw, CConsoleBase *con);
+
+ // Run console UI
+ void Run();
+
+ // Stop
+ void Stop();
+
+protected:
+ // Cancel asynchronous read.
+ void DoCancel();
+
+ // Implementation: called when read has completed.
+ void RunL();
+
+private:
+ CActiveSchedulerWait *asw_;
+ CConsoleBase *con_;
+};
+
+
+ConsoleUI::ConsoleUI(CActiveSchedulerWait *asw, CConsoleBase *con)
+: CActive(EPriorityHigh), asw_(asw), con_(con)
+{
+ CActiveScheduler::Add(this);
+}
+
+// Run console UI
+void ConsoleUI::Run()
+{
+ con_->Read(iStatus);
+ SetActive();
+}
+
+// Stop console UI
+void ConsoleUI::Stop()
+{
+ DoCancel();
+}
+
+// Cancel asynchronous read.
+void ConsoleUI::DoCancel()
+{
+ con_->ReadCancel();
+}
+
+static void PrintMenu()
+{
+ PJ_LOG(3, (THIS_FILE, "\n\n"
+ "Menu:\n"
+ " b Start bidir sound\n"
+ " r Start recorder\n"
+ " p Start player\n"
+ " c Stop & close sound\n"
+ " q Quit\n"));
+}
+
+// Implementation: called when read has completed.
+void ConsoleUI::RunL()
+{
+ TKeyCode kc = con_->KeyCode();
+ pj_bool_t reschedule = PJ_TRUE;
+
+ switch (kc) {
+ case 'q':
+ asw_->AsyncStop();
+ reschedule = PJ_FALSE;
+ break;
+ case 'b':
+ snd_start(PJMEDIA_DIR_CAPTURE_PLAYBACK);
+ break;
+ case 'r':
+ snd_start(PJMEDIA_DIR_CAPTURE);
+ break;
+ case 'p':
+ snd_start(PJMEDIA_DIR_PLAYBACK);
+ break;
+ case 'c':
+ snd_stop();
+ break;
+ default:
+ PJ_LOG(3,(THIS_FILE, "Keycode '%c' (%d) is pressed",
+ kc, kc));
+ break;
+ }
+
+ PrintMenu();
+
+ if (reschedule)
+ Run();
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+int app_main()
+{
+ if (app_init() != PJ_SUCCESS)
+ return -1;
+
+ // Run the UI
+ CActiveSchedulerWait *asw = new CActiveSchedulerWait;
+ ConsoleUI *con = new ConsoleUI(asw, console);
+
+ con->Run();
+
+ PrintMenu();
+ asw->Start();
+
+ delete con;
+ delete asw;
+
+ app_fini();
+ return 0;
+}
+
diff --git a/pjsip-apps/src/symsndtest/main_symbian.cpp b/pjsip-apps/src/symsndtest/main_symbian.cpp
new file mode 100644
index 00000000..8c4d8026
--- /dev/null
+++ b/pjsip-apps/src/symsndtest/main_symbian.cpp
@@ -0,0 +1,153 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2007 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 <e32std.h>
+#include <e32base.h>
+#include <e32std.h>
+#include <e32cons.h>
+#include <stdlib.h>
+
+
+// Global Variables
+CConsoleBase* console;
+
+int app_main();
+
+
+////////////////////////////////////////////////////////////////////////////
+class MyTask : public CActive
+{
+public:
+ static MyTask *NewL(CActiveSchedulerWait *asw);
+ ~MyTask();
+ void Start();
+
+protected:
+ MyTask(CActiveSchedulerWait *asw);
+ void ConstructL();
+ virtual void RunL();
+ virtual void DoCancel();
+
+private:
+ RTimer timer_;
+ CActiveSchedulerWait *asw_;
+};
+
+MyTask::MyTask(CActiveSchedulerWait *asw)
+: CActive(EPriorityNormal), asw_(asw)
+{
+}
+
+MyTask::~MyTask()
+{
+ timer_.Close();
+}
+
+void MyTask::ConstructL()
+{
+ timer_.CreateLocal();
+ CActiveScheduler::Add(this);
+}
+
+MyTask *MyTask::NewL(CActiveSchedulerWait *asw)
+{
+ MyTask *self = new (ELeave) MyTask(asw);
+ CleanupStack::PushL(self);
+
+ self->ConstructL();
+
+ CleanupStack::Pop(self);
+ return self;
+}
+
+void MyTask::Start()
+{
+ timer_.After(iStatus, 0);
+ SetActive();
+}
+
+void MyTask::RunL()
+{
+ int rc = app_main();
+ asw_->AsyncStop();
+}
+
+void MyTask::DoCancel()
+{
+
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+LOCAL_C void DoStartL()
+{
+ CActiveScheduler *scheduler = new (ELeave) CActiveScheduler;
+ CleanupStack::PushL(scheduler);
+ CActiveScheduler::Install(scheduler);
+
+ CActiveSchedulerWait *asw = new CActiveSchedulerWait;
+ CleanupStack::PushL(asw);
+
+ MyTask *task = MyTask::NewL(asw);
+ task->Start();
+
+ asw->Start();
+
+ delete task;
+
+ CleanupStack::Pop(asw);
+ delete asw;
+
+ CActiveScheduler::Install(NULL);
+ CleanupStack::Pop(scheduler);
+ delete scheduler;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+
+// E32Main()
+GLDEF_C TInt E32Main()
+{
+ // Mark heap usage
+ __UHEAP_MARK;
+
+ // Create cleanup stack
+ CTrapCleanup* cleanup = CTrapCleanup::New();
+
+ // Create output console
+ TRAPD(createError, console = Console::NewL(_L("Console"), TSize(KConsFullScreen,KConsFullScreen)));
+ if (createError)
+ return createError;
+
+ TRAPD(startError, DoStartL());
+
+ console->Printf(_L("[press any key to close]\n"));
+ console->Getch();
+
+ delete console;
+ delete cleanup;
+
+ CloseSTDLIB();
+
+ // Mark end of heap usage, detect memory leaks
+ __UHEAP_MARKEND;
+ return KErrNone;
+}
+