* #27232: jni: added pjproject checkout as regular git content

We will remove it once the next release of pjsip (with Android support)
comes out and is merged into SFLphone.
diff --git a/jni/pjproject-android/pjmedia/src/pjmedia-codec/amr_sdp_match.c b/jni/pjproject-android/pjmedia/src/pjmedia-codec/amr_sdp_match.c
new file mode 100644
index 0000000..44b104b
--- /dev/null
+++ b/jni/pjproject-android/pjmedia/src/pjmedia-codec/amr_sdp_match.c
@@ -0,0 +1,176 @@
+/* $Id: amr_sdp_match.c 3911 2011-12-15 06:45:23Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjmedia-codec/amr_sdp_match.h>
+#include <pjmedia/errno.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+
+#define GET_FMTP_IVAL_BASE(ival, base, fmtp, param, default_val) \
+    do { \
+	pj_str_t s; \
+	char *p; \
+	p = pj_stristr(&fmtp.fmt_param, &param); \
+	if (!p) { \
+	    ival = default_val; \
+	    break; \
+	} \
+	pj_strset(&s, p + param.slen, fmtp.fmt_param.slen - \
+		  (p - fmtp.fmt_param.ptr) - param.slen); \
+	ival = pj_strtoul2(&s, NULL, base); \
+    } while (0)
+
+#define GET_FMTP_IVAL(ival, fmtp, param, default_val) \
+	GET_FMTP_IVAL_BASE(ival, 10, fmtp, param, default_val)
+
+
+/* Toggle AMR octet-align setting in the fmtp. */
+static pj_status_t amr_toggle_octet_align(pj_pool_t *pool,
+					  pjmedia_sdp_media *media,
+					  unsigned fmt_idx)
+{
+    pjmedia_sdp_attr *attr;
+    pjmedia_sdp_fmtp fmtp;
+    const pj_str_t STR_OCTET_ALIGN = {"octet-align=", 12};
+    
+    enum { MAX_FMTP_STR_LEN = 160 };
+
+    attr = pjmedia_sdp_media_find_attr2(media, "fmtp", 
+					&media->desc.fmt[fmt_idx]);
+    /* Check if the AMR media format has FMTP attribute */
+    if (attr) {
+	char *p;
+	pj_status_t status;
+
+	status = pjmedia_sdp_attr_get_fmtp(attr, &fmtp);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	/* Check if the fmtp has octet-align field. */
+	p = pj_stristr(&fmtp.fmt_param, &STR_OCTET_ALIGN);
+	if (p) {
+	    /* It has, just toggle the value */
+	    unsigned octet_align;
+	    pj_str_t s;
+
+	    pj_strset(&s, p + STR_OCTET_ALIGN.slen, fmtp.fmt_param.slen -
+		      (p - fmtp.fmt_param.ptr) - STR_OCTET_ALIGN.slen);
+	    octet_align = pj_strtoul(&s);
+	    *(p + STR_OCTET_ALIGN.slen) = (char)(octet_align? '0' : '1');
+	} else {
+	    /* It doesn't, append octet-align field */
+	    char buf[MAX_FMTP_STR_LEN];
+
+	    pj_ansi_snprintf(buf, MAX_FMTP_STR_LEN, "%.*s;octet-align=1",
+			     (int)fmtp.fmt_param.slen, fmtp.fmt_param.ptr);
+	    attr->value = pj_strdup3(pool, buf);
+	}
+    } else {
+	/* Add new attribute for the AMR media format with octet-align 
+	 * field set.
+	 */
+	char buf[MAX_FMTP_STR_LEN];
+
+	attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
+	attr->name = pj_str("fmtp");
+	pj_ansi_snprintf(buf, MAX_FMTP_STR_LEN, "%.*s octet-align=1",
+			 (int)media->desc.fmt[fmt_idx].slen,
+		         media->desc.fmt[fmt_idx].ptr);
+	attr->value = pj_strdup3(pool, buf);
+	media->attr[media->attr_count++] = attr;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_codec_amr_match_sdp( pj_pool_t *pool,
+						 pjmedia_sdp_media *offer,
+						 unsigned o_fmt_idx,
+						 pjmedia_sdp_media *answer,
+						 unsigned a_fmt_idx,
+						 unsigned option)
+{
+    /* Negotiated format-param field-names constants. */
+    const pj_str_t STR_OCTET_ALIGN	= {"octet-align=", 12};
+    const pj_str_t STR_CRC		= {"crc=", 4};
+    const pj_str_t STR_ROBUST_SORTING	= {"robust-sorting=", 15};
+    const pj_str_t STR_INTERLEAVING	= {"interleaving=", 13};
+
+    /* Negotiated params and their default values. */
+    unsigned a_octet_align = 0, o_octet_align = 0;
+    unsigned a_crc = 0, o_crc = 0;
+    unsigned a_robust_sorting = 0, o_robust_sorting = 0;
+    unsigned a_interleaving = 0, o_interleaving = 0;
+
+    const pjmedia_sdp_attr *attr_ans;
+    const pjmedia_sdp_attr *attr_ofr;
+    pjmedia_sdp_fmtp fmtp;
+    pj_status_t status;
+
+    /* Parse offerer FMTP */
+    attr_ofr = pjmedia_sdp_media_find_attr2(offer, "fmtp", 
+					    &offer->desc.fmt[o_fmt_idx]);
+    if (attr_ofr) {
+	status = pjmedia_sdp_attr_get_fmtp(attr_ofr, &fmtp);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	GET_FMTP_IVAL(o_octet_align, fmtp, STR_OCTET_ALIGN, 0);
+	GET_FMTP_IVAL(o_crc, fmtp, STR_CRC, 0);
+	GET_FMTP_IVAL(o_robust_sorting, fmtp, STR_ROBUST_SORTING, 0);
+	GET_FMTP_IVAL(o_interleaving, fmtp, STR_INTERLEAVING, 0);
+    }
+
+    /* Parse answerer FMTP */
+    attr_ans = pjmedia_sdp_media_find_attr2(answer, "fmtp", 
+					    &answer->desc.fmt[a_fmt_idx]);
+    if (attr_ans) {
+	status = pjmedia_sdp_attr_get_fmtp(attr_ans, &fmtp);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	GET_FMTP_IVAL(a_octet_align, fmtp, STR_OCTET_ALIGN, 0);
+	GET_FMTP_IVAL(a_crc, fmtp, STR_CRC, 0);
+	GET_FMTP_IVAL(a_robust_sorting, fmtp, STR_ROBUST_SORTING, 0);
+	GET_FMTP_IVAL(a_interleaving, fmtp, STR_INTERLEAVING, 0);
+    }
+
+    /* First, match crc, robust-sorting, interleaving settings */
+    if (a_crc != o_crc ||
+	a_robust_sorting != o_robust_sorting ||
+	a_interleaving != o_interleaving)
+    {
+	return PJMEDIA_SDP_EFORMATNOTEQUAL;
+    }
+
+    /* Match octet-align setting */
+    if (a_octet_align != o_octet_align) {
+	/* Check if answer can be modified to match to the offer */
+	if (option & PJMEDIA_SDP_NEG_FMT_MATCH_ALLOW_MODIFY_ANSWER) {
+	    status = amr_toggle_octet_align(pool, answer, a_fmt_idx);
+	    return status;
+	} else {
+	    return PJMEDIA_SDP_EFORMATNOTEQUAL;
+	}
+    }
+
+    return PJ_SUCCESS;
+}
diff --git a/jni/pjproject-android/pjmedia/src/pjmedia-codec/audio_codecs.c b/jni/pjproject-android/pjmedia/src/pjmedia-codec/audio_codecs.c
new file mode 100644
index 0000000..0c21821
--- /dev/null
+++ b/jni/pjproject-android/pjmedia/src/pjmedia-codec/audio_codecs.c
@@ -0,0 +1,126 @@
+/* $Id: audio_codecs.c 4335 2013-01-29 08:09:15Z ming $ */
+/* 
+ * Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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.h>
+#include <pjmedia/g711.h>
+
+PJ_DEF(void) pjmedia_audio_codec_config_default(pjmedia_audio_codec_config*cfg)
+{
+    pj_bzero(cfg, sizeof(*cfg));
+    cfg->speex.option = 0;
+    cfg->speex.quality = PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY;
+    cfg->speex.complexity = PJMEDIA_CODEC_SPEEX_DEFAULT_COMPLEXITY;
+    cfg->ilbc.mode = 30;
+    cfg->passthrough.setting.ilbc_mode = cfg->ilbc.mode;
+}
+
+PJ_DEF(pj_status_t)
+pjmedia_codec_register_audio_codecs(pjmedia_endpt *endpt,
+                                    const pjmedia_audio_codec_config *c)
+{
+    pjmedia_audio_codec_config default_cfg;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(endpt, PJ_EINVAL);
+    if (!c) {
+	pjmedia_audio_codec_config_default(&default_cfg);
+	c = &default_cfg;
+    }
+
+    PJ_ASSERT_RETURN(c->ilbc.mode==20 || c->ilbc.mode==30, PJ_EINVAL);
+
+#if PJMEDIA_HAS_PASSTHROUGH_CODECS
+    status = pjmedia_codec_passthrough_init2(endpt, &c->passthrough.setting);
+    if (status != PJ_SUCCESS)
+	return status;
+#endif
+
+#if PJMEDIA_HAS_SPEEX_CODEC
+    /* Register speex. */
+    status = pjmedia_codec_speex_init(endpt, c->speex.option,
+				      c->speex.quality,
+				      c->speex.complexity);
+    if (status != PJ_SUCCESS)
+	return status;
+#endif
+
+#if PJMEDIA_HAS_ILBC_CODEC
+    /* Register iLBC. */
+    status = pjmedia_codec_ilbc_init( endpt, c->ilbc.mode);
+    if (status != PJ_SUCCESS)
+	return status;
+#endif /* PJMEDIA_HAS_ILBC_CODEC */
+
+#if PJMEDIA_HAS_GSM_CODEC
+    /* Register GSM */
+    status = pjmedia_codec_gsm_init(endpt);
+    if (status != PJ_SUCCESS)
+	return status;
+#endif /* PJMEDIA_HAS_GSM_CODEC */
+
+#if PJMEDIA_HAS_G711_CODEC
+    /* Register PCMA and PCMU */
+    status = pjmedia_codec_g711_init(endpt);
+    if (status != PJ_SUCCESS)
+	return status;
+#endif	/* PJMEDIA_HAS_G711_CODEC */
+
+#if PJMEDIA_HAS_G722_CODEC
+    status = pjmedia_codec_g722_init(endpt );
+    if (status != PJ_SUCCESS)
+	return status;
+#endif  /* PJMEDIA_HAS_G722_CODEC */
+
+#if PJMEDIA_HAS_INTEL_IPP
+    /* Register IPP codecs */
+    status = pjmedia_codec_ipp_init(endpt);
+    if (status != PJ_SUCCESS)
+	return status;
+#endif /* PJMEDIA_HAS_INTEL_IPP */
+
+#if PJMEDIA_HAS_G7221_CODEC
+    /* Register G722.1 codecs */
+    status = pjmedia_codec_g7221_init(endpt);
+    if (status != PJ_SUCCESS)
+	return status;
+#endif /* PJMEDIA_HAS_G7221_CODEC */
+
+#if PJMEDIA_HAS_L16_CODEC
+    /* Register L16 family codecs */
+    status = pjmedia_codec_l16_init(endpt, 0);
+    if (status != PJ_SUCCESS)
+	return status;
+#endif	/* PJMEDIA_HAS_L16_CODEC */
+
+#if PJMEDIA_HAS_OPENCORE_AMRNB_CODEC || PJMEDIA_HAS_OPENCORE_AMRWB_CODEC
+    /* Register OpenCORE AMR */
+    status = pjmedia_codec_opencore_amr_init(endpt, 0);
+    if (status != PJ_SUCCESS)
+	return status;
+#endif
+
+#if PJMEDIA_HAS_SILK_CODEC
+    /* Register SILK */
+    status = pjmedia_codec_silk_init(endpt);
+    if (status != PJ_SUCCESS)
+	return status;
+#endif
+
+    return PJ_SUCCESS;
+}
+
diff --git a/jni/pjproject-android/pjmedia/src/pjmedia-codec/ffmpeg_vid_codecs.c b/jni/pjproject-android/pjmedia/src/pjmedia-codec/ffmpeg_vid_codecs.c
new file mode 100644
index 0000000..a0abef8
--- /dev/null
+++ b/jni/pjproject-android/pjmedia/src/pjmedia-codec/ffmpeg_vid_codecs.c
@@ -0,0 +1,1824 @@
+/* $Id: ffmpeg_vid_codecs.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2010-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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/ffmpeg_vid_codecs.h>
+#include <pjmedia-codec/h263_packetizer.h>
+#include <pjmedia-codec/h264_packetizer.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/vid_codec_util.h>
+#include <pj/assert.h>
+#include <pj/list.h>
+#include <pj/log.h>
+#include <pj/math.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/os.h>
+
+
+/*
+ * Only build this file if PJMEDIA_HAS_FFMPEG_VID_CODEC != 0 and 
+ * PJMEDIA_HAS_VIDEO != 0
+ */
+#if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && \
+            PJMEDIA_HAS_FFMPEG_VID_CODEC != 0 && \
+    defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
+
+#define THIS_FILE   "ffmpeg_vid_codecs.c"
+
+#define LIBAVCODEC_VER_AT_LEAST(major,minor)  (LIBAVCODEC_VERSION_MAJOR > major || \
+     					       (LIBAVCODEC_VERSION_MAJOR == major && \
+					        LIBAVCODEC_VERSION_MINOR >= minor))
+
+#include "../pjmedia/ffmpeg_util.h"
+#include <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+#if LIBAVCODEC_VER_AT_LEAST(53,20)
+  /* Needed by 264 so far, on libavcodec 53.20 */
+# include <libavutil/opt.h>
+#endif
+
+
+/* Various compatibility */
+
+#if LIBAVCODEC_VER_AT_LEAST(53,20)
+#  define AVCODEC_OPEN(ctx,c)		avcodec_open2(ctx,c,NULL)
+#else
+#  define AVCODEC_OPEN(ctx,c)		avcodec_open(ctx,c)
+#endif
+
+#if LIBAVCODEC_VER_AT_LEAST(53,61)
+#  if LIBAVCODEC_VER_AT_LEAST(54,59)
+   /* Not sure when AVCodec::encode is obsoleted/removed. */
+#      define AVCODEC_HAS_ENCODE(c)	(c->encode2)
+#  else
+   /* Not sure when AVCodec::encode2 is introduced. It appears in 
+    * libavcodec 53.61 where some codecs actually still use AVCodec::encode
+    * (e.g: H263, H264).
+    */
+#      define AVCODEC_HAS_ENCODE(c)	(c->encode || c->encode2)
+#  endif
+#  define AV_OPT_SET(obj,name,val,opt)	(av_opt_set(obj,name,val,opt)==0)
+#  define AV_OPT_SET_INT(obj,name,val)	(av_opt_set_int(obj,name,val,0)==0)
+#else
+#  define AVCODEC_HAS_ENCODE(c)		(c->encode)
+#  define AV_OPT_SET(obj,name,val,opt)	(av_set_string3(obj,name,val,opt,NULL)==0)
+#  define AV_OPT_SET_INT(obj,name,val)	(av_set_int(obj,name,val)!=NULL)
+#endif
+#define AVCODEC_HAS_DECODE(c)		(c->decode)
+
+
+/* Prototypes for FFMPEG codecs factory */
+static pj_status_t ffmpeg_test_alloc( pjmedia_vid_codec_factory *factory, 
+				      const pjmedia_vid_codec_info *id );
+static pj_status_t ffmpeg_default_attr( pjmedia_vid_codec_factory *factory, 
+				        const pjmedia_vid_codec_info *info, 
+				        pjmedia_vid_codec_param *attr );
+static pj_status_t ffmpeg_enum_codecs( pjmedia_vid_codec_factory *factory, 
+				       unsigned *count, 
+				       pjmedia_vid_codec_info codecs[]);
+static pj_status_t ffmpeg_alloc_codec( pjmedia_vid_codec_factory *factory, 
+				       const pjmedia_vid_codec_info *info, 
+				       pjmedia_vid_codec **p_codec);
+static pj_status_t ffmpeg_dealloc_codec( pjmedia_vid_codec_factory *factory, 
+				         pjmedia_vid_codec *codec );
+
+/* Prototypes for FFMPEG codecs implementation. */
+static pj_status_t  ffmpeg_codec_init( pjmedia_vid_codec *codec, 
+				       pj_pool_t *pool );
+static pj_status_t  ffmpeg_codec_open( pjmedia_vid_codec *codec, 
+				       pjmedia_vid_codec_param *attr );
+static pj_status_t  ffmpeg_codec_close( pjmedia_vid_codec *codec );
+static pj_status_t  ffmpeg_codec_modify(pjmedia_vid_codec *codec, 
+				        const pjmedia_vid_codec_param *attr );
+static pj_status_t  ffmpeg_codec_get_param(pjmedia_vid_codec *codec,
+					   pjmedia_vid_codec_param *param);
+static pj_status_t ffmpeg_codec_encode_begin(pjmedia_vid_codec *codec,
+					     const pjmedia_vid_encode_opt *opt,
+                                             const pjmedia_frame *input,
+					     unsigned out_size,
+					     pjmedia_frame *output,
+					     pj_bool_t *has_more);
+static pj_status_t ffmpeg_codec_encode_more(pjmedia_vid_codec *codec,
+					    unsigned out_size,
+					    pjmedia_frame *output,
+					    pj_bool_t *has_more);
+static pj_status_t ffmpeg_codec_decode( pjmedia_vid_codec *codec,
+					pj_size_t pkt_count,
+					pjmedia_frame packets[],
+					unsigned out_size,
+					pjmedia_frame *output);
+
+/* Definition for FFMPEG codecs operations. */
+static pjmedia_vid_codec_op ffmpeg_op = 
+{
+    &ffmpeg_codec_init,
+    &ffmpeg_codec_open,
+    &ffmpeg_codec_close,
+    &ffmpeg_codec_modify,
+    &ffmpeg_codec_get_param,
+    &ffmpeg_codec_encode_begin,
+    &ffmpeg_codec_encode_more,
+    &ffmpeg_codec_decode,
+    NULL
+};
+
+/* Definition for FFMPEG codecs factory operations. */
+static pjmedia_vid_codec_factory_op ffmpeg_factory_op =
+{
+    &ffmpeg_test_alloc,
+    &ffmpeg_default_attr,
+    &ffmpeg_enum_codecs,
+    &ffmpeg_alloc_codec,
+    &ffmpeg_dealloc_codec
+};
+
+
+/* FFMPEG codecs factory */
+static struct ffmpeg_factory {
+    pjmedia_vid_codec_factory    base;
+    pjmedia_vid_codec_mgr	*mgr;
+    pj_pool_factory             *pf;
+    pj_pool_t		        *pool;
+    pj_mutex_t		        *mutex;
+} ffmpeg_factory;
+
+
+typedef struct ffmpeg_codec_desc ffmpeg_codec_desc;
+
+
+/* FFMPEG codecs private data. */
+typedef struct ffmpeg_private
+{
+    const ffmpeg_codec_desc	    *desc;
+    pjmedia_vid_codec_param	     param;	/**< Codec param	    */
+    pj_pool_t			    *pool;	/**< Pool for each instance */
+
+    /* Format info and apply format param */
+    const pjmedia_video_format_info *enc_vfi;
+    pjmedia_video_apply_fmt_param    enc_vafp;
+    const pjmedia_video_format_info *dec_vfi;
+    pjmedia_video_apply_fmt_param    dec_vafp;
+
+    /* Buffers, only needed for multi-packets */
+    pj_bool_t			     whole;
+    void			    *enc_buf;
+    unsigned			     enc_buf_size;
+    pj_bool_t			     enc_buf_is_keyframe;
+    unsigned			     enc_frame_len;
+    unsigned     		     enc_processed;
+    void			    *dec_buf;
+    unsigned			     dec_buf_size;
+    pj_timestamp		     last_dec_keyframe_ts; 
+
+    /* The ffmpeg codec states. */
+    AVCodec			    *enc;
+    AVCodec			    *dec;
+    AVCodecContext		    *enc_ctx;
+    AVCodecContext		    *dec_ctx;
+
+    /* The ffmpeg decoder cannot set the output format, so format conversion
+     * may be needed for post-decoding.
+     */
+    enum PixelFormat		     expected_dec_fmt;
+						/**< Expected output format of 
+						     ffmpeg decoder	    */
+
+    void			    *data;	/**< Codec specific data    */		    
+} ffmpeg_private;
+
+
+/* Shortcuts for packetize & unpacketize function declaration,
+ * as it has long params and is reused many times!
+ */
+#define FUNC_PACKETIZE(name) \
+    pj_status_t(name)(ffmpeg_private *ff, pj_uint8_t *bits, \
+		      pj_size_t bits_len, unsigned *bits_pos, \
+		      const pj_uint8_t **payload, pj_size_t *payload_len)
+
+#define FUNC_UNPACKETIZE(name) \
+    pj_status_t(name)(ffmpeg_private *ff, const pj_uint8_t *payload, \
+		      pj_size_t payload_len, pj_uint8_t *bits, \
+		      pj_size_t bits_len, unsigned *bits_pos)
+
+#define FUNC_FMT_MATCH(name) \
+    pj_status_t(name)(pj_pool_t *pool, \
+		      pjmedia_sdp_media *offer, unsigned o_fmt_idx, \
+		      pjmedia_sdp_media *answer, unsigned a_fmt_idx, \
+		      unsigned option)
+
+
+/* Type definition of codec specific functions */
+typedef FUNC_PACKETIZE(*func_packetize);
+typedef FUNC_UNPACKETIZE(*func_unpacketize);
+typedef pj_status_t (*func_preopen)	(ffmpeg_private *ff);
+typedef pj_status_t (*func_postopen)	(ffmpeg_private *ff);
+typedef FUNC_FMT_MATCH(*func_sdp_fmt_match);
+
+
+/* FFMPEG codec info */
+struct ffmpeg_codec_desc
+{
+    /* Predefined info */
+    pjmedia_vid_codec_info       info;
+    pjmedia_format_id		 base_fmt_id;	/**< Some codecs may be exactly
+						     same or compatible with
+						     another codec, base format
+						     will tell the initializer
+						     to copy this codec desc
+						     from its base format   */
+    pjmedia_rect_size            size;
+    pjmedia_ratio                fps;
+    pj_uint32_t			 avg_bps;
+    pj_uint32_t			 max_bps;
+    func_packetize		 packetize;
+    func_unpacketize		 unpacketize;
+    func_preopen		 preopen;
+    func_preopen		 postopen;
+    func_sdp_fmt_match		 sdp_fmt_match;
+    pjmedia_codec_fmtp		 dec_fmtp;
+
+    /* Init time defined info */
+    pj_bool_t			 enabled;
+    AVCodec                     *enc;
+    AVCodec                     *dec;
+};
+
+
+#if PJMEDIA_HAS_FFMPEG_CODEC_H264 && !LIBAVCODEC_VER_AT_LEAST(53,20)
+#   error "Must use libavcodec version 53.20 or later to enable FFMPEG H264"
+#endif
+
+/* H264 constants */
+#define PROFILE_H264_BASELINE		66
+#define PROFILE_H264_MAIN		77
+
+/* Codec specific functions */
+#if PJMEDIA_HAS_FFMPEG_CODEC_H264
+static pj_status_t h264_preopen(ffmpeg_private *ff);
+static pj_status_t h264_postopen(ffmpeg_private *ff);
+static FUNC_PACKETIZE(h264_packetize);
+static FUNC_UNPACKETIZE(h264_unpacketize);
+#endif
+
+static pj_status_t h263_preopen(ffmpeg_private *ff);
+static FUNC_PACKETIZE(h263_packetize);
+static FUNC_UNPACKETIZE(h263_unpacketize);
+
+
+/* Internal codec info */
+static ffmpeg_codec_desc codec_desc[] =
+{
+#if PJMEDIA_HAS_FFMPEG_CODEC_H264
+    {
+	{PJMEDIA_FORMAT_H264, PJMEDIA_RTP_PT_H264, {"H264",4},
+	 {"Constrained Baseline (level=30, pack=1)", 39}},
+	0,
+	{720, 480},	{15, 1},	256000, 256000,
+	&h264_packetize, &h264_unpacketize, &h264_preopen, &h264_postopen,
+	&pjmedia_vid_codec_h264_match_sdp,
+	/* Leading space for better compatibility (strange indeed!) */
+	{2, { {{"profile-level-id",16},    {"42e01e",6}}, 
+	      {{" packetization-mode",19},  {"1",1}}, } },
+    },
+#endif
+
+#if PJMEDIA_HAS_FFMPEG_CODEC_H263P
+    {
+	{PJMEDIA_FORMAT_H263P, PJMEDIA_RTP_PT_H263P, {"H263-1998",9}},
+	PJMEDIA_FORMAT_H263,
+	{352, 288},	{15, 1},	256000, 256000,
+	&h263_packetize, &h263_unpacketize, &h263_preopen, NULL, NULL,
+	{2, { {{"CIF",3},   {"1",1}}, 
+	      {{"QCIF",4},  {"1",1}}, } },
+    },
+#endif
+
+    {
+	{PJMEDIA_FORMAT_H263,	PJMEDIA_RTP_PT_H263,	{"H263",4}},
+    },
+    {
+	{PJMEDIA_FORMAT_H261,	PJMEDIA_RTP_PT_H261,	{"H261",4}},
+    },
+    {
+	{PJMEDIA_FORMAT_MJPEG,	PJMEDIA_RTP_PT_JPEG,	{"JPEG",4}},
+	PJMEDIA_FORMAT_MJPEG, {640, 480}, {25, 1},
+    },
+    {
+	{PJMEDIA_FORMAT_MPEG4,	0,			{"MP4V",4}},
+	PJMEDIA_FORMAT_MPEG4, {640, 480}, {25, 1},
+    },
+};
+
+#if PJMEDIA_HAS_FFMPEG_CODEC_H264
+
+typedef struct h264_data
+{
+    pjmedia_vid_codec_h264_fmtp	 fmtp;
+    pjmedia_h264_packetizer	*pktz;
+} h264_data;
+
+
+static pj_status_t h264_preopen(ffmpeg_private *ff)
+{
+    h264_data *data;
+    pjmedia_h264_packetizer_cfg pktz_cfg;
+    pj_status_t status;
+
+    data = PJ_POOL_ZALLOC_T(ff->pool, h264_data);
+    ff->data = data;
+
+    /* Parse remote fmtp */
+    status = pjmedia_vid_codec_h264_parse_fmtp(&ff->param.enc_fmtp,
+					       &data->fmtp);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Create packetizer */
+    pktz_cfg.mtu = ff->param.enc_mtu;
+#if 0
+    if (data->fmtp.packetization_mode == 0)
+	pktz_cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL;
+    else if (data->fmtp.packetization_mode == 1)
+	pktz_cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED;
+    else
+	return PJ_ENOTSUP;
+#else
+    if (data->fmtp.packetization_mode!=
+				PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL &&
+	data->fmtp.packetization_mode!=
+				PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED)
+    {
+	return PJ_ENOTSUP;
+    }
+    /* Better always send in single NAL mode for better compatibility */
+    pktz_cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL;
+#endif
+
+    status = pjmedia_h264_packetizer_create(ff->pool, &pktz_cfg, &data->pktz);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Apply SDP fmtp to format in codec param */
+    if (!ff->param.ignore_fmtp) {
+	status = pjmedia_vid_codec_h264_apply_fmtp(&ff->param);
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+
+    if (ff->param.dir & PJMEDIA_DIR_ENCODING) {
+	pjmedia_video_format_detail *vfd;
+	AVCodecContext *ctx = ff->enc_ctx;
+	const char *profile = NULL;
+
+	vfd = pjmedia_format_get_video_format_detail(&ff->param.enc_fmt, 
+						     PJ_TRUE);
+
+	/* Override generic params after applying SDP fmtp */
+	ctx->width = vfd->size.w;
+	ctx->height = vfd->size.h;
+	ctx->time_base.num = vfd->fps.denum;
+	ctx->time_base.den = vfd->fps.num;
+
+	/* Apply profile. */
+	ctx->profile  = data->fmtp.profile_idc;
+	switch (ctx->profile) {
+	case PROFILE_H264_BASELINE:
+	    profile = "baseline";
+	    break;
+	case PROFILE_H264_MAIN:
+	    profile = "main";
+	    break;
+	default:
+	    break;
+	}
+	if (profile && !AV_OPT_SET(ctx->priv_data, "profile", profile, 0))
+	{
+	    PJ_LOG(3, (THIS_FILE, "Failed to set H264 profile to '%s'",
+		       profile));
+	}
+
+	/* Apply profile constraint bits. */
+	//PJ_TODO(set_h264_constraint_bits_properly_in_ffmpeg);
+	if (data->fmtp.profile_iop) {
+#if defined(FF_PROFILE_H264_CONSTRAINED)
+	    ctx->profile |= FF_PROFILE_H264_CONSTRAINED;
+#endif
+	}
+
+	/* Apply profile level. */
+	ctx->level    = data->fmtp.level;
+
+	/* Limit NAL unit size as we prefer single NAL unit packetization */
+	if (!AV_OPT_SET_INT(ctx->priv_data, "slice-max-size", ff->param.enc_mtu))
+	{
+	    PJ_LOG(3, (THIS_FILE, "Failed to set H264 max NAL size to %d",
+		       ff->param.enc_mtu));
+	}
+
+	/* Apply intra-refresh */
+	if (!AV_OPT_SET_INT(ctx->priv_data, "intra-refresh", 1))
+	{
+	    PJ_LOG(3, (THIS_FILE, "Failed to set x264 intra-refresh"));
+	}
+
+	/* Misc x264 settings (performance, quality, latency, etc).
+	 * Let's just use the x264 predefined preset & tune.
+	 */
+	if (!AV_OPT_SET(ctx->priv_data, "preset", "veryfast", 0)) {
+	    PJ_LOG(3, (THIS_FILE, "Failed to set x264 preset 'veryfast'"));
+	}
+	if (!AV_OPT_SET(ctx->priv_data, "tune", "animation+zerolatency", 0)) {
+	    PJ_LOG(3, (THIS_FILE, "Failed to set x264 tune 'zerolatency'"));
+	}
+    }
+
+    if (ff->param.dir & PJMEDIA_DIR_DECODING) {
+	AVCodecContext *ctx = ff->dec_ctx;
+
+	/* Apply the "sprop-parameter-sets" fmtp from remote SDP to
+	 * extradata of ffmpeg codec context.
+	 */
+	if (data->fmtp.sprop_param_sets_len) {
+	    ctx->extradata_size = (int)data->fmtp.sprop_param_sets_len;
+	    ctx->extradata = data->fmtp.sprop_param_sets;
+	}
+    }
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t h264_postopen(ffmpeg_private *ff)
+{
+    h264_data *data = (h264_data*)ff->data;
+    PJ_UNUSED_ARG(data);
+    return PJ_SUCCESS;
+}
+
+static FUNC_PACKETIZE(h264_packetize)
+{
+    h264_data *data = (h264_data*)ff->data;
+    return pjmedia_h264_packetize(data->pktz, bits, bits_len, bits_pos,
+				  payload, payload_len);
+}
+
+static FUNC_UNPACKETIZE(h264_unpacketize)
+{
+    h264_data *data = (h264_data*)ff->data;
+    return pjmedia_h264_unpacketize(data->pktz, payload, payload_len,
+				    bits, bits_len, bits_pos);
+}
+
+#endif /* PJMEDIA_HAS_FFMPEG_CODEC_H264 */
+
+
+#if PJMEDIA_HAS_FFMPEG_CODEC_H263P
+
+typedef struct h263_data
+{
+    pjmedia_h263_packetizer	*pktz;
+} h263_data;
+
+/* H263 pre-open */
+static pj_status_t h263_preopen(ffmpeg_private *ff)
+{
+    h263_data *data;
+    pjmedia_h263_packetizer_cfg pktz_cfg;
+    pj_status_t status;
+
+    data = PJ_POOL_ZALLOC_T(ff->pool, h263_data);
+    ff->data = data;
+
+    /* Create packetizer */
+    pktz_cfg.mtu = ff->param.enc_mtu;
+    pktz_cfg.mode = PJMEDIA_H263_PACKETIZER_MODE_RFC4629;
+    status = pjmedia_h263_packetizer_create(ff->pool, &pktz_cfg, &data->pktz);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Apply fmtp settings to codec param */
+    if (!ff->param.ignore_fmtp) {
+	status = pjmedia_vid_codec_h263_apply_fmtp(&ff->param);
+    }
+
+    /* Override generic params after applying SDP fmtp */
+    if (ff->param.dir & PJMEDIA_DIR_ENCODING) {
+	pjmedia_video_format_detail *vfd;
+	AVCodecContext *ctx = ff->enc_ctx;
+
+	vfd = pjmedia_format_get_video_format_detail(&ff->param.enc_fmt, 
+						     PJ_TRUE);
+
+	/* Override generic params after applying SDP fmtp */
+	ctx->width = vfd->size.w;
+	ctx->height = vfd->size.h;
+	ctx->time_base.num = vfd->fps.denum;
+	ctx->time_base.den = vfd->fps.num;
+    }
+
+    return status;
+}
+
+static FUNC_PACKETIZE(h263_packetize)
+{
+    h263_data *data = (h263_data*)ff->data;
+    return pjmedia_h263_packetize(data->pktz, bits, bits_len, bits_pos,
+				  payload, payload_len);
+}
+
+static FUNC_UNPACKETIZE(h263_unpacketize)
+{
+    h263_data *data = (h263_data*)ff->data;
+    return pjmedia_h263_unpacketize(data->pktz, payload, payload_len,
+				    bits, bits_len, bits_pos);
+}
+
+#endif /* PJMEDIA_HAS_FFMPEG_CODEC_H263P */
+
+
+static const ffmpeg_codec_desc* find_codec_desc_by_info(
+			const pjmedia_vid_codec_info *info)
+{
+    int i;
+
+    for (i=0; i<PJ_ARRAY_SIZE(codec_desc); ++i) {
+	ffmpeg_codec_desc *desc = &codec_desc[i];
+
+	if (desc->enabled &&
+	    (desc->info.fmt_id == info->fmt_id) &&
+            ((desc->info.dir & info->dir) == info->dir) &&
+	    (desc->info.pt == info->pt) &&
+	    (desc->info.packings & info->packings))
+        {
+            return desc;
+        }
+    }
+
+    return NULL;
+}
+
+
+static int find_codec_idx_by_fmt_id(pjmedia_format_id fmt_id)
+{
+    int i;
+    for (i=0; i<PJ_ARRAY_SIZE(codec_desc); ++i) {
+	if (codec_desc[i].info.fmt_id == fmt_id)
+	    return i;
+    }
+
+    return -1;
+}
+
+
+/*
+ * Initialize and register FFMPEG codec factory to pjmedia endpoint.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_ffmpeg_vid_init(pjmedia_vid_codec_mgr *mgr,
+                                                  pj_pool_factory *pf)
+{
+    pj_pool_t *pool;
+    AVCodec *c;
+    pj_status_t status;
+    unsigned i;
+
+    if (ffmpeg_factory.pool != NULL) {
+	/* Already initialized. */
+	return PJ_SUCCESS;
+    }
+
+    if (!mgr) mgr = pjmedia_vid_codec_mgr_instance();
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    /* Create FFMPEG codec factory. */
+    ffmpeg_factory.base.op = &ffmpeg_factory_op;
+    ffmpeg_factory.base.factory_data = NULL;
+    ffmpeg_factory.mgr = mgr;
+    ffmpeg_factory.pf = pf;
+
+    pool = pj_pool_create(pf, "ffmpeg codec factory", 256, 256, NULL);
+    if (!pool)
+	return PJ_ENOMEM;
+
+    /* Create mutex. */
+    status = pj_mutex_create_simple(pool, "ffmpeg codec factory", 
+				    &ffmpeg_factory.mutex);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    pjmedia_ffmpeg_add_ref();
+#if !LIBAVCODEC_VER_AT_LEAST(53,20)
+    /* avcodec_init() dissappeared between version 53.20 and 54.15, not sure
+     * exactly when 
+     */
+    avcodec_init();
+#endif
+    avcodec_register_all();
+
+    /* Enum FFMPEG codecs */
+    for (c=av_codec_next(NULL); c; c=av_codec_next(c)) {
+        ffmpeg_codec_desc *desc;
+	pjmedia_format_id fmt_id;
+	int codec_info_idx;
+        
+#if LIBAVCODEC_VERSION_MAJOR <= 52
+#   define AVMEDIA_TYPE_VIDEO	CODEC_TYPE_VIDEO
+#endif
+        if (c->type != AVMEDIA_TYPE_VIDEO)
+            continue;
+
+        /* Video encoder and decoder are usually implemented in separate
+         * AVCodec instances. While the codec attributes (e.g: raw formats,
+	 * supported fps) are in the encoder.
+         */
+
+	//PJ_LOG(3, (THIS_FILE, "%s", c->name));
+	status = CodecID_to_pjmedia_format_id(c->id, &fmt_id);
+	/* Skip if format ID is unknown */
+	if (status != PJ_SUCCESS)
+	    continue;
+
+	codec_info_idx = find_codec_idx_by_fmt_id(fmt_id);
+	/* Skip if codec is unwanted by this wrapper (not listed in 
+	 * the codec info array)
+	 */
+	if (codec_info_idx < 0)
+	    continue;
+
+	desc = &codec_desc[codec_info_idx];
+
+	/* Skip duplicated codec implementation */
+	if ((AVCODEC_HAS_ENCODE(c) && (desc->info.dir & PJMEDIA_DIR_ENCODING))
+	    ||
+	    (AVCODEC_HAS_DECODE(c) && (desc->info.dir & PJMEDIA_DIR_DECODING)))
+	{
+	    continue;
+	}
+
+	/* Get raw/decoded format ids in the encoder */
+	if (c->pix_fmts && AVCODEC_HAS_ENCODE(c)) {
+	    pjmedia_format_id raw_fmt[PJMEDIA_VID_CODEC_MAX_DEC_FMT_CNT];
+	    unsigned raw_fmt_cnt = 0;
+	    unsigned raw_fmt_cnt_should_be = 0;
+	    const enum PixelFormat *p = c->pix_fmts;
+
+	    for(;(p && *p != -1) &&
+		 (raw_fmt_cnt < PJMEDIA_VID_CODEC_MAX_DEC_FMT_CNT);
+		 ++p)
+	    {
+		pjmedia_format_id fmt_id;
+
+		raw_fmt_cnt_should_be++;
+		status = PixelFormat_to_pjmedia_format_id(*p, &fmt_id);
+		if (status != PJ_SUCCESS) {
+		    PJ_LOG(6, (THIS_FILE, "Unrecognized ffmpeg pixel "
+			       "format %d", *p));
+		    continue;
+		}
+		
+		//raw_fmt[raw_fmt_cnt++] = fmt_id;
+		/* Disable some formats due to H.264 error:
+		 * x264 [error]: baseline profile doesn't support 4:4:4
+		 */
+		if (desc->info.pt != PJMEDIA_RTP_PT_H264 ||
+		    fmt_id != PJMEDIA_FORMAT_RGB24)
+		{
+		    raw_fmt[raw_fmt_cnt++] = fmt_id;
+		}
+	    }
+
+	    if (raw_fmt_cnt == 0) {
+		PJ_LOG(5, (THIS_FILE, "No recognized raw format "
+				      "for codec [%s/%s], codec ignored",
+				      c->name, c->long_name));
+		/* Skip this encoder */
+		continue;
+	    }
+
+	    if (raw_fmt_cnt < raw_fmt_cnt_should_be) {
+		PJ_LOG(6, (THIS_FILE, "Codec [%s/%s] have %d raw formats, "
+				      "recognized only %d raw formats",
+				      c->name, c->long_name,
+				      raw_fmt_cnt_should_be, raw_fmt_cnt));
+	    }
+
+	    desc->info.dec_fmt_id_cnt = raw_fmt_cnt;
+	    pj_memcpy(desc->info.dec_fmt_id, raw_fmt, 
+		      sizeof(raw_fmt[0])*raw_fmt_cnt);
+	}
+
+	/* Get supported framerates */
+	if (c->supported_framerates) {
+	    const AVRational *fr = c->supported_framerates;
+	    while ((fr->num != 0 || fr->den != 0) && 
+		   desc->info.fps_cnt < PJMEDIA_VID_CODEC_MAX_FPS_CNT)
+	    {
+		desc->info.fps[desc->info.fps_cnt].num = fr->num;
+		desc->info.fps[desc->info.fps_cnt].denum = fr->den;
+		++desc->info.fps_cnt;
+		++fr;
+	    }
+	}
+
+	/* Get ffmpeg encoder instance */
+	if (AVCODEC_HAS_ENCODE(c) && !desc->enc) {
+            desc->info.dir |= PJMEDIA_DIR_ENCODING;
+            desc->enc = c;
+        }
+	
+	/* Get ffmpeg decoder instance */
+        if (AVCODEC_HAS_DECODE(c) && !desc->dec) {
+            desc->info.dir |= PJMEDIA_DIR_DECODING;
+            desc->dec = c;
+        }
+
+	/* Enable this codec when any ffmpeg codec instance are recognized
+	 * and the supported raw formats info has been collected.
+	 */
+	if ((desc->dec || desc->enc) && desc->info.dec_fmt_id_cnt)
+	{
+	    desc->enabled = PJ_TRUE;
+	}
+
+	/* Normalize default value of clock rate */
+	if (desc->info.clock_rate == 0)
+	    desc->info.clock_rate = 90000;
+
+	/* Set supported packings */
+	desc->info.packings |= PJMEDIA_VID_PACKING_WHOLE;
+	if (desc->packetize && desc->unpacketize)
+	    desc->info.packings |= PJMEDIA_VID_PACKING_PACKETS;
+
+    }
+
+    /* Review all codecs for applying base format, registering format match for
+     * SDP negotiation, etc.
+     */
+    for (i = 0; i < PJ_ARRAY_SIZE(codec_desc); ++i) {
+	ffmpeg_codec_desc *desc = &codec_desc[i];
+
+	/* Init encoder/decoder description from base format */
+	if (desc->base_fmt_id && (!desc->dec || !desc->enc)) {
+	    ffmpeg_codec_desc *base_desc = NULL;
+	    int base_desc_idx;
+	    pjmedia_dir copied_dir = PJMEDIA_DIR_NONE;
+
+	    base_desc_idx = find_codec_idx_by_fmt_id(desc->base_fmt_id);
+	    if (base_desc_idx != -1)
+		base_desc = &codec_desc[base_desc_idx];
+	    if (!base_desc || !base_desc->enabled)
+		continue;
+
+	    /* Copy description from base codec */
+	    if (!desc->info.dec_fmt_id_cnt) {
+		desc->info.dec_fmt_id_cnt = base_desc->info.dec_fmt_id_cnt;
+		pj_memcpy(desc->info.dec_fmt_id, base_desc->info.dec_fmt_id, 
+			  sizeof(pjmedia_format_id)*desc->info.dec_fmt_id_cnt);
+	    }
+	    if (!desc->info.fps_cnt) {
+		desc->info.fps_cnt = base_desc->info.fps_cnt;
+		pj_memcpy(desc->info.fps, base_desc->info.fps, 
+			  sizeof(desc->info.fps[0])*desc->info.fps_cnt);
+	    }
+	    if (!desc->info.clock_rate) {
+		desc->info.clock_rate = base_desc->info.clock_rate;
+	    }
+	    if (!desc->dec && base_desc->dec) {
+		copied_dir |= PJMEDIA_DIR_DECODING;
+		desc->dec = base_desc->dec;
+	    }
+	    if (!desc->enc && base_desc->enc) {
+		copied_dir |= PJMEDIA_DIR_ENCODING;
+		desc->enc = base_desc->enc;
+	    }
+
+	    desc->info.dir |= copied_dir;
+	    desc->enabled = (desc->info.dir != PJMEDIA_DIR_NONE);
+
+	    /* Set supported packings */
+	    desc->info.packings |= PJMEDIA_VID_PACKING_WHOLE;
+	    if (desc->packetize && desc->unpacketize)
+		desc->info.packings |= PJMEDIA_VID_PACKING_PACKETS;
+
+	    if (copied_dir != PJMEDIA_DIR_NONE) {
+		const char *dir_name[] = {NULL, "encoder", "decoder", "codec"};
+		PJ_LOG(5, (THIS_FILE, "The %.*s %s is using base codec (%.*s)",
+			   desc->info.encoding_name.slen,
+			   desc->info.encoding_name.ptr,
+			   dir_name[copied_dir],
+			   base_desc->info.encoding_name.slen,
+			   base_desc->info.encoding_name.ptr));
+	    }
+        }
+
+	/* Registering format match for SDP negotiation */
+	if (desc->sdp_fmt_match) {
+	    status = pjmedia_sdp_neg_register_fmt_match_cb(
+						&desc->info.encoding_name,
+						desc->sdp_fmt_match);
+	    pj_assert(status == PJ_SUCCESS);
+	}
+
+	/* Print warning about missing encoder/decoder */
+	if (!desc->enc) {
+	    PJ_LOG(4, (THIS_FILE, "Cannot find %.*s encoder in ffmpeg library",
+		       desc->info.encoding_name.slen,
+		       desc->info.encoding_name.ptr));
+	}
+	if (!desc->dec) {
+	    PJ_LOG(4, (THIS_FILE, "Cannot find %.*s decoder in ffmpeg library",
+		       desc->info.encoding_name.slen,
+		       desc->info.encoding_name.ptr));
+	}
+    }
+
+    /* Register codec factory to codec manager. */
+    status = pjmedia_vid_codec_mgr_register_factory(mgr, 
+						    &ffmpeg_factory.base);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    ffmpeg_factory.pool = pool;
+
+    /* Done. */
+    return PJ_SUCCESS;
+
+on_error:
+    pj_pool_release(pool);
+    return status;
+}
+
+/*
+ * Unregister FFMPEG codecs factory from pjmedia endpoint.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_ffmpeg_vid_deinit(void)
+{
+    pj_status_t status = PJ_SUCCESS;
+
+    if (ffmpeg_factory.pool == NULL) {
+	/* Already deinitialized */
+	return PJ_SUCCESS;
+    }
+
+    pj_mutex_lock(ffmpeg_factory.mutex);
+
+    /* Unregister FFMPEG codecs factory. */
+    status = pjmedia_vid_codec_mgr_unregister_factory(ffmpeg_factory.mgr,
+						      &ffmpeg_factory.base);
+
+    /* Destroy mutex. */
+    pj_mutex_destroy(ffmpeg_factory.mutex);
+
+    /* Destroy pool. */
+    pj_pool_release(ffmpeg_factory.pool);
+    ffmpeg_factory.pool = NULL;
+
+    pjmedia_ffmpeg_dec_ref();
+
+    return status;
+}
+
+
+/* 
+ * Check if factory can allocate the specified codec. 
+ */
+static pj_status_t ffmpeg_test_alloc( pjmedia_vid_codec_factory *factory, 
+				      const pjmedia_vid_codec_info *info )
+{
+    const ffmpeg_codec_desc *desc;
+
+    PJ_ASSERT_RETURN(factory==&ffmpeg_factory.base, PJ_EINVAL);
+    PJ_ASSERT_RETURN(info, PJ_EINVAL);
+
+    desc = find_codec_desc_by_info(info);
+    if (!desc) {
+        return PJMEDIA_CODEC_EUNSUP;
+    }
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Generate default attribute.
+ */
+static pj_status_t ffmpeg_default_attr( pjmedia_vid_codec_factory *factory, 
+				        const pjmedia_vid_codec_info *info, 
+				        pjmedia_vid_codec_param *attr )
+{
+    const ffmpeg_codec_desc *desc;
+    unsigned i;
+
+    PJ_ASSERT_RETURN(factory==&ffmpeg_factory.base, PJ_EINVAL);
+    PJ_ASSERT_RETURN(info && attr, PJ_EINVAL);
+
+    desc = find_codec_desc_by_info(info);
+    if (!desc) {
+        return PJMEDIA_CODEC_EUNSUP;
+    }
+
+    pj_bzero(attr, sizeof(pjmedia_vid_codec_param));
+
+    /* Scan the requested packings and use the lowest number */
+    attr->packing = 0;
+    for (i=0; i<15; ++i) {
+	unsigned packing = (1 << i);
+	if ((desc->info.packings & info->packings) & packing) {
+	    attr->packing = (pjmedia_vid_packing)packing;
+	    break;
+	}
+    }
+    if (attr->packing == 0) {
+	/* No supported packing in info */
+	return PJMEDIA_CODEC_EUNSUP;
+    }
+
+    /* Direction */
+    attr->dir = desc->info.dir;
+
+    /* Encoded format */
+    pjmedia_format_init_video(&attr->enc_fmt, desc->info.fmt_id,
+                              desc->size.w, desc->size.h,
+			      desc->fps.num, desc->fps.denum);
+
+    /* Decoded format */
+    pjmedia_format_init_video(&attr->dec_fmt, desc->info.dec_fmt_id[0],
+                              desc->size.w, desc->size.h,
+			      desc->fps.num, desc->fps.denum);
+
+    /* Decoding fmtp */
+    attr->dec_fmtp = desc->dec_fmtp;
+
+    /* Bitrate */
+    attr->enc_fmt.det.vid.avg_bps = desc->avg_bps;
+    attr->enc_fmt.det.vid.max_bps = desc->max_bps;
+
+    /* Encoding MTU */
+    attr->enc_mtu = PJMEDIA_MAX_VID_PAYLOAD_SIZE;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Enum codecs supported by this factory.
+ */
+static pj_status_t ffmpeg_enum_codecs( pjmedia_vid_codec_factory *factory,
+				       unsigned *count, 
+				       pjmedia_vid_codec_info codecs[])
+{
+    unsigned i, max_cnt;
+
+    PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory == &ffmpeg_factory.base, PJ_EINVAL);
+
+    max_cnt = PJ_MIN(*count, PJ_ARRAY_SIZE(codec_desc));
+    *count = 0;
+
+    for (i=0; i<max_cnt; ++i) {
+	if (codec_desc[i].enabled) {
+	    pj_memcpy(&codecs[*count], &codec_desc[i].info, 
+		      sizeof(pjmedia_vid_codec_info));
+	    (*count)++;
+	}
+    }
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Allocate a new codec instance.
+ */
+static pj_status_t ffmpeg_alloc_codec( pjmedia_vid_codec_factory *factory, 
+				       const pjmedia_vid_codec_info *info,
+				       pjmedia_vid_codec **p_codec)
+{
+    ffmpeg_private *ff;
+    const ffmpeg_codec_desc *desc;
+    pjmedia_vid_codec *codec;
+    pj_pool_t *pool = NULL;
+    pj_status_t status = PJ_SUCCESS;
+
+    PJ_ASSERT_RETURN(factory && info && p_codec, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory == &ffmpeg_factory.base, PJ_EINVAL);
+
+    desc = find_codec_desc_by_info(info);
+    if (!desc) {
+        return PJMEDIA_CODEC_EUNSUP;
+    }
+
+    /* Create pool for codec instance */
+    pool = pj_pool_create(ffmpeg_factory.pf, "ffmpeg codec", 512, 512, NULL);
+    codec = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_codec);
+    if (!codec) {
+        status = PJ_ENOMEM;
+        goto on_error;
+    }
+    codec->op = &ffmpeg_op;
+    codec->factory = factory;
+    ff = PJ_POOL_ZALLOC_T(pool, ffmpeg_private);
+    if (!ff) {
+        status = PJ_ENOMEM;
+        goto on_error;
+    }
+    codec->codec_data = ff;
+    ff->pool = pool;
+    ff->enc = desc->enc;
+    ff->dec = desc->dec;
+    ff->desc = desc;
+
+    *p_codec = codec;
+    return PJ_SUCCESS;
+
+on_error:
+    if (pool)
+        pj_pool_release(pool);
+    return status;
+}
+
+/*
+ * Free codec.
+ */
+static pj_status_t ffmpeg_dealloc_codec( pjmedia_vid_codec_factory *factory, 
+				         pjmedia_vid_codec *codec )
+{
+    ffmpeg_private *ff;
+    pj_pool_t *pool;
+
+    PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory == &ffmpeg_factory.base, PJ_EINVAL);
+
+    /* Close codec, if it's not closed. */
+    ff = (ffmpeg_private*) codec->codec_data;
+    pool = ff->pool;
+    codec->codec_data = NULL;
+    pj_pool_release(pool);
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Init codec.
+ */
+static pj_status_t ffmpeg_codec_init( pjmedia_vid_codec *codec, 
+				      pj_pool_t *pool )
+{
+    PJ_UNUSED_ARG(codec);
+    PJ_UNUSED_ARG(pool);
+    return PJ_SUCCESS;
+}
+
+static void print_ffmpeg_err(int err)
+{
+#if LIBAVCODEC_VER_AT_LEAST(52,72)
+    char errbuf[512];
+    if (av_strerror(err, errbuf, sizeof(errbuf)) >= 0)
+        PJ_LOG(5, (THIS_FILE, "ffmpeg err %d: %s", err, errbuf));
+#else
+    PJ_LOG(5, (THIS_FILE, "ffmpeg err %d", err));
+#endif
+
+}
+
+static pj_status_t open_ffmpeg_codec(ffmpeg_private *ff,
+                                     pj_mutex_t *ff_mutex)
+{
+    enum PixelFormat pix_fmt;
+    pjmedia_video_format_detail *vfd;
+    pj_bool_t enc_opened = PJ_FALSE, dec_opened = PJ_FALSE;
+    pj_status_t status;
+
+    /* Get decoded pixel format */
+    status = pjmedia_format_id_to_PixelFormat(ff->param.dec_fmt.id,
+                                              &pix_fmt);
+    if (status != PJ_SUCCESS)
+        return status;
+    ff->expected_dec_fmt = pix_fmt;
+
+    /* Get video format detail for shortcut access to encoded format */
+    vfd = pjmedia_format_get_video_format_detail(&ff->param.enc_fmt, 
+						 PJ_TRUE);
+
+    /* Allocate ffmpeg codec context */
+    if (ff->param.dir & PJMEDIA_DIR_ENCODING) {
+#if LIBAVCODEC_VER_AT_LEAST(53,20)
+	ff->enc_ctx = avcodec_alloc_context3(ff->enc);
+#else
+	ff->enc_ctx = avcodec_alloc_context();
+#endif
+	if (ff->enc_ctx == NULL)
+	    goto on_error;
+    }
+    if (ff->param.dir & PJMEDIA_DIR_DECODING) {
+#if LIBAVCODEC_VER_AT_LEAST(53,20)
+	ff->dec_ctx = avcodec_alloc_context3(ff->dec);
+#else
+	ff->dec_ctx = avcodec_alloc_context();
+#endif
+	if (ff->dec_ctx == NULL)
+	    goto on_error;
+    }
+
+    /* Init generic encoder params */
+    if (ff->param.dir & PJMEDIA_DIR_ENCODING) {
+        AVCodecContext *ctx = ff->enc_ctx;
+
+        ctx->pix_fmt = pix_fmt;
+	ctx->width = vfd->size.w;
+	ctx->height = vfd->size.h;
+	ctx->time_base.num = vfd->fps.denum;
+	ctx->time_base.den = vfd->fps.num;
+	if (vfd->avg_bps) {
+	    ctx->bit_rate = vfd->avg_bps;
+	    if (vfd->max_bps > vfd->avg_bps)
+		ctx->bit_rate_tolerance = vfd->max_bps - vfd->avg_bps;
+	}
+	ctx->strict_std_compliance = FF_COMPLIANCE_STRICT;
+        ctx->workaround_bugs = FF_BUG_AUTODETECT;
+        ctx->opaque = ff;
+
+	/* Set no delay, note that this may cause some codec functionals
+	 * not working (e.g: rate control).
+	 */
+#if LIBAVCODEC_VER_AT_LEAST(52,113) && !LIBAVCODEC_VER_AT_LEAST(53,20)
+	ctx->rc_lookahead = 0;
+#endif
+    }
+
+    /* Init generic decoder params */
+    if (ff->param.dir & PJMEDIA_DIR_DECODING) {
+	AVCodecContext *ctx = ff->dec_ctx;
+
+	/* Width/height may be overriden by ffmpeg after first decoding. */
+	ctx->width  = ctx->coded_width  = ff->param.dec_fmt.det.vid.size.w;
+	ctx->height = ctx->coded_height = ff->param.dec_fmt.det.vid.size.h;
+	ctx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
+        ctx->workaround_bugs = FF_BUG_AUTODETECT;
+        ctx->opaque = ff;
+    }
+
+    /* Override generic params or apply specific params before opening
+     * the codec.
+     */
+    if (ff->desc->preopen) {
+	status = (*ff->desc->preopen)(ff);
+	if (status != PJ_SUCCESS)
+	    goto on_error;
+    }
+
+    /* Open encoder */
+    if (ff->param.dir & PJMEDIA_DIR_ENCODING) {
+	int err;
+
+	pj_mutex_lock(ff_mutex);
+	err = AVCODEC_OPEN(ff->enc_ctx, ff->enc);
+        pj_mutex_unlock(ff_mutex);
+        if (err < 0) {
+            print_ffmpeg_err(err);
+            status = PJMEDIA_CODEC_EFAILED;
+	    goto on_error;
+        }
+	enc_opened = PJ_TRUE;
+    }
+
+    /* Open decoder */
+    if (ff->param.dir & PJMEDIA_DIR_DECODING) {
+	int err;
+
+	pj_mutex_lock(ff_mutex);
+	err = AVCODEC_OPEN(ff->dec_ctx, ff->dec);
+        pj_mutex_unlock(ff_mutex);
+        if (err < 0) {
+            print_ffmpeg_err(err);
+            status = PJMEDIA_CODEC_EFAILED;
+	    goto on_error;
+        }
+	dec_opened = PJ_TRUE;
+    }
+
+    /* Let the codec apply specific params after the codec opened */
+    if (ff->desc->postopen) {
+	status = (*ff->desc->postopen)(ff);
+	if (status != PJ_SUCCESS)
+	    goto on_error;
+    }
+
+    return PJ_SUCCESS;
+
+on_error:
+    if (ff->enc_ctx) {
+	if (enc_opened)
+	    avcodec_close(ff->enc_ctx);
+	av_free(ff->enc_ctx);
+	ff->enc_ctx = NULL;
+    }
+    if (ff->dec_ctx) {
+	if (dec_opened)
+	    avcodec_close(ff->dec_ctx);
+	av_free(ff->dec_ctx);
+	ff->dec_ctx = NULL;
+    }
+    return status;
+}
+
+/*
+ * Open codec.
+ */
+static pj_status_t ffmpeg_codec_open( pjmedia_vid_codec *codec, 
+				      pjmedia_vid_codec_param *attr )
+{
+    ffmpeg_private *ff;
+    pj_status_t status;
+    pj_mutex_t *ff_mutex;
+
+    PJ_ASSERT_RETURN(codec && attr, PJ_EINVAL);
+    ff = (ffmpeg_private*)codec->codec_data;
+
+    pj_memcpy(&ff->param, attr, sizeof(*attr));
+
+    /* Normalize encoding MTU in codec param */
+    if (attr->enc_mtu > PJMEDIA_MAX_VID_PAYLOAD_SIZE)
+	attr->enc_mtu = PJMEDIA_MAX_VID_PAYLOAD_SIZE;
+
+    /* Open the codec */
+    ff_mutex = ((struct ffmpeg_factory*)codec->factory)->mutex;
+    status = open_ffmpeg_codec(ff, ff_mutex);
+    if (status != PJ_SUCCESS)
+        goto on_error;
+
+    /* Init format info and apply-param of decoder */
+    ff->dec_vfi = pjmedia_get_video_format_info(NULL, ff->param.dec_fmt.id);
+    if (!ff->dec_vfi) {
+        status = PJ_EINVAL;
+        goto on_error;
+    }
+    pj_bzero(&ff->dec_vafp, sizeof(ff->dec_vafp));
+    ff->dec_vafp.size = ff->param.dec_fmt.det.vid.size;
+    ff->dec_vafp.buffer = NULL;
+    status = (*ff->dec_vfi->apply_fmt)(ff->dec_vfi, &ff->dec_vafp);
+    if (status != PJ_SUCCESS) {
+        goto on_error;
+    }
+
+    /* Init format info and apply-param of encoder */
+    ff->enc_vfi = pjmedia_get_video_format_info(NULL, ff->param.dec_fmt.id);
+    if (!ff->enc_vfi) {
+        status = PJ_EINVAL;
+        goto on_error;
+    }
+    pj_bzero(&ff->enc_vafp, sizeof(ff->enc_vafp));
+    ff->enc_vafp.size = ff->param.enc_fmt.det.vid.size;
+    ff->enc_vafp.buffer = NULL;
+    status = (*ff->enc_vfi->apply_fmt)(ff->enc_vfi, &ff->enc_vafp);
+    if (status != PJ_SUCCESS) {
+        goto on_error;
+    }
+
+    /* Alloc buffers if needed */
+    ff->whole = (ff->param.packing == PJMEDIA_VID_PACKING_WHOLE);
+    if (!ff->whole) {
+	ff->enc_buf_size = (unsigned)ff->enc_vafp.framebytes;
+	ff->enc_buf = pj_pool_alloc(ff->pool, ff->enc_buf_size);
+
+	ff->dec_buf_size = (unsigned)ff->dec_vafp.framebytes;
+	ff->dec_buf = pj_pool_alloc(ff->pool, ff->dec_buf_size);
+    }
+
+    /* Update codec attributes, e.g: encoding format may be changed by
+     * SDP fmtp negotiation.
+     */
+    pj_memcpy(attr, &ff->param, sizeof(*attr));
+
+    return PJ_SUCCESS;
+
+on_error:
+    ffmpeg_codec_close(codec);
+    return status;
+}
+
+/*
+ * Close codec.
+ */
+static pj_status_t ffmpeg_codec_close( pjmedia_vid_codec *codec )
+{
+    ffmpeg_private *ff;
+    pj_mutex_t *ff_mutex;
+
+    PJ_ASSERT_RETURN(codec, PJ_EINVAL);
+    ff = (ffmpeg_private*)codec->codec_data;
+    ff_mutex = ((struct ffmpeg_factory*)codec->factory)->mutex;
+
+    pj_mutex_lock(ff_mutex);
+    if (ff->enc_ctx) {
+        avcodec_close(ff->enc_ctx);
+        av_free(ff->enc_ctx);
+    }
+    if (ff->dec_ctx && ff->dec_ctx!=ff->enc_ctx) {
+        avcodec_close(ff->dec_ctx);
+        av_free(ff->dec_ctx);
+    }
+    ff->enc_ctx = NULL;
+    ff->dec_ctx = NULL;
+    pj_mutex_unlock(ff_mutex);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Modify codec settings.
+ */
+static pj_status_t  ffmpeg_codec_modify( pjmedia_vid_codec *codec, 
+				         const pjmedia_vid_codec_param *attr)
+{
+    ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
+
+    PJ_UNUSED_ARG(attr);
+    PJ_UNUSED_ARG(ff);
+
+    return PJ_ENOTSUP;
+}
+
+static pj_status_t  ffmpeg_codec_get_param(pjmedia_vid_codec *codec,
+					   pjmedia_vid_codec_param *param)
+{
+    ffmpeg_private *ff;
+
+    PJ_ASSERT_RETURN(codec && param, PJ_EINVAL);
+
+    ff = (ffmpeg_private*)codec->codec_data;
+    pj_memcpy(param, &ff->param, sizeof(*param));
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t  ffmpeg_packetize ( pjmedia_vid_codec *codec,
+                                       pj_uint8_t *bits,
+                                       pj_size_t bits_len,
+                                       unsigned *bits_pos,
+                                       const pj_uint8_t **payload,
+                                       pj_size_t *payload_len)
+{
+    ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
+
+    if (ff->desc->packetize) {
+	return (*ff->desc->packetize)(ff, bits, bits_len, bits_pos,
+                                      payload, payload_len);
+    }
+
+    return PJ_ENOTSUP;
+}
+
+static pj_status_t  ffmpeg_unpacketize(pjmedia_vid_codec *codec,
+                                       const pj_uint8_t *payload,
+                                       pj_size_t   payload_len,
+                                       pj_uint8_t *bits,
+                                       pj_size_t   bits_len,
+				       unsigned   *bits_pos)
+{
+    ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
+
+    if (ff->desc->unpacketize) {
+        return (*ff->desc->unpacketize)(ff, payload, payload_len,
+                                        bits, bits_len, bits_pos);
+    }
+    
+    return PJ_ENOTSUP;
+}
+
+
+/*
+ * Encode frames.
+ */
+static pj_status_t ffmpeg_codec_encode_whole(pjmedia_vid_codec *codec,
+					     const pjmedia_vid_encode_opt *opt,
+					     const pjmedia_frame *input,
+					     unsigned output_buf_len,
+					     pjmedia_frame *output)
+{
+    ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
+    pj_uint8_t *p = (pj_uint8_t*)input->buf;
+    AVFrame avframe;
+    AVPacket avpacket;
+    int err, got_packet;
+    //AVRational src_timebase;
+    /* For some reasons (e.g: SSE/MMX usage), the avcodec_encode_video() must
+     * have stack aligned to 16 bytes. Let's try to be safe by preparing the
+     * 16-bytes aligned stack here, in case it's not managed by the ffmpeg.
+     */
+    PJ_ALIGN_DATA(pj_uint32_t i[4], 16);
+
+    if ((long)(pj_ssize_t)i & 0xF) {
+	PJ_LOG(2,(THIS_FILE, "Stack alignment fails"));
+    }
+
+    /* Check if encoder has been opened */
+    PJ_ASSERT_RETURN(ff->enc_ctx, PJ_EINVALIDOP);
+
+    avcodec_get_frame_defaults(&avframe);
+
+    // Let ffmpeg manage the timestamps
+    /*
+    src_timebase.num = 1;
+    src_timebase.den = ff->desc->info.clock_rate;
+    avframe.pts = av_rescale_q(input->timestamp.u64, src_timebase,
+			       ff->enc_ctx->time_base);
+    */
+    
+    for (i[0] = 0; i[0] < ff->enc_vfi->plane_cnt; ++i[0]) {
+        avframe.data[i[0]] = p;
+        avframe.linesize[i[0]] = ff->enc_vafp.strides[i[0]];
+        p += ff->enc_vafp.plane_bytes[i[0]];
+    }
+
+    /* Force keyframe */
+    if (opt && opt->force_keyframe) {
+#if LIBAVCODEC_VER_AT_LEAST(53,20)
+	avframe.pict_type = AV_PICTURE_TYPE_I;
+#else
+	avframe.pict_type = FF_I_TYPE;
+#endif
+    }
+
+    av_init_packet(&avpacket);
+    avpacket.data = (pj_uint8_t*)output->buf;
+    avpacket.size = output_buf_len;
+
+#if LIBAVCODEC_VER_AT_LEAST(54,15)
+    err = avcodec_encode_video2(ff->enc_ctx, &avpacket, &avframe, &got_packet);
+    if (!err && got_packet)
+	err = avpacket.size;
+#else
+    PJ_UNUSED_ARG(got_packet);
+    err = avcodec_encode_video(ff->enc_ctx, avpacket.data, avpacket.size, &avframe);
+#endif
+
+    if (err < 0) {
+        print_ffmpeg_err(err);
+        return PJMEDIA_CODEC_EFAILED;
+    } else {
+        output->size = err;
+	output->bit_info = 0;
+	if (ff->enc_ctx->coded_frame->key_frame)
+	    output->bit_info |= PJMEDIA_VID_FRM_KEYFRAME;
+    }
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t ffmpeg_codec_encode_begin(pjmedia_vid_codec *codec,
+					     const pjmedia_vid_encode_opt *opt,
+					     const pjmedia_frame *input,
+					     unsigned out_size,
+					     pjmedia_frame *output,
+					     pj_bool_t *has_more)
+{
+    ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
+    pj_status_t status;
+
+    *has_more = PJ_FALSE;
+
+    if (ff->whole) {
+	status = ffmpeg_codec_encode_whole(codec, opt, input, out_size,
+					   output);
+    } else {
+	pjmedia_frame whole_frm;
+        const pj_uint8_t *payload;
+        pj_size_t payload_len;
+
+	pj_bzero(&whole_frm, sizeof(whole_frm));
+	whole_frm.buf = ff->enc_buf;
+	whole_frm.size = ff->enc_buf_size;
+	status = ffmpeg_codec_encode_whole(codec, opt, input,
+	                                   (unsigned)whole_frm.size, 
+					   &whole_frm);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	ff->enc_buf_is_keyframe = (whole_frm.bit_info & 
+				   PJMEDIA_VID_FRM_KEYFRAME);
+	ff->enc_frame_len = (unsigned)whole_frm.size;
+	ff->enc_processed = 0;
+        status = ffmpeg_packetize(codec, (pj_uint8_t*)whole_frm.buf,
+                                  whole_frm.size, &ff->enc_processed,
+				  &payload, &payload_len);
+        if (status != PJ_SUCCESS)
+            return status;
+
+        if (out_size < payload_len)
+            return PJMEDIA_CODEC_EFRMTOOSHORT;
+
+        output->type = PJMEDIA_FRAME_TYPE_VIDEO;
+        pj_memcpy(output->buf, payload, payload_len);
+        output->size = payload_len;
+
+	if (ff->enc_buf_is_keyframe)
+	    output->bit_info |= PJMEDIA_VID_FRM_KEYFRAME;
+
+        *has_more = (ff->enc_processed < ff->enc_frame_len);
+    }
+
+    return status;
+}
+
+static pj_status_t ffmpeg_codec_encode_more(pjmedia_vid_codec *codec,
+					    unsigned out_size,
+					    pjmedia_frame *output,
+					    pj_bool_t *has_more)
+{
+    ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
+    const pj_uint8_t *payload;
+    pj_size_t payload_len;
+    pj_status_t status;
+
+    *has_more = PJ_FALSE;
+
+    if (ff->enc_processed >= ff->enc_frame_len) {
+	/* No more frame */
+	return PJ_EEOF;
+    }
+
+    status = ffmpeg_packetize(codec, (pj_uint8_t*)ff->enc_buf,
+                              ff->enc_frame_len, &ff->enc_processed,
+                              &payload, &payload_len);
+    if (status != PJ_SUCCESS)
+        return status;
+
+    if (out_size < payload_len)
+        return PJMEDIA_CODEC_EFRMTOOSHORT;
+
+    output->type = PJMEDIA_FRAME_TYPE_VIDEO;
+    pj_memcpy(output->buf, payload, payload_len);
+    output->size = payload_len;
+
+    if (ff->enc_buf_is_keyframe)
+	output->bit_info |= PJMEDIA_VID_FRM_KEYFRAME;
+
+    *has_more = (ff->enc_processed < ff->enc_frame_len);
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t check_decode_result(pjmedia_vid_codec *codec,
+				       const pj_timestamp *ts,
+				       pj_bool_t got_keyframe)
+{
+    ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
+    pjmedia_video_apply_fmt_param *vafp = &ff->dec_vafp;
+    pjmedia_event event;
+
+    /* Check for format change.
+     * Decoder output format is set by libavcodec, in case it is different
+     * to the configured param.
+     */
+    if (ff->dec_ctx->pix_fmt != ff->expected_dec_fmt ||
+	ff->dec_ctx->width != (int)vafp->size.w ||
+	ff->dec_ctx->height != (int)vafp->size.h)
+    {
+	pjmedia_format_id new_fmt_id;
+	pj_status_t status;
+
+	/* Get current raw format id from ffmpeg decoder context */
+	status = PixelFormat_to_pjmedia_format_id(ff->dec_ctx->pix_fmt, 
+						  &new_fmt_id);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	/* Update decoder format in param */
+		ff->param.dec_fmt.id = new_fmt_id;
+	ff->param.dec_fmt.det.vid.size.w = ff->dec_ctx->width;
+	ff->param.dec_fmt.det.vid.size.h = ff->dec_ctx->height;
+	ff->expected_dec_fmt = ff->dec_ctx->pix_fmt;
+
+	/* Re-init format info and apply-param of decoder */
+	ff->dec_vfi = pjmedia_get_video_format_info(NULL, ff->param.dec_fmt.id);
+	if (!ff->dec_vfi)
+	    return PJ_ENOTSUP;
+	pj_bzero(&ff->dec_vafp, sizeof(ff->dec_vafp));
+	ff->dec_vafp.size = ff->param.dec_fmt.det.vid.size;
+	ff->dec_vafp.buffer = NULL;
+	status = (*ff->dec_vfi->apply_fmt)(ff->dec_vfi, &ff->dec_vafp);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	/* Realloc buffer if necessary */
+	if (ff->dec_vafp.framebytes > ff->dec_buf_size) {
+	    PJ_LOG(5,(THIS_FILE, "Reallocating decoding buffer %u --> %u",
+		       (unsigned)ff->dec_buf_size,
+		       (unsigned)ff->dec_vafp.framebytes));
+	    ff->dec_buf_size = (unsigned)ff->dec_vafp.framebytes;
+	    ff->dec_buf = pj_pool_alloc(ff->pool, ff->dec_buf_size);
+	}
+
+	/* Broadcast format changed event */
+	pjmedia_event_init(&event, PJMEDIA_EVENT_FMT_CHANGED, ts, codec);
+	event.data.fmt_changed.dir = PJMEDIA_DIR_DECODING;
+	pj_memcpy(&event.data.fmt_changed.new_fmt, &ff->param.dec_fmt,
+		  sizeof(ff->param.dec_fmt));
+	pjmedia_event_publish(NULL, codec, &event, 0);
+    }
+
+    /* Check for missing/found keyframe */
+    if (got_keyframe) {
+	pj_get_timestamp(&ff->last_dec_keyframe_ts);
+
+	/* Broadcast keyframe event */
+        pjmedia_event_init(&event, PJMEDIA_EVENT_KEYFRAME_FOUND, ts, codec);
+        pjmedia_event_publish(NULL, codec, &event, 0);
+    } else if (ff->last_dec_keyframe_ts.u64 == 0) {
+	/* Broadcast missing keyframe event */
+	pjmedia_event_init(&event, PJMEDIA_EVENT_KEYFRAME_MISSING, ts, codec);
+	pjmedia_event_publish(NULL, codec, &event, 0);
+    }
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Decode frame.
+ */
+static pj_status_t ffmpeg_codec_decode_whole(pjmedia_vid_codec *codec,
+					     const pjmedia_frame *input,
+					     unsigned output_buf_len,
+					     pjmedia_frame *output)
+{
+    ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
+    AVFrame avframe;
+    AVPacket avpacket;
+    int err, got_picture;
+
+    /* Check if decoder has been opened */
+    PJ_ASSERT_RETURN(ff->dec_ctx, PJ_EINVALIDOP);
+
+    /* Reset output frame bit info */
+    output->bit_info = 0;
+
+    /* Validate output buffer size */
+    // Do this validation later after getting decoding result, where the real
+    // decoded size will be assured.
+    //if (ff->dec_vafp.framebytes > output_buf_len)
+	//return PJ_ETOOSMALL;
+
+    /* Init frame to receive the decoded data, the ffmpeg codec context will
+     * automatically provide the decoded buffer (single buffer used for the
+     * whole decoding session, and seems to be freed when the codec context
+     * closed).
+     */
+    avcodec_get_frame_defaults(&avframe);
+
+    /* Init packet, the container of the encoded data */
+    av_init_packet(&avpacket);
+    avpacket.data = (pj_uint8_t*)input->buf;
+    avpacket.size = (int)input->size;
+
+    /* ffmpeg warns:
+     * - input buffer padding, at least FF_INPUT_BUFFER_PADDING_SIZE
+     * - null terminated
+     * Normally, encoded buffer is allocated more than needed, so lets just
+     * bzero the input buffer end/pad, hope it will be just fine.
+     */
+    pj_bzero(avpacket.data+avpacket.size, FF_INPUT_BUFFER_PADDING_SIZE);
+
+    output->bit_info = 0;
+    output->timestamp = input->timestamp;
+
+#if LIBAVCODEC_VER_AT_LEAST(52,72)
+    //avpacket.flags = AV_PKT_FLAG_KEY;
+#else
+    avpacket.flags = 0;
+#endif
+
+#if LIBAVCODEC_VER_AT_LEAST(52,72)
+    err = avcodec_decode_video2(ff->dec_ctx, &avframe, 
+                                &got_picture, &avpacket);
+#else
+    err = avcodec_decode_video(ff->dec_ctx, &avframe,
+                               &got_picture, avpacket.data, avpacket.size);
+#endif
+    if (err < 0) {
+	pjmedia_event event;
+
+	output->type = PJMEDIA_FRAME_TYPE_NONE;
+	output->size = 0;
+        print_ffmpeg_err(err);
+
+	/* Broadcast missing keyframe event */
+	pjmedia_event_init(&event, PJMEDIA_EVENT_KEYFRAME_MISSING,
+			   &input->timestamp, codec);
+	pjmedia_event_publish(NULL, codec, &event, 0);
+
+	return PJMEDIA_CODEC_EBADBITSTREAM;
+    } else if (got_picture) {
+        pjmedia_video_apply_fmt_param *vafp = &ff->dec_vafp;
+        pj_uint8_t *q = (pj_uint8_t*)output->buf;
+	unsigned i;
+	pj_status_t status;
+
+	/* Check decoding result, e.g: see if the format got changed,
+	 * keyframe found/missing.
+	 */
+	status = check_decode_result(codec, &input->timestamp,
+				     avframe.key_frame);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	/* Check provided buffer size */
+	if (vafp->framebytes > output_buf_len)
+	    return PJ_ETOOSMALL;
+
+	/* Get the decoded data */
+	for (i = 0; i < ff->dec_vfi->plane_cnt; ++i) {
+	    pj_uint8_t *p = avframe.data[i];
+
+	    /* The decoded data may contain padding */
+	    if (avframe.linesize[i]!=vafp->strides[i]) {
+		/* Padding exists, copy line by line */
+		pj_uint8_t *q_end;
+                    
+		q_end = q+vafp->plane_bytes[i];
+		while(q < q_end) {
+		    pj_memcpy(q, p, vafp->strides[i]);
+		    q += vafp->strides[i];
+		    p += avframe.linesize[i];
+		}
+	    } else {
+		/* No padding, copy the whole plane */
+		pj_memcpy(q, p, vafp->plane_bytes[i]);
+		q += vafp->plane_bytes[i];
+	    }
+	}
+
+	output->type = PJMEDIA_FRAME_TYPE_VIDEO;
+        output->size = vafp->framebytes;
+    } else {
+	output->type = PJMEDIA_FRAME_TYPE_NONE;
+	output->size = 0;
+    }
+    
+    return PJ_SUCCESS;
+}
+
+static pj_status_t ffmpeg_codec_decode( pjmedia_vid_codec *codec,
+					pj_size_t pkt_count,
+					pjmedia_frame packets[],
+					unsigned out_size,
+					pjmedia_frame *output)
+{
+    ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(codec && pkt_count > 0 && packets && output,
+                     PJ_EINVAL);
+
+    if (ff->whole) {
+	pj_assert(pkt_count==1);
+	return ffmpeg_codec_decode_whole(codec, &packets[0], out_size, output);
+    } else {
+	pjmedia_frame whole_frm;
+	unsigned whole_len = 0;
+	unsigned i;
+
+	for (i=0; i<pkt_count; ++i) {
+	    if (whole_len + packets[i].size > ff->dec_buf_size) {
+		PJ_LOG(5,(THIS_FILE, "Decoding buffer overflow"));
+		break;
+	    }
+
+	    status = ffmpeg_unpacketize(codec, packets[i].buf, packets[i].size,
+	                                ff->dec_buf, ff->dec_buf_size,
+	                                &whole_len);
+	    if (status != PJ_SUCCESS) {
+		PJ_PERROR(5,(THIS_FILE, status, "Unpacketize error"));
+		continue;
+	    }
+	}
+
+	whole_frm.buf = ff->dec_buf;
+	whole_frm.size = whole_len;
+	whole_frm.timestamp = output->timestamp = packets[i].timestamp;
+	whole_frm.bit_info = 0;
+
+	return ffmpeg_codec_decode_whole(codec, &whole_frm, out_size, output);
+    }
+}
+
+
+#ifdef _MSC_VER
+#   pragma comment( lib, "avcodec.lib")
+#endif
+
+#endif	/* PJMEDIA_HAS_FFMPEG_VID_CODEC */
+
diff --git a/jni/pjproject-android/pjmedia/src/pjmedia-codec/g722.c b/jni/pjproject-android/pjmedia/src/pjmedia-codec/g722.c
new file mode 100644
index 0000000..7e0e4e4
--- /dev/null
+++ b/jni/pjproject-android/pjmedia/src/pjmedia-codec/g722.c
@@ -0,0 +1,714 @@
+/* $Id: g722.c 3664 2011-07-19 03:42:28Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjmedia-codec/g722.h>
+#include <pjmedia/codec.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/endpoint.h>
+#include <pjmedia/plc.h>
+#include <pjmedia/port.h>
+#include <pjmedia/silencedet.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/os.h>
+
+#if defined(PJMEDIA_HAS_G722_CODEC) && (PJMEDIA_HAS_G722_CODEC != 0)
+
+#include "g722/g722_enc.h"
+#include "g722/g722_dec.h"
+
+#define THIS_FILE   "g722.c"
+
+/* Defines */
+#define PTIME			(10)
+#define SAMPLES_PER_FRAME	(16000 * PTIME /1000)
+#define FRAME_LEN		(80)
+#define PLC_DISABLED		0
+
+/* Tracing */
+#ifndef PJ_TRACE
+#   define PJ_TRACE	0	
+#endif
+
+#if PJ_TRACE 
+#   define TRACE_(expr)	PJ_LOG(4,expr)
+#else
+#   define TRACE_(expr)
+#endif
+
+
+/* Prototypes for G722 factory */
+static pj_status_t g722_test_alloc(pjmedia_codec_factory *factory, 
+				   const pjmedia_codec_info *id );
+static pj_status_t g722_default_attr(pjmedia_codec_factory *factory, 
+				     const pjmedia_codec_info *id, 
+				     pjmedia_codec_param *attr );
+static pj_status_t g722_enum_codecs(pjmedia_codec_factory *factory, 
+				    unsigned *count, 
+				    pjmedia_codec_info codecs[]);
+static pj_status_t g722_alloc_codec(pjmedia_codec_factory *factory, 
+				    const pjmedia_codec_info *id, 
+				    pjmedia_codec **p_codec);
+static pj_status_t g722_dealloc_codec(pjmedia_codec_factory *factory, 
+				      pjmedia_codec *codec );
+
+/* Prototypes for G722 implementation. */
+static pj_status_t  g722_codec_init(pjmedia_codec *codec, 
+				    pj_pool_t *pool );
+static pj_status_t  g722_codec_open(pjmedia_codec *codec, 
+				    pjmedia_codec_param *attr );
+static pj_status_t  g722_codec_close(pjmedia_codec *codec );
+static pj_status_t  g722_codec_modify(pjmedia_codec *codec, 
+				      const pjmedia_codec_param *attr );
+static pj_status_t  g722_codec_parse(pjmedia_codec *codec,
+				     void *pkt,
+				     pj_size_t pkt_size,
+				     const pj_timestamp *ts,
+				     unsigned *frame_cnt,
+				     pjmedia_frame frames[]);
+static pj_status_t  g722_codec_encode(pjmedia_codec *codec, 
+				      const struct pjmedia_frame *input,
+				      unsigned output_buf_len, 
+				      struct pjmedia_frame *output);
+static pj_status_t  g722_codec_decode(pjmedia_codec *codec, 
+				      const struct pjmedia_frame *input,
+				      unsigned output_buf_len, 
+				      struct pjmedia_frame *output);
+#if !PLC_DISABLED
+static pj_status_t  g722_codec_recover(pjmedia_codec *codec,
+				      unsigned output_buf_len,
+				      struct pjmedia_frame *output);
+#endif
+
+/* Definition for G722 codec operations. */
+static pjmedia_codec_op g722_op = 
+{
+    &g722_codec_init,
+    &g722_codec_open,
+    &g722_codec_close,
+    &g722_codec_modify,
+    &g722_codec_parse,
+    &g722_codec_encode,
+    &g722_codec_decode,
+#if !PLC_DISABLED
+    &g722_codec_recover
+#else
+    NULL
+#endif
+};
+
+/* Definition for G722 codec factory operations. */
+static pjmedia_codec_factory_op g722_factory_op =
+{
+    &g722_test_alloc,
+    &g722_default_attr,
+    &g722_enum_codecs,
+    &g722_alloc_codec,
+    &g722_dealloc_codec,
+    &pjmedia_codec_g722_deinit
+};
+
+/* G722 factory */
+static struct g722_codec_factory
+{
+    pjmedia_codec_factory    base;
+    pjmedia_endpt	    *endpt;
+    pj_pool_t		    *pool;
+    pj_mutex_t		    *mutex;
+    pjmedia_codec	     codec_list;
+    unsigned		     pcm_shift;
+} g722_codec_factory;
+
+
+/* G722 codec private data. */
+struct g722_data
+{
+    g722_enc_t		 encoder;
+    g722_dec_t		 decoder;
+    unsigned		 pcm_shift;
+    pj_int16_t		 pcm_clip_mask;
+    pj_bool_t		 plc_enabled;
+    pj_bool_t		 vad_enabled;
+    pjmedia_silence_det	*vad;
+    pj_timestamp	 last_tx;
+#if !PLC_DISABLED
+    pjmedia_plc		*plc;
+#endif
+};
+
+
+
+/*
+ * Initialize and register G722 codec factory to pjmedia endpoint.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_g722_init( pjmedia_endpt *endpt )
+{
+    pjmedia_codec_mgr *codec_mgr;
+    pj_status_t status;
+
+    if (g722_codec_factory.pool != NULL)
+	return PJ_SUCCESS;
+
+    /* Create G722 codec factory. */
+    g722_codec_factory.base.op = &g722_factory_op;
+    g722_codec_factory.base.factory_data = NULL;
+    g722_codec_factory.endpt = endpt;
+    g722_codec_factory.pcm_shift = PJMEDIA_G722_DEFAULT_PCM_SHIFT;
+
+    g722_codec_factory.pool = pjmedia_endpt_create_pool(endpt, "g722", 1000, 
+						        1000);
+    if (!g722_codec_factory.pool)
+	return PJ_ENOMEM;
+
+    pj_list_init(&g722_codec_factory.codec_list);
+
+    /* Create mutex. */
+    status = pj_mutex_create_simple(g722_codec_factory.pool, "g722", 
+				    &g722_codec_factory.mutex);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Get the codec manager. */
+    codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
+    if (!codec_mgr) {
+	status = PJ_EINVALIDOP;
+	goto on_error;
+    }
+
+    /* Register codec factory to endpoint. */
+    status = pjmedia_codec_mgr_register_factory(codec_mgr, 
+						&g722_codec_factory.base);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    TRACE_((THIS_FILE, "G722 codec factory initialized"));
+    
+    /* Done. */
+    return PJ_SUCCESS;
+
+on_error:
+    pj_pool_release(g722_codec_factory.pool);
+    g722_codec_factory.pool = NULL;
+    return status;
+}
+
+/*
+ * Unregister G722 codec factory from pjmedia endpoint and deinitialize
+ * the G722 codec library.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_g722_deinit(void)
+{
+    pjmedia_codec_mgr *codec_mgr;
+    pj_status_t status;
+
+    if (g722_codec_factory.pool == NULL)
+	return PJ_SUCCESS;
+
+    /* Get the codec manager. */
+    codec_mgr = pjmedia_endpt_get_codec_mgr(g722_codec_factory.endpt);
+    if (!codec_mgr) {
+	pj_pool_release(g722_codec_factory.pool);
+	g722_codec_factory.pool = NULL;
+	return PJ_EINVALIDOP;
+    }
+
+    /* Unregister G722 codec factory. */
+    status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
+						  &g722_codec_factory.base);
+    
+    /* Destroy mutex. */
+    pj_mutex_destroy(g722_codec_factory.mutex);
+
+    /* Destroy pool. */
+    pj_pool_release(g722_codec_factory.pool);
+    g722_codec_factory.pool = NULL;
+    
+    TRACE_((THIS_FILE, "G722 codec factory shutdown"));
+    return status;
+}
+
+
+/*
+ * Set level adjustment.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_g722_set_pcm_shift(unsigned val)
+{
+    g722_codec_factory.pcm_shift = val;
+    return PJ_SUCCESS;
+}
+
+
+/* 
+ * Check if factory can allocate the specified codec. 
+ */
+static pj_status_t g722_test_alloc(pjmedia_codec_factory *factory, 
+				   const pjmedia_codec_info *info )
+{
+    PJ_UNUSED_ARG(factory);
+
+    /* Check payload type. */
+    if (info->pt != PJMEDIA_RTP_PT_G722)
+	return PJMEDIA_CODEC_EUNSUP;
+
+    /* Ignore the rest, since it's static payload type. */
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Generate default attribute.
+ */
+static pj_status_t g722_default_attr( pjmedia_codec_factory *factory, 
+				      const pjmedia_codec_info *id, 
+				      pjmedia_codec_param *attr )
+{
+    PJ_UNUSED_ARG(factory);
+    PJ_UNUSED_ARG(id);
+
+    pj_bzero(attr, sizeof(pjmedia_codec_param));
+    attr->info.clock_rate = 16000;
+    attr->info.channel_cnt = 1;
+    attr->info.avg_bps = 64000;
+    attr->info.max_bps = 64000;
+    attr->info.pcm_bits_per_sample = 16;
+    attr->info.frm_ptime = PTIME;
+    attr->info.pt = PJMEDIA_RTP_PT_G722;
+
+    attr->setting.frm_per_pkt = 2;
+    attr->setting.vad = 1;
+    attr->setting.plc = 1;
+
+    /* Default all other flag bits disabled. */
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Enum codecs supported by this factory (i.e. only G722!).
+ */
+static pj_status_t g722_enum_codecs(pjmedia_codec_factory *factory, 
+				    unsigned *count, 
+				    pjmedia_codec_info codecs[])
+{
+    PJ_UNUSED_ARG(factory);
+    PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
+
+    pj_bzero(&codecs[0], sizeof(pjmedia_codec_info));
+    codecs[0].encoding_name = pj_str("G722");
+    codecs[0].pt = PJMEDIA_RTP_PT_G722;
+    codecs[0].type = PJMEDIA_TYPE_AUDIO;
+    codecs[0].clock_rate = 16000;
+    codecs[0].channel_cnt = 1;
+
+    *count = 1;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Allocate a new G722 codec instance.
+ */
+static pj_status_t g722_alloc_codec(pjmedia_codec_factory *factory, 
+				    const pjmedia_codec_info *id,
+				    pjmedia_codec **p_codec)
+{
+    pjmedia_codec *codec;
+    struct g722_data *g722_data;
+
+    PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory == &g722_codec_factory.base, PJ_EINVAL);
+
+    pj_mutex_lock(g722_codec_factory.mutex);
+
+    /* Get free nodes, if any. */
+    if (!pj_list_empty(&g722_codec_factory.codec_list)) {
+	codec = g722_codec_factory.codec_list.next;
+	pj_list_erase(codec);
+    } else {
+	pj_status_t status;
+
+	codec = PJ_POOL_ZALLOC_T(g722_codec_factory.pool, pjmedia_codec);
+	PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM);
+	codec->op = &g722_op;
+	codec->factory = factory;
+
+	g722_data = PJ_POOL_ZALLOC_T(g722_codec_factory.pool, struct g722_data);
+	codec->codec_data = g722_data;
+
+#if !PLC_DISABLED
+    	/* Create PLC */
+    	status = pjmedia_plc_create(g722_codec_factory.pool, 16000, 
+			            SAMPLES_PER_FRAME, 0, &g722_data->plc);
+	if (status != PJ_SUCCESS) {
+	    pj_mutex_unlock(g722_codec_factory.mutex);
+	    return status;
+	}
+#endif
+
+	/* Create silence detector */
+	status = pjmedia_silence_det_create(g722_codec_factory.pool,
+					    16000, SAMPLES_PER_FRAME,
+					    &g722_data->vad);
+	if (status != PJ_SUCCESS) {
+	    pj_mutex_unlock(g722_codec_factory.mutex);
+	    TRACE_((THIS_FILE, "Create silence detector failed (status = %d)", 
+                               status));
+	    return status;
+	}
+    }
+
+
+    pj_mutex_unlock(g722_codec_factory.mutex);
+
+    *p_codec = codec;
+    return PJ_SUCCESS;
+}
+
+/*
+ * Free codec.
+ */
+static pj_status_t g722_dealloc_codec(pjmedia_codec_factory *factory, 
+				      pjmedia_codec *codec )
+{
+    struct g722_data *g722_data;
+    int i;
+
+    PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory == &g722_codec_factory.base, PJ_EINVAL);
+
+    g722_data = (struct g722_data*) codec->codec_data;
+
+    /* Close codec, if it's not closed. */
+    g722_codec_close(codec);
+
+#if !PLC_DISABLED
+    /* Clear left samples in the PLC, since codec+plc will be reused
+     * next time.
+     */
+    for (i=0; i<2; ++i) {
+	pj_int16_t frame[SAMPLES_PER_FRAME];
+	pjmedia_zero_samples(frame, PJ_ARRAY_SIZE(frame));
+	pjmedia_plc_save(g722_data->plc, frame);
+    }
+#else
+    PJ_UNUSED_ARG(i);
+#endif
+
+    /* Re-init silence_period */
+    pj_set_timestamp32(&g722_data->last_tx, 0, 0);
+
+    /* Put in the free list. */
+    pj_mutex_lock(g722_codec_factory.mutex);
+    pj_list_push_front(&g722_codec_factory.codec_list, codec);
+    pj_mutex_unlock(g722_codec_factory.mutex);
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Init codec.
+ */
+static pj_status_t g722_codec_init(pjmedia_codec *codec, 
+				   pj_pool_t *pool )
+{
+    PJ_UNUSED_ARG(codec);
+    PJ_UNUSED_ARG(pool);
+    return PJ_SUCCESS;
+}
+
+/*
+ * Open codec.
+ */
+static pj_status_t g722_codec_open(pjmedia_codec *codec, 
+				   pjmedia_codec_param *attr )
+{
+    struct g722_data *g722_data = (struct g722_data*) codec->codec_data;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(codec && attr, PJ_EINVAL);
+    PJ_ASSERT_RETURN(g722_data != NULL, PJ_EINVALIDOP);
+
+    status = g722_enc_init(&g722_data->encoder);
+    if (status != PJ_SUCCESS) {
+	TRACE_((THIS_FILE, "g722_enc_init() failed, status=%d", status));
+	pj_mutex_unlock(g722_codec_factory.mutex);
+	return PJMEDIA_CODEC_EFAILED;
+    }
+
+    status = g722_dec_init(&g722_data->decoder);
+    if (status != PJ_SUCCESS) {
+	TRACE_((THIS_FILE, "g722_dec_init() failed, status=%d", status));
+	pj_mutex_unlock(g722_codec_factory.mutex);
+	return PJMEDIA_CODEC_EFAILED;
+    }
+
+    g722_data->vad_enabled = (attr->setting.vad != 0);
+    g722_data->plc_enabled = (attr->setting.plc != 0);
+    g722_data->pcm_shift = g722_codec_factory.pcm_shift;
+    g722_data->pcm_clip_mask = (pj_int16_t)(1<<g722_codec_factory.pcm_shift)-1;
+    g722_data->pcm_clip_mask <<= (16-g722_codec_factory.pcm_shift);
+
+    TRACE_((THIS_FILE, "G722 codec opened: vad=%d, plc=%d",
+			g722_data->vad_enabled, g722_data->plc_enabled));
+    return PJ_SUCCESS;
+}
+
+/*
+ * Close codec.
+ */
+static pj_status_t g722_codec_close( pjmedia_codec *codec )
+{
+    /* The codec, encoder, and decoder will be reused, so there's
+     * nothing to do here
+     */
+
+    PJ_UNUSED_ARG(codec);
+    
+    TRACE_((THIS_FILE, "G722 codec closed"));
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Modify codec settings.
+ */
+static pj_status_t  g722_codec_modify(pjmedia_codec *codec, 
+				     const pjmedia_codec_param *attr )
+{
+    struct g722_data *g722_data = (struct g722_data*) codec->codec_data;
+
+    pj_assert(g722_data != NULL);
+
+    g722_data->vad_enabled = (attr->setting.vad != 0);
+    g722_data->plc_enabled = (attr->setting.plc != 0);
+
+    TRACE_((THIS_FILE, "G722 codec modified: vad=%d, plc=%d",
+			g722_data->vad_enabled, g722_data->plc_enabled));
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Get frames in the packet.
+ */
+static pj_status_t  g722_codec_parse(pjmedia_codec *codec,
+				     void *pkt,
+				     pj_size_t pkt_size,
+				     const pj_timestamp *ts,
+				     unsigned *frame_cnt,
+				     pjmedia_frame frames[])
+{
+    unsigned count = 0;
+
+    PJ_UNUSED_ARG(codec);
+
+    PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL);
+
+    TRACE_((THIS_FILE, "G722 parse(): input len=%d", pkt_size));
+
+    while (pkt_size >= FRAME_LEN && count < *frame_cnt) {
+	frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
+	frames[count].buf = pkt;
+	frames[count].size = FRAME_LEN;
+	frames[count].timestamp.u64 = ts->u64 + count * SAMPLES_PER_FRAME;
+
+	pkt = ((char*)pkt) + FRAME_LEN;
+	pkt_size -= FRAME_LEN;
+
+	++count;
+    }
+
+    TRACE_((THIS_FILE, "G722 parse(): got %d frames", count));
+
+    *frame_cnt = count;
+    return PJ_SUCCESS;
+}
+
+/*
+ * Encode frame.
+ */
+static pj_status_t g722_codec_encode(pjmedia_codec *codec, 
+				     const struct pjmedia_frame *input,
+				     unsigned output_buf_len, 
+				     struct pjmedia_frame *output)
+{
+    struct g722_data *g722_data = (struct g722_data*) codec->codec_data;
+    pj_status_t status;
+
+    pj_assert(g722_data && input && output);
+
+    PJ_ASSERT_RETURN((input->size >> 2) <= output_buf_len, 
+                     PJMEDIA_CODEC_EFRMTOOSHORT);
+
+    /* Detect silence */
+    if (g722_data->vad_enabled) {
+	pj_bool_t is_silence;
+	pj_int32_t silence_duration;
+
+	silence_duration = pj_timestamp_diff32(&g722_data->last_tx, 
+					       &input->timestamp);
+
+	is_silence = pjmedia_silence_det_detect(g722_data->vad, 
+					        (const pj_int16_t*) input->buf,
+						(input->size >> 1),
+						NULL);
+	if (is_silence &&
+	    (PJMEDIA_CODEC_MAX_SILENCE_PERIOD == -1 ||
+	     silence_duration < PJMEDIA_CODEC_MAX_SILENCE_PERIOD*16000/1000))
+	{
+	    output->type = PJMEDIA_FRAME_TYPE_NONE;
+	    output->buf = NULL;
+	    output->size = 0;
+	    output->timestamp = input->timestamp;
+	    return PJ_SUCCESS;
+	} else {
+	    g722_data->last_tx = input->timestamp;
+	}
+    }
+
+    /* Adjust input signal level from 16-bit to 14-bit */
+    if (g722_data->pcm_shift) {
+	pj_int16_t *p, *end;
+
+	p = (pj_int16_t*)input->buf;
+	end = p + input->size/2;
+	while (p < end) {
+	    *p++ >>= g722_data->pcm_shift;
+	}
+    }
+
+    /* Encode to temporary buffer */
+    output->size = output_buf_len;
+    status = g722_enc_encode(&g722_data->encoder, (pj_int16_t*)input->buf, 
+			     (input->size >> 1), output->buf, &output->size);
+    if (status != PJ_SUCCESS) {
+	output->size = 0;
+	output->buf = NULL;
+	output->type = PJMEDIA_FRAME_TYPE_NONE;
+	TRACE_((THIS_FILE, "G722 encode() status: %d", status));
+	return PJMEDIA_CODEC_EFAILED;
+    }
+
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+    output->timestamp = input->timestamp;
+    
+    TRACE_((THIS_FILE, "G722 encode(): size=%d", output->size));
+    return PJ_SUCCESS;
+}
+
+/*
+ * Decode frame.
+ */
+static pj_status_t g722_codec_decode(pjmedia_codec *codec, 
+				     const struct pjmedia_frame *input,
+				     unsigned output_buf_len, 
+				     struct pjmedia_frame *output)
+{
+    struct g722_data *g722_data = (struct g722_data*) codec->codec_data;
+    pj_status_t status;
+
+    pj_assert(g722_data != NULL);
+    PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
+
+    TRACE_((THIS_FILE, "G722 decode(): inbuf=%p, insize=%d, outbuf=%p,"
+		       "outsize=%d",
+	               input->buf, input->size, output->buf, output_buf_len));
+    
+    if (output_buf_len < SAMPLES_PER_FRAME * 2) {
+        TRACE_((THIS_FILE, "G722 decode() ERROR: PJMEDIA_CODEC_EPCMTOOSHORT"));
+	return PJMEDIA_CODEC_EPCMTOOSHORT;
+    }
+
+    if (input->size != FRAME_LEN) {
+        TRACE_((THIS_FILE, "G722 decode() ERROR: PJMEDIA_CODEC_EFRMTOOSHORT"));
+	return PJMEDIA_CODEC_EFRMTOOSHORT;
+    }
+
+
+    /* Decode */
+    output->size = SAMPLES_PER_FRAME;
+    status = g722_dec_decode(&g722_data->decoder, input->buf, input->size,
+			     (pj_int16_t*)output->buf, &output->size);
+    if (status != PJ_SUCCESS) {
+	TRACE_((THIS_FILE, "G722 decode() status: %d", status));
+	return PJMEDIA_CODEC_EFAILED;
+    }
+
+    pj_assert(output->size == SAMPLES_PER_FRAME);
+
+    /* Adjust input signal level from 14-bit to 16-bit */
+    if (g722_data->pcm_shift) {
+	pj_int16_t *p, *end;
+
+	p = (pj_int16_t*)output->buf;
+	end = p + output->size;
+	while (p < end) {
+#if PJMEDIA_G722_STOP_PCM_SHIFT_ON_CLIPPING
+	    /* If there is clipping, stop the PCM shifting */
+	    if (*p & g722_data->pcm_clip_mask) {
+		g722_data->pcm_shift = 0;
+		break;
+	    }
+#endif
+	    *p++ <<= g722_data->pcm_shift;
+	}
+    }
+
+    output->size = SAMPLES_PER_FRAME * 2;
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+    output->timestamp = input->timestamp;
+
+#if !PLC_DISABLED
+    if (g722_data->plc_enabled)
+	pjmedia_plc_save(g722_data->plc, (pj_int16_t*)output->buf);
+#endif
+
+    TRACE_((THIS_FILE, "G722 decode done"));
+    return PJ_SUCCESS;
+}
+
+
+#if !PLC_DISABLED
+/*
+ * Recover lost frame.
+ */
+static pj_status_t  g722_codec_recover(pjmedia_codec *codec,
+				       unsigned output_buf_len,
+				       struct pjmedia_frame *output)
+{
+    struct g722_data *g722_data = (struct g722_data*)codec->codec_data;
+
+    PJ_ASSERT_RETURN(g722_data->plc_enabled, PJ_EINVALIDOP);
+
+    PJ_ASSERT_RETURN(output_buf_len >= SAMPLES_PER_FRAME * 2, 
+                     PJMEDIA_CODEC_EPCMTOOSHORT);
+
+    pjmedia_plc_generate(g722_data->plc, (pj_int16_t*)output->buf);
+
+    output->size = SAMPLES_PER_FRAME * 2;
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+    
+    return PJ_SUCCESS;
+}
+#endif
+
+#endif // PJMEDIA_HAS_G722_CODEC
+
diff --git a/jni/pjproject-android/pjmedia/src/pjmedia-codec/g722/g722_dec.c b/jni/pjproject-android/pjmedia/src/pjmedia-codec/g722/g722_dec.c
new file mode 100644
index 0000000..c6edc36
--- /dev/null
+++ b/jni/pjproject-android/pjmedia/src/pjmedia-codec/g722/g722_dec.c
@@ -0,0 +1,549 @@
+/* $Id: g722_dec.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+/*
+ * Based on implementation found in Carnegie Mellon Speech Group Software
+ * depository (ftp://ftp.cs.cmu.edu/project/fgdata/index.html). No copyright
+ * was claimed in the original source codes.
+ */
+#include <pjmedia/errno.h>
+#include <pj/assert.h>
+#include <pj/pool.h>
+
+#include "g722_dec.h"
+
+#if defined(PJMEDIA_HAS_G722_CODEC) && (PJMEDIA_HAS_G722_CODEC != 0)
+
+#define MODE 1
+
+#define SATURATE(v, max, min) \
+    if (v>max) v = max; \
+    else if (v<min) v = min
+
+extern const int g722_qmf_coeff[24];
+
+static const int qm4[16] = 
+{
+	0, -20456, -12896, -8968,
+    -6288,  -4240,  -2584, -1200,
+    20456,  12896,   8968,  6288,
+     4240,   2584,   1200,     0
+};
+static const int ilb[32] = {
+    2048, 2093, 2139, 2186, 2233, 2282, 2332,
+    2383, 2435, 2489, 2543, 2599, 2656, 2714, 
+    2774, 2834, 2896, 2960, 3025, 3091, 3158, 
+    3228, 3298, 3371, 3444, 3520, 3597, 3676,
+    3756, 3838, 3922, 4008
+};
+
+
+static int block2l (int il, int detl)
+{
+    int dlt ;
+    int ril, wd2 ;
+
+    /* INVQAL */
+    ril = il >> 2 ;
+    wd2 = qm4[ril] ;
+    dlt = (detl * wd2) >> 15 ;
+
+    return (dlt) ;
+}
+
+
+static int block3l (g722_dec_t *dec, int il)
+{
+    int detl ;
+    int ril, il4, wd, wd1, wd2, wd3, nbpl, depl ;
+    static const int  wl[8] = {
+	-60, -30, 58, 172, 334, 538, 1198, 3042
+    };
+    static const int rl42[16] = {
+	0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, 2, 1, 0
+    };
+
+    /* LOGSCL */
+    ril = il >> 2 ;
+    il4 = rl42[ril] ;
+    wd = (dec->nbl * 32512) >> 15 ;
+    nbpl = wd + wl[il4] ;
+
+    if (nbpl <     0) nbpl = 0 ;
+    if (nbpl > 18432) nbpl = 18432 ;
+
+    /* SCALEL */
+    wd1 =  (nbpl >> 6) & 31 ;
+    wd2 = nbpl >> 11 ;
+    if ((8 - wd2) < 0)   wd3 = ilb[wd1] << (wd2 - 8) ;
+    else wd3 = ilb[wd1] >> (8 - wd2) ;
+    depl = wd3 << 2 ;
+
+    /* DELAYA */
+    dec->nbl = nbpl ;
+    /* DELAYL */
+    detl = depl ;
+
+    return (detl) ;
+}
+
+
+static int block4l (g722_dec_t *dec, int dl)
+{
+    int sl = dec->slow ;
+    int i ;
+    int wd, wd1, wd2, wd3, wd4, wd5/*, wd6 */;
+
+    dec->dlt[0] = dl;
+
+    /* RECONS */ 
+    dec->rlt[0] = sl + dec->dlt[0] ;
+    SATURATE(dec->rlt[0], 32767, -32768);
+
+    /* PARREC */
+    dec->plt[0] = dec->dlt[0] + dec->szl ;
+    SATURATE(dec->plt[0], 32767, -32768);
+
+    /* UPPOL2 */
+    dec->sgl[0] = dec->plt[0] >> 15 ;
+    dec->sgl[1] = dec->plt[1] >> 15 ;
+    dec->sgl[2] = dec->plt[2] >> 15 ;
+
+    wd1 = dec->al[1] << 2;
+    SATURATE(wd1, 32767, -32768);
+
+    if ( dec->sgl[0] == dec->sgl[1] )  wd2 = - wd1 ;
+    else  wd2 = wd1 ;
+    if (wd2 > 32767) wd2 = 32767;
+    wd2 = wd2 >> 7 ;
+
+    if ( dec->sgl[0] == dec->sgl[2] )  wd3 = 128 ; 
+    else  wd3 = - 128 ;
+
+    wd4 = wd2 + wd3 ;
+    wd5 = (dec->al[2] * 32512) >> 15 ;
+
+    dec->apl[2] = wd4 + wd5 ;
+    SATURATE(dec->apl[2], 12288, -12288);
+    
+    /* UPPOL1 */
+    dec->sgl[0] = dec->plt[0] >> 15 ;
+    dec->sgl[1] = dec->plt[1] >> 15 ;
+
+    if ( dec->sgl[0] == dec->sgl[1] )  wd1 = 192 ;
+    else  wd1 = - 192 ;
+
+    wd2 = (dec->al[1] * 32640) >> 15 ;
+
+    dec->apl[1] = wd1 + wd2 ;
+    SATURATE(dec->apl[1], 32767, -32768);
+
+    wd3 = (15360 - dec->apl[2]) ;
+    SATURATE(wd3, 32767, -32768);
+    if ( dec->apl[1] >  wd3)  dec->apl[1] =  wd3 ;
+    if ( dec->apl[1] < -wd3)  dec->apl[1] = -wd3 ;
+
+    /* UPZERO */
+    if ( dec->dlt[0] == 0 )  wd1 = 0 ;
+    else  wd1 = 128 ;
+
+    dec->sgl[0] = dec->dlt[0] >> 15 ;
+
+    for ( i = 1; i < 7; i++ ) {
+	dec->sgl[i] = dec->dlt[i] >> 15 ;
+	if ( dec->sgl[i] == dec->sgl[0] )  wd2 = wd1 ;
+	else  wd2 = - wd1 ;
+	wd3 = (dec->bl[i] * 32640) >> 15 ;
+	dec->bpl[i] = wd2 + wd3 ;
+	SATURATE(dec->bpl[i], 32767, -32768);
+    }
+
+    /* DELAYA */
+    for ( i = 6; i > 0; i-- ) {
+	dec->dlt[i] = dec->dlt[i-1] ;
+	dec->bl[i]  = dec->bpl[i] ;
+    }
+
+    for ( i = 2; i > 0; i-- ) {
+	dec->rlt[i] = dec->rlt[i-1] ;
+	dec->plt[i] = dec->plt[i-1] ;
+	dec->al[i]  = dec->apl[i] ;
+    }
+
+    /* FILTEP */
+    wd1 = dec->rlt[1] << 1;
+    SATURATE(wd1, 32767, -32768);
+    wd1 = ( dec->al[1] * wd1 ) >> 15 ;
+
+    wd2 = dec->rlt[2] << 1;
+    SATURATE(wd2, 32767, -32768);
+    wd2 = ( dec->al[2] * wd2 ) >> 15 ;
+
+    dec->spl = wd1 + wd2 ;
+    SATURATE(dec->spl, 32767, -32768);
+
+    /* FILTEZ */
+    dec->szl = 0 ;
+    for (i=6; i>0; i--) {
+	wd = dec->dlt[i] << 1;
+	SATURATE(wd, 32767, -32768);
+	dec->szl += (dec->bl[i] * wd) >> 15 ;
+	SATURATE(dec->szl, 32767, -32768);
+    }
+
+    /* PREDIC */
+    sl = dec->spl + dec->szl ;
+    SATURATE(sl, 32767, -32768);
+
+    return (sl) ;
+}
+
+static int block5l (int ilr, int sl, int detl, int mode)
+{
+    int yl ;
+    int ril, dl, wd2 = 0;
+    static const int qm5[32] = {
+	  -280,   -280, -23352, -17560,
+	-14120, -11664,  -9752,  -8184,
+	 -6864,  -5712,  -4696,  -3784,
+	 -2960,  -2208,  -1520,   -880,
+	 23352,  17560,  14120,  11664,
+	  9752,   8184,   6864,   5712,
+	  4696,   3784,   2960,   2208,
+	  1520,    880,    280,   -280
+    };
+    static const int qm6[64] = {
+	-136,	-136,	-136,	-136,
+	-24808,	-21904,	-19008,	-16704,
+	-14984,	-13512,	-12280,	-11192,
+	-10232,	-9360,	-8576,	-7856,
+	-7192,	-6576,	-6000,	-5456,
+	-4944,	-4464,	-4008,	-3576,
+	-3168,	-2776,	-2400,	-2032,
+	-1688,	-1360,	-1040,	-728,
+	24808,	21904,	19008,	16704,
+	14984,	13512,	12280,	11192,
+	10232,	9360,	8576,	7856,
+	7192,	6576,	6000,	5456,
+	4944,	4464,	4008,	3576,
+	3168,	2776,	2400,	2032,
+	1688,	1360,	1040,	728,
+	432,	136,	-432,	-136
+    };
+    
+    /* INVQBL */
+    if (mode == 1) {
+	ril = ilr ;
+	wd2 = qm6[ril] ;
+    }
+
+    if (mode == 2) {
+	ril = ilr >> 1 ;
+	wd2 = qm5[ril] ;
+    }
+
+    if (mode == 3) {
+	ril = ilr >> 2 ;
+	wd2 = qm4[ril] ;
+    }
+
+    dl = (detl * wd2 ) >> 15 ;
+
+    /* RECONS */
+    yl = sl + dl ;
+    SATURATE(yl, 32767, -32768);
+
+    return (yl) ;
+}
+
+static int block6l (int yl)
+{
+    int rl ;
+
+    rl = yl ;
+    SATURATE(rl, 16383, -16384);
+
+    return (rl) ;
+}
+
+static int block2h (int ih, int deth)
+{
+    int dh ;
+    int wd2 ;
+    static const int qm2[4] = {-7408, -1616, 7408, 1616} ;
+    
+    /* INVQAH */
+    wd2 = qm2[ih] ;
+    dh = (deth * wd2) >> 15 ;
+
+    return (dh) ;
+}
+
+static int block3h (g722_dec_t *dec, int ih)
+{
+    int deth ;
+    int ih2, wd, wd1, wd2, wd3, nbph, deph ;
+    static const int wh[3] = {0, -214, 798} ;
+    static const int rh2[4] = {2, 1, 2, 1} ;
+    
+    /* LOGSCH */
+    ih2 = rh2[ih] ;
+    wd = (dec->nbh * 32512) >> 15 ;
+    nbph = wd + wh[ih2] ;
+
+    if (nbph <     0) nbph = 0 ;
+    if (nbph > 22528) nbph = 22528 ;
+
+
+    /* SCALEH */
+    wd1 =  (nbph >> 6) & 31 ;
+    wd2 = nbph >> 11 ;
+    if ((10 - wd2) < 0) wd3 = ilb[wd1] << (wd2 - 10) ;
+    else wd3 = ilb[wd1] >> (10 - wd2) ;
+    deph = wd3 << 2 ;
+
+    /* DELAYA */
+    dec->nbh = nbph ;
+    
+    /* DELAYH */
+    deth = deph ;
+
+    return (deth) ;
+}
+
+static int block4h (g722_dec_t *dec, int d)
+{
+    int sh = dec->shigh;
+    int i ;
+    int wd, wd1, wd2, wd3, wd4, wd5/*, wd6 */;
+
+    dec->dh[0] = d;
+
+    /* RECONS */ 
+    dec->rh[0] = sh + dec->dh[0] ;
+    SATURATE(dec->rh[0], 32767, -32768);
+
+    /* PARREC */
+    dec->ph[0] = dec->dh[0] + dec->szh ;
+    SATURATE(dec->ph[0], 32767, -32768);
+
+    /* UPPOL2 */
+    dec->sgh[0] = dec->ph[0] >> 15 ;
+    dec->sgh[1] = dec->ph[1] >> 15 ;
+    dec->sgh[2] = dec->ph[2] >> 15 ;
+
+    wd1 = dec->ah[1] << 2;
+    SATURATE(wd1, 32767, -32768);
+
+    if ( dec->sgh[0] == dec->sgh[1] )  wd2 = - wd1 ;
+    else  wd2 = wd1 ;
+    if (wd2 > 32767) wd2 = 32767;
+
+    wd2 = wd2 >> 7 ;
+
+    if ( dec->sgh[0] == dec->sgh[2] )  wd3 = 128 ; 
+    else  wd3 = - 128 ;
+
+    wd4 = wd2 + wd3 ;
+    wd5 = (dec->ah[2] * 32512) >> 15 ;
+
+    dec->aph[2] = wd4 + wd5 ;
+    SATURATE(dec->aph[2], 12288, -12288);
+    
+    /* UPPOL1 */
+    dec->sgh[0] = dec->ph[0] >> 15 ;
+    dec->sgh[1] = dec->ph[1] >> 15 ;
+
+    if ( dec->sgh[0] == dec->sgh[1] )  wd1 = 192 ;
+    else  wd1 = - 192 ;
+
+    wd2 = (dec->ah[1] * 32640) >> 15 ;
+
+    dec->aph[1] = wd1 + wd2 ;
+    SATURATE(dec->aph[1], 32767, -32768);
+    //dec->aph[2]?
+    //if (aph[2] > 32767) aph[2] = 32767;
+    //if (aph[2] < -32768) aph[2] = -32768;
+
+    wd3 = (15360 - dec->aph[2]) ;
+    SATURATE(wd3, 32767, -32768);
+    if ( dec->aph[1] >  wd3)  dec->aph[1] =  wd3 ;
+    if ( dec->aph[1] < -wd3)  dec->aph[1] = -wd3 ;
+
+    /* UPZERO */
+    if ( dec->dh[0] == 0 )  wd1 = 0 ;
+    if ( dec->dh[0] != 0 )  wd1 = 128 ;
+
+    dec->sgh[0] = dec->dh[0] >> 15 ;
+
+    for ( i = 1; i < 7; i++ ) {
+	dec->sgh[i] = dec->dh[i] >> 15 ;
+	if ( dec->sgh[i] == dec->sgh[0] )  wd2 = wd1 ;
+	else wd2 = - wd1 ;
+	wd3 = (dec->bh[i] * 32640) >> 15 ;
+	dec->bph[i] = wd2 + wd3 ;
+    }
+ 
+    /* DELAYA */
+    for ( i = 6; i > 0; i-- ) {
+	dec->dh[i] = dec->dh[i-1] ;
+	dec->bh[i] = dec->bph[i] ;
+    }
+
+    for ( i = 2; i > 0; i-- ) {
+	dec->rh[i] = dec->rh[i-1] ;
+	dec->ph[i] = dec->ph[i-1] ;
+	dec->ah[i] = dec->aph[i] ;
+    }
+
+    /* FILTEP */
+    wd1 = dec->rh[1] << 1 ;
+    SATURATE(wd1, 32767, -32768);
+    wd1 = ( dec->ah[1] * wd1 ) >> 15 ;
+
+    wd2 = dec->rh[2] << 1;
+    SATURATE(wd2, 32767, -32768);
+    wd2 = ( dec->ah[2] * wd2 ) >> 15 ;
+
+    dec->sph = wd1 + wd2 ;
+    SATURATE(dec->sph, 32767, -32768);
+
+    /* FILTEZ */
+    dec->szh = 0 ;
+    for (i=6; i>0; i--) {
+	wd = dec->dh[i] << 1;
+	SATURATE(wd, 32767, -32768);
+	dec->szh += (dec->bh[i] * wd) >> 15 ;
+	SATURATE(dec->szh, 32767, -32768);
+    }
+
+    /* PREDIC */
+    sh = dec->sph + dec->szh ;
+    SATURATE(sh, 32767, -32768);
+
+    return (sh) ;
+}
+
+static int block5h (int dh, int sh)
+{
+    int rh ;
+
+    rh = dh + sh;
+    SATURATE(rh, 16383, -16384);
+
+    return (rh) ;
+}
+
+void rx_qmf(g722_dec_t *dec, int rl, int rh, int *xout1, int *xout2)
+{
+    int i;
+
+    pj_memmove(&dec->xd[1], dec->xd, 11*sizeof(dec->xd[0]));
+    pj_memmove(&dec->xs[1], dec->xs, 11*sizeof(dec->xs[0]));
+
+    /* RECA */
+    dec->xd[0] = rl - rh ;
+    if (dec->xd[0] > 16383) dec->xd[0] = 16383;
+    else if (dec->xd[0] < -16384) dec->xd[0] = -16384;
+    
+    /* RECB */
+    dec->xs[0] = rl + rh ;
+    if (dec->xs[0] > 16383) dec->xs[0] = 16383;
+    else if (dec->xs[0] < -16384) dec->xs[0] = -16384;
+    
+    /* ACCUMC */
+    *xout1 = 0;
+    for (i=0; i<12; ++i) *xout1 += dec->xd[i] * g722_qmf_coeff[2*i];
+    *xout1 = *xout1 >> 12 ;
+    if (*xout1 >  16383)  *xout1 =  16383 ;
+    else if (*xout1 < -16384)  *xout1 = -16384 ;
+    
+    /* ACCUMD */
+    *xout2 = 0;
+    for (i=0; i<12; ++i) *xout2 += dec->xs[i] * g722_qmf_coeff[2*i+1];
+    *xout2  = *xout2  >> 12 ;
+    if (*xout2  >  16383)  *xout2  =  16383 ;
+    else if (*xout2  < -16384)  *xout2  = -16384 ;
+}
+
+
+PJ_DEF(pj_status_t) g722_dec_init(g722_dec_t *dec)
+{
+    PJ_ASSERT_RETURN(dec, PJ_EINVAL);
+
+    pj_bzero(dec, sizeof(g722_dec_t));
+
+    dec->detlow = 32;
+    dec->dethigh = 8;
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) g722_dec_decode( g722_dec_t *dec, 
+				    void *in, 
+				    pj_size_t in_size,
+				    pj_int16_t out[],
+				    pj_size_t *nsamples)
+{
+    unsigned i;
+    int ilowr, ylow, rlow, dlowt;
+    int ihigh, rhigh, dhigh;
+    int pcm1, pcm2;
+    pj_uint8_t *in_ = (pj_uint8_t*) in;
+
+    PJ_ASSERT_RETURN(dec && in && in_size && out && nsamples, PJ_EINVAL);
+    PJ_ASSERT_RETURN(*nsamples >= (in_size << 1), PJ_ETOOSMALL);
+
+    for(i = 0; i < in_size; ++i) {
+	ilowr = in_[i] & 63;
+	ihigh = (in_[i] >> 6) & 3;
+
+	/* low band decoder */
+	ylow = block5l (ilowr, dec->slow, dec->detlow, MODE) ;	
+	rlow = block6l (ylow) ;
+	dlowt = block2l (ilowr, dec->detlow) ;
+	dec->detlow = block3l (dec, ilowr) ;
+	dec->slow = block4l (dec, dlowt) ;
+	/* rlow <= output low band pcm */
+
+	/* high band decoder */
+	dhigh = block2h (ihigh, dec->dethigh) ;
+	rhigh = block5h (dhigh, dec->shigh) ;
+	dec->dethigh = block3h (dec, ihigh) ;
+	dec->shigh = block4h (dec, dhigh) ;
+	/* rhigh <= output high band pcm */
+
+	rx_qmf(dec, rlow, rhigh, &pcm1, &pcm2);
+	out[i*2]   = (pj_int16_t)pcm1;
+	out[i*2+1] = (pj_int16_t)pcm2;
+    }
+
+    *nsamples = in_size << 1;
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) g722_dec_deinit(g722_dec_t *dec)
+{
+    pj_bzero(dec, sizeof(g722_dec_t));
+
+    return PJ_SUCCESS;
+}
+
+#endif
diff --git a/jni/pjproject-android/pjmedia/src/pjmedia-codec/g722/g722_dec.h b/jni/pjproject-android/pjmedia/src/pjmedia-codec/g722/g722_dec.h
new file mode 100644
index 0000000..3cf841e
--- /dev/null
+++ b/jni/pjproject-android/pjmedia/src/pjmedia-codec/g722/g722_dec.h
@@ -0,0 +1,79 @@
+/* $Id: g722_dec.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+/*
+ * Based on implementation found in Carnegie Mellon Speech Group Software
+ * depository (ftp://ftp.cs.cmu.edu/project/fgdata/index.html). No copyright
+ * was claimed in the original source codes.
+ */
+#ifndef __PJMEDIA_CODEC_G722_DEC_H__
+#define __PJMEDIA_CODEC_G722_DEC_H__
+
+#include <pjmedia-codec/types.h>
+
+/* Decoder state */
+typedef struct g722_dec_t {
+    /* PCM low band */
+    int slow;
+    int detlow;
+    int spl;
+    int szl;
+    int rlt  [3];
+    int al   [3];
+    int apl  [3];
+    int plt  [3];
+    int dlt  [7];
+    int bl   [7];
+    int bpl  [7];
+    int sgl  [7];
+    int nbl;
+
+    /* PCM high band*/
+    int shigh;
+    int dethigh;
+    int sph;
+    int szh;
+    int rh   [3];
+    int ah   [3];
+    int aph  [3];
+    int ph   [3];
+    int dh   [7];
+    int bh   [7];
+    int bph  [7];
+    int sgh  [7];
+    int nbh;
+
+    /* QMF signal history */
+    int xd[12];
+    int xs[12];
+} g722_dec_t;
+
+
+PJ_DECL(pj_status_t) g722_dec_init(g722_dec_t *dec);
+
+PJ_DECL(pj_status_t) g722_dec_decode(g722_dec_t *dec, 
+				     void *in, 
+				     pj_size_t in_size,
+				     pj_int16_t out[],
+				     pj_size_t *nsamples);
+
+PJ_DECL(pj_status_t) g722_dec_deinit(g722_dec_t *dec);
+
+#endif /* __PJMEDIA_CODEC_G722_DEC_H__ */
+
diff --git a/jni/pjproject-android/pjmedia/src/pjmedia-codec/g722/g722_enc.c b/jni/pjproject-android/pjmedia/src/pjmedia-codec/g722/g722_enc.c
new file mode 100644
index 0000000..d0b2011
--- /dev/null
+++ b/jni/pjproject-android/pjmedia/src/pjmedia-codec/g722/g722_enc.c
@@ -0,0 +1,576 @@
+/* $Id: g722_enc.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+/*
+ * Based on implementation found in Carnegie Mellon Speech Group Software
+ * depository (ftp://ftp.cs.cmu.edu/project/fgdata/index.html). No copyright
+ * was claimed in the original source codes.
+ */
+#include <pjmedia/errno.h>
+#include <pj/assert.h>
+#include <pj/pool.h>
+
+#include "g722_enc.h"
+
+#if defined(PJMEDIA_HAS_G722_CODEC) && (PJMEDIA_HAS_G722_CODEC != 0)
+
+#define SATURATE(v, max, min) \
+    if (v>max) v = max; \
+    else if (v<min) v = min
+
+/* QMF tap coefficients */
+const int g722_qmf_coeff[24] = {
+     3,	    -11,    -11,    53,	    12,	    -156,
+    32,	    362,    -210,   -805,   951,    3876,
+    3876,   951,    -805,   -210,   362,    32,
+    -156,   12,	    53,	    -11,    -11,    3
+};
+
+
+static int block1l (int xl, int sl, int detl)
+{
+    int il ;
+
+    int i, el, sil, mil, wd, wd1, hdu ;
+
+    static const int q6[32] = {
+	0, 35, 72, 110, 150, 190, 233, 276, 323,
+	370, 422, 473, 530, 587, 650, 714, 786,
+	858, 940, 1023, 1121, 1219, 1339, 1458,
+	1612, 1765, 1980, 2195, 2557, 2919, 0, 0
+    };
+
+    static const int iln[32] = {
+	0, 63, 62, 31, 30, 29, 28, 27, 26, 25,
+	24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14,
+	13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 0 
+    };
+
+    static const int ilp[32] = {
+	0, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52,
+	51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41,
+	40, 39, 38, 37, 36, 35, 34, 33, 32, 0 
+    };
+
+    /* SUBTRA */
+
+    el = xl - sl ;
+    SATURATE(el, 32767, -32768);
+
+    /* QUANTL */
+
+    sil = el >> 15 ;
+    if (sil == 0 )  wd = el ;
+    else wd = (32767 - el) & 32767 ;
+
+    mil = 1 ;
+
+    for (i = 1; i < 30; i++) {
+	hdu = (q6[i] << 3) * detl;
+	wd1 = (hdu >> 15) ;
+	if (wd >= wd1)  mil = (i + 1) ;
+	else break ;
+    }
+
+    if (sil == -1 ) il = iln[mil] ;
+    else il = ilp[mil] ;
+
+    return (il) ;
+}
+
+static int block2l (int il, int detl)
+{
+    int dlt;
+    int ril, wd2 ;
+    static const int qm4[16] = {
+	0,	-20456,	-12896,	-8968,
+	-6288,	-4240,	-2584,	-1200,
+	20456,	12896,	8968,	6288,
+	4240,	2584,	1200,	0
+    };
+
+    /* INVQAL */
+    ril = il >> 2 ;
+    wd2 = qm4[ril] ;
+    dlt = (detl * wd2) >> 15 ;
+
+    return (dlt) ;
+}
+
+static int block3l (g722_enc_t *enc, int il)
+{
+    int detl;
+    int ril, il4, wd, wd1, wd2, wd3, nbpl, depl ;
+    static int const wl[8] = {
+	-60, -30, 58, 172, 334, 538, 1198, 3042
+    } ;
+    static int const rl42[16] = {
+	0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, 2, 1, 0
+    };
+    static const int ilb[32] = {
+	2048, 2093, 2139, 2186, 2233, 2282, 2332,
+	2383, 2435, 2489, 2543, 2599, 2656, 2714,
+	2774, 2834, 2896, 2960, 3025, 3091, 3158,
+	3228, 3298, 3371, 3444, 3520, 3597, 3676,
+	3756, 3838, 3922, 4008
+    };
+
+    /* LOGSCL */
+
+    ril = il >> 2 ;
+    il4 = rl42[ril] ;
+    
+    wd = (enc->nbl * 32512) >> 15 ;
+    nbpl = wd + wl[il4] ;
+
+    if (nbpl <     0) nbpl = 0 ;
+    if (nbpl > 18432) nbpl = 18432 ;
+
+    /* SCALEL */
+
+    wd1 =  (nbpl >> 6) & 31 ;
+    wd2 = nbpl >> 11 ;
+    if ((8 - wd2) < 0)    wd3 = ilb[wd1] << (wd2 - 8) ;
+    else   wd3 = ilb[wd1] >> (8 - wd2) ;
+    depl = wd3 << 2 ;
+
+    /* DELAYA */
+    enc->nbl = nbpl ;
+
+    /* DELAYL */
+    detl = depl ;
+
+#ifdef DEBUG_VERBOSE
+	printf ("BLOCK3L il=%4d, ril=%4d, il4=%4d, nbl=%4d, wd=%4d, nbpl=%4d\n",
+		 il, ril, il4, enc->nbl, wd, nbpl) ;
+	printf ("wd1=%4d, wd2=%4d, wd3=%4d, depl=%4d, detl=%4d\n",
+		wd1, wd2, wd3, depl, detl) ;
+#endif
+
+    return (detl) ;
+}
+
+static int block4l (g722_enc_t *enc, int dl)
+{
+    int sl = enc->slow;
+    int i ;
+    int wd, wd1, wd2, wd3, wd4, wd5 /*, wd6 */;
+
+    enc->dlt[0] = dl;
+    
+    /* RECONS */
+
+    enc->rlt[0] = sl + enc->dlt[0] ;
+    SATURATE(enc->rlt[0], 32767, -32768);
+
+    /* PARREC */
+
+    enc->plt[0] = enc->dlt[0] + enc->szl ;
+    SATURATE(enc->plt[0], 32767, -32768);
+
+    /* UPPOL2 */
+
+    enc->sgl[0] = enc->plt[0] >> 15 ;
+    enc->sgl[1] = enc->plt[1] >> 15 ;
+    enc->sgl[2] = enc->plt[2] >> 15 ;
+
+    wd1 = enc->al[1] << 2;
+    SATURATE(wd1, 32767, -32768);
+
+    if ( enc->sgl[0] == enc->sgl[1] )  wd2 = - wd1 ;
+    else  wd2 = wd1 ;
+    if ( wd2 > 32767 ) wd2 = 32767;
+
+    wd2 = wd2 >> 7 ;
+
+    if ( enc->sgl[0] == enc->sgl[2] )  wd3 = 128 ;
+    else  wd3 = - 128 ;
+
+    wd4 = wd2 + wd3 ;
+    wd5 = (enc->al[2] * 32512) >> 15 ;
+
+    enc->apl[2] = wd4 + wd5 ;
+    SATURATE(enc->apl[2], 12288, -12288);
+
+    /* UPPOL1 */
+
+    enc->sgl[0] = enc->plt[0] >> 15 ;
+    enc->sgl[1] = enc->plt[1] >> 15 ;
+
+    if ( enc->sgl[0] == enc->sgl[1] )  wd1 = 192 ;
+    else  wd1 = - 192 ;
+
+    wd2 = (enc->al[1] * 32640) >> 15 ;
+
+    enc->apl[1] = wd1 + wd2 ;
+    SATURATE(enc->apl[1], 32767, -32768);
+
+    wd3 = (15360 - enc->apl[2]) ;
+    SATURATE(wd3, 32767, -32768);
+
+    if ( enc->apl[1] >  wd3)  enc->apl[1] =  wd3 ;
+    if ( enc->apl[1] < -wd3)  enc->apl[1] = -wd3 ;
+
+    /* UPZERO */
+
+    if ( enc->dlt[0] == 0 )  wd1 = 0 ;
+    else wd1 = 128 ;
+
+    enc->sgl[0] = enc->dlt[0] >> 15 ;
+
+    for ( i = 1; i < 7; i++ ) {
+	enc->sgl[i] = enc->dlt[i] >> 15 ;
+	if ( enc->sgl[i] == enc->sgl[0] )  wd2 = wd1 ;
+	else wd2 = - wd1 ;
+	wd3 = (enc->bl[i] * 32640) >> 15 ;
+	enc->bpl[i] = wd2 + wd3 ;
+	SATURATE(enc->bpl[i], 32767, -32768);
+    }
+
+    /* DELAYA */
+
+    for ( i = 6; i > 0; i-- ) {
+	enc->dlt[i] = enc->dlt[i-1] ;
+	enc->bl[i]  = enc->bpl[i] ;
+    }
+
+    for ( i = 2; i > 0; i-- ) {
+	enc->rlt[i] = enc->rlt[i-1] ;
+	enc->plt[i] = enc->plt[i-1] ;
+	enc->al[i] = enc->apl[i] ;
+    }
+
+    /* FILTEP */
+
+    wd1 = enc->rlt[1] + enc->rlt[1];
+    SATURATE(wd1, 32767, -32768);
+    wd1 = ( enc->al[1] * wd1 ) >> 15 ;
+
+    wd2 = enc->rlt[2] + enc->rlt[2];
+    SATURATE(wd2, 32767, -32768);
+    wd2 = ( enc->al[2] * wd2 ) >> 15 ;
+
+    enc->spl = wd1 + wd2 ;
+    SATURATE(enc->spl, 32767, -32768);
+
+    /* FILTEZ */
+
+    enc->szl = 0 ;
+    for (i=6; i>0; i--) {
+	wd = enc->dlt[i] + enc->dlt[i];
+	SATURATE(wd, 32767, -32768);
+	enc->szl += (enc->bl[i] * wd) >> 15 ;
+	SATURATE(enc->szl, 32767, -32768);
+    }
+
+    /* PREDIC */
+
+    sl = enc->spl + enc->szl ;
+    SATURATE(sl, 32767, -32768);
+
+    return (sl) ;
+}
+
+static int block1h (int xh, int sh, int deth)
+{
+    int ih ;
+
+    int eh, sih, mih, wd, wd1, hdu ;
+
+    static const int ihn[3] = { 0, 1, 0 } ;
+    static const int ihp[3] = { 0, 3, 2 } ;
+
+    /* SUBTRA */
+
+    eh = xh - sh ;
+    SATURATE(eh, 32767, -32768);
+
+    /* QUANTH */
+
+    sih = eh >> 15 ;
+    if (sih == 0 )  wd = eh ;
+    else wd = (32767 - eh) & 32767 ;
+
+    hdu = (564 << 3) * deth;
+    wd1 = (hdu >> 15) ;
+    if (wd >= wd1)  mih = 2 ;
+    else mih = 1 ;
+
+    if (sih == -1 ) ih = ihn[mih] ;
+    else ih = ihp[mih] ;
+
+    return (ih) ;
+}
+
+static int block2h (int ih, int deth)
+{
+    int dh ;
+    int wd2 ;
+    static const int qm2[4] = {-7408, -1616, 7408, 1616};
+    
+    /* INVQAH */
+
+    wd2 = qm2[ih] ;
+    dh = (deth * wd2) >> 15 ;
+
+    return (dh) ;
+}
+
+static int block3h (g722_enc_t *enc, int ih)
+{
+    int deth ;
+    int ih2, wd, wd1, wd2, wd3, nbph, deph ;
+    static const int wh[3] = {0, -214, 798} ;
+    static const int rh2[4] = {2, 1, 2, 1} ;
+    static const int ilb[32] = {
+	2048, 2093, 2139, 2186, 2233, 2282, 2332,
+	2383, 2435, 2489, 2543, 2599, 2656, 2714,
+	2774, 2834, 2896, 2960, 3025, 3091, 3158,
+	3228, 3298, 3371, 3444, 3520, 3597, 3676,
+	3756, 3838, 3922, 4008
+    };
+
+    /* LOGSCH */
+
+    ih2 = rh2[ih] ;
+    wd = (enc->nbh * 32512) >> 15 ;
+    nbph = wd + wh[ih2] ;
+
+    if (nbph <     0) nbph = 0 ;
+    if (nbph > 22528) nbph = 22528 ;
+
+    /* SCALEH */
+
+    wd1 =  (nbph >> 6) & 31 ;
+    wd2 = nbph >> 11 ;
+    if ((10-wd2) < 0) wd3 = ilb[wd1] << (wd2-10) ;
+    else wd3 = ilb[wd1] >> (10-wd2) ;
+    deph = wd3 << 2 ;
+
+    /* DELAYA */
+    enc->nbh = nbph ;
+    /* DELAYH */
+    deth = deph ;
+
+    return (deth) ;
+}
+
+static int block4h (g722_enc_t *enc, int d)
+{
+    int sh = enc->shigh;
+    int i ;
+    int wd, wd1, wd2, wd3, wd4, wd5 /*, wd6 */;
+
+    enc->dh[0] = d;
+
+    /* RECONS */
+
+    enc->rh[0] = sh + enc->dh[0] ;
+    SATURATE(enc->rh[0], 32767, -32768);
+
+    /* PARREC */
+
+    enc->ph[0] = enc->dh[0] + enc->szh ;
+    SATURATE(enc->ph[0], 32767, -32768);
+
+    /* UPPOL2 */
+
+    enc->sgh[0] = enc->ph[0] >> 15 ;
+    enc->sgh[1] = enc->ph[1] >> 15 ;
+    enc->sgh[2] = enc->ph[2] >> 15 ;
+
+    wd1 = enc->ah[1] << 2;
+    SATURATE(wd1, 32767, -32768);
+
+    if ( enc->sgh[0] == enc->sgh[1] )  wd2 = - wd1 ;
+    else  wd2 = wd1 ;
+    if ( wd2 > 32767 ) wd2 = 32767;
+
+    wd2 = wd2 >> 7 ;
+
+    if ( enc->sgh[0] == enc->sgh[2] )  wd3 = 128 ;
+    else  wd3 = - 128 ;
+
+    wd4 = wd2 + wd3 ;
+    wd5 = (enc->ah[2] * 32512) >> 15 ;
+
+    enc->aph[2] = wd4 + wd5 ;
+    SATURATE(enc->aph[2], 12288, -12288);
+
+    /* UPPOL1 */
+
+    enc->sgh[0] = enc->ph[0] >> 15 ;
+    enc->sgh[1] = enc->ph[1] >> 15 ;
+
+    if ( enc->sgh[0] == enc->sgh[1] )  wd1 = 192 ;
+    else wd1 = - 192 ;
+
+    wd2 = (enc->ah[1] * 32640) >> 15 ;
+
+    enc->aph[1] = wd1 + wd2 ;
+    SATURATE(enc->aph[1], 32767, -32768);
+
+    wd3 = (15360 - enc->aph[2]) ;
+    SATURATE(wd3, 32767, -32768);
+
+    if ( enc->aph[1] >  wd3)  enc->aph[1] =  wd3 ;
+    else if ( enc->aph[1] < -wd3)  enc->aph[1] = -wd3 ;
+
+    /* UPZERO */
+
+    if ( enc->dh[0] == 0 )  wd1 = 0 ;
+    else wd1 = 128 ;
+
+    enc->sgh[0] = enc->dh[0] >> 15 ;
+
+    for ( i = 1; i < 7; i++ ) {
+	enc->sgh[i] = enc->dh[i] >> 15 ;
+	if ( enc->sgh[i] == enc->sgh[0] )  wd2 = wd1 ;
+	else wd2 = - wd1 ;
+	wd3 = (enc->bh[i] * 32640) >> 15 ;
+	enc->bph[i] = wd2 + wd3 ;
+	SATURATE(enc->bph[i], 32767, -32768);
+    }
+
+    /* DELAYA */
+    for ( i = 6; i > 0; i-- ) {
+	enc->dh[i] = enc->dh[i-1] ;
+	enc->bh[i]  = enc->bph[i] ;
+    }
+
+    for ( i = 2; i > 0; i-- ) {
+	enc->rh[i] = enc->rh[i-1] ;
+	enc->ph[i] = enc->ph[i-1] ;
+	enc->ah[i] = enc->aph[i] ;
+    }
+
+    /* FILTEP */
+
+    wd1 = enc->rh[1] + enc->rh[1];
+    SATURATE(wd1, 32767, -32768);
+    wd1 = ( enc->ah[1] * wd1 ) >> 15 ;
+
+    wd2 = enc->rh[2] + enc->rh[2];
+    SATURATE(wd2, 32767, -32768);
+    wd2 = ( enc->ah[2] * wd2 ) >> 15 ;
+
+    enc->sph = wd1 + wd2 ;
+    SATURATE(enc->sph, 32767, -32768);
+
+    /* FILTEZ */
+
+    enc->szh = 0 ;
+    for (i=6; i>0; i--) {
+	wd = enc->dh[i] + enc->dh[i];
+	SATURATE(wd, 32767, -32768);
+	enc->szh += (enc->bh[i] * wd) >> 15 ;
+	SATURATE(enc->szh, 32767, -32768);
+    }
+
+    /* PREDIC */
+
+    sh = enc->sph + enc->szh ;
+    SATURATE(sh, 32767, -32768);
+
+    return (sh) ;
+}
+
+/* PROCESS PCM THROUGH THE QMF FILTER */
+static void tx_qmf(g722_enc_t *enc, int pcm1, int pcm2, int *lo, int *hi)
+{
+    int sumodd, sumeven;
+    int i;
+
+    pj_memmove(&enc->x[2], enc->x, 22 * sizeof(enc->x[0]));
+    enc->x[1] = pcm1; 
+    enc->x[0] = pcm2;
+
+    sumodd = 0;
+    for (i=1; i<24; i+=2) sumodd += enc->x[i] * g722_qmf_coeff[i];
+
+    sumeven = 0;
+    for (i=0; i<24; i+=2) sumeven += enc->x[i] * g722_qmf_coeff[i];
+
+    *lo  = (sumeven + sumodd) >> 13  ;
+    *hi = (sumeven - sumodd) >> 13  ;
+
+    SATURATE(*lo, 16383, -16384);
+    SATURATE(*hi, 16383, -16383);
+}
+
+
+PJ_DEF(pj_status_t) g722_enc_init(g722_enc_t *enc)
+{
+    PJ_ASSERT_RETURN(enc, PJ_EINVAL);
+    
+    pj_bzero(enc, sizeof(g722_enc_t));
+
+    enc->detlow = 32;
+    enc->dethigh = 8;
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) g722_enc_encode( g722_enc_t *enc, 
+				     pj_int16_t in[], 
+				     pj_size_t nsamples,
+				     void *out,
+				     pj_size_t *out_size)
+{
+    unsigned i;
+    int xlow, ilow, dlowt;
+    int xhigh, ihigh, dhigh;
+    pj_uint8_t *out_ = (pj_uint8_t*) out;
+
+    PJ_ASSERT_RETURN(enc && in && nsamples && out && out_size, PJ_EINVAL);
+    PJ_ASSERT_RETURN(nsamples % 2 == 0, PJ_EINVAL);
+    PJ_ASSERT_RETURN(*out_size >= (nsamples >> 1), PJ_ETOOSMALL);
+    
+    for(i = 0; i < nsamples; i += 2) {
+	tx_qmf(enc, in[i], in[i+1], &xlow, &xhigh);
+
+	/* low band encoder */
+	ilow = block1l (xlow, enc->slow, enc->detlow) ;
+	dlowt = block2l (ilow, enc->detlow) ;
+	enc->detlow = block3l (enc, ilow) ;
+	enc->slow = block4l (enc, dlowt) ;
+
+	/* high band encoder */
+	ihigh = block1h (xhigh, enc->shigh, enc->dethigh) ;
+	dhigh = block2h (ihigh, enc->dethigh) ;
+	enc->dethigh = block3h (enc, ihigh) ;
+	enc->shigh = block4h (enc, dhigh) ;
+
+	/* bits mix low & high adpcm */
+	out_[i/2] = (pj_uint8_t)((ihigh << 6) | ilow);
+    }
+
+    *out_size = nsamples >> 1;
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) g722_enc_deinit(g722_enc_t *enc)
+{
+    pj_bzero(enc, sizeof(g722_enc_t));
+
+    return PJ_SUCCESS;
+}
+
+#endif
diff --git a/jni/pjproject-android/pjmedia/src/pjmedia-codec/g722/g722_enc.h b/jni/pjproject-android/pjmedia/src/pjmedia-codec/g722/g722_enc.h
new file mode 100644
index 0000000..96f9af6
--- /dev/null
+++ b/jni/pjproject-android/pjmedia/src/pjmedia-codec/g722/g722_enc.h
@@ -0,0 +1,78 @@
+/* $Id: g722_enc.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+/*
+ * Based on implementation found in Carnegie Mellon Speech Group Software
+ * depository (ftp://ftp.cs.cmu.edu/project/fgdata/index.html). No copyright
+ * was claimed in the original source codes.
+ */
+#ifndef __PJMEDIA_CODEC_G722_ENC_H__
+#define __PJMEDIA_CODEC_G722_ENC_H__
+
+#include <pjmedia-codec/types.h>
+
+/* Encoder state */
+typedef struct g722_enc_t {
+    /* PCM low band */
+    int slow;
+    int detlow;
+    int spl;
+    int szl;
+    int rlt  [3];
+    int al   [3];
+    int apl  [3];
+    int plt  [3];
+    int dlt  [7];
+    int bl   [7];
+    int bpl  [7];
+    int sgl  [7];
+    int nbl;
+
+    /* PCM high band*/
+    int shigh;
+    int dethigh;
+    int sph;
+    int szh;
+    int rh   [3];
+    int ah   [3];
+    int aph  [3];
+    int ph   [3];
+    int dh   [7];
+    int bh   [7];
+    int bph  [7];
+    int sgh  [7];
+    int nbh;
+
+    /* QMF signal history */
+    int x[24];
+} g722_enc_t;
+
+
+PJ_DECL(pj_status_t) g722_enc_init(g722_enc_t *enc);
+
+PJ_DECL(pj_status_t) g722_enc_encode(g722_enc_t *enc, 
+				     pj_int16_t in[], 
+				     pj_size_t nsamples,
+				     void *out,
+				     pj_size_t *out_size);
+
+PJ_DECL(pj_status_t) g722_enc_deinit(g722_enc_t *enc);
+
+#endif /* __PJMEDIA_CODEC_G722_ENC_H__ */
+
diff --git a/jni/pjproject-android/pjmedia/src/pjmedia-codec/g7221.c b/jni/pjproject-android/pjmedia/src/pjmedia-codec/g7221.c
new file mode 100644
index 0000000..2d38483
--- /dev/null
+++ b/jni/pjproject-android/pjmedia/src/pjmedia-codec/g7221.c
@@ -0,0 +1,950 @@
+/* $Id: g7221.c 4001 2012-03-30 07:53:36Z bennylp $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjmedia-codec/g7221.h>
+#include <pjmedia-codec/g7221_sdp_match.h>
+#include <pjmedia/codec.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/endpoint.h>
+#include <pjmedia/port.h>
+#include <pjmedia/silencedet.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/math.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/os.h>
+
+/*
+ * Only build this file if PJMEDIA_HAS_G7221_CODEC != 0
+ */
+#if defined(PJMEDIA_HAS_G7221_CODEC) && PJMEDIA_HAS_G7221_CODEC!=0
+
+#include "../../../third_party/g7221/common/defs.h"
+
+#define THIS_FILE	    "g7221.c"
+
+/* Codec tag, it is the SDP encoding name and also MIME subtype name */
+#define CODEC_TAG	    "G7221"
+
+/* Sampling rates definition */
+#define WB_SAMPLE_RATE	    16000
+#define UWB_SAMPLE_RATE	    32000
+
+/* Maximum number of samples per frame. */
+#define MAX_SAMPLES_PER_FRAME (UWB_SAMPLE_RATE * 20 / 1000)
+
+/* Maximum number of codec params. */
+#define MAX_CODEC_MODES	    8
+#define START_RSV_MODES_IDX 6
+
+
+/* Prototypes for G722.1 codec factory */
+static pj_status_t test_alloc( pjmedia_codec_factory *factory, 
+			       const pjmedia_codec_info *id );
+static pj_status_t default_attr( pjmedia_codec_factory *factory, 
+				 const pjmedia_codec_info *id, 
+				 pjmedia_codec_param *attr );
+static pj_status_t enum_codecs( pjmedia_codec_factory *factory, 
+				unsigned *count, 
+				pjmedia_codec_info codecs[]);
+static pj_status_t alloc_codec( pjmedia_codec_factory *factory, 
+				const pjmedia_codec_info *id, 
+				pjmedia_codec **p_codec);
+static pj_status_t dealloc_codec( pjmedia_codec_factory *factory, 
+				  pjmedia_codec *codec );
+
+/* Prototypes for G722.1 codec implementation. */
+static pj_status_t codec_init( pjmedia_codec *codec, 
+			       pj_pool_t *pool );
+static pj_status_t codec_open( pjmedia_codec *codec, 
+			       pjmedia_codec_param *attr );
+static pj_status_t codec_close( pjmedia_codec *codec );
+static pj_status_t codec_modify(pjmedia_codec *codec, 
+			        const pjmedia_codec_param *attr );
+static pj_status_t codec_parse( pjmedia_codec *codec,
+			        void *pkt,
+				pj_size_t pkt_size,
+				const pj_timestamp *ts,
+				unsigned *frame_cnt,
+				pjmedia_frame frames[]);
+static pj_status_t codec_encode( pjmedia_codec *codec, 
+				 const struct pjmedia_frame *input,
+				 unsigned output_buf_len,
+				 struct pjmedia_frame *output);
+static pj_status_t codec_decode( pjmedia_codec *codec,
+				 const struct pjmedia_frame *input,
+				 unsigned output_buf_len, 
+				 struct pjmedia_frame *output);
+static pj_status_t codec_recover( pjmedia_codec *codec, 
+				  unsigned output_buf_len, 
+				  struct pjmedia_frame *output);
+
+/* Definition for G722.1 codec operations. */
+static pjmedia_codec_op codec_op = 
+{
+    &codec_init,
+    &codec_open,
+    &codec_close,
+    &codec_modify,
+    &codec_parse,
+    &codec_encode,
+    &codec_decode,
+    &codec_recover
+};
+
+/* Definition for G722.1 codec factory operations. */
+static pjmedia_codec_factory_op codec_factory_op =
+{
+    &test_alloc,
+    &default_attr,
+    &enum_codecs,
+    &alloc_codec,
+    &dealloc_codec,
+    &pjmedia_codec_g7221_deinit
+};
+
+
+/* Structure of G722.1 mode */
+typedef struct codec_mode
+{
+    pj_bool_t	     enabled;		/* Is this mode enabled?	    */
+    pj_uint8_t	     pt;		/* Payload type.		    */
+    unsigned	     sample_rate;	/* Default sampling rate to be used.*/
+    unsigned	     bitrate;		/* Bitrate.			    */
+    char	     bitrate_str[8];	/* Bitrate in string.		    */
+} codec_mode;
+
+
+/* G722.1 codec factory */
+static struct codec_factory {
+    pjmedia_codec_factory    base;	    /**< Base class.		    */
+    pjmedia_endpt	    *endpt;	    /**< PJMEDIA endpoint instance. */
+    pj_pool_t		    *pool;	    /**< Codec factory pool.	    */
+    pj_mutex_t		    *mutex;	    /**< Codec factory mutex.	    */
+
+    int			     pcm_shift;	    /**< Level adjustment	    */
+    unsigned		     mode_count;    /**< Number of G722.1 modes.    */
+    codec_mode		     modes[MAX_CODEC_MODES]; /**< The G722.1 modes. */
+    unsigned		     mode_rsv_start;/**< Start index of G722.1 non-
+						 standard modes, currently
+						 there can only be up to two 
+						 non-standard modes enabled
+						 at the same time.	    */
+} codec_factory;
+
+/* G722.1 codec private data. */
+typedef struct codec_private {
+    pj_pool_t		*pool;		    /**< Pool for each instance.    */
+    pj_bool_t		 plc_enabled;	    /**< PLC enabled?		    */
+    pj_bool_t		 vad_enabled;	    /**< VAD enabled?		    */
+    pjmedia_silence_det	*vad;		    /**< PJMEDIA VAD instance.	    */
+    pj_timestamp	 last_tx;	    /**< Timestamp of last transmit.*/
+
+    /* ITU ref implementation seems to leave the codec engine states to be
+     * managed by the application, so here we go.
+     */
+
+    /* Common engine state */
+    pj_uint16_t		 samples_per_frame; /**< Samples per frame.	    */
+    pj_uint16_t		 bitrate;	    /**< Coded stream bitrate.	    */
+    pj_uint16_t		 frame_size;	    /**< Coded frame size.	    */
+    pj_uint16_t		 frame_size_bits;   /**< Coded frame size in bits.  */
+    pj_uint16_t		 number_of_regions; /**< Number of regions.	    */
+    int			 pcm_shift;	    /**< Adjustment for PCM in/out  */
+    
+    /* Encoder specific state */
+    Word16		*enc_frame;	    /**< 16bit to 14bit buffer	    */
+    Word16		*enc_old_frame;
+    
+    /* Decoder specific state */
+    Word16		*dec_old_frame;
+    Rand_Obj		 dec_randobj;
+    Word16		 dec_old_mag_shift;
+    Word16		*dec_old_mlt_coefs;
+} codec_private_t;
+
+/* 
+ * Helper function for looking up mode based on payload type.
+ */
+static codec_mode* lookup_mode(unsigned pt)
+{
+    codec_mode* mode = NULL;
+    unsigned i;
+
+    for (i = 0; i < codec_factory.mode_count; ++i) {
+	mode = &codec_factory.modes[i];
+	if (mode->pt == pt)
+	    break;
+    }
+
+    return mode;
+}
+
+/* 
+ * Helper function to validate G722.1 mode. Valid modes are defined as:
+ * 1. sample rate must be 16kHz or 32kHz,
+ * 2. bitrate:
+ *    - for sampling rate 16kHz: 16000 to 32000 bps, it must be a multiple 
+ *      of 400 (to keep RTP payload octed-aligned)
+ *    - for sampling rate 32kHz: 24000 to 48000 bps, it must be a multiple 
+ *      of 400 (to keep RTP payload octed-aligned)
+ */
+static pj_bool_t validate_mode(unsigned sample_rate, unsigned bitrate)
+{
+    if (sample_rate == WB_SAMPLE_RATE) {
+	if (bitrate < 16000 || bitrate > 32000 ||
+	    ((bitrate-16000) % 400 != 0))
+	{
+	    return PJ_FALSE;
+	}
+    } else if (sample_rate == UWB_SAMPLE_RATE) {
+	if (bitrate < 24000 || bitrate > 48000 ||
+	    ((bitrate-24000) % 400 != 0))
+	{
+	    return PJ_FALSE;
+	}
+    } else {
+	return PJ_FALSE;
+    }
+
+    return PJ_TRUE;
+}
+
+#if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN!=0
+PJ_INLINE(void) swap_bytes(pj_uint16_t *buf, unsigned count)
+{
+    pj_uint16_t *end = buf + count;
+    while (buf != end) {
+	*buf = (pj_uint16_t)((*buf << 8) | (*buf >> 8));
+	++buf;
+    }
+}
+#else
+#define swap_bytes(buf, count)
+#endif
+
+/*
+ * Initialize and register G722.1 codec factory to pjmedia endpoint.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_g7221_init( pjmedia_endpt *endpt )
+{
+    pjmedia_codec_mgr *codec_mgr;
+    codec_mode *mode;
+    pj_str_t codec_name;
+    pj_status_t status;
+
+    if (codec_factory.pool != NULL) {
+	/* Already initialized. */
+	return PJ_SUCCESS;
+    }
+
+    /* Initialize codec modes, by default all standard bitrates are enabled */
+    codec_factory.mode_count = 0;
+    codec_factory.pcm_shift = PJMEDIA_G7221_DEFAULT_PCM_SHIFT;
+
+    mode = &codec_factory.modes[codec_factory.mode_count++];
+    mode->enabled = PJ_TRUE;
+    mode->pt = PJMEDIA_RTP_PT_G722_1_24;
+    mode->sample_rate = WB_SAMPLE_RATE;
+    mode->bitrate = 24000;
+    pj_utoa(mode->bitrate, mode->bitrate_str);
+
+    mode = &codec_factory.modes[codec_factory.mode_count++];
+    mode->enabled = PJ_TRUE;
+    mode->pt = PJMEDIA_RTP_PT_G722_1_32;
+    mode->sample_rate = WB_SAMPLE_RATE;
+    mode->bitrate = 32000;
+    pj_utoa(mode->bitrate, mode->bitrate_str);
+
+    mode = &codec_factory.modes[codec_factory.mode_count++];
+    mode->enabled = PJ_TRUE;
+    mode->pt = PJMEDIA_RTP_PT_G7221C_24;
+    mode->sample_rate = UWB_SAMPLE_RATE;
+    mode->bitrate = 24000;
+    pj_utoa(mode->bitrate, mode->bitrate_str);
+
+    mode = &codec_factory.modes[codec_factory.mode_count++];
+    mode->enabled = PJ_TRUE;
+    mode->pt = PJMEDIA_RTP_PT_G7221C_32;
+    mode->sample_rate = UWB_SAMPLE_RATE;
+    mode->bitrate = 32000;
+    pj_utoa(mode->bitrate, mode->bitrate_str);
+
+    mode = &codec_factory.modes[codec_factory.mode_count++];
+    mode->enabled = PJ_TRUE;
+    mode->pt = PJMEDIA_RTP_PT_G7221C_48;
+    mode->sample_rate = UWB_SAMPLE_RATE;
+    mode->bitrate = 48000;
+    pj_utoa(mode->bitrate, mode->bitrate_str);
+
+    /* Non-standard bitrates */
+
+    /* Bitrate 16kbps is non-standard but rather commonly used. */
+    mode = &codec_factory.modes[codec_factory.mode_count++];
+    mode->enabled = PJ_FALSE;
+    mode->pt = PJMEDIA_RTP_PT_G722_1_16;
+    mode->sample_rate = WB_SAMPLE_RATE;
+    mode->bitrate = 16000;
+    pj_utoa(mode->bitrate, mode->bitrate_str);
+
+    /* Reserved two modes for non-standard bitrates */
+    codec_factory.mode_rsv_start = codec_factory.mode_count;
+    mode = &codec_factory.modes[codec_factory.mode_count++];
+    mode->enabled = PJ_FALSE;
+    mode->pt = PJMEDIA_RTP_PT_G7221_RSV1;
+
+    mode = &codec_factory.modes[codec_factory.mode_count++];
+    mode->enabled = PJ_FALSE;
+    mode->pt = PJMEDIA_RTP_PT_G7221_RSV2;
+
+    pj_assert(codec_factory.mode_count <= MAX_CODEC_MODES);
+
+    /* Create G722.1 codec factory. */
+    codec_factory.base.op = &codec_factory_op;
+    codec_factory.base.factory_data = NULL;
+    codec_factory.endpt = endpt;
+
+    codec_factory.pool = pjmedia_endpt_create_pool(endpt, "G722.1 codec",
+						   4000, 4000);
+    if (!codec_factory.pool)
+	return PJ_ENOMEM;
+
+    /* Create mutex. */
+    status = pj_mutex_create_simple(codec_factory.pool, "G722.1 codec",
+				    &codec_factory.mutex);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Get the codec manager. */
+    codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
+    if (!codec_mgr) {
+	status = PJ_EINVALIDOP;
+	goto on_error;
+    }
+
+    /* Register format match callback. */
+    pj_cstr(&codec_name, CODEC_TAG);
+    status = pjmedia_sdp_neg_register_fmt_match_cb(
+					&codec_name,
+					&pjmedia_codec_g7221_match_sdp);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Register codec factory to endpoint. */
+    status = pjmedia_codec_mgr_register_factory(codec_mgr, 
+						&codec_factory.base);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Done. */
+    return PJ_SUCCESS;
+
+on_error:
+    if (codec_factory.mutex) {
+	pj_mutex_destroy(codec_factory.mutex);
+	codec_factory.mutex = NULL;
+    }
+
+    pj_pool_release(codec_factory.pool);
+    codec_factory.pool = NULL;
+    return status;
+}
+
+
+/**
+ * Enable and disable G722.1 modes, including non-standard modes.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_g7221_set_mode(unsigned sample_rate, 
+						 unsigned bitrate, 
+						 pj_bool_t enabled)
+{
+    unsigned i;
+
+    /* Validate mode */
+    if (!validate_mode(sample_rate, bitrate))
+	return PJMEDIA_CODEC_EINMODE;
+
+    /* Look up in factory modes table */
+    for (i = 0; i < codec_factory.mode_count; ++i) {
+	if (codec_factory.modes[i].sample_rate == sample_rate &&
+	    codec_factory.modes[i].bitrate == bitrate)
+	{
+	    codec_factory.modes[i].enabled = enabled;
+	    return PJ_SUCCESS;
+	}
+    }
+
+    /* Mode not found in modes table, this may be a request to enable
+     * a non-standard G722.1 mode.
+     */
+
+    /* Non-standard mode need to be initialized first before user 
+     * can disable it.
+     */
+    if (!enabled)
+	return PJ_ENOTFOUND;
+
+    /* Initialize a non-standard mode, look for available space. */
+    for (i = codec_factory.mode_rsv_start; 
+	 i < codec_factory.mode_count; ++i) 
+    {
+	if (!codec_factory.modes[i].enabled)
+	{
+	    codec_mode *mode = &codec_factory.modes[i];
+	    mode->enabled = PJ_TRUE;
+	    mode->sample_rate = sample_rate;
+	    mode->bitrate = bitrate;
+	    pj_utoa(mode->bitrate, mode->bitrate_str);
+
+	    return PJ_SUCCESS;
+	}
+    }
+    
+    /* No space for non-standard mode. */
+    return PJ_ETOOMANY;
+}
+
+
+/*
+ * Set level adjustment.
+ */
+PJ_DEF(pj_status_t)  pjmedia_codec_g7221_set_pcm_shift(int val)
+{
+    codec_factory.pcm_shift = val;
+    return PJ_SUCCESS;
+}
+
+/*
+ * Unregister G722.1 codec factory from pjmedia endpoint.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_g7221_deinit(void)
+{
+    pjmedia_codec_mgr *codec_mgr;
+    pj_status_t status;
+
+    if (codec_factory.pool == NULL) {
+	/* Already deinitialized */
+	return PJ_SUCCESS;
+    }
+
+    pj_mutex_lock(codec_factory.mutex);
+
+    /* Get the codec manager. */
+    codec_mgr = pjmedia_endpt_get_codec_mgr(codec_factory.endpt);
+    if (!codec_mgr) {
+	pj_pool_release(codec_factory.pool);
+	codec_factory.pool = NULL;
+	return PJ_EINVALIDOP;
+    }
+
+    /* Unregister G722.1 codec factory. */
+    status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
+						  &codec_factory.base);
+    
+    /* Destroy mutex. */
+    pj_mutex_destroy(codec_factory.mutex);
+
+    /* Destroy pool. */
+    pj_pool_release(codec_factory.pool);
+    codec_factory.pool = NULL;
+
+    return status;
+}
+
+/* 
+ * Check if factory can allocate the specified codec. 
+ */
+static pj_status_t test_alloc( pjmedia_codec_factory *factory, 
+			       const pjmedia_codec_info *info )
+{
+    PJ_UNUSED_ARG(factory);
+
+    /* Type MUST be audio. */
+    if (info->type != PJMEDIA_TYPE_AUDIO)
+	return PJMEDIA_CODEC_EUNSUP;
+
+    /* Check encoding name. */
+    if (pj_stricmp2(&info->encoding_name, CODEC_TAG) != 0)
+	return PJMEDIA_CODEC_EUNSUP;
+
+    /* Check clock-rate */
+    if (info->clock_rate != WB_SAMPLE_RATE && 
+	info->clock_rate != UWB_SAMPLE_RATE)
+    {
+	return PJMEDIA_CODEC_EUNSUP;
+    }
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Generate default attribute.
+ */
+static pj_status_t default_attr ( pjmedia_codec_factory *factory, 
+				  const pjmedia_codec_info *id, 
+				  pjmedia_codec_param *attr )
+{
+    codec_mode *mode;
+
+    PJ_ASSERT_RETURN(factory==&codec_factory.base, PJ_EINVAL);
+
+    pj_bzero(attr, sizeof(pjmedia_codec_param));
+
+    mode = lookup_mode(id->pt);
+    if (mode == NULL || !mode->enabled)
+	return PJMEDIA_CODEC_EUNSUP;
+
+    attr->info.pt = (pj_uint8_t)id->pt;
+    attr->info.channel_cnt = 1;
+    attr->info.clock_rate = mode->sample_rate;
+    attr->info.max_bps = mode->bitrate;
+    attr->info.avg_bps = mode->bitrate;
+    attr->info.pcm_bits_per_sample = 16;
+    attr->info.frm_ptime = 20;
+
+    /* Default flags. */
+    attr->setting.plc = 1;
+    attr->setting.vad = 0;
+    attr->setting.frm_per_pkt = 1;
+
+    /* Default FMTP setting */
+    attr->setting.dec_fmtp.cnt = 1;
+    attr->setting.dec_fmtp.param[0].name = pj_str("bitrate");
+    attr->setting.dec_fmtp.param[0].val = pj_str(mode->bitrate_str);
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Enum codecs supported by this factory.
+ */
+static pj_status_t enum_codecs( pjmedia_codec_factory *factory, 
+				unsigned *count, 
+				pjmedia_codec_info codecs[])
+{
+    unsigned i, max_cnt;
+
+    PJ_ASSERT_RETURN(factory==&codec_factory.base, PJ_EINVAL);
+    PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
+
+    max_cnt = *count;
+    *count = 0;
+    
+    for (i=0; (i < codec_factory.mode_count) && (*count < max_cnt); ++i)
+    {
+	if (!codec_factory.modes[i].enabled)
+	    continue;
+
+	pj_bzero(&codecs[*count], sizeof(pjmedia_codec_info));
+	codecs[*count].encoding_name = pj_str((char*)CODEC_TAG);
+	codecs[*count].pt = codec_factory.modes[i].pt;
+	codecs[*count].type = PJMEDIA_TYPE_AUDIO;
+	codecs[*count].clock_rate = codec_factory.modes[i].sample_rate;
+	codecs[*count].channel_cnt = 1;
+
+	++ *count;
+    }
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Allocate a new codec instance.
+ */
+static pj_status_t alloc_codec( pjmedia_codec_factory *factory, 
+				const pjmedia_codec_info *id,
+				pjmedia_codec **p_codec)
+{
+    codec_private_t *codec_data;
+    pjmedia_codec *codec;
+    pj_pool_t *pool;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory == &codec_factory.base, PJ_EINVAL);
+
+    pj_mutex_lock(codec_factory.mutex);
+
+    /* Create pool for codec instance */
+    pool = pjmedia_endpt_create_pool(codec_factory.endpt, "G7221", 512, 512);
+    codec = PJ_POOL_ZALLOC_T(pool, pjmedia_codec);
+    codec->op = &codec_op;
+    codec->factory = factory;
+    codec->codec_data = PJ_POOL_ZALLOC_T(pool, codec_private_t);
+    codec_data = (codec_private_t*) codec->codec_data;
+    codec_data->pool = pool;
+
+    /* Create silence detector */
+    status = pjmedia_silence_det_create(pool, id->clock_rate, 
+					id->clock_rate * 20 / 1000,
+					&codec_data->vad);
+    if (status != PJ_SUCCESS) {
+	pj_mutex_unlock(codec_factory.mutex);
+	return status;
+    }
+
+    pj_mutex_unlock(codec_factory.mutex);
+
+    *p_codec = codec;
+    return PJ_SUCCESS;
+}
+
+/*
+ * Free codec.
+ */
+static pj_status_t dealloc_codec( pjmedia_codec_factory *factory, 
+				  pjmedia_codec *codec )
+{
+    codec_private_t *codec_data;
+    pj_pool_t *pool;
+
+    PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory == &codec_factory.base, PJ_EINVAL);
+
+    /* Close codec, if it's not closed. */
+    codec_data = (codec_private_t*) codec->codec_data;
+    pool = codec_data->pool;
+    codec_close(codec);
+
+    /* Release codec pool */
+    pj_pool_release(pool);
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Init codec.
+ */
+static pj_status_t codec_init( pjmedia_codec *codec, 
+			       pj_pool_t *pool )
+{
+    PJ_UNUSED_ARG(codec);
+    PJ_UNUSED_ARG(pool);
+    return PJ_SUCCESS;
+}
+
+/*
+ * Open codec.
+ */
+static pj_status_t codec_open( pjmedia_codec *codec, 
+			       pjmedia_codec_param *attr )
+{
+    codec_private_t *codec_data = (codec_private_t*) codec->codec_data;
+    pj_pool_t *pool;
+    unsigned tmp;
+
+    /* Validation mode first! */
+    if (!validate_mode(attr->info.clock_rate, attr->info.avg_bps))
+	return PJMEDIA_CODEC_EINMODE;
+
+    pool = codec_data->pool;
+
+    /* Initialize common state */
+    codec_data->vad_enabled = (attr->setting.vad != 0);
+    codec_data->plc_enabled = (attr->setting.plc != 0);
+
+    codec_data->bitrate = (pj_uint16_t)attr->info.avg_bps;
+    codec_data->frame_size_bits = (pj_uint16_t)(attr->info.avg_bps*20/1000);
+    codec_data->frame_size = (pj_uint16_t)(codec_data->frame_size_bits>>3);
+    codec_data->samples_per_frame = (pj_uint16_t)
+				    (attr->info.clock_rate*20/1000);
+    codec_data->number_of_regions = (pj_uint16_t)
+				    (attr->info.clock_rate <= WB_SAMPLE_RATE?
+				     NUMBER_OF_REGIONS:MAX_NUMBER_OF_REGIONS);
+    codec_data->pcm_shift = codec_factory.pcm_shift;
+
+    /* Initialize encoder state */
+    tmp = codec_data->samples_per_frame << 1;
+    codec_data->enc_old_frame = (Word16*)pj_pool_zalloc(pool, tmp);
+    codec_data->enc_frame = (Word16*)pj_pool_alloc(pool, tmp);
+
+    /* Initialize decoder state */
+    tmp = codec_data->samples_per_frame;
+    codec_data->dec_old_frame = (Word16*)pj_pool_zalloc(pool, tmp);
+
+    tmp = codec_data->samples_per_frame << 1;
+    codec_data->dec_old_mlt_coefs = (Word16*)pj_pool_zalloc(pool, tmp);
+
+    codec_data->dec_randobj.seed0 = 1;
+    codec_data->dec_randobj.seed1 = 1;
+    codec_data->dec_randobj.seed2 = 1;
+    codec_data->dec_randobj.seed3 = 1;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Close codec.
+ */
+static pj_status_t codec_close( pjmedia_codec *codec )
+{
+    PJ_UNUSED_ARG(codec);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Modify codec settings.
+ */
+static pj_status_t codec_modify( pjmedia_codec *codec, 
+				 const pjmedia_codec_param *attr )
+{
+    codec_private_t *codec_data = (codec_private_t*) codec->codec_data;
+
+    codec_data->vad_enabled = (attr->setting.vad != 0);
+    codec_data->plc_enabled = (attr->setting.plc != 0);
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Get frames in the packet.
+ */
+static pj_status_t codec_parse( pjmedia_codec *codec,
+				void *pkt,
+				pj_size_t pkt_size,
+				const pj_timestamp *ts,
+				unsigned *frame_cnt,
+				pjmedia_frame frames[])
+{
+    codec_private_t *codec_data = (codec_private_t*) codec->codec_data;
+    unsigned count = 0;
+
+    PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL);
+
+    /* Parse based on fixed frame size. */
+    while (pkt_size >= codec_data->frame_size && count < *frame_cnt) {
+	frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
+	frames[count].buf = pkt;
+	frames[count].size = codec_data->frame_size;
+	frames[count].timestamp.u64 = ts->u64 + 
+				      count * codec_data->samples_per_frame;
+
+	pkt = (pj_uint8_t*)pkt + codec_data->frame_size;
+	pkt_size -= codec_data->frame_size;
+
+	++count;
+    }
+
+    pj_assert(pkt_size == 0);
+    *frame_cnt = count;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Encode frames.
+ */
+static pj_status_t codec_encode( pjmedia_codec *codec, 
+				 const struct pjmedia_frame *input,
+				 unsigned output_buf_len, 
+				 struct pjmedia_frame *output)
+{
+    codec_private_t *codec_data = (codec_private_t*) codec->codec_data;
+    unsigned nsamples, processed;
+
+    /* Check frame in & out size */
+    nsamples = input->size >> 1;
+    PJ_ASSERT_RETURN(nsamples % codec_data->samples_per_frame == 0, 
+		     PJMEDIA_CODEC_EPCMFRMINLEN);
+    PJ_ASSERT_RETURN(output_buf_len >= codec_data->frame_size * nsamples /
+		     codec_data->samples_per_frame,
+		     PJMEDIA_CODEC_EFRMTOOSHORT);
+
+    /* Apply silence detection if VAD is enabled */
+    if (codec_data->vad_enabled) {
+	pj_bool_t is_silence;
+	pj_int32_t silence_duration;
+
+	pj_assert(codec_data->vad);
+
+	silence_duration = pj_timestamp_diff32(&codec_data->last_tx, 
+					       &input->timestamp);
+
+	is_silence = pjmedia_silence_det_detect(codec_data->vad, 
+					        (const pj_int16_t*) input->buf,
+						(input->size >> 1),
+						NULL);
+	if (is_silence &&
+	    (PJMEDIA_CODEC_MAX_SILENCE_PERIOD == -1 ||
+	     silence_duration < (PJMEDIA_CODEC_MAX_SILENCE_PERIOD *
+			         (int)codec_data->samples_per_frame / 20)))
+	{
+	    output->type = PJMEDIA_FRAME_TYPE_NONE;
+	    output->buf = NULL;
+	    output->size = 0;
+	    output->timestamp = input->timestamp;
+	    return PJ_SUCCESS;
+	} else {
+	    codec_data->last_tx = input->timestamp;
+	}
+    }
+
+    processed = 0;
+    output->size = 0;
+    while (processed < nsamples) {
+	Word16 mlt_coefs[MAX_SAMPLES_PER_FRAME];
+	Word16 mag_shift;
+	const Word16 *pcm_input;
+	pj_int8_t *out_bits;
+	
+	pcm_input = (const Word16*)input->buf + processed;
+	out_bits = (pj_int8_t*)output->buf + output->size;
+
+	/* Encoder adjust the input signal level */
+	if (codec_data->pcm_shift) {
+	    unsigned i;
+	    for (i=0; i<codec_data->samples_per_frame; ++i) {
+		codec_data->enc_frame[i] = 
+			(Word16)(pcm_input[i] >> codec_data->pcm_shift);
+	    }
+	    pcm_input = codec_data->enc_frame;
+	}
+
+	/* Convert input samples to rmlt coefs */
+	mag_shift = samples_to_rmlt_coefs(pcm_input,
+					  codec_data->enc_old_frame, 
+					  mlt_coefs, 
+					  codec_data->samples_per_frame);
+
+	/* Encode the mlt coefs. Note that encoder output stream is
+	 * 16 bit array, so we need to take care about endianness.
+	 */
+	encoder(codec_data->frame_size_bits,
+		codec_data->number_of_regions,
+		mlt_coefs,
+		mag_shift,
+		(Word16*)out_bits);
+
+	/* Encoder output are in native host byte order, while ITU says
+	 * it must be in network byte order (MSB first).
+	 */
+	swap_bytes((pj_uint16_t*)out_bits, codec_data->frame_size/2);
+
+	processed += codec_data->samples_per_frame;
+	output->size += codec_data->frame_size;
+    }
+
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+    output->timestamp = input->timestamp;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Decode frame.
+ */
+static pj_status_t codec_decode( pjmedia_codec *codec, 
+				 const struct pjmedia_frame *input,
+				 unsigned output_buf_len, 
+				 struct pjmedia_frame *output)
+{
+    codec_private_t *codec_data = (codec_private_t*) codec->codec_data;
+    Word16 mlt_coefs[MAX_SAMPLES_PER_FRAME];
+    Word16 mag_shift;
+    Bit_Obj bitobj;
+    Word16 frame_error_flag = 0;
+
+    /* Check frame out length size */
+    PJ_ASSERT_RETURN(output_buf_len >= 
+		    (unsigned)(codec_data->samples_per_frame<<1),
+		     PJMEDIA_CODEC_EPCMTOOSHORT);
+
+    /* If input is NULL, perform PLC by settting frame_error_flag to 1 */
+    if (input) {
+	/* Check frame in length size */
+	PJ_ASSERT_RETURN((pj_uint16_t)input->size == codec_data->frame_size,
+			 PJMEDIA_CODEC_EFRMINLEN);
+
+	/* Decoder requires input of 16-bits array in native host byte
+	 * order, while the frame received from the network are in
+	 * network byte order (MSB first).
+	 */
+	swap_bytes((pj_uint16_t*)input->buf, codec_data->frame_size/2);
+
+	bitobj.code_word_ptr = (Word16*)input->buf;
+	bitobj.current_word =  *bitobj.code_word_ptr;
+	bitobj.code_bit_count = 0;
+	bitobj.number_of_bits_left = codec_data->frame_size_bits;
+
+	output->timestamp = input->timestamp;
+    } else {
+	pj_bzero(&bitobj, sizeof(bitobj));
+	frame_error_flag = 1;
+    }
+
+    /* Process the input frame to get mlt coefs */
+    decoder(&bitobj,
+	    &codec_data->dec_randobj,
+            codec_data->number_of_regions,
+	    mlt_coefs,
+            &mag_shift,
+	    &codec_data->dec_old_mag_shift,
+            codec_data->dec_old_mlt_coefs,
+            frame_error_flag);
+
+    /* Convert the mlt_coefs to PCM samples */
+    rmlt_coefs_to_samples(mlt_coefs, 
+			  codec_data->dec_old_frame, 
+			  (Word16*)output->buf, 
+			  codec_data->samples_per_frame, 
+			  mag_shift);
+
+    /* Decoder adjust PCM signal */
+    if (codec_data->pcm_shift) {
+	unsigned i;
+	pj_int16_t *buf = (Word16*)output->buf;
+
+	for (i=0; i<codec_data->samples_per_frame; ++i) {
+	    buf[i] <<= codec_data->pcm_shift;
+	}
+    }
+
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+    output->size = codec_data->samples_per_frame << 1;
+
+    return PJ_SUCCESS;
+}
+
+/* 
+ * Recover lost frame.
+ */
+static pj_status_t codec_recover( pjmedia_codec *codec, 
+				  unsigned output_buf_len, 
+				  struct pjmedia_frame *output)
+{
+    codec_private_t *codec_data = (codec_private_t*) codec->codec_data;
+
+    /* Use native PLC when PLC is enabled. */
+    if (codec_data->plc_enabled)
+	return codec_decode(codec, NULL, output_buf_len, output);
+
+    /* Otherwise just return zero-fill frame. */
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+    output->size = codec_data->samples_per_frame << 1;
+
+    pjmedia_zero_samples((pj_int16_t*)output->buf, 
+			 codec_data->samples_per_frame);
+
+    return PJ_SUCCESS;
+}
+
+#endif	/* PJMEDIA_HAS_G7221_CODEC */
diff --git a/jni/pjproject-android/pjmedia/src/pjmedia-codec/g7221_sdp_match.c b/jni/pjproject-android/pjmedia/src/pjmedia-codec/g7221_sdp_match.c
new file mode 100644
index 0000000..21e5b2c
--- /dev/null
+++ b/jni/pjproject-android/pjmedia/src/pjmedia-codec/g7221_sdp_match.c
@@ -0,0 +1,91 @@
+/* $Id: g7221_sdp_match.c 3911 2011-12-15 06:45:23Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjmedia-codec/g7221_sdp_match.h>
+#include <pjmedia/errno.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+
+#define GET_FMTP_IVAL_BASE(ival, base, fmtp, param, default_val) \
+    do { \
+	pj_str_t s; \
+	char *p; \
+	p = pj_stristr(&fmtp.fmt_param, &param); \
+	if (!p) { \
+	    ival = default_val; \
+	    break; \
+	} \
+	pj_strset(&s, p + param.slen, fmtp.fmt_param.slen - \
+		  (p - fmtp.fmt_param.ptr) - param.slen); \
+	ival = pj_strtoul2(&s, NULL, base); \
+    } while (0)
+
+#define GET_FMTP_IVAL(ival, fmtp, param, default_val) \
+	GET_FMTP_IVAL_BASE(ival, 10, fmtp, param, default_val)
+
+
+
+PJ_DEF(pj_status_t) pjmedia_codec_g7221_match_sdp(pj_pool_t *pool,
+						  pjmedia_sdp_media *offer,
+						  unsigned o_fmt_idx,
+						  pjmedia_sdp_media *answer,
+						  unsigned a_fmt_idx,
+						  unsigned option)
+{
+    const pjmedia_sdp_attr *attr_ans;
+    const pjmedia_sdp_attr *attr_ofr;
+    pjmedia_sdp_fmtp fmtp;
+    unsigned a_bitrate, o_bitrate;
+    const pj_str_t bitrate = {"bitrate=", 8};
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(pool);
+    PJ_UNUSED_ARG(option);
+
+    /* Parse offer */
+    attr_ofr = pjmedia_sdp_media_find_attr2(offer, "fmtp", 
+					    &offer->desc.fmt[o_fmt_idx]);
+    if (!attr_ofr)
+	return PJMEDIA_SDP_EINFMTP;
+
+    status = pjmedia_sdp_attr_get_fmtp(attr_ofr, &fmtp);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    GET_FMTP_IVAL(o_bitrate, fmtp, bitrate, 0);
+
+    /* Parse answer */
+    attr_ans = pjmedia_sdp_media_find_attr2(answer, "fmtp", 
+					    &answer->desc.fmt[a_fmt_idx]);
+    if (!attr_ans)
+	return PJMEDIA_SDP_EINFMTP;
+
+    status = pjmedia_sdp_attr_get_fmtp(attr_ans, &fmtp);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    GET_FMTP_IVAL(a_bitrate, fmtp, bitrate, 0);
+
+    /* Compare bitrate in answer and offer. */
+    if (a_bitrate != o_bitrate)
+	return PJMEDIA_SDP_EFORMATNOTEQUAL;
+
+    return PJ_SUCCESS;
+}
diff --git a/jni/pjproject-android/pjmedia/src/pjmedia-codec/gsm.c b/jni/pjproject-android/pjmedia/src/pjmedia-codec/gsm.c
new file mode 100644
index 0000000..c4508d0
--- /dev/null
+++ b/jni/pjproject-android/pjmedia/src/pjmedia-codec/gsm.c
@@ -0,0 +1,645 @@
+/* $Id: gsm.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjmedia-codec/gsm.h>
+#include <pjmedia/codec.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/endpoint.h>
+#include <pjmedia/plc.h>
+#include <pjmedia/port.h>
+#include <pjmedia/silencedet.h>
+#include <pj/assert.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/os.h>
+
+/*
+ * Only build this file if PJMEDIA_HAS_GSM_CODEC != 0
+ */
+#if defined(PJMEDIA_HAS_GSM_CODEC) && PJMEDIA_HAS_GSM_CODEC != 0
+
+#if defined(PJMEDIA_EXTERNAL_GSM_CODEC) && PJMEDIA_EXTERNAL_GSM_CODEC
+# if PJMEDIA_EXTERNAL_GSM_GSM_H
+#   include <gsm/gsm.h>
+# elif PJMEDIA_EXTERNAL_GSM_H
+#   include <gsm.h>
+# else
+#   error Please set the location of gsm.h
+# endif
+#else
+#   include "../../third_party/gsm/inc/gsm.h"
+#endif
+
+/* We removed PLC in 0.6 (and re-enabled it again in 0.9!) */
+#define PLC_DISABLED	0
+
+
+/* Prototypes for GSM factory */
+static pj_status_t gsm_test_alloc( pjmedia_codec_factory *factory, 
+				   const pjmedia_codec_info *id );
+static pj_status_t gsm_default_attr( pjmedia_codec_factory *factory, 
+				     const pjmedia_codec_info *id, 
+				     pjmedia_codec_param *attr );
+static pj_status_t gsm_enum_codecs( pjmedia_codec_factory *factory, 
+				    unsigned *count, 
+				    pjmedia_codec_info codecs[]);
+static pj_status_t gsm_alloc_codec( pjmedia_codec_factory *factory, 
+				    const pjmedia_codec_info *id, 
+				    pjmedia_codec **p_codec);
+static pj_status_t gsm_dealloc_codec( pjmedia_codec_factory *factory, 
+				      pjmedia_codec *codec );
+
+/* Prototypes for GSM implementation. */
+static pj_status_t  gsm_codec_init( pjmedia_codec *codec, 
+				    pj_pool_t *pool );
+static pj_status_t  gsm_codec_open( pjmedia_codec *codec, 
+				    pjmedia_codec_param *attr );
+static pj_status_t  gsm_codec_close( pjmedia_codec *codec );
+static pj_status_t  gsm_codec_modify(pjmedia_codec *codec, 
+				     const pjmedia_codec_param *attr );
+static pj_status_t  gsm_codec_parse( pjmedia_codec *codec,
+				     void *pkt,
+				     pj_size_t pkt_size,
+				     const pj_timestamp *ts,
+				     unsigned *frame_cnt,
+				     pjmedia_frame frames[]);
+static pj_status_t  gsm_codec_encode( pjmedia_codec *codec, 
+				      const struct pjmedia_frame *input,
+				      unsigned output_buf_len, 
+				      struct pjmedia_frame *output);
+static pj_status_t  gsm_codec_decode( pjmedia_codec *codec, 
+				      const struct pjmedia_frame *input,
+				      unsigned output_buf_len, 
+				      struct pjmedia_frame *output);
+#if !PLC_DISABLED
+static pj_status_t  gsm_codec_recover(pjmedia_codec *codec,
+				      unsigned output_buf_len,
+				      struct pjmedia_frame *output);
+#endif
+
+/* Definition for GSM codec operations. */
+static pjmedia_codec_op gsm_op = 
+{
+    &gsm_codec_init,
+    &gsm_codec_open,
+    &gsm_codec_close,
+    &gsm_codec_modify,
+    &gsm_codec_parse,
+    &gsm_codec_encode,
+    &gsm_codec_decode,
+#if !PLC_DISABLED
+    &gsm_codec_recover
+#else
+    NULL
+#endif
+};
+
+/* Definition for GSM codec factory operations. */
+static pjmedia_codec_factory_op gsm_factory_op =
+{
+    &gsm_test_alloc,
+    &gsm_default_attr,
+    &gsm_enum_codecs,
+    &gsm_alloc_codec,
+    &gsm_dealloc_codec,
+    &pjmedia_codec_gsm_deinit
+};
+
+/* GSM factory */
+static struct gsm_codec_factory
+{
+    pjmedia_codec_factory    base;
+    pjmedia_endpt	    *endpt;
+    pj_pool_t		    *pool;
+    pj_mutex_t		    *mutex;
+    pjmedia_codec	     codec_list;
+} gsm_codec_factory;
+
+
+/* GSM codec private data. */
+struct gsm_data
+{
+    struct gsm_state	*encoder;
+    struct gsm_state	*decoder;
+    pj_bool_t		 plc_enabled;
+#if !PLC_DISABLED
+    pjmedia_plc		*plc;
+#endif
+    pj_bool_t		 vad_enabled;
+    pjmedia_silence_det	*vad;
+    pj_timestamp	 last_tx;
+};
+
+
+
+/*
+ * Initialize and register GSM codec factory to pjmedia endpoint.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_gsm_init( pjmedia_endpt *endpt )
+{
+    pjmedia_codec_mgr *codec_mgr;
+    pj_status_t status;
+
+    if (gsm_codec_factory.pool != NULL)
+	return PJ_SUCCESS;
+
+    /* Create GSM codec factory. */
+    gsm_codec_factory.base.op = &gsm_factory_op;
+    gsm_codec_factory.base.factory_data = NULL;
+    gsm_codec_factory.endpt = endpt;
+
+    gsm_codec_factory.pool = pjmedia_endpt_create_pool(endpt, "gsm", 4000, 
+						       4000);
+    if (!gsm_codec_factory.pool)
+	return PJ_ENOMEM;
+
+    pj_list_init(&gsm_codec_factory.codec_list);
+
+    /* Create mutex. */
+    status = pj_mutex_create_simple(gsm_codec_factory.pool, "gsm", 
+				    &gsm_codec_factory.mutex);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Get the codec manager. */
+    codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
+    if (!codec_mgr) {
+	status = PJ_EINVALIDOP;
+	goto on_error;
+    }
+
+    /* Register codec factory to endpoint. */
+    status = pjmedia_codec_mgr_register_factory(codec_mgr, 
+						&gsm_codec_factory.base);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Done. */
+    return PJ_SUCCESS;
+
+on_error:
+    pj_pool_release(gsm_codec_factory.pool);
+    gsm_codec_factory.pool = NULL;
+    return status;
+}
+
+
+
+/*
+ * Unregister GSM codec factory from pjmedia endpoint and deinitialize
+ * the GSM codec library.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_gsm_deinit(void)
+{
+    pjmedia_codec_mgr *codec_mgr;
+    pj_status_t status;
+
+    if (gsm_codec_factory.pool == NULL)
+	return PJ_SUCCESS;
+
+    /* We don't want to deinit if there's outstanding codec. */
+    /* This is silly, as we'll always have codec in the list if
+       we ever allocate a codec! A better behavior maybe is to 
+       deallocate all codecs in the list.
+    pj_mutex_lock(gsm_codec_factory.mutex);
+    if (!pj_list_empty(&gsm_codec_factory.codec_list)) {
+	pj_mutex_unlock(gsm_codec_factory.mutex);
+	return PJ_EBUSY;
+    }
+    */
+
+    /* Get the codec manager. */
+    codec_mgr = pjmedia_endpt_get_codec_mgr(gsm_codec_factory.endpt);
+    if (!codec_mgr) {
+	pj_pool_release(gsm_codec_factory.pool);
+	gsm_codec_factory.pool = NULL;
+	return PJ_EINVALIDOP;
+    }
+
+    /* Unregister GSM codec factory. */
+    status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
+						  &gsm_codec_factory.base);
+    
+    /* Destroy mutex. */
+    pj_mutex_destroy(gsm_codec_factory.mutex);
+
+    /* Destroy pool. */
+    pj_pool_release(gsm_codec_factory.pool);
+    gsm_codec_factory.pool = NULL;
+
+    return status;
+}
+
+/* 
+ * Check if factory can allocate the specified codec. 
+ */
+static pj_status_t gsm_test_alloc( pjmedia_codec_factory *factory, 
+				   const pjmedia_codec_info *info )
+{
+    PJ_UNUSED_ARG(factory);
+
+    /* Check payload type. */
+    if (info->pt != PJMEDIA_RTP_PT_GSM)
+	return PJMEDIA_CODEC_EUNSUP;
+
+    /* Ignore the rest, since it's static payload type. */
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Generate default attribute.
+ */
+static pj_status_t gsm_default_attr (pjmedia_codec_factory *factory, 
+				      const pjmedia_codec_info *id, 
+				      pjmedia_codec_param *attr )
+{
+    PJ_UNUSED_ARG(factory);
+    PJ_UNUSED_ARG(id);
+
+    pj_bzero(attr, sizeof(pjmedia_codec_param));
+    attr->info.clock_rate = 8000;
+    attr->info.channel_cnt = 1;
+    attr->info.avg_bps = 13200;
+    attr->info.max_bps = 13200;
+    attr->info.pcm_bits_per_sample = 16;
+    attr->info.frm_ptime = 20;
+    attr->info.pt = PJMEDIA_RTP_PT_GSM;
+
+    attr->setting.frm_per_pkt = 1;
+    attr->setting.vad = 1;
+#if !PLC_DISABLED
+    attr->setting.plc = 1;
+#endif
+
+    /* Default all other flag bits disabled. */
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Enum codecs supported by this factory (i.e. only GSM!).
+ */
+static pj_status_t gsm_enum_codecs(pjmedia_codec_factory *factory, 
+				    unsigned *count, 
+				    pjmedia_codec_info codecs[])
+{
+    PJ_UNUSED_ARG(factory);
+    PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
+
+    pj_bzero(&codecs[0], sizeof(pjmedia_codec_info));
+    codecs[0].encoding_name = pj_str("GSM");
+    codecs[0].pt = PJMEDIA_RTP_PT_GSM;
+    codecs[0].type = PJMEDIA_TYPE_AUDIO;
+    codecs[0].clock_rate = 8000;
+    codecs[0].channel_cnt = 1;
+
+    *count = 1;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Allocate a new GSM codec instance.
+ */
+static pj_status_t gsm_alloc_codec( pjmedia_codec_factory *factory, 
+				    const pjmedia_codec_info *id,
+				    pjmedia_codec **p_codec)
+{
+    pjmedia_codec *codec;
+    struct gsm_data *gsm_data;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory == &gsm_codec_factory.base, PJ_EINVAL);
+
+
+    pj_mutex_lock(gsm_codec_factory.mutex);
+
+    /* Get free nodes, if any. */
+    if (!pj_list_empty(&gsm_codec_factory.codec_list)) {
+	codec = gsm_codec_factory.codec_list.next;
+	pj_list_erase(codec);
+    } else {
+	codec = PJ_POOL_ZALLOC_T(gsm_codec_factory.pool, pjmedia_codec);
+	PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM);
+	codec->op = &gsm_op;
+	codec->factory = factory;
+
+	gsm_data = PJ_POOL_ZALLOC_T(gsm_codec_factory.pool, struct gsm_data);
+	codec->codec_data = gsm_data;
+
+#if !PLC_DISABLED
+	/* Create PLC */
+	status = pjmedia_plc_create(gsm_codec_factory.pool, 8000, 
+				    160, 0, &gsm_data->plc);
+	if (status != PJ_SUCCESS) {
+	    pj_mutex_unlock(gsm_codec_factory.mutex);
+	    return status;
+	}
+#endif
+
+	/* Create silence detector */
+	status = pjmedia_silence_det_create(gsm_codec_factory.pool,
+					    8000, 160,
+					    &gsm_data->vad);
+	if (status != PJ_SUCCESS) {
+	    pj_mutex_unlock(gsm_codec_factory.mutex);
+	    return status;
+	}
+    }
+
+    pj_mutex_unlock(gsm_codec_factory.mutex);
+
+    *p_codec = codec;
+    return PJ_SUCCESS;
+}
+
+/*
+ * Free codec.
+ */
+static pj_status_t gsm_dealloc_codec( pjmedia_codec_factory *factory, 
+				      pjmedia_codec *codec )
+{
+    struct gsm_data *gsm_data;
+    int i;
+
+    PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory == &gsm_codec_factory.base, PJ_EINVAL);
+
+    gsm_data = (struct gsm_data*) codec->codec_data;
+
+    /* Close codec, if it's not closed. */
+    gsm_codec_close(codec);
+
+#if !PLC_DISABLED
+    /* Clear left samples in the PLC, since codec+plc will be reused
+     * next time.
+     */
+    for (i=0; i<2; ++i) {
+	pj_int16_t frame[160];
+	pjmedia_zero_samples(frame, PJ_ARRAY_SIZE(frame));
+	pjmedia_plc_save(gsm_data->plc, frame);
+    }
+#else
+    PJ_UNUSED_ARG(i);
+#endif
+
+    /* Re-init silence_period */
+    pj_set_timestamp32(&gsm_data->last_tx, 0, 0);
+
+    /* Put in the free list. */
+    pj_mutex_lock(gsm_codec_factory.mutex);
+    pj_list_push_front(&gsm_codec_factory.codec_list, codec);
+    pj_mutex_unlock(gsm_codec_factory.mutex);
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Init codec.
+ */
+static pj_status_t gsm_codec_init( pjmedia_codec *codec, 
+				   pj_pool_t *pool )
+{
+    PJ_UNUSED_ARG(codec);
+    PJ_UNUSED_ARG(pool);
+    return PJ_SUCCESS;
+}
+
+/*
+ * Open codec.
+ */
+static pj_status_t gsm_codec_open( pjmedia_codec *codec, 
+				   pjmedia_codec_param *attr )
+{
+    struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data;
+
+    pj_assert(gsm_data != NULL);
+    pj_assert(gsm_data->encoder == NULL && gsm_data->decoder == NULL);
+
+    gsm_data->encoder = gsm_create();
+    if (!gsm_data->encoder)
+	return PJMEDIA_CODEC_EFAILED;
+
+    gsm_data->decoder = gsm_create();
+    if (!gsm_data->decoder)
+	return PJMEDIA_CODEC_EFAILED;
+
+    gsm_data->vad_enabled = (attr->setting.vad != 0);
+    gsm_data->plc_enabled = (attr->setting.plc != 0);
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Close codec.
+ */
+static pj_status_t gsm_codec_close( pjmedia_codec *codec )
+{
+    struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data;
+
+    pj_assert(gsm_data != NULL);
+
+    if (gsm_data->encoder) {
+	gsm_destroy(gsm_data->encoder);
+	gsm_data->encoder = NULL;
+    }
+    if (gsm_data->decoder) {
+	gsm_destroy(gsm_data->decoder);
+	gsm_data->decoder = NULL;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Modify codec settings.
+ */
+static pj_status_t  gsm_codec_modify(pjmedia_codec *codec, 
+				     const pjmedia_codec_param *attr )
+{
+    struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data;
+
+    pj_assert(gsm_data != NULL);
+    pj_assert(gsm_data->encoder != NULL && gsm_data->decoder != NULL);
+
+    gsm_data->vad_enabled = (attr->setting.vad != 0);
+    gsm_data->plc_enabled = (attr->setting.plc != 0);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Get frames in the packet.
+ */
+static pj_status_t  gsm_codec_parse( pjmedia_codec *codec,
+				     void *pkt,
+				     pj_size_t pkt_size,
+				     const pj_timestamp *ts,
+				     unsigned *frame_cnt,
+				     pjmedia_frame frames[])
+{
+    unsigned count = 0;
+
+    PJ_UNUSED_ARG(codec);
+
+    PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL);
+
+    while (pkt_size >= 33 && count < *frame_cnt) {
+	frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
+	frames[count].buf = pkt;
+	frames[count].size = 33;
+	frames[count].timestamp.u64 = ts->u64 + count * 160;
+
+	pkt = ((char*)pkt) + 33;
+	pkt_size -= 33;
+
+	++count;
+    }
+
+    *frame_cnt = count;
+    return PJ_SUCCESS;
+}
+
+/*
+ * Encode frame.
+ */
+static pj_status_t gsm_codec_encode( pjmedia_codec *codec, 
+				     const struct pjmedia_frame *input,
+				     unsigned output_buf_len, 
+				     struct pjmedia_frame *output)
+{
+    struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data;
+    pj_int16_t *pcm_in;
+    pj_size_t in_size;
+
+    pj_assert(gsm_data && input && output);
+    
+    pcm_in = (pj_int16_t*)input->buf;
+    in_size = input->size;
+
+    PJ_ASSERT_RETURN(in_size % 320 == 0, PJMEDIA_CODEC_EPCMFRMINLEN);
+    PJ_ASSERT_RETURN(output_buf_len >= 33 * in_size/320, 
+		     PJMEDIA_CODEC_EFRMTOOSHORT);
+
+    /* Detect silence */
+    if (gsm_data->vad_enabled) {
+	pj_bool_t is_silence;
+	pj_int32_t silence_duration;
+
+	silence_duration = pj_timestamp_diff32(&gsm_data->last_tx, 
+					       &input->timestamp);
+
+	is_silence = pjmedia_silence_det_detect(gsm_data->vad, 
+					        (const pj_int16_t*) input->buf,
+						(input->size >> 1),
+						NULL);
+	if (is_silence &&
+	    (PJMEDIA_CODEC_MAX_SILENCE_PERIOD == -1 ||
+	     silence_duration < PJMEDIA_CODEC_MAX_SILENCE_PERIOD*8000/1000))
+	{
+	    output->type = PJMEDIA_FRAME_TYPE_NONE;
+	    output->buf = NULL;
+	    output->size = 0;
+	    output->timestamp = input->timestamp;
+	    return PJ_SUCCESS;
+	} else {
+	    gsm_data->last_tx = input->timestamp;
+	}
+    }
+
+    /* Encode */
+    output->size = 0;
+    while (in_size >= 320) {
+	gsm_encode(gsm_data->encoder, pcm_in, 
+		   (unsigned char*)output->buf + output->size);
+	pcm_in += 160;
+	output->size += 33;
+	in_size -= 320;
+    }
+
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+    output->timestamp = input->timestamp;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Decode frame.
+ */
+static pj_status_t gsm_codec_decode( pjmedia_codec *codec, 
+				     const struct pjmedia_frame *input,
+				     unsigned output_buf_len, 
+				     struct pjmedia_frame *output)
+{
+    struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data;
+
+    pj_assert(gsm_data != NULL);
+    PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
+
+    if (output_buf_len < 320)
+	return PJMEDIA_CODEC_EPCMTOOSHORT;
+
+    if (input->size < 33)
+	return PJMEDIA_CODEC_EFRMTOOSHORT;
+
+    gsm_decode(gsm_data->decoder, 
+	       (unsigned char*)input->buf, 
+	       (short*)output->buf);
+
+    output->size = 320;
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+    output->timestamp = input->timestamp;
+
+#if !PLC_DISABLED
+    if (gsm_data->plc_enabled)
+	pjmedia_plc_save( gsm_data->plc, (pj_int16_t*)output->buf);
+#endif
+
+    return PJ_SUCCESS;
+}
+
+
+#if !PLC_DISABLED
+/*
+ * Recover lost frame.
+ */
+static pj_status_t  gsm_codec_recover(pjmedia_codec *codec,
+				      unsigned output_buf_len,
+				      struct pjmedia_frame *output)
+{
+    struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data;
+
+    PJ_ASSERT_RETURN(gsm_data->plc_enabled, PJ_EINVALIDOP);
+
+    PJ_ASSERT_RETURN(output_buf_len >= 320, PJMEDIA_CODEC_EPCMTOOSHORT);
+
+    pjmedia_plc_generate(gsm_data->plc, (pj_int16_t*)output->buf);
+    output->size = 320;
+
+    return PJ_SUCCESS;
+}
+#endif
+
+
+#endif	/* PJMEDIA_HAS_GSM_CODEC */
+
diff --git a/jni/pjproject-android/pjmedia/src/pjmedia-codec/h263_packetizer.c b/jni/pjproject-android/pjmedia/src/pjmedia-codec/h263_packetizer.c
new file mode 100644
index 0000000..045583a
--- /dev/null
+++ b/jni/pjproject-android/pjmedia/src/pjmedia-codec/h263_packetizer.c
@@ -0,0 +1,294 @@
+/* $Id: h263_packetizer.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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/h263_packetizer.h>
+#include <pjmedia/types.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/string.h>
+
+
+#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
+
+
+#define THIS_FILE	"h263_packetizer.c"
+
+
+/* H.263 packetizer definition */
+struct pjmedia_h263_packetizer {
+    /* Current settings */
+    pjmedia_h263_packetizer_cfg cfg;
+    
+    /* Unpacketizer state */
+    unsigned	    unpack_last_sync_pos;
+    pj_bool_t	    unpack_prev_lost;
+};
+
+
+/*
+ * Find synchronization point (PSC, slice, GSBC, EOS, EOSBS) in H.263 
+ * bitstream.
+ */
+static pj_uint8_t* find_sync_point(pj_uint8_t *data,
+				   pj_size_t data_len)
+{
+    pj_uint8_t *p = data, *end = data+data_len-1;
+
+    while (p < end && (*p || *(p+1)))
+        ++p;
+
+    if (p == end)
+        return NULL;
+        
+    return p;
+}
+
+
+/*
+ * Find synchronization point (PSC, slice, GSBC, EOS, EOSBS) in H.263 
+ * bitstream, in reversed manner.
+ */
+static pj_uint8_t* find_sync_point_rev(pj_uint8_t *data,
+                                       pj_size_t data_len)
+{
+    pj_uint8_t *p = data+data_len-2;
+
+    while (p >= data && (*p || *(p+1)))
+        --p;
+
+    if (p < data)
+        return (data + data_len);
+        
+    return p;
+}
+
+
+/*
+ * Create H263 packetizer.
+ */
+PJ_DEF(pj_status_t) pjmedia_h263_packetizer_create(
+				pj_pool_t *pool,
+				const pjmedia_h263_packetizer_cfg *cfg,
+				pjmedia_h263_packetizer **p)
+{
+    pjmedia_h263_packetizer *p_;
+
+    PJ_ASSERT_RETURN(pool && p, PJ_EINVAL);
+
+    if (cfg && cfg->mode != PJMEDIA_H263_PACKETIZER_MODE_RFC4629)
+	return PJ_ENOTSUP;
+
+    p_ = PJ_POOL_ZALLOC_T(pool, pjmedia_h263_packetizer);
+    if (cfg) {
+	pj_memcpy(&p_->cfg, cfg, sizeof(*cfg));
+    } else {
+	p_->cfg.mode = PJMEDIA_H263_PACKETIZER_MODE_RFC4629;
+	p_->cfg.mtu = PJMEDIA_MAX_VID_PAYLOAD_SIZE;
+    }
+
+    *p = p_;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Generate an RTP payload from H.263 frame bitstream, in-place processing.
+ */
+PJ_DEF(pj_status_t) pjmedia_h263_packetize(pjmedia_h263_packetizer *pktz,
+					   pj_uint8_t *bits,
+                                           pj_size_t bits_len,
+                                           unsigned *pos,
+                                           const pj_uint8_t **payload,
+                                           pj_size_t *payload_len)
+{
+    pj_uint8_t *p, *end;
+
+    pj_assert(pktz && bits && pos && payload && payload_len);
+    pj_assert(*pos <= bits_len);
+
+    p = bits + *pos;
+    end = bits + bits_len;
+
+    /* Put two octets payload header */
+    if ((end-p > 2) && *p==0 && *(p+1)==0) {
+        /* The bitstream starts with synchronization point, just override
+         * the two zero octets (sync point mark) for payload header.
+         */
+        *p = 0x04;
+    } else {
+        /* Not started in synchronization point, we will use two octets
+         * preceeding the bitstream for payload header!
+         */
+
+	if (*pos < 2) {
+	    /* Invalid H263 bitstream, it's not started with PSC */
+	    return PJ_EINVAL;
+	}
+
+	p -= 2;
+        *p = 0;
+    }
+    *(p+1) = 0;
+
+    /* When bitstream truncation needed because of payload length/MTU 
+     * limitation, try to use sync point for the payload boundary.
+     */
+    if (end-p > pktz->cfg.mtu) {
+	end = find_sync_point_rev(p+2, pktz->cfg.mtu-2);
+    }
+
+    *payload = p;
+    *payload_len = end-p;
+    *pos = (unsigned)(end - bits);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Append an RTP payload to a H.263 picture bitstream.
+ */
+PJ_DEF(pj_status_t) pjmedia_h263_unpacketize (pjmedia_h263_packetizer *pktz,
+					      const pj_uint8_t *payload,
+                                              pj_size_t payload_len,
+                                              pj_uint8_t *bits,
+                                              pj_size_t bits_size,
+					      unsigned *pos)
+{
+    pj_uint8_t P, V, PLEN;
+    const pj_uint8_t *p = payload;
+    pj_uint8_t *q;
+
+    q = bits + *pos;
+
+    /* Check if this is a missing/lost packet */
+    if (payload == NULL) {
+	pktz->unpack_prev_lost = PJ_TRUE;
+	return PJ_SUCCESS;
+    }
+
+    /* H263 payload header size is two octets */
+    if (payload_len < 2) {
+	/* Invalid bitstream, discard this payload */
+	pktz->unpack_prev_lost = PJ_TRUE;
+	return PJ_EINVAL;
+    }
+
+    /* Reset last sync point for every new picture bitstream */
+    if (*pos == 0)
+	pktz->unpack_last_sync_pos = 0;
+
+    /* Get payload header info */
+    P = *p & 0x04;
+    V = *p & 0x02;
+    PLEN = ((*p & 0x01) << 5) + ((*(p+1) & 0xF8)>>3);
+
+    /* Get start bitstream pointer */
+    p += 2;	    /* Skip payload header */
+    if (V)
+        p += 1;	    /* Skip VRC data */
+    if (PLEN)
+        p += PLEN;  /* Skip extra picture header data */
+
+    /* Get bitstream length */
+    if (payload_len > (pj_size_t)(p - payload)) {
+	payload_len -= (p - payload);
+    } else {
+	/* Invalid bitstream, discard this payload */
+	pktz->unpack_prev_lost = PJ_TRUE;
+	return PJ_EINVAL;
+    }
+
+    /* Validate bitstream length */
+    if (bits_size < *pos + payload_len + 2) {
+	/* Insufficient bistream buffer, discard this payload */
+	pj_assert(!"Insufficient H.263 bitstream buffer");
+	pktz->unpack_prev_lost = PJ_TRUE;
+	return PJ_ETOOSMALL;
+    }
+
+    /* Start writing bitstream */
+
+    /* No sync point flag */
+    if (!P) {
+	if (*pos == 0) {
+	    /* Previous packet must be lost */
+	    pktz->unpack_prev_lost = PJ_TRUE;
+
+	    /* If there is extra picture header, let's use it. */
+	    if (PLEN) {
+		/* Write two zero octets for PSC */
+		*q++ = 0;
+		*q++ = 0;
+		/* Copy the picture header */
+		p -= PLEN;
+		pj_memcpy(q, p, PLEN);
+		p += PLEN;
+		q += PLEN;
+	    }
+	} else if (pktz->unpack_prev_lost) {
+	    /* If prev packet was lost, revert the bitstream pointer to
+	     * the last sync point.
+	     */
+	    pj_assert(pktz->unpack_last_sync_pos <= *pos);
+	    q = bits + pktz->unpack_last_sync_pos;
+	}
+
+	/* There was packet lost, see if this payload contain sync point
+	 * (usable data).
+	 */
+	if (pktz->unpack_prev_lost) {
+	    pj_uint8_t *sync;
+	    sync = find_sync_point((pj_uint8_t*)p, payload_len);
+	    if (sync) {
+		/* Got sync point, update P/sync-point flag */
+		P = 1;
+		/* Skip the two zero octets */
+		sync += 2;
+		/* Update payload length and start bitstream pointer */
+		payload_len -= (sync - p);
+		p = sync;
+	    } else {
+		/* No sync point in it, just discard this payload */
+		return PJ_EIGNORED;
+	    }
+	}
+    }
+
+    /* Write two zero octets when payload flagged with sync point */
+    if (P) {
+	pktz->unpack_last_sync_pos = (unsigned)(q - bits);
+        *q++ = 0;
+        *q++ = 0;
+    }
+
+    /* Write the payload to the bitstream */
+    pj_memcpy(q, p, payload_len);
+    q += payload_len;
+
+    /* Update the bitstream writing offset */
+    *pos = (unsigned)(q - bits);
+
+    pktz->unpack_prev_lost = PJ_FALSE;
+
+    return PJ_SUCCESS;
+}
+
+
+#endif /* PJMEDIA_HAS_VIDEO */
diff --git a/jni/pjproject-android/pjmedia/src/pjmedia-codec/h264_packetizer.c b/jni/pjproject-android/pjmedia/src/pjmedia-codec/h264_packetizer.c
new file mode 100644
index 0000000..9b22334
--- /dev/null
+++ b/jni/pjproject-android/pjmedia/src/pjmedia-codec/h264_packetizer.c
@@ -0,0 +1,535 @@
+/* $Id: h264_packetizer.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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/h264_packetizer.h>
+#include <pjmedia/types.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+
+#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
+
+
+#define THIS_FILE		"h264_packetizer.c"
+
+#define DBG_PACKETIZE		0
+#define DBG_UNPACKETIZE		0
+
+
+/* H.264 packetizer definition */
+struct pjmedia_h264_packetizer
+{
+    /* Current settings */
+    pjmedia_h264_packetizer_cfg cfg;
+    
+    /* Unpacketizer state */
+    unsigned	    unpack_last_sync_pos;
+    pj_bool_t	    unpack_prev_lost;
+};
+
+
+/* Enumeration of H.264 NAL unit types */
+enum
+{
+    NAL_TYPE_SINGLE_NAL_MIN	= 1,
+    NAL_TYPE_SINGLE_NAL_MAX	= 23,
+    NAL_TYPE_STAP_A		= 24,
+    NAL_TYPE_FU_A		= 28,
+};
+
+
+/*
+ * Find next NAL unit from the specified H.264 bitstream data.
+ */
+static pj_uint8_t* find_next_nal_unit(pj_uint8_t *start,
+                                      pj_uint8_t *end)
+{
+    pj_uint8_t *p = start;
+
+    /* Simply lookup "0x000001" pattern */
+    while (p <= end-3 && (p[0] || p[1] || p[2]!=1))
+        ++p;
+
+    if (p > end-3)
+	/* No more NAL unit in this bitstream */
+        return NULL;
+
+    /* Include 8 bits leading zero */
+    if (p>start && *(p-1)==0)
+	return (p-1);
+
+    return p;
+}
+
+
+/*
+ * Create H264 packetizer.
+ */
+PJ_DEF(pj_status_t) pjmedia_h264_packetizer_create(
+				pj_pool_t *pool,
+				const pjmedia_h264_packetizer_cfg *cfg,
+				pjmedia_h264_packetizer **p)
+{
+    pjmedia_h264_packetizer *p_;
+
+    PJ_ASSERT_RETURN(pool && p, PJ_EINVAL);
+
+    if (cfg &&
+	cfg->mode != PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED &&
+	cfg->mode != PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL)
+    {
+	return PJ_ENOTSUP;
+    }
+
+    p_ = PJ_POOL_ZALLOC_T(pool, pjmedia_h264_packetizer);
+    if (cfg) {
+	pj_memcpy(&p_->cfg, cfg, sizeof(*cfg));
+    } else {
+	p_->cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED;
+	p_->cfg.mtu = PJMEDIA_MAX_VID_PAYLOAD_SIZE;
+    }
+
+    *p = p_;
+
+    return PJ_SUCCESS;
+}
+
+
+
+/*
+ * Generate an RTP payload from H.264 frame bitstream, in-place processing.
+ */
+PJ_DEF(pj_status_t) pjmedia_h264_packetize(pjmedia_h264_packetizer *pktz,
+					   pj_uint8_t *buf,
+                                           pj_size_t buf_len,
+                                           unsigned *pos,
+                                           const pj_uint8_t **payload,
+                                           pj_size_t *payload_len)
+{
+    pj_uint8_t *nal_start = NULL, *nal_end = NULL, *nal_octet = NULL;
+    pj_uint8_t *p, *end;
+    enum { 
+	HEADER_SIZE_FU_A	     = 2,
+	HEADER_SIZE_STAP_A	     = 3,
+    };
+    enum { MAX_NALS_IN_AGGR = 32 };
+
+#if DBG_PACKETIZE
+    if (*pos == 0 && buf_len) {
+	PJ_LOG(3, ("h264pack", "<< Start packing new frame >>"));
+    }
+#endif
+
+    p = buf + *pos;
+    end = buf + buf_len;
+
+    /* Find NAL unit startcode */
+    if (end-p >= 4)
+	nal_start = find_next_nal_unit(p, p+4);
+    if (nal_start) {
+	/* Get NAL unit octet pointer */
+	while (*nal_start++ == 0);
+	nal_octet = nal_start;
+    } else {
+	/* This NAL unit is being fragmented */
+	nal_start = p;
+    }
+
+    /* Get end of NAL unit */
+    p = nal_start+pktz->cfg.mtu+1;
+    if (p > end || pktz->cfg.mode==PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL) 
+	p = end;
+    nal_end = find_next_nal_unit(nal_start, p); 
+    if (!nal_end)
+	nal_end = p;
+
+    /* Validate MTU vs NAL length on single NAL unit packetization */
+    if ((pktz->cfg.mode==PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL) &&
+	nal_end - nal_start > pktz->cfg.mtu)
+    {
+	//pj_assert(!"MTU too small for H.264 single NAL packetization mode");
+	PJ_LOG(2,("h264_packetizer.c",
+		  "MTU too small for H.264 (required=%u, MTU=%u)",
+		  nal_end - nal_start, pktz->cfg.mtu));
+	return PJ_ETOOSMALL;
+    }
+
+    /* Evaluate the proper payload format structure */
+
+    /* Fragmentation (FU-A) packet */
+    if ((pktz->cfg.mode != PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL) &&
+	(!nal_octet || nal_end-nal_start > pktz->cfg.mtu))
+    {
+	pj_uint8_t NRI, TYPE;
+
+	if (nal_octet) {
+	    /* We have NAL unit octet, so this is the first fragment */
+	    NRI = (*nal_octet & 0x60) >> 5;
+	    TYPE = *nal_octet & 0x1F;
+
+	    /* Skip nal_octet in nal_start to be overriden by FU header */
+	    ++nal_start;
+	} else {
+	    /* Not the first fragment, get NRI and NAL unit type
+	     * from the previous fragment.
+	     */
+	    p = nal_start - pktz->cfg.mtu;
+	    NRI = (*p & 0x60) >> 5;
+	    TYPE = *(p+1) & 0x1F;
+	}
+
+	/* Init FU indicator (one octet: F+NRI+TYPE) */
+	p = nal_start - HEADER_SIZE_FU_A;
+	*p = (NRI << 5) | NAL_TYPE_FU_A;
+	++p;
+
+	/* Init FU header (one octed: S+E+R+TYPE) */
+	*p = TYPE;
+	if (nal_octet)
+	    *p |= (1 << 7); /* S bit flag = start of fragmentation */
+	if (nal_end-nal_start+HEADER_SIZE_FU_A <= pktz->cfg.mtu)
+	    *p |= (1 << 6); /* E bit flag = end of fragmentation */
+
+	/* Set payload, payload length */
+	*payload = nal_start - HEADER_SIZE_FU_A;
+	if (nal_end-nal_start+HEADER_SIZE_FU_A > pktz->cfg.mtu)
+	    *payload_len = pktz->cfg.mtu;
+	else
+	    *payload_len = nal_end - nal_start + HEADER_SIZE_FU_A;
+	*pos = (unsigned)(*payload + *payload_len - buf);
+
+#if DBG_PACKETIZE
+	PJ_LOG(3, ("h264pack", "Packetized fragmented H264 NAL unit "
+		   "(pos=%d, type=%d, NRI=%d, S=%d, E=%d, len=%d/%d)",
+		   *payload-buf, TYPE, NRI, *p>>7, (*p>>6)&1, *payload_len,
+		   buf_len));
+#endif
+
+	return PJ_SUCCESS;
+    }
+
+    /* Aggregation (STAP-A) packet */
+    if ((pktz->cfg.mode != PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL) &&
+	(nal_end != end) &&
+	(nal_end - nal_start + HEADER_SIZE_STAP_A) < pktz->cfg.mtu) 
+    {
+	int total_size;
+	unsigned nal_cnt = 1;
+	pj_uint8_t *nal[MAX_NALS_IN_AGGR];
+	pj_size_t nal_size[MAX_NALS_IN_AGGR];
+	pj_uint8_t NRI;
+
+	pj_assert(nal_octet);
+
+	/* Init the first NAL unit in the packet */
+	nal[0] = nal_start;
+	nal_size[0] = nal_end - nal_start;
+	total_size = (int)nal_size[0] + HEADER_SIZE_STAP_A;
+	NRI = (*nal_octet & 0x60) >> 5;
+
+	/* Populate next NAL units */
+	while (nal_cnt < MAX_NALS_IN_AGGR) {
+	    pj_uint8_t *tmp_end;
+
+	    /* Find start address of the next NAL unit */
+	    p = nal[nal_cnt-1] + nal_size[nal_cnt-1];
+	    while (*p++ == 0);
+	    nal[nal_cnt] = p;
+
+	    /* Find end address of the next NAL unit */
+	    tmp_end = p + (pktz->cfg.mtu - total_size);
+	    if (tmp_end > end)
+		tmp_end = end;
+	    p = find_next_nal_unit(p+1, tmp_end);
+	    if (p) {
+		nal_size[nal_cnt] = p - nal[nal_cnt];
+	    } else {
+		break;
+	    }
+
+	    /* Update total payload size (2 octet NAL size + NAL) */
+	    total_size += (2 + (int)nal_size[nal_cnt]);
+	    if (total_size <= pktz->cfg.mtu) {
+		pj_uint8_t tmp_nri;
+
+		/* Get maximum NRI of the aggregated NAL units */
+		tmp_nri = (*(nal[nal_cnt]-1) & 0x60) >> 5;
+		if (tmp_nri > NRI)
+		    NRI = tmp_nri;
+	    } else {
+		break;
+	    }
+
+	    ++nal_cnt;
+	}
+
+	/* Only use STAP-A when we found more than one NAL units */
+	if (nal_cnt > 1) {
+	    unsigned i;
+
+	    /* Init STAP-A NAL header (F+NRI+TYPE) */
+	    p = nal[0] - HEADER_SIZE_STAP_A;
+	    *p++ = (NRI << 5) | NAL_TYPE_STAP_A;
+
+	    /* Append all populated NAL units into payload (SIZE+NAL) */
+	    for (i = 0; i < nal_cnt; ++i) {
+		/* Put size (2 octets in network order) */
+		pj_assert(nal_size[i] <= 0xFFFF);
+		*p++ = (pj_uint8_t)(nal_size[i] >> 8);
+		*p++ = (pj_uint8_t)(nal_size[i] & 0xFF);
+		
+		/* Append NAL unit, watchout memmove()-ing bitstream! */
+		if (p != nal[i])
+		    pj_memmove(p, nal[i], nal_size[i]);
+		p += nal_size[i];
+	    }
+
+	    /* Set payload, payload length, and pos */
+	    *payload = nal[0] - HEADER_SIZE_STAP_A;
+	    pj_assert(*payload >= buf+*pos);
+	    *payload_len = p - *payload;
+	    *pos = (unsigned)(nal[nal_cnt-1] + nal_size[nal_cnt-1] - buf);
+
+#if DBG_PACKETIZE
+	    PJ_LOG(3, ("h264pack", "Packetized aggregation of "
+		       "%d H264 NAL units (pos=%d, NRI=%d len=%d/%d)",
+		       nal_cnt, *payload-buf, NRI, *payload_len, buf_len));
+#endif
+
+	    return PJ_SUCCESS;
+	}
+    }
+
+    /* Single NAL unit packet */
+    *payload = nal_start;
+    *payload_len = nal_end - nal_start;
+    *pos = (unsigned)(nal_end - buf);
+
+#if DBG_PACKETIZE
+    PJ_LOG(3, ("h264pack", "Packetized single H264 NAL unit "
+	       "(pos=%d, type=%d, NRI=%d, len=%d/%d)",
+	       nal_start-buf, *nal_octet&0x1F, (*nal_octet&0x60)>>5,
+	       *payload_len, buf_len));
+#endif
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Append RTP payload to a H.264 picture bitstream. Note that the only
+ * payload format that cares about packet lost is the NAL unit
+ * fragmentation format (FU-A/B), so we will only manage the "prev_lost"
+ * state for the FU-A/B packets.
+ */
+PJ_DEF(pj_status_t) pjmedia_h264_unpacketize(pjmedia_h264_packetizer *pktz,
+					     const pj_uint8_t *payload,
+                                             pj_size_t   payload_len,
+                                             pj_uint8_t *bits,
+                                             pj_size_t   bits_len,
+					     unsigned   *bits_pos)
+{
+    const pj_uint8_t nal_start_code[3] = {0, 0, 1};
+    enum { MIN_PAYLOAD_SIZE = 2 };
+    pj_uint8_t nal_type;
+
+    PJ_UNUSED_ARG(pktz);
+
+#if DBG_UNPACKETIZE
+    if (*bits_pos == 0 && payload_len) {
+	PJ_LOG(3, ("h264unpack", ">> Start unpacking new frame <<"));
+    }
+#endif
+
+    /* Check if this is a missing/lost packet */
+    if (payload == NULL) {
+	pktz->unpack_prev_lost = PJ_TRUE;
+	return PJ_SUCCESS;
+    }
+
+    /* H264 payload size */
+    if (payload_len < MIN_PAYLOAD_SIZE) {
+	/* Invalid bitstream, discard this payload */
+	pktz->unpack_prev_lost = PJ_TRUE;
+	return PJ_EINVAL;
+    }
+
+    /* Reset last sync point for every new picture bitstream */
+    if (*bits_pos == 0)
+	pktz->unpack_last_sync_pos = 0;
+
+    nal_type = *payload & 0x1F;
+    if (nal_type >= NAL_TYPE_SINGLE_NAL_MIN &&
+	nal_type <= NAL_TYPE_SINGLE_NAL_MAX)
+    {
+	/* Single NAL unit packet */
+	pj_uint8_t *p = bits + *bits_pos;
+
+	/* Validate bitstream length */
+	if (bits_len-*bits_pos < payload_len+PJ_ARRAY_SIZE(nal_start_code)) {
+	    /* Insufficient bistream buffer, discard this payload */
+	    pj_assert(!"Insufficient H.263 bitstream buffer");
+	    return PJ_ETOOSMALL;
+	}
+
+	/* Write NAL unit start code */
+	pj_memcpy(p, &nal_start_code, PJ_ARRAY_SIZE(nal_start_code));
+	p += PJ_ARRAY_SIZE(nal_start_code);
+
+	/* Write NAL unit */
+	pj_memcpy(p, payload, payload_len);
+	p += payload_len;
+
+	/* Update the bitstream writing offset */
+	*bits_pos = (unsigned)(p - bits);
+	pktz->unpack_last_sync_pos = *bits_pos;
+
+#if DBG_UNPACKETIZE
+	PJ_LOG(3, ("h264unpack", "Unpacked single H264 NAL unit "
+		   "(type=%d, NRI=%d, len=%d)",
+		   nal_type, (*payload&0x60)>>5, payload_len));
+#endif
+
+    }
+    else if (nal_type == NAL_TYPE_STAP_A)
+    {
+	/* Aggregation packet */
+	pj_uint8_t *p, *p_end;
+	const pj_uint8_t *q, *q_end;
+	unsigned cnt = 0;
+
+	/* Validate bitstream length */
+	if (bits_len - *bits_pos < payload_len + 32) {
+	    /* Insufficient bistream buffer, discard this payload */
+	    pj_assert(!"Insufficient H.263 bitstream buffer");
+	    return PJ_ETOOSMALL;
+	}
+
+	/* Fill bitstream */
+	p = bits + *bits_pos;
+	p_end = bits + bits_len;
+	q = payload + 1;
+	q_end = payload + payload_len;
+	while (q < q_end && p < p_end) {
+	    pj_uint16_t tmp_nal_size;
+
+	    /* Write NAL unit start code */
+	    pj_memcpy(p, &nal_start_code, PJ_ARRAY_SIZE(nal_start_code));
+	    p += PJ_ARRAY_SIZE(nal_start_code);
+
+	    /* Get NAL unit size */
+	    tmp_nal_size = (*q << 8) | *(q+1);
+	    q += 2;
+	    if (q + tmp_nal_size > q_end) {
+		/* Invalid bitstream, discard the rest of the payload */
+		return PJ_EINVAL;
+	    }
+
+	    /* Write NAL unit */
+	    pj_memcpy(p, q, tmp_nal_size);
+	    p += tmp_nal_size;
+	    q += tmp_nal_size;
+	    ++cnt;
+
+	    /* Update the bitstream writing offset */
+	    *bits_pos = (unsigned)(p - bits);
+	    pktz->unpack_last_sync_pos = *bits_pos;
+	}
+
+#if DBG_UNPACKETIZE
+	PJ_LOG(3, ("h264unpack", "Unpacked %d H264 NAL units (len=%d)",
+		   cnt, payload_len));
+#endif
+
+    }
+    else if (nal_type == NAL_TYPE_FU_A)
+    {
+	/* Fragmentation packet */
+	pj_uint8_t *p;
+	const pj_uint8_t *q = payload;
+	pj_uint8_t NRI, TYPE, S, E;
+
+	p = bits + *bits_pos;
+
+	/* Validate bitstream length */
+	if (bits_len-*bits_pos < payload_len+PJ_ARRAY_SIZE(nal_start_code)) {
+	    /* Insufficient bistream buffer, drop this packet */
+	    pj_assert(!"Insufficient H.263 bitstream buffer");
+	    pktz->unpack_prev_lost = PJ_TRUE;
+	    return PJ_ETOOSMALL;
+	}
+
+	/* Get info */
+	S = *(q+1) & 0x80;    /* Start bit flag	*/
+	E = *(q+1) & 0x40;    /* End bit flag	*/
+	TYPE = *(q+1) & 0x1f;
+	NRI = (*q & 0x60) >> 5;
+
+	/* Fill bitstream */
+	if (S) {
+	    /* This is the first part, write NAL unit start code */
+	    pj_memcpy(p, &nal_start_code, PJ_ARRAY_SIZE(nal_start_code));
+	    p += PJ_ARRAY_SIZE(nal_start_code);
+
+	    /* Write NAL unit octet */
+	    *p++ = (NRI << 5) | TYPE;
+	} else if (pktz->unpack_prev_lost) {
+	    /* If prev packet was lost, revert the bitstream pointer to
+	     * the last sync point.
+	     */
+	    pj_assert(pktz->unpack_last_sync_pos <= *bits_pos);
+	    *bits_pos = pktz->unpack_last_sync_pos;
+	    /* And discard this payload (and the following fragmentation
+	     * payloads carrying this same NAL unit.
+	     */
+	    return PJ_EIGNORED;
+	}
+	q += 2;
+
+	/* Write NAL unit */
+	pj_memcpy(p, q, payload_len - 2);
+	p += (payload_len - 2);
+
+	/* Update the bitstream writing offset */
+	*bits_pos = (unsigned)(p - bits);
+	if (E) {
+	    /* Update the sync pos only if the end bit flag is set */
+	    pktz->unpack_last_sync_pos = *bits_pos;
+	}
+
+#if DBG_UNPACKETIZE
+	PJ_LOG(3, ("h264unpack", "Unpacked fragmented H264 NAL unit "
+		   "(type=%d, NRI=%d, len=%d)",
+		   TYPE, NRI, payload_len));
+#endif
+
+    } else {
+	*bits_pos = 0;
+	return PJ_ENOTSUP;
+    }
+
+    pktz->unpack_prev_lost = PJ_FALSE;
+
+    return PJ_SUCCESS;
+}
+
+
+#endif /* PJMEDIA_HAS_VIDEO */
diff --git a/jni/pjproject-android/pjmedia/src/pjmedia-codec/ilbc.c b/jni/pjproject-android/pjmedia/src/pjmedia-codec/ilbc.c
new file mode 100644
index 0000000..82fbb47
--- /dev/null
+++ b/jni/pjproject-android/pjmedia/src/pjmedia-codec/ilbc.c
@@ -0,0 +1,883 @@
+/* $Id: ilbc.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjmedia-codec/ilbc.h>
+#include <pjmedia-codec/types.h>
+#include <pjmedia/codec.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/endpoint.h>
+#include <pjmedia/plc.h>
+#include <pjmedia/port.h>
+#include <pjmedia/silencedet.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/os.h>
+
+#if defined(PJMEDIA_ILBC_CODEC_USE_COREAUDIO)&& PJMEDIA_ILBC_CODEC_USE_COREAUDIO
+    #include <AudioToolbox/AudioToolbox.h>
+    #define iLBC_Enc_Inst_t AudioConverterRef
+    #define iLBC_Dec_Inst_t AudioConverterRef
+    #define BLOCKL_MAX 		1
+#else
+    #include "../../third_party/ilbc/iLBC_encode.h"
+    #include "../../third_party/ilbc/iLBC_decode.h"
+#endif
+
+/*
+ * Only build this file if PJMEDIA_HAS_ILBC_CODEC != 0
+ */
+#if defined(PJMEDIA_HAS_ILBC_CODEC) && PJMEDIA_HAS_ILBC_CODEC != 0
+
+
+#define THIS_FILE	"ilbc.c"
+#define CLOCK_RATE	8000
+#define DEFAULT_MODE	30
+
+
+/* Prototypes for iLBC factory */
+static pj_status_t ilbc_test_alloc(pjmedia_codec_factory *factory, 
+				   const pjmedia_codec_info *id );
+static pj_status_t ilbc_default_attr(pjmedia_codec_factory *factory, 
+				     const pjmedia_codec_info *id, 
+				     pjmedia_codec_param *attr );
+static pj_status_t ilbc_enum_codecs(pjmedia_codec_factory *factory, 
+				    unsigned *count, 
+				    pjmedia_codec_info codecs[]);
+static pj_status_t ilbc_alloc_codec(pjmedia_codec_factory *factory, 
+				    const pjmedia_codec_info *id, 
+				    pjmedia_codec **p_codec);
+static pj_status_t ilbc_dealloc_codec(pjmedia_codec_factory *factory, 
+				      pjmedia_codec *codec );
+
+/* Prototypes for iLBC implementation. */
+static pj_status_t  ilbc_codec_init(pjmedia_codec *codec, 
+				    pj_pool_t *pool );
+static pj_status_t  ilbc_codec_open(pjmedia_codec *codec, 
+				    pjmedia_codec_param *attr );
+static pj_status_t  ilbc_codec_close(pjmedia_codec *codec );
+static pj_status_t  ilbc_codec_modify(pjmedia_codec *codec, 
+				      const pjmedia_codec_param *attr );
+static pj_status_t  ilbc_codec_parse(pjmedia_codec *codec,
+				     void *pkt,
+				     pj_size_t pkt_size,
+				     const pj_timestamp *ts,
+				     unsigned *frame_cnt,
+				     pjmedia_frame frames[]);
+static pj_status_t  ilbc_codec_encode(pjmedia_codec *codec, 
+				      const struct pjmedia_frame *input,
+				      unsigned output_buf_len, 
+				      struct pjmedia_frame *output);
+static pj_status_t  ilbc_codec_decode(pjmedia_codec *codec, 
+				      const struct pjmedia_frame *input,
+				      unsigned output_buf_len, 
+				      struct pjmedia_frame *output);
+static pj_status_t  ilbc_codec_recover(pjmedia_codec *codec,
+				       unsigned output_buf_len,
+				       struct pjmedia_frame *output);
+
+/* Definition for iLBC codec operations. */
+static pjmedia_codec_op ilbc_op = 
+{
+    &ilbc_codec_init,
+    &ilbc_codec_open,
+    &ilbc_codec_close,
+    &ilbc_codec_modify,
+    &ilbc_codec_parse,
+    &ilbc_codec_encode,
+    &ilbc_codec_decode,
+    &ilbc_codec_recover
+};
+
+/* Definition for iLBC codec factory operations. */
+static pjmedia_codec_factory_op ilbc_factory_op =
+{
+    &ilbc_test_alloc,
+    &ilbc_default_attr,
+    &ilbc_enum_codecs,
+    &ilbc_alloc_codec,
+    &ilbc_dealloc_codec,
+    &pjmedia_codec_ilbc_deinit
+};
+
+/* iLBC factory */
+static struct ilbc_factory
+{
+    pjmedia_codec_factory    base;
+    pjmedia_endpt	    *endpt;
+
+    int			     mode;
+    int			     bps;
+} ilbc_factory;
+
+
+/* iLBC codec private data. */
+struct ilbc_codec
+{
+    pjmedia_codec	 base;
+    pj_pool_t		*pool;
+    char		 obj_name[PJ_MAX_OBJ_NAME];
+    pjmedia_silence_det	*vad;
+    pj_bool_t		 vad_enabled;
+    pj_bool_t		 plc_enabled;
+    pj_timestamp	 last_tx;
+
+
+    pj_bool_t		 enc_ready;
+    iLBC_Enc_Inst_t	 enc;
+    unsigned		 enc_frame_size;
+    unsigned		 enc_samples_per_frame;
+    float		 enc_block[BLOCKL_MAX];
+
+    pj_bool_t		 dec_ready;
+    iLBC_Dec_Inst_t	 dec;
+    unsigned		 dec_frame_size;
+    unsigned		 dec_samples_per_frame;
+    float		 dec_block[BLOCKL_MAX];
+
+#if defined(PJMEDIA_ILBC_CODEC_USE_COREAUDIO)&& PJMEDIA_ILBC_CODEC_USE_COREAUDIO
+    unsigned		 enc_total_packets;
+    char		 *enc_buffer;
+    unsigned		 enc_buffer_offset;
+
+    unsigned		 dec_total_packets;
+    char		 *dec_buffer;
+    unsigned		 dec_buffer_offset;
+#endif
+};
+
+static pj_str_t STR_MODE = {"mode", 4};
+
+/*
+ * Initialize and register iLBC codec factory to pjmedia endpoint.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_ilbc_init( pjmedia_endpt *endpt,
+					     int mode )
+{
+    pjmedia_codec_mgr *codec_mgr;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(endpt != NULL, PJ_EINVAL);
+    PJ_ASSERT_RETURN(mode==0 || mode==20 || mode==30, PJ_EINVAL);
+
+    /* Create iLBC codec factory. */
+    ilbc_factory.base.op = &ilbc_factory_op;
+    ilbc_factory.base.factory_data = NULL;
+    ilbc_factory.endpt = endpt;
+
+    if (mode == 0)
+	mode = DEFAULT_MODE;
+
+    ilbc_factory.mode = mode;
+
+    if (mode == 20) {
+	ilbc_factory.bps = 15200;	
+    } else {
+	ilbc_factory.bps = 13333;
+    }
+
+    /* 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, 
+						&ilbc_factory.base);
+    if (status != PJ_SUCCESS)
+	return status;
+
+
+    /* Done. */
+    return PJ_SUCCESS;
+}
+
+
+
+/*
+ * Unregister iLBC codec factory from pjmedia endpoint and deinitialize
+ * the iLBC codec library.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_ilbc_deinit(void)
+{
+    pjmedia_codec_mgr *codec_mgr;
+    pj_status_t status;
+
+
+    /* Get the codec manager. */
+    codec_mgr = pjmedia_endpt_get_codec_mgr(ilbc_factory.endpt);
+    if (!codec_mgr)
+	return PJ_EINVALIDOP;
+
+    /* Unregister iLBC codec factory. */
+    status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
+						  &ilbc_factory.base);
+    
+    return status;
+}
+
+/* 
+ * Check if factory can allocate the specified codec. 
+ */
+static pj_status_t ilbc_test_alloc( pjmedia_codec_factory *factory, 
+				   const pjmedia_codec_info *info )
+{
+    const pj_str_t ilbc_tag = { "iLBC", 4};
+
+    PJ_UNUSED_ARG(factory);
+    PJ_ASSERT_RETURN(factory==&ilbc_factory.base, PJ_EINVAL);
+
+
+    /* Type MUST be audio. */
+    if (info->type != PJMEDIA_TYPE_AUDIO)
+	return PJMEDIA_CODEC_EUNSUP;
+
+    /* Check encoding name. */
+    if (pj_stricmp(&info->encoding_name, &ilbc_tag) != 0)
+	return PJMEDIA_CODEC_EUNSUP;
+
+    /* Check clock-rate */
+    if (info->clock_rate != CLOCK_RATE)
+	return PJMEDIA_CODEC_EUNSUP;
+    
+    /* Channel count must be one */
+    if (info->channel_cnt != 1)
+	return PJMEDIA_CODEC_EUNSUP;
+
+    /* Yes, this should be iLBC! */
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Generate default attribute.
+ */
+static pj_status_t ilbc_default_attr (pjmedia_codec_factory *factory, 
+				      const pjmedia_codec_info *id, 
+				      pjmedia_codec_param *attr )
+{
+    PJ_UNUSED_ARG(factory);
+    PJ_ASSERT_RETURN(factory==&ilbc_factory.base, PJ_EINVAL);
+
+    PJ_UNUSED_ARG(id);
+    PJ_ASSERT_RETURN(pj_stricmp2(&id->encoding_name, "iLBC")==0, PJ_EINVAL);
+
+    pj_bzero(attr, sizeof(pjmedia_codec_param));
+
+    attr->info.clock_rate = CLOCK_RATE;
+    attr->info.channel_cnt = 1;
+    attr->info.avg_bps = ilbc_factory.bps;
+    attr->info.max_bps = 15200;
+    attr->info.pcm_bits_per_sample = 16;
+    attr->info.frm_ptime = (short)ilbc_factory.mode;
+    attr->info.pt = PJMEDIA_RTP_PT_ILBC;
+
+    attr->setting.frm_per_pkt = 1;
+    attr->setting.vad = 1;
+    attr->setting.plc = 1;
+    attr->setting.penh = 1;
+    attr->setting.dec_fmtp.cnt = 1;
+    attr->setting.dec_fmtp.param[0].name = STR_MODE;
+    if (ilbc_factory.mode == 30)
+	attr->setting.dec_fmtp.param[0].val = pj_str("30");
+    else
+	attr->setting.dec_fmtp.param[0].val = pj_str("20");
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Enum codecs supported by this factory (i.e. only iLBC!).
+ */
+static pj_status_t ilbc_enum_codecs(pjmedia_codec_factory *factory, 
+				    unsigned *count, 
+				    pjmedia_codec_info codecs[])
+{
+    PJ_UNUSED_ARG(factory);
+    PJ_ASSERT_RETURN(factory==&ilbc_factory.base, PJ_EINVAL);
+
+    PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
+
+    pj_bzero(&codecs[0], sizeof(pjmedia_codec_info));
+
+    codecs[0].encoding_name = pj_str("iLBC");
+    codecs[0].pt = PJMEDIA_RTP_PT_ILBC;
+    codecs[0].type = PJMEDIA_TYPE_AUDIO;
+    codecs[0].clock_rate = 8000;
+    codecs[0].channel_cnt = 1;
+
+    *count = 1;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Allocate a new iLBC codec instance.
+ */
+static pj_status_t ilbc_alloc_codec(pjmedia_codec_factory *factory, 
+				    const pjmedia_codec_info *id,
+				    pjmedia_codec **p_codec)
+{
+    pj_pool_t *pool;
+    struct ilbc_codec *codec;
+
+    PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory == &ilbc_factory.base, PJ_EINVAL);
+
+    pool = pjmedia_endpt_create_pool(ilbc_factory.endpt, "iLBC%p",
+				     2000, 2000);
+    PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+
+    codec = PJ_POOL_ZALLOC_T(pool, struct ilbc_codec);
+    codec->base.op = &ilbc_op;
+    codec->base.factory = factory;
+    codec->pool = pool;
+
+    pj_ansi_snprintf(codec->obj_name,  sizeof(codec->obj_name),
+		     "ilbc%p", codec);
+
+    *p_codec = &codec->base;
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Free codec.
+ */
+static pj_status_t ilbc_dealloc_codec( pjmedia_codec_factory *factory, 
+				      pjmedia_codec *codec )
+{
+    struct ilbc_codec *ilbc_codec;
+
+    PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
+    PJ_UNUSED_ARG(factory);
+    PJ_ASSERT_RETURN(factory == &ilbc_factory.base, PJ_EINVAL);
+
+    ilbc_codec = (struct ilbc_codec*) codec;
+
+#if defined(PJMEDIA_ILBC_CODEC_USE_COREAUDIO)&& PJMEDIA_ILBC_CODEC_USE_COREAUDIO
+    if (ilbc_codec->enc) {
+	AudioConverterDispose(ilbc_codec->enc);
+	ilbc_codec->enc = NULL;
+    }
+    if (ilbc_codec->dec) {
+	AudioConverterDispose(ilbc_codec->dec);
+	ilbc_codec->dec = NULL;
+    }
+#endif
+
+    pj_pool_release(ilbc_codec->pool);
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Init codec.
+ */
+static pj_status_t ilbc_codec_init(pjmedia_codec *codec, 
+				   pj_pool_t *pool )
+{
+    PJ_UNUSED_ARG(codec);
+    PJ_UNUSED_ARG(pool);
+    return PJ_SUCCESS;
+}
+
+/*
+ * Open codec.
+ */
+static pj_status_t ilbc_codec_open(pjmedia_codec *codec, 
+				   pjmedia_codec_param *attr )
+{
+    struct ilbc_codec *ilbc_codec = (struct ilbc_codec*)codec;
+    pj_status_t status;
+    unsigned i;
+    pj_uint16_t dec_fmtp_mode = DEFAULT_MODE, 
+		enc_fmtp_mode = DEFAULT_MODE;
+
+#if defined(PJMEDIA_ILBC_CODEC_USE_COREAUDIO)&& PJMEDIA_ILBC_CODEC_USE_COREAUDIO
+    AudioStreamBasicDescription srcFormat, dstFormat;
+    UInt32 size;
+
+    srcFormat.mSampleRate       = attr->info.clock_rate;
+    srcFormat.mFormatID         = kAudioFormatLinearPCM;
+    srcFormat.mFormatFlags      = kLinearPCMFormatFlagIsSignedInteger
+				  | kLinearPCMFormatFlagIsPacked;
+    srcFormat.mBitsPerChannel   = attr->info.pcm_bits_per_sample;
+    srcFormat.mChannelsPerFrame = attr->info.channel_cnt;
+    srcFormat.mBytesPerFrame    = srcFormat.mChannelsPerFrame
+	                          * srcFormat.mBitsPerChannel >> 3;
+    srcFormat.mFramesPerPacket  = 1;
+    srcFormat.mBytesPerPacket   = srcFormat.mBytesPerFrame *
+				  srcFormat.mFramesPerPacket;
+
+    memset(&dstFormat, 0, sizeof(dstFormat));
+    dstFormat.mSampleRate 	= attr->info.clock_rate;
+    dstFormat.mFormatID 	= kAudioFormatiLBC;
+    dstFormat.mChannelsPerFrame = attr->info.channel_cnt;
+#endif
+
+    pj_assert(ilbc_codec != NULL);
+    pj_assert(ilbc_codec->enc_ready == PJ_FALSE && 
+	      ilbc_codec->dec_ready == PJ_FALSE);
+
+    /* Get decoder mode */
+    for (i = 0; i < attr->setting.dec_fmtp.cnt; ++i) {
+	if (pj_stricmp(&attr->setting.dec_fmtp.param[i].name, &STR_MODE) == 0)
+	{
+	    dec_fmtp_mode = (pj_uint16_t)
+			    pj_strtoul(&attr->setting.dec_fmtp.param[i].val);
+	    break;
+	}
+    }
+
+    /* Decoder mode must be set */
+    PJ_ASSERT_RETURN(dec_fmtp_mode == 20 || dec_fmtp_mode == 30, 
+		     PJMEDIA_CODEC_EINMODE);
+
+    /* Get encoder mode */
+    for (i = 0; i < attr->setting.enc_fmtp.cnt; ++i) {
+	if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name, &STR_MODE) == 0)
+	{
+	    enc_fmtp_mode = (pj_uint16_t)
+			    pj_strtoul(&attr->setting.enc_fmtp.param[i].val);
+	    break;
+	}
+    }
+
+    PJ_ASSERT_RETURN(enc_fmtp_mode==20 || enc_fmtp_mode==30, 
+		     PJMEDIA_CODEC_EINMODE);
+
+    /* Both sides of a bi-directional session MUST use the same "mode" value.
+     * In this point, possible values are only 20 or 30, so when encoder and
+     * decoder modes are not same, just use the default mode, it is 30.
+     */
+    if (enc_fmtp_mode != dec_fmtp_mode) {
+	enc_fmtp_mode = dec_fmtp_mode = DEFAULT_MODE;
+	PJ_LOG(4,(ilbc_codec->obj_name, 
+		  "Normalized iLBC encoder and decoder modes to %d", 
+		  DEFAULT_MODE));
+    }
+
+    /* Update some attributes based on negotiated mode. */
+    attr->info.avg_bps = (dec_fmtp_mode == 30? 13333 : 15200);
+    attr->info.frm_ptime = dec_fmtp_mode;
+
+    /* Create encoder */
+#if defined(PJMEDIA_ILBC_CODEC_USE_COREAUDIO)&& PJMEDIA_ILBC_CODEC_USE_COREAUDIO
+    dstFormat.mFramesPerPacket  = CLOCK_RATE * enc_fmtp_mode / 1000;
+    dstFormat.mBytesPerPacket   = (enc_fmtp_mode == 20? 38 : 50);
+
+    /* Use AudioFormat API to fill out the rest of the description */
+    size = sizeof(dstFormat);
+    AudioFormatGetProperty(kAudioFormatProperty_FormatInfo,
+ 	                   0, NULL, &size, &dstFormat);
+
+    if (AudioConverterNew(&srcFormat, &dstFormat, &ilbc_codec->enc) != noErr)
+	return PJMEDIA_CODEC_EFAILED;
+    ilbc_codec->enc_frame_size = (enc_fmtp_mode == 20? 38 : 50);
+#else
+    ilbc_codec->enc_frame_size = initEncode(&ilbc_codec->enc, enc_fmtp_mode);
+#endif
+    ilbc_codec->enc_samples_per_frame = CLOCK_RATE * enc_fmtp_mode / 1000;
+    ilbc_codec->enc_ready = PJ_TRUE;
+
+    /* Create decoder */
+#if defined(PJMEDIA_ILBC_CODEC_USE_COREAUDIO)&& PJMEDIA_ILBC_CODEC_USE_COREAUDIO
+    if (AudioConverterNew(&dstFormat, &srcFormat, &ilbc_codec->dec) != noErr)
+	return PJMEDIA_CODEC_EFAILED;
+    ilbc_codec->dec_samples_per_frame = CLOCK_RATE * dec_fmtp_mode / 1000;
+#else
+    ilbc_codec->dec_samples_per_frame = initDecode(&ilbc_codec->dec,
+						   dec_fmtp_mode,
+						   attr->setting.penh);
+#endif
+    ilbc_codec->dec_frame_size = (dec_fmtp_mode == 20? 38 : 50);
+    ilbc_codec->dec_ready = PJ_TRUE;
+
+    /* Save plc flags */
+    ilbc_codec->plc_enabled = (attr->setting.plc != 0);
+
+    /* Create silence detector. */
+    ilbc_codec->vad_enabled = (attr->setting.vad != 0);
+    status = pjmedia_silence_det_create(ilbc_codec->pool, CLOCK_RATE,
+					ilbc_codec->enc_samples_per_frame,
+					&ilbc_codec->vad);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Init last_tx (not necessary because of zalloc, but better
+     * be safe in case someone remove zalloc later.
+     */
+    pj_set_timestamp32(&ilbc_codec->last_tx, 0, 0);
+
+    PJ_LOG(5,(ilbc_codec->obj_name, 
+	      "iLBC codec opened, mode=%d", dec_fmtp_mode));
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Close codec.
+ */
+static pj_status_t ilbc_codec_close( pjmedia_codec *codec )
+{
+    struct ilbc_codec *ilbc_codec = (struct ilbc_codec*)codec;
+
+    PJ_UNUSED_ARG(codec);
+
+    PJ_LOG(5,(ilbc_codec->obj_name, "iLBC codec closed"));
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Modify codec settings.
+ */
+static pj_status_t  ilbc_codec_modify(pjmedia_codec *codec, 
+				      const pjmedia_codec_param *attr )
+{
+    struct ilbc_codec *ilbc_codec = (struct ilbc_codec*)codec;
+
+    ilbc_codec->plc_enabled = (attr->setting.plc != 0);
+    ilbc_codec->vad_enabled = (attr->setting.vad != 0);
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Get frames in the packet.
+ */
+static pj_status_t  ilbc_codec_parse( pjmedia_codec *codec,
+				     void *pkt,
+				     pj_size_t pkt_size,
+				     const pj_timestamp *ts,
+				     unsigned *frame_cnt,
+				     pjmedia_frame frames[])
+{
+    struct ilbc_codec *ilbc_codec = (struct ilbc_codec*)codec;
+    unsigned count;
+
+    PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL);
+
+    count = 0;
+    while (pkt_size >= ilbc_codec->dec_frame_size && count < *frame_cnt) {
+	frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
+	frames[count].buf = pkt;
+	frames[count].size = ilbc_codec->dec_frame_size;
+	frames[count].timestamp.u64 = ts->u64 + count * 
+				      ilbc_codec->dec_samples_per_frame;
+
+	pkt = ((char*)pkt) + ilbc_codec->dec_frame_size;
+	pkt_size -= ilbc_codec->dec_frame_size;
+
+	++count;
+    }
+
+    *frame_cnt = count;
+    return PJ_SUCCESS;
+}
+
+#if defined(PJMEDIA_ILBC_CODEC_USE_COREAUDIO)&& PJMEDIA_ILBC_CODEC_USE_COREAUDIO
+static OSStatus encodeDataProc (
+    AudioConverterRef             inAudioConverter,
+    UInt32                        *ioNumberDataPackets,
+    AudioBufferList               *ioData,
+    AudioStreamPacketDescription  **outDataPacketDescription,
+    void                          *inUserData
+)
+{
+    struct ilbc_codec *ilbc_codec = (struct ilbc_codec*)inUserData;
+
+    /* Initialize in case of failure */
+    ioData->mBuffers[0].mData = NULL;
+    ioData->mBuffers[0].mDataByteSize = 0;
+
+    if (ilbc_codec->enc_total_packets < *ioNumberDataPackets) {
+	*ioNumberDataPackets = ilbc_codec->enc_total_packets;
+    }
+
+    if (*ioNumberDataPackets) {
+	ioData->mBuffers[0].mData = ilbc_codec->enc_buffer +
+				    ilbc_codec->enc_buffer_offset;
+	ioData->mBuffers[0].mDataByteSize = *ioNumberDataPackets *
+					    ilbc_codec->enc_samples_per_frame
+					    << 1;
+	ilbc_codec->enc_buffer_offset += ioData->mBuffers[0].mDataByteSize;
+    }
+
+    ilbc_codec->enc_total_packets -= *ioNumberDataPackets;
+    return noErr;
+}
+
+static OSStatus decodeDataProc (
+    AudioConverterRef             inAudioConverter,
+    UInt32                        *ioNumberDataPackets,
+    AudioBufferList               *ioData,
+    AudioStreamPacketDescription  **outDataPacketDescription,
+    void                          *inUserData
+)
+{
+    struct ilbc_codec *ilbc_codec = (struct ilbc_codec*)inUserData;
+
+    /* Initialize in case of failure */
+    ioData->mBuffers[0].mData = NULL;
+    ioData->mBuffers[0].mDataByteSize = 0;
+
+    if (ilbc_codec->dec_total_packets < *ioNumberDataPackets) {
+	*ioNumberDataPackets = ilbc_codec->dec_total_packets;
+    }
+
+    if (*ioNumberDataPackets) {
+	ioData->mBuffers[0].mData = ilbc_codec->dec_buffer +
+				    ilbc_codec->dec_buffer_offset;
+	ioData->mBuffers[0].mDataByteSize = *ioNumberDataPackets *
+					    ilbc_codec->dec_frame_size;
+	ilbc_codec->dec_buffer_offset += ioData->mBuffers[0].mDataByteSize;
+    }
+
+    ilbc_codec->dec_total_packets -= *ioNumberDataPackets;
+    return noErr;
+}
+#endif
+
+/*
+ * Encode frame.
+ */
+static pj_status_t ilbc_codec_encode(pjmedia_codec *codec, 
+				     const struct pjmedia_frame *input,
+				     unsigned output_buf_len, 
+				     struct pjmedia_frame *output)
+{
+    struct ilbc_codec *ilbc_codec = (struct ilbc_codec*)codec;
+    pj_int16_t *pcm_in;
+    pj_size_t nsamples;
+#if defined(PJMEDIA_ILBC_CODEC_USE_COREAUDIO)&& PJMEDIA_ILBC_CODEC_USE_COREAUDIO
+    UInt32 npackets;
+    OSStatus err;
+    AudioBufferList theABL;
+#endif
+
+    pj_assert(ilbc_codec && input && output);
+
+    pcm_in = (pj_int16_t*)input->buf;
+    nsamples = input->size >> 1;
+
+    PJ_ASSERT_RETURN(nsamples % ilbc_codec->enc_samples_per_frame == 0, 
+		     PJMEDIA_CODEC_EPCMFRMINLEN);
+    PJ_ASSERT_RETURN(output_buf_len >= ilbc_codec->enc_frame_size * nsamples /
+		     ilbc_codec->enc_samples_per_frame,
+		     PJMEDIA_CODEC_EFRMTOOSHORT);
+
+    /* Detect silence */
+    if (ilbc_codec->vad_enabled) {
+	pj_bool_t is_silence;
+	pj_int32_t silence_period;
+
+	silence_period = pj_timestamp_diff32(&ilbc_codec->last_tx,
+					      &input->timestamp);
+
+	is_silence = pjmedia_silence_det_detect(ilbc_codec->vad, 
+					        (const pj_int16_t*)input->buf,
+						(input->size >> 1),
+						NULL);
+	if (is_silence &&
+	    (PJMEDIA_CODEC_MAX_SILENCE_PERIOD == -1 ||
+	     silence_period < PJMEDIA_CODEC_MAX_SILENCE_PERIOD*8000/1000))
+	{
+	    output->type = PJMEDIA_FRAME_TYPE_NONE;
+	    output->buf = NULL;
+	    output->size = 0;
+	    output->timestamp = input->timestamp;
+	    return PJ_SUCCESS;
+	} else {
+	    ilbc_codec->last_tx = input->timestamp;
+	}
+    }
+
+    /* Encode */
+    output->size = 0;
+#if defined(PJMEDIA_ILBC_CODEC_USE_COREAUDIO)&& PJMEDIA_ILBC_CODEC_USE_COREAUDIO
+    npackets = nsamples / ilbc_codec->enc_samples_per_frame;
+
+    theABL.mNumberBuffers = 1;
+    theABL.mBuffers[0].mNumberChannels = 1;
+    theABL.mBuffers[0].mDataByteSize = output_buf_len;
+    theABL.mBuffers[0].mData = output->buf;
+
+    ilbc_codec->enc_total_packets = npackets;
+    ilbc_codec->enc_buffer = (char *)input->buf;
+    ilbc_codec->enc_buffer_offset = 0;
+
+    err = AudioConverterFillComplexBuffer(ilbc_codec->enc, encodeDataProc,
+					  ilbc_codec, &npackets,
+					  &theABL, NULL);
+    if (err == noErr) {
+	output->size = npackets * ilbc_codec->enc_frame_size;
+    }
+#else
+    while (nsamples >= ilbc_codec->enc_samples_per_frame) {
+	unsigned i;
+	
+	/* Convert to float */
+	for (i=0; i<ilbc_codec->enc_samples_per_frame; ++i) {
+	    ilbc_codec->enc_block[i] = (float) (*pcm_in++);
+	}
+
+	iLBC_encode((unsigned char *)output->buf + output->size, 
+		    ilbc_codec->enc_block, 
+		    &ilbc_codec->enc);
+
+	output->size += ilbc_codec->enc.no_of_bytes;
+	nsamples -= ilbc_codec->enc_samples_per_frame;
+    }
+#endif
+
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+    output->timestamp = input->timestamp;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Decode frame.
+ */
+static pj_status_t ilbc_codec_decode(pjmedia_codec *codec, 
+				     const struct pjmedia_frame *input,
+				     unsigned output_buf_len, 
+				     struct pjmedia_frame *output)
+{
+    struct ilbc_codec *ilbc_codec = (struct ilbc_codec*)codec;
+#if defined(PJMEDIA_ILBC_CODEC_USE_COREAUDIO)&& PJMEDIA_ILBC_CODEC_USE_COREAUDIO
+    UInt32 npackets;
+    OSStatus err;
+    AudioBufferList theABL;
+#else
+    unsigned i;
+#endif
+
+    pj_assert(ilbc_codec != NULL);
+    PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
+
+    if (output_buf_len < (ilbc_codec->dec_samples_per_frame << 1))
+	return PJMEDIA_CODEC_EPCMTOOSHORT;
+
+    if (input->size != ilbc_codec->dec_frame_size)
+	return PJMEDIA_CODEC_EFRMINLEN;
+
+    /* Decode to temporary buffer */
+#if defined(PJMEDIA_ILBC_CODEC_USE_COREAUDIO)&& PJMEDIA_ILBC_CODEC_USE_COREAUDIO
+    npackets = input->size / ilbc_codec->dec_frame_size *
+	       ilbc_codec->dec_samples_per_frame;
+
+    theABL.mNumberBuffers = 1;
+    theABL.mBuffers[0].mNumberChannels = 1;
+    theABL.mBuffers[0].mDataByteSize = output_buf_len;
+    theABL.mBuffers[0].mData = output->buf;
+
+    ilbc_codec->dec_total_packets = npackets;
+    ilbc_codec->dec_buffer = (char *)input->buf;
+    ilbc_codec->dec_buffer_offset = 0;
+
+    err = AudioConverterFillComplexBuffer(ilbc_codec->dec, decodeDataProc,
+					  ilbc_codec, &npackets,
+					  &theABL, NULL);
+    if (err == noErr) {
+	output->size = npackets * (ilbc_codec->dec_samples_per_frame << 1);
+    }
+#else
+    iLBC_decode(ilbc_codec->dec_block, (unsigned char*) input->buf,
+		&ilbc_codec->dec, 1);
+
+    /* Convert decodec samples from float to short */
+    for (i=0; i<ilbc_codec->dec_samples_per_frame; ++i) {
+	((short*)output->buf)[i] = (short)ilbc_codec->dec_block[i];
+    }
+    output->size = (ilbc_codec->dec_samples_per_frame << 1);
+#endif
+
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+    output->timestamp = input->timestamp;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Recover lost frame.
+ */
+static pj_status_t  ilbc_codec_recover(pjmedia_codec *codec,
+				      unsigned output_buf_len,
+				      struct pjmedia_frame *output)
+{
+    struct ilbc_codec *ilbc_codec = (struct ilbc_codec*)codec;
+#if defined(PJMEDIA_ILBC_CODEC_USE_COREAUDIO)&& PJMEDIA_ILBC_CODEC_USE_COREAUDIO
+    UInt32 npackets;
+    OSStatus err;
+    AudioBufferList theABL;
+#else
+    unsigned i;
+#endif
+
+    pj_assert(ilbc_codec != NULL);
+    PJ_ASSERT_RETURN(output, PJ_EINVAL);
+
+    if (output_buf_len < (ilbc_codec->dec_samples_per_frame << 1))
+	return PJMEDIA_CODEC_EPCMTOOSHORT;
+
+    /* Decode to temporary buffer */
+#if defined(PJMEDIA_ILBC_CODEC_USE_COREAUDIO)&& PJMEDIA_ILBC_CODEC_USE_COREAUDIO
+    npackets = 1;
+
+    theABL.mNumberBuffers = 1;
+    theABL.mBuffers[0].mNumberChannels = 1;
+    theABL.mBuffers[0].mDataByteSize = output_buf_len;
+    theABL.mBuffers[0].mData = output->buf;
+
+    ilbc_codec->dec_total_packets = npackets;
+    ilbc_codec->dec_buffer_offset = 0;
+    if (ilbc_codec->dec_buffer) {
+	err = AudioConverterFillComplexBuffer(ilbc_codec->dec, decodeDataProc,
+					      ilbc_codec, &npackets,
+					      &theABL, NULL);
+	if (err == noErr) {
+	    output->size = npackets *
+		           (ilbc_codec->dec_samples_per_frame << 1);
+	}
+    } else {
+	output->size = npackets * (ilbc_codec->dec_samples_per_frame << 1);
+	pj_bzero(output->buf, output->size);
+    }
+#else
+    iLBC_decode(ilbc_codec->dec_block, NULL, &ilbc_codec->dec, 0);
+
+    /* Convert decodec samples from float to short */
+    for (i=0; i<ilbc_codec->dec_samples_per_frame; ++i) {
+	((short*)output->buf)[i] = (short)ilbc_codec->dec_block[i];
+    }
+    output->size = (ilbc_codec->dec_samples_per_frame << 1);
+#endif
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+
+    return PJ_SUCCESS;
+}
+
+
+#endif	/* PJMEDIA_HAS_ILBC_CODEC */
diff --git a/jni/pjproject-android/pjmedia/src/pjmedia-codec/ipp_codecs.c b/jni/pjproject-android/pjmedia/src/pjmedia-codec/ipp_codecs.c
new file mode 100644
index 0000000..c7a3a77
--- /dev/null
+++ b/jni/pjproject-android/pjmedia/src/pjmedia-codec/ipp_codecs.c
@@ -0,0 +1,1680 @@
+/* $Id: ipp_codecs.c 4002 2012-03-30 08:05:43Z bennylp $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjmedia-codec/ipp_codecs.h>
+#include <pjmedia-codec/amr_sdp_match.h>
+#include <pjmedia-codec/g7221_sdp_match.h>
+#include <pjmedia/codec.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/endpoint.h>
+#include <pjmedia/plc.h>
+#include <pjmedia/port.h>
+#include <pjmedia/silencedet.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/math.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/os.h>
+
+
+/*
+ * Only build this file if PJMEDIA_HAS_INTEL_IPP != 0
+ */
+#if defined(PJMEDIA_HAS_INTEL_IPP) && PJMEDIA_HAS_INTEL_IPP != 0
+
+#include <usc.h>
+#include <ippversion.h>
+
+#define THIS_FILE   "ipp_codecs.c"
+
+
+/* Prototypes for IPP codecs factory */
+static pj_status_t ipp_test_alloc( pjmedia_codec_factory *factory, 
+				   const pjmedia_codec_info *id );
+static pj_status_t ipp_default_attr( pjmedia_codec_factory *factory, 
+				     const pjmedia_codec_info *id, 
+				     pjmedia_codec_param *attr );
+static pj_status_t ipp_enum_codecs( pjmedia_codec_factory *factory, 
+				    unsigned *count, 
+				    pjmedia_codec_info codecs[]);
+static pj_status_t ipp_alloc_codec( pjmedia_codec_factory *factory, 
+				    const pjmedia_codec_info *id, 
+				    pjmedia_codec **p_codec);
+static pj_status_t ipp_dealloc_codec( pjmedia_codec_factory *factory, 
+				      pjmedia_codec *codec );
+
+/* Prototypes for IPP codecs implementation. */
+static pj_status_t  ipp_codec_init( pjmedia_codec *codec, 
+				    pj_pool_t *pool );
+static pj_status_t  ipp_codec_open( pjmedia_codec *codec, 
+				    pjmedia_codec_param *attr );
+static pj_status_t  ipp_codec_close( pjmedia_codec *codec );
+static pj_status_t  ipp_codec_modify(pjmedia_codec *codec, 
+				     const pjmedia_codec_param *attr );
+static pj_status_t  ipp_codec_parse( pjmedia_codec *codec,
+				     void *pkt,
+				     pj_size_t pkt_size,
+				     const pj_timestamp *ts,
+				     unsigned *frame_cnt,
+				     pjmedia_frame frames[]);
+static pj_status_t  ipp_codec_encode( pjmedia_codec *codec, 
+				      const struct pjmedia_frame *input,
+				      unsigned output_buf_len, 
+				      struct pjmedia_frame *output);
+static pj_status_t  ipp_codec_decode( pjmedia_codec *codec, 
+				      const struct pjmedia_frame *input,
+				      unsigned output_buf_len, 
+				      struct pjmedia_frame *output);
+static pj_status_t  ipp_codec_recover(pjmedia_codec *codec, 
+				      unsigned output_buf_len, 
+				      struct pjmedia_frame *output);
+
+/* Definition for IPP codecs operations. */
+static pjmedia_codec_op ipp_op = 
+{
+    &ipp_codec_init,
+    &ipp_codec_open,
+    &ipp_codec_close,
+    &ipp_codec_modify,
+    &ipp_codec_parse,
+    &ipp_codec_encode,
+    &ipp_codec_decode,
+    &ipp_codec_recover
+};
+
+/* Definition for IPP codecs factory operations. */
+static pjmedia_codec_factory_op ipp_factory_op =
+{
+    &ipp_test_alloc,
+    &ipp_default_attr,
+    &ipp_enum_codecs,
+    &ipp_alloc_codec,
+    &ipp_dealloc_codec,
+    &pjmedia_codec_ipp_deinit
+};
+
+/* IPP codecs factory */
+static struct ipp_factory {
+    pjmedia_codec_factory    base;
+    pjmedia_endpt	    *endpt;
+    pj_pool_t		    *pool;
+    pj_mutex_t		    *mutex;
+    unsigned		     g7221_pcm_shift;
+} ipp_factory;
+
+/* IPP codecs private data. */
+typedef struct ipp_private {
+    int			 codec_idx;	    /**< Codec index.		    */
+    void		*codec_setting;	    /**< Specific codec setting.    */
+    pj_pool_t		*pool;		    /**< Pool for each instance.    */
+
+    USC_Handle		 enc;		    /**< Encoder state.		    */
+    USC_Handle		 dec;		    /**< Decoder state.		    */
+    USC_CodecInfo	*info;		    /**< Native codec info.	    */
+    pj_uint16_t		 frame_size;	    /**< Bitstream frame size.	    */
+
+    pj_bool_t		 plc_enabled;	    /**< PLC enabled flag.	    */
+    pjmedia_plc		*plc;		    /**< PJMEDIA PLC engine, NULL if 
+						 codec has internal PLC.    */
+
+    pj_bool_t		 vad_enabled;	    /**< VAD enabled flag.	    */
+    pjmedia_silence_det	*vad;		    /**< PJMEDIA VAD engine, NULL if 
+						 codec has internal VAD.    */
+    pj_timestamp	 last_tx;	    /**< Timestamp of last transmit.*/
+
+    unsigned		 g7221_pcm_shift;   /**< G722.1 PCM level adjustment*/
+} ipp_private_t;
+
+
+/* USC codec implementations. */
+extern USC_Fxns USC_G729AFP_Fxns;
+extern USC_Fxns USC_G729I_Fxns;
+extern USC_Fxns USC_G723_Fxns;
+extern USC_Fxns USC_G726_Fxns;
+extern USC_Fxns USC_G728_Fxns;
+extern USC_Fxns USC_G722_Fxns;
+extern USC_Fxns USC_GSMAMR_Fxns;
+extern USC_Fxns USC_AMRWB_Fxns;
+extern USC_Fxns USC_AMRWBE_Fxns;
+
+
+/* CUSTOM CALLBACKS */
+
+/* This callback is useful for translating RTP frame into USC frame, e.g:
+ * reassigning frame attributes, reorder bitstream. Default behaviour of
+ * the translation is just setting the USC frame buffer & its size as 
+ * specified in RTP frame, setting USC frame frametype to 0, setting bitrate
+ * of USC frame to bitrate info of codec_data. Implement this callback when 
+ * the default behaviour is unapplicable.
+ */
+typedef void (*predecode_cb)(ipp_private_t *codec_data,
+			     const pjmedia_frame *rtp_frame,
+			     USC_Bitstream *usc_frame);
+
+/* Parse frames from a packet. Default behaviour of frame parsing is 
+ * just separating frames based on calculating frame length derived 
+ * from bitrate. Implement this callback when the default behaviour is 
+ * unapplicable.
+ */
+typedef pj_status_t (*parse_cb)(ipp_private_t *codec_data, void *pkt, 
+				pj_size_t pkt_size, const pj_timestamp *ts,
+				unsigned *frame_cnt, pjmedia_frame frames[]);
+
+/* Pack frames into a packet. Default behaviour of packing frames is 
+ * just stacking the frames with octet aligned without adding any 
+ * payload header. Implement this callback when the default behaviour is
+ * unapplicable.
+ */
+typedef pj_status_t (*pack_cb)(ipp_private_t *codec_data, void *pkt, 
+			       pj_size_t *pkt_size, pj_size_t max_pkt_size);
+
+
+
+/* Custom callback implementations. */
+static    void predecode_g723( ipp_private_t *codec_data,
+			       const pjmedia_frame *rtp_frame,
+			       USC_Bitstream *usc_frame);
+static pj_status_t parse_g723( ipp_private_t *codec_data, void *pkt, 
+			       pj_size_t pkt_size, const pj_timestamp *ts,
+			       unsigned *frame_cnt, pjmedia_frame frames[]);
+
+static void predecode_g729( ipp_private_t *codec_data,
+			    const pjmedia_frame *rtp_frame,
+			    USC_Bitstream *usc_frame);
+
+static    void predecode_amr( ipp_private_t *codec_data,
+			      const pjmedia_frame *rtp_frame,
+			      USC_Bitstream *usc_frame);
+static pj_status_t parse_amr( ipp_private_t *codec_data, void *pkt, 
+			      pj_size_t pkt_size, const pj_timestamp *ts,
+			      unsigned *frame_cnt, pjmedia_frame frames[]);
+static  pj_status_t pack_amr( ipp_private_t *codec_data, void *pkt, 
+			      pj_size_t *pkt_size, pj_size_t max_pkt_size);
+
+static    void predecode_g7221( ipp_private_t *codec_data,
+				const pjmedia_frame *rtp_frame,
+				USC_Bitstream *usc_frame);
+static  pj_status_t pack_g7221( ipp_private_t *codec_data, void *pkt, 
+			        pj_size_t *pkt_size, pj_size_t max_pkt_size);
+
+/* IPP codec implementation descriptions. */
+static struct ipp_codec {
+    int		     enabled;		/* Is this codec enabled?	    */
+    const char	    *name;		/* Codec name.			    */
+    pj_uint8_t	     pt;		/* Payload type.		    */
+    USC_Fxns	    *fxns;		/* USC callback functions.	    */
+    unsigned	     clock_rate;	/* Codec's clock rate.		    */
+    unsigned	     channel_count;	/* Codec's channel count.	    */
+    unsigned	     samples_per_frame;	/* Codec's samples count.	    */
+
+    unsigned	     def_bitrate;	/* Default bitrate of this codec.   */
+    unsigned	     max_bitrate;	/* Maximum bitrate of this codec.   */
+    pj_uint8_t	     frm_per_pkt;	/* Default num of frames per packet.*/
+    int		     has_native_vad;	/* Codec has internal VAD?	    */
+    int		     has_native_plc;	/* Codec has internal PLC?	    */
+
+    predecode_cb     predecode;		/* Callback to translate RTP frame
+					   into USC frame.		    */
+    parse_cb	     parse;		/* Callback to parse bitstream.	    */
+    pack_cb	     pack;		/* Callback to pack bitstream.	    */
+
+    pjmedia_codec_fmtp dec_fmtp;	/* Decoder's fmtp params.	    */
+}
+
+ipp_codec[] = 
+{
+#   if PJMEDIA_HAS_INTEL_IPP_CODEC_AMR
+    {1, "AMR",	    PJMEDIA_RTP_PT_AMR,       &USC_GSMAMR_Fxns,  8000, 1, 160, 
+		    7400, 12200, 2, 1, 1, 
+		    &predecode_amr, &parse_amr, &pack_amr,
+		    {1, {{{"octet-align", 11}, {"1", 1}}} }
+    },
+#   endif
+
+#   if PJMEDIA_HAS_INTEL_IPP_CODEC_AMRWB
+    {1, "AMR-WB",   PJMEDIA_RTP_PT_AMRWB,     &USC_AMRWB_Fxns,  16000, 1, 320,
+		    15850, 23850, 2, 1, 1, 
+		    &predecode_amr, &parse_amr, &pack_amr,
+		    {1, {{{"octet-align", 11}, {"1", 1}}} }
+    },
+#   endif
+
+#   if PJMEDIA_HAS_INTEL_IPP_CODEC_G729
+#	if defined(PJ_HAS_FLOATING_POINT) && (PJ_HAS_FLOATING_POINT != 0)
+    {1, "G729",	    PJMEDIA_RTP_PT_G729,      &USC_G729AFP_Fxns, 8000, 1,  80,
+		    8000, 11800, 2, 1, 1, 
+		    &predecode_g729, NULL, NULL
+    },
+#	else
+    {1, "G729",	    PJMEDIA_RTP_PT_G729,      &USC_G729I_Fxns,	 8000, 1,  80,
+		    8000, 11800, 2, 1, 1, 
+		    &predecode_g729, NULL, NULL
+    },
+#	endif
+#   endif
+
+#   if PJMEDIA_HAS_INTEL_IPP_CODEC_G723_1
+    /* This is actually G.723.1 */
+    {1, "G723",	    PJMEDIA_RTP_PT_G723,      &USC_G723_Fxns,	 8000, 1, 240,  
+		    6300,  6300, 1, 1, 1, 
+		    &predecode_g723, &parse_g723, NULL
+    },
+#   endif
+
+#   if PJMEDIA_HAS_INTEL_IPP_CODEC_G726
+    {0, "G726-16",  PJMEDIA_RTP_PT_G726_16,   &USC_G726_Fxns,	 8000, 1,  80, 
+		    16000, 16000, 2, 0, 0,
+		    NULL, NULL, NULL
+    },
+    {0, "G726-24",  PJMEDIA_RTP_PT_G726_24,   &USC_G726_Fxns,	 8000, 1,  80, 
+		    24000, 24000, 2, 0, 0,
+		    NULL, NULL, NULL
+    },
+    {1, "G726-32",  PJMEDIA_RTP_PT_G726_32,   &USC_G726_Fxns,	 8000, 1,  80, 
+		    32000, 32000, 2, 0, 0,
+		    NULL, NULL, NULL
+    },
+    {0, "G726-40",  PJMEDIA_RTP_PT_G726_40,   &USC_G726_Fxns,	 8000, 1,  80, 
+		    40000, 40000, 2, 0, 0,
+		    NULL, NULL, NULL
+    },
+    /* Old definition of G726-32 */
+    {1, "G721",	    PJMEDIA_RTP_PT_G721,      &USC_G726_Fxns,	 8000, 1,  80, 
+		    32000, 32000, 2, 0, 0,
+		    NULL, NULL, NULL
+    },
+#   endif
+
+#   if PJMEDIA_HAS_INTEL_IPP_CODEC_G728
+    {1, "G728",	    PJMEDIA_RTP_PT_G728,      &USC_G728_Fxns,	 8000, 1,  80, 
+		    16000, 16000, 2, 0, 1,
+		    NULL, NULL, NULL
+    },
+#   endif
+
+#   if PJMEDIA_HAS_INTEL_IPP_CODEC_G722_1
+    {0, "G7221",    PJMEDIA_RTP_PT_G722_1_16, &USC_G722_Fxns,	16000, 1, 320, 
+		    16000, 16000, 1, 0, 1,
+		    predecode_g7221, NULL, pack_g7221,
+		    {1, {{{"bitrate", 7}, {"16000", 5}}} }
+    },
+    {1, "G7221",    PJMEDIA_RTP_PT_G722_1_24, &USC_G722_Fxns,	16000, 1, 320, 
+		    24000, 24000, 1, 0, 1,
+		    predecode_g7221, NULL, pack_g7221,
+		    {1, {{{"bitrate", 7}, {"24000", 5}}} }
+    },
+    {1, "G7221",    PJMEDIA_RTP_PT_G722_1_32, &USC_G722_Fxns,	16000, 1, 320, 
+		    32000, 32000, 1, 0, 1,
+		    predecode_g7221, NULL, pack_g7221,
+		    {1, {{{"bitrate", 7}, {"32000", 5}}} }
+    },
+#   endif
+};
+
+
+#if PJMEDIA_HAS_INTEL_IPP_CODEC_G729
+
+static void predecode_g729( ipp_private_t *codec_data,
+			    const pjmedia_frame *rtp_frame,
+			    USC_Bitstream *usc_frame)
+{
+    switch (rtp_frame->size) {
+    case 2:
+	/* SID */
+	usc_frame->frametype = 1;
+	usc_frame->bitrate = codec_data->info->params.modes.bitrate;
+	break;
+    case 8:  
+	/* G729D */
+	usc_frame->frametype = 2;
+	usc_frame->bitrate = 6400;
+	break;
+    case 10: 
+	/* G729 */
+	usc_frame->frametype = 3;
+	usc_frame->bitrate = 8000;
+	break;
+    case 15: 
+	/* G729E */
+	usc_frame->frametype = 4;
+	usc_frame->bitrate = 11800;
+	break;
+    default: 
+	usc_frame->frametype = 0;
+	usc_frame->bitrate = 0;
+	break;
+    }
+
+    usc_frame->pBuffer = rtp_frame->buf;
+    usc_frame->nbytes = rtp_frame->size;
+}
+
+#endif /* PJMEDIA_HAS_INTEL_IPP_CODEC_G729 */
+
+
+#if PJMEDIA_HAS_INTEL_IPP_CODEC_G723_1
+
+static    void predecode_g723( ipp_private_t *codec_data,
+			       const pjmedia_frame *rtp_frame,
+			       USC_Bitstream *usc_frame)
+{
+    int i, HDR = 0;
+    pj_uint8_t *f = (pj_uint8_t*)rtp_frame->buf;
+
+    PJ_UNUSED_ARG(codec_data);
+
+    for (i = 0; i < 2; ++i){
+	int tmp;
+	tmp = (f[0] >> (i & 0x7)) & 1;
+	HDR +=  tmp << i ;
+    }
+
+    usc_frame->pBuffer = rtp_frame->buf;
+    usc_frame->nbytes = rtp_frame->size;
+    usc_frame->bitrate = HDR == 0? 6300 : 5300;
+    usc_frame->frametype = 0;
+}
+
+static pj_status_t parse_g723(ipp_private_t *codec_data, void *pkt, 
+			      pj_size_t pkt_size, const pj_timestamp *ts,
+			      unsigned *frame_cnt, pjmedia_frame frames[])
+{
+    unsigned count = 0;
+    pj_uint8_t *f = (pj_uint8_t*)pkt;
+
+    while (pkt_size && count < *frame_cnt) {
+	int framesize, i, j;
+	int HDR = 0;
+
+	for (i = 0; i < 2; ++i){
+	    j = (f[0] >> (i & 0x7)) & 1;
+	    HDR +=  j << i ;
+	}
+
+	if (HDR == 0)
+	    framesize = 24;
+	else if (HDR == 1)
+	    framesize = 20;
+	else if (HDR == 2)
+	    framesize = 4;
+	else if (HDR == 3)
+	    framesize = 1;
+	else {
+	    pj_assert(!"Unknown G723.1 frametype, packet may be corrupted!");
+	    return PJMEDIA_CODEC_EINMODE;
+	}
+
+	frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
+	frames[count].buf = f;
+	frames[count].size = framesize;
+	frames[count].timestamp.u64 = ts->u64 + count * 
+			ipp_codec[codec_data->codec_idx].samples_per_frame;
+
+	f += framesize;
+	pkt_size -= framesize;
+
+	++count;
+    }
+
+    *frame_cnt = count;
+    return PJ_SUCCESS;
+}
+
+#endif /* PJMEDIA_HAS_INTEL_IPP_CODEC_G723_1 */
+
+
+#if PJMEDIA_HAS_INTEL_IPP_CODEC_AMR || PJMEDIA_HAS_INTEL_IPP_CODEC_AMRWB
+
+#include <pjmedia-codec/amr_helper.h>
+
+typedef struct amr_settings_t {
+    pjmedia_codec_amr_pack_setting enc_setting;
+    pjmedia_codec_amr_pack_setting dec_setting;
+    pj_int8_t enc_mode;
+} amr_settings_t;
+
+
+/* Rearrange AMR bitstream and convert RTP frame into USC frame:
+ * - make the start_bit to be 0
+ * - if it is speech frame, reorder bitstream from sensitivity bits order
+ *   to encoder bits order.
+ * - set the appropriate value of usc_frame.
+ */
+static void predecode_amr( ipp_private_t *codec_data,
+			   const pjmedia_frame *rtp_frame,
+			   USC_Bitstream *usc_frame)
+{
+    pjmedia_frame frame;
+    pjmedia_codec_amr_bit_info *info;
+    pjmedia_codec_amr_pack_setting *setting;
+
+    setting = &((amr_settings_t*)codec_data->codec_setting)->dec_setting;
+
+    frame = *rtp_frame;
+    pjmedia_codec_amr_predecode(rtp_frame, setting, &frame);
+    info = (pjmedia_codec_amr_bit_info*) &frame.bit_info;
+
+    usc_frame->pBuffer = frame.buf;
+    usc_frame->nbytes = frame.size;
+    if (info->mode != -1) {
+	usc_frame->bitrate = setting->amr_nb? 
+			     pjmedia_codec_amrnb_bitrates[info->mode]:
+			     pjmedia_codec_amrwb_bitrates[info->mode];
+    } else {
+	usc_frame->bitrate = 0;
+    }
+
+    if (frame.size > 5) {
+	/* Speech */
+	if (info->good_quality)
+	    usc_frame->frametype = 0;
+	else
+	    usc_frame->frametype = setting->amr_nb ? 5 : 6;
+    } else if (frame.size == 5) {
+	/* SID */
+	if (info->good_quality) {
+	    usc_frame->frametype = info->STI? 2 : 1;
+	} else {
+	    usc_frame->frametype = setting->amr_nb ? 6 : 7;
+	}
+    } else {
+	/* no data */
+	usc_frame->frametype = 3;
+    }
+}
+
+/* Pack AMR payload */
+static pj_status_t pack_amr(ipp_private_t *codec_data, void *pkt, 
+			    pj_size_t *pkt_size, pj_size_t max_pkt_size)
+{
+    enum {MAX_FRAMES_PER_PACKET = PJMEDIA_MAX_FRAME_DURATION_MS / 20};
+
+    pjmedia_frame frames[MAX_FRAMES_PER_PACKET];
+    unsigned nframes = 0;
+    pjmedia_codec_amr_bit_info *info;
+    pj_uint8_t *r; /* Read cursor */
+    pj_uint8_t SID_FT;
+    pjmedia_codec_amr_pack_setting *setting;
+    const pj_uint8_t *framelen_tbl;
+
+    setting = &((amr_settings_t*)codec_data->codec_setting)->enc_setting;
+    framelen_tbl = setting->amr_nb? pjmedia_codec_amrnb_framelen:
+				    pjmedia_codec_amrwb_framelen;
+
+    SID_FT = (pj_uint8_t)(setting->amr_nb? 8 : 9);
+
+    /* Align pkt buf right */
+    r = (pj_uint8_t*)pkt + max_pkt_size - *pkt_size;
+    pj_memmove(r, pkt, *pkt_size);
+
+    /* Get frames */
+    for (;;) {
+	pj_bool_t eof;
+	pj_uint16_t info_;
+
+	info_ = *((pj_uint16_t*)r);
+	eof = ((info_ & 0x40) != 0);
+
+	info = (pjmedia_codec_amr_bit_info*) &frames[nframes].bit_info;
+	pj_bzero(info, sizeof(*info));
+	info->frame_type = (pj_uint8_t)(info_ & 0x0F);
+	info->good_quality = (pj_uint8_t)((info_ & 0x80) == 0);
+	info->mode = (pj_int8_t) ((info_ >> 8) & 0x0F);
+	info->STI = (pj_uint8_t)((info_ >> 5) & 1);
+
+	frames[nframes].buf = r + 2;
+	frames[nframes].size = info->frame_type <= SID_FT ?
+			       framelen_tbl[info->frame_type] : 0;
+
+	r += frames[nframes].size + 2;
+
+	/* Last frame */
+	if (++nframes >= MAX_FRAMES_PER_PACKET || eof)
+	    break;
+    }
+
+    /* Pack */
+    *pkt_size = max_pkt_size;
+    return pjmedia_codec_amr_pack(frames, nframes, setting, pkt, pkt_size);
+}
+
+
+/* Parse AMR payload into frames. */
+static pj_status_t parse_amr(ipp_private_t *codec_data, void *pkt, 
+			     pj_size_t pkt_size, const pj_timestamp *ts,
+			     unsigned *frame_cnt, pjmedia_frame frames[])
+{
+    amr_settings_t* s = (amr_settings_t*)codec_data->codec_setting;
+    pjmedia_codec_amr_pack_setting *setting;
+    pj_status_t status;
+    pj_uint8_t cmr;
+
+    setting = &s->dec_setting;
+
+    status = pjmedia_codec_amr_parse(pkt, pkt_size, ts, setting, frames, 
+				     frame_cnt, &cmr);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Check Change Mode Request. */
+    if (((setting->amr_nb && cmr <= 7) || (!setting->amr_nb && cmr <= 8)) &&
+	s->enc_mode != cmr)
+    {
+	struct ipp_codec *ippc = &ipp_codec[codec_data->codec_idx];
+
+	s->enc_mode = cmr;
+	codec_data->info->params.modes.bitrate = s->enc_setting.amr_nb?
+				pjmedia_codec_amrnb_bitrates[s->enc_mode] :
+				pjmedia_codec_amrwb_bitrates[s->enc_mode];
+	ippc->fxns->std.Control(&codec_data->info->params.modes, 
+				codec_data->enc);
+
+	PJ_LOG(4,(THIS_FILE, "AMR%s switched encoding mode to: %d (%dbps)",
+		  (s->enc_setting.amr_nb?"":"-WB"),
+		  s->enc_mode,
+		  codec_data->info->params.modes.bitrate));
+    }
+
+    return PJ_SUCCESS;
+}
+
+#endif /* PJMEDIA_HAS_INTEL_IPP_CODEC_AMR */
+
+
+#if PJMEDIA_HAS_INTEL_IPP_CODEC_G722_1
+
+static void predecode_g7221( ipp_private_t *codec_data,
+			     const pjmedia_frame *rtp_frame,
+			     USC_Bitstream *usc_frame)
+{
+    usc_frame->pBuffer = (char*)rtp_frame->buf;
+    usc_frame->nbytes = rtp_frame->size;
+    usc_frame->frametype = 0;
+    usc_frame->bitrate = codec_data->info->params.modes.bitrate;
+
+#if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN!=0
+    {
+	pj_uint16_t *p, *p_end;
+
+	p = (pj_uint16_t*)rtp_frame->buf;
+	p_end = p + rtp_frame->size/2;
+	while (p < p_end) {
+	    *p = pj_ntohs(*p);
+	    ++p;
+	}
+    }
+#endif
+}
+
+static pj_status_t pack_g7221( ipp_private_t *codec_data, void *pkt, 
+			       pj_size_t *pkt_size, pj_size_t max_pkt_size)
+{
+    PJ_UNUSED_ARG(codec_data);
+    PJ_UNUSED_ARG(max_pkt_size);
+
+#if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN!=0
+    {
+	pj_uint16_t *p, *p_end;
+
+	p = (pj_uint16_t*)pkt;
+	p_end = p + *pkt_size/2;
+	while (p < p_end) {
+	    *p = pj_htons(*p);
+	    ++p;
+	}
+    }
+#else
+    PJ_UNUSED_ARG(pkt);
+    PJ_UNUSED_ARG(pkt_size);
+#endif
+
+    return PJ_SUCCESS;
+}
+
+
+#include <pjmedia-codec/g7221.h>
+
+
+PJ_DEF(pj_status_t) pjmedia_codec_g7221_set_pcm_shift(int val)
+{
+    PJ_ASSERT_RETURN(val >= 0, PJ_EINVAL);
+
+    ipp_factory.g7221_pcm_shift = val;
+    return PJ_SUCCESS;
+}
+
+
+#endif /* PJMEDIA_HAS_INTEL_IPP_CODEC_G722_1 */
+
+/*
+ * Initialize and register IPP codec factory to pjmedia endpoint.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_ipp_init( pjmedia_endpt *endpt )
+{
+    pjmedia_codec_mgr *codec_mgr;
+    pj_str_t codec_name;
+    pj_status_t status;
+
+    if (ipp_factory.pool != NULL) {
+	/* Already initialized. */
+	return PJ_SUCCESS;
+    }
+
+    /* Create IPP codec factory. */
+    ipp_factory.base.op = &ipp_factory_op;
+    ipp_factory.base.factory_data = NULL;
+    ipp_factory.endpt = endpt;
+    ipp_factory.g7221_pcm_shift = PJMEDIA_G7221_DEFAULT_PCM_SHIFT;
+
+    ipp_factory.pool = pjmedia_endpt_create_pool(endpt, "IPP codecs", 4000, 4000);
+    if (!ipp_factory.pool)
+	return PJ_ENOMEM;
+
+    /* Create mutex. */
+    status = pj_mutex_create_simple(ipp_factory.pool, "IPP codecs", 
+				    &ipp_factory.mutex);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Get the codec manager. */
+    codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
+    if (!codec_mgr) {
+	status = PJ_EINVALIDOP;
+	goto on_error;
+    }
+
+    /* Register format match callback. */
+#if PJMEDIA_HAS_INTEL_IPP_CODEC_G722_1
+    pj_cstr(&codec_name, "G7221");
+    status = pjmedia_sdp_neg_register_fmt_match_cb(
+					&codec_name,
+					&pjmedia_codec_g7221_match_sdp);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+#endif
+
+#if PJMEDIA_HAS_INTEL_IPP_CODEC_AMR
+    pj_cstr(&codec_name, "AMR");
+    status = pjmedia_sdp_neg_register_fmt_match_cb(
+					&codec_name,
+					&pjmedia_codec_amr_match_sdp);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+#endif
+
+#if PJMEDIA_HAS_INTEL_IPP_CODEC_AMRWB
+    pj_cstr(&codec_name, "AMR-WB");
+    status = pjmedia_sdp_neg_register_fmt_match_cb(
+					&codec_name,
+					&pjmedia_codec_amr_match_sdp);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+#endif
+
+    /* Suppress compile warning */
+    PJ_UNUSED_ARG(codec_name);
+
+    /* Register codec factory to endpoint. */
+    status = pjmedia_codec_mgr_register_factory(codec_mgr, 
+						&ipp_factory.base);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Done. */
+    return PJ_SUCCESS;
+
+on_error:
+    pj_pool_release(ipp_factory.pool);
+    ipp_factory.pool = NULL;
+    return status;
+}
+
+/*
+ * Unregister IPP codecs factory from pjmedia endpoint.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_ipp_deinit(void)
+{
+    pjmedia_codec_mgr *codec_mgr;
+    pj_status_t status;
+
+    if (ipp_factory.pool == NULL) {
+	/* Already deinitialized */
+	return PJ_SUCCESS;
+    }
+
+    pj_mutex_lock(ipp_factory.mutex);
+
+    /* Get the codec manager. */
+    codec_mgr = pjmedia_endpt_get_codec_mgr(ipp_factory.endpt);
+    if (!codec_mgr) {
+	pj_pool_release(ipp_factory.pool);
+	ipp_factory.pool = NULL;
+	return PJ_EINVALIDOP;
+    }
+
+    /* Unregister IPP codecs factory. */
+    status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
+						  &ipp_factory.base);
+    
+    /* Destroy mutex. */
+    pj_mutex_destroy(ipp_factory.mutex);
+
+    /* Destroy pool. */
+    pj_pool_release(ipp_factory.pool);
+    ipp_factory.pool = NULL;
+
+    return status;
+}
+
+
+/* 
+ * Check if factory can allocate the specified codec. 
+ */
+static pj_status_t ipp_test_alloc( pjmedia_codec_factory *factory, 
+				   const pjmedia_codec_info *info )
+{
+    unsigned i;
+
+    PJ_UNUSED_ARG(factory);
+
+    /* Type MUST be audio. */
+    if (info->type != PJMEDIA_TYPE_AUDIO)
+	return PJMEDIA_CODEC_EUNSUP;
+
+    for (i = 0; i < PJ_ARRAY_SIZE(ipp_codec); ++i) {
+	pj_str_t name = pj_str((char*)ipp_codec[i].name);
+	if ((pj_stricmp(&info->encoding_name, &name) == 0) &&
+	    (info->clock_rate == (unsigned)ipp_codec[i].clock_rate) &&
+	    (info->channel_cnt == (unsigned)ipp_codec[i].channel_count) &&
+	    (ipp_codec[i].enabled))
+	{
+	    return PJ_SUCCESS;
+	}
+    }
+    
+    /* Unsupported, or mode is disabled. */
+    return PJMEDIA_CODEC_EUNSUP;
+}
+
+/*
+ * Generate default attribute.
+ */
+static pj_status_t ipp_default_attr (pjmedia_codec_factory *factory, 
+				      const pjmedia_codec_info *id, 
+				      pjmedia_codec_param *attr )
+{
+    unsigned i;
+
+    PJ_ASSERT_RETURN(factory==&ipp_factory.base, PJ_EINVAL);
+
+    pj_bzero(attr, sizeof(pjmedia_codec_param));
+
+    for (i = 0; i < PJ_ARRAY_SIZE(ipp_codec); ++i) {
+	pj_str_t name = pj_str((char*)ipp_codec[i].name);
+	if ((pj_stricmp(&id->encoding_name, &name) == 0) &&
+	    (id->clock_rate == (unsigned)ipp_codec[i].clock_rate) &&
+	    (id->channel_cnt == (unsigned)ipp_codec[i].channel_count) &&
+	    (id->pt == (unsigned)ipp_codec[i].pt))
+	{
+	    attr->info.pt = (pj_uint8_t)id->pt;
+	    attr->info.channel_cnt = ipp_codec[i].channel_count;
+	    attr->info.clock_rate = ipp_codec[i].clock_rate;
+	    attr->info.avg_bps = ipp_codec[i].def_bitrate;
+	    attr->info.max_bps = ipp_codec[i].max_bitrate;
+	    attr->info.pcm_bits_per_sample = 16;
+	    attr->info.frm_ptime =  (pj_uint16_t)
+				    (ipp_codec[i].samples_per_frame * 1000 / 
+				    ipp_codec[i].channel_count / 
+				    ipp_codec[i].clock_rate);
+	    attr->setting.frm_per_pkt = ipp_codec[i].frm_per_pkt;
+
+	    /* Default flags. */
+	    attr->setting.plc = 1;
+	    attr->setting.penh= 0;
+	    attr->setting.vad = 1;
+	    attr->setting.cng = attr->setting.vad;
+	    attr->setting.dec_fmtp = ipp_codec[i].dec_fmtp;
+
+	    if (attr->setting.vad == 0) {
+#if PJMEDIA_HAS_INTEL_IPP_CODEC_G729
+		if (id->pt == PJMEDIA_RTP_PT_G729) {
+		    /* Signal G729 Annex B is being disabled */
+		    attr->setting.dec_fmtp.cnt = 1;
+		    pj_strset2(&attr->setting.dec_fmtp.param[0].name, "annexb");
+		    pj_strset2(&attr->setting.dec_fmtp.param[0].val, "no");
+		}
+#endif
+	    }
+
+	    return PJ_SUCCESS;
+	}
+    }
+
+    return PJMEDIA_CODEC_EUNSUP;
+}
+
+/*
+ * Enum codecs supported by this factory.
+ */
+static pj_status_t ipp_enum_codecs(pjmedia_codec_factory *factory, 
+				    unsigned *count, 
+				    pjmedia_codec_info codecs[])
+{
+    unsigned max;
+    unsigned i;
+
+    PJ_UNUSED_ARG(factory);
+    PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
+
+    max = *count;
+    
+    for (i = 0, *count = 0; i < PJ_ARRAY_SIZE(ipp_codec) && *count < max; ++i) 
+    {
+	if (!ipp_codec[i].enabled)
+	    continue;
+
+	pj_bzero(&codecs[*count], sizeof(pjmedia_codec_info));
+	codecs[*count].encoding_name = pj_str((char*)ipp_codec[i].name);
+	codecs[*count].pt = ipp_codec[i].pt;
+	codecs[*count].type = PJMEDIA_TYPE_AUDIO;
+	codecs[*count].clock_rate = ipp_codec[i].clock_rate;
+	codecs[*count].channel_cnt = ipp_codec[i].channel_count;
+
+	++*count;
+    }
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Allocate a new codec instance.
+ */
+static pj_status_t ipp_alloc_codec( pjmedia_codec_factory *factory, 
+				    const pjmedia_codec_info *id,
+				    pjmedia_codec **p_codec)
+{
+    ipp_private_t *codec_data;
+    pjmedia_codec *codec;
+    int idx;
+    pj_pool_t *pool;
+    unsigned i;
+
+    PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory == &ipp_factory.base, PJ_EINVAL);
+
+    pj_mutex_lock(ipp_factory.mutex);
+
+    /* Find codec's index */
+    idx = -1;
+    for (i = 0; i < PJ_ARRAY_SIZE(ipp_codec); ++i) {
+	pj_str_t name = pj_str((char*)ipp_codec[i].name);
+	if ((pj_stricmp(&id->encoding_name, &name) == 0) &&
+	    (id->clock_rate == (unsigned)ipp_codec[i].clock_rate) &&
+	    (id->channel_cnt == (unsigned)ipp_codec[i].channel_count) &&
+	    (ipp_codec[i].enabled))
+	{
+	    idx = i;
+	    break;
+	}
+    }
+    if (idx == -1) {
+	*p_codec = NULL;
+	return PJMEDIA_CODEC_EFAILED;
+    }
+
+    /* Create pool for codec instance */
+    pool = pjmedia_endpt_create_pool(ipp_factory.endpt, "IPPcodec", 512, 512);
+    codec = PJ_POOL_ZALLOC_T(pool, pjmedia_codec);
+    PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM);
+    codec->op = &ipp_op;
+    codec->factory = factory;
+    codec->codec_data = PJ_POOL_ZALLOC_T(pool, ipp_private_t);
+    codec_data = (ipp_private_t*) codec->codec_data;
+
+    /* Create PLC if codec has no internal PLC */
+    if (!ipp_codec[idx].has_native_plc) {
+	pj_status_t status;
+	status = pjmedia_plc_create(pool, ipp_codec[idx].clock_rate, 
+				    ipp_codec[idx].samples_per_frame, 0,
+				    &codec_data->plc);
+	if (status != PJ_SUCCESS) {
+	    pj_pool_release(pool);
+	    pj_mutex_unlock(ipp_factory.mutex);
+	    return status;
+	}
+    }
+
+    /* Create silence detector if codec has no internal VAD */
+    if (!ipp_codec[idx].has_native_vad) {
+	pj_status_t status;
+	status = pjmedia_silence_det_create(pool,
+					    ipp_codec[idx].clock_rate,
+					    ipp_codec[idx].samples_per_frame,
+					    &codec_data->vad);
+	if (status != PJ_SUCCESS) {
+	    pj_pool_release(pool);
+	    pj_mutex_unlock(ipp_factory.mutex);
+	    return status;
+	}
+    }
+
+    codec_data->pool = pool;
+    codec_data->codec_idx = idx;
+
+    pj_mutex_unlock(ipp_factory.mutex);
+
+    *p_codec = codec;
+    return PJ_SUCCESS;
+}
+
+/*
+ * Free codec.
+ */
+static pj_status_t ipp_dealloc_codec( pjmedia_codec_factory *factory, 
+				      pjmedia_codec *codec )
+{
+    ipp_private_t *codec_data;
+
+    PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory == &ipp_factory.base, PJ_EINVAL);
+
+    /* Close codec, if it's not closed. */
+    codec_data = (ipp_private_t*) codec->codec_data;
+    if (codec_data->enc != NULL || codec_data->dec != NULL) {
+	ipp_codec_close(codec);
+    }
+
+    pj_pool_release(codec_data->pool);
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Init codec.
+ */
+static pj_status_t ipp_codec_init( pjmedia_codec *codec, 
+				   pj_pool_t *pool )
+{
+    PJ_UNUSED_ARG(codec);
+    PJ_UNUSED_ARG(pool);
+    return PJ_SUCCESS;
+}
+
+/*
+ * Open codec.
+ */
+static pj_status_t ipp_codec_open( pjmedia_codec *codec, 
+				   pjmedia_codec_param *attr )
+{
+    ipp_private_t *codec_data = (ipp_private_t*) codec->codec_data;
+    struct ipp_codec *ippc = &ipp_codec[codec_data->codec_idx];
+    int info_size;
+    pj_pool_t *pool;
+    int i, j;
+    USC_MemBank *membanks;
+    int nb_membanks;
+
+    pool = codec_data->pool;
+
+    /* Get the codec info size */
+    if (USC_NoError != ippc->fxns->std.GetInfoSize(&info_size)) {
+	PJ_LOG(1,(THIS_FILE, "Error getting codec info size"));
+	goto on_error;
+    }
+    /* Get the codec info */
+    codec_data->info = pj_pool_zalloc(pool, info_size);
+    if (USC_NoError != ippc->fxns->std.GetInfo((USC_Handle)NULL, 
+					       codec_data->info))
+    {
+	PJ_LOG(1,(THIS_FILE, "Error getting codec info"));
+	goto on_error;
+    }
+
+    /* PREPARING THE ENCODER */
+
+    /* Setting the encoder params */
+    codec_data->info->params.direction = USC_ENCODE;
+    codec_data->info->params.modes.vad = attr->setting.vad && 
+					 ippc->has_native_vad;
+    codec_data->info->params.modes.bitrate = attr->info.avg_bps;
+    codec_data->info->params.law = 0; /* Linear PCM input */
+
+#if PJMEDIA_HAS_INTEL_IPP_CODEC_G729
+    if (ippc->pt == PJMEDIA_RTP_PT_G729) {
+	/* Check if G729 Annex B is signaled to be disabled */
+	for (i = 0; i < attr->setting.enc_fmtp.cnt; ++i) {
+	    if (pj_stricmp2(&attr->setting.enc_fmtp.param[i].name, "annexb")==0)
+	    {
+		if (pj_stricmp2(&attr->setting.enc_fmtp.param[i].val, "no")==0)
+		{
+		    attr->setting.vad = 0;
+		    codec_data->info->params.modes.vad = 0;
+		}
+		break;
+	    }
+	}
+    }
+#endif
+
+    /* Get number of memory blocks needed by the encoder */
+    if (USC_NoError != ippc->fxns->std.NumAlloc(&codec_data->info->params,
+					        &nb_membanks))
+    {
+	PJ_LOG(1,(THIS_FILE, "Error getting no of memory blocks of encoder"));
+	goto on_error;
+    }
+
+    /* Allocate memory blocks table */
+    membanks = (USC_MemBank*) pj_pool_zalloc(pool, 
+					     sizeof(USC_MemBank) * nb_membanks);
+    /* Get size of each memory block */
+    if (USC_NoError != ippc->fxns->std.MemAlloc(&codec_data->info->params, 
+					        membanks))
+    {
+	PJ_LOG(1,(THIS_FILE, "Error getting memory blocks size of encoder"));
+	goto on_error;
+    }
+
+    /* Allocate memory for each block */
+    for (i = 0; i < nb_membanks; i++) {
+	membanks[i].pMem = (char*) pj_pool_zalloc(pool, membanks[i].nbytes);
+    }
+
+    /* Create encoder instance */
+    if (USC_NoError != ippc->fxns->std.Init(&codec_data->info->params,
+					    membanks, 
+					    &codec_data->enc))
+    {
+	PJ_LOG(1,(THIS_FILE, "Error initializing encoder"));
+	goto on_error;
+    }
+
+    /* PREPARING THE DECODER */
+
+    /* Setting the decoder params */
+    codec_data->info->params.direction = USC_DECODE;
+
+    /* Not sure if VAD affects decoder, just try to be safe */
+    //codec_data->info->params.modes.vad = ippc->has_native_vad;
+
+    /* Get number of memory blocks needed by the decoder */
+    if (USC_NoError != ippc->fxns->std.NumAlloc(&codec_data->info->params, 
+						 &nb_membanks))
+    {
+	PJ_LOG(1,(THIS_FILE, "Error getting no of memory blocks of decoder"));
+	goto on_error;
+    }
+
+    /* Allocate memory blocks table */
+    membanks = (USC_MemBank*) pj_pool_zalloc(pool, 
+					     sizeof(USC_MemBank) * nb_membanks);
+    /* Get size of each memory block */
+    if (USC_NoError != ippc->fxns->std.MemAlloc(&codec_data->info->params, 
+						membanks))
+    {
+	PJ_LOG(1,(THIS_FILE, "Error getting memory blocks size of decoder"));
+	goto on_error;
+    }
+
+    /* Allocate memory for each block */
+    for (i = 0; i < nb_membanks; i++) {
+	membanks[i].pMem = (char*) pj_pool_zalloc(pool, membanks[i].nbytes);
+    }
+
+    /* Create decoder instance */
+    if (USC_NoError != ippc->fxns->std.Init(&codec_data->info->params, 
+					    membanks, &codec_data->dec))
+    {
+	PJ_LOG(1,(THIS_FILE, "Error initializing decoder"));
+	goto on_error;
+    }
+
+    /* Update codec info */
+    ippc->fxns->std.GetInfo((USC_Handle)codec_data->enc, codec_data->info);
+
+    /* Get bitstream size */
+    i = codec_data->info->params.modes.bitrate * ippc->samples_per_frame;
+    j = ippc->clock_rate << 3;
+    codec_data->frame_size = (pj_uint16_t)(i / j);
+    if (i % j) ++codec_data->frame_size;
+
+    codec_data->vad_enabled = (attr->setting.vad != 0);
+    codec_data->plc_enabled = (attr->setting.plc != 0);
+
+#if PJMEDIA_HAS_INTEL_IPP_CODEC_AMR
+    /* Init AMR settings */
+    if (ippc->pt == PJMEDIA_RTP_PT_AMR || ippc->pt == PJMEDIA_RTP_PT_AMRWB) {
+	amr_settings_t *s;
+	pj_uint8_t octet_align = 0;
+	pj_int8_t enc_mode;
+
+	enc_mode = pjmedia_codec_amr_get_mode(
+				codec_data->info->params.modes.bitrate);
+	pj_assert(enc_mode >= 0 && enc_mode <= 8);
+
+	/* Check AMR specific attributes */
+
+	for (i = 0; i < attr->setting.dec_fmtp.cnt; ++i) {
+	    /* octet-align, one of the parameters that must have same value 
+	     * in offer & answer (RFC 4867 Section 8.3.1). Just check fmtp
+	     * in the decoder side, since it's value is guaranteed to fulfil 
+	     * above requirement (by SDP negotiator).
+	     */
+	    const pj_str_t STR_FMTP_OCTET_ALIGN = {"octet-align", 11};
+	    
+	    if (pj_stricmp(&attr->setting.dec_fmtp.param[i].name, 
+			   &STR_FMTP_OCTET_ALIGN) == 0)
+	    {
+		octet_align=(pj_uint8_t)
+			    pj_strtoul(&attr->setting.dec_fmtp.param[i].val);
+		break;
+	    }
+	}
+
+	for (i = 0; i < attr->setting.enc_fmtp.cnt; ++i) {
+	    /* mode-set, encoding mode is chosen based on local default mode 
+	     * setting:
+	     * - if local default mode is included in the mode-set, use it
+	     * - otherwise, find the closest mode to local default mode;
+	     *   if there are two closest modes, prefer to use the higher
+	     *   one, e.g: local default mode is 4, the mode-set param
+	     *   contains '2,3,5,6', then 5 will be chosen.
+	     */
+	    const pj_str_t STR_FMTP_MODE_SET = {"mode-set", 8};
+	    
+	    if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name, 
+			   &STR_FMTP_MODE_SET) == 0)
+	    {
+		const char *p;
+		pj_size_t l;
+		pj_int8_t diff = 99;
+		
+		p = pj_strbuf(&attr->setting.enc_fmtp.param[i].val);
+		l = pj_strlen(&attr->setting.enc_fmtp.param[i].val);
+
+		while (l--) {
+		    if ((ippc->pt==PJMEDIA_RTP_PT_AMR && *p>='0' && *p<='7') ||
+		        (ippc->pt==PJMEDIA_RTP_PT_AMRWB && *p>='0' && *p<='8'))
+		    {
+			pj_int8_t tmp = (pj_int8_t)(*p - '0' - enc_mode);
+
+			if (PJ_ABS(diff) > PJ_ABS(tmp) || 
+			    (PJ_ABS(diff) == PJ_ABS(tmp) && tmp > diff))
+			{
+			    diff = tmp;
+			    if (diff == 0) break;
+			}
+		    }
+		    ++p;
+		}
+
+		if (diff == 99)
+		    goto on_error;
+
+		enc_mode = (pj_int8_t)(enc_mode + diff);
+
+		break;
+	    }
+	}
+
+	/* Initialize AMR specific settings */
+	s = PJ_POOL_ZALLOC_T(pool, amr_settings_t);
+	codec_data->codec_setting = s;
+
+	s->enc_setting.amr_nb = (pj_uint8_t)(ippc->pt == PJMEDIA_RTP_PT_AMR);
+	s->enc_setting.octet_aligned = octet_align;
+	s->enc_setting.reorder = PJ_TRUE;
+	s->enc_setting.cmr = 15;
+
+	s->dec_setting.amr_nb = (pj_uint8_t)(ippc->pt == PJMEDIA_RTP_PT_AMR);
+	s->dec_setting.octet_aligned = octet_align;
+	s->dec_setting.reorder = PJ_TRUE;
+
+	/* Apply encoder mode/bitrate */
+	s->enc_mode = enc_mode;
+	codec_data->info->params.modes.bitrate = s->enc_setting.amr_nb?
+				pjmedia_codec_amrnb_bitrates[s->enc_mode]:
+				pjmedia_codec_amrwb_bitrates[s->enc_mode];
+	ippc->fxns->std.Control(&codec_data->info->params.modes, 
+				codec_data->enc);
+
+	PJ_LOG(4,(THIS_FILE, "AMR%s encoding mode: %d (%dbps)", 
+		  (s->enc_setting.amr_nb?"":"-WB"),
+		  s->enc_mode,
+		  codec_data->info->params.modes.bitrate));
+
+	/* Return back bitrate info to application */
+	attr->info.avg_bps = codec_data->info->params.modes.bitrate;
+    }
+#endif
+
+#if PJMEDIA_HAS_INTEL_IPP_CODEC_G722_1
+    if (ippc->pt >= PJMEDIA_RTP_PT_G722_1_16 && 
+	ippc->pt <= PJMEDIA_RTP_PT_G7221_RSV2)
+    {
+	codec_data->g7221_pcm_shift = ipp_factory.g7221_pcm_shift;
+    }
+#endif
+
+    return PJ_SUCCESS;
+
+on_error:
+    return PJMEDIA_CODEC_EFAILED;
+}
+
+/*
+ * Close codec.
+ */
+static pj_status_t ipp_codec_close( pjmedia_codec *codec )
+{
+    PJ_UNUSED_ARG(codec);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Modify codec settings.
+ */
+static pj_status_t  ipp_codec_modify(pjmedia_codec *codec, 
+				     const pjmedia_codec_param *attr )
+{
+    ipp_private_t *codec_data = (ipp_private_t*) codec->codec_data;
+    struct ipp_codec *ippc = &ipp_codec[codec_data->codec_idx];
+
+    codec_data->vad_enabled = (attr->setting.vad != 0);
+    codec_data->plc_enabled = (attr->setting.plc != 0);
+
+    if (ippc->has_native_vad) {
+	USC_Modes modes;
+
+	modes = codec_data->info->params.modes;
+	modes.vad = codec_data->vad_enabled;
+	ippc->fxns->std.Control(&modes, codec_data->enc);
+    }
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Get frames in the packet.
+ */
+static pj_status_t  ipp_codec_parse( pjmedia_codec *codec,
+				     void *pkt,
+				     pj_size_t pkt_size,
+				     const pj_timestamp *ts,
+				     unsigned *frame_cnt,
+				     pjmedia_frame frames[])
+{
+    ipp_private_t *codec_data = (ipp_private_t*) codec->codec_data;
+    struct ipp_codec *ippc = &ipp_codec[codec_data->codec_idx];
+    unsigned count = 0;
+
+    PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL);
+
+    if (ippc->parse != NULL) {
+	return ippc->parse(codec_data, pkt,  pkt_size, ts, frame_cnt, frames);
+    }
+
+    while (pkt_size >= codec_data->frame_size && count < *frame_cnt) {
+	frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
+	frames[count].buf = pkt;
+	frames[count].size = codec_data->frame_size;
+	frames[count].timestamp.u64 = ts->u64 + count*ippc->samples_per_frame;
+
+	pkt = ((char*)pkt) + codec_data->frame_size;
+	pkt_size -= codec_data->frame_size;
+
+	++count;
+    }
+
+    if (pkt_size && count < *frame_cnt) {
+	frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
+	frames[count].buf = pkt;
+	frames[count].size = pkt_size;
+	frames[count].timestamp.u64 = ts->u64 + count*ippc->samples_per_frame;
+	++count;
+    }
+
+    *frame_cnt = count;
+    return PJ_SUCCESS;
+}
+
+/*
+ * Encode frames.
+ */
+static pj_status_t ipp_codec_encode( pjmedia_codec *codec, 
+				     const struct pjmedia_frame *input,
+				     unsigned output_buf_len, 
+				     struct pjmedia_frame *output)
+{
+    ipp_private_t *codec_data = (ipp_private_t*) codec->codec_data;
+    struct ipp_codec *ippc = &ipp_codec[codec_data->codec_idx];
+    unsigned samples_per_frame;
+    unsigned nsamples;
+    pj_size_t tx = 0;
+    pj_int16_t *pcm_in   = (pj_int16_t*)input->buf;
+    pj_uint8_t  *bits_out = (pj_uint8_t*) output->buf;
+    pj_uint8_t pt;
+
+    /* Invoke external VAD if codec has no internal VAD */
+    if (codec_data->vad && codec_data->vad_enabled) {
+	pj_bool_t is_silence;
+	pj_int32_t silence_duration;
+
+	silence_duration = pj_timestamp_diff32(&codec_data->last_tx, 
+					       &input->timestamp);
+
+	is_silence = pjmedia_silence_det_detect(codec_data->vad, 
+					        (const pj_int16_t*) input->buf,
+						(input->size >> 1),
+						NULL);
+	if (is_silence &&
+	    (PJMEDIA_CODEC_MAX_SILENCE_PERIOD == -1 ||
+	     silence_duration < (PJMEDIA_CODEC_MAX_SILENCE_PERIOD *
+	 			 (int)ippc->clock_rate / 1000)))
+	{
+	    output->type = PJMEDIA_FRAME_TYPE_NONE;
+	    output->buf = NULL;
+	    output->size = 0;
+	    output->timestamp = input->timestamp;
+	    return PJ_SUCCESS;
+	} else {
+	    codec_data->last_tx = input->timestamp;
+	}
+    }
+
+    nsamples = input->size >> 1;
+    samples_per_frame = ippc->samples_per_frame;
+    pt = ippc->pt;
+
+    PJ_ASSERT_RETURN(nsamples % samples_per_frame == 0, 
+		     PJMEDIA_CODEC_EPCMFRMINLEN);
+
+    /* Encode the frames */
+    while (nsamples >= samples_per_frame) {
+	USC_PCMStream in;
+	USC_Bitstream out;
+
+	in.bitrate = codec_data->info->params.modes.bitrate;
+	in.nbytes = samples_per_frame << 1;
+	in.pBuffer = (char*)pcm_in;
+	in.pcmType.bitPerSample = codec_data->info->params.pcmType.bitPerSample;
+	in.pcmType.nChannels = codec_data->info->params.pcmType.nChannels;
+	in.pcmType.sample_frequency = codec_data->info->params.pcmType.sample_frequency;
+
+	out.pBuffer = (char*)bits_out;
+
+#if PJMEDIA_HAS_INTEL_IPP_CODEC_AMR
+	/* For AMR: reserve two octets for AMR frame info */
+	if (pt == PJMEDIA_RTP_PT_AMR || pt == PJMEDIA_RTP_PT_AMRWB) {
+	    out.pBuffer += 2;
+	}
+#endif
+
+#if PJMEDIA_HAS_INTEL_IPP_CODEC_G722_1
+	/* For G722.1: adjust the encoder input signal level */
+	if (pt >= PJMEDIA_RTP_PT_G722_1_16 && 
+	    pt <= PJMEDIA_RTP_PT_G7221_RSV2 &&
+	    codec_data->g7221_pcm_shift)
+	{
+	    unsigned i;
+	    for (i = 0; i < samples_per_frame; ++i)
+		pcm_in[i] >>= codec_data->g7221_pcm_shift;
+	}
+#endif
+
+	if (USC_NoError != ippc->fxns->Encode(codec_data->enc, &in, &out)) {
+	    break;
+	}
+
+#if PJMEDIA_HAS_INTEL_IPP_CODEC_AMR
+	/* For AMR: put info (frametype, degraded, last frame, mode) in the 
+	 * first two octets for payload packing.
+	 */
+	if (pt == PJMEDIA_RTP_PT_AMR || pt == PJMEDIA_RTP_PT_AMRWB) {
+	    pj_uint16_t *info = (pj_uint16_t*)bits_out;
+
+	    /* Two octets for AMR frame info, 0=LSB:
+	     * bit 0-3	: frame type
+	     * bit 5	: STI flag
+	     * bit 6	: last frame flag
+	     * bit 7	: quality flag
+	     * bit 8-11	: mode
+	     */
+	    out.nbytes += 2;
+	    if (out.frametype == 0 || out.frametype == 4 || 
+		(pt == PJMEDIA_RTP_PT_AMR && out.frametype == 5) ||
+		(pt == PJMEDIA_RTP_PT_AMRWB && out.frametype == 6))
+	    {
+		/* Speech frame type */
+		*info = (char)pjmedia_codec_amr_get_mode(out.bitrate);
+		/* Quality */
+		if (out.frametype == 5 || out.frametype == 6)
+		    *info |= 0x80;
+	    } else if (out.frametype == 1 || out.frametype == 2 || 
+		       (pt == PJMEDIA_RTP_PT_AMR && out.frametype == 6) ||
+		       (pt == PJMEDIA_RTP_PT_AMRWB && out.frametype == 7))
+	    {
+		/* SID frame type */
+		*info = (pj_uint8_t)(pt == PJMEDIA_RTP_PT_AMRWB? 9 : 8);
+		/* Quality */
+		if (out.frametype == 6 || out.frametype == 7)
+		    *info |= 0x80;
+		/* STI */
+		if (out.frametype != 1)
+		    *info |= 0x20;
+	    } else {
+		/* Untransmited */
+		*info = 15;
+		out.nbytes = 2;
+	    }
+
+	    /* Mode */
+	    *info |= (char)pjmedia_codec_amr_get_mode(out.bitrate) << 8;
+
+	    /* Last frame flag */
+	    if (nsamples == samples_per_frame)
+		*info |= 0x40;
+	}
+#endif
+
+	pcm_in += samples_per_frame;
+	nsamples -= samples_per_frame;
+	tx += out.nbytes;
+	bits_out += out.nbytes;
+
+#if PJMEDIA_HAS_INTEL_IPP_CODEC_G729
+	if (pt == PJMEDIA_RTP_PT_G729) {
+	    if (out.frametype == 1) {
+		/* SID */
+		break;
+	    } else if (out.frametype == 0) {
+		/* Untransmitted */
+		tx -= out.nbytes;
+		break;
+	    }
+	}
+#endif
+
+    }
+
+    if (ippc->pack != NULL) {
+	ippc->pack(codec_data, output->buf, &tx, output_buf_len);
+    }
+
+    /* Check if we don't need to transmit the frame (DTX) */
+    if (tx == 0) {
+	output->buf = NULL;
+	output->size = 0;
+	output->timestamp.u64 = input->timestamp.u64;
+	output->type = PJMEDIA_FRAME_TYPE_NONE;
+	return PJ_SUCCESS;
+    }
+
+    output->size = tx;
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+    output->timestamp = input->timestamp;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Decode frame.
+ */
+static pj_status_t ipp_codec_decode( pjmedia_codec *codec, 
+				     const struct pjmedia_frame *input,
+				     unsigned output_buf_len, 
+				     struct pjmedia_frame *output)
+{
+    ipp_private_t *codec_data = (ipp_private_t*) codec->codec_data;
+    struct ipp_codec *ippc = &ipp_codec[codec_data->codec_idx];
+    unsigned samples_per_frame;
+    USC_PCMStream out;
+    USC_Bitstream in;
+    pj_uint8_t pt;
+
+    pt = ippc->pt; 
+    samples_per_frame = ippc->samples_per_frame;
+
+    PJ_ASSERT_RETURN(output_buf_len >= samples_per_frame << 1,
+		     PJMEDIA_CODEC_EPCMTOOSHORT);
+
+    if (input->type == PJMEDIA_FRAME_TYPE_AUDIO) {
+	if (ippc->predecode) {
+	    ippc->predecode(codec_data, input, &in);
+	} else {
+	    /* Most IPP codecs have frametype==0 for speech frame */
+	    in.pBuffer = (char*)input->buf;
+	    in.nbytes = input->size;
+	    in.frametype = 0;
+	    in.bitrate = codec_data->info->params.modes.bitrate;
+	}
+
+	out.pBuffer = output->buf;
+    }
+
+    if (input->type != PJMEDIA_FRAME_TYPE_AUDIO ||
+	USC_NoError != ippc->fxns->Decode(codec_data->dec, &in, &out)) 
+    {
+	pjmedia_zero_samples((pj_int16_t*)output->buf, samples_per_frame);
+	output->size = samples_per_frame << 1;
+	output->timestamp.u64 = input->timestamp.u64;
+	output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+	return PJ_SUCCESS;
+    }
+
+#if PJMEDIA_HAS_INTEL_IPP_CODEC_G726
+    /* For G.726: amplify decoding result (USC G.726 encoder deamplified it) */
+    if (pt == PJMEDIA_RTP_PT_G726_16 || pt == PJMEDIA_RTP_PT_G726_24 ||
+	pt == PJMEDIA_RTP_PT_G726_32 || pt == PJMEDIA_RTP_PT_G726_40 ||
+	pt == PJMEDIA_RTP_PT_G721)
+    {
+	unsigned i;
+	pj_int16_t *s = (pj_int16_t*)output->buf;
+
+	for (i = 0; i < samples_per_frame; ++i)
+	    s[i] <<= 2;
+    }
+#endif
+
+#if PJMEDIA_HAS_INTEL_IPP_CODEC_G722_1
+    /* For G722.1: adjust the decoder output signal level */
+    if (pt >= PJMEDIA_RTP_PT_G722_1_16 && 
+	pt <= PJMEDIA_RTP_PT_G7221_RSV2 &&
+	codec_data->g7221_pcm_shift)
+    {
+	unsigned i;
+	pj_int16_t *s = (pj_int16_t*)output->buf;
+
+	for (i = 0; i < samples_per_frame; ++i)
+	    s[i] <<= codec_data->g7221_pcm_shift;
+    }
+#endif
+
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+    output->size = samples_per_frame << 1;
+    output->timestamp.u64 = input->timestamp.u64;
+
+    /* Invoke external PLC if codec has no internal PLC */
+    if (codec_data->plc && codec_data->plc_enabled)
+	pjmedia_plc_save(codec_data->plc, (pj_int16_t*)output->buf);
+
+    return PJ_SUCCESS;
+}
+
+/* 
+ * Recover lost frame.
+ */
+static pj_status_t  ipp_codec_recover(pjmedia_codec *codec, 
+				      unsigned output_buf_len, 
+				      struct pjmedia_frame *output)
+{
+    ipp_private_t *codec_data = (ipp_private_t*) codec->codec_data;
+    struct ipp_codec *ippc = &ipp_codec[codec_data->codec_idx];
+    unsigned samples_per_frame;
+
+    PJ_UNUSED_ARG(output_buf_len);
+
+    samples_per_frame = ippc->samples_per_frame;
+
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+    output->size = samples_per_frame << 1;
+
+    if (codec_data->plc_enabled) {
+	if (codec_data->plc) {
+	    pjmedia_plc_generate(codec_data->plc, (pj_int16_t*)output->buf);
+	} else {
+	    USC_PCMStream out;
+	    out.pBuffer = output->buf;
+	    ippc->fxns->Decode(codec_data->dec, NULL, &out);
+	}
+    } else {
+	pjmedia_zero_samples((pj_int16_t*)output->buf, samples_per_frame);
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+#if defined(_MSC_VER) && PJMEDIA_AUTO_LINK_IPP_LIBS
+#   pragma comment( lib, "ippcore.lib")
+#   pragma comment( lib, "ipps.lib")
+#   pragma comment( lib, "ippsc.lib")
+#   if defined(IPP_VERSION_MAJOR) && IPP_VERSION_MAJOR<=6
+#	pragma comment( lib, "ippsr.lib")
+#   endif
+//#   pragma comment( lib, "ippcorel.lib")
+//#   pragma comment( lib, "ippsemerged.lib")
+//#   pragma comment( lib, "ippsmerged.lib")
+//#   pragma comment( lib, "ippscemerged.lib")
+//#   pragma comment( lib, "ippscmerged.lib")
+//#   pragma comment( lib, "ippsremerged.lib")
+//#   pragma comment( lib, "ippsrmerged.lib")
+#   if defined(IPP_VERSION_MAJOR) && IPP_VERSION_MAJOR>=6
+#	pragma comment( lib, "speech.lib")
+#   else
+#	pragma comment( lib, "usc.lib")
+#   endif
+#endif
+
+
+#endif	/* PJMEDIA_HAS_INTEL_IPP */
+
diff --git a/jni/pjproject-android/pjmedia/src/pjmedia-codec/l16.c b/jni/pjproject-android/pjmedia/src/pjmedia-codec/l16.c
new file mode 100644
index 0000000..063abc9
--- /dev/null
+++ b/jni/pjproject-android/pjmedia/src/pjmedia-codec/l16.c
@@ -0,0 +1,729 @@
+/* $Id: l16.c 3664 2011-07-19 03:42:28Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjmedia-codec/l16.h>
+#include <pjmedia/codec.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/endpoint.h>
+#include <pjmedia/plc.h>
+#include <pjmedia/silencedet.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
+
+#define PLC_DISABLED	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_modify(pjmedia_codec *codec, 
+			       const pjmedia_codec_param *attr );
+static pj_status_t  l16_parse(pjmedia_codec *codec,
+			      void *pkt,
+			      pj_size_t pkt_size,
+			      const pj_timestamp *ts,
+			      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);
+#if !PLC_DISABLED
+static pj_status_t  l16_recover(pjmedia_codec *codec,
+				unsigned output_buf_len,
+				struct pjmedia_frame *output);
+#endif
+
+/* Definition for L16 codec operations. */
+static pjmedia_codec_op l16_op = 
+{
+    &l16_init,
+    &l16_open,
+    &l16_close,
+    &l16_modify,
+    &l16_parse,
+    &l16_encode,
+    &l16_decode,
+#if !PLC_DISABLED
+    &l16_recover
+#else
+    NULL
+#endif
+};
+
+/* 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,
+    &pjmedia_codec_l16_deinit
+};
+
+/* L16 factory private data */
+static struct l16_factory
+{
+    pjmedia_codec_factory	base;
+    pjmedia_endpt	       *endpt;
+    pj_pool_t		       *pool;
+    pj_mutex_t		       *mutex;
+} l16_factory;
+
+
+/* L16 codec private data. */
+struct l16_data
+{
+    pj_pool_t		*pool;
+    unsigned		 frame_size;    /* Frame size, in bytes */
+    unsigned		 clock_rate;    /* Clock rate */
+
+#if !PLC_DISABLED
+    pj_bool_t		 plc_enabled;
+    pjmedia_plc		*plc;
+#endif
+    pj_bool_t		 vad_enabled;
+    pjmedia_silence_det	*vad;
+    pj_timestamp	 last_tx;
+};
+
+
+
+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;
+
+    /* 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_bzero(attr, sizeof(pjmedia_codec_param));
+    attr->info.pt = (pj_uint8_t)id->pt;
+    attr->info.clock_rate = id->clock_rate;
+    attr->info.channel_cnt = id->channel_cnt;
+    attr->info.avg_bps = id->clock_rate * id->channel_cnt * 16;
+    attr->info.max_bps = attr->info.avg_bps;
+    attr->info.pcm_bits_per_sample = 16;
+
+    /* To keep frame size below 1400 MTU, set ptime to 10ms for
+     * sampling rate > 35 KHz
+     */
+    attr->info.frm_ptime = GET_PTIME(id->clock_rate);
+
+    attr->setting.frm_per_pkt = 1;
+
+    attr->setting.vad = 1;
+#if !PLC_DISABLED
+    attr->setting.plc = 1;
+#endif
+
+    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;
+    }
+
+// disable some L16 modes
+#if 0
+    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;
+    }
+#endif
+
+    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;
+    }
+
+// disable some L16 modes
+#if 0
+    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_STEREO;
+	codecs[count].encoding_name = STR_L16;
+	codecs[count].clock_rate = 48000;
+	codecs[count].channel_cnt = 2;
+	++count;
+    }
+#endif
+
+
+    *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_pool_t *pool;
+
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(factory==&l16_factory.base, PJ_EINVAL);
+
+    /* Lock mutex. */
+    pj_mutex_lock(l16_factory.mutex);
+
+
+    pool = pjmedia_endpt_create_pool(l16_factory.endpt, "l16", 4000, 4000);
+    codec = PJ_POOL_ZALLOC_T(pool, pjmedia_codec);
+    codec->codec_data = pj_pool_alloc(pool, sizeof(struct l16_data));
+    codec->factory = factory;
+    codec->op = &l16_op;
+
+    /* Init private data */
+    ptime = GET_PTIME(id->clock_rate);
+    data = (struct l16_data*) codec->codec_data;
+    data->frame_size = ptime * id->clock_rate * id->channel_cnt * 2 / 1000;
+    data->clock_rate = id->clock_rate;
+    data->pool = pool;
+
+#if !PLC_DISABLED
+    /* Create PLC */
+    status = pjmedia_plc_create(pool, id->clock_rate, 
+				data->frame_size >> 1, 0, 
+				&data->plc);
+    if (status != PJ_SUCCESS) {
+	pj_mutex_unlock(l16_factory.mutex);
+	return status;
+    }
+#endif
+
+    /* Create silence detector */
+    status = pjmedia_silence_det_create(pool, id->clock_rate, 
+					data->frame_size >> 1,
+					&data->vad);
+    if (status != PJ_SUCCESS) {
+	pj_mutex_unlock(l16_factory.mutex);
+	return status;
+    }
+
+    *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 )
+{
+    struct l16_data *data;
+
+    PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory==&l16_factory.base, PJ_EINVAL);
+
+    /* Lock mutex. */
+    pj_mutex_lock(l16_factory.mutex);
+
+    /* Just release codec data pool */
+    data = (struct l16_data*) codec->codec_data;
+    pj_assert(data);
+    pj_pool_release(data->pool);
+
+    /* 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 )
+{
+    struct l16_data *data = NULL;
+    
+    PJ_ASSERT_RETURN(codec && codec->codec_data && attr, PJ_EINVAL);
+
+    data = (struct l16_data*) codec->codec_data;
+
+    data->vad_enabled = (attr->setting.vad != 0);
+#if !PLC_DISABLED
+    data->plc_enabled = (attr->setting.plc != 0);
+#endif
+
+    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_modify(pjmedia_codec *codec, 
+			       const pjmedia_codec_param *attr )
+{
+    struct l16_data *data = (struct l16_data*) codec->codec_data;
+
+    pj_assert(data != NULL);
+
+    data->vad_enabled = (attr->setting.vad != 0);
+#if !PLC_DISABLED
+    data->plc_enabled = (attr->setting.plc != 0);
+#endif
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t  l16_parse( pjmedia_codec *codec,
+			       void *pkt,
+			       pj_size_t pkt_size,
+			       const pj_timestamp *ts,
+			       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[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
+	frames[count].buf = pkt;
+	frames[count].size = data->frame_size;
+	frames[count].timestamp.u64 = ts->u64 + (count * 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)
+{
+    struct l16_data *data = (struct l16_data*) codec->codec_data;
+    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_assert(data && input && output);
+
+    /* Check output buffer length */
+    if (output_buf_len < input->size)
+	return PJMEDIA_CODEC_EFRMTOOSHORT;
+
+    /* Detect silence */
+    if (data->vad_enabled) {
+	pj_bool_t is_silence;
+	pj_int32_t silence_duration;
+
+	silence_duration = pj_timestamp_diff32(&data->last_tx, 
+					       &input->timestamp);
+
+	is_silence = pjmedia_silence_det_detect(data->vad, 
+					        (const pj_int16_t*) input->buf,
+						(input->size >> 1),
+						NULL);
+	if (is_silence &&
+	    (PJMEDIA_CODEC_MAX_SILENCE_PERIOD == -1 ||
+	     silence_duration < PJMEDIA_CODEC_MAX_SILENCE_PERIOD*
+			        (int)data->clock_rate/1000))
+	{
+	    output->type = PJMEDIA_FRAME_TYPE_NONE;
+	    output->buf = NULL;
+	    output->size = 0;
+	    output->timestamp = input->timestamp;
+	    return PJ_SUCCESS;
+	} else {
+	    data->last_tx = input->timestamp;
+	}
+    }
+
+    /* Encode */
+#if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN!=0
+    while (samp!=samp_end)
+	*samp_out++ = pj_htons(*samp++);
+#else
+    pjmedia_copy_samples(samp_out, samp, input->size >> 1);
+#endif
+
+
+    /* Done */
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+    output->size = input->size;
+    output->timestamp = input->timestamp;
+
+    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)
+{
+    struct l16_data *l16_data = (struct l16_data*) codec->codec_data;
+    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_assert(l16_data != NULL);
+    PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
+
+
+    /* 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++);
+#else
+    pjmedia_copy_samples(samp_out, samp, input->size >> 1);
+#endif
+
+
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+    output->size = input->size;
+    output->timestamp = input->timestamp;
+
+#if !PLC_DISABLED
+    if (l16_data->plc_enabled)
+	pjmedia_plc_save( l16_data->plc, (pj_int16_t*)output->buf);
+#endif
+
+    return PJ_SUCCESS;
+}
+
+#if !PLC_DISABLED
+/*
+ * Recover lost frame.
+ */
+static pj_status_t  l16_recover(pjmedia_codec *codec,
+				      unsigned output_buf_len,
+				      struct pjmedia_frame *output)
+{
+    struct l16_data *data = (struct l16_data*) codec->codec_data;
+
+    PJ_ASSERT_RETURN(data->plc_enabled, PJ_EINVALIDOP);
+
+    PJ_ASSERT_RETURN(output_buf_len >= data->frame_size, 
+		     PJMEDIA_CODEC_EPCMTOOSHORT);
+
+    pjmedia_plc_generate(data->plc, (pj_int16_t*)output->buf);
+    output->size = data->frame_size;
+
+    return PJ_SUCCESS;
+}
+#endif
+
+#endif	/* PJMEDIA_HAS_L16_CODEC */
+
+
diff --git a/jni/pjproject-android/pjmedia/src/pjmedia-codec/opencore_amr.c b/jni/pjproject-android/pjmedia/src/pjmedia-codec/opencore_amr.c
new file mode 100644
index 0000000..82b6c0f
--- /dev/null
+++ b/jni/pjproject-android/pjmedia/src/pjmedia-codec/opencore_amr.c
@@ -0,0 +1,1035 @@
+/* $Id: opencore_amr.c 4487 2013-04-23 05:37:41Z bennylp $ */
+/* 
+ * Copyright (C) 2011-2013 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2011 Dan Arrhenius <dan@keystream.se>
+ *
+ * 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 
+ */
+
+/* 
+ * AMR codec implementation with OpenCORE AMR library
+ */
+#include <pjmedia-codec/g722.h>
+#include <pjmedia-codec/amr_sdp_match.h>
+#include <pjmedia/codec.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/endpoint.h>
+#include <pjmedia/plc.h>
+#include <pjmedia/port.h>
+#include <pjmedia/silencedet.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/os.h>
+#include <pj/math.h>
+
+#if defined(PJMEDIA_HAS_OPENCORE_AMRNB_CODEC) && \
+    (PJMEDIA_HAS_OPENCORE_AMRNB_CODEC != 0)
+#define USE_AMRNB
+#endif
+
+#if defined(PJMEDIA_HAS_OPENCORE_AMRWB_CODEC) && \
+    (PJMEDIA_HAS_OPENCORE_AMRWB_CODEC != 0)
+#define USE_AMRWB
+#endif
+
+#if defined(USE_AMRNB) || defined(USE_AMRWB)
+
+#ifdef USE_AMRNB
+#include <opencore-amrnb/interf_enc.h>
+#include <opencore-amrnb/interf_dec.h>
+#endif
+
+#ifdef USE_AMRWB
+#include <vo-amrwbenc/enc_if.h>
+#include <opencore-amrwb/dec_if.h>
+#endif
+
+#include <pjmedia-codec/amr_helper.h>
+#include <pjmedia-codec/opencore_amr.h>
+
+#define THIS_FILE "opencore_amr.c"
+
+/* Tracing */
+#define PJ_TRACE    0
+
+#if PJ_TRACE
+#   define TRACE_(expr)	PJ_LOG(4,expr)
+#else
+#   define TRACE_(expr)
+#endif
+
+/* Use PJMEDIA PLC */
+#define USE_PJMEDIA_PLC	    1
+
+#define FRAME_LENGTH_MS     20
+
+
+/* Prototypes for AMR factory */
+static pj_status_t amr_test_alloc(pjmedia_codec_factory *factory, 
+				   const pjmedia_codec_info *id );
+static pj_status_t amr_default_attr(pjmedia_codec_factory *factory, 
+				     const pjmedia_codec_info *id, 
+				     pjmedia_codec_param *attr );
+static pj_status_t amr_enum_codecs(pjmedia_codec_factory *factory, 
+				    unsigned *count, 
+				    pjmedia_codec_info codecs[]);
+static pj_status_t amr_alloc_codec(pjmedia_codec_factory *factory, 
+				    const pjmedia_codec_info *id, 
+				    pjmedia_codec **p_codec);
+static pj_status_t amr_dealloc_codec(pjmedia_codec_factory *factory, 
+				      pjmedia_codec *codec );
+
+/* Prototypes for AMR implementation. */
+static pj_status_t  amr_codec_init(pjmedia_codec *codec, 
+				    pj_pool_t *pool );
+static pj_status_t  amr_codec_open(pjmedia_codec *codec, 
+				    pjmedia_codec_param *attr );
+static pj_status_t  amr_codec_close(pjmedia_codec *codec );
+static pj_status_t  amr_codec_modify(pjmedia_codec *codec, 
+				      const pjmedia_codec_param *attr );
+static pj_status_t  amr_codec_parse(pjmedia_codec *codec,
+				     void *pkt,
+				     pj_size_t pkt_size,
+				     const pj_timestamp *ts,
+				     unsigned *frame_cnt,
+				     pjmedia_frame frames[]);
+static pj_status_t  amr_codec_encode(pjmedia_codec *codec, 
+				      const struct pjmedia_frame *input,
+				      unsigned output_buf_len, 
+				      struct pjmedia_frame *output);
+static pj_status_t  amr_codec_decode(pjmedia_codec *codec, 
+				      const struct pjmedia_frame *input,
+				      unsigned output_buf_len, 
+				      struct pjmedia_frame *output);
+static pj_status_t  amr_codec_recover(pjmedia_codec *codec,
+				      unsigned output_buf_len,
+				      struct pjmedia_frame *output);
+
+
+
+/* Definition for AMR codec operations. */
+static pjmedia_codec_op amr_op = 
+{
+    &amr_codec_init,
+    &amr_codec_open,
+    &amr_codec_close,
+    &amr_codec_modify,
+    &amr_codec_parse,
+    &amr_codec_encode,
+    &amr_codec_decode,
+    &amr_codec_recover
+};
+
+/* Definition for AMR codec factory operations. */
+static pjmedia_codec_factory_op amr_factory_op =
+{
+    &amr_test_alloc,
+    &amr_default_attr,
+    &amr_enum_codecs,
+    &amr_alloc_codec,
+    &amr_dealloc_codec,
+    &pjmedia_codec_opencore_amr_deinit
+};
+
+
+/* AMR factory */
+static struct amr_codec_factory
+{
+    pjmedia_codec_factory    base;
+    pjmedia_endpt	    *endpt;
+    pj_pool_t		    *pool;
+    pj_bool_t                init[2];
+} amr_codec_factory;
+
+
+/* AMR codec private data. */
+struct amr_data
+{
+    pj_pool_t		*pool;
+    unsigned             clock_rate;
+    void		*encoder;
+    void		*decoder;
+    pj_bool_t		 plc_enabled;
+    pj_bool_t		 vad_enabled;
+    int			 enc_mode;
+    pjmedia_codec_amr_pack_setting enc_setting;
+    pjmedia_codec_amr_pack_setting dec_setting;
+#if USE_PJMEDIA_PLC
+    pjmedia_plc		*plc;
+#endif
+    pj_timestamp	 last_tx;
+};
+
+/* Index for AMR tables. */
+enum
+{
+    IDX_AMR_NB,	/* Index for narrowband.    */
+    IDX_AMR_WB	/* Index for wideband.      */
+};
+
+static pjmedia_codec_amr_config def_config[2] =
+{{ /* AMR-NB */
+    PJ_FALSE,	    /* octet align	*/
+    5900	    /* bitrate		*/
+ },
+ { /* AMR-WB */
+    PJ_FALSE,	    /* octet align	*/
+    12650	    /* bitrate		*/
+ }};
+
+static const pj_uint16_t* amr_bitrates[2] =
+    {pjmedia_codec_amrnb_bitrates, pjmedia_codec_amrwb_bitrates};
+
+static const unsigned amr_bitrates_size[2] =
+{
+    PJ_ARRAY_SIZE(pjmedia_codec_amrnb_bitrates),
+    PJ_ARRAY_SIZE(pjmedia_codec_amrwb_bitrates)
+};
+
+
+/*
+ * Initialize and register AMR codec factory to pjmedia endpoint.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_opencore_amr_init( pjmedia_endpt *endpt,
+                                                     unsigned options)
+{
+    pjmedia_codec_mgr *codec_mgr;
+    pj_str_t codec_name;
+    pj_status_t status;
+
+    if (amr_codec_factory.pool != NULL)
+	return PJ_SUCCESS;
+
+    /* Create AMR codec factory. */
+    amr_codec_factory.base.op = &amr_factory_op;
+    amr_codec_factory.base.factory_data = NULL;
+    amr_codec_factory.endpt = endpt;
+#ifdef USE_AMRNB
+    amr_codec_factory.init[IDX_AMR_NB] = ((options & PJMEDIA_AMR_NO_NB) == 0);
+#else
+    amr_codec_factory.init[IDX_AMR_NB] = PJ_FALSE;
+#endif
+#ifdef USE_AMRWB
+    amr_codec_factory.init[IDX_AMR_WB] = ((options & PJMEDIA_AMR_NO_WB) == 0);
+#else
+    amr_codec_factory.init[IDX_AMR_WB] = PJ_FALSE;
+#endif
+
+    amr_codec_factory.pool = pjmedia_endpt_create_pool(endpt, "amr", 1000,
+						       1000);
+    if (!amr_codec_factory.pool)
+	return PJ_ENOMEM;
+
+    /* Get the codec manager. */
+    codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
+    if (!codec_mgr) {
+	status = PJ_EINVALIDOP;
+	goto on_error;
+    }
+
+    /* Register format match callback. */
+    pj_cstr(&codec_name, "AMR");
+    status = pjmedia_sdp_neg_register_fmt_match_cb(
+					&codec_name,
+					&pjmedia_codec_amr_match_sdp);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Register codec factory to endpoint. */
+    status = pjmedia_codec_mgr_register_factory(codec_mgr, 
+						&amr_codec_factory.base);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Done. */
+    return PJ_SUCCESS;
+
+on_error:
+    pj_pool_release(amr_codec_factory.pool);
+    amr_codec_factory.pool = NULL;
+    return status;
+}
+
+PJ_DEF(pj_status_t)
+pjmedia_codec_opencore_amr_init_default( pjmedia_endpt *endpt )
+{
+    return pjmedia_codec_opencore_amr_init(endpt, 0);
+}
+
+PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_init( pjmedia_endpt *endpt )
+{
+    return pjmedia_codec_opencore_amr_init(endpt, PJMEDIA_AMR_NO_WB);
+}
+
+
+/*
+ * Unregister AMR codec factory from pjmedia endpoint and deinitialize
+ * the AMR codec library.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_opencore_amr_deinit(void)
+{
+    pjmedia_codec_mgr *codec_mgr;
+    pj_status_t status;
+
+    amr_codec_factory.init[IDX_AMR_NB] = PJ_FALSE;
+    amr_codec_factory.init[IDX_AMR_WB] = PJ_FALSE;
+    
+    if (amr_codec_factory.pool == NULL)
+	return PJ_SUCCESS;
+
+    /* Get the codec manager. */
+    codec_mgr = pjmedia_endpt_get_codec_mgr(amr_codec_factory.endpt);
+    if (!codec_mgr) {
+	pj_pool_release(amr_codec_factory.pool);
+	amr_codec_factory.pool = NULL;
+	return PJ_EINVALIDOP;
+    }
+
+    /* Unregister AMR codec factory. */
+    status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
+						  &amr_codec_factory.base);
+    
+    /* Destroy pool. */
+    pj_pool_release(amr_codec_factory.pool);
+    amr_codec_factory.pool = NULL;
+    
+    return status;
+}
+
+PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_deinit(void)
+{
+    if (amr_codec_factory.init[IDX_AMR_NB] &&
+        amr_codec_factory.init[IDX_AMR_WB])
+    {
+        PJ_LOG(4, (THIS_FILE, "Should call "
+                              "pjmedia_codec_opencore_amr_deinit() instead"));
+        
+        return PJ_EINVALIDOP;
+    }
+    
+    return pjmedia_codec_opencore_amr_deinit();
+}
+
+static pj_status_t
+amr_set_config(unsigned idx, const pjmedia_codec_amr_config *config)
+{
+    unsigned nbitrates;
+
+    def_config[idx] = *config;
+
+    /* Normalize bitrate. */
+    nbitrates = amr_bitrates_size[idx];
+    if (def_config[idx].bitrate < amr_bitrates[idx][0]) {
+	def_config[idx].bitrate = amr_bitrates[idx][0];
+    } else if (def_config[idx].bitrate > amr_bitrates[idx][nbitrates-1]) {
+	def_config[idx].bitrate = amr_bitrates[idx][nbitrates-1];
+    } else
+    {
+	unsigned i;
+	
+	for (i = 0; i < nbitrates; ++i) {
+	    if (def_config[idx].bitrate <= amr_bitrates[idx][i])
+		break;
+	}
+	def_config[idx].bitrate = amr_bitrates[idx][i];
+    }
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_set_config(
+                                    const pjmedia_codec_amrnb_config *config)
+{
+    return amr_set_config(IDX_AMR_NB, (const pjmedia_codec_amr_config *)config);
+}
+
+PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrwb_set_config(
+                                    const pjmedia_codec_amrwb_config *config)
+{
+    return amr_set_config(IDX_AMR_WB, (const pjmedia_codec_amr_config *)config);
+}
+
+/*
+ * Check if factory can allocate the specified codec.
+ */
+static pj_status_t amr_test_alloc( pjmedia_codec_factory *factory, 
+				   const pjmedia_codec_info *info )
+{
+    const pj_str_t amr_tag = { "AMR", 3};
+    const pj_str_t amrwb_tag = { "AMR-WB", 6};
+    PJ_UNUSED_ARG(factory);
+
+    /* Type MUST be audio. */
+    if (info->type != PJMEDIA_TYPE_AUDIO)
+	return PJMEDIA_CODEC_EUNSUP;
+    
+    /* Check payload type. */
+    if (info->pt != PJMEDIA_RTP_PT_AMR && info->pt != PJMEDIA_RTP_PT_AMRWB)
+	return PJMEDIA_CODEC_EUNSUP;
+    
+    /* Check encoding name. */
+    if (pj_stricmp(&info->encoding_name, &amr_tag) != 0 &&
+        pj_stricmp(&info->encoding_name, &amrwb_tag) != 0)
+    {
+	return PJMEDIA_CODEC_EUNSUP;
+    }
+    
+    /* Check clock-rate */
+    if ((info->clock_rate == 8000 && amr_codec_factory.init[IDX_AMR_NB]) ||
+        (info->clock_rate == 16000 && amr_codec_factory.init[IDX_AMR_WB]))
+    {
+        return PJ_SUCCESS;
+    }
+
+    /* Unsupported or disabled. */
+    return PJMEDIA_CODEC_EUNSUP;
+}
+
+/*
+ * Generate default attribute.
+ */
+static pj_status_t amr_default_attr( pjmedia_codec_factory *factory, 
+				     const pjmedia_codec_info *id, 
+				     pjmedia_codec_param *attr )
+{
+    unsigned idx;
+    
+    PJ_UNUSED_ARG(factory);
+
+    idx = (id->clock_rate <= 8000? IDX_AMR_NB: IDX_AMR_WB);
+    pj_bzero(attr, sizeof(pjmedia_codec_param));
+    attr->info.clock_rate = (id->clock_rate <= 8000? 8000: 16000);
+    attr->info.channel_cnt = 1;
+    attr->info.avg_bps = def_config[idx].bitrate;
+    attr->info.max_bps = amr_bitrates[idx][amr_bitrates_size[idx]-1];
+    attr->info.pcm_bits_per_sample = 16;
+    attr->info.frm_ptime = 20;
+    attr->info.pt = (pj_uint8_t)id->pt;
+
+    attr->setting.frm_per_pkt = 1;
+    attr->setting.vad = 1;
+    attr->setting.plc = 1;
+
+    if (def_config[idx].octet_align) {
+	attr->setting.dec_fmtp.cnt = 1;
+	attr->setting.dec_fmtp.param[0].name = pj_str("octet-align");
+	attr->setting.dec_fmtp.param[0].val = pj_str("1");
+    }
+
+    /* Default all other flag bits disabled. */
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Enum codecs supported by this factory (i.e. AMR-NB and AMR-WB).
+ */
+static pj_status_t amr_enum_codecs( pjmedia_codec_factory *factory, 
+				    unsigned *count, 
+				    pjmedia_codec_info codecs[])
+{
+    PJ_UNUSED_ARG(factory);
+    PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
+
+    *count = 0;
+
+    if (amr_codec_factory.init[IDX_AMR_NB]) {
+        pj_bzero(&codecs[*count], sizeof(pjmedia_codec_info));
+        codecs[*count].encoding_name = pj_str("AMR");
+        codecs[*count].pt = PJMEDIA_RTP_PT_AMR;
+        codecs[*count].type = PJMEDIA_TYPE_AUDIO;
+        codecs[*count].clock_rate = 8000;
+        codecs[*count].channel_cnt = 1;
+        (*count)++;
+    }
+    
+    if (amr_codec_factory.init[IDX_AMR_WB]) {
+        pj_bzero(&codecs[*count], sizeof(pjmedia_codec_info));
+        codecs[*count].encoding_name = pj_str("AMR-WB");
+        codecs[*count].pt = PJMEDIA_RTP_PT_AMRWB;
+        codecs[*count].type = PJMEDIA_TYPE_AUDIO;
+        codecs[*count].clock_rate = 16000;
+        codecs[*count].channel_cnt = 1;
+        (*count)++;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Allocate a new AMR codec instance.
+ */
+static pj_status_t amr_alloc_codec( pjmedia_codec_factory *factory, 
+				    const pjmedia_codec_info *id,
+				    pjmedia_codec **p_codec)
+{
+    pj_pool_t *pool;
+    pjmedia_codec *codec;
+    struct amr_data *amr_data;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory == &amr_codec_factory.base, PJ_EINVAL);
+
+    pool = pjmedia_endpt_create_pool(amr_codec_factory.endpt, "amr-inst", 
+				     512, 512);
+
+    codec = PJ_POOL_ZALLOC_T(pool, pjmedia_codec);
+    PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM);
+    codec->op = &amr_op;
+    codec->factory = factory;
+
+    amr_data = PJ_POOL_ZALLOC_T(pool, struct amr_data);
+    codec->codec_data = amr_data;
+    amr_data->pool = pool;
+
+#if USE_PJMEDIA_PLC
+    /* Create PLC */
+    status = pjmedia_plc_create(pool, id->clock_rate,
+                                id->clock_rate * FRAME_LENGTH_MS / 1000, 0,
+                                &amr_data->plc);
+    if (status != PJ_SUCCESS) {
+	return status;
+    }
+#else
+    PJ_UNUSED_ARG(status);
+#endif
+    *p_codec = codec;
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Free codec.
+ */
+static pj_status_t amr_dealloc_codec( pjmedia_codec_factory *factory, 
+				      pjmedia_codec *codec )
+{
+    struct amr_data *amr_data;
+
+    PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory == &amr_codec_factory.base, PJ_EINVAL);
+
+    amr_data = (struct amr_data*) codec->codec_data;
+
+    /* Close codec, if it's not closed. */
+    amr_codec_close(codec);
+
+    pj_pool_release(amr_data->pool);
+    amr_data = NULL;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Init codec.
+ */
+static pj_status_t amr_codec_init( pjmedia_codec *codec, 
+				   pj_pool_t *pool )
+{
+    PJ_UNUSED_ARG(codec);
+    PJ_UNUSED_ARG(pool);
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Open codec.
+ */
+static pj_status_t amr_codec_open( pjmedia_codec *codec, 
+				   pjmedia_codec_param *attr )
+{
+    struct amr_data *amr_data = (struct amr_data*) codec->codec_data;
+    pjmedia_codec_amr_pack_setting *setting;
+    unsigned i;
+    pj_uint8_t octet_align = 0;
+    pj_int8_t enc_mode;
+    const pj_str_t STR_FMTP_OCTET_ALIGN = {"octet-align", 11};
+    unsigned idx;
+
+    PJ_ASSERT_RETURN(codec && attr, PJ_EINVAL);
+    PJ_ASSERT_RETURN(amr_data != NULL, PJ_EINVALIDOP);
+
+    idx = (attr->info.clock_rate <= 8000? IDX_AMR_NB: IDX_AMR_WB);
+    enc_mode = pjmedia_codec_amr_get_mode(attr->info.avg_bps);
+    pj_assert(enc_mode >= 0 && enc_mode < amr_bitrates_size[idx]);
+
+    /* Check octet-align */
+    for (i = 0; i < attr->setting.dec_fmtp.cnt; ++i) {
+	if (pj_stricmp(&attr->setting.dec_fmtp.param[i].name, 
+		       &STR_FMTP_OCTET_ALIGN) == 0)
+	{
+	    octet_align = (pj_uint8_t)
+			  (pj_strtoul(&attr->setting.dec_fmtp.param[i].val));
+	    break;
+	}
+    }
+
+    /* Check mode-set */
+    for (i = 0; i < attr->setting.enc_fmtp.cnt; ++i) {
+	const pj_str_t STR_FMTP_MODE_SET = {"mode-set", 8};
+        
+	if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name, 
+		       &STR_FMTP_MODE_SET) == 0)
+	{
+	    const char *p;
+	    pj_size_t l;
+	    pj_int8_t diff = 99;
+
+	    /* Encoding mode is chosen based on local default mode setting:
+	     * - if local default mode is included in the mode-set, use it
+	     * - otherwise, find the closest mode to local default mode;
+	     *   if there are two closest modes, prefer to use the higher
+	     *   one, e.g: local default mode is 4, the mode-set param
+	     *   contains '2,3,5,6', then 5 will be chosen.
+	     */
+	    p = pj_strbuf(&attr->setting.enc_fmtp.param[i].val);
+	    l = pj_strlen(&attr->setting.enc_fmtp.param[i].val);
+	    while (l--) {
+		if (*p>='0' && *p<=('0'+amr_bitrates_size[idx]-1)) {
+		    pj_int8_t tmp = *p - '0' - enc_mode;
+
+		    if (PJ_ABS(diff) > PJ_ABS(tmp) || 
+			(PJ_ABS(diff) == PJ_ABS(tmp) && tmp > diff))
+		    {
+			diff = tmp;
+			if (diff == 0) break;
+		    }
+		}
+		++p;
+	    }
+	    PJ_ASSERT_RETURN(diff != 99, PJMEDIA_CODEC_EFAILED);
+
+	    enc_mode = enc_mode + diff;
+
+	    break;
+	}
+    }
+
+    amr_data->clock_rate = attr->info.clock_rate;
+    amr_data->vad_enabled = (attr->setting.vad != 0);
+    amr_data->plc_enabled = (attr->setting.plc != 0);
+    amr_data->enc_mode = enc_mode;
+
+    if (idx == IDX_AMR_NB) {
+#ifdef USE_AMRNB
+        amr_data->encoder = Encoder_Interface_init(amr_data->vad_enabled);
+#endif
+    } else {
+#ifdef USE_AMRWB
+        amr_data->encoder = E_IF_init();
+#endif
+    }
+    if (amr_data->encoder == NULL) {
+	TRACE_((THIS_FILE, "Encoder initialization failed"));
+	amr_codec_close(codec);
+	return PJMEDIA_CODEC_EFAILED;
+    }
+    setting = &amr_data->enc_setting;
+    pj_bzero(setting, sizeof(pjmedia_codec_amr_pack_setting));
+    setting->amr_nb = (idx == IDX_AMR_NB? 1: 0);
+    setting->reorder = 0;
+    setting->octet_aligned = octet_align;
+    setting->cmr = 15;
+
+    if (idx == IDX_AMR_NB) {
+#ifdef USE_AMRNB
+        amr_data->decoder = Decoder_Interface_init();
+#endif
+    } else {
+#ifdef USE_AMRWB
+        amr_data->decoder = D_IF_init();
+#endif
+    }
+    if (amr_data->decoder == NULL) {
+	TRACE_((THIS_FILE, "Decoder initialization failed"));
+	amr_codec_close(codec);
+	return PJMEDIA_CODEC_EFAILED;
+    }
+    setting = &amr_data->dec_setting;
+    pj_bzero(setting, sizeof(pjmedia_codec_amr_pack_setting));
+    setting->amr_nb = (idx == IDX_AMR_NB? 1: 0);
+    setting->reorder = 0;
+    setting->octet_aligned = octet_align;
+
+    TRACE_((THIS_FILE, "AMR codec allocated: clockrate=%d vad=%d, plc=%d,"
+                       " bitrate=%d", amr_data->clock_rate,
+			amr_data->vad_enabled, amr_data->plc_enabled, 
+			amr_bitrates[idx][amr_data->enc_mode]));
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Close codec.
+ */
+static pj_status_t amr_codec_close( pjmedia_codec *codec )
+{
+    struct amr_data *amr_data;
+
+    PJ_ASSERT_RETURN(codec, PJ_EINVAL);
+
+    amr_data = (struct amr_data*) codec->codec_data;
+    PJ_ASSERT_RETURN(amr_data != NULL, PJ_EINVALIDOP);
+
+    if (amr_data->encoder) {
+        if (amr_data->enc_setting.amr_nb) {
+#ifdef USE_AMRNB
+            Encoder_Interface_exit(amr_data->encoder);
+#endif
+        } else {
+#ifdef USE_AMRWB
+            E_IF_exit(amr_data->encoder);
+#endif
+        }
+        amr_data->encoder = NULL;
+    }
+
+    if (amr_data->decoder) {
+        if (amr_data->dec_setting.amr_nb) {
+#ifdef USE_AMRNB
+            Decoder_Interface_exit(amr_data->decoder);
+#endif
+        } else {
+#ifdef USE_AMRWB
+            D_IF_exit(amr_data->decoder);
+#endif
+        }
+        amr_data->decoder = NULL;
+    }
+    
+    TRACE_((THIS_FILE, "AMR codec closed"));
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Modify codec settings.
+ */
+static pj_status_t amr_codec_modify( pjmedia_codec *codec, 
+				     const pjmedia_codec_param *attr )
+{
+    struct amr_data *amr_data = (struct amr_data*) codec->codec_data;
+    pj_bool_t prev_vad_state;
+
+    pj_assert(amr_data != NULL);
+    pj_assert(amr_data->encoder != NULL && amr_data->decoder != NULL);
+
+    prev_vad_state = amr_data->vad_enabled;
+    amr_data->vad_enabled = (attr->setting.vad != 0);
+    amr_data->plc_enabled = (attr->setting.plc != 0);
+
+    if (amr_data->enc_setting.amr_nb &&
+        prev_vad_state != amr_data->vad_enabled)
+    {
+	/* Reinit AMR encoder to update VAD setting */
+	TRACE_((THIS_FILE, "Reiniting AMR encoder to update VAD setting."));
+#ifdef USE_AMRNB
+        Encoder_Interface_exit(amr_data->encoder);
+        amr_data->encoder = Encoder_Interface_init(amr_data->vad_enabled);
+#endif
+        if (amr_data->encoder == NULL) {
+	    TRACE_((THIS_FILE, "Encoder_Interface_init() failed"));
+	    amr_codec_close(codec);
+	    return PJMEDIA_CODEC_EFAILED;
+	}
+    }
+
+    TRACE_((THIS_FILE, "AMR codec modified: vad=%d, plc=%d",
+			amr_data->vad_enabled, amr_data->plc_enabled));
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Get frames in the packet.
+ */
+static pj_status_t amr_codec_parse( pjmedia_codec *codec,
+				    void *pkt,
+				    pj_size_t pkt_size,
+				    const pj_timestamp *ts,
+				    unsigned *frame_cnt,
+				    pjmedia_frame frames[])
+{
+    struct amr_data *amr_data = (struct amr_data*) codec->codec_data;
+    pj_uint8_t cmr;
+    pj_status_t status;
+    unsigned idx = (amr_data->enc_setting.amr_nb? 0: 1);
+
+    status = pjmedia_codec_amr_parse(pkt, pkt_size, ts, &amr_data->dec_setting,
+				     frames, frame_cnt, &cmr);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Check for Change Mode Request. */
+    if (cmr < amr_bitrates_size[idx] && amr_data->enc_mode != cmr) {
+	amr_data->enc_mode = cmr;
+	TRACE_((THIS_FILE, "AMR encoder switched mode to %d (%dbps)",
+                amr_data->enc_mode, 
+                amr_bitrates[idx][amr_data->enc_mode]));
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Encode frame.
+ */
+static pj_status_t amr_codec_encode( pjmedia_codec *codec, 
+				     const struct pjmedia_frame *input,
+				     unsigned output_buf_len, 
+				     struct pjmedia_frame *output)
+{
+    struct amr_data *amr_data = (struct amr_data*) codec->codec_data;
+    unsigned char *bitstream;
+    pj_int16_t *speech;
+    unsigned nsamples, samples_per_frame;
+    enum {MAX_FRAMES_PER_PACKET = 16};
+    pjmedia_frame frames[MAX_FRAMES_PER_PACKET];
+    pj_uint8_t *p;
+    unsigned i, out_size = 0, nframes = 0;
+    pj_size_t payload_len;
+    unsigned dtx_cnt, sid_cnt;
+    pj_status_t status;
+
+    pj_assert(amr_data != NULL);
+    PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
+
+    nsamples = input->size >> 1;
+    samples_per_frame = amr_data->clock_rate * FRAME_LENGTH_MS / 1000;
+    PJ_ASSERT_RETURN(nsamples % samples_per_frame == 0, 
+		     PJMEDIA_CODEC_EPCMFRMINLEN);
+
+    nframes = nsamples / samples_per_frame;
+    PJ_ASSERT_RETURN(nframes <= MAX_FRAMES_PER_PACKET, 
+		     PJMEDIA_CODEC_EFRMTOOSHORT);
+
+    /* Encode the frames */
+    speech = (pj_int16_t*)input->buf;
+    bitstream = (unsigned char*)output->buf;
+    while (nsamples >= samples_per_frame) {
+	int size;
+        if (amr_data->enc_setting.amr_nb) {
+#ifdef USE_AMRNB
+            size = Encoder_Interface_Encode (amr_data->encoder,
+                                             amr_data->enc_mode,
+                                             speech, bitstream, 0);
+#else
+            size = 0;
+#endif
+        } else {
+#ifdef USE_AMRWB
+            size = E_IF_encode (amr_data->encoder, amr_data->enc_mode,
+                                speech, bitstream, 0);
+#else
+            size = 0;
+#endif
+        }
+	if (size == 0) {
+	    output->size = 0;
+	    output->buf = NULL;
+	    output->type = PJMEDIA_FRAME_TYPE_NONE;
+	    TRACE_((THIS_FILE, "AMR encode() failed"));
+	    return PJMEDIA_CODEC_EFAILED;
+	}
+	nsamples -= samples_per_frame;
+	speech += samples_per_frame;
+	bitstream += size;
+	out_size += size;
+	TRACE_((THIS_FILE, "AMR encode(): mode=%d, size=%d",
+		amr_data->enc_mode, out_size));
+    }
+
+    /* Pack payload */
+    p = (pj_uint8_t*)output->buf + output_buf_len - out_size;
+    pj_memmove(p, output->buf, out_size);
+    dtx_cnt = sid_cnt = 0;
+    for (i = 0; i < nframes; ++i) {
+	pjmedia_codec_amr_bit_info *info = (pjmedia_codec_amr_bit_info*)
+					   &frames[i].bit_info;
+	info->frame_type = (pj_uint8_t)((*p >> 3) & 0x0F);
+	info->good_quality = (pj_uint8_t)((*p >> 2) & 0x01);
+	info->mode = (pj_int8_t)amr_data->enc_mode;
+	info->start_bit = 0;
+	frames[i].buf = p + 1;
+        if (amr_data->enc_setting.amr_nb) {
+            frames[i].size = (info->frame_type <= 8)?
+                             pjmedia_codec_amrnb_framelen[info->frame_type] : 0;
+        } else {
+            frames[i].size = (info->frame_type <= 9)?
+                             pjmedia_codec_amrwb_framelen[info->frame_type] : 0;
+        }
+	p += frames[i].size + 1;
+
+	/* Count the number of SID and DTX frames */
+	if (info->frame_type == 15) /* DTX*/
+	    ++dtx_cnt;
+	else if (info->frame_type == 8) /* SID */
+	    ++sid_cnt;
+    }
+
+    /* VA generates DTX frames as DTX+SID frames switching quickly and it
+     * seems that the SID frames occur too often (assuming the purpose is 
+     * only for keeping NAT alive?). So let's modify the behavior a bit.
+     * Only an SID frame will be sent every PJMEDIA_CODEC_MAX_SILENCE_PERIOD
+     * milliseconds.
+     */
+    if (sid_cnt + dtx_cnt == nframes) {
+	pj_int32_t dtx_duration;
+
+	dtx_duration = pj_timestamp_diff32(&amr_data->last_tx, 
+					   &input->timestamp);
+	if (PJMEDIA_CODEC_MAX_SILENCE_PERIOD == -1 ||
+	    dtx_duration < PJMEDIA_CODEC_MAX_SILENCE_PERIOD*
+                           amr_data->clock_rate/1000)
+	{
+	    output->size = 0;
+	    output->type = PJMEDIA_FRAME_TYPE_NONE;
+	    output->timestamp = input->timestamp;
+	    return PJ_SUCCESS;
+	}
+    }
+
+    payload_len = output_buf_len;
+
+    status = pjmedia_codec_amr_pack(frames, nframes, &amr_data->enc_setting,
+				    output->buf, &payload_len);
+    if (status != PJ_SUCCESS) {
+	output->size = 0;
+	output->buf = NULL;
+	output->type = PJMEDIA_FRAME_TYPE_NONE;
+	TRACE_((THIS_FILE, "Failed to pack AMR payload, status=%d", status));
+	return status;
+    }
+
+    output->size = payload_len;
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+    output->timestamp = input->timestamp;
+
+    amr_data->last_tx = input->timestamp;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Decode frame.
+ */
+static pj_status_t amr_codec_decode( pjmedia_codec *codec, 
+				     const struct pjmedia_frame *input,
+				     unsigned output_buf_len, 
+				     struct pjmedia_frame *output)
+{
+    struct amr_data *amr_data = (struct amr_data*) codec->codec_data;
+    pjmedia_frame input_;
+    pjmedia_codec_amr_bit_info *info;
+    unsigned out_size;
+    /* AMR decoding buffer: AMR max frame size + 1 byte header. */
+    unsigned char bitstream[61];
+
+    pj_assert(amr_data != NULL);
+    PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
+
+    out_size = amr_data->clock_rate * FRAME_LENGTH_MS / 1000 * 2;
+    if (output_buf_len < out_size)
+	return PJMEDIA_CODEC_EPCMTOOSHORT;
+
+    input_.buf = &bitstream[1];
+    /* AMR max frame size */
+    input_.size = (amr_data->dec_setting.amr_nb? 31: 60);
+    pjmedia_codec_amr_predecode(input, &amr_data->dec_setting, &input_);
+    info = (pjmedia_codec_amr_bit_info*)&input_.bit_info;
+
+    /* VA AMR decoder requires frame info in the first byte. */
+    bitstream[0] = (info->frame_type << 3) | (info->good_quality << 2);
+
+    TRACE_((THIS_FILE, "AMR decode(): mode=%d, ft=%d, size=%d",
+	    info->mode, info->frame_type, input_.size));
+
+    /* Decode */
+    if (amr_data->dec_setting.amr_nb) {
+#ifdef USE_AMRNB
+        Decoder_Interface_Decode(amr_data->decoder, bitstream,
+                                 (pj_int16_t*)output->buf, 0);
+#endif
+    } else {
+#ifdef USE_AMRWB
+        D_IF_decode(amr_data->decoder, bitstream,
+                    (pj_int16_t*)output->buf, 0);
+#endif
+    }
+
+    output->size = out_size;
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+    output->timestamp = input->timestamp;
+
+#if USE_PJMEDIA_PLC
+    if (amr_data->plc_enabled)
+	pjmedia_plc_save(amr_data->plc, (pj_int16_t*)output->buf);
+#endif
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Recover lost frame.
+ */
+#if USE_PJMEDIA_PLC
+/*
+ * Recover lost frame.
+ */
+static pj_status_t  amr_codec_recover( pjmedia_codec *codec,
+				       unsigned output_buf_len,
+				       struct pjmedia_frame *output)
+{
+    struct amr_data *amr_data = codec->codec_data;
+    unsigned out_size = amr_data->clock_rate * FRAME_LENGTH_MS / 1000 * 2;
+
+    TRACE_((THIS_FILE, "amr_codec_recover"));
+
+    PJ_ASSERT_RETURN(amr_data->plc_enabled, PJ_EINVALIDOP);
+
+    PJ_ASSERT_RETURN(output_buf_len >= out_size,  PJMEDIA_CODEC_EPCMTOOSHORT);
+
+    pjmedia_plc_generate(amr_data->plc, (pj_int16_t*)output->buf);
+
+    output->size = out_size;
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+    
+    return PJ_SUCCESS;
+}
+#endif
+
+#if defined(_MSC_VER) && PJMEDIA_AUTO_LINK_OPENCORE_AMR_LIBS
+#   if PJMEDIA_OPENCORE_AMR_BUILT_WITH_GCC
+#       ifdef USE_AMRNB
+#           pragma comment( lib, "libopencore-amrnb.a")
+#       endif
+#       ifdef USE_AMRWB
+#           pragma comment( lib, "libopencore-amrwb.a")
+#           pragma comment( lib, "libvo-amrwbenc.a")
+#       endif
+#   else
+#       error Unsupported OpenCORE AMR library, fix here
+#   endif
+#endif
+
+#endif
diff --git a/jni/pjproject-android/pjmedia/src/pjmedia-codec/passthrough.c b/jni/pjproject-android/pjmedia/src/pjmedia-codec/passthrough.c
new file mode 100644
index 0000000..d315993
--- /dev/null
+++ b/jni/pjproject-android/pjmedia/src/pjmedia-codec/passthrough.c
@@ -0,0 +1,1054 @@
+/* $Id: passthrough.c 4082 2012-04-24 13:09:14Z bennylp $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjmedia-codec/passthrough.h>
+#include <pjmedia-codec/amr_sdp_match.h>
+#include <pjmedia/codec.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/endpoint.h>
+#include <pjmedia/port.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/math.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/os.h>
+
+/*
+ * Only build this file if PJMEDIA_HAS_PASSTHROUGH_CODECS != 0
+ */
+#if defined(PJMEDIA_HAS_PASSTHROUGH_CODECS) && PJMEDIA_HAS_PASSTHROUGH_CODECS!=0
+
+#define THIS_FILE   "passthrough.c"
+
+
+/* Prototypes for passthrough codecs factory */
+static pj_status_t test_alloc( pjmedia_codec_factory *factory, 
+			       const pjmedia_codec_info *id );
+static pj_status_t default_attr( pjmedia_codec_factory *factory, 
+				 const pjmedia_codec_info *id, 
+				 pjmedia_codec_param *attr );
+static pj_status_t enum_codecs( pjmedia_codec_factory *factory, 
+				unsigned *count, 
+				pjmedia_codec_info codecs[]);
+static pj_status_t alloc_codec( pjmedia_codec_factory *factory, 
+				const pjmedia_codec_info *id, 
+				pjmedia_codec **p_codec);
+static pj_status_t dealloc_codec( pjmedia_codec_factory *factory, 
+				  pjmedia_codec *codec );
+
+/* Prototypes for passthrough codecs implementation. */
+static pj_status_t codec_init( pjmedia_codec *codec, 
+			       pj_pool_t *pool );
+static pj_status_t codec_open( pjmedia_codec *codec, 
+			       pjmedia_codec_param *attr );
+static pj_status_t codec_close( pjmedia_codec *codec );
+static pj_status_t codec_modify(pjmedia_codec *codec, 
+			        const pjmedia_codec_param *attr );
+static pj_status_t codec_parse( pjmedia_codec *codec,
+			        void *pkt,
+				pj_size_t pkt_size,
+				const pj_timestamp *ts,
+				unsigned *frame_cnt,
+				pjmedia_frame frames[]);
+static pj_status_t codec_encode( pjmedia_codec *codec, 
+				 const struct pjmedia_frame *input,
+				 unsigned output_buf_len,
+				 struct pjmedia_frame *output);
+static pj_status_t codec_decode( pjmedia_codec *codec,
+				 const struct pjmedia_frame *input,
+				 unsigned output_buf_len, 
+				 struct pjmedia_frame *output);
+static pj_status_t codec_recover( pjmedia_codec *codec, 
+				  unsigned output_buf_len, 
+				  struct pjmedia_frame *output);
+
+/* Definition for passthrough codecs operations. */
+static pjmedia_codec_op codec_op = 
+{
+    &codec_init,
+    &codec_open,
+    &codec_close,
+    &codec_modify,
+    &codec_parse,
+    &codec_encode,
+    &codec_decode,
+    &codec_recover
+};
+
+/* Definition for passthrough codecs factory operations. */
+static pjmedia_codec_factory_op codec_factory_op =
+{
+    &test_alloc,
+    &default_attr,
+    &enum_codecs,
+    &alloc_codec,
+    &dealloc_codec,
+    &pjmedia_codec_passthrough_deinit
+};
+
+/* Passthrough codecs factory */
+static struct codec_factory {
+    pjmedia_codec_factory    base;
+    pjmedia_endpt	    *endpt;
+    pj_pool_t		    *pool;
+    pj_mutex_t		    *mutex;
+} codec_factory;
+
+/* Passthrough codecs private data. */
+typedef struct codec_private {
+    pj_pool_t		*pool;		    /**< Pool for each instance.    */
+    int			 codec_idx;	    /**< Codec index.		    */
+    void		*codec_setting;	    /**< Specific codec setting.    */
+    pj_uint16_t		 avg_frame_size;    /**< Average of frame size.	    */
+    unsigned		 samples_per_frame; /**< Samples per frame, for iLBC
+						 this can be 240 or 160, can
+						 only be known after codec is
+						 opened.		    */
+} codec_private_t;
+
+
+
+/* CUSTOM CALLBACKS */
+
+/* Parse frames from a packet. Default behaviour of frame parsing is 
+ * just separating frames based on calculating frame length derived 
+ * from bitrate. Implement this callback when the default behaviour is 
+ * unapplicable.
+ */
+typedef pj_status_t (*parse_cb)(codec_private_t *codec_data, void *pkt, 
+				pj_size_t pkt_size, const pj_timestamp *ts,
+				unsigned *frame_cnt, pjmedia_frame frames[]);
+
+/* Pack frames into a packet. Default behaviour of packing frames is 
+ * just stacking the frames with octet aligned without adding any 
+ * payload header. Implement this callback when the default behaviour is
+ * unapplicable.
+ */
+typedef pj_status_t (*pack_cb)(codec_private_t *codec_data, 
+			       const struct pjmedia_frame_ext *input,
+			       unsigned output_buf_len, 
+			       struct pjmedia_frame *output);
+
+
+/* Custom callback implementations. */
+static pj_status_t parse_amr( codec_private_t *codec_data, void *pkt, 
+			      pj_size_t pkt_size, const pj_timestamp *ts,
+			      unsigned *frame_cnt, pjmedia_frame frames[]);
+static pj_status_t pack_amr ( codec_private_t *codec_data,
+			      const struct pjmedia_frame_ext *input,
+			      unsigned output_buf_len, 
+			      struct pjmedia_frame *output);
+
+
+/* Passthrough codec implementation descriptions. */
+static struct codec_desc {
+    int		     enabled;		/* Is this codec enabled?	    */
+    const char	    *name;		/* Codec name.			    */
+    pj_uint8_t	     pt;		/* Payload type.		    */
+    pjmedia_format_id fmt_id;		/* Source format.		    */
+    unsigned	     clock_rate;	/* Codec's clock rate.		    */
+    unsigned	     channel_count;	/* Codec's channel count.	    */
+    unsigned	     samples_per_frame;	/* Codec's samples count.	    */
+    unsigned	     def_bitrate;	/* Default bitrate of this codec.   */
+    unsigned	     max_bitrate;	/* Maximum bitrate of this codec.   */
+    pj_uint8_t	     frm_per_pkt;	/* Default num of frames per packet.*/
+    pj_uint8_t	     vad;		/* VAD enabled/disabled.	    */
+    pj_uint8_t	     plc;		/* PLC enabled/disabled.	    */
+    parse_cb	     parse;		/* Callback to parse bitstream.	    */
+    pack_cb	     pack;		/* Callback to pack bitstream.	    */
+    pjmedia_codec_fmtp dec_fmtp;	/* Decoder's fmtp params.	    */
+}
+
+codec_desc[] = 
+{
+#   if PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR
+    {1, "AMR",	    PJMEDIA_RTP_PT_AMR,       PJMEDIA_FORMAT_AMR,
+		    8000, 1, 160, 
+		    7400, 12200, 2, 1, 1,
+		    &parse_amr, &pack_amr
+		    /*, {1, {{{"octet-align", 11}, {"1", 1}}} } */
+    },
+#   endif
+
+#   if PJMEDIA_HAS_PASSTHROUGH_CODEC_G729
+    {1, "G729",	    PJMEDIA_RTP_PT_G729,      PJMEDIA_FORMAT_G729,
+		    8000, 1,  80,
+		    8000, 8000, 2, 1, 1
+    },
+#   endif
+
+#   if PJMEDIA_HAS_PASSTHROUGH_CODEC_ILBC
+    {1, "iLBC",	    PJMEDIA_RTP_PT_ILBC,      PJMEDIA_FORMAT_ILBC,
+		    8000, 1,  240,
+		    13333, 15200, 1, 1, 1,
+		    NULL, NULL,
+		    {1, {{{"mode", 4}, {"30", 2}}} }
+    },
+#   endif
+
+#   if PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMU
+    {1, "PCMU",	    PJMEDIA_RTP_PT_PCMU,      PJMEDIA_FORMAT_PCMU,
+		    8000, 1,  80,
+		    64000, 64000, 2, 1, 1
+    },
+#   endif
+
+#   if PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMA
+    {1, "PCMA",	    PJMEDIA_RTP_PT_PCMA,      PJMEDIA_FORMAT_PCMA,
+		    8000, 1,  80,
+		    64000, 64000, 2, 1, 1
+    },
+#   endif
+};
+
+
+#if PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR
+
+#include <pjmedia-codec/amr_helper.h>
+
+typedef struct amr_settings_t {
+    pjmedia_codec_amr_pack_setting enc_setting;
+    pjmedia_codec_amr_pack_setting dec_setting;
+    pj_int8_t enc_mode;
+} amr_settings_t;
+
+
+/* Pack AMR payload */
+static pj_status_t pack_amr ( codec_private_t *codec_data,
+			      const struct pjmedia_frame_ext *input,
+			      unsigned output_buf_len, 
+			      struct pjmedia_frame *output)
+{
+    enum {MAX_FRAMES_PER_PACKET = PJMEDIA_MAX_FRAME_DURATION_MS / 20};
+
+    pjmedia_frame frames[MAX_FRAMES_PER_PACKET];
+    amr_settings_t* setting = (amr_settings_t*)codec_data->codec_setting;
+    pjmedia_codec_amr_pack_setting *enc_setting = &setting->enc_setting;
+    pj_uint8_t SID_FT;
+    unsigned i;
+
+    pj_assert(input->subframe_cnt <= MAX_FRAMES_PER_PACKET);
+
+    SID_FT = (pj_uint8_t)(enc_setting->amr_nb? 8 : 9);
+
+    /* Get frames */
+    for (i = 0; i < input->subframe_cnt; ++i) {
+	pjmedia_frame_ext_subframe *sf;
+	pjmedia_codec_amr_bit_info *info;
+	unsigned len;
+	
+	sf = pjmedia_frame_ext_get_subframe(input, i);
+	len = (sf->bitlen + 7) >> 3;
+	
+	info = (pjmedia_codec_amr_bit_info*) &frames[i].bit_info;
+	pj_bzero(info, sizeof(*info));
+	
+	if (len == 0) {
+	    /* DTX */
+	    info->frame_type = 15;
+	} else {
+	    info->frame_type = pjmedia_codec_amr_get_mode2(enc_setting->amr_nb, 
+							   len);
+	}
+	info->good_quality = 1;
+	info->mode = setting->enc_mode;
+	if (info->frame_type == SID_FT)
+	    info->STI = (sf->data[4] >> 4) & 1;
+
+	frames[i].buf = sf->data;
+	frames[i].size = len;
+    }
+
+    output->size = output_buf_len;
+
+    return pjmedia_codec_amr_pack(frames, input->subframe_cnt, enc_setting, 
+				  output->buf, &output->size);
+}
+
+
+/* Parse AMR payload into frames. */
+static pj_status_t parse_amr(codec_private_t *codec_data, void *pkt, 
+			     pj_size_t pkt_size, const pj_timestamp *ts,
+			     unsigned *frame_cnt, pjmedia_frame frames[])
+{
+    amr_settings_t* s = (amr_settings_t*)codec_data->codec_setting;
+    pjmedia_codec_amr_pack_setting *setting;
+    pj_status_t status;
+    pj_uint8_t cmr;
+
+    setting = &s->dec_setting;
+
+    status = pjmedia_codec_amr_parse(pkt, pkt_size, ts, setting, frames, 
+				     frame_cnt, &cmr);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    // CMR is not supported for now. 
+    /* Check Change Mode Request. */
+    //if ((setting->amr_nb && cmr <= 7) || (!setting->amr_nb && cmr <= 8)) {
+    //	s->enc_mode = cmr;
+    //}
+
+    return PJ_SUCCESS;
+}
+
+#endif /* PJMEDIA_HAS_PASSTROUGH_CODEC_AMR */
+
+
+/*
+ * Initialize and register passthrough codec factory to pjmedia endpoint.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_passthrough_init( pjmedia_endpt *endpt )
+{
+    pjmedia_codec_mgr *codec_mgr;
+    pj_str_t codec_name;
+    pj_status_t status;
+
+    if (codec_factory.pool != NULL) {
+	/* Already initialized. */
+	return PJ_EEXISTS;
+    }
+
+    /* Create passthrough codec factory. */
+    codec_factory.base.op = &codec_factory_op;
+    codec_factory.base.factory_data = NULL;
+    codec_factory.endpt = endpt;
+
+    codec_factory.pool = pjmedia_endpt_create_pool(endpt, "Passthrough codecs",
+						   4000, 4000);
+    if (!codec_factory.pool)
+	return PJ_ENOMEM;
+
+    /* Create mutex. */
+    status = pj_mutex_create_simple(codec_factory.pool, "Passthrough codecs",
+				    &codec_factory.mutex);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Get the codec manager. */
+    codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
+    if (!codec_mgr) {
+	status = PJ_EINVALIDOP;
+	goto on_error;
+    }
+
+    /* Register format match callback. */
+#if PJMEDIA_HAS_PASSTROUGH_CODEC_AMR
+    pj_cstr(&codec_name, "AMR");
+    status = pjmedia_sdp_neg_register_fmt_match_cb(
+					&codec_name,
+					&pjmedia_codec_amr_match_sdp);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+#endif
+
+    /* Suppress compile warning */
+    PJ_UNUSED_ARG(codec_name);
+
+    /* Register codec factory to endpoint. */
+    status = pjmedia_codec_mgr_register_factory(codec_mgr, 
+						&codec_factory.base);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Done. */
+    return PJ_SUCCESS;
+
+on_error:
+    pj_pool_release(codec_factory.pool);
+    codec_factory.pool = NULL;
+    return status;
+}
+
+/*
+ * Initialize and register passthrough codec factory to pjmedia endpoint.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_passthrough_init2( 
+		      pjmedia_endpt *endpt,
+		      const pjmedia_codec_passthrough_setting *setting)
+{
+    if (codec_factory.pool != NULL) {
+	/* Already initialized. */
+	return PJ_EEXISTS;
+    }
+
+    if (setting != NULL) {
+	unsigned i;
+
+	/* Enable/disable codecs based on the specified encoding formats */
+	for (i = 0; i < PJ_ARRAY_SIZE(codec_desc); ++i) {
+	    pj_bool_t enabled = PJ_FALSE;
+	    unsigned j;
+
+	    for (j = 0; j < setting->fmt_cnt && !enabled; ++j) {
+		if ((pj_uint32_t)codec_desc[i].fmt_id == setting->fmts[j].id)
+		    enabled = PJ_TRUE;
+	    }
+
+	    codec_desc[i].enabled = enabled;
+	}
+
+#if PJMEDIA_HAS_PASSTHROUGH_CODEC_ILBC
+	/* Update iLBC codec description based on default mode setting. */
+	for (i = 0; i < PJ_ARRAY_SIZE(codec_desc); ++i) {
+	    if (codec_desc[i].enabled && 
+		codec_desc[i].fmt_id == PJMEDIA_FORMAT_ILBC)
+	    {
+		codec_desc[i].samples_per_frame = 
+				(setting->ilbc_mode == 20? 160 : 240);
+		codec_desc[i].def_bitrate = 
+				(setting->ilbc_mode == 20? 15200 : 13333);
+		pj_strset2(&codec_desc[i].dec_fmtp.param[0].val, 
+				(setting->ilbc_mode == 20? "20" : "30"));
+		break;
+	    }
+	}
+#endif
+    }
+
+    return pjmedia_codec_passthrough_init(endpt);
+}
+
+/*
+ * Unregister passthrough codecs factory from pjmedia endpoint.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_passthrough_deinit(void)
+{
+    pjmedia_codec_mgr *codec_mgr;
+    unsigned i;
+    pj_status_t status;
+
+    if (codec_factory.pool == NULL) {
+	/* Already deinitialized */
+	return PJ_SUCCESS;
+    }
+
+    pj_mutex_lock(codec_factory.mutex);
+
+    /* Get the codec manager. */
+    codec_mgr = pjmedia_endpt_get_codec_mgr(codec_factory.endpt);
+    if (!codec_mgr) {
+	pj_pool_release(codec_factory.pool);
+	codec_factory.pool = NULL;
+	return PJ_EINVALIDOP;
+    }
+
+    /* Unregister passthrough codecs factory. */
+    status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
+						  &codec_factory.base);
+    
+    /* Destroy mutex. */
+    pj_mutex_destroy(codec_factory.mutex);
+
+    /* Destroy pool. */
+    pj_pool_release(codec_factory.pool);
+    codec_factory.pool = NULL;
+
+    /* Re-enable all codecs in the codec_desc. */
+    for (i = 0; i < PJ_ARRAY_SIZE(codec_desc); ++i) {
+	codec_desc[i].enabled = PJ_TRUE;
+    }
+
+    return status;
+}
+
+/* 
+ * Check if factory can allocate the specified codec. 
+ */
+static pj_status_t test_alloc( pjmedia_codec_factory *factory, 
+			       const pjmedia_codec_info *info )
+{
+    unsigned i;
+
+    PJ_UNUSED_ARG(factory);
+
+    /* Type MUST be audio. */
+    if (info->type != PJMEDIA_TYPE_AUDIO)
+	return PJMEDIA_CODEC_EUNSUP;
+
+    for (i = 0; i < PJ_ARRAY_SIZE(codec_desc); ++i) {
+	pj_str_t name = pj_str((char*)codec_desc[i].name);
+	if ((pj_stricmp(&info->encoding_name, &name) == 0) &&
+	    (info->clock_rate == (unsigned)codec_desc[i].clock_rate) &&
+	    (info->channel_cnt == (unsigned)codec_desc[i].channel_count) &&
+	    (codec_desc[i].enabled))
+	{
+	    return PJ_SUCCESS;
+	}
+    }
+    
+    /* Unsupported, or mode is disabled. */
+    return PJMEDIA_CODEC_EUNSUP;
+}
+
+/*
+ * Generate default attribute.
+ */
+static pj_status_t default_attr ( pjmedia_codec_factory *factory, 
+				  const pjmedia_codec_info *id, 
+				  pjmedia_codec_param *attr )
+{
+    unsigned i;
+
+    PJ_ASSERT_RETURN(factory==&codec_factory.base, PJ_EINVAL);
+
+    pj_bzero(attr, sizeof(pjmedia_codec_param));
+
+    for (i = 0; i < PJ_ARRAY_SIZE(codec_desc); ++i) {
+	pj_str_t name = pj_str((char*)codec_desc[i].name);
+	if ((pj_stricmp(&id->encoding_name, &name) == 0) &&
+	    (id->clock_rate == (unsigned)codec_desc[i].clock_rate) &&
+	    (id->channel_cnt == (unsigned)codec_desc[i].channel_count) &&
+	    (id->pt == (unsigned)codec_desc[i].pt))
+	{
+	    attr->info.pt = (pj_uint8_t)id->pt;
+	    attr->info.channel_cnt = codec_desc[i].channel_count;
+	    attr->info.clock_rate = codec_desc[i].clock_rate;
+	    attr->info.avg_bps = codec_desc[i].def_bitrate;
+	    attr->info.max_bps = codec_desc[i].max_bitrate;
+	    attr->info.pcm_bits_per_sample = 16;
+	    attr->info.frm_ptime =  (pj_uint16_t)
+				    (codec_desc[i].samples_per_frame * 1000 / 
+				    codec_desc[i].channel_count / 
+				    codec_desc[i].clock_rate);
+	    attr->info.fmt_id = codec_desc[i].fmt_id;
+
+	    /* Default flags. */
+	    attr->setting.frm_per_pkt = codec_desc[i].frm_per_pkt;
+	    attr->setting.plc = codec_desc[i].plc;
+	    attr->setting.penh= 0;
+	    attr->setting.vad = codec_desc[i].vad;
+	    attr->setting.cng = attr->setting.vad;
+	    attr->setting.dec_fmtp = codec_desc[i].dec_fmtp;
+
+	    if (attr->setting.vad == 0) {
+#if PJMEDIA_HAS_PASSTHROUGH_CODEC_G729
+		if (id->pt == PJMEDIA_RTP_PT_G729) {
+		    /* Signal G729 Annex B is being disabled */
+		    attr->setting.dec_fmtp.cnt = 1;
+		    pj_strset2(&attr->setting.dec_fmtp.param[0].name, "annexb");
+		    pj_strset2(&attr->setting.dec_fmtp.param[0].val, "no");
+		}
+#endif
+	    }
+
+	    return PJ_SUCCESS;
+	}
+    }
+
+    return PJMEDIA_CODEC_EUNSUP;
+}
+
+/*
+ * Enum codecs supported by this factory.
+ */
+static pj_status_t enum_codecs( pjmedia_codec_factory *factory, 
+				unsigned *count, 
+				pjmedia_codec_info codecs[])
+{
+    unsigned max;
+    unsigned i;
+
+    PJ_UNUSED_ARG(factory);
+    PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
+
+    max = *count;
+    
+    for (i = 0, *count = 0; i < PJ_ARRAY_SIZE(codec_desc) && *count < max; ++i) 
+    {
+	if (!codec_desc[i].enabled)
+	    continue;
+
+	pj_bzero(&codecs[*count], sizeof(pjmedia_codec_info));
+	codecs[*count].encoding_name = pj_str((char*)codec_desc[i].name);
+	codecs[*count].pt = codec_desc[i].pt;
+	codecs[*count].type = PJMEDIA_TYPE_AUDIO;
+	codecs[*count].clock_rate = codec_desc[i].clock_rate;
+	codecs[*count].channel_cnt = codec_desc[i].channel_count;
+
+	++*count;
+    }
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Allocate a new codec instance.
+ */
+static pj_status_t alloc_codec( pjmedia_codec_factory *factory, 
+				const pjmedia_codec_info *id,
+				pjmedia_codec **p_codec)
+{
+    codec_private_t *codec_data;
+    pjmedia_codec *codec;
+    int idx;
+    pj_pool_t *pool;
+    unsigned i;
+
+    PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory == &codec_factory.base, PJ_EINVAL);
+
+    pj_mutex_lock(codec_factory.mutex);
+
+    /* Find codec's index */
+    idx = -1;
+    for (i = 0; i < PJ_ARRAY_SIZE(codec_desc); ++i) {
+	pj_str_t name = pj_str((char*)codec_desc[i].name);
+	if ((pj_stricmp(&id->encoding_name, &name) == 0) &&
+	    (id->clock_rate == (unsigned)codec_desc[i].clock_rate) &&
+	    (id->channel_cnt == (unsigned)codec_desc[i].channel_count) &&
+	    (codec_desc[i].enabled))
+	{
+	    idx = i;
+	    break;
+	}
+    }
+    if (idx == -1) {
+	*p_codec = NULL;
+	return PJMEDIA_CODEC_EUNSUP;
+    }
+
+    /* Create pool for codec instance */
+    pool = pjmedia_endpt_create_pool(codec_factory.endpt, "passthroughcodec",
+				     512, 512);
+    codec = PJ_POOL_ZALLOC_T(pool, pjmedia_codec);
+    codec->op = &codec_op;
+    codec->factory = factory;
+    codec->codec_data = PJ_POOL_ZALLOC_T(pool, codec_private_t);
+    codec_data = (codec_private_t*) codec->codec_data;
+    codec_data->pool = pool;
+    codec_data->codec_idx = idx;
+
+    pj_mutex_unlock(codec_factory.mutex);
+
+    *p_codec = codec;
+    return PJ_SUCCESS;
+}
+
+/*
+ * Free codec.
+ */
+static pj_status_t dealloc_codec( pjmedia_codec_factory *factory, 
+				  pjmedia_codec *codec )
+{
+    codec_private_t *codec_data;
+
+    PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory == &codec_factory.base, PJ_EINVAL);
+
+    /* Close codec, if it's not closed. */
+    codec_data = (codec_private_t*) codec->codec_data;
+    codec_close(codec);
+
+    pj_pool_release(codec_data->pool);
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Init codec.
+ */
+static pj_status_t codec_init( pjmedia_codec *codec, 
+			       pj_pool_t *pool )
+{
+    PJ_UNUSED_ARG(codec);
+    PJ_UNUSED_ARG(pool);
+    return PJ_SUCCESS;
+}
+
+/*
+ * Open codec.
+ */
+static pj_status_t codec_open( pjmedia_codec *codec, 
+			       pjmedia_codec_param *attr )
+{
+    codec_private_t *codec_data = (codec_private_t*) codec->codec_data;
+    struct codec_desc *desc = &codec_desc[codec_data->codec_idx];
+    pj_pool_t *pool;
+    int i, j;
+
+    pool = codec_data->pool;
+
+    /* Cache samples per frame value */
+    codec_data->samples_per_frame = desc->samples_per_frame;
+
+    /* Calculate bitstream size */
+    i = attr->info.avg_bps * codec_data->samples_per_frame;
+    j = desc->clock_rate << 3;
+    codec_data->avg_frame_size = (pj_uint16_t)(i / j);
+    if (i % j) ++codec_data->avg_frame_size;
+
+#if PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR
+    /* Init AMR settings */
+    if (desc->pt == PJMEDIA_RTP_PT_AMR || desc->pt == PJMEDIA_RTP_PT_AMRWB) {
+	amr_settings_t *s;
+	pj_uint8_t octet_align = 0;
+	pj_int8_t enc_mode;
+	
+	enc_mode = pjmedia_codec_amr_get_mode(attr->info.avg_bps);
+	pj_assert(enc_mode >= 0 && enc_mode <= 8);
+
+	for (i = 0; i < attr->setting.dec_fmtp.cnt; ++i) {
+	    const pj_str_t STR_FMTP_OCTET_ALIGN = {"octet-align", 11};
+	    
+	    /* Fetch octet-align setting. It should be fine to fetch only 
+	     * the decoder, since encoder & decoder must use the same setting 
+	     * (RFC 4867 section 8.3.1).
+	     */
+	    if (pj_stricmp(&attr->setting.dec_fmtp.param[i].name, 
+			   &STR_FMTP_OCTET_ALIGN) == 0)
+	    {
+		octet_align=(pj_uint8_t)
+			    (pj_strtoul(&attr->setting.dec_fmtp.param[i].val));
+		break;
+	    }
+	}
+
+	for (i = 0; i < attr->setting.enc_fmtp.cnt; ++i) {
+	    const pj_str_t STR_FMTP_MODE_SET = {"mode-set", 8};
+
+	    /* mode-set, encoding mode is chosen based on local default mode 
+	     * setting:
+	     * - if local default mode is included in the mode-set, use it
+	     * - otherwise, find the closest mode to local default mode;
+	     *   if there are two closest modes, prefer to use the higher
+	     *   one, e.g: local default mode is 4, the mode-set param
+	     *   contains '2,3,5,6', then 5 will be chosen.
+	     */
+	    if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name, 
+			   &STR_FMTP_MODE_SET) == 0)
+	    {
+		const char *p;
+		pj_size_t l;
+		pj_int8_t diff = 99;
+		
+		p = pj_strbuf(&attr->setting.enc_fmtp.param[i].val);
+		l = pj_strlen(&attr->setting.enc_fmtp.param[i].val);
+
+		while (l--) {
+		    if ((desc->pt==PJMEDIA_RTP_PT_AMR && *p>='0' && *p<='7') ||
+		        (desc->pt==PJMEDIA_RTP_PT_AMRWB && *p>='0' && *p<='8'))
+		    {
+			pj_int8_t tmp = (pj_int8_t)(*p - '0' - enc_mode);
+
+			if (PJ_ABS(diff) > PJ_ABS(tmp) || 
+			    (PJ_ABS(diff) == PJ_ABS(tmp) && tmp > diff))
+			{
+			    diff = tmp;
+			    if (diff == 0) break;
+			}
+		    }
+		    ++p;
+		}
+
+		if (diff == 99)
+		    return PJMEDIA_CODEC_EFAILED;
+
+		enc_mode = (pj_int8_t)(enc_mode + diff);
+
+		break;
+	    }
+	}
+
+	s = PJ_POOL_ZALLOC_T(pool, amr_settings_t);
+	codec_data->codec_setting = s;
+
+	s->enc_mode = enc_mode;
+	if (s->enc_mode < 0)
+	    return PJMEDIA_CODEC_EINMODE;
+
+	s->enc_setting.amr_nb = (pj_uint8_t)(desc->pt == PJMEDIA_RTP_PT_AMR);
+	s->enc_setting.octet_aligned = octet_align;
+	s->enc_setting.reorder = PJ_FALSE; /* Note this! passthrough codec
+					      doesn't do sensitivity bits 
+					      reordering */
+	s->enc_setting.cmr = 15;
+	
+	s->dec_setting.amr_nb = (pj_uint8_t)(desc->pt == PJMEDIA_RTP_PT_AMR);
+	s->dec_setting.octet_aligned = octet_align;
+	s->dec_setting.reorder = PJ_FALSE; /* Note this! passthrough codec
+					      doesn't do sensitivity bits 
+					      reordering */
+	
+	/* Return back bitrate info to application */
+	attr->info.avg_bps = s->enc_setting.amr_nb?
+			     pjmedia_codec_amrnb_bitrates[s->enc_mode]:
+			     pjmedia_codec_amrwb_bitrates[s->enc_mode];
+    }
+#endif
+
+#if PJMEDIA_HAS_PASSTHROUGH_CODEC_ILBC
+    /* Init iLBC settings */
+    if (desc->pt == PJMEDIA_RTP_PT_ILBC)
+    {
+	enum { DEFAULT_MODE = 30 };
+	static pj_str_t STR_MODE = {"mode", 4};
+	pj_uint16_t dec_fmtp_mode = DEFAULT_MODE, 
+		    enc_fmtp_mode = DEFAULT_MODE;
+
+	/* Get decoder mode */
+	for (i = 0; i < attr->setting.dec_fmtp.cnt; ++i) {
+	    if (pj_stricmp(&attr->setting.dec_fmtp.param[i].name, &STR_MODE) == 0)
+	    {
+		dec_fmtp_mode = (pj_uint16_t)
+				pj_strtoul(&attr->setting.dec_fmtp.param[i].val);
+		break;
+	    }
+	}
+
+	/* Decoder mode must be set */
+	PJ_ASSERT_RETURN(dec_fmtp_mode == 20 || dec_fmtp_mode == 30, 
+			 PJMEDIA_CODEC_EINMODE);
+
+	/* Get encoder mode */
+	for (i = 0; i < attr->setting.enc_fmtp.cnt; ++i) {
+	    if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name, &STR_MODE) == 0)
+	    {
+		enc_fmtp_mode = (pj_uint16_t)
+				pj_strtoul(&attr->setting.enc_fmtp.param[i].val);
+		break;
+	    }
+	}
+
+	PJ_ASSERT_RETURN(enc_fmtp_mode==20 || enc_fmtp_mode==30, 
+			 PJMEDIA_CODEC_EINMODE);
+
+	/* Both sides of a bi-directional session MUST use the same "mode" value.
+	 * In this point, possible values are only 20 or 30, so when encoder and
+	 * decoder modes are not same, just use the default mode, it is 30.
+	 */
+	if (enc_fmtp_mode != dec_fmtp_mode) {
+	    enc_fmtp_mode = dec_fmtp_mode = DEFAULT_MODE;
+	    PJ_LOG(4,(pool->obj_name, 
+		      "Normalized iLBC encoder and decoder modes to %d", 
+		      DEFAULT_MODE));
+	}
+
+	/* Update some attributes based on negotiated mode. */
+	attr->info.avg_bps = (dec_fmtp_mode == 30? 13333 : 15200);
+	attr->info.frm_ptime = dec_fmtp_mode;
+
+	/* Override average frame size */
+	codec_data->avg_frame_size = (dec_fmtp_mode == 30? 50 : 38);
+
+	/* Override samples per frame */
+	codec_data->samples_per_frame = (dec_fmtp_mode == 30? 240 : 160);
+    }
+#endif
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Close codec.
+ */
+static pj_status_t codec_close( pjmedia_codec *codec )
+{
+    PJ_UNUSED_ARG(codec);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Modify codec settings.
+ */
+static pj_status_t codec_modify( pjmedia_codec *codec, 
+				 const pjmedia_codec_param *attr )
+{
+    /* Not supported yet. */
+    PJ_UNUSED_ARG(codec);
+    PJ_UNUSED_ARG(attr);
+
+    return PJ_ENOTSUP;
+}
+
+/*
+ * Get frames in the packet.
+ */
+static pj_status_t codec_parse( pjmedia_codec *codec,
+				void *pkt,
+				pj_size_t pkt_size,
+				const pj_timestamp *ts,
+				unsigned *frame_cnt,
+				pjmedia_frame frames[])
+{
+    codec_private_t *codec_data = (codec_private_t*) codec->codec_data;
+    struct codec_desc *desc = &codec_desc[codec_data->codec_idx];
+    unsigned count = 0;
+
+    PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL);
+
+    if (desc->parse != NULL) {
+	return desc->parse(codec_data, pkt,  pkt_size, ts, frame_cnt, frames);
+    }
+
+    while (pkt_size >= codec_data->avg_frame_size && count < *frame_cnt) {
+	frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
+	frames[count].buf = pkt;
+	frames[count].size = codec_data->avg_frame_size;
+	frames[count].timestamp.u64 = ts->u64 + 
+				      count * codec_data->samples_per_frame;
+
+	pkt = (pj_uint8_t*)pkt + codec_data->avg_frame_size;
+	pkt_size -= codec_data->avg_frame_size;
+
+	++count;
+    }
+
+    if (pkt_size && count < *frame_cnt) {
+	frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
+	frames[count].buf = pkt;
+	frames[count].size = pkt_size;
+	frames[count].timestamp.u64 = ts->u64 + 
+				       count * codec_data->samples_per_frame;
+	++count;
+    }
+
+    *frame_cnt = count;
+    return PJ_SUCCESS;
+}
+
+/*
+ * Encode frames.
+ */
+static pj_status_t codec_encode( pjmedia_codec *codec, 
+				 const struct pjmedia_frame *input,
+				 unsigned output_buf_len, 
+				 struct pjmedia_frame *output)
+{
+    codec_private_t *codec_data = (codec_private_t*) codec->codec_data;
+    struct codec_desc *desc = &codec_desc[codec_data->codec_idx];
+    const pjmedia_frame_ext *input_ = (const pjmedia_frame_ext*) input;
+
+    pj_assert(input && input->type == PJMEDIA_FRAME_TYPE_EXTENDED);
+
+    if (desc->pack != NULL) {
+	desc->pack(codec_data, input_, output_buf_len, output);
+    } else {
+	if (input_->subframe_cnt == 0) {
+	    /* DTX */
+	    output->buf = NULL;
+	    output->size = 0;
+	    output->type = PJMEDIA_FRAME_TYPE_NONE;
+	} else {
+	    unsigned i;
+	    pj_uint8_t *p = output->buf;
+
+	    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+	    output->size = 0;
+	    
+	    for (i = 0; i < input_->subframe_cnt; ++i) {
+		pjmedia_frame_ext_subframe *sf;
+		unsigned sf_len;
+
+		sf = pjmedia_frame_ext_get_subframe(input_, i);
+		pj_assert(sf);
+
+		sf_len = (sf->bitlen + 7) >> 3;
+
+		pj_memcpy(p, sf->data, sf_len);
+		p += sf_len;
+		output->size += sf_len;
+
+		/* If there is SID or DTX frame, break the loop. */
+		if (desc->pt == PJMEDIA_RTP_PT_G729 && 
+		    sf_len < codec_data->avg_frame_size)
+		{
+		    break;
+		}
+		
+	    }
+	}
+    }
+
+    output->timestamp = input->timestamp;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Decode frame.
+ */
+static pj_status_t codec_decode( pjmedia_codec *codec, 
+				 const struct pjmedia_frame *input,
+				 unsigned output_buf_len, 
+				 struct pjmedia_frame *output)
+{
+    codec_private_t *codec_data = (codec_private_t*) codec->codec_data;
+#if PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR
+    struct codec_desc *desc = &codec_desc[codec_data->codec_idx];
+#endif
+    pjmedia_frame_ext *output_ = (pjmedia_frame_ext*) output;
+
+    pj_assert(input);
+    PJ_UNUSED_ARG(output_buf_len);
+
+#if PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR
+    /* Need to rearrange the AMR bitstream, since the bitstream may not be 
+     * started from bit 0 or may need to be reordered from sensitivity order 
+     * into encoder bits order.
+     */
+    if (desc->pt == PJMEDIA_RTP_PT_AMR || desc->pt == PJMEDIA_RTP_PT_AMRWB) {
+	pjmedia_frame input_;
+	pjmedia_codec_amr_pack_setting *setting;
+
+	setting = &((amr_settings_t*)codec_data->codec_setting)->dec_setting;
+
+	input_ = *input;
+	pjmedia_codec_amr_predecode(input, setting, &input_);
+	
+	pjmedia_frame_ext_append_subframe(output_, input_.buf, 
+					  (pj_uint16_t)(input_.size << 3),
+					  (pj_uint16_t)codec_data->samples_per_frame);
+	output->timestamp = input->timestamp;
+	
+	return PJ_SUCCESS;
+    }
+#endif
+    
+    pjmedia_frame_ext_append_subframe(output_, input->buf, 
+				      (pj_uint16_t)(input->size << 3),
+				      (pj_uint16_t)codec_data->samples_per_frame);
+    output->timestamp = input->timestamp;
+
+    return PJ_SUCCESS;
+}
+
+/* 
+ * Recover lost frame.
+ */
+static pj_status_t codec_recover( pjmedia_codec *codec, 
+				  unsigned output_buf_len, 
+				  struct pjmedia_frame *output)
+{
+    codec_private_t *codec_data = (codec_private_t*) codec->codec_data;
+    pjmedia_frame_ext *output_ = (pjmedia_frame_ext*) output;
+
+    PJ_UNUSED_ARG(output_buf_len);
+
+    pjmedia_frame_ext_append_subframe(output_, NULL, 0,
+				      (pj_uint16_t)codec_data->samples_per_frame);
+
+    return PJ_SUCCESS;
+}
+
+#endif	/* PJMEDIA_HAS_PASSTHROUGH_CODECS */
+
diff --git a/jni/pjproject-android/pjmedia/src/pjmedia-codec/silk.c b/jni/pjproject-android/pjmedia/src/pjmedia-codec/silk.c
new file mode 100644
index 0000000..75ee7ca
--- /dev/null
+++ b/jni/pjproject-android/pjmedia/src/pjmedia-codec/silk.c
@@ -0,0 +1,953 @@
+/* $Id: silk.c 4339 2013-01-31 05:23:46Z ming $ */
+/* 
+ * Copyright (C) 2012-2012 Teluu Inc. (http://www.teluu.com)
+ * Contributed by Regis Montoya (aka r3gis - www.r3gis.fr)
+ *
+ * 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/silk.h>
+#include <pjmedia/codec.h>
+#include <pjmedia/delaybuf.h>
+#include <pjmedia/endpoint.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/port.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+
+#if defined(PJMEDIA_HAS_SILK_CODEC) && (PJMEDIA_HAS_SILK_CODEC!=0)
+
+#include "SKP_Silk_SDK_API.h"
+
+#define THIS_FILE		"silk.c"
+
+#ifndef PJMEDIA_SILK_DELAY_BUF_OPTIONS
+    #define PJMEDIA_SILK_DELAY_BUF_OPTIONS PJMEDIA_DELAY_BUF_SIMPLE_FIFO
+#endif
+
+#define FRAME_LENGTH_MS                 20
+#define SILK_ENC_CTL_PACKET_LOSS_PCT    10
+#define SILK_MIN_BITRATE                5000
+#define CALC_BITRATE_QUALITY(quality, max_br) \
+                (quality * max_br / 10)
+#define CALC_BITRATE(max_br) \
+                CALC_BITRATE_QUALITY(PJMEDIA_CODEC_SILK_DEFAULT_QUALITY, \
+                                     max_br);
+
+
+/* Prototypes for SILK factory */
+static pj_status_t silk_test_alloc( pjmedia_codec_factory *factory,
+				    const pjmedia_codec_info *id );
+static pj_status_t silk_default_attr( pjmedia_codec_factory *factory,
+				      const pjmedia_codec_info *id,
+				      pjmedia_codec_param *attr );
+static pj_status_t silk_enum_codecs ( pjmedia_codec_factory *factory,
+				      unsigned *count,
+				      pjmedia_codec_info codecs[]);
+static pj_status_t silk_alloc_codec( pjmedia_codec_factory *factory,
+				     const pjmedia_codec_info *id,
+				     pjmedia_codec **p_codec);
+static pj_status_t silk_dealloc_codec( pjmedia_codec_factory *factory,
+				       pjmedia_codec *codec );
+
+/* Prototypes for SILK implementation. */
+static pj_status_t  silk_codec_init( pjmedia_codec *codec,
+				     pj_pool_t *pool );
+static pj_status_t  silk_codec_open( pjmedia_codec *codec,
+				     pjmedia_codec_param *attr );
+static pj_status_t  silk_codec_close( pjmedia_codec *codec );
+static pj_status_t  silk_codec_modify( pjmedia_codec *codec,
+				       const pjmedia_codec_param *attr );
+static pj_status_t  silk_codec_parse( pjmedia_codec *codec,
+				      void *pkt,
+				      pj_size_t pkt_size,
+				      const pj_timestamp *timestamp,
+				      unsigned *frame_cnt,
+				      pjmedia_frame frames[]);
+static pj_status_t  silk_codec_encode( pjmedia_codec *codec,
+				       const struct pjmedia_frame *input,
+				       unsigned output_buf_len,
+				       struct pjmedia_frame *output);
+static pj_status_t  silk_codec_decode( pjmedia_codec *codec,
+				       const struct pjmedia_frame *input,
+				       unsigned output_buf_len,
+				       struct pjmedia_frame *output);
+static pj_status_t  silk_codec_recover( pjmedia_codec *codec,
+					unsigned output_buf_len,
+					struct pjmedia_frame *output);
+
+
+typedef enum
+{
+    PARAM_NB,   /* Index for narrowband parameter.	*/
+    PARAM_MB,	/* Index for medium parameter.		*/
+    PARAM_WB,	/* Index for wideband parameter.	*/
+    PARAM_SWB,	/* Index for super-wideband parameter	*/
+} silk_mode;
+
+
+/* Silk default parameter */
+typedef struct silk_param
+{
+    int		 enabled;	    /* Is this mode enabled?		    */
+    int		 pt;		    /* Payload type.			    */
+    unsigned	 clock_rate;	    /* Default sampling rate to be used.    */
+    pj_uint16_t	 ptime;		    /* packet length (in ms).		    */
+    pj_uint32_t  bitrate;	    /* Bit rate for current mode.	    */
+    pj_uint32_t  max_bitrate;	    /* Max bit rate for current mode.	    */
+    int 	 complexity;	    /* Complexity mode: 0/lowest to 2.	    */
+} silk_param;
+
+
+/* Definition for SILK codec operations. */
+static pjmedia_codec_op silk_op =
+{
+    &silk_codec_init,
+    &silk_codec_open,
+    &silk_codec_close,
+    &silk_codec_modify,
+    &silk_codec_parse,
+    &silk_codec_encode,
+    &silk_codec_decode,
+    &silk_codec_recover
+};
+
+/* Definition for SILK codec factory operations. */
+static pjmedia_codec_factory_op silk_factory_op =
+{
+    &silk_test_alloc,
+    &silk_default_attr,
+    &silk_enum_codecs,
+    &silk_alloc_codec,
+    &silk_dealloc_codec,
+    &pjmedia_codec_silk_deinit
+};
+
+
+/* SILK factory private data */
+static struct silk_factory
+{
+    pjmedia_codec_factory	base;
+    pjmedia_endpt	       *endpt;
+    pj_pool_t		       *pool;
+    pj_mutex_t		       *mutex;
+    struct silk_param		silk_param[4];
+} silk_factory;
+
+
+/* SILK codec private data. */
+typedef struct silk_private
+{
+    silk_mode	 mode;		/**< Silk mode.	*/
+    pj_pool_t	*pool;		/**< Pool for each instance.    */
+    unsigned	 samples_per_frame;
+    pj_uint8_t   pcm_bytes_per_sample;
+
+    pj_bool_t	 enc_ready;
+    SKP_SILK_SDK_EncControlStruct enc_ctl;
+    void	*enc_st;
+
+    pj_bool_t	 dec_ready;
+    SKP_SILK_SDK_DecControlStruct dec_ctl;
+    void	*dec_st;
+
+    /* Buffer to hold decoded frames. */
+    void        *dec_buf[SILK_MAX_FRAMES_PER_PACKET-1];
+    SKP_int16    dec_buf_size[SILK_MAX_FRAMES_PER_PACKET-1];
+    pj_size_t    dec_buf_sz;
+    unsigned     dec_buf_cnt;
+    pj_uint32_t  pkt_info;    /**< Packet info for buffered frames.  */
+} silk_private;
+
+
+silk_mode silk_get_mode_from_clock_rate(unsigned clock_rate) {
+    if (clock_rate <= silk_factory.silk_param[PARAM_NB].clock_rate) {
+	return PARAM_NB;
+    } else if (clock_rate <= silk_factory.silk_param[PARAM_MB].clock_rate) {
+	return PARAM_MB;
+    } else if (clock_rate <= silk_factory.silk_param[PARAM_WB].clock_rate) {
+	return PARAM_WB;
+    }
+    return PARAM_SWB;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_codec_silk_init(pjmedia_endpt *endpt)
+{
+    pjmedia_codec_mgr *codec_mgr;
+    silk_param *sp;
+    pj_status_t status;
+
+    if (silk_factory.endpt != NULL) {
+	/* Already initialized. */
+	return PJ_SUCCESS;
+    }
+
+    /* Init factory */
+    pj_bzero(&silk_factory, sizeof(silk_factory));
+    silk_factory.base.op = &silk_factory_op;
+    silk_factory.base.factory_data = NULL;
+    silk_factory.endpt = endpt;
+
+    /* Create pool */
+    silk_factory.pool = pjmedia_endpt_create_pool(endpt, "silk", 4000, 4000);
+    if (!silk_factory.pool)
+	return PJ_ENOMEM;
+
+    /* Create mutex. */
+    status = pj_mutex_create_simple(silk_factory.pool, "silk",
+				    &silk_factory.mutex);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Initialize default codec params */
+
+    /* From SILK docs:
+       - SILK bitrate tables:
+         +----------------+---------+-----------+
+         |                | fs (Hz) | BR (kbps) |
+         +----------------+---------+-----------+
+         |   Narrowband   |   8000  |   6 - 20  |
+         |   Mediumband   |  12000  |   7 - 25  |
+         |    Wideband    |  16000  |   8 - 30  |
+         | Super Wideband |  24000  |  12 - 40  |
+         +----------------+---------+-----------+
+       - The upper limits of the bit rate ranges in this table are
+         recommended values.
+     */
+
+    sp = &silk_factory.silk_param[PARAM_NB];
+    sp->pt = PJMEDIA_RTP_PT_SILK_NB;
+    sp->clock_rate = 8000;
+    sp->max_bitrate = 22000;
+    sp->bitrate = CALC_BITRATE(sp->max_bitrate);
+    sp->ptime = FRAME_LENGTH_MS;
+    sp->complexity = PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY;
+    sp->enabled = 1;
+
+    sp = &silk_factory.silk_param[PARAM_MB];
+    sp->pt = PJMEDIA_RTP_PT_SILK_MB;
+    sp->clock_rate = 12000;
+    sp->max_bitrate = 28000;
+    sp->bitrate = CALC_BITRATE(sp->max_bitrate);
+    sp->ptime = FRAME_LENGTH_MS;
+    sp->complexity = PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY;
+    sp->enabled = 0;
+
+    sp = &silk_factory.silk_param[PARAM_WB];
+    sp->pt = PJMEDIA_RTP_PT_SILK_WB;
+    sp->clock_rate = 16000;
+    sp->max_bitrate = 36000;
+    sp->bitrate = CALC_BITRATE(sp->max_bitrate);
+    sp->ptime = FRAME_LENGTH_MS;
+    sp->complexity = PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY;
+    sp->enabled = 1;
+
+    sp = &silk_factory.silk_param[PARAM_SWB];
+    sp->pt = PJMEDIA_RTP_PT_SILK_SWB;
+    sp->clock_rate = 24000;
+    sp->max_bitrate = 46000;
+    sp->bitrate = CALC_BITRATE(sp->max_bitrate);
+    sp->ptime = FRAME_LENGTH_MS;
+    sp->complexity = PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY;
+    sp->enabled = 0;
+
+
+    /* 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,
+						&silk_factory.base);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    PJ_LOG(4,(THIS_FILE, "SILK codec version %s initialized",
+	      SKP_Silk_SDK_get_version()));
+    return PJ_SUCCESS;
+
+on_error:
+    if (silk_factory.mutex) {
+	pj_mutex_destroy(silk_factory.mutex);
+	silk_factory.mutex = NULL;
+    }
+    if (silk_factory.pool) {
+	pj_pool_release(silk_factory.pool);
+	silk_factory.pool = NULL;
+    }
+
+    return status;
+}
+
+
+/*
+ * Change the configuration setting of the SILK codec for the specified
+ * clock rate.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_silk_set_config(
+				    unsigned clock_rate, 
+				    const pjmedia_codec_silk_setting *opt)
+{
+    unsigned i;
+
+    /* Look up in factory modes table */
+    for (i = 0; i < sizeof(silk_factory.silk_param)/
+                    sizeof(silk_factory.silk_param[0]); ++i)
+    {
+        if (silk_factory.silk_param[i].clock_rate == clock_rate) {
+            int quality = PJMEDIA_CODEC_SILK_DEFAULT_QUALITY;
+            int complexity = PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY;
+
+	    silk_factory.silk_param[i].enabled = opt->enabled;
+            if (opt->complexity >= 0)
+                complexity = opt->complexity;
+            silk_factory.silk_param[i].complexity = complexity;
+            if (opt->quality >= 0)
+                quality = opt->quality;
+            silk_factory.silk_param[i].bitrate =
+                CALC_BITRATE_QUALITY(quality,
+                                     silk_factory.silk_param[i].max_bitrate);
+            if (silk_factory.silk_param[i].bitrate < SILK_MIN_BITRATE)
+                silk_factory.silk_param[i].bitrate = SILK_MIN_BITRATE;
+
+	    return PJ_SUCCESS;
+	}
+    }
+
+    return PJ_ENOTFOUND;
+}
+
+
+/*
+ * Unregister SILK codec factory from pjmedia endpoint and deinitialize
+ * the SILK codec library.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_silk_deinit(void)
+{
+    pjmedia_codec_mgr *codec_mgr;
+    pj_status_t status;
+
+    if (silk_factory.endpt == NULL) {
+	/* Not registered. */
+	return PJ_SUCCESS;
+    }
+
+    /* Lock mutex. */
+    pj_mutex_lock(silk_factory.mutex);
+
+    /* Get the codec manager. */
+    codec_mgr = pjmedia_endpt_get_codec_mgr(silk_factory.endpt);
+    if (!codec_mgr) {
+	silk_factory.endpt = NULL;
+	pj_mutex_unlock(silk_factory.mutex);
+	return PJ_EINVALIDOP;
+    }
+
+    /* Unregister silk codec factory. */
+    status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
+						  &silk_factory.base);
+    silk_factory.endpt = NULL;
+
+    /* Destroy mutex. */
+    pj_mutex_destroy(silk_factory.mutex);
+    silk_factory.mutex = NULL;
+
+
+    /* Release pool. */
+    pj_pool_release(silk_factory.pool);
+    silk_factory.pool = NULL;
+
+    return status;
+}
+
+
+/*
+ * Check if factory can allocate the specified codec.
+ */
+static pj_status_t silk_test_alloc(pjmedia_codec_factory *factory,
+				   const pjmedia_codec_info *info )
+{
+    const pj_str_t silk_tag = {"SILK", 4};
+    unsigned i;
+
+    PJ_UNUSED_ARG(factory);
+    PJ_ASSERT_RETURN(factory==&silk_factory.base, PJ_EINVAL);
+
+    /* Type MUST be audio. */
+    if (info->type != PJMEDIA_TYPE_AUDIO)
+	return PJMEDIA_CODEC_EUNSUP;
+
+    /* Check encoding name. */
+    if (pj_stricmp(&info->encoding_name, &silk_tag) != 0)
+	return PJMEDIA_CODEC_EUNSUP;
+
+    /* Channel count must be one */
+    if (info->channel_cnt != 1)
+	return PJMEDIA_CODEC_EUNSUP;
+
+    /* Check clock-rate */
+    for (i=0; i<PJ_ARRAY_SIZE(silk_factory.silk_param); ++i) {
+	silk_param *sp = &silk_factory.silk_param[i];
+	if (sp->enabled && info->clock_rate == sp->clock_rate)
+	{
+	    return PJ_SUCCESS;
+	}
+    }
+    /* Clock rate not supported */
+    return PJMEDIA_CODEC_EUNSUP;
+}
+
+
+/*
+ * Generate default attribute.
+ */
+static pj_status_t silk_default_attr( pjmedia_codec_factory *factory,
+				      const pjmedia_codec_info *id,
+				      pjmedia_codec_param *attr )
+{
+    silk_param *sp;
+    int i;
+    
+    PJ_ASSERT_RETURN(factory==&silk_factory.base, PJ_EINVAL);
+    
+    i = silk_get_mode_from_clock_rate(id->clock_rate);
+    pj_assert(i >= PARAM_NB && i <= PARAM_SWB);
+
+    sp = &silk_factory.silk_param[i];
+
+    pj_bzero(attr, sizeof(pjmedia_codec_param));
+    attr->info.channel_cnt = 1;
+    attr->info.clock_rate = sp->clock_rate;
+    attr->info.avg_bps = sp->bitrate;
+    attr->info.max_bps = sp->max_bitrate;
+    attr->info.frm_ptime = sp->ptime;
+    attr->info.pcm_bits_per_sample = 16;
+    attr->info.pt = (pj_uint8_t) sp->pt;
+    attr->setting.frm_per_pkt = 1;
+    attr->setting.vad = 0; /* DTX is not recommended for quality reason */
+    attr->setting.plc = 1;
+
+    i = 0;
+    attr->setting.dec_fmtp.param[i].name = pj_str("useinbandfec");
+    attr->setting.dec_fmtp.param[i++].val = pj_str("0");
+    /*
+    attr->setting.dec_fmtp.param[i].name = pj_str("maxaveragebitrate");
+    attr->setting.dec_fmtp.param[i++].val = pj_str(mode->bitrate_str);
+    */
+    attr->setting.dec_fmtp.cnt = (pj_uint8_t)i;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Enum codecs supported by this factory.
+ */
+static pj_status_t silk_enum_codecs(pjmedia_codec_factory *factory,
+				    unsigned *count,
+				    pjmedia_codec_info codecs[])
+{
+    unsigned max;
+    int i;
+
+    PJ_ASSERT_RETURN(factory==&silk_factory.base, PJ_EINVAL);
+    PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
+
+    max = *count;
+    *count = 0;
+
+    for (i = 0; i<PJ_ARRAY_SIZE(silk_factory.silk_param) && *count<max; ++i)
+    {
+	silk_param *sp = &silk_factory.silk_param[i];
+
+    	if (!sp->enabled)
+    	    continue;
+
+    	pj_bzero(&codecs[*count], sizeof(pjmedia_codec_info));
+    	codecs[*count].encoding_name = pj_str("SILK");
+    	codecs[*count].pt = sp->pt;
+    	codecs[*count].type = PJMEDIA_TYPE_AUDIO;
+    	codecs[*count].clock_rate = sp->clock_rate;
+    	codecs[*count].channel_cnt = 1;
+
+    	++*count;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Allocate a new SILK codec instance.
+ */
+static pj_status_t silk_alloc_codec(pjmedia_codec_factory *factory,
+				    const pjmedia_codec_info *id,
+				    pjmedia_codec **p_codec)
+{
+    pj_pool_t *pool;
+    pjmedia_codec *codec;
+    silk_private *silk;
+
+    PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory == &silk_factory.base, PJ_EINVAL);
+
+    /* Create pool for codec instance */
+    pool = pjmedia_endpt_create_pool(silk_factory.endpt, "silk", 512, 512);
+
+    /* Allocate codec */
+    codec = PJ_POOL_ZALLOC_T(pool, pjmedia_codec);
+    codec->op = &silk_op;
+    codec->factory = factory;
+    codec->codec_data = PJ_POOL_ZALLOC_T(pool, silk_private);
+    silk = (silk_private*) codec->codec_data;
+    silk->pool = pool;
+    silk->enc_ready = PJ_FALSE;
+    silk->dec_ready = PJ_FALSE;
+    silk->mode = silk_get_mode_from_clock_rate(id->clock_rate);
+
+    *p_codec = codec;
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Free codec.
+ */
+static pj_status_t silk_dealloc_codec( pjmedia_codec_factory *factory,
+				      pjmedia_codec *codec )
+{
+    silk_private *silk;
+
+    PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory == &silk_factory.base, PJ_EINVAL);
+
+    silk = (silk_private*)codec->codec_data;
+
+    /* Close codec, if it's not closed. */
+    if (silk->enc_ready == PJ_TRUE || silk->dec_ready == PJ_TRUE) {
+    	silk_codec_close(codec);
+    }
+
+    pj_pool_release(silk->pool);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Init codec.
+ */
+static pj_status_t silk_codec_init(pjmedia_codec *codec,
+				   pj_pool_t *pool )
+{
+    PJ_UNUSED_ARG(codec);
+    PJ_UNUSED_ARG(pool);
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Open codec.
+ */
+static pj_status_t silk_codec_open(pjmedia_codec *codec,
+				   pjmedia_codec_param *attr )
+{
+
+    silk_private *silk;
+    silk_param *sp;
+    SKP_int st_size, err;
+    pj_bool_t enc_use_fec;
+    unsigned enc_bitrate, i;
+
+    PJ_ASSERT_RETURN(codec && attr, PJ_EINVAL);
+
+    silk = (silk_private*)codec->codec_data;
+    sp = &silk_factory.silk_param[silk->mode];
+
+    /* Already opened? */
+    if (silk->enc_ready || silk->dec_ready)
+	return PJ_SUCCESS;
+
+    /* Allocate and initialize encoder */
+    err = SKP_Silk_SDK_Get_Encoder_Size(&st_size);
+    if (err) {
+        PJ_LOG(3,(THIS_FILE, "Failed to get encoder state size (err=%d)",
+		  err));
+	return PJMEDIA_CODEC_EFAILED;
+    }
+    silk->enc_st = pj_pool_zalloc(silk->pool, st_size);
+    err = SKP_Silk_SDK_InitEncoder(silk->enc_st, &silk->enc_ctl);
+    if (err) {
+        PJ_LOG(3,(THIS_FILE, "Failed to init encoder (err=%d)", err));
+	return PJMEDIA_CODEC_EFAILED;
+    }
+
+    /* Check fmtp params */
+    enc_use_fec = PJ_TRUE;
+    enc_bitrate = sp->bitrate;
+    for (i = 0; i < attr->setting.enc_fmtp.cnt; ++i) {
+	pjmedia_codec_fmtp *fmtp = &attr->setting.enc_fmtp;
+	const pj_str_t STR_USEINBANDFEC = {"useinbandfec", 12};
+	const pj_str_t STR_MAXAVERAGEBITRATE = {"maxaveragebitrate", 17};
+
+	if (!pj_stricmp(&fmtp->param[i].name, &STR_USEINBANDFEC)) {
+	    enc_use_fec = pj_strtoul(&fmtp->param[i].val) != 0;
+	} else if (!pj_stricmp(&fmtp->param[i].name, &STR_MAXAVERAGEBITRATE)) {
+	    enc_bitrate = pj_strtoul(&fmtp->param[i].val);
+	    if (enc_bitrate > sp->max_bitrate) {
+		enc_bitrate = sp->max_bitrate;
+	    }
+	}
+    }
+
+    /* Setup encoder control for encoding process */
+    silk->enc_ready = PJ_TRUE;
+    silk->samples_per_frame = FRAME_LENGTH_MS *
+			      attr->info.clock_rate / 1000;
+    silk->pcm_bytes_per_sample = attr->info.pcm_bits_per_sample / 8;
+
+    silk->enc_ctl.API_sampleRate        = attr->info.clock_rate;
+    silk->enc_ctl.maxInternalSampleRate = attr->info.clock_rate;
+    silk->enc_ctl.packetSize            = attr->setting.frm_per_pkt *
+                                          silk->samples_per_frame;
+    /* For useInBandFEC setting to be useful, we need to set
+     * packetLossPercentage greater than LBRR_LOSS_THRES (1)
+     */
+    silk->enc_ctl.packetLossPercentage  = SILK_ENC_CTL_PACKET_LOSS_PCT;
+    silk->enc_ctl.useInBandFEC          = enc_use_fec;
+    silk->enc_ctl.useDTX                = attr->setting.vad;
+    silk->enc_ctl.complexity            = sp->complexity;
+    silk->enc_ctl.bitRate               = enc_bitrate;
+    
+
+    /* Allocate and initialize decoder */
+    err = SKP_Silk_SDK_Get_Decoder_Size(&st_size);
+    if (err) {
+        PJ_LOG(3,(THIS_FILE, "Failed to get decoder state size (err=%d)",
+		  err));
+	return PJMEDIA_CODEC_EFAILED;
+    }
+    silk->dec_st = pj_pool_zalloc(silk->pool, st_size);
+    err = SKP_Silk_SDK_InitDecoder(silk->dec_st);
+    if (err) {
+        PJ_LOG(3,(THIS_FILE, "Failed to init decoder (err=%d)", err));
+	return PJMEDIA_CODEC_EFAILED;
+    }
+
+    /* Setup decoder control for decoding process */
+    silk->dec_ctl.API_sampleRate        = attr->info.clock_rate;
+    silk->dec_ctl.framesPerPacket	= 1; /* for proper PLC at start */
+    silk->dec_ready = PJ_TRUE;
+    silk->dec_buf_sz = attr->info.clock_rate * attr->info.channel_cnt *
+                       attr->info.frm_ptime / 1000 *
+                       silk->pcm_bytes_per_sample;
+
+    /* Inform the stream to prepare a larger buffer since we cannot parse
+     * SILK packets and split it into individual frames.
+     */
+    attr->info.max_rx_frame_size = attr->info.max_bps * 
+			           attr->info.frm_ptime / 8 / 1000;
+    if ((attr->info.max_bps * attr->info.frm_ptime) % 8000 != 0)
+    {
+	++attr->info.max_rx_frame_size;
+    }
+    attr->info.max_rx_frame_size *= SILK_MAX_FRAMES_PER_PACKET;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Close codec.
+ */
+static pj_status_t silk_codec_close( pjmedia_codec *codec )
+{
+    silk_private *silk;
+    silk = (silk_private*)codec->codec_data;
+
+    silk->enc_ready = PJ_FALSE;
+    silk->dec_ready = PJ_FALSE;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Modify codec settings.
+ */
+static pj_status_t  silk_codec_modify(pjmedia_codec *codec,
+				      const pjmedia_codec_param *attr )
+{
+    PJ_TODO(implement_silk_codec_modify);
+
+    PJ_UNUSED_ARG(codec);
+    PJ_UNUSED_ARG(attr);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Encode frame.
+ */
+static pj_status_t silk_codec_encode(pjmedia_codec *codec,
+				     const struct pjmedia_frame *input,
+				     unsigned output_buf_len,
+				     struct pjmedia_frame *output)
+{
+    silk_private *silk;
+    SKP_int err;
+    unsigned nsamples;
+    SKP_int16 out_size;
+
+    PJ_ASSERT_RETURN(codec && input && output_buf_len && output, PJ_EINVAL);
+    silk = (silk_private*)codec->codec_data;
+
+    /* Check frame in size */
+    nsamples = input->size >> 1;
+    PJ_ASSERT_RETURN(nsamples % silk->samples_per_frame == 0,
+		     PJMEDIA_CODEC_EPCMFRMINLEN);
+
+    /* Encode */
+    output->size = 0;
+    out_size = (SKP_int16)output_buf_len;
+    err = SKP_Silk_SDK_Encode(silk->enc_st, &silk->enc_ctl,
+			     (SKP_int16*)input->buf, nsamples,
+			     (SKP_uint8*)output->buf, &out_size);
+    if (err) {
+	PJ_LOG(3, (THIS_FILE, "Failed to encode frame (err=%d)", err));
+	output->type = PJMEDIA_FRAME_TYPE_NONE;
+	if (err == SKP_SILK_ENC_PAYLOAD_BUF_TOO_SHORT)
+	    return PJMEDIA_CODEC_EFRMTOOSHORT;
+	return PJMEDIA_CODEC_EFAILED;
+    }
+
+    output->size = out_size;
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+    output->timestamp = input->timestamp;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Get frames in the packet.
+ */
+static pj_status_t  silk_codec_parse( pjmedia_codec *codec,
+				     void *pkt,
+				     pj_size_t pkt_size,
+				     const pj_timestamp *ts,
+				     unsigned *frame_cnt,
+				     pjmedia_frame frames[])
+{
+    silk_private *silk;
+    SKP_Silk_TOC_struct toc;
+    unsigned i, count;
+
+    PJ_ASSERT_RETURN(codec && ts && frames && frame_cnt, PJ_EINVAL);
+    silk = (silk_private*)codec->codec_data;
+
+    SKP_Silk_SDK_get_TOC(pkt, pkt_size, &toc);
+    count = toc.framesInPacket;
+    pj_assert(count <= SILK_MAX_FRAMES_PER_PACKET);
+
+    for (i = 0; i < count; i++) {
+        frames[i].type = PJMEDIA_FRAME_TYPE_AUDIO;
+        frames[i].bit_info = (((unsigned)ts->u64 & 0xFFFF) << 16) |
+                              (((unsigned)pkt & 0xFF) << 8) |
+                              (toc.framesInPacket << 4) | i;
+        frames[i].buf = pkt;
+        frames[i].size = pkt_size;
+        frames[i].timestamp.u64 = ts->u64 + i * silk->samples_per_frame;
+    }
+
+    *frame_cnt = count;
+    return PJ_SUCCESS;
+}
+
+static pj_status_t silk_codec_decode(pjmedia_codec *codec,
+				     const struct pjmedia_frame *input,
+				     unsigned output_buf_len,
+				     struct pjmedia_frame *output)
+{
+    silk_private *silk;
+    SKP_int16 out_size;
+    SKP_Silk_TOC_struct toc;
+    SKP_int err = 0;
+    unsigned pkt_info, frm_info;
+
+    PJ_ASSERT_RETURN(codec && input && output_buf_len && output, PJ_EINVAL);
+    silk = (silk_private*)codec->codec_data;
+    PJ_ASSERT_RETURN(output_buf_len >= silk->dec_buf_sz, PJ_ETOOSMALL);
+
+    SKP_Silk_SDK_get_TOC(input->buf, input->size, &toc);
+    pkt_info = input->bit_info & 0xFFFFFF00;
+    frm_info = input->bit_info & 0xF;
+
+    if (toc.framesInPacket == 0) {
+        /* In SILK ARM version, the table of content can indicate
+         * that the number of frames in the packet is 0.
+         * Try to get the number of frames in packet that we save
+         * in the frame instead.
+         */
+        toc.framesInPacket = (input->bit_info & 0xF0) >> 4;
+    }
+    
+    if (toc.framesInPacket == 0) {
+        output->size = 0;
+    } else if (silk->pkt_info != pkt_info || input->bit_info == 0) {
+        unsigned i;
+        SKP_int16 nsamples;
+
+        silk->pkt_info = pkt_info;
+        nsamples = (SKP_int16)silk->dec_buf_sz / silk->pcm_bytes_per_sample;
+
+        if (toc.framesInPacket-1 > (SKP_int)silk->dec_buf_cnt) {
+            /* Grow the buffer */
+            for (i = silk->dec_buf_cnt+1; i < (unsigned)toc.framesInPacket;
+                i++)
+            {
+                silk->dec_buf[i-1] = pj_pool_alloc(silk->pool,
+                                                   silk->dec_buf_sz);
+            }
+            silk->dec_buf_cnt = toc.framesInPacket-1;
+        }
+
+        /* We need to decode all the frames in the packet. */
+        for (i = 0; i < (unsigned)toc.framesInPacket;) {
+            void *buf;
+            SKP_int16 *size;
+
+            if (i == 0 || i == frm_info) {
+                buf = output->buf;
+                size = &out_size;
+            } else {
+                buf = silk->dec_buf[i-1];
+                size = &silk->dec_buf_size[i-1];
+            }
+
+            *size = nsamples;
+            err = SKP_Silk_SDK_Decode(silk->dec_st, &silk->dec_ctl,
+			              0, /* Normal frame flag */
+			              input->buf, input->size,
+			              buf, size);
+            if (err) {
+	        PJ_LOG(3, (THIS_FILE, "Failed to decode frame (err=%d)",
+                                      err));
+                *size = 0;
+            } else {
+                *size = *size * silk->pcm_bytes_per_sample;
+            }
+
+            if (i == frm_info) {
+                output->size = *size;
+            }
+
+            i++;
+            if (!silk->dec_ctl.moreInternalDecoderFrames &&
+                i < (unsigned)toc.framesInPacket)
+            {
+                /* It turns out that the packet does not have
+                 * the number of frames as mentioned in the TOC.
+                 */
+                for (; i < (unsigned)toc.framesInPacket; i++) {
+                    silk->dec_buf_size[i-1] = 0;
+                    if (i == frm_info) {
+                        output->size = 0;
+                    }
+                }
+            }
+        }
+    } else {
+        /* We have already decoded this packet. */
+        if (frm_info == 0 || silk->dec_buf_size[frm_info-1] == 0) {
+            /* The decoding was a failure. */
+            output->size = 0;
+        } else {
+            /* Copy the decoded frame from the buffer. */
+            pj_assert(frm_info-1 < silk->dec_buf_cnt);
+            if (frm_info-1 >= silk->dec_buf_cnt) {
+                output->size = 0;
+            } else {
+                pj_memcpy(output->buf, silk->dec_buf[frm_info-1],
+                          silk->dec_buf_size[frm_info-1]);
+                output->size = silk->dec_buf_size[frm_info-1];
+            }
+        }
+    }
+
+    if (output->size == 0) {
+        output->type = PJMEDIA_TYPE_NONE;
+        output->buf = NULL;
+        return PJMEDIA_CODEC_EFAILED;
+    }
+
+    output->type = PJMEDIA_TYPE_AUDIO;
+    output->timestamp = input->timestamp;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Recover lost frame.
+ */
+static pj_status_t  silk_codec_recover(pjmedia_codec *codec,
+				       unsigned output_buf_len,
+				       struct pjmedia_frame *output)
+{
+    silk_private *silk;
+    SKP_int16 out_size;
+    SKP_int err;
+
+    PJ_ASSERT_RETURN(codec && output_buf_len && output, PJ_EINVAL);
+    silk = (silk_private*)codec->codec_data;
+
+    out_size = (SKP_int16)output_buf_len / silk->pcm_bytes_per_sample;
+    err = SKP_Silk_SDK_Decode(silk->dec_st, &silk->dec_ctl,
+			      1, /* Lost frame flag */
+			      NULL,
+			      0,
+			      output->buf,
+			      &out_size);
+    if (err) {
+	PJ_LOG(3, (THIS_FILE, "Failed to conceal lost frame (err=%d)", err));
+	output->type = PJMEDIA_FRAME_TYPE_NONE;
+	output->buf = NULL;
+	output->size = 0;
+	return PJMEDIA_CODEC_EFAILED;
+    }
+
+    output->size = out_size * silk->pcm_bytes_per_sample;
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+
+    return PJ_SUCCESS;
+}
+
+#if defined(_MSC_VER)
+#  if PJ_DEBUG
+#   pragma comment(lib, "SKP_Silk_FLP_Win32_debug.lib")
+#  else
+#   pragma comment(lib, "SKP_Silk_FLP_Win32_mt.lib")
+#  endif
+#endif
+
+
+#endif /* PJMEDIA_HAS_SILK_CODEC */
diff --git a/jni/pjproject-android/pjmedia/src/pjmedia-codec/speex_codec.c b/jni/pjproject-android/pjmedia/src/pjmedia-codec/speex_codec.c
new file mode 100644
index 0000000..ea3fca6
--- /dev/null
+++ b/jni/pjproject-android/pjmedia/src/pjmedia-codec/speex_codec.c
@@ -0,0 +1,997 @@
+/* $Id: speex_codec.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+
+#include <pjmedia-codec/speex.h>
+#include <pjmedia/codec.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/endpoint.h>
+#include <pjmedia/port.h>
+#include <speex/speex.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/os.h>
+
+/*
+ * Only build this file if PJMEDIA_HAS_SPEEX_CODEC != 0
+ */
+#if defined(PJMEDIA_HAS_SPEEX_CODEC) && PJMEDIA_HAS_SPEEX_CODEC!=0
+
+
+#define THIS_FILE   "speex_codec.c"
+
+/* Prototypes for Speex factory */
+static pj_status_t spx_test_alloc( pjmedia_codec_factory *factory, 
+				   const pjmedia_codec_info *id );
+static pj_status_t spx_default_attr( pjmedia_codec_factory *factory, 
+				     const pjmedia_codec_info *id, 
+				     pjmedia_codec_param *attr );
+static pj_status_t spx_enum_codecs( pjmedia_codec_factory *factory, 
+				    unsigned *count, 
+				    pjmedia_codec_info codecs[]);
+static pj_status_t spx_alloc_codec( pjmedia_codec_factory *factory, 
+				    const pjmedia_codec_info *id, 
+				    pjmedia_codec **p_codec);
+static pj_status_t spx_dealloc_codec( pjmedia_codec_factory *factory, 
+				      pjmedia_codec *codec );
+
+/* Prototypes for Speex implementation. */
+static pj_status_t  spx_codec_init( pjmedia_codec *codec, 
+				    pj_pool_t *pool );
+static pj_status_t  spx_codec_open( pjmedia_codec *codec, 
+				    pjmedia_codec_param *attr );
+static pj_status_t  spx_codec_close( pjmedia_codec *codec );
+static pj_status_t  spx_codec_modify(pjmedia_codec *codec, 
+				     const pjmedia_codec_param *attr );
+static pj_status_t  spx_codec_parse( pjmedia_codec *codec,
+				     void *pkt,
+				     pj_size_t pkt_size,
+				     const pj_timestamp *ts,
+				     unsigned *frame_cnt,
+				     pjmedia_frame frames[]);
+static pj_status_t  spx_codec_encode( pjmedia_codec *codec, 
+				      const struct pjmedia_frame *input,
+				      unsigned output_buf_len, 
+				      struct pjmedia_frame *output);
+static pj_status_t  spx_codec_decode( pjmedia_codec *codec, 
+				      const struct pjmedia_frame *input,
+				      unsigned output_buf_len, 
+				      struct pjmedia_frame *output);
+static pj_status_t  spx_codec_recover(pjmedia_codec *codec, 
+				      unsigned output_buf_len, 
+				      struct pjmedia_frame *output);
+
+/* Definition for Speex codec operations. */
+static pjmedia_codec_op spx_op = 
+{
+    &spx_codec_init,
+    &spx_codec_open,
+    &spx_codec_close,
+    &spx_codec_modify,
+    &spx_codec_parse,
+    &spx_codec_encode,
+    &spx_codec_decode,
+    &spx_codec_recover
+};
+
+/* Definition for Speex codec factory operations. */
+static pjmedia_codec_factory_op spx_factory_op =
+{
+    &spx_test_alloc,
+    &spx_default_attr,
+    &spx_enum_codecs,
+    &spx_alloc_codec,
+    &spx_dealloc_codec,
+    &pjmedia_codec_speex_deinit
+};
+
+/* Index to Speex parameter. */
+enum
+{
+    PARAM_NB,	/* Index for narrowband parameter.	*/
+    PARAM_WB,	/* Index for wideband parameter.	*/
+    PARAM_UWB,	/* Index for ultra-wideband parameter	*/
+};
+
+/* Speex default parameter */
+struct speex_param
+{
+    int		     enabled;		/* Is this mode enabled?	    */
+    const SpeexMode *mode;		/* Speex mode.			    */
+    int		     pt;		/* Payload type.		    */
+    unsigned	     clock_rate;	/* Default sampling rate to be used.*/
+    int		     quality;		/* Default encoder quality.	    */
+    int		     complexity;	/* Default encoder complexity.	    */
+    int		     samples_per_frame;	/* Samples per frame.		    */
+    int		     framesize;		/* Frame size for current mode.	    */
+    int		     bitrate;		/* Bit rate for current mode.	    */
+    int		     max_bitrate;	/* Max bit rate for current mode.   */
+};
+
+/* Speex factory */
+static struct spx_factory
+{
+    pjmedia_codec_factory    base;
+    pjmedia_endpt	    *endpt;
+    pj_pool_t		    *pool;
+    pj_mutex_t		    *mutex;
+    pjmedia_codec	     codec_list;
+    struct speex_param	     speex_param[3];
+
+} spx_factory;
+
+/* Speex codec private data. */
+struct spx_private
+{
+    int			 param_id;	    /**< Index to speex param.	*/
+
+    void		*enc;		    /**< Encoder state.		*/
+    SpeexBits		 enc_bits;	    /**< Encoder bits.		*/
+    void		*dec;		    /**< Decoder state.		*/
+    SpeexBits		 dec_bits;	    /**< Decoder bits.		*/
+};
+
+
+/*
+ * Get codec bitrate and frame size.
+ */
+static pj_status_t get_speex_info( struct speex_param *p )
+{
+    void *state;
+    int tmp;
+
+    /* Create temporary encoder */
+    state = speex_encoder_init(p->mode);
+    if (!state)
+	return PJMEDIA_CODEC_EFAILED;
+
+    /* Set the quality */
+    if (p->quality != -1)
+	speex_encoder_ctl(state, SPEEX_SET_QUALITY, &p->quality);
+
+    /* Sampling rate. */
+    speex_encoder_ctl(state, SPEEX_SET_SAMPLING_RATE, &p->clock_rate);
+
+    /* VAD off to have max bitrate */
+    tmp = 0;
+    speex_encoder_ctl(state, SPEEX_SET_VAD, &tmp);
+
+    /* Complexity. */
+    if (p->complexity != -1)
+	speex_encoder_ctl(state, SPEEX_SET_COMPLEXITY, &p->complexity);
+
+    /* Now get the frame size */
+    speex_encoder_ctl(state, SPEEX_GET_FRAME_SIZE, &p->samples_per_frame);
+
+    /* Now get the average bitrate */
+    speex_encoder_ctl(state, SPEEX_GET_BITRATE, &p->bitrate);
+
+    /* Calculate framesize. */
+    p->framesize = p->bitrate * 20 / 1000;
+
+    /* Now get the maximum bitrate by using maximum quality (=10) */
+    tmp = 10;
+    speex_encoder_ctl(state, SPEEX_SET_QUALITY, &tmp);
+    speex_encoder_ctl(state, SPEEX_GET_BITRATE, &p->max_bitrate);
+
+    /* Destroy encoder. */
+    speex_encoder_destroy(state);
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Initialize and register Speex codec factory to pjmedia endpoint.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_speex_init( pjmedia_endpt *endpt,
+					      unsigned options,
+					      int quality,
+					      int complexity )
+{
+    pjmedia_codec_mgr *codec_mgr;
+    unsigned i;
+    pj_status_t status;
+
+    if (spx_factory.pool != NULL) {
+	/* Already initialized. */
+	return PJ_SUCCESS;
+    }
+
+    /* Get defaults */
+    if (quality < 0) quality = PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY;
+    if (complexity < 0) complexity = PJMEDIA_CODEC_SPEEX_DEFAULT_COMPLEXITY;
+
+    /* Validate quality & complexity */
+    PJ_ASSERT_RETURN(quality >= 0 && quality <= 10, PJ_EINVAL);
+    PJ_ASSERT_RETURN(complexity >= 1 && complexity <= 10, PJ_EINVAL);
+
+    /* Create Speex codec factory. */
+    spx_factory.base.op = &spx_factory_op;
+    spx_factory.base.factory_data = NULL;
+    spx_factory.endpt = endpt;
+
+    spx_factory.pool = pjmedia_endpt_create_pool(endpt, "speex", 
+						       4000, 4000);
+    if (!spx_factory.pool)
+	return PJ_ENOMEM;
+
+    pj_list_init(&spx_factory.codec_list);
+
+    /* Create mutex. */
+    status = pj_mutex_create_simple(spx_factory.pool, "speex", 
+				    &spx_factory.mutex);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Initialize default Speex parameter. */
+    spx_factory.speex_param[PARAM_NB].enabled = 
+	((options & PJMEDIA_SPEEX_NO_NB) == 0);
+    spx_factory.speex_param[PARAM_NB].pt = PJMEDIA_RTP_PT_SPEEX_NB;
+    spx_factory.speex_param[PARAM_NB].mode = speex_lib_get_mode(SPEEX_MODEID_NB);
+    spx_factory.speex_param[PARAM_NB].clock_rate = 8000;
+    spx_factory.speex_param[PARAM_NB].quality = quality;
+    spx_factory.speex_param[PARAM_NB].complexity = complexity;
+
+    spx_factory.speex_param[PARAM_WB].enabled = 
+	((options & PJMEDIA_SPEEX_NO_WB) == 0);
+    spx_factory.speex_param[PARAM_WB].pt = PJMEDIA_RTP_PT_SPEEX_WB;
+    spx_factory.speex_param[PARAM_WB].mode = speex_lib_get_mode(SPEEX_MODEID_WB);
+    spx_factory.speex_param[PARAM_WB].clock_rate = 16000;
+    spx_factory.speex_param[PARAM_WB].quality = quality;
+    spx_factory.speex_param[PARAM_WB].complexity = complexity;
+
+    spx_factory.speex_param[PARAM_UWB].enabled = 
+	((options & PJMEDIA_SPEEX_NO_UWB) == 0);
+    spx_factory.speex_param[PARAM_UWB].pt = PJMEDIA_RTP_PT_SPEEX_UWB;
+    spx_factory.speex_param[PARAM_UWB].mode = speex_lib_get_mode(SPEEX_MODEID_UWB);
+    spx_factory.speex_param[PARAM_UWB].clock_rate = 32000;
+    spx_factory.speex_param[PARAM_UWB].quality = quality;
+    spx_factory.speex_param[PARAM_UWB].complexity = complexity;
+
+    /* Somehow quality <=4 is broken in linux. */
+    if (quality <= 4 && quality >= 0) {
+	PJ_LOG(5,(THIS_FILE, "Adjusting quality to 5 for uwb"));
+	spx_factory.speex_param[PARAM_UWB].quality = 5;
+    }
+
+    /* Get codec framesize and avg bitrate for each mode. */
+    for (i=0; i<PJ_ARRAY_SIZE(spx_factory.speex_param); ++i) {
+	status = get_speex_info(&spx_factory.speex_param[i]);
+    }
+
+    /* Get the codec manager. */
+    codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
+    if (!codec_mgr) {
+	status = PJ_EINVALIDOP;
+	goto on_error;
+    }
+
+    /* Register codec factory to endpoint. */
+    status = pjmedia_codec_mgr_register_factory(codec_mgr, 
+						&spx_factory.base);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Done. */
+    return PJ_SUCCESS;
+
+on_error:
+    pj_pool_release(spx_factory.pool);
+    spx_factory.pool = NULL;
+    return status;
+}
+
+
+/*
+ * Initialize with default settings.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_speex_init_default(pjmedia_endpt *endpt)
+{
+    return pjmedia_codec_speex_init(endpt, 0, -1, -1);
+}
+
+/*
+ * Change the settings of Speex codec.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_speex_set_param(unsigned clock_rate,
+						  int quality,
+						  int complexity)
+{
+    unsigned i;
+
+    /* Get defaults */
+    if (quality < 0) quality = PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY;
+    if (complexity < 0) complexity = PJMEDIA_CODEC_SPEEX_DEFAULT_COMPLEXITY;
+
+    /* Validate quality & complexity */
+    PJ_ASSERT_RETURN(quality >= 0 && quality <= 10, PJ_EINVAL);
+    PJ_ASSERT_RETURN(complexity >= 1 && complexity <= 10, PJ_EINVAL);
+
+    /* Apply the settings */
+    for (i=0; i<PJ_ARRAY_SIZE(spx_factory.speex_param); ++i) {
+	if (spx_factory.speex_param[i].clock_rate == clock_rate) {
+	    pj_status_t status;
+
+	    spx_factory.speex_param[i].quality = quality;
+	    spx_factory.speex_param[i].complexity = complexity;
+
+	    /* Somehow quality<=4 is broken in linux. */
+	    if (i == PARAM_UWB && quality <= 4 && quality >= 0) {
+		PJ_LOG(5,(THIS_FILE, "Adjusting quality to 5 for uwb"));
+		spx_factory.speex_param[PARAM_UWB].quality = 5;
+	    }
+
+	    status = get_speex_info(&spx_factory.speex_param[i]);
+
+	    return status;
+	}
+    }
+
+    return PJ_EINVAL;
+}
+
+/*
+ * Unregister Speex codec factory from pjmedia endpoint and deinitialize
+ * the Speex codec library.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_speex_deinit(void)
+{
+    pjmedia_codec_mgr *codec_mgr;
+    pj_status_t status;
+
+    if (spx_factory.pool == NULL) {
+	/* Already deinitialized */
+	return PJ_SUCCESS;
+    }
+
+    pj_mutex_lock(spx_factory.mutex);
+
+    /* We don't want to deinit if there's outstanding codec. */
+    /* This is silly, as we'll always have codec in the list if
+       we ever allocate a codec! A better behavior maybe is to 
+       deallocate all codecs in the list.
+    if (!pj_list_empty(&spx_factory.codec_list)) {
+	pj_mutex_unlock(spx_factory.mutex);
+	return PJ_EBUSY;
+    }
+    */
+
+    /* Get the codec manager. */
+    codec_mgr = pjmedia_endpt_get_codec_mgr(spx_factory.endpt);
+    if (!codec_mgr) {
+	pj_pool_release(spx_factory.pool);
+	spx_factory.pool = NULL;
+	return PJ_EINVALIDOP;
+    }
+
+    /* Unregister Speex codec factory. */
+    status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
+						  &spx_factory.base);
+    
+    /* Destroy mutex. */
+    pj_mutex_destroy(spx_factory.mutex);
+
+    /* Destroy pool. */
+    pj_pool_release(spx_factory.pool);
+    spx_factory.pool = NULL;
+
+    return status;
+}
+
+/* 
+ * Check if factory can allocate the specified codec. 
+ */
+static pj_status_t spx_test_alloc( pjmedia_codec_factory *factory, 
+				   const pjmedia_codec_info *info )
+{
+    const pj_str_t speex_tag = { "speex", 5};
+    unsigned i;
+
+    PJ_UNUSED_ARG(factory);
+
+    /* Type MUST be audio. */
+    if (info->type != PJMEDIA_TYPE_AUDIO)
+	return PJMEDIA_CODEC_EUNSUP;
+
+    /* Check encoding name. */
+    if (pj_stricmp(&info->encoding_name, &speex_tag) != 0)
+	return PJMEDIA_CODEC_EUNSUP;
+
+    /* Check clock-rate */
+    for (i=0; i<PJ_ARRAY_SIZE(spx_factory.speex_param); ++i) {
+	if (info->clock_rate == spx_factory.speex_param[i].clock_rate) {
+	    /* Okay, let's Speex! */
+	    return PJ_SUCCESS;
+	}
+    }
+
+    
+    /* Unsupported, or mode is disabled. */
+    return PJMEDIA_CODEC_EUNSUP;
+}
+
+/*
+ * Generate default attribute.
+ */
+static pj_status_t spx_default_attr (pjmedia_codec_factory *factory, 
+				      const pjmedia_codec_info *id, 
+				      pjmedia_codec_param *attr )
+{
+
+    PJ_ASSERT_RETURN(factory==&spx_factory.base, PJ_EINVAL);
+
+    pj_bzero(attr, sizeof(pjmedia_codec_param));
+    attr->info.pt = (pj_uint8_t)id->pt;
+    attr->info.channel_cnt = 1;
+
+    if (id->clock_rate <= 8000) {
+	attr->info.clock_rate = spx_factory.speex_param[PARAM_NB].clock_rate;
+	attr->info.avg_bps = spx_factory.speex_param[PARAM_NB].bitrate;
+	attr->info.max_bps = spx_factory.speex_param[PARAM_NB].max_bitrate;
+
+    } else if (id->clock_rate <= 16000) {
+	attr->info.clock_rate = spx_factory.speex_param[PARAM_WB].clock_rate;
+	attr->info.avg_bps = spx_factory.speex_param[PARAM_WB].bitrate;
+	attr->info.max_bps = spx_factory.speex_param[PARAM_WB].max_bitrate;
+
+    } else {
+	/* Wow.. somebody is doing ultra-wideband. Cool...! */
+	attr->info.clock_rate = spx_factory.speex_param[PARAM_UWB].clock_rate;
+	attr->info.avg_bps = spx_factory.speex_param[PARAM_UWB].bitrate;
+	attr->info.max_bps = spx_factory.speex_param[PARAM_UWB].max_bitrate;
+    }
+
+    attr->info.pcm_bits_per_sample = 16;
+    attr->info.frm_ptime = 20;
+    attr->info.pt = (pj_uint8_t)id->pt;
+
+    attr->setting.frm_per_pkt = 1;
+
+    /* Default flags. */
+    attr->setting.cng = 1;
+    attr->setting.plc = 1;
+    attr->setting.penh =1 ;
+    attr->setting.vad = 1;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Enum codecs supported by this factory (i.e. only Speex!).
+ */
+static pj_status_t spx_enum_codecs(pjmedia_codec_factory *factory, 
+				    unsigned *count, 
+				    pjmedia_codec_info codecs[])
+{
+    unsigned max;
+    int i;  /* Must be signed */
+
+    PJ_UNUSED_ARG(factory);
+    PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
+
+    max = *count;
+    *count = 0;
+
+    /*
+     * We return three codecs here, and in this order:
+     *	- ultra-wideband, wideband, and narrowband.
+     */
+    for (i=PJ_ARRAY_SIZE(spx_factory.speex_param)-1; i>=0 && *count<max; --i) {
+
+	if (!spx_factory.speex_param[i].enabled)
+	    continue;
+
+	pj_bzero(&codecs[*count], sizeof(pjmedia_codec_info));
+	codecs[*count].encoding_name = pj_str("speex");
+	codecs[*count].pt = spx_factory.speex_param[i].pt;
+	codecs[*count].type = PJMEDIA_TYPE_AUDIO;
+	codecs[*count].clock_rate = spx_factory.speex_param[i].clock_rate;
+	codecs[*count].channel_cnt = 1;
+
+	++*count;
+    }
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Allocate a new Speex codec instance.
+ */
+static pj_status_t spx_alloc_codec( pjmedia_codec_factory *factory, 
+				    const pjmedia_codec_info *id,
+				    pjmedia_codec **p_codec)
+{
+    pjmedia_codec *codec;
+    struct spx_private *spx;
+
+    PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory == &spx_factory.base, PJ_EINVAL);
+
+
+    pj_mutex_lock(spx_factory.mutex);
+
+    /* Get free nodes, if any. */
+    if (!pj_list_empty(&spx_factory.codec_list)) {
+	codec = spx_factory.codec_list.next;
+	pj_list_erase(codec);
+    } else {
+	codec = PJ_POOL_ZALLOC_T(spx_factory.pool, pjmedia_codec);
+	PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM);
+	codec->op = &spx_op;
+	codec->factory = factory;
+	codec->codec_data = pj_pool_alloc(spx_factory.pool,
+					  sizeof(struct spx_private));
+    }
+
+    pj_mutex_unlock(spx_factory.mutex);
+
+    spx = (struct spx_private*) codec->codec_data;
+    spx->enc = NULL;
+    spx->dec = NULL;
+
+    if (id->clock_rate <= 8000)
+	spx->param_id = PARAM_NB;
+    else if (id->clock_rate <= 16000)
+	spx->param_id = PARAM_WB;
+    else
+	spx->param_id = PARAM_UWB;
+
+    *p_codec = codec;
+    return PJ_SUCCESS;
+}
+
+/*
+ * Free codec.
+ */
+static pj_status_t spx_dealloc_codec( pjmedia_codec_factory *factory, 
+				      pjmedia_codec *codec )
+{
+    struct spx_private *spx;
+
+    PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory == &spx_factory.base, PJ_EINVAL);
+
+    /* Close codec, if it's not closed. */
+    spx = (struct spx_private*) codec->codec_data;
+    if (spx->enc != NULL || spx->dec != NULL) {
+	spx_codec_close(codec);
+    }
+
+    /* Put in the free list. */
+    pj_mutex_lock(spx_factory.mutex);
+    pj_list_push_front(&spx_factory.codec_list, codec);
+    pj_mutex_unlock(spx_factory.mutex);
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Init codec.
+ */
+static pj_status_t spx_codec_init( pjmedia_codec *codec, 
+				   pj_pool_t *pool )
+{
+    PJ_UNUSED_ARG(codec);
+    PJ_UNUSED_ARG(pool);
+    return PJ_SUCCESS;
+}
+
+/*
+ * Open codec.
+ */
+static pj_status_t spx_codec_open( pjmedia_codec *codec, 
+				   pjmedia_codec_param *attr )
+{
+    struct spx_private *spx;
+    int id, tmp;
+
+    spx = (struct spx_private*) codec->codec_data;
+    id = spx->param_id;
+
+    /* 
+     * Create and initialize encoder. 
+     */
+    spx->enc = speex_encoder_init(spx_factory.speex_param[id].mode);
+    if (!spx->enc)
+	return PJMEDIA_CODEC_EFAILED;
+    speex_bits_init(&spx->enc_bits);
+
+    /* Set the quality*/
+    if (spx_factory.speex_param[id].quality != -1) {
+	speex_encoder_ctl(spx->enc, SPEEX_SET_QUALITY, 
+			  &spx_factory.speex_param[id].quality);
+    }
+
+    /* Sampling rate. */
+    tmp = attr->info.clock_rate;
+    speex_encoder_ctl(spx->enc, SPEEX_SET_SAMPLING_RATE, 
+		      &spx_factory.speex_param[id].clock_rate);
+
+    /* VAD */
+    tmp = (attr->setting.vad != 0);
+    speex_encoder_ctl(spx->enc, SPEEX_SET_VAD, &tmp);
+    speex_encoder_ctl(spx->enc, SPEEX_SET_DTX, &tmp);
+
+    /* Complexity */
+    if (spx_factory.speex_param[id].complexity != -1) {
+	speex_encoder_ctl(spx->enc, SPEEX_SET_COMPLEXITY, 
+			  &spx_factory.speex_param[id].complexity);
+    }
+
+    /* 
+     * Create and initialize decoder. 
+     */
+    spx->dec = speex_decoder_init(spx_factory.speex_param[id].mode);
+    if (!spx->dec) {
+	spx_codec_close(codec);
+	return PJMEDIA_CODEC_EFAILED;
+    }
+    speex_bits_init(&spx->dec_bits);
+
+    /* Sampling rate. */
+    speex_decoder_ctl(spx->dec, SPEEX_SET_SAMPLING_RATE, 
+		      &spx_factory.speex_param[id].clock_rate);
+
+    /* PENH */
+    tmp = attr->setting.penh;
+    speex_decoder_ctl(spx->dec, SPEEX_SET_ENH, &tmp);
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Close codec.
+ */
+static pj_status_t spx_codec_close( pjmedia_codec *codec )
+{
+    struct spx_private *spx;
+
+    spx = (struct spx_private*) codec->codec_data;
+
+    /* Destroy encoder*/
+    if (spx->enc) {
+	speex_encoder_destroy( spx->enc );
+	spx->enc = NULL;
+	speex_bits_destroy( &spx->enc_bits );
+    }
+
+    /* Destroy decoder */
+    if (spx->dec) {
+	speex_decoder_destroy( spx->dec);
+	spx->dec = NULL;
+	speex_bits_destroy( &spx->dec_bits );
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Modify codec settings.
+ */
+static pj_status_t  spx_codec_modify(pjmedia_codec *codec, 
+				     const pjmedia_codec_param *attr )
+{
+    struct spx_private *spx;
+    int tmp;
+
+    spx = (struct spx_private*) codec->codec_data;
+
+    /* VAD */
+    tmp = (attr->setting.vad != 0);
+    speex_encoder_ctl(spx->enc, SPEEX_SET_VAD, &tmp);
+    speex_encoder_ctl(spx->enc, SPEEX_SET_DTX, &tmp);
+
+    /* PENH */
+    tmp = attr->setting.penh;
+    speex_decoder_ctl(spx->dec, SPEEX_SET_ENH, &tmp);
+
+    return PJ_SUCCESS;
+}
+
+#if 0
+#  define TRACE__(args)	    PJ_LOG(5,args)
+#else
+#  define TRACE__(args)
+#endif
+
+#undef THIS_FUNC
+#define THIS_FUNC "speex_get_next_frame"
+
+#define NB_SUBMODES 16
+#define NB_SUBMODE_BITS 4
+
+#define SB_SUBMODES 8
+#define SB_SUBMODE_BITS 3
+
+/* This function will iterate frames & submodes in the Speex bits.
+ * Returns 0 if a frame found, otherwise returns -1.
+ */
+int speex_get_next_frame(SpeexBits *bits)
+{
+    static const int inband_skip_table[NB_SUBMODES] =
+       {1, 1, 4, 4, 4, 4, 4, 4, 8, 8, 16, 16, 32, 32, 64, 64 };
+    static const int wb_skip_table[SB_SUBMODES] =
+       {SB_SUBMODE_BITS+1, 36, 112, 192, 352, -1, -1, -1};
+
+    unsigned submode;
+    unsigned nb_count = 0;
+
+    while (speex_bits_remaining(bits) >= 5) {
+	unsigned wb_count = 0;
+	unsigned bit_ptr = bits->bitPtr;
+	unsigned char_ptr = bits->charPtr;
+
+	/* WB frame */
+	while ((speex_bits_remaining(bits) >= 4)
+	    && speex_bits_unpack_unsigned(bits, 1))
+	{
+	    int advance;
+
+	    submode = speex_bits_unpack_unsigned(bits, 3);
+	    advance = wb_skip_table[submode];
+	    if (advance < 0) {
+		TRACE__((THIS_FUNC, "Invalid mode encountered. "
+			 "The stream is corrupted."));
+		return -1;
+	    } 
+	    TRACE__((THIS_FUNC, "WB layer skipped: %d bits", advance));
+	    advance -= (SB_SUBMODE_BITS+1);
+	    speex_bits_advance(bits, advance);
+
+	    bit_ptr = bits->bitPtr;
+	    char_ptr = bits->charPtr;
+
+	    /* Consecutive subband frames may not exceed 2 frames */
+	    if (++wb_count > 2)
+		return -1;
+	}
+
+	/* End of bits, return the frame */
+	if (speex_bits_remaining(bits) < 4) {
+	    TRACE__((THIS_FUNC, "End of stream"));
+	    return 0;
+	}
+
+	/* Stop iteration, return the frame */
+	if (nb_count > 0) {
+	    bits->bitPtr = bit_ptr;
+	    bits->charPtr = char_ptr;
+	    return 0;
+	}
+
+	/* Get control bits */
+	submode = speex_bits_unpack_unsigned(bits, 4);
+	TRACE__((THIS_FUNC, "Control bits: %d at %d", 
+		 submode, bits->charPtr*8+bits->bitPtr));
+
+	if (submode == 15) {
+	    TRACE__((THIS_FUNC, "Found submode: terminator"));
+	    return -1;
+	} else if (submode == 14) {
+	    /* in-band signal; next 4 bits contain signal id */
+	    submode = speex_bits_unpack_unsigned(bits, 4);
+	    TRACE__((THIS_FUNC, "Found submode: in-band %d bits", 
+		     inband_skip_table[submode]));
+	    speex_bits_advance(bits, inband_skip_table[submode]);
+	} else if (submode == 13) {
+	    /* user in-band; next 5 bits contain msg len */
+	    submode = speex_bits_unpack_unsigned(bits, 5);
+	    TRACE__((THIS_FUNC, "Found submode: user-band %d bytes", submode));
+	    speex_bits_advance(bits, submode * 8);
+	} else if (submode > 8) {
+	    TRACE__((THIS_FUNC, "Unknown sub-mode %d", submode));
+	    return -1;
+	} else {
+	    /* NB frame */
+	    unsigned int advance = submode;
+	    speex_mode_query(&speex_nb_mode, SPEEX_SUBMODE_BITS_PER_FRAME, &advance);
+	    if (advance < 0) {
+		TRACE__((THIS_FUNC, "Invalid mode encountered. "
+			 "The stream is corrupted."));
+		return -1;
+	    }
+	    TRACE__((THIS_FUNC, "Submode %d: %d bits", submode, advance));
+	    advance -= (NB_SUBMODE_BITS+1);
+	    speex_bits_advance(bits, advance);
+
+	    ++nb_count;
+	}
+    }
+
+    return 0;
+}
+
+
+/*
+ * Get frames in the packet.
+ */
+static pj_status_t  spx_codec_parse( pjmedia_codec *codec,
+				     void *pkt,
+				     pj_size_t pkt_size,
+				     const pj_timestamp *ts,
+				     unsigned *frame_cnt,
+				     pjmedia_frame frames[])
+{
+    struct spx_private *spx = (struct spx_private*) codec->codec_data;
+    unsigned samples_per_frame;
+    unsigned count = 0;
+    int char_ptr = 0;
+    int bit_ptr = 0;
+
+    samples_per_frame=spx_factory.speex_param[spx->param_id].samples_per_frame;
+
+    /* Copy the data into the speex bit-stream */
+    speex_bits_read_from(&spx->dec_bits, (char*)pkt, (int)pkt_size);
+
+    while (speex_get_next_frame(&spx->dec_bits) == 0 && 
+	   spx->dec_bits.charPtr != char_ptr)
+    {
+	frames[count].buf = (char*)pkt + char_ptr;
+	/* Bit info contains start bit offset of the frame */
+	frames[count].bit_info = bit_ptr;
+	frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
+	frames[count].timestamp.u64 = ts->u64 + count * samples_per_frame;
+	frames[count].size = spx->dec_bits.charPtr - char_ptr;
+	if (spx->dec_bits.bitPtr)
+	    ++frames[count].size;
+
+	bit_ptr = spx->dec_bits.bitPtr;
+	char_ptr = spx->dec_bits.charPtr;
+
+	++count;
+    }
+
+    *frame_cnt = count;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Encode frames.
+ */
+static pj_status_t spx_codec_encode( pjmedia_codec *codec, 
+				     const struct pjmedia_frame *input,
+				     unsigned output_buf_len, 
+				     struct pjmedia_frame *output)
+{
+    struct spx_private *spx;
+    unsigned samples_per_frame;
+    int tx = 0;
+    spx_int16_t *pcm_in = (spx_int16_t*)input->buf;
+    pj_size_t nsamples;
+
+    spx = (struct spx_private*) codec->codec_data;
+
+    if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) {
+	output->size = 0;
+	output->buf = NULL;
+	output->timestamp = input->timestamp;
+	output->type = input->type;
+	return PJ_SUCCESS;
+    }
+
+    nsamples = input->size >> 1;
+    samples_per_frame=spx_factory.speex_param[spx->param_id].samples_per_frame;
+
+    PJ_ASSERT_RETURN(nsamples % samples_per_frame == 0, 
+		     PJMEDIA_CODEC_EPCMFRMINLEN);
+
+    /* Flush all the bits in the struct so we can encode a new frame */
+    speex_bits_reset(&spx->enc_bits);
+
+    /* Encode the frames */
+    while (nsamples >= samples_per_frame) {
+	tx += speex_encode_int(spx->enc, pcm_in, &spx->enc_bits);
+	pcm_in += samples_per_frame;
+	nsamples -= samples_per_frame;
+    }
+
+    /* Check if we need not to transmit the frame (DTX) */
+    if (tx == 0) {
+	output->buf = NULL;
+	output->size = 0;
+	output->timestamp.u64 = input->timestamp.u64;
+	output->type = PJMEDIA_FRAME_TYPE_NONE;
+	return PJ_SUCCESS;
+    }
+
+    /* Check size. */
+    pj_assert(speex_bits_nbytes(&spx->enc_bits) <= (int)output_buf_len);
+
+    /* Copy the bits to an array of char that can be written */
+    output->size = speex_bits_write(&spx->enc_bits, 
+				    (char*)output->buf, output_buf_len);
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+    output->timestamp = input->timestamp;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Decode frame.
+ */
+static pj_status_t spx_codec_decode( pjmedia_codec *codec, 
+				     const struct pjmedia_frame *input,
+				     unsigned output_buf_len, 
+				     struct pjmedia_frame *output)
+{
+    struct spx_private *spx;
+    unsigned samples_per_frame;
+
+    spx = (struct spx_private*) codec->codec_data;
+    samples_per_frame=spx_factory.speex_param[spx->param_id].samples_per_frame;
+
+    PJ_ASSERT_RETURN(output_buf_len >= samples_per_frame << 1,
+		     PJMEDIA_CODEC_EPCMTOOSHORT);
+
+    if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) {
+	pjmedia_zero_samples((pj_int16_t*)output->buf, samples_per_frame);
+	output->size = samples_per_frame << 1;
+	output->timestamp.u64 = input->timestamp.u64;
+	output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+	return PJ_SUCCESS;
+    }
+
+    /* Copy the data into the bit-stream struct */
+    speex_bits_read_from(&spx->dec_bits, (char*)input->buf, (int)input->size);
+    
+    /* Set Speex dec_bits pointer to the start bit of the frame */
+    speex_bits_advance(&spx->dec_bits, input->bit_info);
+
+    /* Decode the data */
+    speex_decode_int(spx->dec, &spx->dec_bits, (spx_int16_t*)output->buf);
+
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+    output->size = samples_per_frame << 1;
+    output->timestamp.u64 = input->timestamp.u64;
+
+    return PJ_SUCCESS;
+}
+
+/* 
+ * Recover lost frame.
+ */
+static pj_status_t  spx_codec_recover(pjmedia_codec *codec, 
+				      unsigned output_buf_len, 
+				      struct pjmedia_frame *output)
+{
+    struct spx_private *spx;
+    unsigned count;
+
+    /* output_buf_len is unreferenced when building in Release mode */
+    PJ_UNUSED_ARG(output_buf_len);
+
+    spx = (struct spx_private*) codec->codec_data;
+
+    count = spx_factory.speex_param[spx->param_id].clock_rate * 20 / 1000;
+    pj_assert(count <= output_buf_len/2);
+
+    /* Recover packet loss */
+    speex_decode_int(spx->dec, NULL, (spx_int16_t*) output->buf);
+
+    output->size = count * 2;
+
+    return PJ_SUCCESS;
+}
+
+
+#endif	/* PJMEDIA_HAS_SPEEX_CODEC */