summaryrefslogtreecommitdiff
path: root/pjsip-apps/src/symsndtest/app_main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'pjsip-apps/src/symsndtest/app_main.cpp')
-rw-r--r--pjsip-apps/src/symsndtest/app_main.cpp434
1 files changed, 434 insertions, 0 deletions
diff --git a/pjsip-apps/src/symsndtest/app_main.cpp b/pjsip-apps/src/symsndtest/app_main.cpp
new file mode 100644
index 0000000..d4f5523
--- /dev/null
+++ b/pjsip-apps/src/symsndtest/app_main.cpp
@@ -0,0 +1,434 @@
+/* $Id: app_main.cpp 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * 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-audiodev/audiodev.h>
+#include <pjmedia/delaybuf.h>
+#include <pj/assert.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 20
+#define SAMPLES_PER_FRAME (CLOCK_RATE*PTIME/1000)
+#define BITS_PER_SAMPLE 16
+
+extern CConsoleBase* console;
+
+static pj_caching_pool cp;
+static pjmedia_aud_stream *strm;
+static unsigned rec_cnt, play_cnt;
+static pj_time_val t_start;
+static pjmedia_aud_param param;
+static pj_pool_t *pool;
+static pjmedia_delay_buf *delaybuf;
+static char frame_buf[256];
+
+static void copy_frame_ext(pjmedia_frame_ext *f_dst,
+ const pjmedia_frame_ext *f_src)
+{
+ pj_bzero(f_dst, sizeof(*f_dst));
+ if (f_src->subframe_cnt) {
+ f_dst->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
+ for (unsigned i = 0; i < f_src->subframe_cnt; ++i) {
+ pjmedia_frame_ext_subframe *sf;
+ sf = pjmedia_frame_ext_get_subframe(f_src, i);
+ pjmedia_frame_ext_append_subframe(f_dst, sf->data, sf->bitlen,
+ param.samples_per_frame);
+ }
+ } else {
+ f_dst->base.type = PJMEDIA_FRAME_TYPE_NONE;
+ }
+}
+
+/* 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);
+ pj_log_set_level(3);
+
+ /* 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_aud_subsys_init(&cp.factory);
+ if (status != PJ_SUCCESS) {
+ app_perror("pjmedia_snd_init()", status);
+ pj_caching_pool_destroy(&cp);
+ pj_shutdown();
+ return status;
+ }
+
+ count = pjmedia_aud_dev_count();
+ PJ_LOG(3,(THIS_FILE, "Device count: %d", count));
+ for (i=0; i<count; ++i) {
+ pjmedia_aud_dev_info info;
+ pj_status_t status;
+
+ status = pjmedia_aud_dev_get_info(i, &info);
+ pj_assert(status == PJ_SUCCESS);
+ PJ_LOG(3, (THIS_FILE, "%d: %s %d/%d %dHz",
+ i, info.name, info.input_count, info.output_count,
+ info.default_samples_per_sec));
+
+ unsigned j;
+
+ /* Print extended formats supported by this audio device */
+ PJ_LOG(3, (THIS_FILE, " Extended formats supported:"));
+ for (j = 0; j < info.ext_fmt_cnt; ++j) {
+ const char *fmt_name = NULL;
+
+ switch (info.ext_fmt[j].id) {
+ case PJMEDIA_FORMAT_PCMA:
+ fmt_name = "PCMA";
+ break;
+ case PJMEDIA_FORMAT_PCMU:
+ fmt_name = "PCMU";
+ break;
+ case PJMEDIA_FORMAT_AMR:
+ fmt_name = "AMR-NB";
+ break;
+ case PJMEDIA_FORMAT_G729:
+ fmt_name = "G729";
+ break;
+ case PJMEDIA_FORMAT_ILBC:
+ fmt_name = "ILBC";
+ break;
+ case PJMEDIA_FORMAT_PCM:
+ fmt_name = "PCM";
+ break;
+ default:
+ fmt_name = "Unknown";
+ break;
+ }
+ PJ_LOG(3, (THIS_FILE, " - %s", fmt_name));
+ }
+ }
+
+ /* 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;
+}
+
+
+/* Sound capture callback */
+static pj_status_t rec_cb(void *user_data,
+ pjmedia_frame *frame)
+{
+ PJ_UNUSED_ARG(user_data);
+
+ if (param.ext_fmt.id == PJMEDIA_FORMAT_PCM) {
+ pjmedia_delay_buf_put(delaybuf, (pj_int16_t*)frame->buf);
+
+ if (frame->size != SAMPLES_PER_FRAME*2) {
+ PJ_LOG(3, (THIS_FILE, "Size captured = %u",
+ frame->size));
+ }
+ } else {
+ pjmedia_frame_ext *f_src = (pjmedia_frame_ext*)frame;
+ pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frame_buf;
+
+ copy_frame_ext(f_dst, f_src);
+ }
+
+ ++rec_cnt;
+ return PJ_SUCCESS;
+}
+
+/* Play cb */
+static pj_status_t play_cb(void *user_data,
+ pjmedia_frame *frame)
+{
+ PJ_UNUSED_ARG(user_data);
+
+ if (param.ext_fmt.id == PJMEDIA_FORMAT_PCM) {
+ pjmedia_delay_buf_get(delaybuf, (pj_int16_t*)frame->buf);
+ frame->size = SAMPLES_PER_FRAME*2;
+ frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
+ } else {
+ pjmedia_frame_ext *f_src = (pjmedia_frame_ext*)frame_buf;
+ pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frame;
+
+ copy_frame_ext(f_dst, f_src);
+ }
+
+ ++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;
+ }
+
+ pjmedia_aud_dev_default_param(0, &param);
+ param.channel_count = CHANNEL_COUNT;
+ param.clock_rate = CLOCK_RATE;
+ param.samples_per_frame = SAMPLES_PER_FRAME;
+ param.dir = (pjmedia_dir) flag;
+ param.ext_fmt.id = PJMEDIA_FORMAT_AMR;
+ param.ext_fmt.bitrate = 12200;
+ param.output_route = PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER;
+
+ status = pjmedia_aud_stream_create(&param, &rec_cb, &play_cb, NULL, &strm);
+ if (status != PJ_SUCCESS) {
+ app_perror("snd open", status);
+ return status;
+ }
+
+ rec_cnt = play_cnt = 0;
+ pj_gettimeofday(&t_start);
+
+ pjmedia_delay_buf_reset(delaybuf);
+
+ status = pjmedia_aud_stream_start(strm);
+ if (status != PJ_SUCCESS) {
+ app_perror("snd start", status);
+ pjmedia_aud_stream_destroy(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_aud_stream_stop(strm);
+ if (status != PJ_SUCCESS) {
+ app_perror("snd failed to stop", status);
+ }
+ status = pjmedia_aud_stream_destroy(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_aud_subsys_shutdown();
+ pjmedia_delay_buf_destroy(delaybuf);
+ pj_pool_release(pool);
+ pj_caching_pool_destroy(&cp);
+ pj_shutdown();
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+/*
+ * The interractive console UI
+ */
+#include <e32base.h>
+
+class ConsoleUI : public CActive
+{
+public:
+ ConsoleUI(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:
+ CConsoleBase *con_;
+};
+
+
+ConsoleUI::ConsoleUI(CConsoleBase *con)
+: CActive(EPriorityUserInput), 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"
+ " a Start bidir sound\n"
+ " t Start recorder\n"
+ " p Start player\n"
+ " d Stop & close sound\n"
+ " w Quit\n"));
+}
+
+// Implementation: called when read has completed.
+void ConsoleUI::RunL()
+{
+ TKeyCode kc = con_->KeyCode();
+ pj_bool_t reschedule = PJ_TRUE;
+
+ switch (kc) {
+ case 'w':
+ snd_stop();
+ CActiveScheduler::Stop();
+ reschedule = PJ_FALSE;
+ break;
+ case 'a':
+ snd_start(PJMEDIA_DIR_CAPTURE_PLAYBACK);
+ break;
+ case 't':
+ snd_start(PJMEDIA_DIR_CAPTURE);
+ break;
+ case 'p':
+ snd_start(PJMEDIA_DIR_PLAYBACK);
+ break;
+ case 'd':
+ 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
+ ConsoleUI *con = new ConsoleUI(console);
+
+ con->Run();
+
+ PrintMenu();
+ CActiveScheduler::Start();
+
+ delete con;
+
+ app_fini();
+ return 0;
+}
+