Compare commits

...

4 Commits

7 changed files with 208 additions and 177 deletions
+10 -2
View File
@@ -360,13 +360,21 @@ endif()
target_compile_definitions(rtphone PUBLIC ${DEFINES} ) target_compile_definitions(rtphone PUBLIC ${DEFINES} )
if (TARGET_LINUX) if (TARGET_LINUX)
target_link_options(rtphone PUBLIC -Wl,-Bstatic) # PRIVATE, not PUBLIC: rtphone is a STATIC library, so these link options are
# never used to build rtphone itself and must not propagate to consumers.
# As PUBLIC they leaked into every consumer's LINK_FLAGS as an adjacent
# "-Wl,-Bstatic -Wl,-Bdynamic" pair (the wrapped libraries land in a separate
# LINK_LIBRARIES section, so nothing is actually wrapped). The trailing
# -Bdynamic forced the linker back into dynamic-search mode, which broke
# fully-static consumers (e.g. vq-core built with SERVER_STATIC_LINKING=ON:
# "attempted static link of dynamic object libz.so").
target_link_options(rtphone PRIVATE -Wl,-Bstatic)
target_compile_options(rtphone PUBLIC -Wno-deprecated -Wno-deprecated-declarations) target_compile_options(rtphone PUBLIC -Wno-deprecated -Wno-deprecated-declarations)
endif() endif()
target_link_libraries(rtphone PUBLIC ${LIBS_STATIC}) target_link_libraries(rtphone PUBLIC ${LIBS_STATIC})
if (TARGET_LINUX) if (TARGET_LINUX)
target_link_options(rtphone PUBLIC -Wl,-Bdynamic) target_link_options(rtphone PRIVATE -Wl,-Bdynamic)
endif() endif()
target_include_directories(rtphone target_include_directories(rtphone
+32 -28
View File
@@ -9,11 +9,7 @@
using namespace Audio; using namespace Audio;
DataWindow::DataWindow() DataWindow::DataWindow()
{ {}
mFilled = 0;
mData = nullptr;
mCapacity = 0;
}
DataWindow::~DataWindow() DataWindow::~DataWindow()
{ {
@@ -24,24 +20,30 @@ DataWindow::~DataWindow()
} }
} }
void DataWindow::setCapacity(int capacity) void DataWindow::setCapacity(size_t capacity)
{ {
Lock l(mMutex); Lock l(mMutex);
int tail = capacity - mCapacity;
char* buffer = mData; if (capacity >= mCapacity)
mData = (char*)realloc(mData, capacity);
if (!mData)
{ {
// Realloc failed size_t tail = capacity - mCapacity;
mData = buffer; char* buffer = mData;
throw std::bad_alloc(); mData = (char*)realloc(mData, capacity);
if (!mData)
{
// Realloc failed
mData = buffer;
throw std::bad_alloc();
}
if (tail > 0)
memset(mData + mCapacity, 0, tail);
mCapacity = capacity;
} }
if (tail > 0) else
memset(mData + mCapacity, 0, tail); throw std::bad_alloc();
mCapacity = capacity;
} }
void DataWindow::addZero(int length) void DataWindow::addZero(size_t length)
{ {
Lock l(mMutex); Lock l(mMutex);
@@ -60,7 +62,7 @@ void DataWindow::addZero(int length)
} }
void DataWindow::add(const void* data, int length) void DataWindow::add(const void* data, size_t length)
{ {
Lock l(mMutex); Lock l(mMutex);
@@ -94,7 +96,7 @@ void DataWindow::add(short sample)
add(&sample, sizeof sample); add(&sample, sizeof sample);
} }
void DataWindow::erase(int length) void DataWindow::erase(size_t length)
{ {
Lock l(mMutex); Lock l(mMutex);
if (length > mFilled) if (length > mFilled)
@@ -120,21 +122,21 @@ void DataWindow::clear()
mFilled = 0; mFilled = 0;
} }
short DataWindow::shortAt(int index) const short DataWindow::shortAt(size_t index) const
{ {
Lock l(mMutex); Lock l(mMutex);
assert(index < mFilled / 2); assert(index < mFilled / 2);
return ((short*)mData)[index]; return ((short*)mData)[index];
} }
void DataWindow::setShortAt(short value, int index) void DataWindow::setShortAt(short value, size_t index)
{ {
Lock l(mMutex); Lock l(mMutex);
assert(index < mFilled / 2); assert(index < mFilled / 2);
((short*)mData)[index] = value; ((short*)mData)[index] = value;
} }
int DataWindow::read(void* buffer, int length) size_t DataWindow::read(void* buffer, size_t length)
{ {
Lock l(mMutex); Lock l(mMutex);
if (length > mFilled) if (length > mFilled)
@@ -150,25 +152,27 @@ int DataWindow::read(void* buffer, int length)
return length; return length;
} }
int DataWindow::filled() const size_t DataWindow::filled() const
{ {
Lock l(mMutex); Lock l(mMutex);
return mFilled; return mFilled;
} }
void DataWindow::setFilled(int filled) void DataWindow::setFilled(size_t filled)
{ {
Lock l(mMutex); Lock l(mMutex);
if (filled > mCapacity)
throw std::bad_alloc();
mFilled = filled; mFilled = filled;
} }
int DataWindow::capacity() const size_t DataWindow::capacity() const
{ {
Lock l(mMutex); Lock l(mMutex);
return mCapacity; return mCapacity;
} }
void DataWindow::zero(int length) void DataWindow::zero(size_t length)
{ {
Lock l(mMutex); Lock l(mMutex);
assert(length <= mCapacity); assert(length <= mCapacity);
@@ -189,10 +193,10 @@ size_t DataWindow::moveTo(DataWindow& dst, size_t size)
return avail; return avail;
} }
std::chrono::milliseconds DataWindow::getTimeLength(int samplerate, int channels) const std::chrono::milliseconds DataWindow::getTimeLength(const Audio::Format& fmt) const
{ {
Lock l(mMutex); Lock l(mMutex);
return std::chrono::milliseconds(mFilled / sizeof(short) / channels / (samplerate / 1000)); return std::chrono::milliseconds(mFilled / sizeof(short) / fmt.channels() / (fmt.rate()/ 1000));
} }
void DataWindow::makeStereoFromMono(DataWindow& dst, DataWindow& src) void DataWindow::makeStereoFromMono(DataWindow& dst, DataWindow& src)
+17 -16
View File
@@ -8,6 +8,7 @@
#include "../helper/HL_ByteBuffer.h" #include "../helper/HL_ByteBuffer.h"
#include "../helper/HL_Sync.h" #include "../helper/HL_Sync.h"
#include "Audio_Interface.h"
namespace Audio namespace Audio
{ {
@@ -17,34 +18,34 @@ public:
DataWindow(); DataWindow();
~DataWindow(); ~DataWindow();
void setCapacity(int capacity); void setCapacity(size_t capacity);
int capacity() const; size_t capacity() const;
void addZero(int length); void addZero(size_t length);
void add(const void* data, int length); void add(const void* data, size_t length);
void add(short sample); void add(short sample);
int read(void* buffer, int length); size_t read(void* buffer, size_t length);
void erase(int length = -1); void erase(size_t length);
const char* data() const; const char* data() const;
char* mutableData(); char* mutableData();
int filled() const; size_t filled() const;
void setFilled(int filled); void setFilled(size_t filled);
void clear(); void clear();
short shortAt(int index) const; short shortAt(size_t index) const;
void setShortAt(short value, int index); void setShortAt(short value, size_t index);
void zero(int length); void zero(size_t length);
size_t moveTo(DataWindow& dst, size_t size); size_t moveTo(DataWindow& dst, size_t size /* in bytes*/ );
std::chrono::milliseconds getTimeLength(int samplerate, int channels) const; std::chrono::milliseconds getTimeLength(const Format& fmt) const;
static void makeStereoFromMono(DataWindow& dst, DataWindow& src); static void makeStereoFromMono(DataWindow& dst, DataWindow& src);
protected: protected:
mutable Mutex mMutex; mutable Mutex mMutex;
char* mData; char* mData = nullptr;
int mFilled; size_t mFilled = 0;
int mCapacity; size_t mCapacity = 0;
}; };
} }
#endif #endif
+122 -121
View File
@@ -28,8 +28,7 @@ using namespace MT;
// ----------------- RtpBuffer::Packet -------------- // ----------------- RtpBuffer::Packet --------------
RtpBuffer::Packet::Packet(const std::shared_ptr<RTPPacket>& packet, std::chrono::milliseconds timelength, int samplerate) RtpBuffer::Packet::Packet(const std::shared_ptr<RTPPacket>& packet, std::chrono::milliseconds timelength, int samplerate)
:mRtp(packet), mTimelength(timelength), mSamplerate(samplerate) :mRtp(packet), mTimelength(timelength), mSamplerate(samplerate)
{ {}
}
std::shared_ptr<RTPPacket> RtpBuffer::Packet::rtp() const std::shared_ptr<RTPPacket> RtpBuffer::Packet::rtp() const
{ {
@@ -66,7 +65,8 @@ RtpBuffer::RtpBuffer(Statistics& stat)
RtpBuffer::~RtpBuffer() RtpBuffer::~RtpBuffer()
{ {
ICELogDebug(<< "Number of add packets: " << mAddCounter << ", number of retrieved packets " << mReturnedCounter); if (mAddCounter)
ICELogDebug(<< "Number of add packets: " << mAddCounter << ", number of retrieved packets " << mReturnedCounter);
} }
void RtpBuffer::setHigh(std::chrono::milliseconds t) void RtpBuffer::setHigh(std::chrono::milliseconds t)
@@ -129,7 +129,7 @@ std::shared_ptr<RtpBuffer::Packet> RtpBuffer::add(const std::shared_ptr<jrtplib:
mStat.mSsrc = static_cast<uint16_t>(packet->GetSSRC()); mStat.mSsrc = static_cast<uint16_t>(packet->GetSSRC());
// Update jitter // Update jitter
ICELogMedia(<< "Adding new packet into jitter buffer"); ICELogMedia(<< "Adding new packet seqno " << packet->GetSequenceNumber() << " into jitter buffer");
mAddCounter++; mAddCounter++;
// Look for maximum&minimal sequence number; check for dublicates // Look for maximum&minimal sequence number; check for dublicates
@@ -138,7 +138,7 @@ std::shared_ptr<RtpBuffer::Packet> RtpBuffer::add(const std::shared_ptr<jrtplib:
// New sequence number // New sequence number
unsigned newSeqno = packet->GetExtendedSequenceNumber(); unsigned newSeqno = packet->GetExtendedSequenceNumber();
for (std::shared_ptr<Packet>& p: mPacketList) for (auto& p: mPacketList)
{ {
unsigned seqno = p->rtp()->GetExtendedSequenceNumber(); unsigned seqno = p->rtp()->GetExtendedSequenceNumber();
@@ -171,7 +171,7 @@ std::shared_ptr<RtpBuffer::Packet> RtpBuffer::add(const std::shared_ptr<jrtplib:
available = findTimelength(); available = findTimelength();
if (available > mHigh) if (available > mHigh)
ICELogMedia(<< "Available " << available << "ms with limit " << mHigh << "ms"); ICELogMedia(<< "Available " << available << " with limit " << mHigh);
return p; return p;
} }
@@ -346,16 +346,14 @@ int RtpBuffer::getNumberOfAddPackets() const
//-------------- Receiver --------------- //-------------- Receiver ---------------
Receiver::Receiver(Statistics& stat) Receiver::Receiver(Statistics& stat)
:mStat(stat) :mStat(stat)
{ {}
}
Receiver::~Receiver() 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), mDtmfBuffer(stat), mCodecSettings(settings), mCodecList(settings), mDtmfReceiver(stat) :Receiver(stat), mRtpBuffer(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);
@@ -367,12 +365,17 @@ AudioReceiver::AudioReceiver(const CodecList::Settings& settings, MT::Statistics
mCodecList.setSettings(settings); mCodecList.setSettings(settings);
mCodecList.fillCodecMap(mCodecMap); mCodecList.fillCodecMap(mCodecMap);
mAvailable.setCapacity(AUDIO_SAMPLERATE * sizeof(short)); // 10 seconds is the maximum length of decoded audio in single step
// It is important - DTX may produce silence up to few seconds easily
mAvailable.setCapacity(AUDIO_SAMPLERATE * 10 * sizeof(short));
mDtmfBuffer.setPrebuffer(0ms); mDtmfBuffer.setPrebuffer(0ms);
mDtmfBuffer.setLow(0ms); mDtmfBuffer.setLow(0ms);
mDtmfBuffer.setHigh(1ms); mDtmfBuffer.setHigh(1ms);
// Avoid collecting too much data
mRtpBuffer.setHigh(240ms);
#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);
@@ -386,6 +389,11 @@ AudioReceiver::~AudioReceiver()
mResampler32.stop(); mResampler32.stop();
mResampler48.stop(); mResampler48.stop();
mDecodedDump.reset(); mDecodedDump.reset();
if (mRequestedAudio != 0ms)
ICELogDebug(<< "Requested " << mRequestedAudio << ", produced " << mProducedAudio);
if (mDecodeCount)
ICELogDebug(<< "Average interval between packet decoding " << mIntervalBetweenDecode / mDecodeCount);
} }
// Update codec settings // Update codec settings
@@ -450,7 +458,7 @@ Codec* AudioReceiver::add(const std::shared_ptr<jrtplib::RTPPacket>& p)
payloadLength = p->GetPayloadLength(), payloadLength = p->GetPayloadLength(),
ptype = p->GetPayloadType(); ptype = p->GetPayloadType();
ICELogMedia(<< "Adding packet No " << p->GetSequenceNumber()); // ICELogMedia(<< "Adding packet No " << p->GetSequenceNumber());
// Increase codec counter // Increase codec counter
mStat.mCodecCount[ptype]++; mStat.mCodecCount[ptype]++;
@@ -508,12 +516,12 @@ Codec* AudioReceiver::add(const std::shared_ptr<jrtplib::RTPPacket>& p)
{ {
// It will cause statistics to report about bad RTP packet // It will cause statistics to report about bad RTP packet
// I have to replay last packet payload here to avoid report about lost packet // I have to replay last packet payload here to avoid report about lost packet
mBuffer.add(p, std::chrono::milliseconds(time_length), samplerate); mRtpBuffer.add(p, std::chrono::milliseconds(time_length), samplerate);
return nullptr; return nullptr;
} }
// Queue packet to buffer // Queue packet to buffer
mBuffer.add(p, std::chrono::milliseconds(time_length), samplerate).get(); mRtpBuffer.add(p, std::chrono::milliseconds(time_length), samplerate).get();
} }
return codec; return codec;
} }
@@ -533,8 +541,12 @@ void AudioReceiver::processDecoded(Audio::DataWindow& output, DecodeOptions opti
void AudioReceiver::produceSilence(std::chrono::milliseconds length, Audio::DataWindow& output, DecodeOptions options) void AudioReceiver::produceSilence(std::chrono::milliseconds length, Audio::DataWindow& output, DecodeOptions options)
{ {
if (!mCodec)
return;
// Fill mDecodeBuffer as much as needed and call processDecoded() // Fill mDecodeBuffer as much as needed and call processDecoded()
// Depending on used codec mono or stereo silence should be produced // Depending on used codec mono or stereo silence should be produced
size_t chunks = length.count() / 10; size_t chunks = length.count() / 10;
size_t tail = length.count() % 10; size_t tail = length.count() % 10;
size_t chunk_size = 10 * sizeof(int16_t) * mCodec->samplerate() / 1000 * mCodec->channels(); size_t chunk_size = 10 * sizeof(int16_t) * mCodec->samplerate() / 1000 * mCodec->channels();
@@ -635,21 +647,23 @@ AudioReceiver::DecodeResult AudioReceiver::decodePacketTo(Audio::DataWindow& out
auto& rtp = *packet->rtp(); // Syntax sugar auto& rtp = *packet->rtp(); // Syntax sugar
mFailedCount = 0; mFailedCount = 0;
// Check if we need to emit silence or CNG - previously CNG packet was detected. Emit CNG audio here if needed.
// Check if we need to emit silence - it may happen in the case if next packet has RTP timestamp much beyond the previous one; maybe DTX was active.
if (mLastPacketTimestamp && mLastPacketTimeLength && mCodec) if (mLastPacketTimestamp && mLastPacketTimeLength && mCodec)
{ {
int units = rtp.GetTimestamp() - *mLastPacketTimestamp; int units = rtp.GetTimestamp() - *mLastPacketTimestamp;
int milliseconds = units / (mCodec->samplerate() / 1000); int milliseconds = units / (mCodec->samplerate() / 1000);
if (milliseconds > mLastPacketTimeLength) if (milliseconds > mLastPacketTimeLength)
{ {
auto silenceLength = std::chrono::milliseconds(milliseconds - mLastPacketTimeLength); auto silenceLength = std::chrono::milliseconds(milliseconds - mLastPacketTimeLength);
ICELogDebug(<< "Emit " << silenceLength << " silence while requested " << options.mElapsed);
if (mCngPacket && options.mFillGapByCNG) silenceLength = std::min(silenceLength, options.mElapsed);
produceCNG(silenceLength, output, options); if (mCngPacket && options.mFillGapByCNG)
else produceCNG(silenceLength, output, options);
produceSilence(silenceLength, output, options); else
} produceSilence(silenceLength, output, options);
} }
}
mLastPacketTimestamp = rtp.GetTimestamp(); mLastPacketTimestamp = rtp.GetTimestamp();
@@ -677,6 +691,7 @@ AudioReceiver::DecodeResult AudioReceiver::decodePacketTo(Audio::DataWindow& out
mDecodedLength = 0; mDecodedLength = 0;
else else
{ {
ICELogDebug(<< "Decoding CNG");
mCngPacket = packet; mCngPacket = packet;
mCngDecoder.decode3389(rtp.GetPayloadData(), rtp.GetPayloadLength()); mCngDecoder.decode3389(rtp.GetPayloadData(), rtp.GetPayloadLength());
@@ -775,7 +790,7 @@ AudioReceiver::DecodeResult AudioReceiver::decodeEmptyTo(Audio::DataWindow& outp
else else
{ {
// Emit silence if codec information is available - it is to properly handle the gaps // Emit silence if codec information is available - it is to properly handle the gaps
auto avail = output.getTimeLength(fmt.rate(), fmt.channels()); auto avail = output.getTimeLength(fmt);
if (options.mElapsed > avail) if (options.mElapsed > avail)
output.addZero(fmt.sizeFromTime(options.mElapsed - avail)); output.addZero(fmt.sizeFromTime(options.mElapsed - avail));
} }
@@ -785,47 +800,77 @@ AudioReceiver::DecodeResult AudioReceiver::decodeEmptyTo(Audio::DataWindow& outp
return {.mStatus = DecodeResult::Status::Skip}; return {.mStatus = DecodeResult::Status::Skip};
} }
void MT::AudioReceiver::processDtmf()
{
if (mDtmfBuffer.getCount())
{
auto fr = mDtmfBuffer.fetch();
if (fr.mPacket && fr.mStatus == RtpBuffer::FetchResult::Status::RegularPacket)
mDtmfReceiver.add(fr.mPacket->rtp());
}
}
void MT::AudioReceiver::updateDecodingTimeStatistics()
{
if (!mDecodeTimestamp)
mDecodeTimestamp = std::chrono::steady_clock::now();
else
{
auto t = std::chrono::steady_clock::now();
mStat.mDecodingInterval.process(std::chrono::duration_cast<std::chrono::milliseconds>(t - *mDecodeTimestamp).count());
mDecodeTimestamp = t;
}
}
AudioReceiver::DecodeResult AudioReceiver::getAudioTo(Audio::DataWindow& output, DecodeOptions options) AudioReceiver::DecodeResult AudioReceiver::getAudioTo(Audio::DataWindow& output, DecodeOptions options)
{ {
// ICELogDebug(<< "getAudioTo() for " << options.mElapsed);
assert (options.mElapsed != 0ms);
// Increase counter of requested audio
mRequestedAudio += options.mElapsed;
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 // Process RFC2833 here; it doesn't result in any audio - only callbacks and statistics
auto fr = mDtmfBuffer.fetch(); processDtmf();
if (fr.mPacket && fr.mStatus == RtpBuffer::FetchResult::Status::RegularPacket)
mDtmfReceiver.add(fr.mPacket->rtp());
// How much time length audio we produced here
auto produced = 0ms; auto produced = 0ms;
if (mAvailable.filled() && mCodec && options.mElapsed != 0ms) Audio::Format fmt;
{
Audio::Format fmt = options.mResampleToMainRate ? Audio::Format(AUDIO_SAMPLERATE, 1) : mCodec->getAudioFormat();
auto initiallyAvailable = mCodec ? mAvailable.getTimeLength(fmt.rate(), fmt.channels()) : 0ms;
if (initiallyAvailable != 0ms)
{
std::chrono::milliseconds resultTime = std::min(initiallyAvailable, options.mElapsed);
auto resultLen = fmt.sizeFromTime(resultTime);
mAvailable.moveTo(output, resultLen);
produced += resultTime;
// Maybe request is satisfied ? // Have we anything from the previous decode attempts ?
if (produced >= options.mElapsed) if (mAvailable.filled())
return {.mStatus = DecodeResult::Status::Ok, .mSamplerate = fmt.rate(), .mChannels = fmt.channels()}; {
// Find what audio format is used in mAvailable data
fmt = options.mResampleToMainRate ? Audio::Format(AUDIO_SAMPLERATE, 1) : mCodec->getAudioFormat();
// How much milliseconds are available ?
auto availTime = mAvailable.getTimeLength(fmt);
if (availTime != 0ms)
{
// How much we can consume from the mAvailable buffer ?
std::chrono::milliseconds resultTime = std::min(availTime, options.mElapsed);
// Number of bytes
mAvailable.moveTo(output, fmt.sizeFromTime(resultTime));
// Increase the counter of produced milliseconds
produced += resultTime;
} }
} }
std::chrono::milliseconds decoded = 0ms; while (produced < options.mElapsed)
do
{ {
// Get next packet from buffer // Get next packet from buffer
RtpBuffer::ResultList rl; RtpBuffer::FetchResult fr = mRtpBuffer.fetch();
RtpBuffer::FetchResult fr = mBuffer.fetch();
// ICELogDebug(<< fr.toString() << " " << mBuffer.findTimelength());
// Decode to mAvailable buffer
switch (fr.mStatus) switch (fr.mStatus)
{ {
case RtpBuffer::FetchResult::Status::Gap: result = decodeGapTo(mAvailable, options); break; case RtpBuffer::FetchResult::Status::Gap: result = decodeGapTo(mAvailable, options.decreaseElapsedBy(produced)); break;
case RtpBuffer::FetchResult::Status::NoPacket: result = decodeEmptyTo(mAvailable, options); break; case RtpBuffer::FetchResult::Status::NoPacket: result = decodeEmptyTo(mAvailable, options.decreaseElapsedBy(produced)); break;
case RtpBuffer::FetchResult::Status::RegularPacket: result = decodePacketTo(mAvailable, options, fr.mPacket); break; case RtpBuffer::FetchResult::Status::RegularPacket: result = decodePacketTo(mAvailable, options.decreaseElapsedBy(produced), fr.mPacket); updateDecodeIntervalStatistics(); break;
default: default:
assert(0); assert(0);
} }
@@ -834,46 +879,29 @@ AudioReceiver::DecodeResult AudioReceiver::getAudioTo(Audio::DataWindow& output,
if (!mCodec) if (!mCodec)
break; // No sense to continue - we have no information at all break; // No sense to continue - we have no information at all
Audio::Format fmt = options.mResampleToMainRate ? Audio::Format(AUDIO_SAMPLERATE, 1) : mCodec->getAudioFormat(); fmt = options.mResampleToMainRate ? Audio::Format(AUDIO_SAMPLERATE, 1) : mCodec->getAudioFormat();
result.mSamplerate = fmt.rate(); result.mSamplerate = fmt.rate();
result.mChannels = fmt.channels(); result.mChannels = fmt.channels();
// Have we anything interesting in the buffer ? // How much milliseconds we have in audio buffer ?
auto bufferAvailable = mAvailable.getTimeLength(fmt.rate(), fmt.channels()); auto bufferAvailable = mAvailable.getTimeLength(fmt);
if (bufferAvailable == 0ms) if (bufferAvailable == 0ms)
break; // No sense to continue - decoding / CNG / PLC stopped totally break; // No sense to continue - decoding / CNG / PLC stopped totally
// How much data should be moved to result buffer ? // How much data should be moved to result buffer ?
if (options.mElapsed != 0ms) std::chrono::milliseconds resultTime = std::min(bufferAvailable, options.mElapsed - produced);
{ mAvailable.moveTo(output, fmt.sizeFromTime(resultTime));
std::chrono::milliseconds resultTime = std::min(bufferAvailable, options.mElapsed - produced); produced += resultTime;
auto resultLen = fmt.sizeFromTime(resultTime);
mAvailable.moveTo(output, resultLen);
produced += resultTime;
}
else
mAvailable.moveTo(output, mAvailable.filled());
decoded += bufferAvailable;
} }
while (produced < options.mElapsed);
if (produced != 0ms) if (produced != 0ms)
result.mStatus = DecodeResult::Status::Ok;
// Time statistics
if (result.mStatus == DecodeResult::Status::Ok)
{ {
// Decode statistics result.mStatus = DecodeResult::Status::Ok;
if (!mDecodeTimestamp) updateDecodingTimeStatistics();
mDecodeTimestamp = std::chrono::steady_clock::now();
else
{
auto t = std::chrono::steady_clock::now();
mStat.mDecodingInterval.process(std::chrono::duration_cast<std::chrono::milliseconds>(t - *mDecodeTimestamp).count());
mDecodeTimestamp = t;
}
} }
mProducedAudio += produced;
// ICELogDebug(<< "Requested " << options.mElapsed << ", produced " << produced << ", remains " << mAvailable.getTimeLength(fmt) << ", packets " << getRtpBuffer().getCount());
return result; return result;
} }
@@ -987,43 +1015,16 @@ AudioReceiver::MediaInfo AudioReceiver::infoFor(jrtplib::RTPPacket& p)
return {packetTime, codec->samplerate()}; return {packetTime, codec->samplerate()};
} }
// int AudioReceiver::timelengthFor(jrtplib::RTPPacket& p) void AudioReceiver::updateDecodeIntervalStatistics()
// { {
// CodecMap::iterator codecIter = mCodecMap.find(p.GetPayloadType()); auto now = std::chrono::steady_clock::now();
// if (codecIter == mCodecMap.end()) if (mLastDecodeTimestamp)
// return 0; {
mIntervalBetweenDecode += std::chrono::duration_cast<std::chrono::microseconds>(now - *mLastDecodeTimestamp);
// PCodec codec = codecIter->second; mDecodeCount ++;
// if (codec) }
// { mLastDecodeTimestamp = now;
// int frame_count = 0; }
// if (codec->rtpLength() != 0)
// {
// frame_count = static_cast<int>(p.GetPayloadLength() / codec->rtpLength());
// if (p.GetPayloadType() == 9/*G729A silence*/ && p.GetPayloadLength() % codec->rtpLength())
// frame_count++;
// }
// else
// frame_count = 1;
// return frame_count * codec->frameTime();
// }
// else
// return 0;
// }
// int AudioReceiver::samplerateFor(jrtplib::RTPPacket& p)
// {
// CodecMap::iterator codecIter = mCodecMap.find(p.GetPayloadType());
// if (codecIter != mCodecMap.end())
// {
// PCodec codec = codecIter->second;
// if (codec)
// return codec->samplerate();
// }
// return 8000;
// }
// ----------------------- DtmfReceiver ------------------- // ----------------------- DtmfReceiver -------------------
DtmfReceiver::DtmfReceiver(Statistics& stat) DtmfReceiver::DtmfReceiver(Statistics& stat)
+27 -7
View File
@@ -122,6 +122,7 @@ protected:
std::optional<uint32_t> mLastSeqno; std::optional<uint32_t> mLastSeqno;
std::optional<jrtplib::RTPTime> mLastReceiveTime; std::optional<jrtplib::RTPTime> mLastReceiveTime;
// To calculate average interval between packet add. It is close to jitter but more useful in debugging. // To calculate average interval between packet add. It is close to jitter but more useful in debugging.
float mLastAddTime = 0.0f; float mLastAddTime = 0.0f;
}; };
@@ -169,10 +170,22 @@ public:
struct DecodeOptions struct DecodeOptions
{ {
bool mRealtimeProcessing = false; // Target PCAP parsing by default
bool mResampleToMainRate = true; // Resample all decoded audio to AUDIO_SAMPLERATE bool mResampleToMainRate = true; // Resample all decoded audio to AUDIO_SAMPLERATE
bool mFillGapByCNG = false; // Use CNG information if available bool mFillGapByCNG = false; // Use CNG information if available
bool mSkipDecode = false; // Don't do decode, just dry run - fetch packets, remove them from the jitter buffer bool mSkipDecode = false; // Don't do decode, just dry run - fetch packets, remove them from the jitter buffer
std::chrono::milliseconds mElapsed = 0ms; // How much milliseconds should be decoded; zero value means "decode just next packet from the buffer" std::chrono::milliseconds mElapsed = 0ms; // How much milliseconds should be decoded; zero value means "decode just next packet from the buffer"
DecodeOptions decreaseElapsedBy(std::chrono::milliseconds delta)
{
return
{
.mRealtimeProcessing = mRealtimeProcessing,
.mResampleToMainRate = mResampleToMainRate,
.mFillGapByCNG = mFillGapByCNG,
.mSkipDecode = mSkipDecode,
.mElapsed = std::max(mElapsed - delta, 0ms)
};
}
}; };
struct DecodeResult struct DecodeResult
@@ -192,8 +205,8 @@ public:
DecodeResult getAudioTo(Audio::DataWindow& output, DecodeOptions options); DecodeResult getAudioTo(Audio::DataWindow& output, DecodeOptions options);
// Looks for codec by payload type // Looks for codec by payload type
Codec* findCodec(int payloadType); Codec* findCodec(int payloadType);
RtpBuffer& getRtpBuffer() { return mBuffer; } RtpBuffer& getRtpBuffer() { return mRtpBuffer; }
// Returns size of AudioReceiver's instance in bytes (including size of all data + codecs + etc.) // Returns size of AudioReceiver's instance in bytes (including size of all data + codecs + etc.)
int getSize() const; int getSize() const;
@@ -205,14 +218,12 @@ public:
}; };
MediaInfo infoFor(jrtplib::RTPPacket& p); MediaInfo infoFor(jrtplib::RTPPacket& p);
// // Returns timelength for given packet void processDtmf();
// int timelengthFor(jrtplib::RTPPacket& p);
// // Return samplerate for given packet void updateDecodingTimeStatistics();
// int samplerateFor(jrtplib::RTPPacket& p);
protected: protected:
RtpBuffer mBuffer; // Jitter buffer itself RtpBuffer mRtpBuffer; // RTP jitter buffer itself; here are audio packets
RtpBuffer mDtmfBuffer; // These two (mDtmfBuffer / mDtmfReceiver) are for our analyzer stack only; in normal softphone logic DTMF packets goes via SingleAudioStream::mDtmfReceiver RtpBuffer mDtmfBuffer; // These two (mDtmfBuffer / mDtmfReceiver) are for our analyzer stack only; in normal softphone logic DTMF packets goes via SingleAudioStream::mDtmfReceiver
DtmfReceiver mDtmfReceiver; DtmfReceiver mDtmfReceiver;
@@ -258,6 +269,9 @@ protected:
float mIntervalSum = 0.0f; float mIntervalSum = 0.0f;
int mIntervalCount = 0; int mIntervalCount = 0;
std::chrono::milliseconds mRequestedAudio = 0ms;
std::chrono::milliseconds mProducedAudio = 0ms;
// Zero rate will make audio mono but resampling will be skipped // Zero rate will make audio mono but resampling will be skipped
void makeMonoAndResample(int rate, int channels); void makeMonoAndResample(int rate, int channels);
@@ -272,6 +286,12 @@ protected:
DecodeResult decodeGapTo(Audio::DataWindow& output, DecodeOptions options); DecodeResult decodeGapTo(Audio::DataWindow& output, DecodeOptions options);
DecodeResult decodePacketTo(Audio::DataWindow& output, DecodeOptions options, const std::shared_ptr<RtpBuffer::Packet>& p); DecodeResult decodePacketTo(Audio::DataWindow& output, DecodeOptions options, const std::shared_ptr<RtpBuffer::Packet>& p);
DecodeResult decodeEmptyTo(Audio::DataWindow& output, DecodeOptions options); DecodeResult decodeEmptyTo(Audio::DataWindow& output, DecodeOptions options);
std::optional<std::chrono::steady_clock::time_point> mLastDecodeTimestamp;
std::chrono::microseconds mIntervalBetweenDecode = 0us;
size_t mDecodeCount = 0;
void updateDecodeIntervalStatistics();
}; };
} }
-1
View File
@@ -137,7 +137,6 @@ 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();
} }
-2
View File
@@ -342,8 +342,6 @@ void Logger::beginLine(LogLevel level, const char* filename, int linenumber, con
mFilename = filenamestart; mFilename = filenamestart;
mLine = linenumber; mLine = linenumber;
mSubsystem = subsystem; mSubsystem = subsystem;
// mStream << std::setw(8) << ICETimeHelper::timestamp() << " | " << std::setw(8) << ThreadInfo::currentThread() << " | " << std::setw(30) << filenamestart << " | " << std::setw(4) << linenumber << " | " << std::setw(12) << subsystem << " | ";
} }
void void