diff --git a/src/engine/media/MT_AudioCodec.cpp b/src/engine/media/MT_AudioCodec.cpp index 1ddc7ea1..5e7a2358 100644 --- a/src/engine/media/MT_AudioCodec.cpp +++ b/src/engine/media/MT_AudioCodec.cpp @@ -1132,13 +1132,15 @@ G722Codec::~G722Codec() Codec::Info G722Codec::info() { - // ToDo: double check the G722 calls - remember RFC has bug about samplerate + // G.722 decodes to 16 kHz (640 bytes of PCM per 20 ms frame), but RFC 3551 keeps its RTP + // clock at 8 kHz for historical reasons: the timestamp unit is 1/8000, not 1/samplerate. return {.mName = G722_MIME_NAME, - .mSamplerate = 8000, + .mSamplerate = 16000, .mChannels = 1, .mPcmLength = 640, .mFrameTime = 20, - .mRtpLength = 160}; + .mRtpLength = 160, + .mTimestampUnit = 1.0f / 8000}; } Codec::EncodeResult G722Codec::encode(std::span input, std::span output) @@ -1186,7 +1188,8 @@ const char* G722Codec::G722Factory::name() int G722Codec::G722Factory::samplerate() { - // Although G722 uses 16000 as rate for timestamping RTP frames - in fact it is 8KHz codec. So return 8KHz here. + // The rate SDP advertises (rtpmap G722/8000) is the RTP clock, 8 kHz by RFC 3551, although the + // audio is 16 kHz; see G722Codec::info(). return 8000; } diff --git a/src/engine/media/MT_AudioReceiver.cpp b/src/engine/media/MT_AudioReceiver.cpp index 6cca4480..45776bd2 100644 --- a/src/engine/media/MT_AudioReceiver.cpp +++ b/src/engine/media/MT_AudioReceiver.cpp @@ -526,7 +526,7 @@ Codec* AudioReceiver::add(const std::shared_ptr& p) time_length = lround(double(payloadLength) / codec->rtpLength() * codec->frameTime()); if (codec) - samplerate = codec->samplerate(); + samplerate = codec->rtpClockRate(); // RTP timestamp units, not PCM rate (G.722) } // Process jitter anyway - can we decode payload or not @@ -724,7 +724,7 @@ AudioReceiver::DecodeResult AudioReceiver::decodePacketTo(Audio::DataWindow& out if (mLastPacketTimestamp && mLastPacketTimeLength && mCodec) { int units = rtp.GetTimestamp() - *mLastPacketTimestamp; - int milliseconds = units / (mCodec->samplerate() / 1000); + int milliseconds = units / (mCodec->rtpClockRate() / 1000); if (milliseconds > mLastPacketTimeLength) { auto silenceLength = std::chrono::milliseconds(milliseconds - mLastPacketTimeLength); @@ -1157,11 +1157,16 @@ AudioReceiver::MediaInfo AudioReceiver::infoFor(jrtplib::RTPPacket& p) if (codec->rtpLength() != 0) { - int frameCount = static_cast(p.GetPayloadLength() / codec->rtpLength()); - if (p.GetPayloadType() == 9 /*G729A silence*/ && p.GetPayloadLength() % codec->rtpLength()) - frameCount++; - - packetTime = std::chrono::milliseconds(frameCount * codec->frameTime()); + // A G.729 (PT 18) SID frame is shorter than a voice frame but stands for one; other + // codecs scale with the payload (G.722 at 10 ms ptime carries half a 160-byte frame). + if (p.GetPayloadType() == 18 && p.GetPayloadLength() % codec->rtpLength()) + { + int frameCount = static_cast(p.GetPayloadLength() / codec->rtpLength()) + 1; + packetTime = std::chrono::milliseconds(frameCount * codec->frameTime()); + } + else + packetTime = std::chrono::milliseconds( + lround(double(p.GetPayloadLength()) / codec->rtpLength() * codec->frameTime())); } else if (typeid(*codec) == typeid(OpusCodec)) { diff --git a/src/engine/media/MT_AudioStream.cpp b/src/engine/media/MT_AudioStream.cpp index 3c29f490..bc63518a 100644 --- a/src/engine/media/MT_AudioStream.cpp +++ b/src/engine/media/MT_AudioStream.cpp @@ -114,7 +114,7 @@ void AudioStream::setTransmittingCodec(Codec::Factory& factory, int payloadType) mTransmittingCodec = factory.create(); mTransmittingPayloadType = payloadType; if (mRtpSession.IsActive()) - mRtpSession.SetTimestampUnit(1.0 / mTransmittingCodec->samplerate()); + mRtpSession.SetTimestampUnit(mTransmittingCodec->timestampUnit()); } PCodec AudioStream::transmittingCodec() @@ -241,7 +241,7 @@ void AudioStream::addData(const void* buffer, int bytes) ICELogMedia(<< "Sending RTP packet pt = " << mTransmittingPayloadType << ", plength = " << (int)mEncodedAudio.size() << " to "); mRtpSession.SendPacketEx(mEncodedAudio.data(), mEncodedAudio.size(), mTransmittingPayloadType, false, - packetTime * codec->samplerate() / 1000, 0, nullptr, 0); + packetTime * codec->rtpClockRate() / 1000, 0, nullptr, 0); mEncodedAudio.clear(); encodedTime = 0; } diff --git a/src/engine/media/MT_Codec.h b/src/engine/media/MT_Codec.h index 987b7d03..799c0bff 100644 --- a/src/engine/media/MT_Codec.h +++ b/src/engine/media/MT_Codec.h @@ -66,6 +66,10 @@ public: int frameTime() { return info().mFrameTime; } std::string name() { return info().mName; } float timestampUnit() { return info().mTimestampUnit == 0.0f ? 1.0f / info().mSamplerate : info().mTimestampUnit; } + // RTP clock rate in Hz, the unit of RTP timestamps. Equals samplerate() except for codecs + // whose RTP clock differs from the decoded audio: G.722 decodes to 16 kHz but its RTP clock + // is 8 kHz (RFC 3551). Use it for timestamp arithmetic and jitter, samplerate() for PCM. + int rtpClockRate() { return static_cast(1.0f / timestampUnit() + 0.5f); } Audio::Format getAudioFormat() { return Audio::Format(this->info().mSamplerate, this->info().mChannels); }