- optimize memory usage

This commit is contained in:
2026-06-21 14:02:40 +03:00
parent 1e020a7b5f
commit e8a71d5b03
14 changed files with 296 additions and 44 deletions
+179
View File
@@ -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
+2 -2
View File
@@ -188,11 +188,11 @@ std::shared_ptr<jrtplib::RTPPacket> RtpDump::parseRtpData(const uint8_t* data, s
try {
// 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];
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);
if (packet->GetCreationError() != 0)
+38 -18
View File
@@ -10,6 +10,7 @@
#include "MT_Dtmf.h"
#include "../helper/HL_Log.h"
#include "../helper/HL_Time.h"
#include "../helper/HL_PoolAllocator.h"
#include "../audio/Audio_Interface.h"
#include "../audio/Audio_Resampler.h"
#include <cmath>
@@ -158,8 +159,9 @@ std::shared_ptr<RtpBuffer::Packet> RtpBuffer::add(const std::shared_ptr<jrtplib:
if (newSeqno > minno || (available < mHigh))
{
// Insert into queue
auto p = std::make_shared<Packet>(packet, timelength, rate);
// Insert into queue. Pool the per-packet Packet node (object + shared_ptr control block)
// 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);
// Sort again
@@ -411,13 +413,38 @@ void AudioReceiver::setCodecSettings(const CodecList::Settings& codecSettings)
if (mCodecSettings == codecSettings)
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.mLazyCodecMap = lazy;
mCodecList.setSettings(mCodecSettings); // This builds factory list with proper payload types according to payload types in settings
// Rebuild codec map from factory list
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()
{
return mCodecSettings;
@@ -548,7 +575,7 @@ void AudioReceiver::produceCNG(std::chrono::milliseconds length, Audio::DataWind
if (options.mSkipDecode)
mDecodedLength = 0;
else
mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), 100, mDecodedFrame.data(), false);
mDecodedLength = cng().produce(mCodec->samplerate(), 100, mDecodedFrame.data(), false);
if (mDecodedLength)
processDecoded(output, options);
@@ -561,7 +588,7 @@ void AudioReceiver::produceCNG(std::chrono::milliseconds length, Audio::DataWind
if (options.mSkipDecode)
mDecodedLength = 0;
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)
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.
// 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
decodePacketTo(output, options, mCngPacket);
@@ -668,10 +695,10 @@ AudioReceiver::DecodeResult AudioReceiver::decodePacketTo(Audio::DataWindow& out
{
ICELogDebug(<< "Decoding CNG");
mCngPacket = packet;
mCngDecoder.decode3389(rtp.GetPayloadData(), rtp.GetPayloadLength());
cng().decode3389(rtp.GetPayloadData(), rtp.GetPayloadLength());
// 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)
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;
if (ms <= 0)
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);
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)
{
MT::CodecMap::const_iterator codecIter = mCodecMap.find(payloadType);
if (codecIter == mCodecMap.end())
return nullptr;
return codecIter->second.get();
return ensureCodec(payloadType);
}
@@ -987,10 +1010,7 @@ int AudioReceiver::getSize() const
AudioReceiver::MediaInfo AudioReceiver::infoFor(jrtplib::RTPPacket& p)
{
CodecMap::iterator codecIter = mCodecMap.find(p.GetPayloadType());
if (codecIter == mCodecMap.end())
return {};
PCodec codec = codecIter->second;
Codec* codec = ensureCodec(p.GetPayloadType());
if (!codec)
return {};
@@ -1007,7 +1027,7 @@ AudioReceiver::MediaInfo AudioReceiver::infoFor(jrtplib::RTPPacket& p)
else
if (typeid(*codec) == typeid(OpusCodec))
{
OpusCodec* oc = dynamic_cast<OpusCodec*>(codec.get());
OpusCodec* oc = dynamic_cast<OpusCodec*>(codec);
assert(oc);
size_t samplesCount = oc->getNumberOfSamples({p.GetPayloadData(), p.GetPayloadLength()});
int sampleratePerMs = codec->samplerate() / 1000;
+10 -1
View File
@@ -237,6 +237,13 @@ public:
void updateDecodingTimeStatistics();
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 mDtmfBuffer; // These two (mDtmfBuffer / mDtmfReceiver) are for our analyzer stack only; in normal softphone logic DTMF packets goes via SingleAudioStream::mDtmfReceiver
DtmfReceiver mDtmfReceiver;
@@ -248,7 +255,9 @@ protected:
CodecList mCodecList;
JitterStatistics mJitterStats;
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
// Already decoded data that can be retrieved without actual decoding - it may happen because of getAudioTo() may be limited by time interval
+4 -2
View File
@@ -410,8 +410,10 @@ void CodecList::fillCodecMap(CodecMap& cm)
cm.clear();
for (auto& factory: mFactoryList)
{
// Create codec here. Although they are not needed right now - they can be needed to find codec's info.
PCodec c = factory->create();
// Register the payload-type key. With mLazyCodecMap the codec object is left null
// 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});
}
}
+8
View File
@@ -28,6 +28,14 @@ public:
bool mWrapIuUP = 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
int mTelephoneEvent = -1;
+1 -1
View File
@@ -86,9 +86,9 @@ public:
#endif // RTPDEBUG
virtual ~RTPAddress() { }
protected:
// only allow subclasses to be created
RTPAddress(const AddressType t) : addresstype(t) { }
protected:
private:
const AddressType addresstype;
};
@@ -772,6 +772,16 @@ void RTPExternalTransmitter::AbortWaitInternal()
#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)
{
if (!init)
@@ -801,13 +811,16 @@ void RTPExternalTransmitter::InjectRTP(const void *data, size_t len, const RTPAd
RTPTime curtime = RTPTime::CurrentTime();
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)
{
RTPDelete(addr,GetMemoryManager());
RTPDeleteByteArray(localhostname,GetMemoryManager());
return;
}
RTPDelete(addr,GetMemoryManager());
rawpacketlist.push_back(pack);
AbortWaitInternal();
@@ -843,13 +856,16 @@ void RTPExternalTransmitter::InjectRTCP(const void *data, size_t len, const RTPA
RTPTime curtime = RTPTime::CurrentTime();
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)
{
RTPDelete(addr,GetMemoryManager());
RTPDeleteByteArray(localhostname,GetMemoryManager());
return;
}
RTPDelete(addr,GetMemoryManager());
rawpacketlist.push_back(pack);
AbortWaitInternal();
@@ -893,13 +909,16 @@ void RTPExternalTransmitter::InjectRTPorRTCP(const void *data, size_t len, const
RTPTime curtime = RTPTime::CurrentTime();
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)
{
RTPDelete(addr,GetMemoryManager());
RTPDeleteByteArray(localhostname,GetMemoryManager());
return;
}
RTPDelete(addr,GetMemoryManager());
rawpacketlist.push_back(pack);
AbortWaitInternal();
+1
View File
@@ -97,6 +97,7 @@ public:
#ifdef RTPDEBUG
std::string GetAddressString() const;
#endif // RTPDEBUG
private:
in6_addr ip;
uint16_t port;
+16 -8
View File
@@ -41,11 +41,16 @@
#include "rtpconfig.h"
#include "rtptimeutilities.h"
#include "rtpaddress.h"
#include "rtpipv4address.h"
#include "rtpipv6address.h"
#include "rtptypes.h"
#include "rtpmemoryobject.h"
#include <variant>
#include <utility>
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. */
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
* 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();
/** Returns the pointer to the data which is contained in this packet. */
@@ -71,7 +76,14 @@ public:
RTPTime GetReceiveTime() const { return receivetime; }
/** 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. */
bool IsRTP() const { return isrtp; }
@@ -88,25 +100,21 @@ private:
uint8_t *packetdata;
size_t packetdatalength;
RTPTime receivetime;
RTPAddress *senderaddress;
RTPIPAddress senderaddress;
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;
packetdatalength = datalen;
senderaddress = address;
isrtp = rtp;
receivetime = recvtime;
}
inline RTPRawPacket::~RTPRawPacket()
{
if (packetdata)
RTPDeleteByteArray(packetdata,GetMemoryManager());
if (senderaddress)
RTPDelete(senderaddress,GetMemoryManager());
}
} // end namespace
+1 -1
View File
@@ -278,7 +278,7 @@ int RTPSources::ProcessRawPacket(RTPRawPacket *rawpack,RTPTransmitter *rtptrans[
}
else // not our own packet
{
status = ProcessRTCPCompoundPacket(&rtcpcomppack,rawpack->GetReceiveTime(),rawpack->GetSenderAddress());
status = ProcessRTCPCompoundPacket(&rtcpcomppack, rawpack->GetReceiveTime(), rawpack->GetSenderAddress());
if (status < 0)
return status;
}
+4 -1
View File
@@ -1316,13 +1316,16 @@ int RTPUDPv4Transmitter::PollSocket(bool rtp)
}
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)
{
RTPDelete(addr,GetMemoryManager());
RTPDeleteByteArray(datacopy,GetMemoryManager());
return ERR_RTP_OUTOFMEM;
}
RTPDelete(addr,GetMemoryManager());
rawpacketlist.push_back(pack);
}
}
+4 -1
View File
@@ -1325,13 +1325,16 @@ int RTPUDPv6Transmitter::PollSocket(bool rtp)
}
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)
{
RTPDelete(addr,GetMemoryManager());
RTPDeleteByteArray(datacopy,GetMemoryManager());
return ERR_RTP_OUTOFMEM;
}
RTPDelete(addr,GetMemoryManager());
rawpacketlist.push_back(pack);
}
}