diff options
Diffstat (limited to 'pjsip-apps/src/samples/auddemo.c')
-rw-r--r-- | pjsip-apps/src/samples/auddemo.c | 544 |
1 files changed, 544 insertions, 0 deletions
diff --git a/pjsip-apps/src/samples/auddemo.c b/pjsip-apps/src/samples/auddemo.c new file mode 100644 index 00000000..e805f176 --- /dev/null +++ b/pjsip-apps/src/samples/auddemo.c @@ -0,0 +1,544 @@ +/* $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 +#define WAV_FILE "auddemo.wav" + + +static unsigned dev_count; + +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)); + + for (i=0; i<dev_count; ++i) { + pjmedia_aud_dev_info info; + + status = pjmedia_aud_dev_get_info(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]; + unsigned i; + + text[0] = '\0'; + + for (i=0; i<31; ++i) { + if ((1 << i) & caps) { + const char *capname; + capname = pjmedia_aud_dev_cap_name((pjmedia_aud_dev_cap)(1 << i), + NULL); + strcat(text, capname); + strcat(text, " "); + } + } + + 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(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", index, 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].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_param param; + pjmedia_aud_test_results result; + pj_status_t status; + + if (dir & PJMEDIA_DIR_CAPTURE) { + status = pjmedia_aud_dev_default_param(rec_id, ¶m); + } else { + status = pjmedia_aud_dev_default_param(play_id, ¶m); + } + + if (status != PJ_SUCCESS) { + app_perror("pjmedia_aud_dev_default_param()", status); + return; + } + + param.dir = dir; + param.rec_id = rec_id; + param.play_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: interval (min/max/avg/dev)=%u/%u/%u/%u, burst=%u", + "Recording result", + result.rec.min_interval, + result.rec.max_interval, + result.rec.avg_interval, + result.rec.dev_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: interval (min/max/avg/dev)=%u/%u/%u/%u, burst=%u", + "Playback result", + result.play.min_interval, + result.play.max_interval, + result.play.avg_interval, + result.play.dev_interval, + result.play.max_burst)); + } + } + + if (dir==PJMEDIA_DIR_CAPTURE_PLAYBACK) { + if (result.rec_drift_per_sec == 0) { + 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 pj_status_t wav_rec_cb(void *user_data, pjmedia_frame *frame) +{ + return pjmedia_port_put_frame((pjmedia_port*)user_data, frame); +} + +static void record(unsigned rec_index, const char *filename) +{ + pj_pool_t *pool = NULL; + pjmedia_port *wav = NULL; + pjmedia_aud_param param; + pjmedia_aud_stream *strm = NULL; + char line[10]; + pj_status_t status; + + if (filename == NULL) + filename = WAV_FILE; + + pool = pj_pool_create(pjmedia_aud_subsys_get_pool_factory(), "wav", + 1000, 1000, NULL); + + status = pjmedia_wav_writer_port_create(pool, filename, 16000, + 1, 320, 16, 0, 0, &wav); + if (status != PJ_SUCCESS) { + app_perror("Error creating WAV file", status); + goto on_return; + } + + status = pjmedia_aud_dev_default_param(rec_index, ¶m); + if (status != PJ_SUCCESS) { + app_perror("pjmedia_aud_dev_default_param()", status); + goto on_return; + } + + param.dir = PJMEDIA_DIR_CAPTURE; + param.clock_rate = wav->info.clock_rate; + param.samples_per_frame = wav->info.samples_per_frame; + param.channel_count = wav->info.channel_count; + param.bits_per_sample = wav->info.bits_per_sample; + + status = pjmedia_aud_stream_create(¶m, &wav_rec_cb, NULL, wav, + &strm); + if (status != PJ_SUCCESS) { + app_perror("Error opening the sound device", status); + goto on_return; + } + + status = pjmedia_aud_stream_start(strm); + if (status != PJ_SUCCESS) { + app_perror("Error starting the sound device", status); + goto on_return; + } + + PJ_LOG(3,(THIS_FILE, "Recording started, press ENTER to stop")); + fgets(line, sizeof(line), stdin); + +on_return: + if (strm) { + pjmedia_aud_stream_stop(strm); + pjmedia_aud_stream_destroy(strm); + } + if (wav) + pjmedia_port_destroy(wav); + if (pool) + pj_pool_release(pool); +} + + +static pj_status_t wav_play_cb(void *user_data, pjmedia_frame *frame) +{ + return pjmedia_port_get_frame((pjmedia_port*)user_data, frame); +} + + +static void play_file(unsigned play_index, const char *filename) +{ + pj_pool_t *pool = NULL; + pjmedia_port *wav = NULL; + pjmedia_aud_param param; + pjmedia_aud_stream *strm = NULL; + char line[10]; + pj_status_t status; + + if (filename == NULL) + filename = WAV_FILE; + + pool = pj_pool_create(pjmedia_aud_subsys_get_pool_factory(), "wav", + 1000, 1000, NULL); + + status = pjmedia_wav_player_port_create(pool, filename, 20, 0, 0, &wav); + if (status != PJ_SUCCESS) { + app_perror("Error opening WAV file", status); + goto on_return; + } + + status = pjmedia_aud_dev_default_param(play_index, ¶m); + if (status != PJ_SUCCESS) { + app_perror("pjmedia_aud_dev_default_param()", status); + goto on_return; + } + + param.dir = PJMEDIA_DIR_PLAYBACK; + param.clock_rate = wav->info.clock_rate; + param.samples_per_frame = wav->info.samples_per_frame; + param.channel_count = wav->info.channel_count; + param.bits_per_sample = wav->info.bits_per_sample; + + status = pjmedia_aud_stream_create(¶m, NULL, &wav_play_cb, wav, + &strm); + if (status != PJ_SUCCESS) { + app_perror("Error opening the sound device", status); + goto on_return; + } + + status = pjmedia_aud_stream_start(strm); + if (status != PJ_SUCCESS) { + app_perror("Error starting the sound device", status); + goto on_return; + } + + PJ_LOG(3,(THIS_FILE, "Playback started, press ENTER to stop")); + fgets(line, sizeof(line), stdin); + +on_return: + if (strm) { + pjmedia_aud_stream_stop(strm); + pjmedia_aud_stream_destroy(strm); + } + if (wav) + pjmedia_port_destroy(wav); + if (pool) + pj_pool_release(pool); +} + + +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(" r RID [FILE] Record capture device RID to WAV file"); + puts(" p PID [FILE] Playback WAV file to device ID PID"); + 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 'r': + /* record */ + { + int index; + char filename[80]; + int count; + + count = sscanf(line+2, "%d %s", &index, filename); + if (count==1) + record(index, NULL); + else if (count==2) + record(index, filename); + else + puts("error: invalid command syntax"); + } + break; + + case 'p': + /* playback */ + { + int index; + char filename[80]; + int count; + + count = sscanf(line+2, "%d %s", &index, filename); + if (count==1) + play_file(index, NULL); + else if (count==2) + play_file(index, filename); + else + puts("error: invalid command syntax"); + } + 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; +} + + |