diff --git a/configure.in b/configure.in index 96fd4ab5..f4e76495 100644 --- a/configure.in +++ b/configure.in @@ -446,6 +446,7 @@ HAVE_ALSA="yes" AC_MSG_RESULT([$HAVE_ALSA]) AC_SUBST(HAVE_ALSA) + HAVE_GSM=no GSM_INC="" AC_ARG_WITH(libgsm,AC_HELP_STRING([--with-libgsm],[use GSM codec if available (default)]),[ac_cv_use_libgsm=$withval],[ac_cv_use_libgsm=yes]) @@ -466,6 +467,7 @@ fi AC_SUBST(HAVE_GSM) AC_SUBST(GSM_INC) + HAVE_ILBC=no ILBC_INC="" AC_ARG_ENABLE(ilbc,AC_HELP_STRING([--enable-ilbc],[Enable iLBC codec (default: yes)]),want_ilbc=$enableval,want_ilbc=yes) @@ -516,6 +518,31 @@ fi AC_SUBST(HAVE_SPEEX) AC_SUBST(SPEEX_INC) + +HAVE_AMRNB=no +AMRNB_INC="" +AMRNB_LIB="" +AC_ARG_WITH(amrnb,AC_HELP_STRING([--with-amrnb=DIR],[use AMR-NB if available (default)]),[ac_cv_use_amrnb=$withval],[ac_cv_use_amrnb=/usr]) +if [[ "x$ac_cv_use_amrnb" != "xno" ]]; then +AC_MSG_CHECKING([for AMR-NB in $ac_cv_use_amrnb]) +local_lib="$ARCHLIB" +amrinc="$ac_cv_use_amrnb/include/amrnb" +test -f "$ac_cv_use_amrnb/$local_lib/libamrnb.so" || local_lib="lib" +if [[ -f "$ac_cv_use_amrnb/$local_lib/libamrnb.so" -a -f "$amrinc/interf_rom.h" ]]; then + HAVE_AMRNB=yes + AMRNB_LIB="-L$ac_cv_use_amrnb/$local_lib -lamrnb" + AMRNB_INC="-I$amrinc" +fi +AC_MSG_RESULT([$HAVE_AMRNB]) +if [[ "x$HAVE_AMRNB" != "xyes" -a "x$YATE_VER" != "xno" ]]; then + AC_ERROR([Could not find AMR-NB devel files]) +fi +fi +AC_SUBST(HAVE_AMRNB) +AC_SUBST(AMRNB_INC) +AC_SUBST(AMRNB_LIB) + + HAVE_SPANDSP=no SPANDSP_INC="" AC_ARG_WITH(spandsp,AC_HELP_STRING([--with-spandsp],[use spandsp library if available (default)]),[ac_cv_use_spandsp=$withval],[ac_cv_use_spandsp=yes]) @@ -564,6 +591,7 @@ fi AC_SUBST(HAVE_SPANDSP) AC_SUBST(SPANDSP_INC) + HAVE_PWLIB=no PWLIB_RTTI=none PWLIB_INC="" @@ -623,6 +651,7 @@ fi AC_MSG_RESULT([$HAVE_PWLIB $verpw RTTI: $PWLIB_RTTI]) fi + HAVE_H323=no H323_INC="" H323_LIB="" @@ -673,6 +702,7 @@ AC_SUBST(H323_INC) AC_SUBST(H323_LIB) AC_SUBST(H323_RUN) + HAVE_OPENSSL=no OPENSSL_INC="" OPENSSL_LIB="" @@ -703,6 +733,7 @@ AC_SUBST(OPENSSL_INC) AC_SUBST(OPENSSL_LIB) AC_SUBST(OPENSSL_VER) + HAVE_GTK2=no GTK2_INC="" GTK2_LIB="" @@ -726,6 +757,7 @@ AC_SUBST(HAVE_GTK2) AC_SUBST(GTK2_INC) AC_SUBST(GTK2_LIB) + HAVE_GMOZ=no GMOZ_INC="" GMOZ_LIB="" @@ -762,6 +794,7 @@ AC_SUBST(HAVE_GMOZ) AC_SUBST(GMOZ_INC) AC_SUBST(GMOZ_LIB) + HAVE_QT4=no QT4_INC="" QT4_LIB="" @@ -792,6 +825,7 @@ AC_SUBST(QT4_LIB) AC_SUBST(QT4_MOC) AC_SUBST(QT4_VER) + HAVE_COREDUMPER=no COREDUMPER_INC="" COREDUMPER_LIB="" @@ -816,6 +850,7 @@ AC_SUBST(HAVE_COREDUMPER) AC_SUBST(COREDUMPER_INC) AC_SUBST(COREDUMPER_LIB) + RTTI_OPT="" AC_ARG_ENABLE(rtti,AC_HELP_STRING([--enable-rtti],[Enable RTTI support (default: like pwlib)]),want_rtti=$enableval,want_rtti=auto) AC_MSG_CHECKING([whether to enable RTTI support]) @@ -848,6 +883,7 @@ AC_MSG_WARN([Mismatched RTTI setting between Yate ($USE_RTTI) and pwlib ($PWLIB_ fi fi + INSTALL_D="install -D" MODULE_CFLAGS="-fno-exceptions -fPIC $HAVE_GCC_FORMAT_CHECK" MODULE_CPPFLAGS="-fno-check-new $RTTI_OPT $MODULE_CFLAGS" @@ -867,6 +903,7 @@ AC_SUBST(MODULE_LDRELAX) AC_SUBST(MODULE_LDFLAGS) AC_SUBST(MODULE_SYMBOLS) + DOXYGEN_BIN="" AC_ARG_WITH(doxygen,AC_HELP_STRING([--with-doxygen=EXE],[use doxygen to generate API docs (default: PATH)]),[ac_cv_use_doxygen=$withval],[ac_cv_use_doxygen=yes]) if [[ "x$ac_cv_use_doxygen" != "xno" ]]; then @@ -882,6 +919,7 @@ AC_MSG_RESULT([$ac_cv_use_doxygen]) fi AC_SUBST(DOXYGEN_BIN) + KDOC_BIN="" AC_ARG_WITH(kdoc,AC_HELP_STRING([--with-kdoc=EXE],[use kdoc to generate API docs (default: PATH)]),[ac_cv_use_kdoc=$withval],[ac_cv_use_kdoc=yes]) if [[ "x$ac_cv_use_kdoc" != "xno" ]]; then @@ -897,6 +935,7 @@ AC_MSG_RESULT([$ac_cv_use_kdoc]) fi AC_SUBST(KDOC_BIN) + m4_sinclude(./YateLocal.ac) AC_CONFIG_FILES([packing/rpm/yate.spec diff --git a/modules/Makefile.in b/modules/Makefile.in index b9bf2cae..ce8ae918 100644 --- a/modules/Makefile.in +++ b/modules/Makefile.in @@ -92,6 +92,10 @@ ifneq (@HAVE_SPEEX@,no) PROGS := $(PROGS) speexcodec.yate endif +ifneq (@HAVE_AMRNB@,no) +PROGS := $(PROGS) amrnbcodec.yate +endif + ifneq (@HAVE_OPENSSL@,no) PROGS := $(PROGS) openssl.yate endif @@ -246,6 +250,9 @@ gsmcodec.yate: LOCALFLAGS = @GSM_INC@ speexcodec.yate: LOCALLIBS = -lspeex speexcodec.yate: LOCALFLAGS = @SPEEX_INC@ +amrnbcodec.yate: LOCALFLAGS = @AMRNB_INC@ +amrnbcodec.yate: LOCALLIBS = @AMRNB_LIB@ + faxchan.yate: LOCALLIBS = -lspandsp faxchan.yate: LOCALFLAGS = @SPANDSP_INC@ diff --git a/modules/amrnbcodec.cpp b/modules/amrnbcodec.cpp new file mode 100644 index 00000000..1824dd9b --- /dev/null +++ b/modules/amrnbcodec.cpp @@ -0,0 +1,369 @@ +/** + * amrnbcodec.cpp + * This file is part of the YATE Project http://YATE.null.ro + * + * AMR narrowband transcoder implemented using 3GPP codec + * + * Yet Another Telephony Engine - a fully featured software PBX and IVR + * Copyright (C) 2004-2007 Null Team + * Author: Paul Chitescu + * + * AMR codec library by Stanislav Brabec at http://www.penguin.cz/~utx/amr + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include + +extern "C" { +#include +#include +} +namespace RxTypes { +// There is a conflict between encoder and decoder so insulate in a namespace +#include +}; + +// IF1/GP3 is Bandwidth-Efficient Mode +// IF2 is Octet-aligned Mode (not supported here) + +using namespace TelEngine; +namespace { // anonymous + +#define MODNAME "amrnbcodec" + +// Transcoding voice size, 20ms of 8kHz slin data +#define SAMPLES_FRAME 160 + +// Transcoding buffer size, 2 bytes per sample +#define BUFFER_SIZE (2*SAMPLES_FRAME) + +// Maximum compressed frame size +#define MAX_AMRNB_SIZE 32 + +// Maximum number of frames we are willing to decode in a packet +#define MAX_PKT_FRAMES 4 + +class AmrPlugin : public Plugin, public TranslatorFactory +{ +public: + AmrPlugin(); + ~AmrPlugin(); + virtual void initialize() {} + virtual bool isBusy() const; + virtual DataTranslator* create(const DataFormat& sFormat, const DataFormat& dFormat); + virtual const TranslatorCaps* getCapabilities() const; +}; + +class AmrTrans : public DataTranslator +{ +public: + AmrTrans(const char* sFormat, const char* dFormat, void* amrState); + virtual ~AmrTrans(); + virtual void Consume(const DataBlock& data, unsigned long tStamp); + inline bool valid() const + { return 0 != m_amrState; } +protected: + bool dataError(const char* text = 0); + virtual bool pushData(unsigned long& tStamp) = 0; + void* m_amrState; + DataBlock m_data; + bool m_showError; + Mode m_cmr; +}; + +// Encoding specific class +class AmrEncoder : public AmrTrans +{ +public: + inline AmrEncoder(const char* sFormat, const char* dFormat, bool discont = false) + : AmrTrans(sFormat,dFormat,::Encoder_Interface_init(discont ? 1 : 0)), + m_mode(MR122) + { } + virtual ~AmrEncoder(); +protected: + virtual bool pushData(unsigned long& tStamp); + Mode m_mode; +}; + +// Decoding specific class +class AmrDecoder : public AmrTrans +{ +public: + inline AmrDecoder(const char* sFormat, const char* dFormat) + : AmrTrans(sFormat,dFormat,::Decoder_Interface_init()) + { } + virtual ~AmrDecoder(); +protected: + virtual bool pushData(unsigned long& tStamp); +}; + +// Module data +static int count = 0; // Created objects + +static TranslatorCaps caps[] = { + { 0, 0 }, + { 0, 0 }, + { 0, 0 } +}; + +// Voice bits per mode 0-7, 8 = Silence, 15 = No Data +static int modeBits[16] = { + 95, 103, 118, 134, 148, 159, 204, 244, 39, + -1, -1, -1, -1, -1, -1, 0 +}; + +// Discontinuous Transmission (DTX) +bool s_discontinuous = false; + + +// Helper function, gets a number of bits and advances pointer, return -1 for error +static int getBits(unsigned const char*& ptr, int& len, int& bpos, unsigned char bits) +{ + if (!ptr) + return -1; + if (!bits) + return 0; + int ret = 0; + int mask = 0x80; + while (bits--) { + if (len <= 0) + return -1; + if (((ptr[0] >> (7 - bpos)) & 1) != 0) + ret |= mask; + mask = mask >> 1; + if (++bpos >= 8) { + bpos = 0; + ptr++; + len--; + } + } + return ret; +} + + +// Arbitrary type transcoder constructor +AmrTrans::AmrTrans(const char* sFormat, const char* dFormat, void* amrState) + : DataTranslator(sFormat,dFormat), + m_amrState(amrState), m_showError(true), m_cmr(MR122) +{ + Debug(MODNAME,DebugAll,"AmrTrans::AmrTrans('%s','%s',%p) [%p]", + sFormat,dFormat,amrState,this); + count++; +} + +// Destructor, closes the channel +AmrTrans::~AmrTrans() +{ + Debug(MODNAME,DebugAll,"AmrTrans::~AmrTrans() [%p]",this); + m_amrState = 0; + count--; +} + +// Actual transcoding of data +void AmrTrans::Consume(const DataBlock& data, unsigned long tStamp) +{ + if (!(m_amrState && getTransSource())) + return; + ref(); + m_data += data; + if (!tStamp) + tStamp = timeStamp() + SAMPLES_FRAME; + while (pushData(tStamp)) + ; + deref(); +} + +// Data error, report error 1st time and clear buffer +bool AmrTrans::dataError(const char* text) +{ + if (m_showError) { + m_showError = false; + const char* prefix = ": "; + if (!text) + prefix = text = ""; + Debug(MODNAME,DebugWarn,"Error transcoding data%s%s [%p]",prefix,text,this); + } + m_data.clear(); + return false; +} + + +// Encoder cleanup +AmrEncoder::~AmrEncoder() +{ + Debug(MODNAME,DebugAll,"AmrEncoder::~AmrEncoder() %p [%p]",m_amrState,this); + if (m_amrState) + ::Encoder_Interface_exit(m_amrState); +} + +// Encode accumulated slin data and push it to the consumer +bool AmrEncoder::pushData(unsigned long& tStamp) +{ + if (m_data.length() < BUFFER_SIZE) + return false; + + unsigned char unpacked[MAX_AMRNB_SIZE+1]; + int len = ::Encoder_Interface_Encode(m_amrState,m_mode,(short*)m_data.data(),unpacked,0); + if ((len <= 0) || (len > MAX_AMRNB_SIZE)) + return dataError("encoder"); + unpacked[len] = 0; + XDebug(MODNAME,DebugAll,"Encoded mode %d frame to %d bytes first %02x [%p]", + m_mode,len,unpacked[0],this); + unsigned char buffer[MAX_AMRNB_SIZE]; + // build a TOC with just one entry + // 4 bit CMR, 1 bit follows (forced 0), 3 bits of mode + buffer[0] = (m_cmr << 4) | ((unpacked[0] >> 4) & 0x07); + // 1 bit of mode and 1 bit Q + unsigned char leftover = (unpacked[0] << 4) & 0xc0; + for (int i = 1; i < len; i++) { + buffer[i] = leftover | (unpacked[i] >> 2); + leftover = (unpacked[i] << 6) & 0xc0; + } + m_data.cut(-BUFFER_SIZE); + DataBlock outData(buffer,len,false); + getTransSource()->Forward(outData,tStamp); + outData.clear(false); + tStamp += SAMPLES_FRAME; + return (0 != m_data.length()); +} + + +// Decoder cleanup +AmrDecoder::~AmrDecoder() +{ + Debug(MODNAME,DebugAll,"AmrDecoder::~AmrDecoder() %p [%p]",m_amrState,this); + if (m_amrState) + ::Decoder_Interface_exit(m_amrState); +} + +// Decode AMR data and push it to the consumer +bool AmrDecoder::pushData(unsigned long& tStamp) +{ + if (m_data.length() < 2) + return false; + unsigned const char* ptr = (unsigned const char*)m_data.data(); + int len = m_data.length(); + int bpos = 0; + unsigned char cmr = getBits(ptr,len,bpos,4) >> 4; + unsigned int tocLen = 0; + unsigned char toc[MAX_PKT_FRAMES]; + int dataBits = 0; + // read the TOC + for (;;) { + int ft = getBits(ptr,len,bpos,6); + if (ft < 0) + return dataError("TOC truncated"); + int nBits = modeBits[(ft >> 3) & 0x0f]; + // discard the entire packet if an invalid frame is found + if (nBits < 0) + return dataError("invalid mode"); + dataBits += nBits; + toc[tocLen++] = ft & 0x7c; // keep type and quality bit + // does another TOC follow? + if (0 == (ft & 0x80)) + break; + if (tocLen >= MAX_PKT_FRAMES) + return dataError("TOC too large"); + } + if (dataBits > (8*len - bpos)) + return dataError("data truncated"); + // We read the TOC, now pick the following voice frames and decode + for (unsigned int idx = 0; idx < tocLen; idx++) { + int mode = (toc[idx] >> 3) & 0x0f; + bool good = 0 != (toc[idx] & 0x04); + int nBits = modeBits[mode]; + XDebug(MODNAME,DebugAll,"Decoding %d bits %s mode %d frame %u [%p]", + nBits,(good ? "good" : "bad"),mode,idx,this); + unsigned char unpacked[MAX_AMRNB_SIZE]; + unpacked[0] = toc[idx]; + for (unsigned int i = 1; i < MAX_AMRNB_SIZE; i++) { + int bits = (nBits <= 8) ? nBits : 8; + unpacked[i] = getBits(ptr,len,bpos,bits); + nBits -= bits; + } + short buffer[SAMPLES_FRAME]; + int type = (MRDTX == mode) ? + (good ? RxTypes::RX_SID_UPDATE : RxTypes::RX_SID_BAD) : + (good ? RxTypes::RX_SPEECH_GOOD : RxTypes::RX_SPEECH_DEGRADED); + ::Decoder_Interface_Decode(m_amrState,unpacked,buffer,type); + DataBlock outData(buffer,BUFFER_SIZE,false); + getTransSource()->Forward(outData,tStamp); + outData.clear(false); + tStamp += SAMPLES_FRAME; + } + if (bpos) + len--; + // now len holds how many bytes we should keep in data buffer + m_data.cut(len-(int)m_data.length()); + if (cmr != m_cmr) { + Debug(MODNAME,DebugNote,"Remote CMR changed from %d to %d [%p]", + m_cmr,cmr,this); + m_cmr = (Mode)cmr; + // TODO: find and notify paired encoder about the mode change request + } + return (0 != m_data.length()); +} + + +// Plugin and translator factory +AmrPlugin::AmrPlugin() +{ + Output("Loaded module AMR-NB codec - based on 3GPP code"); + const FormatInfo* f = FormatRepository::addFormat("amr",0,20000); + caps[0].src = caps[1].dest = f; + caps[0].dest = caps[1].src = FormatRepository::getFormat("slin"); + // FIXME: put proper conversion costs + caps[0].cost = caps[1].cost = 5; +} + +AmrPlugin::~AmrPlugin() +{ + Output("Unloading module AMR-NB with %d codecs still in use",count); +} + +bool AmrPlugin::isBusy() const +{ + return (count != 0); +} + +// Create transcoder instance for requested formats +DataTranslator* AmrPlugin::create(const DataFormat& sFormat, const DataFormat& dFormat) +{ + if (sFormat == "slin" && dFormat == "amr") + return new AmrEncoder(sFormat,dFormat,s_discontinuous); + else if (sFormat == "amr" && dFormat == "slin") + return new AmrDecoder(sFormat,dFormat); + else return 0; +} + +const TranslatorCaps* AmrPlugin::getCapabilities() const +{ + return caps; +} + + +INIT_PLUGIN(AmrPlugin); + +UNLOAD_PLUGIN(unloadNow) +{ + if (unloadNow) + return !__plugin.isBusy(); + return true; +} + +}; // anonymous namespace + +/* vi: set ts=8 sw=4 sts=4 noet: */ diff --git a/packing/rpm/yate.spec.in b/packing/rpm/yate.spec.in index 2aa07da6..4688616d 100644 --- a/packing/rpm/yate.spec.in +++ b/packing/rpm/yate.spec.in @@ -412,7 +412,11 @@ chmod +x %{local_find_requires} %{local_find_provides} %endif %build -./configure --prefix=%{prefix} --sysconfdir=%{_sysconfdir} --datadir=%{_datadir} --includedir=%{_includedir} --libdir=%{_libdir} --mandir=%{_mandir} --with-archlib=%{_lib} --without-libspeex --without-spandsp --without-coredumper +./configure --prefix=%{prefix} --sysconfdir=%{_sysconfdir} \ + --datadir=%{_datadir} --includedir=%{_includedir} \ + --libdir=%{_libdir} --mandir=%{_mandir} --with-archlib=%{_lib} \ + --without-libspeex --without-amrnb \ + --without-spandsp --without-coredumper make strip %install @@ -428,6 +432,9 @@ rm -rf %{buildroot} %changelog +* Thu May 15 2008 Paul Chitescu +- Disabled the newly added AMR-NB from building + * Mon Jan 07 2008 Paul Chitescu - Added Qt client