Added AMR-NB codec based on 3GPP code converted to library by Stanislav Brabec.

git-svn-id: http://voip.null.ro/svn/yate@1984 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
paulc 2008-05-15 10:13:52 +00:00
parent 0d3d4aa509
commit 4b4d836687
4 changed files with 423 additions and 1 deletions

View File

@ -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

View File

@ -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@

369
modules/amrnbcodec.cpp Normal file
View File

@ -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 <yatephone.h>
extern "C" {
#include <interf_enc.h>
#include <interf_dec.h>
}
namespace RxTypes {
// There is a conflict between encoder and decoder so insulate in a namespace
#include <sp_dec.h>
};
// 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: */

View File

@ -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 <paulc@voip.null.ro>
- Disabled the newly added AMR-NB from building
* Mon Jan 07 2008 Paul Chitescu <paulc@voip.null.ro>
- Added Qt client