From 93fc9bd99172cb248ee5ca8a7fc93776e8967f97 Mon Sep 17 00:00:00 2001 From: Emmanuel Milou <emmanuel.milou@savoirfairelinux.comemmanuel.milou@savoirfairelinux.com> Date: Thu, 24 Jan 2008 09:25:21 -0500 Subject: [PATCH] Dynamic loading of audio codecs Note: Manually copy the .so library in /usr/lib , then ldconfig --- src/audio/Makefile.am | 8 +-- src/audio/alaw.cpp | 8 +-- src/audio/audiocodec.h | 3 +- src/audio/audiofile.cpp | 29 +++++++-- src/audio/audiofile.h | 4 +- src/audio/audiortp.cpp | 118 ++++++++++++++++++++-------------- src/audio/audiortp.h | 3 + src/audio/codecDescriptor.cpp | 50 +++++++------- src/audio/codecDescriptor.h | 27 ++++---- src/audio/codec_alaw.so | Bin 11881 -> 11538 bytes src/audio/codec_gsm.so | Bin 12024 -> 11685 bytes src/audio/codec_ulaw.so | Bin 11857 -> 11522 bytes src/audio/gsmcodec.cpp | 8 +-- src/audio/ulaw.cpp | 7 +- src/call.cpp | 6 +- src/call.h | 13 ++-- src/global.h | 10 +++ src/iaxcall.cpp | 20 ++++-- src/iaxvoiplink.cpp | 88 ++++++++++++++++++++----- src/managerimpl.cpp | 39 +++++------ src/managerimpl.h | 4 +- src/sipcall.cpp | 32 +++++---- src/sipvoiplink.cpp | 16 ++--- 23 files changed, 311 insertions(+), 182 deletions(-) diff --git a/src/audio/Makefile.am b/src/audio/Makefile.am index 04c7127315..73333764de 100644 --- a/src/audio/Makefile.am +++ b/src/audio/Makefile.am @@ -12,9 +12,9 @@ SPEEX_FLAG= SPEEX_LIB= endif -libaudio_la_SOURCES = audiofile.cpp g711.cpp tonelist.cpp \ +libaudio_la_SOURCES = audiofile.cpp tonelist.cpp \ audiortp.cpp dtmf.cpp tone.cpp audiolayer.cpp audiodevice.cpp dtmfgenerator.cpp gsmcodec.cpp \ -tonegenerator.cpp ulaw.cpp codecDescriptor.cpp \ +tonegenerator.cpp codecDescriptor.cpp \ audioloop.cpp ringbuffer.cpp $(SPEEX_SOURCES_CPP) AM_CXXFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/libs $(libccext2_CFLAGS) $(libdbuscpp_CFLAGS) $(libccrtp1_CFLAGS) $(USER_INCLUDES) @@ -22,8 +22,8 @@ AM_CXXFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/libs $(libccext2_CFLAGS) $ libaudio_la_CPPFLAGS = $(SPEEX_FLAG) -noinst_HEADERS = audioloop.h common.h ringbuffer.h audiofile.h g711.h \ +noinst_HEADERS = audioloop.h common.h ringbuffer.h audiofile.h \ tonelist.h audiortp.h audiocodec.h audiolayer.h audiodevice.h \ - dtmfgenerator.h gsmcodec.h tonegenerator.h ulaw.h \ + dtmfgenerator.h gsmcodec.h tonegenerator.h \ codecDescriptor.h dtmf.h tone.h \ CodecSpeex.h diff --git a/src/audio/alaw.cpp b/src/audio/alaw.cpp index 52d8177a54..cb9c810824 100644 --- a/src/audio/alaw.cpp +++ b/src/audio/alaw.cpp @@ -76,10 +76,6 @@ virtual int codecEncode (unsigned char *dst, short *src, unsigned int size) return size; } -virtual void test() -{ -printf("MON OSTIE ALAW!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); -} uint8 ALawEncode (int16 pcm16) { @@ -124,11 +120,11 @@ uint8 ALawEncode (int16 pcm16) }; // the class factories -extern "C" AudioCodec* create_alaw() { +extern "C" AudioCodec* create() { return new Alaw(); } -extern "C" void destroy_alaw(AudioCodec* a) { +extern "C" void destroy(AudioCodec* a) { delete a; } diff --git a/src/audio/audiocodec.h b/src/audio/audiocodec.h index cf0b636e56..399e9416df 100644 --- a/src/audio/audiocodec.h +++ b/src/audio/audiocodec.h @@ -3,6 +3,7 @@ #include <string> #include <iostream> +#include <dlfcn.h> class AudioCodec { protected: @@ -41,7 +42,6 @@ public: */ virtual int codecDecode(short *, unsigned char *, unsigned int) = 0; virtual int codecEncode(unsigned char *, short *, unsigned int) = 0; - virtual void test()=0; /** Returns description for GUI usage */ std::string getDescription() { return _description; } @@ -53,6 +53,7 @@ public: unsigned int getChannel() { return _channel; } bool isActive() { return _active; } void setActive(bool active) { _active = active; } + }; diff --git a/src/audio/audiofile.cpp b/src/audio/audiofile.cpp index bae4379c42..d96dc3852e 100644 --- a/src/audio/audiofile.cpp +++ b/src/audio/audiofile.cpp @@ -25,20 +25,40 @@ #include <fstream> #include <math.h> #include <samplerate.h> - +#include <dlfcn.h> AudioFile::AudioFile() : AudioLoop() { // could vary later... - _ulaw = new Ulaw(PAYLOAD_CODEC_ULAW); + //_ulaw = new Ulaw(PAYLOAD_CODEC_ULAW); _start = false; + + using std::cout; + using std::cerr; + void* codec = dlopen("codec_ulaw.so", RTLD_LAZY); + if(!codec){ + cerr<<"cannot load library: "<< dlerror() <<'\n'; + } + dlerror(); + create_t* create_codec = (create_t*)dlsym(codec, "create"); + const char* dlsym_error = dlerror(); + if(dlsym_error){ + cerr << "Cannot load symbol create: " << dlsym_error << '\n'; + } + destroy_t* destroy_codec = (destroy_t*) dlsym(codec, "destroy"); + dlsym_error = dlerror(); + if(dlsym_error){ + cerr << "Cannot load symbol destroy" << dlsym_error << '\n'; + } + + _ulaw = create_codec(); } AudioFile::~AudioFile() { - delete _ulaw; + delete _ulaw; } // load file in mono format @@ -82,6 +102,7 @@ AudioFile::loadFile(const std::string& filename, unsigned int sampleRate=8000) file.read (fileBuffer,length); file.close(); + // Decode file.ul // expandedsize is the number of bytes, not the number of int // expandedsize should be exactly two time more, else failed @@ -145,7 +166,7 @@ AudioFile::loadFile(const std::string& filename, unsigned int sampleRate=8000) _buffer = bufferTmp; // just send the buffer pointer; bufferTmp = 0; } - + return true; } diff --git a/src/audio/audiofile.h b/src/audio/audiofile.h index 25773ad62e..e476fe98ff 100644 --- a/src/audio/audiofile.h +++ b/src/audio/audiofile.h @@ -24,7 +24,7 @@ #define __AUDIOFILE_H__ #include "audioloop.h" -#include "ulaw.h" +#include "audiocodec.h" /** @author Yan Morin <yan.morin@savoirfairelinux.com> @@ -42,7 +42,7 @@ public: private: std::string _filename; - Ulaw* _ulaw; + AudioCodec* _ulaw; bool _start; }; diff --git a/src/audio/audiortp.cpp b/src/audio/audiortp.cpp index 920741d37b..f91414d530 100644 --- a/src/audio/audiortp.cpp +++ b/src/audio/audiortp.cpp @@ -107,8 +107,10 @@ AudioRtpRTX::AudioRtpRTX (SIPCall *sipcall, bool sym) _sym = sym; // AudioRtpRTX should be close if we change sample rate - _codecSampleRate = _ca->getAudioCodec()->getClockRate(); - + //_codecSampleRate = _ca->getAudioCodec()->getClockRate(); + _codecSampleRate = 8000; + + // TODO: Change bind address according to user settings. // TODO: this should be the local ip not the external (router) IP std::string localipConfig = _ca->getLocalIp(); // _ca->getLocalIp(); @@ -212,15 +214,19 @@ AudioRtpRTX::initAudioRtpSession (void) return; } - AudioCodec* audiocodec = _ca->getAudioCodec(); + //AudioCodec* audiocodec = _ca->getAudioCodec(); + CodecType audiocodec = _ca->getAudioCodec(); bool payloadIsSet = false; if (audiocodec) { - if (audiocodec->hasDynamicPayload()) { - payloadIsSet = _sessionRecv->setPayloadFormat(ost::DynamicPayloadFormat((ost::PayloadType) audiocodec->getPayload(), audiocodec->getClockRate())); - } else { - payloadIsSet= _sessionRecv->setPayloadFormat(ost::StaticPayloadFormat((ost::StaticPayloadType) audiocodec->getPayload())); - payloadIsSet = _sessionSend->setPayloadFormat(ost::StaticPayloadFormat((ost::StaticPayloadType) audiocodec->getPayload())); - } + /*if (audiocodec->hasDynamicPayload()) { + //payloadIsSet = _sessionRecv->setPayloadFormat(ost::DynamicPayloadFormat((ost::PayloadType) audiocodec->getPayload(), audiocodec->getClockRate())); + payloadIsSet = _sessionRecv->setPayloadFormat(ost::DynamicPayloadFormat((ost::PayloadType) audiocodec, 8000)); + } else {*/ + //payloadIsSet= _sessionRecv->setPayloadFormat(ost::StaticPayloadFormat((ost::StaticPayloadType) audiocodec->getPayload())); + //payloadIsSet = _sessionSend->setPayloadFormat(ost::StaticPayloadFormat((ost::StaticPayloadType) audiocodec->getPayload())); + payloadIsSet= _sessionRecv->setPayloadFormat(ost::StaticPayloadFormat((ost::StaticPayloadType) audiocodec)); + payloadIsSet = _sessionSend->setPayloadFormat(ost::StaticPayloadFormat((ost::StaticPayloadType) audiocodec)); + //} } _sessionSend->setMark(true); } else { @@ -231,14 +237,16 @@ AudioRtpRTX::initAudioRtpSession (void) return; } - AudioCodec* audiocodec = _ca->getAudioCodec(); + //AudioCodec* audiocodec = _ca->getAudioCodec(); + CodecType audiocodec = _ca->getAudioCodec(); bool payloadIsSet = false; if (audiocodec) { - if (audiocodec->hasDynamicPayload()) { + /*if (audiocodec->hasDynamicPayload()) { payloadIsSet = _session->setPayloadFormat(ost::DynamicPayloadFormat((ost::PayloadType) audiocodec->getPayload(), audiocodec->getClockRate())); - } else { - payloadIsSet = _session->setPayloadFormat(ost::StaticPayloadFormat((ost::StaticPayloadType) audiocodec->getPayload())); - } + } else {*/ + //payloadIsSet = _session->setPayloadFormat(ost::StaticPayloadFormat((ost::StaticPayloadType) audiocodec->getPayload())); + payloadIsSet = _session->setPayloadFormat(ost::StaticPayloadFormat((ost::StaticPayloadType) audiocodec)); + //} } } } catch(...) { @@ -246,35 +254,51 @@ AudioRtpRTX::initAudioRtpSession (void) throw; } } - -void -AudioRtpRTX::sendSessionFromMic(int timestamp) + +AudioCodec* +AudioRtpRTX::loadCodec(int payload) { using std::cout; - using std::cerr; -void* codec = dlopen("codec_alaw.so", RTLD_LAZY); -if(!codec){ - cerr<<"cannot load library: "<< dlerror() <<'\n'; -} + using std::cerr; + void* codec; + -//reset errors -dlerror(); + switch(payload){ + case 0: + codec = dlopen("codec_ulaw.so", RTLD_LAZY); + break; + case 3: + codec = dlopen("codec_gsm.so", RTLD_LAZY); + break; + case 8: + codec = dlopen("codec_alaw.so", RTLD_LAZY); + break; + } + + if(!codec){ + cerr<<"cannot load library: "<< dlerror() <<'\n'; + } + dlerror(); + create_t* create_codec = (create_t*)dlsym(codec, "create"); + const char* dlsym_error = dlerror(); + if(dlsym_error){ + cerr << "Cannot load symbol create: " << dlsym_error << '\n'; + } + destroy_t* destroy_codec = (destroy_t*) dlsym(codec, "destroy"); + dlsym_error = dlerror(); + if(dlsym_error){ + cerr << "Cannot load symbol destroy" << dlsym_error << '\n'; + } + return create_codec(); -//load the symbols -create_t* create_codec = (create_t*)dlsym(codec, "create_alaw"); -const char* dlsym_error = dlerror(); -if(dlsym_error){ - cerr << "Cannot load symbol create: " << dlsym_error << '\n'; -} -destroy_t* destroy_codec = (destroy_t*) dlsym(codec, "destroy_alaw"); -dlsym_error = dlerror(); -if(dlsym_error){ - cerr << "Cannot load symbol destroy" << dlsym_error << '\n'; } -int pl = 0; -AudioCodec* audiocodec = create_codec(); -audiocodec->test(); + +void +AudioRtpRTX::sendSessionFromMic(int timestamp) +{ + + AudioCodec* audiocodec = loadCodec(_ca->getAudioCodec()); // STEP: // 1. get data from mic @@ -289,7 +313,6 @@ try { AudioLayer* audiolayer = Manager::instance().getAudioDriver(); if (!audiolayer) { _debug(" !ARTP: No audiolayer available for mic\n"); return; } - //AudioCodec* audiocodec = _ca->getAudioCodec(); //AudioCodec* audiocodec = _ca->getAudioCodec(); if (!audiocodec) { _debug(" !ARTP: No audiocodec available for mic\n"); return; } @@ -338,8 +361,8 @@ try { } -destroy_codec(audiocodec); -dlclose(codec); +//destroy_codec(audiocodec); +//dlclose(codec); } @@ -369,7 +392,7 @@ try { unsigned char* data = (unsigned char*)adu->getData(); // data in char unsigned int size = adu->getSize(); // size in char -using std::cout; +/*using std::cout; using std::cerr; void* codec = dlopen("codec_alaw.so", RTLD_LAZY); if(!codec){ @@ -380,21 +403,20 @@ if(!codec){ dlerror(); //load the symbols -create_t* create_codec = (create_t*)dlsym(codec, "create_alaw"); +create_t* create_codec = (create_t*)dlsym(codec, "create"); const char* dlsym_error = dlerror(); if(dlsym_error){ cerr << "Cannot load symbol create: " << dlsym_error << '\n'; } -destroy_t* destroy_codec = (destroy_t*) dlsym(codec, "destroy_alaw"); +destroy_t* destroy_codec = (destroy_t*) dlsym(codec, "destroy"); dlsym_error = dlerror(); if(dlsym_error){ cerr << "Cannot load symbol destroy" << dlsym_error << '\n'; } -int pl = 0; AudioCodec* audiocodec = create_codec(); -//audiocodec1->test(); - +*/ +AudioCodec* audiocodec = loadCodec(payload); // Decode data with relevant codec //AudioCodec* audiocodec = _ca->getCodecMap().getCodec((CodecType)payload); _codecSampleRate = audiocodec->getClockRate(); @@ -446,8 +468,8 @@ AudioCodec* audiocodec = create_codec(); } delete adu; adu = NULL; -destroy_codec(audiocodec); -dlclose(codec); +//destroy_codec(audiocodec); +//dlclose(codec); } catch(...) { _debugException("! ARTP: receiving failed"); throw; diff --git a/src/audio/audiortp.h b/src/audio/audiortp.h index 2444be5676..09dda4c205 100644 --- a/src/audio/audiortp.h +++ b/src/audio/audiortp.h @@ -119,6 +119,9 @@ class AudioRtpRTX : public ost::Thread, public ost::TimerPort { * @return int The number of samples after the operation */ int downSampleData(int, int); + + + AudioCodec* loadCodec(int payload); }; /////////////////////////////////////////////////////////////////////////////// diff --git a/src/audio/codecDescriptor.cpp b/src/audio/codecDescriptor.cpp index e9a0e0db85..92cf39c6aa 100644 --- a/src/audio/codecDescriptor.cpp +++ b/src/audio/codecDescriptor.cpp @@ -23,63 +23,63 @@ #include "audiocodec.h" #include "gsmcodec.h" -//#include "alaw.h" -#include "ulaw.h" #include "codecDescriptor.h" -#ifdef HAVE_SPEEX +/*#ifdef HAVE_SPEEX #include "CodecSpeex.h" -#endif +#endif*/ -CodecDescriptorMap::CodecDescriptorMap() +CodecDescriptor::CodecDescriptor() { - //_codecMap[PAYLOAD_CODEC_ALAW] = new Alaw(); - _codecMap[PAYLOAD_CODEC_ALAW] = new Ulaw(); - _codecMap[PAYLOAD_CODEC_ULAW] = new Ulaw(); - _codecMap[PAYLOAD_CODEC_GSM] = new Gsm(); + // Default codecs + _codecMap[PAYLOAD_CODEC_ALAW] = "PCMA"; + _codecMap[PAYLOAD_CODEC_ULAW] = "PCMU"; + _codecMap[PAYLOAD_CODEC_GSM] = "GSM"; #ifdef HAVE_SPEEX - _codecMap[PAYLOAD_CODEC_SPEEX] = new CodecSpeex(PAYLOAD_CODEC_SPEEX); // TODO: this is a variable payload! + //_codecMap[PAYLOAD_CODEC_SPEEX] = new CodecSpeex(PAYLOAD_CODEC_SPEEX); // TODO: this is a variable payload! #endif // theses one are not implemented yet.. // _codecMap[PAYLOAD_CODEC_ILBC] = Ilbc(); // _codecMap[PAYLOAD_CODEC_SPEEX] = Speex(); } -AudioCodec* -CodecDescriptorMap::getCodec(CodecType payload) +std::string& +CodecDescriptor::getCodecName(CodecType payload) { CodecMap::iterator iter = _codecMap.find(payload); if (iter!=_codecMap.end()) { return (iter->second); } - return NULL; + //return ; } -void -CodecDescriptorMap::setActive(const std::string& codecDescription) +bool +CodecDescriptor::setActive(CodecType payload) { CodecMap::iterator iter = _codecMap.begin(); while(iter!=_codecMap.end()) { - if (iter->second!=0) { - if (iter->second->getDescription() == codecDescription) { - iter->second->setActive(true); - break; + if (iter->first == payload) { + // codec is already in the map --> nothing to do + _debug("Codec with payload %i already in the map\n", payload); + //break; + return true; } - } iter++; } + + ///TODO: add the codec in the activ codecs list + return false; } void -CodecDescriptorMap::setInactive(const std::string& codecDescription) +CodecDescriptor::setInactive(CodecType payload) { CodecMap::iterator iter = _codecMap.begin(); while(iter!=_codecMap.end()) { - if (iter->second!=0) { - if (iter->second->getDescription() == codecDescription) { - iter->second->setActive(false); + if (iter->first == payload) { + ///TODO : erase the codec from the list () + //iter->second->setActive(false); break; } - } iter++; } diff --git a/src/audio/codecDescriptor.h b/src/audio/codecDescriptor.h index e001862adb..193ea88933 100644 --- a/src/audio/codecDescriptor.h +++ b/src/audio/codecDescriptor.h @@ -2,6 +2,7 @@ * Copyright (C) 2004-2005 Savoir-Faire Linux inc. * Author: Yan Morin <yan.morin@savoirfairelinux.com> * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.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 @@ -25,6 +26,7 @@ #include <string> #include <map> +#include "../global.h" typedef enum { // http://www.iana.org/assignments/rtp-parameters // http://www.gnu.org/software/ccrtp/doc/refman/html/formats_8h.html#a0 @@ -45,33 +47,36 @@ typedef enum { } CodecType; #include "audiocodec.h" -typedef std::map<CodecType, AudioCodec*> CodecMap; -class CodecDescriptorMap { +/* A codec is identified by its payload. A payload is associated with a name. */ +typedef std::map<CodecType, std::string> CodecMap; + +class CodecDescriptor { public: /** * Initialize all codec */ - CodecDescriptorMap(); - ~CodecDescriptorMap() {}; - CodecMap getMap() { return _codecMap; } + CodecDescriptor(); + ~CodecDescriptor() {}; + CodecMap& getCodecMap() { return _codecMap; } /** * Get codec with is associated payload * @param payload the payload associated with the payload * same as getPayload() - * @return the address of the codec or 0 + * @return the name of the codec */ - AudioCodec* getCodec(CodecType payload); + std::string& getCodecName(CodecType payload); /** - * Get codec with is associated payload - * Put a codec active, with it's codec's _description + * Put a codec active, with its codec's _description * O(n) if not found where n is the number of element * @param codecDescription is the same as with getCodec(number)->getDescription() */ - void setActive(const std::string& codecDescription); - void setInactive(const std::string& codecDescription); + //void setActive(const std::string& codecName); + bool setActive(CodecType payload); + //void setInactive(const std::string& codecName); + void setInactive(CodecType payload); private: CodecMap _codecMap; }; diff --git a/src/audio/codec_alaw.so b/src/audio/codec_alaw.so index 4fd5503da3ba915f1a67fac93488c80142c776cc..48ed19010a3b631d572d2630789887954b97ad86 100755 GIT binary patch delta 4519 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 delta 4827 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 TBL^|ZtE#e+x{-2C*-QTe<B*zI diff --git a/src/audio/codec_gsm.so b/src/audio/codec_gsm.so index 832e6ddff6b8903494b819f128231876b48fa17d..fe907692027f6e0dae596df4e0d5afac6d6b7422 100755 GIT binary patch delta 4583 zcmZ8k3vg7`89wLU>?RwMu*rsO-up<h2@n#*3b9iJiaeUq!jwlrDhPxI6i9@$K)bqO z2pXMKf+spmi_^5)3JgPq(3WXK%fk+8al{cY&d`}U18qP=P_eW;w%>Q}xpBQ`=il#q z|9|dz{^vjE+<f>qi@OUm$7T!hH6g@nWjmtLmFt5-P(08GzYr482HsX}OCc0th*#SU zU>EFNuu}%WDYU0S=3H!R8Vm^GNF2KOjy?5N^~f-lK+++jAy%cp2!tuP<4*N`s8p0f zhEao5YlHP)jQ^3E=L72?1&~6uNRF08DmSx4#?-H5$f~glmR!gPNEqUz;mC|pAIe~& z#!&$|G7MByLNXvus?Zr9a1?|!7C|^#4#YTcVQc>u|LlwfW4Cp7*f#c)11*DuAnXq7 zi9wu%wyTB)!-}26nVBE_<DWiy?~|Ov2S@xz^%~)DK~LVhlivQ<FTVT4693d`XRl<R z>Y1@8J@4ILhSQC!*MH(ugGQ-bqdaDP;vJ_VAXZcy<fwEa|Er6C<l+g=gib8~H-`Yp zC%MW``l{U&n}wQgmXDk1s%PF;%ZVaaJ-ehDZ<ebb)1Ui=%d|tyPYWfsyR;rx1C_8h zqKEX5DEx<>(F9*)$`e`Q6WEvJ2(ifG!>qxa%CgL``@{j%*QH&;FRmwVqkJ#EXOW1= zPKb?Av|;`CLBR%&;Der&4|<_Lj0QGpdn3!ELkZ2#0ms7o9Q$Lif1YB$3wt<a0GGjc z50y_06MJ1foB)0ZJ*2;)hvK)WFq|iZ56BMI!M+c6?rz#wKz|DRKMbA+`#RYBVTa1H zFMz!hyioH)z|#&_()&%7JEl-Id-to6>DN_N#(DFex_i}d-$+vkQ%&^UmyqIl)QK@z zZOn;n#x^#^ap;&Ne!bdGz9F<4+sY7cX?rFnk7EgHjA@*!aU#Y>`>Pr^Ay|-i)Y#Z5 zYgA6=9}>e+-oV6-CSptriCD(9L<EklM7YT=A~tdr$`W7FI06G8zoc<B7M*-A%OjlR zvK;UkjgsZS&)S#}^Z5Z31?VX8w8ju7ll&+ul668fpaSA+8sqqm{5{wWOwGSFnrsOC z+ZvZ@{Hewh8sE1N{`Q$xWUyg;xJF|_;{)s|WEC+JF&Zex2yt6u56Y2mryZSsN#k%1 z47^O^e(Dj{PHL>9J^{rHW`MaIG4Mrl92~C@A$9Bs{BkDIE5tG)V&!%s<haJ)5#gVg zh={jeXzW70vRsI6VkPz=F)GAPU_v55@1g=n0C%v2L%%{qoZU@?znsy?{Ub5Whl#Z~ z&4>ur=ZO&^{z-(BUC_7*{v+YlO{l{Qzg<Oy|F0n;<iu-4xk!hh9g1`)=09_B?$#@5 zgYZ+ac*}{MCF+UnplnbtWKWk9)cNeC#{0!;V$RyKZhSC!JDE(Le6TWuE)9g!0n;iQ z)ybS%qr0Z>M$T#3_kq9AFxms^v%FP=hXY%4yOGe+g1Lh~`(HA-<-~aPabUckXW!tV z|0a`*7QcC7#@z4Vge+6>{8@=js1}137hu~3Ov6X70~>)?fXjjT*f~E29>riz1HS=2 z^Hwss3V0OQ18hKlj{=ti2Z3EcKgws_L3v;oa22rOF3JOWvLmV4BO;#3XgjQq6@+}V zplSe{M&@Y)TcD1OsP;^UE+4v*0A@q5e_hZpe5SoDRZFmJ_%-AQ9Np8lt`z;KM1QSv zk5N~ox(YI6t2$Cp;c10+J#rx*>)Z*}rm}<8p4X9kL){;&_FhA-^p^TzFy<*mmv^Zh zF!MCquZ{#yK01!`hJ=Mm+BrA`G7Nk~1~|kT!J~zNl}8Yl@eJg~-DFEG><q{Q_=Dq% zL2&FqaSq5!4|WnlrVy<)>S|%Zlzvo${UT;z7sK~OD{QFIihDfam@v65S+Ju=EvQ9Y z1Mbfd0+Z0<!<tVh=3UK6dFWLXLKo|(?t8JQm>bEy`S5GIyY>L0U`+w_R&jaeO>~6g zhzI&UEj}PiI6gB+M(QIIABr?L*Vkh^ODQ}C%KxQwZ9dDJJ{gHb5P~g3JfJ3(71i;> zs}N4iMh-<MXF~bmZAcup_CViLW$UC|sBV;pe4IK;zbXrbGC6USYlfO0D$nG6QqCD_ zU8vm0@l%$l7eXN)XNNLb9RcH9Qd-r;P{_*}?$1)0;i#8gJC&uzA<G`z&QcGBqds1l zt!uN|77lsYZ47ZQ4DD7A5d+lY`2jT;&hic2d)1wA$k&8eL1|M}k&t%)%QaA-8Y5A! zb-fO%^~knkW!i)4=g4*<+Zj~Hk+tsEL3KG2^)S#hsmCJ$RT=erTftfj)z_m@ueE-K zYI!uOdLpH&H|jUK3e~}A%(ENuZ;sWcYf#=SQbtA0C@xW>01uR??^VP+GqDpVTSdAn z{6=S~`Zbiiq&}kFuBh-lGE#_X>PE$c1m~N{6n2I)ZgV=RJ>@p1bJ%lkb2_;_>o%ve z+w*R7L~|t0K9gyAwC!~^#{%*0#D!ootzBZTgw45IY&WvGU66}nbM8L4$Pl+TE*FH! z6gVT1xGYSjwezr8daIb$wrj%b3@Nz(utY=LuG!gJrm$<~f-#xa%Yz%);dV=0Hzrfy zJVffMQO{L9x7=x0qTMQ}on|Gr{k`B$vl82Xvh|rAZzQg3vuW4WN>sZ5oF2<$AgZhZ zc4w<6W93ugy7z2m8`K>`JWo6ienwA+#Psxnr#8C}e60FMtUkdY$lkN^4}n+E*fh;^ z5e0dcF&MIQA!Hg+p0mU9)!@#YNaK2PBDgar5_be%n@>8OH-?F2;LaSg{G-ykQ48^X zRLCXiK!MbIHG{g7!pOYp;(rF`E@tAx;23!7z5WC6)Q809Dc>_X-;%u1>qJF{-V;=j zm$-P$#qW3Vso<&i_<1h-Qt;G!d$aqE7Ds{9mqaIc7(Kx*wjKg6gQq?O_JM!-vKS_g zf;))=7ywUw7+eBReHbJ@b$xIbJoV|5X9owXPFC0ep8B?W&}ILDi#LOZv9``jW&?OH zmW*56dW*$fA3Wva3Ow~~wiA5lDT8x)kE{Ie!BZcH{otu@w>QAQ9Kgcs$#+~8oYnmA z=pmi*W6f_MABFhr@Q9r4BLyx#2^>eylcO%HHKU(ZMs2@(vo;i&_pN)zPWe&O>XxZ3 oO)FQ3<_&9?0+AO@D>t;QZ`s`EA2VBeSFB&TtZk)QSNH7y0p~r9zyJUM literal 12024 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 bi+0n>_dmR9O3zQXBb>*hY*eQ-xtRV3apidu diff --git a/src/audio/codec_ulaw.so b/src/audio/codec_ulaw.so index 0c7c40fc05cb5c467620b498603ade019cc00da3..441893d69d3a397b1c6b914b13afc7905502ebf2 100755 GIT binary patch delta 4527 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 AiU0rr delta 4734 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 diff --git a/src/audio/gsmcodec.cpp b/src/audio/gsmcodec.cpp index 1556185632..fbc924feb9 100644 --- a/src/audio/gsmcodec.cpp +++ b/src/audio/gsmcodec.cpp @@ -63,10 +63,6 @@ virtual int codecEncode (unsigned char *dst, short *src, unsigned int size) return 33; } -virtual void test() -{ - printf("MOn OSTIE GSM!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); -} private: gsm _decode_gsmhandle; @@ -75,10 +71,10 @@ private: }; // the class factories -extern "C" AudioCodec* create_gsm() { +extern "C" AudioCodec* create() { return new Gsm(); } -extern "C" void destroy_gsm(AudioCodec* a) { +extern "C" void destroy(AudioCodec* a) { delete a; } diff --git a/src/audio/ulaw.cpp b/src/audio/ulaw.cpp index b99416a57f..fd1c58370f 100644 --- a/src/audio/ulaw.cpp +++ b/src/audio/ulaw.cpp @@ -12,9 +12,6 @@ public: _channel = 1; } - virtual void test(){ - printf("MON OSTIE !!!!!!!!!!!!!!!!!!!!!!!!!! ULAW\n"); - } virtual int codecDecode (short *dst, unsigned char *src, unsigned int size) { int16* end = dst+size; @@ -91,10 +88,10 @@ public: }; // the class factories -extern "C" AudioCodec* create_ulaw() { +extern "C" AudioCodec* create() { return new Ulaw(); } -extern "C" void destroy_ulaw(AudioCodec* a) { +extern "C" void destroy(AudioCodec* a) { delete a; } diff --git a/src/call.cpp b/src/call.cpp index b19a198ded..36428177aa 100644 --- a/src/call.cpp +++ b/src/call.cpp @@ -25,7 +25,7 @@ Call::Call(const CallID& id, Call::CallType type) : _id(id), _type(type), { _connectionState = Call::Disconnected; _callState = Call::Inactive; - _audioCodec = 0; + //_audioCodec = 0; _localAudioPort = 0; _localExternalAudioPort = 0; _remoteAudioPort = 0; @@ -65,7 +65,7 @@ Call::getState() return _callState; } -CodecDescriptorMap& +CodecDescriptor& Call::getCodecMap() { return _codecMap; @@ -99,7 +99,7 @@ Call::getRemoteIp() return _remoteIPAddress; } -AudioCodec* +CodecType Call::getAudioCodec() { ost::MutexLock m(_callMutex); diff --git a/src/call.h b/src/call.h index 7794be6815..a01fe6173c 100644 --- a/src/call.h +++ b/src/call.h @@ -121,8 +121,8 @@ public: // AUDIO /** Set internal codec Map: initialization only, not protected */ - void setCodecMap(const CodecDescriptorMap& map) { _codecMap = map; } - CodecDescriptorMap& getCodecMap(); + void setCodecMap(const CodecDescriptor& map) { _codecMap = map; } + CodecDescriptor& getCodecMap(); /** Set my IP [not protected] */ void setLocalIp(const std::string& ip) { _localIPAddress = ip; } @@ -149,7 +149,7 @@ public: const std::string& getRemoteIp(); /** Return audio codec [mutex protected] */ - AudioCodec* getAudioCodec(); + CodecType getAudioCodec(); @@ -164,13 +164,14 @@ protected: void setRemoteAudioPort(unsigned int port) { _remoteAudioPort = port; } /** Set the audio codec used. [not protected] */ - void setAudioCodec(AudioCodec* audioCodec) { _audioCodec = audioCodec; } + void setAudioCodec(CodecType audioCodec) { _audioCodec = audioCodec; } /** Codec Map */ - CodecDescriptorMap _codecMap; + CodecDescriptor _codecMap; /** Codec pointer */ - AudioCodec* _audioCodec; + //AudioCodec* _audioCodec; + CodecType _audioCodec; bool _audioStarted; diff --git a/src/global.h b/src/global.h index 06a3206afe..0f13cf1c46 100644 --- a/src/global.h +++ b/src/global.h @@ -72,4 +72,14 @@ typedef short int16; #define CHANNELS 2 #define SIZEBUF 1024*1024 +// Codecs payloads, as defined in RFC3551 +// http://www.iana.org/assignments/rtp-parameters +// http://www.gnu.org/software/ccrtp/doc/refman/html/formats_8h.html#a0 +/*#define PAYLOAD_CODEC_ULAW 0 // PCMU 8000 +#define PAYLOAD_CODEC_ALAW 8 // PCMA 8000 +#define PAYLOAD_CODEC_GSM 3 // GSM 8000 +// http://www.ietf.org/rfc/rfc3952.txt +#define PAYLOAD_CODEC_ILBC 97*/ + + #endif // __GLOBAL_H__ diff --git a/src/iaxcall.cpp b/src/iaxcall.cpp index 314f57a351..a2c07838f5 100644 --- a/src/iaxcall.cpp +++ b/src/iaxcall.cpp @@ -36,7 +36,7 @@ IAXCall::setFormat(int format) _format = format; switch(format) { - case AST_FORMAT_ULAW: + /*case AST_FORMAT_ULAW: setAudioCodec(_codecMap.getCodec(PAYLOAD_CODEC_ULAW)); break; case AST_FORMAT_GSM: setAudioCodec(_codecMap.getCodec(PAYLOAD_CODEC_GSM)); break; @@ -45,9 +45,19 @@ IAXCall::setFormat(int format) case AST_FORMAT_ILBC: setAudioCodec(_codecMap.getCodec(PAYLOAD_CODEC_ILBC)); break; case AST_FORMAT_SPEEX: - setAudioCodec(_codecMap.getCodec(PAYLOAD_CODEC_SPEEX)); break; + setAudioCodec(_codecMap.getCodec(PAYLOAD_CODEC_SPEEX)); break;*/ + case AST_FORMAT_ULAW: + setAudioCodec(PAYLOAD_CODEC_ULAW); break; + case AST_FORMAT_GSM: + setAudioCodec(PAYLOAD_CODEC_GSM); break; + case AST_FORMAT_ALAW: + setAudioCodec(PAYLOAD_CODEC_ALAW); break; + case AST_FORMAT_ILBC: + setAudioCodec(PAYLOAD_CODEC_ILBC); break; + case AST_FORMAT_SPEEX: + setAudioCodec(PAYLOAD_CODEC_SPEEX); break; default: - setAudioCodec(NULL); + setAudioCodec((CodecType) -1); break; } } @@ -56,7 +66,7 @@ IAXCall::setFormat(int format) int IAXCall::getSupportedFormat() { - CodecMap map = getCodecMap().getMap(); + CodecMap map = getCodecMap().getCodecMap(); int format = 0; CodecMap::iterator iter = map.begin(); @@ -84,7 +94,7 @@ IAXCall::getSupportedFormat() int IAXCall::getFirstMatchingFormat(int needles) { - CodecMap map = getCodecMap().getMap(); + CodecMap map = getCodecMap().getCodecMap(); int format = 0; CodecMap::iterator iter = map.begin(); diff --git a/src/iaxvoiplink.cpp b/src/iaxvoiplink.cpp index 2996b75038..fa77f7ce95 100644 --- a/src/iaxvoiplink.cpp +++ b/src/iaxvoiplink.cpp @@ -29,6 +29,7 @@ #include <samplerate.h> #include <iax/iax-client.h> #include <math.h> +#include <dlfcn.h> #define IAX_BLOCKING 1 @@ -231,9 +232,36 @@ IAXVoIPLink::getEvent() void IAXVoIPLink::sendAudioFromMic(void) { + IAXCall* currentCall = getIAXCall(Manager::instance().getCurrentCallId()); - AudioCodec* audiocodec = NULL; + //CodecType audiocodec = (CodecType) -1; + using std::cout; + using std::cerr; + void* codec = dlopen("codec_alaw.so", RTLD_LAZY); + if(!codec){ + cerr<<"cannot load library: "<< dlerror() <<'\n'; + } + +//reset errors + dlerror(); + +//load the symbols + create_t* create_codec = (create_t*)dlsym(codec, "create"); + const char* dlsym_error = dlerror(); + if(dlsym_error){ + cerr << "Cannot load symbol create: " << dlsym_error << '\n'; + } + destroy_t* destroy_codec = (destroy_t*) dlsym(codec, "destroy"); + dlsym_error = dlerror(); + if(dlsym_error){ + cerr << "Cannot load symbol destroy" << dlsym_error << '\n'; + } + + + AudioCodec* audiocodec = create_codec(); + + if (!currentCall) { // Let's mind our own business. return; @@ -249,7 +277,7 @@ IAXVoIPLink::sendAudioFromMic(void) return; } - audiocodec = currentCall->getAudioCodec(); + //audiocodec = currentCall->getAudioCodec(); if (!audiocodec) { // Audio codec still not determined. @@ -285,6 +313,7 @@ IAXVoIPLink::sendAudioFromMic(void) // Audio ici est PARFAIT int16* toIAX = NULL; + //if (audiolayer->getSampleRate() != audiocodec->getClockRate() && nbSample) { if (audiolayer->getSampleRate() != audiocodec->getClockRate() && nbSample) { SRC_DATA src_data; #ifdef DATAFORMAT_IS_FLOAT @@ -302,7 +331,7 @@ IAXVoIPLink::sendAudioFromMic(void) src_data.input_frames = nbSample; src_data.output_frames = (int) floor(factord * nbSample); src_data.data_out = _floatBuffer8000; - src_data.end_of_input = 0; /* More data to come */ + src_data.end_of_input = 0; src_process(_src_state_mic, &src_data); @@ -328,17 +357,17 @@ IAXVoIPLink::sendAudioFromMic(void) // NOTE: L'audio ici est bon. - /* + // // LE PROBLÈME est dans cette snippet de fonction: // C'est une fonction destructrice ! On n'en veut pas! - if ( nbSample < (IAX__20S_8KHZ_MAX - 10) ) { // if only 10 is missing, it's ok + //if ( nbSample < (IAX__20S_8KHZ_MAX - 10) ) { // if only 10 is missing, it's ok // fill end with 0... - _debug("begin: %p, nbSample: %d\n", toIAX, nbSample); - _debug("has to fill: %d chars at %p\n", (IAX__20S_8KHZ_MAX-nbSample)*sizeof(int16), toIAX + nbSample); - memset(toIAX + nbSample, 0, (IAX__20S_8KHZ_MAX-nbSample)*sizeof(int16)); - nbSample = IAX__20S_8KHZ_MAX; - } - */ + //_debug("begin: %p, nbSample: %d\n", toIAX, nbSample); + //_debug("has to fill: %d chars at %p\n", (IAX__20S_8KHZ_MAX-nbSample)*sizeof(int16), toIAX + nbSample); + //memset(toIAX + nbSample, 0, (IAX__20S_8KHZ_MAX-nbSample)*sizeof(int16)); + //nbSample = IAX__20S_8KHZ_MAX; + //} + //_debug("AR: Nb sample: %d int, [0]=%d [1]=%d [2]=%d\n", nbSample, toIAX[0], toIAX[1], toIAX[2]); // NOTE: Le son dans toIAX (nbSamle*sizeof(int16)) est mauvais, // s'il passe par le snippet précédent. @@ -365,6 +394,9 @@ IAXVoIPLink::sendAudioFromMic(void) } _mutexIAX.leaveMutex(); } + + destroy_codec(audiocodec); + dlclose(codec); } @@ -746,7 +778,7 @@ IAXVoIPLink::iaxHandleCallEvent(iax_event* event, IAXCall* call) /* Handle audio event, VOICE packet received */ void IAXVoIPLink::iaxHandleVoiceEvent(iax_event* event, IAXCall* call) -{ +{ // If we receive datalen == 0, some things of the jitter buffer in libiax2/iax.c // were triggered if (!event->datalen) { @@ -755,15 +787,37 @@ IAXVoIPLink::iaxHandleVoiceEvent(iax_event* event, IAXCall* call) return; } + using std::cout; +using std::cerr; +void* codec = dlopen("codec_alaw.so", RTLD_LAZY); +if(!codec){ + cerr<<"cannot load library: "<< dlerror() <<'\n'; +} + +//reset errors +dlerror(); +create_t* create_codec = (create_t*)dlsym(codec, "create"); +const char* dlsym_error = dlerror(); +if(dlsym_error){ + cerr << "Cannot load symbol create: " << dlsym_error << '\n'; +} +destroy_t* destroy_codec = (destroy_t*) dlsym(codec, "destroy"); +dlsym_error = dlerror(); +if(dlsym_error){ + cerr << "Cannot load symbol destroy" << dlsym_error << '\n'; +} +AudioCodec* audiocodec; + if (audiolayer) { - AudioCodec* audiocodec = call->getAudioCodec(); + //AudioCodec* audiocodec = call->getAudioCodec(); + audiocodec = create_codec(); // On-the-fly codec changing (normally, when we receive a full packet) // as per http://tools.ietf.org/id/draft-guy-iax-03.txt // - subclass holds the voiceformat property. if (event->subclass && event->subclass != call->getFormat()) { call->setFormat(event->subclass); - audiocodec = call->getAudioCodec(); + //audiocodec = call->getAudioCodec(); } //_debug("Receive: len=%d, format=%d, _receiveDataDecoded=%p\n", event->datalen, call->getFormat(), _receiveDataDecoded); @@ -798,7 +852,7 @@ IAXVoIPLink::iaxHandleVoiceEvent(iax_event* event, IAXCall* call) src_data.input_frames = nbSample; src_data.output_frames = (int) floor(factord * nbSample); src_data.src_ratio = factord; - src_data.end_of_input = 0; /* More data will come */ + src_data.end_of_input = 0; src_short_to_float_array(_receiveDataDecoded, _floatBuffer8000, nbSample); // samplerate convert, go! @@ -826,6 +880,10 @@ IAXVoIPLink::iaxHandleVoiceEvent(iax_event* event, IAXCall* call) } else { _debug("IAX: incoming audio, but no sound card open"); } +destroy_codec(audiocodec); +dlclose(codec); + + } diff --git a/src/managerimpl.cpp b/src/managerimpl.cpp index b16891dc9b..7887e499db 100644 --- a/src/managerimpl.cpp +++ b/src/managerimpl.cpp @@ -1090,9 +1090,9 @@ ManagerImpl::initAudioCodec (void) //_codecDescriptorMap.setActive(getConfigString("Audio", "Codecs.codec1")); //_codecDescriptorMap.setActive(getConfigString("Audio", "Codec.codec2")); //_codecDescriptorMap.setActive(getConfigString("Audio", "Codec.codec3")); - _codecDescriptorMap.setActive("G711a"); - _codecDescriptorMap.setActive("G711u"); - _codecDescriptorMap.setActive("GSM"); + //_codecDescriptorMap.setActive("G711a"); + //_codecDescriptorMap.setActive("G711u"); + //_codecDescriptorMap.setActive("GSM"); } void @@ -1106,11 +1106,11 @@ ManagerImpl::setPreferedCodec(const ::DBus::String& codec_name) tmp = list[0]; list[0] = list[i]; list[i] = tmp; - _codecDescriptorMap.setActive(list[0]); + //_codecDescriptorMap.setActive(list[0]); //_codecDescriptorMap.setInactive(list[1]); //_codecDescriptorMap.setInactive(list[2]); - _codecDescriptorMap.setActive(list[1]); - _codecDescriptorMap.setActive(list[2]); + //_codecDescriptorMap.setActive(list[1]); + //_codecDescriptorMap.setActive(list[2]); setConfig("Audio", "Codecs.codec1", list[0]); setConfig("Audio", "Codecs.codec2", list[1]); setConfig("Audio", "Codecs.codec3", list[2]); @@ -1127,8 +1127,8 @@ ManagerImpl::getDefaultCodecList( void ) { std::vector< std::string > v; std::string desc=DFT_CODEC1; - std::string rate=""+clockRate(desc); - printf("%s\n",rate.c_str()); + //std::string rate=""+clockRate(desc); + //printf("%s\n",rate.c_str()); v.push_back(DFT_CODEC1); // G711u v.push_back(DFT_CODEC2); // G711a v.push_back(DFT_CODEC3); // GSM @@ -1137,18 +1137,19 @@ ManagerImpl::getDefaultCodecList( void ) unsigned int ManagerImpl::clockRate(std::string& name) -{ - CodecMap codecs = _codecDescriptorMap.getMap(); +{/* + CodecMap codecs = _codecDescriptorMap.getCodecMap(); CodecMap::iterator iter = codecs.begin(); while(iter!=codecs.end()) { if(iter->second!=NULL) { - if(iter->second->getDescription() == name) - return iter->second->getClockRate(); - } - iter++; + if(iter->second == name) + //return iter->second->getClockRate(); + return 1; } + iter++; + }*/ return -1; } @@ -1483,19 +1484,20 @@ ManagerImpl::setConfig(const std::string& section, const std::string& name, int bool ManagerImpl::getConfigList(const std::string& sequenceId, const std::string& name) { + /* bool returnValue = false; TokenList tk; if (name == "codecdescriptor") { - CodecMap map = _codecDescriptorMap.getMap(); + CodecMap map = _codecDescriptorMap.getCodecMap(); CodecMap::iterator iter = map.begin(); while( iter != map.end() ) { tk.clear(); std::ostringstream strType; strType << iter->first; tk.push_back(strType.str()); - if (iter->second) { - tk.push_back(iter->second->getDescription()); + if (iter->second != -1) { + tk.push_back(iter->second); } else { tk.push_back(strType.str()); } @@ -1528,7 +1530,8 @@ ManagerImpl::getConfigList(const std::string& sequenceId, const std::string& nam } else if (name == "countrytones") { returnValue = getCountryTones(sequenceId); } - return returnValue; + return returnValue;*/ + return true; } //THREAD=Main diff --git a/src/managerimpl.h b/src/managerimpl.h index cfdac255d8..38d25a22c8 100644 --- a/src/managerimpl.h +++ b/src/managerimpl.h @@ -110,7 +110,7 @@ public: /** * Get a descriptor map of codec available */ - CodecDescriptorMap& getCodecDescriptorMap(void) {return _codecDescriptorMap;} + CodecDescriptor& getCodecDescriptorMap(void) {return _codecDescriptorMap;} /** * Functions which occur with a user's action @@ -513,7 +513,7 @@ private: DTMF* _dtmfKey; // map of codec (for configlist request) - CodecDescriptorMap _codecDescriptorMap; + CodecDescriptor _codecDescriptorMap; ///////////////////// // Protected by Mutex diff --git a/src/sipcall.cpp b/src/sipcall.cpp index 37f2b84d4e..3bbb6f361d 100644 --- a/src/sipcall.cpp +++ b/src/sipcall.cpp @@ -76,6 +76,7 @@ SIPCall::SIPCallInvite(eXosip_event_t *event) } if (!setAudioCodecFromSDP(remote_med, event->tid)) { + _debug("SIP Failure: unable to set audio codecs from the remote SDP\n"); sdp_message_free (remote_sdp); return false; } @@ -90,7 +91,6 @@ SIPCall::SIPCallInvite(eXosip_event_t *event) _debug("< Sending Answer 415 : unsupported media type\n"); eXosip_call_send_answer (event->tid, 415, NULL); } else { - sdp_message_t *local_sdp = eXosip_get_sdp_info(answer); sdp_media_t *local_med = NULL; if (local_sdp != NULL) { @@ -283,11 +283,12 @@ SIPCall::SIPCallAnsweredWithoutHold(eXosip_event_t* event) #else char *tmp = (char*) osip_list_get (&(remote_med->m_payloads), 0); #endif - setAudioCodec(0); + setAudioCodec((CodecType)-1); if (tmp != NULL) { int payload = atoi (tmp); _debug(" Remote Payload: %d\n", payload); - setAudioCodec(_codecMap.getCodec((CodecType)payload)); // codec builder for the mic + //setAudioCodec(_codecMap.getCodecName((CodecType)payload)); // codec builder for the mic + setAudioCodec((CodecType)payload); // codec builder for the mic } /* @@ -352,17 +353,21 @@ SIPCall::sdp_complete_message(sdp_message_t * remote_sdp, osip_message_t * msg) remote_med_m_payloads = &(remote_med->m_payloads); #endif - while (!osip_list_eol(remote_med_m_payloads, iPayload)) { + while (!osip_list_eol(remote_med_m_payloads, iPayload) && iPayload < 2) { tmp = (char *)osip_list_get(remote_med_m_payloads, iPayload); if (tmp!=NULL) { int payload = atoi(tmp); - AudioCodec* audiocodec = _codecMap.getCodec((CodecType)payload); - if (audiocodec != NULL && audiocodec->isActive()) { + _debug("remote payload = %s\n", tmp); + CodecType audiocodec = (CodecType)payload; + //if (audiocodec != NULL && audiocodec->isActive()) { + if (audiocodec != (CodecType)-1 && _codecMap.setActive(audiocodec)) { //TODO: check if the remote payload matches with one of the codec map listCodec << payload << " "; - listRtpMap << "a=rtpmap:" << payload << " " << audiocodec->getCodecName() << "/" << audiocodec->getClockRate(); - if ( audiocodec->getChannel() != 1) { + //listRtpMap << "a=rtpmap:" << payload << " " << audiocodec->getCodecName() << "/" << audiocodec->getClockRate(); + listRtpMap << "a=rtpmap:" << payload << " " << _codecMap.getCodecName(audiocodec) << "/" << 8000; + // TODO: manage a way to get the channel infos + /*if ( audiocodec->getChannel() != 1) { listRtpMap << "/" << audiocodec->getChannel(); - } + }*/ listRtpMap << "\r\n"; } } @@ -581,7 +586,7 @@ SIPCall::setAudioCodecFromSDP(sdp_media_t* remote_med, int tid) if (tmp != NULL ) { int payload = atoi(tmp); // stop if we find a correct codec - if (_codecMap.getCodec((CodecType)payload) != NULL){ + if (_codecMap.setActive((CodecType)payload)){ break; } } @@ -589,13 +594,14 @@ SIPCall::setAudioCodecFromSDP(sdp_media_t* remote_med, int tid) pos++; } - setAudioCodec(0); + setAudioCodec((CodecType)-1); if (tmp != NULL) { int payload = atoi (tmp); _debug(" Payload: %d\n", payload); - setAudioCodec(_codecMap.getCodec((CodecType)payload)); // codec builder for the mic + setAudioCodec((CodecType)payload); // codec builder for the mic + _debug("SetAUDIOcodec!!\n"); } - if (getAudioCodec() == 0) { + if (getAudioCodec() == (CodecType) -1) { _debug("SIPCall Failure: Unable to set codec\n"); _debug("< Sending 415 Unsupported media type\n"); eXosip_lock(); diff --git a/src/sipvoiplink.cpp b/src/sipvoiplink.cpp index 168a2d0e8b..a23936925f 100644 --- a/src/sipvoiplink.cpp +++ b/src/sipvoiplink.cpp @@ -947,24 +947,24 @@ SIPVoIPLink::SIPStartCall(SIPCall* call, const std::string& subject) int nbChannel; // Set rtpmap according to the supported codec order - CodecMap map = call->getCodecMap().getMap(); + CodecMap map = call->getCodecMap().getCodecMap(); + //CodecMap map = Manager::instance().getCodecDescriptorMap(); CodecMap::iterator iter = map.begin(); while(iter != map.end()) { - printf("codec = %s\n",iter->second->getCodecName().data()); - if (iter->second!=0 && iter->second->isActive()) { - printf("codec = %s\n",iter->second->getCodecName().data()); - payload = iter->first; + //if (iter->second!=0 && iter->second->isActive()) { + if(iter->first != -1){ + payload = iter->first; // add each payload in the list of payload media_audio << payload << " "; rtpmap_attr << "a=rtpmap:" << payload << " " << - iter->second->getCodecName().data() << "/" << iter->second->getClockRate(); + iter->second.data() << "/" << 8000; //iter->second->getClockRate(); - nbChannel = iter->second->getChannel(); + /*nbChannel = iter->second->getChannel(); if (nbChannel!=1) { rtpmap_attr << "/" << nbChannel; - } + }*/ rtpmap_attr << "\r\n"; } // go to next codec -- GitLab