summaryrefslogtreecommitdiff
path: root/pjmedia/src/pjmedia-codec/gsm.c
diff options
context:
space:
mode:
Diffstat (limited to 'pjmedia/src/pjmedia-codec/gsm.c')
-rw-r--r--pjmedia/src/pjmedia-codec/gsm.c450
1 files changed, 450 insertions, 0 deletions
diff --git a/pjmedia/src/pjmedia-codec/gsm.c b/pjmedia/src/pjmedia-codec/gsm.c
new file mode 100644
index 00000000..cc41612a
--- /dev/null
+++ b/pjmedia/src/pjmedia-codec/gsm.c
@@ -0,0 +1,450 @@
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia-codec/gsm.h>
+#include <pjmedia/codec.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/endpoint.h>
+#include <pj/assert.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/os.h>
+#include "gsm/gsm.h"
+
+/* 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_default_attr(pjmedia_codec *codec,
+ pjmedia_codec_param *attr);
+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_get_frames( pjmedia_codec *codec,
+ void *pkt,
+ pj_size_t pkt_size,
+ 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);
+
+/* Definition for GSM codec operations. */
+static pjmedia_codec_op gsm_op =
+{
+ &gsm_codec_default_attr,
+ &gsm_codec_init,
+ &gsm_codec_open,
+ &gsm_codec_close,
+ &gsm_codec_get_frames,
+ &gsm_codec_encode,
+ &gsm_codec_decode
+};
+
+/* 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
+};
+
+/* 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_private
+{
+ int dummy;
+};
+
+
+
+/*
+ * 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. */
+ 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_memset(attr, 0, sizeof(pjmedia_codec_param));
+ attr->sample_rate = 8000;
+ attr->avg_bps = 13200;
+ attr->pcm_bits_per_sample = 16;
+ attr->ptime = 20;
+ attr->pt = PJMEDIA_RTP_PT_GSM;
+
+ /* Default all 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_memset(&codecs[0], 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].sample_rate = 8000;
+
+ *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;
+
+ 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(gsm_codec_factory.pool,
+ sizeof(pjmedia_codec));
+ PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM);
+ }
+
+ pj_mutex_unlock(gsm_codec_factory.mutex);
+
+ pj_assert(codec->codec_data == NULL);
+
+ *p_codec = codec;
+ return PJ_SUCCESS;
+}
+
+/*
+ * Free codec.
+ */
+static pj_status_t gsm_dealloc_codec( pjmedia_codec_factory *factory,
+ pjmedia_codec *codec )
+{
+ PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
+ PJ_ASSERT_RETURN(factory == &gsm_codec_factory.base, PJ_EINVAL);
+
+ /* Close codec, if it's not closed. */
+ if (codec->codec_data != NULL) {
+ gsm_destroy(codec->codec_data);
+ codec->codec_data = NULL;
+ }
+
+ /* 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;
+}
+
+/*
+ * Get codec default attributes.
+ */
+static pj_status_t gsm_codec_default_attr( pjmedia_codec *codec,
+ pjmedia_codec_param *attr)
+{
+ return gsm_default_attr( codec->factory, NULL, attr);
+}
+
+/*
+ * Init codec.
+ */
+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 )
+{
+ pj_assert(codec->codec_data == NULL);
+
+ PJ_UNUSED_ARG(attr);
+
+ codec->codec_data = gsm_create();
+ if (!codec->codec_data)
+ return PJMEDIA_CODEC_EFAILED;
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Close codec.
+ */
+static pj_status_t gsm_codec_close( pjmedia_codec *codec )
+{
+ pj_assert(codec->codec_data != NULL);
+
+ if (codec->codec_data) {
+ gsm_destroy(codec->codec_data);
+ codec->codec_data = NULL;
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Get frames in the packet.
+ */
+static pj_status_t gsm_codec_get_frames( pjmedia_codec *codec,
+ void *pkt,
+ pj_size_t pkt_size,
+ 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[0].type = PJMEDIA_FRAME_TYPE_AUDIO;
+ frames[0].buf = pkt;
+ frames[0].size = 33;
+
+ 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)
+{
+ PJ_ASSERT_RETURN(codec && codec->codec_data && input && output,
+ PJ_EINVAL);
+
+ if (output_buf_len < 33)
+ return PJMEDIA_CODEC_EFRMTOOSHORT;
+
+ if (input->size < 320)
+ return PJMEDIA_CODEC_EPCMTOOSHORT;
+
+ gsm_encode(codec->codec_data, (const short*)input->buf,
+ (unsigned char*)output->buf);
+
+ output->size = 33;
+ output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+
+ 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)
+{
+ PJ_ASSERT_RETURN(codec && codec->codec_data && input && output,
+ PJ_EINVAL);
+
+ if (output_buf_len < 320)
+ return PJMEDIA_CODEC_EPCMTOOSHORT;
+
+ if (input->size < 33)
+ return PJMEDIA_CODEC_EFRMTOOSHORT;
+
+ gsm_decode(codec->codec_data,
+ (const unsigned char*)input->buf,
+ (short*)output->buf);
+
+ output->size = 320;
+ output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+
+ return PJ_SUCCESS;
+}