From c46ba3462c30eaa56a6d943e7963b3e877bf9bc4 Mon Sep 17 00:00:00 2001 From: Stefan Sayer Date: Thu, 23 Sep 2010 02:12:45 +0200 Subject: [PATCH] G.729 codec module (wrapper for Intel IPP 0.6) --- Makefile.defs | 2 +- core/amci/codecs.h | 2 + core/plug-in/g729/Makefile | 24 ++ core/plug-in/g729/Readme.g729.txt | 51 ++++ core/plug-in/g729/g729.c | 432 ++++++++++++++++++++++++++++++ 5 files changed, 510 insertions(+), 1 deletion(-) create mode 100644 core/plug-in/g729/Makefile create mode 100644 core/plug-in/g729/Readme.g729.txt create mode 100644 core/plug-in/g729/g729.c diff --git a/Makefile.defs b/Makefile.defs index b59c5454..54dfaf75 100644 --- a/Makefile.defs +++ b/Makefile.defs @@ -76,7 +76,7 @@ CPPFLAGS += -D_DEBUG \ # exclude some modules from compilation? # e.g. python modules: -exclude_core_modules = sipctrl +exclude_core_modules = sipctrl g729 #exclude_apps_modules = py_sems ivr mailbox pin_collect conf_auth mp3 examples # build in support for monitoring? diff --git a/core/amci/codecs.h b/core/amci/codecs.h index 4f7f4e50..bb2d7783 100644 --- a/core/amci/codecs.h +++ b/core/amci/codecs.h @@ -55,4 +55,6 @@ #define CODEC_L16 12 #define CODEC_G722_NB 13 + +#define CODEC_G729 14 #endif diff --git a/core/plug-in/g729/Makefile b/core/plug-in/g729/Makefile new file mode 100644 index 00000000..f32e97e6 --- /dev/null +++ b/core/plug-in/g729/Makefile @@ -0,0 +1,24 @@ +plug_in_name = g729 +COREPATH =../.. + +# adapt these paths to version/arch +IPPROOT?=/opt/intel/ipp/6.1.2.051/ia32/ +IPP_SAMPLES_PATH?=/opt/intel/ipp/ipp-samples/ +IPP_SAMPLES_ARCH=linux32_gcc4 + +###### + +IPP_LIB_PATH=$(IPPROOT)sharedlib/ + +IPP_INC = -I$(IPP_SAMPLES_PATH)speech-codecs/core/usc/include +IPP_LIB = -L$(IPP_SAMPLES_PATH)speech-codecs/_bin/$(IPP_SAMPLES_ARCH)/lib -lspeech -lcore \ + -L$(IPP_LIB_PATH) -lippsc -lipps + +module_ldflags = $(IPP_LIB) +module_cflags = $(IPP_INC) + +ifdef NOFPU + module_cflags += -DNOFPU +endif + +include ../Makefile.audio_module diff --git a/core/plug-in/g729/Readme.g729.txt b/core/plug-in/g729/Readme.g729.txt new file mode 100644 index 00000000..8b4b7ff4 --- /dev/null +++ b/core/plug-in/g729/Readme.g729.txt @@ -0,0 +1,51 @@ + + Intel IPP G.729 codec wrapper + ----------------------------- + + Copyright (c) 2007, Vadim Lebedev + Copyright (c) 2010, Stefan Sayer + + This is a wrapper for Intel IPP 0.6 G729 codec. + + How to build g729 module + ------------------------ + + 1. Get and install Intel IPP from: + http://software.intel.com/en-us/intel-ipp/ + (at least 3G disk space required) + + 2. Get IPP Code Samples (including USC) from + http://software.intel.com/en-us/articles/intel-integrated-performance-primitives-code-samples/ + Extract (preferably to /opt/intel/ipp/ipp-samples) + + 3. Build USC library + run build script in ipp-samples/speech-codecs + + 4. adapt paths in Makefile (especially IPP_SAMPLES_PATH/IPP_SAMPLES_ARCH) + + About Licensing + --------------- + + There is two distinct parts to G.729 licensing: The software licensing + and the codec (patents) licensing. + + A. Software licensing + + 1. The g729 codec wrapper code is licensed under simplified BSD license (see g729.c). + + 2. Intel IPP license is required to use Intel IPP software. + + 3. SEMS is licensed under GPL; the Intel IPP license is not a GPL compatible + license. Under the GPL, you may use (modify, compile, link, run, etc) SEMS code + included by the g729 module, but the GPL does NOT allow redistribution of + binaries linked with code licensed under the IPP license terms. + + If you are interested in distributing SEMS binaries with the g729 module + (g729.so), please contact info@iptel.org to obtain a commercial license (see + doc/COPYING for details). + + B. Codec licensing + + To use G.729 codec, in many countries you need to get a license to use the + patents (at least until 2016 or so). The G.729 patent pool is managed by + Sipro: http://www.sipro.com/ diff --git a/core/plug-in/g729/g729.c b/core/plug-in/g729/g729.c new file mode 100644 index 00000000..f0435a80 --- /dev/null +++ b/core/plug-in/g729/g729.c @@ -0,0 +1,432 @@ +/* + + This file is part of SEMS, a free SIP media server. + + Copyright (c) 2007, Vadim Lebedev + Copyright (c) 2010, Stefan Sayer + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + Notes: + + This is a wrapper for Intel IPP 0.6 G729 codec. + +*/ + +#ifndef TEST +#define _GNU_SOURCE +#include +#include +#include "amci.h" +#include "codecs.h" +#include "../../log.h" +#else +#include +#define ERROR printf +#endif + + +#include +#include + + +static int pcm16_2_g729(unsigned char* out_buf, unsigned char* in_buf, unsigned int size, + unsigned int channels, unsigned int rate, long h_codec ); + +static int g729_2_pcm16(unsigned char* out_buf, unsigned char* in_buf, unsigned int size, + unsigned int channels, unsigned int rate, long h_codec ); + +static long g729_create(const char* format_parameters, amci_codec_fmt_info_t* format_description); +static void g729_destroy(long h_codec); + +static unsigned int g729_bytes2samples(long, unsigned int); +static unsigned int g729_samples2bytes(long, unsigned int); + +#define G729_PAYLOAD_ID 18 +#define G729_BYTES_PER_FRAME 10 +#define G729_SAMPLES_PER_FRAME 10 + +#ifndef TEST +BEGIN_EXPORTS( "g729", AMCI_NO_MODULEINIT, AMCI_NO_MODULEDESTROY) + + BEGIN_CODECS + CODEC( CODEC_G729, pcm16_2_g729, g729_2_pcm16, AMCI_NO_CODEC_PLC, + (amci_codec_init_t)g729_create, (amci_codec_destroy_t)g729_destroy, + g729_bytes2samples, g729_samples2bytes + ) + END_CODECS + + BEGIN_PAYLOADS + PAYLOAD( G729_PAYLOAD_ID, "G729", 8000, 1, CODEC_G729, AMCI_PT_AUDIO_FRAME ) + END_PAYLOADS + + BEGIN_FILE_FORMATS + END_FILE_FORMATS + +END_EXPORTS + +#endif + +struct stream +{ + USC_Handle handle; + int nBanks; + USC_MemBank* banks; +}; + +struct G729_codec +{ + struct stream dec, enc; + USC_CodecInfo pInfo; +}; + + +extern USC_Fxns USC_G729I_Fxns; +USC_Fxns *fns = &USC_G729I_Fxns; + +static int +stream_alloc(USC_CodecInfo *info, struct stream *md, const char *name) +{ + + int i; + + if (USC_NoError != fns->std.NumAlloc(&info->params, &md->nBanks)) + { + ERROR("g729_stream_alloc: can't query memory reqirements for %s\n", name); + return -1; + } + + /* allocate memory for memory bank table */ + md->banks = (USC_MemBank*)malloc(sizeof(USC_MemBank)*md->nBanks); + + /* Query how big has to be each block */ + if (USC_NoError != fns->std.MemAlloc(&info->params, md->banks)) + { + ERROR("g729_stream_alloc: can't query memory bank size for %s\n", name); + return -1; + } + + + /* allocate memory for each block */ + for(i=0; inBanks; i++) + { + md->banks[i].pMem = (char*)malloc(md->banks[i].nbytes); + } + + return 0; +} + +static void +stream_free(struct stream *st) +{ + int i; + + for(i = 0; i < st->nBanks; i++) + free(st->banks[i].pMem); + + free(st->banks); + +} + + +static int +stream_create(USC_CodecInfo *info, struct stream *st, const char *name) +{ + if (stream_alloc(info, st, name)) + { + return -1; + } + + /* Create encoder instance */ + if(USC_NoError != fns->std.Init(&info->params, st->banks, &st->handle)) + { + ERROR("g729_stream_create: can't intialize stream %s\n", name); + stream_free(st); + return -1; + } + + return 0; +} + + +static void +stream_destroy(struct stream *st) +{ + stream_free(st); +} + + +long g729_create(const char* format_parameters, amci_codec_fmt_info_t* format_description) +{ + USC_CodecInfo pInfo; + struct G729_codec *codec; + + + if (USC_NoError != fns->std.GetInfo((USC_Handle)NULL, &pInfo)) + { + ERROR("g729: Can't query codec info\n"); + return (0); + } + + + pInfo.params.direction = 0; /* Direction: encode */ + pInfo.params.modes.vad = 0; /* Suppress a silence compression */ + pInfo.params.law = 0; /* Linear PCM input */ + pInfo.params.modes.bitrate = 8000; /* pInfo.pRateTbl[pInfo.nRates-1].bitrate; */ /* Set highest bitrate */ + + + codec = calloc(sizeof(struct G729_codec), 1); + + if (stream_create(&pInfo, &codec->enc, "encoder")) + { + free(codec); + return 0; + } + + + pInfo.params.direction = 1; /* Direction: decode */ + + if (stream_create(&pInfo, &codec->dec, "decoder")) + { + stream_destroy(&codec->enc); + free(codec); + return 0; + } + + codec->pInfo = pInfo; + return (long) codec; +} + + +static void +g729_destroy(long h_codec) +{ + struct G729_codec *codec = (struct G729_codec *) h_codec; + + if (h_codec) + return; + + stream_destroy(&codec->dec); + stream_destroy(&codec->enc); + free(codec); +} + + + +static int pcm16_2_g729(unsigned char* out_buf, unsigned char* in_buf, unsigned int size, + unsigned int channels, unsigned int rate, long h_codec ) +{ + div_t blocks; + struct G729_codec *codec = (struct G729_codec *) h_codec; + int out_size = 0; + int err; + + if (!h_codec) + return -1; + + blocks = div(size, codec->pInfo.params.framesize); + + if (blocks.rem) + { + ERROR("pcm16_2_G729: number of blocks should be integral (block size = %d)\n", + codec->pInfo.params.framesize); + return -1; + } + + + + while(size >= codec->pInfo.params.framesize) + { + USC_PCMStream in; + USC_Bitstream out; + + + /* Set input stream params */ + in.bitrate = codec->pInfo.params.modes.bitrate; + in.nbytes = size; + in.pBuffer = (char*)in_buf; + in.pcmType = codec->pInfo.params.pcmType; + + /* Set output buffer */ + out.pBuffer = (char*)out_buf; + + /* Encode a frame */ + err = fns->Encode (codec->enc.handle, &in, &out); + if (USC_NoError != err) + { + ERROR("pcm16_2_G729: error %d encoding\n", err); + return -1; + } + + size -= in.nbytes; + in_buf += in.nbytes; + + out_buf += out.nbytes; + out_size += out.nbytes; + } + + return out_size; +} + +static int g729_2_pcm16(unsigned char* out_buf, unsigned char* in_buf, unsigned int size, + unsigned int channels, unsigned int rate, long h_codec ) +{ + /* div_t blocks; */ + unsigned int out_size = 0; + int frameSize = 0; + int x; + struct G729_codec *codec = (struct G729_codec *) h_codec; + int err; + + if (!h_codec) + return -1; + + for(x = 0; x < size; x += frameSize) + { + USC_PCMStream out; + USC_Bitstream in; + + + in.pBuffer = (char*)in_buf; + in.nbytes = size; + in.bitrate = codec->pInfo.params.modes.bitrate; + in.frametype = 3; + + out.pcmType = codec->pInfo.params.pcmType; + out.pBuffer = (char*)out_buf; + + + err = fns->Decode (codec->dec.handle, &in, &out); + if (USC_NoError != err) + { + ERROR("g729_2_pcm16: error %d decoding data\n", err); + break; + } + + in_buf += in.nbytes; + frameSize = in.nbytes; + + out_buf += out.nbytes; + out_size += out.nbytes; + } + + return out_size; +} + +static unsigned int g729_bytes2samples(long h_codec, unsigned int num_bytes) { + return (G729_SAMPLES_PER_FRAME * num_bytes) / G729_BYTES_PER_FRAME; +} + +static unsigned int g729_samples2bytes(long h_codec, unsigned int num_samples) { + return G729_BYTES_PER_FRAME * num_samples / G729_SAMPLES_PER_FRAME; +} + +#ifdef TEST +#define N 160 + +#include +#include +#include + +int inpcm[N]; +unsigned char outg729[1024]; +int outpcm[N]; + + +void encodeFile(const char *iname, const char *oname) +{ + int ifd = open(iname, O_RDONLY); + int ofd = open(oname, O_WRONLY|O_CREAT, 0666); + short ibuf[ 2 * 8000/100 ]; + unsigned char obuf[1024]; + int h = g729_create(); + + int ilen, olen; + + while(0 <= (ilen = read(ifd, ibuf, sizeof(ibuf)))) + { + olen = pcm16_2_g729(obuf, ibuf, ilen, 1, 8000, h); + + if (olen <= 0) + break; + + write(ofd, obuf, olen); + } + + g729_destroy(h); + close(ofd); + close(ifd); +} + + + + +void decodeFile(const char *iname, const char *oname) +{ + int ifd = open(iname, O_RDONLY); + int ofd = open(oname, O_WRONLY|O_CREAT, 0666); + short obuf[ 2 * 8000/100 ]; + unsigned char ibuf[20]; + int h = g729_create(); + + int ilen, olen; + + while(0 <= (ilen = read(ifd, ibuf, sizeof(ibuf)))) + { + olen = g729_2_pcm16(obuf, ibuf, ilen, 1, 8000, h); + + if (olen <= 0) + break; + + write(ofd, obuf, olen); + } + + g729_destroy(h); + close(ofd); + close(ifd); +} + + +int main(int argc, char *argv[]) +{ + + if (!strcmp(argv[1], "-e")) + { + printf("encoding %s to %s\n", argv[2], argv[3]); + encodeFile(argv[2], argv[3]); + } + else if (!strcmp(argv[1], "-d")) + { + printf("decoding %s to %s\n", argv[2], argv[3]); + decodeFile(argv[2], argv[3]); + } + + + return(0); + + + +} +#endif