From 034c45396fc23899df021d4838ef963f5b041671 Mon Sep 17 00:00:00 2001 From: Dave Behnke <916775+dbehnke@users.noreply.github.com> Date: Sun, 28 Dec 2025 12:37:06 -0500 Subject: [PATCH] Fix M17 sped-up audio: Pace input packets by 20ms --- reflector/M17Protocol.cpp | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/reflector/M17Protocol.cpp b/reflector/M17Protocol.cpp index 1908bdc..5406bc1 100644 --- a/reflector/M17Protocol.cpp +++ b/reflector/M17Protocol.cpp @@ -22,6 +22,16 @@ #include "M17Protocol.h" #include "M17Packet.h" #include "Global.h" +#include +#include + +struct DelayedM17Packet { + std::chrono::steady_clock::time_point releaseTime; + std::unique_ptr packet; + CIp ip; +}; + +static std::deque g_M17DelayedQueue; //////////////////////////////////////////////////////////////////////////////////////// // constructor @@ -137,7 +147,14 @@ void CM17Protocol::Task(void) Frame->SetLastPacket(false); OnDvFramePacketIn(Frame, &Ip); - OnDvFramePacketIn(secondFrame, &Ip); + + // Delay second packet by 20ms to pace output for P25/DMR destination + // Pacing is critical to prevent jitter buffer collapse ("sped up" audio) + DelayedM17Packet delayed; + delayed.releaseTime = std::chrono::steady_clock::now() + std::chrono::milliseconds(20); + delayed.packet = std::move(secondFrame); + delayed.ip = Ip; + g_M17DelayedQueue.push_back(std::move(delayed)); } else { @@ -222,6 +239,25 @@ void CM17Protocol::Task(void) // handle queue from reflector HandleQueue(); + // handle delayed input (pacing) + if (!g_M17DelayedQueue.empty()) { + auto now = std::chrono::steady_clock::now(); + while (!g_M17DelayedQueue.empty()) { + if (now >= g_M17DelayedQueue.front().releaseTime) { + // Process delayed packet + auto& item = g_M17DelayedQueue.front(); + OnDvFramePacketIn(item.packet, &item.ip); // Helper called on instance? OnDvFramePacketIn is member. + // Wait, OnDvFramePacketIn is non-static member function. + // g_M17DelayedQueue is static (global). + // But Task() is member. We are inside member function. + // We can call member function. + g_M17DelayedQueue.pop_front(); + } else { + break; // Queue is sorted by time + } + } + } + // keep client alive if ( m_LastKeepaliveTime.time() > M17_KEEPALIVE_PERIOD ) {