diff --git a/src/engine/engine_config.h b/src/engine/engine_config.h index a0ba0390..88b97966 100644 --- a/src/engine/engine_config.h +++ b/src/engine/engine_config.h @@ -114,4 +114,7 @@ // In milliseconds #define MT_SEVANA_FRAME_TIME 680 +// Number of samples +#define MT_MAX_DECODEBUFFER 32768 + #endif diff --git a/src/engine/media/MT_AudioReceiver.cpp b/src/engine/media/MT_AudioReceiver.cpp index d608a31a..2339998b 100644 --- a/src/engine/media/MT_AudioReceiver.cpp +++ b/src/engine/media/MT_AudioReceiver.cpp @@ -79,7 +79,7 @@ void RtpBuffer::setHigh(int milliseconds) mHigh = milliseconds; } -int RtpBuffer::high() +int RtpBuffer::high() const { return mHigh; } @@ -89,7 +89,7 @@ void RtpBuffer::setLow(int milliseconds) mLow = milliseconds; } -int RtpBuffer::low() +int RtpBuffer::low() const { return mLow; } @@ -99,7 +99,7 @@ void RtpBuffer::setPrebuffer(int milliseconds) mPrebuffer = milliseconds; } -int RtpBuffer::prebuffer() +int RtpBuffer::prebuffer() const { return mPrebuffer; } @@ -224,7 +224,7 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl) } else { - if (mLastSeqno.has_value()) + if (mLastSeqno) // It means we had previous packet { if (mPacketList.empty()) { @@ -237,6 +237,7 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl) auto& packet = *mPacketList.front(); uint32_t seqno = packet.rtp()->GetExtendedSequenceNumber(); + // Gap between new packet and previous on int gap = (int64_t)seqno - (int64_t)*mLastSeqno - 1; gap = std::min(gap, 127); @@ -244,9 +245,16 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl) { // std::cout << "Increase the packet loss for SSRC " << std::hex << mSsrc << std::endl; mStat.mPacketLoss++; - auto currentTimestamp = uint64_t(packet.rtp()->GetReceiveTime().GetDouble() * 1000000); - mStat.mPacketLossTimeline.push_back({gap, std::chrono::microseconds(currentTimestamp)}); - mLastSeqno = *mLastSeqno + 1; + auto currentTimestamp = std::chrono::microseconds(uint64_t(packet.rtp()->GetReceiveTime().GetDouble() * 1000000)); + + if (mStat.mPacketLossTimeline.empty() || (mStat.mPacketLossTimeline.back().mEndSeqno != seqno)) + mStat.mPacketLossTimeline.push_back({.mStartSeqno = *mLastSeqno, + .mEndSeqno = seqno, + .mGap = gap, + .mTimestamp = currentTimestamp}); + + mLastSeqno = *mLastSeqno + 1; // As we deal with the audio gap - return the silence and increase last seqno + result = FetchResult::Gap; } else @@ -475,235 +483,249 @@ bool AudioReceiver::add(const std::shared_ptr& p, Codec** de // Queue packet to buffer auto packet = mBuffer.add(p, time_length, samplerate).get(); - if (packet) - { - // Check if early decoding configured - if (mEarlyDecode && codec) - { - // Move data to packet buffer - size_t available = decode_packet(*codec, *p, mDecodedFrame, sizeof mDecodedFrame); - if (available > 0) - { - packet->pcm().resize(available / 2); - memcpy(packet->pcm().data(), mDecodedFrame, available / 2); - } - } - return true; - } - else - return false; + return packet; } -void AudioReceiver::processDecoded(Audio::DataWindow& output, int options) +void AudioReceiver::processDecoded(Audio::DataWindow& output, DecodeOptions options) { // Write to audio dump if requested if (mDecodedDump && mDecodedLength) mDecodedDump->write(mDecodedFrame, mDecodedLength); // Resample to target rate - bool resample = !(options & DecodeOptions_DontResample); - makeMonoAndResample(resample ? mCodec->samplerate() : 0, - mCodec->channels()); + makeMonoAndResample(options.mResampleToMainRate ? mCodec->samplerate() : 0, mCodec->channels()); // Send to output output.add(mResampledFrame, mResampledLength); } -AudioReceiver::DecodeResult AudioReceiver::getAudio(Audio::DataWindow& output, int options, int* rate) +void AudioReceiver::produceSilence(std::chrono::milliseconds length, Audio::DataWindow& output, DecodeOptions options) +{ + // Fill mDecodeBuffer as much as needed and call processDecoded() + // Depending on used codec mono or stereo silence should be produced + size_t chunks = length.count() / 10; + size_t tail = length.count() % 10; + size_t chunk_size = 10 * sizeof(int16_t) * mCodec->samplerate() / 1000 * mCodec->channels(); + size_t tail_size = tail * sizeof(int16_t) * mCodec->samplerate() / 1000 * mCodec->channels(); + for (size_t i = 0; i < chunks; i++) + { + memset(mDecodedFrame, 0, chunk_size); + mDecodedLength = chunk_size; + processDecoded(output, options); + } + if (tail) + { + memset(mDecodedFrame, 0, tail_size); + mDecodedLength = tail_size; + processDecoded(output, options); + } +} + +void AudioReceiver::produceCNG(std::chrono::milliseconds length, Audio::DataWindow& output, DecodeOptions options) +{ + int frames100ms = length.count() / 100; + for (int frameIndex = 0; frameIndex < frames100ms; frameIndex++) + { + if (options.mSkipDecode) + mDecodedLength = 0; + else + mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), 100, mDecodedFrame, false); + + if (mDecodedLength) + processDecoded(output, options); + } + + // Do not forget about tail! + int tail = length.count() % 100; + if (tail) + { + if (options.mSkipDecode) + mDecodedLength = 0; + else + mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), tail, reinterpret_cast(mDecodedFrame), false); + + if (mDecodedLength) + processDecoded(output, options); + } +} + +AudioReceiver::DecodeResult AudioReceiver::decodeGap(Audio::DataWindow& output, DecodeOptions options) +{ + ICELogDebug(<< "Gap detected."); + + mDecodedLength = mResampledLength = 0; + if (mCngPacket && mCodec) + { + // 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(mDecodedFrame), false); + } + else + if (mCodec && mFrameCount && !mCodecSettings.mSkipDecode) + { + // Do PLC to mDecodedFrame/mDecodedLength + if (options.mSkipDecode) + mDecodedLength = 0; + else + { + mDecodedLength = mCodec->plc(mFrameCount, mDecodedFrame, sizeof mDecodedFrame); + if (!mDecodedLength) + { + // PLC is not support or failed + // So substitute the silence + size_t nr_of_samples = mCodec->frameTime() * mCodec->samplerate() / 1000 * sizeof(short); + mDecodedLength = nr_of_samples * sizeof(short); + memset(mDecodedFrame, 0, mDecodedLength); + } + } + } + + if (mDecodedLength) + { + processDecoded(output, options); + return DecodeResult_Ok; + } + else + return DecodeResult_Skip; +} + +AudioReceiver::DecodeResult AudioReceiver::decodePacket(const RtpBuffer::ResultList& rl, Audio::DataWindow& output, DecodeOptions options, int* rate) +{ + DecodeResult result = DecodeResult_Skip; + + mFailedCount = 0; + for (const std::shared_ptr& p: rl) + { + assert(p); + // Check if we need to emit silence or CNG - previously CNG packet was detected. Emit CNG audio here if needed. + if (mLastPacketTimestamp && mLastPacketTimeLength && mCodec) + { + int units = p->rtp()->GetTimestamp() - *mLastPacketTimestamp; + int milliseconds = units / (mCodec->samplerate() / 1000); + if (milliseconds > mLastPacketTimeLength) + { + auto silenceLength = std::chrono::milliseconds(milliseconds - mLastPacketTimeLength); + + if (mCngPacket && options.mFillGapByCNG) + produceCNG(silenceLength, output, options); + else + produceSilence(silenceLength, output, options); + } + } + + mLastPacketTimestamp = p->rtp()->GetTimestamp(); + + // Find codec by payload type + int ptype = p->rtp()->GetPayloadType(); + mCodec = mCodecMap[ptype]; + if (mCodec) + { + if (rate) + *rate = mCodec->samplerate(); + + // Check if it is CNG packet + if ((ptype == 0 || ptype == 8) && p->rtp()->GetPayloadLength() >= 1 && p->rtp()->GetPayloadLength() <= 6) + { + if (options.mSkipDecode) + mDecodedLength = 0; + else + { + mCngPacket = p->rtp(); + mCngDecoder.decode3389(p->rtp()->GetPayloadData(), p->rtp()->GetPayloadLength()); + + // Emit CNG mLastPacketLength milliseconds + mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), mLastPacketTimeLength, + (short*)mDecodedFrame, true); + if (mDecodedLength) + processDecoded(output, options); + } + result = DecodeResult_Ok; + } + else + { + // Reset CNG packet as we get regular RTP packet + mCngPacket.reset(); + + // Handle here regular RTP packets + // Check if payload length is ok + size_t payload_length = p->rtp()->GetPayloadLength(); + size_t rtp_frame_length = mCodec->rtpLength(); + + int tail = rtp_frame_length ? payload_length % rtp_frame_length : 0; + + if (!tail) + { + // Find number of frames + mFrameCount = mCodec->rtpLength() ? p->rtp()->GetPayloadLength() / mCodec->rtpLength() : 1; + int frameLength = mCodec->rtpLength() ? mCodec->rtpLength() : (int)p->rtp()->GetPayloadLength(); + + // Save last packet time length + mLastPacketTimeLength = mFrameCount * mCodec->frameTime(); + + // Decode + for (int i=0; idecode(p->rtp()->GetPayloadData() + i * mCodec->rtpLength(), + frameLength, mDecodedFrame, sizeof mDecodedFrame); + if (mDecodedLength > 0) + processDecoded(output, options); + } + } + result = mFrameCount > 0 ? DecodeResult_Ok : DecodeResult_Skip; + + // Check for bitrate counter + updateAmrCodecStats(mCodec.get()); + } + else + { + // RTP packet with tail - it should not happen + result = DecodeResult_BadPacket; + } + } + } + } + return result; +} + +AudioReceiver::DecodeResult AudioReceiver::decodeNone(Audio::DataWindow& output, DecodeOptions options) +{ + ICELogDebug(<< "No packet available in jitter buffer"); + mFailedCount++; + return DecodeResult_Skip; +} + +AudioReceiver::DecodeResult AudioReceiver::getAudio(Audio::DataWindow& output, DecodeOptions options, int* rate) { DecodeResult result = DecodeResult_Skip; - bool had_decode = false; // Get next packet from buffer RtpBuffer::ResultList rl; RtpBuffer::FetchResult fr = mBuffer.fetch(rl); switch (fr) { - case RtpBuffer::FetchResult::Gap: - ICELogDebug(<< "Gap detected."); - - mDecodedLength = mResampledLength = 0; - if (mCngPacket && mCodec) - { - // 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(mDecodedFrame), false); - } - else - if (mCodec && mFrameCount && !mCodecSettings.mSkipDecode) - { - // Do PLC to mDecodedFrame/mDecodedLength - if (options & DecodeOptions_SkipDecode) - mDecodedLength = 0; - else - { - mDecodedLength = mCodec->plc(mFrameCount, mDecodedFrame, sizeof mDecodedFrame); - if (!mDecodedLength) - { - // PLC is not support or failed - // So substitute the silence - size_t nr_of_samples = mCodec->frameTime() * mCodec->samplerate() / 1000 * sizeof(short); - mDecodedLength = nr_of_samples * sizeof(short); - memset(mDecodedFrame, 0, mDecodedLength); - } - } - } - - if (mDecodedLength) - { - processDecoded(output, options); - result = DecodeResult_Ok; - } - break; - - case RtpBuffer::FetchResult::NoPacket: - ICELogDebug(<< "No packet available in jitter buffer"); - mFailedCount++; - break; - - case RtpBuffer::FetchResult::RegularPacket: - mFailedCount = 0; - for (std::shared_ptr& p: rl) - { - assert(p); - // Check if previously CNG packet was detected. Emit CNG audio here if needed. - if (options & DecodeOptions_FillCngGap && mCngPacket && mCodec) - { - // Fill CNG audio is server mode is present - int units = p->rtp()->GetTimestamp() - mCngPacket->GetTimestamp(); - int milliseconds = units / (mCodec->samplerate() / 1000); - if (milliseconds > mLastPacketTimeLength) - { - int frames100ms = milliseconds / 100; - for (int frameIndex = 0; frameIndex < frames100ms; frameIndex++) - { - if (options & DecodeOptions_SkipDecode) - mDecodedLength = 0; - else - mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), 100, - reinterpret_cast(mDecodedFrame), false); - - if (mDecodedLength) - processDecoded(output, options); - } - // Do not forget about tail! - int tail = milliseconds % 100; - if (tail) - { - if (options & DecodeOptions_SkipDecode) - mDecodedLength = 0; - else - mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), tail, - reinterpret_cast(mDecodedFrame), false); - - if (mDecodedLength) - processDecoded(output, options); - } - result = DecodeResult_Ok; - } - } - - if (mEarlyDecode) - { - // ToDo - copy the decoded data to output buffer - - } - else - { - // Find codec by payload type - int ptype = p->rtp()->GetPayloadType(); - mCodec = mCodecMap[ptype]; - if (mCodec) - { - if (rate) - *rate = mCodec->samplerate(); - - // Check if it is CNG packet - if ((ptype == 0 || ptype == 8) && p->rtp()->GetPayloadLength() >= 1 && p->rtp()->GetPayloadLength() <= 6) - { - if (options & DecodeOptions_SkipDecode) - mDecodedLength = 0; - else - { - mCngPacket = p->rtp(); - mCngDecoder.decode3389(p->rtp()->GetPayloadData(), p->rtp()->GetPayloadLength()); - // Emit CNG mLastPacketLength milliseconds - mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), mLastPacketTimeLength, - (short*)mDecodedFrame, true); - if (mDecodedLength) - processDecoded(output, options); - } - result = DecodeResult_Ok; - } - else - { - // Reset CNG packet - mCngPacket.reset(); - - // Handle here regular RTP packets - // Check if payload length is ok - size_t payload_length = p->rtp()->GetPayloadLength(); - size_t rtp_frame_length = mCodec->rtpLength(); - - int tail = rtp_frame_length ? payload_length % rtp_frame_length : 0; - - if (!tail) - { - // Find number of frames - mFrameCount = mCodec->rtpLength() ? p->rtp()->GetPayloadLength() / mCodec->rtpLength() : 1; - int frameLength = mCodec->rtpLength() ? mCodec->rtpLength() : (int)p->rtp()->GetPayloadLength(); - - // Save last packet time length - mLastPacketTimeLength = mFrameCount * mCodec->frameTime(); - - // Decode - for (int i=0; idecode(p->rtp()->GetPayloadData() + i * mCodec->rtpLength(), - frameLength, mDecodedFrame, sizeof mDecodedFrame); - if (mDecodedLength > 0) - processDecoded(output, options); - } - } - result = mFrameCount > 0 ? DecodeResult_Ok : DecodeResult_Skip; - - // Check for bitrate counter - processStatisticsWithAmrCodec(mCodec.get()); - } - else - { - result = DecodeResult_BadPacket; - // ICELogMedia(<< "RTP packet with tail."); - } - } - } - } - } - break; - + case RtpBuffer::FetchResult::Gap: result = decodeGap(output, options); break; + case RtpBuffer::FetchResult::NoPacket: result = decodeNone(output, options); break; + case RtpBuffer::FetchResult::RegularPacket: result = decodePacket(rl, output, options, rate); break; default: assert(0); } - if (had_decode) + if (result == DecodeResult_Ok) { - // mStat.mDecodeRequested++; - if (mLastDecodeTime == 0.0) - mLastDecodeTime = now_ms(); + // Decode statistics + if (!mLastDecodeTimestamp) + mLastDecodeTimestamp = std::chrono::steady_clock::now(); else { - float t = now_ms(); - mStat.mDecodingInterval.process(t - mLastDecodeTime); - mLastDecodeTime = t; + auto t = std::chrono::steady_clock::now(); + mStat.mDecodingInterval.process(std::chrono::duration_cast(t - *mLastDecodeTimestamp).count()); + mLastDecodeTimestamp = t; } } return result; @@ -752,7 +774,7 @@ Codec* AudioReceiver::findCodec(int payloadType) } -void AudioReceiver::processStatisticsWithAmrCodec(Codec* c) +void AudioReceiver::updateAmrCodecStats(Codec* c) { #if !defined(TARGET_ANDROID) && !defined(TARGET_OPENWRT) && !defined(TARGET_WIN) && !defined(TARGET_RPI) && defined(USE_AMR_CODEC) AmrNbCodec* nb = dynamic_cast(c); @@ -761,8 +783,8 @@ void AudioReceiver::processStatisticsWithAmrCodec(Codec* c) if (nb != nullptr) mStat.mBitrateSwitchCounter = nb->getSwitchCounter(); else - if (wb != nullptr) - mStat.mBitrateSwitchCounter = wb->getSwitchCounter(); + if (wb != nullptr) + mStat.mBitrateSwitchCounter = wb->getSwitchCounter(); #endif } @@ -819,13 +841,10 @@ int AudioReceiver::samplerateFor(jrtplib::RTPPacket& p) // ----------------------- DtmfReceiver ------------------- DtmfReceiver::DtmfReceiver(Statistics& stat) :Receiver(stat) -{ -} +{} DtmfReceiver::~DtmfReceiver() -{ -} +{} void DtmfReceiver::add(std::shared_ptr /*p*/) -{ -} +{} diff --git a/src/engine/media/MT_AudioReceiver.h b/src/engine/media/MT_AudioReceiver.h index af739dce..8a3efd70 100644 --- a/src/engine/media/MT_AudioReceiver.h +++ b/src/engine/media/MT_AudioReceiver.h @@ -6,6 +6,7 @@ #ifndef __MT_AUDIO_RECEIVER_H #define __MT_AUDIO_RECEIVER_H +#include "../engine_config.h" #include "MT_Stream.h" #include "MT_CodecList.h" #include "MT_CngHelper.h" @@ -61,17 +62,17 @@ public: RtpBuffer(Statistics& stat); ~RtpBuffer(); - unsigned ssrc(); + unsigned ssrc() const; void setSsrc(unsigned ssrc); void setHigh(int milliseconds); - int high(); + int high() const; void setLow(int milliseconds); - int low(); + int low() const; void setPrebuffer(int milliseconds); - int prebuffer(); + int prebuffer() const; int getNumberOfReturnedPackets() const; int getNumberOfAddPackets() const; @@ -88,12 +89,12 @@ public: FetchResult fetch(ResultList& rl); protected: - unsigned mSsrc = 0; - int mHigh = RTP_BUFFER_HIGH, - mLow = RTP_BUFFER_LOW, - mPrebuffer = RTP_BUFFER_PREBUFFER; - int mReturnedCounter = 0, - mAddCounter = 0; + unsigned mSsrc = 0; + int mHigh = RTP_BUFFER_HIGH, + mLow = RTP_BUFFER_LOW, + mPrebuffer = RTP_BUFFER_PREBUFFER; + int mReturnedCounter = 0, + mAddCounter = 0; mutable Mutex mGuard; typedef std::vector> PacketList; @@ -105,7 +106,7 @@ protected: std::optional mLastSeqno; // To calculate average interval between packet add. It is close to jitter but more useful in debugging. - float mLastAddTime = 0.0; + float mLastAddTime = 0.0f; }; class Receiver @@ -125,29 +126,37 @@ public: ~AudioReceiver(); // Update codec settings - void setCodecSettings(const CodecList::Settings& codecSettings); - CodecList::Settings& getCodecSettings(); + void setCodecSettings(const CodecList::Settings& codecSettings); + CodecList::Settings& getCodecSettings(); + // Returns false when packet is rejected as illegal. codec parameter will show codec which will be used for decoding. // Lifetime of pointer to codec is limited by lifetime of AudioReceiver (it is container). bool add(const std::shared_ptr& p, Codec** codec = nullptr); // Returns false when there is no rtp data from jitter - enum DecodeOptions + /*enum DecodeOptions { DecodeOptions_ResampleToMainRate = 0, DecodeOptions_DontResample = 1, DecodeOptions_FillCngGap = 2, DecodeOptions_SkipDecode = 4 + };*/ + + struct DecodeOptions + { + bool mResampleToMainRate = true; + bool mFillGapByCNG = false; + bool mSkipDecode = false; }; enum DecodeResult { - DecodeResult_Ok, - DecodeResult_Skip, - DecodeResult_BadPacket + DecodeResult_Ok, // Decoded ok + DecodeResult_Skip, // Just no data - emit silence instead + DecodeResult_BadPacket // Error happened during the decode }; - DecodeResult getAudio(Audio::DataWindow& output, int options = DecodeOptions_ResampleToMainRate, int* rate = nullptr); + DecodeResult getAudio(Audio::DataWindow& output, DecodeOptions options = {.mResampleToMainRate = true, .mFillGapByCNG = false, .mSkipDecode = false}, int* rate = nullptr); // Looks for codec by payload type Codec* findCodec(int payloadType); @@ -163,52 +172,57 @@ public: int samplerateFor(jrtplib::RTPPacket& p); protected: - RtpBuffer mBuffer; - CodecMap mCodecMap; - PCodec mCodec; - int mFrameCount = 0; - CodecList::Settings mCodecSettings; - CodecList mCodecList; - JitterStatistics mJitterStats; + RtpBuffer mBuffer; // Jitter buffer itself + CodecMap mCodecMap; + PCodec mCodec; + int mFrameCount = 0; + CodecList::Settings mCodecSettings; + CodecList mCodecList; + JitterStatistics mJitterStats; std::shared_ptr mCngPacket; - CngDecoder mCngDecoder; - - // Decode RTP early, do not wait for speaker callback - bool mEarlyDecode = false; + CngDecoder mCngDecoder; + size_t mDTXSamplesToEmit = 0; // How much silence (or CNG) should be emited before next RTP packet gets into the action // Buffer to hold decoded data - char mDecodedFrame[65536]; - int mDecodedLength = 0; + int16_t mDecodedFrame[MT_MAX_DECODEBUFFER]; + size_t mDecodedLength = 0; - // Buffer to hold data converted to stereo/mono - char mConvertedFrame[32768]; - int mConvertedLength = 0; + // Buffer to hold data converted to stereo/mono; there is multiplier 2 as it can be stereo audio + int16_t mConvertedFrame[MT_MAX_DECODEBUFFER * 2]; + size_t mConvertedLength = 0; // Buffer to hold data resampled to AUDIO_SAMPLERATE - char mResampledFrame[65536]; - int mResampledLength = 0; + int16_t mResampledFrame[MT_MAX_DECODEBUFFER]; + size_t mResampledLength = 0; // Last packet time length int mLastPacketTimeLength = 0; + std::optional mLastPacketTimestamp; int mFailedCount = 0; - Audio::Resampler mResampler8, mResampler16, - mResampler32, mResampler48; + Audio::Resampler mResampler8, mResampler16, mResampler32, mResampler48; Audio::PWavFileWriter mDecodedDump; - float mLastDecodeTime = 0.0; // Time last call happened to codec->decode() + std::optional mLastDecodeTimestamp; // Time last call happened to codec->decode() - float mIntervalSum = 0.0; + float mIntervalSum = 0.0f; int mIntervalCount = 0; // Zero rate will make audio mono but resampling will be skipped void makeMonoAndResample(int rate, int channels); // Resamples, sends to analysis, writes to dump and queues to output decoded frames from mDecodedFrame - void processDecoded(Audio::DataWindow& output, int options); + void processDecoded(Audio::DataWindow& output, DecodeOptions options); + void produceSilence(std::chrono::milliseconds length, Audio::DataWindow& output, DecodeOptions options); + void produceCNG(std::chrono::milliseconds length, Audio::DataWindow& output, DecodeOptions options); - void processStatisticsWithAmrCodec(Codec* c); + // Calculate bitrate switch statistics for AMR codecs + void updateAmrCodecStats(Codec* c); + + DecodeResult decodeGap(Audio::DataWindow& output, DecodeOptions options); + DecodeResult decodePacket(const RtpBuffer::ResultList& rl, Audio::DataWindow& output, DecodeOptions options, int* rate = nullptr); + DecodeResult decodeNone(Audio::DataWindow& output, DecodeOptions options); }; class DtmfReceiver: public Receiver diff --git a/src/engine/media/MT_SingleAudioStream.cpp b/src/engine/media/MT_SingleAudioStream.cpp index f755fb86..2b3065f5 100644 --- a/src/engine/media/MT_SingleAudioStream.cpp +++ b/src/engine/media/MT_SingleAudioStream.cpp @@ -35,7 +35,7 @@ void SingleAudioStream::copyPcmTo(Audio::DataWindow& output, int needed) { while (output.filled() < needed) { - if (mReceiver.getAudio(output) != AudioReceiver::DecodeResult_Ok) + if (mReceiver.getAudio(output, {}) != AudioReceiver::DecodeResult_Ok) break; } diff --git a/src/engine/media/MT_Statistics.h b/src/engine/media/MT_Statistics.h index 9c838b0f..cf2c505e 100644 --- a/src/engine/media/MT_Statistics.h +++ b/src/engine/media/MT_Statistics.h @@ -52,7 +52,10 @@ protected: struct PacketLossEvent { - int mGap = 0; + // This is extended sequence numbers (not the raw uint16_t seqno) + uint32_t mStartSeqno = 0, + mEndSeqno = 0; + int mGap = 0; std::chrono::microseconds mTimestamp; };