Commit 21410c23 authored by Tristan Matthews's avatar Tristan Matthews

* #15528: sip/audiortp: support asymmetric audio codecs

A call can send one codec and receive another.
parent d7377c89
......@@ -109,6 +109,7 @@ AudioRtpRecord::AudioRtpRecord() :
#endif
, dtmfPayloadType_(101) // same as Asterisk
, dead_(false)
, currentCodecIndex_(0)
{}
// Call from processData*
......@@ -121,6 +122,16 @@ bool AudioRtpRecord::isDead()
#endif
}
sfl::AudioCodec *
AudioRtpRecord::getCurrentCodec() const
{
if (audioCodecs_.empty() or currentCodecIndex_ >= audioCodecs_.size()) {
ERROR("No codec found");
return 0;
}
return audioCodecs_[currentCodecIndex_];
}
void
AudioRtpRecord::deleteCodecs()
{
......@@ -129,6 +140,23 @@ AudioRtpRecord::deleteCodecs()
audioCodecs_.clear();
}
bool AudioRtpRecord::tryToSwitchPayloadTypes(int newPt)
{
for (std::vector<AudioCodec *>::iterator i = audioCodecs_.begin(); i != audioCodecs_.end(); ++i)
if (*i and (*i)->getPayloadType() == newPt) {
codecPayloadType_ = (*i)->getPayloadType();
codecSampleRate_ = (*i)->getClockRate();
codecFrameSize_ = (*i)->getFrameSize();
hasDynamicPayloadType_ = (*i)->hasDynamicPayload();
currentCodecIndex_ = std::distance(audioCodecs_.begin(), i);
DEBUG("Switched payload type to %d", newPt);
return true;
}
ERROR("Could not switch payload types");
return false;
}
AudioRtpRecord::~AudioRtpRecord()
{
dead_ = true;
......@@ -174,6 +202,8 @@ void AudioRtpRecordHandler::setRtpMedia(const std::vector<AudioCodec*> &audioCod
audioRtpRecord_.deleteCodecs();
// Set varios codec info to reduce indirection
audioRtpRecord_.audioCodecs_ = audioCodecs;
audioRtpRecord_.currentCodecIndex_ = 0;
audioRtpRecord_.codecPayloadType_ = audioCodecs[0]->getPayloadType();
audioRtpRecord_.codecSampleRate_ = audioCodecs[0]->getClockRate();
audioRtpRecord_.codecFrameSize_ = audioCodecs[0]->getFrameSize();
......@@ -273,13 +303,9 @@ int AudioRtpRecordHandler::processDataEncode()
{
ost::MutexLock lock(audioRtpRecord_.audioCodecMutex_);
if (audioRtpRecord_.audioCodecs_.empty()) {
ERROR("Audio codecs already destroyed");
return 0;
}
RETURN_IF_NULL(audioRtpRecord_.audioCodecs_[0], 0, "Audio codec already destroyed");
RETURN_IF_NULL(audioRtpRecord_.getCurrentCodec(), 0, "Audio codec already destroyed");
unsigned char *micDataEncoded = audioRtpRecord_.encodedData_.data();
return audioRtpRecord_.audioCodecs_[0]->encode(micDataEncoded, out, getCodecFrameSize());
return audioRtpRecord_.getCurrentCodec()->encode(micDataEncoded, out, getCodecFrameSize());
}
}
#undef RETURN_IF_NULL
......@@ -291,27 +317,24 @@ void AudioRtpRecordHandler::processDataDecode(unsigned char *spkrData, size_t si
if (audioRtpRecord_.isDead())
return;
if (audioRtpRecord_.codecPayloadType_ != payloadType) {
if (!warningInterval_) {
warningInterval_ = 250;
WARN("Invalid payload type %d, expected %d", payloadType, audioRtpRecord_.codecPayloadType_);
WARN("We have %u codecs total", audioRtpRecord_.audioCodecs_.size());
const bool switched = audioRtpRecord_.tryToSwitchPayloadTypes(payloadType);
if (not switched) {
if (!warningInterval_) {
warningInterval_ = 250;
WARN("Invalid payload type %d, expected %d", payloadType, audioRtpRecord_.codecPayloadType_);
}
warningInterval_--;
return;
}
warningInterval_--;
return;
}
int inSamples = 0;
size = std::min(size, audioRtpRecord_.decData_.size());
SFLDataFormat *spkrDataDecoded = audioRtpRecord_.decData_.data();
{
ost::MutexLock lock(audioRtpRecord_.audioCodecMutex_);
if (audioRtpRecord_.audioCodecs_.empty()) {
ERROR("Audio codecs already destroyed");
return;
}
RETURN_IF_NULL(audioRtpRecord_.audioCodecs_[0], "Audio codecs already destroyed");
RETURN_IF_NULL(audioRtpRecord_.getCurrentCodec(), "Audio codecs already destroyed");
// Return the size of data in samples
inSamples = audioRtpRecord_.audioCodecs_[0]->decode(spkrDataDecoded, spkrData, size);
inSamples = audioRtpRecord_.getCurrentCodec()->decode(spkrDataDecoded, spkrData, size);
}
#if HAVE_SPEEXDSP
......@@ -354,6 +377,7 @@ void AudioRtpRecord::fadeInDecodedData(size_t size)
if (fadeFactor_ >= 1.0 or size > decData_.size())
return;
// FIXME: this takes a lot more cycles than a plain old loop
std::transform(decData_.begin(), decData_.begin() + size, decData_.begin(),
std::bind1st(std::multiplies<double>(), fadeFactor_));
......@@ -361,4 +385,23 @@ void AudioRtpRecord::fadeInDecodedData(size_t size)
const double FADEIN_STEP_SIZE = 4.0;
fadeFactor_ *= FADEIN_STEP_SIZE;
}
bool
AudioRtpRecordHandler::codecsDiffer(const std::vector<AudioCodec*> &codecs) const
{
const std::vector<AudioCodec*> &current = audioRtpRecord_.audioCodecs_;
if (codecs.size() != current.size())
return true;
for (std::vector<AudioCodec*>::const_iterator i = codecs.begin(); i != codecs.end(); ++i) {
if (*i) {
bool matched = false;
for (std::vector<AudioCodec*>::const_iterator j = current.begin(); !matched and j != current.end(); ++j)
matched = (*i)->getPayloadType() == (*j)->getPayloadType();
if (not matched)
return true;
}
}
return false;
}
}
......@@ -73,6 +73,8 @@ class AudioRtpRecord {
AudioRtpRecord();
~AudioRtpRecord();
void deleteCodecs();
bool tryToSwitchPayloadTypes(int newPt);
sfl::AudioCodec* getCurrentCodec() const;
std::string callId_;
int codecSampleRate_;
std::list<DTMFEvent> dtmfQueue_;
......@@ -112,6 +114,7 @@ class AudioRtpRecord {
#else
ucommon::atomic::counter dead_;
#endif
size_t currentCodecIndex_;
};
......@@ -180,6 +183,7 @@ class AudioRtpRecordHandler {
void putDtmfEvent(char digit);
protected:
bool codecsDiffer(const std::vector<AudioCodec*> &codecs) const;
AudioRtpRecord audioRtpRecord_;
private:
......
......@@ -65,7 +65,8 @@ void AudioRtpSession::updateSessionMedia(const std::vector<AudioCodec*> &audioCo
{
int lastSamplingRate = audioRtpRecord_.codecSampleRate_;
setSessionMedia(audioCodecs);
if (codecsDiffer(audioCodecs))
setSessionMedia(audioCodecs);
Manager::instance().audioSamplingRateChanged(audioRtpRecord_.codecSampleRate_);
......
......@@ -475,7 +475,7 @@ IAXVoIPLink::getCurrentVideoCodecName(Call * /*call*/) const
}
std::string
IAXVoIPLink::getCurrentAudioCodecName(Call *c) const
IAXVoIPLink::getCurrentAudioCodecNames(Call *c) const
{
IAXCall *call = static_cast<IAXCall*>(c);
sfl::Codec *audioCodec = Manager::instance().audioCodecFactory.getCodec(call->getAudioCodec());
......
......@@ -193,7 +193,7 @@ class IAXVoIPLink : public VoIPLink {
* @param id The call identifier
*/
virtual std::string getCurrentVideoCodecName(Call *c) const;
virtual std::string getCurrentAudioCodecName(Call *c) const;
virtual std::string getCurrentAudioCodecNames(Call *c) const;
private:
NON_COPYABLE(IAXVoIPLink);
......
......@@ -1874,7 +1874,7 @@ std::string ManagerImpl::getCurrentAudioCodecName(const std::string& id)
Call::CallState state = call->getState();
if (state == Call::ACTIVE or state == Call::CONFERENCING)
codecName = link->getCurrentAudioCodecName(call);
codecName = link->getCurrentAudioCodecNames(call);
}
return codecName;
......
......@@ -71,15 +71,23 @@ Sdp::Sdp(pj_pool_t *pool)
, telephoneEventPayload_(101) // same as asterisk
{}
namespace {
bool hasPayload(const std::vector<sfl::AudioCodec*> &codecs, int pt)
{
for (std::vector<sfl::AudioCodec*>::const_iterator i = codecs.begin(); i != codecs.end(); ++i)
if (*i and (*i)->getPayloadType() == pt)
return true;
return false;
}
}
void Sdp::setActiveLocalSdpSession(const pjmedia_sdp_session *sdp)
{
activeLocalSession_ = (pjmedia_sdp_session*) sdp;
if (activeLocalSession_->media_count < 1)
return;
for (unsigned media = 0; media < activeLocalSession_->media_count; ++media) {
pjmedia_sdp_media *current = activeLocalSession_->media[media];
for (unsigned i = 0; i < activeLocalSession_->media_count; ++i) {
pjmedia_sdp_media *current = activeLocalSession_->media[i];
for (unsigned fmt = 0; fmt < current->desc.fmt_count; ++fmt) {
static const pj_str_t STR_RTPMAP = { (char*) "rtpmap", 6 };
......@@ -93,24 +101,22 @@ void Sdp::setActiveLocalSdpSession(const pjmedia_sdp_session *sdp)
pjmedia_sdp_rtpmap *rtpmap;
pjmedia_sdp_attr_to_rtpmap(memPool_, attribute, &rtpmap);
string type(current->desc.media.ptr, current->desc.media.slen);
if (type == "audio") {
if (!pj_stricmp2(&current->desc.media, "audio")) {
const int pt = pj_strtoul(&rtpmap->pt);
sfl::Codec *codec = Manager::instance().audioCodecFactory.getCodec(pt);
if (codec)
sessionAudioMedia_.push_back(codec);
else {
DEBUG("Could not get codec for payload type %lu", pt);
break;
if (not hasPayload(sessionAudioMedia_, pt)) {
sfl::AudioCodec *codec = Manager::instance().audioCodecFactory.getCodec(pt);
if (codec)
sessionAudioMedia_.push_back(codec);
else
ERROR("Could not get codec for payload type %lu", pt);
}
} else if (type == "video")
} else if (!pj_stricmp2(&current->desc.media, "video"))
sessionVideoMedia_.push_back(string(rtpmap->enc_name.ptr, rtpmap->enc_name.slen));
}
}
}
void Sdp::setActiveRemoteSdpSession(const pjmedia_sdp_session *sdp)
{
activeRemoteSession_ = (pjmedia_sdp_session*) sdp;
......@@ -121,22 +127,44 @@ void Sdp::setActiveRemoteSdpSession(const pjmedia_sdp_session *sdp)
}
for (unsigned i = 0; i < sdp->media_count; i++) {
if (pj_stricmp2(&sdp->media[i]->desc.media, "audio") == 0) {
pjmedia_sdp_media *r_media = sdp->media[i];
pjmedia_sdp_media *r_media = sdp->media[i];
if (!pj_stricmp2(&r_media->desc.media, "audio")) {
static const pj_str_t STR_TELEPHONE_EVENT = { (char*) "telephone-event", 15};
pjmedia_sdp_attr *attribute = pjmedia_sdp_attr_find(r_media->attr_count, r_media->attr, &STR_TELEPHONE_EVENT, NULL);
pjmedia_sdp_attr *telephoneEvent = pjmedia_sdp_attr_find(r_media->attr_count, r_media->attr, &STR_TELEPHONE_EVENT, NULL);
if (attribute != NULL) {
if (telephoneEvent != NULL) {
pjmedia_sdp_rtpmap *rtpmap;
pjmedia_sdp_attr_to_rtpmap(memPool_, attribute, &rtpmap);
pjmedia_sdp_attr_to_rtpmap(memPool_, telephoneEvent, &rtpmap);
telephoneEventPayload_ = pj_strtoul(&rtpmap->pt);
}
return;
// add audio codecs from remote as needed
for (unsigned fmt = 0; fmt < r_media->desc.fmt_count; ++fmt) {
static const pj_str_t STR_RTPMAP = { (char*) "rtpmap", 6 };
pjmedia_sdp_attr *rtpMapAttr = pjmedia_sdp_media_find_attr(r_media, &STR_RTPMAP, NULL);
if (!rtpMapAttr) {
ERROR("Could not find rtpmap attribute");
continue;
}
pjmedia_sdp_rtpmap *rtpmap;
pjmedia_sdp_attr_to_rtpmap(memPool_, rtpMapAttr, &rtpmap);
const int pt = pj_strtoul(&rtpmap->pt);
if (not hasPayload(sessionAudioMedia_, pt)) {
sfl::AudioCodec *codec = Manager::instance().audioCodecFactory.getCodec(pt);
if (codec) {
DEBUG("Adding codec with new payload type %d", pt);
sessionAudioMedia_.push_back(codec);
} else
DEBUG("Could not get codec for payload type %lu", pt);
}
}
}
}
ERROR("Could not found dtmf event from remote sdp");
DEBUG("Using %u audio codecs total", sessionAudioMedia_.size());
}
string Sdp::getSessionVideoCodec() const
......@@ -148,20 +176,23 @@ string Sdp::getSessionVideoCodec() const
return sessionVideoMedia_[0];
}
string Sdp::getAudioCodecName() const
string Sdp::getAudioCodecNames() const
{
sfl::AudioCodec *codec = getSessionAudioMedia();
return codec ? codec->getMimeSubtype() : "";
std::string result;
char sep = ' ';
for (std::vector<sfl::AudioCodec*>::const_iterator i = sessionAudioMedia_.begin();
i != sessionAudioMedia_.end(); ++i) {
if (i == sessionAudioMedia_.end() - 1)
sep = '\0';
if (*i)
result += (*i)->getMimeSubtype() + sep;
}
return result;
}
sfl::AudioCodec* Sdp::getSessionAudioMedia() const
void Sdp::getSessionAudioMedia(std::vector<sfl::AudioCodec*> &codecs) const
{
if (sessionAudioMedia_.empty()) {
ERROR("No codec description for this media");
return 0;
}
return static_cast<sfl::AudioCodec *>(sessionAudioMedia_[0]);
codecs = sessionAudioMedia_;
}
......
......@@ -239,9 +239,9 @@ class Sdp {
void setMediaTransportInfoFromRemoteSdp();
std::string getAudioCodecName() const;
std::string getAudioCodecNames() const;
std::string getSessionVideoCodec() const;
sfl::AudioCodec* getSessionAudioMedia() const;
void getSessionAudioMedia(std::vector<sfl::AudioCodec*> &) const;
// Sets @param settings with appropriate values and returns true if
// we are sending video, false otherwise
bool getOutgoingVideoSettings(std::map<std::string, std::string> &settings) const;
......@@ -298,7 +298,7 @@ class Sdp {
/**
* The codecs that will be used by the session (after the SDP negotiation)
*/
std::vector<sfl::Codec *> sessionAudioMedia_;
std::vector<sfl::AudioCodec *> sessionAudioMedia_;
std::vector<std::string> sessionVideoMedia_;
std::string localIpAddr_;
......
......@@ -89,7 +89,7 @@ std::map<std::string, std::string>
SIPCall::createHistoryEntry() const
{
std::map<std::string, std::string> entry(Call::createHistoryEntry());
entry[HistoryItem::AUDIO_CODEC_KEY] = local_sdp_->getAudioCodecName();
entry[HistoryItem::AUDIO_CODEC_KEY] = local_sdp_->getAudioCodecNames();
#ifdef SFL_VIDEO
entry[HistoryItem::VIDEO_CODEC_KEY] = local_sdp_->getSessionVideoCodec();
#endif
......
......@@ -1030,19 +1030,23 @@ SIPVoIPLink::offhold(const std::string& id)
throw VoipLinkException("Could not find sdp session");
try {
int pl = PAYLOAD_CODEC_ULAW;
sfl::AudioCodec *sessionMedia = sdpSession->getSessionAudioMedia();
if (sessionMedia)
pl = sessionMedia->getPayloadType();
std::vector<sfl::AudioCodec*> sessionMedia;
sdpSession->getSessionAudioMedia(sessionMedia);
std::vector<sfl::AudioCodec*> audioCodecs;
for (std::vector<sfl::AudioCodec*>::const_iterator i = sessionMedia.begin();
i != sessionMedia.end(); ++i) {
// Create a new instance for this codec
sfl::AudioCodec* ac = Manager::instance().audioCodecFactory.instantiateCodec(pl);
if (!*i)
continue;
if (ac == NULL)
throw VoipLinkException("Could not instantiate codec");
// Create a new instance for this codec
sfl::AudioCodec* ac = Manager::instance().audioCodecFactory.instantiateCodec((*i)->getPayloadType());
std::vector<sfl::AudioCodec *> audioCodecs;
audioCodecs.push_back(ac);
if (ac == NULL)
throw VoipLinkException("Could not instantiate codec");
audioCodecs.push_back(ac);
}
call->getAudioRtp().initConfig();
call->getAudioRtp().initSession();
......@@ -1274,9 +1278,9 @@ SIPVoIPLink::getCurrentVideoCodecName(Call *call) const
}
std::string
SIPVoIPLink::getCurrentAudioCodecName(Call *call) const
SIPVoIPLink::getCurrentAudioCodecNames(Call *call) const
{
return static_cast<SIPCall*>(call)->getLocalSDP()->getAudioCodecName();
return static_cast<SIPCall*>(call)->getLocalSDP()->getAudioCodecNames();
}
/* Only use this macro with string literals or character arrays, will not work
......@@ -1751,8 +1755,9 @@ void sdp_media_update_cb(pjsip_inv_session *inv, pj_status_t status)
}
#endif // HAVE_SDES
sfl::AudioCodec *sessionMedia = sdpSession->getSessionAudioMedia();
if (!sessionMedia)
std::vector<sfl::AudioCodec*> sessionMedia;
sdpSession->getSessionAudioMedia(sessionMedia);
if (sessionMedia.empty())
return;
try {
......@@ -1760,16 +1765,19 @@ void sdp_media_update_cb(pjsip_inv_session *inv, pj_status_t status)
Manager::instance().getAudioDriver()->startStream();
Manager::instance().audioLayerMutexUnlock();
int pl = sessionMedia->getPayloadType();
std::vector<AudioCodec*> audioCodecs;
for (std::vector<sfl::AudioCodec*>::const_iterator i = sessionMedia.begin(); i != sessionMedia.end(); ++i) {
if (!*i)
continue;
const int pl = (*i)->getPayloadType();
if (pl != call->getAudioRtp().getSessionMedia()) {
sfl::AudioCodec *ac = Manager::instance().audioCodecFactory.instantiateCodec(pl);
if (!ac)
throw std::runtime_error("Could not instantiate codec");
std::vector<AudioCodec*> audioCodecs;
audioCodecs.push_back(ac);
call->getAudioRtp().updateSessionMedia(audioCodecs);
}
if (not audioCodecs.empty())
call->getAudioRtp().updateSessionMedia(audioCodecs);
} catch (const SdpException &e) {
ERROR("%s", e.what());
} catch (const std::exception &rtpException) {
......
......@@ -252,7 +252,7 @@ class SIPVoIPLink : public VoIPLink {
* @param c The call identifier
*/
std::string getCurrentVideoCodecName(Call *c) const;
std::string getCurrentAudioCodecName(Call *c) const;
std::string getCurrentAudioCodecNames(Call *c) const;
/**
* Retrive useragent name from account
......
......@@ -32,6 +32,7 @@
*/
#include "voiplink.h"
#include "account.h"
VoIPLink::VoIPLink() : handlingEvents_(false) {}
......
......@@ -147,7 +147,7 @@ class VoIPLink {
* @param call The call
*/
virtual std::string getCurrentVideoCodecName(Call *call) const = 0;
virtual std::string getCurrentAudioCodecName(Call *call) const = 0;
virtual std::string getCurrentAudioCodecNames(Call *call) const = 0;
/**
* Send a message to a call identified by its callid
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment