Index: pjproject/branches/projects/aps-direct/pjmedia/src/pjmedia-audiodev/audiodev.c
===================================================================
--- pjproject/branches/projects/aps-direct/pjmedia/src/pjmedia-audiodev/audiodev.c	(revision 2463)
+++ pjproject/branches/projects/aps-direct/pjmedia/src/pjmedia-audiodev/audiodev.c	(revision 2464)
@@ -45,4 +45,5 @@
 static struct aud_subsys
 {
+    unsigned	     init_count;
     pj_pool_factory *pf;
     unsigned	     factory_cnt;
@@ -65,4 +66,11 @@
     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,4 +109,12 @@
 {
     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) {
Index: pjproject/branches/projects/aps-direct/pjmedia/src/pjmedia-audiodev/pa_dev.c
===================================================================
--- pjproject/branches/projects/aps-direct/pjmedia/src/pjmedia-audiodev/pa_dev.c	(revision 2463)
+++ pjproject/branches/projects/aps-direct/pjmedia/src/pjmedia-audiodev/pa_dev.c	(revision 2464)
@@ -686,4 +686,6 @@
     PaError err;
 
+    PJ_ASSERT_RETURN(rec_cb && p_snd_strm, PJ_EINVAL);
+
     rec_id = param->rec_id;
     if (rec_id < 0) {
@@ -788,4 +790,6 @@
     unsigned paFrames, paRate, paLatency;
     PaError err;
+
+    PJ_ASSERT_RETURN(play_cb && p_snd_strm, PJ_EINVAL);
 
     play_id = param->play_id;
@@ -898,4 +902,6 @@
     unsigned paFrames, paRate, paInputLatency, paOutputLatency;
     PaError err;
+
+    PJ_ASSERT_RETURN(play_cb && rec_cb && p_snd_strm, PJ_EINVAL);
 
     rec_id = param->rec_id;
Index: pjproject/branches/projects/aps-direct/pjsip-apps/src/samples/auddemo.c
===================================================================
--- pjproject/branches/projects/aps-direct/pjsip-apps/src/samples/auddemo.c	(revision 2463)
+++ pjproject/branches/projects/aps-direct/pjsip-apps/src/samples/auddemo.c	(revision 2464)
@@ -26,4 +26,5 @@
 #define THIS_FILE	"auddemo.c"
 #define MAX_DEVICES	64
+#define WAV_FILE	"auddemo.wav"
 
 
@@ -167,5 +168,5 @@
 	    char bitrate[32];
 
-	    switch (info.ext_fmt[i].fmt_id) {
+	    switch (info.ext_fmt[i].id) {
 	    case PJMEDIA_FORMAT_L16:
 		strcat(formats, "L16/");
@@ -273,4 +274,138 @@
 	}
     }
+}
+
+
+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);
 }
 
@@ -289,4 +424,6 @@
     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");
@@ -392,4 +529,38 @@
 	    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) {
