Compare commits

..

4 Commits

8 changed files with 72 additions and 14 deletions
+1
View File
@@ -189,6 +189,7 @@ void AgentImpl::processConfig(JsonCpp::Value &d, JsonCpp::Value &answer)
config()[CONFIG_MULTIPLEXING] = true; config()[CONFIG_MULTIPLEXING] = true;
config()[CONFIG_DISPLAYNAME] = "Voip quality tester"; config()[CONFIG_DISPLAYNAME] = "Voip quality tester";
config()[CONFIG_USERAGENT] = d["useragent"].asString();
mUseNativeAudio = d["nativeaudio"].asBool(); mUseNativeAudio = d["nativeaudio"].asBool();
config()[CONFIG_OWN_DNS] = d["dns_servers"].asString(); config()[CONFIG_OWN_DNS] = d["dns_servers"].asString();
+6 -3
View File
@@ -66,10 +66,13 @@ size_t SpeexResampler::processBuffer(const void* src, size_t sourceLength, size_
if (mDestRate == mSourceRate) if (mDestRate == mSourceRate)
{ {
// Pass-through, but never write past the caller's buffer: clamp to its
// capacity instead of trusting sourceLength (which is caller/file driven).
assert(destCapacity >= sourceLength); assert(destCapacity >= sourceLength);
memcpy(dest, src, sourceLength); size_t copied = std::min(sourceLength, destCapacity);
sourceProcessed = sourceLength; memcpy(dest, src, copied);
return sourceLength; sourceProcessed = copied;
return copied;
} }
if (!mContext) if (!mContext)
+8
View File
@@ -150,6 +150,14 @@ bool WavFileReader::open(const std::filesystem::path& p)
if (mBits != 16) if (mBits != 16)
THROW_READERROR; THROW_READERROR;
// The read path is mono-only: it divides byte counts by AUDIO_CHANNELS
// and starts the resampler with AUDIO_CHANNELS. Reject anything else -
// a multi-channel file would make read() copy more source bytes than the
// caller's (mono-sized) output buffer can hold. Also reject a zero rate,
// which would divide by zero in readRaw() / poison the resampler ratio.
if (mChannels != AUDIO_CHANNELS || mSamplerate == 0)
THROW_READERROR;
// Look for the chunk 'data' // Look for the chunk 'data'
mInput->seekg(fmtStart + std::streampos(fmtSize)); mInput->seekg(fmtStart + std::streampos(fmtSize));
+12
View File
@@ -94,8 +94,20 @@ static AmrPayload parseAmrPayload(AmrPayloadInfo& input, size_t& cngCounter)
// Table of contents // Table of contents
uint8_t F, FT, Q; uint8_t F, FT, Q;
// Bits consumed by a single ToC entry: F(1) + FT(4) + Q(1), plus 2 padding
// bits in octet-aligned mode.
const size_t tocEntryBits = input.mOctetAligned ? 8 : 6;
do do
{ {
// Stop when the payload no longer holds a full ToC entry. BitReader
// saturates at end-of-stream (re-reads the last bit) instead of
// signalling EOF, so trusting the F continuation bit alone would loop
// forever - and append AmrFrames unbounded - on a truncated or crafted
// payload whose final bit is 1.
if (bit_reader.position() + tocEntryBits > bit_reader.count())
break;
// Read TOC. It is still relates to RTP part of AMR frames packing; not the AMR frame itself. // Read TOC. It is still relates to RTP part of AMR frames packing; not the AMR frame itself.
// F (1 bit): If set to 1, indicates that this frame is followed by // F (1 bit): If set to 1, indicates that this frame is followed by
// another speech frame in this payload; if set to 0, indicates that // another speech frame in this payload; if set to 0, indicates that
+4 -1
View File
@@ -37,11 +37,14 @@ void SingleAudioStream::copyPcmTo(Audio::DataWindow& output, int needed)
// Number of bytes to fill on this step // Number of bytes to fill on this step
auto requested = needed - output.filled(); auto requested = needed - output.filled();
// requested is in bytes: 16-bit samples at AUDIO_SAMPLERATE/AUDIO_CHANNELS
constexpr int bytesPerMs = AUDIO_SAMPLERATE / 1000 * sizeof(int16_t) * AUDIO_CHANNELS;
auto options = AudioReceiver::DecodeOptions{ auto options = AudioReceiver::DecodeOptions{
.mRealtimeProcessing = true, .mRealtimeProcessing = true,
.mResampleToMainRate = true, .mResampleToMainRate = true,
.mSkipDecode = false, .mSkipDecode = false,
.mElapsed = std::chrono::milliseconds(requested / (AUDIO_SAMPLERATE / 1000)) .mElapsed = std::chrono::milliseconds(requested / bytesPerMs)
}; };
// Try to get the data from receiver / decoder // Try to get the data from receiver / decoder
+28 -4
View File
@@ -497,12 +497,12 @@ size_t BufferReader::count() const
} }
// -------------- BufferWriter ---------------------- // -------------- BufferWriter ----------------------
BufferWriter::BufferWriter(ByteBuffer &buffer) BufferWriter::BufferWriter(ByteBuffer &buffer, size_t startOffset)
:mData(buffer.mutableData()), mIndex(0) :mBuffer(&buffer), mData(buffer.mutableData() + startOffset), mBase(startOffset), mIndex(0), mCapacity(0)
{} {}
BufferWriter::BufferWriter(void *output) BufferWriter::BufferWriter(void *output, size_t capacity)
:mData(reinterpret_cast<uint8_t*>(output)), mIndex(0) :mBuffer(nullptr), mData(reinterpret_cast<uint8_t*>(output)), mBase(0), mIndex(0), mCapacity(capacity)
{} {}
@@ -545,6 +545,30 @@ void BufferWriter::writeIp(const NetworkAddress& ip)
void BufferWriter::writeBuffer(const void* dataPtr, size_t dataSize) void BufferWriter::writeBuffer(const void* dataPtr, size_t dataSize)
{ {
if (dataSize == 0)
return;
if (mBuffer)
{
// Growable mode: ensure the backing buffer is large enough, then refresh
// the cached pointer in case resize() reallocated the storage.
size_t needed = mBase + mIndex + dataSize;
if (needed > mBuffer->size())
mBuffer->resize(needed);
mData = mBuffer->mutableData() + mBase;
}
else
{
// Bounded mode: never write past the caller-supplied capacity.
if (mIndex + dataSize > mCapacity)
{
assert(0 && "BufferWriter capacity exceeded");
dataSize = mIndex < mCapacity ? mCapacity - mIndex : 0;
if (dataSize == 0)
return;
}
}
memmove(mData + mIndex, dataPtr, dataSize); memmove(mData + mIndex, dataPtr, dataSize);
mIndex += dataSize; mIndex += dataSize;
} }
+9 -4
View File
@@ -172,12 +172,17 @@ namespace ice
class BufferWriter class BufferWriter
{ {
protected: protected:
uint8_t* mData; ByteBuffer* mBuffer; // Non-null => growable: writes resize this buffer to fit
size_t mIndex; uint8_t* mData; // Cached write pointer (refreshed on growth in growable mode)
size_t mBase; // Start offset within mBuffer (growable mode)
size_t mIndex; // Bytes written since mBase
size_t mCapacity; // Usable capacity from mData (raw/bounded mode)
public: public:
BufferWriter(void* output); // Bounded writer over caller-owned memory. Writes past 'capacity' are refused.
BufferWriter(ByteBuffer& buffer); BufferWriter(void* output, size_t capacity);
// Growable writer: the backing buffer is resized as needed so writes never overflow.
BufferWriter(ByteBuffer& buffer, size_t startOffset = 0);
void writeUInt(uint32_t value); void writeUInt(uint32_t value);
void writeUShort(uint16_t value); void writeUShort(uint16_t value);
+4 -2
View File
@@ -216,8 +216,10 @@ void StunMessage::buildPacket(ByteBuffer& buffer, const std::string& password)
for (size_t i=0; i<4; i++) for (size_t i=0; i<4; i++)
bitstream.writeBit(bit(msgtype, 3-i)); bitstream.writeBit(bit(msgtype, 3-i));
// Enqueue 2 bytes of length - now it is zero // Enqueue 2 bytes of length - now it is zero.
BufferWriter stream(buffer.mutableData() + bitstream.count() / 8); // Growable writer starting right after the bits already emitted; it resizes
// 'buffer' as attributes are appended so an oversized message cannot overflow.
BufferWriter stream(buffer, bitstream.count() / 8);
stream.writeUShort(0); stream.writeUShort(0);
// Enqueue magic cookie value // Enqueue magic cookie value