Updated audio test

git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/aps-direct@2464 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjmedia/src/pjmedia-audiodev/audiodev.c b/pjmedia/src/pjmedia-audiodev/audiodev.c
index ab72ec0..e2f209d 100644
--- a/pjmedia/src/pjmedia-audiodev/audiodev.c
+++ b/pjmedia/src/pjmedia-audiodev/audiodev.c
@@ -44,6 +44,7 @@
 /* The audio subsystem */
 static struct aud_subsys
 {
+    unsigned	     init_count;
     pj_pool_factory *pf;
     unsigned	     factory_cnt;
 
@@ -64,6 +65,13 @@
     unsigned i;
     pj_status_t status = PJ_ENOMEM;
 
+    /* Allow init() to be called multiple times as long as there is matching
+     * number of shutdown().
+     */
+    if (aud_subsys.init_count++ != 0) {
+	return PJ_SUCCESS;
+    }
+
     aud_subsys.pf = pf;
     aud_subsys.factory_cnt = 0;
 
@@ -101,6 +109,14 @@
 {
     unsigned i;
 
+    /* Allow shutdown() to be called multiple times as long as there is matching
+     * number of init().
+     */
+    if (aud_subsys.init_count == 0) {
+	return PJ_SUCCESS;
+    }
+    --aud_subsys.init_count;
+
     for (i=0; i<aud_subsys.factory_cnt; ++i) {
 	pjmedia_aud_dev_factory *f = aud_subsys.factories[i].f;
 
diff --git a/pjmedia/src/pjmedia-audiodev/pa_dev.c b/pjmedia/src/pjmedia-audiodev/pa_dev.c
index 06d273c..0a0e7a2 100644
--- a/pjmedia/src/pjmedia-audiodev/pa_dev.c
+++ b/pjmedia/src/pjmedia-audiodev/pa_dev.c
@@ -685,6 +685,8 @@
     const PaStreamInfo *paSI;
     PaError err;
 
+    PJ_ASSERT_RETURN(rec_cb && p_snd_strm, PJ_EINVAL);
+
     rec_id = param->rec_id;
     if (rec_id < 0) {
 	rec_id = pa_get_default_input_dev(param->channel_count);
@@ -788,6 +790,8 @@
     unsigned paFrames, paRate, paLatency;
     PaError err;
 
+    PJ_ASSERT_RETURN(play_cb && p_snd_strm, PJ_EINVAL);
+
     play_id = param->play_id;
     if (play_id < 0) {
 	play_id = pa_get_default_output_dev(param->channel_count);
@@ -898,6 +902,8 @@
     unsigned paFrames, paRate, paInputLatency, paOutputLatency;
     PaError err;
 
+    PJ_ASSERT_RETURN(play_cb && rec_cb && p_snd_strm, PJ_EINVAL);
+
     rec_id = param->rec_id;
     if (rec_id < 0) {
 	rec_id = pa_get_default_input_dev(param->channel_count);
diff --git a/pjsip-apps/src/samples/auddemo.c b/pjsip-apps/src/samples/auddemo.c
index dbb2b8f..e4cbba5 100644
--- a/pjsip-apps/src/samples/auddemo.c
+++ b/pjsip-apps/src/samples/auddemo.c
@@ -25,6 +25,7 @@
 
 #define THIS_FILE	"auddemo.c"
 #define MAX_DEVICES	64
+#define WAV_FILE	"auddemo.wav"
 
 
 static unsigned dev_count;
@@ -166,7 +167,7 @@
 	for (i=0; i<info.ext_fmt_cnt; ++i) {
 	    char bitrate[32];
 
-	    switch (info.ext_fmt[i].fmt_id) {
+	    switch (info.ext_fmt[i].id) {
 	    case PJMEDIA_FORMAT_L16:
 		strcat(formats, "L16/");
 		break;
@@ -275,6 +276,140 @@
 }
 
 
+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_dev_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(dev_id[rec_index], &param);
+    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(&param, &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_dev_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(dev_id[play_index], &param);
+    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(&param, 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("");
@@ -288,6 +423,8 @@
     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("");
@@ -391,6 +528,40 @@
 	    }
 	    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);