- optimize memory usage
This commit is contained in:
@@ -0,0 +1,179 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/// @file
|
||||||
|
/// @brief A thread-local fixed-block memory pool plus a std-conforming Allocator wrapper, used to
|
||||||
|
/// pool the small, short-lived, per-RTP-packet shared_ptr nodes produced by std::allocate_shared
|
||||||
|
/// (e.g. allocate_shared<jrtplib::RTPPacket> on the capture path and allocate_shared<RtpBuffer::
|
||||||
|
/// Packet> in the jitter buffer). Those objects are fixed-size and churn at the packet rate, so a
|
||||||
|
/// pool removes them from the general allocator's hot path.
|
||||||
|
///
|
||||||
|
/// Pooling is active by default. Define HL_RTP_POOL=0 at compile time to make hl::PoolAllocator a
|
||||||
|
/// transparent passthrough to the global allocator (i.e. allocate_shared behaves like make_shared)
|
||||||
|
/// for A/B benchmarking without touching the call sites.
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <mutex>
|
||||||
|
#include <new>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#ifndef HL_RTP_POOL
|
||||||
|
# define HL_RTP_POOL 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace hl
|
||||||
|
{
|
||||||
|
#if HL_RTP_POOL
|
||||||
|
/// @class FixedBlockPool
|
||||||
|
/// A process-wide, fixed-block pool with a lock-free thread-local fast path. Identical in
|
||||||
|
/// design to the pcpp Layer pool: uniform 256-byte blocks carved from 64 KB chunks, an
|
||||||
|
/// intrusive thread-local free list, and a per-block header tag so deallocate() is O(1) and
|
||||||
|
/// lock-free for any block (and can tell pooled blocks from the global-allocator fallback used
|
||||||
|
/// for oversized requests) regardless of the freeing thread. Uniform block size makes a block
|
||||||
|
/// allocated on one thread safe to free on another (it joins the freeing thread's free list).
|
||||||
|
class FixedBlockPool
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Usable bytes handed back to the caller from a pooled block. Comfortably covers the
|
||||||
|
/// shared_ptr nodes we pool (control block + RTPPacket / RtpBuffer::Packet, ~90-130 bytes).
|
||||||
|
static constexpr std::size_t PayloadSize = 240;
|
||||||
|
static constexpr std::size_t BlocksPerChunk = 256;
|
||||||
|
|
||||||
|
static void* allocate(std::size_t size)
|
||||||
|
{
|
||||||
|
if (size > PayloadSize)
|
||||||
|
{
|
||||||
|
uint8_t* raw = static_cast<uint8_t*>(::operator new(size + HeaderSize));
|
||||||
|
tagOf(raw) = TagGlobal;
|
||||||
|
return raw + HeaderSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void*& head = freeListHead();
|
||||||
|
if (head == nullptr)
|
||||||
|
head = registry().refill();
|
||||||
|
|
||||||
|
uint8_t* block = static_cast<uint8_t*>(head);
|
||||||
|
head = nextOf(block);
|
||||||
|
return block + HeaderSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void deallocate(void* ptr) noexcept
|
||||||
|
{
|
||||||
|
if (ptr == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint8_t* block = static_cast<uint8_t*>(ptr) - HeaderSize;
|
||||||
|
if (tagOf(block) == TagPool)
|
||||||
|
{
|
||||||
|
void*& head = freeListHead();
|
||||||
|
nextOf(block) = head;
|
||||||
|
head = block;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
::operator delete(static_cast<void*>(block));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr std::size_t HeaderSize =
|
||||||
|
alignof(std::max_align_t) >= sizeof(uint64_t) ? alignof(std::max_align_t) : sizeof(uint64_t);
|
||||||
|
static constexpr std::size_t BlockSize = HeaderSize + PayloadSize;
|
||||||
|
|
||||||
|
static constexpr uint64_t TagPool = 0x504F4F4C52545008ULL; // "POOLRTP\b"
|
||||||
|
static constexpr uint64_t TagGlobal = 0x474C4F42524C0808ULL; // "GLOBRL\b\b"
|
||||||
|
|
||||||
|
static uint64_t& tagOf(void* block) noexcept
|
||||||
|
{
|
||||||
|
return *reinterpret_cast<uint64_t*>(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void*& nextOf(void* block) noexcept
|
||||||
|
{
|
||||||
|
return *reinterpret_cast<void**>(static_cast<uint8_t*>(block) + HeaderSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void*& freeListHead() noexcept
|
||||||
|
{
|
||||||
|
static thread_local void* head = nullptr;
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ChunkRegistry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
~ChunkRegistry()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||||
|
for (uint8_t* chunk : m_Chunks)
|
||||||
|
::operator delete(chunk);
|
||||||
|
m_Chunks.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void* refill()
|
||||||
|
{
|
||||||
|
const std::size_t chunkBytes = BlockSize * BlocksPerChunk;
|
||||||
|
uint8_t* chunk = static_cast<uint8_t*>(::operator new(chunkBytes));
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||||
|
m_Chunks.push_back(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* list = nullptr;
|
||||||
|
for (std::size_t i = 0; i < BlocksPerChunk; ++i)
|
||||||
|
{
|
||||||
|
uint8_t* block = chunk + i * BlockSize;
|
||||||
|
tagOf(block) = TagPool;
|
||||||
|
nextOf(block) = list;
|
||||||
|
list = block;
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex m_Mutex;
|
||||||
|
std::vector<uint8_t*> m_Chunks;
|
||||||
|
};
|
||||||
|
|
||||||
|
static ChunkRegistry& registry()
|
||||||
|
{
|
||||||
|
static ChunkRegistry instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif // HL_RTP_POOL
|
||||||
|
|
||||||
|
/// @class PoolAllocator
|
||||||
|
/// A stateless, std-conforming Allocator suitable for std::allocate_shared. When HL_RTP_POOL is
|
||||||
|
/// enabled it serves single-node allocations from FixedBlockPool; otherwise (and for any request
|
||||||
|
/// that does not fit a pooled block) it delegates to the global allocator, matching make_shared.
|
||||||
|
template <class T> struct PoolAllocator
|
||||||
|
{
|
||||||
|
using value_type = T;
|
||||||
|
|
||||||
|
PoolAllocator() noexcept = default;
|
||||||
|
template <class U> PoolAllocator(const PoolAllocator<U>&) noexcept {}
|
||||||
|
|
||||||
|
T* allocate(std::size_t n)
|
||||||
|
{
|
||||||
|
#if HL_RTP_POOL
|
||||||
|
return static_cast<T*>(FixedBlockPool::allocate(n * sizeof(T)));
|
||||||
|
#else
|
||||||
|
return static_cast<T*>(::operator new(n * sizeof(T)));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void deallocate(T* p, std::size_t /*n*/) noexcept
|
||||||
|
{
|
||||||
|
#if HL_RTP_POOL
|
||||||
|
FixedBlockPool::deallocate(p);
|
||||||
|
#else
|
||||||
|
::operator delete(static_cast<void*>(p));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class U> bool operator==(const PoolAllocator<U>&) const noexcept { return true; }
|
||||||
|
template <class U> bool operator!=(const PoolAllocator<U>&) const noexcept { return false; }
|
||||||
|
};
|
||||||
|
} // namespace hl
|
||||||
@@ -188,11 +188,11 @@ std::shared_ptr<jrtplib::RTPPacket> RtpDump::parseRtpData(const uint8_t* data, s
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Both are heap-allocated; RTPRawPacket takes ownership and deletes them
|
// Both are heap-allocated; RTPRawPacket takes ownership and deletes them
|
||||||
auto* addr = new jrtplib::RTPIPv4Address(uint32_t(0), uint16_t(0));
|
jrtplib::RTPIPAddress senderAddress = {jrtplib::RTPIPv4Address(uint32_t(0), uint16_t(0))};
|
||||||
uint8_t* dataCopy = new uint8_t[len];
|
uint8_t* dataCopy = new uint8_t[len];
|
||||||
std::memcpy(dataCopy, data, len);
|
std::memcpy(dataCopy, data, len);
|
||||||
|
|
||||||
jrtplib::RTPRawPacket raw(dataCopy, len, addr, jrtplib::RTPTime(0), true);
|
jrtplib::RTPRawPacket raw(dataCopy, len, senderAddress, jrtplib::RTPTime(0), true);
|
||||||
auto packet = std::make_shared<jrtplib::RTPPacket>(raw);
|
auto packet = std::make_shared<jrtplib::RTPPacket>(raw);
|
||||||
|
|
||||||
if (packet->GetCreationError() != 0)
|
if (packet->GetCreationError() != 0)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "MT_Dtmf.h"
|
#include "MT_Dtmf.h"
|
||||||
#include "../helper/HL_Log.h"
|
#include "../helper/HL_Log.h"
|
||||||
#include "../helper/HL_Time.h"
|
#include "../helper/HL_Time.h"
|
||||||
|
#include "../helper/HL_PoolAllocator.h"
|
||||||
#include "../audio/Audio_Interface.h"
|
#include "../audio/Audio_Interface.h"
|
||||||
#include "../audio/Audio_Resampler.h"
|
#include "../audio/Audio_Resampler.h"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
@@ -158,8 +159,9 @@ std::shared_ptr<RtpBuffer::Packet> RtpBuffer::add(const std::shared_ptr<jrtplib:
|
|||||||
|
|
||||||
if (newSeqno > minno || (available < mHigh))
|
if (newSeqno > minno || (available < mHigh))
|
||||||
{
|
{
|
||||||
// Insert into queue
|
// Insert into queue. Pool the per-packet Packet node (object + shared_ptr control block)
|
||||||
auto p = std::make_shared<Packet>(packet, timelength, rate);
|
// via allocate_shared; this churns at the RTP packet rate even on network-MOS-only streams.
|
||||||
|
auto p = std::allocate_shared<Packet>(hl::PoolAllocator<Packet>{}, packet, timelength, rate);
|
||||||
mPacketList.push_back(p);
|
mPacketList.push_back(p);
|
||||||
|
|
||||||
// Sort again
|
// Sort again
|
||||||
@@ -411,13 +413,38 @@ void AudioReceiver::setCodecSettings(const CodecList::Settings& codecSettings)
|
|||||||
if (mCodecSettings == codecSettings)
|
if (mCodecSettings == codecSettings)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Preserve the lazy-codec-map policy across SDP-driven updates: codecSettings comes from
|
||||||
|
// parseSdp()/per-call negotiation and defaults mLazyCodecMap to false, but the policy is
|
||||||
|
// set by the owner (vq-core) and must not be lost mid-stream.
|
||||||
|
const bool lazy = mCodecSettings.mLazyCodecMap;
|
||||||
mCodecSettings = codecSettings;
|
mCodecSettings = codecSettings;
|
||||||
|
mCodecSettings.mLazyCodecMap = lazy;
|
||||||
mCodecList.setSettings(mCodecSettings); // This builds factory list with proper payload types according to payload types in settings
|
mCodecList.setSettings(mCodecSettings); // This builds factory list with proper payload types according to payload types in settings
|
||||||
|
|
||||||
// Rebuild codec map from factory list
|
// Rebuild codec map from factory list
|
||||||
mCodecList.fillCodecMap(mCodecMap);
|
mCodecList.fillCodecMap(mCodecMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Codec* AudioReceiver::ensureCodec(int payloadType)
|
||||||
|
{
|
||||||
|
auto codecIter = mCodecMap.find(payloadType);
|
||||||
|
if (codecIter == mCodecMap.end())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// mLazyCodecMap leaves the value null until first use - create it now.
|
||||||
|
if (!codecIter->second)
|
||||||
|
codecIter->second = mCodecList.createCodecByPayloadType(payloadType);
|
||||||
|
|
||||||
|
return codecIter->second.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
CngDecoder& AudioReceiver::cng()
|
||||||
|
{
|
||||||
|
if (!mCngDecoder)
|
||||||
|
mCngDecoder = std::make_unique<CngDecoder>();
|
||||||
|
return *mCngDecoder;
|
||||||
|
}
|
||||||
|
|
||||||
CodecList::Settings& AudioReceiver::getCodecSettings()
|
CodecList::Settings& AudioReceiver::getCodecSettings()
|
||||||
{
|
{
|
||||||
return mCodecSettings;
|
return mCodecSettings;
|
||||||
@@ -548,7 +575,7 @@ void AudioReceiver::produceCNG(std::chrono::milliseconds length, Audio::DataWind
|
|||||||
if (options.mSkipDecode)
|
if (options.mSkipDecode)
|
||||||
mDecodedLength = 0;
|
mDecodedLength = 0;
|
||||||
else
|
else
|
||||||
mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), 100, mDecodedFrame.data(), false);
|
mDecodedLength = cng().produce(mCodec->samplerate(), 100, mDecodedFrame.data(), false);
|
||||||
|
|
||||||
if (mDecodedLength)
|
if (mDecodedLength)
|
||||||
processDecoded(output, options);
|
processDecoded(output, options);
|
||||||
@@ -561,7 +588,7 @@ void AudioReceiver::produceCNG(std::chrono::milliseconds length, Audio::DataWind
|
|||||||
if (options.mSkipDecode)
|
if (options.mSkipDecode)
|
||||||
mDecodedLength = 0;
|
mDecodedLength = 0;
|
||||||
else
|
else
|
||||||
mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), tail, reinterpret_cast<short*>(mDecodedFrame.data()), false);
|
mDecodedLength = cng().produce(mCodec->samplerate(), tail, reinterpret_cast<short*>(mDecodedFrame.data()), false);
|
||||||
|
|
||||||
if (mDecodedLength)
|
if (mDecodedLength)
|
||||||
processDecoded(output, options);
|
processDecoded(output, options);
|
||||||
@@ -579,7 +606,7 @@ AudioReceiver::DecodeResult AudioReceiver::decodeGapTo(Audio::DataWindow& output
|
|||||||
{
|
{
|
||||||
// Synthesize comfort noise. It will be done on AUDIO_SAMPLERATE rate directly to mResampledFrame buffer.
|
// Synthesize comfort noise. It will be done on AUDIO_SAMPLERATE rate directly to mResampledFrame buffer.
|
||||||
// Do not forget to send this noise to analysis
|
// Do not forget to send this noise to analysis
|
||||||
mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), mLastPacketTimeLength, reinterpret_cast<short*>(mDecodedFrame.data()), false);
|
mDecodedLength = cng().produce(mCodec->samplerate(), mLastPacketTimeLength, reinterpret_cast<short*>(mDecodedFrame.data()), false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
decodePacketTo(output, options, mCngPacket);
|
decodePacketTo(output, options, mCngPacket);
|
||||||
@@ -668,10 +695,10 @@ AudioReceiver::DecodeResult AudioReceiver::decodePacketTo(Audio::DataWindow& out
|
|||||||
{
|
{
|
||||||
ICELogDebug(<< "Decoding CNG");
|
ICELogDebug(<< "Decoding CNG");
|
||||||
mCngPacket = packet;
|
mCngPacket = packet;
|
||||||
mCngDecoder.decode3389(rtp.GetPayloadData(), rtp.GetPayloadLength());
|
cng().decode3389(rtp.GetPayloadData(), rtp.GetPayloadLength());
|
||||||
|
|
||||||
// Emit CNG mLastPacketLength milliseconds
|
// Emit CNG mLastPacketLength milliseconds
|
||||||
mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), mLastPacketTimeLength, (short*)mDecodedFrame.data(), true);
|
mDecodedLength = cng().produce(mCodec->samplerate(), mLastPacketTimeLength, (short*)mDecodedFrame.data(), true);
|
||||||
if (mDecodedLength)
|
if (mDecodedLength)
|
||||||
processDecoded(output, options);
|
processDecoded(output, options);
|
||||||
}
|
}
|
||||||
@@ -758,7 +785,7 @@ AudioReceiver::DecodeResult AudioReceiver::decodeEmptyTo(Audio::DataWindow& outp
|
|||||||
int ms = bytesPerMs ? (int)std::min<int64_t>(options.mElapsed.count(), (int64_t)(room / bytesPerMs)) : 0;
|
int ms = bytesPerMs ? (int)std::min<int64_t>(options.mElapsed.count(), (int64_t)(room / bytesPerMs)) : 0;
|
||||||
if (ms <= 0)
|
if (ms <= 0)
|
||||||
return {.mStatus = DecodeResult::Status::Skip};
|
return {.mStatus = DecodeResult::Status::Skip};
|
||||||
auto produced = mCngDecoder.produce(fmt.rate(), ms, (short*)(output.mutableData() + output.filled()), false);
|
auto produced = cng().produce(fmt.rate(), ms, (short*)(output.mutableData() + output.filled()), false);
|
||||||
output.setFilled(output.filled() + produced);
|
output.setFilled(output.filled() + produced);
|
||||||
return {.mStatus = DecodeResult::Status::Ok, .mSamplerate = fmt.rate(), .mChannels = fmt.channels()};
|
return {.mStatus = DecodeResult::Status::Ok, .mSamplerate = fmt.rate(), .mChannels = fmt.channels()};
|
||||||
}
|
}
|
||||||
@@ -946,11 +973,7 @@ void AudioReceiver::makeMonoAndResample(int rate, int channels)
|
|||||||
|
|
||||||
Codec* AudioReceiver::findCodec(int payloadType)
|
Codec* AudioReceiver::findCodec(int payloadType)
|
||||||
{
|
{
|
||||||
MT::CodecMap::const_iterator codecIter = mCodecMap.find(payloadType);
|
return ensureCodec(payloadType);
|
||||||
if (codecIter == mCodecMap.end())
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
return codecIter->second.get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -987,10 +1010,7 @@ int AudioReceiver::getSize() const
|
|||||||
|
|
||||||
AudioReceiver::MediaInfo AudioReceiver::infoFor(jrtplib::RTPPacket& p)
|
AudioReceiver::MediaInfo AudioReceiver::infoFor(jrtplib::RTPPacket& p)
|
||||||
{
|
{
|
||||||
CodecMap::iterator codecIter = mCodecMap.find(p.GetPayloadType());
|
Codec* codec = ensureCodec(p.GetPayloadType());
|
||||||
if (codecIter == mCodecMap.end())
|
|
||||||
return {};
|
|
||||||
PCodec codec = codecIter->second;
|
|
||||||
if (!codec)
|
if (!codec)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
@@ -1007,7 +1027,7 @@ AudioReceiver::MediaInfo AudioReceiver::infoFor(jrtplib::RTPPacket& p)
|
|||||||
else
|
else
|
||||||
if (typeid(*codec) == typeid(OpusCodec))
|
if (typeid(*codec) == typeid(OpusCodec))
|
||||||
{
|
{
|
||||||
OpusCodec* oc = dynamic_cast<OpusCodec*>(codec.get());
|
OpusCodec* oc = dynamic_cast<OpusCodec*>(codec);
|
||||||
assert(oc);
|
assert(oc);
|
||||||
size_t samplesCount = oc->getNumberOfSamples({p.GetPayloadData(), p.GetPayloadLength()});
|
size_t samplesCount = oc->getNumberOfSamples({p.GetPayloadData(), p.GetPayloadLength()});
|
||||||
int sampleratePerMs = codec->samplerate() / 1000;
|
int sampleratePerMs = codec->samplerate() / 1000;
|
||||||
|
|||||||
@@ -237,6 +237,13 @@ public:
|
|||||||
void updateDecodingTimeStatistics();
|
void updateDecodingTimeStatistics();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
// Resolve (and lazily create) the codec for a payload type. Returns null when no
|
||||||
|
// factory handles it. Used by add()/findCodec()/infoFor() so mLazyCodecMap works.
|
||||||
|
Codec* ensureCodec(int payloadType);
|
||||||
|
|
||||||
|
// Lazily create the comfort-noise decoder on first use.
|
||||||
|
CngDecoder& cng();
|
||||||
|
|
||||||
RtpBuffer mRtpBuffer; // RTP jitter buffer itself; here are audio packets
|
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;
|
||||||
@@ -248,7 +255,9 @@ protected:
|
|||||||
CodecList mCodecList;
|
CodecList mCodecList;
|
||||||
JitterStatistics mJitterStats;
|
JitterStatistics mJitterStats;
|
||||||
std::shared_ptr<RtpBuffer::Packet> mCngPacket;
|
std::shared_ptr<RtpBuffer::Packet> mCngPacket;
|
||||||
CngDecoder mCngDecoder;
|
// Lazily created on first CNG use (getAudioTo); its ctor calls WebRtcCng_CreateDec,
|
||||||
|
// which is wasted for the many streams that never decode comfort noise. Access via cng().
|
||||||
|
std::unique_ptr<CngDecoder> mCngDecoder;
|
||||||
size_t mDTXSamplesToEmit = 0; // How much silence (or CNG) should be emited before next RTP packet gets into the action
|
size_t mDTXSamplesToEmit = 0; // How much silence (or CNG) should be emited before next RTP packet gets into the action
|
||||||
|
|
||||||
// Already decoded data that can be retrieved without actual decoding - it may happen because of getAudioTo() may be limited by time interval
|
// Already decoded data that can be retrieved without actual decoding - it may happen because of getAudioTo() may be limited by time interval
|
||||||
|
|||||||
@@ -410,8 +410,10 @@ void CodecList::fillCodecMap(CodecMap& cm)
|
|||||||
cm.clear();
|
cm.clear();
|
||||||
for (auto& factory: mFactoryList)
|
for (auto& factory: mFactoryList)
|
||||||
{
|
{
|
||||||
// Create codec here. Although they are not needed right now - they can be needed to find codec's info.
|
// Register the payload-type key. With mLazyCodecMap the codec object is left null
|
||||||
PCodec c = factory->create();
|
// and created on first use (see AudioReceiver::add/findCodec/infoFor); otherwise it
|
||||||
|
// is created eagerly so codec info is available without a packet.
|
||||||
|
PCodec c = mSettings.mLazyCodecMap ? PCodec{} : factory->create();
|
||||||
cm.insert({factory->payloadType(), c});
|
cm.insert({factory->payloadType(), c});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,14 @@ public:
|
|||||||
bool mWrapIuUP = false;
|
bool mWrapIuUP = false;
|
||||||
bool mSkipDecode = false;
|
bool mSkipDecode = false;
|
||||||
|
|
||||||
|
// When set, fillCodecMap() registers the payload-type keys but leaves the codec
|
||||||
|
// objects null; each codec is created on first use (add()/findCodec()/infoFor()).
|
||||||
|
// A passive monitor (vq-core) creates one AudioReceiver per media stream and most
|
||||||
|
// streams use only 1-2 payload types, so eagerly creating every registered codec
|
||||||
|
// (G722 enc+dec, G729, Opus, AMR, EVS, ...) per stream is pure waste. Default false
|
||||||
|
// keeps the eager behaviour for all other consumers (softphone, etc.).
|
||||||
|
bool mLazyCodecMap = false;
|
||||||
|
|
||||||
// RFC2833 DTMF
|
// RFC2833 DTMF
|
||||||
int mTelephoneEvent = -1;
|
int mTelephoneEvent = -1;
|
||||||
|
|
||||||
|
|||||||
@@ -86,9 +86,9 @@ public:
|
|||||||
#endif // RTPDEBUG
|
#endif // RTPDEBUG
|
||||||
|
|
||||||
virtual ~RTPAddress() { }
|
virtual ~RTPAddress() { }
|
||||||
protected:
|
|
||||||
// only allow subclasses to be created
|
// only allow subclasses to be created
|
||||||
RTPAddress(const AddressType t) : addresstype(t) { }
|
RTPAddress(const AddressType t) : addresstype(t) { }
|
||||||
|
protected:
|
||||||
private:
|
private:
|
||||||
const AddressType addresstype;
|
const AddressType addresstype;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -772,6 +772,16 @@ void RTPExternalTransmitter::AbortWaitInternal()
|
|||||||
#endif // WIN32
|
#endif // WIN32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert a polymorphic RTPAddress into the RTPIPAddress variant that RTPRawPacket
|
||||||
|
// now stores by value. Only IPv4 / IPv6 are representable; the external transmitter
|
||||||
|
// is only ever fed IP addresses in this codebase.
|
||||||
|
static RTPIPAddress ToRTPIPAddress(const RTPAddress *addr)
|
||||||
|
{
|
||||||
|
if (addr->GetAddressType() == RTPAddress::IPv6Address)
|
||||||
|
return RTPIPAddress{ *static_cast<const RTPIPv6Address *>(addr) };
|
||||||
|
return RTPIPAddress{ *static_cast<const RTPIPv4Address *>(addr) };
|
||||||
|
}
|
||||||
|
|
||||||
void RTPExternalTransmitter::InjectRTP(const void *data, size_t len, const RTPAddress &a)
|
void RTPExternalTransmitter::InjectRTP(const void *data, size_t len, const RTPAddress &a)
|
||||||
{
|
{
|
||||||
if (!init)
|
if (!init)
|
||||||
@@ -801,13 +811,16 @@ void RTPExternalTransmitter::InjectRTP(const void *data, size_t len, const RTPAd
|
|||||||
RTPTime curtime = RTPTime::CurrentTime();
|
RTPTime curtime = RTPTime::CurrentTime();
|
||||||
RTPRawPacket *pack;
|
RTPRawPacket *pack;
|
||||||
|
|
||||||
pack = RTPNew(GetMemoryManager(),RTPMEM_TYPE_CLASS_RTPRAWPACKET) RTPRawPacket(datacopy,len,addr,curtime,true,GetMemoryManager());
|
// RTPRawPacket now stores the address by value (RTPIPAddress variant), so it no
|
||||||
|
// longer takes ownership of addr; release it on every path.
|
||||||
|
pack = RTPNew(GetMemoryManager(),RTPMEM_TYPE_CLASS_RTPRAWPACKET) RTPRawPacket(datacopy,len,ToRTPIPAddress(addr),curtime,true,GetMemoryManager());
|
||||||
if (pack == 0)
|
if (pack == 0)
|
||||||
{
|
{
|
||||||
RTPDelete(addr,GetMemoryManager());
|
RTPDelete(addr,GetMemoryManager());
|
||||||
RTPDeleteByteArray(localhostname,GetMemoryManager());
|
RTPDeleteByteArray(localhostname,GetMemoryManager());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
RTPDelete(addr,GetMemoryManager());
|
||||||
rawpacketlist.push_back(pack);
|
rawpacketlist.push_back(pack);
|
||||||
AbortWaitInternal();
|
AbortWaitInternal();
|
||||||
|
|
||||||
@@ -843,13 +856,16 @@ void RTPExternalTransmitter::InjectRTCP(const void *data, size_t len, const RTPA
|
|||||||
RTPTime curtime = RTPTime::CurrentTime();
|
RTPTime curtime = RTPTime::CurrentTime();
|
||||||
RTPRawPacket *pack;
|
RTPRawPacket *pack;
|
||||||
|
|
||||||
pack = RTPNew(GetMemoryManager(),RTPMEM_TYPE_CLASS_RTPRAWPACKET) RTPRawPacket(datacopy,len,addr,curtime,false,GetMemoryManager());
|
// RTPRawPacket now stores the address by value (RTPIPAddress variant), so it no
|
||||||
|
// longer takes ownership of addr; release it on every path.
|
||||||
|
pack = RTPNew(GetMemoryManager(),RTPMEM_TYPE_CLASS_RTPRAWPACKET) RTPRawPacket(datacopy,len,ToRTPIPAddress(addr),curtime,false,GetMemoryManager());
|
||||||
if (pack == 0)
|
if (pack == 0)
|
||||||
{
|
{
|
||||||
RTPDelete(addr,GetMemoryManager());
|
RTPDelete(addr,GetMemoryManager());
|
||||||
RTPDeleteByteArray(localhostname,GetMemoryManager());
|
RTPDeleteByteArray(localhostname,GetMemoryManager());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
RTPDelete(addr,GetMemoryManager());
|
||||||
rawpacketlist.push_back(pack);
|
rawpacketlist.push_back(pack);
|
||||||
AbortWaitInternal();
|
AbortWaitInternal();
|
||||||
|
|
||||||
@@ -893,13 +909,16 @@ void RTPExternalTransmitter::InjectRTPorRTCP(const void *data, size_t len, const
|
|||||||
RTPTime curtime = RTPTime::CurrentTime();
|
RTPTime curtime = RTPTime::CurrentTime();
|
||||||
RTPRawPacket *pack;
|
RTPRawPacket *pack;
|
||||||
|
|
||||||
pack = RTPNew(GetMemoryManager(),RTPMEM_TYPE_CLASS_RTPRAWPACKET) RTPRawPacket(datacopy,len,addr,curtime,rtp,GetMemoryManager());
|
// RTPRawPacket now stores the address by value (RTPIPAddress variant), so it no
|
||||||
|
// longer takes ownership of addr; release it on every path.
|
||||||
|
pack = RTPNew(GetMemoryManager(),RTPMEM_TYPE_CLASS_RTPRAWPACKET) RTPRawPacket(datacopy,len,ToRTPIPAddress(addr),curtime,rtp,GetMemoryManager());
|
||||||
if (pack == 0)
|
if (pack == 0)
|
||||||
{
|
{
|
||||||
RTPDelete(addr,GetMemoryManager());
|
RTPDelete(addr,GetMemoryManager());
|
||||||
RTPDeleteByteArray(localhostname,GetMemoryManager());
|
RTPDeleteByteArray(localhostname,GetMemoryManager());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
RTPDelete(addr,GetMemoryManager());
|
||||||
rawpacketlist.push_back(pack);
|
rawpacketlist.push_back(pack);
|
||||||
AbortWaitInternal();
|
AbortWaitInternal();
|
||||||
|
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ public:
|
|||||||
#ifdef RTPDEBUG
|
#ifdef RTPDEBUG
|
||||||
std::string GetAddressString() const;
|
std::string GetAddressString() const;
|
||||||
#endif // RTPDEBUG
|
#endif // RTPDEBUG
|
||||||
|
|
||||||
private:
|
private:
|
||||||
in6_addr ip;
|
in6_addr ip;
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
|
|||||||
@@ -41,11 +41,16 @@
|
|||||||
#include "rtpconfig.h"
|
#include "rtpconfig.h"
|
||||||
#include "rtptimeutilities.h"
|
#include "rtptimeutilities.h"
|
||||||
#include "rtpaddress.h"
|
#include "rtpaddress.h"
|
||||||
|
#include "rtpipv4address.h"
|
||||||
|
#include "rtpipv6address.h"
|
||||||
#include "rtptypes.h"
|
#include "rtptypes.h"
|
||||||
#include "rtpmemoryobject.h"
|
#include "rtpmemoryobject.h"
|
||||||
|
#include <variant>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
namespace jrtplib
|
namespace jrtplib
|
||||||
{
|
{
|
||||||
|
typedef std::variant<RTPIPv4Address, RTPIPv6Address> RTPIPAddress;
|
||||||
|
|
||||||
/** This class is used by the transmission component to store the incoming RTP and RTCP data in. */
|
/** This class is used by the transmission component to store the incoming RTP and RTCP data in. */
|
||||||
class JRTPLIB_IMPORTEXPORT RTPRawPacket : public RTPMemoryObject
|
class JRTPLIB_IMPORTEXPORT RTPRawPacket : public RTPMemoryObject
|
||||||
@@ -58,7 +63,7 @@ public:
|
|||||||
* The flag which indicates whether this data is RTP or RTCP data is set to \c rtp. A memory
|
* The flag which indicates whether this data is RTP or RTCP data is set to \c rtp. A memory
|
||||||
* manager can be installed as well.
|
* manager can be installed as well.
|
||||||
*/
|
*/
|
||||||
RTPRawPacket(uint8_t *data, size_t datalen, RTPAddress *address, const RTPTime &recvtime, bool rtp, RTPMemoryManager *mgr = 0);
|
RTPRawPacket(uint8_t *data, size_t datalen, RTPIPAddress address, const RTPTime &recvtime, bool rtp, RTPMemoryManager *mgr = 0);
|
||||||
~RTPRawPacket();
|
~RTPRawPacket();
|
||||||
|
|
||||||
/** Returns the pointer to the data which is contained in this packet. */
|
/** Returns the pointer to the data which is contained in this packet. */
|
||||||
@@ -71,7 +76,14 @@ public:
|
|||||||
RTPTime GetReceiveTime() const { return receivetime; }
|
RTPTime GetReceiveTime() const { return receivetime; }
|
||||||
|
|
||||||
/** Returns the address stored in this packet. */
|
/** Returns the address stored in this packet. */
|
||||||
const RTPAddress *GetSenderAddress() const { return senderaddress; }
|
const RTPAddress* GetSenderAddress() const {
|
||||||
|
switch (senderaddress.index()) {
|
||||||
|
case 0: return std::get_if<RTPIPv4Address>(&senderaddress);
|
||||||
|
case 1: return std::get_if<RTPIPv6Address>(&senderaddress);
|
||||||
|
default:
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns \c true if this data is RTP data, \c false if it is RTCP data. */
|
/** Returns \c true if this data is RTP data, \c false if it is RTCP data. */
|
||||||
bool IsRTP() const { return isrtp; }
|
bool IsRTP() const { return isrtp; }
|
||||||
@@ -88,25 +100,21 @@ private:
|
|||||||
uint8_t *packetdata;
|
uint8_t *packetdata;
|
||||||
size_t packetdatalength;
|
size_t packetdatalength;
|
||||||
RTPTime receivetime;
|
RTPTime receivetime;
|
||||||
RTPAddress *senderaddress;
|
RTPIPAddress senderaddress;
|
||||||
bool isrtp;
|
bool isrtp;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline RTPRawPacket::RTPRawPacket(uint8_t *data, size_t datalen, RTPAddress *address, const RTPTime &recvtime, bool rtp, RTPMemoryManager *mgr):RTPMemoryObject(mgr),receivetime(recvtime)
|
inline RTPRawPacket::RTPRawPacket(uint8_t *data, size_t datalen, RTPIPAddress address, const RTPTime &recvtime, bool rtp, RTPMemoryManager *mgr):RTPMemoryObject(mgr),receivetime(recvtime),senderaddress(std::move(address))
|
||||||
{
|
{
|
||||||
packetdata = data;
|
packetdata = data;
|
||||||
packetdatalength = datalen;
|
packetdatalength = datalen;
|
||||||
senderaddress = address;
|
|
||||||
isrtp = rtp;
|
isrtp = rtp;
|
||||||
receivetime = recvtime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline RTPRawPacket::~RTPRawPacket()
|
inline RTPRawPacket::~RTPRawPacket()
|
||||||
{
|
{
|
||||||
if (packetdata)
|
if (packetdata)
|
||||||
RTPDeleteByteArray(packetdata,GetMemoryManager());
|
RTPDeleteByteArray(packetdata,GetMemoryManager());
|
||||||
if (senderaddress)
|
|
||||||
RTPDelete(senderaddress,GetMemoryManager());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end namespace
|
} // end namespace
|
||||||
|
|||||||
@@ -1316,13 +1316,16 @@ int RTPUDPv4Transmitter::PollSocket(bool rtp)
|
|||||||
}
|
}
|
||||||
memcpy(datacopy,packetbuffer,recvlen);
|
memcpy(datacopy,packetbuffer,recvlen);
|
||||||
|
|
||||||
pack = RTPNew(GetMemoryManager(),RTPMEM_TYPE_CLASS_RTPRAWPACKET) RTPRawPacket(datacopy,recvlen,addr,curtime,rtp,GetMemoryManager());
|
// RTPRawPacket now stores the address by value (RTPIPAddress variant),
|
||||||
|
// so it no longer takes ownership of addr; release it on every path.
|
||||||
|
pack = RTPNew(GetMemoryManager(),RTPMEM_TYPE_CLASS_RTPRAWPACKET) RTPRawPacket(datacopy,recvlen,*addr,curtime,rtp,GetMemoryManager());
|
||||||
if (pack == 0)
|
if (pack == 0)
|
||||||
{
|
{
|
||||||
RTPDelete(addr,GetMemoryManager());
|
RTPDelete(addr,GetMemoryManager());
|
||||||
RTPDeleteByteArray(datacopy,GetMemoryManager());
|
RTPDeleteByteArray(datacopy,GetMemoryManager());
|
||||||
return ERR_RTP_OUTOFMEM;
|
return ERR_RTP_OUTOFMEM;
|
||||||
}
|
}
|
||||||
|
RTPDelete(addr,GetMemoryManager());
|
||||||
rawpacketlist.push_back(pack);
|
rawpacketlist.push_back(pack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1325,13 +1325,16 @@ int RTPUDPv6Transmitter::PollSocket(bool rtp)
|
|||||||
}
|
}
|
||||||
memcpy(datacopy,packetbuffer,recvlen);
|
memcpy(datacopy,packetbuffer,recvlen);
|
||||||
|
|
||||||
pack = RTPNew(GetMemoryManager(),RTPMEM_TYPE_CLASS_RTPRAWPACKET) RTPRawPacket(datacopy,recvlen,addr,curtime,rtp,GetMemoryManager());
|
// RTPRawPacket now stores the address by value (RTPIPAddress variant),
|
||||||
|
// so it no longer takes ownership of addr; release it on every path.
|
||||||
|
pack = RTPNew(GetMemoryManager(),RTPMEM_TYPE_CLASS_RTPRAWPACKET) RTPRawPacket(datacopy,recvlen,*addr,curtime,rtp,GetMemoryManager());
|
||||||
if (pack == 0)
|
if (pack == 0)
|
||||||
{
|
{
|
||||||
RTPDelete(addr,GetMemoryManager());
|
RTPDelete(addr,GetMemoryManager());
|
||||||
RTPDeleteByteArray(datacopy,GetMemoryManager());
|
RTPDeleteByteArray(datacopy,GetMemoryManager());
|
||||||
return ERR_RTP_OUTOFMEM;
|
return ERR_RTP_OUTOFMEM;
|
||||||
}
|
}
|
||||||
|
RTPDelete(addr,GetMemoryManager());
|
||||||
rawpacketlist.push_back(pack);
|
rawpacketlist.push_back(pack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user