Added audiotest and initial work on audio demo sample
git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/aps-direct@2463 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjsip-apps/build/sample_debug.dsp b/pjsip-apps/build/sample_debug.dsp
index c788f77..03c49d1 100644
--- a/pjsip-apps/build/sample_debug.dsp
+++ b/pjsip-apps/build/sample_debug.dsp
@@ -90,13 +90,6 @@
# Begin Source File
SOURCE=..\src\samples\debug.c
-
-!IF "$(CFG)" == "sample_debug - Win32 Release"
-
-!ELSEIF "$(CFG)" == "sample_debug - Win32 Debug"
-
-!ENDIF
-
# End Source File
# End Group
# Begin Group "Header Files"
diff --git a/pjsip-apps/build/samples.dsp b/pjsip-apps/build/samples.dsp
index 1305397..d76f1a0 100644
--- a/pjsip-apps/build/samples.dsp
+++ b/pjsip-apps/build/samples.dsp
@@ -90,6 +90,10 @@
# End Source File
# Begin Source File
+SOURCE=..\src\samples\auddemo.c
+# End Source File
+# Begin Source File
+
SOURCE=..\src\samples\confbench.c
# End Source File
# Begin Source File
diff --git a/pjsip-apps/src/samples/auddemo.c b/pjsip-apps/src/samples/auddemo.c
new file mode 100644
index 0000000..dbb2b8f
--- /dev/null
+++ b/pjsip-apps/src/samples/auddemo.c
@@ -0,0 +1,415 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2009 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-audiodev/audiotest.h>
+#include <pjmedia.h>
+#include <pjlib.h>
+#include <pjlib-util.h>
+
+#define THIS_FILE "auddemo.c"
+#define MAX_DEVICES 64
+
+
+static unsigned dev_count;
+static pjmedia_aud_dev_id dev_id[MAX_DEVICES];
+
+static void app_perror(const char *title, pj_status_t status)
+{
+ char errmsg[PJ_ERR_MSG_SIZE];
+
+ pj_strerror(status, errmsg, sizeof(errmsg));
+ printf( "%s: %s (err=%d)\n",
+ title, errmsg, status);
+}
+
+static void list_devices(void)
+{
+ unsigned i;
+ pj_status_t status;
+
+ dev_count = pjmedia_aud_dev_count();
+ if (dev_count == 0) {
+ PJ_LOG(3,(THIS_FILE, "No devices found"));
+ return;
+ }
+
+ PJ_LOG(3,(THIS_FILE, "Found %d devices:", dev_count));
+
+ dev_count = pjmedia_aud_dev_enum(PJ_ARRAY_SIZE(dev_id), dev_id);
+
+ for (i=0; i<dev_count; ++i) {
+ pjmedia_aud_dev_info info;
+
+ status = pjmedia_aud_dev_get_info(dev_id[i], &info);
+ if (status != PJ_SUCCESS)
+ continue;
+
+ PJ_LOG(3,(THIS_FILE," %2d: %s [%s] (%d/%d)",
+ i, info.driver, info.name, info.input_count, info.output_count));
+ }
+}
+
+static const char *decode_caps(unsigned caps)
+{
+ static char text[200];
+
+ text[0] = '\0';
+
+ if (caps & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT) {
+ strcat(text, "extfmt ");
+ }
+
+ if (caps & PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY) {
+ strcat(text, "inlatency ");
+ }
+
+ if (caps & PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY) {
+ strcat(text, "outlatency ");
+ }
+
+ if (caps & PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING) {
+ strcat(text, "invol ");
+ }
+
+ if (caps & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
+ strcat(text, "outvol ");
+ }
+
+ if (caps & PJMEDIA_AUD_DEV_CAP_INPUT_SIGNAL_VOLUME) {
+ strcat(text, "insignal ");
+ }
+
+ if (caps & PJMEDIA_AUD_DEV_CAP_OUTPUT_SIGNAL_VOLUME) {
+ strcat(text, "outsignal ");
+ }
+
+ if (caps & PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE) {
+ strcat(text, "inroute ");
+ }
+
+ if (caps & PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE) {
+ strcat(text, "outroute ");
+ }
+
+ if (caps & PJMEDIA_AUD_DEV_CAP_EC) {
+ strcat(text, "ec ");
+ }
+
+ if (caps & PJMEDIA_AUD_DEV_CAP_EC_TAIL) {
+ strcat(text, "ectail ");
+ }
+
+ if (caps & PJMEDIA_AUD_DEV_CAP_VAD) {
+ strcat(text, "vad ");
+ }
+
+ if (caps & PJMEDIA_AUD_DEV_CAP_CNG) {
+ strcat(text, "cng ");
+ }
+
+ if (caps & PJMEDIA_AUD_DEV_CAP_PLC) {
+ strcat(text, "plc ");
+ }
+
+ return text;
+}
+
+static void show_dev_info(unsigned index)
+{
+#define H "%-20s"
+ pjmedia_aud_dev_info info;
+ char formats[200];
+ pj_status_t status;
+
+ if (index >= dev_count) {
+ PJ_LOG(1,(THIS_FILE, "Error: invalid index %u", index));
+ return;
+ }
+
+ status = pjmedia_aud_dev_get_info(dev_id[index], &info);
+ if (status != PJ_SUCCESS) {
+ app_perror("pjmedia_aud_dev_get_info() error", status);
+ return;
+ }
+
+ PJ_LOG(3, (THIS_FILE, "Device at index %u:", index));
+ PJ_LOG(3, (THIS_FILE, "-------------------------"));
+
+ PJ_LOG(3, (THIS_FILE, H": %u (0x%x)", "ID", dev_id[index], dev_id[index]));
+ PJ_LOG(3, (THIS_FILE, H": %s", "Name", info.name));
+ PJ_LOG(3, (THIS_FILE, H": %s", "Driver", info.driver));
+ PJ_LOG(3, (THIS_FILE, H": %u", "Input channels", info.input_count));
+ PJ_LOG(3, (THIS_FILE, H": %u", "Output channels", info.output_count));
+ PJ_LOG(3, (THIS_FILE, H": %s", "Capabilities", decode_caps(info.caps)));
+
+ formats[0] = '\0';
+ if (info.caps & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT) {
+ unsigned i;
+
+ for (i=0; i<info.ext_fmt_cnt; ++i) {
+ char bitrate[32];
+
+ switch (info.ext_fmt[i].fmt_id) {
+ case PJMEDIA_FORMAT_L16:
+ strcat(formats, "L16/");
+ break;
+ case PJMEDIA_FORMAT_PCMA:
+ strcat(formats, "PCMA/");
+ break;
+ case PJMEDIA_FORMAT_PCMU:
+ strcat(formats, "PCMU/");
+ break;
+ case PJMEDIA_FORMAT_AMR:
+ strcat(formats, "AMR/");
+ break;
+ case PJMEDIA_FORMAT_G729:
+ strcat(formats, "G729/");
+ break;
+ case PJMEDIA_FORMAT_ILBC:
+ strcat(formats, "ILBC/");
+ break;
+ default:
+ strcat(formats, "unknown/");
+ break;
+ }
+ sprintf(bitrate, "%u", info.ext_fmt[i].bitrate);
+ strcat(formats, bitrate);
+ strcat(formats, " ");
+ }
+ }
+ PJ_LOG(3, (THIS_FILE, H": %s", "Extended formats", formats));
+
+#undef H
+}
+
+static void test_device(pjmedia_dir dir, unsigned rec_id, unsigned play_id,
+ unsigned clock_rate, unsigned ptime,
+ unsigned chnum)
+{
+ pjmedia_aud_dev_param param;
+ pjmedia_aud_test_results result;
+ pj_status_t status;
+
+ if (dir & PJMEDIA_DIR_CAPTURE) {
+ status = pjmedia_aud_dev_default_param(dev_id[rec_id], ¶m);
+ } else {
+ status = pjmedia_aud_dev_default_param(dev_id[play_id], ¶m);
+ }
+
+ if (status != PJ_SUCCESS) {
+ app_perror("pjmedia_aud_dev_default_param()", status);
+ return;
+ }
+
+ param.dir = dir;
+ param.rec_id = dev_id[rec_id];
+ param.play_id = dev_id[play_id];
+ param.clock_rate = clock_rate;
+ param.channel_count = chnum;
+ param.samples_per_frame = clock_rate * chnum * ptime / 1000;
+
+ PJ_LOG(3,(THIS_FILE, "Performing test.."));
+
+ status = pjmedia_aud_test(¶m, &result);
+ if (status != PJ_SUCCESS) {
+ app_perror("Test has completed with error", status);
+ return;
+ }
+
+ PJ_LOG(3,(THIS_FILE, "Done. Result:"));
+
+ if (dir & PJMEDIA_DIR_CAPTURE) {
+ if (result.rec.frame_cnt==0) {
+ PJ_LOG(1,(THIS_FILE, "Error: no frames captured!"));
+ } else {
+ PJ_LOG(3,(THIS_FILE, " %-20s: max interval=%u, burst=%u",
+ "Recording result",
+ result.rec.max_interval,
+ result.rec.max_burst));
+ }
+ }
+
+ if (dir & PJMEDIA_DIR_PLAYBACK) {
+ if (result.play.frame_cnt==0) {
+ PJ_LOG(1,(THIS_FILE, "Error: no playback!"));
+ } else {
+ PJ_LOG(3,(THIS_FILE, " %-20s: max interval=%u, burst=%u",
+ "Playback result",
+ result.play.max_interval,
+ result.play.max_burst));
+ }
+ }
+
+ if (dir==PJMEDIA_DIR_CAPTURE_PLAYBACK) {
+ if (result.rec_drift_per_sec) {
+ PJ_LOG(3,(THIS_FILE, " No clock drift detected"));
+ } else {
+ const char *which = result.rec_drift_per_sec>=0 ? "faster" : "slower";
+ unsigned drift = result.rec_drift_per_sec>=0 ?
+ result.rec_drift_per_sec :
+ -result.rec_drift_per_sec;
+
+ PJ_LOG(3,(THIS_FILE, " Clock drifts detected. Capture device "
+ "is running %d samples per second %s "
+ "than the playback device",
+ drift, which));
+ }
+ }
+}
+
+
+static void print_menu(void)
+{
+ puts("");
+ puts("Audio demo menu:");
+ puts("-------------------------------");
+ puts(" l List devices");
+ puts(" i ID Show device info for device ID");
+ puts(" t RID PID CR PTIM [CH] Perform test on the device:");
+ puts(" RID: record device ID (-1 for no)");
+ puts(" PID: playback device ID (-1 for no)");
+ puts(" CR: clock rate");
+ puts(" PTIM: ptime in ms");
+ puts(" CH: # of channels");
+ puts(" v Toggle log verbosity");
+ puts(" q Quit");
+ puts("");
+ printf("Enter selection: ");
+ fflush(stdout);
+}
+
+int main()
+{
+ pj_caching_pool cp;
+ pj_bool_t done = PJ_FALSE;
+ pj_status_t status;
+
+ /* Init pjlib */
+ status = pj_init();
+ PJ_ASSERT_RETURN(status==PJ_SUCCESS, 1);
+
+ pj_log_set_decor(PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_COLOR);
+
+ /* Must create a pool factory before we can allocate any memory. */
+ pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
+
+ status = pjmedia_aud_subsys_init(&cp.factory);
+ if (status != PJ_SUCCESS) {
+ app_perror("pjmedia_aud_subsys_init()", status);
+ pj_caching_pool_destroy(&cp);
+ pj_shutdown();
+ return 1;
+ }
+
+ list_devices();
+
+ while (!done) {
+ char line[80];
+
+ print_menu();
+
+ if (fgets(line, sizeof(line), stdin)==NULL)
+ break;
+
+ switch (line[0]) {
+ case 'l':
+ list_devices();
+ break;
+
+ case 'i':
+ {
+ unsigned dev_index;
+ if (sscanf(line+2, "%u", &dev_index) != 1) {
+ puts("error: device ID required");
+ break;
+ }
+ show_dev_info(dev_index);
+ }
+ break;
+
+ case 't':
+ {
+ pjmedia_dir dir;
+ int rec_id, play_id;
+ unsigned clock_rate, ptime, chnum;
+ int cnt;
+
+ cnt = sscanf(line+2, "%d %d %u %u %u", &rec_id, &play_id,
+ &clock_rate, &ptime, &chnum);
+ if (cnt < 4) {
+ puts("error: not enough parameters");
+ break;
+ }
+ if (clock_rate < 8000 || clock_rate > 128000) {
+ puts("error: invalid clock rate");
+ break;
+ }
+ if (ptime < 10 || ptime > 500) {
+ puts("error: invalid ptime");
+ break;
+ }
+ if (cnt==5) {
+ if (chnum < 1 || chnum > 4) {
+ puts("error: invalid number of channels");
+ break;
+ }
+ } else {
+ chnum = 1;
+ }
+
+ if (rec_id >= 0 && rec_id < (int)dev_count) {
+ if (play_id >= 0 && play_id < (int)dev_count)
+ dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
+ else
+ dir = PJMEDIA_DIR_CAPTURE;
+ } else if (play_id >= 0 && play_id < (int)dev_count) {
+ dir = PJMEDIA_DIR_PLAYBACK;
+ } else {
+ puts("error: at least one valid device index required");
+ break;
+ }
+
+ test_device(dir, rec_id, play_id, clock_rate, ptime, chnum);
+
+ }
+ break;
+
+ case 'v':
+ if (pj_log_get_level() <= 3) {
+ pj_log_set_level(5);
+ puts("Logging set to detail");
+ } else {
+ pj_log_set_level(3);
+ puts("Logging set to quiet");
+ }
+ break;
+
+ case 'q':
+ done = PJ_TRUE;
+ break;
+ }
+ }
+
+ pj_caching_pool_destroy(&cp);
+ pj_shutdown();
+ return 0;
+}
+
+
diff --git a/pjsip-apps/src/samples/debug.c b/pjsip-apps/src/samples/debug.c
index c7453d9..1a145b9 100644
--- a/pjsip-apps/src/samples/debug.c
+++ b/pjsip-apps/src/samples/debug.c
@@ -28,5 +28,5 @@
* E.g.:
* #include "playfile.c"
*/
-#include "aectest.c"
+#include "auddemo.c"