Index: pjproject/trunk/pjmedia/build/Makefile
===================================================================
--- pjproject/trunk/pjmedia/build/Makefile	(revision 410)
+++ pjproject/trunk/pjmedia/build/Makefile	(revision 411)
@@ -65,9 +65,10 @@
 export PJMEDIA_SRCDIR = ../src/pjmedia
 export PJMEDIA_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
-			codec.o conference.o endpoint.o errno.o file_player.o \
-			file_writer.o \
-			g711.o jbuf.o null_port.o port.o resample.o resample_port.o \
-			rtcp.o rtp.o sdp.o sdp_cmp.o sdp_neg.o session.o silencedet.o \
-			sound_port.o stream.o wave.o $(SOUND_OBJS) $(NULLSOUND_OBJS)
+			clock_thread.o codec.o conference.o endpoint.o errno.o \
+			wav_player.o wav_writer.o g711.o jbuf.o \
+			master_port.o null_port.o port.o resample.o \
+			resample_port.o rtcp.o rtp.o sdp.o sdp_cmp.o sdp_neg.o \
+			session.o silencedet.o sound_port.o stream.o wave.o \
+			$(SOUND_OBJS) $(NULLSOUND_OBJS)
 
 export PJMEDIA_CFLAGS += $(_CFLAGS)
@@ -88,13 +89,16 @@
 SPEEX_OBJS :=	speex_codec.o \
 		speex/bits.o speex/cb_search.o speex/exc_10_16_table.o  \
-		speex/exc_10_32_table.o speex/exc_20_32_table.o speex/exc_5_256_table.o \
-		speex/exc_5_64_table.o speex/exc_8_128_table.o speex/filters.o \
-		speex/gain_table.o speex/gain_table_lbr.o speex/hexc_10_32_table.o \
-		speex/hexc_table.o speex/high_lsp_tables.o speex/lpc_spx.o \
-		speex/lsp.o speex/lsp_tables_nb.o speex/ltp.o speex/math_approx.o \
-		speex/misc.o speex/modes.o speex/nb_celp.o speex/preprocess_spx.o \
-		speex/quant_lsp.o speex/sb_celp.o speex/smallft.o speex/speex.o \
-		speex/speex_callbacks.o speex/speex_header.o speex/stereo.o \
-		speex/vbr.o speex/vq.o
+		speex/exc_10_32_table.o speex/exc_20_32_table.o \
+		speex/exc_5_256_table.o speex/exc_5_64_table.o \
+		speex/exc_8_128_table.o speex/filters.o \
+		speex/gain_table.o speex/gain_table_lbr.o \
+		speex/hexc_10_32_table.o speex/hexc_table.o \
+		speex/high_lsp_tables.o speex/lpc_spx.o \
+		speex/lsp.o speex/lsp_tables_nb.o speex/ltp.o \
+		speex/math_approx.o speex/misc.o speex/modes.o \
+		speex/nb_celp.o speex/preprocess_spx.o \
+		speex/quant_lsp.o speex/sb_celp.o speex/smallft.o \
+		speex/speex.o speex/speex_callbacks.o speex/speex_header.o \
+		speex/stereo.o speex/vbr.o speex/vq.o
 SPEEX_CFLAGS := -DHAVE_CONFIG=1 -I../src/pjmedia-codec
 
Index: pjproject/trunk/pjmedia/build/pjmedia.dsp
===================================================================
--- pjproject/trunk/pjmedia/build/pjmedia.dsp	(revision 410)
+++ pjproject/trunk/pjmedia/build/pjmedia.dsp	(revision 411)
@@ -66,5 +66,5 @@
 # PROP Target_Dir ""
 # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
-# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../src/pjmedia/portaudio" /D "_DEBUG" /D "PA_NO_ASIO" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /GZ /c
+# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../src/pjmedia/portaudio" /D "_DEBUG" /D "PA_NO_ASIO" /D "PA_NO_WMME" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /GZ /c
 # SUBTRACT CPP /YX
 # ADD BASE RSC /l 0x409 /d "_DEBUG"
@@ -112,12 +112,4 @@
 # Begin Source File
 
-SOURCE=..\src\pjmedia\file_player.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjmedia\file_writer.c
-# End Source File
-# Begin Source File
-
 SOURCE=..\src\pjmedia\g711.c
 # End Source File
@@ -189,4 +181,12 @@
 
 SOURCE=..\src\pjmedia\stream.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\wav_player.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\wav_writer.c
 # End Source File
 # Begin Source File
@@ -228,8 +228,4 @@
 # Begin Source File
 
-SOURCE=..\include\pjmedia\file_port.h
-# End Source File
-# Begin Source File
-
 SOURCE=..\include\pjmedia\g711.h
 # End Source File
@@ -297,4 +293,8 @@
 
 SOURCE=..\include\pjmedia\types.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\pjmedia\wav_port.h
 # End Source File
 # Begin Source File
Index: pjproject/trunk/pjmedia/build/pjmedia_codec.dsp
===================================================================
--- pjproject/trunk/pjmedia/build/pjmedia_codec.dsp	(revision 410)
+++ pjproject/trunk/pjmedia/build/pjmedia_codec.dsp	(revision 411)
@@ -92,4 +92,8 @@
 # Begin Source File
 
+SOURCE="..\src\pjmedia-codec\l16.c"
+# End Source File
+# Begin Source File
+
 SOURCE="..\src\pjmedia-codec\speex_codec.c"
 # End Source File
@@ -105,4 +109,8 @@
 
 SOURCE="..\include\pjmedia-codec\gsm.h"
+# End Source File
+# Begin Source File
+
+SOURCE="..\include\pjmedia-codec\l16.h"
 # End Source File
 # Begin Source File
Index: pjproject/trunk/pjmedia/include/pjmedia-codec.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia-codec.h	(revision 410)
+++ pjproject/trunk/pjmedia/include/pjmedia-codec.h	(revision 411)
@@ -20,4 +20,5 @@
 #define __PJMEDIA_CODEC_PJMEDIA_CODEC_H__
 
+#include <pjmedia-codec/l16.h>
 #include <pjmedia-codec/gsm.h>
 #include <pjmedia-codec/speex.h>
Index: pjproject/trunk/pjmedia/include/pjmedia-codec/config.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia-codec/config.h	(revision 410)
+++ pjproject/trunk/pjmedia/include/pjmedia-codec/config.h	(revision 411)
@@ -24,4 +24,12 @@
 
 /**
+ * Unless specified otherwise, L16 codec is included by default.
+ */
+#ifndef PJMEDIA_HAS_L16_CODEC
+#   define PJMEDIA_HAS_L16_CODEC    1
+#endif
+
+
+/**
  * Unless specified otherwise, GSM codec is included by default.
  */
Index: pjproject/trunk/pjmedia/include/pjmedia-codec/l16.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia-codec/l16.h	(revision 411)
+++ pjproject/trunk/pjmedia/include/pjmedia-codec/l16.h	(revision 411)
@@ -0,0 +1,53 @@
+/* $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 
+ */
+#ifndef __PJMEDIA_CODEC_L16_H__
+#define __PJMEDIA_CODEC_L16_H__
+
+#include <pjmedia-codec/types.h>
+
+
+PJ_BEGIN_DECL
+
+
+/**
+ * Initialize and register L16 codec factory to pjmedia endpoint.
+ *
+ * @param endpt	    The pjmedia endpoint.
+ * @param options   Must be zero for now.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_codec_l16_init( pjmedia_endpt *endpt,
+					     unsigned options);
+
+
+
+/**
+ * Unregister L16 codec factory from pjmedia endpoint.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_codec_l16_deinit(void);
+
+
+PJ_END_DECL
+
+
+#endif	/* __PJMEDIA_CODEC_L16_H__ */
+
Index: pjproject/trunk/pjmedia/include/pjmedia-codec/types.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia-codec/types.h	(revision 410)
+++ pjproject/trunk/pjmedia/include/pjmedia-codec/types.h	(revision 411)
@@ -23,3 +23,34 @@
 
 
+/**
+ * These are the dynamic payload types that are used by codecs in
+ * this library. Also see the header file <pjmedia/codec.h> for list
+ * of static payload types.
+ */
+enum
+{
+    /* Telephone events must have pt=101, or otherwise some code needs
+     * to be updated (this would not affect outgoing pt).
+     */
+    PJMEDIA_RTP_PT_TELEPHONE_EVENTS = 101,	/**< telephone-events	    */
+
+    PJMEDIA_RTP_PT_SPEEX_NB,			/**< Speex narrowband/8KHz  */
+    PJMEDIA_RTP_PT_SPEEX_WB,			/**< Speex wideband/16KHz   */
+    PJMEDIA_RTP_PT_SPEEX_UWB,			/**< Speex 32KHz	    */
+    PJMEDIA_RTP_PT_L16_8KHZ_MONO,		/**< L16 @ 8KHz, mono	    */
+    PJMEDIA_RTP_PT_L16_8KHZ_STEREO,		/**< L16 @ 8KHz, stereo     */
+    PJMEDIA_RTP_PT_L16_11KHZ_MONO,		/**< L16 @ 11KHz, mono	    */
+    PJMEDIA_RTP_PT_L16_11KHZ_STEREO,		/**< L16 @ 11KHz, stereo    */
+    PJMEDIA_RTP_PT_L16_16KHZ_MONO,		/**< L16 @ 16KHz, mono	    */
+    PJMEDIA_RTP_PT_L16_16KHZ_STEREO,		/**< L16 @ 16KHz, stereo    */
+    PJMEDIA_RTP_PT_L16_22KHZ_MONO,		/**< L16 @ 22KHz, mono	    */
+    PJMEDIA_RTP_PT_L16_22KHZ_STEREO,		/**< L16 @ 22KHz, stereo    */
+    PJMEDIA_RTP_PT_L16_32KHZ_MONO,		/**< L16 @ 32KHz, mono	    */
+    PJMEDIA_RTP_PT_L16_32KHZ_STEREO,		/**< L16 @ 32KHz, stereo    */
+    PJMEDIA_RTP_PT_L16_48KHZ_MONO,		/**< L16 @ 48KHz, mono	    */
+    PJMEDIA_RTP_PT_L16_48KHZ_STEREO,		/**< L16 @ 48KHz, stereo    */
+};
+
+
+
 #endif	/* __PJMEDIA_CODEC_TYPES_H__ */
Index: pjproject/trunk/pjmedia/include/pjmedia.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia.h	(revision 410)
+++ pjproject/trunk/pjmedia/include/pjmedia.h	(revision 411)
@@ -31,5 +31,4 @@
 #include <pjmedia/endpoint.h>
 #include <pjmedia/errno.h>
-#include <pjmedia/file_port.h>
 #include <pjmedia/g711.h>
 #include <pjmedia/jbuf.h>
@@ -46,4 +45,5 @@
 #include <pjmedia/sound.h>
 #include <pjmedia/sound_port.h>
+#include <pjmedia/wav_port.h>
 #include <pjmedia/wave.h>
 
Index: pjproject/trunk/pjmedia/include/pjmedia/codec.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/codec.h	(revision 410)
+++ pjproject/trunk/pjmedia/include/pjmedia/codec.h	(revision 411)
@@ -49,4 +49,6 @@
 /** 
  * Standard RTP static payload types, as defined by RFC 3551. 
+ * The header file <pjmedia-codec/types.h> also declares dynamic payload
+ * types that are supported by pjmedia-codec library.
  */
 enum pjmedia_rtp_pt
@@ -92,6 +94,13 @@
     unsigned	    pt;		    /**< Payload type (can be dynamic). */
     pj_str_t	    encoding_name;  /**< Encoding name.			*/
-    unsigned	    sample_rate;    /**< Sampling rate.			*/
-};
+    unsigned	    clock_rate;	    /**< Sampling rate.			*/
+    unsigned	    channel_cnt;    /**< Channel count.			*/
+};
+
+
+/**
+ * @see pjmedia_codec_info
+ */
+typedef struct pjmedia_codec_info pjmedia_codec_info;
 
 
@@ -102,20 +111,32 @@
 struct pjmedia_codec_param
 {
-    pj_uint32_t	sample_rate;	    /**< Sampling rate in Hz		*/
+    unsigned	clock_rate;	    /**< Sampling rate in Hz		*/
+    unsigned	channel_cnt;	    /**< Channel count.			*/
     pj_uint32_t	avg_bps;	    /**< Average bandwidth in bits/sec	*/
 
+    pj_uint16_t	ptime;		    /**< Packet time in miliseconds	*/
     pj_uint8_t	pcm_bits_per_sample;/**< Bits/sample in the PCM side	*/
-    pj_uint16_t	ptime;		    /**< Packet time in miliseconds	*/
 
     unsigned	pt:8;		    /**< Payload type.			*/
-    unsigned    vad_enabled:1;	    /**< Voice Activity Detector.	*/
-    unsigned    cng_enabled:1;	    /**< Comfort Noise Generator.	*/
-    unsigned    lpf_enabled:1;	    /**< Low pass filter		*/
-    unsigned    hpf_enabled:1;	    /**< High pass filter		*/
-    unsigned    penh_enabled:1;	    /**< Perceptual Enhancement		*/
-    unsigned    concl_enabled:1;    /**< Packet loss concealment	*/
-    unsigned    reserved_bit:1;	    /**< Reserved, must be NULL.	*/
-
-};
+    unsigned    vad:1;		    /**< Voice Activity Detector.	*/
+    unsigned    cng:1;		    /**< Comfort Noise Generator.	*/
+    unsigned    lpf:1;		    /**< Low pass filter		*/
+    unsigned    hpf:1;		    /**< High pass filter		*/
+    unsigned    penh:1;		    /**< Perceptual Enhancement		*/
+    unsigned    concl:1;	    /**< Packet loss concealment	*/
+    unsigned    reserved:1;	    /**< Reserved, must be NULL.	*/
+
+};
+
+/**
+ * @see pjmedia_codec_param
+ */
+typedef struct pjmedia_codec_param pjmedia_codec_param;
+
+
+/**
+ * @see pjmedia_codec
+ */
+typedef struct pjmedia_codec pjmedia_codec;
 
 
@@ -126,15 +147,4 @@
 struct pjmedia_codec_op
 {
-    /** 
-     * Get default attributes for this codec. 
-     *
-     * @param codec	The codec instance.
-     * @param attr	Pointer to receive default codec attributes.
-     *
-     * @return		PJ_SUCCESS on success.
-     */
-    pj_status_t (*default_attr)(pjmedia_codec *codec, 
-				pjmedia_codec_param *attr);
-
     /** 
      * Initialize codec using the specified attribute.
@@ -226,4 +236,16 @@
 
 /**
+ * Codec operation.
+ */
+typedef struct pjmedia_codec_op pjmedia_codec_op;
+
+
+/**
+ * @see pjmedia_codec_factory
+ */
+typedef struct pjmedia_codec_factory pjmedia_codec_factory;
+
+
+/**
  * This structure describes a codec instance. 
  */
@@ -234,12 +256,13 @@
 
     /** Codec's private data. */
-    void	*codec_data;
+    void		    *codec_data;
 
     /** Codec factory where this codec was allocated. */
-    pjmedia_codec_factory *factory;
+    pjmedia_codec_factory   *factory;
 
     /** Operations to codec. */
-    pjmedia_codec_op	*op;
-};
+    pjmedia_codec_op	    *op;
+};
+
 
 
@@ -274,6 +297,6 @@
      */
     pj_status_t (*default_attr)(pjmedia_codec_factory *factory, 
-				const pjmedia_codec_info *info,
-				pjmedia_codec_param *attr );
+    				const pjmedia_codec_info *info,
+    				pjmedia_codec_param *attr );
 
     /** 
@@ -323,4 +346,10 @@
 
 /**
+ * @see pjmedia_codec_factory_op
+ */
+typedef struct pjmedia_codec_factory_op pjmedia_codec_factory_op;
+
+
+/**
  * Codec factory describes a module that is able to create codec with specific
  * capabilities. These capabilities can be queried by codec manager to create
@@ -340,4 +369,5 @@
 };
 
+
 /**
  * Declare maximum codecs
@@ -345,23 +375,100 @@
 #define PJMEDIA_CODEC_MGR_MAX_CODECS	    32
 
-/**
- * Codec manager maintains codec factory etc.
+
+/**
+ * Specify these values to set the codec priority, by calling
+ * #pjmedia_codec_mgr_set_codec_priority().
+ */
+enum pjmedia_codec_priority
+{
+    /**
+     * This priority makes the codec the highest in the order.
+     * The last codec specified with this priority will get the
+     * highest place in the order, and will change the priority
+     * of previously highest priority codec to NEXT_HIGHER.
+     */
+    PJMEDIA_CODEC_PRIO_HIGHEST,
+
+    /**
+     * This priority will put the codec as the next codec after
+     * codecs with this same priority.
+     */
+    PJMEDIA_CODEC_PRIO_NEXT_HIGHER,
+
+    /**
+     * This is the initial codec priority when it is registered to
+     * codec manager by codec factory.
+     */
+    PJMEDIA_CODEC_PRIO_NORMAL,
+
+    /**
+     * This priority makes the codec the lowest in the order.
+     * The last codec specified with this priority will be put
+     * in the last place in the order.
+     */
+    PJMEDIA_CODEC_PRIO_LOWEST,
+
+    /**
+     * This priority will prevent the codec from being listed in the
+     * SDP created by media endpoint, thus should prevent the codec
+     * from being used in the sessions. However, the codec will still
+     * be listed by #pjmedia_codec_mgr_enum_codecs() and other codec
+     * query functions.
+     */
+    PJMEDIA_CODEC_PRIO_DISABLED,
+};
+
+
+/**
+ * @see pjmedia_codec_priority
+ */
+typedef enum pjmedia_codec_priority pjmedia_codec_priority;
+
+
+/** Fully qualified codec name  (e.g. "pcmu/8000/1") */
+typedef char pjmedia_codec_id[32];
+
+
+/** 
+ * Codec manager maintains array of these structs for each supported
+ * codec.
+ */
+struct pjmedia_codec_desc
+{
+    pjmedia_codec_info	    info;	/**< Codec info.	    */
+    pjmedia_codec_id	    id;		/**< Fully qualified name   */
+    pjmedia_codec_priority  prio;	/**< Priority.		    */
+    pjmedia_codec_factory  *factory;	/**< The factory.	    */
+};
+
+
+/**
+ * The declaration for codec manager. Application doesn't normally need
+ * to see this declaration, but nevertheless this declaration is needed
+ * by media endpoint to instantiate the codec manager.
  */
 struct pjmedia_codec_mgr
 {
     /** List of codec factories registered to codec manager. */
-    pjmedia_codec_factory   factory_list;
+    pjmedia_codec_factory	factory_list;
 
     /** Number of supported codesc. */
-    unsigned		    codec_cnt;
-
-    /** Array of codec info. */
-    pjmedia_codec_info	    codecs[PJMEDIA_CODEC_MGR_MAX_CODECS];
-};
-
-
-
-/**
- * Initialize codec manager.
+    unsigned			codec_cnt;
+
+    /** Array of codec descriptor. */
+    struct pjmedia_codec_desc	codec_desc[PJMEDIA_CODEC_MGR_MAX_CODECS];
+};
+
+
+/**
+ * @see pjmedia_codec_mgr
+ */
+typedef struct pjmedia_codec_mgr pjmedia_codec_mgr;
+
+
+
+/**
+ * Initialize codec manager. Normally this function is called by pjmedia
+ * endpoint's initialization code.
  *
  * @param mgr	    Codec manager instance.
@@ -373,7 +480,9 @@
 
 /** 
- * Register codec factory to codec manager. 
- *
- * @param mgr	    The codec manager.
+ * Register codec factory to codec manager. This will also register
+ * all supported codecs in the factory to the codec manager.
+ *
+ * @param mgr	    The codec manager instance. Application can get the
+ *		    instance by calling #pjmedia_endpt_get_codec_mgr().
  * @param factory   The codec factory to be registered.
  *
@@ -385,7 +494,10 @@
 
 /**
- * Unregister codec factory from the codec manager.
- *
- * @param mgr	    The codec manager.
+ * Unregister codec factory from the codec manager. This will also
+ * remove all the codecs registered by the codec factory from the
+ * codec manager's list of supported codecs.
+ *
+ * @param mgr	    The codec manager instance. Application can get the
+ *		    instance by calling #pjmedia_endpt_get_codec_mgr().
  * @param factory   The codec factory to be unregistered.
  *
@@ -397,7 +509,9 @@
 
 /**
- * Enumerate all supported codec.
- *
- * @param mgr	    The codec manager.
+ * Enumerate all supported codecs that have been registered to the
+ * codec manager by codec factories.
+ *
+ * @param mgr	    The codec manager instance. Application can get the
+ *		    instance by calling #pjmedia_endpt_get_codec_mgr().
  * @param count	    On input, specifies the number of elements in
  *		    the array. On output, the value will be set to
@@ -406,4 +520,5 @@
  * @param info	    The codec info array, which contents will be 
  *		    initialized upon return.
+ * @param prio	    Optional pointer to receive array of codec priorities.
  *
  * @return	    PJ_SUCCESS on success.
@@ -411,10 +526,15 @@
 PJ_DECL(pj_status_t) pjmedia_codec_mgr_enum_codecs( pjmedia_codec_mgr *mgr, 
 						    unsigned *count, 
-						    pjmedia_codec_info info[]);
-
-/**
- * Get codec info for the specified static payload type.
- *
- * @param mgr	    The codec manager.
+						    pjmedia_codec_info info[],
+						    unsigned *prio);
+
+/**
+ * Get codec info for the specified static payload type. Note that
+ * this can only find codec with static payload types. This function can
+ * be used to find codec info for a payload type inside SDP which doesn't
+ * have the corresponding rtpmap attribute.
+ *
+ * @param mgr	    The codec manager instance. Application can get the
+ *		    instance by calling #pjmedia_endpt_get_codec_mgr().
  * @param pt	    Static payload type/number.
  * @param inf	    Pointer to receive codec info.
@@ -422,12 +542,76 @@
  * @return	    PJ_SUCCESS on success.
  */
-PJ_DECL(pj_status_t) pjmedia_codec_mgr_get_codec_info(pjmedia_codec_mgr *mgr,
-						      unsigned pt,
-						      pjmedia_codec_info *inf);
+PJ_DECL(pj_status_t) 
+pjmedia_codec_mgr_get_codec_info( pjmedia_codec_mgr *mgr,
+				  unsigned pt,
+				  const pjmedia_codec_info **inf);
+
+/**
+ * Convert codec info struct into a unique codec identifier.
+ * A codec identifier looks something like "L16/44100/2".
+ *
+ * @param info	    The codec info
+ * @param id	    Buffer to put the codec info string.
+ * @param max_len   The length of the buffer.
+ *
+ * @return	    The null terminated codec info string, or NULL if
+ *		    the buffer is not long enough.
+ */
+PJ_DECL(char*) pjmedia_codec_info_to_id(const pjmedia_codec_info *info,
+				        char *id, unsigned max_len );
+
+
+/**
+ * Find codecs by the unique codec identifier. This function will find
+ * all codecs that match the codec identifier prefix. For example, if
+ * "L16" is specified, then it will find "L16/8000/1", "L16/16000/1",
+ * and so on, up to the maximum count specified in the argument.
+ *
+ * @param mgr	    The codec manager instance. Application can get the
+ *		    instance by calling #pjmedia_endpt_get_codec_mgr().
+ * @param codec_id  The full codec ID or codec ID prefix. If an empty
+ *		    string is given, it will match all codecs.
+ * @param count	    Maximum number of codecs to find. On return, it
+ *		    contains the actual number of codecs found.
+ * @param p_info    Array of pointer to codec info to be filled. This
+ *		    argument may be NULL, which in this case, only
+ *		    codec count will be returned.
+ * @param prio	    Optional array of codec priorities.
+ *
+ * @return	    PJ_SUCCESS if at least one codec info is found.
+ */
+PJ_DECL(pj_status_t) 
+pjmedia_codec_mgr_find_codecs_by_id( pjmedia_codec_mgr *mgr,
+				     const pj_str_t *codec_id,
+				     unsigned *count,
+				     const pjmedia_codec_info *p_info[],
+				     unsigned prio[]);
+
+
+/**
+ * Set codec priority. The codec priority determines the order of
+ * the codec in the SDP created by the endpoint. If more than one codecs
+ * are found with the same codec_id prefix, then the function sets the
+ * priorities of all those codecs.
+ *
+ * @param mgr	    The codec manager instance. Application can get the
+ *		    instance by calling #pjmedia_endpt_get_codec_mgr().
+ * @param codec_id  The full codec ID or codec ID prefix. If an empty
+ *		    string is given, it will match all codecs.
+ * @param prio	    Priority to be set.
+ *
+ * @return	    PJ_SUCCESS if at least one codec info is found.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_codec_mgr_set_codec_priority(pjmedia_codec_mgr *mgr, 
+				     const pj_str_t *codec_id,
+				     pjmedia_codec_priority prio);
+
 
 /**
  * Get default codec param for the specified codec info.
  *
- * @param mgr	    The codec manager.
+ * @param mgr	    The codec manager instance. Application can get the
+ *		    instance by calling #pjmedia_endpt_get_codec_mgr().
  * @param info	    The codec info, which default parameter's is being
  *		    queried.
@@ -437,7 +621,8 @@
  * @return	    PJ_SUCCESS on success.
  */
-PJ_DECL(pj_status_t) pjmedia_codec_mgr_get_default_param(pjmedia_codec_mgr *mgr,
-							 const pjmedia_codec_info *info,
-							 pjmedia_codec_param *param );
+PJ_DECL(pj_status_t) 
+pjmedia_codec_mgr_get_default_param( pjmedia_codec_mgr *mgr,
+				     const pjmedia_codec_info *info,
+				     pjmedia_codec_param *param );
 
 /**
@@ -446,5 +631,6 @@
  * until it finds factory that is able to create the specified codec.
  *
- * @param mgr	    The codec manager.
+ * @param mgr	    The codec manager instance. Application can get the
+ *		    instance by calling #pjmedia_endpt_get_codec_mgr().
  * @param info	    The information about the codec to be created.
  * @param p_codec   Pointer to receive the codec instance.
@@ -452,7 +638,8 @@
  * @return	    PJ_SUCCESS on success.
  */
-PJ_DECL(pj_status_t) pjmedia_codec_mgr_alloc_codec(pjmedia_codec_mgr *mgr, 
-						   const pjmedia_codec_info *info,
-						   pjmedia_codec **p_codec);
+PJ_DECL(pj_status_t) 
+pjmedia_codec_mgr_alloc_codec( pjmedia_codec_mgr *mgr, 
+			       const pjmedia_codec_info *info,
+			       pjmedia_codec **p_codec);
 
 /**
@@ -460,5 +647,6 @@
  * the instance of the codec back to its factory.
  *
- * @param mgr	    The codec manager.
+ * @param mgr	    The codec manager instance. Application can get the
+ *		    instance by calling #pjmedia_endpt_get_codec_mgr().
  * @param codec	    The codec instance.
  *
@@ -468,4 +656,8 @@
 						     pjmedia_codec *codec);
 
+
+
+
+
 /**
  * @}
Index: pjproject/trunk/pjmedia/include/pjmedia/endpoint.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/endpoint.h	(revision 410)
+++ pjproject/trunk/pjmedia/include/pjmedia/endpoint.h	(revision 411)
@@ -40,4 +40,5 @@
 #include <pjmedia/sound.h>
 #include <pjmedia/codec.h>
+#include <pjmedia/sdp.h>
 
 
Index: pjproject/trunk/pjmedia/include/pjmedia/file_port.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/file_port.h	(revision 410)
+++ 	(revision )
@@ -1,89 +1,0 @@
-/* $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 
- */
-#ifndef __PJMEDIA_FILE_PORT_H__
-#define __PJMEDIA_FILE_PORT_H__
-
-/**
- * @file file_port.h
- * @brief File player and recorder.
- */
-#include <pjmedia/port.h>
-
-
-
-PJ_BEGIN_DECL
-
-
-/**
- * Create a media port to play streams from a WAV file.
- *
- * @param pool		Pool to create memory buffers for this port.
- * @param filename	File name to open.
- * @param flags		Port creation flags.
- * @param buf_size	Buffer size to be allocated. If the value is zero or
- *			negative, the port will use default buffer size (which
- *			is about 4KB).
- * @param user_data	User data to be associated with the file player port.
- * @param p_port	Pointer to receive the file port instance.
- *
- * @return		PJ_SUCCESS on success.
- */
-PJ_DECL(pj_status_t) pjmedia_file_player_port_create( pj_pool_t *pool,
-						      const char *filename,
-						      unsigned flags,
-						      pj_ssize_t buff_size,
-						      void *user_data,
-						      pjmedia_port **p_port );
-
-
-
-/**
- * Create a media port to record streams to a WAV file. Note that the port
- * must be closed properly (with #pjmedia_port_destroy()) so that the WAV
- * header can be filled with correct values (such as the file length).
- *
- * @param pool		Pool to create memory buffers for this port.
- * @param filename	File name.
- * @param flags		Port creation flags.
- * @param buf_size	Buffer size to be allocated. If the value is zero or
- *			negative, the port will use default buffer size (which
- *			is about 4KB).
- * @param user_data	User data to be associated with the file writer port.
- * @param p_port	Pointer to receive the file port instance.
- *
- * @return		PJ_SUCCESS on success.
- */
-PJ_DECL(pj_status_t) pjmedia_file_writer_port_create( pj_pool_t *pool,
-						      const char *filename,
-						      unsigned sampling_rate,
-						      unsigned channel_count,
-						      unsigned samples_per_frame,
-						      unsigned bits_per_sample,
-						      unsigned flags,
-						      pj_ssize_t buff_size,
-						      void *user_data,
-						      pjmedia_port **p_port );
-
-
-
-
-PJ_END_DECL
-
-
-#endif	/* __PJMEDIA_FILE_PORT_H__ */
Index: pjproject/trunk/pjmedia/include/pjmedia/port.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/port.h	(revision 410)
+++ pjproject/trunk/pjmedia/include/pjmedia/port.h	(revision 411)
@@ -78,5 +78,5 @@
     unsigned	    pt;			/**< Payload type (can be dynamic). */
     pj_str_t	    encoding_name;	/**< Encoding name.		    */
-    unsigned	    sample_rate;	/**< Sampling rate.		    */
+    unsigned	    clock_rate;		/**< Sampling rate.		    */
     unsigned	    channel_count;	/**< Number of channels.	    */
     unsigned	    bits_per_sample;	/**< Bits/sample		    */
@@ -102,4 +102,11 @@
 };
 
+
+/** 
+ * @see pjmedia_frame_type
+ */
+typedef enum pjmedia_frame_type pjmedia_frame_type;
+
+
 /** 
  * This structure describes a media frame. 
@@ -112,4 +119,11 @@
     pj_timestamp	 timestamp; /**< Frame timestamp.	    */
 };
+
+
+/** 
+ * @see pjmedia_frame
+ */
+typedef struct pjmedia_frame pjmedia_frame;
+
 
 /**
Index: pjproject/trunk/pjmedia/include/pjmedia/sdp.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/sdp.h	(revision 410)
+++ pjproject/trunk/pjmedia/include/pjmedia/sdp.h	(revision 411)
@@ -78,4 +78,9 @@
     pj_str_t		value;	    /**< Attribute value.   */
 };
+
+/**
+ * @see pjmedia_sdp_attr
+ */
+typedef struct pjmedia_sdp_attr pjmedia_sdp_attr;
 
 
@@ -203,4 +208,9 @@
 };
 
+/**
+ * @see pjmedia_sdp_rtpmap
+ */
+typedef struct pjmedia_sdp_rtpmap pjmedia_sdp_rtpmap;
+
 
 /**
@@ -262,4 +272,11 @@
     pj_str_t		fmt_param;  /**< Format specific parameter. */
 };
+
+
+/**
+ * @see pjmedia_sdp_fmtp
+ */
+typedef struct pjmedia_sdp_fmtp pjmedia_sdp_fmtp;
+
 
 
@@ -291,4 +308,10 @@
     pj_str_t	addr;		/**< The address.			*/
 };
+
+
+/**
+ * @see pjmedia_sdp_conn
+ */
+typedef struct pjmedia_sdp_conn pjmedia_sdp_conn;
 
 
@@ -336,4 +359,10 @@
 
 
+/**
+ * @see pjmedia_sdp_media
+ */
+typedef struct pjmedia_sdp_media pjmedia_sdp_media;
+
+
 /** 
  * Clone SDP media description. 
@@ -482,4 +511,10 @@
 };
 
+/**
+ * @see pjmedia_sdp_session
+ */
+typedef struct pjmedia_sdp_session pjmedia_sdp_session;
+
+
 
 /**
Index: pjproject/trunk/pjmedia/include/pjmedia/sdp_neg.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/sdp_neg.h	(revision 410)
+++ pjproject/trunk/pjmedia/include/pjmedia/sdp_neg.h	(revision 411)
@@ -234,5 +234,5 @@
  */
 
-#include <pjmedia/types.h>
+#include <pjmedia/sdp.h>
 
 PJ_BEGIN_DECL
@@ -277,4 +277,16 @@
 
 /**
+ * @see pjmedia_sdp_neg_state
+ */
+typedef enum pjmedia_sdp_neg_state pjmedia_sdp_neg_state;
+
+
+/**
+ * Opaque declaration of SDP negotiator.
+ */
+typedef struct pjmedia_sdp_neg pjmedia_sdp_neg;
+
+
+/**
  * Get the state string description of the specified state.
  *
Index: pjproject/trunk/pjmedia/include/pjmedia/types.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/types.h	(revision 410)
+++ pjproject/trunk/pjmedia/include/pjmedia/types.h	(revision 411)
@@ -44,4 +44,10 @@
 };
 
+/**
+ * @see pjmedia_type
+ */
+typedef enum pjmedia_type pjmedia_type;
+
+
 
 /** 
@@ -64,4 +70,10 @@
 };
 
+/**
+ * @see pjmedia_dir
+ */
+typedef enum pjmedia_dir pjmedia_dir;
+
+
 /* Alternate names for media direction: */
 
@@ -81,59 +93,4 @@
 #define PJMEDIA_DIR_CAPTURE_PLAYBACK	PJMEDIA_DIR_ENCODING_DECODING
 
-
-/**
- * Top level media type.
- */
-typedef enum pjmedia_type pjmedia_type;
-
-/**
- * Media direction.
- */
-typedef enum pjmedia_dir pjmedia_dir;
-
-/**
- * Codec info.
- */
-typedef struct pjmedia_codec_info pjmedia_codec_info;
-
-/**
- * Codec initialization parameter.
- */
-typedef struct pjmedia_codec_param pjmedia_codec_param;
-
-/** 
- * Types of media frames. 
- */
-typedef enum pjmedia_frame_type pjmedia_frame_type;
-
-/** 
- * This structure describes a media frame. 
- */
-typedef struct pjmedia_frame pjmedia_frame;
-
-/**
- * Codec instance.
- */
-typedef struct pjmedia_codec pjmedia_codec;
-
-/**
- * Codec factory.
- */
-typedef struct pjmedia_codec_factory pjmedia_codec_factory;
-
-/**
- * Codec operation.
- */
-typedef struct pjmedia_codec_op pjmedia_codec_op;
-
-/**
- * Codec factory operation.
- */
-typedef struct pjmedia_codec_factory_op pjmedia_codec_factory_op;
-
-/**
- * Codec manager.
- */
-typedef struct pjmedia_codec_mgr pjmedia_codec_mgr;
 
 /** 
@@ -188,44 +145,4 @@
 
 /**
- * Forward declaration for SDP attribute (sdp.h)
- */
-typedef struct pjmedia_sdp_attr pjmedia_sdp_attr;
-
-/**
- * Forward declaration for SDP rtpmap attribute (sdp.h)
- */
-typedef struct pjmedia_sdp_rtpmap pjmedia_sdp_rtpmap;
-
-/**
- * Forward declaration for SDP fmtp attribute (sdp.h)
- */
-typedef struct pjmedia_sdp_fmtp pjmedia_sdp_fmtp;
-
-/**
- * Forward declaration for SDP connection info (sdp.h)
- */
-typedef struct pjmedia_sdp_conn pjmedia_sdp_conn;
-
-/**
- * Forward declaration for SDP media line (sdp.h)
- */
-typedef struct pjmedia_sdp_media pjmedia_sdp_media;
-
-/**
- * Forward declaration for SDP session (sdp.h)
- */
-typedef struct pjmedia_sdp_session pjmedia_sdp_session;
-
-/**
- * Forward declaration for SDP negotiator state (sdp_neg.h).
- */
-typedef enum pjmedia_sdp_neg_state pjmedia_sdp_neg_state;
-
-/**
- * Forward declaration for SDP negotiator (sdp_neg.h).
- */
-typedef struct pjmedia_sdp_neg pjmedia_sdp_neg;
-
-/**
  * Types of frame returned from jitter buffer (jbuf.h).
  */
Index: pjproject/trunk/pjmedia/include/pjmedia/wav_port.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/wav_port.h	(revision 411)
+++ pjproject/trunk/pjmedia/include/pjmedia/wav_port.h	(revision 411)
@@ -0,0 +1,92 @@
+/* $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 
+ */
+#ifndef __PJMEDIA_WAV_PORT_H__
+#define __PJMEDIA_WAV_PORT_H__
+
+/**
+ * @file wav_port.h
+ * @brief WAV file player and writer.
+ */
+#include <pjmedia/port.h>
+
+
+
+PJ_BEGIN_DECL
+
+
+/**
+ * Create a media port to play streams from a WAV file.
+ *
+ * @param pool		Pool to create memory buffers for this port.
+ * @param filename	File name to open.
+ * @param ptime		The duration (in miliseconds) of each frame read
+ *			from this port. If the value is zero, the default
+ *			duration (20ms) will be used.
+ * @param flags		Port creation flags.
+ * @param buf_size	Buffer size to be allocated. If the value is zero or
+ *			negative, the port will use default buffer size (which
+ *			is about 4KB).
+ * @param user_data	User data to be associated with the file player port.
+ * @param p_port	Pointer to receive the file port instance.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool,
+						     const char *filename,
+						     unsigned ptime,
+						     unsigned flags,
+						     pj_ssize_t buff_size,
+						     void *user_data,
+						     pjmedia_port **p_port );
+
+
+/**
+ * Create a media port to record streams to a WAV file. Note that the port
+ * must be closed properly (with #pjmedia_port_destroy()) so that the WAV
+ * header can be filled with correct values (such as the file length).
+ *
+ * @param pool		Pool to create memory buffers for this port.
+ * @param filename	File name.
+ * @param flags		Port creation flags.
+ * @param buf_size	Buffer size to be allocated. If the value is zero or
+ *			negative, the port will use default buffer size (which
+ *			is about 4KB).
+ * @param user_data	User data to be associated with the file writer port.
+ * @param p_port	Pointer to receive the file port instance.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_wav_writer_port_create( pj_pool_t *pool,
+						      const char *filename,
+						      unsigned sampling_rate,
+						      unsigned channel_count,
+						      unsigned samples_per_frame,
+						      unsigned bits_per_sample,
+						      unsigned flags,
+						      pj_ssize_t buff_size,
+						      void *user_data,
+						      pjmedia_port **p_port );
+
+
+
+
+PJ_END_DECL
+
+
+#endif	/* __PJMEDIA_WAV_PORT_H__ */
Index: pjproject/trunk/pjmedia/src/pjmedia-codec/gsm.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia-codec/gsm.c	(revision 410)
+++ pjproject/trunk/pjmedia/src/pjmedia-codec/gsm.c	(revision 411)
@@ -50,6 +50,4 @@
 
 /* Prototypes for GSM implementation. */
-static pj_status_t  gsm_codec_default_attr(pjmedia_codec *codec, 
-					   pjmedia_codec_param *attr);
 static pj_status_t  gsm_codec_init( pjmedia_codec *codec, 
 				    pj_pool_t *pool );
@@ -74,5 +72,4 @@
 static pjmedia_codec_op gsm_op = 
 {
-    &gsm_codec_default_attr,
     &gsm_codec_init,
     &gsm_codec_open,
@@ -234,5 +231,6 @@
 
     pj_memset(attr, 0, sizeof(pjmedia_codec_param));
-    attr->sample_rate = 8000;
+    attr->clock_rate = 8000;
+    attr->channel_cnt = 1;
     attr->avg_bps = 13200;
     attr->pcm_bits_per_sample = 16;
@@ -259,5 +257,6 @@
     codecs[0].pt = PJMEDIA_RTP_PT_GSM;
     codecs[0].type = PJMEDIA_TYPE_AUDIO;
-    codecs[0].sample_rate = 8000;
+    codecs[0].clock_rate = 8000;
+    codecs[0].channel_cnt = 1;
 
     *count = 1;
@@ -329,13 +328,4 @@
 
 /*
- * Get codec default attributes.
- */
-static pj_status_t gsm_codec_default_attr( pjmedia_codec *codec, 
-					   pjmedia_codec_param *attr)
-{
-    return gsm_default_attr( codec->factory, NULL, attr);
-}
-
-/*
  * Init codec.
  */
Index: pjproject/trunk/pjmedia/src/pjmedia-codec/l16.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia-codec/l16.c	(revision 411)
+++ pjproject/trunk/pjmedia/src/pjmedia-codec/l16.c	(revision 411)
@@ -0,0 +1,594 @@
+/* $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-codec/l16.h>
+#include <pjmedia/codec.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/endpoint.h>
+#include <pj/assert.h>
+#include <pj/pool.h>
+#include <pj/sock.h>
+#include <pj/string.h>
+
+
+/*
+ * Only build this file if PJMEDIA_HAS_L16_CODEC != 0
+ */
+#if defined(PJMEDIA_HAS_L16_CODEC) && PJMEDIA_HAS_L16_CODEC != 0
+
+
+static const pj_str_t STR_L16 = { "L16", 3 };
+
+/* To keep frame size below 1400 MTU, set ptime to 10ms for
+ * sampling rate > 35 KHz
+ */
+#define GET_PTIME(clock_rate)	((pj_uint16_t)(clock_rate > 35000 ? 10 : 20))
+
+
+/* Prototypes for L16 factory */
+static pj_status_t l16_test_alloc( pjmedia_codec_factory *factory, 
+				    const pjmedia_codec_info *id );
+static pj_status_t l16_default_attr( pjmedia_codec_factory *factory, 
+				      const pjmedia_codec_info *id, 
+				      pjmedia_codec_param *attr );
+static pj_status_t l16_enum_codecs (pjmedia_codec_factory *factory, 
+				     unsigned *count, 
+				     pjmedia_codec_info codecs[]);
+static pj_status_t l16_alloc_codec( pjmedia_codec_factory *factory, 
+				     const pjmedia_codec_info *id, 
+				     pjmedia_codec **p_codec);
+static pj_status_t l16_dealloc_codec( pjmedia_codec_factory *factory, 
+				       pjmedia_codec *codec );
+
+/* Prototypes for L16 implementation. */
+static pj_status_t  l16_init( pjmedia_codec *codec, 
+			       pj_pool_t *pool );
+static pj_status_t  l16_open( pjmedia_codec *codec, 
+			       pjmedia_codec_param *attr );
+static pj_status_t  l16_close( pjmedia_codec *codec );
+static pj_status_t  l16_get_frames(pjmedia_codec *codec,
+				    void *pkt,
+				    pj_size_t pkt_size,
+				    unsigned *frame_cnt,
+				    pjmedia_frame frames[]);
+static pj_status_t  l16_encode( pjmedia_codec *codec, 
+				 const struct pjmedia_frame *input,
+				 unsigned output_buf_len, 
+				 struct pjmedia_frame *output);
+static pj_status_t  l16_decode( pjmedia_codec *codec, 
+				 const struct pjmedia_frame *input,
+				 unsigned output_buf_len, 
+				 struct pjmedia_frame *output);
+
+/* Definition for L16 codec operations. */
+static pjmedia_codec_op l16_op = 
+{
+    &l16_init,
+    &l16_open,
+    &l16_close,
+    &l16_get_frames,
+    &l16_encode,
+    &l16_decode
+};
+
+/* Definition for L16 codec factory operations. */
+static pjmedia_codec_factory_op l16_factory_op =
+{
+    &l16_test_alloc,
+    &l16_default_attr,
+    &l16_enum_codecs,
+    &l16_alloc_codec,
+    &l16_dealloc_codec
+};
+
+/* L16 factory private data */
+static struct l16_factory
+{
+    pjmedia_codec_factory	base;
+    pjmedia_endpt	       *endpt;
+    pj_pool_t		       *pool;
+    pj_mutex_t		       *mutex;
+    pjmedia_codec		codec_list;
+} l16_factory;
+
+
+/* L16 codec private data. */
+struct l16_data
+{
+    unsigned frame_size;    /* Frame size, in bytes */
+};
+
+
+
+PJ_DEF(pj_status_t) pjmedia_codec_l16_init(pjmedia_endpt *endpt,
+					   unsigned options)
+{
+    pjmedia_codec_mgr *codec_mgr;
+    pj_status_t status;
+
+
+    PJ_UNUSED_ARG(options);
+
+
+    if (l16_factory.endpt != NULL) {
+	/* Already initialized. */
+	return PJ_SUCCESS;
+    }
+
+    /* Init factory */
+    l16_factory.base.op = &l16_factory_op;
+    l16_factory.base.factory_data = NULL;
+    l16_factory.endpt = endpt;
+
+    pj_list_init(&l16_factory.codec_list);
+
+    /* Create pool */
+    l16_factory.pool = pjmedia_endpt_create_pool(endpt, "l16", 4000, 4000);
+    if (!l16_factory.pool)
+	return PJ_ENOMEM;
+
+    /* Create mutex. */
+    status = pj_mutex_create_simple(l16_factory.pool, "l16", 
+				    &l16_factory.mutex);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Get the codec manager. */
+    codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
+    if (!codec_mgr) {
+	return PJ_EINVALIDOP;
+    }
+
+    /* Register codec factory to endpoint. */
+    status = pjmedia_codec_mgr_register_factory(codec_mgr, 
+						&l16_factory.base);
+    if (status != PJ_SUCCESS)
+	return status;
+
+
+    return PJ_SUCCESS;
+
+on_error:
+    if (l16_factory.mutex) {
+	pj_mutex_destroy(l16_factory.mutex);
+	l16_factory.mutex = NULL;
+    }
+    if (l16_factory.pool) {
+	pj_pool_release(l16_factory.pool);
+	l16_factory.pool = NULL;
+    }
+    return status;
+}
+
+PJ_DEF(pj_status_t) pjmedia_codec_l16_deinit(void)
+{
+    pjmedia_codec_mgr *codec_mgr;
+    pj_status_t status;
+
+    if (l16_factory.endpt == NULL) {
+	/* Not registered. */
+	return PJ_SUCCESS;
+    }
+
+    /* Lock mutex. */
+    pj_mutex_lock(l16_factory.mutex);
+
+    /* Get the codec manager. */
+    codec_mgr = pjmedia_endpt_get_codec_mgr(l16_factory.endpt);
+    if (!codec_mgr) {
+	l16_factory.endpt = NULL;
+	pj_mutex_unlock(l16_factory.mutex);
+	return PJ_EINVALIDOP;
+    }
+
+    /* Unregister L16 codec factory. */
+    status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
+						  &l16_factory.base);
+    l16_factory.endpt = NULL;
+
+    /* Destroy mutex. */
+    pj_mutex_destroy(l16_factory.mutex);
+    l16_factory.mutex = NULL;
+
+
+    /* Release pool. */
+    pj_pool_release(l16_factory.pool);
+    l16_factory.pool = NULL;
+
+
+    return status;
+}
+
+static pj_status_t l16_test_alloc(pjmedia_codec_factory *factory, 
+				  const pjmedia_codec_info *id )
+{
+    PJ_UNUSED_ARG(factory);
+
+    if (pj_stricmp(&id->encoding_name, &STR_L16)==0) {
+	/* Match! */
+	return PJ_SUCCESS;
+    }
+
+    return -1;
+}
+
+static pj_status_t l16_default_attr( pjmedia_codec_factory *factory, 
+				     const pjmedia_codec_info *id, 
+				     pjmedia_codec_param *attr )
+{
+    PJ_UNUSED_ARG(factory);
+
+    pj_memset(attr, 0, sizeof(pjmedia_codec_param));
+    attr->pt = id->pt;
+    attr->clock_rate = id->clock_rate;
+    attr->channel_cnt = id->channel_cnt;
+    attr->avg_bps = id->clock_rate * id->channel_cnt * 16;
+    attr->pcm_bits_per_sample = 16;
+
+    /* To keep frame size below 1400 MTU, set ptime to 10ms for
+     * sampling rate > 35 KHz
+     */
+    attr->ptime = GET_PTIME(id->clock_rate);
+
+    /* Default all flag bits disabled. */
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t l16_enum_codecs( pjmedia_codec_factory *factory, 
+				    unsigned *max_count, 
+				    pjmedia_codec_info codecs[])
+{
+    unsigned count = 0;
+
+    PJ_UNUSED_ARG(factory);
+
+    if (count < *max_count) {
+	/* Register 44100Hz 1 channel L16 codec */
+	codecs[count].type = PJMEDIA_TYPE_AUDIO;
+	codecs[count].pt = PJMEDIA_RTP_PT_L16_1;
+	codecs[count].encoding_name = STR_L16;
+	codecs[count].clock_rate = 44100;
+	codecs[count].channel_cnt = 1;
+	++count;
+    }
+
+    if (count < *max_count) {
+	/* Register 44100Hz 2 channels L16 codec */
+	codecs[count].type = PJMEDIA_TYPE_AUDIO;
+	codecs[count].pt = PJMEDIA_RTP_PT_L16_2;
+	codecs[count].encoding_name = STR_L16;
+	codecs[count].clock_rate = 44100;
+	codecs[count].channel_cnt = 2;
+	++count;
+    }
+
+    if (count < *max_count) {
+	/* 8KHz mono */
+	codecs[count].type = PJMEDIA_TYPE_AUDIO;
+	codecs[count].pt = PJMEDIA_RTP_PT_L16_8KHZ_MONO;
+	codecs[count].encoding_name = STR_L16;
+	codecs[count].clock_rate = 8000;
+	codecs[count].channel_cnt = 1;
+	++count;
+    }
+
+    if (count < *max_count) {
+	/* 8KHz stereo */
+	codecs[count].type = PJMEDIA_TYPE_AUDIO;
+	codecs[count].pt = PJMEDIA_RTP_PT_L16_8KHZ_STEREO;
+	codecs[count].encoding_name = STR_L16;
+	codecs[count].clock_rate = 8000;
+	codecs[count].channel_cnt = 2;
+	++count;
+    }
+
+    if (count < *max_count) {
+	/* 11025 Hz mono */
+	codecs[count].type = PJMEDIA_TYPE_AUDIO;
+	codecs[count].pt = PJMEDIA_RTP_PT_L16_11KHZ_MONO;
+	codecs[count].encoding_name = STR_L16;
+	codecs[count].clock_rate = 11025;
+	codecs[count].channel_cnt = 1;
+	++count;
+    }
+
+    if (count < *max_count) {
+	/* 11025 Hz stereo */
+	codecs[count].type = PJMEDIA_TYPE_AUDIO;
+	codecs[count].pt = PJMEDIA_RTP_PT_L16_11KHZ_STEREO;
+	codecs[count].encoding_name = STR_L16;
+	codecs[count].clock_rate = 11025;
+	codecs[count].channel_cnt = 2;
+	++count;
+    }
+
+    if (count < *max_count) {
+	/* 16000 Hz mono */
+	codecs[count].type = PJMEDIA_TYPE_AUDIO;
+	codecs[count].pt = PJMEDIA_RTP_PT_L16_16KHZ_MONO;
+	codecs[count].encoding_name = STR_L16;
+	codecs[count].clock_rate = 16000;
+	codecs[count].channel_cnt = 1;
+	++count;
+    }
+
+
+    if (count < *max_count) {
+	/* 16000 Hz stereo */
+	codecs[count].type = PJMEDIA_TYPE_AUDIO;
+	codecs[count].pt = PJMEDIA_RTP_PT_L16_16KHZ_STEREO;
+	codecs[count].encoding_name = STR_L16;
+	codecs[count].clock_rate = 16000;
+	codecs[count].channel_cnt = 2;
+	++count;
+    }
+
+    if (count < *max_count) {
+	/* 22050 Hz mono */
+	codecs[count].type = PJMEDIA_TYPE_AUDIO;
+	codecs[count].pt = PJMEDIA_RTP_PT_L16_22KHZ_MONO;
+	codecs[count].encoding_name = STR_L16;
+	codecs[count].clock_rate = 22050;
+	codecs[count].channel_cnt = 1;
+	++count;
+    }
+
+
+    if (count < *max_count) {
+	/* 22050 Hz stereo */
+	codecs[count].type = PJMEDIA_TYPE_AUDIO;
+	codecs[count].pt = PJMEDIA_RTP_PT_L16_22KHZ_STEREO;
+	codecs[count].encoding_name = STR_L16;
+	codecs[count].clock_rate = 22050;
+	codecs[count].channel_cnt = 2;
+	++count;
+    }
+
+    if (count < *max_count) {
+	/* 32000 Hz mono */
+	codecs[count].type = PJMEDIA_TYPE_AUDIO;
+	codecs[count].pt = PJMEDIA_RTP_PT_L16_32KHZ_MONO;
+	codecs[count].encoding_name = STR_L16;
+	codecs[count].clock_rate = 32000;
+	codecs[count].channel_cnt = 1;
+	++count;
+    }
+
+    if (count < *max_count) {
+	/* 32000 Hz stereo */
+	codecs[count].type = PJMEDIA_TYPE_AUDIO;
+	codecs[count].pt = PJMEDIA_RTP_PT_L16_32KHZ_STEREO;
+	codecs[count].encoding_name = STR_L16;
+	codecs[count].clock_rate = 32000;
+	codecs[count].channel_cnt = 2;
+	++count;
+    }
+
+    if (count < *max_count) {
+	/* 48KHz mono */
+	codecs[count].type = PJMEDIA_TYPE_AUDIO;
+	codecs[count].pt = PJMEDIA_RTP_PT_L16_48KHZ_MONO;
+	codecs[count].encoding_name = STR_L16;
+	codecs[count].clock_rate = 48000;
+	codecs[count].channel_cnt = 1;
+	++count;
+    }
+
+    if (count < *max_count) {
+	/* 48KHz stereo */
+	codecs[count].type = PJMEDIA_TYPE_AUDIO;
+	codecs[count].pt = PJMEDIA_RTP_PT_L16_48KHZ_MONO;
+	codecs[count].encoding_name = STR_L16;
+	codecs[count].clock_rate = 48000;
+	codecs[count].channel_cnt = 2;
+	++count;
+    }
+
+
+    *max_count = count;
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t l16_alloc_codec( pjmedia_codec_factory *factory, 
+				    const pjmedia_codec_info *id,
+				    pjmedia_codec **p_codec)
+{
+    pjmedia_codec *codec = NULL;
+    struct l16_data *data;
+    unsigned ptime;
+
+    PJ_ASSERT_RETURN(factory==&l16_factory.base, PJ_EINVAL);
+
+    /* Lock mutex. */
+    pj_mutex_lock(l16_factory.mutex);
+
+    /* Allocate new codec if no more is available */
+    if (pj_list_empty(&l16_factory.codec_list)) {
+
+	codec = pj_pool_alloc(l16_factory.pool, sizeof(pjmedia_codec));
+	codec->codec_data = pj_pool_alloc(l16_factory.pool, 
+					  sizeof(struct l16_data));
+	codec->factory = factory;
+	codec->op = &l16_op;
+
+    } else {
+	codec = l16_factory.codec_list.next;
+	pj_list_erase(codec);
+    }
+
+    /* Init private data */
+    ptime = GET_PTIME(id->clock_rate);
+    data = codec->codec_data;
+    data->frame_size = ptime * id->clock_rate * id->channel_cnt * 2 / 1000;
+
+    /* Zero the list, for error detection in l16_dealloc_codec */
+    codec->next = codec->prev = NULL;
+
+    *p_codec = codec;
+
+    /* Unlock mutex. */
+    pj_mutex_unlock(l16_factory.mutex);
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t l16_dealloc_codec(pjmedia_codec_factory *factory, 
+				     pjmedia_codec *codec )
+{
+    
+    PJ_ASSERT_RETURN(factory==&l16_factory.base, PJ_EINVAL);
+
+    /* Check that this node has not been deallocated before */
+    pj_assert (codec->next==NULL && codec->prev==NULL);
+    if (codec->next!=NULL || codec->prev!=NULL) {
+	return PJ_EINVALIDOP;
+    }
+
+    /* Lock mutex. */
+    pj_mutex_lock(l16_factory.mutex);
+
+    /* Insert at the back of the list */
+    pj_list_insert_before(&l16_factory.codec_list, codec);
+
+    /* Unlock mutex. */
+    pj_mutex_unlock(l16_factory.mutex);
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t l16_init( pjmedia_codec *codec, pj_pool_t *pool )
+{
+    /* There's nothing to do here really */
+    PJ_UNUSED_ARG(codec);
+    PJ_UNUSED_ARG(pool);
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t l16_open(pjmedia_codec *codec, 
+			    pjmedia_codec_param *attr )
+{
+    /* Nothing to do.. */
+    PJ_UNUSED_ARG(codec);
+    PJ_UNUSED_ARG(attr);
+    return PJ_SUCCESS;
+}
+
+static pj_status_t l16_close( pjmedia_codec *codec )
+{
+    PJ_UNUSED_ARG(codec);
+    /* Nothing to do */
+    return PJ_SUCCESS;
+}
+
+static pj_status_t  l16_get_frames( pjmedia_codec *codec,
+				    void *pkt,
+				    pj_size_t pkt_size,
+				    unsigned *frame_cnt,
+				    pjmedia_frame frames[])
+{
+    unsigned count = 0;
+    struct l16_data *data = (struct l16_data*) codec->codec_data;
+
+    PJ_UNUSED_ARG(codec);
+    PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL);
+
+    while (pkt_size >= data->frame_size && count < *frame_cnt) {
+	frames[0].type = PJMEDIA_FRAME_TYPE_AUDIO;
+	frames[0].buf = pkt;
+	frames[0].size = data->frame_size;
+
+	pkt = ((char*)pkt) + data->frame_size;
+	pkt_size -= data->frame_size;
+
+	++count;
+    }
+
+    *frame_cnt = count;
+    return PJ_SUCCESS;
+}
+
+static pj_status_t l16_encode(pjmedia_codec *codec, 
+			      const struct pjmedia_frame *input,
+			      unsigned output_buf_len, 
+			      struct pjmedia_frame *output)
+{
+    const pj_int16_t *samp = (const pj_int16_t*) input->buf;
+    const pj_int16_t *samp_end = samp + input->size/sizeof(pj_int16_t);
+    pj_int16_t *samp_out = (pj_int16_t*) output->buf;    
+
+
+    PJ_UNUSED_ARG(codec);
+
+
+    /* Check output buffer length */
+    if (output_buf_len < input->size)
+	return PJMEDIA_CODEC_EFRMTOOSHORT;
+
+
+    /* Encode */
+#if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN!=0
+    while (samp!=samp_end)
+	*samp_out++ = pj_htons(*samp++);
+#endif
+
+
+    /* Done */
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+    output->size = input->size;
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t l16_decode(pjmedia_codec *codec, 
+			      const struct pjmedia_frame *input,
+			      unsigned output_buf_len, 
+			      struct pjmedia_frame *output)
+{
+    const pj_int16_t *samp = (const pj_int16_t*) input->buf;
+    const pj_int16_t *samp_end = samp + input->size/sizeof(pj_int16_t);
+    pj_int16_t *samp_out = (pj_int16_t*) output->buf;    
+
+
+    PJ_UNUSED_ARG(codec);
+
+
+    /* Check output buffer length */
+    if (output_buf_len < input->size)
+	return PJMEDIA_CODEC_EPCMTOOSHORT;
+
+
+    /* Decode */
+#if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN!=0
+    while (samp!=samp_end)
+	*samp_out++ = pj_htons(*samp++);
+#endif
+
+
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+    output->size = input->size;
+
+    return PJ_SUCCESS;
+}
+
+
+#endif	/* PJMEDIA_HAS_L16_CODEC */
+
+
Index: pjproject/trunk/pjmedia/src/pjmedia-codec/speex_codec.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia-codec/speex_codec.c	(revision 410)
+++ pjproject/trunk/pjmedia/src/pjmedia-codec/speex_codec.c	(revision 411)
@@ -57,6 +57,4 @@
 
 /* Prototypes for Speex implementation. */
-static pj_status_t  spx_codec_default_attr(pjmedia_codec *codec, 
-					   pjmedia_codec_param *attr);
 static pj_status_t  spx_codec_init( pjmedia_codec *codec, 
 				    pj_pool_t *pool );
@@ -81,5 +79,4 @@
 static pjmedia_codec_op spx_op = 
 {
-    &spx_codec_default_attr,
     &spx_codec_init,
     &spx_codec_open,
@@ -231,5 +228,5 @@
     spx_factory.speex_param[PARAM_NB].enabled = 
 	((options & PJMEDIA_SPEEX_NO_NB) == 0);
-    spx_factory.speex_param[PARAM_NB].pt = 102;
+    spx_factory.speex_param[PARAM_NB].pt = PJMEDIA_RTP_PT_SPEEX_NB;
     spx_factory.speex_param[PARAM_NB].mode = &speex_nb_mode;
     spx_factory.speex_param[PARAM_NB].clock_rate = 8000;
@@ -239,5 +236,5 @@
     spx_factory.speex_param[PARAM_WB].enabled = 
 	((options & PJMEDIA_SPEEX_NO_WB) == 0);
-    spx_factory.speex_param[PARAM_WB].pt = 103;
+    spx_factory.speex_param[PARAM_WB].pt = PJMEDIA_RTP_PT_SPEEX_WB;
     spx_factory.speex_param[PARAM_WB].mode = &speex_wb_mode;
     spx_factory.speex_param[PARAM_WB].clock_rate = 16000;
@@ -247,5 +244,5 @@
     spx_factory.speex_param[PARAM_UWB].enabled = 
 	((options & PJMEDIA_SPEEX_NO_UWB) == 0);
-    spx_factory.speex_param[PARAM_UWB].pt = 104;
+    spx_factory.speex_param[PARAM_UWB].pt = PJMEDIA_RTP_PT_SPEEX_UWB;
     spx_factory.speex_param[PARAM_UWB].mode = &speex_uwb_mode;
     spx_factory.speex_param[PARAM_UWB].clock_rate = 32000;
@@ -359,5 +356,5 @@
     /* Check clock-rate */
     for (i=0; i<PJ_ARRAY_SIZE(spx_factory.speex_param); ++i) {
-	if (info->sample_rate == spx_factory.speex_param[i].clock_rate) {
+	if (info->clock_rate == spx_factory.speex_param[i].clock_rate) {
 	    /* Okay, let's Speex! */
 	    return PJ_SUCCESS;
@@ -382,16 +379,17 @@
     pj_memset(attr, 0, sizeof(pjmedia_codec_param));
     attr->pt = id->pt;
-
-    if (id->sample_rate <= 8000) {
-	attr->sample_rate = spx_factory.speex_param[PARAM_NB].clock_rate;
+    attr->channel_cnt = 1;
+
+    if (id->clock_rate <= 8000) {
+	attr->clock_rate = spx_factory.speex_param[PARAM_NB].clock_rate;
 	attr->avg_bps = spx_factory.speex_param[PARAM_NB].bitrate;
 
-    } else if (id->sample_rate <= 16000) {
-	attr->sample_rate = spx_factory.speex_param[PARAM_WB].clock_rate;
+    } else if (id->clock_rate <= 16000) {
+	attr->clock_rate = spx_factory.speex_param[PARAM_WB].clock_rate;
 	attr->avg_bps = spx_factory.speex_param[PARAM_WB].bitrate;
 
     } else {
 	/* Wow.. somebody is doing ultra-wideband. Cool...! */
-	attr->sample_rate = spx_factory.speex_param[PARAM_UWB].clock_rate;
+	attr->clock_rate = spx_factory.speex_param[PARAM_UWB].clock_rate;
 	attr->avg_bps = spx_factory.speex_param[PARAM_UWB].bitrate;
     }
@@ -402,12 +400,12 @@
 
     /* Default flags. */
-    attr->cng_enabled = 1;
-    attr->concl_enabled = 1;
-    attr->hpf_enabled = 1;
-    attr->lpf_enabled =1 ;
-    attr->penh_enabled =1 ;
+    attr->cng = 1;
+    attr->concl = 1;
+    attr->hpf = 1;
+    attr->lpf =1 ;
+    attr->penh =1 ;
 
     /* Default, set VAD off as it caused voice chip off */
-    attr->vad_enabled = 0;
+    attr->vad = 0;
 
     return PJ_SUCCESS;
@@ -443,5 +441,6 @@
 	codecs[*count].pt = spx_factory.speex_param[i].pt;
 	codecs[*count].type = PJMEDIA_TYPE_AUDIO;
-	codecs[*count].sample_rate = spx_factory.speex_param[i].clock_rate;
+	codecs[*count].clock_rate = spx_factory.speex_param[i].clock_rate;
+	codecs[*count].channel_cnt = 1;
 
 	++*count;
@@ -487,7 +486,7 @@
     spx->dec = NULL;
 
-    if (id->sample_rate <= 8000)
+    if (id->clock_rate <= 8000)
 	spx->param_id = PARAM_NB;
-    else if (id->sample_rate <= 16000)
+    else if (id->clock_rate <= 16000)
 	spx->param_id = PARAM_WB;
     else
@@ -521,23 +520,4 @@
 
     return PJ_SUCCESS;
-}
-
-/*
- * Get codec default attributes.
- */
-static pj_status_t spx_codec_default_attr( pjmedia_codec *codec, 
-					   pjmedia_codec_param *attr)
-{
-    struct spx_private *spx;
-    pjmedia_codec_info info;
-
-    spx = (struct spx_private*) codec->codec_data;
-
-    info.encoding_name = pj_str("speex");
-    info.pt = 200;  /* Don't care */
-    info.sample_rate = spx_factory.speex_param[spx->param_id].clock_rate;
-    info.type = PJMEDIA_TYPE_AUDIO;
-
-    return spx_default_attr( codec->factory, &info, attr);
 }
 
@@ -580,10 +560,10 @@
 
     /* Sampling rate. */
-    tmp = attr->sample_rate;
+    tmp = attr->clock_rate;
     speex_encoder_ctl(spx->enc, SPEEX_SET_SAMPLING_RATE, 
 		      &spx_factory.speex_param[id].clock_rate);
 
     /* VAD */
-    tmp = attr->vad_enabled;
+    tmp = attr->vad;
     speex_encoder_ctl(spx->enc, SPEEX_SET_VAD, &tmp);
 
@@ -609,5 +589,5 @@
 
     /* PENH */
-    tmp = attr->penh_enabled;
+    tmp = attr->penh;
     speex_decoder_ctl(spx->dec, SPEEX_SET_ENH, &tmp);
 
Index: pjproject/trunk/pjmedia/src/pjmedia/codec.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/codec.c	(revision 410)
+++ pjproject/trunk/pjmedia/src/pjmedia/codec.c	(revision 411)
@@ -19,34 +19,18 @@
 #include <pjmedia/codec.h>
 #include <pjmedia/errno.h>
+#include <pj/array.h>
+#include <pj/assert.h>
+#include <pj/log.h>
 #include <pj/pool.h>
 #include <pj/string.h>
-#include <pj/assert.h>
-#include <pj/log.h>
 
 #define THIS_FILE   "codec.c"
 
-/*
- * Reinitialize array of supported codecs.
- */
-static void enum_all_codecs (pjmedia_codec_mgr *mgr)
-{
-    pjmedia_codec_factory *factory;
-
-    mgr->codec_cnt = 0;
-
-    factory = mgr->factory_list.next;
-    while (factory != &mgr->factory_list) {
-	unsigned count;
-	pj_status_t status;
-
-	count = PJ_ARRAY_SIZE(mgr->codecs) - mgr->codec_cnt;
-	status = factory->op->enum_info(factory, &count, 
-					mgr->codecs+mgr->codec_cnt);
-	if (status == PJ_SUCCESS)
-	    mgr->codec_cnt += count;
-
-	factory = factory->next;
-    }
-}
+
+
+
+/* Sort codecs in codec manager based on priorities */
+static void sort_codecs(pjmedia_codec_mgr *mgr);
+
 
 /*
@@ -62,4 +46,5 @@
     return PJ_SUCCESS;
 }
+
 
 /*
@@ -70,11 +55,46 @@
 				    pjmedia_codec_factory *factory)
 {
+    pjmedia_codec_info info[PJMEDIA_CODEC_MGR_MAX_CODECS];
+    unsigned i, count;
+    pj_status_t status;
+
     PJ_ASSERT_RETURN(mgr && factory, PJ_EINVAL);
 
+    /* Enum codecs */
+    count = PJ_ARRAY_SIZE(info);
+    status = factory->op->enum_info(factory, &count, info);
+    if (status != PJ_SUCCESS)
+	return status;
+    
+
+    /* Check codec count */
+    if (count + mgr->codec_cnt > PJ_ARRAY_SIZE(mgr->codec_desc))
+	return PJ_ETOOMANY;
+
+
+    /* Save the codecs */
+    for (i=0; i<count; ++i) {
+	pj_memcpy( &mgr->codec_desc[mgr->codec_cnt+i],
+		   &info[i], sizeof(pjmedia_codec_info));
+	mgr->codec_desc[mgr->codec_cnt+i].prio = PJMEDIA_CODEC_PRIO_NORMAL;
+	mgr->codec_desc[mgr->codec_cnt+i].factory = factory;
+	pjmedia_codec_info_to_id( &info[i],
+				  mgr->codec_desc[mgr->codec_cnt+i].id,
+				  sizeof(pjmedia_codec_id));
+    }
+
+    /* Update count */
+    mgr->codec_cnt += count;
+
+    /* Re-sort codec based on priorities */
+    sort_codecs(mgr);
+
+    /* Add factory to the list */
     pj_list_push_back(&mgr->factory_list, factory);
-    enum_all_codecs (mgr);
+
 
     return PJ_SUCCESS;
 }
+
 
 /*
@@ -85,5 +105,5 @@
 				     pjmedia_codec_factory *factory)
 {
-
+    unsigned i;
     PJ_ASSERT_RETURN(mgr && factory, PJ_EINVAL);
 
@@ -92,10 +112,28 @@
 		     PJ_ENOTFOUND);
 
-
+    /* Erase factory from the factory list */
     pj_list_erase(factory);
-    enum_all_codecs (mgr);
+
+
+    /* Remove all supported codecs from the codec manager that were created 
+     * by the specified factory.
+     */
+    for (i=0; i<mgr->codec_cnt; ) {
+
+	if (mgr->codec_desc[i].factory == factory) {
+
+	    pj_array_erase(mgr->codec_desc, sizeof(mgr->codec_desc[0]), 
+			   mgr->codec_cnt, i);
+	    --mgr->codec_cnt;
+
+	} else {
+	    ++i;
+	}
+    }
+
 
     return PJ_SUCCESS;
 }
+
 
 /*
@@ -105,6 +143,9 @@
 pjmedia_codec_mgr_enum_codecs(pjmedia_codec_mgr *mgr, 
 			      unsigned *count, 
-			      pjmedia_codec_info codecs[])
-{
+			      pjmedia_codec_info codecs[],
+			      unsigned *prio)
+{
+    unsigned i;
+
     PJ_ASSERT_RETURN(mgr && count && codecs, PJ_EINVAL);
 
@@ -112,23 +153,34 @@
 	*count = mgr->codec_cnt;
     
-    pj_memcpy(codecs, mgr->codecs, *count * sizeof(pjmedia_codec_info));
+    for (i=0; i<*count; ++i) {
+	pj_memcpy(&codecs[i], 
+		  &mgr->codec_desc[i].info, 
+		  sizeof(pjmedia_codec_info));
+    }
+
+    if (prio) {
+	for (i=0; i < *count; ++i)
+	    prio[i] = mgr->codec_desc[i].prio;
+    }
 
     return PJ_SUCCESS;
 }
 
+
 /*
  * Get codec info for static payload type.
  */
-PJ_DEF(pj_status_t) pjmedia_codec_mgr_get_codec_info(pjmedia_codec_mgr *mgr,
-						     unsigned pt,
-						     pjmedia_codec_info *inf)
+PJ_DEF(pj_status_t) 
+pjmedia_codec_mgr_get_codec_info( pjmedia_codec_mgr *mgr,
+				  unsigned pt,
+				  const pjmedia_codec_info **p_info)
 {
     unsigned i;
 
-    PJ_ASSERT_RETURN(mgr && inf && pt>=0 && pt < 96, PJ_EINVAL);
+    PJ_ASSERT_RETURN(mgr && p_info && pt>=0 && pt < 96, PJ_EINVAL);
 
     for (i=0; i<mgr->codec_cnt; ++i) {
-	if (mgr->codecs[i].pt == pt) {
-	    pj_memcpy(inf, &mgr->codecs[i], sizeof(pjmedia_codec_info));
+	if (mgr->codec_desc[i].info.pt == pt) {
+	    *p_info = &mgr->codec_desc[i].info;
 	    return PJ_SUCCESS;
 	}
@@ -137,4 +189,153 @@
     return PJMEDIA_CODEC_EUNSUP;
 }
+
+
+/*
+ * Convert codec info struct into a unique codec identifier.
+ * A codec identifier looks something like "L16/44100/2".
+ */
+PJ_DEF(char*) pjmedia_codec_info_to_id( const pjmedia_codec_info *info,
+				        char *id, unsigned max_len )
+{
+    int len;
+
+    PJ_ASSERT_RETURN(info && id && max_len, NULL);
+
+    len = pj_ansi_snprintf(id, max_len, "%.*s/%u/%u", 
+			   (int)info->encoding_name.slen,
+			   info->encoding_name.ptr,
+			   info->clock_rate,
+			   info->channel_cnt);
+
+    if (len < 1 || len >= (int)max_len) {
+	id[0] = '\0';
+	return NULL;
+    }
+
+    return id;
+}
+
+
+/*
+ * Find codecs by the unique codec identifier. This function will find
+ * all codecs that match the codec identifier prefix. For example, if
+ * "L16" is specified, then it will find "L16/8000/1", "L16/16000/1",
+ * and so on, up to the maximum count specified in the argument.
+ */
+PJ_DEF(pj_status_t) 
+pjmedia_codec_mgr_find_codecs_by_id( pjmedia_codec_mgr *mgr,
+				     const pj_str_t *codec_id,
+				     unsigned *count,
+				     const pjmedia_codec_info *p_info[],
+				     unsigned prio[])
+{
+    unsigned i, found = 0;
+
+    PJ_ASSERT_RETURN(mgr && codec_id && count && *count, PJ_EINVAL);
+
+    for (i=0; i<mgr->codec_cnt; ++i) {
+
+	if (pj_strnicmp2(codec_id, mgr->codec_desc[i].id, 
+			 codec_id->slen) == 0) 
+	{
+
+	    if (p_info)
+		p_info[found] = &mgr->codec_desc[i].info;
+	    if (prio)
+		prio[found] = mgr->codec_desc[i].prio;
+
+	    ++found;
+
+	    if (found >= *count)
+		break;
+	}
+
+    }
+
+    *count = found;
+
+    return found ? PJ_SUCCESS : PJ_ENOTFOUND;
+}
+
+
+/* Swap two codecs positions in codec manager */
+static void swap_codec(pjmedia_codec_mgr *mgr, unsigned i, unsigned j)
+{
+    struct pjmedia_codec_desc tmp;
+
+    pj_memcpy(&tmp, &mgr->codec_desc[i], sizeof(struct pjmedia_codec_desc));
+
+    pj_memcpy(&mgr->codec_desc[i], &mgr->codec_desc[j], 
+	       sizeof(struct pjmedia_codec_desc));
+
+    pj_memcpy(&mgr->codec_desc[j], &tmp, sizeof(struct pjmedia_codec_desc));
+}
+
+
+/* Sort codecs in codec manager based on priorities */
+static void sort_codecs(pjmedia_codec_mgr *mgr)
+{
+    unsigned i;
+
+   /* Re-sort */
+    for (i=0; i<mgr->codec_cnt; ++i) {
+	unsigned j, max;
+
+	for (max=i, j=i+1; j<mgr->codec_cnt; ++j) {
+	    if (mgr->codec_desc[j].prio > mgr->codec_desc[max].prio)
+		max = j;
+	}
+
+	if (max != i)
+	    swap_codec(mgr, i, max);
+    }
+
+    /* Change PJMEDIA_CODEC_PRIO_HIGHEST codecs to NEXT_HIGHER */
+    for (i=0; i<mgr->codec_cnt; ++i) {
+	if (mgr->codec_desc[i].prio == PJMEDIA_CODEC_PRIO_HIGHEST)
+	    mgr->codec_desc[i].prio = PJMEDIA_CODEC_PRIO_NEXT_HIGHER;
+	else
+	    break;
+    }
+}
+
+
+/**
+ * Set codec priority. The codec priority determines the order of
+ * the codec in the SDP created by the endpoint. If more than one codecs
+ * are found with the same codec_id prefix, then the function sets the
+ * priorities of all those codecs.
+ */
+PJ_DEF(pj_status_t)
+pjmedia_codec_mgr_set_codec_priority(pjmedia_codec_mgr *mgr, 
+				     const pj_str_t *codec_id,
+				     pjmedia_codec_priority prio)
+{
+    unsigned i, found = 0;
+
+    PJ_ASSERT_RETURN(mgr && codec_id, PJ_EINVAL);
+
+    /* Update the priorities of affected codecs */
+    for (i=0; i<mgr->codec_cnt; ++i) 
+    {
+	if (codec_id->slen == 0 ||
+	    pj_strnicmp2(codec_id, mgr->codec_desc[i].id, 
+			 codec_id->slen) == 0) 
+	{
+	    mgr->codec_desc[i].prio = prio;
+	    ++found;
+	}
+    }
+
+    if (!found)
+	return PJ_ENOTFOUND;
+
+    /* Re-sort codecs */
+    sort_codecs(mgr);
+ 
+
+    return PJ_SUCCESS;
+}
+
 
 /*
Index: pjproject/trunk/pjmedia/src/pjmedia/conference.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/conference.c	(revision 410)
+++ pjproject/trunk/pjmedia/src/pjmedia/conference.c	(revision 411)
@@ -238,5 +238,5 @@
     if (port) {
 	conf_port->port = port;
-	conf_port->clock_rate = port->info.sample_rate;
+	conf_port->clock_rate = port->info.clock_rate;
 	conf_port->samples_per_frame = port->info.samples_per_frame;
     } else {
@@ -443,5 +443,5 @@
     conf->master_port->info.need_info = 0;
     conf->master_port->info.pt = 0xFF;
-    conf->master_port->info.sample_rate = clock_rate;
+    conf->master_port->info.clock_rate = clock_rate;
     conf->master_port->info.samples_per_frame = samples_per_frame;
     conf->master_port->info.signature = 0;
@@ -1091,4 +1091,5 @@
     pj_int16_t *buf;
     unsigned j;
+    pj_status_t status;
 
     /* If port is muted or nobody is transmitting to this port, 
@@ -1181,5 +1182,5 @@
     }
 
-    /* If port has the same clock_date and samples_per_frame settings as
+    /* If port has the same clock_rate and samples_per_frame settings as
      * the conference bridge, transmit the frame as is.
      */
@@ -1225,9 +1226,10 @@
     }
 
-    /* Transmit once we have enough frame in the tx_buf. */
-    if (cport->tx_buf_count >= cport->samples_per_frame) {
+    /* Transmit while we have enough frame in the tx_buf. */
+    status = PJ_SUCCESS;
+    while (cport->tx_buf_count >= cport->samples_per_frame &&
+	   status == PJ_SUCCESS) 
+    {
 	
-	pj_status_t status;
-
 	TRACE_((THIS_FILE, "write_port %.*s: count=%d", 
 			   (int)cport->name.slen, cport->name.ptr,
@@ -1260,9 +1262,7 @@
 	TRACE_((THIS_FILE, " tx_buf count now is %d", 
 			   cport->tx_buf_count));
-
-	return status;
-    }
-
-    return PJ_SUCCESS;
+    }
+
+    return status;
 }
 
Index: pjproject/trunk/pjmedia/src/pjmedia/endpoint.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/endpoint.c	(revision 410)
+++ pjproject/trunk/pjmedia/src/pjmedia/endpoint.c	(revision 411)
@@ -330,8 +330,15 @@
     for (i=0; i<endpt->codec_mgr.codec_cnt; ++i) {
 
-	pjmedia_codec_info *codec_info = &endpt->codec_mgr.codecs[i];
+	pjmedia_codec_info *codec_info;
 	pjmedia_sdp_rtpmap rtpmap;
+	char tmp_param[3];
 	pjmedia_sdp_attr *attr;
-	pj_str_t *fmt = &m->desc.fmt[m->desc.fmt_count++];
+	pj_str_t *fmt;
+
+	if (endpt->codec_mgr.codec_desc[i].prio == PJMEDIA_CODEC_PRIO_DISABLED)
+	    break;
+
+	codec_info = &endpt->codec_mgr.codec_desc[i].info;
+	fmt = &m->desc.fmt[m->desc.fmt_count++];
 
 	fmt->ptr = pj_pool_alloc(pool, 8);
@@ -339,7 +346,25 @@
 
 	rtpmap.pt = *fmt;
-	rtpmap.clock_rate = codec_info->sample_rate;
+	rtpmap.clock_rate = codec_info->clock_rate;
 	rtpmap.enc_name = codec_info->encoding_name;
-	rtpmap.param.slen = 0;
+	
+	/* For audio codecs, rtpmap parameters denotes the number
+	 * of channels, which can be omited if the value is 1.
+	 */
+	if (codec_info->type == PJMEDIA_TYPE_AUDIO &&
+	    codec_info->channel_cnt > 1)
+	{
+	    /* Can only support one digit channel count */
+	    pj_assert(codec_info->channel_cnt < 10);
+
+	    tmp_param[0] = '/';
+	    tmp_param[1] = (char)('0' + codec_info->channel_cnt);
+
+	    rtpmap.param.ptr = tmp_param;
+	    rtpmap.param.slen = 2;
+
+	} else {
+	    rtpmap.param.slen = 0;
+	}
 
 	pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr);
@@ -389,5 +414,5 @@
     count = PJ_ARRAY_SIZE(codec_info);
     if (pjmedia_codec_mgr_enum_codecs(&endpt->codec_mgr, 
-				      &count, codec_info) != PJ_SUCCESS)
+				      &count, codec_info, NULL) != PJ_SUCCESS)
     {
 	PJ_LOG(3,(THIS_FILE, " -error: failed to enum codecs"));
@@ -417,12 +442,13 @@
 
 	PJ_LOG(3,(THIS_FILE, 
-		  "   %s codec #%2d: pt=%d (%.*s @%dKHz, %d bps, ptime=%d ms, vad=%d, cng=%d)", 
+		  "   %s codec #%2d: pt=%d (%.*s @%dKHz/%d, %d bps, ptime=%d ms, vad=%d, cng=%d)", 
 		  type, i, codec_info[i].pt,
 		  (int)codec_info[i].encoding_name.slen,
 		  codec_info[i].encoding_name.ptr,
-		  codec_info[i].sample_rate/1000,
+		  codec_info[i].clock_rate/1000,
+		  codec_info[i].channel_cnt,
 		  param.avg_bps, param.ptime,
-		  param.vad_enabled,
-		  param.cng_enabled));
+		  param.vad,
+		  param.cng));
     }
 #endif
Index: pjproject/trunk/pjmedia/src/pjmedia/file_player.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/file_player.c	(revision 410)
+++ 	(revision )
@@ -1,396 +1,0 @@
-/* $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/file_port.h>
-#include <pjmedia/errno.h>
-#include <pjmedia/wave.h>
-#include <pj/assert.h>
-#include <pj/file_access.h>
-#include <pj/file_io.h>
-#include <pj/log.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-
-
-#define THIS_FILE   "file_port.c"
-
-
-#define SIGNATURE	    ('F'<<24|'P'<<16|'L'<<8|'Y')
-#define BYTES_PER_SAMPLE    2
-
-
-#if 1
-#   define TRACE_(x)	PJ_LOG(4,x)
-#else
-#   define TRACE_(x)
-#endif
-
-#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
-    static void samples_to_host(pj_int16_t *samples, unsigned count)
-    {
-	unsigned i;
-	for (i=0; i<count; ++i) {
-	    samples[i] = pj_swap16(samples[i]);
-	}
-    }
-#else
-#   define samples_to_host(samples,count)
-#endif
-
-struct file_port
-{
-    pjmedia_port     base;
-    pj_size_t	     bufsize;
-    char	    *buf;
-    char	    *readpos;
-
-    pj_off_t	     fsize;
-    pj_off_t	     fpos;
-    pj_oshandle_t    fd;
-
-};
-
-
-static pj_status_t file_put_frame(pjmedia_port *this_port, 
-				  const pjmedia_frame *frame);
-static pj_status_t file_get_frame(pjmedia_port *this_port, 
-				  pjmedia_frame *frame);
-static pj_status_t file_on_destroy(pjmedia_port *this_port);
-
-static struct file_port *create_file_port(pj_pool_t *pool)
-{
-    struct file_port *port;
-
-    port = pj_pool_zalloc(pool, sizeof(struct file_port));
-    if (!port)
-	return NULL;
-
-    port->base.info.name = pj_str("file");
-    port->base.info.signature = SIGNATURE;
-    port->base.info.type = PJMEDIA_TYPE_AUDIO;
-    port->base.info.has_info = PJ_TRUE;
-    port->base.info.need_info = PJ_FALSE;
-    port->base.info.pt = 0xFF;
-    port->base.info.encoding_name = pj_str("pcm");
-
-    port->base.put_frame = &file_put_frame;
-    port->base.get_frame = &file_get_frame;
-    port->base.on_destroy = &file_on_destroy;
-
-
-    /* Put in default values.
-     * These will be overriden once the file is read.
-     */
-    port->base.info.sample_rate = 8000;
-    port->base.info.bits_per_sample = 16;
-    port->base.info.samples_per_frame = 160;
-    port->base.info.bytes_per_frame = 320;
-
-    return port;
-}
-
-/*
- * Fill buffer.
- */
-static pj_status_t fill_buffer(struct file_port *fport)
-{
-    pj_ssize_t size_left = fport->bufsize;
-    unsigned size_to_read;
-    pj_ssize_t size;
-    pj_status_t status;
-
-    while (size_left > 0) {
-
-	/* Calculate how many bytes to read in this run. */
-	size = size_to_read = size_left;
-	status = pj_file_read(fport->fd, 
-			      &fport->buf[fport->bufsize-size_left], 
-			      &size);
-	if (status != PJ_SUCCESS)
-	    return status;
-	if (size < 0) {
-	    /* Should return more appropriate error code here.. */
-	    return PJ_ECANCELLED;
-	}
-
-	size_left -= size;
-	fport->fpos += size;
-
-	/* If size is less than size_to_read, it indicates that we've
-	 * encountered EOF. Rewind the file.
-	 */
-	if (size < (pj_ssize_t)size_to_read) {
-	    PJ_LOG(5,(THIS_FILE, "File port %.*s EOF, rewinding..",
-		      (int)fport->base.info.name.slen,
-		      fport->base.info.name.ptr));
-	    fport->fpos = sizeof(struct pjmedia_wave_hdr);
-	    pj_file_setpos( fport->fd, fport->fpos, PJ_SEEK_SET);
-	}
-    }
-
-    /* Convert samples to host rep */
-    samples_to_host((pj_int16_t*)fport->buf, fport->bufsize/BYTES_PER_SAMPLE);
-
-    return PJ_SUCCESS;
-}
-
-
-/*
- * Create WAVE player port.
- */
-PJ_DEF(pj_status_t) pjmedia_file_player_port_create( pj_pool_t *pool,
-						     const char *filename,
-						     unsigned flags,
-						     pj_ssize_t buff_size,
-						     void *user_data,
-						     pjmedia_port **p_port )
-{
-    pjmedia_wave_hdr wave_hdr;
-    pj_ssize_t size_read;
-    struct file_port *fport;
-    pj_status_t status;
-
-
-    PJ_UNUSED_ARG(flags);
-
-    /* Check arguments. */
-    PJ_ASSERT_RETURN(pool && filename && p_port, PJ_EINVAL);
-
-    /* Check the file really exists. */
-    if (!pj_file_exists(filename)) {
-	return PJ_ENOTFOUND;
-    }
-
-    /* Create fport instance. */
-    fport = create_file_port(pool);
-    if (!fport) {
-	return PJ_ENOMEM;
-    }
-
-
-    /* Get the file size. */
-    fport->fsize = pj_file_size(filename);
-
-    /* Size must be more than WAVE header size */
-    if (fport->fsize <= sizeof(pjmedia_wave_hdr)) {
-	return PJMEDIA_ENOTVALIDWAVE;
-    }
-
-    /* Open file. */
-    status = pj_file_open( pool, filename, PJ_O_RDONLY, &fport->fd);
-    if (status != PJ_SUCCESS)
-	return status;
-
-    /* Read the WAVE header. */
-    size_read = sizeof(wave_hdr);
-    status = pj_file_read( fport->fd, &wave_hdr, &size_read);
-    if (status != PJ_SUCCESS) {
-	pj_file_close(fport->fd);
-	return status;
-    }
-    if (size_read != sizeof(wave_hdr)) {
-	pj_file_close(fport->fd);
-	return PJMEDIA_ENOTVALIDWAVE;
-    }
-
-    /* Normalize WAVE header fields values from little-endian to host
-     * byte order.
-     */
-    pjmedia_wave_hdr_file_to_host(&wave_hdr);
-    
-    /* Validate WAVE file. */
-    if (wave_hdr.riff_hdr.riff != PJMEDIA_RIFF_TAG ||
-	wave_hdr.riff_hdr.wave != PJMEDIA_WAVE_TAG ||
-	wave_hdr.fmt_hdr.fmt != PJMEDIA_FMT_TAG)
-    {
-	pj_file_close(fport->fd);
-	TRACE_((THIS_FILE, 
-		"actual value|expected riff=%x|%x, wave=%x|%x fmt=%x|%x",
-		wave_hdr.riff_hdr.riff, PJMEDIA_RIFF_TAG,
-		wave_hdr.riff_hdr.wave, PJMEDIA_WAVE_TAG,
-		wave_hdr.fmt_hdr.fmt, PJMEDIA_FMT_TAG));
-	return PJMEDIA_ENOTVALIDWAVE;
-    }
-
-    /* Must be PCM with 16bits per sample */
-    if (wave_hdr.fmt_hdr.fmt_tag != 1 ||
-	wave_hdr.fmt_hdr.bits_per_sample != 16)
-    {
-	pj_file_close(fport->fd);
-	return PJMEDIA_EWAVEUNSUPP;
-    }
-
-    /* Block align must be 2*nchannels */
-    if (wave_hdr.fmt_hdr.block_align != wave_hdr.fmt_hdr.nchan*BYTES_PER_SAMPLE) {
-	pj_file_close(fport->fd);
-	return PJMEDIA_EWAVEUNSUPP;
-    }
-
-    /* Validate length. */
-    if (wave_hdr.data_hdr.len != fport->fsize-sizeof(pjmedia_wave_hdr)) {
-	pj_file_close(fport->fd);
-	return PJMEDIA_EWAVEUNSUPP;
-    }
-    if (wave_hdr.data_hdr.len < 400) {
-	pj_file_close(fport->fd);
-	return PJMEDIA_EWAVETOOSHORT;
-    }
-
-    /* It seems like we have a valid WAVE file. */
-
-    /* Initialize */
-    fport->base.user_data = user_data;
-
-    /* Update port info. */
-    fport->base.info.channel_count = wave_hdr.fmt_hdr.nchan;
-    fport->base.info.sample_rate = wave_hdr.fmt_hdr.sample_rate;
-    fport->base.info.bits_per_sample = wave_hdr.fmt_hdr.bits_per_sample;
-    fport->base.info.samples_per_frame = fport->base.info.sample_rate *
-					 wave_hdr.fmt_hdr.nchan *
-					 20 / 1000;
-    fport->base.info.bytes_per_frame = 
-	fport->base.info.samples_per_frame * 
-	fport->base.info.bits_per_sample / 8;
-
-    pj_strdup2(pool, &fport->base.info.name, filename);
-
-    /* Create file buffer.
-     */
-    if (buff_size < 1) buff_size = PJMEDIA_FILE_PORT_BUFSIZE;
-    fport->bufsize = buff_size;
-
-
-    /* Create buffer. */
-    fport->buf = pj_pool_alloc(pool, fport->bufsize);
-    if (!fport->buf) {
-	pj_file_close(fport->fd);
-	return PJ_ENOMEM;
-    }
-
-    fport->readpos = fport->buf;
-
-    /* Set initial position of the file. */
-    fport->fpos = sizeof(struct pjmedia_wave_hdr);
-
-    /* Fill up the buffer. */
-    status = fill_buffer(fport);
-    if (status != PJ_SUCCESS) {
-	pj_file_close(fport->fd);
-	return status;
-    }
-
-    /* Done. */
-
-    *p_port = &fport->base;
-
-
-    PJ_LOG(4,(THIS_FILE, 
-	      "File player '%.*s' created: samp.rate=%d, ch=%d, bufsize=%uKB, "
-	      "filesize=%luKB",
-	      (int)fport->base.info.name.slen,
-	      fport->base.info.name.ptr,
-	      fport->base.info.sample_rate,
-	      fport->base.info.channel_count,
-	      fport->bufsize / 1000,
-	      (unsigned long)(fport->fsize / 1000)));
-
-    return PJ_SUCCESS;
-}
-
-
-/*
- * Put frame to file.
- */
-static pj_status_t file_put_frame(pjmedia_port *this_port, 
-				  const pjmedia_frame *frame)
-{
-    PJ_UNUSED_ARG(this_port);
-    PJ_UNUSED_ARG(frame);
-    return PJ_EINVALIDOP;
-}
-
-/*
- * Get frame from file.
- */
-static pj_status_t file_get_frame(pjmedia_port *this_port, 
-				  pjmedia_frame *frame)
-{
-    struct file_port *fport = (struct file_port*)this_port;
-    unsigned frame_size;
-    pj_status_t status;
-
-    pj_assert(fport->base.info.signature == SIGNATURE);
-
-    //frame_size = fport->base.info.bytes_per_frame;
-    //pj_assert(frame->size == frame_size);
-    frame_size = frame->size;
-
-    /* Copy frame from buffer. */
-    frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
-    frame->size = frame_size;
-    frame->timestamp.u64 = 0;
-
-    if (fport->readpos + frame_size <= fport->buf + fport->bufsize) {
-
-	/* Read contiguous buffer. */
-	pj_memcpy(frame->buf, fport->readpos, frame_size);
-
-	/* Fill up the buffer if all has been read. */
-	fport->readpos += frame_size;
-	if (fport->readpos == fport->buf + fport->bufsize) {
-	    fport->readpos = fport->buf;
-
-	    status = fill_buffer(fport);
-	    if (status != PJ_SUCCESS)
-		return status;
-	}
-    } else {
-	unsigned endread;
-
-	/* Split read.
-	 * First stage: read until end of buffer. 
-	 */
-	endread = (fport->buf+fport->bufsize) - fport->readpos;
-	pj_memcpy(frame->buf, fport->readpos, endread);
-
-	/* Second stage: fill up buffer, and read from the start of buffer. */
-	status = fill_buffer(fport);
-	if (status != PJ_SUCCESS) {
-	    pj_memset(((char*)frame->buf)+endread, 0, frame_size-endread);
-	    return status;
-	}
-
-	pj_memcpy(((char*)frame->buf)+endread, fport->buf, frame_size-endread);
-	fport->readpos = fport->buf + (frame_size - endread);
-    }
-
-    return PJ_SUCCESS;
-}
-
-/*
- * Destroy port.
- */
-static pj_status_t file_on_destroy(pjmedia_port *this_port)
-{
-    struct file_port *fport = (struct file_port*) this_port;
-
-    pj_assert(this_port->info.signature == SIGNATURE);
-
-    pj_file_close(fport->fd);
-    return PJ_SUCCESS;
-}
Index: pjproject/trunk/pjmedia/src/pjmedia/file_writer.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/file_writer.c	(revision 410)
+++ 	(revision )
@@ -1,306 +1,0 @@
-/* $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/file_port.h>
-#include <pjmedia/errno.h>
-#include <pjmedia/wave.h>
-#include <pj/assert.h>
-#include <pj/file_access.h>
-#include <pj/file_io.h>
-#include <pj/log.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-
-
-#define THIS_FILE	    "file_writer.c"
-#define SIGNATURE	    ('F'<<24|'W'<<16|'R'<<8|'T')
-#define BYTES_PER_SAMPLE    2
-
-
-struct file_port
-{
-    pjmedia_port     base;
-    pj_size_t	     bufsize;
-    char	    *buf;
-    char	    *writepos;
-
-    pj_oshandle_t    fd;
-};
-
-static pj_status_t file_put_frame(pjmedia_port *this_port, 
-				  const pjmedia_frame *frame);
-static pj_status_t file_get_frame(pjmedia_port *this_port, 
-				  pjmedia_frame *frame);
-static pj_status_t file_on_destroy(pjmedia_port *this_port);
-
-
-/*
- * Create file writer port.
- */
-PJ_DEF(pj_status_t) pjmedia_file_writer_port_create( pj_pool_t *pool,
-						     const char *filename,
-						     unsigned sampling_rate,
-						     unsigned channel_count,
-						     unsigned samples_per_frame,
-						     unsigned bits_per_sample,
-						     unsigned flags,
-						     pj_ssize_t buff_size,
-						     void *user_data,
-						     pjmedia_port **p_port )
-{
-    struct file_port *fport;
-    pjmedia_wave_hdr wave_hdr;
-    pj_ssize_t size;
-    pj_status_t status;
-
-    PJ_UNUSED_ARG(flags);
-    PJ_UNUSED_ARG(user_data);
-
-    /* Check arguments. */
-    PJ_ASSERT_RETURN(pool && filename && p_port, PJ_EINVAL);
-
-    /* Only supports 16bits per sample for now.
-     * See flush_buffer().
-     */
-    PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL);
-
-    /* Create file port instance. */
-    fport = pj_pool_zalloc(pool, sizeof(struct file_port));
-    PJ_ASSERT_RETURN(fport != NULL, PJ_ENOMEM);
-
-    /* Initialize port info. */
-    fport->base.info.bits_per_sample = bits_per_sample;
-    fport->base.info.bytes_per_frame = samples_per_frame * bits_per_sample *
-				       channel_count / 8;
-    fport->base.info.channel_count = channel_count;
-    fport->base.info.encoding_name = pj_str("pom");
-    fport->base.info.has_info = 1;
-    pj_strdup2(pool, &fport->base.info.name, filename);
-    fport->base.info.need_info = 0;
-    fport->base.info.pt = 0xFF;
-    fport->base.info.sample_rate = sampling_rate;
-    fport->base.info.samples_per_frame = samples_per_frame;
-    fport->base.info.signature = SIGNATURE;
-    fport->base.info.type = PJMEDIA_TYPE_AUDIO;
-    
-    fport->base.get_frame = &file_get_frame;
-    fport->base.put_frame = &file_put_frame;
-    fport->base.on_destroy = &file_on_destroy;
-
-
-    /* Open file in write and read mode.
-     * We need the read mode because we'll modify the WAVE header once
-     * the recording has completed.
-     */
-    status = pj_file_open(pool, filename, PJ_O_WRONLY, &fport->fd);
-    if (status != PJ_SUCCESS)
-	return status;
-
-    /* Initialize WAVE header */
-    pj_memset(&wave_hdr, 0, sizeof(pjmedia_wave_hdr));
-    wave_hdr.riff_hdr.riff = PJMEDIA_RIFF_TAG;
-    wave_hdr.riff_hdr.file_len = 0; /* will be filled later */
-    wave_hdr.riff_hdr.wave = PJMEDIA_WAVE_TAG;
-
-    wave_hdr.fmt_hdr.fmt = PJMEDIA_FMT_TAG;
-    wave_hdr.fmt_hdr.len = 16;
-    wave_hdr.fmt_hdr.fmt_tag = 1;
-    wave_hdr.fmt_hdr.nchan = (pj_int16_t)channel_count;
-    wave_hdr.fmt_hdr.sample_rate = sampling_rate;
-    wave_hdr.fmt_hdr.bytes_per_sec = sampling_rate * channel_count * 
-				     bits_per_sample / 8;
-    wave_hdr.fmt_hdr.block_align = (pj_int16_t) (channel_count * 
-						 bits_per_sample / 8);
-    wave_hdr.fmt_hdr.bits_per_sample = (pj_int16_t)bits_per_sample;
-
-    wave_hdr.data_hdr.data = PJMEDIA_DATA_TAG;
-    wave_hdr.data_hdr.len = 0;	    /* will be filled later */
-
-
-    /* Convert WAVE header from host byte order to little endian
-     * before writing the header.
-     */
-    pjmedia_wave_hdr_host_to_file(&wave_hdr);
-
-
-    /* Write WAVE header */
-    size = sizeof(pjmedia_wave_hdr);
-    status = pj_file_write(fport->fd, &wave_hdr, &size);
-    if (status != PJ_SUCCESS) {
-	pj_file_close(fport->fd);
-	return status;
-    }
-
-    /* Set buffer size. */
-    if (buff_size < 1) buff_size = PJMEDIA_FILE_PORT_BUFSIZE;
-    fport->bufsize = buff_size;
-
-    /* Check that buffer size is greater than bytes per frame */
-    pj_assert(fport->bufsize >= fport->base.info.bytes_per_frame);
-
-
-    /* Allocate buffer and set initial write position */
-    fport->buf = pj_pool_alloc(pool, fport->bufsize);
-    if (fport->buf == NULL) {
-	pj_file_close(fport->fd);
-	return PJ_ENOMEM;
-    }
-    fport->writepos = fport->buf;
-
-    /* Done. */
-    *p_port = &fport->base;
-
-    PJ_LOG(4,(THIS_FILE, 
-	      "File writer '%.*s' created: samp.rate=%d, bufsize=%uKB",
-	      (int)fport->base.info.name.slen,
-	      fport->base.info.name.ptr,
-	      fport->base.info.sample_rate,
-	      fport->bufsize / 1000));
-
-
-    return PJ_SUCCESS;
-}
-
-
-#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
-    static void swap_samples(pj_int16_t *samples, unsigned count)
-    {
-	unsigned i;
-	for (i=0; i<count; ++i) {
-	    samples[i] = pj_swap16(samples[i]);
-	}
-    }
-#else
-#   define swap_samples(samples,count)
-#endif
-
-/*
- * Flush the contents of the buffer to the file.
- */
-static pj_status_t flush_buffer(struct file_port *fport)
-{
-    pj_ssize_t bytes = fport->writepos - fport->buf;
-    pj_status_t status;
-
-    /* Convert samples to little endian */
-    swap_samples((pj_int16_t*)fport->buf, bytes/BYTES_PER_SAMPLE);
-
-    /* Write to file. */
-    status = pj_file_write(fport->fd, fport->buf, &bytes);
-
-    /* Reset writepos */
-    fport->writepos = fport->buf;
-
-    return status;
-}
-
-/*
- * Put a frame into the buffer. When the buffer is full, flush the buffer
- * to the file.
- */
-static pj_status_t file_put_frame(pjmedia_port *this_port, 
-				  const pjmedia_frame *frame)
-{
-    struct file_port *fport = (struct file_port *)this_port;
-
-    /* Flush buffer if we don't have enough room for the frame. */
-    if (fport->writepos + frame->size > fport->buf + fport->bufsize) {
-	pj_status_t status;
-	status = flush_buffer(fport);
-	if (status != PJ_SUCCESS)
-	    return status;
-    }
-
-    /* Check if frame is not too large. */
-    PJ_ASSERT_RETURN(fport->writepos+frame->size <= fport->buf+fport->bufsize,
-		     PJMEDIA_EFRMFILETOOBIG);
-
-    /* Copy frame to buffer. */
-    pj_memcpy(fport->writepos, frame->buf, frame->size);
-    fport->writepos += frame->size;
-
-    return PJ_SUCCESS;
-}
-
-/*
- * Get frame, basicy is a no-op operation.
- */
-static pj_status_t file_get_frame(pjmedia_port *this_port, 
-				  pjmedia_frame *frame)
-{
-    PJ_UNUSED_ARG(this_port);
-    PJ_UNUSED_ARG(frame);
-    return PJ_EINVALIDOP;
-}
-
-/*
- * Close the port, modify file header with updated file length.
- */
-static pj_status_t file_on_destroy(pjmedia_port *this_port)
-{
-    enum { FILE_LEN_POS = 4, DATA_LEN_POS = 40 };
-    struct file_port *fport = (struct file_port *)this_port;
-    pj_off_t file_size;
-    pj_ssize_t bytes;
-    pj_uint32_t wave_file_len;
-    pj_uint32_t wave_data_len;
-    pj_status_t status;
-
-    /* Flush remaining buffers. */
-    if (fport->writepos != fport->buf) 
-	flush_buffer(fport);
-
-    /* Get file size. */
-    status = pj_file_getpos(fport->fd, &file_size);
-    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
-    /* Calculate wave fields */
-    wave_file_len = (pj_uint32_t)(file_size - 8);
-    wave_data_len = (pj_uint32_t)(file_size - sizeof(pjmedia_wave_hdr));
-
-#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
-    wave_file_len = pj_swap32(wave_file_len);
-    wave_data_len = pj_swap32(wave_data_len);
-#endif
-
-    /* Seek to the file_len field. */
-    status = pj_file_setpos(fport->fd, FILE_LEN_POS, PJ_SEEK_SET);
-    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
-    /* Write file_len */
-    bytes = sizeof(wave_file_len);
-    status = pj_file_write(fport->fd, &wave_file_len, &bytes);
-    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
-    /* Seek to data_len field. */
-    status = pj_file_setpos(fport->fd, DATA_LEN_POS, PJ_SEEK_SET);
-    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
-    /* Write file_len */
-    bytes = sizeof(wave_data_len);
-    status = pj_file_write(fport->fd, &wave_data_len, &bytes);
-    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
-    /* Close file */
-    status = pj_file_close(fport->fd);
-    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
-    /* Done. */
-    return PJ_SUCCESS;
-}
-
Index: pjproject/trunk/pjmedia/src/pjmedia/g711.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/g711.c	(revision 410)
+++ pjproject/trunk/pjmedia/src/pjmedia/g711.c	(revision 411)
@@ -36,5 +36,4 @@
 /* These are the only public functions exported to applications */
 PJ_DECL(pj_status_t) g711_init_factory (pjmedia_codec_factory *factory, pj_pool_t *pool);
-PJ_DECL(pj_status_t) g711_deinit_factory (pjmedia_codec_factory *factory);
 
 /* Algorithm prototypes. */
@@ -60,6 +59,4 @@
 
 /* Prototypes for G711 implementation. */
-static pj_status_t  g711_codec_default_attr (pjmedia_codec *codec, 
-					     pjmedia_codec_param *attr);
 static pj_status_t  g711_init( pjmedia_codec *codec, 
 			       pj_pool_t *pool );
@@ -84,5 +81,4 @@
 static pjmedia_codec_op g711_op = 
 {
-    &g711_codec_default_attr ,
     &g711_init,
     &g711_open,
@@ -120,13 +116,4 @@
 
 
-PJ_DEF(pj_status_t) g711_deinit_factory (pjmedia_codec_factory *factory)
-{
-    PJ_ASSERT_RETURN(factory==&g711_factory.base, PJ_EINVAL);
-
-    /* Invalidate member to help detect errors */
-    g711_factory.codec_list.next = g711_factory.codec_list.prev = NULL;
-    return 0;
-}
-
 PJ_DEF(pj_status_t) pjmedia_codec_g711_init(pjmedia_endpt *endpt)
 {
@@ -223,5 +210,6 @@
 }
 
-static pj_status_t g711_test_alloc( pjmedia_codec_factory *factory, const pjmedia_codec_info *id )
+static pj_status_t g711_test_alloc(pjmedia_codec_factory *factory, 
+				   const pjmedia_codec_info *id )
 {
     PJ_UNUSED_ARG(factory);
@@ -238,5 +226,6 @@
 
     pj_memset(attr, 0, sizeof(pjmedia_codec_param));
-    attr->sample_rate = 8000;
+    attr->clock_rate = 8000;
+    attr->channel_cnt = 1;
     attr->avg_bps = G711_BPS;
     attr->pcm_bits_per_sample = 16;
@@ -261,12 +250,14 @@
 	codecs[count].pt = PJMEDIA_RTP_PT_PCMU;
 	codecs[count].encoding_name = pj_str("PCMU");
-	codecs[count].sample_rate = 8000;
+	codecs[count].clock_rate = 8000;
+	codecs[count].channel_cnt = 1;
 	++count;
     }
     if (count < *max_count) {
-	codecs[1].type = PJMEDIA_TYPE_AUDIO;
-	codecs[1].pt = PJMEDIA_RTP_PT_PCMA;
-	codecs[1].encoding_name = pj_str("PCMA");
-	codecs[1].sample_rate = 8000;
+	codecs[count].type = PJMEDIA_TYPE_AUDIO;
+	codecs[count].pt = PJMEDIA_RTP_PT_PCMA;
+	codecs[count].encoding_name = pj_str("PCMA");
+	codecs[count].clock_rate = 8000;
+	codecs[count].channel_cnt = 1;
 	++count;
     }
@@ -293,5 +284,6 @@
 
 	codec = pj_pool_alloc(g711_factory.pool, sizeof(pjmedia_codec));
-	codec_priv = pj_pool_alloc(g711_factory.pool, sizeof(struct g711_private));
+	codec_priv = pj_pool_alloc(g711_factory.pool, 
+				   sizeof(struct g711_private));
 	if (!codec || !codec_priv) {
 	    pj_mutex_unlock(g711_factory.mutex);
@@ -320,5 +312,6 @@
 }
 
-static pj_status_t g711_dealloc_codec( pjmedia_codec_factory *factory, pjmedia_codec *codec )
+static pj_status_t g711_dealloc_codec(pjmedia_codec_factory *factory, 
+				      pjmedia_codec *codec )
 {
     
@@ -343,13 +336,4 @@
 }
 
-static pj_status_t g711_codec_default_attr  (pjmedia_codec *codec, pjmedia_codec_param *attr)
-{
-    struct g711_private *priv = codec->codec_data;
-    pjmedia_codec_info id;
-
-    id.pt = priv->pt;
-    return g711_default_attr (NULL, &id, attr);
-}
-
 static pj_status_t g711_init( pjmedia_codec *codec, pj_pool_t *pool )
 {
@@ -361,5 +345,6 @@
 }
 
-static pj_status_t g711_open( pjmedia_codec *codec, pjmedia_codec_param *attr )
+static pj_status_t g711_open(pjmedia_codec *codec, 
+			     pjmedia_codec_param *attr )
 {
     struct g711_private *priv = codec->codec_data;
@@ -401,6 +386,8 @@
 }
 
-static pj_status_t  g711_encode( pjmedia_codec *codec, const struct pjmedia_frame *input,
-				 unsigned output_buf_len, struct pjmedia_frame *output)
+static pj_status_t  g711_encode(pjmedia_codec *codec, 
+				const struct pjmedia_frame *input,
+				unsigned output_buf_len, 
+				struct pjmedia_frame *output)
 {
     pj_int16_t *samples = (pj_int16_t*) input->buf;
@@ -409,5 +396,5 @@
     /* Check output buffer length */
     if (output_buf_len < input->size / 2)
-	return -1;
+	return PJMEDIA_CODEC_EFRMTOOSHORT;
 
     /* Encode */
@@ -428,5 +415,5 @@
 
     } else {
-	return -1;
+	return PJMEDIA_EINVALIDPT;
     }
 
@@ -434,9 +421,11 @@
     output->size = input->size / 2;
 
-    return 0;
-}
-
-static pj_status_t  g711_decode( pjmedia_codec *codec, const struct pjmedia_frame *input,
-				 unsigned output_buf_len, struct pjmedia_frame *output)
+    return PJ_SUCCESS;
+}
+
+static pj_status_t  g711_decode(pjmedia_codec *codec, 
+				const struct pjmedia_frame *input,
+				unsigned output_buf_len, 
+				struct pjmedia_frame *output)
 {
     struct g711_private *priv = codec->codec_data;
@@ -444,5 +433,5 @@
     /* Check output buffer length */
     if (output_buf_len < input->size * 2)
-	return -1;
+	return PJMEDIA_CODEC_EPCMTOOSHORT;
 
     /* Decode */
@@ -465,5 +454,5 @@
 
     } else {
-	return -1;
+	return PJMEDIA_EINVALIDPT;
     }
 
@@ -471,5 +460,5 @@
     output->size = input->size * 2;
 
-    return 0;
+    return PJ_SUCCESS;
 }
 
Index: pjproject/trunk/pjmedia/src/pjmedia/jbuf.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/jbuf.c	(revision 410)
+++ pjproject/trunk/pjmedia/src/pjmedia/jbuf.c	(revision 411)
@@ -323,5 +323,5 @@
 static void jbuf_calculate_jitter(pjmedia_jbuf *jb)
 {
-    enum { STABLE_HISTORY_LIMIT = (500/20) };
+    enum { STABLE_HISTORY_LIMIT = (100*2) };
 
     jb->jb_last_jitter = PJ_ABS(jb->jb_level-jb->jb_last_level);
@@ -342,5 +342,5 @@
 	    jb->jb_max_hist_jitter = 0;
 
-	    if (jb->jb_op_count >= 100 &&
+	    if (jb->jb_op_count >= STABLE_HISTORY_LIMIT*2 &&
 		(int)jb_framelist_size(&jb->jb_framelist) > jb->jb_prefetch+2)
 	    {
@@ -361,5 +361,5 @@
 	jb->jb_max_hist_jitter = 0;
 
-	if (jb->jb_op_count >= 100) {
+	if (jb->jb_op_count >= STABLE_HISTORY_LIMIT * 2) {
 	    if ((int)jb_framelist_size(&jb->jb_framelist) > jb->jb_prefetch+2) 
 	    {
Index: pjproject/trunk/pjmedia/src/pjmedia/master_port.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/master_port.c	(revision 410)
+++ pjproject/trunk/pjmedia/src/pjmedia/master_port.c	(revision 411)
@@ -60,11 +60,11 @@
 
     /* Both ports MUST have the equal ptime */
-    PJ_ASSERT_RETURN(u_port->info.sample_rate/u_port->info.samples_per_frame==
-		     d_port->info.sample_rate/d_port->info.samples_per_frame,
+    PJ_ASSERT_RETURN(u_port->info.clock_rate/u_port->info.samples_per_frame==
+		     d_port->info.clock_rate/d_port->info.samples_per_frame,
 		     PJMEDIA_ENCSAMPLESPFRAME);
 
 
     /* Get clock_rate and samples_per_frame from one of the port. */
-    clock_rate = u_port->info.sample_rate;
+    clock_rate = u_port->info.clock_rate;
     samples_per_frame = u_port->info.samples_per_frame;
 
Index: pjproject/trunk/pjmedia/src/pjmedia/null_port.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/null_port.c	(revision 410)
+++ pjproject/trunk/pjmedia/src/pjmedia/null_port.c	(revision 411)
@@ -52,5 +52,5 @@
     port->info.need_info = 0;
     port->info.pt = 0xFF;
-    port->info.sample_rate = sampling_rate;
+    port->info.clock_rate = sampling_rate;
     port->info.samples_per_frame = samples_per_frame;
     port->info.channel_count = channel_count;
Index: pjproject/trunk/pjmedia/src/pjmedia/resample_port.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/resample_port.c	(revision 410)
+++ pjproject/trunk/pjmedia/src/pjmedia/resample_port.c	(revision 411)
@@ -76,5 +76,5 @@
     rport->base.info.need_info = 0;
     rport->base.info.pt = 0xFF;
-    rport->base.info.sample_rate = upstream_rate;
+    rport->base.info.clock_rate = upstream_rate;
     rport->base.info.samples_per_frame = upstream_samples_per_frame;
     rport->base.info.signature = 0;
Index: pjproject/trunk/pjmedia/src/pjmedia/rtcp.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/rtcp.c	(revision 410)
+++ pjproject/trunk/pjmedia/src/pjmedia/rtcp.c	(revision 411)
@@ -134,6 +134,10 @@
     pj_time_val now;
     
+    /* Reset packet */
     pj_memset(rtcp_pkt, 0, sizeof(pjmedia_rtcp_pkt));
     
+    /* Reset statistics */
+    pj_memset(&sess->stat, 0, sizeof(pjmedia_rtcp_stat));
+
     /* Name */
     sess->name = name ? name : THIS_FILE,
Index: pjproject/trunk/pjmedia/src/pjmedia/session.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/session.c	(revision 410)
+++ pjproject/trunk/pjmedia/src/pjmedia/session.c	(revision 411)
@@ -195,14 +195,35 @@
 	    si->fmt.pt = pj_strtoul(&local_m->desc.fmt[0]);
 	    pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name);
-	    si->fmt.sample_rate = rtpmap->clock_rate;
+	    si->fmt.clock_rate = rtpmap->clock_rate;
+	    
+	    /* For audio codecs, rtpmap parameters denotes the number of
+	     * channels.
+	     */
+	    if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) {
+		
+		if (rtpmap->param.slen == 2) {
+		    si->fmt.channel_cnt = rtpmap->param.ptr[1] - '0';
+		} else {
+		    pj_str_t cnt;
+		    cnt.ptr = rtpmap->param.ptr + 1;
+		    cnt.slen = rtpmap->param.slen - 1;
+		    si->fmt.channel_cnt = (unsigned) pj_strtoul(&cnt);
+		}
+
+	    } else {
+		si->fmt.channel_cnt = 1;
+	    }
 
 	} else {
 	    pjmedia_codec_mgr *mgr;
+	    pjmedia_codec_info *p_info;
 
 	    mgr = pjmedia_endpt_get_codec_mgr(endpt);
 
-	    status = pjmedia_codec_mgr_get_codec_info( mgr, pt, &si->fmt);
+	    status = pjmedia_codec_mgr_get_codec_info( mgr, pt, &p_info);
 	    if (status != PJ_SUCCESS)
 		return status;
+
+	    pj_memcpy(&si->fmt, p_info, sizeof(pjmedia_codec_info));
 	}
 
@@ -225,5 +246,23 @@
 	si->fmt.pt = pj_strtoul(&local_m->desc.fmt[0]);
 	pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name);
-	si->fmt.sample_rate = rtpmap->clock_rate;
+	si->fmt.clock_rate = rtpmap->clock_rate;
+
+	/* For audio codecs, rtpmap parameters denotes the number of
+	 * channels.
+	 */
+	if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) {
+	    
+	    if (rtpmap->param.slen == 2) {
+		si->fmt.channel_cnt = rtpmap->param.ptr[1] - '0';
+	    } else {
+		pj_str_t cnt;
+		cnt.ptr = rtpmap->param.ptr + 1;
+		cnt.slen = rtpmap->param.slen - 1;
+		si->fmt.channel_cnt = (unsigned) pj_strtoul(&cnt);
+	    }
+
+	} else {
+	    si->fmt.channel_cnt = 1;
+	}
 
 	/* Determine payload type for outgoing channel, by finding
Index: pjproject/trunk/pjmedia/src/pjmedia/sound_port.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/sound_port.c	(revision 410)
+++ pjproject/trunk/pjmedia/src/pjmedia/sound_port.c	(revision 411)
@@ -345,5 +345,5 @@
      */
     pinfo = &port->info;
-    if (pinfo->sample_rate != snd_port->clock_rate)
+    if (pinfo->clock_rate != snd_port->clock_rate)
 	return PJMEDIA_ENCCLOCKRATE;
 
Index: pjproject/trunk/pjmedia/src/pjmedia/stream.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 410)
+++ pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 411)
@@ -159,4 +159,10 @@
     struct pjmedia_frame frame_in, frame_out;
 
+    /* Return no frame is channel is paused */
+    if (channel->paused) {
+	frame->type = PJMEDIA_FRAME_TYPE_NONE;
+	return PJ_SUCCESS;
+    }
+
     /* Lock jitter buffer mutex */
     pj_mutex_lock( stream->jb_mutex );
@@ -175,5 +181,4 @@
 	return PJ_SUCCESS;
     }
-
 
     /* Decode */
@@ -321,4 +326,10 @@
     int rtphdrlen;
     pj_ssize_t sent;
+
+
+    /* Don't do anything if stream is paused */
+    if (channel->paused)
+	return PJ_SUCCESS;
+
 
     /* Number of samples in the frame */
@@ -758,5 +769,6 @@
     /* Allocate buffer for decoding to PCM: */
 
-    channel->pcm_buf_size = codec_param->sample_rate * 
+    channel->pcm_buf_size = codec_param->clock_rate * 
+			    codec_param->channel_cnt *
 			    codec_param->pcm_bits_per_sample / 8 *
 			    PJMEDIA_MAX_FRAME_DURATION_MS / 1000;
@@ -791,4 +803,5 @@
     pj_ioqueue_callback ioqueue_cb;
     pj_uint16_t rtcp_port;
+    unsigned jbuf_init, jbuf_max;
     pj_status_t status;
 
@@ -814,6 +827,6 @@
     stream->port.info.pt = info->fmt.pt;
     pj_strdup(pool, &stream->port.info.encoding_name, &info->fmt.encoding_name);
-    stream->port.info.channel_count = 1;
-    stream->port.info.sample_rate = info->fmt.sample_rate;
+    stream->port.info.clock_rate = info->fmt.clock_rate;
+    stream->port.info.channel_count = info->fmt.channel_cnt;
     stream->port.user_data = stream;
     stream->port.put_frame = &put_frame;
@@ -832,5 +845,5 @@
     stream->rem_rtcp_addr.sin_port = pj_htons(rtcp_port);
     stream->rtcp_interval = (PJMEDIA_RTCP_INTERVAL + (pj_rand() % 8000)) * 
-			    info->fmt.sample_rate / 1000;
+			    info->fmt.clock_rate / 1000;
 
     stream->tx_event_pt = info->tx_event_pt ? info->tx_event_pt : -1;
@@ -856,5 +869,7 @@
     /* Get default codec param: */
 
-    status = stream->codec->op->default_attr(stream->codec, &codec_param);
+    //status = stream->codec->op->default_attr(stream->codec, &codec_param);
+    status = pjmedia_codec_mgr_get_default_param( stream->codec_mgr, 
+						  &info->fmt, &codec_param);
     if (status != PJ_SUCCESS)
 	goto err_cleanup;
@@ -862,5 +877,5 @@
     /* Set additional info. */
     stream->port.info.bits_per_sample = 16;
-    stream->port.info.samples_per_frame = info->fmt.sample_rate*codec_param.ptime/1000;
+    stream->port.info.samples_per_frame = info->fmt.clock_rate*codec_param.ptime/1000;
     stream->port.info.bytes_per_frame = codec_param.avg_bps/8 * codec_param.ptime/1000;
 
@@ -881,5 +896,5 @@
 
     pjmedia_rtcp_init(&stream->rtcp, stream->port.info.name.ptr,
-		      info->fmt.sample_rate, 
+		      info->fmt.clock_rate, 
 		      stream->port.info.samples_per_frame, 
 		      info->ssrc);
@@ -887,7 +902,11 @@
 
     /* Create jitter buffer: */
-
+    jbuf_init = 100 / (stream->port.info.samples_per_frame * 1000 / 
+		       info->fmt.clock_rate);
+    jbuf_max = 600 / (stream->port.info.samples_per_frame * 1000 / 
+		       info->fmt.clock_rate);
     status = pjmedia_jbuf_create(pool, &stream->port.info.name,
-				 stream->frame_size, 15, 100,
+				 stream->frame_size, 
+				 jbuf_init, jbuf_max,
 				 &stream->jb);
     if (status != PJ_SUCCESS)
Index: pjproject/trunk/pjmedia/src/pjmedia/wav_player.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/wav_player.c	(revision 411)
+++ pjproject/trunk/pjmedia/src/pjmedia/wav_player.c	(revision 411)
@@ -0,0 +1,401 @@
+/* $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/wav_port.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/wave.h>
+#include <pj/assert.h>
+#include <pj/file_access.h>
+#include <pj/file_io.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+
+#define THIS_FILE   "wav_player.c"
+
+
+#define SIGNATURE	    ('F'<<24|'P'<<16|'L'<<8|'Y')
+#define BYTES_PER_SAMPLE    2
+
+
+#if 1
+#   define TRACE_(x)	PJ_LOG(4,x)
+#else
+#   define TRACE_(x)
+#endif
+
+#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
+    static void samples_to_host(pj_int16_t *samples, unsigned count)
+    {
+	unsigned i;
+	for (i=0; i<count; ++i) {
+	    samples[i] = pj_swap16(samples[i]);
+	}
+    }
+#else
+#   define samples_to_host(samples,count)
+#endif
+
+struct file_port
+{
+    pjmedia_port     base;
+    pj_size_t	     bufsize;
+    char	    *buf;
+    char	    *readpos;
+
+    pj_off_t	     fsize;
+    pj_off_t	     fpos;
+    pj_oshandle_t    fd;
+
+};
+
+
+static pj_status_t file_put_frame(pjmedia_port *this_port, 
+				  const pjmedia_frame *frame);
+static pj_status_t file_get_frame(pjmedia_port *this_port, 
+				  pjmedia_frame *frame);
+static pj_status_t file_on_destroy(pjmedia_port *this_port);
+
+static struct file_port *create_file_port(pj_pool_t *pool)
+{
+    struct file_port *port;
+
+    port = pj_pool_zalloc(pool, sizeof(struct file_port));
+    if (!port)
+	return NULL;
+
+    port->base.info.name = pj_str("file");
+    port->base.info.signature = SIGNATURE;
+    port->base.info.type = PJMEDIA_TYPE_AUDIO;
+    port->base.info.has_info = PJ_TRUE;
+    port->base.info.need_info = PJ_FALSE;
+    port->base.info.pt = 0xFF;
+    port->base.info.encoding_name = pj_str("pcm");
+
+    port->base.put_frame = &file_put_frame;
+    port->base.get_frame = &file_get_frame;
+    port->base.on_destroy = &file_on_destroy;
+
+
+    /* Put in default values.
+     * These will be overriden once the file is read.
+     */
+    port->base.info.clock_rate = 8000;
+    port->base.info.bits_per_sample = 16;
+    port->base.info.samples_per_frame = 160;
+    port->base.info.bytes_per_frame = 320;
+
+    return port;
+}
+
+/*
+ * Fill buffer.
+ */
+static pj_status_t fill_buffer(struct file_port *fport)
+{
+    pj_ssize_t size_left = fport->bufsize;
+    unsigned size_to_read;
+    pj_ssize_t size;
+    pj_status_t status;
+
+    while (size_left > 0) {
+
+	/* Calculate how many bytes to read in this run. */
+	size = size_to_read = size_left;
+	status = pj_file_read(fport->fd, 
+			      &fport->buf[fport->bufsize-size_left], 
+			      &size);
+	if (status != PJ_SUCCESS)
+	    return status;
+	if (size < 0) {
+	    /* Should return more appropriate error code here.. */
+	    return PJ_ECANCELLED;
+	}
+
+	size_left -= size;
+	fport->fpos += size;
+
+	/* If size is less than size_to_read, it indicates that we've
+	 * encountered EOF. Rewind the file.
+	 */
+	if (size < (pj_ssize_t)size_to_read) {
+	    PJ_LOG(5,(THIS_FILE, "File port %.*s EOF, rewinding..",
+		      (int)fport->base.info.name.slen,
+		      fport->base.info.name.ptr));
+	    fport->fpos = sizeof(struct pjmedia_wave_hdr);
+	    pj_file_setpos( fport->fd, fport->fpos, PJ_SEEK_SET);
+	}
+    }
+
+    /* Convert samples to host rep */
+    samples_to_host((pj_int16_t*)fport->buf, fport->bufsize/BYTES_PER_SAMPLE);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Create WAVE player port.
+ */
+PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool,
+						     const char *filename,
+						     unsigned ptime,
+						     unsigned flags,
+						     pj_ssize_t buff_size,
+						     void *user_data,
+						     pjmedia_port **p_port )
+{
+    pjmedia_wave_hdr wave_hdr;
+    pj_ssize_t size_read;
+    struct file_port *fport;
+    pj_status_t status;
+
+
+    PJ_UNUSED_ARG(flags);
+
+    /* Check arguments. */
+    PJ_ASSERT_RETURN(pool && filename && p_port, PJ_EINVAL);
+
+    /* Check the file really exists. */
+    if (!pj_file_exists(filename)) {
+	return PJ_ENOTFOUND;
+    }
+
+    /* Normalize ptime */
+    if (ptime == 0)
+	ptime = 20;
+
+    /* Create fport instance. */
+    fport = create_file_port(pool);
+    if (!fport) {
+	return PJ_ENOMEM;
+    }
+
+
+    /* Get the file size. */
+    fport->fsize = pj_file_size(filename);
+
+    /* Size must be more than WAVE header size */
+    if (fport->fsize <= sizeof(pjmedia_wave_hdr)) {
+	return PJMEDIA_ENOTVALIDWAVE;
+    }
+
+    /* Open file. */
+    status = pj_file_open( pool, filename, PJ_O_RDONLY, &fport->fd);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Read the WAVE header. */
+    size_read = sizeof(wave_hdr);
+    status = pj_file_read( fport->fd, &wave_hdr, &size_read);
+    if (status != PJ_SUCCESS) {
+	pj_file_close(fport->fd);
+	return status;
+    }
+    if (size_read != sizeof(wave_hdr)) {
+	pj_file_close(fport->fd);
+	return PJMEDIA_ENOTVALIDWAVE;
+    }
+
+    /* Normalize WAVE header fields values from little-endian to host
+     * byte order.
+     */
+    pjmedia_wave_hdr_file_to_host(&wave_hdr);
+    
+    /* Validate WAVE file. */
+    if (wave_hdr.riff_hdr.riff != PJMEDIA_RIFF_TAG ||
+	wave_hdr.riff_hdr.wave != PJMEDIA_WAVE_TAG ||
+	wave_hdr.fmt_hdr.fmt != PJMEDIA_FMT_TAG)
+    {
+	pj_file_close(fport->fd);
+	TRACE_((THIS_FILE, 
+		"actual value|expected riff=%x|%x, wave=%x|%x fmt=%x|%x",
+		wave_hdr.riff_hdr.riff, PJMEDIA_RIFF_TAG,
+		wave_hdr.riff_hdr.wave, PJMEDIA_WAVE_TAG,
+		wave_hdr.fmt_hdr.fmt, PJMEDIA_FMT_TAG));
+	return PJMEDIA_ENOTVALIDWAVE;
+    }
+
+    /* Must be PCM with 16bits per sample */
+    if (wave_hdr.fmt_hdr.fmt_tag != 1 ||
+	wave_hdr.fmt_hdr.bits_per_sample != 16)
+    {
+	pj_file_close(fport->fd);
+	return PJMEDIA_EWAVEUNSUPP;
+    }
+
+    /* Block align must be 2*nchannels */
+    if (wave_hdr.fmt_hdr.block_align != wave_hdr.fmt_hdr.nchan*BYTES_PER_SAMPLE) {
+	pj_file_close(fport->fd);
+	return PJMEDIA_EWAVEUNSUPP;
+    }
+
+    /* Validate length. */
+    if (wave_hdr.data_hdr.len != fport->fsize-sizeof(pjmedia_wave_hdr)) {
+	pj_file_close(fport->fd);
+	return PJMEDIA_EWAVEUNSUPP;
+    }
+    if (wave_hdr.data_hdr.len < 400) {
+	pj_file_close(fport->fd);
+	return PJMEDIA_EWAVETOOSHORT;
+    }
+
+    /* It seems like we have a valid WAVE file. */
+
+    /* Initialize */
+    fport->base.user_data = user_data;
+
+    /* Update port info. */
+    fport->base.info.channel_count = wave_hdr.fmt_hdr.nchan;
+    fport->base.info.clock_rate = wave_hdr.fmt_hdr.sample_rate;
+    fport->base.info.bits_per_sample = wave_hdr.fmt_hdr.bits_per_sample;
+    fport->base.info.samples_per_frame = fport->base.info.clock_rate *
+					 wave_hdr.fmt_hdr.nchan *
+					 ptime / 1000;
+    fport->base.info.bytes_per_frame = 
+	fport->base.info.samples_per_frame * 
+	fport->base.info.bits_per_sample / 8;
+
+    pj_strdup2(pool, &fport->base.info.name, filename);
+
+    /* Create file buffer.
+     */
+    if (buff_size < 1) buff_size = PJMEDIA_FILE_PORT_BUFSIZE;
+    fport->bufsize = buff_size;
+
+
+    /* Create buffer. */
+    fport->buf = pj_pool_alloc(pool, fport->bufsize);
+    if (!fport->buf) {
+	pj_file_close(fport->fd);
+	return PJ_ENOMEM;
+    }
+
+    fport->readpos = fport->buf;
+
+    /* Set initial position of the file. */
+    fport->fpos = sizeof(struct pjmedia_wave_hdr);
+
+    /* Fill up the buffer. */
+    status = fill_buffer(fport);
+    if (status != PJ_SUCCESS) {
+	pj_file_close(fport->fd);
+	return status;
+    }
+
+    /* Done. */
+
+    *p_port = &fport->base;
+
+
+    PJ_LOG(4,(THIS_FILE, 
+	      "File player '%.*s' created: samp.rate=%d, ch=%d, bufsize=%uKB, "
+	      "filesize=%luKB",
+	      (int)fport->base.info.name.slen,
+	      fport->base.info.name.ptr,
+	      fport->base.info.clock_rate,
+	      fport->base.info.channel_count,
+	      fport->bufsize / 1000,
+	      (unsigned long)(fport->fsize / 1000)));
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Put frame to file.
+ */
+static pj_status_t file_put_frame(pjmedia_port *this_port, 
+				  const pjmedia_frame *frame)
+{
+    PJ_UNUSED_ARG(this_port);
+    PJ_UNUSED_ARG(frame);
+    return PJ_EINVALIDOP;
+}
+
+/*
+ * Get frame from file.
+ */
+static pj_status_t file_get_frame(pjmedia_port *this_port, 
+				  pjmedia_frame *frame)
+{
+    struct file_port *fport = (struct file_port*)this_port;
+    unsigned frame_size;
+    pj_status_t status;
+
+    pj_assert(fport->base.info.signature == SIGNATURE);
+
+    //frame_size = fport->base.info.bytes_per_frame;
+    //pj_assert(frame->size == frame_size);
+    frame_size = frame->size;
+
+    /* Copy frame from buffer. */
+    frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
+    frame->size = frame_size;
+    frame->timestamp.u64 = 0;
+
+    if (fport->readpos + frame_size <= fport->buf + fport->bufsize) {
+
+	/* Read contiguous buffer. */
+	pj_memcpy(frame->buf, fport->readpos, frame_size);
+
+	/* Fill up the buffer if all has been read. */
+	fport->readpos += frame_size;
+	if (fport->readpos == fport->buf + fport->bufsize) {
+	    fport->readpos = fport->buf;
+
+	    status = fill_buffer(fport);
+	    if (status != PJ_SUCCESS)
+		return status;
+	}
+    } else {
+	unsigned endread;
+
+	/* Split read.
+	 * First stage: read until end of buffer. 
+	 */
+	endread = (fport->buf+fport->bufsize) - fport->readpos;
+	pj_memcpy(frame->buf, fport->readpos, endread);
+
+	/* Second stage: fill up buffer, and read from the start of buffer. */
+	status = fill_buffer(fport);
+	if (status != PJ_SUCCESS) {
+	    pj_memset(((char*)frame->buf)+endread, 0, frame_size-endread);
+	    return status;
+	}
+
+	pj_memcpy(((char*)frame->buf)+endread, fport->buf, frame_size-endread);
+	fport->readpos = fport->buf + (frame_size - endread);
+    }
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Destroy port.
+ */
+static pj_status_t file_on_destroy(pjmedia_port *this_port)
+{
+    struct file_port *fport = (struct file_port*) this_port;
+
+    pj_assert(this_port->info.signature == SIGNATURE);
+
+    pj_file_close(fport->fd);
+    return PJ_SUCCESS;
+}
Index: pjproject/trunk/pjmedia/src/pjmedia/wav_writer.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/wav_writer.c	(revision 411)
+++ pjproject/trunk/pjmedia/src/pjmedia/wav_writer.c	(revision 411)
@@ -0,0 +1,306 @@
+/* $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/wav_port.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/wave.h>
+#include <pj/assert.h>
+#include <pj/file_access.h>
+#include <pj/file_io.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+
+#define THIS_FILE	    "wav_writer.c"
+#define SIGNATURE	    ('F'<<24|'W'<<16|'R'<<8|'T')
+#define BYTES_PER_SAMPLE    2
+
+
+struct file_port
+{
+    pjmedia_port     base;
+    pj_size_t	     bufsize;
+    char	    *buf;
+    char	    *writepos;
+
+    pj_oshandle_t    fd;
+};
+
+static pj_status_t file_put_frame(pjmedia_port *this_port, 
+				  const pjmedia_frame *frame);
+static pj_status_t file_get_frame(pjmedia_port *this_port, 
+				  pjmedia_frame *frame);
+static pj_status_t file_on_destroy(pjmedia_port *this_port);
+
+
+/*
+ * Create file writer port.
+ */
+PJ_DEF(pj_status_t) pjmedia_wav_writer_port_create( pj_pool_t *pool,
+						     const char *filename,
+						     unsigned sampling_rate,
+						     unsigned channel_count,
+						     unsigned samples_per_frame,
+						     unsigned bits_per_sample,
+						     unsigned flags,
+						     pj_ssize_t buff_size,
+						     void *user_data,
+						     pjmedia_port **p_port )
+{
+    struct file_port *fport;
+    pjmedia_wave_hdr wave_hdr;
+    pj_ssize_t size;
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(flags);
+    PJ_UNUSED_ARG(user_data);
+
+    /* Check arguments. */
+    PJ_ASSERT_RETURN(pool && filename && p_port, PJ_EINVAL);
+
+    /* Only supports 16bits per sample for now.
+     * See flush_buffer().
+     */
+    PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL);
+
+    /* Create file port instance. */
+    fport = pj_pool_zalloc(pool, sizeof(struct file_port));
+    PJ_ASSERT_RETURN(fport != NULL, PJ_ENOMEM);
+
+    /* Initialize port info. */
+    fport->base.info.bits_per_sample = bits_per_sample;
+    fport->base.info.bytes_per_frame = samples_per_frame * bits_per_sample *
+				       channel_count / 8;
+    fport->base.info.channel_count = channel_count;
+    fport->base.info.encoding_name = pj_str("pom");
+    fport->base.info.has_info = 1;
+    pj_strdup2(pool, &fport->base.info.name, filename);
+    fport->base.info.need_info = 0;
+    fport->base.info.pt = 0xFF;
+    fport->base.info.clock_rate = sampling_rate;
+    fport->base.info.samples_per_frame = samples_per_frame;
+    fport->base.info.signature = SIGNATURE;
+    fport->base.info.type = PJMEDIA_TYPE_AUDIO;
+    
+    fport->base.get_frame = &file_get_frame;
+    fport->base.put_frame = &file_put_frame;
+    fport->base.on_destroy = &file_on_destroy;
+
+
+    /* Open file in write and read mode.
+     * We need the read mode because we'll modify the WAVE header once
+     * the recording has completed.
+     */
+    status = pj_file_open(pool, filename, PJ_O_WRONLY, &fport->fd);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Initialize WAVE header */
+    pj_memset(&wave_hdr, 0, sizeof(pjmedia_wave_hdr));
+    wave_hdr.riff_hdr.riff = PJMEDIA_RIFF_TAG;
+    wave_hdr.riff_hdr.file_len = 0; /* will be filled later */
+    wave_hdr.riff_hdr.wave = PJMEDIA_WAVE_TAG;
+
+    wave_hdr.fmt_hdr.fmt = PJMEDIA_FMT_TAG;
+    wave_hdr.fmt_hdr.len = 16;
+    wave_hdr.fmt_hdr.fmt_tag = 1;
+    wave_hdr.fmt_hdr.nchan = (pj_int16_t)channel_count;
+    wave_hdr.fmt_hdr.sample_rate = sampling_rate;
+    wave_hdr.fmt_hdr.bytes_per_sec = sampling_rate * channel_count * 
+				     bits_per_sample / 8;
+    wave_hdr.fmt_hdr.block_align = (pj_int16_t) (channel_count * 
+						 bits_per_sample / 8);
+    wave_hdr.fmt_hdr.bits_per_sample = (pj_int16_t)bits_per_sample;
+
+    wave_hdr.data_hdr.data = PJMEDIA_DATA_TAG;
+    wave_hdr.data_hdr.len = 0;	    /* will be filled later */
+
+
+    /* Convert WAVE header from host byte order to little endian
+     * before writing the header.
+     */
+    pjmedia_wave_hdr_host_to_file(&wave_hdr);
+
+
+    /* Write WAVE header */
+    size = sizeof(pjmedia_wave_hdr);
+    status = pj_file_write(fport->fd, &wave_hdr, &size);
+    if (status != PJ_SUCCESS) {
+	pj_file_close(fport->fd);
+	return status;
+    }
+
+    /* Set buffer size. */
+    if (buff_size < 1) buff_size = PJMEDIA_FILE_PORT_BUFSIZE;
+    fport->bufsize = buff_size;
+
+    /* Check that buffer size is greater than bytes per frame */
+    pj_assert(fport->bufsize >= fport->base.info.bytes_per_frame);
+
+
+    /* Allocate buffer and set initial write position */
+    fport->buf = pj_pool_alloc(pool, fport->bufsize);
+    if (fport->buf == NULL) {
+	pj_file_close(fport->fd);
+	return PJ_ENOMEM;
+    }
+    fport->writepos = fport->buf;
+
+    /* Done. */
+    *p_port = &fport->base;
+
+    PJ_LOG(4,(THIS_FILE, 
+	      "File writer '%.*s' created: samp.rate=%d, bufsize=%uKB",
+	      (int)fport->base.info.name.slen,
+	      fport->base.info.name.ptr,
+	      fport->base.info.clock_rate,
+	      fport->bufsize / 1000));
+
+
+    return PJ_SUCCESS;
+}
+
+
+#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
+    static void swap_samples(pj_int16_t *samples, unsigned count)
+    {
+	unsigned i;
+	for (i=0; i<count; ++i) {
+	    samples[i] = pj_swap16(samples[i]);
+	}
+    }
+#else
+#   define swap_samples(samples,count)
+#endif
+
+/*
+ * Flush the contents of the buffer to the file.
+ */
+static pj_status_t flush_buffer(struct file_port *fport)
+{
+    pj_ssize_t bytes = fport->writepos - fport->buf;
+    pj_status_t status;
+
+    /* Convert samples to little endian */
+    swap_samples((pj_int16_t*)fport->buf, bytes/BYTES_PER_SAMPLE);
+
+    /* Write to file. */
+    status = pj_file_write(fport->fd, fport->buf, &bytes);
+
+    /* Reset writepos */
+    fport->writepos = fport->buf;
+
+    return status;
+}
+
+/*
+ * Put a frame into the buffer. When the buffer is full, flush the buffer
+ * to the file.
+ */
+static pj_status_t file_put_frame(pjmedia_port *this_port, 
+				  const pjmedia_frame *frame)
+{
+    struct file_port *fport = (struct file_port *)this_port;
+
+    /* Flush buffer if we don't have enough room for the frame. */
+    if (fport->writepos + frame->size > fport->buf + fport->bufsize) {
+	pj_status_t status;
+	status = flush_buffer(fport);
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+
+    /* Check if frame is not too large. */
+    PJ_ASSERT_RETURN(fport->writepos+frame->size <= fport->buf+fport->bufsize,
+		     PJMEDIA_EFRMFILETOOBIG);
+
+    /* Copy frame to buffer. */
+    pj_memcpy(fport->writepos, frame->buf, frame->size);
+    fport->writepos += frame->size;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Get frame, basicy is a no-op operation.
+ */
+static pj_status_t file_get_frame(pjmedia_port *this_port, 
+				  pjmedia_frame *frame)
+{
+    PJ_UNUSED_ARG(this_port);
+    PJ_UNUSED_ARG(frame);
+    return PJ_EINVALIDOP;
+}
+
+/*
+ * Close the port, modify file header with updated file length.
+ */
+static pj_status_t file_on_destroy(pjmedia_port *this_port)
+{
+    enum { FILE_LEN_POS = 4, DATA_LEN_POS = 40 };
+    struct file_port *fport = (struct file_port *)this_port;
+    pj_off_t file_size;
+    pj_ssize_t bytes;
+    pj_uint32_t wave_file_len;
+    pj_uint32_t wave_data_len;
+    pj_status_t status;
+
+    /* Flush remaining buffers. */
+    if (fport->writepos != fport->buf) 
+	flush_buffer(fport);
+
+    /* Get file size. */
+    status = pj_file_getpos(fport->fd, &file_size);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+    /* Calculate wave fields */
+    wave_file_len = (pj_uint32_t)(file_size - 8);
+    wave_data_len = (pj_uint32_t)(file_size - sizeof(pjmedia_wave_hdr));
+
+#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
+    wave_file_len = pj_swap32(wave_file_len);
+    wave_data_len = pj_swap32(wave_data_len);
+#endif
+
+    /* Seek to the file_len field. */
+    status = pj_file_setpos(fport->fd, FILE_LEN_POS, PJ_SEEK_SET);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+    /* Write file_len */
+    bytes = sizeof(wave_file_len);
+    status = pj_file_write(fport->fd, &wave_file_len, &bytes);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+    /* Seek to data_len field. */
+    status = pj_file_setpos(fport->fd, DATA_LEN_POS, PJ_SEEK_SET);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+    /* Write file_len */
+    bytes = sizeof(wave_data_len);
+    status = pj_file_write(fport->fd, &wave_data_len, &bytes);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+    /* Close file */
+    status = pj_file_close(fport->fd);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+    /* Done. */
+    return PJ_SUCCESS;
+}
+
