summaryrefslogtreecommitdiff
path: root/pjmedia/src/pjmedia-codec
diff options
context:
space:
mode:
Diffstat (limited to 'pjmedia/src/pjmedia-codec')
-rw-r--r--pjmedia/src/pjmedia-codec/amr_sdp_match.c176
-rw-r--r--pjmedia/src/pjmedia-codec/audio_codecs.c119
-rw-r--r--pjmedia/src/pjmedia-codec/ffmpeg_vid_codecs.c1818
-rw-r--r--pjmedia/src/pjmedia-codec/g722.c714
-rw-r--r--pjmedia/src/pjmedia-codec/g722/g722_dec.c549
-rw-r--r--pjmedia/src/pjmedia-codec/g722/g722_dec.h79
-rw-r--r--pjmedia/src/pjmedia-codec/g722/g722_enc.c576
-rw-r--r--pjmedia/src/pjmedia-codec/g722/g722_enc.h78
-rw-r--r--pjmedia/src/pjmedia-codec/g7221.c950
-rw-r--r--pjmedia/src/pjmedia-codec/g7221_sdp_match.c91
-rw-r--r--pjmedia/src/pjmedia-codec/gsm.c645
-rw-r--r--pjmedia/src/pjmedia-codec/h263_packetizer.c294
-rw-r--r--pjmedia/src/pjmedia-codec/h264_packetizer.c535
-rw-r--r--pjmedia/src/pjmedia-codec/ilbc.c883
-rw-r--r--pjmedia/src/pjmedia-codec/ipp_codecs.c1680
-rw-r--r--pjmedia/src/pjmedia-codec/l16.c729
-rw-r--r--pjmedia/src/pjmedia-codec/opencore_amrnb.c831
-rw-r--r--pjmedia/src/pjmedia-codec/passthrough.c1054
-rw-r--r--pjmedia/src/pjmedia-codec/speex_codec.c997
19 files changed, 12798 insertions, 0 deletions
diff --git a/pjmedia/src/pjmedia-codec/amr_sdp_match.c b/pjmedia/src/pjmedia-codec/amr_sdp_match.c
new file mode 100644
index 0000000..44b104b
--- /dev/null
+++ b/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/pjmedia/src/pjmedia-codec/audio_codecs.c b/pjmedia/src/pjmedia-codec/audio_codecs.c
new file mode 100644
index 0000000..caf63e5
--- /dev/null
+++ b/pjmedia/src/pjmedia-codec/audio_codecs.c
@@ -0,0 +1,119 @@
+/* $Id: audio_codecs.c 3910 2011-12-15 06:34:25Z nanang $ */
+/*
+ * 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
+ /* Register OpenCORE AMR-NB */
+ status = pjmedia_codec_opencore_amrnb_init(endpt);
+ if (status != PJ_SUCCESS)
+ return status;
+#endif
+
+ return PJ_SUCCESS;
+}
+
diff --git a/pjmedia/src/pjmedia-codec/ffmpeg_vid_codecs.c b/pjmedia/src/pjmedia-codec/ffmpeg_vid_codecs.c
new file mode 100644
index 0000000..d0e87ef
--- /dev/null
+++ b/pjmedia/src/pjmedia-codec/ffmpeg_vid_codecs.c
@@ -0,0 +1,1818 @@
+/* $Id: ffmpeg_vid_codecs.c 4089 2012-04-26 07:27:06Z nanang $ */
+/*
+ * 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)
+/* 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)
+# 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 = 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 = ff->enc_vafp.framebytes;
+ ff->enc_buf = pj_pool_alloc(ff->pool, ff->enc_buf_size);
+
+ ff->dec_buf_size = 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)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,
+ 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 = 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 = 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/pjmedia/src/pjmedia-codec/g722.c b/pjmedia/src/pjmedia-codec/g722.c
new file mode 100644
index 0000000..7e0e4e4
--- /dev/null
+++ b/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/pjmedia/src/pjmedia-codec/g722/g722_dec.c b/pjmedia/src/pjmedia-codec/g722/g722_dec.c
new file mode 100644
index 0000000..c6edc36
--- /dev/null
+++ b/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/pjmedia/src/pjmedia-codec/g722/g722_dec.h b/pjmedia/src/pjmedia-codec/g722/g722_dec.h
new file mode 100644
index 0000000..3cf841e
--- /dev/null
+++ b/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/pjmedia/src/pjmedia-codec/g722/g722_enc.c b/pjmedia/src/pjmedia-codec/g722/g722_enc.c
new file mode 100644
index 0000000..d0b2011
--- /dev/null
+++ b/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/pjmedia/src/pjmedia-codec/g722/g722_enc.h b/pjmedia/src/pjmedia-codec/g722/g722_enc.h
new file mode 100644
index 0000000..96f9af6
--- /dev/null
+++ b/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/pjmedia/src/pjmedia-codec/g7221.c b/pjmedia/src/pjmedia-codec/g7221.c
new file mode 100644
index 0000000..2d38483
--- /dev/null
+++ b/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/pjmedia/src/pjmedia-codec/g7221_sdp_match.c b/pjmedia/src/pjmedia-codec/g7221_sdp_match.c
new file mode 100644
index 0000000..21e5b2c
--- /dev/null
+++ b/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/pjmedia/src/pjmedia-codec/gsm.c b/pjmedia/src/pjmedia-codec/gsm.c
new file mode 100644
index 0000000..c84f241
--- /dev/null
+++ b/pjmedia/src/pjmedia-codec/gsm.c
@@ -0,0 +1,645 @@
+/* $Id: gsm.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/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;
+ unsigned 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/pjmedia/src/pjmedia-codec/h263_packetizer.c b/pjmedia/src/pjmedia-codec/h263_packetizer.c
new file mode 100644
index 0000000..a953242
--- /dev/null
+++ b/pjmedia/src/pjmedia-codec/h263_packetizer.c
@@ -0,0 +1,294 @@
+/* $Id: h263_packetizer.c 4006 2012-04-02 08:40:54Z nanang $ */
+/*
+ * 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 = 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 = 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 = q - bits;
+
+ pktz->unpack_prev_lost = PJ_FALSE;
+
+ return PJ_SUCCESS;
+}
+
+
+#endif /* PJMEDIA_HAS_VIDEO */
diff --git a/pjmedia/src/pjmedia-codec/h264_packetizer.c b/pjmedia/src/pjmedia-codec/h264_packetizer.c
new file mode 100644
index 0000000..59ca418
--- /dev/null
+++ b/pjmedia/src/pjmedia-codec/h264_packetizer.c
@@ -0,0 +1,535 @@
+/* $Id: h264_packetizer.c 4006 2012-04-02 08:40:54Z nanang $ */
+/*
+ * 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 = *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 = 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 + 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 = 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 = 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 = 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 = 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 = 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/pjmedia/src/pjmedia-codec/ilbc.c b/pjmedia/src/pjmedia-codec/ilbc.c
new file mode 100644
index 0000000..bbebdfe
--- /dev/null
+++ b/pjmedia/src/pjmedia-codec/ilbc.c
@@ -0,0 +1,883 @@
+/* $Id: ilbc.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/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;
+ unsigned 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/pjmedia/src/pjmedia-codec/ipp_codecs.c b/pjmedia/src/pjmedia-codec/ipp_codecs.c
new file mode 100644
index 0000000..c7a3a77
--- /dev/null
+++ b/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/pjmedia/src/pjmedia-codec/l16.c b/pjmedia/src/pjmedia-codec/l16.c
new file mode 100644
index 0000000..063abc9
--- /dev/null
+++ b/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/pjmedia/src/pjmedia-codec/opencore_amrnb.c b/pjmedia/src/pjmedia-codec/opencore_amrnb.c
new file mode 100644
index 0000000..9b6daa9
--- /dev/null
+++ b/pjmedia/src/pjmedia-codec/opencore_amrnb.c
@@ -0,0 +1,831 @@
+/* $Id: opencore_amrnb.c 3939 2012-01-10 05:38:40Z nanang $ */
+/*
+ * Copyright (C) 2011 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-NB codec implementation with OpenCORE AMRNB 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)
+
+#include <opencore-amrnb/interf_enc.h>
+#include <opencore-amrnb/interf_dec.h>
+#include <pjmedia-codec/amr_helper.h>
+#include <pjmedia-codec/opencore_amrnb.h>
+
+#define THIS_FILE "opencore_amrnb.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
+
+
+
+/* Prototypes for AMR-NB 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-NB 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-NB 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-NB 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_amrnb_deinit
+};
+
+
+/* AMR-NB factory */
+static struct amr_codec_factory
+{
+ pjmedia_codec_factory base;
+ pjmedia_endpt *endpt;
+ pj_pool_t *pool;
+} amr_codec_factory;
+
+
+/* AMR-NB codec private data. */
+struct amr_data
+{
+ pj_pool_t *pool;
+ 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;
+};
+
+static pjmedia_codec_amrnb_config def_config =
+{
+ PJ_FALSE, /* octet align */
+ 5900 /* bitrate */
+};
+
+
+
+/*
+ * Initialize and register AMR-NB codec factory to pjmedia endpoint.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_init( pjmedia_endpt *endpt )
+{
+ 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-NB codec factory. */
+ amr_codec_factory.base.op = &amr_factory_op;
+ amr_codec_factory.base.factory_data = NULL;
+ amr_codec_factory.endpt = endpt;
+
+ amr_codec_factory.pool = pjmedia_endpt_create_pool(endpt, "amrnb", 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;
+}
+
+
+/*
+ * Unregister AMR-NB codec factory from pjmedia endpoint and deinitialize
+ * the AMR-NB codec library.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_deinit(void)
+{
+ pjmedia_codec_mgr *codec_mgr;
+ pj_status_t status;
+
+ 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-NB 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_set_config(
+ const pjmedia_codec_amrnb_config *config)
+{
+ unsigned nbitrates;
+
+
+ def_config = *config;
+
+ /* Normalize bitrate. */
+ nbitrates = PJ_ARRAY_SIZE(pjmedia_codec_amrnb_bitrates);
+ if (def_config.bitrate < pjmedia_codec_amrnb_bitrates[0])
+ def_config.bitrate = pjmedia_codec_amrnb_bitrates[0];
+ else if (def_config.bitrate > pjmedia_codec_amrnb_bitrates[nbitrates-1])
+ def_config.bitrate = pjmedia_codec_amrnb_bitrates[nbitrates-1];
+ else
+ {
+ unsigned i;
+
+ for (i = 0; i < nbitrates; ++i) {
+ if (def_config.bitrate <= pjmedia_codec_amrnb_bitrates[i])
+ break;
+ }
+ def_config.bitrate = pjmedia_codec_amrnb_bitrates[i];
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Check if factory can allocate the specified codec.
+ */
+static pj_status_t amr_test_alloc( pjmedia_codec_factory *factory,
+ const pjmedia_codec_info *info )
+{
+ PJ_UNUSED_ARG(factory);
+
+ /* Check payload type. */
+ if (info->pt != PJMEDIA_RTP_PT_AMR)
+ return PJMEDIA_CODEC_EUNSUP;
+
+ /* Ignore the rest, since it's static payload type. */
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Generate default attribute.
+ */
+static pj_status_t amr_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 = def_config.bitrate;
+ attr->info.max_bps = pjmedia_codec_amrnb_bitrates[7];
+ attr->info.pcm_bits_per_sample = 16;
+ attr->info.frm_ptime = 20;
+ attr->info.pt = PJMEDIA_RTP_PT_AMR;
+
+ attr->setting.frm_per_pkt = 2;
+ attr->setting.vad = 1;
+ attr->setting.plc = 1;
+
+ if (def_config.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. only AMR-NB!).
+ */
+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);
+
+ pj_bzero(&codecs[0], sizeof(pjmedia_codec_info));
+ codecs[0].encoding_name = pj_str("AMR");
+ codecs[0].pt = PJMEDIA_RTP_PT_AMR;
+ codecs[0].type = PJMEDIA_TYPE_AUDIO;
+ codecs[0].clock_rate = 8000;
+ codecs[0].channel_cnt = 1;
+
+ *count = 1;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Allocate a new AMR-NB 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, "amrnb-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, 8000, 160, 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};
+
+ PJ_ASSERT_RETURN(codec && attr, PJ_EINVAL);
+ PJ_ASSERT_RETURN(amr_data != NULL, PJ_EINVALIDOP);
+
+ enc_mode = pjmedia_codec_amr_get_mode(attr->info.avg_bps);
+ pj_assert(enc_mode >= 0 && enc_mode <= 7);
+
+ /* 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<='7') {
+ 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->vad_enabled = (attr->setting.vad != 0);
+ amr_data->plc_enabled = (attr->setting.plc != 0);
+ amr_data->enc_mode = enc_mode;
+
+ amr_data->encoder = Encoder_Interface_init(amr_data->vad_enabled);
+ if (amr_data->encoder == NULL) {
+ TRACE_((THIS_FILE, "Encoder_Interface_init() 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 = 1;
+ setting->reorder = 0;
+ setting->octet_aligned = octet_align;
+ setting->cmr = 15;
+
+ amr_data->decoder = Decoder_Interface_init();
+ if (amr_data->decoder == NULL) {
+ TRACE_((THIS_FILE, "Decoder_Interface_init() 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 = 1;
+ setting->reorder = 0;
+ setting->octet_aligned = octet_align;
+
+ TRACE_((THIS_FILE, "AMR-NB codec allocated: vad=%d, plc=%d, bitrate=%d",
+ amr_data->vad_enabled, amr_data->plc_enabled,
+ pjmedia_codec_amrnb_bitrates[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) {
+ Encoder_Interface_exit(amr_data->encoder);
+ amr_data->encoder = NULL;
+ }
+
+ if (amr_data->decoder) {
+ Decoder_Interface_exit(amr_data->decoder);
+ amr_data->decoder = NULL;
+ }
+
+ TRACE_((THIS_FILE, "AMR-NB 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 (prev_vad_state != amr_data->vad_enabled) {
+ /* Reinit AMR encoder to update VAD setting */
+ TRACE_((THIS_FILE, "Reiniting AMR encoder to update VAD setting."));
+ Encoder_Interface_exit(amr_data->encoder);
+ amr_data->encoder = Encoder_Interface_init(amr_data->vad_enabled);
+ if (amr_data->encoder == NULL) {
+ TRACE_((THIS_FILE, "Encoder_Interface_init() failed"));
+ amr_codec_close(codec);
+ return PJMEDIA_CODEC_EFAILED;
+ }
+ }
+
+ TRACE_((THIS_FILE, "AMR-NB 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;
+
+ 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 <= 7 && amr_data->enc_mode != cmr) {
+ amr_data->enc_mode = cmr;
+ TRACE_((THIS_FILE, "AMR-NB encoder switched mode to %d (%dbps)",
+ amr_data->enc_mode,
+ pjmedia_codec_amrnb_bitrates[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;
+ int size;
+
+ pj_assert(amr_data != NULL);
+ PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
+
+ nsamples = input->size >> 1;
+ samples_per_frame = 160;
+ 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) {
+ size = Encoder_Interface_Encode (amr_data->encoder, amr_data->enc_mode,
+ speech, bitstream, 0);
+ if (size == 0) {
+ output->size = 0;
+ output->buf = NULL;
+ output->type = PJMEDIA_FRAME_TYPE_NONE;
+ TRACE_((THIS_FILE, "AMR-NB encode() failed"));
+ return PJMEDIA_CODEC_EFAILED;
+ }
+ nsamples -= 160;
+ speech += samples_per_frame;
+ bitstream += size;
+ out_size += size;
+ TRACE_((THIS_FILE, "AMR-NB 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;
+ frames[i].size = (info->frame_type <= 8)?
+ pjmedia_codec_amrnb_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*8000/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;
+ /* VA AMR-NB decoding buffer: AMR-NB max frame size + 1 byte header. */
+ unsigned char bitstream[32];
+
+ pj_assert(amr_data != NULL);
+ PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
+
+ if (output_buf_len < 320)
+ return PJMEDIA_CODEC_EPCMTOOSHORT;
+
+ input_.buf = &bitstream[1];
+ input_.size = 31; /* AMR-NB max frame size */
+ pjmedia_codec_amr_predecode(input, &amr_data->dec_setting, &input_);
+ info = (pjmedia_codec_amr_bit_info*)&input_.bit_info;
+
+ /* VA AMRNB decoder requires frame info in the first byte. */
+ bitstream[0] = (info->frame_type << 3) | (info->good_quality << 2);
+
+ TRACE_((THIS_FILE, "AMR-NB decode(): mode=%d, ft=%d, size=%d",
+ info->mode, info->frame_type, input_.size));
+
+ /* Decode */
+ Decoder_Interface_Decode(amr_data->decoder, bitstream,
+ (pj_int16_t*)output->buf, 0);
+
+ output->size = 320;
+ 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;
+
+ TRACE_((THIS_FILE, "amr_codec_recover"));
+
+ PJ_ASSERT_RETURN(amr_data->plc_enabled, PJ_EINVALIDOP);
+
+ PJ_ASSERT_RETURN(output_buf_len >= 320, PJMEDIA_CODEC_EPCMTOOSHORT);
+
+ pjmedia_plc_generate(amr_data->plc, (pj_int16_t*)output->buf);
+
+ output->size = 320;
+ 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
+# pragma comment( lib, "libopencore-amrnb.a")
+# else
+# error Unsupported OpenCORE AMR library, fix here
+# endif
+#endif
+
+#endif
diff --git a/pjmedia/src/pjmedia-codec/passthrough.c b/pjmedia/src/pjmedia-codec/passthrough.c
new file mode 100644
index 0000000..d315993
--- /dev/null
+++ b/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/pjmedia/src/pjmedia-codec/speex_codec.c b/pjmedia/src/pjmedia-codec/speex_codec.c
new file mode 100644
index 0000000..4623ef5
--- /dev/null
+++ b/pjmedia/src/pjmedia-codec/speex_codec.c
@@ -0,0 +1,997 @@
+/* $Id: speex_codec.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/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, 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;
+ unsigned 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, 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 */