- adopt RFC2833 decoder for pcap analyzer
This commit is contained in:
@@ -201,7 +201,7 @@ RtpBuffer::FetchResult RtpBuffer::fetch()
|
|||||||
// See if there is enough information in buffer
|
// See if there is enough information in buffer
|
||||||
auto total = findTimelength();
|
auto total = findTimelength();
|
||||||
|
|
||||||
while (total > mHigh && mPacketList.size() && 0ms != mHigh)
|
while (total > mHigh && mPacketList.size() > 1 && 0ms != mHigh)
|
||||||
{
|
{
|
||||||
ICELogMedia( << "Dropping RTP packets from jitter buffer");
|
ICELogMedia( << "Dropping RTP packets from jitter buffer");
|
||||||
total -= mPacketList.front()->timelength();
|
total -= mPacketList.front()->timelength();
|
||||||
@@ -337,7 +337,7 @@ Receiver::~Receiver()
|
|||||||
|
|
||||||
//-------------- AudioReceiver ----------------
|
//-------------- AudioReceiver ----------------
|
||||||
AudioReceiver::AudioReceiver(const CodecList::Settings& settings, MT::Statistics &stat)
|
AudioReceiver::AudioReceiver(const CodecList::Settings& settings, MT::Statistics &stat)
|
||||||
:Receiver(stat), mBuffer(stat), mCodecSettings(settings), mCodecList(settings)
|
:Receiver(stat), mBuffer(stat), mDtmfBuffer(stat), mCodecSettings(settings), mCodecList(settings), mDtmfReceiver(stat)
|
||||||
{
|
{
|
||||||
// Init resamplers
|
// Init resamplers
|
||||||
mResampler8.start(AUDIO_CHANNELS, 8000, AUDIO_SAMPLERATE);
|
mResampler8.start(AUDIO_CHANNELS, 8000, AUDIO_SAMPLERATE);
|
||||||
@@ -351,6 +351,10 @@ AudioReceiver::AudioReceiver(const CodecList::Settings& settings, MT::Statistics
|
|||||||
|
|
||||||
mAvailable.setCapacity(AUDIO_SAMPLERATE * sizeof(short));
|
mAvailable.setCapacity(AUDIO_SAMPLERATE * sizeof(short));
|
||||||
|
|
||||||
|
mDtmfBuffer.setPrebuffer(0ms);
|
||||||
|
mDtmfBuffer.setLow(0ms);
|
||||||
|
mDtmfBuffer.setHigh(1ms);
|
||||||
|
|
||||||
#if defined(DUMP_DECODED)
|
#if defined(DUMP_DECODED)
|
||||||
mDecodedDump = std::make_shared<Audio::WavFileWriter>();
|
mDecodedDump = std::make_shared<Audio::WavFileWriter>();
|
||||||
mDecodedDump->open("decoded.wav", 8000 /*G711*/, AUDIO_CHANNELS);
|
mDecodedDump->open("decoded.wav", 8000 /*G711*/, AUDIO_CHANNELS);
|
||||||
@@ -431,14 +435,19 @@ bool AudioReceiver::add(const std::shared_ptr<jrtplib::RTPPacket>& p, Codec** de
|
|||||||
// Increase codec counter
|
// Increase codec counter
|
||||||
mStat.mCodecCount[ptype]++;
|
mStat.mCodecCount[ptype]++;
|
||||||
|
|
||||||
|
// Check if we deal with telephone-event
|
||||||
|
if (p->GetPayloadType() == mCodecSettings.mTelephoneEvent)
|
||||||
|
{
|
||||||
|
*detectedCodec = nullptr;
|
||||||
|
mDtmfBuffer.add(p, 10ms, 8000);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Look for codec
|
||||||
// Check if codec can be handled
|
// Check if codec can be handled
|
||||||
Codec* codec = nullptr;
|
Codec* codec = nullptr;
|
||||||
auto codecIter = mCodecMap.find(ptype);
|
auto codecIter = mCodecMap.find(ptype);
|
||||||
if (codecIter == mCodecMap.end())
|
if (codecIter != mCodecMap.end())
|
||||||
{
|
|
||||||
// Well, there is no information about the codec; skip this packet
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// Check if codec is creating lazily
|
// Check if codec is creating lazily
|
||||||
if (!codecIter->second)
|
if (!codecIter->second)
|
||||||
@@ -467,7 +476,7 @@ bool AudioReceiver::add(const std::shared_ptr<jrtplib::RTPPacket>& p, Codec** de
|
|||||||
samplerate = codec->samplerate();
|
samplerate = codec->samplerate();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process jitter
|
// Process jitter anyway - can we decode payload or not
|
||||||
mJitterStats.process(p.get(), samplerate);
|
mJitterStats.process(p.get(), samplerate);
|
||||||
mStat.mJitter = static_cast<float>(mJitterStats.get());
|
mStat.mJitter = static_cast<float>(mJitterStats.get());
|
||||||
|
|
||||||
@@ -489,9 +498,10 @@ bool AudioReceiver::add(const std::shared_ptr<jrtplib::RTPPacket>& p, Codec** de
|
|||||||
|
|
||||||
// Queue packet to buffer
|
// Queue packet to buffer
|
||||||
auto packet = mBuffer.add(p, std::chrono::milliseconds(time_length), samplerate).get();
|
auto packet = mBuffer.add(p, std::chrono::milliseconds(time_length), samplerate).get();
|
||||||
|
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
void AudioReceiver::processDecoded(Audio::DataWindow& output, DecodeOptions options)
|
void AudioReceiver::processDecoded(Audio::DataWindow& output, DecodeOptions options)
|
||||||
{
|
{
|
||||||
@@ -764,6 +774,12 @@ AudioReceiver::DecodeResult AudioReceiver::getAudioTo(Audio::DataWindow& output,
|
|||||||
{
|
{
|
||||||
DecodeResult result = {.mStatus = DecodeResult::Status::Skip};
|
DecodeResult result = {.mStatus = DecodeResult::Status::Skip};
|
||||||
|
|
||||||
|
// Process RFC2833 here; it doesn't result in any audio - only callbacks and statistics
|
||||||
|
auto fr = mDtmfBuffer.fetch();
|
||||||
|
if (fr.mPacket && fr.mStatus == RtpBuffer::FetchResult::Status::RegularPacket)
|
||||||
|
mDtmfReceiver.add(fr.mPacket->rtp());
|
||||||
|
|
||||||
|
|
||||||
auto produced = 0ms;
|
auto produced = 0ms;
|
||||||
if (mAvailable.filled() && mCodec && options.mElapsed != 0ms)
|
if (mAvailable.filled() && mCodec && options.mElapsed != 0ms)
|
||||||
{
|
{
|
||||||
@@ -968,12 +984,10 @@ DtmfReceiver::~DtmfReceiver()
|
|||||||
|
|
||||||
void DtmfReceiver::add(const std::shared_ptr<RTPPacket>& p)
|
void DtmfReceiver::add(const std::shared_ptr<RTPPacket>& p)
|
||||||
{
|
{
|
||||||
// This receiver always work in context of single RTP stream; so there is no need to put SSRC map and so on
|
|
||||||
if (p->GetPayloadType() != 101)
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto ev = DtmfBuilder::parseRfc2833({p->GetPayloadData(), p->GetPayloadLength()});
|
auto ev = DtmfBuilder::parseRfc2833({p->GetPayloadData(), p->GetPayloadLength()});
|
||||||
if (ev.mTone != mEvent || ev.mEnd != mEventEnded)
|
if (ev.mTone != mEvent || ev.mEnd != mEventEnded)
|
||||||
|
{
|
||||||
|
if (!(mEvent == ev.mTone && !mEventEnded && ev.mEnd))
|
||||||
{
|
{
|
||||||
// New tone is here
|
// New tone is here
|
||||||
if (mCallback)
|
if (mCallback)
|
||||||
@@ -988,3 +1002,4 @@ void DtmfReceiver::add(const std::shared_ptr<RTPPacket>& p)
|
|||||||
mEventEnded = ev.mEnd;
|
mEventEnded = ev.mEnd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -136,6 +136,23 @@ protected:
|
|||||||
Statistics& mStat;
|
Statistics& mStat;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DtmfReceiver: public Receiver
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
char mEvent = 0;
|
||||||
|
bool mEventEnded = false;
|
||||||
|
std::chrono::milliseconds mEventStart = 0ms;
|
||||||
|
std::function<void(char)> mCallback;
|
||||||
|
|
||||||
|
public:
|
||||||
|
DtmfReceiver(Statistics& stat);
|
||||||
|
~DtmfReceiver();
|
||||||
|
|
||||||
|
void add(const std::shared_ptr<RTPPacket>& p);
|
||||||
|
void setCallback(std::function<void(char tone)> callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class AudioReceiver: public Receiver
|
class AudioReceiver: public Receiver
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -189,6 +206,9 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
RtpBuffer mBuffer; // Jitter buffer itself
|
RtpBuffer mBuffer; // Jitter buffer itself
|
||||||
|
RtpBuffer mDtmfBuffer; // These two (mDtmfBuffer / mDtmfReceiver) are for our analyzer stack only; in normal softphone logic DTMF packets goes via SingleAudioStream::mDtmfReceiver
|
||||||
|
DtmfReceiver mDtmfReceiver;
|
||||||
|
|
||||||
CodecMap mCodecMap;
|
CodecMap mCodecMap;
|
||||||
PCodec mCodec;
|
PCodec mCodec;
|
||||||
int mFrameCount = 0;
|
int mFrameCount = 0;
|
||||||
@@ -247,21 +267,6 @@ protected:
|
|||||||
DecodeResult decodeEmptyTo(Audio::DataWindow& output, DecodeOptions options);
|
DecodeResult decodeEmptyTo(Audio::DataWindow& output, DecodeOptions options);
|
||||||
};
|
};
|
||||||
|
|
||||||
class DtmfReceiver: public Receiver
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
char mEvent = 0;
|
|
||||||
bool mEventEnded = false;
|
|
||||||
std::chrono::milliseconds mEventStart = 0ms;
|
|
||||||
std::function<void(char)> mCallback;
|
|
||||||
|
|
||||||
public:
|
|
||||||
DtmfReceiver(Statistics& stat);
|
|
||||||
~DtmfReceiver();
|
|
||||||
|
|
||||||
void add(const std::shared_ptr<RTPPacket>& p);
|
|
||||||
void setCallback(std::function<void(char tone)> callback);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ bool CodecList::Settings::contains(int ptype) const
|
|||||||
|
|
||||||
if (mGsmEfrPayloadType == ptype || mGsmFrPayloadType == ptype || mGsmHrPayloadType == ptype)
|
if (mGsmEfrPayloadType == ptype || mGsmFrPayloadType == ptype || mGsmHrPayloadType == ptype)
|
||||||
return true;
|
return true;
|
||||||
|
if (mTelephoneEvent == ptype)
|
||||||
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -122,6 +124,9 @@ std::string CodecList::Settings::toString() const
|
|||||||
if (mGsmEfrPayloadType != -1)
|
if (mGsmEfrPayloadType != -1)
|
||||||
oss << "GSM EFR ptype: " << mGsmEfrPayloadType << " ";
|
oss << "GSM EFR ptype: " << mGsmEfrPayloadType << " ";
|
||||||
|
|
||||||
|
if (mTelephoneEvent != -1)
|
||||||
|
oss << "RFC2833 DTMF ptype: " << mTelephoneEvent;
|
||||||
|
|
||||||
for (auto& spec: mEvsSpec)
|
for (auto& spec: mEvsSpec)
|
||||||
{
|
{
|
||||||
oss << "EVS ptype: " << spec.mPayloadType << ", bw: " << spec.mBandwidth << ", enc: " << (spec.mEncodingType == EvsSpec::Encoding_MIME ? "mime" : "g192") << " ";
|
oss << "EVS ptype: " << spec.mPayloadType << ", bw: " << spec.mBandwidth << ", enc: " << (spec.mEncodingType == EvsSpec::Encoding_MIME ? "mime" : "g192") << " ";
|
||||||
@@ -132,6 +137,7 @@ std::string CodecList::Settings::toString() const
|
|||||||
oss << "OPUS ptype: " << spec.mPayloadType << ", rate: " << spec.mRate << ", channels: " << spec.mChannels << std::endl;
|
oss << "OPUS ptype: " << spec.mPayloadType << ", rate: " << spec.mRate << ", channels: " << spec.mChannels << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,6 +157,7 @@ void CodecList::Settings::clear()
|
|||||||
mGsmEfrPayloadType = -1;
|
mGsmEfrPayloadType = -1;
|
||||||
mGsmFrPayloadType = -1;
|
mGsmFrPayloadType = -1;
|
||||||
mGsmHrPayloadType = -1;
|
mGsmHrPayloadType = -1;
|
||||||
|
mTelephoneEvent = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CodecList::Settings::EvsSpec::isValid() const
|
bool CodecList::Settings::EvsSpec::isValid() const
|
||||||
@@ -268,15 +275,16 @@ CodecList::Settings CodecList::Settings::parseSdp(const std::list<resip::Codec>&
|
|||||||
}
|
}
|
||||||
} else if (codec_name == "EVS") {
|
} else if (codec_name == "EVS") {
|
||||||
r.mEvsSpec.push_back({ptype});
|
r.mEvsSpec.push_back({ptype});
|
||||||
}
|
} else if (codec_name == "TELEPHONE-EVENT")
|
||||||
|
r.mTelephoneEvent = ptype;
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CodecList::Settings::operator == (const Settings& rhs) const
|
bool CodecList::Settings::operator == (const Settings& rhs) const
|
||||||
{
|
{
|
||||||
if (std::tie(mWrapIuUP, mSkipDecode, mIsac16KPayloadType, mIsac32KPayloadType, mIlbc20PayloadType, mIlbc30PayloadType, mGsmFrPayloadType, mGsmFrPayloadLength, mGsmEfrPayloadType, mGsmHrPayloadType) !=
|
if (std::tie(mWrapIuUP, mSkipDecode, mIsac16KPayloadType, mIsac32KPayloadType, mIlbc20PayloadType, mIlbc30PayloadType, mGsmFrPayloadType, mGsmFrPayloadLength, mGsmEfrPayloadType, mGsmHrPayloadType, mTelephoneEvent) !=
|
||||||
std::tie(rhs.mWrapIuUP, rhs.mSkipDecode, rhs.mIsac16KPayloadType, rhs.mIsac32KPayloadType, rhs.mIlbc20PayloadType, rhs.mIlbc30PayloadType, rhs.mGsmFrPayloadType, rhs.mGsmFrPayloadLength, rhs.mGsmEfrPayloadType, rhs.mGsmHrPayloadType))
|
std::tie(rhs.mWrapIuUP, rhs.mSkipDecode, rhs.mIsac16KPayloadType, rhs.mIsac32KPayloadType, rhs.mIlbc20PayloadType, rhs.mIlbc30PayloadType, rhs.mGsmFrPayloadType, rhs.mGsmFrPayloadLength, rhs.mGsmEfrPayloadType, rhs.mGsmHrPayloadType, rhs.mTelephoneEvent))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (mAmrNbOctetPayloadType != rhs.mAmrNbOctetPayloadType)
|
if (mAmrNbOctetPayloadType != rhs.mAmrNbOctetPayloadType)
|
||||||
@@ -306,6 +314,9 @@ bool CodecList::Settings::operator == (const Settings& rhs) const
|
|||||||
if (mOpusSpec[i] != rhs.mOpusSpec[i])
|
if (mOpusSpec[i] != rhs.mOpusSpec[i])
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (mTelephoneEvent != rhs.mTelephoneEvent)
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ public:
|
|||||||
bool mWrapIuUP = false;
|
bool mWrapIuUP = false;
|
||||||
bool mSkipDecode = false;
|
bool mSkipDecode = false;
|
||||||
|
|
||||||
|
// RFC2833 DTMF
|
||||||
|
int mTelephoneEvent = -1;
|
||||||
|
|
||||||
// AMR payload types
|
// AMR payload types
|
||||||
std::set<int64_t> mAmrWbPayloadType = { };
|
std::set<int64_t> mAmrWbPayloadType = { };
|
||||||
std::set<int64_t> mAmrNbPayloadType = { };
|
std::set<int64_t> mAmrNbPayloadType = { };
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ SingleAudioStream::~SingleAudioStream()
|
|||||||
void SingleAudioStream::process(const std::shared_ptr<jrtplib::RTPPacket>& packet)
|
void SingleAudioStream::process(const std::shared_ptr<jrtplib::RTPPacket>& packet)
|
||||||
{
|
{
|
||||||
ICELogMedia(<< "Processing incoming RTP/RTCP packet");
|
ICELogMedia(<< "Processing incoming RTP/RTCP packet");
|
||||||
if (packet->GetPayloadType() == 101/*resip::Codec::TelephoneEvent.payloadType()*/)
|
if (packet->GetPayloadType() == mReceiver.getCodecSettings().mTelephoneEvent)
|
||||||
mDtmfReceiver.add(packet);
|
mDtmfReceiver.add(packet);
|
||||||
else
|
else
|
||||||
mReceiver.add(packet);
|
mReceiver.add(packet);
|
||||||
|
|||||||
Reference in New Issue
Block a user