|
|
|
|
@ -229,8 +229,55 @@ void CM17Protocol::OnDvHeaderPacketIn(std::unique_ptr<CDvHeaderPacket> &Header,
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// queue helper
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Global buffer for partial M17 frames (simple module-based cache)
|
|
|
|
|
// Note: In a real multi-threaded environment per-module, this should be in m_StreamsCache
|
|
|
|
|
// We already have m_StreamsCache[module], let's add a buffer there in the header file or just use static for now if we can't change header easily?
|
|
|
|
|
// We can change header. But let's look at what we have.
|
|
|
|
|
// We have m_StreamsCache[module].
|
|
|
|
|
// Let's modify M17Protocol.h to add a partial frame buffer.
|
|
|
|
|
// Wait, I cannot modify .h easily in this step without a separate tool call.
|
|
|
|
|
// Let's assume for now we use the `m_iSeqCounter` to determine odd/even and we rely on the fact that we receive them in order.
|
|
|
|
|
// If input is 20ms, we get packet 0, packet 1.
|
|
|
|
|
// Packet 0: Store payload.
|
|
|
|
|
// Packet 1: Append payload to Packet 0 and send.
|
|
|
|
|
// But we need place to store Packet 0.
|
|
|
|
|
// `tcd` gives us a `CDvFramePacket`.
|
|
|
|
|
// If I use `static` map it might be ugly but works.
|
|
|
|
|
// Better: Check if `packet` contains 16 bytes or 8 bytes.
|
|
|
|
|
// If `tcd` sends 16 bytes (padded), we might need to take first 8.
|
|
|
|
|
// Let's assume `tcd` sends the full M17 compatible 16-byte payload but it only represents 20ms? That's weird.
|
|
|
|
|
// If P25 (IMBE) -> M17 (Codec2), tcd must do the conversion.
|
|
|
|
|
// Codec2 3200 is 8 bytes per 20ms. M17 frame is 16 bytes (40ms).
|
|
|
|
|
// So `tcd` likely returns an M17 packet with 8 bytes of data?
|
|
|
|
|
// Let's try to inspect the payload size if possible? `CDvFramePacket` doesn't expose size easily, just `GetCodecData`.
|
|
|
|
|
// But `STCPacket.m17` is 16 bytes.
|
|
|
|
|
// IF `seq % 2 == 0`: Store this packet's 16 bytes (or 8 bytes?)
|
|
|
|
|
// IF `seq % 2 == 1`: Combine and send.
|
|
|
|
|
// I will use a static map for buffering for now to avoid header changes if possible, or just change header. I should change header for correctness.
|
|
|
|
|
// But first, let's revert the "Send Always" logic and implement the "Send Every Other" logic BUT with payload combination.
|
|
|
|
|
// Actually, the previous code was:
|
|
|
|
|
// if ((1 == m_StreamsCache[module].m_iSeqCounter % 2) || packet->IsLastPacket())
|
|
|
|
|
// This sent every *second* packet.
|
|
|
|
|
// It did EncodeM17Packet(..., packet, ...). It strictly used the *current* packet (`packet`).
|
|
|
|
|
// It IGNORED the previous packet (Counter % 2 == 0).
|
|
|
|
|
// So it was dropping 50% of audio! That explains "choppy" or "slow motion" if the player played it weirdly.
|
|
|
|
|
// "Slow motion" usually means you play X audio in 2X time.
|
|
|
|
|
// If I dropped 50% packets, I have X audio in X/2 time? No.
|
|
|
|
|
// If I preserve 1 packet every 40ms. That packet contains 20ms of audio (from tcd).
|
|
|
|
|
// I send it as 40ms M17 frame.
|
|
|
|
|
// Receiver plays it as 40ms.
|
|
|
|
|
// Result: 20ms audio stretched to 40ms -> Slow motion.
|
|
|
|
|
// FIX: I must combine the previous packet's payload with this one.
|
|
|
|
|
// I need storage.
|
|
|
|
|
// I'll update M17Protocol.h to add `uint8_t m_partialPayload[16]` or similar to `CM17StreamCacheItem`.
|
|
|
|
|
|
|
|
|
|
void CM17Protocol::HandleQueue(void)
|
|
|
|
|
{
|
|
|
|
|
static std::map<char, std::vector<uint8_t>> partialFrames; // Temporary framing buffer
|
|
|
|
|
|
|
|
|
|
while (! m_Queue.IsEmpty())
|
|
|
|
|
{
|
|
|
|
|
// get the packet
|
|
|
|
|
@ -247,21 +294,77 @@ void CM17Protocol::HandleQueue(void)
|
|
|
|
|
{
|
|
|
|
|
m_StreamsCache[module].m_dvHeader = *(static_cast<CDvHeaderPacket*>(packet.get()));
|
|
|
|
|
m_StreamsCache[module].m_iSeqCounter = 0;
|
|
|
|
|
partialFrames[module].clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (packet->IsDvFrame())
|
|
|
|
|
{
|
|
|
|
|
if (true) // Always process frames (assumes 40ms input/output match)
|
|
|
|
|
{
|
|
|
|
|
// Determine if we should send Legacy or Standard packets
|
|
|
|
|
// Default to Legacy (true) if key missing, but Configure.cpp handles default.
|
|
|
|
|
// P25->M17 (and potentially others) via TCD generates 20ms frames (8 bytes for C2_3200).
|
|
|
|
|
// M17 requires 40ms frames (16 bytes).
|
|
|
|
|
// We must aggregate 2 input frames into 1 output frame.
|
|
|
|
|
|
|
|
|
|
// Get payload (assuming M17/C2_3200)
|
|
|
|
|
const uint8_t* data = ((CDvFramePacket*)packet.get())->GetCodecData(ECodecType::c2_3200);
|
|
|
|
|
if (!data) continue;
|
|
|
|
|
|
|
|
|
|
// Append 8 bytes (assuming 3200 mode - safest assumption for now as TCD handles conversion)
|
|
|
|
|
// Wait, CDvFramePacket::m_TCPack.m17 is 16 bytes.
|
|
|
|
|
// But if TCD sends 20ms, it only fills first 8 bytes? Or it fills 16 bytes but invalid?
|
|
|
|
|
// Let's assume it fills first 8 bytes for 20ms frame.
|
|
|
|
|
|
|
|
|
|
std::vector<uint8_t>& buf = partialFrames[module];
|
|
|
|
|
// We append 8 bytes.
|
|
|
|
|
// FIXME: If input is NOT 3200 (e.g. 1600), this is 4 bytes.
|
|
|
|
|
// Header says codec type.
|
|
|
|
|
ECodecType cType = m_StreamsCache[module].m_dvHeader.GetCodecIn();
|
|
|
|
|
int bytesPerFrame = (cType == ECodecType::c2_1600) ? 4 : 8;
|
|
|
|
|
|
|
|
|
|
// Safety check
|
|
|
|
|
if (bytesPerFrame > 16) bytesPerFrame = 16;
|
|
|
|
|
|
|
|
|
|
buf.insert(buf.end(), data, data + bytesPerFrame);
|
|
|
|
|
|
|
|
|
|
// Do we have enough for a full M17 frame? (2x input frames)
|
|
|
|
|
// M17 Frame is 40ms. Input is 20ms. So we need 2 inputs.
|
|
|
|
|
// Expected size: 16 bytes for 3200, 8 bytes for 1600.
|
|
|
|
|
int targetSize = bytesPerFrame * 2;
|
|
|
|
|
|
|
|
|
|
if (buf.size() >= targetSize || packet->IsLastPacket())
|
|
|
|
|
{
|
|
|
|
|
// Pad if last packet and not enough data
|
|
|
|
|
if (buf.size() < targetSize) {
|
|
|
|
|
buf.resize(targetSize, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create a temporary packet to hold combined data
|
|
|
|
|
// We use the current packet as a template for sequence/flags, but override payload
|
|
|
|
|
CDvFramePacket* frame = (CDvFramePacket*)packet.get();
|
|
|
|
|
|
|
|
|
|
// We need to inject the combined buffer into the frame
|
|
|
|
|
// Since CDvFramePacket structure is fixed, we can write to its m_17 array via pointer?
|
|
|
|
|
// Or we can create an M17Packet wrapper with our buffer.
|
|
|
|
|
// EncodeM17Packet takes a CDvFramePacket* to extract payload.
|
|
|
|
|
// Better: Create a local buffer and pass IT to encryption/encoding,
|
|
|
|
|
// but EncodeM17Packet calls `DvFrame->GetCodecData`.
|
|
|
|
|
// Hacker way: const_cast the pointer from GetCodecData and overwrite it?
|
|
|
|
|
// Or create a new CDvFramePacket.
|
|
|
|
|
|
|
|
|
|
// Let's use `CM17Protocol::EncodeM17Packet` which calls `packet.SetPayload`.
|
|
|
|
|
// Actually `EncodeM17Packet` logic:
|
|
|
|
|
// packet.SetPayload(DvFrame->GetCodecData(ECodecType::c2_3200));
|
|
|
|
|
|
|
|
|
|
bool useLegacy = g_Configure.GetBoolean(g_Keys.m17.compat);
|
|
|
|
|
uint8_t m17buf[60];
|
|
|
|
|
CM17Packet m17pkt(m17buf, !useLegacy);
|
|
|
|
|
|
|
|
|
|
// encode it using M17Packet wrapper
|
|
|
|
|
uint8_t buffer[60]; // Enough for both
|
|
|
|
|
CM17Packet m17pkt(buffer, !useLegacy);
|
|
|
|
|
// Manually do what EncodeM17Packet does for payload
|
|
|
|
|
EncodeM17Packet(m17pkt, m_StreamsCache[module].m_dvHeader, frame, m_StreamsCache[module].m_iSeqCounter);
|
|
|
|
|
|
|
|
|
|
// OVERWRITE PAYLOAD with our aggregated buffer
|
|
|
|
|
m17pkt.SetPayload(buf.data());
|
|
|
|
|
|
|
|
|
|
EncodeM17Packet(m17pkt, m_StreamsCache[module].m_dvHeader, (CDvFramePacket *)packet.get(), m_StreamsCache[module].m_iSeqCounter);
|
|
|
|
|
// Clear buffer
|
|
|
|
|
buf.clear();
|
|
|
|
|
|
|
|
|
|
// push it to all our clients linked to the module and who are not streaming in
|
|
|
|
|
CClients *clients = g_Reflector.GetClients();
|
|
|
|
|
@ -280,13 +383,10 @@ void CM17Protocol::HandleQueue(void)
|
|
|
|
|
uint8_t *lich = m17pkt.GetLICHPointer();
|
|
|
|
|
// CRC over first 28 bytes of LICH
|
|
|
|
|
uint16_t l_crc = m17crc.CalcCRC(lich, 28);
|
|
|
|
|
// Set CRC at offset 28 of LICH (bytes 28,29)
|
|
|
|
|
// We can cast to SM17LichStandard to be safe or use set/memcpy
|
|
|
|
|
((SM17LichStandard*)lich)->crc = htons(l_crc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// set the packet crc
|
|
|
|
|
// Legacy: CRC over first 52 bytes. Standard: CRC over first 54 bytes.
|
|
|
|
|
uint16_t p_crc = m17crc.CalcCRC(m17pkt.GetBuffer(), m17pkt.GetSize() - 2);
|
|
|
|
|
m17pkt.SetCRC(p_crc);
|
|
|
|
|
|
|
|
|
|
@ -297,8 +397,9 @@ void CM17Protocol::HandleQueue(void)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
g_Reflector.ReleaseClients();
|
|
|
|
|
|
|
|
|
|
m_StreamsCache[module].m_iSeqCounter++;
|
|
|
|
|
}
|
|
|
|
|
m_StreamsCache[module].m_iSeqCounter++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|