Index: /pjproject/trunk/pjsip-apps/build/Samples-vc.mak
===================================================================
--- /pjproject/trunk/pjsip-apps/build/Samples-vc.mak	(revision 360)
+++ /pjproject/trunk/pjsip-apps/build/Samples-vc.mak	(revision 361)
@@ -40,9 +40,11 @@
 SAMPLES = $(BINDIR)\simpleua.exe $(BINDIR)\playfile.exe $(BINDIR)\playsine.exe \
 	  $(BINDIR)\confsample.exe $(BINDIR)\sndinfo.exe \
-	  $(BINDIR)\level.exe
+	  $(BINDIR)\level.exe $(BINDIR)\recfile.exe  \
+	  $(BINDIR)\resampleplay.exe
+
 
 all: $(OBJDIR) $(SAMPLES)
 
-$(SAMPLES): $(SRCDIR)\$(@B).c $(LIBS) Samples-vc.mak
+$(SAMPLES): $(SRCDIR)\$(@B).c $(LIBS) $(SRCDIR)\util.h Samples-vc.mak
 	cl -nologo -c $(SRCDIR)\$(@B).c /Fo$(OBJDIR)\$(@B).obj $(CFLAGS) 
 	cl /nologo $(OBJDIR)\$(@B).obj /Fe$@ /Fm$(OBJDIR)\$(@B).map $(LDFLAGS)
Index: /pjproject/trunk/pjsip-apps/build/Samples.mak
===================================================================
--- /pjproject/trunk/pjsip-apps/build/Samples.mak	(revision 360)
+++ /pjproject/trunk/pjsip-apps/build/Samples.mak	(revision 361)
@@ -39,5 +39,5 @@
 BINDIR := ../bin/samples
 
-SAMPLES := simpleua playfile playsine confsample sndinfo level
+SAMPLES := simpleua playfile playsine confsample sndinfo level recfile resampleplay
 
 EXES := $(foreach file, $(SAMPLES), $(BINDIR)/$(file)-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(HOST_EXE))
Index: /pjproject/trunk/pjsip-apps/build/sample_debug.dsp
===================================================================
--- /pjproject/trunk/pjsip-apps/build/sample_debug.dsp	(revision 360)
+++ /pjproject/trunk/pjsip-apps/build/sample_debug.dsp	(revision 361)
@@ -51,5 +51,5 @@
 LINK32=link.exe
 # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
-# ADD LINK32 ole32.lib user32.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"../bin/samples/debug_vc6.exe"
+# ADD LINK32 ole32.lib user32.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"../bin/samples/sampledebug_vc6.exe"
 
 !ELSEIF  "$(CFG)" == "sample_debug - Win32 Debug"
@@ -67,5 +67,5 @@
 # PROP Target_Dir ""
 # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
-# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../pjsip/include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjmedia/include" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../../pjsip/include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjmedia/include" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c
 # ADD BASE RSC /l 0x409 /d "_DEBUG"
 # ADD RSC /l 0x409 /d "_DEBUG"
@@ -75,5 +75,5 @@
 LINK32=link.exe
 # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
-# ADD LINK32 ole32.lib user32.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../bin/samples/debug_vc6d.exe" /pdbtype:sept
+# ADD LINK32 ole32.lib user32.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../bin/samples/sampledebug_vc6d.exe" /pdbtype:sept
 
 !ENDIF 
Index: /pjproject/trunk/pjsip-apps/build/samples.dsp
===================================================================
--- /pjproject/trunk/pjsip-apps/build/samples.dsp	(revision 360)
+++ /pjproject/trunk/pjsip-apps/build/samples.dsp	(revision 361)
@@ -103,4 +103,12 @@
 # Begin Source File
 
+SOURCE=..\src\samples\recfile.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\samples\resampleplay.c
+# End Source File
+# Begin Source File
+
 SOURCE=..\src\samples\simpleua.c
 # End Source File
Index: /pjproject/trunk/pjsip-apps/src/pjsip-perf/main.c
===================================================================
--- /pjproject/trunk/pjsip-apps/src/pjsip-perf/main.c	(revision 360)
+++ /pjproject/trunk/pjsip-apps/src/pjsip-perf/main.c	(revision 361)
@@ -18,5 +18,4 @@
  */
 #include "pjsip_perf.h"
-#include <pjsua-lib/getopt.h>
 #include <stdlib.h>		/* atoi */
 
Index: /pjproject/trunk/pjsip-apps/src/samples/confsample.c
===================================================================
--- /pjproject/trunk/pjsip-apps/src/samples/confsample.c	(revision 360)
+++ /pjproject/trunk/pjsip-apps/src/samples/confsample.c	(revision 361)
@@ -19,4 +19,19 @@
 
 #include <pjmedia.h>
+#include <pjlib-util.h>	/* pj_getopt */
+#include <pjlib.h>
+
+#include <stdlib.h>	/* atoi() */
+#include <stdio.h>
+
+#include "util.h"
+
+/* For logging purpose. */
+#define THIS_FILE   "confsample.c"
+
+
+/* Shall we put recorder in the conference */
+#define RECORDER    1
+
 
 static const char *desc = 
@@ -31,7 +46,9 @@
  " USAGE:								    \n"
  "									    \n"
- "  confsample [file1.wav] [file2.wav] ...				    \n"
- "									    \n"
- " where:								    \n"
+ "  confsample [options] [file1.wav] [file2.wav] ...			    \n"
+ "									    \n"
+ " options:								    \n"
+ SND_USAGE
+ "									    \n"
  "  fileN.wav are optional WAV files to be connected to the conference      \n"
  "  bridge. The WAV files MUST have single channel (mono) and 16 bit PCM    \n"
@@ -46,21 +63,6 @@
  "  to slot starting from number one in the bridge. The WAV files can have  \n"
  "  arbitrary sampling rate; the bridge will convert it to its clock rate.  \n"
- "  However, the files MUST have a single audio channel only (i.e. mono).   \n";
-
-#include <pjmedia.h>
-#include <pjlib.h>
-
-#include <stdlib.h>	/* atoi() */
-#include <stdio.h>
-
-
-/* For logging purpose. */
-#define THIS_FILE   "confsample.c"
-
-/* Constants */
-#define CLOCK_RATE	44100
-#define NSAMPLES	(CLOCK_RATE * 20 / 1000)
-#define NCHANNELS	1
-#define NBITS		16
+ "  However, the files MUST have a single audio channel only (i.e. mono).  \n";
+
 
  
@@ -74,17 +76,4 @@
 /* Display VU meter */
 static void monitor_level(pjmedia_conf *conf, int slot, int dir, int dur);
-
-
-/* Util to display the error message for the specified error code  */
-static int app_perror( const char *sender, const char *title, 
-		       pj_status_t status)
-{
-    char errmsg[PJ_ERR_MSG_SIZE];
-
-    pj_strerror(status, errmsg, sizeof(errmsg));
-
-    printf("%s: %s [code=%d]\n", title, errmsg, status);
-    return 1;
-}
 
 
@@ -124,4 +113,10 @@
 int main(int argc, char *argv[])
 {
+    int dev_id = -1;
+    int clock_rate = CLOCK_RATE;
+    int channel_count = NCHANNELS;
+    int samples_per_frame = NSAMPLES;
+    int bits_per_sample = NBITS;
+
     pj_caching_pool cp;
     pjmedia_endpt *med_endpt;
@@ -131,14 +126,8 @@
     int i, port_count, file_count;
     pjmedia_port **file_port;	/* Array of file ports */
+    pjmedia_port *rec_port = NULL;  /* Wav writer port */
 
     char tmp[10];
     pj_status_t status;
-
-
-    /* Just in case user needs help */
-    if (argc > 1 && (*argv[1]=='-' || *argv[1]=='/' || *argv[1]=='?')) {
-	usage();
-	return 1;
-    }
 
 
@@ -146,4 +135,12 @@
     status = pj_init();
     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+
+    /* Get command line options. */
+    if (get_snd_options(THIS_FILE, argc, argv, &dev_id, &clock_rate,
+			&channel_count, &samples_per_frame, &bits_per_sample))
+    {
+	usage();
+	return 1;
+    }
 
     /* Must create a pool factory before we can allocate any memory. */
@@ -166,6 +163,6 @@
 
 
-    file_count = argc-1;
-    port_count = file_count + 1;
+    file_count = argc - pj_optind;
+    port_count = file_count + 1 + RECORDER;
 
     /* Create the conference bridge. 
@@ -175,8 +172,8 @@
     status = pjmedia_conf_create( pool,	    /* pool to use	    */
 				  port_count,/* number of ports	    */
-				  CLOCK_RATE,/* sampling rate	    */
-				  NCHANNELS,/* # of channels.	    */
-				  NSAMPLES, /* samples per frame    */
-				  NBITS,    /* bits per sample	    */
+				  clock_rate,
+				  channel_count,
+				  samples_per_frame,
+				  bits_per_sample,
 				  0,	    /* options		    */
 				  &conf	    /* result		    */
@@ -187,4 +184,18 @@
     }
 
+#if RECORDER
+    status = pjmedia_file_writer_port_create( pool, "confrecord.wav",
+					      clock_rate, channel_count,
+					      samples_per_frame, 
+					      bits_per_sample, 0, 0, NULL,
+					      &rec_port);
+    if (status != PJ_SUCCESS) {
+	app_perror(THIS_FILE, "Unable to create WAV writer", status);
+	return 1;
+    }
+
+    pjmedia_conf_add_port(conf, pool, rec_port, NULL, NULL);
+#endif
+
 
     /* Create file ports. */
@@ -194,14 +205,15 @@
 
 	/* Load the WAV file to file port. */
-	status = pjmedia_file_player_port_create( pool,	    /* pool.	    */
-						  argv[i+1],/* filename	    */
-						  0,	    /* flags	    */
-						  0,	    /* buf size	    */
-						  NULL,	    /* user data    */
-						  &file_port[i]	/* result   */
-						  );
+	status = pjmedia_file_player_port_create( 
+			pool,		    /* pool.	    */
+			argv[i+pj_optind],  /* filename	    */
+			0,		    /* flags	    */
+			0,		    /* buf size	    */
+			NULL,		    /* user data    */
+			&file_port[i]	    /* result	    */
+			);
 	if (status != PJ_SUCCESS) {
 	    char title[80];
-	    pj_ansi_sprintf(title, "Unable to use %s", argv[i+1]);
+	    pj_ansi_sprintf(title, "Unable to use %s", argv[i+pj_optind]);
 	    app_perror(THIS_FILE, title, status);
 	    usage();
@@ -214,5 +226,5 @@
 					file_port[i],	/* port to connect  */
 					NULL,		/* Use port's name  */
-					NULL		/* ptr to receive slot # */
+					NULL		/* ptr for slot #   */
 					);
 	if (status != PJ_SUCCESS) {
@@ -229,4 +241,7 @@
      */
 
+
+    /* Dump memory usage */
+    dump_pool_usage(THIS_FILE, &cp);
 
     /* Sleep to allow log messages to flush */
@@ -421,4 +436,8 @@
     }
 
+    /* Destroy recorder port */
+    if (rec_port)
+	pjmedia_port_destroy(rec_port);
+
     /* Release application pool */
     pj_pool_release( pool );
@@ -484,4 +503,5 @@
 		   "  Name                    : %.*s\n"
 		   "  Sampling rate           : %d Hz\n"
+		   "  Samples per frame       : %d\n"
 		   "  Frame time              : %d ms\n"
 		   "  Signal level adjustment : tx=%d, rx=%d\n"
@@ -492,4 +512,5 @@
 		   port_info->name.ptr,
 		   port_info->clock_rate,
+		   port_info->samples_per_frame,
 		   port_info->samples_per_frame*1000/port_info->clock_rate,
 		   port_info->tx_adj_level,
Index: /pjproject/trunk/pjsip-apps/src/samples/debug.c
===================================================================
--- /pjproject/trunk/pjsip-apps/src/samples/debug.c	(revision 360)
+++ /pjproject/trunk/pjsip-apps/src/samples/debug.c	(revision 361)
@@ -28,4 +28,4 @@
  *  #include "playfile.c"
  */
-#include "confsample.c"
+#include "resampleplay.c"
 
Index: /pjproject/trunk/pjsip-apps/src/samples/playfile.c
===================================================================
--- /pjproject/trunk/pjsip-apps/src/samples/playfile.c	(revision 360)
+++ /pjproject/trunk/pjsip-apps/src/samples/playfile.c	(revision 361)
@@ -19,4 +19,11 @@
 
 #include <pjmedia.h>
+#include <pjlib-util.h>
+#include <pjlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "util.h"
+
 
 /*
@@ -34,8 +41,4 @@
  */
 
-#include <pjmedia.h>
-#include <pjlib.h>
-
-#include <stdio.h>
 
 /* For logging purpose. */
@@ -43,15 +46,19 @@
 
 
-/* Util to display the error message for the specified error code  */
-static int app_perror( const char *sender, const char *title, 
-		       pj_status_t status)
-{
-    char errmsg[PJ_ERR_MSG_SIZE];
-
-    pj_strerror(status, errmsg, sizeof(errmsg));
-
-    printf("%s: %s [code=%d]\n", title, errmsg, status);
-    return 1;
-}
+static const char *desc = 
+" FILE		    						    \n"
+"		    						    \n"
+"  playfile.c	    						    \n"
+"		    						    \n"
+" PURPOSE	    						    \n"
+"		    						    \n"
+"  Demonstrate how to play a WAV file.				    \n"
+"		    						    \n"
+" USAGE		    						    \n"
+"		    						    \n"
+"  playfile FILE.WAV						    \n"
+"		    						    \n"
+"  The WAV file could have mono or stereo channels with arbitrary   \n"
+"  sampling rate, but MUST contain uncompressed (i.e. 16bit) PCM.   \n";
 
 
@@ -69,15 +76,4 @@
     pj_status_t status;
 
-
-    /* Verify cmd line arguments. */
-    if (argc != 2) {
-	puts("");
-	puts("Usage: ");
-	puts("  playfile <wav-file>");
-	puts("");
-	puts("The WAV file could have mono or stereo channels with arbitrary");
-	puts("sampling rate, but MUST contain uncompressed (i.e. 16bit) PCM.");
-	return 0;
-    }
 
     /* Must init PJLIB first: */
Index: /pjproject/trunk/pjsip-apps/src/samples/recfile.c
===================================================================
--- /pjproject/trunk/pjsip-apps/src/samples/recfile.c	(revision 361)
+++ /pjproject/trunk/pjsip-apps/src/samples/recfile.c	(revision 361)
@@ -0,0 +1,183 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2006 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.h>
+#include <pjlib.h>
+
+#include <stdio.h>
+
+/* For logging purpose. */
+#define THIS_FILE   "recfile.c"
+
+/* Configs */
+#define CLOCK_RATE	    44100
+#define SAMPLES_PER_FRAME   (CLOCK_RATE * 20 / 1000)
+#define NCHANNELS	    2
+#define BITS_PER_SAMPLE	    16
+
+
+static const char *desc = 
+ " FILE					    \n"
+ "  recfile.c				    \n"
+ "					    \n"
+ " PURPOSE:				    \n"
+ "  Record microphone to WAVE file.	    \n"
+ "					    \n"
+ " USAGE:				    \n"
+ "  recfile FILE.WAV			    \n"
+ "";
+
+
+/* Util to display the error message for the specified error code  */
+static int app_perror( const char *sender, const char *title, 
+		       pj_status_t status)
+{
+    char errmsg[PJ_ERR_MSG_SIZE];
+
+    PJ_UNUSED_ARG(sender);
+
+    pj_strerror(status, errmsg, sizeof(errmsg));
+
+    printf("%s: %s [code=%d]\n", title, errmsg, status);
+    return 1;
+}
+
+
+/*
+ * main()
+ */
+int main(int argc, char *argv[])
+{
+    pj_caching_pool cp;
+    pjmedia_endpt *med_endpt;
+    pj_pool_t *pool;
+    pjmedia_port *file_port;
+    pjmedia_snd_port *snd_port;
+    char tmp[10];
+    pj_status_t status;
+
+
+    /* Verify cmd line arguments. */
+    if (argc != 2) {
+	puts("");
+	puts(desc);
+	return 0;
+    }
+
+    /* Must init PJLIB first: */
+    status = pj_init();
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+
+    /* Must create a pool factory before we can allocate any memory. */
+    pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
+
+    /* 
+     * Initialize media endpoint.
+     * This will implicitly initialize PJMEDIA too.
+     */
+    status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+
+    /* Create memory pool for our file player */
+    pool = pj_pool_create( &cp.factory,	    /* pool factory	    */
+			   "app",	    /* pool name.	    */
+			   4000,	    /* init size	    */
+			   4000,	    /* increment size	    */
+			   NULL		    /* callback on error    */
+			   );
+
+    /* Create WAVE file writer port. */
+    status = pjmedia_file_writer_port_create( pool, argv[1],
+					      CLOCK_RATE,
+					      NCHANNELS,
+					      SAMPLES_PER_FRAME,
+					      BITS_PER_SAMPLE,
+					      0, 0, NULL,
+					      &file_port);
+    if (status != PJ_SUCCESS) {
+	app_perror(THIS_FILE, "Unable to open WAV file for writing", status);
+	return 1;
+    }
+
+    /* Create sound player port. */
+    status = pjmedia_snd_port_create_rec( 
+		 pool,				    /* pool		    */
+		 -1,				    /* use default dev.	    */
+		 file_port->info.sample_rate,	    /* clock rate.	    */
+		 file_port->info.channel_count,	    /* # of channels.	    */
+		 file_port->info.samples_per_frame, /* samples per frame.   */
+		 file_port->info.bits_per_sample,   /* bits per sample.	    */
+		 0,				    /* options		    */
+		 &snd_port			    /* returned port	    */
+		 );
+    if (status != PJ_SUCCESS) {
+	app_perror(THIS_FILE, "Unable to open sound device", status);
+	return 1;
+    }
+
+    /* Connect file port to the sound player.
+     * Stream playing will commence immediately.
+     */
+    status = pjmedia_snd_port_connect( snd_port, file_port);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+
+
+
+    /* 
+     * Recording should be started now. 
+     */
+
+
+    /* Sleep to allow log messages to flush */
+    pj_thread_sleep(10);
+
+
+    printf("Recodring %s..\n", argv[1]);
+    puts("");
+    puts("Press <ENTER> to stop recording and quit");
+
+    fgets(tmp, sizeof(tmp), stdin);
+
+    
+    /* Start deinitialization: */
+
+    /* Destroy sound device */
+    status = pjmedia_snd_port_destroy( snd_port );
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+
+
+    /* Destroy file port */
+    status = pjmedia_port_destroy( file_port );
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+
+
+    /* Release application pool */
+    pj_pool_release( pool );
+
+    /* Destroy media endpoint. */
+    pjmedia_endpt_destroy( med_endpt );
+
+    /* Destroy pool factory */
+    pj_caching_pool_destroy( &cp );
+
+
+    /* Done. */
+    return 0;
+}
Index: /pjproject/trunk/pjsip-apps/src/samples/resampleplay.c
===================================================================
--- /pjproject/trunk/pjsip-apps/src/samples/resampleplay.c	(revision 361)
+++ /pjproject/trunk/pjsip-apps/src/samples/resampleplay.c	(revision 361)
@@ -0,0 +1,227 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2006 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.h>
+#include <pjlib-util.h>
+#include <pjlib.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "util.h"
+
+/* For logging purpose. */
+#define THIS_FILE   "resampleplay.c"
+
+
+static const char *desc = 
+" FILE		    						    \n"
+"		    						    \n"
+"  resampleplay.c	    					    \n"
+"		    						    \n"
+" PURPOSE	    						    \n"
+"		    						    \n"
+"  Demonstrate how use resample port to play a WAV file to sound    \n"
+"  device using different sampling rate.			    \n"
+"		    						    \n"
+" USAGE		    						    \n"
+"		    						    \n"
+"  resampleplay [options] FILE.WAV				    \n"
+"		    						    \n"
+" where options:						    \n"
+SND_USAGE
+"		    						    \n"
+"  The WAV file could have mono or stereo channels with arbitrary   \n"
+"  sampling rate, but MUST contain uncompressed (i.e. 16bit) PCM.   \n";
+
+
+int main(int argc, char *argv[])
+{
+    pj_caching_pool cp;
+    pjmedia_endpt *med_endpt;
+    pj_pool_t *pool;
+    pjmedia_port *file_port;
+    pjmedia_port *resample_port;
+    pjmedia_snd_port *snd_port;
+    char tmp[10];
+    pj_status_t status;
+
+    int dev_id = -1;
+    int sampling_rate = CLOCK_RATE;
+    int channel_count = NCHANNELS;
+    int samples_per_frame = NSAMPLES;
+    int bits_per_sample = NBITS;
+    //int ptime;
+    //int down_samples;
+
+    /* Must init PJLIB first: */
+    status = pj_init();
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+
+
+    /* Get options */
+    if (get_snd_options(THIS_FILE, argc, argv, &dev_id, &sampling_rate,
+			&channel_count, &samples_per_frame, &bits_per_sample))
+    {
+	puts("");
+	puts(desc);
+	return 1;
+    }
+
+    if (!argv[pj_optind]) {
+	puts("Error: no file is specified");
+	puts(desc);
+	return 1;
+    }
+
+    /* Must create a pool factory before we can allocate any memory. */
+    pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
+
+    /* 
+     * Initialize media endpoint.
+     * This will implicitly initialize PJMEDIA too.
+     */
+    status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+
+    /* Create memory pool for our file player */
+    pool = pj_pool_create( &cp.factory,	    /* pool factory	    */
+			   "app",	    /* pool name.	    */
+			   4000,	    /* init size	    */
+			   4000,	    /* increment size	    */
+			   NULL		    /* callback on error    */
+			   );
+
+    /* Create the file port. */
+    status = pjmedia_file_player_port_create( pool, argv[pj_optind], 0,
+					      0, 0, &file_port);
+    if (status != PJ_SUCCESS) {
+	app_perror(THIS_FILE, "Unable to open file", status);
+	return 1;
+    }
+
+    /* File must have same number of channels. */
+    if (file_port->info.channel_count != (unsigned)channel_count) {
+	PJ_LOG(3,(THIS_FILE, "Error: file has different number of channels. "
+			     "Perhaps you'd need -c option?"));
+	pjmedia_port_destroy(file_port);
+	return 1;
+    }
+
+    /* Calculate number of samples per frame to be taken from file port */
+    //ptime = samples_per_frame * 1000 / sampling_rate;
+
+    /* Create the resample port. */
+    status = pjmedia_resample_port_create( pool, 1, 1,
+					   file_port->info.sample_rate,
+					   sampling_rate,
+					   channel_count,
+					   (unsigned)(
+					   samples_per_frame * 1.0 *
+					    file_port->info.sample_rate / 
+					    sampling_rate),
+					   &resample_port);
+    if (status != PJ_SUCCESS) {
+	app_perror(THIS_FILE, "Unable to create resample port", status);
+	return 1;
+    }
+
+    /* Connect resample port to file port */
+    status = pjmedia_port_connect( pool, resample_port, file_port);
+    if (status != PJ_SUCCESS) {
+	app_perror(THIS_FILE, "Error connecting ports", status);
+	return 1;
+    }
+
+    /* Create sound player port. */
+    status = pjmedia_snd_port_create( 
+		 pool,			/* pool			    */
+		 dev_id,		/* device		    */
+		 dev_id,		/* device		    */
+		 sampling_rate,		/* clock rate.		    */
+		 channel_count,		/* # of channels.	    */
+		 samples_per_frame,	/* samples per frame.	    */
+		 bits_per_sample,	/* bits per sample.	    */
+		 0,			/* options		    */
+		 &snd_port		/* returned port	    */
+		 );
+    if (status != PJ_SUCCESS) {
+	app_perror(THIS_FILE, "Unable to open sound device", status);
+	return 1;
+    }
+
+    /* Connect resample port to sound device */
+    status = pjmedia_snd_port_connect( snd_port, resample_port);
+    if (status != PJ_SUCCESS) {
+	app_perror(THIS_FILE, "Error connecting sound ports", status);
+	return 1;
+    }
+
+
+    /* Dump memory usage */
+    dump_pool_usage(THIS_FILE, &cp);
+
+    /* 
+     * File should be playing and looping now, using sound device's thread. 
+     */
+
+
+    /* Sleep to allow log messages to flush */
+    pj_thread_sleep(100);
+
+
+    printf("Playing %s at sampling rate %d (original file sampling rate=%d)\n",
+	   argv[pj_optind], sampling_rate, file_port->info.sample_rate);
+    puts("");
+    puts("Press <ENTER> to stop playing and quit");
+
+    fgets(tmp, sizeof(tmp), stdin);
+
+    
+    /* Start deinitialization: */
+
+
+    /* Destroy sound device */
+    status = pjmedia_snd_port_destroy( snd_port );
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+
+
+    /* Destroy resample port. 
+     * This will destroy all downstream ports (e.g. the file port)
+     */
+    status = pjmedia_port_destroy( resample_port );
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+
+
+    /* Release application pool */
+    pj_pool_release( pool );
+
+    /* Destroy media endpoint. */
+    pjmedia_endpt_destroy( med_endpt );
+
+    /* Destroy pool factory */
+    pj_caching_pool_destroy( &cp );
+
+
+    /* Done. */
+    return 0;
+
+}
+
+
+
Index: /pjproject/trunk/pjsip-apps/src/samples/simpleua.c
===================================================================
--- /pjproject/trunk/pjsip-apps/src/samples/simpleua.c	(revision 360)
+++ /pjproject/trunk/pjsip-apps/src/samples/simpleua.c	(revision 361)
@@ -62,4 +62,5 @@
 #define THIS_FILE   "simpleua.c"
 
+#include "util.h"
 
 /*
@@ -125,18 +126,4 @@
 
 
-/* 
- * Util to display the error message for the specified error code  
- */
-static int app_perror( const char *sender, const char *title, 
-		       pj_status_t status)
-{
-    char errmsg[PJ_ERR_MSG_SIZE];
-
-    pj_strerror(status, errmsg, sizeof(errmsg));
-
-    PJ_LOG(1,(sender, "%s: %s [code=%d]", title, errmsg, status));
-    return 1;
-}
-
 
 /*
@@ -416,22 +403,6 @@
     }
 
-    /* On exit, dump memory usage: */
-    {
-	pj_pool_t   *p;
-	unsigned     total_alloc = 0;
-	unsigned     total_used = 0;
-
-	/* Accumulate memory usage in active list. */
-	p = cp.used_list.next;
-	while (p != (pj_pool_t*) &cp.used_list) {
-	    total_alloc += pj_pool_get_capacity(p);
-	    total_used += pj_pool_get_used_size(p);
-	    p = p->next;
-	}
-
-	printf("Total pool memory allocated=%d KB, used=%d KB\n",
-		total_alloc / 1000,
-		total_used / 1000);
-    }
+    /* On exit, dump current memory usage: */
+    dump_pool_usage(THIS_FILE, &cp);
 
     return 0;
Index: /pjproject/trunk/pjsip-apps/src/samples/util.h
===================================================================
--- /pjproject/trunk/pjsip-apps/src/samples/util.h	(revision 360)
+++ /pjproject/trunk/pjsip-apps/src/samples/util.h	(revision 361)
@@ -1,37 +1,5 @@
-
-/* Include all PJSIP core headers. */
-#include <pjsip.h>
-
-/* Include all PJMEDIA headers. */
-#include <pjmedia.h>
-
-/* Include all PJMEDIA-CODEC headers. */
-#include <pjmedia-codec.h>
-
-/* Include all PJSIP-UA headers */
-#include <pjsip_ua.h>
-
-/* Include all PJSIP-SIMPLE headers */
-#include <pjsip_simple.h>
-
-/* Include all PJLIB-UTIL headers. */
-#include <pjlib-util.h>
-
-/* Include all PJLIB headers. */
-#include <pjlib.h>
 
 
-/* Global endpoint instance. */
-static pjsip_endpoint *g_endpt;
-
-/* Global caching pool factory. */
-static pj_caching_pool cp;
-
-/* Global media endpoint. */
-static pjmedia_endpt *g_med_endpt;
-
-/* 
- * Show error.
- */
+/* Util to display the error message for the specified error code  */
 static int app_perror( const char *sender, const char *title, 
 		       pj_status_t status)
@@ -41,83 +9,144 @@
     pj_strerror(status, errmsg, sizeof(errmsg));
 
-    PJ_LOG(1,(sender, "%s: %s [code=%d]", title, errmsg, status));
+    PJ_LOG(3,(sender, "%s: %s [code=%d]", title, errmsg, status));
     return 1;
 }
 
+
+
+/* Constants */
+#define CLOCK_RATE	44100
+#define NSAMPLES	(CLOCK_RATE * 20 / 1000)
+#define NCHANNELS	1
+#define NBITS		16
+
 /*
- * Perform the very basic initialization:
- *  - init PJLIB.
- *  - init memory pool
- *  - create SIP endpoint instance.
+ * Common sound options.
  */
-static pj_status_t util_init(void)
+#define SND_USAGE   \
+"  -d, --dev=NUM        Sound device use device id NUM (default=-1)	 \n"\
+"  -r, --rate=HZ        Set clock rate in samples per sec (default=44100)\n"\
+"  -c, --channel=NUM    Set # of channels (default=1 for mono).		 \n"\
+"  -f, --frame=NUM      Set # of samples per frame (default equival 20ms)\n"\
+"  -b, --bit=NUM        Set # of bits per sample (default=16)		 \n"
+
+
+/*
+ * This utility function parses the command line and look for
+ * common sound options.
+ */
+static pj_status_t get_snd_options( const char *app_name,
+				    int argc, 
+				    char *argv[],
+				    int *dev_id,
+				    int *clock_rate,
+				    int *channel_count,
+				    int *samples_per_frame,
+				    int *bits_per_sample)
 {
-    pj_status_t status;
+    struct pj_getopt_option long_options[] = {
+	{ "dev",	1, 0, 'd' },
+	{ "rate",	1, 0, 'r' },
+	{ "channel",	1, 0, 'c' },
+	{ "frame",	1, 0, 'f' },
+	{ "bit",	1, 0, 'b' },
+	{ NULL, 0, 0, 0 },
+    };
+    int c;
+    int option_index;
+    long val;
+    char *err;
 
-    /* Init PJLIB */
-    status = pj_init();
-    if (status != PJ_SUCCESS) {
-	app_perror(THIS_FILE, "pj_init() error", status);
-	return status;
+    *samples_per_frame = 0;
+
+    pj_optind = 0;
+    while((c=pj_getopt_long(argc,argv, "d:r:c:f:b:", 
+			    long_options, &option_index))!=-1) 
+    {
+
+	switch (c) {
+	case 'd':
+	    /* device */
+	    val = strtol(pj_optarg, &err, 10);
+	    if (*err) {
+		PJ_LOG(3,(app_name, "Error: invalid value for device id"));
+		return PJ_EINVAL;
+	    }
+	    *dev_id = val;
+	    break;
+
+	case 'r':
+	    /* rate */
+	    val = strtol(pj_optarg, &err, 10);
+	    if (*err) {
+		PJ_LOG(3,(app_name, "Error: invalid value for clock rate"));
+		return PJ_EINVAL;
+	    }
+	    *clock_rate = val;
+	    break;
+
+	case 'c':
+	    /* channel count */
+	    val = strtol(pj_optarg, &err, 10);
+	    if (*err) {
+		PJ_LOG(3,(app_name, "Error: invalid channel count"));
+		return PJ_EINVAL;
+	    }
+	    *channel_count = val;
+	    break;
+
+	case 'f':
+	    /* frame count/samples per frame */
+	    val = strtol(pj_optarg, &err, 10);
+	    if (*err) {
+		PJ_LOG(3,(app_name, "Error: invalid samples per frame"));
+		return PJ_EINVAL;
+	    }
+	    *samples_per_frame = val;
+	    break;
+
+	case 'b':
+	    /* bit per sample */
+	    val = strtol(pj_optarg, &err, 10);
+	    if (*err) {
+		PJ_LOG(3,(app_name, "Error: invalid samples bits per sample"));
+		return PJ_EINVAL;
+	    }
+	    *bits_per_sample = val;
+	    break;
+
+	default:
+	    /* Unknown options */
+	    PJ_LOG(3,(app_name, "Error: unknown options '%c'", pj_optopt));
+	    return PJ_EINVAL;
+	}
+
     }
 
-    /* Init PJLIB-UTIL: */
-    status = pjlib_util_init();
-    if (status != PJ_SUCCESS) {
-	app_perror(THIS_FILE, "pjlib_util_init() error", status);
-	return status;
+    if (*samples_per_frame == 0) {
+	*samples_per_frame = *clock_rate * *channel_count * 20 / 1000;
     }
 
-    /* Init memory pool: */
+    return 0;
+}
 
-    /* Init caching pool. */
-    pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
 
-    /* Create global endpoint: */
+/* Dump memory pool usage. */
+static void dump_pool_usage( const char *app_name, pj_caching_pool *cp )
+{
+    pj_pool_t   *p;
+    unsigned     total_alloc = 0;
+    unsigned     total_used = 0;
 
-    {
-	const pj_str_t *hostname;
-	const char *endpt_name;
-
-	/* Endpoint MUST be assigned a globally unique name.
-	 * The name will be used as the hostname in Warning header.
-	 */
-
-	/* For this implementation, we'll use hostname for simplicity */
-	hostname = pj_gethostname();
-	endpt_name = hostname->ptr;
-
-	/* Create the endpoint: */
-
-	status = pjsip_endpt_create(&cp.factory, endpt_name, 
-				    &g_endpt);
-	if (status != PJ_SUCCESS) {
-	    app_perror(THIS_FILE, "Unable to create SIP endpoint", status);
-	    return status;
-	}
+    /* Accumulate memory usage in active list. */
+    p = cp->used_list.next;
+    while (p != (pj_pool_t*) &cp->used_list) {
+	total_alloc += pj_pool_get_capacity(p);
+	total_used += pj_pool_get_used_size(p);
+	p = p->next;
     }
 
-    return PJ_SUCCESS;
+    PJ_LOG(3, (app_name, "Total pool memory allocated=%d KB, used=%d KB",
+	       total_alloc / 1000,
+	       total_used / 1000));
 }
-
-/*
- * Add UDP transport to endpoint.
- */
-static pj_status_t util_add_udp_transport(int port)
-{
-    pj_sockaddr_in addr;
-    pj_status_t status;
-    
-    addr.sin_family = PJ_AF_INET;
-    addr.sin_addr.s_addr = 0;
-    addr.sin_port = port;
-
-    status = pjsip_udp_transport_start( g_endpt, &addr, NULL, 1, NULL);
-    if (status != PJ_SUCCESS) {
-	app_perror(THIS_FILE, "Unable to start UDP transport", status);
-	return status;
-    }
-
-    return status;
-}
-
